【Hello World編 最終回】クラスにまとめよう!

本日の課題

本日はHellowWorld編 最終回ということで、

今までのコードをクラスというものにまとめたいと思います。

前回のコード

from tkinter import *
import tkinter.ttk as ttk

#アプリケーションを終了する applicationにはTKのインスタンスを指定する
def exit(application):
    application.destroy()

# ラベルの色を変更する 各オプションには[オプション]でアクセスする colorは16進数表記
def changeColor(targetLabel,color):
    targetLabel["foreground"]=color



if __name__ == '__main__':
    #Tkインスタンスを作成し、app変数に格納する
    app  = Tk()
    #縦幅400横幅300に画面サイズを変更します。
    app.geometry("400x300")
    #タイトルを指定
    app.title("Hello World Program")
    #フレームを作成する
    frame = ttk.Frame(app)
    frame.pack()
    #ラベル作成
    label = ttk.Label(frame,text="Hello World")
    label.pack()
    #ボタン作成
    exitButton = ttk.Button(frame,text="exit",command=lambda:exit(app))
    exitButton.pack()
    #赤色に変更
    redButton = ttk.Button(frame,text="red",command=lambda:changeColor(label,"#ff0000"))
    redButton.pack()
    #緑色に変更
    greenButton = ttk.Button(frame,text="green",command=lambda:changeColor(label,"#00ff00"))
    greenButton.pack()
    #青色に変更
    blueButton = ttk.Button(frame,text="blue",command=lambda:changeColor(label,"#0000ff"))
    blueButton.pack()
    #黒色に変更
    blackButton = ttk.Button(frame,text="black",command=lambda:changeColor(label,"#000000"))
    blackButton.pack()
    #格納したTkインスタンスのmainloopで画面を起こす
    app.mainloop()

 

開発環境

前回と同じ環境を使用します。

・Python3.x(僕は3.6を使用しています)

・Windows10(7でもおそらく問題ないです)

実践

新しいファイルを作成しよう!

前回のファイルはそのまま残しておいて、

今回はHelloWorld_Final.pyというファイル名で新しく作成していきたいと思います。

※その3までは同じファイルを複製して使用していましたが今回は新しく作成します。

Tkinterが使用できるようにライブラリをimportしよう!

HelloWorld_Final.pyに下記コードを追加してください。

from tkinter import *
import tkinter.ttk as ttk

tkinterを使用するためにはtkinterライブラリと、見た目をよくするためにtkinter.ttkをimportします。

クラスを作成しよう

先ほどのimport文のあとに下記コードを追加しましょう。

class HelloWorldApp(ttk.Frame):

    def __init__(self, app):
        super().__init__(app)
        self.pack()

これは前回のコードに例えると下記のコードです。

#フレームを作成する
frame = ttk.Frame(app)
frame.pack()

クラスは先頭にclassと書きます。

class クラス名(継承するクラス): です。

継承するクラスにttk.Frameを与えることで、ttk.Frameと同じように機能する(引き継ぐ)ことができます。

def __init__(self, app):

__init__関数はこのFrameを作成したときに最初に呼ばれる関数です。

例えばttk.Frame()としたときに即座にttk.Frameの__init__が実行されます。

selfは自身のクラスを表します。self.pack()はHelloWorldApp.pack()ということになります。

またselfの名前は自由に決めることができますが、強いこだわりがない限りselfで統一するのが無難です。

super().__init__(app)

super()というのは継承したクラスです。

ここでのsuper().__init__(app)はttk.Frame(app)と同じことを行っています。(appを親として与える)

現段階で、ttk.FrameとHelloWorldAppとの違いはpackをクラス内で行うか、使うときに命令するかの違いだけです。

HelloWorldApp

frame = HelloWorldApp(app)

ttk.Frame

frame = ttk.Frame(app)
frame.pack()

__init__にpackを記述することでオブジェクトを作成したときにpackを書く必要がなくなりました。

ではHelloWorldAppを実際に使用してみましょう。

ファイルの最後に下記コードを追加しましょう。

if __name__ == '__main__':
    #Tkインスタンスを作成し、app変数に格納する
    app  = Tk()
    #縦幅400横幅300に画面サイズを変更します。
    app.geometry("400x300")
    #タイトルを指定
    app.title("Hello World Program")
    # HelloWorldAppをインスタンス化する
    frame = HelloWorldApp(app)
    # 格納したTkインスタンスのmainloopで画面を起こす
    app.mainloop()

 

追加後のコード

from tkinter import *
import tkinter.ttk as ttk

class HelloWorldApp(ttk.Frame):

    def __init__(self, app):
        super().__init__(app)
        self.pack()



