Frage

Ich habe einen menubutton, die, wenn geklickt wird, sollte ein Menü anzuzeigen, mit einer bestimmten Sequenz von Zeichenfolgen.Genau das, was strings sind, in dieser Reihenfolge, wissen wir nicht erst zur Laufzeit, also das Menü, das erscheint, erzeugt werden müssen, in diesem moment.Hier ist was ich habe:

class para_frame(Frame):
    def __init__(self, para=None, *args, **kwargs):
        # ...

        # menu button for adding tags that already exist in other para's
        self.add_tag_mb = Menubutton(self, text='Add tags...')

        # this menu needs to re-create itself every time it's clicked
        self.add_tag_menu = Menu(self.add_tag_mb,
                                 tearoff=0,
                                 postcommand = self.build_add_tag_menu)

        self.add_tag_mb['menu'] = self.add_tag_menu

    # ...

    def build_add_tag_menu(self):
        self.add_tag_menu.delete(0, END) # clear whatever was in the menu before

        all_tags = self.get_article().all_tags()
        # we don't want the menu to include tags that already in this para
        menu_tags = [tag for tag in all_tags if tag not in self.para.tags]

        if menu_tags:
            for tag in menu_tags:
                def new_command():
                    self.add_tag(tag)

                self.add_tag_menu.add_command(label = tag,
                                              command = new_command)
        else:
            self.add_tag_menu.add_command(label = "<No tags>")

Der wichtige Teil ist der Stoff unter "wenn menu_tags:" -- Angenommen menu_tags ist die Liste ['stack', 'over', 'flow'].Dann, was ich tun möchte, ist tatsächlich:

self.add_tag_menu.add_command(label = 'stack', command = add_tag_stack)
self.add_tag_menu.add_command(label = 'over', command = add_tag_over)
self.add_tag_menu.add_command(label = 'flow', command = add_tag_flow)

wo add_tag_stack() ist definiert als:

def add_tag_stack():
    self.add_tag('stack')

und so weiter.

Das problem ist, dass die variable 'tag' den Wert 'stack' und dann den Wert 'über' und so weiter, und es wird nicht ausgewertet, wenn new_command genannt wird, an welcher Stelle die variable 'tag' ist einfach 'Fluss'.Also der tag, der Hinzugefügt wird, ist immer der Letzte auf der Speisekarte, egal, was der Benutzer klickt.

Ich war ursprünglich mit einem lambda-Ausdruck, und ich dachte, vielleicht explizit definieren die Funktion wie oben, möglicherweise besser.Entweder Weg, das problem Auftritt.Ich habe versucht, mit einer Kopie der variable " tag " (entweder mit "current_tag = tag" oder mit Hilfe der copy-Modul), aber das macht es nicht lösen.Ich bin mir nicht sicher, warum.

Mein Geist beginnt zu Wandern hin zu Dingen wie "eval" aber ich bin der Hoffnung, jemand einen cleveren Weg, dass sich nicht um solche schrecklichen Dinge.

Viel Dank!

(Falls es relevant ist, Tkinter.__version__ gibt '$Revision:67083 $' und ich bin mit Python 2.6.1 auf Windows XP.)

War es hilfreich?

Lösung

Erste von alle, Ihr problem hat nichts zu tun mit Tkinter;es ist am besten, wenn Sie senken Sie es nach unten, um ein einfaches Stück code demonstriert Ihre problem, so können Sie Experimentieren mit es mehr leicht.Hier ist eine vereinfachte version von dem, was du tust, dass ich experimentierte mit.Ich bin zu ersetzen ein dict in Stelle des Menüs machen es einfach, zu schreiben einen kleinen test case.

items = ["stack", "over", "flow"]
map = { }

for item in items:
    def new_command():
        print(item)

    map[item] = new_command

map["stack"]()
map["over"]()
map["flow"]()

Nun, wenn wir diese ausführen, als Sie sagte, wir bekommen:

flow
flow
flow

Die Frage ist hier Python Begriff der Umfang.Insbesondere die for Anweisung nicht vorstellen, eine neue Ebene der Umfang, noch eine neue Bindung für item;so ist es aktualisieren, die gleiche item variable jedes mal durch die Schleife, und alle die new_command() Funktionen beziehen sich auf das gleiche Element.

Was Sie tun müssen, ist die Einführung einer neuen Ebene der Umfang, mit einer neuen Bindung, denn jeder der itemen.Der einfachste Weg das zu tun ist, wickeln Sie es in einer neuen Funktion definition:

for item in items:
    def item_command(name):
        def new_command():
            print(name)
        return new_command

    map[item] = item_command(item)

Nun, wenn Sie Ersatz, der in den vorangehenden Programm, erhalten Sie das gewünschte Ergebnis:

stack
over
flow

Andere Tipps

Diese Art von Ding ist ein Recht häufiges problem in Tkinter, denke ich.

Versuchen Sie dies (an der entsprechenden Stelle):

def new_command(tag=tag):
    self.add_tag(tag)

Ich hatte einen ähnlichen Fehler.Nur das Letzte Element in der Liste angezeigt wurde.Behoben, indem die Einstellung

command=lambda x=i: f(x)

Beachten Sie die x=i nach lambda.Diese Zuordnung lässt Ihre lokale variable i gehen Sie nach rechts in die f(x) - Funktion Ihres command.Hoffe, diese einfachen Beispiel helfen:

# Using lambda keyword to create a dynamic menu.
import tkinter as tk

def f(x):
    print(x)

root = tk.Tk()
menubar = tk.Menu(root)
root.configure(menu=menubar)
menu = tk.Menu(menubar, tearoff=False)
l = ['one', 'two', 'three']
for i in l:
    menu.add_command(label=i, command=lambda x=i: f(x))
menubar.add_cascade(label='File', menu=menu)
root.mainloop()
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top