By Chris Aung


2013-07-04 09:21:47 8 Comments

The following is the overall structure of my typical python tkinter program.

def funA():
    def funA1():
        def funA12():
            # stuff

    def funA2():
        # stuff

def funB():
    def funB1():
        # stuff

    def funB2():
        # stuff

def funC():
    def funC1():
        # stuff

    def funC2():
        # stuff


root = tk.Tk()

button1 = tk.Button(root, command=funA)
button1.pack()
button2 = tk.Button(root, command=funB)
button2.pack()
button3 = tk.Button(root, command=funC)
button3.pack()

funA funB and funC will bring up another Toplevel windows with widgets when user click on button 1, 2, 3.

I am wondering if this is the right way to write a python tkinter program? Sure, it will work even if I write this way, but is it the best way? It sounds stupid but when I see the codes other people written, their code is not messed up with bunch of functions and mostly they have classes.

Is there any specific structure that we should follow as good practice? How should I plan before start writing a python program?

I know there is no such thing as best practice in programming and I am not asking for it either. I just want some advice and explanations to keep me on the right direction as I am learning Python by myself.

6 comments

@Bryan Oakley 2013-07-04 12:52:50

I advocate an object oriented approach. This is the template that I start out with:

# Use Tkinter for python 2, tkinter for python 3
import tkinter as tk

