Tkinterで、簡易電卓を作るシリーズ③見た目を整える

Twitterでシェア FaceBookでシェア はてなブックマークでシェア

Python - Tkinter
2018年11月22日21:14に更新(約21日前)
2018年10月6日22:01に作成(約68日前)

旧ブログからの移行記事です。

概要

Tkinterで、簡易電卓を作るシリーズの1つです。 見た目を整えていきます。

ttkとは

Tk8.5で、Tk のテーマ付きウィジェットであるttkが導入されました。 今まで from tkinter import ttk としていましたが、このttkでアクセスできるのが上記のテーマ付きウィジェットです。 これらのウィジェットを使うことでルック&フィールが今風になり、テーマやスタイルといった機能を利用することで見た目を便利にカスタマイズすることができます。

ttkサンプル

まず、以下のようなシンプルなプログラムを作ります。

from tkinter import *
from tkinter import ttk

root = Tk()

frame = ttk.Frame(root)
frame.grid(column=0, row=0, sticky=(N, S, E, W))

# 1行目
ttk.Label(frame, text='姓').grid(column=0, row=0)
ttk.Entry(frame).grid(column=1, row=0)

# 2行目
ttk.Label(frame, text='名').grid(column=0, row=1)
ttk.Entry(frame).grid(column=1, row=1)

# 3行目
ttk.Label(frame, text='国').grid(column=0, row=2)
country_combo = ttk.Combobox(frame)
country_combo['values'] = ('日本', 'アメリカ', '中国', 'イギリス', 'ロシア')
country_combo['state'] = 'readonly'
country_combo.current(0)
country_combo.grid(column=1, row=2)

# frame内の全てのウィジェットに下記padding
for child in frame.winfo_children():
    child.grid_configure(padx=5, pady=5)

root.mainloop()

これを実行すると、以下のような画面です。
姓、名と国の入力欄がある

これにテーマを設定していきましょう。その前に、現在のテーマは以下のように確認できます。

style = ttk.Style()
current_theme = style.theme_use()
print(current_theme)

現在のテーマはdefaultのようです。

default

利用可能な全てのテーマを取得してみましょう。

style = ttk.Style()
all_theme = style.theme_names()
print(all_theme)

実行結果。これは各プラットフォームによって、増えたり減ったりします。

('clam', 'alt', 'default', 'classic')

サンプルプログラムに、テーマを設定してみましょう。 以下のように追記し、classicテーマを利用してみます。

style = ttk.Style()
style.theme_use('classic')

root.mainloop()

すると、以下のようになりました。
classicテーマを使った

前に比べると、ちょっと違いますね。サンプルプログラムがシンプルなせいで、ちょっと実感が少なめです。

試しに、サンプルプログラムを拡張し、テーマを自在に切り替えるようにしましょう。

from tkinter import *
from tkinter import ttk


def change_theme(event):
    """テーマを変更する."""
    selected_theme = combo.get()
    style.theme_use(selected_theme)


root = Tk()

# 全てのテーマ
style = ttk.Style()
all_themes = style.theme_names()

frame = ttk.Frame(root)
frame.grid(column=0, row=0, sticky=(N, S, E, W))

# 1行目
ttk.Label(frame, text='姓').grid(column=0, row=0)
ttk.Entry(frame).grid(column=1, row=0)

# 2行目
ttk.Label(frame, text='名').grid(column=0, row=1)
ttk.Entry(frame).grid(column=1, row=1)

# 3行目
ttk.Label(frame, text='テーマ').grid(column=0, row=2)
combo = ttk.Combobox(frame)
combo['values'] = all_themes
combo['state'] = 'readonly'
combo.grid(column=1, row=2)
combo.bind('<<ComboboxSelected>>', change_theme)

# frame内の全てのウィジェットに下記padding
for child in frame.winfo_children():
    child.grid_configure(padx=5, pady=5)

root.mainloop()

ちゃんと変化していますね。
defaultテーマ
clamテーマ
altテーマ
classicテーマ

①で、以下のようなことを書きました。

やろうと思えば、Tk()で作成したトップレベルのウィジェットに直接ボタンを配置することもできます。 実際、ちょっとした小物ならこれで十分なこともあります。 しかし、このトップレベルのウィジェットはttkのテーマ、スタイル機能をサポートしていません。 なので、ある程度キレイな見た目にしようと思うと、ttk.Frame などにボタンなどを配置することになります。 これがどういう影響があるかは、③で少し触れます。

Tk()で作られるトップレベルウィジェットがテーマをサポートしないというのは、以下のようにすると分かりやすいです。

#frame = ttk.Frame(root)
#frame.grid(column=0, row=0, sticky=(N, S, E, W))
frame = root

各ウィジェットをttk.Frameではなくroot = Tk()に直接配置しました。

この状態で起動し、テーマを変えてみましょう。本来clamはフレーム自体の色なんかも少しだけ変えるのですが、frame=root にすると色が変わりません。
rootに直接おいたdefaultテーマ
rootに直接おいたclamテーマ

