|
15 | 15 |
|
16 | 16 | assert sys.version[0] == '3' |
17 | 17 |
|
| 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 | + |
18 | 24 | from argparse import ArgumentParser |
19 | 25 | parser = ArgumentParser(description='DroneCAN GUI tool') |
20 | 26 |
|
|
30 | 36 | parser.add_argument("--target-system", help="set the targetted system", type=int, default=0) |
31 | 37 | parser.add_argument("--source-system", help="set the source system", type=int, default=250) |
32 | 38 |
|
| 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 | + |
33 | 41 | args = parser.parse_args() |
34 | 42 |
|
35 | 43 | # |
|
103 | 111 | from .widgets.can_adapter_control_panel import spawn_window as spawn_can_adapter_control_panel |
104 | 112 |
|
105 | 113 | from .panels import PANELS |
| 114 | +from .panels import import_panel |
106 | 115 |
|
| 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!") |
107 | 126 |
|
108 | 127 | NODE_NAME = 'org.dronecan.gui_tool' |
109 | 128 |
|
@@ -212,6 +231,39 @@ def __init__(self, node, iface_name, iface_kwargs): |
212 | 231 | action.triggered.connect(lambda state, panel=panel: panel.safe_spawn(self, self._node)) |
213 | 232 | panels_menu.addAction(action) |
214 | 233 |
|
| 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 | + |
215 | 267 | # |
216 | 268 | # Help menu |
217 | 269 | # |
|
0 commit comments