5
6

More than 3 years have passed since last update.

Tkinterでダイアログをカスタマイズする方法

Last updated at Posted at 2021-08-15

概要

GUIオブジェクトを作っていく中で、既に用意されているダイアログだけでなく、
ユーザー自身でカスタマイズしたダイアログを使用したくなる場面があります。

カスタマイズ可能なダイアログとして、Tkinterにはsimpledialog.Dialogがあります。

カスタマイズ方法について、本記事で解説いたします。

環境

Python3系

デフォルト状態のダイアログ

何もカスタマイズしない状態では、simpledialog.DialogOKCancelのボタンしかありません。

tkinter_demo_dialog.jpg
上記はrootウィンドウです。ダイアログ表示ボタンを押すと以下のダイアログが表示されます。

tkinter_demo_simple_dialog_no_custom.jpg


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()

ダイアログの基本的なカスタマイズ方法

任意のオブジェクトをダイアログへ埋め込む

ダイアログには基本的なオブジェクト(ラベル、エントリー、コンボボックス、ボタンなど)を埋め込むことができます。
以下のコードではエントリーオブジェクトを埋め込みます。

tkinter_demo_simple_dialog_add_entry.jpg

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()

任意のオブジェクトをダイアログへ埋め込むために以下を実施しています。

  1. simpledialog.Dialogを継承したCustomDialogというクラスを作成します。
  2. ダイアログを初期化するために、simpledialog.Dialog__init__メソッドを実行します(super(CustomDialog, self).init(parent=master, title=title))。
  3. ダイアログへ配置するオブジェクトを、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メソッドをオーバーライドします。

例えば、エントリーオブジェクトに入力した文字列の文字数が不足している場合に、エラーメッセージを出すことが可能です。

tkinter_demo_simple_dialog_add_validate.jpg

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__メソッドをオーバーライドします。

tkinter_demo_simple_dialog_bg_color_change.jpg

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()

参考資料

simpledialogのソースコード

How to set background color of tk.simpledialog?
質問者:Tasmotanizer
参考にした回答
回答者:TheFluffDragon9

Python Tkinter のダイアログボックスをカスタマイズしてみた

5
6
0

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
5
6