我有一个中等复杂的GUI,我正在构建用于与一些模拟进行交互和观察。我希望能够随着项目的进展继续重构和添加功能。出于这个原因,我希望应用程序中不同小部件之间的耦合尽可能宽松。
我的应用程序结构如下:
import tkinter as tk
class Application(tk.Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.instance_a = ClassA(self)
self.instance_b = ClassB(self)
# ... #
class ClassA(tk.Frame):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# ... #
class ClassB(tk.Frame):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# ... #
def main():
application = Application()
application.mainloop()
if __name__ == '__main__':
main()
我希望能够在一个窗口小部件中执行某些操作(例如在Treeview窗口小部件中选择项目或单击画布的一部分),这会更改其他窗口小部件的状态。
一种方法是在A类中使用以下代码:
self.bind('<<SomeEvent>>', self.master.instance_b.callback())
使用B类附带的代码:
def callback(self): print('The more that things change, ')
我对这种方法的问题是A类必须知道B类。由于项目仍然是原型,我一直在改变事物,我希望能够重命名{{1}其他东西,或者完全摆脱属于B类的小部件,或者使instance_a成为某个callback
对象的子节点(在这种情况下PanedWindow
需要被master
替换)。
另一种方法是在应用程序类中放置一个方法,只要触发某个事件就会调用该方法:
winfo_toplevel()
并修改A类中的绑定事件:
class Application(tk.Tk):
# ... #
def application_callback():
self.instance_b.callback()
这肯定更容易维护,但需要更多代码。它还要求应用程序类了解在类B中实现的方法以及instance_b位于小部件层次结构中的位置。在一个完美的世界里,我希望能够做到这样的事情:
self.bind('<<SomeEvent>>', self.master.application_callback())
这样,如果我在一个窗口小部件中执行操作,第二个窗口小部件将自动知道以某种方式响应,而无需任何窗口小部件知道其他的实现细节。经过一些测试和头疼,我得出结论,使用tkinter的事件系统这种行为是不可能的。所以,这是我的问题:
答案 0 :(得分:0)
我在answer中的代码避免了A类必须通过调用处理程序对象的方法来了解B类内部的问题。在类Scanner
中的以下代码方法中,不需要了解ScanWindow
实例的内部。 Scanner
类的实例包含对处理程序类实例的引用,并通过ScannerWindow
类的方法与Handler
实例进行通信。
# this class could be replaced with a class inheriting
# a Tkinter widget, threading is not necessary
class Scanner(object):
def __init__(self, handler, *args, **kw):
self.thread = threading.Thread(target=self.run)
self.handler = handler
def run(self):
while True:
if self.handler.need_stop():
break
img = self.cam.read()
self.handler.send_frame(img)
class ScanWindow(tk.Toplevel):
def __init__(self, parent, *args, **kw):
tk.Toplevel.__init__(self, master=parent, *args, **kw)
# a reference to parent widget if virtual events are to be sent
self.parent = parent
self.lock = threading.Lock()
self.stop_event = threading.Event()
self.frames = []
def start(self):
class Handler(object):
# note self and self_ are different
# self refers to the instance of ScanWindow
def need_stop(self_):
return self.stop_event.is_set()
def send_frame(self_, frame):
self.lock.acquire(True)
self.frames.append(frame)
self.lock.release()
# send an event to another widget
# self.parent.event_generate('<<ScannerFrame>>', when='tail')
def send_symbol(self_, data):
self.lock.acquire(True)
self.symbols.append(data)
self.lock.release()
# send an event to another widget
# self.parent.event_generate('<<ScannerSymbol>>', when='tail')
self.stop_event.clear()
self.scanner = Scanner(Handler())