Example #1
0
    def __init__(self, plugin_provider, settings, application_context, settings_prefix=''):
        super(PluginManager, self).__init__()
        self.setObjectName('PluginManager')

        self._plugin_provider = plugin_provider
        self._settings = Settings(SettingsProxy(settings), '/'.join([x for x in ['plugin_manager', settings_prefix] if x != '']))
        self._application_context = application_context

        self._main_window = None
        self._container_manager = None
        self._plugin_menu = None
        self._minimized_dock_widgets_toolbar = None

        self._global_settings = None
        self._perspective_settings = None
        self._plugin_descriptors = None
        self._running_plugins = {}

        self._number_of_ongoing_calls = None

        if self._application_context.options.multi_process or self._application_context.options.embed_plugin:
            try:
                from .plugin_handler_xembed import PluginHandlerXEmbed  # @UnusedImport
            except ImportError:
                qCritical('PluginManager.__init__() multiprocess-mode only available under linux')
                exit(-1)

        # force connection type to queued, to delay the 'reloading' giving the 'unloading' time to finish
        self._deferred_reload_plugin_signal.connect(self._reload_plugin_load, type=Qt.QueuedConnection)

        if self._application_context.provide_app_dbus_interfaces:
            from .plugin_manager_dbus_interface import PluginManagerDBusInterface
            self._dbus_service = PluginManagerDBusInterface(self, self._application_context)
Example #2
0
 def _shutdown_plugin(self):
     if hasattr(self._plugin, 'shutdown_plugin'):
         try:
             self._plugin.shutdown_plugin()
         except Exception:
             qCritical('PluginHandlerDirect._shutdown_plugin() plugin "%s" raised an exception:\n%s' % (str(self._instance_id), traceback.format_exc()))
     self.emit_shutdown_plugin_completed()
Example #3
0
    def _load_plugin_load(self, instance_id, callback, argv=None):
        # if the requested instance is already running, do nothing
        if str(instance_id) in self._running_plugins:
            raise Exception('PluginManager._load_plugin(%s) instance already loaded' % str(instance_id))

        # containers are pseudo-plugins and handled by a special handler
        if self._container_manager is not None and instance_id.plugin_id == self._container_manager.get_container_descriptor().plugin_id():
            handler = PluginHandlerContainer(self, self._main_window, instance_id, self._application_context, self._container_manager)

        # use platform specific handler for multiprocess-mode if available
        elif self._application_context.options.multi_process or self._application_context.options.embed_plugin:
            try:
                from .plugin_handler_xembed import PluginHandlerXEmbed
                handler = PluginHandlerXEmbed(self, self._main_window, instance_id, self._application_context, self._container_manager, argv)
            except ImportError:
                qCritical('PluginManager._load_plugin() could not load plugin in a separate process')
                return

        # use direct handler for in-process plugins
        else:
            handler = PluginHandlerDirect(self, self._main_window, instance_id, self._application_context, self._container_manager, argv)

        handler.set_minimized_dock_widgets_toolbar(self._minimized_dock_widgets_toolbar)

        plugin_descriptor = self._plugin_descriptors[instance_id.plugin_id]
        handler.set_plugin_descriptor(plugin_descriptor)

        self._add_running_plugin(instance_id, handler)
        handler.load(self._plugin_provider, callback)
Example #4
0
    def __init__(self, plugin_provider, application_context):
        super(PluginManager, self).__init__()
        self.setObjectName('PluginManager')

        self._plugin_provider = plugin_provider
        self._application_context = application_context

        self._main_window = None
        self._container_manager = None
        self._plugin_menu = None

        self._global_settings = None
        self._perspective_settings = None
        self._plugin_descriptors = None
        self._running_plugins = {}

        self._number_of_ongoing_calls = None

        if self._application_context.options.multi_process or self._application_context.options.embed_plugin:
            try:
                from .plugin_handler_xembed import PluginHandlerXEmbed  # @UnusedImport
            except ImportError:
                qCritical(
                    'PluginManager.__init__() multiprocess-mode only available under linux'
                )
                exit(-1)

        # force connection type to queued, to delay the 'reloading' giving the 'unloading' time to finish
        self._deferred_reload_plugin_signal.connect(self._reload_plugin_load,
                                                    type=Qt.QueuedConnection)

        if self._application_context.provide_app_dbus_interfaces:
            from .plugin_manager_dbus_interface import PluginManagerDBusInterface
            self._dbus_service = PluginManagerDBusInterface(
                self, self._application_context)
