Edit on GitHub

sysmon_pytk.application

System monitor application.

  1# SPDX-FileCopyrightText: © 2024 Stacey Adams <stacey.belle.rose@gmail.com>
  2# SPDX-License-Identifier: MIT
  3
  4"""
  5System monitor application.
  6"""
  7
  8from __future__ import annotations
  9
 10import sys
 11import tkinter as tk
 12from socket import gethostname
 13from tkinter import font, ttk
 14from typing import TYPE_CHECKING
 15
 16import psutil
 17
 18from . import _common, about
 19from .app_locale import get_translator, reload_translated_modules
 20from .file_utils import get_full_path, settings_path
 21from .modals import AboutDialog, AboutMetadata, LicenseMetadata, SettingsDialog
 22from .settings import Settings
 23from .style_manager import StyleManager
 24from .widgets import TempToolTip, ToolTip
 25from .widgets.meters import CpuMeter, DiskMeter, RamMeter, TempMeter
 26
 27if TYPE_CHECKING:
 28    from tkinter import Event, Variable
 29
 30_ = get_translator()
 31
 32APP_TITLE = _("System Monitor")
 33"""Application title, which appears in window title bars."""
 34
 35
 36class Application(tk.Tk):
 37    """
 38    System monitor application.
 39
 40    Toplevel widget of Tk which represents the main window of the application.
 41
 42    Attributes
 43    ----------
 44    _name : StringVar
 45        The hostname of the system.
 46    _ip_addr : StringVar
 47        The IP Address of the system.
 48    _uptime : StringVar
 49        The current uptime of the system.
 50    _processes : StringVar
 51        The current process count of the system.
 52    _update_job : str
 53        The ID of the scheduled job to update the screen.
 54    _menu_icons : dict[str, PhotoImage]
 55        The icons used in the menu.
 56    """
 57
 58    def __init__(self) -> None:
 59        """Return a new Toplevel Tk widget."""
 60        super().__init__()
 61        self.title(APP_TITLE)
 62        self.iconphoto(False, tk.PhotoImage(file=get_full_path("images/icon.png")))
 63        self.read_settings()
 64        self._name = tk.StringVar()
 65        self._ip_addr = tk.StringVar()
 66        self._uptime = tk.StringVar()
 67        self._processes = tk.StringVar()
 68        self._update_job: str | None = None
 69        StyleManager.init_theme(self, self.settings)
 70        self.create_widgets()
 71        self._load_menu_images()
 72        self.build_menu()
 73        self.bind_events()
 74        self.update()
 75        self.minsize(self.winfo_width(), self.winfo_height())
 76        self.update_screen()
 77
 78    def read_settings(self, *_args) -> None:
 79        """
 80        Read and process application settings from configuration file.
 81
 82        This method is triggered on a `<<SettingsChanged>>` event, and is part
 83        of the application startup program flow.
 84        """
 85        self.settings = Settings(settings_path())
 86        self.call("wm", "attributes", ".", "-topmost", f"{self.settings.always_on_top}")
 87
 88    def create_widgets(self) -> None:
 89        """
 90        Create the widgets to be displayed in the main application window.
 91        """
 92        frame = ttk.Frame(self)
 93        frame.grid(sticky=tk.NSEW)
 94        for row in [1, 2]:
 95            frame.rowconfigure(row, weight=1)
 96        for column in [1, 2, 3, 4]:
 97            frame.columnconfigure(column, weight=1)
 98        self._add_variable_label(frame, self._name, 1, 1)
 99        ip_label = self._add_variable_label(frame, self._ip_addr, 1, 2)
