2014-04-01 32 views
0

我正在用python制作应用程序。这一切都工作。到目前为止,所有内容都在一个源文件中你从小开始,然后全部成长。 我已经到了代码难以理解的地步。所以我决定我需要在模块和类中分解代码。在Python中用Tkinter进行子类化

我终于得到了一些东西在一起,让这一切工作。但是,我无法找到有关使用python制作复杂GUI的更多信息。因此使用类来创建小部件等等。

我做了演示以下小示例应用程序:

  1. 拆分GUI代码和行动代码。在我的例子中,动作代码是由一个独立的类来处理的,这也可能只是一个单独的模块。
  2. 在我的示例Tkinter.LabelFrame中,通过继承容器创建自定义小部件。
  3. 使用传播的虚拟/自定义事件触发主代码中的操作。
  4. 与子类/小部件

此篇的目的交换数据是双重的。

  1. 我希望其他人能够从我必须弄清楚的斗争中受益。
  2. 也许别人可以进一步改进这个例子。

我的例子有四个源文件。

  1. start.py。该模块只启动应用程序,创建Gui类的一个对象。

    import main 
    
    if __name__ == '__main__': 
        title = "Test" 
        gui = main.Gui(title) 
    
  2. 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) 
    
  3. widget.py。该模块包含两个用于GUI的自定义小部件。

    import Tkinter 
    
    class Input(Tkinter.LabelFrame): 
        def __init__(self, master): 
         Tkinter.LabelFrame.__init__(self, master, text = "Input") 
         self.inputText = Tkinter.StringVar() 
    
         #create entry box 
         self.entInput = Tkinter.Entry(self, textvariable = self.inputText, width = 20,) 
         self.entInput.grid(row = 0, column = 0, padx = 5, pady = 2, sticky = Tkinter.N) 
    
         #create submite button 
         self.btnSubmit = Tkinter.Button(self, text = "Add", width = 10, 
          command = self.__handlerSubmitButton) 
         self.btnSubmit.grid(row = 1, column = 0, padx = 5, pady = 2, sticky = Tkinter.N) 
    
        def getValue(self): 
         value = self.inputText.get() 
         if value.isdigit(): 
          return int(value) 
         else: 
          None 
    
        def __handlerSubmitButton(self, event = None): 
         self.btnSubmit.event_generate("<<input_submit>>") 
    
    class Output(Tkinter.LabelFrame): 
        def __init__(self, master): 
         Tkinter.LabelFrame.__init__(self, master, text = "Output") 
         self.outputText = Tkinter.StringVar() 
    
         #create out put label box 
         self.lblOutput = Tkinter.Label(self, textvariable = self.outputText, width = 20, 
          anchor = Tkinter.E) 
         self.lblOutput.grid(row = 0, column = 0, padx = 5, pady = 2, sticky = Tkinter.N) 
    
        def setValue(self, value): 
         self.outputText.set(value) 
    
  4. action.py。该模块包含将执行应用程序的实际任务的代码。

    class Adder(): 
        def __init__(self): 
         self.count = 0 
    
        def addValue(self, value): 
         if value: 
          self.count += value 
         return self.count 
    

任何改进都非常欢迎。

+1

如果你唯一的问题是,“也许别人可以进一步改善的例子”,这将是更适合于[代码审查(http://codereview.stackexchange.com/。) – Kevin

回答

3

通常,用于实现的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的都有自己的小部件的层次结构),也可以使用普通的CLAS拦截这些行为例如方法
  • 您的根类可以传递您自己的消息处理方案(用于处理需求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代码编辑器,自动编译和运行代码在另一个窗口中键入(这实际上成了恼人的),要么显示的代码输出或异常发生。我使用的责任链,以收集编辑器插件的代码,同时也发送程序输出到输出窗口。我还用它将语法突出显示更改同时应用于两个窗口。