コンテンツにスキップ

Top

Tkinter 入門

Python で GUI を作る方法はQtを使ったりいろいろあるけど、とりま簡単なのはTkinter(ティーケーインター)。

ので、ざっくり使い方を述べます。

環境

OS: Ubuntu 18.04
Python: Python 3.6.9
Tkinter: 8.6

インストール済みのPythonモジュールのバージョンは以下のような感じで調べられるよ!。

1
2
$ python3 -c "import tkinter;print(tkinter.TkVersion)"
8.6

Tkinter の ドキュメント

ドキュメント?
正直ちゃんと読んだことない。
https://docs.python.org/ja/3.6/library/tkinter.html

tk と ttk

Tkinter 8.5 以降は tk のテーマ付きウィジェットである ttk を用いれるようになります。

ttkとはtkより見た目がかっちょよくなって、操作もしやすくなるウィジェット郡で、ユーザビリティが向上します。

が、ここでは tk しか説明しません。
(めんどいから)

Tkinter の インストール

aptで入れられる。

1
$ sudo apt install -y python3-tk

とりま動作確認。

以下のコマンドでサンプルプログラムが動けばちゃんとインストールできている。

1
$ python3 -m tkinter

空の画面

とりあえず何もない空の画面を出す。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#!/usr/bin/env python3

import tkinter as tk

class TKSample(tk.Frame):
    def __init__(self, root):
        super().__init__(root)

if __name__ == '__main__':
    app = TKSample(tk.Tk())
    app.mainloop()

これが何もないデフォルトの状態。

タイトルをつける

title というそのままの method があるのでそこにタイトルをぶっこみましょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#!/usr/bin/env python3

import tkinter as tk

class TKSample(tk.Frame):
    def __init__(self, root):
        super().__init__(root)
        self.root = root
        self.root.title("Tkinter Sample")

if __name__ == '__main__':
    app = TKSample(tk.Tk())
    app.mainloop()

タイトルバーにタイトルが表示されました!

・・・が、文字がはみ出ちゃいましたね。

フォントを変えたりする方法もありますが、そもそも小さすぎるので大きさを変えてみましょう。

大きさの変更

geometry というメソッドを使うと大きさを変えられます。
幅、高さをピクセル単位で指定しましょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#!/usr/bin/env python3

import tkinter as tk

class TKSample(tk.Frame):
    def __init__(self, root):
        super().__init__(root)
        self.root = root
        self.root.title("Tkinter Sample")
        self.root.geometry("640x480")

if __name__ == '__main__':
    app = TKSample(tk.Tk())
    app.mainloop()

大きさが 640x480 になりました!

大きさの固定

でもこのままだとマウスを使って画面サイズを変更できてしまいます。
サイズを変更できないようにしたい場合は、 resizable というメソッドを用いて固定にできます。
幅だけ固定、高さだけ固定、もできます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#!/usr/bin/env python3

import tkinter as tk

class TKSample(tk.Frame):
    def __init__(self, root):
        super().__init__(root)
        self.root = root
        self.root.title("Tkinter Sample")
        self.root.geometry("640x480")
        self.root.resizable(width = False, height = False)

if __name__ == '__main__':
    app = TKSample(tk.Tk())
    app.mainloop()

これで画面サイズの変更はできなくなりました。

フルスクリーン

そもそも画面サイズ云々以前にフルスクリーンにしたいんですけど!!

という場合は、geometry の代わりに attributes を使うことで可能になります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#!/usr/bin/env python3

import tkinter as tk

class TKSample(tk.Frame):
    def __init__(self, root):
        super().__init__(root)
        self.root = root
        self.root.title("Tkinter Sample")
        self.root.attributes('-fullscreen', True)

if __name__ == '__main__':
    app = TKSample(tk.Tk())
    app.mainloop()

フルスクリーンになりました!

でも、タイトルバーも消えてしまい、閉じることもできません。
フルスクリーンだけどタイトルバーを残したい、というのはできないようです。
解決策としてはフルサイズになるようなサイズでgeometryするか、自分でタイトルバーを作る、という方法が一般的なようですね。
前者の場合、Ubuntuではうまく全画面になりませんでした。 Ubuntu自身のタイトルバーやツールバーが上に表示されるので。
後者はなんとかなりますが、なぜタイトルバーを自作しなければならないのか・・・。正直馬鹿らしいです。
タイトルバーを残すオプションがあれば済む話なんですが・・・。

