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.
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.
I made a small example application that demonstrates the following:
- 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.
- Create custom widgets by subclassing a container, in my example Tkinter.LabelFrame.
- Use virtual/custom events, which are propagated, to trigger actions in the main code.
- Exchange data with subclasses/widgets
The intention of this post is two fold.
- I hope other people can benefit from the struggle that I had to figure this out.
- Maybe others can improve the example further.
My example has four source files.
start.py. This module only starts the application, creating an object of the Gui class.
import main if __name__ == '__main__': title = "Test" gui = main.Gui(title)
main.py. This module contains the Gui class, and holds the root element of the 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)
widget.py. This module contains two custom widgets, which are used in the 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)
action.py. This module contains the code that will perform the actual tasks of the application.
class Adder(): def __init__(self): self.count = 0 def addValue(self, value): if value: self.count += value return self.count
Any improvements are very welcome.