plugin_handler_xembed_client.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 os
32 import traceback
33 
34 from dbus import Interface
35 from dbus.connection import Connection
36 from python_qt_binding.QtCore import (QByteArray, qCritical, QDataStream,
37  qDebug, QIODevice, Qt, qWarning, Slot)
38 from python_qt_binding.QtGui import QVBoxLayout, QX11EmbedWidget
39 
40 from qt_gui.plugin_handler_direct import PluginHandlerDirect
41 from qt_gui.settings import Settings
42 from qt_gui.window_changed_signaler import WindowChangedSignaler
43 
44 
46  """
47  Client part of the `PluginHandlerXEmbed`.
48 
49  Client utilizes the `PluginHandlerDBusService` of the `PluginHandlerXEmbedContainer` through a
50  peer-to-peer DBus connection.
51  """
52 
53  def __init__(self, parent, main_window, instance_id,
54  application_context, container_manager, argv, dbus_object_path):
55  super(PluginHandlerXEmbedClient, self).__init__(
56  parent, main_window, instance_id, application_context, container_manager, argv)
57  self.setObjectName('PluginHandlerXEmbedClient')
58  self._dbus_object_path = dbus_object_path
59  self._remote_container = None
62  # mapping of added widgets to their embed widget and WindowChangedSignaler
63  self._embed_widgets = {}
64 
65  def _load(self):
66  conn = Connection(self._application_context.options.embed_plugin_address)
67  proxy = conn.get_object(None, self._dbus_object_path)
68  self._remote_container = Interface(proxy, 'org.ros.qt_gui.PluginHandlerContainer')
69  self._remote_container.connect_to_signal('shutdown_plugin', self._shutdown_plugin)
70  self._remote_container.connect_to_signal('save_settings', self._save_settings_from_remote)
71  self._remote_container.connect_to_signal(
72  'restore_settings', self._restore_settings_from_remote)
73  self._remote_container.connect_to_signal(
74  'trigger_configuration', self._trigger_configuration)
75  self._remote_container.connect_to_signal(
76  'toolbar_orientation_changed', self._toolbar_orientation_changed)
77 
78  proxy = conn.get_object(None, self._dbus_object_path + '/plugin')
79  self._remote_plugin_settings = Interface(proxy, 'org.ros.qt_gui.Settings')
80  proxy = conn.get_object(None, self._dbus_object_path + '/instance')
81  self._remote_instance_settings = Interface(proxy, 'org.ros.qt_gui.Settings')
82 
83  super(PluginHandlerXEmbedClient, self)._load()
84 
85  def _emit_load_completed(self, exception=None):
86  # signal failed loading before emitting signal, as it might not be possible afterwards
87  if exception is not None:
88  self._remote_container.load_completed(False, False)
89  super(PluginHandlerXEmbedClient, self)._emit_load_completed(exception)
90  # signal successful loading after emitting signal, for better message order
91  if exception is None:
92  self._remote_container.load_completed(True, self._plugin_has_configuration)
93 
94  def shutdown_plugin(self, callback):
95  # this method should never be called for embedded clients
96  assert(False)
97 
99  self._remote_container.shutdown_plugin_completed()
100 
101  def save_settings(self, plugin_settings, instance_settings, callback=None):
102  # this method should never be called for embedded clients
103  assert(False)
104 
106  qDebug('PluginHandlerXEmbedClient._save_settings_from_remote()')
107  try:
108  plugin_settings = Settings(self._remote_plugin_settings, '')
109  instance_settings = Settings(self._remote_instance_settings, '')
110  self._save_settings(plugin_settings, instance_settings)
111  except Exception:
112  qCritical(
113  'PluginHandlerXEmbedClient._save_settings_from_remote() plugin "%s" '
114  'raised an exception:\n%s' % (str(self._instance_id), traceback.format_exc()))
116 
118  self._remote_container.save_settings_completed()
119 
120  def restore_settings(self, plugin_settings, instance_settings, callback=None):
121  # this method should never be called for embedded clients
122  assert(False)
123 
125  qDebug('PluginHandlerXEmbedClient._restore_settings_from_remote()')
126  try:
127  plugin_settings = Settings(self._remote_plugin_settings, '')
128  instance_settings = Settings(self._remote_instance_settings, '')
129  self._restore_settings(plugin_settings, instance_settings)
130  except Exception:
131  qCritical(
132  'PluginHandlerXEmbedClient._restore_settings_from_remote() '
133  'plugin "%s" raised an exception:\n%s' %
134  (str(self._instance_id), traceback.format_exc()))
136 
138  self._remote_container.restore_settings_completed()
139 
140  # pointer to QWidget must be used for PySide to work (at least with 1.0.1)
141  @Slot('QWidget*')
142  def add_widget(self, widget):
143  if widget in self._embed_widgets:
144  qWarning('PluginHandlerXEmbedClient.add_widget() widget "%s" already added' %
145  widget.objectName())
146  return
147  embed_widget = QX11EmbedWidget()
148  layout = QVBoxLayout()
149  layout.setContentsMargins(0, 0, 0, 0)
150  layout.addWidget(widget)
151  embed_widget.setLayout(layout)
152 
153  # close embed widget when container is closed
154  # TODO necessary?
155  # embed_widget.containerClosed.connect(embed_widget.close)
156 
157  embed_container_window_id = self._remote_container.embed_widget(
158  os.getpid(), widget.objectName())
159  embed_widget.embedInto(embed_container_window_id)
160 
161  signaler = WindowChangedSignaler(widget, widget)
162  signaler.window_icon_changed_signal.connect(self._on_embed_widget_icon_changed)
163  signaler.window_title_changed_signal.connect(self._on_embed_widget_title_changed)
164  self._embed_widgets[widget] = embed_widget, signaler
165  # trigger to update initial window icon and title
166  signaler.window_icon_changed_signal.emit(widget)
167  signaler.window_title_changed_signal.emit(widget)
168 
169  embed_widget.show()
170 
171  def _on_embed_widget_icon_changed(self, widget):
172  # serialize icon base64-encoded string
173  ba = QByteArray()
174  s = QDataStream(ba, QIODevice.WriteOnly)
175  s << widget.windowIcon()
176  icon_str = str(ba.toBase64())
177  self._remote_container.update_embedded_widget_icon(widget.objectName(), icon_str)
178 
180  self._remote_container.update_embedded_widget_title(
181  widget.objectName(), widget.windowTitle())
182 
183  # pointer to QWidget must be used for PySide to work (at least with 1.0.1)
184  @Slot('QWidget*')
185  def remove_widget(self, widget):
186  embed_widget, signaler = self._embed_widgets[widget]
187  del self._embed_widgets[widget]
188  signaler.window_icon_changed_signal.disconnect(self._on_embed_widget_icon_changed)
189  signaler.window_title_changed_signal.disconnect(self._on_embed_widget_title_changed)
190  self._remote_container.unembed_widget(widget.objectName())
191  # do not delete the widget, only the embed widget
192  widget.setParent(None)
193  embed_widget.deleteLater()
194  # triggering close after last widget and toolbar is closed is handled by the container
195 
196  # pointer to QToolBar must be used for PySide to work (at least with 1.0.1)
197  @Slot('QToolBar*')
198  def add_toolbar(self, toolbar):
199  if toolbar in self._embed_widgets:
200  qWarning('PluginHandlerXEmbedClient.add_toolbar() toolbar "%s" already added' %
201  toolbar.objectName())
202  return
203  embed_widget = QX11EmbedWidget()
204  layout = QVBoxLayout()
205  layout.setContentsMargins(0, 0, 0, 0)
206  layout.addWidget(toolbar)
207  embed_widget.setLayout(layout)
208 
209  # close embed widget when container is closed
210  # TODO necessary?
211  # embed_widget.containerClosed.connect(embed_widget.close)
212  def foo():
213  print('embed_widget.containerClosed')
214  embed_widget.containerClosed.connect(foo)
215 
216  embed_container_window_id = self._remote_container.embed_toolbar(
217  os.getpid(), toolbar.objectName())
218  embed_widget.embedInto(embed_container_window_id)
219 
220  self._embed_widgets[toolbar] = embed_widget, None
221 
222  embed_widget.show()
223 
224  def _toolbar_orientation_changed(self, win_id, is_horizontal):
225  for toolbar, (embed_widget, _) in self._embed_widgets.items():
226  if embed_widget.containerWinId() == win_id:
227  toolbar.setOrientation(Qt.Horizontal if is_horizontal else Qt.Vertical)
228  break
229 
230  # pointer to QToolBar must be used for PySide to work (at least with 1.0.1)
231  @Slot('QToolBar*')
232  def remove_toolbar(self, toolbar):
233  embed_widget, _ = self._embed_widgets[toolbar]
234  del self._embed_widgets[toolbar]
235  self._remote_container.unembed_widget(toolbar.objectName())
236  # do not delete the toolbar, only the embed widget
237  toolbar.setParent(None)
238  embed_widget.deleteLater()
239  # triggering close after last widget and toolbar is closed is handled by the container
240 
242  self._remote_container.close_plugin()
def save_settings(self, plugin_settings, instance_settings, callback=None)
def _restore_settings(self, plugin_settings, instance_settings)
def __init__(self, parent, main_window, instance_id, application_context, container_manager, argv, dbus_object_path)
def restore_settings(self, plugin_settings, instance_settings, callback=None)
def _save_settings(self, plugin_settings, instance_settings)


qt_gui
Author(s): Dirk Thomas
autogenerated on Tue Apr 13 2021 03:03:12