class MainApplication(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.parent = parent

        <create the rest of your GUI here>

if __name__ == "__main__":
    root = tk.Tk()
    MainApplication(root).pack(side="top", fill="both", expand=True)
    root.mainloop()

The important things to notice are:

  • I don't use a wildcard import. I import the package as "tk", which requires that I prefix all commands with tk.. This prevents global namespace pollution, plus it makes the code completely obvious when you are using Tkinter classes, ttk classes, or some of your own.

  • The main application is a class. This gives you a private namespace for all of your callbacks and private functions, and just generally makes it easier to organize your code. In a procedural style you have to code top-down, defining functions before using them, etc. With this method you don't since you don't actually create the main window until the very last step. I prefer inheriting from tk.Frame just because I typically start by creating a frame, but it is by no means necessary.

If your app has additional toplevel windows, I recommend making each of those a separate class, inheriting from tk.Toplevel. This gives you all of the same advantages mentioned above -- the windows are atomic, they have their own namespace, and the code is well organized. Plus, it makes it easy to put each into its own module once the code starts to get large.

Finally, you might want to consider using classes for every major portion of your interface. For example, if you're creating an app with a toolbar, a navigation pane, a statusbar, and a main area, you could make each one of those classes. This makes your main code quite small and easy to understand:

class Navbar(tk.Frame): ...
class Toolbar(tk.Frame): ...
class Statusbar(tk.Frame): ...
class Main(tk.Frame): ...

class MainApplication(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.statusbar = Statusbar(self, ...)
        self.toolbar = Toolbar(self, ...)
        self.navbar = Navbar(self, ...)
        self.main = Main(self, ...)

        self.statusbar.pack(side="bottom", fill="x")
        self.toolbar.pack(side="top", fill="x")
        self.navbar.pack(side="left", fill="y")
        self.main.pack(side="right", fill="both", expand=True)

Since all of those instances share a common parent, the parent effectively becomes the "controller" part of a model-view-controller architecture. So, for example, the main window could place something on the statusbar by calling self.parent.statusbar.set("Hello, world"). This allows you to define a simple interface between the components, helping to keep coupling to a minimun.

@Chris Aung 2013-07-05 08:35:03

@Bryan Oakley do you know any good sample codes on internet that i can study their structure?

@Shule 2014-05-19 21:38:44

I second the object-oriented approach. However, refraining from using inheritance on your class that calls the GUI is a good idea, in my experience. It offers you more flexibility if both the Tk and Frame objects are attributes of a class which doesn't inherit from anything. This way you can access the Tk and Frame objects more easily (and less ambiguously), and destroying one won't destroy everything in your class if you don't want it to. I forgot the exact reason why this is vital in some programs, but it does allow you to do more things.

@gcb 2015-09-08 03:21:17

won't simply having a class give you a private namespace? why subclassing the Frame improve on that?

@Bryan Oakley 2015-09-08 11:17:03

@gcb: yes, any class will give you a private namespace. Why subclass a Frame? I'm typically going to create a frame anyway, so it's one less class to manage (subclass of Frame, vs a class inheriting from object, with a frame as an attribute). I've rephrased the answer slightly to make that more clear. Thanks for the feedback.

@Błażej Michalik 2017-01-05 11:56:35

OOP approach here is nice and dandy (and trivial), but what about assigning responsibilities? Which class should be responsible for creating each widget? Which class should be responsible for layouting them in the right way? How to manage controller-gui couplings in a way that doesn't break the boundaries inbetween them?

@Suresh Subedi 2017-04-11 10:44:55

I am using this structure but I am getting: AttributeError: 'MainApplication' object has no attribute 'main'. I have also called Frame constructor and set self.parent to parent in Main class's constructor. Am I missing something?

@madtyn 2017-09-25 17:59:07

@BryanOakley At the second piece of source code I would say that self.parent = parent is missing. Right below the call to the parent init and before self.statusbar = Statusbar(self, ...)

@Bryan Oakley 2017-09-25 18:00:18

@madtyn: there's no need to save a reference to parent, unless you're going to use it later. I didn't save it because none of the code in my example required that it be saved.

@Steven M. Vascellaro 2018-02-20 20:54:01

For complex multi-window applications, do you recommend separating each window-class into it's own file? (ie: main_window.py, login_window.py, import_file_window.py)

@Bryan Oakley 2018-02-20 20:59:22

@StevenVascellaro: yes.

@Nik 2016-07-17 01:52:36

OOP should be the approach and frame should be a class variable instead of instance variable.

from Tkinter import *
class App:
  def __init__(self, master):
    frame = Frame(master)
    frame.pack()
    self.button = Button(frame, 
                         text="QUIT", fg="red",
                         command=frame.quit)
    self.button.pack(side=LEFT)
    self.slogan = Button(frame,
                         text="Hello",
                         command=self.write_slogan)
    self.slogan.pack(side=LEFT)
  def write_slogan(self):
    print "Tkinter is easy to use!"

root = Tk()
app = App(root)
root.mainloop()

enter image description here

Reference: http://www.python-course.eu/tkinter_buttons.php

@Arbiter 2017-01-13 10:42:28

You can only use TKinter on Python 2. I would recommend using tkinter for Python 3. I would also place the last three lines of code under a main() function and call that at the end of the program. I would definitely avoid using from module_name import * as it pollutes the global namespace and can reduce readability.

@Arbiter 2017-01-13 10:46:04

How could you tell the difference between button1 = tk.Button(root, command=funA) and button1 = ttk.Button(root, command=funA) if the tkinter extension module was also being imported? With the * syntax, both lines of code would appear to be button1 = Button(root, command=funA). I wouldn't recommend using that syntax.

@Serial 2013-07-04 09:38:46

This isn't a bad structure; it will work just fine. However, you do have to have functions in a function to do commands when someone clicks on a button or something

So what you could do is write classes for these then have methods in the class that handle commands for the button clicks and such.

Here's an example:

import tkinter as tk

class Window1:
    def __init__(self, master):
        pass
        # Create labels, entries,buttons
    def button_click(self):
        pass
        # If button is clicked, run this method and open window 2


class Window2:
    def __init__(self, master):
        #create buttons,entries,etc

    def button_method(self):
        #run this when button click to close window
        self.master.destroy()

def main(): #run mianloop 
    root = tk.Tk()
    app = Demo1(root)
    root.mainloop()

if __name__ == '__main__':
    main()

Usually tk programs with multiple windows are multiple big classes and in the __init__ all the entries, labels etc are created and then each method is to handle button click events

There isn't really a right way to do it, whatever works for you and gets the job done as long as its readable and you can easily explain it because if you cant easily explain your program, there probably is a better way to do it.

Take a look at Thinking in Tkinter.

@Bryan Oakley 2013-07-04 13:20:41

"Thinking in Tkinter" advocates global imports, which I think is very bad advice.

@Serial 2013-07-04 13:22:42

Thts true i dont suggest you use globals just some of the main class methos structure youre right :)

@erm3nda 2018-08-12 01:05:51

This program has zero sense for me, i don't know where you getting the class names, of how are you calling Demo1 from nowhere.

@gcb 2015-09-07 19:13:43

I personally do not use the objected oriented approach, mostly because it a) only get in the way; b) you will never reuse that as a module.