100        ToolTip(ip_label, _("Click to copy IP Address to clipboard"))
101        ip_label.bind("<Button-1>", self.on_click_ip_address)
102        self._add_variable_label(frame, self._processes, 1, 3)
103        self._add_variable_label(frame, self._uptime, 1, 4)
104        self._add_meters(frame)
105        self._add_sizegrip(frame)
106
107    @classmethod
108    def _add_variable_label(
109        cls, frame: ttk.Frame, textvariable: Variable, row: int, column: int
110    ) -> ttk.Label:
111        base_font = StyleManager.get_base_font()
112        label = ttk.Label(
113            frame, textvariable=textvariable, font=base_font, anchor=tk.CENTER
114        )
115        label.grid(
116            row=row, column=column, sticky=tk.NSEW, pady=(_common.INTERNAL_PAD, 0)
117        )
118        return label
119
120    def _add_meters(self, frame: ttk.Frame) -> None:
121        CpuMeter(frame, width=220, height=165).grid(
122            row=2, column=1, sticky=tk.NSEW, pady=(_common.INTERNAL_PAD, 0)
123        )
124        TempMeter(frame, width=220, height=165).grid(
125            row=2, column=2, sticky=tk.NSEW, pady=(_common.INTERNAL_PAD, 0)
126        )
127        RamMeter(frame, width=220, height=165).grid(
128            row=2, column=3, sticky=tk.NSEW, pady=(_common.INTERNAL_PAD, 0)
129        )
130        DiskMeter(frame, width=220, height=165).grid(
131            row=2, column=4, sticky=tk.NSEW, pady=(_common.INTERNAL_PAD, 0)
132        )
133
134    @classmethod
135    def _add_sizegrip(cls, frame: ttk.Frame) -> None:
136        ttk.Sizegrip(frame).grid(
137            row=3, column=4, sticky=tk.SE, padx=_common.INTERNAL_PAD/2,
138            pady=(0, _common.INTERNAL_PAD/2)
139        )
140
141    def _load_menu_images(self) -> None:
142        self._menu_icons = {
143            "about": tk.PhotoImage(file=get_full_path("images/internet-group-chat.png")),
144            "preferences": tk.PhotoImage(file=get_full_path("images/preferences-system.png")),
145            "restart": tk.PhotoImage(file=get_full_path("images/view-refresh.png")),
146            "quit": tk.PhotoImage(file=get_full_path("images/blank.png"))
147        }
148
149    def build_menu(self) -> None:
150        """
151        Build the application menu and bind associated keypress events to functions.
152        """
153        top = self.winfo_toplevel()
154        top.rowconfigure(0, weight=1)
155        top.columnconfigure(0, weight=1)
156        menu_bar = tk.Menu(
157            top, relief=tk.FLAT, activeborderwidth=0,
158            font=font.nametofont("TkMenuFont"),
159            background=StyleManager.get_menu_background(),
160            foreground=StyleManager.get_menu_foreground()
161        )
162        file_menu = tk.Menu(
163            menu_bar, relief=tk.FLAT, activeborderwidth=0,
164            font=font.nametofont("TkMenuFont"),
165            background=StyleManager.get_menu_background(),
166            foreground=StyleManager.get_menu_foreground()
167        )
168
169        menu_bar.add_cascade(
170            label=_("File"), menu=file_menu,
171        )
172        file_menu.add_command(
173            label=_("About"), accelerator=_("Ctrl+A"), command=self.on_about,
174            compound=tk.LEFT, image=self._menu_icons["about"]
175        )
176        file_menu.add_command(
177            label=_("Preferences"), accelerator=_("Ctrl+Shift+P"), command=self.on_settings,
178            compound=tk.LEFT, image=self._menu_icons["preferences"]
179        )
180        file_menu.add_command(
181            label=_("Restart"), accelerator=_("Ctrl+R"), command=self.on_restart,
182            compound=tk.LEFT, image=self._menu_icons["restart"]
183        )
184        file_menu.add_separator()
185        file_menu.add_command(
186            label=_("Quit"), accelerator=_("Ctrl+Q"), command=lambda: sys.exit(0),
187            compound=tk.LEFT, image=self._menu_icons["quit"]
188        )
189        top["menu"] = menu_bar
190        # bind keypress events for menu here
191        self.bind("<Control-KeyPress-a>", self.on_about)
192        self.bind("<Control-Shift-KeyPress-P>", self.on_settings)
193        self.bind("<Control-KeyPress-r>", self.on_restart)
194        self.bind("<Control-KeyPress-q>", lambda _x: sys.exit(0))
195
196    def bind_events(self) -> None:
197        """
198        Set up bindings for custom application events.
199        """
200        self.bind("<<SettingsChanged>>", self.read_settings)
201        self.bind("<<LanguageChanged>>", self.on_language)
202        self.bind("<<FontChanged>>", self.on_restart)
203
204    def on_click_ip_address(self, event: Event) -> None:
205        """
206        Copy the IP address to the clipboard.
207        """
208        self.clipboard_clear()
209        self.clipboard_append(_common.net_addr())
210        TempToolTip(self, _("Copied!"), (event.x_root, event.y_root), 5000)
211
212    def on_language(self, *_args) -> None:
213        """
214        Update the selected translation after the user selects a new language.
215
216        This method is triggered on a `<<LanguageChanged>>` event.
217        """
218        reload_translated_modules()
219        self.on_restart()
220
221    def on_about(self, *_args) -> None:
222        """
223        Open the About modal dialog box.
224        """
225        metadata = AboutMetadata(
226            about.__app_name__, about.__version__, about.__author_name__,
227            about.__copyright_year__, about.__summary__, about.__url__,
228            LicenseMetadata(
229                about.__full_license__, about.__license__, about.__license_url__
230            )
231        )
232        AboutDialog(self, metadata, iconpath=get_full_path("images/icon.png"))
233
234    def on_restart(self, *_args) -> None:
235        """
236        Restart the application.
237
238        This method is triggered on a `<<FontChanged>>` event, and is part of
239        the program flow for `<<LanguageChanged>>` event processing.
240        """
241        if self._update_job is not None:
242            self.after_cancel(self._update_job)
243            self._update_job = None
244        self.destroy()
245        self.__init__()  # type: ignore[misc] # pylint: disable=unnecessary-dunder-call
246
247    def on_settings(self, *_args) -> None:
248        """
249        Open the Settings modal dialog and process any changes afterward.
250        """
251        SettingsDialog(
252            self.settings, self, _("{} Preferences").format(APP_TITLE),
253            iconpath=get_full_path("images/icon.png")
254        )
255        StyleManager.update_by_dark_mode(self, self.settings)
256
257    def update_screen(self) -> None:
258        """
259        Periodically update the screen to refresh the displayed data.
260
261        This method reschedules itself after `sysmon_pytk._common.REFRESH_INTERVAL`
262        milliseconds.
263        """
264        self._name.set(_("Hostname: {}").format(gethostname()))
265        self._ip_addr.set(_("IP Address: {}").format(_common.net_addr()))
266        self._processes.set(_("Processes: {}").format(len(psutil.pids())))
267        self._uptime.set(_("Uptime: {}").format(_common.system_uptime()))
268        self._update_job = self.after(
269            _common.REFRESH_INTERVAL, self.update_screen
270        )
APP_TITLE = 'System Monitor'

