5.8 KiB
Release Notes — MeshCore GUI
Date: 4 February 2026
Summary
This release replaces the single-file monolith (meshcore_gui.py, 1,395 lines, 3 classes, 51 methods) with a modular package of 16 files (1,955 lines, 10 classes, 90 methods). The refactoring introduces a meshcore_gui/ package with Protocol-based dependency inversion, a widgets/ subpackage with six independent UI components, a message route visualisation page, and full type coverage.
Starting point
The repository contained one file with everything in it:
meshcore_gui.py — 1,395 lines, 3 classes, 51 methods
| Section | Lines | Methods | Responsibility |
|---|---|---|---|
Config + debug_print |
80 | 1 | Constants, debug helper |
SharedData |
225 | 12 | Thread-safe data store |
BLEWorker |
268 | 11 | BLE communication thread |
MeshCoreGUI |
740 | 24 | All GUI: rendering, data updates, user actions |
| Main entry | 74 | 3 | Page handler, main() |
All three classes lived in one file. BLEWorker and MeshCoreGUI both depended directly on the concrete SharedData class. MeshCoreGUI handled everything: 8 render methods, 7 data-update methods, 5 user-action methods, the 500ms update timer, and the DM dialog.
Current state
16 files across a package with a widgets/ subpackage:
| File | Lines | Class | Depends on |
|---|---|---|---|
meshcore_gui.py |
101 | (entry point) | concrete SharedData (composition root) |
meshcore_gui/__init__.py |
8 | — | — |
meshcore_gui/config.py |
54 | — | — |
meshcore_gui/protocols.py |
83 | 4 Protocol classes | — |
meshcore_gui/shared_data.py |
263 | SharedData | config |
meshcore_gui/ble_worker.py |
252 | BLEWorker | SharedDataWriter protocol |
meshcore_gui/main_page.py |
148 | DashboardPage | SharedDataReader protocol |
meshcore_gui/route_builder.py |
174 | RouteBuilder | ContactLookup protocol |
meshcore_gui/route_page.py |
258 | RoutePage | SharedDataReadAndLookup protocol |
meshcore_gui/widgets/__init__.py |
22 | — | — |
meshcore_gui/widgets/device_panel.py |
100 | DevicePanel | config |
meshcore_gui/widgets/map_panel.py |
80 | MapPanel | — |
meshcore_gui/widgets/contacts_panel.py |
114 | ContactsPanel | config |
meshcore_gui/widgets/message_input.py |
83 | MessageInput | — |
meshcore_gui/widgets/message_list.py |
156 | MessageList | — |
meshcore_gui/widgets/rx_log_panel.py |
59 | RxLogPanel | — |
| Total | 1,955 | 10 classes |
What changed
1. Monolith → package
The single file was split into a meshcore_gui/ package. Each class got its own module. Constants and debug_print moved to config.py. The original meshcore_gui.py became a thin entry point (101 lines) that wires components and starts the server.
2. Protocol-based dependency inversion
Four typing.Protocol interfaces were introduced in protocols.py:
| Protocol | Consumer | Methods |
|---|---|---|
| SharedDataWriter | BLEWorker | 10 |
| SharedDataReader | DashboardPage | 4 |
| ContactLookup | RouteBuilder | 1 |
| SharedDataReadAndLookup | RoutePage | 5 |
No consumer imports shared_data.py directly. Only the entry point knows the concrete class.
3. MeshCoreGUI decomposed into DashboardPage + 6 widgets
The 740-line MeshCoreGUI class was split:
| Old (MeshCoreGUI) | New | Lines |
|---|---|---|
8 _render_* methods |
6 widget classes in widgets/ |
592 total |
7 _update_* methods |
Widget update() methods |
(inside widgets) |
| 5 user-action methods | Widget on_command callbacks |
(inside widgets) |
render() + _update_ui() |
DashboardPage (orchestrator) | 148 |
DashboardPage now has 4 methods. It composes widgets and drives the timer. Widgets have zero knowledge of SharedData — they receive plain Dict snapshots and callbacks.
4. Route visualisation (new feature)
Two new modules that did not exist in the monolith:
| Module | Lines | Purpose |
|---|---|---|
route_builder.py |
174 | Constructs route data from message metadata (pure logic) |
route_page.py |
258 | Renders route on a Leaflet map in a separate browser tab |
Clicking a message in the message list opens /route/{msg_index} showing sender → repeater hops → receiver on a map.
5. SharedData extended
SharedData gained 4 new methods to support the protocol interfaces and route feature:
| New method | Purpose |
|---|---|
set_connected() |
Explicit setter (was direct attribute access) |
put_command() |
Queue command from GUI (was cmd_queue.put() directly) |
get_next_command() |
Dequeue command for BLE worker (was cmd_queue.get_nowait() directly) |
get_contact_by_prefix() |
Contact lookup for route building |
get_contact_name_by_prefix() |
Contact name lookup for DM display |
The direct self.shared.lock and self.shared.cmd_queue access from BLEWorker and MeshCoreGUI was replaced with proper method calls through protocol interfaces.
6. Full type coverage
All 90 methods now have complete type annotations (parameters and return types). The old monolith had 51 methods with partial coverage.
Metrics
| Metric | Old | Current |
|---|---|---|
| Files | 1 | 16 |
| Lines | 1,395 | 1,955 |
| Classes | 3 | 10 |
| Methods | 51 | 90 |
| Largest class (lines) | MeshCoreGUI (740) | SharedData (263) |
| Protocol interfaces | 0 | 4 |
| Type-annotated methods | partial | 90/90 |
| Widget classes | 0 | 6 |
Documentation
| Document | Status |
|---|---|
README.md |
Updated: architecture diagram, project structure, features |
docs/MeshCore_GUI_Design.docx |
Updated: widget tables, component descriptions, version history |
docs/SOLID_ANALYSIS.md |
Updated: widget SRP, dependency tree, metrics |
docs/RELEASE.md |
New (this document) |