Skip to content

Commit

Permalink
Merge pull request #28 from ros-visualization/refactoring
Browse files Browse the repository at this point in the history
improve startup time
  • Loading branch information
dirk-thomas committed Oct 9, 2013
2 parents c564423 + ec780cd commit 304c12d
Show file tree
Hide file tree
Showing 29 changed files with 145 additions and 100 deletions.
3 changes: 0 additions & 3 deletions qt_dotgraph/src/qt_dotgraph/dot_to_qt.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,6 @@

import pydot

import roslib
roslib.load_manifest('qt_dotgraph')

from python_qt_binding.QtCore import QPointF, QRectF
from python_qt_binding.QtGui import QColor

Expand Down
1 change: 1 addition & 0 deletions qt_gui/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@
<build_depend>python-qt-bindings</build_depend>

<run_depend>python_qt_binding</run_depend>
<run_depend>python-rospkg</run_depend>
<run_depend>tango-icon-theme</run_depend>
</package>
20 changes: 6 additions & 14 deletions qt_gui/src/qt_gui/about_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
from python_qt_binding import QT_BINDING, QT_BINDING_MODULES, QT_BINDING_VERSION
from python_qt_binding.QtCore import QObject, qVersion
from python_qt_binding.QtGui import QMessageBox
from rospkg.rospack import RosPack

from .ros_package_helper import get_package_path

Expand All @@ -44,8 +43,9 @@ class AboutHandler(QObject):

"""Handler for the about action in the menu bar showing a message box with details on the used libraries and their versions."""

def __init__(self, parent=None):
def __init__(self, qtgui_path, parent=None):
super(AboutHandler, self).__init__(parent)
self._qtgui_path = qtgui_path

def show(self):
# append folder of 'qt_gui_cpp/lib' to module search path
Expand All @@ -54,15 +54,10 @@ def show(self):
sys.path.append(os.path.join(qt_gui_cpp_path, 'src'))
from qt_gui_cpp.cpp_binding_helper import qt_gui_cpp

_rospkg_version = None
try:
import rospkg
_rospkg_version = getattr(rospkg, '__version__', '&lt; 0.2.4')
except ImportError:
pass
import rospkg
_rospkg_version = getattr(rospkg, '__version__', '&lt; 0.2.4')

rp = RosPack()
logo = os.path.join(rp.get_path('qt_gui'), 'resource', 'ros_org_vertical.png')
logo = os.path.join(self._qtgui_path, 'resource', 'ros_org_vertical.png')
text = '<img src="%s" width="56" height="200" style="float: left;"/>' % logo

text += '<h3 style="margin-top: 1px;">%s</h3>' % self.tr('ROS GUI')
Expand All @@ -74,10 +69,7 @@ def show(self):

text += 'Python %s, ' % platform.python_version()

if _rospkg_version is not None:
text += 'rospkg %s, ' % _rospkg_version
else:
text += '%s, ' % self.tr('rospkg not found - using roslib')
text += 'rospkg %s, ' % _rospkg_version

if QT_BINDING == 'pyside':
text += 'PySide'
Expand Down
5 changes: 4 additions & 1 deletion qt_gui/src/qt_gui/application_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
# POSSIBILITY OF SUCH DAMAGE.


class ApplicationContext():
class ApplicationContext(object):

"""Application wide context containing variables used in various places."""

Expand All @@ -48,3 +48,6 @@ def __init__(self):

self.options = None
"""The parsed command line options."""

self.qtgui_path = None
"""The path of the qt_gui package."""
4 changes: 2 additions & 2 deletions qt_gui/src/qt_gui/composite_plugin_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,12 @@ def __init__(self, plugin_providers=None):
def set_plugin_providers(self, plugin_providers):
self._plugin_providers = plugin_providers

