Edit on GitHub

sysmon_pytk.widgets.buttons

Mixin class for standard button sets in dialogs.

  1# SPDX-FileCopyrightText: © 2024 Stacey Adams <stacey.belle.rose@gmail.com>
  2# SPDX-License-Identifier: MIT
  3
  4"""
  5Mixin class for standard button sets in dialogs.
  6"""
  7
  8from __future__ import annotations
  9
 10import dataclasses
 11import tkinter as tk
 12from enum import IntEnum
 13from tkinter import ttk
 14from typing import Any, Callable, TypeVar, Union
 15
 16from ..._common import INTERNAL_PAD
 17from ...app_locale import get_translator
 18
 19_ = get_translator()
 20
 21__all__ = ["ButtonName", "ButtonDefinition", "ButtonMixin"]
 22
 23WidgetFrame = TypeVar("WidgetFrame", bound=Union[ttk.Frame, tk.Frame])
 24
 25
 26class ButtonName(IntEnum):
 27    """
 28    Standard button names.
 29    """
 30
 31    NONE = 0
 32    """None."""
 33    OK = 1
 34    """OK button."""
 35    CANCEL = 2
 36    """Cancel button."""
 37    CLOSE = 3
 38    """Close button."""
 39    YES = 4
 40    """Yes button."""
 41    NO = 5
 42    """No button."""
 43    RETRY = 6
 44    """Retry button."""
 45
 46
 47@dataclasses.dataclass
 48class ButtonDefinition:
 49    """
 50    Necessary metadata for creating a button.
 51    """
 52
 53    text: str
 54    """The text to appear on the button."""
 55    command: Callable[[], Any]
 56    """The command to call when the button is pressed."""
 57
 58
 59class ButtonMixin:  # pylint: disable=too-few-public-methods
 60    """
 61    Mixin class for standard button sets in dialogs.
 62
 63    Attributes
 64    ----------
 65    result : ButtonName
 66        The button pressed to close the message box.
 67    button_definitions : dict[ButtonName, ButtonDefinition]
 68        A dictionary of standard buttons available to use.
 69    """
 70
 71    def __init__(self, *args, **kwargs) -> None:
 72        super().__init__(*args, **kwargs)
 73        self.result = ButtonName.NONE
 74        self.init_standard_buttons()
 75        self.toplevel: tk.Tk | tk.Toplevel | None = None
 76
 77    def add_buttons(
 78        self, frame: WidgetFrame, *, buttons: list[ButtonDefinition],
 79        default: int = -1
 80    ) -> None:
 81        """
 82        Add buttons to the provided frame at the bottom, justified right.
 83
 84        Parameters
 85        ----------
 86        frame : WidgetFrame (tk.Frame or ttk.Frame)
 87            The frame to which the buttons will be added
 88        buttons : list[`ButtonDefinition`]
 89            The list of buttons to add
 90        default : int
 91            The default button, displayed with style="Accent.TButton"
 92        """
 93        self.toplevel = frame.winfo_toplevel()
 94        max_columns, max_rows = frame.grid_size()
 95        buttonframe = ttk.Frame(frame)
 96        buttonframe.grid(
 97            row=max_rows, column=0, sticky=tk.E, columnspan=max_columns,
 98            pady=(0, INTERNAL_PAD)
 99        )
100        for num, button in enumerate(buttons):
101            btn = ttk.Button(buttonframe, text=button.text, command=button.command)
102            if num == default:
103                btn.configure(style="Accent.TButton")
104                self.toplevel.bind(
105                    "<KeyPress-Return>", button.command  # type: ignore[arg-type]
106                )
107                self.toplevel.bind(
108                    "<KeyPress-KP_Enter>", button.command  # type: ignore[arg-type]
109                )
110            btn.grid(row=0, column=num, padx=INTERNAL_PAD/2)
111
112    def init_standard_buttons(self) -> None:
113        """
114        Define the button definitions.
115        """
116        self.button_definitions = {
117            ButtonName.OK: ButtonDefinition(
118                text=_("OK"), command=lambda: self._set_result(ButtonName.OK)
119            ),
120            ButtonName.CANCEL: ButtonDefinition(
121                text=_("Cancel"), command=lambda: self._set_result(ButtonName.CANCEL)
122            ),
123            ButtonName.CLOSE: ButtonDefinition(
124                text=_("Close"), command=lambda: self._set_result(ButtonName.CLOSE)
125            ),
126            ButtonName.YES: ButtonDefinition(
127                text=_("Yes"), command=lambda: self._set_result(ButtonName.YES)
128            ),
129            ButtonName.NO: ButtonDefinition(
130                text=_("No"), command=lambda: self._set_result(ButtonName.NO)
131            ),
132            ButtonName.RETRY: ButtonDefinition(
133                text=_("Retry"), command=lambda: self._set_result(ButtonName.RETRY)
134            ),
135        }
136
137    def _set_result(self, value: ButtonName) -> None:
138        """
139        Set the result to the ButtonName pressed.
140
141        Parameters
142        ----------
143        value : ButtonName
144            The ButtonName of the button pressed.
145        """
146        self.result = value
147        self.dismiss()
148
149    def dismiss(self, *_args) -> None:
150        """
151        Dismiss the window.
152        """
153        if self.toplevel is not None:
154            self.toplevel.grab_release()
155            self.toplevel.destroy()
enum ButtonName(enum.IntEnum):
27class ButtonName(IntEnum):
28    """
29    Standard button names.
30    """
31
32    NONE = 0
33    """None."""
34    OK = 1
35    """OK button."""
36    CANCEL = 2
37    """Cancel button."""
38    CLOSE = 3
39    """Close button."""
40    YES = 4
41    """Yes button."""
42    NO = 5
43    """No button."""
44    RETRY = 6
45    """Retry button."""

