Initial implementation
Contains JSON hanshake and heartbeat procedure, including rejecting handshake when no data exposed by connector Simple loading screen, and then UI opens, no major UI work has been done yet
This commit is contained in:
@@ -1,2 +1,167 @@
|
||||
# Copyright (C) 2026 Hector van der Aa <hector@h3cx.dev>
|
||||
# Copyright (C) 2026 Association Exergie <association.exergie@gmail.com>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from queue import Empty, Queue
|
||||
from threading import Thread
|
||||
import threading
|
||||
import time
|
||||
import traceback
|
||||
import dearpygui.dearpygui as dpg
|
||||
import sys
|
||||
|
||||
from dynalab.data.json import json_thread_main
|
||||
from dynalab.state import AppState
|
||||
import dynalab.ui.windows
|
||||
from dynalab.ui.windows import build_loading, build_windows
|
||||
|
||||
|
||||
def _setup_logging(level: int = logging.INFO) -> None:
|
||||
logging.basicConfig(
|
||||
level=level,
|
||||
format="%(asctime)s | %(levelname)-8s | %(name)s | %(message)s",
|
||||
datefmt="%H:%M:%S",
|
||||
stream=sys.stdout,
|
||||
)
|
||||
|
||||
|
||||
def _asset_path(relative_path: str) -> str:
|
||||
path = Path(relative_path)
|
||||
candidates: list[Path] = []
|
||||
|
||||
bundle_dir = getattr(sys, "_MEIPASS", None)
|
||||
if bundle_dir is not None:
|
||||
candidates.append(Path(bundle_dir) / path)
|
||||
|
||||
candidates.extend(
|
||||
(
|
||||
Path.cwd() / path,
|
||||
Path(__file__).resolve().parents[2] / path,
|
||||
)
|
||||
)
|
||||
|
||||
for candidate in candidates:
|
||||
if candidate.exists():
|
||||
return str(candidate)
|
||||
|
||||
searched = ", ".join(str(candidate) for candidate in candidates)
|
||||
raise FileNotFoundError(f"Missing asset {relative_path!r}. Searched: {searched}")
|
||||
|
||||
|
||||
def _init_worker(
|
||||
state: AppState, stop_event: threading.Event, message_queue: Queue[str]
|
||||
) -> None:
|
||||
time.sleep(1.0)
|
||||
|
||||
message_queue.put("Starting JSON data server")
|
||||
# Init and start json connector thread
|
||||
state.data_json_thread = Thread(
|
||||
target=json_thread_main,
|
||||
name="json-connector-network",
|
||||
daemon=True,
|
||||
args=(state,),
|
||||
)
|
||||
state.data_json_thread.start()
|
||||
time.sleep(0.5)
|
||||
message_queue.put("JSON data server started")
|
||||
time.sleep(0.5)
|
||||
message_queue.put("DynaLab Ready")
|
||||
time.sleep(1.0)
|
||||
|
||||
stop_event.set()
|
||||
|
||||
|
||||
def run() -> None:
|
||||
print("Hello World!")
|
||||
# Setup logging format and level
|
||||
_setup_logging(logging.DEBUG)
|
||||
|
||||
# dpg setup
|
||||
dpg.create_context()
|
||||
|
||||
try:
|
||||
# Switch callback management to manual
|
||||
dpg.configure_app(manual_callback_management=True)
|
||||
# Initialize viewport
|
||||
dpg.create_viewport(title="DynaLab", width=600, height=400)
|
||||
|
||||
# Load fonts at 2x size for HiDPI scaling
|
||||
with dpg.font_registry():
|
||||
app_font = dpg.add_font(
|
||||
_asset_path("assets/fonts/Inter-Regular.ttf"), 18 * 2
|
||||
)
|
||||
mono_font = dpg.add_font(
|
||||
_asset_path("assets/fonts/JetBrainsMono-Regular.ttf"),
|
||||
size=36,
|
||||
label="mono_font",
|
||||
)
|
||||
dpg.bind_font(app_font)
|
||||
|
||||
build_loading()
|
||||
|
||||
# Open viewport
|
||||
dpg.setup_dearpygui()
|
||||
dpg.show_viewport()
|
||||
|
||||
# Config main window size
|
||||
vp_w = dpg.get_viewport_client_width()
|
||||
vp_h = dpg.get_viewport_client_height()
|
||||
dpg.configure_item("loading_window", pos=(0, 0), width=vp_w, height=vp_h)
|
||||
dpg.set_primary_window("loading_window", True)
|
||||
|
||||
# Init global app state
|
||||
state: AppState = AppState()
|
||||
|
||||
loading_msg_queue: Queue[str] = Queue()
|
||||
loading_stop: threading.Event = threading.Event()
|
||||
loading_thread = Thread(
|
||||
target=_init_worker,
|
||||
name="init-worker-thread",
|
||||
args=(state, loading_stop, loading_msg_queue),
|
||||
daemon=True,
|
||||
)
|
||||
loading_thread.start()
|
||||
|
||||
while not loading_stop.is_set():
|
||||
# Fetch and execute callbacks
|
||||
jobs = dpg.get_callback_queue()
|
||||
try:
|
||||
dpg.run_callbacks(jobs)
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
||||
try:
|
||||
msg = loading_msg_queue.get_nowait()
|
||||
except Empty:
|
||||
pass
|
||||
else:
|
||||
dpg.set_value(item="loading_text", value=msg)
|
||||
# Render frame
|
||||
dpg.render_dearpygui_frame()
|
||||
|
||||
dpg.delete_item("loading_window")
|
||||
# Call ui building function
|
||||
dpg.set_viewport_height(720)
|
||||
dpg.set_viewport_width(1280)
|
||||
build_windows()
|
||||
|
||||
# Config main window size
|
||||
vp_w = dpg.get_viewport_client_width()
|
||||
vp_h = dpg.get_viewport_client_height()
|
||||
dpg.configure_item("main_window", pos=(0, 0), width=vp_w, height=vp_h)
|
||||
dpg.set_primary_window("main_window", True)
|
||||
|
||||
# dpg UI loop
|
||||
while dpg.is_dearpygui_running():
|
||||
# Fetch and execute callbacks
|
||||
jobs = dpg.get_callback_queue()
|
||||
try:
|
||||
dpg.run_callbacks(jobs)
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
||||
# Render frame
|
||||
dpg.render_dearpygui_frame()
|
||||
finally:
|
||||
state.stop_event.set()
|
||||
dpg.destroy_context()
|
||||
|
||||
Reference in New Issue
Block a user