memopy

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

python tkinter クラス継承のサンプルスクリプト

python tkinter クラス継承のサンプルスクリプト

前回に引き続き、tkinterのクラス継承についてまとめている。
今回はサンプルスクリプトを中心に具体的な方法について記述する。

前回の記事
python クラスの定義と継承とは - memopy

Buttonクラスを継承して、デフォルトのボタンを定義する

GUIアプリを開発する場合、同じ形式のウィジェットを使い回すことが多い。
基本的なクラス継承は次のようになる。
※python3で作成(python2で使用する場合は、モジュールをimport Tkinter as tkとする)

import tkinter as tk

class my_button(tk.Button):
    def __init__(self,master=None,cnf={},**kw):
        tk.Button.__init__(self,master,cnf,**kw)

このとき、次のような注意点がある

def __init__(self,master=None,cnf={},**kw)

この第2引数以降には、Buttonウィジェットで定義されている引数を定義する。
他のウィジェットを調べるときは、インタプリタで参照するとすぐにわかる。
f:id:memopy:20170617143449p:plain

また、

tk.Button.__init__(self,master,cnf,**kw)

の引数の参照に注意。master=None,cnf={}としてしまうと、上手に継承できない。

デフォルトのオプションを定義する

デフォルトのオプションを定義する場合は、__init__初期化メソッドに記述すればよい。
configureメソッドは、この場合、my_buttonに対して定義するため、selfに対して定義する。
※下記は、フォント、背景色及び文字色を定義した例

import tkinter as tk

class my_button(tk.Button):
    def __init__(self,master=None,cnf={},**kw):
        tk.Button.__init__(self,master,cnf,**kw)
        self.configure(
            font = ("",14),
            bg = "yellow",
            fg = "red"
            )

# my_buttonを使用
root = tk.Tk()
button1 = my_button(root,text="MyButton1")
button1.pack()
button2 = my_button(root,text="MyButton2")
button2.pack()
root.mainloop()

f:id:memopy:20170617163653p:plain

ただし、上記のスクリプトは1つ注意点がある。
このmy_buttonクラスは、デフォルトとして定義したフォント、背景色及び文字色を変更できない。

# my_buttonを使用
root = tk.Tk()
button1 = my_button(root,text="MyButton1",bg="blue")
button1.pack()
button2 = my_button(root,text="MyButton2")
button2.pack()
root.mainloop()

button1のように、my_buttonクラスで定義したオプションと異なるものを設定しても、これは反映されない。
なぜならば、一度、ボタンの背景色は、bg="blue"で青色に変化したが、その後に、self.configure()メソッドで上書きしたからだ。
このように、my_buttonクラスで定義したオプションを、インスタンス作成時に変更したい場合は、次のように記述する必要がある。

class my_button(tk.Button):
    def __init__(self,master=None,cnf={},**kw):
        tk.Button.__init__(self,master,cnf,**kw)
        self.configure(
            font = ("",14),
            bg = "yellow",
            fg = "red"
            )
        self.configure(**kw)

このように記述すれば、インスタンス作成時に渡されたオプションを、再度、configure()メソッドで設定することができる。

デフォルトのボタンクリック時の動作を定義する

my_buttonクラス内にメソッドを定義することができる。

class my_button(tk.Button):
    def __init__(self,master=None,cnf={},**kw):
        tk.Button.__init__(self,master,cnf,**kw)
        self.configure(
            font = ("",14),
            bg = "yellow",
            fg = "red",
            command = self.callback
            )
        self.configure(**kw)
    def callback(self):
        self["bg"]="black"

上記のスクリプトは、ボタンをクリックしたときに、背景色を黒色に変更するものだ。
f:id:memopy:20170617165659p:plain
この、ボタンクリック時の動作をcallbackメソッドとして定義している。このとき、第1引数にはselfを定義しなければならない。
また、commandオプションから呼び出すときも、self.callback と、self. を付けて呼び出さなければならない点に注意する。

まとめ

このように上手にウィジェットを継承したクラスを作成することによって、効率的にGUIを開発することができるようになる。

話はまったく異なるが、みなさんは戦車の砲台が取り外せることはご存じだろうか。
f:id:memopy:20170617171109j:plain
戦車は、砲台や車輪部分など、モジュール化されており、取り外しができる。
なぜならば、戦車というのは、砲台部分が一番被弾しやすいからだ。というか狙われやすい。そのため、砲台部分が一番壊れやすいのだ。
このように砲台部分がモジュール化されていれば、砲台部分が壊れたとしても、車体部分が無事であったら、砲台部分だけ交換して、戦線に復帰させることができる。

プログラムにおけるクラス化(モジュール化)も同じことである。
クラス化をしておけば、プログラム完成後に機能を修正したい場合でも、その部分(クラス)だけを修正すればよいため、整備性や拡張性に優れることになる。クラス化がされていないと、1から100まで、すべてのスクリプトを見直さなければならないだろう。
ただし、クラス化を用いる設計は、通常の設計に比べて考えなければならないことが多い。事前にきちんとした設計図が必要になるため、設計に係る労力は大きくなる。
トレードオフではあるが、大規模なアプリを開発する場合には、きちんとした設計図を書き、将来的に拡張したい部分などをクラス化(モジュール化)及びパラメータ化すべきだ。