ので、とりあえず今は Alt + Tab でコンソールに切り替えて Ctrl + C で殺しましょう。  

Ubuntu の場合は fullscreen ではなく zoomed ?

-fullscreen は Windows にのみ適応できるオプションで、Ubuntuの場合はzoomedを用いないといけない、という話もあったが、少なくとも Tkinter 8.6ではそのようなことはなかった。

attributes のオプションの種類

以下のようなオプションがあります。
が解説は面倒なので省略します。

-alpha
-transparentcolor
-disabled
-fullscreen
-toolwindow
-topmost

ラベル

何もない画面はのっぺらぼうで気持ちが悪いので何か文字を書いてみましょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import tkinter as tk
import tkinter.font as font

class TKSample(tk.Frame):

    def __init__(self, root):
        super().__init__(root)
        self.root = root
        self.root.title("Tkinter Sample")
        self.root.geometry("640x480") 
        self.root.resizable(width = False, height = False)

        self.label_font = font.Font(family="MS ゴシック", size=14, weight="normal")
        self.label_widget = tk.Label(self.root, text="Label Test", fg="white", bg="black", font=self.label_font)
        self.label_widget.pack()

if __name__ == '__main__':
    app = TKSample(tk.Tk())
    app.mainloop()

tkinter.font というのが出てきましたね。
これは文字のフォントを指定するためのものです。そのままですね。
そしてLabel。これが画面に文字を出すのに使われるラベルです。このラベルに先程作ったフォントを設定してあげます。
最後にpack()。これをしないと画面上に配置されません。

画面にラベルやボタンなどのウィジェットを配置する方法は大きく3つあります。

pack と grid と place です。

pack

packは一番シンプルで、呼び出し順に上からどんどんウィジェットを詰めて配置していきます。
ラベルを4つ配置してみましょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import tkinter as tk
import tkinter.font as font

class TKSample(tk.Frame):

    def __init__(self, root):
        super().__init__(root)
        self.root = root
        self.root.title("Tkinter Sample")
        self.root.geometry("640x480") 
        self.root.resizable(width = False, height = False)

        self.label_font = font.Font(family="MS ゴシック", size=14, weight="normal")
        self.label_widget1 = tk.Label(self.root, text="Label Test 1", fg="white", bg="black", font=self.label_font)
        self.label_widget2 = tk.Label(self.root, text="Label Test 2", fg="white", bg="black", font=self.label_font)
        self.label_widget3 = tk.Label(self.root, text="Label Test 3", fg="white", bg="black", font=self.label_font)
        self.label_widget4 = tk.Label(self.root, text="Label Test 4", fg="white", bg="black", font=self.label_font)
        self.label_widget1.pack()
        self.label_widget2.pack()
        self.label_widget3.pack()
        self.label_widget4.pack()

if __name__ == '__main__':
    app = TKSample(tk.Tk())
    app.mainloop()

なんの引数も使わずただ呼べばいいので便利です。
横に詰めていきたい、下から上に詰めていきたい、などのわがままを言う子のために side というパラメーターがあります。
pack(side=tkinter.LEFT) とすると左から詰めていきます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import tkinter as tk
import tkinter.font as font

class TKSample(tk.Frame):

    def __init__(self, root):
        super().__init__(root)
        self.root = root
        self.root.title("Tkinter Sample")
        self.root.geometry("640x480") 
        self.root.resizable(width = False, height = False)

        self.label_font = font.Font(family="MS ゴシック", size=14, weight="normal")
        self.label_widget1 = tk.Label(self.root, text="Label Test 1", fg="white", bg="black", font=self.label_font)
        self.label_widget2 = tk.Label(self.root, text="Label Test 2", fg="white", bg="black", font=self.label_font)
        self.label_widget3 = tk.Label(self.root, text="Label Test 3", fg="white", bg="black", font=self.label_font)
        self.label_widget4 = tk.Label(self.root, text="Label Test 4", fg="white", bg="black", font=self.label_font)
        self.label_widget1.pack(side=tk.LEFT)
        self.label_widget2.pack(side=tk.LEFT)
        self.label_widget3.pack(side=tk.LEFT)
        self.label_widget4.pack(side=tk.LEFT)

if __name__ == '__main__':
    app = TKSample(tk.Tk())
    app.mainloop()

side 意味
tkinter.TOP 上から詰める
tkinter.BOTTOM 下から詰める
tkinter.LEFT 左から詰める
tkinter.RIGHT 右から詰める

