调用已解散的tkinter(python GUI)小部件正在挂起

时间:2017-04-05 23:33:33

标签: python multithreading user-interface tkinter widget

我正在使用tkinter拨打GUI,并且当我的应用程序关闭时,我正在尝试通过清理所有线程来成为负责任的公民。我的应用程序是多线程的,轮询引擎定期将更新发送回我的显示器。我使用资源锁定来控制对共享资源的访问,(未在下面的示例代码中显示)。问题是在关闭应用程序窗口后,我经常遇到剩余的线程。

我试图在下面的代码中展示我正在使用的内容:回复"更新"是异步的。问题是在我检查" self._active"之间的时间。对于底层小部件的状态以及我调用" ... frameWidget.configure的时间(style = ..."和" ... varToUpdate.set(.. 。",这些小部件的状态可能已经被tkinter引擎改变为某种“无法解决”和/或“无响应”的风格。

使用锁定无济于事。我还没有找到一种方法来控制小部件的状态,以防止它们死亡#39;直到我能清理干净事件通知似乎只是:它们告诉您事件已经发生,但它们不允许您延迟窗口小部件 - ' death'直到你完成清理工作。

真正的问题似乎是调用配置窗口小部件的样式(" self.frameWidget.configure(style = stylename)")并更新IntVar(" self .varToUpdate.set(value)")只需在GUI关闭时挂起/阻止。
这些方法调用永远不会返回,而不是产生异常,或者什么都不产生。 (这对我来说似乎不太好。)

因此。怎么处理这个?有任何想法吗?我真的很喜欢将一大块代码指定为" atomic"某种程度上,我可以保证在检查" ._ active"之后立即运行对象操作。但我在Python中没有看到类似的东西。即使我能实现这一点,我也不确定tkinker引擎如何实际更新对象,因此可能无法解决问题。我错过了什么吗?

是的,我是python的新手,bla bla bla ...我来自Java背景,所以我认为我写的很多东西都是明确的"非pythonic" 。我很高兴听到您对我如何改进这些内容的见解,以充分利用该语言的优势。

示例代码!

from tkinter import *
from tkinter import ttk
from myutil import Subscriber

threshold = 20
deactivationEvents = ("Deactivate", "Destroy", "Unmap")
activationEvents = ("Activate", "Map")

# class implements/inherits "listener pattern" functionality
class MyClass(Subscriber)
  def __init__(self, parentWidget):
    self._active = False
    self.frame = ttk.LabelFrame(parentWidget, text='something')
    self.varToUpdate = IntVar()
    self.bar = ttk.Progressbar(self.frame, orient=VERTICAL)
    self.bar.config(mode='determinate', maximum=100, variable=self.varToUpdate)
    self.moreThings()
    self.bindWidgetEvents():

  . more
  . code
  . here

  # I'm using a simple boolean flag to control access here
  # I've tried using threading.Lock as well, to no avail
  def update(self, value):
    stylename = "{}.TFrame".format("Normal" if value > threshold else "Alarm")
    if self._active:
      self.frame.configure(style=stylename)
      self.varToUpdate.set(value)

  def bindWidgetEvents(self):
    widgets = [self.bar, self.frame]
    self.bindActivationEvents(widgets)
    self.bindDeactivationEvents(widgets)

  def bindActivationEvents(self, widgets):
    def activate(event):
      self._active = True
    for widget in widgets:
      for event in activationEvents:
        widget.bind("<{}>".format(event), activate)

  def bindDeactivationEvents(self, widgets):
    def deactivate(event):
      self._active = False
    for widget in widgets:
      for event in deactivationEvents:
        widget.bind("<{}>".format(event), deactivate)

2 个答案:

答案 0 :(得分:0)

将后台线程设置为守护进程正常工作

t = Thread(target=self.pollData)
t.daemon = True
t.start()

线程&#39;当主线程退出时结束

我仍然认为tkinter方法在阻止/挂起时表现不佳,而不是在这种情况下引发异常

答案 1 :(得分:0)

虽然将线程分配为守护进程可以在这种特殊情况下工作,但除非我们实际关闭应用程序,否则这并没有什么帮助。如果我只关闭窗户或框架怎么办?然后我们一直坚持调用这些已解散的GUI项的方法的问题并挂起线程......

我们需要一种方法来防止这些方法被调用,但是我们不能依赖于check-then-call解决方案,因为它们不是原子操作。

这是另一个想法......人们怎么看待这个解决方案?

  # The tkinter GUI objects were hanging indefinitely upon calls to their methods after
  # the window was closed, so we will replace them with dummy objects that will recieve 
  # those calls instead
  def deactivate(self):
    self._active = False
    self.varToUpdate = Stub()  # we could probably just reinitialize an IntVar
    self.bar = Stub()
    self.frame = Stub()

class Stub(object):

  def __init__(self):
    pass

  def config(self, **kwargs):
    pass

  def set(self, value):
    pass

我们也可以简单地删除对象并添加处理以在对现在不存在的对象进行调用时捕获结果异常......

  def deactivate(self):
    self._active = False
    del self.varToUpdate
    del self.bar
    del self.frame

不幸的是,即使使用这些解决方案,我认为在对GUI对象进行调用之后仍有可能关闭对象。