在Python中使用Tkinter进行子类化

在Python中使用Tkinter进行子类化,python,tkinter,Python,Tkinter,我正在用python制作一个应用程序。一切正常。到目前为止,所有内容都在一个源文件中。你从小处做起,然后一切都在成长。 我已经到了代码很难理解的地步。所以我决定我需要把代码分成模块和类 我终于弄到了一些东西来让它发挥作用。然而,我找不到太多关于用python制作复杂GUI的信息。因此,使用类来创建小部件等等 我制作了一个小示例应用程序,演示了以下内容: 拆分GUI代码和操作代码。在我的示例中,操作代码由一个单独的类处理,这也可能只是一个单独的模块 在我的示例Tkinter.LabelFrame中

我正在用python制作一个应用程序。一切正常。到目前为止,所有内容都在一个源文件中。你从小处做起,然后一切都在成长。 我已经到了代码很难理解的地步。所以我决定我需要把代码分成模块和类

我终于弄到了一些东西来让它发挥作用。然而,我找不到太多关于用python制作复杂GUI的信息。因此,使用类来创建小部件等等

我制作了一个小示例应用程序,演示了以下内容:

  • 拆分GUI代码和操作代码。在我的示例中,操作代码由一个单独的类处理,这也可能只是一个单独的模块
  • 在我的示例Tkinter.LabelFrame中,通过子类化容器来创建自定义小部件
  • 使用传播的虚拟/自定义事件触发主代码中的操作
  • 与子类/小部件交换数据
  • 这篇文章有两个目的

  • 我希望其他人能从我为解决这个问题所做的努力中受益
  • 也许其他人可以进一步改进这个例子
  • 我的示例有四个源文件

  • start.py。此模块仅启动应用程序,创建Gui类的对象

    import main
    
    if __name__ == '__main__':
        title = "Test"
        gui = main.Gui(title)
    
  • main.py。此模块包含Gui类,并保存Gui的根元素

    import Tkinter
    import action
    import widget
    
    class Gui():
        def __init__(self, title):
            self.root = Tkinter.Tk()
            self.root.protocol("WM_DELETE_WINDOW", self.applicationExit)
            self.root.title(title)
    
            #create the action object
            self.process = action.Adder()
    
            #create the input frame
            self.frameIn = widget.Input(self.root)
            self.frameIn.grid(row=0, column=0, padx = 5, pady =5, ipadx = 5, ipady = 5, sticky = Tkinter.N)
    
            #create the output frame
            self.frameOut = widget.Output(self.root)
            self.frameOut.grid(row=1, column=0, padx = 5, pady =5, ipadx = 5, ipady = 5, sticky = Tkinter.N)
    
            #bind events
            self.root.bind("<<input_submit>>", self.__submit)
    
            self.root.mainloop()
    
        def applicationExit(self):
            self.root.destroy()
    
        def __submit(self, event = None):
            value = self.frameIn.getValue()
            result = self.process.addValue(value)
            self.frameOut.outputText.set(result)
    

  • 任何改进都是非常受欢迎的。

    通常,实现Tkinter应用程序的标准模式是使用名为
    application
    的根对象或扩展
    Tkinter.Frame
    的对象,然后创建定义界面的所有小部件:

    import Tkinter as tk
    
    class Application(tk.Frame):
    
        def __init__(self, root, *args, **kwargs):
            tk.Frame.__init__(self, root, *args, **kwargs)
            ... #do other initialisation
            self.grid() #or pack()
    
    ... 
    
    if __name__ == '__main__':
        root = tk.Tk()
        app = Application(root)
        root.mainloop()
    
    这种技术的优点有两个方面:

    • 现在,您拥有了一个可以触发Tkinter事件和行为的对象(因为Tkinter有自己的小部件层次结构),并且还可以使用普通的类习惯用法(例如方法)拦截这些行为
    • 您的根类可以传递您自己的消息处理方案(用于处理需求4),该方案可以与您构建接口时将形成的自然层次结构保持一致和协调
    作为后一点的一个例子:

    class Message(object):
        def __init__(self, kind, data):
            self.kind = kind
            self.data = data
    
    class Application(tk.Frame):
        def __init__(self, root, *args, **kwargs):
            self.widgets = []
            ... #do widget declarations
    
        def message_downstream(self, message):
            for widget in self.widgets:
                widget.receive_message(message)
    
        def message_upstream(self, message):
            #do some logic based on the message
            ...
    
    class Widget(tk.Button):
        def __init__(self, master, name, *args, **kwargs):
            tk.Button.__init__(self, master, *args, **kwargs)
            self.master = master
            #perhaps set command event to send a message
            self['command'] = lambda: self.message_upstream(Message(self.name, "I Got Clicked"))
    
        def message_downstream(self, message):
            #similar to above
            pass
    
        def message_upstream(self, message):
            self.master.message_upstream(self, message)
    
    这种方法将责任链模式引入到你的应用程序中,因为你现在可以在链中的任何一点控制消息流(即,做某事或将其上传下传,但通过不同的路径)小心但是,好的应用程序设计会尝试将模型-视图-控制器模式合并到其代码中,如果在“视图”代码链的某个位置引入“控制”代码,这可能会造成混乱,导致头痛

    在Tkinter层次结构中使用责任链的最佳方法是将代码仅限于接口关注点,并将所有其他内容(即修改数据的代码)传递给某个适当的控制器,如您提到的操作类

    那么,为什么要使用上面这样的模式呢?当界面以复杂的方式与自身交互时。例如,某些子菜单中的控件可能会更改某些其他帧中可见的内容。该行为并不真正关心或依赖于模型,因此按照上面的方式实现它是可行的

    我曾经为Python编写了一个代码编辑器,当您键入代码时,它会自动编译并在另一个窗口中运行代码(这实际上很烦人),显示代码输出或发生的异常。我使用责任链从编辑器小部件收集代码,并将程序输出发送到输出窗口。我还使用它将语法突出显示更改同时应用于两个窗口。

    如果您唯一的问题是“也许其他人可以进一步改进示例”,这将更适合于
    import Tkinter as tk
    
    class Application(tk.Frame):
    
        def __init__(self, root, *args, **kwargs):
            tk.Frame.__init__(self, root, *args, **kwargs)
            ... #do other initialisation
            self.grid() #or pack()
    
    ... 
    
    if __name__ == '__main__':
        root = tk.Tk()
        app = Application(root)
        root.mainloop()
    
    class Message(object):
        def __init__(self, kind, data):
            self.kind = kind
            self.data = data
    
    class Application(tk.Frame):
        def __init__(self, root, *args, **kwargs):
            self.widgets = []
            ... #do widget declarations
    
        def message_downstream(self, message):
            for widget in self.widgets:
                widget.receive_message(message)
    
        def message_upstream(self, message):
            #do some logic based on the message
            ...
    
    class Widget(tk.Button):
        def __init__(self, master, name, *args, **kwargs):
            tk.Button.__init__(self, master, *args, **kwargs)
            self.master = master
            #perhaps set command event to send a message
            self['command'] = lambda: self.message_upstream(Message(self.name, "I Got Clicked"))
    
        def message_downstream(self, message):
            #similar to above
            pass
    
        def message_upstream(self, message):
            self.master.message_upstream(self, message)