Как лучше всего структурировать приложение Tkinter?
Ниже приведена общая структура моей типичной программы на Python для Tkinter.
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.
Мне интересно, правильный ли это способ написания программы на Python для Tkinter? Конечно, это будет работать, даже если я напишу таким образом, но лучший ли это способ? Звучит глупо, но когда я вижу код, написанный другими людьми, их код не перегружен кучей функций, и в основном у них есть классы.
Есть ли какая-то конкретная структура, которой мы должны следовать в качестве надлежащей практики? Что я должен спланировать, прежде чем приступить к написанию программы на 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()
Также смотрите:
- простой привет, мир, из документов tkinter
- Пример кода Tkinter для нескольких окон, почему кнопки не загружаются корректно?
- Tkinter: как отобразить / скрыть окно
Надеюсь, это поможет.
Ответ 3
Это неплохая структура; она будет работать просто отлично. Однако в функции должны быть функции для выполнения команд, когда кто-то нажимает на кнопку или что-то в этом роде.
Итак, что вы могли бы сделать, это написать классы для них, а затем иметь методы в классе, которые обрабатывают команды для нажатия кнопок и тому подобное.
Вот пример:
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__
Создаются все записи, метки и т.д., А затем каждый метод предназначен для обработки событий нажатия кнопки
There isn't really a right way to do it, whatever works for you and gets the job done as long as its readable and you can easily explain it because if you cant easily explain your program, there probably is a better way to do it.
Take a look at Thinking in Tkinter.
Ответ 4
OOP should be the approach and frame
should be a class variable instead of instance variable.
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()