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 
00042 
00043 class RosPluginProvider(PluginProvider):
00044 
00045     """Base class for providing plugins based on the ROS package system."""
00046 
00047     _cached_plugins = {}
00048 
00049     def __init__(self, export_tag, base_class_type):
00050         super(RosPluginProvider, self).__init__()
00051         self.setObjectName('RosPluginProvider')
00052 
00053         self._export_tag = export_tag
00054         self._base_class_type = base_class_type
00055         self._plugin_descriptors = {}
00056 
00057     def discover(self):
00058         """
00059         Discover the plugins.
00060         The information of the `PluginDescriptor`s are extracted from the plugin manifests.
00061         """
00062         # search for plugins
00063         plugin_descriptors = []
00064         plugin_file_list = self._get_plugins(self._export_tag)
00065         for plugin_name, xml_file_name in plugin_file_list:
00066             plugin_descriptors += self._parse_plugin_xml(plugin_name, xml_file_name)
00067         # add list of discovered plugins to dictionary of known descriptors index by the plugin id
00068         for plugin_descriptor in plugin_descriptors:
00069             self._plugin_descriptors[plugin_descriptor.plugin_id()] = plugin_descriptor
00070         return plugin_descriptors
00071 
00072     def load(self, plugin_id, plugin_context):
00073         # get class reference from plugin descriptor
00074         attributes = self._plugin_descriptors[plugin_id].attributes()
00075         sys.path.append(attributes['module_base_path'])
00076 
00077         try:
00078             module = __builtin__.__import__(attributes['module_name'], fromlist=[attributes['class_from_class_type']], level=0)
00079         except NotImplementedError as e:
00080             qCritical('RosPluginProvider.load(%s): raised an exception:\n%s' % (plugin_id, e))
00081             return None
00082         except Exception as e:
00083             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()))
00084             raise e
00085 
00086         class_ref = getattr(module, attributes['class_from_class_type'], None)
00087         if class_ref is None:
00088             qCritical('RosPluginProvider.load(%s): could not find class "%s" in module "%s"' % (plugin_id, attributes['class_from_class_type'], module))
00089             return None
00090 
00091         # create plugin provider instance without context
00092         if class_ref.__init__.func_code.co_argcount == 1 and plugin_context is None:
00093             return class_ref()
00094         # create plugin instance
00095         return class_ref(plugin_context)
00096 
00097     def unload(self, plugin_instance):
00098         pass
00099 
00100     def _get_plugins(self, export_tag):
00101         # query available plugins only once
00102         if export_tag not in RosPluginProvider._cached_plugins.keys():
00103             RosPluginProvider._cached_plugins[export_tag] = self._find_plugins(export_tag)
00104         return RosPluginProvider._cached_plugins[export_tag]
00105 
00106     def _find_plugins(self, export_tag):
00107         raise NotImplementedError
00108 
00109     def _parse_plugin_xml(self, plugin_name, xml_file_name):
00110         plugin_descriptors = []
00111         plugin_path = os.path.dirname(os.path.abspath(xml_file_name))
00112 
00113         try:
00114             root = ElementTree.parse(xml_file_name)
00115         except Exception:
00116             qCritical('RosPluginProvider._parse_plugin_xml() could not parse "%s" of plugin "%s"' % (xml_file_name, plugin_name))
00117             return plugin_descriptors
00118         for library_el in root.getiterator('library'):
00119             library_path = library_el.attrib['path']
00120 
00121             for class_el in library_el.getiterator('class'):
00122                 # collect common attributes
00123                 attributes = {
00124                     'plugin_name': plugin_name,
00125                     'plugin_path': plugin_path,
00126                     'library_path': library_path,
00127                 }
00128 
00129                 # add class attributes
00130                 for key, value in class_el.items():
00131                     attributes['class_' + key] = value
00132 
00133                 # skip classes with non-matching _base_class_type
00134                 class_base_class_type = attributes.get('class_base_class_type', None)
00135                 if class_base_class_type != self._base_class_type:
00136                     continue
00137 
00138                 # generate unique identifier
00139                 plugin_id = plugin_name
00140                 if 'class_name' in attributes:
00141                     plugin_id = plugin_id + '/' + attributes['class_name']
00142                 attributes['plugin_id'] = plugin_id
00143 
00144                 # base path to look for module
00145                 module_base_path = plugin_path
00146                 if library_path != '':
00147                     module_base_path = os.path.join(module_base_path, library_path)
00148                 attributes['module_base_path'] = module_base_path
00149 
00150                 # separate module name and class name
00151                 module_name, class_from_class_type = os.path.split(attributes['class_type'].replace('.', os.sep))
00152                 attributes['module_name'] = module_name.replace(os.sep, '.')
00153                 attributes['class_from_class_type'] = class_from_class_type
00154 
00155                 # check if plugin is available
00156                 module_abs_path = os.path.join(module_base_path, module_name) + '.py'
00157                 attributes['not_available'] = plugin_name if not os.path.exists(module_abs_path) else ''
00158 
00159                 plugin_descriptor = PluginDescriptor(plugin_id, attributes)
00160 
00161                 # set action attributes (plugin providers might have none)
00162                 action_attributes, groups = self._parse_plugin(class_el)
00163                 if len(action_attributes) > 0:
00164                     plugin_descriptor.set_action_attributes(
00165                         action_attributes['label'],
00166                         action_attributes.get('statustip', None),
00167                         action_attributes.get('icon', None),
00168                         action_attributes.get('icontype', None),
00169                     )
00170                 # add group attributes
00171                 for group in groups:
00172                     plugin_descriptor.add_group_attributes(
00173                         group['label'],
00174                         group.get('statustip', None),
00175                         group.get('icon', None),
00176                         group.get('icontype', None),
00177                     )
00178 
00179                 # add plugin_descriptor to list
00180                 plugin_descriptors.append(plugin_descriptor)
00181 
00182         return plugin_descriptors
00183 
00184     def _parse_plugin(self, class_el):
00185         # create default plugin descriptor and group
00186         plugin_attributes = {}
00187         groups = []
00188 
00189         # update descriptor and group from qtgui tag
00190         guiplugin_el = class_el.find('qtgui')
00191         if guiplugin_el is not None:
00192             plugin_attributes.update(self._parse_action_group(guiplugin_el))
00193             for group_el in guiplugin_el.getiterator('group'):
00194                 groups.append(self._parse_action_group(group_el))
00195 
00196         return plugin_attributes, groups
00197 
00198     def _parse_action_group(self, group_el):
00199         attributes = {}
00200         for tag in ['label', 'icon', 'statustip']:
00201             text = group_el.findtext(tag)
00202             if text:
00203                 attributes[tag] = str(text)
00204 
00205         icon_el = group_el.find('icon')
00206         if icon_el is not None:
00207             icon_type_attrib = icon_el.get('type')
00208             if icon_type_attrib is not None:
00209                 attributes['icontype'] = str(icon_type_attrib)
00210 
00211         return attributes


rqt_gui
Author(s): Dirk Thomas
autogenerated on Fri Jan 3 2014 11:53:47