Example #5
0
 def _emit_load_completed(self, exception=None):
     if exception is not None:
         self._garbage_widgets_and_toolbars()
     if self.__callback is not None:
         callback = self.__callback
         self.__callback = None
         callback(self, exception)
     elif exception is not None:
         qCritical('PluginHandler.load() failed%s' % (':\n%s' % str(exception) if exception != True else ''))
Example #6
0
 def _emit_load_completed(self, exception=None):
     if exception is not None:
         self._garbage_widgets_and_toolbars()
     if self.__callback is not None:
         callback = self.__callback
         self.__callback = None
         callback(self, exception)
     elif exception is not None:
         qCritical('PluginHandler.load() failed%s' % (':\n%s' % str(exception) if exception != True else ''))
 def _save_settings_from_remote(self):
     qDebug('PluginHandlerXEmbedClient._save_settings_from_remote()')
     try:
         plugin_settings = Settings(self._remote_plugin_settings, '')
         instance_settings = Settings(self._remote_instance_settings, '')
         self._save_settings(plugin_settings, instance_settings)
     except Exception:
         qCritical('PluginHandlerXEmbedClient._save_settings_from_remote() plugin "%s" raised an exception:\n%s' % (str(self._instance_id), traceback.format_exc()))
         self.emit_save_settings_completed()
 def _save_settings_from_remote(self):
     qDebug('PluginHandlerXEmbedClient._save_settings_from_remote()')
     try:
         plugin_settings = Settings(self._remote_plugin_settings, '')
         instance_settings = Settings(self._remote_instance_settings, '')
         self._save_settings(plugin_settings, instance_settings)
     except Exception:
         qCritical('PluginHandlerXEmbedClient._save_settings_from_remote() plugin "%s" raised an exception:\n%s' % (str(self._instance_id), traceback.format_exc()))
         self.emit_save_settings_completed()
Example #9
0
 def _restore_settings(self, plugin_settings, instance_settings):
     if hasattr(self._plugin, 'restore_settings'):
         plugin_settings_plugin = plugin_settings.get_settings('plugin')
         instance_settings_plugin = instance_settings.get_settings('plugin')
         try:
             self._plugin.restore_settings(plugin_settings_plugin, instance_settings_plugin)
         except Exception:
             qCritical('PluginHandlerDirect._restore_settings() plugin "%s" raised an exception:\n%s' % (str(self._instance_id), traceback.format_exc()))
     self.emit_restore_settings_completed()
Example #10
0
 def _call_method_on_all_dock_widgets(self, method_name, instance_settings):
     for dock_widget, _, _ in self._widgets.values():
         name = 'dock_widget' + dock_widget.objectName().replace(self._instance_id.tidy_str(), '', 1)
         settings = instance_settings.get_settings(name)
         method = getattr(dock_widget, method_name)
         try:
             method(settings)
         except Exception:
             qCritical('PluginHandler._call_method_on_all_dock_widgets(%s) failed:\n%s' % (method_name, traceback.format_exc()))
Example #11
0
 def _call_method_on_all_dock_widgets(self, method_name, instance_settings):
     for dock_widget, _, _ in self._widgets.values():
         name = 'dock_widget' + dock_widget.objectName().replace(self._instance_id.tidy_str(), '', 1)
         settings = instance_settings.get_settings(name)
         method = getattr(dock_widget, method_name)
         try:
             method(settings)
         except Exception:
             qCritical('PluginHandler._call_method_on_all_dock_widgets(%s) failed:\n%s' % (method_name, traceback.format_exc()))
 def _shutdown_plugin(self):
     if hasattr(self._plugin, 'shutdown_plugin'):
         try:
             self._plugin.shutdown_plugin()
         except Exception:
             qCritical(
                 'PluginHandlerDirect._shutdown_plugin() plugin "%s" raised an exception:\n%s'
                 % (str(self._instance_id), traceback.format_exc()))
     self.emit_shutdown_plugin_completed()
