Зачем использовать классы при программировании графического интерфейса tkinter на python?

19

Я программирую в основном на python и запрограммировал пару графических интерфейсов с помощью Tkinter, каждый учебник, который я когда-либо видел, рекомендовал определить и использовать класс для графического интерфейса, но мой графический интерфейс работает безупречно, используя только процедуры, без класса.

Зачем использовать класс? С моей точки зрения, это просто дополнительный уровень сложности и ненужный код.

Девон Мураока
источник

Ответы:

19

Зачем использовать класс? Потому что это облегчает работу, предполагая, что вы знаете, как выполнять объектно-ориентированное программирование, и предполагая, что вы пишете нетривиальный графический интерфейс. Использование объектов позволяет вам легко разделить ваш код на модульные модули, которые являются самодостаточными, а модульность вашего кода обычно считается наилучшей практикой.

Программирование GUI легко поддается объектно-ориентированному стилю, поскольку GUI полностью состоит из объектов - меток, кнопок, полос прокрутки, текстовых областей и т. Д. Поскольку вы уже используете объекты, организация кода в более крупные объекты имеет смысл , Панель инструментов - это объект, строка состояния - это объект, панель навигации - это объект, основная область - это объект, каждая вкладка ноутбука - это объект и т. Д.

Даже когда ваш код не очень сложен, с более практической точки зрения он позволяет вам определять привязки и обратные вызовы раньше в файле, чем определение вызываемой вами функции, что, на мой взгляд, имеет большой смысл.

Например, рассмотрим простой пример (при условии, что tkinter был импортирован как import tkinter as tk(python3) или import Tkinter as tk(python2):

def quit(event=None):
    sys.exit()
root = tk.Tk()
label = tk.Label(root, text="Hello, world")
label.pack()
label.bind("<1>", quit)
root.mainloop()

Для меня поток этого кода все неправильно. Я должен определить метод quit, прежде чем ссылаться на него, и создание корневого окна и вызов mainloop разделены всем остальным кодом.

Используя классы, я могу написать код в более естественном порядке:

class MyWindow(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        label = tk.Label(self, text="Hello, world")
        label.pack()
        label.bind("<1>", self.quit)
    def quit(self, event=None):
        sys.exit()

root = tk.Tk()
MyWindow(root).pack()
root.mainloop()

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

Другое преимущество заключается в том, что теперь я могу легко изменить содержащее окно без необходимости что-либо менять в «главном» окне (и наоборот). То есть я могу добавить границы или новый раздел в основной графический интерфейс, но мне не нужно трогать ни одной строки кода внутри MyWindow. Сравните это с процедурным кодом, где вам, возможно, придется изменить label.pack()оператор, и с пакетом (или сеткой) операторов всех других виджетов в пользовательском интерфейсе.

Однако все это говорит о том, что использование объектно-ориентированного подхода не является обязательным для написания хорошего, чистого, поддерживаемого кода. Это может быть, но это также может привести к плохому коду. В конце концов, объектно-ориентированный подход - это всего лишь инструмент. Используете ли вы его или нет, и правильно ли вы его используете или нет, зависит от многих факторов. Поэтому вполне может быть, что для вас и для кода, который вы пишете, функциональный стиль вполне приемлем. Я думаю, вы обнаружите, что по мере усложнения ваших программ объектно-ориентированный подход облегчит организацию и поддержку вашего кода.

Брайан Оукли
источник
Почему вы использовали рамку во втором примере? Не могли бы вы избежать этого, как в первом примере? Есть ли секрет использования Frame с классами?
multigoodverse
2
Рамка просто для удобства. Нет никакого секрета в наследовании от Frame. Я мог бы унаследовать от objectлюбого другого класса, но я все равно в конечном итоге создать кадр. Если я помещаю все в рамку, имеет смысл сделать класс рамкой. ,
Брайан Оукли
1
Имеет смысл, спасибо! Кроме того, я видел, как другие используют self перед переменными, но я вижу, что вы используете что-то вроде label=tk.Label()вместо self.tk.Label(). Это выбор стиля? Вот пример использования self: python-textbok.readthedocs.org/en/1.0/…
multigoodverse
1
@BryanOakley, я думаю, вы хотели использовать parent вместо root в следующей строке MyWindow .__ init__: "label = tk.Label (root, text =" Hello, world ")"
user3885927
1
@ user3885927: да! Вау, почти три года кто-то заметил это. Хотя, parentскорее нет self, так как сам класс является фреймом. Благодарность!
Брайан Оукли