Standard button names.

NONE = <ButtonName.NONE: 0>

None.

OK = <ButtonName.OK: 1>

OK button.

CANCEL = <ButtonName.CANCEL: 2>

Cancel button.

CLOSE = <ButtonName.CLOSE: 3>

Close button.

YES = <ButtonName.YES: 4>

Yes button.

NO = <ButtonName.NO: 5>

No button.

RETRY = <ButtonName.RETRY: 6>

Retry button.

Inherited Members
enum.Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
@dataclasses.dataclass
class ButtonDefinition:
48@dataclasses.dataclass
49class ButtonDefinition:
50    """
51    Necessary metadata for creating a button.
52    """
53
54    text: str
55    """The text to appear on the button."""
56    command: Callable[[], Any]
57    """The command to call when the button is pressed."""

Necessary metadata for creating a button.

text: str

The text to appear on the button.

command: Callable[[], Any]

The command to call when the button is pressed.

class ButtonMixin:
 60class ButtonMixin:  # pylint: disable=too-few-public-methods
 61    """
 62    Mixin class for standard button sets in dialogs.
 63
 64    Attributes
 65    ----------
 66    result : ButtonName
 67        The button pressed to close the message box.
 68    button_definitions : dict[ButtonName, ButtonDefinition]
 69        A dictionary of standard buttons available to use.
 70    """
 71
 72    def __init__(self, *args, **kwargs) -> None:
 73        super().__init__(*args, **kwargs)
 74        self.result = ButtonName.NONE
 75        self.init_standard_buttons()
 76        self.toplevel: tk.Tk | tk.Toplevel | None = None
 77
 78    def add_buttons(
 79        self, frame: WidgetFrame, *, buttons: list[ButtonDefinition],
 80        default: int = -1
 81    ) -> None:
 82        """
 83        Add buttons to the provided frame at the bottom, justified right.
 84
 85        Parameters
 86        ----------
 87        frame : WidgetFrame (tk.Frame or ttk.Frame)
 88            The frame to which the buttons will be added
 89        buttons : list[`ButtonDefinition`]
 90            The list of buttons to add
 91        default : int
 92            The default button, displayed with style="Accent.TButton"
 93        """
 94        self.toplevel = frame.winfo_toplevel()
 95        max_columns, max_rows = frame.grid_size()
 96        buttonframe = ttk.Frame(frame)
 97        buttonframe.grid(
 98            row=max_rows, column=0, sticky=tk.E, columnspan=max_columns,
 99            pady=(0, INTERNAL_PAD)
100        )
101        for num, button in enumerate(buttons):
102            btn = ttk.Button(buttonframe, text=button.text, command=button.command)
103            if num == default:
104                btn.configure(style="Accent.TButton")
105                self.toplevel.bind(
106                    "<KeyPress-Return>", button.command  # type: ignore[arg-type]
107                )
108                self.toplevel.bind(
109                    "<KeyPress-KP_Enter>", button.command  # type: ignore[arg-type]
110                )
111            btn.grid(row=0, column=num, padx=INTERNAL_PAD/2)
112
113    def init_standard_buttons(self) -> None:
114        """
115        Define the button definitions.
116        """
117        self.button_definitions = {
118            ButtonName.OK: ButtonDefinition(
119                text=_("OK"), command=lambda: self._set_result(ButtonName.OK)
120            ),
121            ButtonName.CANCEL: ButtonDefinition(
122                text=_("Cancel"), command=lambda: self._set_result(ButtonName.CANCEL)
123            ),
124            ButtonName.CLOSE: ButtonDefinition(
125                text=_("Close"), command=lambda: self._set_result(ButtonName.CLOSE)
126            ),
127            ButtonName.YES: ButtonDefinition(
128                text=_("Yes"), command=lambda: self._set_result(ButtonName.YES)
129            ),
130            ButtonName.NO: ButtonDefinition(
131                text=_("No"), command=lambda: self._set_result(ButtonName.NO)
132            ),
133            ButtonName.RETRY: ButtonDefinition(
134                text=_("Retry"), command=lambda: self._set_result(ButtonName.RETRY)
135            ),
136        }
137
138    def _set_result(self, value: ButtonName) -> None:
139        """
140        Set the result to the ButtonName pressed.
141
142        Parameters
143        ----------
144        value : ButtonName
145            The ButtonName of the button pressed.
146        """
147        self.result = value
148        self.dismiss()
149
150    def dismiss(self, *_args) -> None:
151        """
152        Dismiss the window.
153        """
154        if self.toplevel is not None:
155            self.toplevel.grab_release()
156            self.toplevel.destroy()

