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.
Related
I have a (hopefully) simple tkinter question, but in pouring over stack overflow, google, and other sites, all my searches seem to result in answers on multithreading which I don't think is the issue here.
Basically when I open a child window from a function in the main window, that calling function continues to execute, but I want it to pause when the child window is opened and resume when it's destroyed. Much like in "normal" (ie not tkinter) code, one function call executes and returns before the rest of the code is executed.
This is the essence of the code:
class ChildWindow(self, mainwin):
def __init__(self):
# build child window with tk.Toplevel(mainwin)
# Get input from entry box
# destroy()
class myGUI:
def __init__(self):
# Typical window setup stuff - canvas and buttons etc
def canvas_clicked(self):
# get data from canvas
ChildWindow()
print('This prints whilst the child window is still open')
print('But I want the Child Window to close before anything below the ChildWindow() call is executed')
print('Basically I want to print to screen what is put into the entry box in the Child Window')
Is this possible? My solution at the moment is to put those print statements in another routine which I call from from the child window right before ChildWindow.destroy() but this seems clunky - I'm sure there's a more elegant way without getting into complex multithreading stuff....
thanks
You can use wait_window method to pause the execution of the main window while the child window is open. Here is an example of how to modify your code:
class ChildWindow:
def init(self, master):
self.master = master
self.top = tk.Toplevel(master)
self.entry = tk.Entry(self.top)
self.entry.pack()
self.button = tk.Button(self.top, text='OK', command=self.close)
self.button.pack()
self.top.wait_window()
def close(self):
self.top.destroy()
class myGUI:
def init(self, master):
self.master = master
self.canvas = tk.Canvas(self.master)
self.canvas.pack()
self.canvas.bind('<Button-1>', self.canvas_clicked)
def canvas_clicked(self, event):
ChildWindow(self.master)
print('The text in the entry box is:', ChildWindow.entry.get())
root = tk.Tk()
app = myGUI(root)
root.mainloop()
The wait_window method will block the execution of the main window until the child window is destroyed. Once the child window is destroyed, the execution of the main window will resume and the print statement will display the text entered in the child window. Note that you need to pass the master argument (i.e., the root window) to the ChildWindow class to create the child window as a Toplevel widget.
I am trying to call a new window by pushing the button of an existing window. The original window should close when the new window is being created. When I push the button the new window will show up as expected but additionally a blank window will appear. Using tk.Tk() or tk.Toplevel() will lead to the same result.
Later in the program I want to destroy the created window again. When using tk.Tk() closing the blank window by mouse will create an error "application has been destroyed" when the destroy() method gets applicated to the new window.
import tkinter as tk
from tkinter import messagebox
def main():
root = tk.Tk()
root.title("Hauptmenü")
Menue = MainMenue(root)
Menue.pack()
root.mainloop()
class MainMenue(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
self.button_rennen = tk.Button(self, text="New Window", width=20, command=self.call_bet)
self.button_rennen.pack()
def call_bet(self):
self.destroy()
root2 = tk.Tk()
Bet = BetFrame(root2)
Bet.pack()
class BetFrame(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
self.button = tk.Button(text="Wette platzieren",
command=self.question)
self.button.pack()
def question(self):
dialog = tk.messagebox.askokcancel(message="Destroy Window?")
if dialog is True:
self.destroy()
main()
I am creating a new class for every new window since the original program should return some variables.
I know that there are already many questions to this topic but for me none of these seemed quite to fit and helped to find the solution for my problem. I am grateful for any help!
Look at this:
import tkinter as tk
from tkinter import messagebox
class MainMenue(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
self.button_rennen = tk.Button(self, text="New Window", width=20,
command=self.call_bet)
self.button_rennen.pack()
def call_bet(self):
# `self.master` is the window
Bet = BetFrame(self.master)
Bet.pack()
self.destroy()
class BetFrame(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
# You need to tell the button that its master should be this BetFrame
# If you don't it will assume you mean the window so
# `self.destroy()` isn't going to destroy the button.
self.button = tk.Button(self, text="Wette platzieren", command=self.question)
self.button.pack()
def question(self):
dialog = tk.messagebox.askokcancel(message="Destroy Window?")
if dialog is True:
self.destroy()
def main():
root = tk.Tk()
Menue = MainMenue(root)
Menue.pack()
root.mainloop()
main()
Your code is great but it was 2 mistakes:
Instead of creating a new window is it better to reuse the old one.
When you create your button in your BetFrame class, you don't pass anything for the master argument (the first argument). That is why the button isn't destroyed when you destroy the BetFrame object using self.destroy()
There are some similar questions already on this site. The difference is that I'm coding the Tkinter window as a class. Then making an instance in another file. For example:
import file
gui = file.Window()
gui.mainloop()
# The program continues, using the variables chosen by the user in the previous window.
while file is another file that contains the window code:
import tkinter as tk
class Window(tk.Tk):
def __init__(self, master):
# some code
def mainWidgets(self):
self.body = Body(self)
self.body.grid(row=0, column=0)
class Body(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.master = master
self.widgets()
def widgets(self):
# Labels entries and selections
self.boton_enviar = tk.Button(self, text='Send', command=self.send)
self.boton_enviar.grid()
def send(self):
variables = self.variables
# What code do I add here?
self.quit()
So I want to close the window when clicking the 'Send' button but I need to retrieve some variables to use in the main program. How can I do this ?
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()
I am writing a multi-window GUI program in tkinter. The code shown below is the layout of the main part of my code. The first window works fine but when I get to the second window by calling self.next_win, some things start to go sightly funny.
The main issue is when I go to destroy the GUI: I want there to be a quit button in each window which, when pushed, closes the entire GUI (root.destroy). From the first window this works fine, I call the self.quit method; but when I get to the second window it doesn't work. I know this is because master in the second window is a Toplevel widget but I'm not sure how to get around this. I don't see how I can call root.destroy from the second window.
How can I fix this? A line in the SecondWin.quit method perhaps, or a better code structure which makes the solution trivial (bear in mind I am a beginner so trivialities will have to be explained)? Thanks.
class FirstWin:
def __init__(self, master):
self.master = master
...
...
def next_win(self):
self.master.withdraw()
root2 = Toplevel()
SecondWin(root2)
def quit(self):
self.master.destroy()
class SecondWin:
def __init__(self, master):
self.master = master
...
...
def quit(self):
self.master.destroy() # What goes in here?
...
def main():
root = Tk()
GUI = FirstWin(root)
root.mainloop()
if __name__ == '__main__':
main()
All you need to do is tell SecondWin what the root window is, and it can destroy it.
...
root2 = Toplevel()
# tell the second window what the master is,
# and also tell it to be a child of FirstWin.
SecondWin(self.master, root2)
...
class SecondWin():
def __init__(self, root, master):
# root is the root window, master is the parent of this window
self.root = root
self.master = master
<other initialization code here>
def quit(self):
self.root.destroy()