Добавление методов в tk.Tk и tk.Toplevel - PullRequest
2 голосов
/ 01 ноября 2019

Я хотел бы добавить 2 метода к экземплярам tk.Tk и tk.Toplevel. Первый существует как корневое окно приложения, где последний создается пользователем любое количество раз. Каждый tk.Toplevel представляет различные функциональные возможности приложения, и существуют 13 существующих вариантов. Приложение сконфигурировано так, что в любой момент может быть создан только 1 экземпляр каждого варианта.

Один из подходов к решению этой проблемы может заключаться в создании подкласса обоих классов:

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

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

Поскольку я только желаючтобы добавить 2 метода для функциональности (которые идентичны, поскольку они используют методы этих классов, такие как winfo_height()), создание подклассов кажется избыточным, а также избыточным, поскольку один и тот же код пишется дважды. Поэтому другим методом может быть использование setattr(), но мне кажется, что это нарушает принцип открытия / закрытия при проектировании классов:

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)

Теперь я написал методы только один раз и изменил классы;по сути, это сокращает первый метод и фактически является тем же.

Учитывая, что tk.Tk - это окно, а tk.Toplevel - это окно, нет ли пути через tkinter, где я могу определитьметод, к которому у всех "оконных" виджетов будет доступ? Это не было бы проблемой, если бы все окна были из одного tkinter класса, но корневое окно tk.Tk отличается от корневого tk.Toplevel (например, для привязок событий, не обязательно визуального поведения).

Редактировать:

Взглянув на решение @BryanOakleys, я обнаружил, что линтерам действительно не нравится это;они указывают на неразрешенные ссылки на атрибуты, к которым, как я знаю, подклассы будут иметь доступ, но не к смешанному классу. Примите во внимание следующее:

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

Линтеры IDE выдают предупреждение, поскольку self не может найти winfo_width определенный. Я, однако, знаю, что он будет вызываться экземплярами классов, которые подклассы из tk.Tk и tk.Toplevel, но неправильное использование приведет к неопределенному поведению, скажем, например:

class OtherWindow(Window):
    pass

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

Это проблематично? Предлагаемое решение работает, но кажется, что его заставляют работать, потому что «я знаю, как оно будет использовано». Очевидное решение состоит в том, чтобы набрать подсказку:

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}')

Таким образом, строки удовлетворены и код доступен для чтения любому (при условии, что им не нужно искать Union). Опять же, это ожидаемое поведение при использовании смешанных классов (никогда не пробовал их)?

Ответы [ 2 ]

2 голосов
/ 01 ноября 2019

Я бы использовал класс mixin. Сохранение пользовательских классов для TopWindow и RootWindow является правильным решением для создания специализации базовых классов Toplevel и Tk, а миксин решает проблему нежелания повторять код.

Например:

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

class TopWindow(CustomMixin, tk.Toplevel):
    pass

class RootWindow(CustomMixin, tk.Tk):
    pass
0 голосов
/ 01 ноября 2019

Напишите один класс с вашими методами, а затем создайте атрибут класса в других ваших классах, которые хранят экземпляр вашего класса дополнительных методов.

Попробуйте и сообщите мне, если у вас есть какие-либо вопросы:

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()

Результаты:

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...