Skip to content

gui: Add support for loading external installed modules - #106

Open
joshanne wants to merge 2 commits into
dronecan:masterfrom
joshanne:pr/add-plugin-support
Open

gui: Add support for loading external installed modules#106
joshanne wants to merge 2 commits into
dronecan:masterfrom
joshanne:pr/add-plugin-support

Conversation

@joshanne

@joshanne joshanne commented Nov 28, 2025

Copy link
Copy Markdown
Collaborator

I've been using a modified version of this for some time now.

It borrows from the idea that a user could extend the interface to provide their own functionality.

The most basic of plugin modules looks like so:

import dronecan
from PyQt6.QtWidgets import QVBoxLayout, QWidget, QLabel, QDialog, QPlainTextEdit
from PyQt6.QtCore import Qt
from logging import getLogger

__all__ = 'PANEL_NAME', 'spawn', 'get_icon'

PANEL_NAME = 'Custom Panel'

logger = getLogger(__name__)

_singleton = None

class CustomPanel(QDialog):
    def __init__(self, parent, node):
        super(CustomPanel, self).__init__(parent)
        self.setWindowTitle('My Custom Panel')
        self.setAttribute(Qt.WA_DeleteOnClose)

        self._node = node

        layout = QVBoxLayout(self)
        layout.addWidget(QLabel('This is a label!', self))
        self.setLayout(layout)

    def closeEvent(self, event):
        global _singleton
        _singleton = None
        super(CustomPanel, self).closeEvent(event)

def spawn(parent, node):
    global _singleton
    if _singleton is None:
        _singleton = CustomPanel(parent, node)

    _singleton.show()
    _singleton.raise_()
    _singleton.activateWindow()

    return _singleton

def get_icon():
    return None

These modules can then be distributed as wheels and loaded by the user when required.

@joshanne
joshanne force-pushed the pr/add-plugin-support branch from 8d7de29 to 7d7cade Compare December 11, 2025 02:33
@tridge
tridge force-pushed the pr/add-plugin-support branch from 7d7cade to 0c6fcdf Compare March 25, 2026 04:37
@joshanne
joshanne force-pushed the pr/add-plugin-support branch from 0c6fcdf to 9b74042 Compare March 30, 2026 20:32
@joshanne
joshanne force-pushed the pr/add-plugin-support branch from 9b74042 to 2f4825c Compare April 7, 2026 00:25
@joshanne
joshanne force-pushed the pr/add-plugin-support branch from 2f4825c to fa7e98b Compare June 1, 2026 23:39
@joshanne joshanne added this to the DroneCAN GUI Tool Release milestone Aug 4, 2026
@joshanne
joshanne requested a lite review from Copilot August 6, 2026 21:40

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a simple plugin mechanism that allows the GUI tool to load externally installed panel modules (e.g., distributed as wheels) and surface them in the UI as additional menu entries.

Changes:

  • Adds dynamic import support for panel modules via a new import_panel() helper and PluginPanelDescriptor.
  • Introduces a --load-module CLI flag to load one or more plugin modules at startup and register them into a new “Plugins” menu.
  • Adds support for optional hierarchical plugin menu placement via a MENU_PATH attribute.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
dronecan_gui_tool/panels/init.py Adds plugin import helper and a descriptor subclass to wrap external panel modules.
dronecan_gui_tool/main.py Adds CLI parsing, sys.path plugin directory injection, plugin loading, and a Plugins menu builder.
Suppressed comments (2)

dronecan_gui_tool/panels/init.py:45

  • import_panel() raises a generic Exception and prints to stdout. Using ModuleNotFoundError/ImportError keeps error handling idiomatic, and avoiding print prevents noisy output (the app already has logging configured).
def import_panel(name):
    """Given a package name like 'foo.bar.quux', imports the package
    and returns the desired module."""
    spec = importlib.util.find_spec(name)
    mod = None

dronecan_gui_tool/main.py:273

  • The shortcut string for plugin panels is malformed ("Ctrl+Shift+[,{n}") and likely won’t register as intended. The built-in Panels menu uses the standard Ctrl+Shift+ format.
                if idx < 9:
                    action.setShortcut(QKeySequence(f'Ctrl+Shift+[,{(idx + 1)}'))
                action.triggered.connect(lambda state, panel=panel: panel.safe_spawn(self, self._node))

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread dronecan_gui_tool/panels/__init__.py
Comment thread dronecan_gui_tool/main.py Outdated
Comment thread dronecan_gui_tool/main.py Outdated
@joshanne
joshanne force-pushed the pr/add-plugin-support branch from fa7e98b to 3053b5d Compare August 6, 2026 23:13
@joshanne

joshanne commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator Author

Copilot found some legitimate issues.
They have been resolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants