plugin_handler_xembed_container.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 sys
00032 
00033 from dbus.server import Server
00034 from python_qt_binding import QT_BINDING
00035 from python_qt_binding.QtCore import QByteArray, QDataStream, qDebug, QIODevice, QProcess, QSignalMapper, Qt, qWarning
00036 from python_qt_binding.QtGui import QIcon, QToolBar, QX11EmbedContainer
00037 
00038 from .main import Main
00039 from .plugin_handler import PluginHandler
00040 from .plugin_handler_dbus_service import PluginHandlerDBusService
00041 from .settings_proxy_dbus_service import SettingsProxyDBusService
00042 
00043 
00044 class PluginHandlerXEmbedContainer(PluginHandler):
00045 
00046     """
00047     Server part of the `PluginHandlerXEmbed`.
00048     It starts the plugin in a subprocess and provides the `PluginHandlerDBusService` through a peer-to-peer DBus connection.
00049     """
00050 
00051     _serial_number = 0
00052 
00053     def __init__(self, parent, main_window, instance_id, application_context, container_manager, argv, dbus_object_path):
00054         super(PluginHandlerXEmbedContainer, self).__init__(parent, main_window, instance_id, application_context, container_manager, argv)
00055         self.setObjectName('PluginHandlerXEmbedContainer')
00056 
00057         self._dbus_object_path = dbus_object_path
00058         self._dbus_server = None
00059         self._dbus_container_service = None
00060         self._dbus_plugin_settings_service = None
00061         self._dbus_instance_settings_service = None
00062 
00063         self._process = None
00064         self._pid = None
00065         # mapping of widget object name to their embed container
00066         self._embed_containers = {}
00067         # mapping of toolbar object name to the toolbar
00068         self._embed_toolbars = {}
00069         self._signal_mapper_toolbars = QSignalMapper(self)
00070         self._signal_mapper_toolbars.mapped[str].connect(self._on_toolbar_orientation_changed)
00071 
00072     def _load(self):
00073         if not Main.main_filename:
00074             raise RuntimeError('PluginHandlerXEmbedContainer._load() filename of initially started script is unknown')
00075 
00076         self._dbus_server = Server('tcp:bind=*')
00077         self._dbus_server.on_connection_added.append(self._add_dbus_connection)
00078         self._dbus_container_service = PluginHandlerDBusService(self, self._dbus_object_path)
00079         self._dbus_plugin_settings_service = SettingsProxyDBusService(self._dbus_object_path + '/plugin')
00080         self._dbus_instance_settings_service = SettingsProxyDBusService(self._dbus_object_path + '/instance')
00081 
00082         self._process = QProcess(self)
00083         self._process.setProcessChannelMode(QProcess.SeparateChannels)
00084         self._process.readyReadStandardOutput.connect(self._print_process_output)
00085         self._process.readyReadStandardError.connect(self._print_process_error)
00086         self._process.finished.connect(self._emit_close_plugin)
00087         # start python with unbuffered stdout/stderr so that the order of the output is retained
00088         cmd = sys.executable + ' -u'
00089         cmd += ' %s' % Main.main_filename
00090         cmd += ' --qt-binding=%s' % QT_BINDING
00091         cmd += ' --embed-plugin=%s --embed-plugin-serial=%s --embed-plugin-address=%s' % (self.instance_id().plugin_id, self.instance_id().serial_number, self._dbus_server.address)
00092         if self.argv():
00093             cmd += ' --args %s' % ' '.join(self.argv())
00094         #qDebug('PluginHandlerXEmbedContainer._load() starting command: %s' % cmd)
00095         self._process.start(cmd)
00096         started = self._process.waitForStarted(3000)
00097         if not started:
00098             self._dbus_container_service.remove_from_connection()
00099             self._dbus_plugin_settings_service.remove_from_connection()
00100             self._dbus_instance_settings_service.remove_from_connection()
00101             raise RuntimeError('PluginHandlerXEmbedContainer._load() could not start subprocess in reasonable time')
00102         # QProcess.pid() has been added to PySide in 1.0.5
00103         if hasattr(self._process, 'pid'):
00104             self._pid = self._process.pid()
00105         else:
00106             # use serial number as a replacement for pid if not available
00107             self.__class__._serial_number = self._serial_number + 1
00108             self._pid = self._serial_number
00109 
00110         qDebug('PluginHandlerXEmbedContainer._load() started subprocess (#%s) for plugin "%s"' % (self._pid, str(self._instance_id)))
00111         # self._emit_load_completed is called asynchronous when client signals finished loading via dbus
00112 
00113     def _add_dbus_connection(self, conn):
00114         self._dbus_container_service.add_to_connection(conn, self._dbus_object_path)
00115         self._dbus_plugin_settings_service.add_to_connection(conn, self._dbus_object_path + '/plugin')
00116         self._dbus_instance_settings_service.add_to_connection(conn, self._dbus_object_path + '/instance')
00117 
00118     def _print_process_output(self):
00119         self._print_process(self._process.readAllStandardOutput(), qDebug)
00120 
00121     def _print_process_error(self):
00122         self._print_process(self._process.readAllStandardError(), qWarning)
00123 
00124     def _print_process(self, data, method):
00125         # indent process output and prefix it with the pid
00126         lines = str(data).split('\n')
00127         if lines[-1] == '':
00128             lines.pop()
00129         for line in lines:
00130             method('    %d %s' % (self._pid, line))
00131 
00132     def load_completed(self, loaded, has_configuration):
00133         # TODO timer to detect no response
00134         exception = None if loaded else True
00135         self._plugin_has_configuration = has_configuration
00136         self._update_title_bars()
00137         self._emit_load_completed(exception)
00138 
00139     def _shutdown_plugin(self):
00140         qDebug('PluginHandlerXEmbedContainer._shutdown_plugin()')
00141         self._process.finished.disconnect(self._emit_close_plugin)
00142         self._dbus_container_service.shutdown_plugin()
00143 
00144     def emit_shutdown_plugin_completed(self):
00145         self._dbus_container_service.remove_from_connection()
00146         self._dbus_plugin_settings_service.remove_from_connection()
00147         self._dbus_instance_settings_service.remove_from_connection()
00148 
00149         self._process.close()
00150         self._process.waitForFinished(5000)
00151         if self._process.state() != QProcess.NotRunning:
00152             self._process.kill()
00153         self._process = None
00154 
00155         super(PluginHandlerXEmbedContainer, self).emit_shutdown_plugin_completed()
00156 
00157     def _unload(self):
00158         qDebug('PluginHandlerXEmbedContainer._unload()')
00159         self._emit_unload_completed()
00160 
00161     def _save_settings(self, plugin_settings, instance_settings):
00162         qDebug('PluginHandlerXEmbedContainer._save_settings()')
00163         self._dbus_plugin_settings_service.set_settings(plugin_settings)
00164         self._dbus_instance_settings_service.set_settings(instance_settings)
00165         self._dbus_container_service.save_settings()
00166 
00167     def emit_save_settings_completed(self):
00168         self._dbus_plugin_settings_service.set_settings(None)
00169         self._dbus_instance_settings_service.set_settings(None)
00170         super(PluginHandlerXEmbedContainer, self).emit_save_settings_completed()
00171 
00172     def _restore_settings(self, plugin_settings, instance_settings):
00173         qDebug('PluginHandlerXEmbedContainer._restore_settings()')
00174         self._dbus_plugin_settings_service.set_settings(plugin_settings)
00175         self._dbus_instance_settings_service.set_settings(instance_settings)
00176         self._dbus_container_service.restore_settings()
00177 
00178     def emit_restore_settings_completed(self):
00179         self._dbus_plugin_settings_service.set_settings(None)
00180         self._dbus_instance_settings_service.set_settings(None)
00181         super(PluginHandlerXEmbedContainer, self).emit_restore_settings_completed()
00182 
00183     def _trigger_configuration(self):
00184         self._dbus_container_service.trigger_configuration()
00185 
00186     def embed_widget(self, pid, widget_object_name):
00187         dock_widget = self._create_dock_widget()
00188         embed_container = QX11EmbedContainer(dock_widget)
00189         #embed_container.clientClosed.connect(self._emit_close_signal)
00190         self._add_dock_widget(dock_widget, embed_container)
00191         # update widget title is triggered by client after embedding
00192         self._embed_containers[widget_object_name] = embed_container
00193         return embed_container.winId()
00194 
00195     def update_embedded_widget_icon(self, widget_object_name, icon_str):
00196         embed_container = self._embed_containers[widget_object_name]
00197         # deserialize icon base64-encoded string
00198         ba = QByteArray.fromBase64(icon_str)
00199         s = QDataStream(ba, QIODevice.ReadOnly)
00200         icon = QIcon()
00201         s >> icon
00202         embed_container.setWindowIcon(icon)
00203 
00204     def update_embedded_widget_title(self, widget_object_name, title):
00205         embed_container = self._embed_containers[widget_object_name]
00206         embed_container.setWindowTitle(title)
00207 
00208     def unembed_widget(self, widget_object_name):
00209         embed_container = self._embed_containers[widget_object_name]
00210         self.remove_widget(embed_container)
00211         del self._embed_containers[widget_object_name]
00212 
00213     def embed_toolbar(self, pid, toolbar_object_name):
00214         toolbar = QToolBar()
00215         toolbar.setObjectName(toolbar_object_name)
00216         embed_container = QX11EmbedContainer(toolbar)
00217         toolbar.addWidget(embed_container)
00218         #embed_container.clientClosed.connect(self._emit_close_signal)
00219         self._add_toolbar(toolbar)
00220         self._embed_containers[toolbar_object_name] = embed_container
00221         # setup mapping to signal change of orientation to client
00222         self._embed_toolbars[toolbar_object_name] = toolbar
00223         self._signal_mapper_toolbars.setMapping(toolbar, toolbar_object_name)
00224         toolbar.orientationChanged.connect(self._signal_mapper_toolbars.map)
00225         return embed_container.winId()
00226 
00227     def _on_toolbar_orientation_changed(self, toolbar_object_name):
00228         embed_container = self._embed_containers[toolbar_object_name]
00229         toolbar = self._embed_toolbars[toolbar_object_name]
00230         self._dbus_container_service.toolbar_orientation_changed(embed_container.winId(), toolbar.orientation() == Qt.Horizontal)
00231 
00232     def unembed_toolbar(self, toolbar_object_name):
00233         embed_container = self._embed_containers[toolbar_object_name]
00234         del self._embed_containers[toolbar_object_name]
00235         del self._embed_toolbars[toolbar_object_name]
00236         self.remove_toolbar(embed_container)
00237         embed_container.close()


qt_gui
Author(s): Dirk Thomas
autogenerated on Fri Aug 28 2015 12:15:40