あとは anchor で配置を変えたり、 expand で間隔を開けたりできます。

が、便利なので本当にちょっとしたGUIを作る場合はこれでOKですが、ちょっと凝った感じのを作ろうとするとかなり面倒なのが解ると思います。
もし解像度固定でよい業務であれば固定座標でどんどん作るほうが楽だし確実ですが現実にはあんまないでしょう。

grid

gridは名前からわかるようにグリッドに配置をします。

グリッド(表)の行と列の番地を指定して配置する、といった感じです。
(最初に表を作る、といったような作業はなく、いきなり番地指定であとは勝手になんかしてくれます)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import tkinter as tk
import tkinter.font as font

class TKSample(tk.Frame):

    def __init__(self, root):
        super().__init__(root)
        self.root = root
        self.root.title("Tkinter Sample")
        self.root.geometry("640x480") 
        self.root.resizable(width = False, height = False)

        self.label_font = font.Font(family="MS ゴシック", size=14, weight="normal")
        self.label_widget1 = tk.Label(self.root, text="Label Test 1", fg="white", bg="black", font=self.label_font)
        self.label_widget2 = tk.Label(self.root, text="Label Test 2", fg="white", bg="black", font=self.label_font)
        self.label_widget3 = tk.Label(self.root, text="Label Test 3", fg="white", bg="black", font=self.label_font)
        self.label_widget4 = tk.Label(self.root, text="Label Test 4", fg="white", bg="black", font=self.label_font)
        self.label_widget1.grid(column=0,row=0)
        self.label_widget2.grid(column=1,row=0)
        self.label_widget3.grid(column=0,row=1)
        self.label_widget4.grid(column=1,row=1)

if __name__ == '__main__':
    app = TKSample(tk.Tk())
    app.mainloop()

エクセルのセルの結合のようにグリッドのセルを結合するような形にもできます。
その場合は columnspan や rowspan を用います。エクセルというよりもHTMLの表といった感じですね。
ので、うまくセルを結合させながらいい感じのGUIを作っていくことが可能になります。

place

place は任意の座標に配置する方法ですね。
解像度固定とかの仕事であればこれが一番です。
ガチガチに座標決め打ちで作るので一番わかり易いし、微調整も完璧にできます。

勘違いしないでほしいのは、比率でも指定できる点です。
x,yと座標でガチガチに指定する方法もあれば、relx,relyと相対位置で指定する方法もあります。
ただまぁここでは(x,y)のガチガチ固定の例しか示しませんが。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import tkinter as tk
import tkinter.font as font

class TKSample(tk.Frame):

    def __init__(self, root):
        super().__init__(root)
        self.root = root
        self.root.title("Tkinter Sample")
        self.root.geometry("640x480") 
        self.root.resizable(width = False, height = False)

        self.label_font = font.Font(family="MS ゴシック", size=14, weight="normal")
        self.label_widget1 = tk.Label(self.root, text="Label Test 1", fg="white", bg="black", font=self.label_font)
        self.label_widget2 = tk.Label(self.root, text="Label Test 2", fg="white", bg="black", font=self.label_font)
        self.label_widget3 = tk.Label(self.root, text="Label Test 3", fg="white", bg="black", font=self.label_font)
        self.label_widget4 = tk.Label(self.root, text="Label Test 4", fg="white", bg="black", font=self.label_font)
        self.label_widget1.place(x=100, y=150)
        self.label_widget2.place(x=490, y=150)
        self.label_widget3.place(x=100, y=330)
        self.label_widget4.place(x=490, y=330)

if __name__ == '__main__':
    app = TKSample(tk.Tk())
    app.mainloop()

ボタン

ボタンをクリックしたら関数が呼ばれてLabel上の文字が書き換わるコードです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import tkinter as tk
import tkinter.font as font

class TKSample(tk.Frame):

    def __init__(self, root):
        super().__init__(root)
        self.root = root
        self.root.title("Tkinter Sample")
        self.root.geometry("320x240") 
        self.root.resizable(width = False, height = False)
        self.font_value = font.Font(family="MS ゴシック", size=14, weight="normal")

        self.label_text = tk.StringVar()
        self.label_text.set("ラベル")
        self.label_widget = tk.Label(self.root, textvariable=self.label_text, fg="white", bg="black", font=self.font_value)
        self.label_widget.pack()

        self.button_widget = tk.Button(self.root, text='ボタン', font=self.font_value, command=self.change_button)
        self.button_widget.pack()

    def change_button(self):
        self.label_text.set("ラベル変更でござる")

