title: Subclassing with Tkinter
body: <p>I am making an application with python. It is all working. So far everything is in one source file. You start small, and then it all grows.
  I got to a point where the code gets pretty hard to understand. So I decided that I needed to split up the code in modules and classes.</p>
  
  <p>I finally got some stuff together to get it all to work. However, I could not find much about making complex GUI's with python. hence using classes to create widget etcetera.</p>
  
  <p>I made a small example application that demonstrates the following:</p>
  
  <ol>
  <li>Split GUI code and action code. In my example the action code is handled by a seperate class, this could also be just a seperate module.</li>
  <li>Create custom widgets by subclassing a container, in my example Tkinter.LabelFrame.</li>
  <li>Use virtual/custom events, which are propagated, to trigger actions in the main code.</li>
  <li>Exchange data with subclasses/widgets</li>
  </ol>
  
  <p>The intention of this post is two fold.</p>
  
  <ol>
  <li>I hope other people can benefit from the struggle that I had to figure this out.</li>
  <li>Maybe others can improve the example further.</li>
  </ol>
  
  <p>My example has four source files.</p>
  
  <ol>
  <li><p>start.py. This module only starts the application, creating an object of the Gui class.</p>
  
  <pre><code>import main
  
  if __name__ == '__main__':
      title = "Test"
      gui = main.Gui(title)
  </code></pre></li>
  <li><p>main.py. This module contains the Gui class, and holds the root element of the GUI.</p>
  
  <pre><code>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("&lt;&lt;input_submit&gt;&gt;", 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)
  </code></pre></li>
  <li><p>widget.py. This module contains two custom widgets, which are used in the GUI.</p>
  
  <pre><code>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("&lt;&lt;input_submit&gt;&gt;")
  
  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)
  </code></pre></li>
  <li><p>action.py. This module contains the code that will perform the actual tasks of the application.</p>
  
  <pre><code>class Adder():
      def __init__(self):
          self.count = 0
  
      def addValue(self, value):
          if value:
              self.count += value
          return self.count
  </code></pre></li>
  </ol>
  
  <p>Any improvements are very welcome.</p>