main.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 # Copyright (c) 2011, Dirk Thomas, Dorian Scholz, TU Darmstadt
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of the TU Darmstadt nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32 
33 from __future__ import print_function
34 
35 from argparse import ArgumentParser, SUPPRESS
36 import os
37 import platform
38 import signal
39 import sys
40 
41 
42 class Main(object):
43 
44  main_filename = None
45 
46  def __init__(self, qtgui_path, invoked_filename=None, settings_filename=None):
47  self._qtgui_path = qtgui_path
48  if invoked_filename is None:
49  invoked_filename = os.path.abspath(__file__)
50  Main.main_filename = invoked_filename
51  if settings_filename is None:
52  settings_filename = 'qt_gui'
53  self._settings_filename = settings_filename
54 
55  self.plugin_providers = []
56  self._options = None
57 
58  # check if DBus is available
59  self._dbus_available = False
60  try:
61  # use qt/glib mainloop integration to get dbus mainloop working
62  from dbus.mainloop.glib import DBusGMainLoop
63  DBusGMainLoop(set_as_default=True)
64  import dbus
65  try:
66  # before being able to check if a session bus is available the dbus
67  # mainloop must be set up
68  dbus.SessionBus()
69  self._dbus_available = True
70  except dbus.exceptions.DBusException:
71  pass
72  except ImportError:
73  pass
74 
75  def add_arguments(self, parser, standalone=False, plugin_argument_provider=None):
76  common_group = parser.add_argument_group('Options for GUI instance')
77  common_group.add_argument(
78  '-b', '--qt-binding', dest='qt_binding', type=str, metavar='BINDING',
79  help='choose Qt bindings to be used [pyqt|pyside]')
80  common_group.add_argument(
81  '--clear-config', dest='clear_config', default=False, action='store_true',
82  help='clear the configuration (including all perspectives and plugin settings)')
83  if not standalone:
84  common_group.add_argument(
85  '-f', '--freeze-layout', dest='freeze_layout', action='store_true',
86  help='freeze the layout of the GUI (prevent rearranging widgets, disable '
87  'undock/redock)')
88  common_group.add_argument(
89  '--force-discover', dest='force_discover', default=False, action='store_true',
90  help='force a rediscover of plugins')
91  common_group.add_argument(
92  '-h', '--help', action='help', help='show this help message and exit')
93  common_group.add_argument(
94  '-ht', '--hide-title', dest='hide_title', action='store_true',
95  help='hide the title label, the icon, and the help button (combine with -l and -f '
96  'to eliminate the entire title bar and reclaim the space)')
97  if not standalone:
98  common_group.add_argument(
99  '-l', '--lock-perspective', dest='lock_perspective', action='store_true',
100  help='lock the GUI to the used perspective (hide menu bar and close buttons of '
101  'plugins)')
102 
103  common_group.add_argument(
104  '-p', '--perspective', dest='perspective', type=str, metavar='PERSPECTIVE',
105  help='start with this named perspective')
106  common_group.add_argument(
107  '--perspective-file', dest='perspective_file', type=str,
108  metavar='PERSPECTIVE_FILE', help='start with a perspective loaded from a file')
109  common_group.add_argument(
110  '--reload-import', dest='reload_import', default=False, action='store_true',
111  help='reload every imported module')
112  if not standalone:
113  common_group.add_argument(
114  '-s', '--standalone', dest='standalone_plugin', type=str, metavar='PLUGIN',
115  help='start only this plugin (implies -l). To pass arguments to the plugin use '
116  '--args')
117  common_group.add_argument(
118  '-t', '--on-top', dest='on_top', default=False, action='store_true',
119  help='set window mode to always on top')
120  common_group.add_argument(
121  '-v', '--verbose', dest='verbose', default=False, action='store_true',
122  help='output qDebug messages')
123 
124  if not standalone:
125  common_group.add_argument(
126  '--args', dest='plugin_args', nargs='*', type=str,
127  help='arbitrary arguments which are passes to the plugin '
128  '(only with -s, --command-start-plugin or --embed-plugin). '
129  'It must be the last option since it collects all following options.')
130 
131  group = parser.add_argument_group(
132  'Options to query information without starting a GUI instance',
133  'These options can be used to query information about valid arguments for various '
134  'options.')
135  group.add_argument(
136  '--list-perspectives', dest='list_perspectives', action='store_true',
137  help='list available perspectives')
138  group.add_argument(
139  '--list-plugins', dest='list_plugins', action='store_true',
140  help='list available plugins')
141  parser.add_argument_group(group)
142 
143  group = parser.add_argument_group(
144  'Options to operate on a running GUI instance',
145  'These options can be used to perform actions on a running GUI instance.')
146  group.add_argument(
147  '--command-pid', dest='command_pid', type=int, metavar='PID',
148  help='pid of the GUI instance to operate on, defaults to oldest running GUI '
149  'instance')
150  group.add_argument(
151  '--command-start-plugin', dest='command_start_plugin', type=str, metavar='PLUGIN',
152  help='start plugin')
153  group.add_argument(
154  '--command-switch-perspective', dest='command_switch_perspective', type=str,
155  metavar='PERSPECTIVE', help='switch perspective')
156  if not self._dbus_available:
157  group.description = 'These options are not available since DBus is not available!'
158  for o in group._group_actions:
159  o.help = SUPPRESS
160  parser.add_argument_group(group)
161 
162  group = parser.add_argument_group(
163  'Special options for embedding widgets from separate processes',
164  'These options should never be used on the CLI but only from the GUI code itself.')
165  group.add_argument(
166  '--embed-plugin', dest='embed_plugin', type=str, metavar='PLUGIN',
167  help='embed a plugin into an already running GUI instance (requires all other '
168  '--embed-* options)')
169  group.add_argument(
170  '--embed-plugin-serial', dest='embed_plugin_serial', type=int, metavar='SERIAL',
171  help='serial number of plugin to be embedded '
172  '(requires all other --embed-* options)')
173  group.add_argument(
174  '--embed-plugin-address', dest='embed_plugin_address', type=str, metavar='ADDRESS',
175  help='dbus server address of the GUI instance to embed plugin into '
176  '(requires all other --embed-* options)')
177  for o in group._group_actions:
178  o.help = SUPPRESS
179  parser.add_argument_group(group)
180 
181  if plugin_argument_provider:
182  plugin_argument_provider(parser)
183 
184  return common_group
185 
187  pass
188 
189  def _add_reload_paths(self, reload_importer):
190  reload_importer.add_reload_path(os.path.join(os.path.dirname(__file__), *('..',) * 4))
191 
193  from python_qt_binding.QtGui import QIcon
194  # TODO find a better way to verify Theme standard compliance
195  if QIcon.fromTheme('document-save').isNull() or \
196  QIcon.fromTheme('document-open').isNull() or \
197  QIcon.fromTheme('edit-cut').isNull() or \
198  QIcon.fromTheme('object-flip-horizontal').isNull():
199  if platform.system() == 'Darwin' and \
200  '/usr/local/share/icons' not in QIcon.themeSearchPaths():
201  QIcon.setThemeSearchPaths(QIcon.themeSearchPaths() + ['/usr/local/share/icons'])
202  original_theme = QIcon.themeName()
203  QIcon.setThemeName('Tango')
204  if QIcon.fromTheme('document-save').isNull():
205  QIcon.setThemeName(original_theme)
206 
207  def create_application(self, argv):
208  from python_qt_binding.QtCore import Qt
209  from python_qt_binding.QtWidgets import QApplication
210  app = QApplication(argv)
211  app.setAttribute(Qt.AA_DontShowIconsInMenus, False)
212  return app
213 
214  def main(self, argv=None, standalone=None, plugin_argument_provider=None,
215  plugin_manager_settings_prefix=''):
216  if argv is None:
217  argv = sys.argv
218 
219  # extract --args and everything behind manually since argparse can not handle that
220  arguments = argv[1:]
221 
222  # extract plugin specific args when not being invoked in standalone mode programmatically
223  if not standalone:
224  plugin_args = []
225  if '--args' in arguments:
226  index = arguments.index('--args')
227  plugin_args = arguments[index + 1:]
228  arguments = arguments[0:index + 1]
229 
230  parser = ArgumentParser(os.path.basename(Main.main_filename), add_help=False)
231  self.add_arguments(parser, standalone=bool(standalone),
232  plugin_argument_provider=plugin_argument_provider)
233  self._options = parser.parse_args(arguments)
234 
235  if standalone:
236  # rerun parsing to separate common arguments from plugin specific arguments
237  parser = ArgumentParser(os.path.basename(Main.main_filename), add_help=False)
238  self.add_arguments(parser, standalone=bool(standalone))
239  self._options, plugin_args = parser.parse_known_args(arguments)
240  self._options.multi_process = False # not supported anymore
241  self._options.plugin_args = plugin_args
242 
243  # set default values for options not available in standalone mode
244  if standalone:
245  self._options.freeze_layout = False
246  self._options.lock_perspective = False
247  self._options.perspective = None
248  self._options.perspective_file = None
249  self._options.standalone_plugin = standalone
250  self._options.list_perspectives = False
251  self._options.list_plugins = False
252  self._options.command_pid = None
253  self._options.command_start_plugin = None
254  self._options.command_switch_perspective = None
255  self._options.embed_plugin = None
256  self._options.embed_plugin_serial = None
257  self._options.embed_plugin_address = None
258 
259  # check option dependencies
260  try:
261  if self._options.plugin_args and \
262  not self._options.standalone_plugin and \
263  not self._options.command_start_plugin and \
264  not self._options.embed_plugin:
265  raise RuntimeError(
266  'Option --args can only be used together with either --standalone, '
267  '--command-start-plugin or --embed-plugin option')
268 
269  if self._options.freeze_layout and not self._options.lock_perspective:
270  raise RuntimeError(
271  'Option --freeze_layout can only be used together with the '
272  '--lock_perspective option')
273 
274  list_options = (self._options.list_perspectives, self._options.list_plugins)
275  list_options_set = [opt for opt in list_options if opt is not False]
276  if len(list_options_set) > 1:
277  raise RuntimeError('Only one --list-* option can be used at a time')
278 
279  command_options = (
280  self._options.command_start_plugin, self._options.command_switch_perspective)
281  command_options_set = [opt for opt in command_options if opt is not None]
282  if len(command_options_set) > 0 and not self._dbus_available:
283  raise RuntimeError(
284  'Without DBus support the --command-* options are not available')
285  if len(command_options_set) > 1:
286  raise RuntimeError(
287  'Only one --command-* option can be used at a time (except --command-pid '
288  'which is optional)')
289  if len(command_options_set) == 0 and self._options.command_pid is not None:
290  raise RuntimeError(
291  'Option --command_pid can only be used together with an other --command-* '
292  'option')
293 
294  embed_options = (
295  self._options.embed_plugin, self._options.embed_plugin_serial,
296  self._options.embed_plugin_address)
297  embed_options_set = [opt for opt in embed_options if opt is not None]
298  if len(command_options_set) > 0 and not self._dbus_available:
299  raise RuntimeError('Without DBus support the --embed-* options are not available')
300  if len(embed_options_set) > 0 and len(embed_options_set) < len(embed_options):
301  raise RuntimeError('Missing option(s) - all \'--embed-*\' options must be set')
302 
303  if len(embed_options_set) > 0 and self._options.clear_config:
304  raise RuntimeError(
305  'Option --clear-config can only be used without any --embed-* option')
306 
307  groups = (list_options_set, command_options_set, embed_options_set)
308  groups_set = [opt for opt in groups if len(opt) > 0]
309  if len(groups_set) > 1:
310  raise RuntimeError(
311  'Options from different groups (--list, --command, --embed) can not be used '
312  'together')
313 
314  perspective_options = (self._options.perspective, self._options.perspective_file)
315  perspective_options_set = [opt for opt in perspective_options if opt is not None]
316  if len(perspective_options_set) > 1:
317  raise RuntimeError('Only one --perspective-* option can be used at a time')
318 
319  if self._options.perspective_file is not None and \
320  not os.path.isfile(self._options.perspective_file):
321  raise RuntimeError('Option --perspective-file must reference existing file')
322 
323  except RuntimeError as e:
324  print(str(e))
325  # parser.parse_args(['--help'])
326  # calling --help will exit
327  return 1
328 
329  # set implicit option dependencies
330  if self._options.standalone_plugin is not None:
331  self._options.lock_perspective = True
332 
333  # create application context containing various relevant information
334  from .application_context import ApplicationContext
335  context = ApplicationContext()
336  context.qtgui_path = self._qtgui_path
337  context.options = self._options
338 
339  if self._dbus_available:
340  from dbus import DBusException, Interface, SessionBus
341 
342  # non-special applications provide various dbus interfaces
343  if self._dbus_available:
344  context.provide_app_dbus_interfaces = len(groups_set) == 0
345  context.dbus_base_bus_name = 'org.ros.qt_gui'
346  if context.provide_app_dbus_interfaces:
347  context.dbus_unique_bus_name = context.dbus_base_bus_name + '.pid%d' % os.getpid()
348 
349  # provide pid of application via dbus
350  from .application_dbus_interface import ApplicationDBusInterface
351  _dbus_server = ApplicationDBusInterface(context.dbus_base_bus_name) # noqa: F841
352 
353  # determine host bus name, either based on pid given on command line or
354  # via dbus application interface if any other instance is available
355  if len(command_options_set) > 0 or len(embed_options_set) > 0:
356  host_pid = None
357  if self._options.command_pid is not None:
358  host_pid = self._options.command_pid
359  else:
360  try:
361  remote_object = SessionBus().get_object(
362  context.dbus_base_bus_name, '/Application')
363  except DBusException:
364  pass
365  else:
366  remote_interface = Interface(
367  remote_object, context.dbus_base_bus_name + '.Application')
368  host_pid = remote_interface.get_pid()
369  if host_pid is not None:
370  context.dbus_host_bus_name = context.dbus_base_bus_name + '.pid%d' % host_pid
371 
372  # execute command on host application instance
373  if len(command_options_set) > 0:
374  if self._options.command_start_plugin is not None:
375  try:
376  remote_object = SessionBus().get_object(
377  context.dbus_host_bus_name, '/PluginManager')
378  except DBusException:
379  (rc, msg) = (1, 'unable to communicate with GUI instance "%s"' %
380  context.dbus_host_bus_name)
381  else:
382  remote_interface = Interface(
383  remote_object, context.dbus_base_bus_name + '.PluginManager')
384  (rc, msg) = remote_interface.start_plugin(
385  self._options.command_start_plugin, ' '.join(self._options.plugin_args))
386  if rc == 0:
387  print('qt_gui_main() started plugin "%s" in GUI "%s"' %
388  (msg, context.dbus_host_bus_name))
389  else:
390  print('qt_gui_main() could not start plugin "%s" in GUI "%s": %s' %
391  (self._options.command_start_plugin, context.dbus_host_bus_name, msg))
392  return rc
393  elif self._options.command_switch_perspective is not None:
394  remote_object = SessionBus().get_object(
395  context.dbus_host_bus_name, '/PerspectiveManager')
396  remote_interface = Interface(
397  remote_object, context.dbus_base_bus_name + '.PerspectiveManager')
398  remote_interface.switch_perspective(self._options.command_switch_perspective)
399  print('qt_gui_main() switched to perspective "%s" in GUI "%s"' %
400  (self._options.command_switch_perspective, context.dbus_host_bus_name))
401  return 0
402  raise RuntimeError('Unknown command not handled')
403 
404  # choose selected or default qt binding
405  setattr(sys, 'SELECT_QT_BINDING', self._options.qt_binding)
406  from python_qt_binding import QT_BINDING
407 
408  from python_qt_binding.QtCore import (qDebug, qInstallMessageHandler,
409  QSettings, Qt, QtCriticalMsg, QtDebugMsg)
410  from python_qt_binding.QtCore import QtFatalMsg, QTimer, QtWarningMsg
411 
412  from python_qt_binding.QtGui import QIcon
413  from python_qt_binding.QtWidgets import QAction
414 
415  from .about_handler import AboutHandler
416  from .composite_plugin_provider import CompositePluginProvider
417  from .container_manager import ContainerManager
418  from .help_provider import HelpProvider
419  from .icon_loader import get_icon
420  from .main_window import MainWindow
421  from .minimized_dock_widgets_toolbar import MinimizedDockWidgetsToolbar
422  from .perspective_manager import PerspectiveManager
423  from .plugin_manager import PluginManager
424 
425  # TODO PySide2 segfaults when invoking this custom message handler atm
426  if QT_BINDING != 'pyside':
427  def message_handler(type_, context, msg):
428  colored_output = 'TERM' in os.environ and 'ANSI_COLORS_DISABLED' not in os.environ
429  cyan_color = '\033[36m' if colored_output else ''
430  red_color = '\033[31m' if colored_output else ''
431  reset_color = '\033[0m' if colored_output else ''
432  if type_ == QtDebugMsg and self._options.verbose:
433  print(msg, file=sys.stderr)
434  elif type_ == QtWarningMsg:
435  print(cyan_color + msg + reset_color, file=sys.stderr)
436  elif type_ == QtCriticalMsg:
437  print(red_color + msg + reset_color, file=sys.stderr)
438  elif type_ == QtFatalMsg:
439  print(red_color + msg + reset_color, file=sys.stderr)
440  sys.exit(1)
441  qInstallMessageHandler(message_handler)
442 
443  app = self.create_application(argv)
444 
446 
447  settings = QSettings(
448  QSettings.IniFormat, QSettings.UserScope, 'ros.org', self._settings_filename)
449  if len(embed_options_set) == 0:
450  if self._options.clear_config:
451  settings.clear()
452 
453  main_window = MainWindow()
454  if self._options.on_top:
455  main_window.setWindowFlags(Qt.WindowStaysOnTopHint)
456 
457  main_window.statusBar()
458 
459  def sigint_handler(*args):
460  qDebug('\nsigint_handler()')
461  main_window.close()
462  signal.signal(signal.SIGINT, sigint_handler)
463  # the timer enables triggering the sigint_handler
464  timer = QTimer()
465  timer.start(500)
466  timer.timeout.connect(lambda: None)
467 
468  if not self._options.lock_perspective:
469  menu_bar = main_window.menuBar()
470  file_menu = menu_bar.addMenu(menu_bar.tr('&File'))
471  action = QAction(file_menu.tr('&Quit'), file_menu)
472  action.setIcon(QIcon.fromTheme('application-exit'))
473  action.triggered.connect(main_window.close)
474  file_menu.addAction(action)
475  else:
476  menu_bar = None
477 
478  else:
479  app.setQuitOnLastWindowClosed(False)
480 
481  main_window = None
482  menu_bar = None
483 
484  self._add_plugin_providers()
485 
486  # setup plugin manager
487  plugin_provider = CompositePluginProvider(self.plugin_providers)
488  plugin_manager = PluginManager(
489  plugin_provider, settings, context, settings_prefix=plugin_manager_settings_prefix)
490 
491  if self._options.list_plugins:
492  # output available plugins
493  print('\n'.join(sorted(plugin_manager.get_plugins().values())))
494  return 0
495 
496  help_provider = HelpProvider()
497  plugin_manager.plugin_help_signal.connect(help_provider.plugin_help_request)
498 
499  # setup perspective manager
500  if main_window is not None:
501  perspective_manager = PerspectiveManager(settings, context)
502 
503  if self._options.list_perspectives:
504  # output available perspectives
505  print('\n'.join(sorted(perspective_manager.perspectives)))
506  return 0
507  else:
508  perspective_manager = None
509 
510  if main_window is not None:
511  container_manager = ContainerManager(main_window, plugin_manager)
512  plugin_manager.set_main_window(main_window, menu_bar, container_manager)
513 
514  if not self._options.freeze_layout:
515  minimized_dock_widgets_toolbar = MinimizedDockWidgetsToolbar(
516  container_manager, main_window)
517  main_window.addToolBar(Qt.BottomToolBarArea, minimized_dock_widgets_toolbar)
518  plugin_manager.set_minimized_dock_widgets_toolbar(minimized_dock_widgets_toolbar)
519 
520  if menu_bar is not None:
521  perspective_menu = menu_bar.addMenu(menu_bar.tr('P&erspectives'))
522  perspective_manager.set_menu(perspective_menu)
523 
524  # connect various signals and slots
525  if perspective_manager is not None and main_window is not None:
526  # signal changed perspective to update window title
527  perspective_manager.perspective_changed_signal.connect(main_window.perspective_changed)
528  # signal new settings due to changed perspective
529  perspective_manager.save_settings_signal.connect(main_window.save_settings)
530  perspective_manager.restore_settings_signal.connect(main_window.restore_settings)
531  perspective_manager.restore_settings_without_plugin_changes_signal.connect(
532  main_window.restore_settings)
533 
534  if perspective_manager is not None and plugin_manager is not None:
535  perspective_manager.save_settings_signal.connect(plugin_manager.save_settings)
536  plugin_manager.save_settings_completed_signal.connect(
537  perspective_manager.save_settings_completed)
538  perspective_manager.restore_settings_signal.connect(plugin_manager.restore_settings)
539  perspective_manager.restore_settings_without_plugin_changes_signal.connect(
540  plugin_manager.restore_settings_without_plugins)
541 
542  if plugin_manager is not None and main_window is not None:
543  # signal before changing plugins to save window state
544  plugin_manager.plugins_about_to_change_signal.connect(main_window.save_setup)
545  # signal changed plugins to restore window state
546  plugin_manager.plugins_changed_signal.connect(main_window.restore_state)
547  # signal save settings to store plugin setup on close
548  main_window.save_settings_before_close_signal.connect(plugin_manager.close_application)
549  # signal save and shutdown called for all plugins, trigger closing main window again
550  plugin_manager.close_application_signal.connect(
551  main_window.close, type=Qt.QueuedConnection)
552 
553  if main_window is not None and menu_bar is not None:
554  about_handler = AboutHandler(context.qtgui_path, main_window)
555  help_menu = menu_bar.addMenu(menu_bar.tr('&Help'))
556  action = QAction(help_menu.tr('&About'), help_menu)
557  action.setIcon(QIcon.fromTheme('help-about'))
558  action.triggered.connect(about_handler.show)
559  help_menu.addAction(action)
560 
561  # set initial size - only used without saved configuration
562  if main_window is not None:
563  # Adjust size to fit the widget if standalone (e.g. single plugin)
564  if standalone:
565  main_window.adjustSize()
566  # On "clean" startup set some size to fully display the menu bar
567  else:
568  main_window.resize(600, 450)
569  main_window.move(100, 100)
570 
571  # ensure that qt_gui/src is in sys.path
572  src_path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..'))
573  if src_path not in sys.path:
574  sys.path.append(src_path)
575 
576  # load specific plugin
577  plugin = None
578  plugin_serial = None
579  if self._options.embed_plugin is not None:
580  plugin = self._options.embed_plugin
581  plugin_serial = self._options.embed_plugin_serial
582  elif self._options.standalone_plugin is not None:
583  plugin = self._options.standalone_plugin
584  plugin_serial = 0
585  if plugin is not None:
586  plugins = plugin_manager.find_plugins_by_name(plugin)
587  if len(plugins) == 0:
588  print('qt_gui_main() found no plugin matching "%s"' % plugin)
589  print('try passing the option "--force-discover"')
590  return 1
591  elif len(plugins) > 1:
592  print('qt_gui_main() found multiple plugins matching "%s"\n%s' %
593  (plugin, '\n'.join(plugins.values())))
594  return 1
595  plugin = list(plugins.keys())[0]
596 
597  qDebug('QtBindingHelper using %s' % QT_BINDING)
598 
599  plugin_manager.discover()
600 
601  if self._options.reload_import:
602  qDebug('ReloadImporter() automatically reload all subsequent imports')
603  from .reload_importer import ReloadImporter
604  _reload_importer = ReloadImporter()
605  self._add_reload_paths(_reload_importer)
606  _reload_importer.enable()
607 
608  # switch perspective
609  if perspective_manager is not None:
610  if plugin:
611  perspective_manager.set_perspective(plugin, hide_and_without_plugin_changes=True)
612  elif self._options.perspective_file:
613  perspective_manager.import_perspective_from_file(
614  self._options.perspective_file,
615  perspective_manager.HIDDEN_PREFIX + os.path.basename(
616  self._options.perspective_file))
617  else:
618  perspective_manager.set_perspective(self._options.perspective)
619 
620  # load specific plugin
621  if plugin:
622  plugin_manager.load_plugin(plugin, plugin_serial, self._options.plugin_args)
623  running = plugin_manager.is_plugin_running(plugin, plugin_serial)
624  if not running:
625  return 1
626  if self._options.standalone_plugin:
627  # use icon of standalone plugin (if available) for application
628  plugin_descriptor = plugin_manager.get_plugin_descriptor(plugin)
629  action_attributes = plugin_descriptor.action_attributes()
630  if 'icon' in action_attributes and action_attributes['icon'] is not None:
631  base_path = plugin_descriptor.attributes().get('plugin_path')
632  try:
633  icon = get_icon(
634  action_attributes['icon'],
635  action_attributes.get('icontype', None),
636  base_path)
637  except UserWarning:
638  pass
639  else:
640  app.setWindowIcon(icon)
641 
642  if main_window is not None:
643  main_window.show()
644  if sys.platform == 'darwin':
645  main_window.raise_()
646 
647  return app.exec_()
648 
649 
650 if __name__ == '__main__':
651  main = Main()
652  sys.exit(main.main())
def _add_plugin_providers(self)
Definition: main.py:186
def create_application(self, argv)
Definition: main.py:207
_settings_filename
Definition: main.py:53
def main(self, argv=None, standalone=None, plugin_argument_provider=None, plugin_manager_settings_prefix='')
Definition: main.py:215
def __init__(self, qtgui_path, invoked_filename=None, settings_filename=None)
Definition: main.py:46
def add_arguments(self, parser, standalone=False, plugin_argument_provider=None)
Definition: main.py:75
def _check_icon_theme_compliance(self)
Definition: main.py:192
def _add_reload_paths(self, reload_importer)
Definition: main.py:189
def get_icon(name, type_=None, base_path=None)
Definition: icon_loader.py:39


qt_gui
Author(s): Dirk Thomas
autogenerated on Mon Feb 28 2022 23:19:28