簡體   English   中英

停止使用已終止的tkinter(python GUI)小部件的調用

[英]Calls to defunct tkinter (python GUI) widgets are hanging

我正在使用tkinter撥打GUI,並且試圖通過關閉應用程序時清理所有線程來成為負責任的公民。 我的應用程序是多線程的,輪詢引擎會定期將更新發送回我的顯示器。 我正在使用資源鎖定來控制對共享資源的訪問(以下示例代碼中未顯示)。 問題是,關閉應用程序窗口后,我經常被剩余的線程卡住。

我試圖在下面的代碼中顯示我正在使用的內容:“更新”的回調是異步的。 問題是,在我檢查“ self._active”基礎窗口小部件的狀態的時間與我對“ ... frameWidget.configure(style = ...”和“ ... varToUpdate.set(...“),tkinter引擎可能已將這些小部件的狀態更改為某種“已終止” /“無響應”的樣式。

使用鎖沒有幫助。 我還沒有找到一種方法來控制小部件的狀態,以防止它們“死亡”,直到我能清理干凈為止。 事件通知看起來很簡單:它們告訴您事件已經發生,但是它們不允許您延遲小部件“死亡”,直到您完成清理為止。

真正的問題似乎在於,配置小部件樣式(“ self.frameWidget.configure(style = stylename)”)和更新IntVar(“ self.varToUpdate.set(value)”)的調用只是掛起/ GUI關閉時阻止。
這些方法調用不會產生異常,也不會產生任何結果,而不會返回。 (這對我來說似乎不是好行為。)

所以。 該如何處理? 有任何想法嗎? 我真的很喜歡以某種方式將一大段代碼指定為“原子的”功能,這樣我可以保證在檢查“ ._active”之后立即確保對象操作可以運行,但是我在Python中沒有看到類似的東西。 即使我能做到這一點,我也不知道tkinker引擎實際上如何更新對象,因此可能無法解決問題。 我想念什么嗎?

是的,我是python的新手,bla bla bla ...我來自Java背景,因此我認為我所寫的大部分內容絕對是“非Python的”。 我很高興聽到您對我將如何改進此處的功能以利用語言優勢的見解。

示例代碼!

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)

將后台線程設置為守護程序就可以了

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

主線程退出時,線程“ t”結束

我仍然覺得在這種情況下,tkinter方法通過阻止/掛起而不是引發異常而表現不佳

盡管在特定情況下可以將線程分配為守護程序,但是除非我們實際上關閉應用程序,否則這實際上並沒有幫助。 如果我只是關閉窗戶或框架怎么辦? 然后,我們陷入了調用這些已失效的GUI項目並掛起線程的問題。

我們需要一種阻止這些方法被調用的方法,但是我們不能依靠先檢查后調用的解決方案,因為它們不是原子操作。

這是另一個想法...人們對此有何看法?

  # 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對象的調用完成后關閉對象。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM