GUI tkinter с или без наследования объектов - PullRequest
0 голосов
/ 15 июня 2019

Я пишу программу на Python с графическим интерфейсом и выбрал tkinter для этого.У меня есть некоторый опыт работы с Python, но я новичок в программировании на tkinter и GUI.

Я читал несколько уроков о том, как использовать tkinter.То, что я мог найти, было очень простым, например, «как отобразить кнопку».Это заставляет меня изо всех сил пытаться найти полезную модель для структурирования части моей программы, которая определяет пользовательский интерфейс.

До сих пор в результате моих поисков было получено только 1 руководство по структурированию графического интерфейса Python / tkinter в стиле ООП: pythonprogramming.net

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

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

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

Пример наследования на основе pythonprogramming.net :

import tkinter as tk


class AppMain(tk.Tk):

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        container = tk.Frame(self)
        container.pack(side="top", fill="both", expand=True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        self.frames = {}

        frame = Page(container, self)

        self.frames[Page] = frame

        frame.grid(row=0, column=0, sticky="nsew")

        self.show_frame(Page)

    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()


class Page(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)

        label = tk.Label(self, text="Start Page", font=("Verdana", 12))
        label.pack(padx=10, pady=10)


def main():
    application = AppMain()
    application.mainloop()


if __name__ == "__main__":
    main()

Альтернатива без наследования:

РЕДАКТ. 1: Добавить сеточную переменную на страницу init

import tkinter as tk


class AppMain(object):

    def __init__(self):
        self.root = tk.Tk()

        container = tk.Frame(self.root)
        container.pack(side="top", fill="both", expand=True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        self.pages = {}

        page = Page(container, self.root, {"row": 0, "column": 0, "sticky": "nsew"})

        self.pages[Page] = page

        self.show_page(Page)

    def show_page(self, container):
        page = self.pages[container]
        page.show()

    def run(self):
        self.root.mainloop()


class Page(object):

    def __init__(self, parent, controller, grid):
        self.frame = tk.Frame(parent)
        self.frame.grid(**grid)

        label = tk.Label(self.frame, text="Start Page", font=("Verdana", 12))
        label.pack(padx=10, pady=10)

    def show(self):
        self.frame.tkraise()


def main():
    application = AppMain()
    application.run()


if __name__ == "__main__":
    main()

1 Ответ

1 голос
/ 15 июня 2019

Основным преимуществом наследования от виджета tkinter (обычно Frame) является то, что вы можете обрабатывать этот объект, как и любой другой виджет, когда дело доходит до разметки вашего пользовательского интерфейса.

Например, типичный пользовательский интерфейс может состоять из панели инструментов, боковой панели для навигации, основной рабочей области и, возможно, строки состояния внизу. Создав класс для каждого из них, который наследуется от Frame, вы можете расположить свой графический интерфейс следующим образом:

toolbar = Toolbar(root)
sidebar = Sidebar(root)
main = WorkArea(root)
statusbar = Statusbar(root)

toolbar.pack(side="top", fill="x")
statusbar.pack(side="bottom", fill="x")
sidebar.pack(side="left", fill="y")
main.pack(side="right", fill="both", expand=True)

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

Например, вам может потребоваться присвоить внутреннему фрейму каждого раздела общее имя, чтобы основная программа могла его разметить:

toolbar.inner_frame.pack(side="top", fill="x")
statusbar.inner_frame.pack(side="bottom", fill="x")
sidebar.inner_frame.pack(side="left", fill="y")
main.inner_frame.pack(side="right", fill="both", expand=True)

В примере в вашем вопросе, где вы наследуете от object, ваш класс Page должен знать, что корневое окно использует grid, а также должен знать, что ему нужно поместить себя в определенную строку и колонка. Это тесно связывает эти две части вашего кода вместе - вы не можете изменить одну часть вашего кода без необходимости изменять другие части.

Например, допустим, у вас есть дюжина страниц. Поработав над кодом некоторое время, вы решаете, что AppMain необходимо разместить дополнительный виджет в нулевой строке контейнера. Теперь вам нужно войти и изменить все дюжины классов страниц, чтобы они могли поместить себя в строку 1 вместо строки 0.

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