Mixin class for standard button sets in dialogs.

Attributes
  • result (ButtonName): The button pressed to close the message box.
  • button_definitions (dict[ButtonName, ButtonDefinition]): A dictionary of standard buttons available to use.
def add_buttons( self, frame: ~WidgetFrame, *, buttons: list[ButtonDefinition], default: int = -1) -> None:
 78    def add_buttons(
 79        self, frame: WidgetFrame, *, buttons: list[ButtonDefinition],
 80        default: int = -1
 81    ) -> None:
 82        """
 83        Add buttons to the provided frame at the bottom, justified right.
 84
 85        Parameters
 86        ----------
 87        frame : WidgetFrame (tk.Frame or ttk.Frame)
 88            The frame to which the buttons will be added
 89        buttons : list[`ButtonDefinition`]
 90            The list of buttons to add
 91        default : int
 92            The default button, displayed with style="Accent.TButton"
 93        """
 94        self.toplevel = frame.winfo_toplevel()
 95        max_columns, max_rows = frame.grid_size()
 96        buttonframe = ttk.Frame(frame)
 97        buttonframe.grid(
 98            row=max_rows, column=0, sticky=tk.E, columnspan=max_columns,
 99            pady=(0, INTERNAL_PAD)
100        )
101        for num, button in enumerate(buttons):
102            btn = ttk.Button(buttonframe, text=button.text, command=button.command)
103            if num == default:
104                btn.configure(style="Accent.TButton")
105                self.toplevel.bind(
106                    "<KeyPress-Return>", button.command  # type: ignore[arg-type]
107                )
108                self.toplevel.bind(
109                    "<KeyPress-KP_Enter>", button.command  # type: ignore[arg-type]
110                )
111            btn.grid(row=0, column=num, padx=INTERNAL_PAD/2)

Add buttons to the provided frame at the bottom, justified right.

Parameters
  • frame (WidgetFrame (tk.Frame or ttk.Frame)): The frame to which the buttons will be added
  • buttons (list[ButtonDefinition]): The list of buttons to add
  • default (int): The default button, displayed with style="Accent.TButton"
def init_standard_buttons(self) -> None:
113    def init_standard_buttons(self) -> None:
114        """
115        Define the button definitions.
116        """
117        self.button_definitions = {
118            ButtonName.OK: ButtonDefinition(
119                text=_("OK"), command=lambda: self._set_result(ButtonName.OK)
120            ),
121            ButtonName.CANCEL: ButtonDefinition(
122                text=_("Cancel"), command=lambda: self._set_result(ButtonName.CANCEL)
123            ),
124            ButtonName.CLOSE: ButtonDefinition(
125                text=_("Close"), command=lambda: self._set_result(ButtonName.CLOSE)
126            ),
127            ButtonName.YES: ButtonDefinition(
128                text=_("Yes"), command=lambda: self._set_result(ButtonName.YES)
129            ),
130            ButtonName.NO: ButtonDefinition(
131                text=_("No"), command=lambda: self._set_result(ButtonName.NO)
132            ),
133            ButtonName.RETRY: ButtonDefinition(
134                text=_("Retry"), command=lambda: self._set_result(ButtonName.RETRY)
135            ),
136        }

Define the button definitions.

def dismiss(self, *_args) -> None:
150    def dismiss(self, *_args) -> None:
151        """
152        Dismiss the window.
153        """
154        if self.toplevel is not None:
155            self.toplevel.grab_release()
156            self.toplevel.destroy()

Dismiss the window.