00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
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
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
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
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
00092 if class_ref.__init__.func_code.co_argcount == 1 and plugin_context is None:
00093 return class_ref()
00094
00095 return class_ref(plugin_context)
00096
00097 def unload(self, plugin_instance):
00098 pass
00099
00100 def _get_plugins(self, export_tag):
00101
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
00123 attributes = {
00124 'plugin_name': plugin_name,
00125 'plugin_path': plugin_path,
00126 'library_path': library_path,
00127 }
00128
00129
00130 for key, value in class_el.items():
00131 attributes['class_' + key] = value
00132
00133
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
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
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
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
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
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
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
00180 plugin_descriptors.append(plugin_descriptor)
00181
00182 return plugin_descriptors
00183
00184 def _parse_plugin(self, class_el):
00185
00186 plugin_attributes = {}
00187 groups = []
00188
00189
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