tkinterアプリケーションを構築する最良の方法?


136

以下は、私の典型的な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を学んでいるので、正しい方向に進むためのアドバイスと説明が欲しいだけです。


2
これは、tkinter GUIデザインに関する優れたチュートリアルで、いくつかの例-python-textbok.readthedocs.org/en/latest/…があります。これは、MVCデザインパターンの別の例-sukhbinder.wordpress.com/2014/12/ 25 /…
ボンドリン

12
この質問は広いかもしれませんが、(他のほとんどすべての[tkinter]回答と比較して)比較的人気のある回答として役立ちます。私はそれを開いているほうが閉じているよりも便利だと思うので、私は再オープンを指名しています。
ブライアンオークリー

回答:


271

私はオブジェクト指向のアプローチを提唱しています。これは私が最初に使用するテンプレートです:

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

これらのインスタンスはすべて共通の親を共有しているため、親は事実上、モデルビューコントローラーアーキテクチャの「コントローラー」部分になります。したがって、たとえば、メインウィンドウはを呼び出すことでステータスバーに何かを配置できますself.parent.statusbar.set("Hello, world")。これにより、コンポーネント間のシンプルなインターフェースを定義することができ、最小限の結合を維持するのに役立ちます。


22
@ブライアンオークリー私がそれらの構造を研究できるインターネット上の良いサンプルコードを知っていますか?
Chris Aung 2013

2
オブジェクト指向のアプローチの2番目です。ただし、私の経験では、GUIを呼び出すクラスで継承を使用しないことをお勧めします。TkオブジェクトとFrameオブジェクトの両方が、何も継承しないクラスの属性である場合、柔軟性が向上します。この方法では、TkオブジェクトとFrameオブジェクトに簡単にアクセスでき(あいまいさを減らすことができます)、1つを破棄しても、不要な場合はクラスのすべてが破棄されません。これが一部のプログラムで不可欠である正確な理由を忘れましたが、それにより、より多くのことができるようになります。
Brōtsyorfuzthrāx

1
クラスを使用するだけでプライベートな名前空間が提供されないのですか?フレームをサブクラス化すると、なぜそれが改善されるのですか?
gcb 2015

3
@gcb:はい、どのクラスでもプライベートな名前空間が提供されます。なぜフレームをサブクラス化するのですか?通常はとにかくフレームを作成するので、管理するクラスが1つ少なくなります(Frameのサブクラスと、属性としてフレームを使用してオブジェクトから継承するクラス)。私はそれをより明確にするために答えを少し言い換えました。フィードバックをお寄せいただきありがとうございます。
Bryan Oakley

2
@madtyn:parent後で使用する場合を除いて、への参照を保存する必要はありません。私の例のどのコードも保存する必要がないため、保存しませんでした。
ブライアンオークリー2017

39

トップレベルのウィンドウをそれぞれ独自のクラスに配置すると、コードを再利用し、コードを整理しやすくなります。ウィンドウに表示されるボタンと関連メソッドは、このクラス内で定義する必要があります。以下に例を示します(ここから取得)。

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

こちらもご覧ください:

お役に立てば幸いです。


6

これは悪い構造ではありません。それはうまくいきます。ただし、誰かがボタンなどをクリックしたときにコマンドを実行するには、関数に関数が必要です

したがって、あなたができることは、これらのクラスを作成し、ボタンクリックなどのコマンドを処理するクラスにメソッドを含めることです。

次に例を示します。

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の中で考えを


3
「Thinking in Tkinter」はグローバルな輸入を擁護していますが、これは非常に悪いアドバイスです。
ブライアンオークリー2013

1
本当のことですが、メインクラスのメソッド構造の一部だけをグローバルに使用することはお勧めしません:)
シリアル

2

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


2
TKinterPython 2でのみ使用できます。Python3の使用をお勧めしtkinterます。最後の3行のコードをmain()関数の下に配置し、プログラムの最後に呼び出します。グローバル名前空間を汚染し、読みやすさを低下させる可能性があるため、使用は絶対に避けfrom module_name import *ます。
Zac

1
間でどのように違いを言うことができるbutton1 = tk.Button(root, command=funA)button1 = ttk.Button(root, command=funA)あればtkinter、拡張モジュールもインポートされていましたか?では*、構文、コードの両方の行があるように思われますbutton1 = Button(root, command=funA)。その構文の使用はお勧めしません。
Zacの2017年

0

クラスを使用してアプリケーションを整理すると、問題をデバッグし、アプリを簡単に改善するために、あなたやあなたと協力する他の人が簡単になります。

次のようにアプリケーションを簡単に整理できます。

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

おそらく、プログラムを構造化する方法を学ぶ最良の方法は、他の人のコードを読むことです。特に、多くの人が貢献した大きなプログラムの場合はそうです。多くのプロジェクトのコードを見ると、コンセンサススタイルがどうあるべきかがわかるはずです。