def discover(self):
def discover(self, discovery_data):
# discover plugins from all providers
discovered_plugins = []
for plugin_provider in self._plugin_providers:
try:
plugin_descriptors = plugin_provider.discover()
plugin_descriptors = plugin_provider.discover(discovery_data)
except Exception:
qCritical('CompositePluginProvider.discover() could not discover plugins from provider "%s":\n%s' % (type(plugin_provider), traceback.format_exc()))
else:
Expand Down
6 changes: 2 additions & 4 deletions qt_gui/src/qt_gui/dock_widget_title_bar.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,17 @@
from python_qt_binding import loadUi
from python_qt_binding.QtCore import QEvent, QObject, Qt, qWarning
from python_qt_binding.QtGui import QDockWidget, QIcon, QWidget
from rospkg.rospack import RosPack


class DockWidgetTitleBar(QWidget):

"""Title bar for dock widgets providing custom actions."""

def __init__(self, dock_widget):
def __init__(self, dock_widget, qtgui_path):
super(DockWidgetTitleBar, self).__init__(dock_widget)
self._dock_widget = dock_widget

rp = RosPack()
ui_file = os.path.join(rp.get_path('qt_gui'), 'resource', 'dock_widget_title_bar.ui')
ui_file = os.path.join(qtgui_path, 'resource', 'dock_widget_title_bar.ui')
loadUi(ui_file, self)
self._extra_buttons = {
'configuration': self.configuration_button,
Expand Down
8 changes: 3 additions & 5 deletions qt_gui/src/qt_gui/help_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@
import webbrowser

from python_qt_binding.QtCore import QObject, Slot
from roslib.packages import get_pkg_dir, InvalidROSPkgException
from rospkg import InvalidManifest, MANIFEST_FILE, parse_manifest_file

from .ros_package_helper import get_package_path


class HelpProvider(QObject):

Expand All @@ -45,10 +46,7 @@ def __init__(self):
@Slot(object)
def plugin_help_request(self, plugin_descriptor):
package_name = plugin_descriptor.attributes()['package_name']
try:
package_path = get_pkg_dir(package_name)
except InvalidROSPkgException:
return
package_path = get_package_path(package_name)
try:
manifest = parse_manifest_file(package_path, MANIFEST_FILE)
except (InvalidManifest, IOError):
Expand Down
28 changes: 14 additions & 14 deletions qt_gui/src/qt_gui/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,15 @@ class Main(object):

main_filename = None

def __init__(self, invoked_filename=None, settings_filename=None):
def __init__(self, qtgui_path, invoked_filename=None, settings_filename=None):
self._qtgui_path = qtgui_path
if invoked_filename is None:
invoked_filename = os.path.abspath(__file__)
Main.main_filename = invoked_filename
if settings_filename is None:
settings_filename = 'qt_gui'
self._settings_filename = settings_filename

self.plugin_providers = []
self._options = None

Expand All @@ -75,11 +77,14 @@ def add_arguments(self, parser, standalone=False, plugin_argument_provider=None)
help='choose Qt bindings to be used [pyqt|pyside]')
common_group.add_argument('--clear-config', dest='clear_config', default=False, action='store_true',
help='clear the configuration (including all perspectives and plugin settings)')
common_group.add_argument('-h', '--help', action='help',
help='show this help message and exit')
if not standalone:
common_group.add_argument('-f', '--freeze-layout', dest='freeze_layout', action='store_true',
help='freeze the layout of the GUI (prevent rearranging widgets, disable undock/redock)')
common_group.add_argument('--force-discover', dest='force_discover', default=False, action='store_true',
help='force a rediscover of plugins')
common_group.add_argument('-h', '--help', action='help',
help='show this help message and exit')
if not standalone:
common_group.add_argument('-l', '--lock-perspective', dest='lock_perspective', action='store_true',
help='lock the GUI to the used perspective (hide menu bar and close buttons of plugins)')
common_group.add_argument('-m', '--multi-process', dest='multi_process', default=False, action='store_true',
Expand Down Expand Up @@ -144,9 +149,6 @@ def add_arguments(self, parser, standalone=False, plugin_argument_provider=None)
def _add_plugin_providers(self):
pass

def _caching_hook(self):
pass

def _add_reload_paths(self, reload_importer):
reload_importer.add_reload_path(os.path.join(os.path.dirname(__file__), *('..',) * 4))

Expand Down Expand Up @@ -273,6 +275,7 @@ def main(self, argv=None, standalone=None, plugin_argument_provider=None):
# create application context containing various relevant information
from .application_context import ApplicationContext
context = ApplicationContext()
context.qtgui_path = self._qtgui_path
context.options = self._options

if self._dbus_available:
Expand Down Expand Up @@ -363,8 +366,8 @@ def message_handler(type_, msg):

self._check_icon_theme_compliance()

settings = QSettings(QSettings.IniFormat, QSettings.UserScope, 'ros.org', self._settings_filename)
if len(embed_options_set) == 0:
settings = QSettings(QSettings.IniFormat, QSettings.UserScope, 'ros.org', self._settings_filename)
if self._options.clear_config:
settings.clear()

Expand Down Expand Up @@ -401,15 +404,14 @@ def sigint_handler(*args):
else:
app.setQuitOnLastWindowClosed(False)

settings = None
main_window = None
menu_bar = None

self._add_plugin_providers()

# setup plugin manager
plugin_provider = CompositePluginProvider(self.plugin_providers)
plugin_manager = PluginManager(plugin_provider, context)
plugin_manager = PluginManager(plugin_provider, settings, context)

if self._options.list_plugins:
# output available plugins
Expand All @@ -420,7 +422,7 @@ def sigint_handler(*args):
plugin_manager.plugin_help_signal.connect(help_provider.plugin_help_request)

# setup perspective manager
if settings is not None:
if main_window is not None:
perspective_manager = PerspectiveManager(settings, context)

if self._options.list_perspectives:
Expand All @@ -438,7 +440,7 @@ def sigint_handler(*args):
main_window.addToolBar(Qt.BottomToolBarArea, minimized_dock_widgets_toolbar)
plugin_manager.set_minimized_dock_widgets_toolbar(minimized_dock_widgets_toolbar)

if settings is not None and menu_bar is not None:
if menu_bar is not None:
perspective_menu = menu_bar.addMenu(menu_bar.tr('Perspectives'))
perspective_manager.set_menu(perspective_menu)

Expand Down Expand Up @@ -468,7 +470,7 @@ def sigint_handler(*args):
plugin_manager.close_application_signal.connect(main_window.close, type=Qt.QueuedConnection)

if main_window is not None and menu_bar is not None:
about_handler = AboutHandler(main_window)
about_handler = AboutHandler(context.qtgui_path, main_window)
help_menu = menu_bar.addMenu(menu_bar.tr('Help'))
action = QAction(file_menu.tr('About'), help_menu)
action.setIcon(QIcon.fromTheme('help-about'))
Expand Down Expand Up @@ -508,8 +510,6 @@ def sigint_handler(*args):

plugin_manager.discover()

self._caching_hook()

if self._options.reload_import:
qDebug('ReloadImporter() automatically reload all subsequent imports')
from .reload_importer import ReloadImporter
Expand Down
6 changes: 3 additions & 3 deletions qt_gui/src/qt_gui/perspective_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
from python_qt_binding import loadUi
from python_qt_binding.QtCore import QByteArray, qDebug, QObject, QSignalMapper, Signal, Slot
from python_qt_binding.QtGui import QAction, QFileDialog, QIcon, QInputDialog, QMessageBox, QValidator
from rospkg.rospack import RosPack

from .menu_manager import MenuManager
from .settings import Settings
Expand All @@ -56,6 +55,8 @@ def __init__(self, settings, application_context):
super(PerspectiveManager, self).__init__()
self.setObjectName('PerspectiveManager')

self._qtgui_path = application_context.qtgui_path

self._settings_proxy = SettingsProxy(settings)
self._global_settings = Settings(self._settings_proxy, 'global')
self._perspective_settings = None
Expand Down Expand Up @@ -182,8 +183,7 @@ def _on_create_perspective(self):
def _choose_new_perspective_name(self, show_cloning=True):
# input dialog for new perspective name
if self._create_perspective_dialog is None:
rp = RosPack()
ui_file = os.path.join(rp.get_path('qt_gui'), 'resource', 'perspective_create.ui')
ui_file = os.path.join(self._qtgui_path, 'resource', 'perspective_create.ui')
self._create_perspective_dialog = loadUi(ui_file)

# custom validator preventing forward slashs
Expand Down
2 changes: 1 addition & 1 deletion qt_gui/src/qt_gui/plugin_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ def _update_dock_widget_features(self, dock_widget):
def _update_title_bar(self, dock_widget, hide_help=False, hide_reload=False):
title_bar = dock_widget.titleBarWidget()
if title_bar is None:
title_bar = DockWidgetTitleBar(dock_widget)
title_bar = DockWidgetTitleBar(dock_widget, self._application_context.qtgui_path)
dock_widget.setTitleBarWidget(title_bar)

# connect extra buttons
Expand Down
38 changes: 35 additions & 3 deletions qt_gui/src/qt_gui/plugin_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,20 @@
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

import os
import time
import traceback

from python_qt_binding.QtCore import qCritical, qDebug, QObject, Qt, qWarning, Signal, Slot
from python_qt_binding.QtCore import qCritical, qDebug, QObject, QSettings, Qt, qWarning, Signal, Slot

from .container_manager import ContainerManager
from .errors import PluginLoadError
from .plugin_handler_container import PluginHandlerContainer
from .plugin_handler_direct import PluginHandlerDirect
from .plugin_instance_id import PluginInstanceId
from .plugin_menu import PluginMenu
from .settings import Settings
from .settings_proxy import SettingsProxy


class PluginManager(QObject):
Expand All @@ -54,11 +58,14 @@ class PluginManager(QObject):
close_application_signal = Signal()
_deferred_reload_plugin_signal = Signal(str)

def __init__(self, plugin_provider, application_context):
discovery_cache_max_age = 60 * 60 * 24 # one day

def __init__(self, plugin_provider, settings, application_context):
super(PluginManager, self).__init__()
self.setObjectName('PluginManager')

self._plugin_provider = plugin_provider
self._settings = Settings(SettingsProxy(settings), 'plugin_manager')
self._application_context = application_context

self._main_window = None
Expand Down Expand Up @@ -104,8 +111,9 @@ def discover(self):
if self._plugin_descriptors is not None:
return
self._plugin_descriptors = {}

# register discovered plugins
plugin_descriptors = self._plugin_provider.discover()
plugin_descriptors = self._discover()
for plugin_descriptor in plugin_descriptors:
self._plugin_descriptors[plugin_descriptor.plugin_id()] = plugin_descriptor

Expand All @@ -118,6 +126,30 @@ def discover(self):
if self._plugin_menu is not None:
self._container_manager.add_to_plugin_menu(self._plugin_menu)

def _discover(self):
discovery_data = self._settings.get_settings('discovery_data')
# check timestamp of the cached discovery data
cache_stamp = self._settings.value('discovery_timestamp', default_value=None)
if cache_stamp:
cache_stamp = float(cache_stamp)

# wipe cached data when forcing discovery or when cache is to old (or from the future)
now = time.time()
if self._application_context.options.force_discover or not cache_stamp or cache_stamp > now or cache_stamp + PluginManager.discovery_cache_max_age < now:
qDebug('PluginManager._discover() force discovery of plugins')
for k in discovery_data.all_keys():
discovery_data.remove(k)
else:
qDebug('PluginManager._discover() using cached plugin discovery information')

plugin_descriptors = self._plugin_provider.discover(discovery_data)

# store timestamp and newly discovered data
if not cache_stamp:
self._settings.set_value('discovery_timestamp', now)

return plugin_descriptors

def find_plugins_by_name(self, lookup_name):
plugins = {}
for plugin_id, plugin_full_name in self.get_plugins().items():
Expand Down
3 changes: 2 additions & 1 deletion qt_gui/src/qt_gui/plugin_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@ def __init__(self):
super(PluginProvider, self).__init__()
self.setObjectName('PluginProvider')

def discover(self):
def discover(self, discovery_data):
"""
Discover the plugins.
@param discovery_data: The settings containing any discovery data which is cached between invocations
@return: Dictionary of plugin ids to `PluginDescriptor`s
"""
raise NotImplementedError
Expand Down
Loading

0 comments on commit 304c12d

Please sign in to comment.