Example #13
0
 def shutdown_plugin(self, callback):
     """
     Shutdown plugin (`Plugin.shutdown_plugin()`) and remove all added widgets.
     Completion is signaled asynchronously if a callback is passed.
     """
     self.__callback = callback
     try:
         self._shutdown_plugin()
     except Exception:
         qCritical('PluginHandler.shutdown_plugin() plugin "%s" raised an exception:\n%s' % (str(self._instance_id), traceback.format_exc()))
         self.emit_shutdown_plugin_completed()
Example #14
0
 def shutdown_plugin(self, callback):
     """
     Shutdown plugin (`Plugin.shutdown_plugin()`) and remove all added widgets.
     Completion is signaled asynchronously if a callback is passed.
     """
     self.__callback = callback
     try:
         self._shutdown_plugin()
     except Exception:
         qCritical('PluginHandler.shutdown_plugin() plugin "%s" raised an exception:\n%s' % (str(self._instance_id), traceback.format_exc()))
         self.emit_shutdown_plugin_completed()
Example #15
0
 def unload(self, callback=None):
     """
     Unload plugin.
     Completion is signaled asynchronously if a callback is passed.
     """
     self.__callback = callback
     try:
         self._unload()
     except Exception:
         qCritical('PluginHandler.unload() plugin "%s" raised an exception:\n%s' % (str(self._instance_id), traceback.format_exc()))
         self._emit_unload_completed()
Example #16
0
 def unload(self, callback=None):
     """
     Unload plugin.
     Completion is signaled asynchronously if a callback is passed.
     """
     self.__callback = callback
     try:
         self._unload()
     except Exception:
         qCritical('PluginHandler.unload() plugin "%s" raised an exception:\n%s' % (str(self._instance_id), traceback.format_exc()))
         self._emit_unload_completed()
Example #17
0
 def _restore_settings(self, plugin_settings, instance_settings):
     if hasattr(self._plugin, 'restore_settings'):
         plugin_settings_plugin = plugin_settings.get_settings('plugin')
         instance_settings_plugin = instance_settings.get_settings('plugin')
         try:
             self._plugin.restore_settings(plugin_settings_plugin,
                                           instance_settings_plugin)
         except Exception:
             qCritical(
                 'PluginHandlerDirect._restore_settings() plugin "%s" raised an exception:\n%s'
                 % (str(self._instance_id), traceback.format_exc()))
     self.emit_restore_settings_completed()
 def discover(self, discovery_data):
     # discover plugins from all providers
     discovered_plugins = []
     for plugin_provider in self._plugin_providers:
         try:
             plugin_descriptors = plugin_provider.discover(discovery_data)
         except Exception:
             qCritical('CompositePluginProvider.discover() could not discover plugins from provider "%s":\n%s' % (type(plugin_provider), traceback.format_exc()))
         else:
             self._discovered_plugins[plugin_provider] = plugin_descriptors
             discovered_plugins += plugin_descriptors
     return discovered_plugins
Example #19
0
 def restore_settings(self, plugin_settings, instance_settings, callback=None):
     """
     Restore settings of the plugin (`Plugin.restore_settings()`) and all dock widget title bars.
     Completion is signaled asynchronously if a callback is passed.
     """
     qDebug('PluginHandler.restore_settings()')
     self.__instance_settings = instance_settings
     self.__callback = callback
     try:
         self._restore_settings(plugin_settings, instance_settings)
     except Exception:
         qCritical('PluginHandler.restore_settings() plugin "%s" raised an exception:\n%s' % (str(self._instance_id), traceback.format_exc()))
         self.emit_restore_settings_completed()
