CUSTOMTKINTER GUIDE VOL.3
- CustomTkinterでオリジナルのメッセージボックス(確認・警告・エラー)を自作する
- こんな人におすすめ
- CustomTkinterにmessageboxが無い理由(tkinter.messageboxとの違い)
- 完成イメージとダウンロード
- ベースとなるダイアログを作る(CTkToplevelでモーダルウィンドウを表示する)
- 種類ごとのアイコンを自作する(info・warning・errorをどう視覚化するか)
- なぜモーダル化が必要なのか(grab_set()のUX的な役割)
- 親ウィンドウの中央に表示する(座標計算)
- 「はい/いいえ」の確認ダイアログと戻り値の受け取り方
- 完成版:どこからでも呼び出せる関数にまとめる(show_info() / show_warning() / show_error() / ask_yes_no())
- 動作確認チェックリスト
- よくある質問
CustomTkinterでオリジナルのメッセージボックス(確認・警告・エラー)を自作する
こんにちは。ニンジンです🥕
これまで、CustomTkinterの基本の使い方、File・表示・設定・Helpのメニューバー自作と紹介してきました。今回はその第3弾として、「保存が完了しました」「このファイルを削除しますか?」のような、いわゆるメッセージボックス(ダイアログ)を自作します。
標準のTkinterにはtkinter.messagebox.showinfo()のような便利な関数があり、CustomTkinterのアプリからそのまま呼び出すこと自体はできます。ただし、このダイアログはOS標準の見た目で描画されるため、CustomTkinterのテーマ(ライト/ダーク、角丸フラットデザイン)には一切追従しません。せっかくアプリ本体をモダンなデザインにまとめても、確認ダイアログだけWindows標準の古めかしい見た目になってしまいます。
この記事では、CTkToplevelを使ってテーマに馴染むメッセージボックスを自作する方法を、5段階のサンプルコードとダウンロード付きで解説します。
この記事で分かること
- CustomTkinterにテーマ対応のmessageboxが無い理由(
tkinter.messageboxとの違い) CTkToplevelを使ったベースとなるダイアログウィンドウの作り方- 「情報(info)」「警告(warning)」「エラー(error)」の3種類を切り替えて表示する実装
- ダイアログを閉じるまで裏画面を操作させない「モーダル化」(
grab_set())の仕組み - 「はい/いいえ」を選ばせる確認ダイアログと、戻り値(bool)を呼び出し元で受け取る方法
- 自分のアプリにそのまま組み込める再利用可能な関数(ダウンロード可能)
こんな人におすすめ
- CustomTkinterでツールを作っていて、保存完了・削除確認などのダイアログを追加したい人
tkinter.messageboxを使ってみたものの、見た目がアプリ本体と合わずに気になっていた人grab_set()の役割がよく分からず、なんとなくコピペで使っていた人
CustomTkinterにmessageboxが無い理由(tkinter.messageboxとの違い)
Tkinter標準のtkinter.messageboxモジュールを使えば、次のように1行で確認ダイアログを表示できます。
from tkinter import messagebox
messagebox.showinfo("お知らせ", "保存が完了しました。")手軽で便利な反面、このmessageboxはCustomTkinterが提供する部品ではなく、あくまでTkinterの標準機能です。そのため、見た目はOSのネイティブダイアログとしてそのまま描画され、CustomTkinterの角丸フラットデザインにも、set_appearance_mode("dark")によるダークモード切り替えにも、一切追従しません。前回のメニューバー自作の記事で扱ったtk.Menuとまったく同じお悩みポイントですね。
CustomTkinterのテーマに馴染むダイアログを表示したい場合、サブウィンドウを表すCTkToplevelを使って自分で組み立てる必要があります。この記事で作るのは、まさにその「CustomTkinterのテーマに馴染む自作メッセージボックス」です。
完成イメージとダウンロード
先に完成形の動きを説明すると、次のような機能を持つダイアログになります。
- 「情報」「警告」「エラー」の3種類を、色付きの丸バッジアイコンで見分けられる
- ダイアログを開いている間は、裏の画面(親ウィンドウ)をクリックしても反応しない(モーダル化)
- 親ウィンドウの中央に自動的に表示される
- 「はい/いいえ」で選ばせる確認ダイアログを出し、選ばれた結果を呼び出し元のコードでそのまま
if文で使える
この記事では、この動きを5段階のサンプルコードに分けて、1つずつ機能を追加しながら組み立てていきます。各段階のsample.pyは以下からまとめてダウンロードできます。
📦 サンプルコード一式をダウンロード(customtkinter-messagebox-samples.zip)ベースとなるダイアログを作る(CTkToplevelでモーダルウィンドウを表示する)
sample1_base_dialog.py(※ VS Codeなどのエディターで実行してください)
まずは、アイコンの出し分けもモーダル化もせず、「メッセージとOKボタンだけの最小構成のダイアログ」を作ります。
import customtkinter as ctk
class SimpleDialog(ctk.CTkToplevel):
def __init__(self, parent, message: str):
super().__init__(parent)
self.title("お知らせ")
self.geometry("320x150")
# メッセージ本文
label = ctk.CTkLabel(
self,
text=message,
font=ctk.CTkFont(size=14),
wraplength=260,
justify="left",
)
label.pack(padx=20, pady=(24, 12), fill="x")
# OKボタン(押したら閉じるだけ)
ok_button = ctk.CTkButton(self, text="OK", command=self.destroy)
ok_button.pack(pady=(0, 20))
# Windows環境では、CTkToplevelが表示された直後に親ウィンドウの
# 裏側へ回ってしまうことがある。明示的に最前面へ持ち上げて
# フォーカスを移すことで、表示された状態を維持させる
self.lift()
self.attributes("-topmost", True)
self.after(200, lambda: self.attributes("-topmost", False))
self.focus_force()
class App(ctk.CTk):
def __init__(self):
super().__init__()
self.title("STEP1 動作確認")
self.geometry("360x200")
open_button = ctk.CTkButton(
self,
text="ダイアログを開く",
command=self.open_dialog,
)
open_button.pack(expand=True)
def open_dialog(self):
SimpleDialog(self, "保存が完了しました。")
if __name__ == "__main__":
app = App()
app.mainloop()ポイントは、ctk.CTkToplevelを継承したSimpleDialogクラスを作っていることです。CTkToplevelは、メインウィンドウ(CTk)の上に浮かぶ「サブウィンドウ」を表す部品で、CTkFrameのようにメインウィンドウの中に収まるのではなく、独立したウィンドウとして表示されます。ダイアログを開くたびに新しいインスタンスを作って表示し、OKボタンが押されたらself.destroy()でそのウィンドウだけを閉じる、という構造です。
つまずきポイント: CTkToplevelは、Windows環境だと表示された直後に親ウィンドウの裏側へ回ってしまうことがあります。実際にこのコードを動かしてみると、「ダイアログを開く」を押した瞬間にダイアログが一瞬だけ見えて、すぐに「STEP1 動作確認」ウィンドウの後ろに隠れてしまう現象に遭遇しました。せっかく表示したメッセージが読めないのでは本末転倒です。そこで、上記コードの最後に次の4行を足しています。
self.lift(): ウィンドウの重なり順を最前面に上げるself.attributes("-topmost", True): OSレベルで「常に最前面」を一時的に有効にするself.after(200, lambda: self.attributes("-topmost", False)): 200ミリ秒後に「常に最前面」を解除する(ずっと有効のままだと、他のアプリを操作したいときにも常にダイアログが上に居座ってしまうため)self.focus_force(): キーボード操作の対象(フォーカス)をこのダイアログに強制的に移す
lift()だけでは裏に回ってしまうタイミングに間に合わないことがあるため、-topmost属性を一時的に使うことで、確実に表示された状態を維持しています。
✓ 動作確認方法(sample1_base_dialog.py)
python sample1_base_dialog.pyで起動する- 「ダイアログを開く」ボタンをクリックする
- 「保存が完了しました。」というメッセージと、OKボタンだけのウィンドウが、隠れることなく前面に表示される
- この段階では、ダイアログを開いたまま裏の「STEP1 動作確認」ウィンドウをクリックしても普通に操作できる(まだモーダル化していないため。これは正しい動作です)
- OKボタンを押すとダイアログだけが閉じる
種類ごとのアイコンを自作する(info・warning・errorをどう視覚化するか)
sample2_icon_types.pyCustomTkinterには、tkinter.messageboxが持っているような標準のアラートアイコン(情報・警告・エラーのマーク)が用意されていません。ここでは、色付きの丸バッジをCTkLabelで自作し、種類ごとに色とシンボル文字を切り替える方法を紹介します。
ICON_STYLES = {
"info": {"bg": "#3B82C4", "symbol": "i"},
"warning": {"bg": "#C08A2E", "symbol": "!"},
"error": {"bg": "#C0392B", "symbol": "×"},
}
class SimpleDialog(ctk.CTkToplevel):
def __init__(self, parent, message: str, msg_type: str = "info"):
super().__init__(parent)
self.title("お知らせ")
self.geometry("340x180")
style = ICON_STYLES.get(msg_type, ICON_STYLES["info"])
# --- アイコンバッジ(丸型のCTkLabel) ---
# 幅と高さを同じにして、corner_radiusをその半分にすると正円になる
icon_badge = ctk.CTkLabel(
self,
text=style["symbol"],
width=40,
height=40,
corner_radius=20,
fg_color=style["bg"],
text_color="white",
font=ctk.CTkFont(size=18, weight="bold"),
)
icon_badge.pack(pady=(20, 8))
label = ctk.CTkLabel(
self, text=message, font=ctk.CTkFont(size=14),
wraplength=280, justify="left",
)
label.pack(padx=20, pady=(0, 12), fill="x")
ctk.CTkButton(self, text="OK", command=self.destroy).pack(pady=(0, 20))
# ステップ1と同じく、裏側へ回ってしまうのを防ぐための対策
self.lift()
self.attributes("-topmost", True)
self.after(200, lambda: self.attributes("-topmost", False))
self.focus_force()丸バッジの正体は、幅と高さを同じ値(width=40, height=40)にして、corner_radiusをその半分(20)にしたCTkLabelです。corner_radiusが幅・高さの半分になると、四隅の丸みが最大化されてちょうど正円になります。中に入れるtextを"i" "!" "×"のようなシンプルな記号に変え、fg_color(背景色)を情報=ブルー系・警告=オレンジ系・エラー=レッド系で塗り分けることで、標準アイコンがなくても種類を一目で見分けられるようにしています。
なお、「もっと手早く済ませたい」という場合は、textに絵文字(💡・⚠️・❌など)をそのまま渡す方法もあります。ただし絵文字はOSやフォントによって見た目(色・太さ)が変わってしまい、サイト全体のデザインとも馴染みにくいため、この記事ではCTkLabelで自作する方法を採用しています。
配色については、fg_colorに直接色コードを指定しているため、set_appearance_mode("dark")でダークモードに切り替えても、このバッジ自体の色は変わりません(常に同じ発色ではっきり見せたいアイコンなので、これは意図した動作です)。周囲のダイアログ本体(背景・文字色)は、CTkToplevel・CTkLabelが持つ標準のテーマカラーがそのまま使われるため、ライト/ダークの切り替えに自動で追従します。
✓ 動作確認方法(sample2_icon_types.py)
python sample2_icon_types.pyで起動する- 「情報」ボタンをクリック → 青い丸バッジ+「保存が完了しました。」が、隠れることなく前面に表示される
- 「警告」ボタンをクリック → オレンジの丸バッジ+「未保存の変更があります。」が、隠れることなく前面に表示される
- 「エラー」ボタンをクリック → 赤い丸バッジ+「ファイルの読み込みに失敗しました。」が、隠れることなく前面に表示される
- どのダイアログも、OKボタンで閉じられる
なぜモーダル化が必要なのか(grab_set()のUX的な役割)
sample3_modal.pyもし grab_set() を付けなかったら?
ステップ1の動作確認で触れたとおり、ここまでのダイアログには重大な弱点があります。ダイアログを開いたまま、裏の画面を普通に操作できてしまうのです。
これがなぜ問題かというと、たとえば「この操作は元に戻せません」という警告ダイアログを表示している最中に、ユーザーが警告を無視して裏の「実行」ボタンを連打してしまうと、警告の意味がないまま処理が二重に実行されたり、想定していない状態でエラーが起きたりする可能性があるためです。
メッセージボックスの最も重要な役割は、実は「メッセージを見せること」以上に、「回答するまで他の操作をさせないこと」にあります。
この「ダイアログが応答するまで他の操作を受け付けない」状態をモーダルと呼びます。
grab_set()でモーダル化する
CustomTkinter(というよりTkinter)では、grab_set()を呼ぶだけでこのモーダル化が実現できます。
ステップ2からの差分だけを見てみます。ウィンドウ設定・アイコン・メッセージ・OKボタンの部分はステップ2とまったく同じなので、ここでは追加した1行にだけ注目してください(全体のコードはダウンロードしたsample3_modal.pyでそのまま確認できます)。
USE_GRAB_SET = True # ← Falseにして実行し、裏画面のボタンの反応を比べてみてください
class SimpleDialog(ctk.CTkToplevel):
def __init__(self, parent, message: str, msg_type: str = "info"):
super().__init__(parent)
# ここまではステップ2と同じ(ウィンドウ設定・アイコン・メッセージ・OKボタン)
# --- ここがモーダル化の本体 ---
# grab_set()を呼ぶと、以降このダイアログ以外へのクリックを
# OSレベルで受け付けなくなる(閉じるまで裏画面を操作できない)
if USE_GRAB_SET:
self.grab_set()grab_set()は、「以降のマウス・キーボード操作を、このウィジェット(とその子要素)に固定する」という命令です。これを呼んだ瞬間から、ダイアログが閉じる(destroy()される)まで、裏のメインウィンドウをクリックしても一切反応しなくなります。
sample3_modal.pyでは、ファイル冒頭のUSE_GRAB_SETをTrue/Falseで切り替えられるようにしてあります。VSCodeなどのエディターで、この行のTrueをFalseに書き換えるだけで動きが変わります。
Falseにして実行すると、警告ダイアログを開いたままでも裏の「裏画面を操作する(+1)」ボタンのカウントが普通に増えてしまう様子を、Trueにすると、ダイアログを閉じるまでそのボタンが完全に反応しなくなる様子を、実際に手元で見比べることができます。
✓ 動作確認方法(sample3_modal.py、USE_GRAB_SET=Trueのまま)
python sample3_modal.pyで起動する- 「裏画面を操作する(+1)」を2〜3回クリックし、カウントが増えることを確認する
- 「警告ダイアログを開く」をクリックする
- ダイアログを開いたまま「裏画面を操作する(+1)」をクリック → 反応せず、カウントは増えない(モーダル化の効果)
- ダイアログのOKボタンを押して閉じる
- 再度「裏画面を操作する(+1)」をクリック → 今度は普通にカウントが増える
余裕があれば、ファイル冒頭のUSE_GRAB_SET = TrueをFalseに書き換えて再実行し、手順4で裏のボタンが反応してしまうことも確認しておくと、grab_set()の役割がより実感できます。
親ウィンドウの中央に表示する(座標計算)
sample3_modal.py(つづき)もう1つ、モーダルなダイアログとして仕上げるうえで欠かせないのが表示位置です。デフォルトのままだと、CTkToplevelは画面の左上寄りなど、親ウィンドウとは無関係な位置に表示されてしまうことがあります。裏画面の操作をロックしているのに、ダイアログ自体がどこにあるか分かりにくいのでは本末転倒です。そこで、親ウィンドウの座標とサイズから、ダイアログを中央に配置する座標を計算します。
class SimpleDialog(ctk.CTkToplevel):
def __init__(self, parent, message: str, msg_type: str = "info"):
super().__init__(parent)
self.title("お知らせ")
self.geometry("340x180")
self.resizable(False, False)
# 親と連動させる(タスクバーに単独表示させない)
self.transient(parent)
# --- 親ウィンドウの中央に表示する ---
self.update_idletasks()
px, py = parent.winfo_x(), parent.winfo_y()
pw, ph = parent.winfo_width(), parent.winfo_height()
x = px + (pw - 340) // 2
y = py + (ph - 180) // 2
self.geometry(f"+{x}+{y}")self.transient(parent): このダイアログが親ウィンドウに従属していることをOSに伝える。タスクバーに別アプリのように単独表示されるのを防ぐself.update_idletasks(): レイアウト計算を強制的に確定させる。これを呼ばずにwinfo_x()などを使うと、まだ確定していない古い座標が返ってくることがあるparent.winfo_x()/winfo_y(): 親ウィンドウの左上の座標(画面全体における位置)を取得するparent.winfo_width()/winfo_height(): 親ウィンドウの現在の幅・高さを取得するgeometry(f"+{x}+{y}"): サイズを指定せず位置(+x+y)だけを指定する書き方。「親の左上座標 + (親の幅 – ダイアログの幅) ÷ 2」という計算で、ダイアログが親のちょうど中央に来るx座標を求めている(y座標も同様)
この処理を__init__の中で、ウィンドウサイズを確定させた直後・部品を配置する前に実行しているのがポイントです。

