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 实例而不是它的主实例。

很抱歉让您抽出时间,我完全被错误的结论误导了。