Example #20
0
 def restore_settings(self, plugin_settings, instance_settings, callback=None):
     """
     Restore settings of the plugin (`Plugin.restore_settings()`) and all dock widget title bars.
     Completion is signaled asynchronously if a callback is passed.
     """
     qDebug('PluginHandler.restore_settings()')
     self.__instance_settings = instance_settings
     self.__callback = callback
     try:
         self._restore_settings(plugin_settings, instance_settings)
     except Exception:
         qCritical('PluginHandler.restore_settings() plugin "%s" raised an exception:\n%s' % (str(self._instance_id), traceback.format_exc()))
         self.emit_restore_settings_completed()
Example #21
0
 def discover(self, discovery_data):
     # discover plugins from all providers
     discovered_plugins = []
     for plugin_provider in self._plugin_providers:
         try:
             plugin_descriptors = plugin_provider.discover(discovery_data)
         except Exception:
             qCritical(
                 'CompositePluginProvider.discover() could not discover plugins from '
                 'provider "%s":\n%s' % (type(plugin_provider), traceback.format_exc()))
         else:
             self._discovered_plugins[plugin_provider] = plugin_descriptors
             discovered_plugins += plugin_descriptors
     return discovered_plugins
Example #22
0
    def _load_plugin_completed(self, handler, exception):
        instance_id = handler.instance_id()
        if exception is not None:
            if isinstance(exception, PluginLoadError):
                qWarning('PluginManager._load_plugin() could not load plugin "%s": %s' % (instance_id.plugin_id, exception))
            else:
                qCritical('PluginManager._load_plugin() could not load plugin "%s"%s' % (instance_id.plugin_id, (':\n%s' % traceback.format_exc() if exception != True else '')))
            self._remove_running_plugin(instance_id)
            # quit embed application
            if self._application_context.options.embed_plugin:
                exit(-1)
            return

        qDebug('PluginManager._load_plugin(%s) successful' % str(instance_id))

        handler.close_signal.connect(self.unload_plugin)
        handler.reload_signal.connect(self.reload_plugin)
        handler.help_signal.connect(self._emit_plugin_help_signal)
    def discover(self):
        # discover plugins, which are providers themselves
        plugin_descriptors = self._plugin_provider.discover()

        # instantiate plugins
        plugin_providers = []
        for plugin_descriptor in plugin_descriptors:
            try:
                # pass None as PluginContext for PluginProviders
                instance = self._plugin_provider.load(plugin_descriptor.plugin_id(), None)
            except Exception:
                qCritical('RecursivePluginProvider.discover() loading plugin "%s" failed:\n%s' % (str(plugin_descriptor.plugin_id()), traceback.format_exc()))
            else:
                if instance is not None:
                    plugin_providers.append(instance)

        # delegate discovery through instantiated plugin providers to base class
        self.set_plugin_providers(plugin_providers)
        return CompositePluginProvider.discover(self)
    def discover(self, discovery_data):
        # discover plugins, which are providers themselves
        plugin_descriptors = self._plugin_provider.discover(discovery_data)

        # instantiate plugins
        plugin_providers = []
        for plugin_descriptor in plugin_descriptors:
            try:
                # pass None as PluginContext for PluginProviders
                instance = self._plugin_provider.load(plugin_descriptor.plugin_id(), None)
            except Exception:
                qCritical('RecursivePluginProvider.discover() loading plugin "%s" failed:\n%s' % (str(plugin_descriptor.plugin_id()), traceback.format_exc()))
            else:
                if instance is not None:
                    plugin_providers.append(instance)

        # delegate discovery through instantiated plugin providers to base class
        self.set_plugin_providers(plugin_providers)
        return CompositePluginProvider.discover(self, discovery_data)
    def load(self, plugin_id, plugin_context):
        # get class reference from plugin descriptor
        attributes = self._plugin_descriptors[plugin_id].attributes()
        sys.path.append(os.path.join(attributes['plugin_path'], attributes['library_path']))

        try:
            module = __builtin__.__import__(
                attributes['module_name'], fromlist=[attributes['class_from_class_type']], level=0)
        except NotImplementedError as e:
            qCritical('RosPluginProvider.load(%s): raised an exception:\n%s' % (plugin_id, e))
            return None
        except Exception as e:
            qCritical('RosPluginProvider.load(%s) exception raised in '
                      '__builtin__.__import__(%s, [%s]):\n%s' % (
                          plugin_id, attributes['module_name'],
                          attributes['class_from_class_type'],
                          traceback.format_exc()))
            raise e

        class_ref = getattr(module, attributes['class_from_class_type'], None)
        if class_ref is None:
            qCritical('RosPluginProvider.load(%s): could not find class "%s" in module "%s"' %
                      (plugin_id, attributes['class_from_class_type'], module))
            return None

        # create plugin provider instance without context
        try:
            code = class_ref.__init__.func_code
        except AttributeError:
            code = class_ref.__init__.__code__
        if code.co_argcount == 1 and plugin_context is None:
            return class_ref()
        # create plugin instance
        return class_ref(plugin_context)
