简体   繁体   English

向 tk.Tk 和 tk.Toplevel 添加方法

[英]Adding methods to tk.Tk and tk.Toplevel

I would like to add 2 methods to instances of tk.Tk and tk.Toplevel .我想为tk.Tktk.Toplevel的实例添加 2 个方法。 The former exists as the applications root window, where the latter is created any number of times by the user.前者作为应用程序根 window 存在,后者由用户创建任意次数。 Each tk.Toplevel represents a different functionality of the application and current 13 variations exist.每个tk.Toplevel代表应用程序的不同功能,目前存在 13 种变体。 The application is configured so that only 1 instance of each variation may be created at any time.应用程序配置为在任何时候只能创建每个变体的 1 个实例。

One approach to solving this may be to subclass both classes:解决这个问题的一种方法可能是对这两个类进行子类化:

class RootWindow(tk.Tk):
    def method_1(self):
        ...
    def method_2(self):
        ...

class TopWindow(tk.Toplevel):
    def method_1(self):
        ...
    def method_2(self):
        ...

Since I only wish to add 2 methods for functionality (that are identical as they use methods of these classes such as winfo_height() ), subclassing seems overkill as well as redundant as the same code is written twice.由于我只希望为功能添加 2 个方法(它们使用这些类的方法相同,例如winfo_height() ),子类化似乎是多余的,因为相同的代码被编写了两次。 Therefore, another method may be to use setattr() , but I feel like that violates the open/close principle of class design:因此,另一种方法可能是使用setattr() ,但我觉得这违反了 class 设计的打开/关闭原则:

def method_1(self):
    ...
def method_2(self):
    ...

class RootWindow(tk.Tk):
    def __init__(self):
        setattr(self, 'method_1', method_1)
        setattr(self, 'method_2', method_2)

class TopWindow(tk.Toplevel):
    def __init__(self):
        setattr(self, 'method_1', method_1)
        setattr(self, 'method_2', method_2)

Now I've only written the methods once and modified the classes;现在我只写了一次方法并修改了类; in essence, this condenses the first method and is effectively the same.本质上,这浓缩了第一种方法并且实际上是相同的。

Given that tk.Tk is a window, and tk.Toplevel is a window, is there not a way through tkinter where I can define a method that all "window" based widgets will have access to?鉴于tk.Tk是一个 window,而tk.Toplevel是一个 window,有没有办法通过tkinter的小部件,我可以定义一个所有“窗口”都可以访问的方法? This wouldn't be an issue if all windows came from a single tkinter class, but the root window of tk.Tk is different than that of tk.Toplevel (such as for event bindings, not necessarily visual behavior). This wouldn't be an issue if all windows came from a single tkinter class, but the root window of tk.Tk is different than that of tk.Toplevel (such as for event bindings, not necessarily visual behavior).

Edit:编辑:

Taking a look at @BryanOakleys solution, I find that linters really dont like this;看看@BryanOakleys 的解决方案,我发现 linter 真的不喜欢这样; they point at unresolved references to attributes that I know the subclasses will have access to but not the mix-in class.他们指向我知道子类可以访问但不是混合 class 的属性的未解决引用。 Consider the following:考虑以下:

class Mixin:
    def method(self):
        width = self.winfo_width()
        height = self.winfo_height()
        print(f'The window is {width} x {height}')

IDE linters throw the caution because self cannot find winfo_width defined. IDE linters 会发出警告,因为self找不到定义的winfo_width I, however, know that its going to be called by instances of classes that subclass from tk.Tk and tk.Toplevel , but misuse would lead to undefined behavior, say for example:但是,我知道它将被从tk.Tktk.Toplevel子类化的类的实例调用,但误用会导致未定义的行为,例如:

class OtherWindow(Window):
    pass

o = OtherWindow()
o.method()
# Attribute error

Is this problematic?这有问题吗? The proposed solution works, but feels like its being forced to work because "I know how it will be used".提议的解决方案有效,但感觉像是被迫工作,因为“我知道它将如何使用”。 An obvious solution is to type hint:一个明显的解决方案是输入提示:

class Mixin:
    def method(self: Union[tk.Tk, tk.Toplevel]):
        width = self.winfo_width()
        height = self.winfo_height()
        print(f'The window is {width} x {height}')

Thus the linters are satisfied and the code is readable to anybody (assuming they dont need to look up Union ).因此, linter 很满意,并且任何人都可以阅读代码(假设他们不需要查找Union )。 Again, is this expected behavior when using mix-in classes (never tried them)?同样,在使用混合类(从未尝试过)时,这是预期的行为吗?

I would use a mixin class.我会使用混合 class。 Keeping custom classes for TopWindow and RootWindow is the right solution for creating a specialization of the base Toplevel and Tk classes, and the mixin solves the problem of not wanting to repeat code.TopWindowRootWindow保留自定义类是创建基础ToplevelTk类的专业化的正确解决方案,而 mixin 解决了不想重复代码的问题。

For example:例如:

class CustomMixin():
    def method_1(self):
        ...
    def method_2(self):
        ...

class TopWindow(CustomMixin, tk.Toplevel):
    pass

class RootWindow(CustomMixin, tk.Tk):
    pass

Write one class that has your methods then create a class attribute in your other classes that keep an instance of your extra methods class.编写一个具有您的方法的 class,然后在您的其他类中创建一个 class 属性,以保留您的额外方法 class 的实例。

Try this and let me know if you have any questions:试试这个,如果您有任何问题,请告诉我:

import tkinter as tk


class SomeExtraMethods:
    def __init__(self, window):
        self.window = window

    def size(self):
        return [self.window.winfo_width(), self.window.winfo_height()]


class RootWindow(tk.Tk):
    def __init__(self):
        super().__init__()
        self.extra = SomeExtraMethods(self)
        tk.Button(self, text='Print root window size', command=self.print_size).pack()
        tk.Button(self, text='Open top window', command=self.open_top).pack()

    def print_size(self):
        print(self.extra.size())

    def open_top(self):
        top = TopWindow()


class TopWindow(tk.Toplevel):
    def __init__(self):
        super().__init__()
        self.extra = SomeExtraMethods(self)
        tk.Button(self, text='Print top window size', command=self.print_size).pack()

    def print_size(self):
        print(self.extra.size())


if __name__ == '__main__':
    RootWindow().mainloop()

Results:结果:

在此处输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM