Reputation: 151
What I mean by this is can you make the program open the window more than once by putting the mainloop() function in a for loop or a while loop? It would probably look something like this:
for i in range(n):
window.mainloop()
Or this if we are using a while loop:
i = 0
while i < n:
window.mainloop()
When I try either of these methods, it just opens one window. Am I doing something wrong or is mainloop() a function that cannot be put in a loop? If that is the case, is there any other method?
Upvotes: 4
Views: 1757
Reputation: 3736
The normal way within tkinter
is to run all windows within one "mainloop". You can also run a mainloop in a separate process, and each process can run a mainloop independently.
The other answer now has an example of the "within one mainloop" way - maybe later I'll edit in my own example here.
Here's a minimal example of how run "mainloop" in separate processes:
import multiprocessing
import tkinter
# Note, due to a quirk of multprocessing and pickle,
# you can't just copy-paste this in the interactive
# Python REPL, it has to go in a file.
def ui():
tkinter.Tk().mainloop()
if __name__ == '__main__':
multiprocessing.Process(target=ui).start()
# The above line did not block, because mainloop
# ran in another process. So now we can run more
# mainloops in parallel if we want:
multiprocessing.Process(target=ui).start()
I wouldn't do this unless each window is its own independent app that just happens to be launched from one parent process for some reason, or you have some other really good reason to isolate it.
As for why it doesn't work:
window.mainloop
is a blocking/ongoing operation - it kinda hints at this by having "loop" in the name - inside that function is what's called an "event loop", and the code just sits in that loop waiting for and handling window events until the window is closed.
You can think of it almost like this:
def mainloop():
while window_exists:
...
# this is why it doesn't work
# it loops forever in here
for i in range(n):
mainloop()
For more detail, you can dive into the tkinter
documentation where you'll find similar explanations:
Tcl/Tk applications are normally event-driven, meaning that after initialization, the interpreter runs an event loop [...] This is different from many GUI toolkits where the GUI runs in a completely separate thread from all application code including event handlers.
In principle, multiple threads or processes could run one event loop each, but in this case, tkinter
assumes that Tcl (and thus Tk) was not compiled with thread-safety (ruling out threads).
Upvotes: 3
Reputation: 385910
Calling mainloop
more than once can't open new windows. mainloop
only processes events, it doesn't create or recreate any windows.
If you need multiple windows, the way to do that is to create instances of Toplevel
for the second and subsequent windows. You then call mainloop
exactly once and all of the windows will be visible assuming they've been set up to be visible.
If you want to create multiple instances of the same window, the normal pattern is to create your app in a class that inherits from Frame
. You can then create as many windows as you want, and in each one you can create an instance of that frame.
Here is an example of the technique:
import tkinter as tk
class App(tk.Frame):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.count = 0
self.button = tk.Button(self, text="Click me!", command=self.click)
self.label = tk.Label(self, text="", width=20)
self.label.pack(side="top", fill="both", expand=True)
self.button.pack(side="bottom", padx=4, pady=4)
self.refresh_clicks()
def click(self):
self.count += 1
self.refresh_clicks()
def refresh_clicks(self):
self.label.configure(text=f"Clicks: {self.count}")
apps = []
n = 5
for i in range(n):
window = tk.Tk() if i == 0 else tk.Toplevel()
app = App(window)
app.pack(fill="both", expand=True)
apps.append(app)
tk.mainloop()
It's important to note that if you delete the very first window, all of the other windows will be deleted. If you don't want that to happen, you can create and then hide the root window so that the user can only see the other windows. You'll then need to add some code to kill the root window when there are no longer any child windows.
Upvotes: 3