LoginSignup
8
17

【執筆中】【Python/GUI】Tkinterのチュートリアル

Last updated at Posted at 2023-10-04

はじめに

PythonのGUIライブラリの一種である「Tkinter」について解説しています。
皆さんも一緒にTkinterを使いこなして、パイソニスターに近づいていきましょう。

環境

私が動かした環境は次の通り。

  • Windows11 Home
  • Python 3.9.9

Tkinterって何ぞや?

Pythonで扱うことのできるGUIライブラリの一種です。最大の特徴は、Pythonさえインストールすればデフォルトで入っており、環境構築をする必要が無いところです。

そういう意味では、最も手軽に使用できるGUIライブラリとも言えますね。

ただ弱点もありまして

  • デザインツールがほとんどない(「Page」や「Tkinter Designer」くらいしか無い)
  • 近代的なデザインに対応していない(最近はCustomTkinterというオシャレなGUIがある)

という点が挙げられます。ですが、ゆるっゆるなライセンスで、色々なUIが作れるという点で私個人としては最強のGUIライブラリだと思っています。

ウィンドウ

とにかくココから。
GUIのすべての基礎となるウィンドウの解説になります。

ウィンドウの表示

ただウィンドウだけを表示してみましょう。

このプログラムはフォーマットのようなものです。
すぐに書けるようにメモなり、ブックマークなりしておくと良いです。

# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成と、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウのループ処理
window.mainloop()

このプログラムを実行すると、以下のようにウィンドウだけが表示されます。

000.png

タイトルを設定する

ウィンドウにタイトルを設定してみます。
ウィンドウにタイトルを設定するには以下のメソッドを使用します。

ウィンドウにタイトルを設定する
<Tkinterオブジェクト>.title("<タイトル名>")

それでは、ウィンドウタイトルを設定してみましょう。

# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成と、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウにタイトルを設定
window.title("Tkinterを使ってみましょう!!")

# ウィンドウのループ処理
window.mainloop()

プログラムを実行すると、ウィンドウ最上部に設定したタイトルが表示されます。

001.png

ウィンドウのサイズと位置を設定する

ウィンドウのサイズと位置を設定してみます。
ウィンドウにサイズと位置を設定するには、次のメソッドを使用します。

ウィンドウのサイズと位置を設定する
<Tkinterオブジェクト>.geometry("<横幅>x<高さ>+<X座標>+<Y座標>")

それでは、サイズと位置を設定してみましょう。

# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成と、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウのサイズと位置を設定
window.geometry("500x400+500+300")

# ウィンドウのループ処理
window.mainloop()

画像では分かりにくいですが、指定した座標とサイズでウィンドウが出てきます。

002.png

変数を使って window.geometry(f"{width}x{height}+{xPos}+{yPos}") のように設定することもできます。

変数を使った方が見やすいと思います。

# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成と、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウのサイズと位置を設定
width   = 500   # 横幅
height  = 400   # 高さ
xPos    = 500   # X座標
yPos    = 300   # Y座標
window.geometry(f"{width}x{height}+{xPos}+{yPos}")

# ウィンドウのループ処理
window.mainloop()

ウィンドウのサイズだけ設定する

ウィンドウのサイズだけを設定することもできます。
サイズだけを設定するのにも geometry メソッドを使用し、サイズだけのところを設定すればOKです。