Example #26
0
    def __init__(self, plugin_provider, settings, application_context, settings_prefix=''):
        super(PluginManager, self).__init__()
        self.setObjectName('PluginManager')

        self._plugin_provider = plugin_provider
        self._settings = Settings(SettingsProxy(settings), '/'.join(
            [x for x in ['plugin_manager', settings_prefix] if x != '']))
        self._application_context = application_context

        self._main_window = None
        self._container_manager = None
        self._plugin_menu = None
        self._minimized_dock_widgets_toolbar = None

        self._global_settings = None
        self._perspective_settings = None
        self._plugin_descriptors = None
        self._running_plugins = {}

        self._number_of_ongoing_calls = None

        if self._application_context.options.multi_process or \
                self._application_context.options.embed_plugin:
            try:
                from qt_gui.plugin_handler_xembed import PluginHandlerXEmbed  # noqa: F401
            except ImportError:
                qCritical('PluginManager.__init__() multiprocess-mode only available under linux')
                exit(-1)

        # force connection type to queued, to delay the 'reloading' giving the
        # 'unloading' time to finish
        self._deferred_reload_plugin_signal.connect(
            self._reload_plugin_load, type=Qt.QueuedConnection)

        if self._application_context.provide_app_dbus_interfaces:
            from qt_gui.plugin_manager_dbus_interface import PluginManagerDBusInterface
            self._dbus_service = PluginManagerDBusInterface(self, self._application_context)