テーマだけでなく、後述するスタイルを使うことも当然できません。 基本的には、ttk.Frameを介してウィジェットを配置していくのがベストでしょう。

Style

テーマはユーザーインターフェイス全体の外観を制御しますが、個別のウィジェットの見た目を変更することはできません。そのような場合は、Styleを利用します。

次のサンプルプログラムは、デフォルトのラベルをカスタマイズしている例です。 'TLabel'は、ttk.Labelのデフォルトのスタイルで、それを上書きしている形になります。

from tkinter import *
from tkinter import ttk

root = Tk()

# スタイルの作成
style = ttk.Style()
style.configure(
    'TLabel', font=('Helvetica', 20),
    background='black', foreground='white'
)

frame = ttk.Frame(root)
frame.grid(column=0, row=0, sticky=(N, S, E, W))

# 1行目
ttk.Label(frame, text='姓').grid(column=0, row=0)
ttk.Entry(frame).grid(column=1, row=0)

# 2行目
ttk.Label(frame, text='名').grid(column=0, row=1)
ttk.Entry(frame).grid(column=1, row=1)

# frame内の全てのウィジェットに下記padding
for child in frame.winfo_children():
    child.grid_configure(padx=5, pady=5)

root.mainloop()

ちゃんと全てのラベルが変更されました。
Styleを使ってラベルをカスタマイズ

自分で独自のスタイルを定義することもできます。

# スタイルの作成
style = ttk.Style()
style.configure(
    'BW.TLabel', font=('Helvetica', 20),  # BlackWhite.TLabel の略
    background='black', foreground='white'
)
...
...

# style=で、定義したものを設定
ttk.Label(frame, text='姓', style='BW.TLabel').grid(column=0, row=0)
ttk.Entry(frame).grid(column=1, row=0)

実行すると、以下のようになります。設定したラベルだけかわっていますね。
設定したラベルだけが変化する

電卓アプリケーションにStyleを適用

前回作ったプログラムにスタイルを設定しましょう。

from tkinter import *
from tkinter import ttk


# 2次元配列のとおりに、gridでレイアウトを作成する
LAYOUT = [
    ['7', '8', '9', '/'],
    ['4', '5', '6', '*'],
    ['1', '2', '3', '-'],
    ['0', 'C', '=', '+'],
]


class CalcApp(ttk.Frame):
    """電卓アプリ."""

    def __init__(self, master=None):
        super().__init__(master)
        self.create_style()
        self.create_widgets()

    def create_style(self):
        """ボタン、ラベルのスタイルを変更."""
        style = ttk.Style()

        # ラベルのスタイルを上書き
        style.configure(
            'TLabel', font=('Helvetica', 20),
            background='black', foreground='white',
        )
        # ボタンのスタイルを上書き
        style.configure('TButton', font=('Helvetica', 20))

    def create_widgets(self):
        """ウィジェットの作成."""
        self.display_var = StringVar()
        self.display_var.set('0')  # 初期値を0にする

        dispay_label = ttk.Label(self, textvariable=self.display_var)
        dispay_label.grid(column=0, row=0, columnspan=4, sticky=(N, S, E, W))

        # レイアウトの作成
        for y, row in enumerate(LAYOUT, 1):
            for x, char in enumerate(row):
                button = ttk.Button(self, text=char)
                button.grid(column=x, row=y, sticky=(N, S, E, W))
                button.bind('<Button-1>', self.calc)
        self.grid(column=0, row=0, sticky=(N, S, E, W))

        # 横の引き伸ばし設定
        self.columnconfigure(0, weight=1)
        self.columnconfigure(1, weight=1)
        self.columnconfigure(2, weight=1)
        self.columnconfigure(3, weight=1)

        # 縦の引き伸ばし設定。0番目の結果表示欄だけ、元の大きさのまま
        self.rowconfigure(0, weight=0)
        self.rowconfigure(1, weight=1)
        self.rowconfigure(2, weight=1)
        self.rowconfigure(3, weight=1)
        self.rowconfigure(4, weight=1)

        # ウィンドウ自体の引き伸ばし設定
        self.master.columnconfigure(0, weight=1)
        self.master.rowconfigure(0, weight=1)

    def calc(self, event):
        char = event.widget['text']
        self.display_var.set(char)


def main():
    root = Tk()
    root.title('簡単電卓')
    CalcApp(root)
    root.mainloop()


if __name__ == '__main__':
    main()

変更箇所は、以下の部分です。create_styleメソッドを定義し、__init__内で呼び出すようにしました。

    def __init__(self, master=None):
        super().__init__(master)
        self.create_style()
        self.create_widgets()

    def create_style(self):
        """ボタン、ラベルのスタイルを変更."""
        style = ttk.Style()

        # ラベルのスタイルを上書き
        style.configure(
            'TLabel', font=('Helvetica', 20),
            background='black', foreground='white',
        )
        # ボタンのスタイルを上書き
        style.configure('TButton', font=('Helvetica', 20))

見た目は以下のようになります。
完成した見た目

Twitterでシェア FaceBookでシェア はてなブックマークでシェア

記事にコメントする