Application title, which appears in window title bars.

class Application(tkinter.Tk):
 37class Application(tk.Tk):
 38    """
 39    System monitor application.
 40
 41    Toplevel widget of Tk which represents the main window of the application.
 42
 43    Attributes
 44    ----------
 45    _name : StringVar
 46        The hostname of the system.
 47    _ip_addr : StringVar
 48        The IP Address of the system.
 49    _uptime : StringVar
 50        The current uptime of the system.
 51    _processes : StringVar
 52        The current process count of the system.
 53    _update_job : str
 54        The ID of the scheduled job to update the screen.
 55    _menu_icons : dict[str, PhotoImage]
 56        The icons used in the menu.
 57    """
 58
 59    def __init__(self) -> None:
 60        """Return a new Toplevel Tk widget."""
 61        super().__init__()
 62        self.title(APP_TITLE)
 63        self.iconphoto(False, tk.PhotoImage(file=get_full_path("images/icon.png")))
 64        self.read_settings()
 65        self._name = tk.StringVar()
 66        self._ip_addr = tk.StringVar()
 67        self._uptime = tk.StringVar()
 68        self._processes = tk.StringVar()
 69        self._update_job: str | None = None
 70        StyleManager.init_theme(self, self.settings)
 71        self.create_widgets()
 72        self._load_menu_images()
 73        self.build_menu()
 74        self.bind_events()
 75        self.update()
 76        self.minsize(self.winfo_width(), self.winfo_height())
 77        self.update_screen()
 78
 79    def read_settings(self, *_args) -> None:
 80        """
 81        Read and process application settings from configuration file.
 82
 83        This method is triggered on a `<<SettingsChanged>>` event, and is part
 84        of the application startup program flow.
 85        """
 86        self.settings = Settings(settings_path())
 87        self.call("wm", "attributes", ".", "-topmost", f"{self.settings.always_on_top}")
 88
 89    def create_widgets(self) -> None:
 90        """
 91        Create the widgets to be displayed in the main application window.
 92        """
 93        frame = ttk.Frame(self)
 94        frame.grid(sticky=tk.NSEW)
 95        for row in [1, 2]:
 96            frame.rowconfigure(row, weight=1)
 97        for column in [1, 2, 3, 4]:
 98            frame.columnconfigure(column, weight=1)
 99        self._add_variable_label(frame, self._name, 1, 1)
