When closing an instance of the ChildWindow class, which was called from another instance of MainWindow, the code does not continue to be executed.
I noticed that only when the MainWindow instance is closed does it continue to run. In general, it has a cumulative property, so the number of times the ChildWindow closes the window, the continuation will be displayed.
I want the function to continue executing when the ChildWindow closes, without closing the main window.
Here's my code:
from tkinter import *
class MainWindow():
def __init__(self):
self.root = Tk()
def run(self):
Button(self.root, text='click', command=lambda:self.open_child_window()).pack()
self.root.mainloop()
def open_child_window(self):
A = ChildWindow()
A.run()
print("why i'm not write?")
class ChildWindow():
def __init__(self):
self.root = Toplevel()
def run(self):
self.root.mainloop()
A = MainWindow()
A.run()
It seems to me that this is due to the self.root.mainloop() loop, so I was looking for how to stop it, the .quit(), .destroy() methods do not help.
It's called a nested event loop or modal loop. Use the mainloop() and quit() like this.
class ChildWindow():
def __init__(self):
self.root = Toplevel()
def run(self):
win = self.root
def on_close():
win.destroy()
win.quit()
win.protocol('WM_DELETE_WINDOW', on_close)
win.mainloop()
print('end of the nested loop')
...
You can also use wait_window() and destroy() like this.(The former runs a nested event loop until the window is destroyed.)
class ChildWindow():
...
def run(self):
win = self.root
def on_close():
win.destroy()
win.protocol('WM_DELETE_WINDOW', on_close)
win.wait_window()
Related
This question already has answers here:
Why is my Button's command executed immediately when I create the Button, and not when I click it? [duplicate]
(5 answers)
Closed 5 months ago.
I want to use a tkinter button as a method but the function the button is meant to call runs immediately after starting the program and does not run subsequently.
import tkinter as tk
class Button():
def __init__(self,window):
self.window = window
def add_button(self, func):
tk.Button(self.window, text='print', command=func).pack()
def do_something(the_thing):
print(f'{the_thing}')
return
root = tk.Tk()
button_o = Button(root)
button_o.add_button(do_something(the_thing='ook'))
root.mainloop()
You can use lambda function here
import tkinter as tk
class Button():
def __init__(self,window):
self.window = window
def add_button(self, func):
tk.Button(self.window, text='print', command=func).pack()
def do_something(the_thing):
print(f'{the_thing}')
return
root = tk.Tk()
button_o = Button(root)
button_o.add_button(lambda:do_something(the_thing='ook'))
root.mainloop()
just realized i could decorate the function.
import tkinter as tk
class Button():
def __init__(self,window):
self.window = window
def add_button(self, func):
tk.Button(self.window, text='print', command=func).pack()
def decorator(the_thing):
def do_something():
print(f'{the_thing}')
return do_something
root = tk.Tk()
button_o = Button(root)
button_o.add_button(decorator(the_thing='ook'))
root.mainloop()
I'm trying to add a custom Gtk.ListBoxRow to a Gtk.ListBox. This custom row is defined in the CustomRow class in the following way:
class CustomRow(Gtk.ListBoxRow):
def __init__(self, label):
super().__init__()
button = Gtk.Button(label='button')
label = Gtk.Label(label=label)
box = Gtk.Box()
box.add(button)
box.add(label)
self.add(box)
class Main:
def __init__(self):
super().__init__()
...
self.listbox = Gtk.ListBox()
...
self.listbox.add(CustomRow('label'))
def button_clicked(self):
print('button clicked')
if __name__ == '__main__':
main = Main()
main.window.show_all()
Gtk.main()
What I now want to do is have is have the button be bound to button_clicked function found in Main - this function will remove the row from the listbox. Issue is that I have no idea how to do this from another class.
below my code:
from tkinter import *
from tkinter import ttk
class MainWindow:
def __init__(self):
self.parent=Tk()
self.parent.geometry("494x410+370+100")
self.parent.title("My Software - TEST")
self.parent.iconbitmap("icon.ico")
Button = ttk.Button(self.parent, text="open a new widnow", command=self.OpenNewWindow)
Button.place(x=16, y=16)
def OpenNewWindow(self):
self.obj = NewWindow(self)
class NewWindow:
def __init__(self, mw):
self.window, self.mw = Toplevel(mw.parent), mw
self.window.geometry("200x150+360+200")
self.window.title("New Window")
self.window.iconbitmap("icon.ico")
# the "try/except" code has an issue..
try:
self.window.focus()
self.mw.parent.attributes('-disabled', 1)
self.window.transient(mw.parent)
self.window.grab_set()
self.mw.parent.wait_window(self.window)
finally:
self.mw.parent.attributes('-disabled', 0)
def main():
app=MainWindow()
if __name__=="__main__":
main()
the try/except code makes the Toplevel window important. when it runs, the user can't touch the root window, and if he try to do it, a bell songs and the Toplevel window flashes. it's the exactly behaivour that I want! but this piece of code has an issue.. when the user close the Toplevel window, the root doesn't became the active window. it's a big issue because it makes the root window to go back the other ones. see my gif to understand better what I mean:
http://www.imagebam.com/image/c983ce1356199964
how can I solve this issue?
You've got the wrong idea about try ... finally; it does not work that way. There are 2 ways to do what you want. One way is to simply put that code in the main window:
from tkinter import *
from tkinter import ttk
class MainWindow:
def __init__(self):
self.parent=Tk()
self.parent.geometry("494x410+370+100")
self.parent.title("My Software - TEST")
self.parent.iconbitmap("icon.ico")
Button = ttk.Button(self.parent, text="open a new widnow", command=self.OpenNewWindow)
Button.place(x=16, y=16)
def OpenNewWindow(self):
self.parent.attributes('-disabled', 1)
self.obj = NewWindow(self)
self.parent.attributes('-disabled', 0)
class NewWindow:
def __init__(self, mw):
self.window, self.mw = Toplevel(mw.parent), mw
self.window.geometry("200x150+360+200")
self.window.title("New Window")
self.window.iconbitmap("icon.ico")
self.window.focus()
self.window.transient(mw.parent)
self.window.grab_set()
self.mw.parent.wait_window(self.window)
def main():
app=MainWindow()
app.parent.mainloop()
if __name__=="__main__":
main()
Another way is to make a method to run when the toplevel closes:
from tkinter import *
from tkinter import ttk
class MainWindow:
def __init__(self):
self.parent=Tk()
self.parent.geometry("494x410+370+100")
self.parent.title("My Software - TEST")
self.parent.iconbitmap("icon.ico")
Button = ttk.Button(self.parent, text="open a new widnow", command=self.OpenNewWindow)
Button.place(x=16, y=16)
def OpenNewWindow(self):
self.obj = NewWindow(self)
class NewWindow:
def __init__(self, mw):
self.window, self.mw = Toplevel(mw.parent), mw
self.window.geometry("200x150+360+200")
self.window.title("New Window")
self.window.iconbitmap("icon.ico")
self.window.protocol("WM_DELETE_WINDOW", self.on_close)
self.window.focus()
self.mw.parent.attributes('-disabled', 1)
self.window.transient(mw.parent)
self.window.grab_set()
self.mw.parent.wait_window(self.window)
def on_close(self):
self.mw.parent.attributes('-disabled', 0)
self.window.destroy()
def main():
app=MainWindow()
app.parent.mainloop()
if __name__=="__main__":
main()
I'm writing a tkinter app that creates a class with multiple toplevels. I need to be able to close the whole gui when any of the X button in the toplevels are pressed. How can I do this?
def main():
root = tk.Tk()
app = Example(master=root)
app.mainloop()
class Example(tk.Frame):
def __init__(self, master):
self.master = master
super().__init__(master)
self.initUI()
def initUI(self):
self.master.withdraw()
self.initUIL = tk.Toplevel(self.master)
self.initUIL.title('Init')
self.pack(fill = tk.BOTH, expand=1)
frame1 = tk.Frame(self.initUIL)
#I need to close the whole gui when the x in this toplevel is pressed
I solved it, it was pretty easy, you need to change the protocol for each toplevel.
self.toplevel.protocol("WM_DELETE_WINDOW", self.ask_quit)
def ask_quit():
MsgBox = tk.messagebox.askquestion ('Quit',"Are you sure you want to quit?")
if MsgBox == 'yes':
self.master.destroy()
I'm trying to do Hide/Show for my application with withdraw()/deiconify() in Tkinter, but after deiconify() method call my app hangs. Run this code on Win7.
What do I do wrong?
import Tkinter as tk
import threading
class MyApp(object):
def __init__(self, parent):
self.root = parent
self.root.geometry('400x300')
self.root.title('My Application')
btn = tk.Button(parent, text='Hide', command=self.onClick)
btn.pack()
def onClick(self):
self.hide()
self.t = threading.Timer(3, self.show)
self.t.start()
def hide(self):
print 'hide()'
print 'state: ', self.root.state()
print 'withdraw()'
self.root.withdraw()
print 'state: ', self.root.state()
def show(self):
print 'show()'
print 'state: ', self.root.state()
print 'deiconify()'
self.root.deiconify()
print 'state: ', self.root.state()
print 'show end'
if __name__ == '__main__':
root = tk.Tk()
app = MyApp(root)
root.mainloop()
UPD: there is a working sample:
import Tkinter as tk
import sched
import time
class MyApp(object):
def __init__(self, parent):
self.root = parent
self.root.geometry('400x300')
btn = tk.Button(parent, text='Hide', command=self.onClick)
btn.pack()
self.scheduler = sched.scheduler(time.time, time.sleep)
def onClick(self):
self.hide()
self.scheduler.enter(3, 1, self.show, ())
self.scheduler.run()
def hide(self):
self.root.withdraw()
def show(self):
self.root.deiconify()
if __name__ == '__main__':
root = tk.Tk()
app = MyApp(root)
root.mainloop()
Tkinter is not thread safe, and you are calling self.root.deiconify() from a thread. That is most likely the source of your problem. You'll have to re-architect your solution to have the thread use a thread-safe queue to request that the main loop make calls into Tkinter.
There's a whole lot you can do with Tkinter without using threads. Are you certain you need them?