2017-02-14 11 views
0

fileselectionダイアログに基づいて設定されたオプションメニューを持つウィンドウを作成しようとしています。私はoptionmenuに、以前に選択したファイルに基づいてすべてのオプションを含めることができます。ただし、どのオプションがユーザーによって選択されても、変数には選択したファイルから最後に読み込まれたオプションが含まれます。Tkinterオプションメニューウィジェットaddコマンドlambdaが期待するコマンドを生成しない

なぜこのようなことが起こったのか誰かが私に説明できれば、私は大好きです。したがって、私は、最小限の例以下含まれている:コードでprintが期待ABCを生成しながら

from Tkinter import * 
Tk() 

class App(): 

    chunks = ['A','B','C'] # These are read from a file in the actual program 
    testVariable = StringVar() 

    def __init__(self,master): 

     def initSummary(): 
      self.dataButton["menu"].delete(0, 'end') 
      for i in self.chunks: 
       print i # To demonstrate what's in there 
       self.dataButton["menu"].add_command(label=i, command = lambda: self.testVariable.set(i)) 

     top = self.top = Toplevel() 
     top.protocol("WM_DELETE_WINDOW", lambda: close(self)) 
     self.sumButton = Button(top, text="Test", width=25, command=lambda: initSummary()) 
     self.sumButton.grid(row=0, column=0, sticky=W) 
     self.dataButton = OptionMenu(top, self.testVariable, "Stuff") 
     self.dataButton.grid(row=0, column=1, sticky=W) 

# Call the main app 
root = Tk() 
app = App(root) 
root.mainloop() 

このコードは関係なく、ユーザが選択したものの、オプションメニューにCを示すであろう。私はprintも3回Cを示したが、何が印刷されているのか、そしてGUIに表示されているものが私を捨てているのか分かります。

+2

forループでlambdaを使用すると、古典的な問題です:https://docs.python.org/3/faq/programming.html#why-do-lambdas-defined-in-a-loop-with-different-値がすべて返される同じ結果 –

+0

私に正しい方向@ j_4321を指摘していただきありがとうございます。私は上記の問題を解決するために中間的な機能を追加しましたが、これがそのような問題に対処する「正しい」方法だとお考えですか? –

+1

この問題に対処する「正しい」方法があるかどうかわかりません。私はあなたのような中間関数を追加していましたが、今は 'lambda j = i:self.testVariable.set(j)'を使うのが好きです。 –

答えて

-1

以下のコードに示すように、中間関数を追加してforループ(@ j_4321のおかげで)にラムダの問題を修正しました。

from Tkinter import * 

class App(): 

    chunks = ['A','B','C'] 

    def __init__(self,master): 

     self.testVariable = StringVar() 

     def refresh(): 
      # Prints what's in the variable 
      print self.testVariable.get() 

     def testFunc(x): 
      return lambda: self.testVariable.set(x) 

     def initSummary(): 
      self.dataButton["menu"].delete(0, 'end') 
      for i in self.chunks: 
       self.dataButton["menu"].add_command(label=i, command = testFunc(i)) 

     top = self.top = Toplevel() 
     self.sumButton = Button(top, text="Test", width=25, command=lambda: initSummary()) 
     self.sumButton.grid(row=0, column=0, sticky=W) 
     self.dataButton = OptionMenu(top, self.testVariable, "Stuff") 
     self.dataButton.grid(row=0, column=1, sticky=W) 
     self.testButton = Button(top, text="Variable", width=25, command = lambda: refresh()) 
     self.testButton.grid(row=1, column=0, sticky=W) 

# Call the main app 
root = Tk() 
app = App(root) 
root.mainloop() 

これは、この問題に対処する「正しい」方法ではありません場合はコメントしてください。

+1

'Tk()#Tkinterの最大の謎 ':これは謎ではなく、' testVariable = StringVar() 'にはマスターウィンドウが必要です。これを '__init__':' self.testVariable = StringVar() 'の中に入れることを考慮する必要があります。そうすれば、クラスを宣言する前にウィンドウを作成する必要はありません。 –

+0

私はいつも 'root - Tk()'がマスターウィンドウを担当すると予想していました。したがって、私はこの質問の例を作成するときに、追加の 'Tk()'が必要であることに驚いていました。この質問が作成されたアプリケーションは、私の 'StringVars'があなたが言及した' __init__'の中にあるので、追加の 'Tk()'の必要はありません。 –

+0

@BasJansen: 'Tk()'は、マスターウィンドウの_responsible_ではありません。マスターウィンドウです。それらのうちの2つを作成すべきではありません。 –

関連する問題