【问题标题】:Dynamically add, hide or forget Tkinter Frames based on Dictionary Record values?根据字典记录值动态添加、隐藏或忘记 Tkinter 帧?
【发布时间】:2015-10-03 11:33:34
【问题描述】:

我正在构建一个 GUI 应用程序来监视/控制远程嵌入式客户端设备。每个客户端的状态机都保存在主字典中,并在 GUI 中表示为 Notebook 小部件中的选项卡(框架)实例。我想在新客户端注册时实例化一个选项卡(在我的应用程序中定义为一个类),并在客户端链接超时时隐藏或忘记选项卡。如果/当客户重新注册时,我将重新实例化。

总的来说,我是 Python、Tkinter 和 OO 的新手,并且正在为程序结构而苦苦挣扎。我需要不断循环遍历字典记录以刷新 GUI,但我得出结论我不能在 root.mainloop() 结构中进行迭代。因此状态机字典记录或元素的“外部”更改需要在外部刷新相应的 GUI Notebook 选项卡实例,包括创建和删除我的选项卡类的实例。

我相信有一个标准的做法,但我在学习方面还没有取得足够的进展来解决这个问题。一些快速的建议可以推动我朝着正确的方向前进。

【问题讨论】:

  • 您是正确的,不应在事件处理程序中连续(或很长时间)循环。 tkinter 的替代品是使用 .after 函数。 SO的答案中有几个例子,包括我的一些。
  • 找到.after,包括你的一个例子。似乎我需要在我的 GUI 对象中刷新的解决方案......只需要改变我的范式以在对象内进行字典管理,而不是像我试图做的那样在外部进行管理。谢谢!

标签: python user-interface dictionary tkinter event-handling


【解决方案1】:

我需要解决三个要素才能使我的解决方案发挥作用:

1) 使用 root.after 函数启用外部异步事件(例如非 tkinter GUI 事件)以使用更新方法控制 GUI 元素...谢谢 Terry Jan Reedy!

2) 在 GUI 实例化本身中执行整体字典更新作为一种方法,而不是尝试使用单独的函数来处理我的字典管理。也许这对大多数人来说是显而易见的,但对我来说这是一个范式转变。

3) GUI 需要是所有字典元素的我的类的单个实例化,而不是每个元素的单独实例化。

下面的代码片段。

def remote_window_mgr():
    remote_window = client_gui()


class client_gui(object):

    def __init__(self):

        self.root = Tk()
        self.root.title('Client Manager')

        # Create main Frame in Window
        main_frame = ttk.Frame(self.root, borderwidth = 5, relief = 'ridge')
        main_frame.grid(column = 0, row = 0, sticky = (N, W, E, S))
        main_frame.columnconfigure(0, weight = 1)
        main_frame.rowconfigure(0, weight = 1)

        # Create Notebook widget to host Client Tabs
        self.tab_frame = ttk.Notebook(main_frame)
        self.tab_frame.grid(column = 1, row = 1)

        self.root.after(1000, func=self.gui_update)
        self.root.mainloop()


    def gui_update(self):

        for client_id in list(Client_Records.keys()):
            # Check for new Client
            if client_id not in list(Client_Tabs.keys()):
                client_data = Client_Records[client_id]
                # Create Client Tab
                self.add_tab(client_id)

        for client_id in list(Client_Tabs.keys()):
            # Check if Client Record has been purged...
            if client_id not in list(Client_Records.keys()):
                # Client Record no longer exists, delete Client Tab
                self.remove_tab(client_id)
                del Client_Tabs[client_id]

        for client_id in list(Client_Tabs.keys()):
            # Refresh client tabs for all active clients
            self.update_tab(client_id)

        self.root.after(1000, func=self.gui_update)

【讨论】:

  • 很高兴看到有人带着提示跑来解决他的问题。从具有自上而下控制的传统命令式编程的角度来看,事件循环编程似乎是向下更新的。 Twisted 包的命名正是为了反映这一点。如果你正在用 gui 做其他事情,发现 gui_update 锁定 gui 太久,你可以分成 update_newupdate_oldupdate_all,并以循环方式分别设置下一个,延迟总计约 1000 - 执行时间。即,update_new 可以以 root.after(0, update_old) 结尾。
  • 伟大的见解...谢谢。我最后一次直接的编程经验是使用 MC6800 汇编和 C... 嵌入式和自上而下。刚刚开始了解 OO、Sockets 和 GUI。 ;-) 感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 2021-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-17
  • 1970-01-01
  • 1970-01-01
  • 2021-05-28
相关资源
最近更新 更多