Download gui_base.py from smlflg/shared-gui: direct link, hf CLI and curl.
- Browser
- Download file 6.07 kB
-
https://huggingface.co/smlflg/shared-gui/resolve/main/gui_base.py
- Command line
-
hf download hf://smlflg/shared-gui/gui_base.py
-
curl -L -o gui_base.py https://huggingface.co/smlflg/shared-gui/resolve/main/gui_base.py
6.07 kB
| #!/usr/bin/env python3 | |
| """Shared GUI Base Module for GTK3 Apps. | |
| Provides BaseApp class with: | |
| - Window setup (title, size, icon, HeaderBar, StatusBar) | |
| - Catppuccin theme via ClaudeCodePanel/theme.py | |
| - COSMIC dark/light auto-detection + watcher | |
| - Refresh timer management | |
| - Extendable CSS | |
| Usage: | |
| from gui_base import BaseApp | |
| class MyApp(BaseApp): | |
| def __init__(self): | |
| super().__init__("My App", 800, 600) | |
| # Add widgets to self.content_box | |
| label = Gtk.Label(label="Hello") | |
| self.content_box.pack_start(label, True, True, 0) | |
| """ | |
| import sys | |
| from pathlib import Path | |
| import gi | |
| gi.require_version("Gdk", "3.0") | |
| gi.require_version("Gtk", "3.0") | |
| from gi.repository import Gdk, GLib, Gtk | |
| # Import theme module from ClaudeCodePanel | |
| sys.path.insert(0, str(Path.home() / "Projekte" / "ClaudeCodePanel")) | |
| from theme import build_css, get_palette, setup_theme_watcher | |
| # --------------------------------------------------------------------------- | |
| # Additional CSS for shared components | |
| # --------------------------------------------------------------------------- | |
| def _base_css(p: dict[str, str]) -> str: | |
| """Generate additional shared CSS classes.""" | |
| return f""" | |
| .base-card {{ | |
| border-radius: 8px; | |
| background: {p["card"]}; | |
| border: 1px solid {p["border"]}; | |
| padding: 10px; | |
| margin: 4px 0; | |
| }} | |
| .base-card:hover {{ | |
| background: {p["border"]}; | |
| }} | |
| .base-toolbar {{ | |
| background: {p["mantle"]}; | |
| border-radius: 8px; | |
| padding: 6px 10px; | |
| }} | |
| .base-badge {{ | |
| border-radius: 12px; | |
| padding: 2px 8px; | |
| font-size: 10px; | |
| font-weight: bold; | |
| }} | |
| .base-badge-ok {{ | |
| background: {p["green_hover_bg"]}; | |
| color: {p["green"]}; | |
| }} | |
| .base-badge-warn {{ | |
| background: {p["yellow_hover_bg"]}; | |
| color: {p["yellow"]}; | |
| }} | |
| .base-badge-error {{ | |
| background: rgba(243,139,168,0.2); | |
| color: {p["red"]}; | |
| }} | |
| .base-accent-btn {{ | |
| padding: 6px 16px; | |
| border-radius: 6px; | |
| background: {p["accent"]}; | |
| color: {p["resume_text"]}; | |
| font-weight: bold; | |
| border: none; | |
| }} | |
| .base-accent-btn:hover {{ | |
| background: {p["lavender"]}; | |
| }} | |
| """ | |
| class BaseApp(Gtk.Window): | |
| """Base class for standalone GTK3 apps with Catppuccin theming.""" | |
| def __init__( | |
| self, | |
| title: str, | |
| width: int = 800, | |
| height: int = 600, | |
| icon_name: str | None = None, | |
| ): | |
| super().__init__(title=title) | |
| self.set_default_size(width, height) | |
| if icon_name: | |
| self.set_icon_name(icon_name) | |
| self.connect("destroy", Gtk.main_quit) | |
| self._refresh_sources: list[int] = [] | |
| # --- Theme setup --- | |
| self._css_provider = Gtk.CssProvider() | |
| self._apply_full_css() | |
| Gtk.StyleContext.add_provider_for_screen( | |
| Gdk.Screen.get_default(), | |
| self._css_provider, | |
| Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION, | |
| ) | |
| setup_theme_watcher(self._css_provider) | |
| # --- Layout: Header + Content + StatusBar --- | |
| root_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) | |
| self.add(root_box) | |
| # HeaderBar | |
| self._header = Gtk.HeaderBar() | |
| self._header.set_show_close_button(True) | |
| self._header.set_title(title) | |
| self.set_titlebar(self._header) | |
| # Content area (subclasses add widgets here) | |
| self.content_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) | |
| self.content_box.set_margin_start(12) | |
| self.content_box.set_margin_end(12) | |
| self.content_box.set_margin_top(12) | |
| self.content_box.set_margin_bottom(6) | |
| root_box.pack_start(self.content_box, True, True, 0) | |
| # StatusBar | |
| self._status_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) | |
| self._status_box.get_style_context().add_class("status-bar") | |
| self._status_label = Gtk.Label(label="Ready") | |
| self._status_label.set_xalign(0) | |
| self._status_box.pack_start(self._status_label, True, True, 0) | |
| root_box.pack_end(self._status_box, False, False, 0) | |
| # --- Public API --- | |
| def set_subtitle(self, subtitle: str) -> None: | |
| """Set the HeaderBar subtitle.""" | |
| self._header.set_subtitle(subtitle) | |
| def add_header_widget(self, widget: Gtk.Widget, end: bool = True) -> None: | |
| """Add a widget to the HeaderBar (end=True for right side).""" | |
| if end: | |
| self._header.pack_end(widget) | |
| else: | |
| self._header.pack_start(widget) | |
| def set_status(self, text: str, css_class: str = "") -> None: | |
| """Update the status bar text and optional CSS class.""" | |
| self._status_label.set_text(text) | |
| ctx = self._status_label.get_style_context() | |
| for cls in ("status-saved", "status-error"): | |
| ctx.remove_class(cls) | |
| if css_class: | |
| ctx.add_class(css_class) | |
| def start_refresh(self, interval_seconds: int, callback) -> int: | |
| """Start a periodic refresh timer. Returns the source ID.""" | |
| source_id = GLib.timeout_add_seconds(interval_seconds, callback) | |
| self._refresh_sources.append(source_id) | |
| return source_id | |
| def stop_all_refresh(self) -> None: | |
| """Stop all refresh timers.""" | |
| for source_id in self._refresh_sources: | |
| GLib.source_remove(source_id) | |
| self._refresh_sources.clear() | |
| def add_css(self, css_string: str) -> None: | |
| """Add extra CSS rules. Call after __init__.""" | |
| palette = get_palette() | |
| full_css = build_css(palette).decode() + _base_css(palette) + css_string | |
| self._css_provider.load_from_data(full_css.encode()) | |
| def run(self) -> None: | |
| """Show all widgets and start the GTK main loop.""" | |
| self.show_all() | |
| Gtk.main() | |
| # --- Internal --- | |
| def _apply_full_css(self) -> None: | |
| """Load theme CSS + base CSS into the provider.""" | |
| palette = get_palette() | |
| full_css = build_css(palette).decode() + _base_css(palette) | |
| self._css_provider.load_from_data(full_css.encode()) | |