if __name__ == '__main__':
    #Tkインスタンスを作成し、app変数に格納する
    app  = Tk()
    #縦幅400横幅300に画面サイズを変更します。
    app.geometry("400x300")
    #タイトルを指定
    app.title("Hello World Program")
    # #フレームを作成する
    frame = HelloWorldApp(app)
    # 格納したTkインスタンスのmainloopで画面を起こす
    app.mainloop()

 

HelloWorld編その2の フレームを作成しよう と同じ動作をすることを確認してください。

class

ラベルをHelloWorldAppに追加しよう

ではラベルを追加する処理をクラス内に追加しましょう

self.pack()の後に下記コードを追加してください。

label = ttk.Label(self,text="HelloWorld")
label.pack()

前回のコードと比べると

#ラベル作成
label = ttk.Label(frame,text="Hello World")
label.pack()

と同じです。

前回ではframeであったものがselfに代わっています。

ですがselfはHelloWorldApp(ttk.Frame)を表すので結果的に同じことをしています。

追加後のコード

from tkinter import *
import tkinter.ttk as ttk

class HelloWorldApp(ttk.Frame):

    def __init__(self, app):
        super().__init__(app)
        self.pack()
        label = ttk.Label(self,text="HelloWorld")
        label.pack()


if __name__ == '__main__':
    #Tkインスタンスを作成し、app変数に格納する
    app  = Tk()
    #縦幅400横幅300に画面サイズを変更します。
    app.geometry("400x300")
    #タイトルを指定
    app.title("Hello World Program")
    # #フレームを作成する
    frame = HelloWorldApp(app)
    # 格納したTkインスタンスのmainloopで画面を起こす
    app.mainloop()

では実行してみましょう。

helloworld

HelloWorldは画面上に出力されていれば成功です。

 

ボタンの作成を関数化!

前回ではexit、red、green、blue、black 5つのボタンを作成しました。

このボタンexitを除き実は色の指定とボタン名以外すべて同じことを行っています。

#赤色に変更
redButton = ttk.Button(frame,text="red",command=lambda:changeColor(label,"#ff0000"))
redButton.pack()
#緑色に変更
greenButton = ttk.Button(frame,text="green",command=lambda:changeColor(label,"#00ff00"))
greenButton.pack()
#青色に変更
blueButton = ttk.Button(frame,text="blue",command=lambda:changeColor(label,"#0000ff"))
blueButton.pack()
#黒色に変更
blackButton = ttk.Button(frame,text="black",command=lambda:changeColor(label,"#000000"))
blackButton.pack()

まずラベル名の色を変更する関数をクラスに追加しましょう

def changeColor(self,targetLabel,color):
        targetLabel["foreground"]=color

クラス内の関数は引数の最初に自身を表すselfを記述します。

HelloWorldAppのchangeColorという意味になります。

もしselfを記載し忘れた場合、第一引数が自身という扱いになるので、

この例ではtargetLabelがHelloWorldAppという扱いになり、その後の処理が正しく動作しない、引数エラーになる可能性があります。

色ボタンを作成する関数を作成しよう

まずボタンを作成する関数を作成するための引数を考えます。

色ボタン毎に違うものは、

・ボタン名

・色

です。

ではこれらを引数に与えればあとは同じものを使っても問題なさそうですね。

下記コードが引数に、ボタン名と色を指定する関数です。

def ChangeColorButton(self,buttonname,color,label):
    button = ttk.Button(self,text= buttonname, command=lambda:self.changeColor(label,color))
    button.pack()

お気づきでしょうか。ボタン名、色の他に引数にlabelが追加してあります。(selfはもう大丈夫ですね)

labelを引数に追加した理由は、

ボタンが押されたときに、どのラベルの文字色を変更するか の情報を与えるためです。

ではred、green、blue、blackボタンを上記の関数で作成してみましょう。

下記コードをlabel.pack()の下に追加しましょう。

#赤
self.ChangeColorButton("red","#ff0000",label)
#緑
self.ChangeColorButton("green","#00ff00",label)
#青
self.ChangeColorButton("blue","#0000ff",label)
#黒
self.ChangeColorButton("black","#000000",label)

追加後のコード

from tkinter import *
import tkinter.ttk as ttk

class HelloWorldApp(ttk.Frame):

    def __init__(self, app):
        super().__init__(app)
        self.pack()
        label = ttk.Label(self,text="HelloWorld")
        label.pack()
        #赤
        self.ChangeColorButton("red","#ff0000",label)
        #緑
        self.ChangeColorButton("green","#00ff00",label)
        #青
        self.ChangeColorButton("blue","#0000ff",label)
        #黒
        self.ChangeColorButton("black","#000000",label)

    #色ボタン作成
    def ChangeColorButton(self,buttonname,color,label):
        button = ttk.Button(self,text= buttonname, command=lambda:self.changeColor(label,color))
        button.pack()
    # targetLabelの文字色をcolorに変更する
    def changeColor(self,targetLabel,color):
        targetLabel["foreground"]=color

