【问题标题】:python 3.2.3 Tkinter scale widget hangs when nested in Framepython 3.2.3 Tkinter缩放小部件嵌套在Frame中时挂起
【发布时间】:2012-05-21 10:59:02
【问题描述】:

我对 Tk 相对缺乏经验,但我之前做过一些简单的项目,这让我很困惑:

我有一个表示时间滑块的比例小部件。 threading.Timer 每隔一段时间就会关闭以移动比例小部件。按下按钮时,线程被禁用;在按钮上,线程被重新启用。因此,用户可以单击并拖动时间滑块来重新定位应用程序的“时间”。到目前为止,在 UI 操作期间禁用线程已经防止了我认为与线程安全有关的崩溃(看似非确定性的崩溃,发生在线程试图更新滑块的时间)。

唯一的其他“活动” UI 元素是一系列按钮,这些按钮使用 command=fnc 参数进行配置。规模是我使用绑定()的唯一地方。该应用程序是全屏应用程序,因此我在根级别使用 self.root.overridedirect(1) 和 geometry() 来删除菜单和边框,并使其全屏显示。

我已更改代码,因此缩放小部件现在位于 Frame 内,而不是在绝对根级别:

self.scale_timescrub = tkinter.Scale(self.root.master, from_=0, to=60, width=height_one, orient=tkinter.HORIZONTAL, showvalue=0)
self.scale_timescrub.grid(row=2,column=0,sticky=tkinter.N+tkinter.S+tkinter.E+tkinter.W)
self.scale_timescrub.bind('<Button-1>', self.press_scrub)
self.scale_timescrub.bind('<ButtonRelease-1>', self.release_scrub)

现在,每当我尝试使用缩放小部件进行任何 UI 操作时,应用程序都会挂起。按下滑块上的任何位置都会锁定 CPU,并且应用程序永远不会返回。我知道这与育儿有关,因为当我破解应用程序以将规模放回根级别时,它又可以工作了。当它是框架的父级时,小部件似乎永远不会进入它的绑定函数,例如 press_scrub。

谁能告诉我我在这里做错了什么?

在 Windows 7 上运行 python 3.2.3 64b。

【问题讨论】:

  • 其余代码是否在某处可用?有一个最小的可行示例肯定会有所帮助,我们可以使用它来演示这个问题。

标签: python-3.x tkinter tk


【解决方案1】:

您写道:“一个 threading.Timer 每隔一段时间就会关闭以移动比例小部件”。在处理此计时器事件时,您是否从另一个线程调用小部件方法?如果是这样,那肯定是问题所在。您根本无法从另一个线程更改 UI。

您永远不需要 Timer 对象 - 您知道您可以使用 after 安排定期运行的东西吗?

要考虑的另一件事,但不能通过查看您的代码 sn-p 来确定,是您是否在同一个容器小部件中使用 pack。将packgrid 组合在同一个master 中会导致您看到的行为。通常,在这种情况下,您会在启动时看到这种行为,但并非总是如此。

【讨论】:

  • 我没有在同一个应用程序中使用 pack() 和 grid()。正如我试图指出的那样,在按下按钮时,我会终止 Thread.timer() 并在按下按钮时重新激活 Thread.timer()。过去对此的正确性没有任何问题,即使 after() 可能是更好的编程风格。我将研究 after(),但证据表明 Frame 父级与它有关。
  • @Mayur Patel:即使你杀死了计时器,你仍然可能有竞争条件。相信我,在 Frames 中嵌入小部件绝对没有问题。您的代码中肯定存在错误。证据似乎指向线程,因为有证据表明 Tkinter 不是线程安全的。
  • @Mayur Patel:考虑这种可能性:您单击滑块。仍在主线程中,Tk 会在调用命令之前尝试更新按钮的外观。现在,可能在取消计时器之前 会优先考虑另一个线程,并且它也会尝试更新 UI。如果您曾经尝试从另一个线程更新 GUI 元素,您的代码最终会中断。
  • 首先,Tkinter 似乎有一些意图是线程安全的,但我愿意相信它不是。 bugs.python.org/issue11077。其次,通过完全禁用定时器线程,问题仍然存在。您的“考虑这种可能性”的情况不会发生;只有一个线程。线程问题往往是间歇性的并且感觉不确定。这个问题是可重复的和直接的。当然,Frames中的育儿widget是一种常见的操作;当然,我的代码中有一个错误。这就是我寻求帮助的原因。 :-)
【解决方案2】:

问题似乎在于我使用“master”关键字作为全窗口 Frame 对象的名称。当我将 self.root.master 批量重命名为 self.root.full_frame 时,一切都按预期工作。感谢 Bryan 指出线程危险,我将用它来重新评估我的应用程序的其他方面。感谢 mgilson 对可重现性表现出兴趣。在发布之前我没有配对可复制,因为我认为我设置了太多奇怪的配置,以至于其中任何一个都可能涉及。无论如何,谢谢大家。自我提醒:不要称任何东西为“大师”。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-29
    • 1970-01-01
    • 1970-01-01
    • 2013-09-14
    • 1970-01-01
    • 2018-07-21
    • 1970-01-01
    相关资源
    最近更新 更多