Bir tkinter uygulaması yapılandırmanın en iyi yolu?


136

Aşağıda tipik python tkinter programımın genel yapısı verilmiştir.

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 funBve kullanıcı 1, 2, 3 düğmesine tıkladığında widget'lı funCbaşka bir Toplevelpencere açacaktır .

Bu bir python tkinter programı yazmak için doğru yolu olup olmadığını merak ediyorum? Tabii, bu şekilde yazsam bile işe yarayacak, ama en iyi yol bu mu? Kulağa aptalca geliyor ama diğer insanların yazdığı kodları gördüğümde, kodları bir grup işlevle uğraşmıyor ve çoğunlukla sınıfları var.

İyi uygulama olarak izlememiz gereken belirli bir yapı var mı? Bir python programı yazmaya başlamadan önce nasıl planlamalıyım?

Programlamada en iyi uygulama diye bir şey olmadığını biliyorum ve ben de istemiyorum. Python'u kendi başıma öğrendiğim için beni doğru yönde tutmak için bazı tavsiye ve açıklamalar istiyorum.


2
İşte birkaç örnek ile tkinter GUI tasarımı hakkında mükemmel bir öğretici - python-textbok.readthedocs.org/en/latest/… İşte MVC tasarım desenine sahip başka bir örnek - sukhbinder.wordpress.com/2014/12/ 25 /…
Bondolin

12
Bu soru geniş olabilir, ancak nispeten popüler bir cevap olarak yararlıdır ve h (diğer tüm [tkinter] cevaplarına göre). Yeniden açmaya aday gösterdim, çünkü açık olmasının kapalı olmasından daha yararlı olduğunu görüyorum.
Bryan Oakley

Yanıtlar:


271

Nesneye yönelik bir yaklaşımı savunuyorum. Bu ile başladığım şablon:

# 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()

Dikkat edilmesi gereken önemli noktalar:

  • Joker karakter içe aktarımı kullanmıyorum. Paketi tüm komutların önekini gerektiren "tk" olarak alıyorum tk.. Bu, global ad alanı kirliliğini önler, ayrıca Tkinter sınıflarını, ttk sınıflarını veya kendi sınıflarınızı kullanırken kodu tamamen açık hale getirir.

  • Ana uygulama bir sınıftır . Bu, tüm geri aramalarınız ve özel işlevleriniz için özel bir ad alanı sağlar ve genellikle kodunuzu düzenlemenizi kolaylaştırır. Yordamsal bir tarzda, yukarıdan aşağıya kod yazmanız, işlevleri kullanmadan önce tanımlamanız gerekir. tk.FrameSadece bir çerçeve oluşturarak başladığım için miras almayı tercih ederim , ancak hiçbir şekilde gerekli değildir.

Uygulamanızın ek üst düzey pencereleri varsa, bunların her birini ayrı bir sınıf haline getirmenizi ve devralınmasını öneririz tk.Toplevel. Bu size yukarıda belirtilen avantajların tümünü verir - pencereler atomik, kendi ad alanlarına sahiptir ve kod iyi organize edilmiştir. Ayrıca, kod büyüdükçe her birini kendi modülüne koymayı kolaylaştırır.

Son olarak, arayüzünüzün her büyük bölümü için sınıf kullanmayı düşünebilirsiniz. Örneğin, bir araç çubuğu, bir gezinme bölmesi, bir durum çubuğu ve bir ana alan içeren bir uygulama oluşturuyorsanız, bu sınıfların her birini yapabilirsiniz. Bu, ana kodunuzu oldukça küçük ve kolay anlaşılır hale getirir:

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)

Bu örneklerin tümü ortak bir üst öğeyi paylaştığından, üst öğe etkili bir şekilde bir model görünümü denetleyici mimarisinin "denetleyici" bölümü haline gelir. Örneğin, ana pencere durum çubuğuna arayarak bir şey yerleştirebilir self.parent.statusbar.set("Hello, world"). Bu, bileşenler arasında basit bir arabirim tanımlamanızı sağlar ve bir minimun ile bağlantıyı sürdürmeye yardımcı olur.


22
@Bryan Oakley, yapılarını inceleyebileceğim internette iyi örnek kodlar biliyor musunuz?
Chris Aung

