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


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