为什么要将框架和小部件放在 类 中?

Why put frames and widgets in classes?

如问题所述,我似乎无法完全理解将 classes 与 tkinter 一起使用的意义。

我已经浏览了相当数量的不同站点,但我一直在搜索有关如何创建和使用 classes 的结果,但是 none 到目前为止已经能够找到我.在问这个问题时,我什至仔细研究了建议的问题。我最接近理解的是 Bryan 对此的解释 answer to the question Why use classes when programming a tkinter gui?

但是,我仍然觉得我差不多,只是还没有完全理解。

在 link 中的示例中,他创建了一个非常规程序,然后创建了一个更好、更传统的程序来做同样的事情。我知道它代表的规模比可以真正受益于面向对象方法的千行程序小得多。

是否每个小部件都需要位于其自己的单独框架中,而该框架可能是更大框架的一部分?

classes 可以有创建和放置框架的方法吗?此外,那些相同的 classes 是否可以拥有可以在先前制作的框架中创建、修改和放置小部件的方法?

我还有一些代码可以让我创建、修改和放置小部件。虽然我知道这不是传统的,但我也非常感谢对此提供一些意见。关于如何使用此代码使其变得更好,有什么建议吗?

import tkinter as tk

def layout(self, row=0, column=0, columnspan=None, row_weight=None, column_weight=None, color=None, sticky=None, ipadx=None, padx=None, ipady=None, pady=None):
    self.grid(row=row, column=column, columnspan=columnspan, sticky=sticky, ipadx=ipadx, padx=padx, ipady=ipady, pady=pady)
    self.grid_rowconfigure(row, weight=row_weight)
    self.grid_columnconfigure(column, weight=column_weight)
    self.config(bg=color)

class MyLabels(tk.Label):
    def __init__(self, parent, text, **kwargs):
        tk.Label.__init__(self, parent, text=text)
        layout(self, **kwargs)

class MyButtons(tk.Button):
    def __init__(self, parent, text, command, **kwargs):
        tk.Button.__init__(self, parent, text=text, command=command)
        layout(self, **kwargs)


window = tk.Tk()
test_button = MyButtons(window, "hi", None, color="pink")
window.mainloop()

评论后编辑: 所以从昨天开始我已经工作了好几个小时试图将你对我的想法融入其中。这是我想出的:

import tkinter as tk

window = tk.Tk()

class MyWidgets(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        self.layout()

    def layout(self):
        self.grid(row=0, column=0)
        self.config(bg="blue")

class MyButtons(MyWidgets):
    def __init__(self, parent, text):
        MyWidgets.__init__(self, parent)
        tk.Button(parent, text=text)
        self.layout()

frme = MyWidgets(window)
btn = MyButtons(frme, text="Test")
window.mainloop()

我试过在这个小程序上四处移动并重写很多区域,即使我能够证明 btn 实际上正在访问 self.config(bg="blue") 属性,按钮似乎没有改变。事实上,我无法找到一种方法来使按钮出现而无需在创建按钮后立即将 self.grid() 放入子 class 中。

不过,即使我确实添加了 self.grid() 按钮仍然不会变成蓝色。 self 有什么问题吗?

为什么子 class 创建按钮,而父 class 放置它时按钮不出现?

注意:我特意省略了整个 layout function,只用一个简单的 config method 代替了它。我想如果我能理解这一点,我就能找到一种方法将整个函数合并回代码中。

Does every widget need to be in its own separate frame that's maybe part of an even bigger frame?

这有点像询问数学表达式的每一部分是否都需要括号。严格来说,答案是"no"。然而,使用框架来组织小部件组是一种旨在使编写和理解代码更容易的工具,就像复杂数学方程式中的括号使编写和理解方程式更容易一样。

Can classes have methods that create and place a frame? In addition, can those same classes have methods than can create, modify, and place a widget within the previously made frame?

是的,是的。 class 中的方法对它们能做什么和不能做什么没有任何限制。 class 的方法可以做普通函数可以做的任何事情。

I also have some code that allows me to create, modify, and place a widget. Although I know it's not conventional, so I would greatly appreciate some input on this as well. Any suggestions on what you would do with this code to make it better?

"Better" 是非常主观的。对 20 行代码来说更好的东西可能对 200、2,000 或 20,000 行代码并不好。对于一个使用了两次的函数来说更好的东西可能对一个使用了数百或数千次的函数来说更好(反之亦然)。

也就是说,您正在做一件非常不合常规的事情,这会导致您的代码更难理解:您正在使用 self 作为不是方法的函数的参数的 class。 self 意味着 python 程序员非常特殊;在方法的上下文之外使用它是非常混乱的。

你应该为方法做两件事之一 layout:

  • self 重命名为 widgetself
  • 以外的任何其他术语
  • 创建定义 layout 的基础 class,然后让您的 class 继承基础 class。在这种情况下,self 是正确的第一个参数。

这部分答案引用了我原答案后添加的代码。

我所指的基础 class 需要是一个单独的 class。例如:

class Base():
    def layout(self):
        self.grid(row=0, column=0)
        self.config(bg="blue")

class MyLabels(Base, tk.Label):
    def __init__(self, parent, text, **kwargs):
        tk.Label.__init__(self, parent, text=text)
        self.layout(self, **kwargs)

class MyButtons(Base, tk.Button):
    def __init__(self, parent, text, command, **kwargs):
        tk.Button.__init__(self, parent, text=text, command=command)
        self.layout(self, **kwargs)

这种类型的 class 有时称为 mixin,因为它不是设计为作为独立对象实例化的。相反,它 "mixes in" 对其他 class 有一些额外的行为。 mixin 通常会有方法,但不会有自己的 __init__.