100        ip_label = self._add_variable_label(frame, self._ip_addr, 1, 2)
101        ToolTip(ip_label, _("Click to copy IP Address to clipboard"))
102        ip_label.bind("<Button-1>", self.on_click_ip_address)
103        self._add_variable_label(frame, self._processes, 1, 3)
104        self._add_variable_label(frame, self._uptime, 1, 4)
105        self._add_meters(frame)
106        self._add_sizegrip(frame)
107
108    @classmethod
109    def _add_variable_label(
110        cls, frame: ttk.Frame, textvariable: Variable, row: int, column: int
111    ) -> ttk.Label:
112        base_font = StyleManager.get_base_font()
113        label = ttk.Label(
114            frame, textvariable=textvariable, font=base_font, anchor=tk.CENTER
115        )
116        label.grid(
117            row=row, column=column, sticky=tk.NSEW, pady=(_common.INTERNAL_PAD, 0)
118        )
119        return label
120
121    def _add_meters(self, frame: ttk.Frame) -> None:
122        CpuMeter(frame, width=220, height=165).grid(
123            row=2, column=1, sticky=tk.NSEW, pady=(_common.INTERNAL_PAD, 0)
124        )
125        TempMeter(frame, width=220, height=165).grid(
126            row=2, column=2, sticky=tk.NSEW, pady=(_common.INTERNAL_PAD, 0)
127        )
128        RamMeter(frame, width=220, height=165).grid(
129            row=2, column=3, sticky=tk.NSEW, pady=(_common.INTERNAL_PAD, 0)
130        )
131        DiskMeter(frame, width=220, height=165).grid(
132            row=2, column=4, sticky=tk.NSEW, pady=(_common.INTERNAL_PAD, 0)
133        )
134
135    @classmethod
136    def _add_sizegrip(cls, frame: ttk.Frame) -> None:
137        ttk.Sizegrip(frame).grid(
138            row=3, column=4, sticky=tk.SE, padx=_common.INTERNAL_PAD/2,
139            pady=(0, _common.INTERNAL_PAD/2)
140        )
141
142    def _load_menu_images(self) -> None:
143        self._menu_icons = {
144            "about": tk.PhotoImage(file=get_full_path("images/internet-group-chat.png")),
145            "preferences": tk.PhotoImage(file=get_full_path("images/preferences-system.png")),
146            "restart": tk.PhotoImage(file=get_full_path("images/view-refresh.png")),
147            "quit": tk.PhotoImage(file=get_full_path("images/blank.png"))
148        }
149
150    def build_menu(self) -> None:
151        """
152        Build the application menu and bind associated keypress events to functions.
153        """
154        top = self.winfo_toplevel()
155        top.rowconfigure(0, weight=1)
156        top.columnconfigure(0, weight=1)
157        menu_bar = tk.Menu(
158            top, relief=tk.FLAT, activeborderwidth=0,
159            font=font.nametofont("TkMenuFont"),
160            background=StyleManager.get_menu_background(),
161            foreground=StyleManager.get_menu_foreground()
162        )
163        file_menu = tk.Menu(
164            menu_bar, relief=tk.FLAT, activeborderwidth=0,
165            font=font.nametofont("TkMenuFont"),
166            background=StyleManager.get_menu_background(),
167            foreground=StyleManager.get_menu_foreground()
168        )
169
170        menu_bar.add_cascade(
171            label=_("File"), menu=file_menu,
172        )
173        file_menu.add_command(
174            label=_("About"), accelerator=_("Ctrl+A"), command=self.on_about,
175            compound=tk.LEFT, image=self._menu_icons["about"]
176        )
177        file_menu.add_command(
178            label=_("Preferences"), accelerator=_("Ctrl+Shift+P"), command=self.on_settings,
179            compound=tk.LEFT, image=self._menu_icons["preferences"]
180        )
181        file_menu.add_command(
182            label=_("Restart"), accelerator=_("Ctrl+R"), command=self.on_restart,
183            compound=tk.LEFT, image=self._menu_icons["restart"]
184        )
185        file_menu.add_separator()
186        file_menu.add_command(
187            label=_("Quit"), accelerator=_("Ctrl+Q"), command=lambda: sys.exit(0),
188            compound=tk.LEFT, image=self._menu_icons["quit"]
189        )
190        top["menu"] = menu_bar
191        # bind keypress events for menu here
192        self.bind("<Control-KeyPress-a>", self.on_about)
193        self.bind("<Control-Shift-KeyPress-P>", self.on_settings)
194        self.bind("<Control-KeyPress-r>", self.on_restart)
195        self.bind("<Control-KeyPress-q>", lambda _x: sys.exit(0))
196
197    def bind_events(self) -> None:
198        """
199        Set up bindings for custom application events.
200        """
201        self.bind("<<SettingsChanged>>", self.read_settings)
202        self.bind("<<LanguageChanged>>", self.on_language)
203        self.bind("<<FontChanged>>", self.on_restart)
204
205    def on_click_ip_address(self, event: Event) -> None:
206        """
207        Copy the IP address to the clipboard.
208        """
209        self.clipboard_clear()
210        self.clipboard_append(_common.net_addr())
211        TempToolTip(self, _("Copied!"), (event.x_root, event.y_root), 5000)
212
213    def on_language(self, *_args) -> None:
214        """
215        Update the selected translation after the user selects a new language.
216
217        This method is triggered on a `<<LanguageChanged>>` event.
218        """
219        reload_translated_modules()
220        self.on_restart()
221
222    def on_about(self, *_args) -> None:
223        """
224        Open the About modal dialog box.
225        """
226        metadata = AboutMetadata(
227            about.__app_name__, about.__version__, about.__author_name__,
228            about.__copyright_year__, about.__summary__, about.__url__,
229            LicenseMetadata(
230                about.__full_license__, about.__license__, about.__license_url__
231            )
232        )
233        AboutDialog(self, metadata, iconpath=get_full_path("images/icon.png"))
234
235    def on_restart(self, *_args) -> None:
236        """
237        Restart the application.
238
239        This method is triggered on a `<<FontChanged>>` event, and is part of
240        the program flow for `<<LanguageChanged>>` event processing.
241        """
242        if self._update_job is not None:
243            self.after_cancel(self._update_job)
244            self._update_job = None
245        self.destroy()
246        self.__init__()  # type: ignore[misc] # pylint: disable=unnecessary-dunder-call
247
248    def on_settings(self, *_args) -> None:
249        """
250        Open the Settings modal dialog and process any changes afterward.
251        """
252        SettingsDialog(
253            self.settings, self, _("{} Preferences").format(APP_TITLE),
254            iconpath=get_full_path("images/icon.png")
255        )
256        StyleManager.update_by_dark_mode(self, self.settings)
257
258    def update_screen(self) -> None:
259        """
260        Periodically update the screen to refresh the displayed data.
261
262        This method reschedules itself after `sysmon_pytk._common.REFRESH_INTERVAL`
263        milliseconds.
264        """
265        self._name.set(_("Hostname: {}").format(gethostname()))
266        self._ip_addr.set(_("IP Address: {}").format(_common.net_addr()))
267        self._processes.set(_("Processes: {}").format(len(psutil.pids())))
268        self._uptime.set(_("Uptime: {}").format(_common.system_uptime()))
269        self._update_job = self.after(
270            _common.REFRESH_INTERVAL, self.update_screen
271        )