✓ 動作確認方法
python sample3_modal.pyを起動した状態で、アプリのウィンドウをドラッグして画面の右下など好きな位置に動かす- 「警告ダイアログを開く」をクリックする
- ダイアログが、移動後の親ウィンドウの中央に表示される(常に画面の同じ位置に出るわけではないことを確認)
「はい/いいえ」の確認ダイアログと戻り値の受け取り方
sample4_confirm_return.pyここまではOKボタン1つだけの「知らせるだけ」のダイアログでした。実務でよく使うのは、「このファイルを削除しますか?」のように、ユーザーに選ばせて、その結果によって処理を分岐させるパターンです。これを実現するには、ボタンが押された結果をどこかに保存し、呼び出し元のコードがその結果を受け取れるようにする必要があります。
class ConfirmDialog(ctk.CTkToplevel):
def __init__(self, parent, message: str):
super().__init__(parent)
self.title("確認")
self.geometry("340x170")
self.resizable(False, False)
self.transient(parent)
self.result = None # ここにユーザーの回答(True/False)を保存する
# (中略: アイコン・メッセージの表示は省略)
button_frame = ctk.CTkFrame(self, fg_color="transparent")
button_frame.pack(pady=(0, 20))
ctk.CTkButton(
button_frame, text="はい", width=100,
command=self._on_yes,
).pack(side="left", padx=6)
ctk.CTkButton(
button_frame, text="いいえ", width=100,
fg_color="gray40", hover_color="gray30",
command=self._on_no,
).pack(side="left", padx=6)
# ウィンドウ右上の×ボタンは「いいえ」と同じ扱いにする
self.protocol("WM_DELETE_WINDOW", self._on_no)
self.grab_set()
def _on_yes(self):
self.result = True
self.destroy()
def _on_no(self):
self.result = False
self.destroy()
def ask_yes_no(parent, message: str) -> bool:
"""確認ダイアログを表示し、ユーザーの回答(True/False)を返す"""
dialog = ConfirmDialog(parent, message)
parent.wait_window(dialog) # ダイアログが閉じるまでここで処理を待たせる
return dialog.resultポイントは2つあります。
1つ目は、self.resultという属性に、ボタンが押された結果(True/False)を保存していることです。「はい」ボタンなら_on_yesが呼ばれてself.result = True、「いいえ」ボタンや右上の×ボタンなら_on_noが呼ばれてself.result = Falseになってから、self.destroy()でダイアログを閉じます。
2つ目は、parent.wait_window(dialog)です。ダイアログを表示するSimpleDialog(...)という呼び出しは、Pythonの処理としては一瞬で完了してしまい、ダイアログが実際に閉じられるのを待ってはくれません。wait_window(dialog)を呼ぶと、そのダイアログがdestroy()されるまで、呼び出し元の処理をそこで足止めできます。これにより、ask_yes_no()関数は「ユーザーが回答し終わってから、その結果(bool)を返す」という、直感的な使い方ができる関数になります。
呼び出し元では、ask_yes_no()の戻り値をそのままif文で使うだけです。ファイル削除を題材にした、実務でそのまま使えるサンプルを見てみます。
def on_delete_click(self):
result = ask_yes_no(
self, "このファイルを削除しますか?\nこの操作は元に戻せません。"
)
if result:
self.status_label.configure(text="削除しました。")
# ここに実際の削除処理を書く
else:
self.status_label.configure(text="キャンセルされました。")ask_yes_no()の呼び出しが、あたかもinput()のように「ユーザーの回答を受け取って次の行に進む」ような書き心地になっている点に注目してください。GUIのイベント駆動処理でありながら、コードの見た目は上から下に読める手続き的な流れのままになっています。
✓ 動作確認方法(sample4_confirm_return.py)
python sample4_confirm_return.pyで起動する- 「ファイルを削除する」ボタンをクリックする
- 「このファイルを削除しますか?」という確認ダイアログが、緑の丸バッジ付きで表示される
- 「はい」をクリック → ダイアログが閉じ、画面下に「削除しました。」と表示される
- 再度「ファイルを削除する」をクリックし、今度は「いいえ」をクリック → 「キャンセルされました。」と表示される
- もう一度開いて、今度はウィンドウ右上の×ボタンをクリック → 「いいえ」を押したときと同じく「キャンセルされました。」と表示される(×ボタンも
_on_noと同じ扱いになっているため)
完成版:どこからでも呼び出せる関数にまとめる(show_info() / show_warning() / show_error() / ask_yes_no())
sample5_final.pyここまでの内容(アイコンの出し分け・モーダル化・中央配置・戻り値の受け取り)を1つにまとめ、show_info() show_warning() show_error() ask_yes_no()という4つの関数を用意した完成版が、ダウンロードできるsample5_final.pyです。
これまでの仕組み(アイコン・メッセージ・中央配置・grab_set())に、ボタンを種類ごとに動的に組み立てる処理を足したのが完成版です。差分にあたる部分だけ抜き出すと以下の通りです(全体はsample5_final.pyをダウンロードして確認してください)。
class _CTkMessageDialog(ctk.CTkToplevel):
"""内部で使う共通ダイアログ本体。直接使わず、下の関数経由で呼び出してください。"""
def __init__(self, parent, message: str, msg_type: str, buttons: list):
super().__init__(parent)
# ここまではステップ3と同じ(アイコン・メッセージ・中央配置・grab_set())
self.result = None
button_frame = ctk.CTkFrame(self, fg_color="transparent")
button_frame.pack(pady=(0, 20))
for label in buttons:
is_secondary = label in ("いいえ", "キャンセル")
ctk.CTkButton(
button_frame,
text=label,
width=100,
fg_color="gray40" if is_secondary else None,
hover_color="gray30" if is_secondary else None,
command=lambda v=label: self._on_button(v),
).pack(side="left", padx=6)
self.protocol("WM_DELETE_WINDOW", lambda: self._on_button(None))
def _on_button(self, value):
self.result = value
self.destroy()
def show_info(parent, message: str) -> None:
"""情報ダイアログを表示する"""
dialog = _CTkMessageDialog(parent, message, "info", buttons=["OK"])
parent.wait_window(dialog)
def ask_yes_no(parent, message: str) -> bool:
"""はい/いいえの確認ダイアログを表示し、結果をboolで返す"""
dialog = _CTkMessageDialog(parent, message, "question", buttons=["はい", "いいえ"])
parent.wait_window(dialog)
return dialog.result == "はい"ステップ4までとの違いは、self.resultにTrue/Falseを直接入れるのではなく、押されたボタンのラベル文字列("OK" "はい" "いいえ")をそのまま入れるようにした点です。こうすることで、OKボタン1つだけのshow_info()系と、はい/いいえ2つのask_yes_no()を、同じ_CTkMessageDialogクラス1つで使い回せます。ask_yes_no()の中でだけ、最後にdialog.result == "はい"という比較でbool値に変換しているので、呼び出し元は今まで通りif result:で分岐できます。
このファイルをそのままctk_messagebox.pyのような名前で自分のプロジェクトにコピーし、次のようにimportすれば、あとは標準のmessageboxとほぼ同じ感覚で使えます。
from ctk_messagebox import show_info, show_warning, show_error, ask_yes_no
show_info(self, "保存が完了しました。")
if ask_yes_no(self, "このファイルを削除しますか?"):
delete_file()ファイル後半のデモ画面(class App)部分は動作確認用なので、実際に組み込む際はICON_STYLESからask_yes_no()までの関数定義部分だけをコピーすれば十分です。

