plugin_handler_xembed_client.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 os
00032 import traceback
00033 
00034 from dbus import Interface
00035 from dbus.connection import Connection
00036 from .plugin_handler_direct import PluginHandlerDirect
00037 from python_qt_binding.QtCore import qCritical, qDebug, Qt, qWarning, Slot
00038 from python_qt_binding.QtGui import QVBoxLayout, QX11EmbedWidget
00039 from .settings import Settings
00040 from .window_title_changed_signaler import WindowTitleChangedSignaler
00041 
00042 
00043 class PluginHandlerXEmbedClient(PluginHandlerDirect):
00044 
00045     """
00046     Client part of the `PluginHandlerXEmbed`.
00047     It utilizes the `PluginHandlerDBusService` of the `PluginHandlerXEmbedContainer` through a peer-to-peer DBus connection.
00048     """
00049 
00050     def __init__(self, parent, main_window, instance_id, application_context, container_manager, argv, dbus_object_path):
00051         super(PluginHandlerXEmbedClient, self).__init__(parent, main_window, instance_id, application_context, container_manager, argv)
00052         self.setObjectName('PluginHandlerXEmbedClient')
00053         self._dbus_object_path = dbus_object_path
00054         self._remote_container = None
00055         self._remote_plugin_settings = None
00056         self._remote_instance_settings = None
00057         # mapping of added widgets to their embed widget and WindowTitleChangedSignaler
00058         self._embed_widgets = {}
00059 
00060     def _load(self):
00061         conn = Connection(self._application_context.options.embed_plugin_address)
00062         proxy = conn.get_object(None, self._dbus_object_path)
00063         self._remote_container = Interface(proxy, 'org.ros.qt_gui.PluginHandlerContainer')
00064         self._remote_container.connect_to_signal('shutdown_plugin', self._shutdown_plugin)
00065         self._remote_container.connect_to_signal('save_settings', self._save_settings_from_remote)
00066         self._remote_container.connect_to_signal('restore_settings', self._restore_settings_from_remote)
00067         self._remote_container.connect_to_signal('trigger_configuration', self._trigger_configuration)
00068         self._remote_container.connect_to_signal('toolbar_orientation_changed', self._toolbar_orientation_changed)
00069 
00070         proxy = conn.get_object(None, self._dbus_object_path + '/plugin')
00071         self._remote_plugin_settings = Interface(proxy, 'org.ros.qt_gui.Settings')
00072         proxy = conn.get_object(None, self._dbus_object_path + '/instance')
00073         self._remote_instance_settings = Interface(proxy, 'org.ros.qt_gui.Settings')
00074 
00075         super(PluginHandlerXEmbedClient, self)._load()
00076 
00077     def _emit_load_completed(self, exception=None):
00078         # signal failed loading before emitting signal, as it might not be possible afterwards
00079         if exception is not None:
00080             self._remote_container.load_completed(False, False)
00081         super(PluginHandlerXEmbedClient, self)._emit_load_completed(exception)
00082         # signal successful loading after emitting signal, for better message order
00083         if exception is None:
00084             self._remote_container.load_completed(True, self._plugin_has_configuration)
00085 
00086     def shutdown_plugin(self, callback):
00087         # this method should never be called for embedded clients
00088         assert(False)
00089 
00090     def emit_shutdown_plugin_completed(self):
00091         self._remote_container.shutdown_plugin_completed()
00092 
00093     def save_settings(self, plugin_settings, instance_settings, callback=None):
00094         # this method should never be called for embedded clients
00095         assert(False)
00096 
00097     def _save_settings_from_remote(self):
00098         qDebug('PluginHandlerXEmbedClient._save_settings_from_remote()')
00099         try:
00100             plugin_settings = Settings(self._remote_plugin_settings, '')
00101             instance_settings = Settings(self._remote_instance_settings, '')
00102             self._save_settings(plugin_settings, instance_settings)
00103         except Exception:
00104             qCritical('PluginHandlerXEmbedClient._save_settings_from_remote() plugin "%s" raised an exception:\n%s' % (str(self._instance_id), traceback.format_exc()))
00105             self.emit_save_settings_completed()
00106 
00107     def emit_save_settings_completed(self):
00108         self._remote_container.save_settings_completed()
00109 
00110     def restore_settings(self, plugin_settings, instance_settings, callback=None):
00111         # this method should never be called for embedded clients
00112         assert(False)
00113 
00114     def _restore_settings_from_remote(self):
00115         qDebug('PluginHandlerXEmbedClient._restore_settings_from_remote()')
00116         try:
00117             plugin_settings = Settings(self._remote_plugin_settings, '')
00118             instance_settings = Settings(self._remote_instance_settings, '')
00119             self._restore_settings(plugin_settings, instance_settings)
00120         except Exception:
00121             qCritical('PluginHandlerXEmbedClient._restore_settings_from_remote() plugin "%s" raised an exception:\n%s' % (str(self._instance_id), traceback.format_exc()))
00122             self.emit_restore_settings_completed()
00123 
00124     def emit_restore_settings_completed(self):
00125         self._remote_container.restore_settings_completed()
00126 
00127     # pointer to QWidget must be used for PySide to work (at least with 1.0.1)
00128     @Slot('QWidget*')
00129     def add_widget(self, widget):
00130         if widget in self._embed_widgets:
00131             qWarning('PluginHandlerXEmbedClient.add_widget() widget "%s" already added' % widget.objectName())
00132             return
00133         embed_widget = QX11EmbedWidget()
00134         layout = QVBoxLayout()
00135         layout.setContentsMargins(0, 0, 0, 0)
00136         layout.addWidget(widget)
00137         embed_widget.setLayout(layout)
00138 
00139         # close embed widget when container is closed
00140         # TODO necessary?
00141         #embed_widget.containerClosed.connect(embed_widget.close)
00142 
00143         embed_container_window_id = self._remote_container.embed_widget(os.getpid(), widget.objectName())
00144         embed_widget.embedInto(embed_container_window_id)
00145 
00146         signaler = WindowTitleChangedSignaler(widget, widget)
00147         signaler.window_title_changed_signal.connect(self._on_embed_widget_title_changed)
00148         self._embed_widgets[widget] = embed_widget, signaler
00149         # trigger to update initial window title
00150         signaler.window_title_changed_signal.emit(widget)
00151 
00152         embed_widget.show()
00153 
00154     def _on_embed_widget_title_changed(self, widget):
00155         self._remote_container.update_embedded_widget_title(widget.objectName(), widget.windowTitle())
00156 
00157     # pointer to QWidget must be used for PySide to work (at least with 1.0.1)
00158     @Slot('QWidget*')
00159     def remove_widget(self, widget):
00160         embed_widget, signaler = self._embed_widgets[widget]
00161         del self._embed_widgets[widget]
00162         signaler.window_title_changed_signal.disconnect(self._on_embed_widget_title_changed)
00163         self._remote_container.unembed_widget(widget.objectName())
00164         # do not delete the widget, only the embed widget
00165         widget.setParent(None)
00166         embed_widget.deleteLater()
00167         # triggering close after last widget and toolbar is closed is handled by the container
00168 
00169     # pointer to QToolBar must be used for PySide to work (at least with 1.0.1)
00170     @Slot('QToolBar*')
00171     def add_toolbar(self, toolbar):
00172         if toolbar in self._embed_widgets:
00173             qWarning('PluginHandlerXEmbedClient.add_toolbar() toolbar "%s" already added' % toolbar.objectName())
00174             return
00175         embed_widget = QX11EmbedWidget()
00176         layout = QVBoxLayout()
00177         layout.setContentsMargins(0, 0, 0, 0)
00178         layout.addWidget(toolbar)
00179         embed_widget.setLayout(layout)
00180 
00181         # close embed widget when container is closed
00182         # TODO necessary?
00183         #embed_widget.containerClosed.connect(embed_widget.close)
00184         def foo():
00185             print('embed_widget.containerClosed')
00186         embed_widget.containerClosed.connect(foo)
00187 
00188         embed_container_window_id = self._remote_container.embed_toolbar(os.getpid(), toolbar.objectName())
00189         embed_widget.embedInto(embed_container_window_id)
00190 
00191         self._embed_widgets[toolbar] = embed_widget, None
00192 
00193         embed_widget.show()
00194 
00195     def _toolbar_orientation_changed(self, win_id, is_horizontal):
00196         for toolbar, (embed_widget, _) in self._embed_widgets.items():
00197             if embed_widget.containerWinId() == win_id:
00198                 toolbar.setOrientation(Qt.Horizontal if is_horizontal else Qt.Vertical)
00199                 break
00200 
00201     # pointer to QToolBar must be used for PySide to work (at least with 1.0.1)
00202     @Slot('QToolBar*')
00203     def remove_toolbar(self, toolbar):
00204         embed_widget, _ = self._embed_widgets[toolbar]
00205         del self._embed_widgets[toolbar]
00206         self._remote_container.unembed_widget(toolbar.objectName())
00207         # do not delete the toolbar, only the embed widget
00208         toolbar.setParent(None)
00209         embed_widget.deleteLater()
00210         # triggering close after last widget and toolbar is closed is handled by the container
00211 
00212     def _emit_close_plugin(self):
00213         self._remote_container.close_plugin()


qt_gui
Author(s): Dirk Thomas
autogenerated on Fri Jan 3 2014 11:44:00