I create a window with Tkinter. When I click the quit button, windows shows the exe stop working. Could any one tell me why this happens? I think the problem is the self.quit, but I do not know the reason.
Here is the codes.
from Tkinter import *
class App(Frame):
def __init__(self, master = None):
Frame.__init__(self, master)
self.pack()
self.createWidgets()
def createWidgets(self):
self.Quit = Button(self, text = "QUIT", command = self.quit)
self.Quit.pack(side = LEFT)
root = Tk()
app =App(master = root)
app.mainloop()
In the quit button, you are calling the quit() method of Frame. In its place, you need to call destroy() on the root element to finish the mainloop correctly.
self.Quit = Button(self, text = "QUIT", command = self.master.destroy)
Related
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()
I was wondering if there was a way to run the tkinter mainloop on a separate thread (not the mainthread) so that the terminal is "free".
Let's say we have a simple GUI:
import tkinter as tk
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.grid()
self.createWidgets()
def printHello(self):
print("Hello")
def createWidgets(self):
self.quitButton = tk.Button(self, text='Quit',
command=self.quit)
self.quitButton.grid(row=1,column=0)
self.printButton = tk.Button(self, text='Print',command=lambda: self.printHello())
self.printButton.grid(row=1,column=1)
app = Application()
app.master.title('Sample application')
app.mainloop()
Observed behavior: if I run this file using the terminal in PyCharm (let's say) using: python application.py, the terminal is then "frozen"/"occupied" by the tkinter mainloop and I am not able to type anything in the terminal.
Desired behavior: if I run the gui file with python application.py, the terminal is "free" such that it is able to take in additional commands.
I'm aware that python application.py & is supposed to free up the terminal but that hasn't worked for me. I'm not very experienced with threading or GUI processes but is there a way to run the tkinter mainloop in a different process / thread so that the terminal will be "free"?
You can use the threading module to run the GUI in a background thread while using the terminal in the main thread.
Try this code. It will start the GUI then allow input from the terminal.
import tkinter as tk
import threading
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.grid()
self.createWidgets()
def printHello(self):
print("Hello")
def createWidgets(self):
self.quitButton = tk.Button(self, text='Quit',
command=self.quit) # exits background (gui) thread
self.quitButton.grid(row=1,column=0)
self.printButton = tk.Button(self, text='Print',command=lambda: self.printHello())
self.printButton.grid(row=1,column=1)
def runtk(): # runs in background thread
app = Application()
app.master.title('Sample application')
app.mainloop()
thd = threading.Thread(target=runtk) # gui thread
thd.daemon = True # background thread will exit if main thread exits
thd.start() # start tk loop
while True: # run in main thread
x = input("Enter a value or Q to quit: ")
if x.lower() == 'q':
exit()
print('You entered', x)
This question already has answers here:
How do I close a tkinter window?
(17 answers)
Closed 4 years ago.
I have a tkinter application with two windows: A MainWindow that is created on startup, and a ChildWindow which is opened after clicking a button.
The ChildWindow should close itself if the user presses a button. However, when I try calling frame.quit, it terminates the entire application instead.
import tkinter as tk
class ChildWindow:
def __init__(self, master):
self.top = tk.Toplevel(master)
self.frame = tk.Frame(self.top)
self.frame.pack()
# BUG: Clicking "Close" will fully exit application
self.close_button = tk.Button(self.frame, text="Close", command=self.frame.quit)
self.close_button.pack()
class MainWindow:
def __init__(self, master):
self.master = master
self.frame = tk.Frame(self.master)
self.about_button = tk.Button(self.frame, text="Open child window", command=self._open_child_window)
self.about_button.pack()
self.frame.pack()
def _open_child_window(self):
self.about_window = ChildWindow(self.master)
root = tk.Tk()
lf = MainWindow(root)
root.mainloop()
Screenshots:
Why does frame.quit exit my application? How can I close the child window without exiting?
It is because quit causes mainloop to exit. With no event loop running, there's nothing left to keep the main window alive.
If you want to close a child window, call the destroy method.
self.close_button = tk.Button(..., command=self.top.destroy)
I'm trying to attach link to a button that will open when clicked.
from Tkinter import *
import webbrowser
class Window(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.master = master
self.init_window()
def init_window(self):
self.master.title("Anonymous Button Box")
self.pack(fill=BOTH, expand=1)
_Button = Button(self, text="Don't Click", command=webbrowser.open("http://example.com", new=2, autoraise="True"))
_Click = Button(self, text="Click")
_Click.place(x=80, y=0)
_Button.place(x=0, y=0)
root = Tk()
root.geometry("300x300")
app = Window(root)
root.mainloop()
When I run this it automatically opens the link before the box itself pops up. How would I write it so the link only runs when the button is pressed?
would I create another function to open the link then command it to use the function when button is pressed?
As a rule of thumb, a button command should call a function that accepts no parameters. By doing this, it becomes easier to write the command, to create the button, and to debug the program.
So, in this case I would create a function named something like open_browser, oron_dont_click_button, or something like that:
def open_browser(self):
webbrowser.open("http://example.com", new=2, autoraise="True")
Then, the button becomes much easier to implement:
_Button = Button(..., command=open_browser, ...)
I wrapped the button command in a lambda function. So my button looks like this:
`_Button = Button(self, text="Don't Click", command=lambda: webbrowser.open("http://example.com", new=2, autoraise="True"))`
I have code for selecting backup that should be loaded. It opens Toplevel window and lets you select one of the backups to load. When this window opens, I would like to block input to original window, so the only way to get back to original window is by closing the new Toplevel window.
The part of code that I hoped would work:
from tkinter import *
class BackupsGui:
def __init__(self, parent):
top = Toplevel()
self.top = top
Some more code and __init__ ending with:
top.update_idletasks()
top.overrideredirect(True)
top.mainloop()
or:
top.transient(parent)
top.mainloop()
Niether code part appears to change Toplevel interaction in any way, nor does changing if top.mainloop() precedes top.transient() or top.update_idletasks().
What do I miss?
transient and overrideredirect have nothing to do with event handling. If you want to block all input except for the toplevel, you need to call grab_set on the toplevel window. This will cause all events to be sent to that window.
Run the following code, and notice that if you don't check the box, you can continue to create new windows and change the value of the checkbox. Once checked, the next window grabs all events, preventing you from interacting with the other windows.
import Tkinter as tk
class Example(tk.Frame):
def __init__(self, root):
tk.Frame.__init__(self, root)
self.do_grab = tk.BooleanVar()
cb = tk.Checkbutton(self, text="New window grabs all events",
variable=self.do_grab, onvalue=True, offvalue=False)
cb.pack()
new_button = tk.Button(self, text="New window", command=self.on_click)
new_button.pack()
def on_click(self):
self.top = tk.Toplevel(self)
button = tk.Button(self.top, text="dismiss", command=self.top.destroy)
do_grab = self.do_grab.get()
if do_grab:
label = tk.Label(self.top, wraplength=200,
text="This window grabs all events")
else:
label = tk.Label(self.top, wraplength = 200,
text="This window does NOT grab all events")
label.pack(fill="x")
button.pack()
if do_grab:
self.top.grab_set()
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(side="top", fill="both", expand=True)
root.mainloop()