if __name__ == '__main__':
    #Tkインスタンスを作成し、app変数に格納する
    app  = Tk()
    #縦幅400横幅300に画面サイズを変更します。
    app.geometry("400x300")
    #タイトルを指定
    app.title("Hello World Program")
    # #フレームを作成する
    frame = HelloWorldApp(app)
    # 格納したTkインスタンスのmainloopで画面を起こす
    app.mainloop()

それでは実行してみましょう。

colorbutton colorbutton_blue

 

 

 

 

 

 

 

正しく起動されボタンを押したときにラベルの色が変更されれば成功です!

色ボタン作成処理をもっとスマートに!

先ほどの色ボタン作成をもっとスマートに作成させてみましょう。

前回の関数はそのまま残しておいてください。

今回は色と、文字色の辞書を作成し、辞書に登録してある色全てのボタンを作成する関数を作成しましょう。

辞書(map)はPythonではdict型といいます。

ではまず色の辞書作成から

colorDict ={"red":"#ff0000",
                "green":"#00ff00",
                "blue":"#0000ff",
                "black":"#000000"}

辞書は”{}”で作成できます。

各要素は”,”で区切り、”要素名:値”で表します。

新しくdictに対応した色ボタン作成関数を作成しましょう。

関数は引数に辞書とラベルを指定するよう作成します。

dictはdict.get(要素名)で値を取得でき、dict.keys()で要素名全てが入った配列を取得できます。

下記関数を追加してください。

def ChangeColorButtonDictVer(self,dict,label):
        keys = dict.keys()
        for key in keys:
            color  = dict.get(key)
            self.ChangeColorButton(key,color,label)

dict.keys()で要素名をすべて取得し、その配列をループを回します。

そして要素に対応した色を取得し、最初作成した関数を呼び出すということをしています。

なんだがプログラミングをしている感が出てきたのではないでしょうか。

追加後のコード(先ほどの各色のボタン作成は削除してあります)

from tkinter import *
import tkinter.ttk as ttk

class HelloWorldApp(ttk.Frame):

    def __init__(self, app):
        super().__init__(app)
        self.pack()
        label = ttk.Label(self,text="HelloWorld")
        label.pack()
        colorDict ={"red":"#ff0000",
                "green":"#00ff00",
                "blue":"#0000ff",
                "black":"#000000"}
        # 色ボタン作成
        self.ChangeColorButtonDictVer(colorDict,label)

    #色ボタン作成
    def ChangeColorButton(self,buttonname,color,label):
        button = ttk.Button(self,text= buttonname, command=lambda:self.changeColor(label,color))
        button.pack()
    #辞書に登録された色の数だけボタン作成
    def ChangeColorButtonDictVer(self,dict,label):
        keys = dict.keys()
        for key in keys:
            color  = dict.get(key)
            self.ChangeColorButton(key,color,label)

    # targetLabelの文字色をcolorに変更する
    def changeColor(self,targetLabel,color):
        targetLabel["foreground"]=color

if __name__ == '__main__':
    #Tkインスタンスを作成し、app変数に格納する
    app  = Tk()
    #縦幅400横幅300に画面サイズを変更します。
    app.geometry("400x300")
    #タイトルを指定
    app.title("Hello World Program")
    # #フレームを作成する
    frame = HelloWorldApp(app)
    # 格納したTkインスタンスのmainloopで画面を起こす
    app.mainloop()

かなりスッキリしたのではないでしょうか。

では実行してみましょう。

colorbutton

無事表示されていれば成功です!!

もちろんcolorDictに新たに要素を追加すれば新しいボタンが作成されます。

ここでHelloWorld編 最終回の講義は終了です。

お疲れ様でした。

 

まとめ

今回はクラス化と、関数化をメインに行いソースをきれいにしました。

今回はあまりクラスの恩恵をあまり感じることはできなかったかもしれませんが、

大きなプロジェクトになると、前回のように書き続けることはとてもしんどいものとなります。

できるだけ共通化し、効率よく組むことを意識することがプログラミングにとっては重要です。

HelloWorld編はいかがでしたでしょうか。初心者向けになっていましたでしょうか?

Tkinterは参考資料も少なく手探りになりがちですがこのブログが少しでも助けになれば幸いです。

 

 

あわせて読みたい