System monitor application.

Toplevel widget of Tk which represents the main window of the application.

Attributes
  • _name (StringVar): The hostname of the system.
  • _ip_addr (StringVar): The IP Address of the system.
  • _uptime (StringVar): The current uptime of the system.
  • _processes (StringVar): The current process count of the system.
  • _update_job (str): The ID of the scheduled job to update the screen.
  • _menu_icons (dict[str, PhotoImage]): The icons used in the menu.
Application()
59    def __init__(self) -> None:
60        """Return a new Toplevel Tk widget."""
61        super().__init__()
62        self.title(APP_TITLE)
63        self.iconphoto(False, tk.PhotoImage(file=get_full_path("images/icon.png")))
64        self.read_settings()
65        self._name = tk.StringVar()
66        self._ip_addr = tk.StringVar()
67        self._uptime = tk.StringVar()
68        self._processes = tk.StringVar()
69        self._update_job: str | None = None
70        StyleManager.init_theme(self, self.settings)
71        self.create_widgets()
72        self._load_menu_images()
73        self.build_menu()
74        self.bind_events()
75        self.update()
76        self.minsize(self.winfo_width(), self.winfo_height())
77        self.update_screen()

Return a new Toplevel Tk widget.

def read_settings(self, *_args) -> None:
79    def read_settings(self, *_args) -> None:
80        """
81        Read and process application settings from configuration file.
82
83        This method is triggered on a `<<SettingsChanged>>` event, and is part
84        of the application startup program flow.
85        """
86        self.settings = Settings(settings_path())
87        self.call("wm", "attributes", ".", "-topmost", f"{self.settings.always_on_top}")

