简短版本:有没有办法控制在Tkinter中处理与不同小部件关联的回调的顺序?
更长的版本:在测试和学习Tkinter时,我一直在IDLE中使用以下程序:
import Tkinter
guiRoot = Tkinter.Tk()
hWindow = Tkinter.Frame(guiRoot)
hWindow.grid(); x = 0; y = 0
et1 = Tkinter.Entry(hWindow)
et2 = Tkinter.Entry(hWindow)
def ut(x, y, event):
print "X",x,", Y:",y
print "Args:",
print "Widget:",event.widget.get()
print
def Tkquit():
print "Leaving program..."
et1 = Tkinter.Entry(hWindow)
et2 = Tkinter.Entry(hWindow)
eb = Tkinter.Button(hWindow, command=Tkquit, text="Send")
et1.grid(column=x, row=y)
et1.bind("<FocusOut>", lambda event, x1=x, y1=y:ut(x1, y1, event))
y = y + 1; et2.grid(column=x, row=y)
et2.bind("<FocusOut>", lambda event, x1=x, y1=y:ut(x1, y1, event))
y = y + 1
eb.grid(column=x, row=y)
guiRoot.mainloop()
当我从一个Entry字段移动到另一个字段时,调用ut()。当我点击按钮时,我收到消息&#34;离开程序......&#34; (稍后将在例程中退出代码),但文本小部件中没有消息丢失焦点。
这引出了两个问题:
1)为什么没有调用Entry字段的回调?
并且,暗示,如果我想要该按钮退出应用程序,我希望在该按钮之前完成其他回调。所以:
2)如何控制Tkinter调用回调的顺序?
答案 0 :(得分:1)
当您按退出时未调用<FocusOut>
事件的原因是因为条目窗口小部件不会失去焦点。默认情况下,单击时,Tkinter按钮不会获得焦点。但是,如果您使用ttk.Button
,则 会窃取焦点,因此您的回调将会被调用。我个人认为这是按钮的ttk实现中的一个错误,但多年来一直如此。
在任何一种情况下,您都可以向按钮添加绑定,这会导致它们在您单击它们时窃取焦点。这样做会导致在先前具有焦点的窗口小部件上触发任何<FocusOut>
个事件。通常这不是必需的,但Tkinter为您提供了灵活性。
例如,将其添加到您的代码中,以便在您点击按钮时触发<FocusOut>
事件:
# force the button to steal focus when clicked
eb.bind("<1>", lambda event: eb.focus_set())
您无法控制处理事件的顺序,因为事件按照它们发生的顺序进行处理。对于工具包来说,做任何事情都是错误的。我想你会发现,一旦你对Tkinter如何运作有了更好的理解,你就不需要处理无序的事件了。
答案 1 :(得分:0)
我认为你在这里解决了错误的问题。当您使用具有回调的库时,您就是库的完全怜悯。除非Tkinter为您提供更改此选项的选项,否则您无法选择何时调用函数(如果有的话)或调用它们的顺序。很可能他们与Tkinter的内部实现(即&#34;硬线连接&#34;)紧密相关,因此无法轻易更改。
如果您的程序本身依赖于按特定顺序触发的事件,那么您已向程序中引入了 fragility 的来源。在极少数情况下,这可能是必要的(例如,出于效率原因,可能),但在大多数情况下,它只会增加程序与库的不必要的耦合。如果库决定更改稍后触发事件的顺序怎么办?如果用户发现导致逻辑中断的事件组合会怎样?如果你稍后添加一些东西并在逻辑中引入错误怎么办?
要使您的界面和逻辑健壮,请尝试设计您的程序,使得处理程序的调用顺序不会对程序行为产生任何影响。尽可能尝试保留您的函数idempotent,以避免程序中出现组合状态爆炸。
在您的情况下,如果您希望在触发按钮之前执行某个操作,请考虑调用所述操作以确保它确实发生,无论Tkinter是否发出FocusOut事件。
答案 2 :(得分:0)
是的,是的。
使用统一&lt; callbackHANDLER &gt;进程,在Tkinter小部件的公共事件路由界面(呼叫方(...), command =
&lt; < em> callbackHANDLER &gt;),&lt; aTkWidgetINSTANCE &gt; .bind(
<<_anEventTYPE_>>,
&lt; callbackHANDLER &gt; )
或&lt; aTkStateVARIABLE &gt; .trace_variable(
{&#34; w&#34; |& #34; r&#34; |&#34; u&#34;},&lt; callbackHANDLER &gt; )
)。
加上设计适当的中间层应用程序逻辑,以接收所有事件 - &gt;在其中路由,快速呼叫者的背景&amp;按顺序堆叠/重新排序/处理传入回调请求的流,您希望在其中显示它们的处理。
Tkinter在每个事件的实例变量中都有很多相关的细节,因此每次调用/每个事件都有一个完全控制。
Edit#1
2014-08-25 07:00
command
设置可以非常强大地使用带有可选实例包装的lambda容器:
command = ( lambda deltaMinutes = 1, # sets lambda-container to use a var.
aWidget = self.B1, # sets lambda-container to ref aWidget->B1
anotherVar = 0:
self.DateTimeNOW_MODEL_Add( deltaMinutes )
or # trick to work with a sequence of commands
self.LogPROCESS( "Step back 1x M1", anotherVar )
)