「チェックボックス」ウィジェットは Tkinter のチェックボタンと同じ機能です。チェックボックスは CTkCheckBox() で生成し、ON / OFF のような二者択一の情報を設定するために使います。「ラジオボタン」ウィジェットは CTkRadioButton() で生成し、複数の値からひとつを選ぶ場合に使います。
CTkCheckBox() と CTkRadioButton() はオプション text でボタンの名前を指定します。CTkRadioButton() を使う場合は、選択する値をオプション value で指定し、その値を格納するオブジェクトをオプション variable で指定します。また、オプション command を設定することもできます。この場合、ボタンの値が更新されると指定した関数が実行されます。CTkCheckBox() は値が ON /OFF の 2 通りしかないので、variable には BooleanVar のオブジェクトを指定するといいでしょう。
簡単なプログラムと実行例を示します。
リスト : チェックボタンとラジオボタン
import customtkinter as ctk
root = ctk.CTk()
my_font = ('Noto Sans CJK JP', 14)
opts1 = ctk.BooleanVar()
opts1.set(True)
opts2 = ctk.BooleanVar()
opts2.set(False)
opts3 = ctk.BooleanVar()
opts3.set(True)
action = ctk.IntVar()
action.set(1)
ctk.CTkLabel(root, text ='Check Box', font = my_font).pack(padx = 4, pady = 4)
ctk.CTkCheckBox(root, text = 'option 1', variable = opts1, font = my_font).pack(pady = 4)
ctk.CTkCheckBox(root, text = 'option 2', variable = opts2, font = my_font).pack(pady = 4)
ctk.CTkCheckBox(root, text = 'option 3', variable = opts3, font = my_font).pack(pady = 4)
ctk.CTkLabel(root, text ='Radio Button', font = my_font).pack(padx = 4, pady = 4)
ctk.CTkRadioButton(root, text = 'action A', variable = action, value = 0, font = my_font).pack(pady = 4)
ctk.CTkRadioButton(root, text = 'action B', variable = action, value = 1, font = my_font).pack(pady = 4)
ctk.CTkRadioButton(root, text = 'action C', variable = action, value = 2, font = my_font).pack(pady = 4)
root.mainloop()
チェックボタンとラジオボタン
今度は画像の取り扱いについて説明しましょう。Tk は標準で GIF, PNG, PPM / PGM 形式の画像ファイルを扱うことができます。PNG は Tk 8.6 からサポートされました。PPM はカラー、PGM はグレイスケールの画像を扱う、UNIX で標準的に用いられるベタフォーマットです。
CustomTkinter で画像を表示するには、Pillow (PIL) ライブラリと CustomTkinter 専用の CTkImage クラスを組み合わせて使用するのが標準的です。Pillow は Python で画像処理を行うための最も一般的で強力なライブラリです。
Pillow は pip を使って簡単にインストールすることができます。
(.venv) $ pip install pillow
M.Hiroi の環境では pillow-12.1.1 がインストールされました。
pillow を使うときは from PIL import Image とします。主要な機能として、Image.open() で画像を開き、resize() や save() メソッドで画像を編集・保存できます。
CustomTkinter の場合、クラス CTkImage を使って画像を取り扱います。従来の Tkinter とは異なり、ライト / ダークモードの自動切り替えや、高 DPI ディスプレイでのスケーリング (ボケ防止) に標準で対応している、とのことです。主なオプションを以下に示します。
たとえば、画像ファイルからイメージを作るには、次のように行います。
リスト : 画像の表示
import customtkinter as ctk
from PIL import Image
root = ctk.CTk()
img = Image.open("earth.gif")
my_img = ctk.CTkImage(light_image = img, size = img.size)
ctk.CTkLabel(root, image = my_img).pack()
root.mainloop()
Image.open() は指定した画像ファイルを読み込みます。earth.gif は Python / Tkinter の配布パッケージに含まれている画像ファイルです。なお、このプログラムは earth.gif がカレントディレクトリにあることを仮定しています。ファイルがカレントディレクトリにないとエラーが表示されて動作しません。earth.gif をカレントディレクトリにコピーするか、ファイル名の指定にパスを追加してください。
CustomTkinter で画像を扱うには、読み込んだイメージ img を CTkImage でラップします。これはウィジェットではなく、画像を保持するためのコンテナです。ラベルやボタンにイメージを表示するには、オプション image に CTkImage のオブジェクトをセットします。これでラベルにイメージが表示されます。
画像の表示
それでは簡単な例題として、画像を表示するプログラムを作りましょう。CustomTkinter にはファイルを選択するためのダイアログは用意されていないので、Tkinter のモジュール tkinter.tkFileDialog を使うことにします。いろいろな関数が用意されていますが、次の関数を使うのが一番簡単でしょう。
これらの関数を実行すると、ファイル選択のウィンドウ (ダイアログ) が開かれ、ウィンドウ上の操作でディレクトリをたどり、ファイルを選ぶことができます。使用できるオプションは次の通りです。
このなかで重要なオプションが filetypes です。アプリケーションで扱うことができるファイル種別を拡張子で指定し、そのファイルだけを表示します。指定はリストとタプルで行います。
filetypes = [ファイル種別, ... ] ファイル種別 := (名前, (拡張子, ...))
たとえば、GIF/PPM ファイルを指定する場合は、次のようになります。
filetypes = [('Image Files', ('.gif', '.ppm'))]
この場合は GIF と PPM ファイルが一緒に表示されます。次のように指定すると、表示するファイルをダイアログの操作で切り替えることができます。
filetypes = [('GIF Files', '.gif'),
('PPM Files', '.ppm'),
('ALL Files', '*')]
指定する拡張子がひとつしかない場合はタプルを使う必要はありません。すべてのファイルを表示する場合は * を使います。また、空文字列 '' を指定すると、拡張子のないファイルを表示します。ファイルを選択すると、ファイル名をフルパス形式で返します。選択しない(キャンセル)場合は、空文字列が返されます。
画像ローダーのプログラムは次のようになります。
リスト : 画像ローダー
import customtkinter as ctk
from PIL import Image
from CTkMenuBar import CTkMenuBar, CustomDropdownMenu
import tkinter as tk
import tkinter.filedialog as fd
import sys, os.path
# ファイルの選択
def load_file():
global image_data, path_name
filename = fd.askopenfilename(filetypes = [('Image Files', ('.gif', '.png', '.ppm')),
('GIF Files', '.gif'),
('PNG Files', '.png'),
('PPM Files', '.ppm')],
initialdir = path_name)
if filename != "":
path_name = os.path.dirname(filename)
img = Image.open(filename)
label.configure(image = ctk.CTkImage(light_image = img, size = img.size))
# トップウィンドウ
root = ctk.CTk()
my_font = ('Noto Sans CJK JP', 14)
# グローバル変数
path_name = os.getcwd()
img = Image.new("RGB", (128, 128), (255, 255, 255))
image_data = ctk.CTkImage(light_image = img, size = (128, 128))
# メニュー
m0 = CTkMenuBar(root);
m1 = m0.add_cascade('File', font = my_font)
m2 = CustomDropdownMenu(widget = m1)
m2.add_option(option = "Open", command = load_file)
m2.add_separator()
m2.add_option(option = "Exit", command = root.quit)
# ラベル
label = ctk.CTkLabel(root, image = image_data, text="")
label.pack()
root.mainloop()
メニュー File の下に、ファイルを選択する Open とアプリケーションを終了する Exit の 2 つのメニューを設定します。選択されたファイルのパスはグローバル変数 path_name は格納しておきます。askopenfilename() にこのパスを指定することで、次にファイルを選ぶときは同じディレクトリから始めることができます。画像データはグローバル変数 image_data にセットします。アプリケーションの開始時にはファイルは指定されていないので、Image.new() で空のイメージを作って表示しておきます。
askopenfilename() でファイル名を取得したら、モジュール os.path の関数 dirname() でパスを取り出してグローバル変数 path_name にセットします。dirname() はファイル名からパス部分を取り出して返します。ファイル名をゲットしたら、それが空文字列でないことを確認します。次に、新しいイメージを Image.open() とCTkImage() で生成します。最後に、ラベルの configure() で表示するイメージを変更します。これで選択した画像ファイルを表示することができます。
表示するファイルの選択
GUI アプリケーションの場合、ボタンを押すとかメニューを選ぶといった操作は、基本的にはユーザーが自由に行うことができます。ところが、ある操作をしないと次の処理に進めない場合があります。たとえば、画像を表示する場合、表示するファイル名をユーザーから入力してもらわないと、画像ファイルを表示することはできません。このときによく使われるのが「ダイアログ (dialog)」です。
前回はファイル選択を行う関数 askopenfilename() を使いましたが、このとき表示されたウィンドウがダイアログです。ダイアログは重要なメッセージを表示するために開かれるウィンドウで、画面の前面に表示され、ユーザーがダイアログに応答しないかぎり、そのアプリケーションではほかの操作を行うことはできません。
ユーザーから簡単なデータを入力してもらう場合、CustomTkinter の標準入力ダイアログ CTkInputDialog() を使うと便利です。主なオプションを以下に示します。
基本的な使い方は簡単です。CTkInputDialog() でダイアログを生成し、メソッド get_input() でユーザーが入力を完了 (OK をクリック) するかキャンセルするまでプログラムの実行を待機させます。ユーザーが「Cancel」を押したり、ウィンドウを直接閉じたりした場合、get_input() は None を返します。簡単なプログラムと実行例を示します。
リスト : 入力ダイアログ
import customtkinter as ctk
my_font = ('Noto Sans CJK JP', 16)
def get_input():
# ダイアログの作成と表示
dialog = ctk.CTkInputDialog(text = "数値を入力してください:",
title = "入力ダイアログ", font = my_font)
# 入力を待ち、結果を取得 (OK なら文字列、キャンセルなら None)
input_value = dialog.get_input()
if input_value is not None:
print(f"入力された値: {input_value}")
else:
print("キャンセルされました")
# メインウィンドウの設定
root = ctk.CTk()
button = ctk.CTkButton(root, text="ダイアログを開く", font = my_font, command = get_input)
button.pack(pady=20)
root.mainloop()
メインウィンドウの画像
入力ダイアログの画像
CustomTkinter には標準のメッセージボックスはありませんが、Tkinter のモジュール tkMessageBox を使うと簡単にダイアログを表示することができます。tkMessageBox に用意されている関数を示します。
これらの関数を実行すると、メッセージを表示してユーザーがボタンを押すまで待ちます。オプション title でダイアログのタイトルを指定し、オプション message でダイアログに表示する文字列を指定します。これらの関数は押したボタンの種別を返します。簡単なプログラムを示します。
リスト : tkMessageBox のサンプル
import tkinter as tk
import tkinter.messagebox as msg
root = tk.Tk()
root.option_add('*font', ('Noto Sans CJK JP', 14))
n = tk.IntVar()
n.set(0)
func_table = (msg.showinfo, msg.showwarning, msg.showerror, msg.askquestion, msg.askokcancel,
msg.askyesno, msg.askretrycancel)
name_table = ('showinfo', 'showwarning', 'showerror', 'askquestion',
'askokcancel', 'askyesno', 'askretrycancel')
def message_box():
func = func_table[n.get()]
func(title = 'about', message = 'message box のサンプルです')
for m, name in enumerate(name_table):
tk.Radiobutton(root, text = name, value = m, variable = n).pack(anchor='w')
tk.Button(root, text = "Open message box", command = message_box).pack()
root.mainloop()
メインウィンドウの画像
メッセージボックス showinfo()の画像
メッセージボックス askretrycancel() の画像
なお、外部ライブラリ CTkMessagebox をインストールすると、CustomTkinter にモダンなメッセージボックスを導入することができます。
興味のある方は試してみてください。
今度は図形を表示する「キャンバス (canvas) ウィジェット」を説明します。キャンバスは、矩形、直線、楕円などの図形のほかに、イメージ、文字列、任意のウィジェットを表示することができます。CustomTkinter の場合、公式の専用 Canvas は存在しないようです。CTkCanvas は存在するようですが、CustomTkinter 内部で使うためのラッパークラスのようで、公式ドキュメントに CTkCanvas の記載はありません。図形描画や画像操作が必要な場合は、標準の tkinter.Canvas をそのまま組み合わせて使用するのが一般的なようです。
キャンバスウィジェットは Canvas() で生成します。次のプログラムを実行すると、空のウィンドウが表示されます。
リスト : キャンバスウィジェット import tkinter as tk import customtkinter as tk root = ctk.CTk() c0 = tk.Canvas(root, width = 150, height = 150) c0.pack() root.mainloop()
これで図形を表示するキャンバスをウィンドウに配置したことになります。また、キャンバスとスクロールバー (CTkScrollableFrame など) を組み合わせて、表示範囲を変更することもできます。
キャンバスを配置しただけでは、なにも図形は描かれていません。図形を生成するには下表に示すメソッドを使います。
| create_line() | 直線 (折れ線) |
| create_oval() | 楕円 |
| create_arc() | 円弧 (楕円の円周の一部) |
| create_rectangle() | 矩形 |
| create_polygon() | 多角形 |
| create_image() | イメージ |
| create_bitmap() | ビットマップ |
| create_text() | 文字列 |
| create_window() | 任意のウィジェット |
それでは実際に図形を表示してみましょう。
リスト : 楕円の描画 import tkinter as tk import customtkinter as ctk root = ctk.CTk() c0 = ctk.Canvas(root, width = 150, height = 150) id = c0.create_oval(10, 10, 140, 140) # c0.itemconfigure(id, fill = 'red') c0.pack() root.mainloop()
楕円の描画
ウィンドウに楕円が描画されました。楕円の場合、指定した矩形に内接するように描画されます。図形を描画するメソッドは、図形を表す番号 (ID) を返します。これを使って図形を操作することができます。ウィジェットのメソッド cget() や configure() に対応するのが、itemcget() と itemconfigure() です。たとえば、楕円の中を赤色に塗りつぶしてみましょう。次の 1 行を追加してください。
c0.itemconfigure(id, fill = 'red')
楕円の描画(塗りつぶし)
楕円の中が赤くなりました。よく使われるオプションには次のものがあります。
矩形も楕円と同じ指定方法です。stipple には、Tk に標準で組み込まれているビットマップを指定するのが一般的です。よく使うビットマップが灰色の模様を表す gray12, gray25, gray50, gray75 です。それでは実際に描画してみましょう。
リスト : 矩形の描画 import tkinter as tk import customtkinter as ctk root = ctk.CTk() c0 = tk.Canvas(root, width = 150, height = 150) c0.create_rectangle(10, 10, 140, 140, fill = 'green', stipple = 'gray25') c0.pack() root.mainloop()
矩形の描画
次は直線です。2 点間だけではなく複数の点を指定すると、その間を直線で結びます。では、直線を描画してみましょう。
リスト : 直線の描画 import tkinter as tk import customtkinter as ctk root = ctk.CTk() c0 = tk.Canvas(root, width = 150, height = 150) c0.create_line(10, 10, 140, 10, 10, 140, 140, 140) c0.pack() root.mainloop()
直線の描画
画面に Z 字型の線が描かれましたね。線の色を指定するオプションは、直線の場合は outline ではなくて fill で指定します。fill = 'green' で線を緑色に、width = 2.0 で線を太くしてみましょう。
直線の色と太さを変更
オプション smooth を真 (True) に指定すると、滑らかな曲線を描画することができます。
直線の描画(smooth 指定)
このほかにも、矢印の設定や折り返しのときの形など、いろいろなオプションが用意されています。
次は多角形です。五角形を作ってみましょう。各頂点の座標を指定しますが、最初の点と最後の点が結ばれて閉じた図形となります。
リスト : 多角形の描画 import tkinter as tk import customtkinter as ctk root = ctk.CTk() c0 = tk.Canvas(root, width = 150, height = 150) c0.create_polygon(75, 10, 140, 70, 110, 140, 40, 140, 10, 70) c0.pack() root.mainloop()
polygon では、デフォルトで fill オプションが黒、outline は描画されません。それから、line と同様に smooth を真 (True) に指定すると、多角形の角を丸めます。実際に試してみてください。
多角形の描画
次は円弧です。楕円の円周の一部分を表示します。座標の指定は oval と同じですが、オプションで表示する範囲を指定します。
角度は度数でプラスが反時計回り、マイナスが時計回りとなります。また、oval と同じオプションが使えます。ただし、style が arc の場合、fill で色を指定しても表示されません。chord か pieslice に変更すると表示されます。
キャンバスはイメージとビットマップも表示することができます。
c0.create_image(x, y, オプション, ... ) c0.create_bitmap(x, y, オプション, ... )
x, y は表示する座標を表します。イメージのどの位置に対応させるかは、オプション anchor で指定します。これは pack() と同じ指定方法です。データとの対応は image と bitmap で指定します。たとえば、earth.gif (Python/Tkinter 配布パッケージ内のファイル) を表示するには、次のようにプログラムします。
リスト : イメージの表示 import tkinter as tk import customtkinter as ctk root = ctk.CTk() c0 = tk.Canvas(root, width = 400, height = 300) c0.pack() image_data = tk.PhotoImage(file = 'earth.gif') c0.create_image(200, 150, image = image_data) root.mainloop()
これでキャンバスの中央にイメージが描画されます。
次は文字列です。当然ですがキャンバスに文字を描くことができます。
c0.create_text(x, y, オプション, ...)
x, y は座標で、オプションには次のものが使えます。
それでは実際に試してみましょう。
リスト : テキストの表示
import tkinter as tk
import customtkinter as ctk
root = ctk.CTk()
c0 = tk.Canvas(root, width = 150, height = 150)
c0.create_text(75, 75, text = 'hello, world!', font = ('Noto Sans CJK JP', 14))
c0.pack()
root.mainloop()
テキストの描画
これでウィンドウの中央に hello, world! が表示されます。
キャンバス中にほかのウィジェットを表示させる場合はメソッド create_window() を使います。
たとえば、ラベルを表示させてみましょう。
リスト : ラベルウィジェットの表示
import tkinter as tk
import customtkinter as ctk
root = ctk.CTk()
c0 = tk.Canvas(root, width = 150, height = 150)
a0 = ctk.CTkLabel(root, text = 'hello, world!', bg_color = 'green', font = ('Noto Sans CJK JP', 16))
c0.create_window(75, 75, window = a0)
c0.pack()
root.mainloop()
ラベルの描画
今度は背景色が緑の hello, world! が表示されました。
キャンバスで使用できる図形を一通り説明したところで、図形を操作するときによく使うメソッドを示します。
| type(ID) | 図形の種別を返す |
| bbox(ID, ...) | 指定した図形を囲む領域(矩形)をリストにして返す |
| coords(ID, x0, y0, ...) | 図形の座標の設定や問い合わせ |
| delete(ID, ...) | 図形の削除 |
| move(ID, dx, dt) | 図形の移動 |
| tag_lower(ID1, ID2) | 重なり順を低くする |
| tag_raise(ID1, ID2) | 重なり順を高くする |
| tag_bind(ID, eventsequence, callback) | バインディングの設定 |
ウィジェットと同様に、図形に対してもバインディングを設定することができます。これはタグと一緒に詳しく説明します。
キャンバスで作成した図形にはバインディングを設定することができます。簡単な例題として、作成した矩形をドラッグで移動させてみましょう。次のプログラムを実行してください。
リスト : バインディングの設定 (1)
import tkinter as tk
import customtkinter as ctk
root = ctk.CTk()
c0 = tk.Canvas(root, width = 200, height = 150)
c0.pack()
id = c0.create_rectangle(10, 10, 20, 20, fill = 'brown')
# 移動
def move_rect(event):
x = event.x
y = event.y
c0.coords(id, x - 5, y - 5, x + 5, y + 5)
# バインディング
c0.tag_bind(id, '<Button1-Motion>', move_rect)
root.mainloop()
最初に、一辺の長さが 10 の矩形を作ります。次に、その矩形に対してバインディングを設定します。イベント <B1-Motion> は、左ボタンを押した状態でマウスを動かした場合、つまりドラッグに対応します。メソッド tag_bind() で設定されたコールバック関数には、第 1 引数にイベントクラスのオブジェクトが渡されます。関数 move_rect() では、新しい座標を計算してから、図形の位置を coords() で変更します。
それでは、操作する矩形を 3 つに増やしてみましょう。それぞれの矩形にバインディングを設定してもいいのですが、同じようなプログラムをいくつも書くのは面倒です。このような場合、タグ (tag) を設定すると簡単にプログラムを記述することができます。タグには荷札という意味があり、図形に識別子をつける働きをします。
図形には複数のタグを設定することができます。そして、図形を操作するコマンドは、操作対象となる図形の指定を、番号のほかにもタグを使って行うことができるのです。タグの設定は、図形を生成するときにオプション tags で行います。それでは矩形にタグをセットして 3 つ作ります。
リスト : バインディングの設定 (2)
import tkinter as tk
import customtkinter as ctk
root = ctk.CTk()
c0 = tk.Canvas(root, width = 200, height = 150)
c0.pack()
c0.create_rectangle(10, 10, 20, 20, fill = 'brown', tags = 'brown')
c0.create_rectangle(20, 10, 30, 20, fill = 'brown', tags = 'brown')
c0.create_rectangle(30, 10, 40, 20, fill = 'brown', tags = 'brown')
# 移動
def move_rect(event):
x = event.x
y = event.y
c0.coords('current', x - 5, y - 5, x + 5, y + 5)
# バインディング
c0.tag_bind('brown', '<Button1-Motion>', move_rect)
root.mainloop()
タグは文字列で指定します。今回は brown としました。このタグに対してバインディングを設定するので、図形の番号ではなくタグ brown を指定します。ただし、このままでは関数 move_rect() で操作対象となる矩形がわかりません。この場合、特別なタグ current を使います。
current は Python/Tkinter が設定するタグで、マウスカーソルがある図形上にくると、その図形にタグ current を設定し、その図形からマウスカーソルから出るとタグ current を削除します。つまり、マウスカーソルが指している図形はタグ current で指定することができるのです。これで、複数の矩形をひとつの関数で操作することができます。
このほかにも、タグには図形をまとめて操作することができる、という利点があります。たとえば、矩形の色をまとめて変更する場合は、タグを使って行えばいいのです。
co.itemconfigure('brown', fill = 'green')
これでタグ brown の図形の色を green に変更することができます。削除する場合もタグを使えば簡単です。
co.delete('brown')
これでタグ brown の図形をすべて削除することができます。