Skip to content

Commit 7d7cade

Browse files
committed
gui: Add support for loading external installed modules
1 parent 9c67cc8 commit 7d7cade

File tree

2 files changed

+71
-0
lines changed

2 files changed

+71
-0
lines changed

dronecan_gui_tool/main.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@
1515

1616
assert sys.version[0] == '3'
1717

18+
def parse_load_modules(argv):
19+
modules = [m.strip() for m in argv.split(",") if m.strip()]
20+
if not modules:
21+
return None
22+
return modules
23+
1824
from argparse import ArgumentParser
1925
parser = ArgumentParser(description='DroneCAN GUI tool')
2026

@@ -28,6 +34,8 @@
2834
parser.add_argument("--filtered", action='store_true', help="enable filtering of DroneCAN traffic")
2935
parser.add_argument("--target-system", help="set the targetted system", type=int, default=0)
3036

37+
parser.add_argument("--load-module", type=parse_load_modules, nargs=1, help="Comma-separated list of modules to load (e.g. mod1,mod2).") # exactly one argument required if flag is present
38+
3139
args = parser.parse_args()
3240

3341
#
@@ -95,7 +103,18 @@
95103
from .widgets.can_adapter_control_panel import spawn_window as spawn_can_adapter_control_panel
96104

97105
from .panels import PANELS
106+
from .panels import import_panel
98107

108+
EXT_PLUGINS = []
109+
modules = args.load_module[0] if args.load_module else []
110+
if len(modules) > 0:
111+
for module in modules:
112+
try:
113+
panel = import_panel(module)
114+
EXT_PLUGINS.append(panel)
115+
except Exception as ex:
116+
print(f"Unable to load {module}: {ex}")
117+
print(f"Loaded {len(EXT_PLUGINS)} plugin modules!")
99118

100119
NODE_NAME = 'org.dronecan.gui_tool'
101120

@@ -204,6 +223,39 @@ def __init__(self, node, iface_name, iface_kwargs):
204223
action.triggered.connect(lambda state, panel=panel: panel.safe_spawn(self, self._node))
205224
panels_menu.addAction(action)
206225

226+
#
227+
# External Modules menu
228+
#
229+
def get_or_create_submenu(parent_menu, menu_name):
230+
"""
231+
Find a submenu with menu_name under parent_menu, or create it if not found.
232+
"""
233+
for action in parent_menu.actions():
234+
submenu = action.menu()
235+
if submenu and submenu.title() == menu_name:
236+
return submenu
237+
# Not found, create new submenu
238+
return parent_menu.addMenu(menu_name)
239+
240+
if len(EXT_PLUGINS) > 0:
241+
extern_modules_menu = self.menuBar().addMenu('P&lugins')
242+
for idx, panel in enumerate(EXT_PLUGINS):
243+
menu_path = getattr(panel, "menu_path", "")
244+
path_parts = [p for p in menu_path.split("/") if p]
245+
246+
current_menu = extern_modules_menu
247+
for part in path_parts:
248+
current_menu = get_or_create_submenu(current_menu, part)
249+
250+
action = QAction(panel.name, self)
251+
icon = panel.get_icon()
252+
if icon:
253+
action.setIcon(icon)
254+
if idx < 9:
255+
action.setShortcut(QKeySequence(f'Ctrl+Shift+[,{(idx + 1)}'))
256+
action.triggered.connect(lambda state, panel=panel: panel.safe_spawn(self, self._node))
257+
current_menu.addAction(action)
258+
207259
#
208260
# Help menu
209261
#

dronecan_gui_tool/panels/__init__.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
from . import hobbywing_esc
1919
from . import rc_panel
2020

21+
import importlib.util
22+
2123
class PanelDescriptor:
2224
def __init__(self, module):
2325
self.name = module.PANEL_NAME
@@ -36,6 +38,23 @@ def safe_spawn(self, parent, node):
3638
except Exception as ex:
3739
show_error('Panel error', 'Could not spawn panel', ex)
3840

41+
def import_panel(name):
42+
"""Given a package name like 'foo.bar.quux', imports the package
43+
and returns the desired module."""
44+
spec = importlib.util.find_spec(name)
45+
mod = None
46+
if spec is None:
47+
raise Exception(f"Module '{name}' not found!")
48+
else:
49+
mod = importlib.import_module(name)
50+
print(f"Successfully imported {name} from {mod.__file__}")
51+
return PluginPanelDescriptor(mod)
52+
53+
class PluginPanelDescriptor(PanelDescriptor):
54+
def __init__(self, module):
55+
super().__init__(module)
56+
57+
self.menu_path = getattr(module, "MENU_PATH", "")
3958

4059
PANELS = [
4160
PanelDescriptor(esc_panel),

0 commit comments

Comments
 (0)