From caf56587fe3b96f2142268ca07eba2cfb825ebfa Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 4 Dec 2017 19:37:03 +0100 Subject: [PATCH] Implement switching sidebar views --- cura/CuraApplication.py | 34 ++++++++++------- cura/Settings/SettingsSidebarView.py | 11 +++--- cura/Sidebar/SidebarController.py | 7 +++- cura/Sidebar/SidebarControllerProxy.py | 36 +++++++++++------- cura/Sidebar/SidebarView.py | 17 +++++++-- cura/Sidebar/SidebarViewModel.py | 15 ++++---- resources/qml/Cura.qml | 52 +++++++++++++++++++++++--- resources/qml/SidebarSettings.qml | 21 ----------- 8 files changed, 121 insertions(+), 72 deletions(-) delete mode 100644 resources/qml/SidebarSettings.qml diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index d7ded98688..e9e521fb49 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -201,6 +201,10 @@ class CuraApplication(QtApplication): } ) + ## As of Cura 3.2, the sidebar is controlled by a controller. + # This functionality was added to allow plugins registering custom sidebar views. + self._sidebar_controller = SidebarController(self) + self._currently_loading_files = [] self._non_sliceable_extensions = [] @@ -221,14 +225,6 @@ class CuraApplication(QtApplication): self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png"))) - ## As of Cura 3.2, the sidebar is controlled by a controller. - # This functionality was added to allow plugins registering custom sidebar views. - self._sidebar_controller = SidebarController(self) - - ## Register the default settings sidebar manually - settings_sidebar_view = SettingsSidebarView() - self._sidebar_controller.addSidebarView(settings_sidebar_view) - self.setRequiredPlugins([ "CuraEngineBackend", "UserAgreement", @@ -327,11 +323,6 @@ class CuraApplication(QtApplication): self._need_to_show_user_agreement = not Preferences.getInstance().getValue("general/accepted_user_agreement") - # Set the active sidebar view based on user preferences - preferences.addPreference("cura/active_sidebar_view", "default") - active_sidebar_view = preferences.getValue("cura/active_sidebar_view") - self._sidebar_controller.setActiveSidebarView(active_sidebar_view) - for key in [ "dialog_load_path", # dialog_save_path is in LocalFileOutputDevicePlugin "dialog_profile_path", @@ -737,6 +728,10 @@ class CuraApplication(QtApplication): if not run_headless: self.initializeEngine() + # Now that the SidebarViewModel has been created we can set the default sidebar view + # TODO: put this in a more elegant place + self._setDefaultSidebarView() + if run_headless or self._engine.rootObjects: self.closeSplash() @@ -1324,6 +1319,19 @@ class CuraApplication(QtApplication): def _addProfileWriter(self, profile_writer): pass + ## Create and register the default sidebar component (settings) + def _setDefaultSidebarView(self): + + # Register the default settings sidebar manually + settings_sidebar_view = SettingsSidebarView() + self._sidebar_controller.addSidebarView(settings_sidebar_view) + + # Set the default sidebar view depending on user preferences. + preferences = Preferences.getInstance() + preferences.addPreference("cura/active_sidebar_view", settings_sidebar_view.getPluginId()) + active_sidebar_view = preferences.getValue("cura/active_sidebar_view") + self._sidebar_controller.setActiveSidebarView(active_sidebar_view) + @pyqtSlot("QSize") def setMinimumWindowSize(self, size): self.getMainWindow().setMinimumSize(size) diff --git a/cura/Settings/SettingsSidebarView.py b/cura/Settings/SettingsSidebarView.py index b1cb88182b..8b861fbaf6 100644 --- a/cura/Settings/SettingsSidebarView.py +++ b/cura/Settings/SettingsSidebarView.py @@ -1,8 +1,8 @@ # Copyright (c) 2017 Ultimaker B.V. -import os.path -from PyQt5.QtCore import QObject, QUrl +from PyQt5.QtCore import QObject from UM.i18n import i18nCatalog + from cura.Sidebar.SidebarView import SidebarView i18n_catalog = i18nCatalog("cura") @@ -19,11 +19,10 @@ class SettingsSidebarView(QObject, SidebarView): def getMetaData(self): return { "sidebar_view": { - "name": i18n_catalog.i18nc("", "Print settings"), + "name": i18n_catalog.i18nc("@item:inmenu", "Print settings"), "weight": 0 } } - ## As the default sidebar is not a plugin, we have a get component path method here to allow the sidebar controller to get the needed data. - def getComponentPath(self): - return QUrl("SidebarSettings.qml") + def getComponent(self): + return None diff --git a/cura/Sidebar/SidebarController.py b/cura/Sidebar/SidebarController.py index d7789b9fb8..420f00a489 100644 --- a/cura/Sidebar/SidebarController.py +++ b/cura/Sidebar/SidebarController.py @@ -1,5 +1,6 @@ # Copyright (c) 2017 Ultimaker B.V. from UM.Logger import Logger +from UM.Preferences import Preferences from UM.Signal import Signal from UM.PluginRegistry import PluginRegistry @@ -52,5 +53,7 @@ class SidebarController: ## Change the active sidebar view to one of the registered views. def setActiveSidebarView(self, sidebar_view_id: str): - self._active_sidebar_view = self._sidebar_views[sidebar_view_id] - self.activeSidebarViewChanged.emit() + if sidebar_view_id in self._sidebar_views: + self._active_sidebar_view = self._sidebar_views[sidebar_view_id] + Preferences.getInstance().setValue("cura/active_sidebar_view", sidebar_view_id) + self.activeSidebarViewChanged.emit() diff --git a/cura/Sidebar/SidebarControllerProxy.py b/cura/Sidebar/SidebarControllerProxy.py index 1278d1cea8..c373dc0a0a 100644 --- a/cura/Sidebar/SidebarControllerProxy.py +++ b/cura/Sidebar/SidebarControllerProxy.py @@ -4,33 +4,41 @@ from UM.Application import Application ## The sidebar controller proxy acts a proxy between the sidebar controller and the QMl context of the controller. +from UM.Logger import Logger + + class SidebarControllerProxy(QObject): def __init__(self, parent = None): super().__init__(parent) self._controller = Application.getInstance().getSidebarController() - self._controller.activeSidebarViewChanged.connect(self._onActiveSidebarComponentChanged) + self._controller.activeSidebarViewChanged.connect(self._onActiveSidebarViewChanged) - ## Emitted when the active view changes. activeSidebarViewChanged = pyqtSignal() @classmethod def createSidebarControllerProxy(self, engine, script_engine): return SidebarControllerProxy() - @pyqtSlot() - def setActiveView(self, sidebar_view): - self._controller.setActiveSidebarView(sidebar_view) + @pyqtProperty(str, notify = activeSidebarViewChanged) + def activeSidebarId(self): + if self._controller.getActiveSidebarView() is not None: + return self._controller.getActiveSidebarView().getPluginId() + else: + return "default" - @pyqtProperty(QUrl, notify = activeSidebarViewChanged) - def activeComponentPath(self): - if not self._controller.getActiveSidebarView(): - return QUrl() - return self._controller.getActiveSidebarView().getComponentPath() + @pyqtSlot(str) + def setActiveSidebarView(self, sidebar_view_id): + Logger.log("d", "Setting active sidebar view to %s", sidebar_view_id) + self._controller.setActiveSidebarView(sidebar_view_id) - @pyqtSlot(QUrl) + @pyqtSlot(str, result = QObject) + def getSidebarComponent(self, sidebar_id): + return self._controller.getSidebarView(sidebar_id).getComponent() + + @pyqtSlot(str, result = QUrl) def getSidebarComponentPath(self, sidebar_id): - self._controller.getSidebarView(sidebar_id).getComponentPath() + return self._controller.getSidebarView(sidebar_id).getComponentPath() - def _onActiveSidebarComponentChanged(self): - self.activeSidebarViewChanged.emit() + def _onActiveSidebarViewChanged(self): + self.activeSidebarViewChanged.emit() \ No newline at end of file diff --git a/cura/Sidebar/SidebarView.py b/cura/Sidebar/SidebarView.py index 6b78de1f17..d5c20e0858 100644 --- a/cura/Sidebar/SidebarView.py +++ b/cura/Sidebar/SidebarView.py @@ -1,6 +1,7 @@ # Copyright (c) 2017 Ultimaker B.V. -from PyQt5.QtCore import QUrl +import os.path +from UM.Application import Application from UM.Logger import Logger from UM.PluginObject import PluginObject from UM.PluginRegistry import PluginRegistry @@ -14,11 +15,21 @@ class SidebarView(PluginObject): super().__init__() self._view = None + def getComponent(self): + if not self._view: + self.createView() + return self._view + + def createView(self): + component_path = self.getComponentPath() + self._view = Application.getInstance().createQmlComponent(component_path, {"manager": self}) + ## Get the path to the component QML file as QUrl def getComponentPath(self): try: + plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId()) sidebar_component_file_path = PluginRegistry.getInstance().getMetaData(self.getPluginId())["sidebar_view"]["sidebar_component"] - return QUrl.fromLocalFile(sidebar_component_file_path) + return os.path.join(plugin_path, sidebar_component_file_path) except KeyError: Logger.log("w", "Could not find sidebar component QML file for %s", self.getPluginId()) - return QUrl() + return "" diff --git a/cura/Sidebar/SidebarViewModel.py b/cura/Sidebar/SidebarViewModel.py index 4ed1ca60cb..de0aae182c 100644 --- a/cura/Sidebar/SidebarViewModel.py +++ b/cura/Sidebar/SidebarViewModel.py @@ -26,17 +26,18 @@ class SidebarViewModel(ListModel): ## Update the model when new views are added or another view is made the active view. def _onSidebarViewsChanged(self): items = [] + current_view_id = None + sidebar_views = self._controller.getAllSidebarViews() current_view = self._controller.getActiveSidebarView() + if current_view: + current_view_id = current_view.getPluginId() for sidebar_view_id, sidebar_view in sidebar_views.items(): - plugin_metadata = PluginRegistry.getInstance().getMetaData(sidebar_view_id) - if plugin_metadata: - # Check if the registered view came from a plugin and extract the metadata if so. - sidebar_view_metadata = plugin_metadata.get("sidebar_view", {}) + if sidebar_view_id != "default": + sidebar_view_metadata = PluginRegistry.getInstance().getMetaData(sidebar_view_id).get("sidebar_view", {}) else: - # Get the meta data directly from the plugin - sidebar_view_metadata = sidebar_view.getMetaData() + sidebar_view_metadata = sidebar_view.getMetaData().get("sidebar_view", {}) # Skip view modes that are marked as not visible if "visible" in sidebar_view_metadata and not sidebar_view_metadata["visible"]: @@ -48,7 +49,7 @@ class SidebarViewModel(ListModel): items.append({ "id": sidebar_view_id, "name": name, - "active": sidebar_view_id == current_view.getPluginId(), + "active": sidebar_view_id == current_view_id, "weight": weight }) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 9385b36b60..5f421881c0 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -331,7 +331,7 @@ UM.MainWindow text: catalog.i18nc("@action:button","Open File"); iconSource: UM.Theme.getIcon("load") style: UM.Theme.styles.tool_button - tooltip: ''; + tooltip: "" anchors { top: topbar.bottom; @@ -366,7 +366,7 @@ UM.MainWindow onStopMonitoringPrint: base.showPrintMonitor = false } - Loader + Rectangle { id: sidebar @@ -379,11 +379,30 @@ UM.MainWindow width: UM.Theme.getSize("sidebar").width - // all sidebar components will have access to the show print monitor flag - property bool showPrintMonitor: base.showPrintMonitor + // The sidebarRepeater exposes sidebar views provided by plugins. + // Whenever a plugin sidebar view is active (e.g. not "default"), that sidebar view is shown. + Repeater + { + id: sidebarRepeater - // dynamically get the component from the sidebar controller - source: Cura.SidebarController.activeComponentPath + model: Cura.SidebarViewModel { } + + delegate: Loader + { + id: delegate + asynchronous: true + visible: model.active + + // dynamically get the component from the sidebar controller or set the default sidebar + sourceComponent: { + if (model.id !== "default") { + return Cura.SidebarController.getSidebarComponent(model.id) + } else { + return defaultSidebar + } + } + } + } } Rectangle @@ -434,6 +453,27 @@ UM.MainWindow } } + // This is the default sidebar view. + // It is used as sourceComponent for the default sidebar view. + Component + { + id: defaultSidebar + + Sidebar + { +// anchors { +// top: parent.top +// bottom: parent.bottom +// left: parent.left +// right: parent.right +// } +// +// width: parent.width +// +// monitoringPrint: base.showPrintMonitor + } + } + UM.PreferencesDialog { id: preferences diff --git a/resources/qml/SidebarSettings.qml b/resources/qml/SidebarSettings.qml deleted file mode 100644 index 85bee1f096..0000000000 --- a/resources/qml/SidebarSettings.qml +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2017 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.2 - -Sidebar -{ - id: sidebarSettings - - property bool showPrintMonitor: false - - anchors { - top: parent.top - bottom: parent.bottom - left: parent.left - right: parent.right - } - - width: parent.width - monitoringPrint: showPrintMonitor -}