I'm writing a Python application using tkinter GUI.
I created the TimestampWindow class which should be one of my Windows and made it extend the Toplevel class of tkinter's library and Window, a custom class where I put common attributes/methods for all my Windows.
Unfortunately when I try to call self.title(...)
on Window class (from a TimestampWindow instance) I get the following error:
File "/home/ela/elaPythonVirtualENV/PythonScripts/pgnclocker/pgnClocker/gui/windows/Window.py", line 54, in setUp
self.title(CommonStringsEnum.APP_NAME.value)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/tkinter/__init__.py", line 2301, in wm_title
return self.tk.call('wm', 'title', self._w, string)
^^^^^^^
AttributeError: 'TimestampWindow' object has no attribute 'tk'
Things doesn't change if I move the invocation of title method directly in TimestampWindow.
I leave two snippets with a "sketch" of TimestampWindow and Window classes below to clarify the whole thing:
Timestamp Window
import tkinter as tk
from pgnClocker.gui.windows.Window import *
... more imports ...
class TimestampWindow(tk.Toplevel, Window):
... code ...
Window
class Window:
... code ...
def setUp(self):
self.title(CommonStringsEnum.APP_NAME.value)
Could you please help me understand what's going on here? Shouldn't I be able to use TimestampWindow as a tkinter window since it inherits all methods from tk.Toplevel? Why it isn't inheriting tk attribute?
tk
is an attribute of the root window. To access it, you need to pass the master to your class.
import tkinter as tk
class Window:
def setUp(self):
self.title("Title")
class TimestampWindow(tk.Toplevel, Window):
def __init__(self, master): # <-
super().__init__(master) # <-
print("master:", master, master.call)
print("self.tk:", self.tk, self.tk.call)
#master.call('wm', 'title', self, "New title")
self.tk.call('wm', 'title', self, "New title")
self.transient(master)
#self.setUp()
root = tk.Tk()
print("root.tk:", root.tk, root.tk.call)
root.title("Root")
t = TimestampWindow(root) # <-
root.mainloop()
Classes:
self.setUp()
will work if you create a root object (and tk attribute) and call tk.Toplevel.__init__(self)
or super().__init__()
.tk.Toplevel.__init__
and Window.__init__
separately.import tkinter as tk
import tkinter.ttk as ttk
print(tk._default_root)
class Window:
# if you need it
def __init__(self, attr):
self.attr = attr
print("Window:", locals())
def setUp(self):
self.title("Title")
class TimestampWindow(tk.Toplevel, Window):
def __init__(self, root_window, attr):
tk.Toplevel.__init__(self, root_window) # super().__init__(root_window)
# if you need it
Window.__init__(self, attr)
# explicit master - self
b1 = tk.Button(self, text="Button2")
b1.pack()
print("TimestampWindow:", locals())
# attr, setUp, tk, and others should be there
# print(dir(self))
self.setUp()
class RootWindow(tk.Tk, Window):
def __init__(self):
tk.Tk.__init__(self) # super().__init__()
# if you need it
Window.__init__(self, "Root")
print("RootWindow:", locals())
self.setUp()
class GUI:
def __init__(self):
root = RootWindow()
root.config(bg="white")
print("root.tk:", root.tk)
# explicit master - root
t = TimestampWindow(root, "Toplevel")
t.config(bg="white")
# the button will be associated with the first root created
b = tk.Button(text="Button1")
b.pack()
print("GUI:", locals())
print("t.master is root:", t.master is root)
print("b.master is root:", b.master is root)
print("root is tk._default_root:", root is tk._default_root)
root.mainloop()
def main():
# try with blue master, see where Button1 is
#root1 = RootWindow()
#root1.config(bg="blue")
#print(root1.tk)
#print(root1 is tk._default_root)
# If there is no call to tk.Tk(), Toplevel and Style initiate the creation of the root behind the scene.
# or try with red master, see where Button1 is
#top = tk.Toplevel()
#top.master.config(bg="red")
#top.config(bg="red")
#print(top.master.tk)
#print(top.master is tk._default_root)
# or try with yellow master, see where Button1 is
#s = ttk.Style()
#s.master.config(bg="yellow")
#print(s.master.tk)
#print(s.master is tk._default_root)
gui = GUI()
if __name__ == "__main__":
main()
Master in tkinter: