ros_plugin_provider.py
Go to the documentation of this file.
00001 # Copyright (c) 2011, Dirk Thomas, Dorian Scholz, TU Darmstadt
00002 # All rights reserved.
00003 #
00004 # Redistribution and use in source and binary forms, with or without
00005 # modification, are permitted provided that the following conditions
00006 # are met:
00007 #
00008 #   * Redistributions of source code must retain the above copyright
00009 #     notice, this list of conditions and the following disclaimer.
00010 #   * Redistributions in binary form must reproduce the above
00011 #     copyright notice, this list of conditions and the following
00012 #     disclaimer in the documentation and/or other materials provided
00013 #     with the distribution.
00014 #   * Neither the name of the TU Darmstadt nor the names of its
00015 #     contributors may be used to endorse or promote products derived
00016 #     from this software without specific prior written permission.
00017 #
00018 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00019 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00020 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00021 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00022 # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00023 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00024 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00026 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00027 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00028 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00029 # POSSIBILITY OF SUCH DAMAGE.
00030 
00031 import __builtin__
00032 import os
00033 import sys
00034 import traceback
00035 from xml.etree import ElementTree
00036 
00037 from python_qt_binding.QtCore import qCritical
00038 
00039 from qt_gui.plugin_descriptor import PluginDescriptor
00040 from qt_gui.plugin_provider import PluginProvider
00041 from qt_gui.ros_package_helper import get_package_path
00042 
00043 
00044 class RosPluginProvider(PluginProvider):
00045 
00046     """Base class for providing plugins based on the ROS package system."""
00047 
00048     def __init__(self, export_tag, base_class_type):
00049         super(RosPluginProvider, self).__init__()
00050         self.setObjectName('RosPluginProvider')
00051 
00052         self._export_tag = export_tag
00053         self._base_class_type = base_class_type
00054         self._plugin_descriptors = {}
00055 
00056     def discover(self, discovery_data):
00057         """
00058         Discover the plugins.
00059         The information of the `PluginDescriptor`s are extracted from the plugin manifests.
00060         """
00061         # search for plugins
00062         plugin_descriptors = []
00063         plugin_file_list = self._find_plugins(self._export_tag, discovery_data)
00064         for package_name, plugin_xml in plugin_file_list:
00065             plugin_descriptors += self._parse_plugin_xml(package_name, plugin_xml)
00066         # add list of discovered plugins to dictionary of known descriptors index by the plugin id
00067         for plugin_descriptor in plugin_descriptors:
00068             self._plugin_descriptors[plugin_descriptor.plugin_id()] = plugin_descriptor
00069         return plugin_descriptors
00070 
00071     def load(self, plugin_id, plugin_context):
00072         # get class reference from plugin descriptor
00073         attributes = self._plugin_descriptors[plugin_id].attributes()
00074         sys.path.append(os.path.join(attributes['plugin_path'], attributes['library_path']))
00075 
00076         try:
00077             module = __builtin__.__import__(attributes['module_name'], fromlist=[attributes['class_from_class_type']], level=0)
00078         except NotImplementedError as e:
00079             qCritical('RosPluginProvider.load(%s): raised an exception:\n%s' % (plugin_id, e))
00080             return None
00081         except Exception as e:
00082             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()))
00083             raise e
00084 
00085         class_ref = getattr(module, attributes['class_from_class_type'], None)
00086         if class_ref is None:
00087             qCritical('RosPluginProvider.load(%s): could not find class "%s" in module "%s"' % (plugin_id, attributes['class_from_class_type'], module))
00088             return None
00089 
00090         # create plugin provider instance without context
00091         if class_ref.__init__.func_code.co_argcount == 1 and plugin_context is None:
00092             return class_ref()
00093         # create plugin instance
00094         return class_ref(plugin_context)
00095 
00096     def unload(self, plugin_instance):
00097         pass
00098 
00099     def _find_plugins(self, export_tag, discovery_data):
00100         raise NotImplementedError
00101 
00102     def _parse_plugin_xml(self, package_name, plugin_xml):
00103         plugin_descriptors = []
00104 
00105         if not os.path.isfile(plugin_xml):
00106             qCritical('RosPluginProvider._parse_plugin_xml() plugin file "%s" in package "%s" not found' % (plugin_xml, package_name))
00107             return plugin_descriptors
00108 
00109         try:
00110             root = ElementTree.parse(plugin_xml)
00111         except Exception:
00112             qCritical('RosPluginProvider._parse_plugin_xml() could not parse "%s" in package "%s"' % (plugin_xml, package_name))
00113             return plugin_descriptors
00114         for library_el in root.getiterator('library'):
00115             library_path = library_el.attrib['path']
00116 
00117             for class_el in library_el.getiterator('class'):
00118                 # collect common attributes
00119                 attributes = {
00120                     'package_name': package_name,
00121                     'plugin_path': os.path.dirname(plugin_xml),
00122                     'library_path': library_path,
00123                 }
00124 
00125                 # add class attributes
00126                 for key, value in class_el.items():
00127                     attributes['class_' + key] = value
00128 
00129                 # skip classes with non-matching _base_class_type
00130                 class_base_class_type = attributes.get('class_base_class_type', None)
00131                 if class_base_class_type != self._base_class_type:
00132                     continue
00133 
00134                 # generate unique identifier
00135                 plugin_id = package_name
00136                 if 'class_name' in attributes:
00137                     plugin_id = plugin_id + '/' + attributes['class_name']
00138                 attributes['plugin_id'] = plugin_id
00139 
00140                 # separate module name and class name
00141                 module_name, class_from_class_type = attributes['class_type'].rsplit('.', 1)
00142                 attributes['module_name'] = module_name
00143                 attributes['class_from_class_type'] = class_from_class_type
00144 
00145                 # we can not check if the plugin is available without loading it
00146                 attributes['not_available'] = ''
00147 
00148                 plugin_descriptor = PluginDescriptor(plugin_id, attributes)
00149 
00150                 # set action attributes (plugin providers might have none)
00151                 action_attributes, groups = self._parse_plugin(class_el)
00152                 if len(action_attributes) > 0:
00153                     plugin_descriptor.set_action_attributes(
00154                         action_attributes['label'],
00155                         action_attributes.get('statustip', None),
00156                         action_attributes.get('icon', None),
00157                         action_attributes.get('icontype', None),
00158                     )
00159                 # add group attributes
00160                 for group in groups:
00161                     plugin_descriptor.add_group_attributes(
00162                         group['label'],
00163                         group.get('statustip', None),
00164                         group.get('icon', None),
00165                         group.get('icontype', None),
00166                     )
00167 
00168                 # add plugin_descriptor to list
00169                 plugin_descriptors.append(plugin_descriptor)
00170 
00171         return plugin_descriptors
00172 
00173     def _parse_plugin(self, class_el):
00174         # create default plugin descriptor and group
00175         plugin_attributes = {}
00176         groups = []
00177 
00178         # update descriptor and group from qtgui tag
00179         guiplugin_el = class_el.find('qtgui')
00180         if guiplugin_el is not None:
00181             plugin_attributes.update(self._parse_action_group(guiplugin_el))
00182             for group_el in guiplugin_el.getiterator('group'):
00183                 groups.append(self._parse_action_group(group_el))
00184 
00185         return plugin_attributes, groups
00186 
00187     def _parse_action_group(self, group_el):
00188         attributes = {}
00189         for tag in ['label', 'icon', 'statustip']:
00190             text = group_el.findtext(tag)
00191             if text:
00192                 attributes[tag] = str(text)
00193 
00194         icon_el = group_el.find('icon')
00195         if icon_el is not None:
00196             icon_type_attrib = icon_el.get('type')
00197             if icon_type_attrib is not None:
00198                 attributes['icontype'] = str(icon_type_attrib)
00199 
00200         return attributes


rqt_gui
Author(s): Dirk Thomas
autogenerated on Thu Aug 27 2015 14:56:52