web-dev-qa-db-ja.com

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

funAfunBおよびfunCは、ユーザーがボタン1、2、3をクリックすると、ウィジェットを含む別のToplevelウィンドウを表示します。

これがpython tkinterプログラムを書く正しい方法かどうか疑問に思っていますか?もちろん、このように書いても機能しますが、最良の方法でしょうか?ばかげているように聞こえますが、他の人が書いたコードを見ると、彼らのコードはたくさんの関数で台無しにされておらず、ほとんどがクラスを持っています。

良い習慣として従うべき特定の構造はありますか? pythonプログラムの作成を開始する前に、どのように計画する必要がありますか?

プログラミングのベストプラクティスのようなものはないことを知っているし、私もそれを求めていません。私は自分でPythonを学んでいるので、正しい方向に私を保つためのアドバイスと説明が欲しいだけです。

102
Chris Aung

オブジェクト指向のアプローチを推奨します。これは私が始めたテンプレートです:

# 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 __== "__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")を呼び出すことでステータスバーに何かを配置できます。これにより、コンポーネント間のシンプルなインターフェースを定義でき、最小との結合を維持できます。

197
Bryan Oakley

最上位の各ウィンドウをそれぞれ独自のクラスに配置すると、コードを再利用して、コードを整理しやすくなります。ウィンドウに存在するボタンと関連メソッドは、このクラス内で定義する必要があります。以下に例を示します( here から取得):

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 __== '__main__':
    main()

参照:

お役に立てば幸いです。

28
alecxe

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

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

以下に例を示します。

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 __== '__main__':
    main()

通常、複数のウィンドウを持つtkプログラムは複数の大きなクラスであり、__init__にすべてのエントリ、ラベルなどが作成され、各メソッドがボタンクリックイベントを処理します

実際にそれを行う正しい方法はありません、あなたのために働くものは何でもあなたがプログラムを簡単に説明できないならおそらくそれを行うためのより良い方法があるので、あなたが読みやすく、簡単にそれを説明できる限り、仕事を完了させます。

Tkinterで考える を見てください。

4
Serial

OOPはアプローチであり、frameインスタンス変数ではなくclass変数でなければなりません。

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

enter image description here

リファレンス: http://www.python-course.eu/tkinter_buttons.php

1
Nik