I continue to struggle with multiple windows in tkinter. It's now developing into an application where each window is in it's own separate file.
I need to open the second window from the first because that's how the application works.
I also need to open the second window independently of the first, i.e. by itself, so I can do unit testing as it develops.
This question destroys the previous window as the second window opens. Not what I want: Python tkinter multiple windows
My own previous question here closes the first window as the second window opens. Not what I want. Although the comments point out a typo error (omitting ()
), the accepted answer uses withdraw
and deiconify
, which will eventually be helpful but not the solution to this problem. Python tkinter cloase first window while opening second window
This is closest because it opens a second window from a first window, but it doesn't address both (i) opening the second window from a separate file and (ii) also being able to open the second window independently: Python tkinter class multiple windows
Here's the SECOND window in a file called Location.py and it opens fine independently:
import tkinter as tk
from tkinter import ttk
class Location(tk.Frame):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
root.title("Location")
root.geometry('400x275')
# Frames
self.mainframe = ttk.Frame(self.master)
self.mainframe.grid(column = 0, row=0)
ttk.Label(self.mainframe, text = "Second Window").grid(column=1, row=1, sticky=(tk.W, tk.E))
if __name__ == "__main__":
root = tk.Tk()
Location(root)
root.mainloop()
Here's the FIRST window, which also opens fine, the problem is when I press either button to call the file to open the second window:
import tkinter as tk
from tkinter import ttk
import Location
class Building_Info(tk.Frame):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Frames
self.infoframe = ttk.Frame(self.master, height=400, width=200, padding="50 100 50 100", borderwidth=10)
self.infoframe['relief'] = 'raised'
self.infoframe.grid(column = 0, row=0, sticky=(tk.E, tk.N))
self.buttonframe = ttk.Frame(self.master, height=400, width=200, padding="50 100 50 100", borderwidth=10)
self.buttonframe['relief'] = 'raised'
self.buttonframe.grid(column = 1, row=0, sticky=(tk.E, tk.N))
# BUTTONS
confirm_button = ttk.Button(self.infoframe, text = 'Stakeholders', command = self.open_location)
confirm_button.grid(column=0, row=2)
confirm_button = ttk.Button(self.buttonframe, text = 'Location', command = self.open_location)
confirm_button.grid(column=0, row=2)
for child in self.infoframe.winfo_children():
child.grid_configure(padx=5, pady=5)
for child in self.buttonframe.winfo_children():
child.grid_configure(padx=5, pady=5)
# METHODS
def open_location(self):
Location.Location()
if __name__ == "__main__":
root = tk.Tk()
root.title("Building Information")
root.geometry('600x400')
Building_Info(root)
root.mainloop()
When I try to pass Location.Location()
or Location.Location(root)
or Location.Location(self)
or Location.Location(self.master)
, I get this error:
Traceback (most recent call last):
File "C:\Users\User\AppData\Local\Programs\Python\Python312\Lib\tkinter\__init__.py", line 1948, in __call__
return self.func(*args)
^^^^^^^^^^^^^^^^
File "c:\Users\User\Documents\Python\Tutorials\BuildingInfo_Stay_Open.py", line 32, in open_location
Location_Stay_Open.Location()
File "c:\Users\User\Documents\Python\Tutorials\Location_Stay_Open.py", line 9, in __init__
root.title("Location")
^^^^
NameError: name 'root' is not defined
But when I try to pass Location.Location(self.root)
, I get asked if I meant root
.
Traceback (most recent call last):
File "C:\Users\User\AppData\Local\Programs\Python\Python312\Lib\tkinter\__init__.py", line 1948, in __call__
return self.func(*args)
^^^^^^^^^^^^^^^^
File "c:\Users\User\Documents\Python\Tutorials\BuildingInfo_Stay_Open.py", line 32, in open_location
Location_Stay_Open.Location(self.root)
^^^^^^^^^
AttributeError: 'Building_Info' object has no attribute 'root'. Did you mean: '_root'?
Now if I go back to the second window class Location(tk.Frame)
and try class Location(tk.Tk)
, then the second window doesn't open independently, and gives this error:
Traceback (most recent call last):
File "c:\Users\User\Documents\Python\Tutorials\Location_Stay_Open.py", line 29, in <module>
Location(root)
File "c:\Users\User\Documents\Python\Tutorials\Location_Stay_Open.py", line 7, in __init__
super().__init__(*args, **kwargs)
File "C:\Users\User\AppData\Local\Programs\Python\Python312\Lib\tkinter\__init__.py", line 2326, in __init__
self.tk = _tkinter.create(screenName, baseName, className, interactive, wantobjects, useTk, sync, use)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: create() argument 1 must be str or None, not Tk
Trying class Location(tk.Toplevel)
opens two windows: one titled Location and one titled tk. Closing one window closes both windows. I only want one of those windows.
What do I need to do to get the second window (Location) to (i) open independently and to (ii) open from the first window?
Note that when Location.py
is executed directly, root
inside Location.__init__()
refers to the global window root
created in the if __name__ == "__main__"
block.
However when Location.py
is imported, the code in the if
block will not be executed and so there is no root
defined because the root
window created in main script cannot be accessed by imported module directly.
You need to add root
argument to class Location
explicity:
class Location(tk.Frame):
# added root argument
def __init__(self, root, *args, **kwargs):
super().__init__(root, *args, **kwargs)
...
And then you need to create a Toplevel
inside Building_Info.open_location()
and pass it to Location
:
class Building_Info(tk.Frame):
...
def open_location(self):
# create a toplevel window
win = tk.Toplevel()
# and pass it to Location class
Location.Location(win)