00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
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
00066 self._embed_containers = {}
00067
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
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
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
00103 if hasattr(self._process, 'pid'):
00104 self._pid = self._process.pid()
00105 else:
00106
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
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
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
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
00190 self._add_dock_widget(dock_widget, embed_container)
00191
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
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
00219 self._add_toolbar(toolbar)
00220 self._embed_containers[toolbar_object_name] = embed_container
00221
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()