if __name__ == '__main__':
    app = TKSample(tk.Tk())
    app.mainloop()

ボタンがクリックされたときに呼ばれる関数はcommandというパラメーターで設定します。
呼び出された関数の中でラベルのテキストを変更しています。

なお、ラベルのテキストを動的に変えたい場合、ここのソースのような小細工が必要となります。
まずは変数を用意して、textではなくtextvariableに設定してあげる必要があります。

1
2
3
        self.label_text = tk.StringVar()
        self.label_text.set("ラベル")
        self.label_widget = tk.Label(self.root, textvariable=self.label_text, fg="white", bg="black", font=self.font_value)

値を変えたい場合も、setを使わないといけません。

1
        self.label_text.set("ラベル")

要は、

1
        self.label_text.text = "ラベル"

のように代入することができないのです。
以外に間違えます。まぁ慣れですが、久しぶりだとほぼ間違いなく間違えてググることになります。

んで、ボタン押したときに引数で値を渡したいな、と思うケースもあるでしょう。
その場合は、 partial を使うことで実現できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import tkinter as tk
import tkinter.font as font

from functools import partial

class TKSample(tk.Frame):

    def __init__(self, root):
        super().__init__(root)
        self.root = root
        self.root.title("Tkinter Sample")
        self.root.geometry("320x240") 
        self.root.resizable(width = False, height = False)
        self.font_value = font.Font(family="MS ゴシック", size=14, weight="normal")

        self.label_text = tk.StringVar()
        self.label_text.set("ラベル")
        self.label_widget = tk.Label(self.root, textvariable=self.label_text, fg="white", bg="black", font=self.font_value)
        self.label_widget.pack()

        self.button_widget = tk.Button(self.root, text='ボタン', font=self.font_value, command=partial(self.change_button, 1))
        self.button_widget.pack()

    def change_button(self, num):
        self.label_text.set("ラベル変更でござる" + str(num))

if __name__ == '__main__':
    app = TKSample(tk.Tk())
    app.mainloop()

これで引数の値がちゃんと渡されました!

もし

1
NameError: name 'partial' is not defined

というエラーが出るようでしたら、

1
from functools import partial

がちゃんと追加されているか確認しましょう。

ボタンの大きさの変更

ボタンの幅と高さ、width と height がピクセル単位じゃないのおかしい!

と思いませんでしたか。私も思っています。

どうも昔のTkinterはピクセル単位だったのですが、Tkinter 8.6 では、なぜかフォントのサイズをもとにサイズを作るという変な感じになってしまいました。
いろんなプラットフォームで問題なく動くように、という配慮なんでしょうけど正直良い迷惑です。
(実際、同じプログラムをRaspberry Piに持っていったら、見栄えが変だったので意味ないじゃーん!という感じだし)

仕方ないので頑張ってピクセル指定する方法にフレームを使う方法があるのですが、それに関してはフレームについて説明したあとで記述します。

ラジオボタン

ラジオボタンは以下のような感じ。
同じ変数を使うことによって黒丸の移動が勝手に制御されます。
違う変数を使うとだめです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import tkinter as tk
import tkinter.font as font

from functools import partial

class TKSample(tk.Frame):

    def __init__(self, root):
        super().__init__(root)
        self.root = root
        self.root.title("Tkinter Sample")
        self.root.geometry("320x240") 
        self.root.resizable(width = False, height = False)
        self.font_value = font.Font(family="MS ゴシック", size=14, weight="normal")

        self.radio_value   = tk.IntVar()
        self.radio_value.set(0)
        self.radio_button1 = tk.Radiobutton(self.root, text='りんご', value=0, variable=self.radio_value)
        self.radio_button2 = tk.Radiobutton(self.root, text='みかん', value=1, variable=self.radio_value)
        self.radio_button3 = tk.Radiobutton(self.root, text='ぶどう', value=2, variable=self.radio_value)
        self.radio_button4 = tk.Radiobutton(self.root, text='すいか', value=3, variable=self.radio_value)
        self.radio_button5 = tk.Radiobutton(self.root, text='メロン', value=4, variable=self.radio_value)
        self.radio_button1.pack()
        self.radio_button2.pack()
        self.radio_button3.pack()
        self.radio_button4.pack()
        self.radio_button5.pack()

if __name__ == '__main__':
    app = TKSample(tk.Tk())
    app.mainloop()