but something that is not discussed here, is that you must use threading or multiprocessing. Always. otherwise your application will be awful.

just do a simple test: start a window, and then fetch some URL or anything else. changes are your UI will not be updated while the network request is happening. Meaning, your application window will be broken. depend on the OS you are on, but most times, it will not redraw, anything you drag over the window will be plastered on it, until the process is back to the TK mainloop.

@Bryan Oakley 2015-09-07 22:12:21

What you say is simply not true. I've written hudreds of tk-based applications, both personal and commercial, and almost never have had to use threads. Threads have their place, but it is simply not true that you must use them when writing tkinter programs. If you have long runnng functions you may need threads or multiprocessing, but there are many, many types of programs you can write that don't need threads.

@Bryan Oakley 2015-09-07 22:21:55

I think if you rephrased your answer to be a bit more clear on that, it would be a better answer. It would also really help to have a canonical example of using threads with tkinter.

@gcb 2015-09-08 03:19:50

didn't care about being the best answer here because it is kinda off topic. but keep in mind that starting with threading/multip is very easy. if you have to add later, it is a lost battle. and nowadays, there absolutely no application that won't ever talk to the network. and even if you ignore and think 'i only have little disk IO', tomorrow your client decides that file will live on NFS and you are waiting for network IO and your app seems dead.

@erm3nda 2016-04-14 04:04:07

@BryanOakley Both of you are right. By the way, there are dozens of software examples of non threaded app's (ie, installers), but, every app connected with the network or doing IO writting will be lot faster using threads or subprocess. It's hard to say that if the black or white are the best, bet for gray.

@Bryan Oakley 2016-04-14 10:57:02

@erm3nda: "every app connected with the network or doing IO writting will be lot faster using threads or subprocess" - that is simply not true. Threading won't necessarily make your program faster, and in some cases will make it slower. In GUI programming, the main reason to use threads is to be able to run some code that would otherwise block the GUI.

@erm3nda 2016-04-14 19:59:50

@BryanOakley Is not true, is not false. Totally depends on the need of that app, but you is saying that threads are not needed at all. I don't know wich kind of softwared did build you, but I can only imagine installers, notepads, and other easy tools. The main loop should be uses only to maintain the GUI, not the work did by it. I am wrong again?

@Bryan Oakley 2016-04-14 20:12:29

@erm3nda: no, I am not saying threads are not needed at all. They are definitely needed (well, threads or multiprocessing) for lots of things. It's just that there's a very large class of GUI applications where tkinter is suitable but where threads simply aren't needed. And yes, "installers, notepads,. and other easy tools" fall into that category. The world is made up of more of these "easy tools" than it is of things like word, excel, photoshop, etc. Plus, remember that the context here is tkinter. Tkinter typically is not used for very large, complex applications.

@erm3nda 2016-04-14 22:20:38

@BryanOakley now Im okay with you. I use Tkinter for small things, or because other tools requested it. For large app's wxWidgets or even QT with templates are the right tool. Large applications with Tkinter needs large knowledge. "The world is made up of more of these". I use threads on every networking app and of course I've understand your position. Regards.

@user626998 2013-07-04 09:36:34

Probably the best way to learn how to structure your program is by reading other people's code, especially if it's a large program to which many people have contributed. After looking at the code of many projects, you should get an idea of what the consensus style should be.

Python, as a language, is special in that there are some strong guidelines as to how you should format your code. The first is the so-called "Zen of Python":

  • Beautiful is better than ugly.
  • Explicit is better than implicit.
  • Simple is better than complex.
  • Complex is better than complicated.
  • Flat is better than nested.
  • Sparse is better than dense.
  • Readability counts.
  • Special cases aren't special enough to break the rules.
  • Although practicality beats purity.
  • Errors should never pass silently.
  • Unless explicitly silenced.
  • In the face of ambiguity, refuse the temptation to guess.
  • There should be one-- and preferably only one --obvious way to do it.
  • Although that way may not be obvious at first unless you're Dutch.
  • Now is better than never.
  • Although never is often better than right now.
  • If the implementation is hard to explain, it's a bad idea.
  • If the implementation is easy to explain, it may be a good idea.
  • Namespaces are one honking great idea -- let's do more of those!

