Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve startup time #28

Merged
merged 4 commits into from
Oct 9, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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