In my program, I am creating a window from my root tkinter window, and hiding the root using the .withdraw() function. When I try to show the root window again by calling the root class, it does not show and my program exits. Here's a rough outline of my code describing the problem:
class MainGUI:
def __init__(self, master):
self.master = master
#....Create and .grid() all GUI Widgets....
# Button for switching to other window
button = Button(text="CLICKME", command=lambda: self.other_window())
# Call and define show function at the end of __init__
self.show()
def show(self):
self.master.update()
self.master.deiconify()
# Create other window and withdraw self on button click
def other_window(self):
OtherGUI(self.master)
self.master.withdraw()
class OtherGUI:
def __init__(self, master):
# Function for returning to main window, calls MainGUI class
# to create window and withdraws self.
def main_window():
MainGUI(self.master)
self.master.withdraw()
master = self.master = Toplevel(master)
#....Create and .grid() all GUI Widgets....
# Button for switching back to main window
button = Button(text="CLICKME", command=lambda: self.main_window())
Using print functions in the MainGUI, I was able to see that when trying to switch back to the main window, show() is actually called, and the entire class does appear to be entered.
This puzzles me as I've only really learn how to do this from other forum posts, and using root.update() and .deiconify() seemed to be the solution for most people, however I have no idea why this isn't working.
Does anyone have an idea as to where I'm going wrong here?
The example you presented will not work for several reason.
#really you should build your gui as an inherited class as it makes things much easier to manage in tkinter.
class MainGUI:
def __init__(self, master):
self.master = master
button = Button(text="CLICKME", command=lambda: self.other_window())
# no need for lambda expressions here.
# missing geometry layout... grid(), pack() or place()
self.show()
# self.show does nothing here because your show method is improperly indented.
# your other_window method is also not properly indented.
def show(self):
self.master.update()
self.master.deiconify()
def other_window(self):
OtherGUI(self.master)
self.master.withdraw()
class OtherGUI:
def __init__(self, master):
# this function should be its own method.
def main_window():
MainGUI(self.master)
self.master.withdraw()
master = self.master = Toplevel(master)
# this is not how you should be defining master.
button = Button(text="CLICKME", command=lambda: self.main_window())
# missing geometry layout... grid(), pack() or place()
# your button command is using a lambda to call a class method but your define it as a function instead.
Here is a simpler version of what you are attempting that will work:
import tkinter as tk
class MainGUI(tk.Tk):
def __init__(self):
super().__init__()
tk.Button(self, text="Open Toplevel", command=self.open_toplevel_window).pack()
def open_toplevel_window(self):
OtherGUI(self)
self.withdraw()
class OtherGUI(tk.Toplevel):
def __init__(self, master):
super().__init__()
tk.Button(self, text="Close top and deiconify main", command=self.main_window).pack()
def main_window(self):
self.master.deiconify()
self.destroy()
MainGUI().mainloop()
As you can see here when you inherit from the tkinter classes that control the main window and toplevel windows it becomes easier to manage them and less code to perform a task.
I'm a python beginner making a program that is supposed to save and present reservations for a campingsite (just for fun...). I've structured it in an OOP-way meaning that I define a class for each seperate window. What I need to do is to update a TopLevel window (SubWindow2) presenting database entries, when another TopLevel window (created from Subwindow2) is closed.
import Tkinter as tk
class MenuWindow(tk.Tk):
def __init__(self, master):
self.master = master
#Widgets
def open_subwindow1(self):
self.window = Toplevel(self.master)
self.SubSubWindow1 = SubSubWindow1(self.window)
def open_subwindow2(self):
self.window = Toplevel(self.master)
self.SubSubWindow2 = SubSubWindow2(self.window)
class SubWindow1(tk.Tk):
def __init__(self, master):
self.master = master
#Widgets
class Subwindow2(tk.TopLevel):
def __init__(self, master):
self.master = master
#Widgets
self.button = tk.Button(master, text='Quit', command=open_subsub1)
def load_values(self):
#loading sqlite db-values into listboxes
def open_subsub1(self):
self.window = Toplevel(self.master)
self.SubSubWindow1 = SubSubWindow1(self.window)
class SubSubWindow1(tk.TopLevel):
def __init__(self, master):
self.master = master
#Widgets
self.button = tk.Button(master, text='Quit', command=on_quit)
def on_quit(self):
#Here I want to call a function that updates SubWindow2 (loads sqlite database values into several listboxes)
self.master.destroy()
root = tk.Tk()
myprog = MyProg(root)
root.mainloop()
How can i access a function in Subwindow2 from SubSubWindow1? self.master only refers to the TopLevel() instance right?
def on_quit(self):
self.SubWindow2.load_values()
self.master.destroy()
doesn't work, I get a TypeError: unbound method load_values() must be called with SubWindow2 instance as first argument (got nothing instead)
Is this an unvalid approch to "nesting" TopLevel-windows? What's the alternative?
Any remarks are greatly appriciated! Thanks for any help
I should preface this by claiming that I am also a novice, and I would greatly appreciate the advice of others in order not to spread misinformation.
From what I can see, you have some misunderstandings with how inheritance vs. encapsulation works in Python. Firstly, within a Tkinter application, there should only be a single instance of Tk(). Within your class definitions, you declare...
class SubWindow1(tk.Tk):
This means that whenever you create a new SubWindow1, a new instance of Tk will become instantiated with it, and SubWindow1 will inherit all of its properties.
If you would like to create a class that refers to, and has all the properties, of a Toplevel instance, your Subwindow2 was correct.
class Subwindow2(tk.TopLevel):
However, within the init, you must also initialize this instance of Toplevel as so:
class SubWindow2(tk.Toplevel):
def __init__(self, master):
tk.Toplevel.__init__(self)
self.master = master
Each 'master' refers to the element above it. Tk applications work as a tree hierarchy. This is why you should only have one instance of Tk(), which works as your 'root.' This instance of Tk contains windows within it, which contains windows or elements within them. So each window or element will have a parent, referred to as master, so you will be able to navigate around.
So when you create an instance of SubWindow2, this refers to everything within your SubWindow2, along with everything included in an instance of Toplevel. Because 'self' now refers to a Toplevel, you can pass it into the children to be a master, as such:
self.sub_sub_window1 = SubSubWindow1(self)
self.master only refers to the TopLevel() instance right?
Yes, but since you will be inheriting all of the Toplevel attributes through your SubWindow2 inheritance, you can add on more methods and still refer to them through your self.master tag.
Lastly, you should also call pack() on elements that you would like to show up correctly on your windows.
Altogether, I've made some edits to your program to try and demonstrate some of the concepts of inheritance and how it works within a Tkinter application. I hope you can look at this and take something from it. Please let me know if there are any elements you disagree with, as it is nowhere near perfect.
import Tkinter as tk
class MenuWindow():
def __init__(self, master):
self.master = master
self.sub_window_1 = SubWindow1(self.master)
self.sub_window_2 = SubWindow2(self.master)
class SubWindow1(tk.Toplevel):
def __init__(self, master):
tk.Toplevel.__init__(self)
self.master = master
class SubWindow2(tk.Toplevel):
def __init__(self, master):
tk.Toplevel.__init__(self)
self.master = master
self.sub_sub_window1 = SubSubWindow1(self)
def print_hello(self):
print "Hello!"
class SubSubWindow1(tk.Toplevel):
def __init__(self, master):
tk.Toplevel.__init__(self)
self.master = master
self.button = tk.Button(self.master, text='Say Hello & Destroy', command=self.on_quit)
self.button.pack()
def on_quit(self):
self.master.print_hello()
self.master.destroy()
root = tk.Tk()
myprog = MenuWindow(root)
root.mainloop()
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 code 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.
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.
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:
simple hello world from tkinter docs
Tkinter example code for multiple windows, why won't buttons load correctly?
Tkinter: How to Show / Hide a Window
Hope that helps.
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 = Window1(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.
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()
Reference: http://www.python-course.eu/tkinter_buttons.php
My preferred way of doing it is like Bryan Oakley's answer.
Here's an example, made by Sentdex on Youtube, go check his "GUIs with Tkinter" playlist.
I think it's really relevant to put it here because it's a great example for the OP, and so it also answers this answer that was upped by 35 people and wasn't answered;
#Bryan Oakley do you know any good sample codes on internet that i can
study their structure? – Chris Aung Jul 5 '13 at 8:35
import tkinter as tk
LARGE_FONT= ("Verdana", 12)
class SeaofBTCapp(tk.Tk):
"""
tkinter example app with OOP
"""
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 = {}
for frame_class in (StartPage,PageOne, PageTwo):
frame = frame_class(container, self)
self.frames[frame_class] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
"""
Put specific frame on top
"""
frame = self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
"""
Starting frame for app
"""
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent,bg='grey')
label = tk.Label(self, text="Start Page", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button_page1 = tk.Button(self, text = 'Visit Page 1', command= lambda: controller.show_frame(PageOne))
button_page1.pack()
button_page2 = tk.Button(self, text = 'Visit Page 2', command= lambda: controller.show_frame(PageTwo))
button_page2.pack()
class PageOne(tk.Frame):
"""
First page of program
"""
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent,bg='light blue')
label = tk.Label(self, text="Page one", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button_home = tk.Button(self, text = 'Back to Home', command= lambda: controller.show_frame(StartPage))
button_home.pack()
button_home = tk.Button(self, text = 'Go to page2', command= lambda: controller.show_frame(PageTwo))
button_home.pack()
class PageTwo(tk.Frame):
"""
First page of program
"""
def __init__(self,parent,controller):
tk.Frame.__init__(self,parent,bg='light green')
label = tk.Label(self, text="Page two", font=LARGE_FONT)
label.pack(pady=10,padx=10)
button_home = tk.Button(self, text = 'Back to Home', command= lambda: controller.show_frame(StartPage))
button_home.pack()
button_home = tk.Button(self, text = 'Go to page1', command= lambda: controller.show_frame(PageOne))
button_home.pack()
app = SeaofBTCapp()
app.mainloop()
Find the code here also : [https://pythonprogramming.net/change-show-new-frame-tkinter/]
Organizing your application using class make it easy to you and others who work with you to debug problems and improve the app easily.
You can easily organize your application like this:
class hello(Tk):
def __init__(self):
super(hello, self).__init__()
self.btn = Button(text = "Click me", command=close)
self.btn.pack()
def close():
self.destroy()
app = hello()
app.mainloop()
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.
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.
How does adding master = Tk() into the __init__ of a subclass of tkinter.Frame
produce two windows (app and app2) when only app.mainloop() is called?
from tkinter import Frame,Button,Tk
class Application(Frame):
def say_hi(self):
print('Hello world?!')
def close(self):
self.master.destroy()
def createWidgets(self):
self.quit_b = Button(self, width=12, text='Quit', bg='tan',
command=self.close)
self.quit_b.grid(row=0, column=0, padx=8, pady=8)
self.hello_b = Button(self, width=12, text='Hello',
command=self.say_hi)
self.hello_b.grid(row=0, column=1, padx=8, pady=8)
def __init__(self):
master = Tk() # <------------------------ ! see here !
Frame.__init__(self, master)
self.grid()
self.createWidgets()
app = Application()
app.master.title('Hello world!')
app2 = Application()
app2.master.title('Hello world! 2')
app.mainloop()
You cannot create two instances of the class Tk, and it's somewhat unusual to instantiate it within the __init__ of another class. Your code should work, but I've never seen it done that way.
You need to create an instance of Tk before creating any other widgets. Since your main app is a subclass of Frame, you're partially creating the instance of a Frame before initializing Tkinter which is simply not the way it should be done. It might work, but the behavior is undefined.
Instead, it's generally better to create your application as a subclass of Tk:
from Tkinter import tk
class Application(tk.Tk):
...
app = Application(...)
app.mainloop()
OR, create an instance of Tk at the global scope, and pass it as an argument to your other widgets:
from Tkinter import tk
class Application(tkFrame):
...
root = tk.Tk()
myframe = Application(root)
root.mainloop()
If you need more than one window, create additional windows with the Toplevel class.