Tkinter 的 event_generate 命令被忽略 [英] Tkinter's event_generate command ignored

查看:97
本文介绍了Tkinter 的 event_generate 命令被忽略的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想弄清楚如何在对话窗口中对绑定命令进行单元测试.我正在尝试使用 tkinter 的 event_generate.它不像我期望的那样工作.对于这个 StackOverflow 问题,我通过一次调用 event_generate 设置了一些代码.有时这条线有效,有时就好像这条线不存在一样.

I am trying to figure out how to unittest a bind command in a dialog window. I'm attempting this with tkinter's event_generate. It is not working the way I expect. For this StackOverflow question I've set up some code with a single call to event_generate. Sometimes that line works and sometimes it is as if the line doesn't even exist.

对话框的 __init__ 方法中的绑定看起来像这样:

The bind in the dialog's __init__ method looks like this:

        self.bind('<BackSpace>',  #Print "BackSpace event generated."
            lambda event: print(event.keysym, 'event generated.'))

对话框中的任何操作都将回调其终止方法(该对话框基于 Frederik Lundh 在Tkinter 简介"中的示例对话框.)

Any action in the dialog will call back to its terminate method (The dialog is based on Frederik Lundh's example Dialog in 'An Introduction to Tkinter'.)

    def terminate(self, event=None):
        print('terminate called')  # Make sure we got here and the next line will be called
        self.event_generate('<BackSpace>')
        self.parent.focus_set()
        self.destroy()

当使用下面的代码调用对话框时,任何用户操作都将最终调用 terminate.在每种情况下,终止被调用"和BackSpace事件生成".显示.这证明对 event_generate 的调用设置正确.

When the dialog is called using the code below any user action will end up calling terminate. In each case "terminate called" and "BackSpace event generated." are displayed. This proves that the call to event_generate is set up correctly.

parent = tk.Tk()
dialog = Dialog(parent)
dialog.wait_window()

如果相关,我应该提到我已将 Lundh 对 self.wait_window 的调用从他的对话框的 __init__ 方法转移到调用者.虽然这打破了他的对话框的简洁封装,但它似乎是自动化单元测试所必需的.否则,单元测试将显示对话框并停止等待用户输入.我不喜欢这个解决方案,但我不知道有任何替代方案.

In case it's relevant I ought to mention that I have moved Lundh's call to self.wait_window from his dialog's __init__ method to the caller. Whilst this breaks the neat encapsulation of his dialog it appears to be necessary for automated unittests. Otherwise the unittest will display the dialog and halt waiting for user input. I don't like this solution but I'm not aware of any alternative.

我遇到的问题是当 wait_window 被替换为对 terminate 方法的直接调用时.这是我希望在单元测试中能够做的事情,即在不运行 tkinter 的 mainloop 或 wait_window 的情况下测试我的 GUI 代码.

The problem I'm having is when wait_window is replaced with a direct call to the terminate method. This is the sort of thing that I'd expect to be able to do in unittesting which is to test my GUI code without running tkinter's mainloop or wait_window.

parent = tk.Tk()
dialog = Dialog(parent)
dialog.terminate()

这只会打印terminate called";并且不打印BackSpace 事件生成".对 event_generate 的调用似乎没有效果.如果我遵循调试器中的调用,我可以看到 tkinter 的 event_generate() 正在使用正确的参数调用.self = {Dialog} .99999999, sequence = {str}'', kw = {dict}{}鉴于 TkCmd 手册页中关于窗口焦点的警告,我已验证具有绑定的对话框在其 __init__ 方法中获得了焦点.

This only prints "terminate called" and does not print "BackSpace event generated.". The call to event_generate appears to have no effect. If I follow the call in the debugger I can see that tkinter's event_generate() is being called with the correct arguments. self = {Dialog} .99999999, sequence = {str}'<BackSpace>', kw = {dict}{} In view of the warning in the TkCmd man pages about window focus I have verified the dialog with the binding is given focus in its __init__ method.

Tkinter 没有执行回调.为什么?

Tkinter is not executing the callback. Why?

这个基本代码显示 update 工作.但是,只有在主程序调用 event_generate 之前在 __init__ 中调用它才有效.(这个难题已作为单独问题提出)

This bare bones code shows update working. However, it only works if it is called in __init__ before event_generate is called by the main program. (This puzzle has been raised as a separate question)

class UpdWin(tk.Tk):
    def __init__(self):
        super().__init__()
        self.bind('<BackSpace>',
                  lambda event: print(event.keysym, 'event generated.'))
        self.update()  # Update works if placed here


app = UpdWin()
app.event_generate('<BackSpace>')
# app.update() # Update doesn't work if placed here

六年过去了

2021 年 4 月 12 日.请参阅 Mark Roseman 的 优秀网站,详细说明为什么要使用 更新是个坏主意.

Six Years On

4/12/2021. See Mark Roseman's excellent web site for a detailed explanation of why any use of update is a bad idea.

更好的程序设计完全避免了这个六年前的问题所带来的问题,其中 tkinter 小部件对象从不子类化.相反,它们应该通过组合创建,在那里它们可以很容易地被猴子修补.(此建议与 Frederik Lundh 在Tkinter 简介"中的示例对话框中显示的模式相反.)对于 unittest 设计,不仅不需要通过 tkinter 启动 Tk/Tcl,而且也是不明智的.

The problem posed by this six year old question is entirely avoided by better program design in which tkinter widget objects are never subclassed. Instead they should be created by composition where they can be easily monkey patched. (This advice is contrary to patterns shown in Frederik Lundh's example Dialog in 'An Introduction to Tkinter'.) For unittest design, not only is there no need to start Tk/Tcl via tkinter but it is also unwise.

推荐答案

event_generate 默认情况下会立即处理所有事件回调.但是,如果您在调用 event_generate 之前不调用 update,则窗口将不可见并且 tkinter 可能会忽略任何事件.您可以使用 when 属性控制何时处理生成的事件.默认情况下,该值为now",但另一个选择是tail",这意味着在处理完任何事件(例如重绘)后将其附加到事件队列中.

event_generate will by default process all event callbacks immediately. However, if you don't call update before calling event_generate, the window won't be visible and tkinter will likely ignore any events. You can control when the generated event is processed with the when attribute. By default the value is "now", but another choice is "tail" which means to append it to the event queue after any events (such as redraws) have been processed.

when 属性的完整文档位于 event_generate 的 tcl/tk 手册页上:http://tcl.tk/man/tcl8.5/TkCmd/event.htm#M34

Full documentation on the when attribute is on the tcl/tk man page for event_generate: http://tcl.tk/man/tcl8.5/TkCmd/event.htm#M34

这篇关于Tkinter 的 event_generate 命令被忽略的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