plugin_handler_xembed_container.py
Go to the documentation of this file.
1 # Copyright (c) 2011, Dirk Thomas, Dorian Scholz, TU Darmstadt
2 # All rights reserved.
3 #
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions
6 # are met:
7 #
8 # * Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer.
10 # * Redistributions in binary form must reproduce the above
11 # copyright notice, this list of conditions and the following
12 # disclaimer in the documentation and/or other materials provided
13 # with the distribution.
14 # * Neither the name of the TU Darmstadt nor the names of its
15 # contributors may be used to endorse or promote products derived
16 # from this software without specific prior written permission.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 # POSSIBILITY OF SUCH DAMAGE.
30 
31 import sys
32 
33 from dbus.server import Server
34 from python_qt_binding import QT_BINDING
35 from python_qt_binding.QtCore import (QByteArray, QDataStream, qDebug, QIODevice,
36  QProcess, QSignalMapper, Qt, qWarning)
37 from python_qt_binding.QtGui import QIcon, QToolBar, QX11EmbedContainer
38 
39 from qt_gui.main import Main
40 from qt_gui.plugin_handler import PluginHandler
41 from qt_gui.plugin_handler_dbus_service import PluginHandlerDBusService
42 from qt_gui.settings_proxy_dbus_service import SettingsProxyDBusService
43 
44 
46  """
47  Server part of the `PluginHandlerXEmbed`.
48 
49  Starts the plugin in a subprocess and provides the `PluginHandlerDBusService` through a
50  peer-to-peer DBus connection.
51  """
52 
53  _serial_number = 0
54 
55  def __init__(self, parent, main_window, instance_id,
56  application_context, container_manager, argv, dbus_object_path):
57  super(PluginHandlerXEmbedContainer, self).__init__(
58  parent, main_window, instance_id, application_context, container_manager, argv)
59  self.setObjectName('PluginHandlerXEmbedContainer')
60 
61  self._dbus_object_path = dbus_object_path
62  self._dbus_server = None
66 
67  self._process = None
68  self._pid = None
69  # mapping of widget object name to their embed container
71  # mapping of toolbar object name to the toolbar
72  self._embed_toolbars = {}
73  self._signal_mapper_toolbars = QSignalMapper(self)
74  self._signal_mapper_toolbars.mapped[str].connect(self._on_toolbar_orientation_changed)
75 
76  def _load(self):
77  if not Main.main_filename:
78  raise RuntimeError(
79  'PluginHandlerXEmbedContainer._load() '
80  'filename of initially started script is unknown')
81 
82  self._dbus_server = Server('tcp:bind=*')
83  self._dbus_server.on_connection_added.append(self._add_dbus_connection)
86  self._dbus_object_path + '/plugin')
88  self._dbus_object_path + '/instance')
89 
90  self._process = QProcess(self)
91  self._process.setProcessChannelMode(QProcess.SeparateChannels)
92  self._process.readyReadStandardOutput.connect(self._print_process_output)
93  self._process.readyReadStandardError.connect(self._print_process_error)
94  self._process.finished.connect(self._emit_close_plugin)
95  # start python with unbuffered stdout/stderr so that the order of the output is retained
96  cmd = sys.executable + ' -u'
97  cmd += ' %s' % Main.main_filename
98  cmd += ' --qt-binding=%s' % QT_BINDING
99  cmd += ' --embed-plugin=%s --embed-plugin-serial=%s --embed-plugin-address=%s' % (
100  self.instance_id().plugin_id, self.instance_id().serial_number,
101  self._dbus_server.address)
102  if self.argv():
103  cmd += ' --args %s' % ' '.join(self.argv())
104  # qDebug('PluginHandlerXEmbedContainer._load() starting command: %s' % cmd)
105  self._process.start(cmd)
106  started = self._process.waitForStarted(3000)
107  if not started:
108  self._dbus_container_service.remove_from_connection()
109  self._dbus_plugin_settings_service.remove_from_connection()
110  self._dbus_instance_settings_service.remove_from_connection()
111  raise RuntimeError(
112  'PluginHandlerXEmbedContainer._load() could not start '
113  'subprocess in reasonable time')
114  # QProcess.pid() has been added to PySide in 1.0.5
115  if hasattr(self._process, 'pid'):
116  self._pid = self._process.pid()
117  else:
118  # use serial number as a replacement for pid if not available
119  self.__class__._serial_number = self._serial_number + 1
120  self._pid = self._serial_number
121 
122  qDebug('PluginHandlerXEmbedContainer._load() started subprocess (#%s) for plugin "%s"' %
123  (self._pid, str(self._instance_id)))
124  # self._emit_load_completed is called asynchronous when client signals
125  # finished loading via dbus
126 
127  def _add_dbus_connection(self, conn):
128  self._dbus_container_service.add_to_connection(conn, self._dbus_object_path)
129  self._dbus_plugin_settings_service.add_to_connection(
130  conn, self._dbus_object_path + '/plugin')
131  self._dbus_instance_settings_service.add_to_connection(
132  conn, self._dbus_object_path + '/instance')
133 
135  self._print_process(self._process.readAllStandardOutput(), qDebug)
136 
138  self._print_process(self._process.readAllStandardError(), qWarning)
139 
140  def _print_process(self, data, method):
141  # indent process output and prefix it with the pid
142  lines = str(data).split('\n')
143  if lines[-1] == '':
144  lines.pop()
145  for line in lines:
146  method(' %d %s' % (self._pid, line))
147 
148  def load_completed(self, loaded, has_configuration):
149  # TODO timer to detect no response
150  exception = None if loaded else True
151  self._plugin_has_configuration = has_configuration
152  self._update_title_bars()
153  self._emit_load_completed(exception)
154 
155  def _shutdown_plugin(self):
156  qDebug('PluginHandlerXEmbedContainer._shutdown_plugin()')
157  self._process.finished.disconnect(self._emit_close_plugin)
159 
161  self._dbus_container_service.remove_from_connection()
162  self._dbus_plugin_settings_service.remove_from_connection()
163  self._dbus_instance_settings_service.remove_from_connection()
164 
165  self._process.close()
166  self._process.waitForFinished(5000)
167  if self._process.state() != QProcess.NotRunning:
168  self._process.kill()
169  self._process = None
170 
171  super(PluginHandlerXEmbedContainer, self).emit_shutdown_plugin_completed()
172 
173  def _unload(self):
174  qDebug('PluginHandlerXEmbedContainer._unload()')
176 
177  def _save_settings(self, plugin_settings, instance_settings):
178  qDebug('PluginHandlerXEmbedContainer._save_settings()')
179  self._dbus_plugin_settings_service.set_settings(plugin_settings)
180  self._dbus_instance_settings_service.set_settings(instance_settings)
182 
184  self._dbus_plugin_settings_service.set_settings(None)
185  self._dbus_instance_settings_service.set_settings(None)
186  super(PluginHandlerXEmbedContainer, self).emit_save_settings_completed()
187 
188  def _restore_settings(self, plugin_settings, instance_settings):
189  qDebug('PluginHandlerXEmbedContainer._restore_settings()')
190  self._dbus_plugin_settings_service.set_settings(plugin_settings)
191  self._dbus_instance_settings_service.set_settings(instance_settings)
193 
195  self._dbus_plugin_settings_service.set_settings(None)
196  self._dbus_instance_settings_service.set_settings(None)
197  super(PluginHandlerXEmbedContainer, self).emit_restore_settings_completed()
198 
200  self._dbus_container_service.trigger_configuration()
201 
202  def embed_widget(self, pid, widget_object_name):
203  dock_widget = self._create_dock_widget()
204  embed_container = QX11EmbedContainer(dock_widget)
205  # embed_container.clientClosed.connect(self._emit_close_signal)
206  self._add_dock_widget(dock_widget, embed_container)
207  # update widget title is triggered by client after embedding
208  self._embed_containers[widget_object_name] = embed_container
209  return embed_container.winId()
210 
211  def update_embedded_widget_icon(self, widget_object_name, icon_str):
212  embed_container = self._embed_containers[widget_object_name]
213  # deserialize icon base64-encoded string
214  ba = QByteArray.fromBase64(icon_str)
215  s = QDataStream(ba, QIODevice.ReadOnly)
216  icon = QIcon()
217  s >> icon
218  embed_container.setWindowIcon(icon)
219 
220  def update_embedded_widget_title(self, widget_object_name, title):
221  embed_container = self._embed_containers[widget_object_name]
222  embed_container.setWindowTitle(title)
223 
224  def unembed_widget(self, widget_object_name):
225  embed_container = self._embed_containers[widget_object_name]
226  self.remove_widget(embed_container)
227  del self._embed_containers[widget_object_name]
228 
229  def embed_toolbar(self, pid, toolbar_object_name):
230  toolbar = QToolBar()
231  toolbar.setObjectName(toolbar_object_name)
232  embed_container = QX11EmbedContainer(toolbar)
233  toolbar.addWidget(embed_container)
234  # embed_container.clientClosed.connect(self._emit_close_signal)
235  self._add_toolbar(toolbar)
236  self._embed_containers[toolbar_object_name] = embed_container
237  # setup mapping to signal change of orientation to client
238  self._embed_toolbars[toolbar_object_name] = toolbar
239  self._signal_mapper_toolbars.setMapping(toolbar, toolbar_object_name)
240  toolbar.orientationChanged.connect(self._signal_mapper_toolbars.map)
241  return embed_container.winId()
242 
243  def _on_toolbar_orientation_changed(self, toolbar_object_name):
244  embed_container = self._embed_containers[toolbar_object_name]
245  toolbar = self._embed_toolbars[toolbar_object_name]
246  self._dbus_container_service.toolbar_orientation_changed(
247  embed_container.winId(), toolbar.orientation() == Qt.Horizontal)
248 
249  def unembed_toolbar(self, toolbar_object_name):
250  embed_container = self._embed_containers[toolbar_object_name]
251  del self._embed_containers[toolbar_object_name]
252  del self._embed_toolbars[toolbar_object_name]
253  self.remove_toolbar(embed_container)
254  embed_container.close()
def save_settings(self, plugin_settings, instance_settings, callback=None)
def _emit_load_completed(self, exception=None)
def restore_settings(self, plugin_settings, instance_settings, callback=None)
def _add_dock_widget(self, dock_widget, widget)
def __init__(self, parent, main_window, instance_id, application_context, container_manager, argv, dbus_object_path)


qt_gui
Author(s): Dirk Thomas
autogenerated on Fri Jun 24 2022 02:42:37