Tkinter Toplevel ウィンドウ
Tkinter の Toplevel で子ウィンドウを作成し、 管理するための実践ガイド。基本、モーダル、配置、一時ウィンドウを作成し、リソースを安全にクリーンアップします。
チュートリアルの進捗
1 動画チュートリアルと作成するもの
このガイドでは、Tkinter を使用して Toplevel ウィンドウを作成および管理する方法を説明します。4つの子ウィンドウ—基本、モーダル、配置済み、一時—を起動するメインウィンドウを作成し、制御、フォーカス、レイヤリングが実際にどのように機能するかを確認します。
目標:オンデマンドでウィンドウを起動し、モーダルにするタイミングを決定し、位置を制御し、閉じるときに安全にクリーンアップする。
2 完全なコード:メインウィンドウと Toplevel の例
このファイルを実行して、さまざまなウィンドウ スタイルを実際に試してください。標準ウィジェット、モダンな外観のためのスタイル調整、子ウィンドウを追跡して UI を整理するための簡単なロジックを使用します。
# main.py
import tkinter as tk
from tkinter import ttk
import random
class MainApplication:
def __init__(self, root):
self.root = root
self.root.title("Main Window")
self.root.geometry("400x300")
# Style for a simple, modern look
style = ttk.Style()
style.configure('Modern.TButton',
padding=10,
font=('Helvetica', 10))
self.header = ttk.Label(
root,
text="Toplevel Windows Demo",
font=('Helvetica', 16, 'bold'),
padding=20
)
self.header.pack()
self.create_buttons()
self.child_windows = []
self.center_window(self.root)
def create_buttons(self):
buttons_frame = ttk.Frame(self.root, padding="20")
buttons_frame.pack(fill=tk.BOTH, expand=True)
ttk.Button(
buttons_frame,
text="Open Basic Window",
style='Modern.TButton',
command=self.create_basic_window
).pack(pady=5, fill=tk.X)
ttk.Button(
buttons_frame,
text="Open Modal Window",
style='Modern.TButton',
command=self.create_modal_window
).pack(pady=5, fill=tk.X)
ttk.Button(
buttons_frame,
text="Open Positioned Window",
style='Modern.TButton',
command=self.create_positioned_window
).pack(pady=5, fill=tk.X)
ttk.Button(
buttons_frame,
text="Open Transient Window",
style='Modern.TButton',
command=self.create_transient_window
).pack(pady=5, fill=tk.X)
def create_basic_window(self):
window = tk.Toplevel(self.root)
window.title("Basic Window")
window.geometry("300x200")
ttk.Label(
window,
text="This is a basic Toplevel window",
padding=20
).pack()
ttk.Button(
window,
text="Close",
command=window.destroy
).pack(pady=10)
self.child_windows.append(window)
window.protocol("WM_DELETE_WINDOW",
lambda: self.cleanup_window(window))
def create_modal_window(self):
window = tk.Toplevel(self.root)
window.title("Modal Window")
window.geometry("300x200")
# Make it modal
window.grab_set()
window.transient(self.root)
ttk.Label(
window,
text="This window must be closed\nbefore using the main window",
padding=20
).pack()
ttk.Button(
window,
text="Close",
command=window.destroy
).pack(pady=10)
def create_positioned_window(self):
window = tk.Toplevel(self.root)
window.title("Positioned Window")
window.geometry("300x200")
# Random position on screen
x = random.randint(0, self.root.winfo_screenwidth() - 300)
y = random.randint(0, self.root.winfo_screenheight() - 200)
window.geometry(f"+{x}+{y}")
ttk.Label(
window,
text="This window appears at a random position",
padding=20
).pack()
ttk.Button(
window,
text="Close",
command=window.destroy
).pack(pady=10)
self.child_windows.append(window)
window.protocol("WM_DELETE_WINDOW",
lambda: self.cleanup_window(window))
def create_transient_window(self):
window = tk.Toplevel(self.root)
window.title("Transient Window")
window.geometry("300x200")
# Make it stay on top of the main window
window.transient(self.root)
ttk.Label(
window,
text="This window always stays\non top of the main window",
padding=20
).pack()
ttk.Button(
window,
text="Close",
command=window.destroy
).pack(pady=10)
self.child_windows.append(window)
window.protocol("WM_DELETE_WINDOW",
lambda: self.cleanup_window(window))
def cleanup_window(self, window):
if window in self.child_windows:
self.child_windows.remove(window)
window.destroy()
def center_window(self, window):
window.update_idletasks()
width = window.winfo_width()
height = window.winfo_height()
x = (window.winfo_screenwidth() // 2) - (width // 2)
y = (window.winfo_screenheight() // 2) - (height // 2)
window.geometry(f'+{x}+{y}')
if __name__ == "__main__":
root = tk.Tk()
app = MainApplication(root)
root.mainloop()
Save this as main.py and run it with Python 3 to try the demo.
3 Toplevel の基本
Toplevel ウィンドウは、デスクトップ上に配置される独立したフレームですが、アプリケーションに属します。メインウィンドウと並存し、同じイベントループを共有します。単純なコンストラクタで作成し、タイトルとサイズを設定します。
window = tk.Toplevel(self.root)
window.title("Basic Window")
window.geometry("300x200")
そこから、メインウィンドウと同じようにウィジェットを pack または grid できます。window.destroy で閉じると、画面から削除され、リソースが解放されます。
4 モーダル ウィンドウ:入力のキャプチャ
モーダル ウィンドウは、閉じられるまで、アプリケーションの他の部分への入力をブロックします。2行で実現できます。
window.grab_set() # Capture input events in this window
window.transient(self.root) # Associate it visually with the main window
grab_set がアクティブな場合、ユーザーはメインウィンドウに戻ることができません。transient は、ウィンドウ マネージャーに親ウィンドウの上に維持するように指示し、多くの場合、独自のタスクバーエントリなしで表示するように指示します。
5 画面上のウィンドウの位置決め
geometry を使用して場所を選択します。" +X+Y" サフィックスは、左上隅を画面座標に配置します。簡単なヘルパー関数で、任意のウィンドウを中央に配置することもできます。
# Place at random coordinates
x = random.randint(0, self.root.winfo_screenwidth() - 300)
y = random.randint(0, self.root.winfo_screenheight() - 200)
window.geometry(f"+{x}+{y}")
# Or center any window
def center_window(self, window):
window.update_idletasks()
width = window.winfo_width()
height = window.winfo_height()
x = (window.winfo_screenwidth() // 2) - (width // 2)
y = (window.winfo_screenheight() // 2) - (height // 2)
window.geometry(f'+{x}+{y}')
ランダムな配置はデモに便利です。本番環境では、ウィンドウを中央に配置するか、最後に使用した位置を復元するようにデフォルト設定することで、ユーザーが失われたダイアログを探す必要がなくなります。
6 一時的ウィンドウ:上に表示
Transient は、子ウィンドウをメインウィンドウに関連付け、その上に浮かせますが、入力をブロックしません。ツールパレットやインスペクターによく使用されます。
window.transient(self.root)
多くのウィンドウマネージャーでは、transient ウィンドウは親ウィンドウがアイコン化されたときに消え、親ウィンドウが復元されたときに再表示されます。
7 子ウィンドウの管理とクリーンアップ
子ウィンドウを一括で閉じる予定がある場合は、リストに保持してください。WM_DELETE_WINDOW にフックすると、X をクリックしたときに、ウィンドウを破棄するだけのデフォルトハンドラではなく、クリーンアップ関数が呼び出されます。
self.child_windows = []
def create_basic_window(self):
window = tk.Toplevel(self.root)
# ...
self.child_windows.append(window)
window.protocol("WM_DELETE_WINDOW", lambda: self.cleanup_window(window))
def cleanup_window(self, window):
if window in self.child_windows:
self.child_windows.remove(window)
window.destroy()
このパターンは、タイムアウトや検証ルーチンにも役立ちます。ウィンドウを確実に追跡、キャンセル、パージできます。