I need help with a particular desired tkinter functionality. I have a pop-up window that I would like to stay in place with its message displayed until its button is pressed. That is, it should not return to the place this function was called from until the OK button is pressed.
fyi …
Windows 11
Python v3.13
Here is a previous post that auto-showed up on my feed as I was creating this post but no solution was given.
I also dabled with this post on stackoverflow but the stated solutions do not appear to be working for me.
Here is my script:
def number_sheets_to_print(self, qty):
root = tk.Tk()
root.title("Copies To Print")
root.minsize(265, 50)
text = tk.Label(root, text = 'Print ' + str(qty) + ' copies.')
text.config(font = ("Times", 20))
text.pack(pady=20)
flag = tk.IntVar()
# button_boarder = tk.Frame(root, highlightbackground='light blue', highlightthickness=1, bd=0)
button = tk.Button(root, text = "OK", command=lambda: flag.set(1))
button.pack()
# button_boarder.pack(pady=30)
button.tkraise()
print('Before: flag =', flag.get())
root.wait_variable(flag)
print('After button press: flag =', flag.get())
root.destroy()
I am able to get to the first print statement but it never gets to the second print statement.
Note that the root.destroy() funtion call does not work because prior to the button being pressed, it does not keep the script from returning from where the pop-up functioni was called from.
Any workaround to this problem would be much appreciated.
I attempted a different approach whereby I call a method to toggle a variable from within the callback method. Unfortunately, there appears to be an issue with Python in general from what I am experiencing here. In embedded C, I am able to do a similar while conditional loop with no problem. Not so with Python. It freezes up.
self.flag = 0 # This attribute is assigned int the def __init__ method
# button_boarder = tk.Frame(root, highlightbackground='light blue', highlightthickness=1, bd=0)
button = tk.Button(root, text = "OK", command = self.toggle_var)
button.pack()
# button_boarder.pack(pady=30)
# button.tkraise()
print('Before: flag =', self.flag)
while not self.flag:
print('In while loop')
if self.flag == 1:
break
print('After button press: flag =', self.flag)
root.destroy()
def toggle_var(self):
print('Inside toggle_var())')
self.flag = 1
From the way you structure this, I assume the number_sheets_to_print function is not meant to create the main window of your application. If that is correct, don’t use a Tk to create the window, but a Toplevel. A Tk has an application attached, so if you create two of those, you need to have two mainloops, making programming it really hard.
Which brings us to the second issue. In a tkinter program you can only react to things through the mainloop. A while True: will give tk no opportunity to process messages and your program will seem to have come to a halt. Yet it is eating one processor to the full.
I have taken the liberty to create a small example of how you could handle the situation. This is by polling (“Has the thing changed already?”), if that is not desirable, e.g. for performance, you may need to create a virtual event.
# Show working of events in tk, by Menno Hölscher
from tkinter import Tk, Toplevel, Label, Entry
class NonModal(Toplevel):
""" A non-modal dialog """
def __init__(self, parent):
super().__init__(parent)
self.show_label = Label(self)
self.show_label.configure(text="Nothing set yet")
self.show_label.grid(row=0, column=0)
self.set_wait_for()
self.parent = parent
def set_wait_for(self):
self.timer_id = self.after(500, self.is_value_larger)
def is_value_larger(self):
entered = self.parent.entered()
self.show_label.configure(text=str(entered))
try:
entered_int = int(entered)
print(entered, "makes it go into oblivion")
if entered_int > 12:
self.withdraw()
else:
self.after(500, self.is_value_larger)
except ValueError:
self.after(500, self.is_value_larger)
class Root(Tk):
""" The main window """
def __init__(self):
super().__init__()
self.geometry('800x600')
self.explain = Label(text="Change the (numeric) value, 12 is magic")
self.explain.grid(row=0, column=0)
self.change_this = Entry(text= "0")
self.change_this.grid(row=0,column=1)
self.the_other = NonModal(self)
def entered(self):
return self.change_this.get()
if __name__ == "__main__":
root = Root()
root.mainloop()
I noticed there is a command tkwait to monitor if a variable has changed, but I never used that. You may also want tinvestigate that.
thank you for responding to my query and offering a potential solution. I have actually found a tkinter built-in funtion that hadles this beautifully. It is the wait_window() built-in function.
You can use it like so (the dots represent miscellenous code).
root = Toplevel()
.
.
.
button = tk.Button(root, text = "OK", command = lambda : root.destroy())
.
.
.
root.wait_window()
# ** Rest of script only executed once button has been pressed **
The wait_window() is only executed if the destroy() function is called to close the window. This only happens if the button from the pop-up window is pressed.