On a more practical level, there is PEP8, the style guide for Python.

With those in mind, I would say that your code style doesn't really fit, particularly the nested functions. Find a way to flatten those out, either by using classes or moving them into separate modules. This will make the structure of your program much easier to understand.

@Bryan Oakley 2013-07-13 21:31:29

-1 for using the Zen of Python. While it's all good advice, it doesn't directly address the question that was asked. Take the last paragraph out and this answer could apply to almost every python question on this site. It's good, positive advice, but it doesn't answer the question.

@Arbiter 2017-01-13 10:39:23

@BryanOakley I disagree with you on that. Yes, the Zen of Python is broad and can be used to address many questions. He did mention in the final paragraph to opt for classes or placing the functions in separate modules. He also mentioned PEP8, a style guide for Python, with references to it. Although not a direct answer, I think this answer is credible in the fact that it mentions many different routes that can be taken. That's just my opinion

@jonathan 2018-03-16 21:32:18

I came here looking for answers to this specific question. Even for an open-ended question, I can't do anything with this response. -1'd from me as well.

@erm3nda 2018-08-12 01:02:54

No way, the question is about to structure a tkinter app, nothing about styling/coding/zen guidelines. Easy as quoting @Arbiter "Although not a direct answer", so, it's NOT an answer. This is like "maybe yes and maybe no", with zen prepended.

@alecxe 2013-07-04 09:40:36

Putting each of your top-level windows into it's own separate class gives you code re-use and better code organization. Any buttons and relevant methods that are present in the window should be defined inside this class. Here's an example (taken from here):

import tkinter as tk

class Demo1:
    def __init__(self, master):
        self.master = master
        self.frame = tk.Frame(self.master)
        self.button1 = tk.Button(self.frame, text = 'New Window', width = 25, command = self.new_window)
        self.button1.pack()
        self.frame.pack()
    def new_window(self):
        self.newWindow = tk.Toplevel(self.master)
        self.app = Demo2(self.newWindow)

class Demo2:
    def __init__(self, master):
        self.master = master
        self.frame = tk.Frame(self.master)
        self.quitButton = tk.Button(self.frame, text = 'Quit', width = 25, command = self.close_windows)
        self.quitButton.pack()
        self.frame.pack()
    def close_windows(self):
        self.master.destroy()

def main(): 
    root = tk.Tk()
    app = Demo1(root)
    root.mainloop()

if __name__ == '__main__':
    main()

Also see:

Hope that helps.

Related Questions

Sponsored Content

25 Answered Questions

[SOLVED] Is there a way to run Python on Android?

9 Answered Questions

[SOLVED] What's the canonical way to check for type in Python?

  • 2008-09-30 11:00:10
  • Herge
  • 680573 View
  • 990 Score
  • 9 Answer
  • Tags:   python types

11 Answered Questions

[SOLVED] Is there a way to substring a string?

  • 2009-03-19 17:29:41
  • Joan Venge
  • 2297395 View
  • 1731 Score
  • 11 Answer
  • Tags:   python string

16 Answered Questions

[SOLVED] Way to create multiline comments in Python?

8 Answered Questions

[SOLVED] Proper way to declare custom exceptions in modern Python?

  • 2009-08-23 21:29:29
  • Nelson
  • 504784 View
  • 1000 Score
  • 8 Answer
  • Tags:   python exception

13 Answered Questions

[SOLVED] Nicest way to pad zeroes to a string

  • 2008-12-03 22:39:51
  • Faisal
  • 633423 View
  • 1087 Score
  • 13 Answer
  • Tags:   python string

1 Answered Questions

How to get tkinter messagebox to appear in front of toplevel

  • 2016-03-28 22:10:25
  • McClamrock
  • 1628 View
  • 0 Score
  • 1 Answer
  • Tags:   tkinter

8 Answered Questions

2 Answered Questions

How to effectively line up tkinter widgets

  • 2013-10-14 17:49:19
  • Jerry
  • 861 View
  • 0 Score
  • 2 Answer
  • Tags:   python tkinter

2 Answered Questions

[SOLVED] Difference between .pack and .configure for widgets in TkInter?

Sponsored Content