Example #27
0
def load_plugins():
    """
    Finds all rqt_bag plugins.

    @return: a list of plugins
    @rtype:  list of functions which return tuples of (MessageView, TimelineRenderer, list of: message type or '*')
    """
    plugins = []

    rospack = rospkg.RosPack()
    to_check = rospack.get_depends_on('rqt_bag', implicit=False)

    for pkg in to_check:
        manifest = rospack.get_manifest(pkg)
        plugin_module_names = manifest.get_export('rqt_bag', 'plugin')
        if not plugin_module_names:
            continue
        elif len(plugin_module_names) != 1:
            qCritical("Cannot load plugin [%s]: invalid 'plugin' attribute" %
                      (pkg))
            continue
        plugin_module_name = plugin_module_names[0]

        try:
            # Load that package's namespace
            roslib.load_manifest(pkg)

            # Import specified plugin module
            plugin_module = __import__(plugin_module_name)
            for sub_module in plugin_module_name.split('.')[1:]:
                plugin_module = getattr(plugin_module, sub_module)

            # Retrieve the function
            plugins_func = None
            try:
                plugins_func = getattr(plugin_module, 'get_rqt_bag_plugins')
            except AttributeError:
                pass

            if plugins_func:
                plugins.extend(plugins_func())
            else:
                qCritical(
                    "Cannot load plugin [%s]: no 'get_rqt_bag_plugins' attribute"
                    % (plugin_module_name))

        except Exception:
            qCritical("Unable to load plugin [%s] from package [%s]: %s" %
                      (plugin_module_name, pkg, traceback.format_exc()))
    return plugins
    def load(self, plugin_id, plugin_context):
        # get class reference from plugin descriptor
        attributes = self._plugin_descriptors[plugin_id].attributes()
        sys.path.append(os.path.join(attributes['plugin_path'], attributes['library_path']))

        try:
            module = __builtin__.__import__(attributes['module_name'], fromlist=[attributes['class_from_class_type']], level=0)
        except NotImplementedError as e:
            qCritical('RosPluginProvider.load(%s): raised an exception:\n%s' % (plugin_id, e))
            return None
        except Exception as e:
            qCritical('RosPluginProvider.load(%s) exception raised in __builtin__.__import__(%s, [%s]):\n%s' % (plugin_id, attributes['module_name'], attributes['class_from_class_type'], traceback.format_exc()))
            raise e

        class_ref = getattr(module, attributes['class_from_class_type'], None)
        if class_ref is None:
            qCritical('RosPluginProvider.load(%s): could not find class "%s" in module "%s"' % (plugin_id, attributes['class_from_class_type'], module))
            return None

        # create plugin provider instance without context
        if class_ref.__init__.func_code.co_argcount == 1 and plugin_context is None:
            return class_ref()
        # create plugin instance
        return class_ref(plugin_context)
    def _parse_plugin_xml(self, package_name, plugin_xml):
        plugin_descriptors = []
        if not os.path.isfile(plugin_xml):
            qCritical('RosPluginProvider._parse_plugin_xml() plugin file "%s" in package "%s" '
                      'not found' % (plugin_xml, package_name))
            return plugin_descriptors

        try:
            root = ElementTree.parse(plugin_xml)
        except Exception:
            qCritical('RosPluginProvider._parse_plugin_xml() could not parse "%s" in package "%s"'
                      % (plugin_xml, package_name))
            return plugin_descriptors
        for library_el in root.getiterator('library'):
            library_path = library_el.attrib['path']

            for class_el in library_el.getiterator('class'):
                # collect common attributes
                attributes = {
                    'package_name': package_name,
                    'plugin_path': os.path.dirname(plugin_xml),
                    'library_path': library_path,
                }
                # add class attributes
                for key, value in class_el.items():
                    attributes['class_' + key] = value

                # skip classes with non-matching _base_class_type
                class_base_class_type = attributes.get('class_base_class_type', None)
                if class_base_class_type != self._base_class_type:
                    continue

                # generate unique identifier
                plugin_id = package_name
                if 'class_name' in attributes:
                    plugin_id = plugin_id + '/' + attributes['class_name']
                attributes['plugin_id'] = plugin_id

                # separate module name and class name
                module_name, class_from_class_type = attributes['class_type'].rsplit('.', 1)
                attributes['module_name'] = module_name
                attributes['class_from_class_type'] = class_from_class_type

                # we can not check if the plugin is available without loading it
                attributes['not_available'] = ''

                plugin_descriptor = PluginDescriptor(plugin_id, attributes)

                # set action attributes (plugin providers might have none)
                action_attributes, groups = self._parse_plugin(class_el)
                if len(action_attributes) > 0:
                    plugin_descriptor.set_action_attributes(
                        action_attributes['label'],
                        action_attributes.get('statustip', None),
                        action_attributes.get('icon', None),
                        action_attributes.get('icontype', None),
                    )
                # add group attributes
                for group in groups:
                    plugin_descriptor.add_group_attributes(
                        group['label'],
                        group.get('statustip', None),
                        group.get('icon', None),
                        group.get('icontype', None),
                    )

                # add plugin_descriptor to list
                plugin_descriptors.append(plugin_descriptor)

        return plugin_descriptors
