2

エッセイをモデル化したGUIアプリケーションを開発しています。特に、ユーザーは新しいトピックを作成してから、そのトピックにメモを入力できます。現在、新しいトピックを作成する方法は2つあります。メニューのドロップダウンオプション(menuコマンド)とメイン画面のボタン(buttonコマンド)です。ボタンは「新しいトピック」というテキストで始まります。ユーザーがボタンを押すと、プログラムは新しいトピックを作成し、を使用してトピックに名前を付けるようにユーザーに求め、tkSimpleDialog.askstringボタンのテキストをトピックの名前とそのトピックのメモの数に設定します。次に、ボタンのコマンドが変更され、そのトピックにメモが追加されます。

プログラムの開発中に、私は最初にメニューコマンドが機能することを確認しました。正常に呼び出さaskstringれ、入力を希望どおりに処理する新しいポップアップウィンドウが作成されます。ただし、ボタンコマンドを追加するとすぐにaskstring、メニューコマンドを使用して呼び出しても、への呼び出しは失敗しました。askstringダイアログが表示されているはずのウィンドウが白く塗りつぶされ、プログラムがハングします。ボタンコマンドをコメントアウトすると、再び機能します。メニューコマンドをコメントアウトすると、ハングします。

メニューにコマンドを追加するコードは次のとおりです。

        TopicBtn.menu.add_command(label="New Topic", underline=0,
                                  command=self.newTopic)

newTopic()のコードは次のとおりです。

 def newTopic(self, button=None):
     """ Create a new topic. If a Button object is passed, associate that Button
          with the new topic. Otherwise, create a new Button for the topic. """

     topicPrompt = "What would you like to call your new topic?"
     topicName = tkSimpleDialog.askstring("New Topic", topicPrompt)

     if topicName in self.topics.keys():
         print "Error: topic already exists"

     else:
         newTopic = {}
         newTopic["name"] = topicName
         newTopic["notes"] = []
         newTopic["button"] = self.newTopicButton(newTopic, button)

         self.topics[topicName] = newTopic
         self.addToTopicLists(newTopic)

newTopicButton()のコードは次のとおりです。

 def newTopicButton(self, topic, button=None):
 """ If a Button object is passed, change its text to display the topic name.
      Otherwise, create and grid a new Button with the topic name. """

     if button is None:
         button = Button(self.topicFrame)
         index = len(self.topics)
         button.grid(row=index/self.TOPICS_PER_ROW, column=(index %
             self.TOPICS_PER_ROW), sticky=NSEW, padx=10, pady=10)
     else:
         button.unbind("<Button-1>")

     buttonText = "%s\n0 notes" % topic["name"]
     button.config(text=buttonText)
     button.config(command=(lambda s=self, t=topic: s.addNoteToTopic(t)))

     return button

そして最後に、ボタンコマンドのコードは次のとおりです。

for col in range(self.TOPICS_PER_ROW):
     button = Button(self.topicFrame, text="New Topic")
     button.bind("<Button-1>", (lambda e, s=self: s.newTopic(e.widget)))
     button.grid(row=0, column=col, sticky=NSEW, padx=10, pady=10)

askstringラムダ式をボタンにバインドするとハングする理由を誰かが知っていますか?

編集:コメントをありがとう。動作を示す最小限の例を次に示します。

from Tkinter import *
import tkSimpleDialog

class Min():

    def __init__(self, master=None):
        root = master
        frame = Frame(root)
        frame.pack()

        button = Button(frame, text="askstring")
        button.bind("<Button-1>", (lambda e, s=self: s.newLabel()))
        button.grid()

    def newLabel(self):
        label = tkSimpleDialog.askstring("New Label", "What should the label be?")
        print label

root = Tk()
m = Min(root)
root.mainloop()

button.bind("<Button-1>", (lambda e, s=self: s.newLabel()))からに切り替えるbutton = Button(frame, text="askstring", command=(lambda s=self: s.newLabel()))とバグが修正されることに注意してください(ただし、押されたボタンへの参照は表示されません)。この問題は、ラムダへの入力の1つとしてイベントをキャプチャすることに関係していると思います。

4

2 に答える 2

1

ここで発生した問題はwait_window、使用しているダイアログでの呼び出しが原因です(自分で呼び出すことはありませんが、ダイアログを実装するコードは呼び出します)。たとえば、次のコードは、(おそらく)ボタンを2回クリックした後に問題を再現します。

import Tkinter

def test(event=None):
    tl = Tkinter.Toplevel()
    tl.wait_window(tl)

root = Tkinter.Tk()
btn = Tkinter.Button(text=u'hi')
btn.bind('<Button-1>', test)
btn.pack(padx=10, pady=10)
root.mainloop()

この呼び出しwait_windowは、コマンドが実行することを効果的に実行します。これは、呼び出しが悪いことupdateである理由の典型的な例です。update処理中のイベントと競合して<Button-1>ハングします。問題はwait_window、ダイアログのコードに属しているため、使用された状態で生活する必要があることです。どうやら、あなたが<ButtonRelease-1>それにバインドするならば、この衝突は決して起こりません。ボタンのパラメータを使用することもできcommandますが、これも問題なく機能します。

最後に、達成したいことに基づいて、よりクリーンな方法でボタンを作成するために、次のことをお勧めします。

for i in range(X):
    btn = Tkinter.Button(text=u'%d' % i)
    btn['command'] = lambda button=btn: some_callback(button)
于 2013-01-31T17:00:36.343 に答える
0

私は回避策を見つけました。最小限の例のテストから、問題はバインドするための別の呼び出しを行い、それによってラムダへの入力としてイベントを受け入れることに起因するようです。なぜそうなるのか説明できる人がいたら、私は彼らの答えを受け入れますが、今のところこれを受け入れます。

回避策は、個別のバインド関数を使用するのではなく、ボタンの配列を作成してから、配列内の正しいエントリをパラメータとしてラムダ関数に渡すことです(ボタン自体は行で作成されているため、渡すことはできません)ラムダ関数があります)。

コードは次のとおりです。

from Tkinter import *
import tkSimpleDialog

class Min():

    def __init__(self, master=None):
        root = master
        frame = Frame(root)
        frame.pack()

        buttons = [None] * 2
        for i in range (2):
            buttons[i] = Button(frame, text="askstring",
                            command=(lambda s=self, var=i: s.newLabel(buttons[var])))
            buttons[i].grid()

    def newLabel(self, button):
        label = tkSimpleDialog.askstring("New Label", "What should the label be?")
        button.config(text=label)
        print label

root = Tk()
m = Min(root)
root.mainloop()
于 2013-01-31T15:55:38.097 に答える