Read and process application settings from configuration file.

This method is triggered on a <<SettingsChanged>> event, and is part of the application startup program flow.

def create_widgets(self) -> None:
 89    def create_widgets(self) -> None:
 90        """
 91        Create the widgets to be displayed in the main application window.
 92        """
 93        frame = ttk.Frame(self)
 94        frame.grid(sticky=tk.NSEW)
 95        for row in [1, 2]:
 96            frame.rowconfigure(row, weight=1)
 97        for column in [1, 2, 3, 4]:
 98            frame.columnconfigure(column, weight=1)
 99        self._add_variable_label(frame, self._name, 1, 1)
100        ip_label = self._add_variable_label(frame, self._ip_addr, 1, 2)
101        ToolTip(ip_label, _("Click to copy IP Address to clipboard"))
102        ip_label.bind("<Button-1>", self.on_click_ip_address)
103        self._add_variable_label(frame, self._processes, 1, 3)
104        self._add_variable_label(frame, self._uptime, 1, 4)
105        self._add_meters(frame)
106        self._add_sizegrip(frame)

Create the widgets to be displayed in the main application window.

def build_menu(self) -> None:
150    def build_menu(self) -> None:
151        """
152        Build the application menu and bind associated keypress events to functions.
153        """
154        top = self.winfo_toplevel()
155        top.rowconfigure(0, weight=1)
156        top.columnconfigure(0, weight=1)
157        menu_bar = tk.Menu(
158            top, relief=tk.FLAT, activeborderwidth=0,
159            font=font.nametofont("TkMenuFont"),
160            background=StyleManager.get_menu_background(),
161            foreground=StyleManager.get_menu_foreground()
162        )
163        file_menu = tk.Menu(
164            menu_bar, relief=tk.FLAT, activeborderwidth=0,
165            font=font.nametofont("TkMenuFont"),
166            background=StyleManager.get_menu_background(),
167            foreground=StyleManager.get_menu_foreground()
168        )
169
170        menu_bar.add_cascade(
171            label=_("File"), menu=file_menu,
172        )
173        file_menu.add_command(
174            label=_("About"), accelerator=_("Ctrl+A"), command=self.on_about,
175            compound=tk.LEFT, image=self._menu_icons["about"]
176        )
177        file_menu.add_command(
178            label=_("Preferences"), accelerator=_("Ctrl+Shift+P"), command=self.on_settings,
179            compound=tk.LEFT, image=self._menu_icons["preferences"]
180        )
181        file_menu.add_command(
182            label=_("Restart"), accelerator=_("Ctrl+R"), command=self.on_restart,
183            compound=tk.LEFT, image=self._menu_icons["restart"]
184        )
185        file_menu.add_separator()
186        file_menu.add_command(
187            label=_("Quit"), accelerator=_("Ctrl+Q"), command=lambda: sys.exit(0),
188            compound=tk.LEFT, image=self._menu_icons["quit"]
189        )
190        top["menu"] = menu_bar
191        # bind keypress events for menu here
192        self.bind("<Control-KeyPress-a>", self.on_about)
193        self.bind("<Control-Shift-KeyPress-P>", self.on_settings)
194        self.bind("<Control-KeyPress-r>", self.on_restart)
195        self.bind("<Control-KeyPress-q>", lambda _x: sys.exit(0))

