Object-Oriented Programming/GUI Applications/Custom Dialog

"""This program demonstrates a tkinter custom dialog box.

Input:
    None

Output:
    Window with a custom dialog box

References:
    https://www.geeksforgeeks.org/python-tkinter-toplevel-widget/
    http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm
    https://www.tutorialspoint.com/python/python_gui_programming.htm
    https://www.python-course.eu/python_tkinter.php
"""

import tkinter


class Root(tkinter.Tk):
    """Creates root window."""

    _main_menu = None
    _label = None

    def __init__(self, *args, **kwargs):
        tkinter.Tk.__init__(self, *args, **kwargs)

        self.title("tkinter Custom Dialog Example")
        self.geometry("%dx%d+0+0" % self.maxsize())

        self._main_menu = MainMenu(self)

        # center the label in a grid
        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(0, weight=1)

        self._label = tkinter.Label(self,
            text="Use Tools / Settings to change this label")
        self._label.grid(row=0, column=0)

    def show_dialog(self):
        dialog = CustomDialog(self)
        if dialog.result:
            self._label.config(text=dialog.text)


class MainMenu(tkinter.Menu):
    """Creates Main menu."""

    @property
    def root(self):
        return self._root

    def __init__(self, root, *args, **kwargs):
        tkinter.Menu.__init__(self, root, *args, **kwargs)
        self._root = root

        file_menu = ToolsMenu(self, tearoff=0)
        self.add_cascade(label="Tools", menu=file_menu)

        root.config(menu=self)


class ToolsMenu(tkinter.Menu):
    """Creates Tools menu."""

    def __init__(self, *args, **kwargs):
        tkinter.Menu.__init__(self, *args, **kwargs)
        self.add_command(
            label="Settings...",
            command=self.master.root.show_dialog)


class CustomDialog(tkinter.Toplevel):
    """Creates custom dialog box."""

    _entry = None
    _result = None
    _text = None

    @property
    def result(self):
        return self._result

    @property
    def text(self):
        return self._text

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs,
            padx=10,
            pady=10)

        self._set_properties()
        self._add_widgets()
        self._entry.focus_set()
        self.wait_window()

    def _add_widgets(self):
        label = tkinter.Label(self,
            text="Label Value:")
        label.grid(row=0, column=0)

        self._entry = tkinter.Entry(self, width=20)
        self._entry.grid(row=1, column=0)

        ok_button = tkinter.Button(self,
            text="OK",
            command=self._ok_click)
        ok_button.grid(row=0, column=1)

        cancel_button = tkinter.Button(self,
            text="Cancel",
            command=self._cancel_click)
        cancel_button.grid(row=1, column=1)

        self.bind("<Return>", self._ok_click)
        self.bind("<Escape>", self._cancel_click)

    def _cancel_click(self, event=None):
        self._text = None
        self._result = False
        self.destroy()

    def _ok_click(self, event=None):
        self._text = self._entry.get()
        self._result = True
        self.destroy()

    def _set_properties(self):
        width = 300
        height = 100

        self.title("Change Label")
        x = (self.master.winfo_screenwidth() - width) // 2
        y = (self.master.winfo_screenheight() - height) // 2
        self.geometry(f"{width}x{height}+{x}+{y}")
        self.resizable(False, False)


if __name__ == "__main__":
    root = Root()
    root.mainloop()