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 traceback
00032
00033 from python_qt_binding.QtCore import qCritical, qDebug, QObject, Qt, qWarning, Signal, Slot
00034 from python_qt_binding.QtGui import QDockWidget, QToolBar
00035
00036 from .dock_widget import DockWidget
00037 from .dock_widget_title_bar import DockWidgetTitleBar
00038 from .icon_loader import get_icon
00039 from .window_changed_signaler import WindowChangedSignaler
00040
00041
00042 class PluginHandler(QObject):
00043
00044 """
00045 Base class for the bidirectional exchange between the framework and one `Plugin` instance.
00046 It utilizes a `PluginProvider` to load/unload the plugin and provides callbacks for the `PluginContext`.
00047 """
00048
00049 close_signal = Signal(str)
00050 reload_signal = Signal(str)
00051 help_signal = Signal(str)
00052 _defered_check_close = Signal()
00053
00054 def __init__(self, parent, main_window, instance_id, application_context, container_manager, argv=None):
00055 super(PluginHandler, self).__init__(parent)
00056 self.setObjectName('PluginHandler')
00057
00058 self._main_window = main_window
00059 self._instance_id = instance_id
00060 self._application_context = application_context
00061 self._container_manager = container_manager
00062 self._argv = argv if argv else []
00063 self._minimized_dock_widgets_toolbar = None
00064 self._plugin_descriptor = None
00065
00066 self._defered_check_close.connect(self._check_close, Qt.QueuedConnection)
00067 self._plugin_provider = None
00068 self.__callback = None
00069 self.__instance_settings = None
00070
00071 self._plugin_has_configuration = False
00072
00073
00074 self._widgets = {}
00075
00076 self._toolbars = []
00077
00078 def instance_id(self):
00079 return self._instance_id
00080
00081 def argv(self):
00082 return self._argv
00083
00084 def set_minimized_dock_widgets_toolbar(self, toolbar):
00085 self._minimized_dock_widgets_toolbar = toolbar
00086
00087 def set_plugin_descriptor(self, plugin_descriptor):
00088 self._plugin_descriptor = plugin_descriptor
00089
00090 def load(self, plugin_provider, callback=None):
00091 """
00092 Load plugin.
00093 Completion is signaled asynchronously if a callback is passed.
00094 """
00095 self._plugin_provider = plugin_provider
00096 self.__callback = callback
00097 try:
00098 self._load()
00099 except Exception as e:
00100 self._emit_load_completed(e)
00101
00102 def _load(self):
00103 raise NotImplementedError
00104
00105 def _emit_load_completed(self, exception=None):
00106 if exception is not None:
00107 self._garbage_widgets_and_toolbars()
00108 if self.__callback is not None:
00109 callback = self.__callback
00110 self.__callback = None
00111 callback(self, exception)
00112 elif exception is not None:
00113 qCritical('PluginHandler.load() failed%s' % (':\n%s' % str(exception) if exception != True else ''))
00114
00115 def _garbage_widgets_and_toolbars(self):
00116 for widget in self._widgets.keys():
00117 self.remove_widget(widget)
00118 self._delete_widget(widget)
00119 for toolbar in self._toolbars:
00120 self.remove_toolbar(toolbar)
00121 self._delete_toolbar(toolbar)
00122
00123 def shutdown_plugin(self, callback):
00124 """
00125 Shutdown plugin (`Plugin.shutdown_plugin()`) and remove all added widgets.
00126 Completion is signaled asynchronously if a callback is passed.
00127 """
00128 self.__callback = callback
00129 try:
00130 self._shutdown_plugin()
00131 except Exception:
00132 qCritical('PluginHandler.shutdown_plugin() plugin "%s" raised an exception:\n%s' % (str(self._instance_id), traceback.format_exc()))
00133 self.emit_shutdown_plugin_completed()
00134
00135 def _shutdown_plugin(self):
00136 raise NotImplementedError
00137
00138 def emit_shutdown_plugin_completed(self):
00139 self._garbage_widgets_and_toolbars()
00140 if self.__callback is not None:
00141 callback = self.__callback
00142 self.__callback = None
00143 callback(self._instance_id)
00144
00145 def _delete_widget(self, widget):
00146 widget.deleteLater()
00147
00148 def _delete_toolbar(self, toolbar):
00149 toolbar.deleteLater()
00150
00151 def unload(self, callback=None):
00152 """
00153 Unload plugin.
00154 Completion is signaled asynchronously if a callback is passed.
00155 """
00156 self.__callback = callback
00157 try:
00158 self._unload()
00159 except Exception:
00160 qCritical('PluginHandler.unload() plugin "%s" raised an exception:\n%s' % (str(self._instance_id), traceback.format_exc()))
00161 self._emit_unload_completed()
00162
00163 def _unload(self):
00164 raise NotImplementedError
00165
00166 def _emit_unload_completed(self):
00167 if self.__callback is not None:
00168 callback = self.__callback
00169 self.__callback = None
00170 callback(self._instance_id)
00171
00172 def save_settings(self, plugin_settings, instance_settings, callback=None):
00173 """
00174 Save settings of the plugin (`Plugin.save_settings()`) and all dock widget title bars.
00175 Completion is signaled asynchronously if a callback is passed.
00176 """
00177 qDebug('PluginHandler.save_settings()')
00178 self.__instance_settings = instance_settings
00179 self.__callback = callback
00180 try:
00181 self._save_settings(plugin_settings, instance_settings)
00182 except Exception:
00183 qCritical('PluginHandler.save_settings() plugin "%s" raised an exception:\n%s' % (str(self._instance_id), traceback.format_exc()))
00184 self.emit_save_settings_completed()
00185
00186 def _save_settings(self, plugin_settings, instance_settings):
00187 raise NotImplementedError
00188
00189 def emit_save_settings_completed(self):
00190 qDebug('PluginHandler.emit_save_settings_completed()')
00191 self._call_method_on_all_dock_widgets('save_settings', self.__instance_settings)
00192 self.__instance_settings = None
00193 if self.__callback is not None:
00194 callback = self.__callback
00195 self.__callback = None
00196 callback(self._instance_id)
00197
00198 def _call_method_on_all_dock_widgets(self, method_name, instance_settings):
00199 for dock_widget, _, _ in self._widgets.values():
00200 name = 'dock_widget' + dock_widget.objectName().replace(self._instance_id.tidy_str(), '', 1)
00201 settings = instance_settings.get_settings(name)
00202 method = getattr(dock_widget, method_name)
00203 try:
00204 method(settings)
00205 except Exception:
00206 qCritical('PluginHandler._call_method_on_all_dock_widgets(%s) failed:\n%s' % (method_name, traceback.format_exc()))
00207
00208 def restore_settings(self, plugin_settings, instance_settings, callback=None):
00209 """
00210 Restore settings of the plugin (`Plugin.restore_settings()`) and all dock widget title bars.
00211 Completion is signaled asynchronously if a callback is passed.
00212 """
00213 qDebug('PluginHandler.restore_settings()')
00214 self.__instance_settings = instance_settings
00215 self.__callback = callback
00216 try:
00217 self._restore_settings(plugin_settings, instance_settings)
00218 except Exception:
00219 qCritical('PluginHandler.restore_settings() plugin "%s" raised an exception:\n%s' % (str(self._instance_id), traceback.format_exc()))
00220 self.emit_restore_settings_completed()
00221
00222 def _restore_settings(self, plugin_settings, instance_settings):
00223 raise NotImplementedError
00224
00225 def emit_restore_settings_completed(self):
00226 qDebug('PluginHandler.emit_restore_settings_completed()')
00227
00228 self._call_method_on_all_dock_widgets('restore_settings', self.__instance_settings)
00229 self.__instance_settings = None
00230 if self.__callback is not None:
00231 callback = self.__callback
00232 self.__callback = None
00233 callback(self._instance_id)
00234
00235 def _create_dock_widget(self):
00236 dock_widget = DockWidget(self._container_manager)
00237 self._update_dock_widget_features(dock_widget)
00238 self._update_title_bar(dock_widget)
00239 self._set_window_icon(dock_widget)
00240 return dock_widget
00241
00242 def _update_dock_widget_features(self, dock_widget):
00243 if self._application_context.options.lock_perspective or self._application_context.options.standalone_plugin:
00244
00245 features = dock_widget.features()
00246 dock_widget.setFeatures(features ^ QDockWidget.DockWidgetClosable)
00247 if self._application_context.options.freeze_layout:
00248
00249 features = dock_widget.features()
00250 dock_widget.setFeatures(features ^ (QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable))
00251
00252 def _update_title_bar(self, dock_widget, hide_help=False, hide_reload=False):
00253 title_bar = dock_widget.titleBarWidget()
00254 if title_bar is None:
00255 title_bar = DockWidgetTitleBar(dock_widget, self._application_context.qtgui_path)
00256 dock_widget.setTitleBarWidget(title_bar)
00257
00258
00259 title_bar.connect_close_button(self._remove_widget_by_dock_widget)
00260 title_bar.connect_button('help', self._emit_help_signal)
00261 if hide_help:
00262 title_bar.show_button('help', not hide_help)
00263 title_bar.connect_button('reload', self._emit_reload_signal)
00264 if hide_reload:
00265 title_bar.show_button('reload', not hide_reload)
00266 title_bar.connect_button('configuration', self._trigger_configuration)
00267 title_bar.show_button('configuration', self._plugin_has_configuration)
00268
00269 def _set_window_icon(self, widget):
00270 if self._plugin_descriptor:
00271 action_attributes = self._plugin_descriptor.action_attributes()
00272 if 'icon' in action_attributes and action_attributes['icon'] is not None:
00273 base_path = self._plugin_descriptor.attributes().get('plugin_path')
00274 icon = get_icon(action_attributes['icon'], action_attributes.get('icontype', None), base_path)
00275 widget.setWindowIcon(icon)
00276
00277 def _update_title_bars(self):
00278 if self._plugin_has_configuration:
00279 for dock_widget, _, _ in self._widgets.values():
00280 title_bar = dock_widget.titleBarWidget()
00281 title_bar.show_button('configuration')
00282
00283 def _remove_widget_by_dock_widget(self, dock_widget):
00284 widget = [key for key, value in self._widgets.iteritems() if value[0] == dock_widget][0]
00285 self.remove_widget(widget)
00286
00287 def _emit_help_signal(self):
00288 self.help_signal.emit(str(self._instance_id))
00289
00290 def _emit_reload_signal(self):
00291 self.reload_signal.emit(str(self._instance_id))
00292
00293 def _trigger_configuration(self):
00294 self._plugin.trigger_configuration()
00295
00296 def _add_dock_widget(self, dock_widget, widget):
00297 dock_widget.setWidget(widget)
00298
00299 dock_widget.setObjectName(self._instance_id.tidy_str() + '__' + widget.objectName())
00300 self._add_dock_widget_to_main_window(dock_widget)
00301 signaler = WindowChangedSignaler(widget, widget)
00302 signaler.window_icon_changed_signal.connect(self._on_widget_icon_changed)
00303 signaler.window_title_changed_signal.connect(self._on_widget_title_changed)
00304 signaler2 = WindowChangedSignaler(dock_widget, dock_widget)
00305 signaler2.hide_signal.connect(self._on_dock_widget_hide)
00306 signaler2.show_signal.connect(self._on_dock_widget_show)
00307 self._widgets[widget] = [dock_widget, signaler, signaler2]
00308
00309 signaler.emit_all()
00310
00311 signaler2.emit_all()
00312
00313 def _add_dock_widget_to_main_window(self, dock_widget):
00314 if self._main_window is not None:
00315
00316 old_dock_widget = self._main_window.findChild(DockWidget, dock_widget.objectName())
00317 if old_dock_widget is not None:
00318 qWarning('PluginHandler._add_dock_widget_to_main_window() duplicate object name "%s", assign unique object names before adding widgets!' % dock_widget.objectName())
00319 self._main_window.addDockWidget(Qt.BottomDockWidgetArea, dock_widget)
00320
00321 def _on_widget_icon_changed(self, widget):
00322 dock_widget, _, _ = self._widgets[widget]
00323 dock_widget.setWindowIcon(widget.windowIcon())
00324
00325 def _on_widget_title_changed(self, widget):
00326 dock_widget, _, _ = self._widgets[widget]
00327 dock_widget.setWindowTitle(widget.windowTitle())
00328
00329 def _on_dock_widget_hide(self, dock_widget):
00330 if self._minimized_dock_widgets_toolbar:
00331 self._minimized_dock_widgets_toolbar.addDockWidget(dock_widget)
00332
00333 def _on_dock_widget_show(self, dock_widget):
00334 if self._minimized_dock_widgets_toolbar:
00335 self._minimized_dock_widgets_toolbar.removeDockWidget(dock_widget)
00336
00337
00338 @Slot('QWidget*')
00339 def remove_widget(self, widget):
00340 dock_widget, signaler, signaler2 = self._widgets[widget]
00341 self._widgets.pop(widget)
00342 if signaler is not None:
00343 signaler.window_icon_changed_signal.disconnect(self._on_widget_icon_changed)
00344 signaler.window_title_changed_signal.disconnect(self._on_widget_title_changed)
00345 if signaler2 is not None:
00346
00347 signaler2.show_signal.emit(dock_widget)
00348 signaler2.hide_signal.disconnect(self._on_dock_widget_hide)
00349 signaler2.show_signal.disconnect(self._on_dock_widget_show)
00350
00351 if self._main_window is not None:
00352 dock_widget.parent().removeDockWidget(dock_widget)
00353
00354 widget.setParent(None)
00355 dock_widget.deleteLater()
00356
00357 self._defered_check_close.emit()
00358
00359 def _add_toolbar(self, toolbar):
00360
00361 toolbar_object_name = toolbar.objectName()
00362 prefix = self._instance_id.tidy_str() + '__'
00363
00364 if not toolbar_object_name.startswith(prefix):
00365 toolbar_object_name = prefix + toolbar_object_name
00366 toolbar.setObjectName(toolbar_object_name)
00367
00368 if self._application_context.options.freeze_layout:
00369 toolbar.setMovable(False)
00370
00371 self._toolbars.append(toolbar)
00372 if self._main_window is not None:
00373
00374 old_toolbar = self._main_window.findChild(QToolBar, toolbar.objectName())
00375 if old_toolbar is not None:
00376 qWarning('PluginHandler._add_toolbar() duplicate object name "%s", assign unique object names before adding toolbars!' % toolbar.objectName())
00377 self._main_window.addToolBar(Qt.TopToolBarArea, toolbar)
00378
00379
00380 @Slot('QToolBar*')
00381 def remove_toolbar(self, toolbar):
00382 self._toolbars.remove(toolbar)
00383
00384 if toolbar.parent():
00385 toolbar.parent().removeToolBar(toolbar)
00386
00387 self._defered_check_close.emit()
00388
00389 def _check_close(self):
00390
00391 if len(self._widgets) + len(self._toolbars) == 0:
00392 self._emit_close_plugin()
00393
00394 def _emit_close_plugin(self):
00395 self.close_signal.emit(str(self._instance_id))