言語としてのPythonは、コードのフォーマット方法に関する強力なガイドラインがあるという点で特別です。1つ目は、いわゆる「Zen of Python」です。

  • 醜いよりも美しい方がいいです。
  • 明示的は暗黙的よりも優れています。
  • シンプルは複雑よりも優れています。
  • 複雑は複雑よりも優れています。
  • ネストよりもフラットの方が優れています。
  • 疎は密よりも優れています。
  • 読みやすさが重要です。
  • 特別なケースは、ルールを破るほど特別なものではありません。
  • 実用性は純粋さを上回りますが。
  • エラーがサイレントに渡ることはありません。
  • 明示的に沈黙させない限り。
  • あいまいな状況に直面して、推測する誘惑を拒否してください。
  • それを行うには、明白な方法が1つ(できれば1つだけ)あるはずです。
  • あなたがオランダ人でない限り、その方法は最初は明白ではないかもしれませんが。
  • 今は決してないよりはましです。
  • 決してますが、多くの場合よりも良好である右の今。
  • 実装の説明が難しい場合は、悪い考えです。
  • 実装が説明しやすい場合は、良いアイデアかもしれません。
  • 名前空間は非常に魅力的なアイデアの1つです。もっと多くのことをしましょう。

より実用的なレベルでは、PythonのスタイルガイドであるPEP8があります。

それらを念頭に置いて、コードスタイル、特にネストされた関数は実際には適合しません。クラスを使用するか、クラスを個別のモジュールに移動することにより、それらを平坦化する方法を見つけます。これにより、プログラムの構造がはるかに理解しやすくなります。


12
Zen of Pythonを使用する場合は-1。それはすべて良いアドバイスですが、尋ねられた質問に直接対処するものではありません。最後の段落を削除すると、この回答はこのサイトのほとんどすべてのpythonの質問に適用できます。これは良い前向きなアドバイスですが、質問に対する答えにはなりません。
Bryan Oakley 2013

1
@BryanOakley私はあなたに同意しません。はい、Zen of Pythonは幅広く、多くの質問に対応するために使用できます。彼は最後の段落で、クラスを選択するか、関数を個別のモジュールに配置することについて言及しました。また、PythonのスタイルガイドであるPEP8についても言及しました。直接的な答えではありませんが、この答えは、取ることができる多くの異なる経路について言及しているという事実において信頼できると思います。それは私の意見です
Zac

1
私はこの特定の質問に対する答えを探してここに来ました。自由回答式の質問であっても、この回答では何もできません。-1も私から。
ジョナサン2018年

いいえ、問題はtkinterアプリを構築することです。スタイリング/コーディング/禅のガイドラインについては何もありません。@Arbiterを「直接的な回答ではないが」と引用するのは簡単なので、回答ではありません。これは「たぶんはい」と「たぶんいいえ」のようなもので、前に禅が付いています。
m3nda 2018

-7

私は個人的には、オブジェクト指向のアプローチを使用しません。これは、主にa)邪魔になるだけだからです。b)モジュールとして再利用すること決してありません

ただし、ここでは説明しませんが、スレッド化またはマルチプロセッシングを使用する必要あります。常に。そうしないと、アプリケーションはひどいものになります。

単純なテストを実行するだけです。ウィンドウを開始してから、URLなどをフェッチします。変更点は、ネットワーク要求が発生している間はUIが更新されないことです。つまり、アプリケーションウィンドウは壊れます。使用しているOSによって異なりますが、ほとんどの場合、再描画は行われません。ウィンドウ上にドラッグしたものは、プロセスがTKメインループに戻るまで、その上に貼り付けられます。


4
あなたの言うことは単に真実ではありません。私は何百ものtkベースのアプリケーション(個人用と商用の両方)を作成しましたが、スレッドを使用する必要はほとんどありませんでした。スレッドにはそれらの場所がありますが、tkinterプログラムを作成するときにスレッドを使用しなければならないというのは事実ではありません。関数が長く実行されている場合は、スレッドまたはマルチプロセッシングが必要になる可能性がありますが、スレッドを必要としない、多くの種類のプログラムを記述できます。
Bryan Oakley

もう少し明確になるようにあなたの答えを言い換えれば、それはより良い答えになると思います。また、tkinterでスレッドを使用する標準的な例があると本当に役立ちます。
Bryan Oakley

トピックから外れているので、ここでベストアンサーになることを気にしませんでした。ただし、スレッディング/マルチプから始めるのは非常に簡単です。後で追加する必要がある場合、それはロストバトルです。今日では、ネットワークと通信しないアプリケーションは絶対にありません。無視して「ディスクIOが少ない」と思っても、明日、クライアントはファイルがNFS上に存在することを決定し、ネットワークIOを待っており、アプリが停止しているように見えます。
gcb

2
@ erm3nda:「ネットワークに接続されているすべてのアプリやIO書き込みを行うと、スレッドやサブプロセスを使用する方がはるかに高速になります」 -これは単に正しくありません。スレッド化によってプログラムが必ずしも高速になるとは限らず、場合によっては低速になることもあります。GUIプログラミングでスレッドを使用する主な理由は、GUIをブロックするコードを実行できるようにすることです。
ブライアンオークリー

2
erm3nda @:いいえ、私はしていないスレッドが必要とされていないと言って、すべてで。それらは多くのことのために確かに必要です(まあ、スレッドまたはマルチプロセッシング)。それは、tkinterが適しているが、スレッドが単に不要な、非常に大規模なクラスのGUIアプリケーションがあるということだけです。そして、はい、「インストーラー、メモ帳、その他の簡単なツール」はそのカテゴリーに分類されます。世界は、単語、エクセル、フォトショップなどよりも、これらの「簡単なツール」で構成されています。さらに、ここでのコンテキストはtkinterであることを忘れないでください。Tkinterは通常、非常に大規模で複雑なアプリケーションには使用されません。
ブライアンオークリー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.