値を取得するには変数をgetすれば良いです。

1
        value = self.radio_value.get()

かんたん!

ラジオボタンの丸の大きさは変えられない。

なんか、ラジオボタンの丸、小さくね?と思ったので大きくしたいと思ったのだけど残念ながらそのようなオプションはない。

なんか頑張って画像を差し替えたりしてる人もいるみたいだけど、めんどそう。単に大きくしたいだけなんだからオプションでちょろっとやってくれよ、と思った。

テキストボックス

テキストボックスはEntryで作れる。
めんどくさくなってきたので割愛。

フレーム

いままではroot上にダイレクトにラベルやらボタンやらを配置しました。

まぁ別にこれでも良いのですが、フレームを作ってその上に配置することで、フレーム単位で操作ができるようになります。
例えばラジオボタンが5個ある画面があったとして、x座標の位置を変えたいと思った場合、5個のラジオボタンすべてを変更する必要があり面倒です。
これをフレームをまず作り、その上にラジオボタンを5つ載せたのであれば、フレームのx座標を変えるだけですべてのラジオボタンが移動します。

と、まぁ、なくてもGUI作れるけど、ふつう使うよね、的な機能です。

ではフレームを作ってその上に、ボタンやラベルを配置しましょう。
いままではself.root上にボタンを配置しましたが、self.frame上に変更になります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import tkinter as tk
import tkinter.font as font

from functools import partial

class TKSample(tk.Frame):

    def __init__(self, root):
        super().__init__(root)
        self.root = root
        self.root.title("Tkinter Sample")
        self.root.geometry("320x240") 
        self.root.resizable(width = False, height = False)
        self.font_value = font.Font(family="MS ゴシック", size=14, weight="normal")

        self.frame = tk.Frame(self.root, width=self.root.winfo_screenwidth(), height=self.root.winfo_screenheight())
        self.frame.pack()
        self.frame1.pack_propagate(False)

        self.label_text = tk.StringVar()
        self.label_text.set("ラベル")
        self.label_widget = tk.Label(self.frame, textvariable=self.label_text, fg="white", bg="black", font=self.font_value)
        self.label_widget.pack()

        self.button_widget = tk.Button(self.frame, text='ボタン', font=self.font_value, command=partial(self.change_button, 1))
        self.button_widget.pack()

    def change_button(self, num):
        self.label_text.set("ラベル変更でござる" + str(num))

if __name__ == '__main__':
    app = TKSample(tk.Tk())
    app.mainloop()

・・・見た目なにも変わらないですね。まぁそりゃそうですが。

フレームに、 pack_propagate という見慣れないコードがありますが、

1
         self.frame1.pack_propagate(False)

これを入れないとフレームの大きさが、あとからpackされたやつ(ここで言うところのラベルやボタン)の大きさに合わさってしまうのです。
フレームの大きさが今どのぐらいかをわかるためにbackgroudの色を赤にしてみましょう。

1
        self.frame = tk.Frame(self.root, width=self.root.winfo_screenwidth(), height=self.root.winfo_screenheight(), bg="red")

全画面に赤が広がっていますね。
フレームが指定したサイズ分しっかり広がっていることがわかります。

では、 self.frame1.pack_propagate(False) をコメントアウトしてもういちどやってみましょう。

上に乗っけたウィジェットのサイズ(ここではボタンが一番幅があるのでボタンに合わさりましたね)に変わってしまいました。
このようにならないためにpack_propagateが必要となります。
ちなみにpackのときはpack_propagateですが、gridのときはgrid_propagateになるので注意が必要です。
でもplace_propagateはありません。placeのときもgrid_propagateです。クソです。

ちなみにというか当然でもありますが、フレームの中にフレームを入れること(というかフレームの上にフレームを乗せること)もできます。
要はフレームの入れ子ですね。

フレームを使った、ボタンのピクセル単位での指定

フレームは幅高さをピクセル単位で設定できます。

そのため、特定の大きさのフレームを作り、そのフレームいっぱいにボタンを表示すれば、結果として、幅高さをピクセルで指定したボタンが完成するのです。

ボタンの数だけフレームが必要でギギギ(はだしのゲン風)となりますが仕方ないです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import tkinter as tk
import tkinter.font as font

from functools import partial

