概要
GUIオブジェクトを作っていく中で、既に用意されているダイアログだけでなく、
ユーザー自身でカスタマイズしたダイアログを使用したくなる場面があります。
カスタマイズ可能なダイアログとして、Tkinterにはsimpledialog.Dialogがあります。
カスタマイズ方法について、本記事で解説いたします。
環境
Python3系
デフォルト状態のダイアログ
何もカスタマイズしない状態では、simpledialog.DialogにOKとCancelのボタンしかありません。
上記はrootウィンドウです。ダイアログ表示ボタンを押すと以下のダイアログが表示されます。
import tkinter
from tkinter import *
from tkinter import simpledialog
if __name__ == "__main__":
root = tkinter.Tk()
def display_dialog():
simpledialog.Dialog(root)
button = Button(root)
button["text"] = "ダイアログ表示"
button["command"] = display_dialog
button.grid(column=0, row=0, padx=10, pady=10)
root.mainloop()
ダイアログの基本的なカスタマイズ方法
任意のオブジェクトをダイアログへ埋め込む
ダイアログには基本的なオブジェクト(ラベル、エントリー、コンボボックス、ボタンなど)を埋め込むことができます。
以下のコードではエントリーオブジェクトを埋め込みます。
import tkinter
from tkinter import *
from tkinter import simpledialog
class CustomDialog(simpledialog.Dialog):
def __init__(self, master, title=None) -> None:
super(CustomDialog, self).__init__(parent=master, title=title)
def body(self, master) -> None:
"""
Dialogオブジェクトへ配置するオブジェクトを定義する。
Parameters
----------
master:
Dialogオブジェクトの親オブジェクト
Returns
-------
None
"""
entry: Entry = Entry(master)
entry.grid(column=0, row=0)
if __name__ == "__main__":
root = tkinter.Tk()
def display_dialog():
CustomDialog(root)
button = Button(root)
button["text"] = "ダイアログ表示"
button["command"] = display_dialog
button.grid(column=0, row=0, padx=10, pady=10)
root.mainloop()
任意のオブジェクトをダイアログへ埋め込むために以下を実施しています。
- simpledialog.Dialogを継承したCustomDialogというクラスを作成します。
- ダイアログを初期化するために、simpledialog.Dialogの**__init__**メソッドを実行します(super(CustomDialog, self).init(parent=master, title=title))。
- ダイアログへ配置するオブジェクトを、bodyメソッドへ記載します。
simpledialog.Dialogに実装されているbodyメソッドをオーバーライドすることで、ユーザーが自由にダイアログ内のオブジェクトを定義できます。
OKボタンが押された後の処理
OKボタンが押された後、何らかの処理を行いたい場合があると思います。
例えば、エントリーオブジェクトに入力された文字列を出力したい場合などです。
そのような場合にはapplyメソッドをオーバーライドします。
以下のコードではエントリーオブジェクト内の文字列を取得して、コンソールへ出力します。
import tkinter
from tkinter import *
from tkinter import simpledialog
class CustomDialog(simpledialog.Dialog):
def __init__(self, master, title=None) -> None:
super(CustomDialog, self).__init__(parent=master, title=title)
def body(self, master) -> None:
"""
Dialogオブジェクトへ配置するオブジェクトを定義する。
Parameters
----------
master:
Dialogオブジェクトの親オブジェクト
Returns
-------
None
"""
self.entry: Entry = Entry(master)
self.entry.grid(column=0, row=0)
def apply(self) -> None:
"""
このDialogオブジェクトが破棄される際に実行される処理を定義する。
Parameters
----------
Returns
-------
None
"""
print(self.entry.get())
if __name__ == "__main__":
root = tkinter.Tk()
def display_dialog():
CustomDialog(root)
button = Button(root)
button["text"] = "ダイアログ表示"
button["command"] = display_dialog
button.grid(column=0, row=0, padx=10, pady=10)
root.mainloop()
バリデーション
OKボタンが押され、ダイアログが削除される際にバリデーション(検証)を行うこともできます。
validateメソッドをオーバーライドします。
例えば、エントリーオブジェクトに入力した文字列の文字数が不足している場合に、エラーメッセージを出すことが可能です。
import tkinter
from tkinter import *
from tkinter import simpledialog
class CustomDialog(simpledialog.Dialog):
def __init__(self, master, title=None) -> None:
super(CustomDialog, self).__init__(parent=master, title=title)
def body(self, master) -> None:
"""
Dialogオブジェクトへ配置するオブジェクトを定義する。
Parameters
----------
master:
Dialogオブジェクトの親オブジェクト
Returns
-------
None
"""
self.label: Label = Label(master)
self.label["text"] = "文字数が足りません。"
self.label["fg"] = "red"
self.entry: Entry = Entry(master)
self.entry.grid(column=0, row=0)
def apply(self) -> None:
"""
このDialogオブジェクトが破棄される際に実行される処理を定義する。
Parameters
----------
Returns
-------
None
"""
print(self.entry.get())
def validate(self) -> bool:
"""
このDialogオブジェクトが破棄される際に行う検証を定義する。
Parameters
----------
Returns
-------
None
"""
if len(self.entry.get()) > 4:
return True
else:
self.label.grid(column=0, row=1)
return False
if __name__ == "__main__":
root = tkinter.Tk()
def display_dialog():
CustomDialog(root)
button = Button(root)
button["text"] = "ダイアログ表示"
button["command"] = display_dialog
button.grid(column=0, row=0, padx=10, pady=10)
root.mainloop()
ダイアログの少し発展的なカスタマイズ方法
背景色変更
ダイアログの背景色を変更したい場合は、**__init__**メソッドをオーバーライドします。
import tkinter
from tkinter import *
from tkinter import simpledialog
class CustomDialog(simpledialog.Dialog):
def __init__(self, master, title=None) -> None:
parent = master
'''
ダイアログの初期化
背景色を変えるためにオーバーライドしている。
'''
Toplevel.__init__(self, parent, bg="red") # 背景色の変更
self.withdraw() # remain invisible for now
# If the master is not viewable, don't
# make the child transient, or else it
# would be opened withdrawn
if parent.winfo_viewable():
self.transient(parent)
if title:
self.title(title)
self.parent = parent
self.result = None
body = Frame(self)
self.initial_focus = self.body(body)
body.pack(padx=5, pady=5)
self.buttonbox()
if not self.initial_focus:
self.initial_focus = self
self.protocol("WM_DELETE_WINDOW", self.cancel)
if self.parent is not None:
self.geometry("+%d+%d" % (parent.winfo_rootx() + 50,
parent.winfo_rooty() + 50))
self.deiconify() # become visible now
self.initial_focus.focus_set()
# wait for window to appear on screen before calling grab_set
self.wait_visibility()
self.grab_set()
self.wait_window(self)
if __name__ == "__main__":
root = tkinter.Tk()
def display_dialog():
CustomDialog(root)
button = Button(root)
button["text"] = "ダイアログ表示"
button["command"] = display_dialog
button.grid(column=0, row=0, padx=10, pady=10)
root.mainloop()
参考資料
How to set background color of tk.simpledialog?
質問者:Tasmotanizer
参考にした回答
回答者:TheFluffDragon9