✓ 動作確認方法(sample5_final.py)
python sample5_final.pyで起動する- 「情報を表示」「警告を表示」「エラーを表示」をそれぞれクリックし、青・オレンジ・赤のアイコンで正しく表示されることを確認する
- 「削除確認(はい/いいえ)」をクリックし、「はい」「いいえ」それぞれで画面下の表示が切り替わることを確認する(ステップ4と同じ挙動)
- どのダイアログも、開いている間は裏の画面をクリックしても反応しないことを確認する(モーダル化)
- アプリ全体を
ctk.set_appearance_mode("dark")に切り替えてから同じ操作を行い、ダイアログの背景・文字色がダークモードに追従することを確認する(丸バッジの色自体は変わらない)
動作確認チェックリスト
5段階すべてを通しで確認したい場合は、以下の順にチェックしてください。1つでも異なる挙動になった場合は、どのステップのファイルで再現するかを先に特定してから見直すと原因を絞り込みやすくなります。
sample1_base_dialog.py: ダイアログを開いても裏画面が普通に操作できる(モーダル化前の正しい状態)sample2_icon_types.py: 情報=青、警告=オレンジ、エラー=赤の丸バッジが正しく出し分けられるsample3_modal.py:USE_GRAB_SET = Trueのとき、ダイアログを開いている間は裏の「+1」ボタンが反応しないsample3_modal.py: アプリのウィンドウを移動してから開いても、ダイアログが常に親ウィンドウの中央に表示されるsample4_confirm_return.py: 「はい」「いいえ」「×ボタン」のいずれでも正しく分岐し、削除処理と結果表示が対応しているsample5_final.py: 4つの関数(show_info/show_warning/show_error/ask_yes_no)がすべて単独で呼び出せるsample5_final.py: ダークモードに切り替えても、ダイアログの背景・文字色が追従する
よくある質問
Qtkinter.messageboxをそのまま使うのと、この記事のように自作するのとでは何が違いますか?
Atkinter.messageboxは手軽に使える反面、OS標準の見た目で描画され、CustomTkinterのテーマ(ライト/ダーク、角丸フラットデザイン)にはまったく追従しません。アプリ全体をCustomTkinterのデザインで統一したい場合は、この記事のようにCTkToplevelで自作する必要があります。
Qgrab_set()を呼ぶタイミングは__init__のどこでも構いませんか?
Aボタンなど、ダイアログ内の部品をすべて配置し終えたあとに呼ぶことをおすすめします。この記事のサンプルでも、部品配置の最後にgrab_set()を呼んでいます。部品の配置前に呼んでも動作すること自体はありますが、処理の流れとして「見た目を組み立て終えてから操作をロックする」という順序のほうが分かりやすく、事故も起きにくくなります。
Qgrab_set()の代わりにgrab_set_global()もあるようですが、どちらを使うべきですか?
Agrab_set()は自分のアプリ内の操作だけをロックしますが、grab_set_global()はアプリの外(他のアプリのウィンドウ)まで操作を奪ってしまいます。通常のメッセージボックス用途では、自分のアプリ内だけをロックできるgrab_set()で十分です。
Qwait_window()を使わずに戻り値を受け取ることはできますか?
Await_window()を使わない場合、ダイアログを表示した直後の行では、まだユーザーが回答する前のNoneのような未確定の値しか受け取れません。ボタンが押された後の処理をcommandのコールバック関数側にすべて書く設計にすればwait_window()なしでも実装できますが、「呼び出した箇所で結果をすぐ使う」という直感的な書き方をしたい場合は、wait_window()を使う今回の設計のほうがシンプルになります。
Qアイコンを絵文字ではなくCTkLabelの丸バッジで自作しているのはなぜですか?
A絵文字(💡・⚠️・❌など)はOSやフォントによって太さ・色・見え方が変わってしまい、アプリのデザインとの一体感を出しにくいためです。CTkLabelのfg_colorとcorner_radiusだけで作る丸バッジであれば、色・サイズ・シンボル文字を自分で完全にコントロールでき、どの環境でも同じ見た目になります。手早く済ませたいだけであれば、text引数に絵文字をそのまま渡す方法でも動作はします。
Qダイアログの数が増えてきた場合、種類ごとにクラスを分けたほうがいいですか?
Aこの記事の完成版(sample5_final.py)のように、ICON_STYLES辞書とbuttons引数で見た目・ボタン構成を切り替えられる共通クラス1つにまとめる設計をおすすめします。種類ごとにクラスを分けると、モーダル化や中央配置といった共通処理が種類の数だけ重複してしまい、修正漏れの原因になりやすいためです。
CustomTkinter Series
CustomTkinterシリーズ
部品の作り方を1つずつ学ぶ3回と、それを全部まとめたテンプレートの4本です。
部品の作り方を学ぶ
VOL.1CustomTkinterのインストールと使い方|最初のウィンドウから入力フォームまでコード付き解説
VOL.2CustomTkinterでオリジナルのメニューバー(File・表示・設定・Help)を自作する
VOL.3この記事CustomTkinterでオリジナルのMessageBox(確認・警告・エラー)を自作する
全部入りのテンプレート
Follow me on X
本ブログの中の人「ニンジン🥕」はXやってます。新着記事はいち早くポストでお知らせしているので、見逃したくない方は @ninjin_py_vba をフォローしてみてください🥕
@ninjin_py_vba をフォロー



















コメント