class TKSample(tk.Frame):

    def __init__(self, root):
        super().__init__(root)
        self.root = root
        self.root.title("Tkinter Sample")
        self.root.geometry("320x240") 
        self.root.resizable(width = False, height = False)
        self.font_value = font.Font(family="MS ゴシック", size=14, weight="normal")

        self.frame = tk.Frame(self.root, width=self.root.winfo_screenwidth(), height=self.root.winfo_screenheight())
        self.frame.pack()
        self.frame1.pack_propagate(False)

        self.label_text = tk.StringVar()
        self.label_text.set("ラベル")
        self.label_widget = tk.Label(self.frame, textvariable=self.label_text, fg="white", bg="black", font=self.font_value)
        self.label_widget.pack()

        self.button_frame  = tk.Frame(self.frame1, width=160, height=120)
        self.button_frame.pack()
        self.button_frame.pack_propagate(False)
        self.button_widget = tk.Button(self.button_frame, text='ボタン', font=self.font_value, command=partial(self.change_button, 1))
        self.button_widget.pack(expand=True, fill=tk.BOTH)


    def change_button(self, num):
        self.label_text.set("ラベル変更でござる" + str(num))

if __name__ == '__main__':
    app = TKSample(tk.Tk())
    app.mainloop()

ちゃんとbutton_ferameで指定したピクセルサイズになっています。

フレームを使った画面遷移

Tkinter で今まで作ってきた画面は1画面でした。

2画面作って画面遷移するにはどうしたら良いでしょうか?

いろいろありますが、一番かんたんなのは2つフレームを作って切り替えることです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import tkinter as tk
import tkinter.font as font

from functools import partial

class TKSample(tk.Frame):

    def __init__(self, root):
        super().__init__(root)
        self.root = root
        self.root.title("Tkinter Sample")
        self.root.geometry("320x240") 
        self.root.resizable(width = False, height = False)
        self.font_value = font.Font(family="MS ゴシック", size=21, weight="normal")

        # 画面A
        self.frame_a = tk.Frame(self.root, width=self.root.winfo_screenwidth(), height=self.root.winfo_screenheight())
        self.frame_a.place(x=0,y=0)
        self.frame_a.grid_propagate(False)

        self.label_a_widget = tk.Label(self.frame_a, text="画面A", fg="black", font=self.font_value)
        self.label_a_widget.pack()

        self.button_a_widget = tk.Button(self.frame_a, text='ボタンA', font=self.font_value, command=partial(self.screen_transition, 'A'))
        self.button_a_widget.pack()

        # 画面B
        self.frame_b = tk.Frame(self.root, width=self.root.winfo_screenwidth(), height=self.root.winfo_screenheight())
        self.frame_b.place(x=0,y=0)
        self.frame_b.grid_propagate(False)

        self.label_b_widget = tk.Label(self.frame_b, text="画面B", fg="black", font=self.font_value)
        self.label_b_widget.pack()

        self.button_b_widget = tk.Button(self.frame_b, text='ボタンB', font=self.font_value, command=partial(self.screen_transition, 'B'))
        self.button_b_widget.pack()

        # 画面Aからスタート
        self.frame_a.place(x=0, y=0)
        self.frame_b.place(x=self.root.winfo_screenwidth(), y=0)
        self.frame_a.tkraise()

    # 画面を切り替える関数
    def screen_transition(self, screen_id):
        if screen_id == 'A':
            self.frame_a.place(x=self.root.winfo_screenwidth(), y=0)
            self.frame_b.place(x=0, y=0)
            self.frame_b.tkraise()
        else:
            self.frame_a.place(x=0, y=0)
            self.frame_b.place(x=self.root.winfo_screenwidth(), y=0)
            self.frame_a.tkraise()

if __name__ == '__main__':
    app = TKSample(tk.Tk())
    app.mainloop()

ボタンを押したらちゃんと切り替わりますね!!

ちなみにですが、コード見ればわかりますが、フレームを枠外に移動させているだけなので、実際には存在しています。

言い換えると画面を切り替えても裏でずっと生きています。

メモリ的にも処理的にも裏で生きていたら困る!というのであれば処理を殺したり別の方法で画面遷移する必要があります。

以上、Tkinter入門でした!

プログレスバーの変数を更新したのに、画面が更新されない

for ループ内で0.1秒ごとにプログレスバーの変数の値を変えたらプログレスバーがびよーんと動くかと思ったら動かなかった。

どうも、関数を出るまで更新されないみたい。

それは普通に困るので調べたところ、

1
self.root.update_idletasks()

を呼び出すと画面が更新されるとのこと。やったね!