gpt4 book ai didi

python - tkinter 中父子部件之间的信号

转载 作者:行者123 更新时间:2023-12-05 07:48:20 25 4
gpt4 key购买 nike

我正在构建一个中等复杂的 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 小部件中选择一个项目或单击 Canvas 的一部分)来更改另一个小部件的状态。

一种方法是在类 A 中添加以下代码:

self.bind('<<SomeEvent>>', self.master.instance_b.callback())

在 B 类中附带代码:

def callback(self): print('The more that things change, ')

我在使用这种方法时遇到的问题是 A 类必须知道 B 类。由于项目仍然是原型(prototype),所以我一直在更改内容,我希望能够重命名 callback 到别的东西,或者完全摆脱属于类 B 的小部件,或者使 instance_a 成为某个 PanedWindow 对象的子对象(在这种情况下,master 需要是替换为 winfo_toplevel())。

另一种方法是在应用程序类中放置一个方法,每当触发某些事件时调用该方法:

class Application(tk.Tk):
# ... #
def application_callback():
self.instance_b.callback()

并修改类A中的绑定(bind)事件:

self.bind('<<SomeEvent>>', self.master.application_callback())

这绝对更容易维护,但需要更多代码。它还要求应用程序类了解类 B 中实现的方法以及 instance_b 在小部件层次结构中的位置。在一个完美的世界中,我希望能够做这样的事情:

# in class A:
self.bind('<<SomeEvent>>', lambda _: self.event_generate('<<AnotherEvent>>'))

# in class B:
self.bind('<<AnotherEvent>>', callback)

这样一来,如果我在一个小部件中执行操作,第二个小部件将自动知道以某种方式做出响应,两个小部件都不知道另一个小部件的实现细节。经过一番测试和摸不着头脑后,我得出结论,使用 tkinter 的事件系统不可能实现这种行为。所以,这是我的问题:

  1. 这种期望的行为真的不可能吗?
  2. 这是个好主意吗?
  3. 是否有更好的方法来达到我想要的模块化程度?
  4. 我可以使用哪些模块/工具代替 tkinter 的内置事件系统?

最佳答案

我的代码在 answer通过调用处理程序对象的方法,避免了类 A 必须了解类 B 的内部结构的问题。在类 Scanner 中的以下代码方法不需要了解 ScanWindow 实例的内部结构。 Scanner 类的实例包含对处理程序类实例的引用,并通过 Handler 的方法与 ScannerWindow 的实例进行通信类。

# 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())

关于python - tkinter 中父子部件之间的信号,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38772877/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com