sevaht-gui 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,10 @@
1
+ __pycache__/
2
+ .ipynb_checkpoints/
3
+ .mypy_cache/
4
+ .pytest_cache/
5
+ .ruff_cache/
6
+ .venv/
7
+ build/
8
+ dist/
9
+ docs/_build/
10
+ .claude/settings.local.json
@@ -0,0 +1,24 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <https://unlicense.org>
@@ -0,0 +1,104 @@
1
+ Metadata-Version: 2.4
2
+ Name: sevaht-gui
3
+ Version: 1.0.0
4
+ Summary: Cross-platform tray icons and a tkinter app base for small GUIs.
5
+ Project-URL: Homepage, https://github.com/sevaht/sevaht-gui
6
+ Project-URL: Documentation, https://sevaht.github.io/sevaht-gui/
7
+ Project-URL: Source, https://github.com/sevaht/sevaht-gui
8
+ Project-URL: Issues, https://github.com/sevaht/sevaht-gui/issues
9
+ Author-email: Jacob McIntosh <nacitar.sevaht@gmail.com>
10
+ License-Expression: Unlicense
11
+ License-File: LICENSE
12
+ Keywords: gui,pystray,status-icon,statusnotifieritem,system-tray,tkinter,tray,xembed
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Operating System :: Microsoft :: Windows
16
+ Classifier: Operating System :: POSIX :: Linux
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Topic :: Desktop Environment
23
+ Classifier: Topic :: Software Development :: Libraries
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.12
26
+ Requires-Dist: dbus-next>=0.2.3; sys_platform == 'linux'
27
+ Requires-Dist: pillow>=12.2.0
28
+ Requires-Dist: pystray>=0.19.5; sys_platform == 'win32'
29
+ Requires-Dist: python-xlib>=0.33; sys_platform == 'linux'
30
+ Requires-Dist: sevaht-utility>=1.0.0
31
+ Description-Content-Type: text/markdown
32
+
33
+ # sevaht-gui
34
+
35
+ Small cross-platform GUI building blocks: a system-tray icon and a tkinter
36
+ application base, shared across Sevaht desktop tools.
37
+
38
+ ## Tray icon
39
+
40
+ `create_tray_icon` returns a single `TrayIcon` interface backed by the right
41
+ implementation for the platform:
42
+
43
+ - **Windows** — [pystray](https://pypi.org/project/pystray/).
44
+ - **Linux** — a `StatusNotifierItem` (D-Bus) item when a usable SNI host is
45
+ present (KDE/GNOME, Wayland, or fluxbox with `snixembed`), falling back to a
46
+ self-contained XEmbed icon on bare X11 window managers with no SNI host.
47
+
48
+ The feature model is deliberately small: a default **activate** action
49
+ (left-click, and the default menu item where a menu is available) and a
50
+ **quit** action. Backends that can show a menu (pystray, SNI) offer both on
51
+ right-click; the menu-less XEmbed backend maps right-click straight to quit.
52
+
53
+ The icon is any **`IconSource`** — a prepared `PIL.Image`, a path to an image
54
+ file (both static; the common case), or a renderer `Callable[[int], Image]`
55
+ for apps that draw their own artwork. The tooltip and the icon can be changed
56
+ live (`set_icon` accepts any `IconSource`), so an app can reflect state either
57
+ by swapping prepared images or by re-rendering.
58
+
59
+ macOS is intentionally unsupported: a real menu-bar item there needs a single
60
+ main-thread Cocoa loop shared with tk (custom PyObjC), which the threaded model
61
+ used here cannot provide.
62
+
63
+ ## TkApp
64
+
65
+ `TkApp` owns a `tkinter.Tk` root and runs its `mainloop()` on the main thread,
66
+ running the tray icon's own event loop on a worker thread. Work originating off
67
+ the UI thread (e.g. tray menu callbacks) is marshalled back with
68
+ `run_on_ui_thread` / `call_on_ui_thread`. `show()` / `hide()` and the
69
+ `run(start_hidden=...)` flag manage window visibility (all thread-safe), and
70
+ `set_window_icon` accepts the same `IconSource` as the tray.
71
+
72
+ ```python
73
+ from sevaht_gui import TkApp, create_tray_icon
74
+
75
+ app = TkApp()
76
+ app.root.title("Example")
77
+ app.set_window_icon("icon.png") # static file; or a PIL image / renderer
78
+
79
+ icon = create_tray_icon(
80
+ "example",
81
+ "Example",
82
+ "icon.png", # any IconSource: path, PIL image, or renderer
83
+ on_activate=app.show,
84
+ on_quit=app.stop,
85
+ )
86
+ app.run(icon, start_hidden=True) # only the tray shows until activated
87
+ ```
88
+
89
+ The library manages the tray lifecycle, window visibility, and threading;
90
+ choosing and (re)drawing the artwork is left to the application.
91
+
92
+ ## Documentation
93
+
94
+ Full documentation lives in `docs/` and is published to GitHub Pages:
95
+ <https://sevaht.github.io/sevaht-gui/>.
96
+
97
+ ### Building the docs locally
98
+
99
+ ```console
100
+ $ uv run --group docs sphinx-build -b html docs docs/_build/html
101
+ ```
102
+
103
+ Then open `docs/_build/html/index.html`. (Publishing to GitHub Pages requires
104
+ enabling Pages with the "GitHub Actions" source in the repository settings.)
@@ -0,0 +1,72 @@
1
+ # sevaht-gui
2
+
3
+ Small cross-platform GUI building blocks: a system-tray icon and a tkinter
4
+ application base, shared across Sevaht desktop tools.
5
+
6
+ ## Tray icon
7
+
8
+ `create_tray_icon` returns a single `TrayIcon` interface backed by the right
9
+ implementation for the platform:
10
+
11
+ - **Windows** — [pystray](https://pypi.org/project/pystray/).
12
+ - **Linux** — a `StatusNotifierItem` (D-Bus) item when a usable SNI host is
13
+ present (KDE/GNOME, Wayland, or fluxbox with `snixembed`), falling back to a
14
+ self-contained XEmbed icon on bare X11 window managers with no SNI host.
15
+
16
+ The feature model is deliberately small: a default **activate** action
17
+ (left-click, and the default menu item where a menu is available) and a
18
+ **quit** action. Backends that can show a menu (pystray, SNI) offer both on
19
+ right-click; the menu-less XEmbed backend maps right-click straight to quit.
20
+
21
+ The icon is any **`IconSource`** — a prepared `PIL.Image`, a path to an image
22
+ file (both static; the common case), or a renderer `Callable[[int], Image]`
23
+ for apps that draw their own artwork. The tooltip and the icon can be changed
24
+ live (`set_icon` accepts any `IconSource`), so an app can reflect state either
25
+ by swapping prepared images or by re-rendering.
26
+
27
+ macOS is intentionally unsupported: a real menu-bar item there needs a single
28
+ main-thread Cocoa loop shared with tk (custom PyObjC), which the threaded model
29
+ used here cannot provide.
30
+
31
+ ## TkApp
32
+
33
+ `TkApp` owns a `tkinter.Tk` root and runs its `mainloop()` on the main thread,
34
+ running the tray icon's own event loop on a worker thread. Work originating off
35
+ the UI thread (e.g. tray menu callbacks) is marshalled back with
36
+ `run_on_ui_thread` / `call_on_ui_thread`. `show()` / `hide()` and the
37
+ `run(start_hidden=...)` flag manage window visibility (all thread-safe), and
38
+ `set_window_icon` accepts the same `IconSource` as the tray.
39
+
40
+ ```python
41
+ from sevaht_gui import TkApp, create_tray_icon
42
+
43
+ app = TkApp()
44
+ app.root.title("Example")
45
+ app.set_window_icon("icon.png") # static file; or a PIL image / renderer
46
+
47
+ icon = create_tray_icon(
48
+ "example",
49
+ "Example",
50
+ "icon.png", # any IconSource: path, PIL image, or renderer
51
+ on_activate=app.show,
52
+ on_quit=app.stop,
53
+ )
54
+ app.run(icon, start_hidden=True) # only the tray shows until activated
55
+ ```
56
+
57
+ The library manages the tray lifecycle, window visibility, and threading;
58
+ choosing and (re)drawing the artwork is left to the application.
59
+
60
+ ## Documentation
61
+
62
+ Full documentation lives in `docs/` and is published to GitHub Pages:
63
+ <https://sevaht.github.io/sevaht-gui/>.
64
+
65
+ ### Building the docs locally
66
+
67
+ ```console
68
+ $ uv run --group docs sphinx-build -b html docs docs/_build/html
69
+ ```
70
+
71
+ Then open `docs/_build/html/index.html`. (Publishing to GitHub Pages requires
72
+ enabling Pages with the "GitHub Actions" source in the repository settings.)
@@ -0,0 +1,146 @@
1
+ [build-system]
2
+ requires = ["hatchling (>=1.29.0,<2.0.0)", "hatch-vcs (>=0.5.0,<1.0.0)"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "sevaht-gui"
7
+ dynamic = ["version"]
8
+ description = "Cross-platform tray icons and a tkinter app base for small GUIs."
9
+ authors = [
10
+ {name = "Jacob McIntosh", email = "nacitar.sevaht@gmail.com"}
11
+ ]
12
+ readme = "README.md"
13
+ license = "Unlicense"
14
+ requires-python = ">=3.12"
15
+ keywords = [
16
+ "tray", "system-tray", "status-icon", "tkinter", "gui", "pystray",
17
+ "statusnotifieritem", "xembed",
18
+ ]
19
+ classifiers = [
20
+ "Development Status :: 4 - Beta",
21
+ "Intended Audience :: Developers",
22
+ "Operating System :: POSIX :: Linux",
23
+ "Operating System :: Microsoft :: Windows",
24
+ "Programming Language :: Python :: 3",
25
+ "Programming Language :: Python :: 3 :: Only",
26
+ "Programming Language :: Python :: 3.12",
27
+ "Programming Language :: Python :: 3.13",
28
+ "Programming Language :: Python :: 3.14",
29
+ "Topic :: Desktop Environment",
30
+ "Topic :: Software Development :: Libraries",
31
+ "Typing :: Typed",
32
+ ]
33
+ dependencies = [
34
+ "Pillow>=12.2.0",
35
+ # Notifications (notify-send/dbus-send/console) live in sevaht-utility so
36
+ # they need no GUI/D-Bus library.
37
+ "sevaht-utility>=1.0.0",
38
+ # Windows uses pystray; Linux uses the StatusNotifierItem (dbus-next)
39
+ # backend, falling back to a self-contained XEmbed icon (python-xlib) on
40
+ # bare X11 window managers with no SNI host. macOS is intentionally
41
+ # unsupported: a real menu-bar item there needs a single main-thread Cocoa
42
+ # loop shared with tk (custom PyObjC), which the threaded model here cannot
43
+ # provide.
44
+ "pystray>=0.19.5; sys_platform == 'win32'",
45
+ "dbus-next>=0.2.3; sys_platform == 'linux'",
46
+ "python-xlib>=0.33; sys_platform == 'linux'",
47
+ ]
48
+
49
+ [dependency-groups]
50
+ dev = [
51
+ "black (>=26.3.1,<27.0.0)",
52
+ "build (>=1.4.2,<2.0.0)",
53
+ "mypy (>=1.20.0,<2.0.0)",
54
+ "pytest (>=9.0.2,<10.0.0)",
55
+ "ruff (>=0.15.8,<0.16.0)",
56
+ "twine (>=6.2.0,<7.0.0)",
57
+ "vulture (>=2.14,<3.0.0)",
58
+ ]
59
+ docs = [
60
+ "sphinx (>=8.1.0,<9.0.0)",
61
+ "furo (>=2024.8.6)",
62
+ "myst-parser (>=4.0.0,<5.0.0)",
63
+ ]
64
+
65
+ [project.urls]
66
+ Homepage = "https://github.com/sevaht/sevaht-gui"
67
+ Documentation = "https://sevaht.github.io/sevaht-gui/"
68
+ Source = "https://github.com/sevaht/sevaht-gui"
69
+ Issues = "https://github.com/sevaht/sevaht-gui/issues"
70
+
71
+ [tool.hatch.version]
72
+ source = "vcs"
73
+
74
+ # Allow building before any tags/commits exist (fresh repo, or a source tree
75
+ # with no .git): hatch-vcs/setuptools-scm falls back to this instead of erroring.
76
+ # Real tags/commits override it, so this only matters pre-history.
77
+ [tool.hatch.version.raw-options]
78
+ fallback_version = "0.0.0"
79
+
80
+ [tool.hatch.build.targets.wheel]
81
+ packages = ["src/sevaht_gui"]
82
+
83
+ [tool.hatch.build.targets.sdist]
84
+ # Allowlist so dev/local files (.claude, checks, publish-test.sh, .github, ...)
85
+ # never leak into a published sdist. pyproject.toml/PKG-INFO/README/.gitignore
86
+ # are force-included by hatchling regardless.
87
+ include = ["/src", "/tests", "/LICENSE"]
88
+
89
+ [tool.black]
90
+ line-length = 79
91
+ skip-magic-trailing-comma = true
92
+
93
+ [tool.mypy]
94
+ strict = true
95
+ mypy_path = ["typings"]
96
+ exclude = ["^docs/", "^dist/"]
97
+
98
+ # The SNI backend annotates D-Bus methods with the bus type-signature strings
99
+ # the dbus-next decorators require (e.g. ``x: "i"``); these are not Python
100
+ # types, so type-checking the module is not meaningful.
101
+ [[tool.mypy.overrides]]
102
+ module = "sevaht_gui._tray_sni"
103
+ ignore_errors = true
104
+
105
+ # pystray is a Windows-only dependency (not installed on Linux, where the
106
+ # linters run) and ships no type stubs.
107
+ [[tool.mypy.overrides]]
108
+ module = "pystray"
109
+ ignore_missing_imports = true
110
+
111
+ [tool.pytest.ini_options]
112
+ testpaths = ["tests"]
113
+
114
+ [tool.ruff]
115
+ line-length = 79
116
+ extend-exclude = ["docs"]
117
+
118
+ [tool.ruff.lint]
119
+ extend-ignore = ["COM812", "COM819", "E203", "E501", "TD003"]
120
+ select = [
121
+ "A", "ANN", "ARG", "ASYNC", "B", "BLE", "C4", "C90", "COM",
122
+ "DTZ", "E", "EM", "ERA", "F", "FA", "FLY", "FURB", "I",
123
+ "ISC", "LOG", "N", "PERF", "PGH", "PIE", "PLR", "PLW", "PTH",
124
+ "Q", "RET", "RSE", "RUF", "S", "SIM", "TC", "TCH", "TD",
125
+ "TID", "TRY", "UP"
126
+ ]
127
+
128
+ [tool.ruff.lint.per-file-ignores]
129
+ "checks" = ["S603"]
130
+ "tests/**/*" = ["PLR2004", "S101"]
131
+ "**/_tray_sni.py" = [
132
+ "ANN201", "ANN202", "ARG002", "F722", "F821", "N802", "PLR0913", "UP037"
133
+ ]
134
+ "typings/Xlib/**/*.pyi" = ["ANN401", "A002", "N818"]
135
+
136
+ [tool.vulture]
137
+ # This is a library: public API surface looks "unused" to vulture since
138
+ # nothing in-repo calls it, which is the false positive vulture's own docs
139
+ # tell you to address by raising min_confidence rather than whitelisting.
140
+ min_confidence = 70
141
+ # The SNI backend implements a D-Bus protocol: its interface methods are
142
+ # invoked over the bus (never from Python) and carry spec-mandated, unused
143
+ # arguments (x, y, ...). vulture matches identifiers by name across the whole
144
+ # tree, so these only look unused in a codebase this small; skip the file, as
145
+ # it is already exempted from mypy for the same protocol-shaped reasons.
146
+ exclude = ["*_tray_sni.py"]
@@ -0,0 +1,53 @@
1
+ """Small cross-platform GUI building blocks.
2
+
3
+ Submodules:
4
+
5
+ * :mod:`sevaht_gui.tray` -- a cross-platform system-tray icon
6
+ (:func:`~sevaht_gui.tray.create_tray_icon`) backed by pystray on Windows and
7
+ StatusNotifierItem/XEmbed on Linux.
8
+ * :mod:`sevaht_gui.tkapp` -- :class:`~sevaht_gui.tkapp.TkApp`, a tkinter base
9
+ that owns the main thread and runs a tray icon on a worker thread.
10
+ * :mod:`sevaht_gui.theme` -- default ttk theming (clam + checkmark indicator).
11
+ * :mod:`sevaht_gui.widgets` -- reusable widgets (e.g. ``LabelGrooveFrame``).
12
+ * :mod:`sevaht_gui.monitors` -- monitor geometry
13
+ (:func:`~sevaht_gui.monitors.primary_monitor_bounds`).
14
+
15
+ :meth:`~sevaht_gui.tkapp.TkApp.notify` shows a desktop notification, using the
16
+ tray icon when one exists and falling back to a standalone notifier otherwise,
17
+ so notifications work with or without a tray.
18
+
19
+ Attributes:
20
+ __version__: The installed distribution version.
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ import importlib.metadata
26
+
27
+ from .monitors import primary_monitor_bounds
28
+ from .theme import apply_theme
29
+ from .tkapp import TkApp, WindowPlacement
30
+ from .tray import (
31
+ SNIRegistrationError,
32
+ TrayIcon,
33
+ as_renderer,
34
+ create_tray_icon,
35
+ tray_available,
36
+ )
37
+ from .widgets import LabelGrooveFrame
38
+
39
+ __version__ = importlib.metadata.version(__package__)
40
+
41
+ __all__ = [
42
+ "LabelGrooveFrame",
43
+ "SNIRegistrationError",
44
+ "TkApp",
45
+ "TrayIcon",
46
+ "WindowPlacement",
47
+ "__version__",
48
+ "apply_theme",
49
+ "as_renderer",
50
+ "create_tray_icon",
51
+ "primary_monitor_bounds",
52
+ "tray_available",
53
+ ]