2
Nesneye yönelik yaklaşımı ikinci olarak belirledim. Ancak, GUI olarak adlandırılan sınıfınızda kalıtım kullanmaktan kaçınmak benim deneyimime göre iyi bir fikirdir. Hem Tk hem de Frame nesneleri, hiçbir şeyden miras almayan bir sınıfın nitelikleri ise size daha fazla esneklik sunar. Bu şekilde Tk ve Frame nesnelerine daha kolay (ve daha az belirsiz) erişebilirsiniz ve birini yok etmek istemiyorsanız sınıfınızdaki her şeyi yok etmez. Bunun bazı programlarda yaşamsal olmasının kesin nedenini unuttum, ancak daha fazla şey yapmanıza izin veriyor.
Brōtsyorfuzthrāx

1
bir sınıfa sahip olmak size özel bir ad alanı vermeyecek mi? Çerçeveyi alt sınıflandırma neden bu kadar gelişir?
gcb

3
@gcb: evet, herhangi bir sınıf size özel bir ad alanı verecektir. Neden bir Çerçeve alt sınıfı? Ben genellikle bir çerçeve oluşturacağım, bu yüzden yönetmek için daha az bir sınıf (Frame alt sınıfı, bir nesne öznitelik olarak bir çerçeve ile miras, bir sınıf vs). Bunu daha açık hale getirmek için cevabı hafifçe yeniden ifade ettim. Geri dönüşünüz için teşekkür ederiz.
Bryan Oakley

2
@madtyn: parentdaha sonra kullanmayacaksanız bir referans kaydetmenize gerek yoktur . Örneğimin hiçbir kodunun kaydedilmesini gerektirmediği için kaydetmedim.
Bryan Oakley

39

Üst düzey pencerelerinizin her birini kendi ayrı sınıfına koymak, kodun yeniden kullanımını ve daha iyi kod organizasyonunu sağlar. Pencerede bulunan düğmeler ve ilgili yöntemler bu sınıf içinde tanımlanmalıdır. İşte bir örnek ( buradan alınmıştır ):

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()

Ayrıca bakınız:

Umarım yardımcı olur.


6

Bu kötü bir yapı değil; gayet iyi çalışacaktır. Ancak, birisi bir düğmeyi veya başka bir şeyi tıkladığında komutlar yapmak için bir işlevde işlevlere sahip olmanız gerekir

Yapabileceğiniz şey, bunlar için sınıf yazmaktır, daha sonra sınıfta düğme tıklamaları ve benzeri komutları işleyen yöntemler vardır.

İşte bir örnek:

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()

Genellikle birden fazla pencereli tk programları birden fazla büyük sınıftır ve __init__tüm girişlerde, etiketler vb. Oluşturulur ve daha sonra her yöntem düğme tıklama olaylarını işlemektir

Bunu yapmak için gerçekten doğru bir yol yoktur, ne olursa olsun sizin için işe yarar ve işi okunabilir olduğu sürece yapar ve kolayca açıklayabilirsiniz çünkü programınızı kolayca açıklayamazsanız, muhtemelen daha iyi bir yol vardır. .

Tkinter'de Düşünme'ye bir göz atın .


3
"Tkinter'de Düşünme" küresel ithalatı savunuyor, bence çok kötü bir tavsiye.
Bryan Oakley

1
Thts true ben size sadece ana sınıf yöntemleri bazı doğru yapı globals kullanmanızı önermiyoruz :)
Seri

2

OOP yaklaşım frameolmalı ve örnek değişkeni yerine bir sınıf değişkeni olmalıdır .

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()

resim açıklamasını buraya girin

Referans: http://www.python-course.eu/tkinter_buttons.php


2
Sadece TKinterPython 2'de kullanabilirsiniz . Python tkinter3 için kullanmanızı tavsiye ederim. Ayrıca, kodun son üç satırını bir main()fonksiyonun altına yerleştiririm ve bunu programın sonunda çağırırdım. Ben ediyorum kesinlikle kullanmaktan kaçının from module_name import *küresel ad kirletir ve okunabilirliği azaltabilir olarak.
Zac

