Наследование с кадра или нет в приложении Tkinter
Я видел два основных способа настройки программы tkinter. Есть ли причина предпочесть друг другу?
from Tkinter import *
class Application():
def __init__(self, root, title):
self.root = root
self.root.title(title)
self.label = Label(self.root, text='Hello')
self.label.grid(row=0, column=0)
root = Tk()
app = Application(root, 'Sample App')
root.mainloop()
и
from Tkinter import *
class Application(Frame):
def __init__(self, title, master=None):
Frame.__init__(self, master)
self.grid()
self.master.title(title)
self.label = Label(self, text='Hello')
self.label.grid(row=0, column=0)
app = Application('Sample App')
app.mainloop()
Ответы
Ответ 1
Параметр, который я предпочитаю *, должен наследовать от класса Tk. Я думаю, что это более разумный выбор, поскольку окно - это, по сути, ваше приложение. Наследование от Frame
не имеет для меня никакого смысла, а затем наследует от Button
или Canvas
или Label
. Поскольку у вас может быть только один корень, имеет смысл, что именно это вы наследуете.
Я также думаю, что он делает код более читаемым, если вы импортируете как import Tkinter as tk
, а не from Tkinter import *
. Затем все ваши вызовы указывают модуль tk
. Я не рекомендую это для всех модулей, но для меня это имеет смысл с Tkinter.
Например:
import Tkinter as tk
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.label = tk.Label(text="Hello, world")
self.label.pack(padx=10, pady=10)
app = SampleApp()
app.mainloop()
* Примечание:, поскольку изначально написав этот ответ, я изменил свою позицию. Теперь я предпочитаю наследовать от Frame
, а не tk
. Нет никакого реального преимущества так или иначе, это скорее философский выбор, чем что-либо еще. Несмотря на это, я считаю, что если вы наследуете от Frame
или tk
, я думаю, что любой выбор лучше, чем первый пример в коде, который наследуется от ничего.
Одно небольшое преимущество, наследуемое от Frame
, превышает tk
, в том случае, если вы хотите, чтобы ваше приложение поддерживало несколько идентичных окон. В этом случае наследование от Frame
позволяет создать первое окно в качестве дочернего элемента root, а дополнительные окна - дочерние экземпляры Toplevel
. Тем не менее, я видел очень мало программ, которые когда-либо нуждаются в этом.
Для получения дополнительной информации о том, как я думаю, программы Tkinter должны быть структурированы, см. мой ответ на вопрос Структура программы Python Tkinter.
Ответ 2
Кадр обычно используется в качестве мастера геометрии для других виджетов.
Поскольку приложение обычно имеет множество виджетов, вы часто захотите содержать их все в кадре или, по крайней мере, использовать фрейм для добавления некоторых borderwidth
, дополнений или других возможностей.
Многие примеры, которые вы можете найти в Интернете, не используют Frame, потому что
они просто хотят продемонстрировать некоторую функцию в кратчайшем количестве кода.
Итак, используйте Frame, если вам это нужно, иначе нет.
Изменить. Я думаю, что лучший способ организовать графический интерфейс можно найти в этом учебнике Tkinter:
simpleApp.py:
import Tkinter as tk
class SimpleApp(object):
def __init__(self, master, **kwargs):
title=kwargs.pop('title')
frame=tk.Frame(master, **kwargs)
frame.pack()
self.label = tk.Label(frame, text=title)
self.label.pack(padx=10,pady=10)
if __name__=='__main__':
root = tk.Tk()
app = SimpleApp(root,title='Hello, world')
root.mainloop()
Это в основном как ваш первый пример в том, что SimpleApp
наследует от object
, а не Frame
. Я думаю, что это лучше, чем подклассификация Frame
, поскольку мы не переопределяем какие-либо методы Frame
. Я предпочитаю думать о SimpleApp
как о Frame
, а не о Frame
.
Наличие подкласса SimpleApp
object
имеет существенное преимущество перед подклассом tk.Tk
: оно упрощает встраивание SimpleApp
в большее приложение:
import simpleApp
import Tkinter as tk
class BigApp(object):
def __init__(self, master, **kwargs):
title=kwargs.pop('title')
frame=tk.Frame(master, **kwargs)
frame.pack()
self.simple = simpleApp.SimpleApp(frame,title=title)
frame.pack(padx=10, pady=10)
self.simple2 = simpleApp.SimpleApp(frame,title=title)
frame.pack()
if __name__=='__main__':
root = tk.Tk()
app = BigApp(root,title='Hello, world')
root.mainloop()
Таким образом, simpleApp.py может быть автономным script, а также импортируемым модулем.
Если вы попробуете это с SimpleApp
, наследующим от tk.Tk
, вы получите лишние нежелательные окна.
Ответ 3
Может быть преимущество перед установкой объекта верхнего уровня наследовать от Tk
вместо Frame
. Преимущество возникает, когда у вас есть динамический элемент для вашего графического интерфейса, например. a Label
, содержимое которого вы хотите установить с помощью textvariable=foo
вместо text= 'Label text'
.
В этом случае очень полезно использовать объекты Tkinter.DoubleVar
, Tkinter.IntVar
и Tkinter.StringVar
для хранения данных, поскольку графический интерфейс будет автоматически обновляться, когда будут установлены эти объекты. Однако, чтобы использовать эти объекты, вы должны указать их хозяина как запущенный экземпляр root Tkinter.Tk()
. Это проще, если вы явно сделаете свой основной объект подклассом Tkinter.Tk,
, а затем создадите фреймы и виджеты, чтобы вы могли пройти по экземпляру Tk
и правильно настроить свои переменные.
Вот краткий пример программы для иллюстрации идеи.
import Tkinter as tk
class Tkclass(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
app=Application(self)
app.master.title("Animal to Meat")
app.mainloop()
class Application(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.grid(sticky=tk.N+tk.S+tk.E+tk.W)
self.meatvar = tk.StringVar(master=parent)
self.meatvar.set("Meat?")
self.createWidgets()
def createWidgets(self):
top=self.winfo_toplevel()
top.rowconfigure(0, weight=1)
top.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1)
self.columnconfigure(0, weight=1)
self.columnconfigure(1, weight=1)
self.columnconfigure(2, weight=1)
self.columnconfigure(3, weight=1)
self.cowButton = tk.Button(self, text='Cow', command=self.setBeef)
self.cowButton.grid(row=0,column=0)
self.pigButton = tk.Button(self, text='Pig',command=self.setPork)
self.pigButton.grid(row=0,column=1)
self.meatLabel = tk.Label(self)
self.meatLabel.configure(textvariable=self.meatvar)
self.meatLabel.grid(row=0,column=2)
self.quit = tk.Button(self, text='Quit',command=self.QuitApp)
self.quit.grid(row=0, column=3)
def setBeef(self):
self.meatvar.set("Beef")
def setPork(self):
self.meatvar.set("Pork")
def QuitApp(self):
top=self.winfo_toplevel()
top.quit()
main = Tkclass()