2014-04-01 33 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代碼編輯器,自動編譯和運行代碼在另一個窗口中鍵入(這實際上成了惱人的),要麼顯示的代碼輸出或異常發生。我使用的責任鏈,以收集編輯器插件的代碼,同時也發送程序輸出到輸出窗口。我還用它將語法突出顯示更改同時應用於兩個窗口。