1
Nasıl ayırt edebiliyordu button1 = tk.Button(root, command=funA)ve button1 = ttk.Button(root, command=funA)eğer tkintermodeli de ithal ediliyordu? İle *sözdizimi, kod her iki satır gibi görünmektedir button1 = Button(root, command=funA). Bu sözdizimini kullanmanızı önermem.
Zac

0

Uygulamanızı sınıf kullanarak organize etmek, sizin ve sizinle birlikte çalışan diğer kişilerin sorunları ayıklamasını ve uygulamayı kolayca geliştirmesini kolaylaştırır.

Uygulamanızı şu şekilde kolayca düzenleyebilirsiniz:

class hello(Tk):
    def __init__(self):
        super(hello, self).__init__()
        self.btn = Button(text = "Click me", command=close)
        self.btn.pack()
    def close():
        self.destroy()

app = hello()
app.mainloop()

-2

Muhtemelen programınızı nasıl yapılandıracağınızı öğrenmenin en iyi yolu, özellikle birçok insanın katkıda bulunduğu büyük bir programsa, diğer kişilerin kodlarını okumaktır. Birçok projenin koduna baktıktan sonra, fikir birliği tarzının ne olması gerektiği hakkında bir fikir edinmelisiniz.

Python, dil olarak, kodunuzu nasıl biçimlendirmeniz gerektiğine dair bazı güçlü yönergeler olması bakımından özeldir. Birincisi "Python Zen" denir:

  • Güzel, çirkin olmaktan iyidir.
  • Açık, örtük olmaktan iyidir.
  • Basit, karmaşık olmaktan iyidir.
  • Karmaşık karmaşık olmaktan iyidir.
  • Düz iç içe geçmişten daha iyidir.
  • Seyrek yoğun olmaktan iyidir.
  • Okunabilirlik önemlidir.
  • Özel durumlar kuralları ihlal edecek kadar özel değildir.
  • Pratiklik saflığı yenmesine rağmen.
  • Hatalar asla sessizce geçmemelidir.
  • Açıkça susturulmadıkça.
  • Belirsizlik karşısında, tahmin etme isteğini reddet.
  • Bunu yapmanın tek ve tercihen tek bir yolu olmalı.
  • Her ne kadar Hollandalı değilseniz bu yol ilk başta belli olmayabilir.
  • Şimdi hiç olmadığı kadar iyi.
  • Asla rağmen çoğu zaman daha iyi olduğunu doğru şimdi.
  • Uygulamanın açıklanması zorsa, bu kötü bir fikirdir.
  • Uygulamanın açıklanması kolaysa, iyi bir fikir olabilir.
  • İsim alanları harika bir fikirdir - bunlardan daha fazlasını yapalım!

Daha pratik bir düzeyde, Python için stil rehberi PEP8 var .

Bunu akılda tutarak, kod tarzınızın gerçekten iç içe işlevlere gerçekten uymadığını söyleyebilirim. Sınıfları kullanarak veya ayrı modüllere taşıyarak bunları düzleştirmenin bir yolunu bulun. Bu, programınızın yapısını daha kolay anlaşılmasını sağlayacaktır.


12
Python Zen kullanmak için -1. Her ne kadar iyi bir tavsiye olsa da, doğrudan sorulan soruya değinmiyor. Son paragrafı çıkarın ve bu cevap bu sitedeki hemen hemen her python sorusuna uygulanabilir. İyi, olumlu bir tavsiye, ama soruya cevap vermiyor.
Bryan Oakley

1
@BryanOakley Bu konuda sana katılmıyorum. Evet, Python Zen'i geniştir ve birçok soruyu ele almak için kullanılabilir. Son paragrafta sınıfları seçmeyi veya fonksiyonları ayrı modüllere yerleştirmeyi belirtti. Ayrıca Python için bir stil rehberi olan PEP8'den söz etti. Doğrudan bir cevap olmasa da, bu cevabın alınabilecek birçok farklı rotadan bahsetmesi gerçeğine inanıyorum. Bu sadece benim fikrim
Zac

1
Buraya bu özel sorunun yanıtlarını aramaya geldim. Açık uçlu bir soru için bile, bu yanıtla hiçbir şey yapamam. -1 benden de.
jonathan

