Reusable pywebview wrapper for building desktop apps from Axion reports.
Provides system tray, toast notifications, keyboard shortcuts, search,
export, screenshot, window persistence, and click-through navigation.
from ObjDesktopApp import ObjDesktopApp
from ObjReportNetworkdashboard import Report
report = Report()
app = ObjDesktopApp(
title="My Dashboard",
render_fn=report.Render,
poll_fn=my_data_refresh,
poll_interval=60,
)
app.run()
| Parameter | Type | Default | Description |
|---|---|---|---|
title |
str | "Axion" | Window title |
render_fn |
Callable | None | Returns HTML string for the main page |
detail_fn |
Callable | None | fn(hostname) returns HTML for detail view |
poll_fn |
Callable | None | Background data refresh (no return value) |
alert_fn |
Callable | None | Returns list of alert strings for toasts |
poll_interval |
int | 60 | Seconds between poll cycles |
width |
int | 1250 | Default window width |
height |
int | 1350 | Default window height |
maximized |
bool | True | Start maximized (overridden by saved state) |
background |
str | "#0f1923" | Window background colour |
accent |
str | "#6c5ce7" | Accent colour for splash screen |
icon_path |
str | "" | Path to PNG for system tray icon |
logo_uri |
str | "" | data: URI for splash screen logo |
tray |
bool | True | Enable system tray icon |
splash |
bool | True | Show splash screen on startup |
splash_message |
str | "Initialising..." | Splash screen subtitle |
gui |
str | "" | pywebview backend (auto: edgechromium on Windows) |
| Shortcut | Action |
|---|---|
Ctrl+F or / |
Toggle search/filter bar |
Escape |
Close search, or go back from detail view |
Ctrl+E |
Export visible table as CSV download |
Ctrl+J |
Export _initData as JSON download |
Ctrl+S |
Screenshot (saves page as HTML) |
Ctrl+R |
Force refresh (go back to main view) |
F11 |
Toggle fullscreen |
Press Ctrl+F to show the search bar. Type to filter heatmap rows
in real-time. Auto-refresh pauses while searching. Press Escape
to close and clear the filter.
Set detail_fn to enable click-through navigation. The main page
must call _appShowDetail(hostname) from onclick handlers:
<a href="#" onclick="_appShowDetail('mariadb');return false">mariadb</a>
The detail page gets a Back button that calls _appGoBack().
When tray=True, the app adds a system tray icon with:
Closing the window hides it to tray instead of quitting.
ObjDesktopApp.toast(
title="Alert",
message="Host mariadb is down",
urgency="critical", # low, normal, critical
timeout=10000, # ms
)
Uses notify-send on Linux. Set alert_fn to auto-fire toasts
on each poll cycle.
The app intercepts fetch('/report/rpc/') calls in the HTML and
routes them through pywebview.api.rpc_refresh(). This means the
dashboard's JS fetchDashboard() works without a web server —
the RPC call goes directly to report.RpcRefresh({}).
The background loop polls data but does NOT reload the page.
Page updates happen through JS RPC, preserving scroll position,
search state, and DOM state.
The .ax-pulse dot in the header changes colour based on DB
connection status:
Auto-reconnect attempts DB.ping(reconnect=True) on poll failure.
Checked every 15 seconds via the JS API.
Window position and size are saved to ~/.axion_app_state.json
on close and restored on next launch. First launch starts maximized,
subsequent launches restore the saved position.
Ctrl+E): Extracts the last table on the page as CSVCtrl+J): Downloads window._initData as formatted JSONCtrl+S): Saves the full page HTMLShows the app logo with a spinner while the first poll runs.
Replaced by the dashboard once data is ready. Customise with
logo_uri and splash_message.
ObjDesktopApp
├── _background_loop (Thread)
│ ├── poll_fn() → refresh data
│ ├── alert_fn() → fire toasts
│ └── (no page reload)
├── _JsApi (pywebview bridge)
│ ├── show_detail() → click-through
│ ├── go_back() → return to main
│ ├── rpc_refresh() → RPC data fetch
│ ├── filter_hosts() → search
│ ├── export_csv() → CSV download
│ ├── export_json() → JSON download
│ ├── screenshot() → HTML save
│ ├── toggle_fullscreen()
│ ├── search_open/close() → pause refresh
│ └── get_connection_status()
├── _start_tray (Thread)
│ └── pystray icon + menu
└── APP_JS (injected into every page)
├── fetch() interceptor → rpc_refresh
├── keyboard shortcuts
├── search bar
└── connection dot updater
On Windows (sys.platform == "win32"), the app auto-selects the
edgechromium backend for pywebview. This requires the Edge WebView2
runtime (install via choco install webview2-runtime).
MSHTML (IE) does not support modern CSS — the edgechromium backend
is required for the ax-* component classes from ObjCss.
from ObjDesktopApp import ObjDesktopApp
from ObjMonitor import Monitor
from ObjReportNetworkdashboard import Report
from ObjReportHostdetail import Report as HostDetail
from ObjAlert import ObjAlert
report = Report()
detail = HostDetail()
monitor = Monitor()
alerts = ObjAlert()
def check_alerts():
data = report._fetch_alerts()
return [
f"{a['HostName']}: DOWN"
for a in (data or [])
if a.get('PollStatus') != 'ok'
] or None
app = ObjDesktopApp(
title="Axion Monitor",
render_fn=report.Render,
detail_fn=lambda h: detail.Render(h),
poll_fn=monitor.poll_all,
alert_fn=check_alerts,
poll_interval=60,
)
app.run()