Лучший способ структурировать приложение tkinter?

Ниже приведена общая структура моей типичной программы tkinter python.

def funA():
    def funA1():
        def funA12():
            # stuff

    def funA2():
        # stuff

def funB():
    def funB1():
        # stuff

    def funB2():
        # stuff

def funC():
    def funC1():
        # stuff

    def funC2():
        # stuff


root = tk.Tk()

button1 = tk.Button(root, command=funA)
button1.pack()
button2 = tk.Button(root, command=funB)
button2.pack()
button3 = tk.Button(root, command=funC)
button3.pack()

funA funB и funC откроет еще один Toplevel окна с виджетами, когда пользователь нажмет на кнопки 1, 2, 3.

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

Есть ли какая-то конкретная структура, которой мы должны следовать в качестве хорошей практики? Как мне планировать до начала написания программы python?

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

Ответ 1

Я выступаю за объектно-ориентированный подход. Это шаблон, который я начинаю с:

# Use Tkinter for python 2, tkinter for python 3
import tkinter as tk

class MainApplication(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.parent = parent

        <create the rest of your GUI here>

if __name__ == "__main__":
    root = tk.Tk()
    MainApplication(root).pack(side="top", fill="both", expand=True)
    root.mainloop()

Важно отметить:

  • Я не использую импорт подстановочных знаков. Я импортирую пакет как "tk", который требует, чтобы я префикс всех команд с помощью tk.. Это предотвращает глобальное загрязнение пространства имен, а также делает код совершенно очевидным, когда вы используете классы Tkinter, классы ttk или некоторые из ваших собственных.

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

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

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

class Navbar(tk.Frame): ...
class Toolbar(tk.Frame): ...
class Statusbar(tk.Frame): ...
class Main(tk.Frame): ...

class MainApplication(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.statusbar = Statusbar(self, ...)
        self.toolbar = Toolbar(self, ...)
        self.navbar = Navbar(self, ...)
        self.main = Main(self, ...)

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

Поскольку все эти экземпляры имеют общий родительский элемент, родительский элемент становится "контрольной" частью архитектуры model-view-controller. Так, например, главное окно может помещать что-то в строку состояния, вызывая self.parent.statusbar.set("Hello, world"). Это позволяет вам определить простой интерфейс между компонентами, помогая поддерживать связь с минимальным.

Ответ 2

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

import tkinter as tk

class Demo1:
    def __init__(self, master):
        self.master = master
        self.frame = tk.Frame(self.master)
        self.button1 = tk.Button(self.frame, text = 'New Window', width = 25, command = self.new_window)
        self.button1.pack()
        self.frame.pack()
    def new_window(self):
        self.newWindow = tk.Toplevel(self.master)
        self.app = Demo2(self.newWindow)

class Demo2:
    def __init__(self, master):
        self.master = master
        self.frame = tk.Frame(self.master)
        self.quitButton = tk.Button(self.frame, text = 'Quit', width = 25, command = self.close_windows)
        self.quitButton.pack()
        self.frame.pack()
    def close_windows(self):
        self.master.destroy()

def main(): 
    root = tk.Tk()
    app = Demo1(root)
    root.mainloop()

if __name__ == '__main__':
    main()

Также смотрите:

Надеюсь, что это поможет.

Ответ 4

Это не плохая структура; это будет работать просто отлично. Тем не менее, вы должны иметь функции в функции для выполнения команд, когда кто-то нажимает на кнопку или что-то

Итак, что вы можете сделать, это написать классы для них, а затем иметь методы в классе, которые обрабатывают команды для нажатий кнопок и тому подобное.

Вот пример:

import tkinter as tk

class Window1:
    def __init__(self, master):
        pass
        # Create labels, entries,buttons
    def button_click(self):
        pass
        # If button is clicked, run this method and open window 2


class Window2:
    def __init__(self, master):
        #create buttons,entries,etc

    def button_method(self):
        #run this when button click to close window
        self.master.destroy()

def main(): #run mianloop 
    root = tk.Tk()
    app = Window1(root)
    root.mainloop()

if __name__ == '__main__':
    main()

Обычно программы tk с несколькими окнами представляют собой несколько больших классов, и в __init__ создаются все записи, метки и т.д., А затем каждый метод должен обрабатывать события нажатия кнопки.

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

Взгляните на мышление в Tkinter.

Ответ 5

OOP должен быть подходом, а frame должен быть переменной класса вместо переменной экземпляра.

from Tkinter import *
class App:
  def __init__(self, master):
    frame = Frame(master)
    frame.pack()
    self.button = Button(frame, 
                         text="QUIT", fg="red",
                         command=frame.quit)
    self.button.pack(side=LEFT)
    self.slogan = Button(frame,
                         text="Hello",
                         command=self.write_slogan)
    self.slogan.pack(side=LEFT)
  def write_slogan(self):
    print "Tkinter is easy to use!"

root = Tk()
app = App(root)
root.mainloop()

введите описание изображения здесь

Ссылка: http://www.python-course.eu/tkinter_buttons.php

Ответ 6

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

Python, как язык, является особенным в том, что существуют четкие рекомендации относительно того, как вы должны форматировать свой код. Первый - это так называемый "Zen of Python":

  • Красивая лучше, чем уродливая.
  • Явный лучше, чем неявный.
  • Простой лучше, чем сложный.
  • Комплекс лучше, чем сложный.
  • Плоский лучше, чем вложенный.
  • Рельеф лучше плотного.
  • Показатели удобочитаемости.
  • Специальные случаи не являются достаточно сложными, чтобы нарушать правила.
  • Хотя практичность превосходит чистоту.
  • Ошибки никогда не должны проходить молча.
  • Если явно не отключено.
  • Перед лицом двусмысленности откажитесь от соблазна угадать.
  • Должен быть один - и желательно только один - простой способ сделать это.
  • Хотя этот путь может быть не очевидным сначала, если вы не голландский.
  • Теперь лучше, чем никогда.
  • Хотя никогда не бывает лучше, чем сейчас.
  • Если внедрение трудно объяснить, это плохая идея.
  • Если внедрение легко объяснить, это может быть хорошей идеей.
  • Пространства имен - одна хорошая идея - позвольте сделать еще больше!

На более практическом уровне существует PEP8, руководство по стилю для Python.

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

Ответ 7

Я лично не использую объективно-ориентированный подход, главным образом потому, что он а) только мешает; б) вы никогда не будете повторно использовать это как модуль.

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

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