ウィンドウのサイズだけを設定する
<Tkinterオブジェクト>.geometry("<横幅>x<高さ>)

それでは、ウィンドウのサイズだけを設定してみましょう。

# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウのサイズだけを設定
width   = 500   # 横幅
height  = 100   # 高さ
window.geometry(f"{width}x{height}")

# ウィンドウのループ処理
window.mainloop()

これを実行するとプログラム通り、横500x縦100の大きさでウィンドウを表示できましたね。

003.png

ウィンドウの位置だけ設定する

あまり需要は無いと思いますが、ウィンドウの位置だけを設定することもできます。
位置だけを設定するのにも geometry メソッドを使用し、位置だけのところを設定すればOKです。

ただ、『+』から始まることに注意してください。

ウィンドウの位置だけを設定する
<Tkinterオブジェクト>.geometry("+<X座標>+<Y座標>")

それでは、ウィンドウの位置だけを設定してみましょう。

# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウの座標だけを設定
xPos = 1000 # X座標
yPos = 700  # Y座標
window.geometry(f"+{xPos}+{yPos}")

# ウィンドウのループ処理
window.mainloop()

画像だけでは分かりにくいですが、しっかりと設定した位置でウィンドウが起動されていますね。

004.png

ウィンドウのサイズ変更を制限する

ウィンドウのサイズ変更に制限を加えることもできます。
resizable メソッドを使うことで、サイズ変更をできなくしたり、特定の方向のみサイズ変更できるようになります。

ウィンドウのサイズの変更制限
<Tkinterオブジェクト>.resizable(<横幅の変更制限>, <高さの変更制限>)

resizable の引数には True または False を指定します。

  • True ならサイズ変更ができる
  • False ならサイズ変更ができない

試しに、横幅と高さの両方をサイズ変更できないようにしてみます。

# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウのサイズ変更を制限する
window.resizable(False, False)

# ウィンドウのループ処理
window.mainloop()

resizable メソッドによってサイズ変更に制限をかけると、最大化の操作が出来なくなっています。
もちろん、ウィンドウの端をドラッグしてサイズ変更することもできなくなります。

005.png

ウィンドウの最大・最小サイズを制限する

ウィンドウの変更できるサイズの最大値と最小値を設定することができます。
最大サイズを設定するには maxsize メソッド、最小サイズを設定するには minsize メソッドを使用します。

ウィンドウの最大・最小サイズを設定する
# ウィンドウの最大サイズを設定
<Tkinterオブジェクト>.maxsize(<横幅>, <高さ>)

# ウィンドウの最小サイズを設定
<Tkinterオブジェクト>.minsize(<横幅>, <高さ>)

画面サイズを最大500x500px、最小300x300pxに制限してみたいと思います。

# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウのサイズ変更を制限する
window.maxsize(500, 500)    # 最大サイズ
window.minsize(300, 300)    # 最小サイズ

# ウィンドウのループ処理
window.mainloop()

このプログラムを実行してみると画面サイズが設定したところで変更できなくなるようになります。
画像では分かりにくいですが、以下のようにマウスで変更できるサイズに制限を与えることができました。

006.png

ウィンドウの色を設定する

ウィンドウの背景色を設定してみます。
背景色を設定するには config メソッドを使用します。

背景色の設定
<Tkinterオブジェクト>.config(bg="<カラーコード>")

この config メソッドは多彩な機能があります。
ここでは背景色の設定だけに焦点を当てて説明します。

背景色を設定するには config メソッドに背景色(backgroundのbg)を指定する引数 bg があります。その bg にカラーコードを指定すればウィンドウの背景色を設定できます。

# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウの背景色を設定
window.config(bg="#FFDDDD")

# ウィンドウのループ処理
window.mainloop()

ちゃんと指定したカラーコードで背景色が設定できたかと思います。

007_0.png

ウィンドウを透明度を設定する

需要は無いかな・・・と思いますが、一応載せときます。
ウィンドウの透明度を変更するには attributes メソッドを使用します。

ウィンドウを透明度の設定
<tkinterオブジェクト>.attributes("-alpha", <透明度>)

<透明度> は 0 ~ 1 の範囲で指定します。
それでは、ウィンドウの透明度を設定してみたいと思います。

# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウの透明度を設定
window.attributes("-alpha", 0.7)

# ウィンドウのループ処理
window.mainloop()

以下のようにウィンドウが透過して背景が見えるようになります。

007_1.png

アイコンを設定する

ウィンドウ左上にあるアイコンを設定してみたいと思います。
デフォルトでは以下のように羽のアイコンとなっております。

008.png

このアイコンを「フリーアイコンSVG、PNG、ICO、ICNS」というフリー画像サイトの「App Essentials による Design Shots (16 アイコン)」ページにある以下のアイコンに変更してみます。

multimedia_player_sound_audio_music_note_musical_note_icon_256744.png

アイコンを変更するには iconbitmap メソッドを使用します。

ウィンドウのアイコンを設定する
<Tkinterオブジェクト>.iconbitmap("<アイコンのパス>")

アイコンに指定できるのは、拡張子が「ico」になっているもののみになります。
PNGやJPEGは指定できません。

それでは、アイコンを変更してみます。

# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウのアイコンを設定
window.iconbitmap(r"note_icon.ico")

# ウィンドウのループ処理
window.mainloop()

左上のアイコンが変更されているのが分かりますね。

009.png

ウィンドウのサイズと座標を取得する

現在のウィンドウのサイズと座標を取得することができます。
ウィンドウのサイズと座標を取得するには winfo_geometry メソッドを使用します。

<Tkinterオブジェクト>.winfo_geometry()

現時点ではウィンドウのサイズと座標を取得するメリットを感じにくいと思います。ですが、2つ目のウィンドウを起動するときにウィンドウ同士を重ねたくない場合や、ウィンドウに表示させる画像などのサイズに応じてウィンドウサイズを変更させるような場合に力を発揮してくれます。

それでは、ウィンドウのサイズと座標を取得してみましょう。

# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウサイズの設定
width  = 300
height = 100
xPos = 400
yPos = 400
window.geometry(f"{width}x{height}+{xPos}+{yPos}")

# ウィンドウのサイズと座標を出力
window.update_idletasks() # mainloopの前に設定値の更新
print("ウィンドウのサイズと座標:", window.winfo_geometry())

# ウィンドウのループ処理
window.mainloop()

010.png

ここで update_idletasks について軽く説明します。
まず、update_idletasks を入れないとどうなるのか見てみましょう。

update_idletasksが無いとどうなる?
# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウサイズの設定
width  = 300
height = 100
xPos = 400
yPos = 400
window.geometry(f"{width}x{height}+{xPos}+{yPos}")

# ウィンドウのサイズと座標を出力
print("ウィンドウのサイズと座標:", window.winfo_geometry())

# ウィンドウのループ処理
window.mainloop()

すると、期待したウィンドウサイズを正しく出力されなくなってしまいます。

011.png

Tkinter では mainloop メソッドにて設定値を更新するため、このような現象が起こってしまいます。
そこで、mainloop メソッドに入る前に設定値を更新すれば期待した値を取得することができます。

ウィンドウのサイズだけ取得する

現在のウィンドウのサイズだけを取得することもできます。
ウィンドウのサイズを取得するには winfo_width メソッドや winfo_height メソッドを使用します。

# ウィンドウの横幅を取得する
<Tkinterオブジェクト>.winfo_width()

# ウィンドウの高さを取得する
<Tkinterオブジェクト>.winfo_height()

では、ウィンドウサイズを取得してみたいと思います。

# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウサイズの設定
width  = 300
height = 100
window.geometry(f"{width}x{height}")

# ウィンドウサイズを出力
window.update_idletasks() # mainloopの前に設定値の更新
print("ウィンドウの横幅:", window.winfo_width())
print("ウィンドウの高さ:", window.winfo_height())

# ウィンドウのループ処理
window.mainloop()

このプログラムを実行するとプロンプトにウィンドウサイズが出力されます。

012.png

ウィンドウの座標だけを取得する

現在のウィンドウの座標を取得することもできます。
ウィンドウの座標はどこを基準にするのかによって取得できる値が変わってきますが、座標を取得するメソッドは4つだけです。

ウィンドウの座標だけを取得
# ウィンドウを基準にしたX座標を取得する
<Tkinterオブジェクト>.winfo_x()

# ウィンドウを基準にしたY座標を取得する
<Tkinterオブジェクト>.winfo_y()

# ディスプレイを基準にしたX座標を取得する
<Tkinterオブジェクト>.winfo_rootx()

# ディスプレイを基準にしたY座標を取得する
<Tkinterオブジェクト>.winfo_rooty()

「ウィンドウを基準」「ディスプレイを基準」がいまいちピンとこないと思いますが、画像で違いを比較すると分かりやすいです。

013.png

文字通り、ウィンドウの左上を基準にするのか、ディスプレイの左上を基準にするのか、その違いだけです。

それでは、ウィンドウの座標を取得してみたいと思います。

# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ウィンドウサイズの設定
width  = 500
height = 300
xPos = 400
yPos = 400
window.geometry(f"{width}x{height}+{xPos}+{yPos}")

# ウィンドウのサイズと座標を出力
window.update_idletasks()
print("親ウィンドウのx座標:", window.winfo_x())
print("親ウィンドウのy座標:", window.winfo_y())
print("ディスプレイ上のx座標:", window.winfo_rootx())
print("ディスプレイ上のy座標:", window.winfo_rooty())

# ウィンドウのループ処理
window.mainloop()

実行するとウィンドウの座標を取得することができます。

014.png

ウィジェットの配置

ボタンやラベルなどのウィジェットの説明に入る前に、先にウィジェットの配置方法について説明します。

ウィジェットの説明をするために、以下にベースとなるプログラムを用意しました。
このプログラムを用いてウィジェットの配置を説明していきます。

今はボタンについては分からなくても全然問題ありません。

# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ボタンオブジェクトの生成と取得(今は分からなくてもOK)
btn1 = tkinter.Button(window, text="【 1 】")
btn2 = tkinter.Button(window, text="【 2 】")
btn3 = tkinter.Button(window, text="【 3 】")

# ウィンドウのループ処理
window.mainloop()

ちなみに、上記のプログラムを動かしてもウィンドウが表示されるだけで、ボタンは表示されません。
ボタンオブジェクトを生成しただけで、ウィンドウに配置していないからです。

100.png

ウィジェットって何ですか?

ウィジェットとはボタンやスクロールバー、画像といったウィンドウ上に表示される機能を持ったものや情報を伝えるものです。
説明するより見た方が速いと思いますので、図解をご覧ください。

101.png

pack メソッド

pack メソッドでウィジェットを配置することができます。

<widgetオブジェクト>.pack()

pack メソッドでは、ウィジェット同士を重ねることなく順番に配置してくれます。
レイアウトの自由度は低いですが、簡単にウィジェットを配置することができます。

pack メソッドを使ってボタンを配置してみたいと思います。

# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ボタンオブジェクトの生成と取得(今は分からなくてもOK)
btn1 = tkinter.Button(window, text="【 1 】")
btn2 = tkinter.Button(window, text="【 2 】")
btn3 = tkinter.Button(window, text="【 3 】")

# packメソッドでウィンドウにボタンを配置
btn1.pack()
btn2.pack()
btn3.pack()

# ウィンドウのループ処理
window.mainloop()

実行すると上から順にボタンが配置されます。

102.png

side オプション

pack メソッドでのウィジェットの配置をある程度制御することもできます。
その1つの方法が side オプションになります。

<widgetオブジェクト>.pack(anchor=tkinter.<配置場所>)

<配置場所>に指定できるオプション一覧
    tkinter.LEFT   : 左から配置
    tkinter.TOP    : 上から配置
    tkinter.RIGHT  : 右から配置
    tkinter.BOTTOM : 下から配置

side オプションをすれば配置する方向性を制御できるようになります。
以下に全方位に2個ずつボタンを配置するプログラムを用意しました。

# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ボタンオブジェクトの生成と取得(今は分からなくてもOK)
btn1_left = tkinter.Button(window, text="【1:左】")
btn2_left = tkinter.Button(window, text="【2:左】")

btn1_right = tkinter.Button(window, text="【1:右】")
btn2_right = tkinter.Button(window, text="【2:右】")

btn1_top = tkinter.Button(window, text="【1:上】")
btn2_top = tkinter.Button(window, text="【2:上】")

btn1_bottom = tkinter.Button(window, text="【1:下】")
btn2_bottom = tkinter.Button(window, text="【2:下】")

# packメソッドでウィンドウにボタンを配置
btn1_left.pack(side=tkinter.LEFT)
btn2_left.pack(side=tkinter.LEFT)

btn1_right.pack(side=tkinter.RIGHT)
btn2_right.pack(side=tkinter.RIGHT)

btn1_top.pack(side=tkinter.TOP)
btn2_top.pack(side=tkinter.TOP)

btn1_bottom.pack(side=tkinter.BOTTOM)
btn2_bottom.pack(side=tkinter.BOTTOM)

# ウィンドウのループ処理
window.mainloop()

このプログラムを実行すれば上下左右にボタンが配置されました。

103.png

ただし pack メソッドでウィジェットを配置する注意点が1つあり、

pack メソッドは配置する順番を考えないとぐちゃぐちゃなウィジェットの配置になってしまう。

と言うことです。

どういうことか? なのですが、先ほどのプログラムの pack メソッドによるウィジェットの配置する順番だけ変えてみました。

# coding: shift-jis

# tkinterライブラリの読み込み
import tkinter

# ウィンドウの作成、Tkinterオブジェクトの取得
window = tkinter.Tk()

# ボタンオブジェクトの生成と取得(今は分からなくてもOK)
btn1_left = tkinter.Button(window, text="【1:左】")
btn2_left = tkinter.Button(window, text="【2:左】")

btn1_right = tkinter.Button(window, text="【1:右】")
btn2_right = tkinter.Button(window, text="【2:右】")

btn1_top = tkinter.Button(window, text="【1:上】")
btn2_top = tkinter.Button(window, text="【2:上】")

btn1_bottom = tkinter.Button(window, text="【1:下】")
btn2_bottom = tkinter.Button(window, text="【2:下】")

# packメソッドでウィンドウにボタンを配置(ここの順番をぐちゃぐちゃにしてみた)
btn1_top.pack(side=tkinter.TOP)
btn1_left.pack(side=tkinter.LEFT)
btn2_top.pack(side=tkinter.TOP)
btn1_right.pack(side=tkinter.RIGHT)
btn2_left.pack(side=tkinter.LEFT)
btn2_bottom.pack(side=tkinter.BOTTOM)
btn2_right.pack(side=tkinter.RIGHT)
btn1_bottom.pack(side=tkinter.BOTTOM)

# ウィンドウのループ処理
window.mainloop()

これを実行すると・・・

104.png

かなり見た目が汚くなりましたね。

どうしてこんな配置になってしまうのか?

実は pack メソッドでウィジェットを配置すると、そのウィジェットの横幅または縦幅が独占されてしまうためです。

文字で説明しても分かりにくいですが、先ほどのプログラムの pack で配置している処理を見ながら図で確認と分かりやすいです。

まず、pack で配置している部分のみのプログラムを再度載せます。

# packメソッドでウィンドウにボタンを配置(ここの順番をぐちゃぐちゃにしてみた)
btn1_top.pack(side=tkinter.TOP)
btn1_left.pack(side=tkinter.LEFT)
btn2_top.pack(side=tkinter.TOP)
btn1_right.pack(side=tkinter.RIGHT)
btn2_left.pack(side=tkinter.LEFT)
btn2_bottom.pack(side=tkinter.BOTTOM)
btn2_right.pack(side=tkinter.RIGHT)
btn1_bottom.pack(side=tkinter.BOTTOM)

最初の btn1_top.pack(side=tkinter.TOP) によって、ウィンドウの最上部にボタンが配置されます。この時、ボタンの横幅いっぱいを独占してしまいます。

105_0.png

続いて btn1_left.pack(side=tkinter.LEFT) で、ウィンドウの最左部にボタンが配置され、同じようにボタンの縦幅いっぱいを独占してしまいます。

ですが、既に pack メソッドで独占された領域を、後から独占することができなくなります。

105_1.png

同じ理屈で btn2_top.pack(side=tkinter.TOP) で、ウィンドウの最上部にボタンを配置しますが、独占済みの領域は独占できません。

105_2.png

同様に btn1_right.pack(side=tkinter.RIGHT) で最右部にボタン配置しますが、独占済みの領域は独占できません。

105_3.png

お気づきの方もいるかもしれませんが、pack によって独占される領域には以下の法則があります。

  • tkinter.TOP または tkinter.BOTTOM で縦方向で配置したウィジェットは、横幅いっぱいの領域を独占
  • tkinter.LEFT または tkinter.RIGHT で横方向で配置したウィジェットは、縦幅いっぱいの領域を独占

そこで pack メソッドでウィジェットを配置する際には、

配置する方向性に規則を持たせる

を意識するとキレイに配置できるようになります。

  • 横方向から配置する際には「左から右に」または「右から左に」
  • 縦方向から配置する際には「上から下に」または「下から上に」
  • 縦横組み合わせて配置するには「上下を先に、左右を後に」または「左右を先に、上下を後に」

と言うことですね。

anchor オプション

執筆中

ラベル

執筆中

8
17
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
17