Skip to content

Commit 782c6b0

Browse files
committed
gui: Add support for loading external installed modules
1 parent 35864d0 commit 782c6b0

2 files changed

Lines changed: 71 additions & 0 deletions

File tree

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

@@ -30,6 +36,8 @@
3036
parser.add_argument("--target-system", help="set the targetted system", type=int, default=0)
3137
parser.add_argument("--source-system", help="set the source system", type=int, default=250)
3238

39+
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
40+
3341
args = parser.parse_args()
3442

3543
#
@@ -103,7 +111,18 @@
103111
from .widgets.can_adapter_control_panel import spawn_window as spawn_can_adapter_control_panel
104112

105113
from .panels import PANELS
114+
from .panels import import_panel
106115

116+
EXT_PLUGINS = []
117+
modules = args.load_module[0] if args.load_module else []
118+
if len(modules) > 0:
119+
for module in modules:
120+
try:
121+
panel = import_panel(module)
122+
EXT_PLUGINS.append(panel)
123+
except Exception as ex:
124+
print(f"Unable to load {module}: {ex}")
125+
print(f"Loaded {len(EXT_PLUGINS)} plugin modules!")
107126

108127
NODE_NAME = 'org.dronecan.gui_tool'
109128

@@ -212,6 +231,39 @@ def __init__(self, node, iface_name, iface_kwargs):
212231
action.triggered.connect(lambda state, panel=panel: panel.safe_spawn(self, self._node))
213232
panels_menu.addAction(action)
214233

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

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)