leaf-framework 0.9.4__tar.gz → 0.9.6__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.
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/PKG-INFO +1 -1
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/ui/desktop_launcher.py +62 -5
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/pyproject.toml +1 -1
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/LICENSE +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/README.md +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/__init__.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/adapters/__init__.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/adapters/core_adapters/__init__.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/adapters/core_adapters/continuous_experiment_adapter.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/adapters/core_adapters/discrete_experiment_adapter.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/adapters/core_adapters/upload_adapter.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/adapters/equipment_adapter.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/config/configuration.yaml +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/data/marketplace.yaml +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/error_handler/__init__.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/error_handler/error_holder.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/error_handler/exceptions.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/measurement_handler/__init__.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/measurement_handler/handler.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/measurement_handler/measurements.yaml +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/measurement_handler/terms.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/.gitkeep +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/__init__.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/input_modules/__init__.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/input_modules/ble_watcher.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/input_modules/db_watcher.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/input_modules/event_watcher.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/input_modules/external_api_watcher.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/input_modules/external_event_watcher.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/input_modules/file_watcher.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/input_modules/http_watcher.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/input_modules/modbus_watcher.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/input_modules/mqtt_external_event_watcher.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/input_modules/mqtt_watcher.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/input_modules/opc_watcher.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/input_modules/polling_watcher.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/input_modules/simple_watcher.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/measurement_modules/__init__.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/measurement_modules/carbon_dioxide.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/measurement_modules/dissolved_oxygen.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/measurement_modules/fluorescence.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/measurement_modules/measurement_module.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/measurement_modules/optical_density.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/measurement_modules/ph.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/measurement_modules/temperature.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/output_modules/__init__.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/output_modules/file.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/output_modules/keydb.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/output_modules/mqtt.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/output_modules/output_module.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/output_modules/sql.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/output_modules/topic_store.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/phase_modules/__init__.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/phase_modules/control.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/phase_modules/error.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/phase_modules/external_event_phase.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/phase_modules/initialisation.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/phase_modules/measure.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/phase_modules/phase.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/phase_modules/start.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/phase_modules/stop.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/process_modules/__init__.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/process_modules/continous_module.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/process_modules/discrete_module.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/process_modules/external_event_process.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/process_modules/process_module.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/process_modules/upload_module.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/register/__init__.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/register/abstract_leaf.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/register/equipment_actions.yaml +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/register/equipment_data.yaml +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/register/metadata.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/register/py.typed +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/register/topic_utilities.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/registry/discovery.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/registry/loader.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/registry/registry.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/registry/utils.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/start.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/ui/__init__.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/ui/adapter_ui_extension.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/ui/components/__init__.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/ui/components/header.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/ui/constants.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/ui/images/icon.icns +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/ui/images/icon.ico +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/ui/images/icon.svg +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/ui/interface.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/ui/markdown/configuration_help.md +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/ui/markdown/documentation_main.md +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/ui/markdown/documentation_protips.md +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/ui/scripts.js +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/ui/styles.css +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/ui/tabs/__init__.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/ui/tabs/adapters.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/ui/tabs/configuration.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/ui/tabs/documentation.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/ui/tabs/logs.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/ui/tabs/monitoring.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/ui/utils.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/utility/config_utils.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/utility/logger/__init__.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/utility/logger/logger_utils.py +0 -0
- {leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/utility/running_utilities.py +0 -0
|
@@ -12,6 +12,28 @@ from leaf.utility.logger.logger_utils import get_logger
|
|
|
12
12
|
|
|
13
13
|
logger = get_logger(__name__, log_file="desktop_launcher.log")
|
|
14
14
|
|
|
15
|
+
# Works around a libXcursor/Xlib bug that aborts the whole process on Linux,
|
|
16
|
+
# the first time a tk.Button is created: ButtonCreate -> TkButtonWorldChanged
|
|
17
|
+
# -> Tk_GetBitmap -> ... -> libXcursor's XcursorSupportsARGB ->
|
|
18
|
+
# XRenderQueryExtension -> XQueryExtension -> Xlib's _XSend, which hits
|
|
19
|
+
# `assert(!xcb_xlib_unknown_seq_number)` and aborts. Root-caused via a core
|
|
20
|
+
# dump (gdb, `thread apply all bt`) on a genuine Debian/Xorg reproduction --
|
|
21
|
+
# NOT a threading race (every other thread was idle, blocked on condition
|
|
22
|
+
# variables); the single main thread aborts inside libXcursor's lazy
|
|
23
|
+
# ARGB-cursor/display-info detection, which corrupts Xlib's XCB
|
|
24
|
+
# sequence-number tracking. XLIB_SKIP_ARGB_VISUALS=1 is libXcursor's own
|
|
25
|
+
# documented escape hatch for exactly this, and was confirmed to fix it:
|
|
26
|
+
# without it, LEAF aborted within ~1s of "Starting desktop control panel..."
|
|
27
|
+
# on every run; with it, the full GUI lifecycle (button creation, 40s+
|
|
28
|
+
# runtime, clean shutdown) ran with no crash, in the same environment.
|
|
29
|
+
#
|
|
30
|
+
# Must be set here, before tkinter is ever imported below -- libXcursor
|
|
31
|
+
# reads this once via a load-time constructor and caches it, so setting it
|
|
32
|
+
# via os.environ any later (even right before the first tk.Tk()) is too
|
|
33
|
+
# late and has no effect. Confirmed by direct reproduction.
|
|
34
|
+
if sys.platform.startswith("linux"):
|
|
35
|
+
os.environ.setdefault("XLIB_SKIP_ARGB_VISUALS", "1")
|
|
36
|
+
|
|
15
37
|
# Check if tkinter is available
|
|
16
38
|
try:
|
|
17
39
|
import tkinter as tk
|
|
@@ -23,6 +45,28 @@ except ImportError:
|
|
|
23
45
|
logger.warning("tkinter not available - desktop GUI will not be available")
|
|
24
46
|
|
|
25
47
|
|
|
48
|
+
def _init_x11_threads() -> None:
|
|
49
|
+
"""Tell Xlib this process is multi-threaded, before it ever touches X11.
|
|
50
|
+
|
|
51
|
+
LEAF runs several other threads by the time this GUI starts (the
|
|
52
|
+
NiceGUI web server thread, adapter/simulation threads -- see start.py).
|
|
53
|
+
None of them were shown to touch Tk/X11 in the libXcursor crash
|
|
54
|
+
reproduction above (see the XLIB_SKIP_ARGB_VISUALS comment near the top
|
|
55
|
+
of this file for that actual root cause), but Xlib documents
|
|
56
|
+
XInitThreads() as required before its first use in any multi-threaded
|
|
57
|
+
process regardless of which thread issues X calls, so there's no reason
|
|
58
|
+
to skip it. No equivalent exists on macOS (Cocoa) or Windows.
|
|
59
|
+
"""
|
|
60
|
+
if not sys.platform.startswith("linux"):
|
|
61
|
+
return
|
|
62
|
+
try:
|
|
63
|
+
import ctypes
|
|
64
|
+
|
|
65
|
+
ctypes.CDLL("libX11.so.6").XInitThreads()
|
|
66
|
+
except Exception as e:
|
|
67
|
+
logger.debug(f"Could not call XInitThreads: {e}")
|
|
68
|
+
|
|
69
|
+
|
|
26
70
|
def is_desktop_environment() -> bool:
|
|
27
71
|
"""
|
|
28
72
|
Detect if we're running in a desktop environment.
|
|
@@ -82,6 +126,7 @@ class LeafDesktopLauncher:
|
|
|
82
126
|
self.is_running = True
|
|
83
127
|
|
|
84
128
|
# Create main window
|
|
129
|
+
_init_x11_threads()
|
|
85
130
|
self.root = tk.Tk()
|
|
86
131
|
self.root.title("LEAF - Lightweight Equipment Adapter Framework")
|
|
87
132
|
self.root.geometry("500x350")
|
|
@@ -281,6 +326,7 @@ class LeafDesktopLauncher:
|
|
|
281
326
|
|
|
282
327
|
# Call the stop callback in a separate thread
|
|
283
328
|
if self.on_stop_callback:
|
|
329
|
+
stop_finished = threading.Event()
|
|
284
330
|
|
|
285
331
|
def stop_thread():
|
|
286
332
|
try:
|
|
@@ -288,13 +334,24 @@ class LeafDesktopLauncher:
|
|
|
288
334
|
except Exception as e:
|
|
289
335
|
logger.error(f"Error in stop callback: {e}")
|
|
290
336
|
finally:
|
|
291
|
-
|
|
292
|
-
try:
|
|
293
|
-
self.root.after(1000, self.root.destroy)
|
|
294
|
-
except Exception:
|
|
295
|
-
pass
|
|
337
|
+
stop_finished.set()
|
|
296
338
|
|
|
297
339
|
threading.Thread(target=stop_thread, daemon=True).start()
|
|
340
|
+
|
|
341
|
+
# Tk/Tcl calls (root.after, root.destroy) must only ever happen
|
|
342
|
+
# on the main thread that's running mainloop() -- on Linux,
|
|
343
|
+
# Xlib can abort the whole process ("XInitThreads has not been
|
|
344
|
+
# called") if a background thread issues X11 calls concurrently
|
|
345
|
+
# with it. Polling here (itself scheduled via root.after, so it
|
|
346
|
+
# always runs on the main thread) instead of destroying directly
|
|
347
|
+
# from stop_thread keeps every Tk call on that one thread.
|
|
348
|
+
def wait_and_destroy():
|
|
349
|
+
if stop_finished.is_set():
|
|
350
|
+
self.root.destroy()
|
|
351
|
+
else:
|
|
352
|
+
self.root.after(100, wait_and_destroy)
|
|
353
|
+
|
|
354
|
+
self.root.after(1000, wait_and_destroy)
|
|
298
355
|
else:
|
|
299
356
|
# If no callback, just close
|
|
300
357
|
self.root.after(500, self.root.destroy)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/input_modules/external_api_watcher.py
RENAMED
|
File without changes
|
{leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/input_modules/external_event_watcher.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/measurement_modules/carbon_dioxide.py
RENAMED
|
File without changes
|
{leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/measurement_modules/dissolved_oxygen.py
RENAMED
|
File without changes
|
{leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/measurement_modules/fluorescence.py
RENAMED
|
File without changes
|
{leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/measurement_modules/measurement_module.py
RENAMED
|
File without changes
|
{leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/measurement_modules/optical_density.py
RENAMED
|
File without changes
|
|
File without changes
|
{leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/measurement_modules/temperature.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/phase_modules/external_event_phase.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/process_modules/continous_module.py
RENAMED
|
File without changes
|
{leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/process_modules/discrete_module.py
RENAMED
|
File without changes
|
{leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/process_modules/external_event_process.py
RENAMED
|
File without changes
|
{leaf_framework-0.9.4 → leaf_framework-0.9.6}/leaf/modules/process_modules/process_module.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|