Tkinter Window 组合对象导致属性错误
Tkinter Window Composition Object causes Attribute Error
我在做什么:
我目前正在制作剪刀石头布游戏。我实际上只想通过控制台执行此操作,但考虑到最终变得如此拥挤,我决定切换到 tkinter。我为 GUI 的每个组件创建了自定义 classes(主要是为了了解 OOP)。我使用组合是因为有人建议我在使用 Thin Wrappers 时避免继承。
预期结果 VS 实际结果:
我创建了一个自定义实例 Window Class(名称:GameWindow)并给它参数(window_name) 是我在 innit 函数中指定的。之后,我创建了自定义按钮 class(名称:ChoiceButton)的实例,并传递了 innit 函数中指定的所需参数。然后我使用 .grid 管理器将按钮放置在指定位置。
我的预期结果很明显:window 打开时在指定位置放置了一个按钮。
实际结果是这样的:window 打开,里面没有按钮。如果我通过编辑器退出代码,它甚至不会出现错误。但是,如果我通过按右上角的 X 按钮关闭 window,它会抛出一个错误,我将在下面附加该错误。
到目前为止的故障排除:
1. 在window 中放置一个框架,然后添加按钮。 (结果: 没有任何改变)
2. 从原来的 tKinter 创建一个按钮 Button() class (NOT 我的自定义 class!) 和
将它放在 window 内的指定位置。 (结果: 没有任何改变)
3. 步骤“1.”和步骤“2.”合并。创建一个“普通”按钮并将其放置在框架中,该框架本身已放置在指定位置。 (结果: 没有任何改变)
3. 从 tk() class 创建了一个“普通”window 对象。然后重复步骤'1.'到'3.'。 (结果: Window 和按钮出现。自定义按钮 class 因此有效。自定义 window class 无效。)
错误和代码
在此下方,您将找到在控制台中看到的错误消息。就代码而言,我只包含了 GameWindow Class 代码,因为很明显这很可能是罪魁祸首。如果您需要完整代码,请随时与我联系!
错误:
Traceback (most recent call last):
File "C:/Users/Jonas/PycharmProjects/RockPaperScissors/classtestfile.py", line 66, in <module>
test_frame = Frame(main_Menu, row=1, column=1, columnspan=2, sticky="nsew")
File "C:\Users\Jonas\AppData\Local\Programs\Python\Python38\lib\tkinter\__init__.py", line 3119, in __init__
Widget.__init__(self, master, 'frame', cnf, {}, extra)
File "C:\Users\Jonas\AppData\Local\Programs\Python\Python38\lib\tkinter\__init__.py", line 2561, in __init__
BaseWidget._setup(self, master, cnf)
File "C:\Users\Jonas\AppData\Local\Programs\Python\Python38\lib\tkinter\__init__.py", line 2530, in _setup
self.tk = master.tk
AttributeError: 'GameWindow' object has no attribute 'tk'
Process finished with exit code 1
代码:
import tkinter
# ||create a class for game windows;
class GameWindow:
# ||set attributes; window height and width;
window_height = 525
window_width = 700
# ||create __init__ function; self and window_name as parameter;
def __init__(self, window_name):
# ||make Tk() master of this class;
self.master = tkinter.Tk()
# ||set title of the file to the value of 'window_name';
self.master.title(window_name)
# ||create a grid by assigning weight to all rows and columns;
self.rows = 0 # -- create row counter --;
while self.rows < 10: # -- make row counter count up to 9 --;
self.master.rowconfigure(self.rows, weight=1) # -- assign weight of 1 to respective row --;
self.master.columnconfigure(self.rows, weight=1) # -- assign weight of 1 to respective column --;
self.rows += 1 # -- increment of 1 the row counter --;
# save screen width and height in variables;
width = self.master.winfo_screenwidth()
height = self.master.winfo_screenheight()
# calculate centered window offset coordinates and save them in respective variables;
pos_x = (width - self.window_width) // 2
pos_y = (height - self.window_height) // 2
# make main window resizable;
self.master.resizable()
# initiate geometry manager with aforementioned values;
self.master.geometry(f"{self.window_width}x{self.window_height}+{pos_x}+{pos_y}")
# create function to execute the window in within the mainloop;
def run(self):
self.master.mainloop()
# create a standard class for choice buttons;
class ChoiceButton:
# set class attributes
is_active = False
# create __init__ function; declare parameters for initialization of name, status, color;
def __init__(self, parent, init_name, init_status, init_color):
# implement tkinter Button through instance variable "master"
self.master = tkinter.Button(parent, text=init_name, bg=init_color, command=self.button_click())
# self.bg_color = init_color -- seems redundant, discontinued;
self.is_active = init_status
# create function that serves as command for button
def button_click(self):
if not self.is_active:
self.is_active = True
else:
self.is_active = False
test_Window = tkinter.Tk()
test_Button = ChoiceButton(test_Window, "Test", False, "Blue")
test_Button.master.pack()
test_Window.mainloop()
当我给按钮一个父参数时,我直接引用了 GameWindow 实例而不是它的主实例。
很抱歉让您抽出时间,我完全被错误的结论误导了。
我在做什么:
我目前正在制作剪刀石头布游戏。我实际上只想通过控制台执行此操作,但考虑到最终变得如此拥挤,我决定切换到 tkinter。我为 GUI 的每个组件创建了自定义 classes(主要是为了了解 OOP)。我使用组合是因为有人建议我在使用 Thin Wrappers 时避免继承。
预期结果 VS 实际结果:
我创建了一个自定义实例 Window Class(名称:GameWindow)并给它参数(window_name) 是我在 innit 函数中指定的。之后,我创建了自定义按钮 class(名称:ChoiceButton)的实例,并传递了 innit 函数中指定的所需参数。然后我使用 .grid 管理器将按钮放置在指定位置。
我的预期结果很明显:window 打开时在指定位置放置了一个按钮。
实际结果是这样的:window 打开,里面没有按钮。如果我通过编辑器退出代码,它甚至不会出现错误。但是,如果我通过按右上角的 X 按钮关闭 window,它会抛出一个错误,我将在下面附加该错误。
到目前为止的故障排除:
1. 在window 中放置一个框架,然后添加按钮。 (结果: 没有任何改变)
2. 从原来的 tKinter 创建一个按钮 Button() class (NOT 我的自定义 class!) 和 将它放在 window 内的指定位置。 (结果: 没有任何改变)
3. 步骤“1.”和步骤“2.”合并。创建一个“普通”按钮并将其放置在框架中,该框架本身已放置在指定位置。 (结果: 没有任何改变)
3. 从 tk() class 创建了一个“普通”window 对象。然后重复步骤'1.'到'3.'。 (结果: Window 和按钮出现。自定义按钮 class 因此有效。自定义 window class 无效。)
错误和代码
在此下方,您将找到在控制台中看到的错误消息。就代码而言,我只包含了 GameWindow Class 代码,因为很明显这很可能是罪魁祸首。如果您需要完整代码,请随时与我联系!
错误:
Traceback (most recent call last):
File "C:/Users/Jonas/PycharmProjects/RockPaperScissors/classtestfile.py", line 66, in <module>
test_frame = Frame(main_Menu, row=1, column=1, columnspan=2, sticky="nsew")
File "C:\Users\Jonas\AppData\Local\Programs\Python\Python38\lib\tkinter\__init__.py", line 3119, in __init__
Widget.__init__(self, master, 'frame', cnf, {}, extra)
File "C:\Users\Jonas\AppData\Local\Programs\Python\Python38\lib\tkinter\__init__.py", line 2561, in __init__
BaseWidget._setup(self, master, cnf)
File "C:\Users\Jonas\AppData\Local\Programs\Python\Python38\lib\tkinter\__init__.py", line 2530, in _setup
self.tk = master.tk
AttributeError: 'GameWindow' object has no attribute 'tk'
Process finished with exit code 1
代码:
import tkinter
# ||create a class for game windows;
class GameWindow:
# ||set attributes; window height and width;
window_height = 525
window_width = 700
# ||create __init__ function; self and window_name as parameter;
def __init__(self, window_name):
# ||make Tk() master of this class;
self.master = tkinter.Tk()
# ||set title of the file to the value of 'window_name';
self.master.title(window_name)
# ||create a grid by assigning weight to all rows and columns;
self.rows = 0 # -- create row counter --;
while self.rows < 10: # -- make row counter count up to 9 --;
self.master.rowconfigure(self.rows, weight=1) # -- assign weight of 1 to respective row --;
self.master.columnconfigure(self.rows, weight=1) # -- assign weight of 1 to respective column --;
self.rows += 1 # -- increment of 1 the row counter --;
# save screen width and height in variables;
width = self.master.winfo_screenwidth()
height = self.master.winfo_screenheight()
# calculate centered window offset coordinates and save them in respective variables;
pos_x = (width - self.window_width) // 2
pos_y = (height - self.window_height) // 2
# make main window resizable;
self.master.resizable()
# initiate geometry manager with aforementioned values;
self.master.geometry(f"{self.window_width}x{self.window_height}+{pos_x}+{pos_y}")
# create function to execute the window in within the mainloop;
def run(self):
self.master.mainloop()
# create a standard class for choice buttons;
class ChoiceButton:
# set class attributes
is_active = False
# create __init__ function; declare parameters for initialization of name, status, color;
def __init__(self, parent, init_name, init_status, init_color):
# implement tkinter Button through instance variable "master"
self.master = tkinter.Button(parent, text=init_name, bg=init_color, command=self.button_click())
# self.bg_color = init_color -- seems redundant, discontinued;
self.is_active = init_status
# create function that serves as command for button
def button_click(self):
if not self.is_active:
self.is_active = True
else:
self.is_active = False
test_Window = tkinter.Tk()
test_Button = ChoiceButton(test_Window, "Test", False, "Blue")
test_Button.master.pack()
test_Window.mainloop()
当我给按钮一个父参数时,我直接引用了 GameWindow 实例而不是它的主实例。
很抱歉让您抽出时间,我完全被错误的结论误导了。