Build the application menu and bind associated keypress events to functions.

def bind_events(self) -> None:
197    def bind_events(self) -> None:
198        """
199        Set up bindings for custom application events.
200        """
201        self.bind("<<SettingsChanged>>", self.read_settings)
202        self.bind("<<LanguageChanged>>", self.on_language)
203        self.bind("<<FontChanged>>", self.on_restart)

Set up bindings for custom application events.

def on_click_ip_address(self, event: tkinter.Event) -> None:
205    def on_click_ip_address(self, event: Event) -> None:
206        """
207        Copy the IP address to the clipboard.
208        """
209        self.clipboard_clear()
210        self.clipboard_append(_common.net_addr())
211        TempToolTip(self, _("Copied!"), (event.x_root, event.y_root), 5000)

Copy the IP address to the clipboard.

def on_language(self, *_args) -> None:
213    def on_language(self, *_args) -> None:
214        """
215        Update the selected translation after the user selects a new language.
216
217        This method is triggered on a `<<LanguageChanged>>` event.
218        """
219        reload_translated_modules()
220        self.on_restart()

Update the selected translation after the user selects a new language.

This method is triggered on a <<LanguageChanged>> event.

def on_about(self, *_args) -> None:
222    def on_about(self, *_args) -> None:
223        """
224        Open the About modal dialog box.
225        """
226        metadata = AboutMetadata(
227            about.__app_name__, about.__version__, about.__author_name__,
228            about.__copyright_year__, about.__summary__, about.__url__,
229            LicenseMetadata(
230                about.__full_license__, about.__license__, about.__license_url__
231            )
232        )
233        AboutDialog(self, metadata, iconpath=get_full_path("images/icon.png"))

Open the About modal dialog box.

def on_restart(self, *_args) -> None:
235    def on_restart(self, *_args) -> None:
236        """
237        Restart the application.
238
239        This method is triggered on a `<<FontChanged>>` event, and is part of
240        the program flow for `<<LanguageChanged>>` event processing.
241        """
242        if self._update_job is not None:
243            self.after_cancel(self._update_job)
244            self._update_job = None
245        self.destroy()
246        self.__init__()  # type: ignore[misc] # pylint: disable=unnecessary-dunder-call

Restart the application.

This method is triggered on a <<FontChanged>> event, and is part of the program flow for <<LanguageChanged>> event processing.

def on_settings(self, *_args) -> None:
248    def on_settings(self, *_args) -> None:
249        """
250        Open the Settings modal dialog and process any changes afterward.
251        """
252        SettingsDialog(
253            self.settings, self, _("{} Preferences").format(APP_TITLE),
254            iconpath=get_full_path("images/icon.png")
255        )
256        StyleManager.update_by_dark_mode(self, self.settings)

