Move all three skills into skills/; add root README; fix paths
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
312742cfef
commit
d95e5d452d
84 files changed
+97
-31
No files matched your search
@@ -0,0 +1,72 @@
|
||||
"""Minimal PyQt6 host for dashboard.html (desktop mode). Pattern from Tebra main_window.py.
|
||||
|
||||
The bot runs as a child process and this window shows its progress. The bot wiring
|
||||
(command, step markers, friendly rules) lives in bot_runner.py, next to this file.
|
||||
Run it with: python dashboard/bridge.py
|
||||
"""
|
||||
import json, sys, threading
|
||||
from pathlib import Path
|
||||
from PyQt6.QtCore import QObject, pyqtSlot, pyqtSignal, QUrl, QSettings
|
||||
from PyQt6.QtWidgets import QApplication, QMainWindow
|
||||
from PyQt6.QtWebEngineWidgets import QWebEngineView
|
||||
from PyQt6.QtWebChannel import QWebChannel
|
||||
|
||||
import bot_runner
|
||||
|
||||
HTML_PATH = Path(__file__).with_name("dashboard.html")
|
||||
|
||||
|
||||
def settings():
|
||||
return QSettings("{{ORG}}", "{{APP_NAME}}")
|
||||
|
||||
|
||||
class Bridge(QObject):
|
||||
_emit = pyqtSignal(str) # thread-safe hop to the GUI thread
|
||||
|
||||
def __init__(self, view):
|
||||
super().__init__()
|
||||
self.view, self._one_run = view, threading.Lock()
|
||||
self._emit.connect(lambda js: self.view.page().runJavaScript(f"window.__pyMsg({js})"))
|
||||
# Restore the saved dark-mode choice on every page load, sample-data mode included.
|
||||
view.loadFinished.connect(lambda _ok: self.send_to_js("apply_dark", {"on": settings().value("dark", False, bool)}))
|
||||
|
||||
def send_to_js(self, event, data):
|
||||
self._emit.emit(json.dumps(json.dumps({"event": event, "data": data})))
|
||||
|
||||
@pyqtSlot()
|
||||
def load_dashboard(self):
|
||||
self.send_to_js("dashboard_data", {"batches": []}) # TODO: read from DB on a worker thread
|
||||
|
||||
@pyqtSlot()
|
||||
def load_latest_report(self):
|
||||
self.send_to_js("report_data", {"rows": []}) # TODO: read the latest report rows
|
||||
|
||||
@pyqtSlot()
|
||||
def download_report(self):
|
||||
pass # TODO: export the report rows to .xlsx
|
||||
|
||||
@pyqtSlot(bool)
|
||||
def set_dark(self, on):
|
||||
settings().setValue("dark", on)
|
||||
|
||||
@pyqtSlot()
|
||||
def start_automation(self):
|
||||
if self._one_run.acquire(blocking=False): # a second click can't start a second bot
|
||||
threading.Thread(target=self._run, daemon=True).start()
|
||||
|
||||
def _run(self):
|
||||
try:
|
||||
bot_runner.run(self.send_to_js)
|
||||
finally:
|
||||
self._one_run.release()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
win, view = QMainWindow(), QWebEngineView()
|
||||
bridge, channel = Bridge(view), QWebChannel()
|
||||
channel.registerObject("pyBridge", bridge)
|
||||
view.page().setWebChannel(channel)
|
||||
view.setUrl(QUrl.fromLocalFile(str(HTML_PATH)))
|
||||
win.setCentralWidget(view); win.resize(1280, 820); win.setWindowTitle("{{APP_NAME}}"); win.show()
|
||||
sys.exit(app.exec())
|
||||
Reference in new issue
Block a user