Hiçbir şekilde, soru bir tkinter uygulaması yapılandırmak , stil / kodlama / zen yönergeleri hakkında hiçbir şey. @Arbiter'den alıntı yapmak kadar kolay "Doğrudan bir cevap olmasa da", bu yüzden bir cevap DEĞİLDİR. Bu "belki evet ve belki hayır" gibidir, zen önceden eklenmiştir.
m3nda

-7

Şahsen itiraz odaklı yaklaşımı kullanmıyorum, çünkü çoğunlukla a) sadece yoluna giriyor; b) bunu asla bir modül olarak tekrar kullanmayacaksınız.

ancak burada tartışılmayan bir şey, diş çekme veya çoklu işleme kullanmanız gerektiğidir . Her zaman. aksi takdirde başvurunuz korkunç olacaktır.

basit bir test yapın: bir pencere başlatın ve ardından bir URL veya başka bir şey getirin. ağ isteğiniz gerçekleşirken kullanıcı arayüzünüz güncellenmeyecektir. Yani, uygulama pencereniz bozulacak. Üzerinde çalıştığınız işletim sistemine bağlıdır, ancak çoğu zaman yeniden çizilmez, işlem TK ana döngüsüne dönene kadar pencerenin üzerine sürüklediğiniz her şey üzerine sıvanacaktır.


4
Söylediklerin doğru değil. Hem kişisel hem de ticari tk tabanlı uygulamaların gruplarını yazdım ve neredeyse hiçbir zaman iş parçacığı kullanmak zorunda kalmadım. İş parçacıklarının yerleri vardır, ancak bunları tkinter programları yazarken kullanmanız gerektiği doğru değildir . Uzun çalışma işlevleriniz varsa, iş parçacıklarına veya çok işlemciliğe ihtiyacınız olabilir, ancak iş parçacığı gerektirmeyen birçok program türü yazabilirsiniz.
Bryan Oakley

Eğer cevabınızı bu konuda biraz daha açık olacak şekilde yeniden ifade ettiyseniz, daha iyi bir cevap olacaktır. Aynı zamanda, tkinter ile iplik kullanımının kanonik bir örneğine sahip olmak gerçekten yardımcı olacaktır.
Bryan Oakley

burada en iyi cevap olmak umurumda değildi çünkü biraz konu dışı. ancak iş parçacığı / multip ile başlamanın çok kolay olduğunu unutmayın. daha sonra eklemek zorunda kalırsanız, bu kayıp bir savaştır. ve günümüzde, kesinlikle ağ ile konuşmayacak hiçbir uygulama yoktur. ve 'yalnızca çok az disk IO'm' olduğunu görmezden gelseniz bile, yarın müşteriniz dosyanın NFS'de yayınlanacağına ve ağ IO'sunu beklediğinize ve uygulamanız ölü gibi göründüğüne karar verir.
gcb

2
@ erm3nda: "ağa bağlı veya IO yazma yapan her uygulama iş parçacıkları veya alt işlem kullanarak çok daha hızlı olacaktır" - bu doğru değil. Diş açma, programınızı daha hızlı hale getirmez ve bazı durumlarda yavaşlar. GUI programlamasında, iş parçacıklarını kullanmanın ana nedeni, GUI'yi başka şekilde engelleyecek bazı kodları çalıştırabilmektir.
Bryan Oakley

2
@ erm3nda: hayır, ben değil diyerek ipler gerekli değildir hiç . Birçok şey için kesinlikle gereklidirler (iyi, iş parçacıkları veya çok işlemli). Sadece tkinter'in uygun olduğu, ancak iş parçacıklarının gerekli olmadığı çok büyük bir GUI uygulamaları sınıfı var. Ve evet, "montajcılar, not defterleri ve diğer kolay araçlar" bu kategoriye girer. Dünya, kelime, excel, photoshop vb . Şeylerden daha fazla bu "kolay araçlardan" oluşur. Artı, buradaki bağlamın daha akılda kalıcı olduğunu unutmayın . Tkinter tipik olarak çok büyük, karmaşık uygulamalar için kullanılmaz.
Bryan Oakley
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.