Implement switching sidebar views

This commit is contained in:
ChrisTerBeke 2017-12-04 19:37:03 +01:00
parent 3c863fc388
commit caf56587fe
8 changed files with 121 additions and 72 deletions

View file

@ -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._currently_loading_files = []
self._non_sliceable_extensions = [] self._non_sliceable_extensions = []
@ -221,14 +225,6 @@ class CuraApplication(QtApplication):
self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png"))) 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([ self.setRequiredPlugins([
"CuraEngineBackend", "CuraEngineBackend",
"UserAgreement", "UserAgreement",
@ -327,11 +323,6 @@ class CuraApplication(QtApplication):
self._need_to_show_user_agreement = not Preferences.getInstance().getValue("general/accepted_user_agreement") 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 [ for key in [
"dialog_load_path", # dialog_save_path is in LocalFileOutputDevicePlugin "dialog_load_path", # dialog_save_path is in LocalFileOutputDevicePlugin
"dialog_profile_path", "dialog_profile_path",
@ -737,6 +728,10 @@ class CuraApplication(QtApplication):
if not run_headless: if not run_headless:
self.initializeEngine() 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: if run_headless or self._engine.rootObjects:
self.closeSplash() self.closeSplash()
@ -1324,6 +1319,19 @@ class CuraApplication(QtApplication):
def _addProfileWriter(self, profile_writer): def _addProfileWriter(self, profile_writer):
pass 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") @pyqtSlot("QSize")
def setMinimumWindowSize(self, size): def setMinimumWindowSize(self, size):
self.getMainWindow().setMinimumSize(size) self.getMainWindow().setMinimumSize(size)

View file

@ -1,8 +1,8 @@
# Copyright (c) 2017 Ultimaker B.V. # Copyright (c) 2017 Ultimaker B.V.
import os.path from PyQt5.QtCore import QObject
from PyQt5.QtCore import QObject, QUrl
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
from cura.Sidebar.SidebarView import SidebarView from cura.Sidebar.SidebarView import SidebarView
i18n_catalog = i18nCatalog("cura") i18n_catalog = i18nCatalog("cura")
@ -19,11 +19,10 @@ class SettingsSidebarView(QObject, SidebarView):
def getMetaData(self): def getMetaData(self):
return { return {
"sidebar_view": { "sidebar_view": {
"name": i18n_catalog.i18nc("", "Print settings"), "name": i18n_catalog.i18nc("@item:inmenu", "Print settings"),
"weight": 0 "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 getComponent(self):
def getComponentPath(self): return None
return QUrl("SidebarSettings.qml")

View file

@ -1,5 +1,6 @@
# Copyright (c) 2017 Ultimaker B.V. # Copyright (c) 2017 Ultimaker B.V.
from UM.Logger import Logger from UM.Logger import Logger
from UM.Preferences import Preferences
from UM.Signal import Signal from UM.Signal import Signal
from UM.PluginRegistry import PluginRegistry from UM.PluginRegistry import PluginRegistry
@ -52,5 +53,7 @@ class SidebarController:
## Change the active sidebar view to one of the registered views. ## Change the active sidebar view to one of the registered views.
def setActiveSidebarView(self, sidebar_view_id: str): def setActiveSidebarView(self, sidebar_view_id: str):
self._active_sidebar_view = self._sidebar_views[sidebar_view_id] if sidebar_view_id in self._sidebar_views:
self.activeSidebarViewChanged.emit() self._active_sidebar_view = self._sidebar_views[sidebar_view_id]
Preferences.getInstance().setValue("cura/active_sidebar_view", sidebar_view_id)
self.activeSidebarViewChanged.emit()

View file

@ -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. ## 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): class SidebarControllerProxy(QObject):
def __init__(self, parent = None): def __init__(self, parent = None):
super().__init__(parent) super().__init__(parent)
self._controller = Application.getInstance().getSidebarController() 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() activeSidebarViewChanged = pyqtSignal()
@classmethod @classmethod
def createSidebarControllerProxy(self, engine, script_engine): def createSidebarControllerProxy(self, engine, script_engine):
return SidebarControllerProxy() return SidebarControllerProxy()
@pyqtSlot() @pyqtProperty(str, notify = activeSidebarViewChanged)
def setActiveView(self, sidebar_view): def activeSidebarId(self):
self._controller.setActiveSidebarView(sidebar_view) if self._controller.getActiveSidebarView() is not None:
return self._controller.getActiveSidebarView().getPluginId()
else:
return "default"
@pyqtProperty(QUrl, notify = activeSidebarViewChanged) @pyqtSlot(str)
def activeComponentPath(self): def setActiveSidebarView(self, sidebar_view_id):
if not self._controller.getActiveSidebarView(): Logger.log("d", "Setting active sidebar view to %s", sidebar_view_id)
return QUrl() self._controller.setActiveSidebarView(sidebar_view_id)
return self._controller.getActiveSidebarView().getComponentPath()
@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): def getSidebarComponentPath(self, sidebar_id):
self._controller.getSidebarView(sidebar_id).getComponentPath() return self._controller.getSidebarView(sidebar_id).getComponentPath()
def _onActiveSidebarComponentChanged(self): def _onActiveSidebarViewChanged(self):
self.activeSidebarViewChanged.emit() self.activeSidebarViewChanged.emit()

View file

@ -1,6 +1,7 @@
# Copyright (c) 2017 Ultimaker B.V. # 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.Logger import Logger
from UM.PluginObject import PluginObject from UM.PluginObject import PluginObject
from UM.PluginRegistry import PluginRegistry from UM.PluginRegistry import PluginRegistry
@ -14,11 +15,21 @@ class SidebarView(PluginObject):
super().__init__() super().__init__()
self._view = None 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 ## Get the path to the component QML file as QUrl
def getComponentPath(self): def getComponentPath(self):
try: try:
plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId())
sidebar_component_file_path = PluginRegistry.getInstance().getMetaData(self.getPluginId())["sidebar_view"]["sidebar_component"] 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: except KeyError:
Logger.log("w", "Could not find sidebar component QML file for %s", self.getPluginId()) Logger.log("w", "Could not find sidebar component QML file for %s", self.getPluginId())
return QUrl() return ""

View file

@ -26,17 +26,18 @@ class SidebarViewModel(ListModel):
## Update the model when new views are added or another view is made the active view. ## Update the model when new views are added or another view is made the active view.
def _onSidebarViewsChanged(self): def _onSidebarViewsChanged(self):
items = [] items = []
current_view_id = None
sidebar_views = self._controller.getAllSidebarViews() sidebar_views = self._controller.getAllSidebarViews()
current_view = self._controller.getActiveSidebarView() current_view = self._controller.getActiveSidebarView()
if current_view:
current_view_id = current_view.getPluginId()
for sidebar_view_id, sidebar_view in sidebar_views.items(): for sidebar_view_id, sidebar_view in sidebar_views.items():
plugin_metadata = PluginRegistry.getInstance().getMetaData(sidebar_view_id) if sidebar_view_id != "default":
if plugin_metadata: sidebar_view_metadata = PluginRegistry.getInstance().getMetaData(sidebar_view_id).get("sidebar_view", {})
# Check if the registered view came from a plugin and extract the metadata if so.
sidebar_view_metadata = plugin_metadata.get("sidebar_view", {})
else: else:
# Get the meta data directly from the plugin sidebar_view_metadata = sidebar_view.getMetaData().get("sidebar_view", {})
sidebar_view_metadata = sidebar_view.getMetaData()
# Skip view modes that are marked as not visible # Skip view modes that are marked as not visible
if "visible" in sidebar_view_metadata and not sidebar_view_metadata["visible"]: if "visible" in sidebar_view_metadata and not sidebar_view_metadata["visible"]:
@ -48,7 +49,7 @@ class SidebarViewModel(ListModel):
items.append({ items.append({
"id": sidebar_view_id, "id": sidebar_view_id,
"name": name, "name": name,
"active": sidebar_view_id == current_view.getPluginId(), "active": sidebar_view_id == current_view_id,
"weight": weight "weight": weight
}) })

View file

@ -331,7 +331,7 @@ UM.MainWindow
text: catalog.i18nc("@action:button","Open File"); text: catalog.i18nc("@action:button","Open File");
iconSource: UM.Theme.getIcon("load") iconSource: UM.Theme.getIcon("load")
style: UM.Theme.styles.tool_button style: UM.Theme.styles.tool_button
tooltip: ''; tooltip: ""
anchors anchors
{ {
top: topbar.bottom; top: topbar.bottom;
@ -366,7 +366,7 @@ UM.MainWindow
onStopMonitoringPrint: base.showPrintMonitor = false onStopMonitoringPrint: base.showPrintMonitor = false
} }
Loader Rectangle
{ {
id: sidebar id: sidebar
@ -379,11 +379,30 @@ UM.MainWindow
width: UM.Theme.getSize("sidebar").width width: UM.Theme.getSize("sidebar").width
// all sidebar components will have access to the show print monitor flag // The sidebarRepeater exposes sidebar views provided by plugins.
property bool showPrintMonitor: base.showPrintMonitor // 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 model: Cura.SidebarViewModel { }
source: Cura.SidebarController.activeComponentPath
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 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 UM.PreferencesDialog
{ {
id: preferences id: preferences

View file

@ -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
}