memopy

pythonで作ってみました的なブログ

python tkinterのクラス化手法によるGUI作成

python tkinterのドキュメント"A Simple Hello World Program"が難しすぎる

皆さんはpython tkinterのドキュメントをご覧になったことがあるだろうか。
※python3の場合
25.1. tkinter — Python interface to Tcl/Tk — Python 3.6.1 documentation
※python2の場合
24.1. Tkinter — Python interface to Tcl/Tk — Python 2.7.13 documentation

A Simple Hello World Program

この中にA Simple Hello World Programというサンプルスクリプトがある。
しかし、このサンプルスクリプトpython初心者にとって非常に難しい。

※python3の場合

import tkinter as tk

class Application(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.pack()
        self.create_widgets()

    def create_widgets(self):
        self.hi_there = tk.Button(self)
        self.hi_there["text"] = "Hello World\n(click me)"
        self.hi_there["command"] = self.say_hi
        self.hi_there.pack(side="top")

        self.quit = tk.Button(self, text="QUIT", fg="red",
                              command=root.destroy)
        self.quit.pack(side="bottom")

    def say_hi(self):
        print("hi there, everyone!")

root = tk.Tk()
app = Application(master=root)
app.mainloop()

※python2の場合

from Tkinter import *

class Application(Frame):
    def say_hi(self):
        print "hi there, everyone!"

    def createWidgets(self):
        self.QUIT = Button(self)
        self.QUIT["text"] = "QUIT"
        self.QUIT["fg"]   = "red"
        self.QUIT["command"] =  self.quit

        self.QUIT.pack({"side": "left"})

        self.hi_there = Button(self)
        self.hi_there["text"] = "Hello",
        self.hi_there["command"] = self.say_hi

        self.hi_there.pack({"side": "left"})

    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.pack()
        self.createWidgets()

root = Tk()
app = Application(master=root)
app.mainloop()
root.destroy()       

いずれにせよ、このスタンドアロンスクリプトを実行すると、次のGUIが作成され、ボタンをクリックすると、インタプリタコンソールにhi there, everyone!と、いかにも英語圏の挨拶が表示されるプログラムとなっている。
f:id:memopy:20170608172803p:plain

このサンプルスクリプトは、A Simple Hello World Programという位置付けでありながら、次の点で初心者にとっては非常に難しいものである。

また次のような遠因も輪をかけてこのA Simple Hello World Programを難しくしている。

  • そもそも、tkinterの日本語ドキュメントや文献が少ない
  • 文献によって、手続き型で記述しているものと、オブジェクト指向によって記述しているものがあり、両者を混在させて参考にできない

私もそうであったが、最初は、インターネットや書籍に公開されているスクリプトをコピーして、自分のパソコンで動かすであろう。
そして、そのあと、自分なりにいろいろ変更を加えてみて、GUIがどのように変化するのか学習するものである。
しかし、pythonはご存知のとおり、日本語のドキュメントが少ないし、さらにtkinterはもっと日本語ドキュメントが少ない。そして、python2系と3系では、クラス化の手法がかなり異なるため、文献によっては自分の環境では動作せず行き詰る。
私もこのtkinterは特に行き詰ったため、その点を整理したいと思う。

tkinterのclass(クラス)化とは何か

クラス化しない場合(手続き型)

私は、最初VBAを覚えて、その後にpythonに入ったため、最初はオブジェクト指向(クラス化)をあまり理解していなかった。
クラス化を説明する前に、クラス化を用いないでこのA Simple Hello World Programを作ってみたいと思う。

※python3の場合

import tkinter as tk

# hi_thereボタンが押された時の処理を定義
def say_hi():
    print("hi there, everyone!")

# 最上位の画面(ルートフレーム)の定義
root = tk.Tk()

# ボタンウィジェットの定義
hi_there = tk.Button(root)
hi_there["text"] = "Hello World\n(click me)"
hi_there["command"] = say_hi
hi_there.pack(side="top")
quit = tk.Button(root, text="QUIT", fg="red",
                              command=root.destroy)
quit.pack(side="bottom")

# GUIの実行
root.mainloop()

※python2の場合
インポートするモジュール名を変更するのみ。

import Tkinter as tk

このように、オブジェクト指向を用いないで記述する方法を手続き型で記述するという。
※厳密にいえば、tkinterというウィジェットを使っている時点でオブジェクト指向によるプログラミングであるが、この場合、自分のスタンドアロンスクリプト内でさらにクラス化しないという意味で「手続き型による記述」という。

手続き型で記述したほうが、若干シンプルな構造になったのではないだろうか。

では、手続き型とはどういう意味だろうか。
次の図で解説する。
f:id:memopy:20170608183758p:plain
このように、部品を1から手順を追って記述するため、「手続き型」といわれる。
手続き型言語の代表格は、SQLではないだろうか)

手続き型の記述は、このようなシンプルな画面やアプリを作成するには向いているが、
仮に、このhi_thereボタンを5こ作ることとなったら、このボタンの定義(手続き)を5回やらなければならない。
f:id:memopy:20170608184316p:plain

クラス化する場合(オブジェクト指向

このように、「同じ部品を使い回す場合は」スクリプトが非常に冗長なものになってしまうので、クラス化という手法を用いる。
すなわち、このhi_thereボタンを自分専用のボタンクラスとして定義してしまうのだ。
そうすると、自分専用の定義したものを、あたかも始めからtkinterのウィジェットであったかのような、同じ使い方ができる。
f:id:memopy:20170608190858p:plain

そして、クラス化をするメリットの1つに、クラス化の元となったウィジェットの同じプロパティを定義できる。
例えば、5番目のhi_thereボタンだけ文字を青色にするといった処理も、もともとのボタンウィジェット同様に後から定義できるのだ。
f:id:memopy:20170608191453p:plain

クラス化をした方がよい場合とそうでない場合

このように、クラス化をするメリットは、同じ部品を使い回すことにある。
そのため、このA Simple Hello World Programは、この部品を使い回すのであればクラス化をするメリットがあると思うが、このGUIしか作成しないのであれば、クラス化をしない、すなわち、手続き型で記述した方がシンプルで分かりやすい。
他方、大規模なGUIアプリを開発する場合は、使い回すウィジェットはクラス化をしたほうが、全体を通してスクリプトがシンプルになる。
ただし、クラス化をする場合は、クラス図やクラスのドキュメントをちゃんと記述しないと、ほかの人から見たらチンプンカンプンなスクリプトになるであろう。(このクラスの引数にはなにを入れればいいんだ?といった疑問が当然生じる)


奥が深いクラス化手法。
次回は、クラスの定義と継承について、python2とpython3の違いを踏まえてまとめようと思う。
次の記事
python クラスの定義と継承とは - memopy




別の記事
memopy.hatenadiary.jp