Example #30
0
    def _parse_plugin_xml(self, plugin_name, xml_file_name):
        plugin_descriptors = []
        plugin_path = os.path.dirname(os.path.abspath(xml_file_name))

        try:
            root = ElementTree.parse(xml_file_name)
        except Exception:
            qCritical(
                'RosPluginProvider._parse_plugin_xml() could not parse "%s" of plugin "%s"'
                % (xml_file_name, plugin_name))
            return plugin_descriptors
        for library_el in root.getiterator('library'):
            library_path = library_el.attrib['path']

            for class_el in library_el.getiterator('class'):
                # collect common attributes
                attributes = {
                    'plugin_name': plugin_name,
                    'plugin_path': plugin_path,
                    'library_path': library_path,
                }

                # add class attributes
                for key, value in class_el.items():
                    attributes['class_' + key] = value

                # skip classes with non-matching _base_class_type
                class_base_class_type = attributes.get('class_base_class_type',
                                                       None)
                if class_base_class_type != self._base_class_type:
                    continue

                # generate unique identifier
                plugin_id = plugin_name
                if 'class_name' in attributes:
                    plugin_id = plugin_id + '/' + attributes['class_name']
                attributes['plugin_id'] = plugin_id

                # base path to look for module
                module_base_path = plugin_path
                if library_path != '':
                    module_base_path = os.path.join(module_base_path,
                                                    library_path)
                attributes['module_base_path'] = module_base_path

                # separate module name and class name
                module_name, class_from_class_type = os.path.split(
                    attributes['class_type'].replace('.', os.sep))
                attributes['module_name'] = module_name.replace(os.sep, '.')
                attributes['class_from_class_type'] = class_from_class_type

                # check if plugin is available
                module_abs_path = os.path.join(module_base_path,
                                               module_name) + '.py'
                attributes[
                    'not_available'] = plugin_name if not os.path.exists(
                        module_abs_path) else ''

                plugin_descriptor = PluginDescriptor(plugin_id, attributes)

                # set action attributes (plugin providers might have none)
                action_attributes, groups = self._parse_plugin(class_el)
                if len(action_attributes) > 0:
                    plugin_descriptor.set_action_attributes(
                        action_attributes['label'],
                        action_attributes.get('statustip', None),
                        action_attributes.get('icon', None),
                        action_attributes.get('icontype', None),
                    )
                # add group attributes
                for group in groups:
                    plugin_descriptor.add_group_attributes(
                        group['label'],
                        group.get('statustip', None),
                        group.get('icon', None),
                        group.get('icontype', None),
                    )

                # add plugin_descriptor to list
                plugin_descriptors.append(plugin_descriptor)

        return plugin_descriptors
    def _parse_plugin_xml(self, package_name, plugin_xml):
        plugin_descriptors = []

        if not os.path.isfile(plugin_xml):
            qCritical('RosPluginProvider._parse_plugin_xml() plugin file "%s" in package "%s" not found' % (plugin_xml, package_name))
            return plugin_descriptors

        try:
            root = ElementTree.parse(plugin_xml)
        except Exception:
            qCritical('RosPluginProvider._parse_plugin_xml() could not parse "%s" in package "%s"' % (plugin_xml, package_name))
            return plugin_descriptors
        for library_el in root.getiterator('library'):
            library_path = library_el.attrib['path']

            for class_el in library_el.getiterator('class'):
                # collect common attributes
                attributes = {
                    'package_name': package_name,
                    'plugin_path': os.path.dirname(plugin_xml),
                    'library_path': library_path,
                }

                # add class attributes
                for key, value in class_el.items():
                    attributes['class_' + key] = value

                # skip classes with non-matching _base_class_type
                class_base_class_type = attributes.get('class_base_class_type', None)
                if class_base_class_type != self._base_class_type:
                    continue

                # generate unique identifier
                plugin_id = package_name
                if 'class_name' in attributes:
                    plugin_id = plugin_id + '/' + attributes['class_name']
                attributes['plugin_id'] = plugin_id

                # separate module name and class name
                module_name, class_from_class_type = attributes['class_type'].rsplit('.', 1)
                attributes['module_name'] = module_name
                attributes['class_from_class_type'] = class_from_class_type

                # we can not check if the plugin is available without loading it
                attributes['not_available'] = ''

                plugin_descriptor = PluginDescriptor(plugin_id, attributes)

                # set action attributes (plugin providers might have none)
                action_attributes, groups = self._parse_plugin(class_el)
                if len(action_attributes) > 0:
                    plugin_descriptor.set_action_attributes(
                        action_attributes['label'],
                        action_attributes.get('statustip', None),
                        action_attributes.get('icon', None),
                        action_attributes.get('icontype', None),
                    )
                # add group attributes
                for group in groups:
                    plugin_descriptor.add_group_attributes(
                        group['label'],
                        group.get('statustip', None),
                        group.get('icon', None),
                        group.get('icontype', None),
                    )

                # add plugin_descriptor to list
                plugin_descriptors.append(plugin_descriptor)

        return plugin_descriptors