I am writing a Python program with a GUI and have selected tkinter to do so. I have some experience with Python, but I am new to tkinter and GUI programming.
I have been reading a few tutorials about how to use tkinter. What I could find was very basic, such as "how to display a button". This leaves me struggling to identify a useful model for structuring the part of my program that defines the UI.
So far my searches only yielded 1 guide for structuring a python/tkinter GUI in an OOP style: pythonprogramming.net
Although this is a welcome example and very helpful in its specificity, it seems to me as though the inheritance of tkinter classes and adding new unrelated code to those new classes violates a strict separation of concerns. It looks very convenient on the short term, but I can't tell if it has undesirable consequences long-term.
As an alternative I created another example, in which I made similar classes, but avoided inheriting from tkinter classes, by composing various tkinter objects. This keeps functionality separated with only a couple of additional methods.
I would appreciate feedback on which approach is more useful as a UI grows in complexity. This could include specific suggestions on other models to use, links to information on the subject, source code examples of programs using tkinter, and so on.
Example of inheritance based on pythonprogramming.net:
import tkinter as tk
class AppMain(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
frame = Page(container, self)
self.frames[Page] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(Page)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class Page(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="Start Page", font=("Verdana", 12))
label.pack(padx=10, pady=10)
def main():
application = AppMain()
application.mainloop()
if __name__ == "__main__":
main()
Alternative without inheritance:
EDIT 1: Add grid variable to Page init
import tkinter as tk
class AppMain(object):
def __init__(self):
self.root = tk.Tk()
container = tk.Frame(self.root)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.pages = {}
page = Page(container, self.root, {"row": 0, "column": 0, "sticky": "nsew"})
self.pages[Page] = page
self.show_page(Page)
def show_page(self, container):
page = self.pages[container]
page.show()
def run(self):
self.root.mainloop()
class Page(object):
def __init__(self, parent, controller, grid):
self.frame = tk.Frame(parent)
self.frame.grid(**grid)
label = tk.Label(self.frame, text="Start Page", font=("Verdana", 12))
label.pack(padx=10, pady=10)
def show(self):
self.frame.tkraise()
def main():
application = AppMain()
application.run()
if __name__ == "__main__":
main()