Подтвердить что ты не робот

Наследование с кадра или нет в приложении 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()   
4b9b3361

Ответ 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()