Open the Settings modal dialog and process any changes afterward.

def update_screen(self) -> None:
258    def update_screen(self) -> None:
259        """
260        Periodically update the screen to refresh the displayed data.
261
262        This method reschedules itself after `sysmon_pytk._common.REFRESH_INTERVAL`
263        milliseconds.
264        """
265        self._name.set(_("Hostname: {}").format(gethostname()))
266        self._ip_addr.set(_("IP Address: {}").format(_common.net_addr()))
267        self._processes.set(_("Processes: {}").format(len(psutil.pids())))
268        self._uptime.set(_("Uptime: {}").format(_common.system_uptime()))
269        self._update_job = self.after(
270            _common.REFRESH_INTERVAL, self.update_screen
271        )

Periodically update the screen to refresh the displayed data.

This method reschedules itself after sysmon_pytk._common.REFRESH_INTERVAL milliseconds.

Inherited Members
tkinter.Tk
destroy
readprofile
report_callback_exception
tkinter.Misc
deletecommand
tk_strictMotif
tk_bisque
tk_setPalette
wait_variable
waitvar
wait_window
wait_visibility
setvar
getvar
getboolean
focus_set
focus
focus_force
focus_get
focus_displayof
focus_lastfor
tk_focusFollowsMouse
tk_focusNext
tk_focusPrev
after
after_idle
after_cancel
bell
clipboard_get
clipboard_clear
clipboard_append
grab_current
grab_release
grab_set
grab_set_global
grab_status
option_add
option_clear
option_get
option_readfile
selection_clear
selection_get
selection_handle
selection_own
selection_own_get
send
lower
tkraise
lift
winfo_atom
winfo_atomname
winfo_cells
winfo_children
winfo_class
winfo_colormapfull
winfo_containing
winfo_depth
winfo_exists
winfo_fpixels
winfo_geometry
winfo_height
winfo_id
winfo_interps
winfo_ismapped
winfo_manager
winfo_name
winfo_parent
winfo_pathname
winfo_pixels
winfo_pointerx
winfo_pointerxy
winfo_pointery
winfo_reqheight
winfo_reqwidth
winfo_rgb
winfo_rootx
winfo_rooty
winfo_screen
winfo_screencells
winfo_screendepth
winfo_screenheight
winfo_screenmmheight
winfo_screenmmwidth
winfo_screenvisual
winfo_screenwidth
winfo_server
winfo_toplevel
winfo_viewable
winfo_visual
winfo_visualid
winfo_visualsavailable
winfo_vrootheight
winfo_vrootwidth
winfo_vrootx
winfo_vrooty
winfo_width
winfo_x
winfo_y
update
update_idletasks
bindtags
bind
unbind
bind_all
unbind_all
bind_class
unbind_class
mainloop
quit
nametowidget
register
configure
config
cget
keys
pack_propagate
propagate
pack_slaves
slaves
place_slaves
grid_anchor
anchor
grid_bbox
bbox
grid_columnconfigure
columnconfigure
grid_location
grid_propagate
grid_rowconfigure
rowconfigure
grid_size
size
grid_slaves
event_add
event_delete
event_generate
event_info
image_names
image_types
tkinter.Wm
wm_aspect
aspect
wm_attributes
attributes
wm_client
client
wm_colormapwindows
colormapwindows
wm_command
command
wm_deiconify
deiconify
wm_focusmodel
focusmodel
wm_forget
forget
wm_frame
frame
wm_geometry
geometry
wm_grid
grid
wm_group
group
wm_iconbitmap
iconbitmap
wm_iconify
iconify
wm_iconmask
iconmask
wm_iconname
iconname
wm_iconphoto
iconphoto
wm_iconposition
iconposition
wm_iconwindow
iconwindow
wm_manage
manage
wm_maxsize
maxsize
wm_minsize
minsize
wm_overrideredirect
overrideredirect
wm_positionfrom
positionfrom
wm_protocol
protocol
wm_resizable
resizable
wm_sizefrom
sizefrom
wm_state
state
wm_title
title
wm_transient
transient
wm_withdraw
withdraw