From d69e397716120d542a74734f6c7ec100ac663beb Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 4 Dec 2017 10:31:21 +0100 Subject: [PATCH 01/39] Add new plugins to ignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index f67add62cf..7284597fa9 100644 --- a/.gitignore +++ b/.gitignore @@ -45,6 +45,7 @@ plugins/cura-big-flame-graph plugins/cura-siemensnx-plugin plugins/CuraVariSlicePlugin plugins/CuraLiveScriptingPlugin +plugins/CuraPrintProfileCreator #Build stuff CMakeCache.txt From 1934bdfb99ad6c407f360f12221e710a0edb524f Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 4 Dec 2017 10:52:40 +0100 Subject: [PATCH 02/39] Add sidebar view object --- cura/SidebarView.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 cura/SidebarView.py diff --git a/cura/SidebarView.py b/cura/SidebarView.py new file mode 100644 index 0000000000..f5a9caba94 --- /dev/null +++ b/cura/SidebarView.py @@ -0,0 +1,13 @@ +# Copyright (c) 2017 Ultimaker B.V. + +from UM.PluginObject import PluginObject + + +# Abstract class for sidebar view objects. +# By default the sidebar is Cura's settings, slicing and printing overview. +# The last plugin to claim the sidebar QML target will be displayed. +class SidebarView(PluginObject): + + def __init__(self): + super().__init__() + print("sidebar view hello") From db2c3525c48411a9611dff17d334728556c9ca27 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 4 Dec 2017 11:06:10 +0100 Subject: [PATCH 03/39] Sidebar view and controller scaffolding --- cura/Sidebar/SidebarController.py | 48 +++++++++++++++++++++++++++++++ cura/{ => Sidebar}/SidebarView.py | 0 cura/Sidebar/__init__.py | 0 3 files changed, 48 insertions(+) create mode 100644 cura/Sidebar/SidebarController.py rename cura/{ => Sidebar}/SidebarView.py (100%) create mode 100644 cura/Sidebar/__init__.py diff --git a/cura/Sidebar/SidebarController.py b/cura/Sidebar/SidebarController.py new file mode 100644 index 0000000000..79e8d55c56 --- /dev/null +++ b/cura/Sidebar/SidebarController.py @@ -0,0 +1,48 @@ +# Copyright (c) 2017 Ultimaker B.V. +from UM.Logger import Logger +from UM.PluginRegistry import PluginRegistry +from UM.Signal import Signal +from .SidebarView import SidebarView +from typing import Optional + +# The sidebar controller manages available sidebar components and decides which one to display. +# The cura.qml file uses this controller to repeat over the sidebars and show the active index. +class SidebarController: + + def __init__(self, application): + self._application = application + self._sidebar_views = {} + self._active_sidebar_view = None + + PluginRegistry.addType("sidebar_view", self.addSidebarView) + + ## Emitted when the list of views changes. + sidebarViewsChanged = Signal() + + ## Emitted when the active view changes. + activeSidebarViewChanged = Signal() + + ## Get the active application instance. + def getApplication(self): + return self._application + + ## Add a sidebar view to the registry. + # It get's a unique name based on the plugin ID. + def addSidebarView(self, sidebar_view: SidebarView): + name = sidebar_view.getPluginId() + if name not in self._sidebar_views: + self._sidebar_views[name] = sidebar_view + self.sidebarViewsChanged.emit() + + ## Get a registered sidebar view by name. + # The name is the ID of the plugin that registered the view. + def getSidebarView(self, name: str) -> Optional[SidebarView]: + try: + return self._sidebar_views[name] + except KeyError: + Logger.log("e", "Unable to find %s in sidebar view list", name) + return None + + ## Change the active sidebar view to one of the registered views. + def setActiveSidebarView(self, name: str): + print("setting active sidebar view") diff --git a/cura/SidebarView.py b/cura/Sidebar/SidebarView.py similarity index 100% rename from cura/SidebarView.py rename to cura/Sidebar/SidebarView.py diff --git a/cura/Sidebar/__init__.py b/cura/Sidebar/__init__.py new file mode 100644 index 0000000000..e69de29bb2 From f2b4cbe182fe01fdd974958f28e35f37952a1b28 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 4 Dec 2017 11:37:49 +0100 Subject: [PATCH 04/39] Register sidebar controller in application, start with default sidebar view --- cura/CuraApplication.py | 19 +++++++++++++++++++ cura/Settings/SettingsSidebarView.py | 10 ++++++++++ cura/Sidebar/SidebarViewModel.py | 16 ++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 cura/Settings/SettingsSidebarView.py create mode 100644 cura/Sidebar/SidebarViewModel.py diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 9427e15552..7f8fef05ad 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -33,9 +33,11 @@ from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation from UM.Operations.GroupedOperation import GroupedOperation from UM.Operations.SetTransformOperation import SetTransformOperation from cura.Arrange import Arrange +from cura.Settings.SettingsSidebarView import SettingsSidebarView from cura.ShapeArray import ShapeArray from cura.ConvexHullDecorator import ConvexHullDecorator from cura.SetParentOperation import SetParentOperation +from cura.Sidebar.SidebarController import SidebarController from cura.SliceableObjectDecorator import SliceableObjectDecorator from cura.BlockSlicingDecorator import BlockSlicingDecorator @@ -204,6 +206,14 @@ class CuraApplication(QtApplication): self._setting_inheritance_manager = None self._simple_mode_settings_manager = None + ## 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._additional_components = {} # Components to add to certain areas in the interface super().__init__(name = "cura", version = CuraVersion, buildtype = CuraBuildType, @@ -775,6 +785,14 @@ class CuraApplication(QtApplication): def getPrintInformation(self): return self._print_information + ## Get the SidebarController of this application. + # A sidebar controller is created if it wasn't yet. + # \returns SidebarControllers \type{SidebarController} + def getSidebarController(self) -> SidebarController: + if self._sidebar_controller is None: + self._sidebar_controller = SidebarController(self) + return self._sidebar_controller + ## Registers objects for the QML engine to use. # # \param engine The QML engine. @@ -800,6 +818,7 @@ class CuraApplication(QtApplication): qmlRegisterType(MachineNameValidator, "Cura", 1, 0, "MachineNameValidator") qmlRegisterType(UserChangesModel, "Cura", 1, 1, "UserChangesModel") qmlRegisterSingletonType(ContainerManager, "Cura", 1, 0, "ContainerManager", ContainerManager.createContainerManager) + qmlRegisterSingletonType(SidebarController, "Cura", 1, 0, "SidebarController", self.getSidebarController) # As of Qt5.7, it is necessary to get rid of any ".." in the path for the singleton to work. actions_url = QUrl.fromLocalFile(os.path.abspath(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Actions.qml"))) diff --git a/cura/Settings/SettingsSidebarView.py b/cura/Settings/SettingsSidebarView.py new file mode 100644 index 0000000000..813f3fef2f --- /dev/null +++ b/cura/Settings/SettingsSidebarView.py @@ -0,0 +1,10 @@ +# Copyright (c) 2017 Ultimaker B.V. + +from PyQt5.QtCore import QObject + +from cura.Sidebar.SidebarView import SidebarView + +class SettingsSidebarView(QObject, SidebarView): + + def __init__(self): + super().__init__() diff --git a/cura/Sidebar/SidebarViewModel.py b/cura/Sidebar/SidebarViewModel.py new file mode 100644 index 0000000000..49e64060bc --- /dev/null +++ b/cura/Sidebar/SidebarViewModel.py @@ -0,0 +1,16 @@ +# Copyright (c) 2017 Ultimaker B.V. +from PyQt5.QtCore import Qt +from UM.Qt.ListModel import ListModel +from UM.Application import Application +from UM.PluginRegistry import PluginRegistry + +## The SidebarViewModel is the default sidebar view in Cura with all the print settings and print button. +class SidebarViewModel(ListModel): + IdRole = Qt.UserRole + 1 + NameRole = Qt.UserRole + 2 + ActiveRole = Qt.UserRole + 3 + + def __init__(self, parent = None): + super().__init__(parent) + + \ No newline at end of file From 713055e320266d2bfa456e545c475cf8ba4849c7 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 4 Dec 2017 12:37:29 +0100 Subject: [PATCH 05/39] Get meta data per sidebar view in sidebar view model --- cura/Settings/SettingsSidebarView.py | 13 +++++++-- cura/Sidebar/SidebarController.py | 6 ++++- cura/Sidebar/SidebarViewModel.py | 40 +++++++++++++++++++++++++++- 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/cura/Settings/SettingsSidebarView.py b/cura/Settings/SettingsSidebarView.py index 813f3fef2f..abd3968ca9 100644 --- a/cura/Settings/SettingsSidebarView.py +++ b/cura/Settings/SettingsSidebarView.py @@ -1,10 +1,19 @@ # Copyright (c) 2017 Ultimaker B.V. - from PyQt5.QtCore import QObject - +from UM.i18n import i18nCatalog from cura.Sidebar.SidebarView import SidebarView +i18n_catalog = i18nCatalog("cura") class SettingsSidebarView(QObject, SidebarView): def __init__(self): super().__init__() + + ## As the default sidebar is not a plugin, we have a get meta data method here to allow the sidebar view model to get the needed data. + def getMetaData(self): + return { + "sidebar_view": { + "name": i18n_catalog.i18nc("", "Print settings"), + "weight": 1 + } + } diff --git a/cura/Sidebar/SidebarController.py b/cura/Sidebar/SidebarController.py index 79e8d55c56..cb0e4a1e8e 100644 --- a/cura/Sidebar/SidebarController.py +++ b/cura/Sidebar/SidebarController.py @@ -3,7 +3,7 @@ from UM.Logger import Logger from UM.PluginRegistry import PluginRegistry from UM.Signal import Signal from .SidebarView import SidebarView -from typing import Optional +from typing import Optional, Dict # The sidebar controller manages available sidebar components and decides which one to display. # The cura.qml file uses this controller to repeat over the sidebars and show the active index. @@ -46,3 +46,7 @@ class SidebarController: ## Change the active sidebar view to one of the registered views. def setActiveSidebarView(self, name: str): print("setting active sidebar view") + + ## Get all sidebar views registered in this controller. + def getAllSidebarViews(self) -> Dict[SidebarView]: + return self._sidebar_views diff --git a/cura/Sidebar/SidebarViewModel.py b/cura/Sidebar/SidebarViewModel.py index 49e64060bc..638800247d 100644 --- a/cura/Sidebar/SidebarViewModel.py +++ b/cura/Sidebar/SidebarViewModel.py @@ -13,4 +13,42 @@ class SidebarViewModel(ListModel): def __init__(self, parent = None): super().__init__(parent) - \ No newline at end of file + self._controller = Application.getInstance().getSidebarController() + + # register Qt list roles + self.addRoleName(self.IdRole, "id") + self.addRoleName(self.NameRole, "name") + self.addRoleName(self.ActiveRole, "active") + + ## Update the model when new views are added or another view is made the active view. + def _onSidebarViewsChanged(self): + items = [] + sidebar_views = self._controller.getAllSidebarViews() + current_view = self._controller.getActiveSidebarView() + + 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", {}) + else: + # Get the meta data directly from the plugin + sidebar_view_metadata = sidebar_view.getMetaData() + + # Skip view modes that are marked as not visible + if "visible" in sidebar_view_metadata and not sidebar_view_metadata["visible"]: + continue + + name = sidebar_view_metadata.get("name", id) + weight = sidebar_view_metadata.get("weight", 0) + + items.append({ + "id": sidebar_view_id, + "name": name, + "active": sidebar_view_id == current_view.getPluginId(), + "weight": weight + }) + + # Sort the views by weight + items.sort(key=lambda t: t["weight"]) + self.setItems(items) From 4ef39ca31374ee77670d33a0096b57f473951210 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 4 Dec 2017 12:47:08 +0100 Subject: [PATCH 06/39] Add sidebar views menu item and expose model to QML --- cura/CuraApplication.py | 2 ++ cura/Settings/SettingsSidebarView.py | 4 ++++ cura/Sidebar/SidebarController.py | 1 + cura/Sidebar/SidebarViewModel.py | 5 ++++- resources/qml/Menus/ViewMenu.qml | 33 ++++++++++++++++++++++------ 5 files changed, 37 insertions(+), 8 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 7f8fef05ad..a2efa887c0 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -38,6 +38,7 @@ from cura.ShapeArray import ShapeArray from cura.ConvexHullDecorator import ConvexHullDecorator from cura.SetParentOperation import SetParentOperation from cura.Sidebar.SidebarController import SidebarController +from cura.Sidebar.SidebarViewModel import SidebarViewModel from cura.SliceableObjectDecorator import SliceableObjectDecorator from cura.BlockSlicingDecorator import BlockSlicingDecorator @@ -819,6 +820,7 @@ class CuraApplication(QtApplication): qmlRegisterType(UserChangesModel, "Cura", 1, 1, "UserChangesModel") qmlRegisterSingletonType(ContainerManager, "Cura", 1, 0, "ContainerManager", ContainerManager.createContainerManager) qmlRegisterSingletonType(SidebarController, "Cura", 1, 0, "SidebarController", self.getSidebarController) + qmlRegisterType(SidebarViewModel, "Cura", 1, 0, "SidebarViewModel") # As of Qt5.7, it is necessary to get rid of any ".." in the path for the singleton to work. actions_url = QUrl.fromLocalFile(os.path.abspath(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Actions.qml"))) diff --git a/cura/Settings/SettingsSidebarView.py b/cura/Settings/SettingsSidebarView.py index abd3968ca9..648e056044 100644 --- a/cura/Settings/SettingsSidebarView.py +++ b/cura/Settings/SettingsSidebarView.py @@ -9,6 +9,10 @@ class SettingsSidebarView(QObject, SidebarView): def __init__(self): super().__init__() + ## As the default sidebar is not a plugin, we have a get plugin ID method to allow the sidebar view model to get the needed data. + def getPluginId(self): + return "default" + ## As the default sidebar is not a plugin, we have a get meta data method here to allow the sidebar view model to get the needed data. def getMetaData(self): return { diff --git a/cura/Sidebar/SidebarController.py b/cura/Sidebar/SidebarController.py index cb0e4a1e8e..1212091b2d 100644 --- a/cura/Sidebar/SidebarController.py +++ b/cura/Sidebar/SidebarController.py @@ -46,6 +46,7 @@ class SidebarController: ## Change the active sidebar view to one of the registered views. def setActiveSidebarView(self, name: str): print("setting active sidebar view") + self.activeSidebarViewChanged.emit() ## Get all sidebar views registered in this controller. def getAllSidebarViews(self) -> Dict[SidebarView]: diff --git a/cura/Sidebar/SidebarViewModel.py b/cura/Sidebar/SidebarViewModel.py index 638800247d..4ed1ca60cb 100644 --- a/cura/Sidebar/SidebarViewModel.py +++ b/cura/Sidebar/SidebarViewModel.py @@ -13,7 +13,10 @@ class SidebarViewModel(ListModel): def __init__(self, parent = None): super().__init__(parent) + # connect views changed signals self._controller = Application.getInstance().getSidebarController() + self._controller.sidebarViewsChanged.connect(self._onSidebarViewsChanged) + self._controller.activeSidebarViewChanged.connect(self._onSidebarViewsChanged) # register Qt list roles self.addRoleName(self.IdRole, "id") @@ -39,7 +42,7 @@ class SidebarViewModel(ListModel): if "visible" in sidebar_view_metadata and not sidebar_view_metadata["visible"]: continue - name = sidebar_view_metadata.get("name", id) + name = sidebar_view_metadata.get("name", sidebar_view_id) weight = sidebar_view_metadata.get("weight", 0) items.append({ diff --git a/resources/qml/Menus/ViewMenu.qml b/resources/qml/Menus/ViewMenu.qml index bb5999edb9..654de3391a 100644 --- a/resources/qml/Menus/ViewMenu.qml +++ b/resources/qml/Menus/ViewMenu.qml @@ -12,21 +12,40 @@ Menu title: catalog.i18nc("@title:menu menubar:toplevel", "&View"); id: menu enabled: !PrintInformation.preSliced + + // main views Instantiator { - model: UM.ViewModel { } + model: UM.ViewModel{} MenuItem { - text: model.name; - checkable: true; - checked: model.active; - exclusiveGroup: group; - onTriggered: UM.Controller.setActiveView(model.id); + text: model.name + checkable: true + checked: model.active + exclusiveGroup: group + onTriggered: UM.Controller.setActiveView(model.id) } onObjectAdded: menu.insertItem(index, object) onObjectRemoved: menu.removeItem(object) } - ExclusiveGroup { id: group; } + ExclusiveGroup { id: group } + + // sidebar views + Instantiator + { + model: Cura.SidebarViewModel{} + MenuItem + { + text: model.name + checkable: true + checked: model.active + exclusiveGroup: sidebarGroup + onTriggered: Cura.SidebarController.setActiveSidebarView(model.id) + } + onObjectAdded: menu.insertItem(index, object) + onObjectRemoved: menu.removeItem(object) + } + ExclusiveGroup { id: sidebarGroup } MenuSeparator {} MenuItem { action: Cura.Actions.homeCamera; } From e8f1073af85c8a5fad8d80896f1954c5b9809144 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 4 Dec 2017 12:48:07 +0100 Subject: [PATCH 07/39] Add menu seperator --- resources/qml/Menus/ViewMenu.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/qml/Menus/ViewMenu.qml b/resources/qml/Menus/ViewMenu.qml index 654de3391a..b5e4c5765f 100644 --- a/resources/qml/Menus/ViewMenu.qml +++ b/resources/qml/Menus/ViewMenu.qml @@ -30,6 +30,8 @@ Menu } ExclusiveGroup { id: group } + MenuSeparator {} + // sidebar views Instantiator { From 5eeb98bbcf41011bc9f49591aa4727e75803f511 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 4 Dec 2017 13:20:16 +0100 Subject: [PATCH 08/39] Move sidebar controller init --- cura/CuraApplication.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index a2efa887c0..6f5746b49d 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -207,14 +207,6 @@ class CuraApplication(QtApplication): self._setting_inheritance_manager = None self._simple_mode_settings_manager = None - ## 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._additional_components = {} # Components to add to certain areas in the interface super().__init__(name = "cura", version = CuraVersion, buildtype = CuraBuildType, @@ -225,6 +217,14 @@ 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", From 3c863fc388b0f7e813005f3b50403572d333b9e5 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 4 Dec 2017 16:28:35 +0100 Subject: [PATCH 09/39] Get default settings view to work as sidebar component --- cura/CuraApplication.py | 37 +++++++++++++++----------- cura/Settings/SettingsSidebarView.py | 16 +++++++---- cura/Sidebar/SidebarController.py | 33 ++++++++++++----------- cura/Sidebar/SidebarControllerProxy.py | 36 +++++++++++++++++++++++++ cura/Sidebar/SidebarView.py | 15 +++++++++-- cura_app.py | 8 +++--- resources/qml/Cura.qml | 21 +++++++++------ resources/qml/SidebarSettings.qml | 21 +++++++++++++++ 8 files changed, 138 insertions(+), 49 deletions(-) create mode 100644 cura/Sidebar/SidebarControllerProxy.py create mode 100644 resources/qml/SidebarSettings.qml diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 6f5746b49d..d7ded98688 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -32,13 +32,11 @@ from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation from UM.Operations.GroupedOperation import GroupedOperation from UM.Operations.SetTransformOperation import SetTransformOperation + from cura.Arrange import Arrange -from cura.Settings.SettingsSidebarView import SettingsSidebarView from cura.ShapeArray import ShapeArray from cura.ConvexHullDecorator import ConvexHullDecorator from cura.SetParentOperation import SetParentOperation -from cura.Sidebar.SidebarController import SidebarController -from cura.Sidebar.SidebarViewModel import SidebarViewModel from cura.SliceableObjectDecorator import SliceableObjectDecorator from cura.BlockSlicingDecorator import BlockSlicingDecorator @@ -78,6 +76,11 @@ from cura.Settings.ContainerManager import ContainerManager from cura.Settings.GlobalStack import GlobalStack from cura.Settings.ExtruderStack import ExtruderStack +from cura.Sidebar.SidebarController import SidebarController +from cura.Sidebar.SidebarControllerProxy import SidebarControllerProxy +from cura.Sidebar.SidebarViewModel import SidebarViewModel +from cura.Settings.SettingsSidebarView import SettingsSidebarView + from PyQt5.QtCore import QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS from UM.FlameProfiler import pyqtSlot from PyQt5.QtGui import QColor, QIcon @@ -131,6 +134,7 @@ class CuraApplication(QtApplication): stacksValidationFinished = pyqtSignal() # Emitted whenever a validation is finished def __init__(self): + # this list of dir names will be used by UM to detect an old cura directory for dir_name in ["extruders", "machine_instances", "materials", "plugins", "quality", "user", "variants"]: Resources.addExpectedDirNameInData(dir_name) @@ -159,7 +163,6 @@ class CuraApplication(QtApplication): SettingDefinition.addSettingType("extruder", None, str, Validator) SettingDefinition.addSettingType("optional_extruder", None, str, None) - SettingDefinition.addSettingType("[int]", None, str, None) SettingFunction.registerOperator("extruderValues", ExtruderManager.getExtruderValues) @@ -184,7 +187,8 @@ class CuraApplication(QtApplication): ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.DefinitionChangesContainer) ## Initialise the version upgrade manager with Cura's storage paths. - import UM.VersionUpgradeManager #Needs to be here to prevent circular dependencies. + # Needs to be here to prevent circular dependencies. + import UM.VersionUpgradeManager UM.VersionUpgradeManager.VersionUpgradeManager.getInstance().setCurrentVersions( { @@ -323,6 +327,11 @@ 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", @@ -678,7 +687,6 @@ class CuraApplication(QtApplication): controller = self.getController() controller.setActiveView("SolidView") - controller.setCameraTool("CameraTool") controller.setSelectionTool("SelectionTool") @@ -741,6 +749,11 @@ class CuraApplication(QtApplication): self.exec_() + ## Get the SidebarController of this application. + # \returns SidebarControllers \type{SidebarController} + def getSidebarController(self) -> SidebarController: + return self._sidebar_controller + def getMachineManager(self, *args): if self._machine_manager is None: self._machine_manager = MachineManager.createMachineManager() @@ -786,14 +799,6 @@ class CuraApplication(QtApplication): def getPrintInformation(self): return self._print_information - ## Get the SidebarController of this application. - # A sidebar controller is created if it wasn't yet. - # \returns SidebarControllers \type{SidebarController} - def getSidebarController(self) -> SidebarController: - if self._sidebar_controller is None: - self._sidebar_controller = SidebarController(self) - return self._sidebar_controller - ## Registers objects for the QML engine to use. # # \param engine The QML engine. @@ -808,6 +813,7 @@ class CuraApplication(QtApplication): qmlRegisterUncreatableType(CuraApplication, "Cura", 1, 0, "ResourceTypes", "Just an Enum type") + qmlRegisterType(SidebarViewModel, "Cura", 1, 0, "SidebarViewModel") qmlRegisterType(ExtrudersModel, "Cura", 1, 0, "ExtrudersModel") qmlRegisterType(ContainerSettingsModel, "Cura", 1, 0, "ContainerSettingsModel") qmlRegisterSingletonType(ProfilesModel, "Cura", 1, 0, "ProfilesModel", ProfilesModel.createProfilesModel) @@ -819,8 +825,7 @@ class CuraApplication(QtApplication): qmlRegisterType(MachineNameValidator, "Cura", 1, 0, "MachineNameValidator") qmlRegisterType(UserChangesModel, "Cura", 1, 1, "UserChangesModel") qmlRegisterSingletonType(ContainerManager, "Cura", 1, 0, "ContainerManager", ContainerManager.createContainerManager) - qmlRegisterSingletonType(SidebarController, "Cura", 1, 0, "SidebarController", self.getSidebarController) - qmlRegisterType(SidebarViewModel, "Cura", 1, 0, "SidebarViewModel") + qmlRegisterSingletonType(SidebarControllerProxy, "Cura", 1, 0, "SidebarController", SidebarControllerProxy.createSidebarControllerProxy) # As of Qt5.7, it is necessary to get rid of any ".." in the path for the singleton to work. actions_url = QUrl.fromLocalFile(os.path.abspath(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Actions.qml"))) diff --git a/cura/Settings/SettingsSidebarView.py b/cura/Settings/SettingsSidebarView.py index 648e056044..b1cb88182b 100644 --- a/cura/Settings/SettingsSidebarView.py +++ b/cura/Settings/SettingsSidebarView.py @@ -1,23 +1,29 @@ # Copyright (c) 2017 Ultimaker B.V. -from PyQt5.QtCore import QObject +import os.path +from PyQt5.QtCore import QObject, QUrl + from UM.i18n import i18nCatalog from cura.Sidebar.SidebarView import SidebarView i18n_catalog = i18nCatalog("cura") class SettingsSidebarView(QObject, SidebarView): - def __init__(self): - super().__init__() + def __init__(self, parent = None): + super().__init__(parent) ## As the default sidebar is not a plugin, we have a get plugin ID method to allow the sidebar view model to get the needed data. def getPluginId(self): return "default" - ## As the default sidebar is not a plugin, we have a get meta data method here to allow the sidebar view model to get the needed data. + ## As the default sidebar is not a plugin, we have a add meta data method here to allow the sidebar view model to get the needed data. def getMetaData(self): return { "sidebar_view": { "name": i18n_catalog.i18nc("", "Print settings"), - "weight": 1 + "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") diff --git a/cura/Sidebar/SidebarController.py b/cura/Sidebar/SidebarController.py index 1212091b2d..d7789b9fb8 100644 --- a/cura/Sidebar/SidebarController.py +++ b/cura/Sidebar/SidebarController.py @@ -1,9 +1,7 @@ # Copyright (c) 2017 Ultimaker B.V. from UM.Logger import Logger -from UM.PluginRegistry import PluginRegistry from UM.Signal import Signal -from .SidebarView import SidebarView -from typing import Optional, Dict +from UM.PluginRegistry import PluginRegistry # The sidebar controller manages available sidebar components and decides which one to display. # The cura.qml file uses this controller to repeat over the sidebars and show the active index. @@ -14,6 +12,7 @@ class SidebarController: self._sidebar_views = {} self._active_sidebar_view = None + # Register the sidebar_view plugin type so plugins can expose custom sidebar views. PluginRegistry.addType("sidebar_view", self.addSidebarView) ## Emitted when the list of views changes. @@ -26,28 +25,32 @@ class SidebarController: def getApplication(self): return self._application + ## Get all sidebar views registered in this controller. + def getAllSidebarViews(self): + return self._sidebar_views + ## Add a sidebar view to the registry. # It get's a unique name based on the plugin ID. - def addSidebarView(self, sidebar_view: SidebarView): - name = sidebar_view.getPluginId() - if name not in self._sidebar_views: - self._sidebar_views[name] = sidebar_view + def addSidebarView(self, sidebar_view): + sidebar_view_id = sidebar_view.getPluginId() + if sidebar_view_id not in self._sidebar_views: + self._sidebar_views[sidebar_view_id] = sidebar_view self.sidebarViewsChanged.emit() ## Get a registered sidebar view by name. # The name is the ID of the plugin that registered the view. - def getSidebarView(self, name: str) -> Optional[SidebarView]: + def getSidebarView(self, name: str): try: return self._sidebar_views[name] except KeyError: Logger.log("e", "Unable to find %s in sidebar view list", name) return None - ## Change the active sidebar view to one of the registered views. - def setActiveSidebarView(self, name: str): - print("setting active sidebar view") - self.activeSidebarViewChanged.emit() + ## Get the active sidebar view. + def getActiveSidebarView(self): + return self._active_sidebar_view - ## Get all sidebar views registered in this controller. - def getAllSidebarViews(self) -> Dict[SidebarView]: - return self._sidebar_views + ## 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() diff --git a/cura/Sidebar/SidebarControllerProxy.py b/cura/Sidebar/SidebarControllerProxy.py new file mode 100644 index 0000000000..1278d1cea8 --- /dev/null +++ b/cura/Sidebar/SidebarControllerProxy.py @@ -0,0 +1,36 @@ +# Copyright (c) 2017 Ultimaker B.V. +from PyQt5.QtCore import QObject, pyqtSlot, QUrl, pyqtProperty, pyqtSignal +from UM.Application import Application + + +## The sidebar controller proxy acts a proxy between the sidebar controller and the QMl context of the controller. +class SidebarControllerProxy(QObject): + + def __init__(self, parent = None): + super().__init__(parent) + self._controller = Application.getInstance().getSidebarController() + self._controller.activeSidebarViewChanged.connect(self._onActiveSidebarComponentChanged) + + ## 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(QUrl, notify = activeSidebarViewChanged) + def activeComponentPath(self): + if not self._controller.getActiveSidebarView(): + return QUrl() + return self._controller.getActiveSidebarView().getComponentPath() + + @pyqtSlot(QUrl) + def getSidebarComponentPath(self, sidebar_id): + self._controller.getSidebarView(sidebar_id).getComponentPath() + + def _onActiveSidebarComponentChanged(self): + self.activeSidebarViewChanged.emit() diff --git a/cura/Sidebar/SidebarView.py b/cura/Sidebar/SidebarView.py index f5a9caba94..6b78de1f17 100644 --- a/cura/Sidebar/SidebarView.py +++ b/cura/Sidebar/SidebarView.py @@ -1,7 +1,9 @@ # Copyright (c) 2017 Ultimaker B.V. +from PyQt5.QtCore import QUrl +from UM.Logger import Logger from UM.PluginObject import PluginObject - +from UM.PluginRegistry import PluginRegistry # Abstract class for sidebar view objects. # By default the sidebar is Cura's settings, slicing and printing overview. @@ -10,4 +12,13 @@ class SidebarView(PluginObject): def __init__(self): super().__init__() - print("sidebar view hello") + self._view = None + + ## Get the path to the component QML file as QUrl + def getComponentPath(self): + try: + sidebar_component_file_path = PluginRegistry.getInstance().getMetaData(self.getPluginId())["sidebar_view"]["sidebar_component"] + return QUrl.fromLocalFile(sidebar_component_file_path) + except KeyError: + Logger.log("w", "Could not find sidebar component QML file for %s", self.getPluginId()) + return QUrl() diff --git a/cura_app.py b/cura_app.py index d725bc1200..dd795e5aa4 100755 --- a/cura_app.py +++ b/cura_app.py @@ -22,9 +22,10 @@ if Platform.isLinux(): # Needed for platform.linux_distribution, which is not av if Platform.isWindows() and hasattr(sys, "frozen"): try: del os.environ["PYTHONPATH"] - except KeyError: pass + except KeyError: + pass -#WORKAROUND: GITHUB-704 GITHUB-708 +# WORKAROUND: GITHUB-704 GITHUB-708 # It looks like setuptools creates a .pth file in # the default /usr/lib which causes the default site-packages # to be inserted into sys.path before PYTHONPATH. @@ -45,6 +46,7 @@ def exceptHook(hook_type, value, traceback): _crash_handler = CrashHandler(hook_type, value, traceback) _crash_handler.show() + sys.excepthook = exceptHook # Workaround for a race condition on certain systems where there @@ -75,7 +77,7 @@ faulthandler.enable() # Force an instance of CuraContainerRegistry to be created and reused later. cura.Settings.CuraContainerRegistry.CuraContainerRegistry.getInstance() -# This prestart up check is needed to determine if we should start the application at all. +# This pre-start up check is needed to determine if we should start the application at all. if not cura.CuraApplication.CuraApplication.preStartUp(): sys.exit(0) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index e144048af7..9385b36b60 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -366,19 +366,24 @@ UM.MainWindow onStopMonitoringPrint: base.showPrintMonitor = false } - Sidebar + Loader { - id: sidebar; + id: sidebar anchors { - top: topbar.bottom; - bottom: parent.bottom; - right: parent.right; + top: topbar.bottom + bottom: parent.bottom + right: parent.right } - z: 1 - width: UM.Theme.getSize("sidebar").width; - monitoringPrint: base.showPrintMonitor + + width: UM.Theme.getSize("sidebar").width + + // all sidebar components will have access to the show print monitor flag + property bool showPrintMonitor: base.showPrintMonitor + + // dynamically get the component from the sidebar controller + source: Cura.SidebarController.activeComponentPath } Rectangle diff --git a/resources/qml/SidebarSettings.qml b/resources/qml/SidebarSettings.qml new file mode 100644 index 0000000000..85bee1f096 --- /dev/null +++ b/resources/qml/SidebarSettings.qml @@ -0,0 +1,21 @@ +// 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 +} From caf56587fe3b96f2142268ca07eba2cfb825ebfa Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 4 Dec 2017 19:37:03 +0100 Subject: [PATCH 10/39] 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 -} From 5673a834bc16edd0c4afaa79c3258ad7bc63401a Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 4 Dec 2017 19:48:42 +0100 Subject: [PATCH 11/39] Fix showing default sidebar, cleanup needed --- resources/qml/Cura.qml | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 5f421881c0..c08080f276 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -379,6 +379,25 @@ UM.MainWindow width: UM.Theme.getSize("sidebar").width + // This is the default sidebar view. + // It is hidden when the active sidebar view ID is not default. + Sidebar + { + id: defaultSidebar + + anchors { + top: parent.top + bottom: parent.bottom + left: parent.left + right: parent.right + } + + width: parent.width + z: 1 + monitoringPrint: base.showPrintMonitor + visible: Cura.SidebarController.activeSidebarId == "default" + } + // 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 @@ -397,8 +416,6 @@ UM.MainWindow sourceComponent: { if (model.id !== "default") { return Cura.SidebarController.getSidebarComponent(model.id) - } else { - return defaultSidebar } } } @@ -453,27 +470,6 @@ 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 From e33288b7c8cf7b4f3e739cf2d2dcb086d4e782d0 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 5 Dec 2017 10:56:59 +0100 Subject: [PATCH 12/39] Move sidebar target for plugins to only middle section --- cura/CuraApplication.py | 11 +---- cura/Settings/SettingsSidebarView.py | 28 ----------- cura/Sidebar/SidebarController.py | 9 +++- cura/Sidebar/SidebarControllerProxy.py | 5 +- cura/Sidebar/SidebarViewModel.py | 16 +++++-- resources/qml/Cura.qml | 24 ---------- resources/qml/Sidebar.qml | 66 +++++++++++++++++--------- 7 files changed, 67 insertions(+), 92 deletions(-) delete mode 100644 cura/Settings/SettingsSidebarView.py diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index e9e521fb49..997ed7782e 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -79,7 +79,6 @@ from cura.Settings.ExtruderStack import ExtruderStack from cura.Sidebar.SidebarController import SidebarController from cura.Sidebar.SidebarControllerProxy import SidebarControllerProxy from cura.Sidebar.SidebarViewModel import SidebarViewModel -from cura.Settings.SettingsSidebarView import SettingsSidebarView from PyQt5.QtCore import QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS from UM.FlameProfiler import pyqtSlot @@ -1319,16 +1318,10 @@ class CuraApplication(QtApplication): def _addProfileWriter(self, profile_writer): pass - ## Create and register the default sidebar component (settings) + ## Set the default sidebar view to "default" 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()) + preferences.addPreference("cura/active_sidebar_view", "default") active_sidebar_view = preferences.getValue("cura/active_sidebar_view") self._sidebar_controller.setActiveSidebarView(active_sidebar_view) diff --git a/cura/Settings/SettingsSidebarView.py b/cura/Settings/SettingsSidebarView.py deleted file mode 100644 index 8b861fbaf6..0000000000 --- a/cura/Settings/SettingsSidebarView.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) 2017 Ultimaker B.V. -from PyQt5.QtCore import QObject - -from UM.i18n import i18nCatalog - -from cura.Sidebar.SidebarView import SidebarView -i18n_catalog = i18nCatalog("cura") - -class SettingsSidebarView(QObject, SidebarView): - - def __init__(self, parent = None): - super().__init__(parent) - - ## As the default sidebar is not a plugin, we have a get plugin ID method to allow the sidebar view model to get the needed data. - def getPluginId(self): - return "default" - - ## As the default sidebar is not a plugin, we have a add meta data method here to allow the sidebar view model to get the needed data. - def getMetaData(self): - return { - "sidebar_view": { - "name": i18n_catalog.i18nc("@item:inmenu", "Print settings"), - "weight": 0 - } - } - - def getComponent(self): - return None diff --git a/cura/Sidebar/SidebarController.py b/cura/Sidebar/SidebarController.py index 420f00a489..a40ee07157 100644 --- a/cura/Sidebar/SidebarController.py +++ b/cura/Sidebar/SidebarController.py @@ -10,7 +10,7 @@ class SidebarController: def __init__(self, application): self._application = application - self._sidebar_views = {} + self._sidebar_views = {"default": {}} # default is needed for the default settings sidebar self._active_sidebar_view = None # Register the sidebar_view plugin type so plugins can expose custom sidebar views. @@ -51,6 +51,13 @@ class SidebarController: def getActiveSidebarView(self): return self._active_sidebar_view + ## Get the ID of the active sidebar view. + def getActiveSidebarViewId(self): + if self._active_sidebar_view: + if hasattr(self._active_sidebar_view, "getPluginId"): + return self._active_sidebar_view.getPluginId() + return "default" + ## Change the active sidebar view to one of the registered views. def setActiveSidebarView(self, sidebar_view_id: str): if sidebar_view_id in self._sidebar_views: diff --git a/cura/Sidebar/SidebarControllerProxy.py b/cura/Sidebar/SidebarControllerProxy.py index c373dc0a0a..a9b23fa08e 100644 --- a/cura/Sidebar/SidebarControllerProxy.py +++ b/cura/Sidebar/SidebarControllerProxy.py @@ -22,10 +22,7 @@ class SidebarControllerProxy(QObject): @pyqtProperty(str, notify = activeSidebarViewChanged) def activeSidebarId(self): - if self._controller.getActiveSidebarView() is not None: - return self._controller.getActiveSidebarView().getPluginId() - else: - return "default" + return self._controller.getActiveSidebarViewId() @pyqtSlot(str) def setActiveSidebarView(self, sidebar_view_id): diff --git a/cura/Sidebar/SidebarViewModel.py b/cura/Sidebar/SidebarViewModel.py index de0aae182c..f6dcb4455b 100644 --- a/cura/Sidebar/SidebarViewModel.py +++ b/cura/Sidebar/SidebarViewModel.py @@ -34,10 +34,18 @@ class SidebarViewModel(ListModel): current_view_id = current_view.getPluginId() for sidebar_view_id, sidebar_view in sidebar_views.items(): - if sidebar_view_id != "default": - sidebar_view_metadata = PluginRegistry.getInstance().getMetaData(sidebar_view_id).get("sidebar_view", {}) - else: - sidebar_view_metadata = sidebar_view.getMetaData().get("sidebar_view", {}) + + # Override fields for default settings sidebar + if sidebar_view_id == "default": + items.append({ + "id": "default", + "name": "Print settings", + "weight": 0, + "active": current_view_id == "default" + }) + continue + + sidebar_view_metadata = PluginRegistry.getInstance().getMetaData(sidebar_view_id).get("sidebar_view", {}) # Skip view modes that are marked as not visible if "visible" in sidebar_view_metadata and not sidebar_view_metadata["visible"]: diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index c08080f276..3e5527c75c 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -395,30 +395,6 @@ UM.MainWindow width: parent.width z: 1 monitoringPrint: base.showPrintMonitor - visible: Cura.SidebarController.activeSidebarId == "default" - } - - // 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 - - 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) - } - } - } } } diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index bed5b4c873..9d0957806d 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -87,25 +87,6 @@ Rectangle } } - SidebarHeader { - id: header - width: parent.width - visible: machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariants - - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() - } - - Rectangle { - id: headerSeparator - width: parent.width - visible: settingsModeSelection.visible && header.visible - height: visible ? UM.Theme.getSize("sidebar_lining").height : 0 - color: UM.Theme.getColor("sidebar_lining") - anchors.top: header.bottom - anchors.topMargin: visible ? UM.Theme.getSize("sidebar_margin").height : 0 - } - onCurrentModeIndexChanged: { UM.Preferences.setValue("cura/active_mode", currentModeIndex); @@ -115,6 +96,24 @@ Rectangle } } + SidebarHeader { + id: header + width: parent.width + visible: (machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariants) && Cura.SidebarController.activeSidebarId == "default" + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() + } + + Rectangle { + id: headerSeparator + width: parent.width + visible: settingsModeSelection.visible && header.visible && Cura.SidebarController.activeSidebarId == "default" + height: visible ? UM.Theme.getSize("sidebar_lining").height : 0 + color: UM.Theme.getColor("sidebar_lining") + anchors.top: header.bottom + anchors.topMargin: visible ? UM.Theme.getSize("sidebar_margin").height : 0 + } + Label { id: settingsModeLabel text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox","Print Setup disabled\nG-code files cannot be modified"); @@ -125,7 +124,7 @@ Rectangle width: Math.floor(parent.width * 0.45) font: UM.Theme.getFont("large") color: UM.Theme.getColor("text") - visible: !monitoringPrint && !hideView + visible: !monitoringPrint && !hideView && Cura.SidebarController.activeSidebarId == "default" } Rectangle { @@ -147,7 +146,7 @@ Rectangle } } anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - visible: !monitoringPrint && !hideSettings && !hideView + visible: !monitoringPrint && !hideSettings && !hideView && Cura.SidebarController.activeSidebarId == "default" Component{ id: wizardDelegate Button { @@ -228,7 +227,7 @@ Rectangle anchors.topMargin: UM.Theme.getSize("sidebar_margin").height anchors.left: base.left anchors.right: base.right - visible: !monitoringPrint && !hideSettings + visible: !monitoringPrint && !hideSettings && Cura.SidebarController.activeSidebarId == "default" delegate: StackViewDelegate { @@ -259,6 +258,29 @@ Rectangle } } + // 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 + + 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) + } + } + } + } + Loader { id: controlItem From 030dc74f3f44c548a4af592bede0c3067114518c Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 5 Dec 2017 12:19:00 +0100 Subject: [PATCH 13/39] Fix now showing any sidebar when in monitor mode --- resources/qml/Cura.qml | 22 +++------------------- resources/qml/Sidebar.qml | 18 +++++++++--------- 2 files changed, 12 insertions(+), 28 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 3e5527c75c..bf67efdba3 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -366,7 +366,7 @@ UM.MainWindow onStopMonitoringPrint: base.showPrintMonitor = false } - Rectangle + Sidebar { id: sidebar @@ -378,24 +378,8 @@ UM.MainWindow } width: UM.Theme.getSize("sidebar").width - - // This is the default sidebar view. - // It is hidden when the active sidebar view ID is not default. - Sidebar - { - id: defaultSidebar - - anchors { - top: parent.top - bottom: parent.bottom - left: parent.left - right: parent.right - } - - width: parent.width - z: 1 - monitoringPrint: base.showPrintMonitor - } + z: 1 + monitoringPrint: base.showPrintMonitor } Rectangle diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index 9d0957806d..4a8a0c6a32 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -87,15 +87,6 @@ Rectangle } } - onCurrentModeIndexChanged: - { - UM.Preferences.setValue("cura/active_mode", currentModeIndex); - if(modesListModel.count > base.currentModeIndex) - { - sidebarContents.push({ "item": modesListModel.get(base.currentModeIndex).item, "replace": true }); - } - } - SidebarHeader { id: header width: parent.width @@ -114,6 +105,15 @@ Rectangle anchors.topMargin: visible ? UM.Theme.getSize("sidebar_margin").height : 0 } + onCurrentModeIndexChanged: + { + UM.Preferences.setValue("cura/active_mode", currentModeIndex); + if(modesListModel.count > base.currentModeIndex) + { + sidebarContents.push({ "item": modesListModel.get(base.currentModeIndex).item, "replace": true }); + } + } + Label { id: settingsModeLabel text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox","Print Setup disabled\nG-code files cannot be modified"); From a6a360284017592c284efa2149f888411fb936d8 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 5 Dec 2017 12:27:13 +0100 Subject: [PATCH 14/39] View default view selected in view menu --- cura/Sidebar/SidebarControllerProxy.py | 2 +- cura/Sidebar/SidebarViewModel.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cura/Sidebar/SidebarControllerProxy.py b/cura/Sidebar/SidebarControllerProxy.py index a9b23fa08e..00a898301a 100644 --- a/cura/Sidebar/SidebarControllerProxy.py +++ b/cura/Sidebar/SidebarControllerProxy.py @@ -38,4 +38,4 @@ class SidebarControllerProxy(QObject): return self._controller.getSidebarView(sidebar_id).getComponentPath() def _onActiveSidebarViewChanged(self): - self.activeSidebarViewChanged.emit() \ No newline at end of file + self.activeSidebarViewChanged.emit() diff --git a/cura/Sidebar/SidebarViewModel.py b/cura/Sidebar/SidebarViewModel.py index f6dcb4455b..8000d87cc5 100644 --- a/cura/Sidebar/SidebarViewModel.py +++ b/cura/Sidebar/SidebarViewModel.py @@ -26,11 +26,11 @@ 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 + current_view_id = "default" sidebar_views = self._controller.getAllSidebarViews() current_view = self._controller.getActiveSidebarView() - if current_view: + if current_view and hasattr(current_view, "getPluginId"): current_view_id = current_view.getPluginId() for sidebar_view_id, sidebar_view in sidebar_views.items(): @@ -39,9 +39,9 @@ class SidebarViewModel(ListModel): if sidebar_view_id == "default": items.append({ "id": "default", - "name": "Print settings", - "weight": 0, - "active": current_view_id == "default" + "name": "Print settings sidebar", + "active": sidebar_view_id == current_view_id, + "weight": 0 }) continue @@ -52,7 +52,7 @@ class SidebarViewModel(ListModel): continue name = sidebar_view_metadata.get("name", sidebar_view_id) - weight = sidebar_view_metadata.get("weight", 0) + weight = sidebar_view_metadata.get("weight", 1) items.append({ "id": sidebar_view_id, From 51405ca0f770fbf9e631fa16f43ae5c1e52140f9 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 5 Dec 2017 13:29:04 +0100 Subject: [PATCH 15/39] tmp --- resources/qml/Sidebar.qml | 9 ---- resources/qml/Sidebar/PrinterOutput.qml | 65 +++++++++++++++++++++++++ resources/qml/Sidebar/Settings.qml | 0 3 files changed, 65 insertions(+), 9 deletions(-) create mode 100644 resources/qml/Sidebar/PrinterOutput.qml create mode 100644 resources/qml/Sidebar/Settings.qml diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index 4a8a0c6a32..d5af3e939b 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -105,15 +105,6 @@ Rectangle anchors.topMargin: visible ? UM.Theme.getSize("sidebar_margin").height : 0 } - onCurrentModeIndexChanged: - { - UM.Preferences.setValue("cura/active_mode", currentModeIndex); - if(modesListModel.count > base.currentModeIndex) - { - sidebarContents.push({ "item": modesListModel.get(base.currentModeIndex).item, "replace": true }); - } - } - Label { id: settingsModeLabel text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox","Print Setup disabled\nG-code files cannot be modified"); diff --git a/resources/qml/Sidebar/PrinterOutput.qml b/resources/qml/Sidebar/PrinterOutput.qml new file mode 100644 index 0000000000..5506944154 --- /dev/null +++ b/resources/qml/Sidebar/PrinterOutput.qml @@ -0,0 +1,65 @@ +// Copyright (c) 2017 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Controls.Styles 1.1 +import QtQuick.Layouts 1.1 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +Item +{ + id: printerOutputSection + + UM.I18nCatalog { + id: catalog + name: "cura" + } + + color: UM.Theme.getColor("sidebar") + + // status + property bool isMonitoring: false + + // printer data + property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 + property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands + property var connectedPrinter: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null + property int backendState: UM.Backend.state + + // print job data + property variant printDuration: PrintInformation.currentPrintTime + property variant printMaterialLengths: PrintInformation.materialLengths + property variant printMaterialWeights: PrintInformation.materialWeights + property variant printMaterialCosts: PrintInformation.materialCosts + property variant printMaterialNames: PrintInformation.materialNames + + // helper function for padding pretty time + function strPadLeft(string, pad, length) { + return (new Array(length + 1).join(pad) + string).slice(-length); + } + + // convert a timestamp to a human readable pretty time + function getPrettyTime (time) { + var hours = Math.floor(time / 3600) + time -= hours * 3600 + var minutes = Math.floor(time / 60) + time -= minutes * 60 + var seconds = Math.floor(time) + var finalTime = strPadLeft(hours, "0", 2) + ":" + strPadLeft(minutes, "0", 2) + ":" + strPadLeft(seconds, "0", 2) + return finalTime + } + + // TODO: change name of this component + MonitorButton + { + id: monitorButton + implicitWidth: base.width + anchors.top: footerSeparator.bottom + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + anchors.bottom: parent.bottom + visible: monitoringPrint + } +} diff --git a/resources/qml/Sidebar/Settings.qml b/resources/qml/Sidebar/Settings.qml new file mode 100644 index 0000000000..e69de29bb2 From ab5449b01e75bfa2e2834b2efcd7875707d10dcd Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 09:10:19 +0100 Subject: [PATCH 16/39] Add CuraStage --- cura/Stages/CuraStage.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 cura/Stages/CuraStage.py diff --git a/cura/Stages/CuraStage.py b/cura/Stages/CuraStage.py new file mode 100644 index 0000000000..77d0deb21f --- /dev/null +++ b/cura/Stages/CuraStage.py @@ -0,0 +1,12 @@ +from UM.Stage import Stage + +class CuraStage(Stage): + + def __init__(self): + super().__init__() + + def getMainView(self): + return None + + def getSidebarView(self): + return None From 44c66e2ad269e6a01b8fd731c3a7554995ec8222 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 09:26:37 +0100 Subject: [PATCH 17/39] Get named view in CuraStage --- cura/Stages/CuraStage.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/Stages/CuraStage.py b/cura/Stages/CuraStage.py index 77d0deb21f..52793134cf 100644 --- a/cura/Stages/CuraStage.py +++ b/cura/Stages/CuraStage.py @@ -6,7 +6,7 @@ class CuraStage(Stage): super().__init__() def getMainView(self): - return None + return self.getView("main") def getSidebarView(self): - return None + return self.getView("sidebar") From 840e80a1fb99ed5b1890a3864e4c19fd2ebb8b74 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 09:29:09 +0100 Subject: [PATCH 18/39] Add missing init --- cura/Stages/__init__.py | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 cura/Stages/__init__.py diff --git a/cura/Stages/__init__.py b/cura/Stages/__init__.py new file mode 100644 index 0000000000..2977645166 --- /dev/null +++ b/cura/Stages/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2017 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. From 84ba486d57bf953b169174b7c6161cc9eb17fe53 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 09:34:09 +0100 Subject: [PATCH 19/39] Add plugins for prepare and monitor stages --- cura/Stages/CuraStage.py | 3 +++ plugins/MonitorStage/__init__.py | 0 plugins/PrepareStage/PrepareStage.py | 11 +++++++++++ plugins/PrepareStage/__init__.py | 18 ++++++++++++++++++ 4 files changed, 32 insertions(+) create mode 100644 plugins/MonitorStage/__init__.py create mode 100644 plugins/PrepareStage/PrepareStage.py create mode 100644 plugins/PrepareStage/__init__.py diff --git a/cura/Stages/CuraStage.py b/cura/Stages/CuraStage.py index 52793134cf..d4def4c00e 100644 --- a/cura/Stages/CuraStage.py +++ b/cura/Stages/CuraStage.py @@ -1,3 +1,6 @@ +# Copyright (c) 2017 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + from UM.Stage import Stage class CuraStage(Stage): diff --git a/plugins/MonitorStage/__init__.py b/plugins/MonitorStage/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/PrepareStage/PrepareStage.py b/plugins/PrepareStage/PrepareStage.py new file mode 100644 index 0000000000..bbd2275d2e --- /dev/null +++ b/plugins/PrepareStage/PrepareStage.py @@ -0,0 +1,11 @@ +# Copyright (c) 2017 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from cura.Stages.CuraStage import CuraStage + + +## Stage for preparing model (slicing). +class PrepareStage(CuraStage): + + def __init__(self): + super().__init__() diff --git a/plugins/PrepareStage/__init__.py b/plugins/PrepareStage/__init__.py new file mode 100644 index 0000000000..d74cf27e39 --- /dev/null +++ b/plugins/PrepareStage/__init__.py @@ -0,0 +1,18 @@ +# Copyright (c) 2017 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from . import PrepareStage + +from UM.i18n import i18nCatalog +i18n_catalog = i18nCatalog("cura") + +def getMetaData(): + return { + "stage": { + "name": i18n_catalog.i18nc("@item:inmenu", "Prepare"), + "weight": 0 + } + } + +def register(app): + return { "stage": PrepareStage.PrepareStage() } From a5b99bd8624c8a2db866ab37c1f31563178993c8 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 09:36:09 +0100 Subject: [PATCH 20/39] Scaffold monitor stage --- plugins/MonitorStage/MonitorStage.py | 11 +++++++++++ plugins/MonitorStage/__init__.py | 19 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 plugins/MonitorStage/MonitorStage.py diff --git a/plugins/MonitorStage/MonitorStage.py b/plugins/MonitorStage/MonitorStage.py new file mode 100644 index 0000000000..e9d8f75645 --- /dev/null +++ b/plugins/MonitorStage/MonitorStage.py @@ -0,0 +1,11 @@ +# Copyright (c) 2017 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from cura.Stages.CuraStage import CuraStage + + +## Stage for monitoring a 3D printing while it's printing. +class MonitorStage(CuraStage): + + def __init__(self): + super().__init__() diff --git a/plugins/MonitorStage/__init__.py b/plugins/MonitorStage/__init__.py index e69de29bb2..c1f7cf0f6b 100644 --- a/plugins/MonitorStage/__init__.py +++ b/plugins/MonitorStage/__init__.py @@ -0,0 +1,19 @@ +# Copyright (c) 2017 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from . import MonitorStage + +from UM.i18n import i18nCatalog +i18n_catalog = i18nCatalog("cura") + +def getMetaData(): + return { + "stage": { + "name": i18n_catalog.i18nc("@item:inmenu", "Monitor"), + "weight": 0, + "icon": "" + } + } + +def register(app): + return { "stage": MonitorStage.MonitorStage() } From f1b9a17611a379ff8a15d701b0fa748573ef35fb Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 09:44:30 +0100 Subject: [PATCH 21/39] Remove sidebar controller implementation in favor of stages --- cura/CuraApplication.py | 26 ---- resources/qml/Sidebar.qml | 33 +---- resources/qml/Sidebar/PrinterOutput.qml | 65 --------- resources/qml/Sidebar/Settings.qml | 0 resources/qml/Topbar.qml | 174 +++++++++++++----------- 5 files changed, 101 insertions(+), 197 deletions(-) delete mode 100644 resources/qml/Sidebar/PrinterOutput.qml delete mode 100644 resources/qml/Sidebar/Settings.qml diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 997ed7782e..5acaecbdd5 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -76,10 +76,6 @@ from cura.Settings.ContainerManager import ContainerManager from cura.Settings.GlobalStack import GlobalStack from cura.Settings.ExtruderStack import ExtruderStack -from cura.Sidebar.SidebarController import SidebarController -from cura.Sidebar.SidebarControllerProxy import SidebarControllerProxy -from cura.Sidebar.SidebarViewModel import SidebarViewModel - from PyQt5.QtCore import QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS from UM.FlameProfiler import pyqtSlot from PyQt5.QtGui import QColor, QIcon @@ -200,10 +196,6 @@ 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 = [] @@ -727,10 +719,6 @@ 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() @@ -743,11 +731,6 @@ class CuraApplication(QtApplication): self.exec_() - ## Get the SidebarController of this application. - # \returns SidebarControllers \type{SidebarController} - def getSidebarController(self) -> SidebarController: - return self._sidebar_controller - def getMachineManager(self, *args): if self._machine_manager is None: self._machine_manager = MachineManager.createMachineManager() @@ -807,7 +790,6 @@ class CuraApplication(QtApplication): qmlRegisterUncreatableType(CuraApplication, "Cura", 1, 0, "ResourceTypes", "Just an Enum type") - qmlRegisterType(SidebarViewModel, "Cura", 1, 0, "SidebarViewModel") qmlRegisterType(ExtrudersModel, "Cura", 1, 0, "ExtrudersModel") qmlRegisterType(ContainerSettingsModel, "Cura", 1, 0, "ContainerSettingsModel") qmlRegisterSingletonType(ProfilesModel, "Cura", 1, 0, "ProfilesModel", ProfilesModel.createProfilesModel) @@ -819,7 +801,6 @@ class CuraApplication(QtApplication): qmlRegisterType(MachineNameValidator, "Cura", 1, 0, "MachineNameValidator") qmlRegisterType(UserChangesModel, "Cura", 1, 1, "UserChangesModel") qmlRegisterSingletonType(ContainerManager, "Cura", 1, 0, "ContainerManager", ContainerManager.createContainerManager) - qmlRegisterSingletonType(SidebarControllerProxy, "Cura", 1, 0, "SidebarController", SidebarControllerProxy.createSidebarControllerProxy) # As of Qt5.7, it is necessary to get rid of any ".." in the path for the singleton to work. actions_url = QUrl.fromLocalFile(os.path.abspath(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Actions.qml"))) @@ -1318,13 +1299,6 @@ class CuraApplication(QtApplication): def _addProfileWriter(self, profile_writer): pass - ## Set the default sidebar view to "default" - def _setDefaultSidebarView(self): - preferences = Preferences.getInstance() - preferences.addPreference("cura/active_sidebar_view", "default") - 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/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index d5af3e939b..3bfc803a78 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -90,7 +90,7 @@ Rectangle SidebarHeader { id: header width: parent.width - visible: (machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariants) && Cura.SidebarController.activeSidebarId == "default" + visible: machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariantst onShowTooltip: base.showTooltip(item, location, text) onHideTooltip: base.hideTooltip() } @@ -98,7 +98,7 @@ Rectangle Rectangle { id: headerSeparator width: parent.width - visible: settingsModeSelection.visible && header.visible && Cura.SidebarController.activeSidebarId == "default" + visible: settingsModeSelection.visible && header.visible height: visible ? UM.Theme.getSize("sidebar_lining").height : 0 color: UM.Theme.getColor("sidebar_lining") anchors.top: header.bottom @@ -115,7 +115,7 @@ Rectangle width: Math.floor(parent.width * 0.45) font: UM.Theme.getFont("large") color: UM.Theme.getColor("text") - visible: !monitoringPrint && !hideView && Cura.SidebarController.activeSidebarId == "default" + visible: !monitoringPrint && !hideView } Rectangle { @@ -137,7 +137,7 @@ Rectangle } } anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - visible: !monitoringPrint && !hideSettings && !hideView && Cura.SidebarController.activeSidebarId == "default" + visible: !monitoringPrint && !hideSettings && !hideView Component{ id: wizardDelegate Button { @@ -218,7 +218,7 @@ Rectangle anchors.topMargin: UM.Theme.getSize("sidebar_margin").height anchors.left: base.left anchors.right: base.right - visible: !monitoringPrint && !hideSettings && Cura.SidebarController.activeSidebarId == "default" + visible: !monitoringPrint && !hideSettings delegate: StackViewDelegate { @@ -249,29 +249,6 @@ Rectangle } } - // 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 - - 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) - } - } - } - } - Loader { id: controlItem diff --git a/resources/qml/Sidebar/PrinterOutput.qml b/resources/qml/Sidebar/PrinterOutput.qml deleted file mode 100644 index 5506944154..0000000000 --- a/resources/qml/Sidebar/PrinterOutput.qml +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) 2017 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.2 -import QtQuick.Controls 1.1 -import QtQuick.Controls.Styles 1.1 -import QtQuick.Layouts 1.1 - -import UM 1.2 as UM -import Cura 1.0 as Cura - -Item -{ - id: printerOutputSection - - UM.I18nCatalog { - id: catalog - name: "cura" - } - - color: UM.Theme.getColor("sidebar") - - // status - property bool isMonitoring: false - - // printer data - property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 - property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands - property var connectedPrinter: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null - property int backendState: UM.Backend.state - - // print job data - property variant printDuration: PrintInformation.currentPrintTime - property variant printMaterialLengths: PrintInformation.materialLengths - property variant printMaterialWeights: PrintInformation.materialWeights - property variant printMaterialCosts: PrintInformation.materialCosts - property variant printMaterialNames: PrintInformation.materialNames - - // helper function for padding pretty time - function strPadLeft(string, pad, length) { - return (new Array(length + 1).join(pad) + string).slice(-length); - } - - // convert a timestamp to a human readable pretty time - function getPrettyTime (time) { - var hours = Math.floor(time / 3600) - time -= hours * 3600 - var minutes = Math.floor(time / 60) - time -= minutes * 60 - var seconds = Math.floor(time) - var finalTime = strPadLeft(hours, "0", 2) + ":" + strPadLeft(minutes, "0", 2) + ":" + strPadLeft(seconds, "0", 2) - return finalTime - } - - // TODO: change name of this component - MonitorButton - { - id: monitorButton - implicitWidth: base.width - anchors.top: footerSeparator.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - anchors.bottom: parent.bottom - visible: monitoringPrint - } -} diff --git a/resources/qml/Sidebar/Settings.qml b/resources/qml/Sidebar/Settings.qml deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/resources/qml/Topbar.qml b/resources/qml/Topbar.qml index db9abafb5c..f2d053ce70 100644 --- a/resources/qml/Topbar.qml +++ b/resources/qml/Topbar.qml @@ -67,91 +67,109 @@ Rectangle anchors.rightMargin: UM.Theme.getSize("default_margin").width spacing: UM.Theme.getSize("default_margin").width - Button + // The topbar is dynamically filled with all available stages + Repeater { - id: showSettings - height: UM.Theme.getSize("sidebar_header").height - text: catalog.i18nc("@title:tab", "Prepare") - checkable: true - checked: isChecked() - exclusiveGroup: sidebarHeaderBarGroup - style: UM.Theme.styles.topbar_header_tab + id: stagesMenu - // We use a Qt.binding to re-bind the checkbox state after manually setting it - // https://stackoverflow.com/questions/38798450/qt-5-7-qml-why-are-my-checkbox-property-bindings-disappearing - onClicked: { - base.stopMonitoringPrint() - checked = Qt.binding(isChecked) - } + model: UM.StageModel{} - function isChecked () { - return !base.monitoringPrint - } - - property color overlayColor: "transparent" - property string overlayIconSource: "" - } - - Button - { - id: showMonitor - width: UM.Theme.getSize("topbar_button").width - height: UM.Theme.getSize("sidebar_header").height - text: catalog.i18nc("@title:tab", "Monitor") - checkable: true - checked: isChecked() - exclusiveGroup: sidebarHeaderBarGroup - style: UM.Theme.styles.topbar_header_tab_no_overlay - - // We use a Qt.binding to re-bind the checkbox state after manually setting it - // https://stackoverflow.com/questions/38798450/qt-5-7-qml-why-are-my-checkbox-property-bindings-disappearing - onClicked: { - base.startMonitoringPrint() - checked = Qt.binding(isChecked) - } - - function isChecked () { - return base.monitoringPrint - } - - property string iconSource: + delegate: Button { - if (!printerConnected) - { - return UM.Theme.getIcon("tab_status_unknown"); - } - else if (!printerAcceptsCommands) - { - return UM.Theme.getIcon("tab_status_unknown"); - } - - if (Cura.MachineManager.printerOutputDevices[0].printerState == "maintenance") - { - return UM.Theme.getIcon("tab_status_busy"); - } - - switch (Cura.MachineManager.printerOutputDevices[0].jobState) - { - case "printing": - case "pre_print": - case "pausing": - case "resuming": - return UM.Theme.getIcon("tab_status_busy"); - case "wait_cleanup": - return UM.Theme.getIcon("tab_status_finished"); - case "ready": - case "": - return UM.Theme.getIcon("tab_status_connected") - case "paused": - return UM.Theme.getIcon("tab_status_paused") - case "error": - return UM.Theme.getIcon("tab_status_stopped") - default: - return UM.Theme.getIcon("tab_status_unknown") - } + text: model.name + checkable: true + checked: model.active + exclusiveGroup: sidebarHeaderBarGroup + style: UM.Theme.styles.topbar_header_tab + height: UM.Theme.getSize("sidebar_header").height } } +// Button +// { +// id: showSettings +// height: UM.Theme.getSize("sidebar_header").height +// text: catalog.i18nc("@title:tab", "Prepare") +// checkable: true +// checked: isChecked() +// exclusiveGroup: sidebarHeaderBarGroup +// style: UM.Theme.styles.topbar_header_tab +// +// // We use a Qt.binding to re-bind the checkbox state after manually setting it +// // https://stackoverflow.com/questions/38798450/qt-5-7-qml-why-are-my-checkbox-property-bindings-disappearing +// onClicked: { +// base.stopMonitoringPrint() +// checked = Qt.binding(isChecked) +// } +// +// function isChecked () { +// return !base.monitoringPrint +// } +// +// property color overlayColor: "transparent" +// property string overlayIconSource: "" +// } + +// Button +// { +// id: showMonitor +// width: UM.Theme.getSize("topbar_button").width +// height: UM.Theme.getSize("sidebar_header").height +// text: catalog.i18nc("@title:tab", "Monitor") +// checkable: true +// checked: isChecked() +// exclusiveGroup: sidebarHeaderBarGroup +// style: UM.Theme.styles.topbar_header_tab_no_overlay +// +// // We use a Qt.binding to re-bind the checkbox state after manually setting it +// // https://stackoverflow.com/questions/38798450/qt-5-7-qml-why-are-my-checkbox-property-bindings-disappearing +// onClicked: { +// base.startMonitoringPrint() +// checked = Qt.binding(isChecked) +// } +// +// function isChecked () { +// return base.monitoringPrint +// } +// +// property string iconSource: +// { +// if (!printerConnected) +// { +// return UM.Theme.getIcon("tab_status_unknown"); +// } +// else if (!printerAcceptsCommands) +// { +// return UM.Theme.getIcon("tab_status_unknown"); +// } +// +// if (Cura.MachineManager.printerOutputDevices[0].printerState == "maintenance") +// { +// return UM.Theme.getIcon("tab_status_busy"); +// } +// +// switch (Cura.MachineManager.printerOutputDevices[0].jobState) +// { +// case "printing": +// case "pre_print": +// case "pausing": +// case "resuming": +// return UM.Theme.getIcon("tab_status_busy"); +// case "wait_cleanup": +// return UM.Theme.getIcon("tab_status_finished"); +// case "ready": +// case "": +// return UM.Theme.getIcon("tab_status_connected") +// case "paused": +// return UM.Theme.getIcon("tab_status_paused") +// case "error": +// return UM.Theme.getIcon("tab_status_stopped") +// default: +// return UM.Theme.getIcon("tab_status_unknown") +// } +// } +// } + ExclusiveGroup { id: sidebarHeaderBarGroup } } From 9b4b6b2eaeb986c7592f59cea6e8c8bac1806c0f Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 09:44:59 +0100 Subject: [PATCH 22/39] Remove sidebar views from views menu --- resources/qml/Menus/ViewMenu.qml | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/resources/qml/Menus/ViewMenu.qml b/resources/qml/Menus/ViewMenu.qml index b5e4c5765f..a610bb0009 100644 --- a/resources/qml/Menus/ViewMenu.qml +++ b/resources/qml/Menus/ViewMenu.qml @@ -30,25 +30,6 @@ Menu } ExclusiveGroup { id: group } - MenuSeparator {} - - // sidebar views - Instantiator - { - model: Cura.SidebarViewModel{} - MenuItem - { - text: model.name - checkable: true - checked: model.active - exclusiveGroup: sidebarGroup - onTriggered: Cura.SidebarController.setActiveSidebarView(model.id) - } - onObjectAdded: menu.insertItem(index, object) - onObjectRemoved: menu.removeItem(object) - } - ExclusiveGroup { id: sidebarGroup } - MenuSeparator {} MenuItem { action: Cura.Actions.homeCamera; } } From 0e1c9146cfa62209d09b71424bc8831e5f2a6ab6 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 09:58:50 +0100 Subject: [PATCH 23/39] Implement stage model in top bar --- cura/CuraApplication.py | 7 +++++-- plugins/MonitorStage/__init__.py | 2 +- plugins/MonitorStage/plugin.json | 8 ++++++++ plugins/PrepareStage/plugin.json | 8 ++++++++ resources/qml/Topbar.qml | 1 + 5 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 plugins/MonitorStage/plugin.json create mode 100644 plugins/PrepareStage/plugin.json diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 5acaecbdd5..c83ed04c82 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -229,7 +229,9 @@ class CuraApplication(QtApplication): "TranslateTool", "FileLogger", "XmlMaterialProfile", - "PluginBrowser" + "PluginBrowser", + "PrepareStage", + "MonitorStage" ]) self._physics = None self._volume = None @@ -668,13 +670,14 @@ class CuraApplication(QtApplication): controller = self.getController() + controller.setActiveStage("PrepareStage") controller.setActiveView("SolidView") controller.setCameraTool("CameraTool") controller.setSelectionTool("SelectionTool") t = controller.getTool("TranslateTool") if t: - t.setEnabledAxis([ToolHandle.XAxis, ToolHandle.YAxis,ToolHandle.ZAxis]) + t.setEnabledAxis([ToolHandle.XAxis, ToolHandle.YAxis, ToolHandle.ZAxis]) Selection.selectionChanged.connect(self.onSelectionChanged) diff --git a/plugins/MonitorStage/__init__.py b/plugins/MonitorStage/__init__.py index c1f7cf0f6b..e18ecba7f9 100644 --- a/plugins/MonitorStage/__init__.py +++ b/plugins/MonitorStage/__init__.py @@ -10,7 +10,7 @@ def getMetaData(): return { "stage": { "name": i18n_catalog.i18nc("@item:inmenu", "Monitor"), - "weight": 0, + "weight": 1, "icon": "" } } diff --git a/plugins/MonitorStage/plugin.json b/plugins/MonitorStage/plugin.json new file mode 100644 index 0000000000..cb3f55a80d --- /dev/null +++ b/plugins/MonitorStage/plugin.json @@ -0,0 +1,8 @@ +{ + "name": "Monitor Stage", + "author": "Ultimaker B.V.", + "version": "1.0.0", + "description": "Provides a monitor stage in Cura.", + "api": 4, + "i18n-catalog": "cura" +} \ No newline at end of file diff --git a/plugins/PrepareStage/plugin.json b/plugins/PrepareStage/plugin.json new file mode 100644 index 0000000000..4fd55e955e --- /dev/null +++ b/plugins/PrepareStage/plugin.json @@ -0,0 +1,8 @@ +{ + "name": "Prepare Stage", + "author": "Ultimaker B.V.", + "version": "1.0.0", + "description": "Provides a prepare stage in Cura.", + "api": 4, + "i18n-catalog": "cura" +} \ No newline at end of file diff --git a/resources/qml/Topbar.qml b/resources/qml/Topbar.qml index f2d053ce70..50e90a9a37 100644 --- a/resources/qml/Topbar.qml +++ b/resources/qml/Topbar.qml @@ -82,6 +82,7 @@ Rectangle exclusiveGroup: sidebarHeaderBarGroup style: UM.Theme.styles.topbar_header_tab height: UM.Theme.getSize("sidebar_header").height + onClicked: UM.Controller.setActiveStage(model.id) } } From a57a5aab6b6fcd44a3e1dd65988600e899e1c0ff Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 12:23:41 +0100 Subject: [PATCH 24/39] Make sidebar view working for active stage --- cura/CuraApplication.py | 1 + cura/Sidebar/SidebarController.py | 66 -- cura/Sidebar/SidebarControllerProxy.py | 41 - cura/Sidebar/SidebarView.py | 35 - cura/Sidebar/SidebarViewModel.py | 66 -- cura/Sidebar/__init__.py | 0 cura/Stages/CuraStage.py | 11 +- plugins/MonitorStage/MonitorStage.py | 6 +- plugins/MonitorStage/__init__.py | 7 +- plugins/PrepareStage/PrepareStage.py | 10 +- plugins/PrepareStage/__init__.py | 4 +- resources/qml/Cura.qml | 6 +- resources/qml/Sidebar.qml | 1133 ++++++++++++------------ resources/qml/Topbar.qml | 31 +- 14 files changed, 605 insertions(+), 812 deletions(-) delete mode 100644 cura/Sidebar/SidebarController.py delete mode 100644 cura/Sidebar/SidebarControllerProxy.py delete mode 100644 cura/Sidebar/SidebarView.py delete mode 100644 cura/Sidebar/SidebarViewModel.py delete mode 100644 cura/Sidebar/__init__.py diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index c83ed04c82..eec6c7f503 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -721,6 +721,7 @@ class CuraApplication(QtApplication): run_headless = self.getCommandLineOption("headless", False) if not run_headless: self.initializeEngine() + controller.setActiveStage("PrepareStage") if run_headless or self._engine.rootObjects: self.closeSplash() diff --git a/cura/Sidebar/SidebarController.py b/cura/Sidebar/SidebarController.py deleted file mode 100644 index a40ee07157..0000000000 --- a/cura/Sidebar/SidebarController.py +++ /dev/null @@ -1,66 +0,0 @@ -# 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 - -# The sidebar controller manages available sidebar components and decides which one to display. -# The cura.qml file uses this controller to repeat over the sidebars and show the active index. -class SidebarController: - - def __init__(self, application): - self._application = application - self._sidebar_views = {"default": {}} # default is needed for the default settings sidebar - self._active_sidebar_view = None - - # Register the sidebar_view plugin type so plugins can expose custom sidebar views. - PluginRegistry.addType("sidebar_view", self.addSidebarView) - - ## Emitted when the list of views changes. - sidebarViewsChanged = Signal() - - ## Emitted when the active view changes. - activeSidebarViewChanged = Signal() - - ## Get the active application instance. - def getApplication(self): - return self._application - - ## Get all sidebar views registered in this controller. - def getAllSidebarViews(self): - return self._sidebar_views - - ## Add a sidebar view to the registry. - # It get's a unique name based on the plugin ID. - def addSidebarView(self, sidebar_view): - sidebar_view_id = sidebar_view.getPluginId() - if sidebar_view_id not in self._sidebar_views: - self._sidebar_views[sidebar_view_id] = sidebar_view - self.sidebarViewsChanged.emit() - - ## Get a registered sidebar view by name. - # The name is the ID of the plugin that registered the view. - def getSidebarView(self, name: str): - try: - return self._sidebar_views[name] - except KeyError: - Logger.log("e", "Unable to find %s in sidebar view list", name) - return None - - ## Get the active sidebar view. - def getActiveSidebarView(self): - return self._active_sidebar_view - - ## Get the ID of the active sidebar view. - def getActiveSidebarViewId(self): - if self._active_sidebar_view: - if hasattr(self._active_sidebar_view, "getPluginId"): - return self._active_sidebar_view.getPluginId() - return "default" - - ## Change the active sidebar view to one of the registered views. - def setActiveSidebarView(self, sidebar_view_id: str): - 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 deleted file mode 100644 index 00a898301a..0000000000 --- a/cura/Sidebar/SidebarControllerProxy.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) 2017 Ultimaker B.V. -from PyQt5.QtCore import QObject, pyqtSlot, QUrl, pyqtProperty, pyqtSignal -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._onActiveSidebarViewChanged) - - activeSidebarViewChanged = pyqtSignal() - - @classmethod - def createSidebarControllerProxy(self, engine, script_engine): - return SidebarControllerProxy() - - @pyqtProperty(str, notify = activeSidebarViewChanged) - def activeSidebarId(self): - return self._controller.getActiveSidebarViewId() - - @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(str, result = QObject) - def getSidebarComponent(self, sidebar_id): - return self._controller.getSidebarView(sidebar_id).getComponent() - - @pyqtSlot(str, result = QUrl) - def getSidebarComponentPath(self, sidebar_id): - return self._controller.getSidebarView(sidebar_id).getComponentPath() - - def _onActiveSidebarViewChanged(self): - self.activeSidebarViewChanged.emit() diff --git a/cura/Sidebar/SidebarView.py b/cura/Sidebar/SidebarView.py deleted file mode 100644 index d5c20e0858..0000000000 --- a/cura/Sidebar/SidebarView.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) 2017 Ultimaker B.V. -import os.path - -from UM.Application import Application -from UM.Logger import Logger -from UM.PluginObject import PluginObject -from UM.PluginRegistry import PluginRegistry - -# Abstract class for sidebar view objects. -# By default the sidebar is Cura's settings, slicing and printing overview. -# The last plugin to claim the sidebar QML target will be displayed. -class SidebarView(PluginObject): - - def __init__(self): - 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 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 "" diff --git a/cura/Sidebar/SidebarViewModel.py b/cura/Sidebar/SidebarViewModel.py deleted file mode 100644 index 8000d87cc5..0000000000 --- a/cura/Sidebar/SidebarViewModel.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright (c) 2017 Ultimaker B.V. -from PyQt5.QtCore import Qt -from UM.Qt.ListModel import ListModel -from UM.Application import Application -from UM.PluginRegistry import PluginRegistry - -## The SidebarViewModel is the default sidebar view in Cura with all the print settings and print button. -class SidebarViewModel(ListModel): - IdRole = Qt.UserRole + 1 - NameRole = Qt.UserRole + 2 - ActiveRole = Qt.UserRole + 3 - - def __init__(self, parent = None): - super().__init__(parent) - - # connect views changed signals - self._controller = Application.getInstance().getSidebarController() - self._controller.sidebarViewsChanged.connect(self._onSidebarViewsChanged) - self._controller.activeSidebarViewChanged.connect(self._onSidebarViewsChanged) - - # register Qt list roles - self.addRoleName(self.IdRole, "id") - self.addRoleName(self.NameRole, "name") - self.addRoleName(self.ActiveRole, "active") - - ## Update the model when new views are added or another view is made the active view. - def _onSidebarViewsChanged(self): - items = [] - current_view_id = "default" - - sidebar_views = self._controller.getAllSidebarViews() - current_view = self._controller.getActiveSidebarView() - if current_view and hasattr(current_view, "getPluginId"): - current_view_id = current_view.getPluginId() - - for sidebar_view_id, sidebar_view in sidebar_views.items(): - - # Override fields for default settings sidebar - if sidebar_view_id == "default": - items.append({ - "id": "default", - "name": "Print settings sidebar", - "active": sidebar_view_id == current_view_id, - "weight": 0 - }) - continue - - sidebar_view_metadata = PluginRegistry.getInstance().getMetaData(sidebar_view_id).get("sidebar_view", {}) - - # Skip view modes that are marked as not visible - if "visible" in sidebar_view_metadata and not sidebar_view_metadata["visible"]: - continue - - name = sidebar_view_metadata.get("name", sidebar_view_id) - weight = sidebar_view_metadata.get("weight", 1) - - items.append({ - "id": sidebar_view_id, - "name": name, - "active": sidebar_view_id == current_view_id, - "weight": weight - }) - - # Sort the views by weight - items.sort(key=lambda t: t["weight"]) - self.setItems(items) diff --git a/cura/Sidebar/__init__.py b/cura/Sidebar/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/cura/Stages/CuraStage.py b/cura/Stages/CuraStage.py index d4def4c00e..6d4e56473d 100644 --- a/cura/Stages/CuraStage.py +++ b/cura/Stages/CuraStage.py @@ -1,5 +1,6 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from PyQt5.QtCore import pyqtProperty, QObject from UM.Stage import Stage @@ -8,8 +9,10 @@ class CuraStage(Stage): def __init__(self): super().__init__() - def getMainView(self): - return self.getView("main") + @pyqtProperty(QObject, constant = True) + def mainComponent(self): + return self.getDisplayComponent("main") - def getSidebarView(self): - return self.getView("sidebar") + @pyqtProperty(QObject, constant = True) + def sidebarComponent(self): + return self.getDisplayComponent("sidebar") diff --git a/plugins/MonitorStage/MonitorStage.py b/plugins/MonitorStage/MonitorStage.py index e9d8f75645..8b98f2872c 100644 --- a/plugins/MonitorStage/MonitorStage.py +++ b/plugins/MonitorStage/MonitorStage.py @@ -1,6 +1,6 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. - +from UM.Application import Application from cura.Stages.CuraStage import CuraStage @@ -9,3 +9,7 @@ class MonitorStage(CuraStage): def __init__(self): super().__init__() + Application.getInstance().engineCreatedSignal.connect(self._engineCreated) + + def _engineCreated(self): + self.setIconSource(Application.getInstance().getTheme().getIcon("tab_status_connected")) diff --git a/plugins/MonitorStage/__init__.py b/plugins/MonitorStage/__init__.py index e18ecba7f9..884d43a8af 100644 --- a/plugins/MonitorStage/__init__.py +++ b/plugins/MonitorStage/__init__.py @@ -10,10 +10,11 @@ def getMetaData(): return { "stage": { "name": i18n_catalog.i18nc("@item:inmenu", "Monitor"), - "weight": 1, - "icon": "" + "weight": 1 } } def register(app): - return { "stage": MonitorStage.MonitorStage() } + return { + "stage": MonitorStage.MonitorStage() + } diff --git a/plugins/PrepareStage/PrepareStage.py b/plugins/PrepareStage/PrepareStage.py index bbd2275d2e..63086b3e93 100644 --- a/plugins/PrepareStage/PrepareStage.py +++ b/plugins/PrepareStage/PrepareStage.py @@ -1,6 +1,8 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. - +import os.path +from UM.Application import Application +from UM.Resources import Resources from cura.Stages.CuraStage import CuraStage @@ -9,3 +11,9 @@ class PrepareStage(CuraStage): def __init__(self): super().__init__() + Application.getInstance().engineCreatedSignal.connect(self._engineCreated) + + def _engineCreated(self): + sidebar_component_path = os.path.join(Resources.getPath(Application.getInstance().ResourceTypes.QmlFiles), "Sidebar.qml") + sidebar_component = Application.getInstance().createQmlComponent(sidebar_component_path) + self.addDisplayComponent("sidebar", sidebar_component) diff --git a/plugins/PrepareStage/__init__.py b/plugins/PrepareStage/__init__.py index d74cf27e39..f085d788f9 100644 --- a/plugins/PrepareStage/__init__.py +++ b/plugins/PrepareStage/__init__.py @@ -15,4 +15,6 @@ def getMetaData(): } def register(app): - return { "stage": PrepareStage.PrepareStage() } + return { + "stage": PrepareStage.PrepareStage() + } diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index bf67efdba3..3c9ca25b05 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -366,8 +366,7 @@ UM.MainWindow onStopMonitoringPrint: base.showPrintMonitor = false } - Sidebar - { + Loader { id: sidebar anchors @@ -379,7 +378,8 @@ UM.MainWindow width: UM.Theme.getSize("sidebar").width z: 1 - monitoringPrint: base.showPrintMonitor + + sourceComponent: UM.Controller.activeStage.sidebarComponent } Rectangle diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index 3bfc803a78..7429ef26df 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -10,603 +10,606 @@ import UM 1.2 as UM import Cura 1.0 as Cura import "Menus" -Rectangle +Component { - id: base; - - property int currentModeIndex; - property bool hideSettings: PrintInformation.preSliced - property bool hideView: Cura.MachineManager.activeMachineName == "" - - // Is there an output device for this printer? - property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 - property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands - property var connectedPrinter: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null - property int backendState: UM.Backend.state - - property bool monitoringPrint: false - - property variant printDuration: PrintInformation.currentPrintTime - property variant printMaterialLengths: PrintInformation.materialLengths - property variant printMaterialWeights: PrintInformation.materialWeights - property variant printMaterialCosts: PrintInformation.materialCosts - property variant printMaterialNames: PrintInformation.materialNames - - color: UM.Theme.getColor("sidebar") - UM.I18nCatalog { id: catalog; name:"cura"} - - Timer { - id: tooltipDelayTimer - interval: 500 - repeat: false - property var item - property string text - - onTriggered: - { - base.showTooltip(base, {x: 0, y: item.y}, text); - } - } - - function showTooltip(item, position, text) - { - tooltip.text = text; - position = item.mapToItem(base, position.x - UM.Theme.getSize("default_arrow").width, position.y); - tooltip.show(position); - } - - function hideTooltip() - { - tooltip.hide(); - } - - function strPadLeft(string, pad, length) { - return (new Array(length + 1).join(pad) + string).slice(-length); - } - - function getPrettyTime(time) - { - var hours = Math.floor(time / 3600) - time -= hours * 3600 - var minutes = Math.floor(time / 60); - time -= minutes * 60 - var seconds = Math.floor(time); - - var finalTime = strPadLeft(hours, "0", 2) + ':' + strPadLeft(minutes,'0',2)+ ':' + strPadLeft(seconds,'0',2); - return finalTime; - } - - MouseArea - { - anchors.fill: parent - acceptedButtons: Qt.AllButtons; - - onWheel: - { - wheel.accepted = true; - } - } - - SidebarHeader { - id: header - width: parent.width - visible: machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariantst - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() - } - - Rectangle { - id: headerSeparator - width: parent.width - visible: settingsModeSelection.visible && header.visible - height: visible ? UM.Theme.getSize("sidebar_lining").height : 0 - color: UM.Theme.getColor("sidebar_lining") - anchors.top: header.bottom - anchors.topMargin: visible ? UM.Theme.getSize("sidebar_margin").height : 0 - } - - Label { - id: settingsModeLabel - text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox","Print Setup disabled\nG-code files cannot be modified"); - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width - anchors.top: headerSeparator.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - width: Math.floor(parent.width * 0.45) - font: UM.Theme.getFont("large") - color: UM.Theme.getColor("text") - visible: !monitoringPrint && !hideView - } - - Rectangle { - id: settingsModeSelection - color: "transparent" - width: Math.floor(parent.width * 0.55) - height: UM.Theme.getSize("sidebar_header_mode_toggle").height - anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width - anchors.top: - { - if (settingsModeLabel.contentWidth >= parent.width - width - UM.Theme.getSize("sidebar_margin").width * 2) - { - return settingsModeLabel.bottom; - } - else - { - return headerSeparator.bottom; - } - } - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - visible: !monitoringPrint && !hideSettings && !hideView - Component{ - id: wizardDelegate - Button { - height: settingsModeSelection.height - anchors.left: parent.left - anchors.leftMargin: model.index * Math.floor(settingsModeSelection.width / 2) - anchors.verticalCenter: parent.verticalCenter - width: Math.floor(0.5 * parent.width) - text: model.text - exclusiveGroup: modeMenuGroup; - checkable: true; - checked: base.currentModeIndex == index - onClicked: base.currentModeIndex = index - - onHoveredChanged: { - if (hovered) - { - tooltipDelayTimer.item = settingsModeSelection - tooltipDelayTimer.text = model.tooltipText - tooltipDelayTimer.start(); - } - else - { - tooltipDelayTimer.stop(); - base.hideTooltip(); - } - } - - style: ButtonStyle { - background: Rectangle { - border.width: control.checked ? UM.Theme.getSize("default_lining").width * 2 : UM.Theme.getSize("default_lining").width - border.color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_border") : - control.hovered ? UM.Theme.getColor("action_button_hovered_border") : - UM.Theme.getColor("action_button_border") - color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active") : - control.hovered ? UM.Theme.getColor("action_button_hovered") : - UM.Theme.getColor("action_button") - Behavior on color { ColorAnimation { duration: 50; } } - Label { - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: UM.Theme.getSize("default_lining").width * 2 - anchors.rightMargin: UM.Theme.getSize("default_lining").width * 2 - color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_text") : - control.hovered ? UM.Theme.getColor("action_button_hovered_text") : - UM.Theme.getColor("action_button_text") - font: UM.Theme.getFont("default") - text: control.text - horizontalAlignment: Text.AlignHCenter - elide: Text.ElideMiddle - } - } - label: Item { } - } - } - } - ExclusiveGroup { id: modeMenuGroup; } - - ListView - { - id: modesList - property var index: 0 - model: modesListModel - delegate: wizardDelegate - anchors.top: parent.top - anchors.left: parent.left - width: parent.width - } - } - - StackView - { - id: sidebarContents - - anchors.bottom: footerSeparator.top - anchors.top: settingsModeSelection.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - anchors.left: base.left - anchors.right: base.right - visible: !monitoringPrint && !hideSettings - - delegate: StackViewDelegate - { - function transitionFinished(properties) - { - properties.exitItem.opacity = 1 - } - - pushTransition: StackViewTransition - { - PropertyAnimation - { - target: enterItem - property: "opacity" - from: 0 - to: 1 - duration: 100 - } - PropertyAnimation - { - target: exitItem - property: "opacity" - from: 1 - to: 0 - duration: 100 - } - } - } - } - - Loader - { - id: controlItem - anchors.bottom: footerSeparator.top - anchors.top: headerSeparator.bottom - anchors.left: base.left - anchors.right: base.right - sourceComponent: - { - if(monitoringPrint && connectedPrinter != null) - { - if(connectedPrinter.controlItem != null) - { - return connectedPrinter.controlItem - } - } - return null - } - } - - Loader - { - anchors.bottom: footerSeparator.top - anchors.top: headerSeparator.bottom - anchors.left: base.left - anchors.right: base.right - source: - { - if(controlItem.sourceComponent == null) - { - if(monitoringPrint) - { - return "PrintMonitor.qml" - } else - { - return "SidebarContents.qml" - } - } - else - { - return "" - } - } - } - Rectangle { - id: footerSeparator - width: parent.width - height: UM.Theme.getSize("sidebar_lining").height - color: UM.Theme.getColor("sidebar_lining") - anchors.bottom: printSpecs.top - anchors.bottomMargin: Math.floor(UM.Theme.getSize("sidebar_margin").height * 2 + UM.Theme.getSize("progressbar").height + UM.Theme.getFont("default_bold").pixelSize) - } + id: base; - Item - { - id: printSpecs - anchors.left: parent.left - anchors.bottom: parent.bottom - anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width - anchors.bottomMargin: UM.Theme.getSize("sidebar_margin").height - height: timeDetails.height + costSpec.height - width: base.width - (saveButton.buttonRowWidth + UM.Theme.getSize("sidebar_margin").width) - visible: !monitoringPrint - clip: true + property int currentModeIndex; + property bool hideSettings: PrintInformation.preSliced + property bool hideView: Cura.MachineManager.activeMachineName == "" - Label - { - id: timeDetails - anchors.left: parent.left - anchors.bottom: costSpec.top - font: UM.Theme.getFont("large") - color: UM.Theme.getColor("text_subtext") - text: (!base.printDuration || !base.printDuration.valid) ? catalog.i18nc("@label Hours and minutes", "00h 00min") : base.printDuration.getDisplayString(UM.DurationFormat.Short) + // Is there an output device for this printer? + property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 + property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands + property var connectedPrinter: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null + property int backendState: UM.Backend.state - MouseArea + property bool monitoringPrint: false + + property variant printDuration: PrintInformation.currentPrintTime + property variant printMaterialLengths: PrintInformation.materialLengths + property variant printMaterialWeights: PrintInformation.materialWeights + property variant printMaterialCosts: PrintInformation.materialCosts + property variant printMaterialNames: PrintInformation.materialNames + + color: UM.Theme.getColor("sidebar") + UM.I18nCatalog { id: catalog; name:"cura"} + + Timer { + id: tooltipDelayTimer + interval: 500 + repeat: false + property var item + property string text + + onTriggered: { - id: timeDetailsMouseArea - anchors.fill: parent - hoverEnabled: true - - onEntered: - { - if(base.printDuration.valid && !base.printDuration.isTotalDurationZero) - { - // All the time information for the different features is achieved - var print_time = PrintInformation.getFeaturePrintTimes(); - var total_seconds = parseInt(base.printDuration.getDisplayString(UM.DurationFormat.Seconds)) - - // A message is created and displayed when the user hover the time label - var tooltip_html = "%1
".arg(catalog.i18nc("@tooltip", "Time specification")); - for(var feature in print_time) - { - if(!print_time[feature].isTotalDurationZero) - { - tooltip_html += "" + - "".arg(print_time[feature].getDisplayString(UM.DurationFormat.ISO8601).slice(0,-3)) + - "".arg(Math.round(100 * parseInt(print_time[feature].getDisplayString(UM.DurationFormat.Seconds)) / total_seconds)) + - ""; - } - } - tooltip_html += "
" + feature + ":  %1  %1%
"; - - base.showTooltip(parent, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), tooltip_html); - } - } - onExited: - { - base.hideTooltip(); - } + base.showTooltip(base, {x: 0, y: item.y}, text); } } - Label + function showTooltip(item, position, text) { - function formatRow(items) + tooltip.text = text; + position = item.mapToItem(base, position.x - UM.Theme.getSize("default_arrow").width, position.y); + tooltip.show(position); + } + + function hideTooltip() + { + tooltip.hide(); + } + + function strPadLeft(string, pad, length) { + return (new Array(length + 1).join(pad) + string).slice(-length); + } + + function getPrettyTime(time) + { + var hours = Math.floor(time / 3600) + time -= hours * 3600 + var minutes = Math.floor(time / 60); + time -= minutes * 60 + var seconds = Math.floor(time); + + var finalTime = strPadLeft(hours, "0", 2) + ':' + strPadLeft(minutes,'0',2)+ ':' + strPadLeft(seconds,'0',2); + return finalTime; + } + + MouseArea + { + anchors.fill: parent + acceptedButtons: Qt.AllButtons; + + onWheel: { - var row_html = ""; - for(var item = 0; item < items.length; item++) - { - if (item == 0) - { - row_html += "%1".arg(items[item]); - } - else - { - row_html += "  %1".arg(items[item]); - } - } - row_html += ""; - return row_html; + wheel.accepted = true; } + } - function getSpecsData() - { - var lengths = []; - var total_length = 0; - var weights = []; - var total_weight = 0; - var costs = []; - var total_cost = 0; - var some_costs_known = false; - var names = []; - if(base.printMaterialLengths) - { - for(var index = 0; index < base.printMaterialLengths.length; index++) - { - if(base.printMaterialLengths[index] > 0) - { - names.push(base.printMaterialNames[index]); - lengths.push(base.printMaterialLengths[index].toFixed(2)); - weights.push(String(Math.floor(base.printMaterialWeights[index]))); - var cost = base.printMaterialCosts[index] == undefined ? 0 : base.printMaterialCosts[index].toFixed(2); - costs.push(cost); - if(cost > 0) - { - some_costs_known = true; - } - - total_length += base.printMaterialLengths[index]; - total_weight += base.printMaterialWeights[index]; - total_cost += base.printMaterialCosts[index]; - } - } - } - if(lengths.length == 0) - { - lengths = ["0.00"]; - weights = ["0"]; - costs = ["0.00"]; - } - - var tooltip_html = "%1
".arg(catalog.i18nc("@label", "Cost specification")); - for(var index = 0; index < lengths.length; index++) - { - tooltip_html += formatRow([ - "%1:".arg(names[index]), - catalog.i18nc("@label m for meter", "%1m").arg(lengths[index]), - catalog.i18nc("@label g for grams", "%1g").arg(weights[index]), - "%1 %2".arg(UM.Preferences.getValue("cura/currency")).arg(costs[index]), - ]); - } - if(lengths.length > 1) - { - tooltip_html += formatRow([ - catalog.i18nc("@label", "Total:"), - catalog.i18nc("@label m for meter", "%1m").arg(total_length.toFixed(2)), - catalog.i18nc("@label g for grams", "%1g").arg(Math.round(total_weight)), - "%1 %2".arg(UM.Preferences.getValue("cura/currency")).arg(total_cost.toFixed(2)), - ]); - } - tooltip_html += "
"; - tooltipText = tooltip_html; - - return tooltipText - } - - id: costSpec - anchors.left: parent.left - anchors.bottom: parent.bottom - font: UM.Theme.getFont("very_small") - color: UM.Theme.getColor("text_subtext") - elide: Text.ElideMiddle + SidebarHeader { + id: header width: parent.width - property string tooltipText - text: + visible: machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariantst + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() + } + + Rectangle { + id: headerSeparator + width: parent.width + visible: settingsModeSelection.visible && header.visible + height: visible ? UM.Theme.getSize("sidebar_lining").height : 0 + color: UM.Theme.getColor("sidebar_lining") + anchors.top: header.bottom + anchors.topMargin: visible ? UM.Theme.getSize("sidebar_margin").height : 0 + } + + Label { + id: settingsModeLabel + text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox","Print Setup disabled\nG-code files cannot be modified"); + anchors.left: parent.left + anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width + anchors.top: headerSeparator.bottom + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + width: Math.floor(parent.width * 0.45) + font: UM.Theme.getFont("large") + color: UM.Theme.getColor("text") + visible: !monitoringPrint && !hideView + } + + Rectangle { + id: settingsModeSelection + color: "transparent" + width: Math.floor(parent.width * 0.55) + height: UM.Theme.getSize("sidebar_header_mode_toggle").height + anchors.right: parent.right + anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width + anchors.top: { - var lengths = []; - var weights = []; - var costs = []; - var someCostsKnown = false; - if(base.printMaterialLengths) { - for(var index = 0; index < base.printMaterialLengths.length; index++) - { - if(base.printMaterialLengths[index] > 0) - { - lengths.push(base.printMaterialLengths[index].toFixed(2)); - weights.push(String(Math.floor(base.printMaterialWeights[index]))); - var cost = base.printMaterialCosts[index] == undefined ? 0 : base.printMaterialCosts[index].toFixed(2); - costs.push(cost); - if(cost > 0) - { - someCostsKnown = true; - } - } - } - } - if(lengths.length == 0) + if (settingsModeLabel.contentWidth >= parent.width - width - UM.Theme.getSize("sidebar_margin").width * 2) { - lengths = ["0.00"]; - weights = ["0"]; - costs = ["0.00"]; - } - if(someCostsKnown) - { - return catalog.i18nc("@label Print estimates: m for meters, g for grams, %4 is currency and %3 is print cost", "%1m / ~ %2g / ~ %4 %3").arg(lengths.join(" + ")) - .arg(weights.join(" + ")).arg(costs.join(" + ")).arg(UM.Preferences.getValue("cura/currency")); + return settingsModeLabel.bottom; } else { - return catalog.i18nc("@label Print estimates: m for meters, g for grams", "%1m / ~ %2g").arg(lengths.join(" + ")).arg(weights.join(" + ")); + return headerSeparator.bottom; } } - MouseArea - { - id: costSpecMouseArea - anchors.fill: parent - hoverEnabled: true + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + visible: !monitoringPrint && !hideSettings && !hideView + Component{ + id: wizardDelegate + Button { + height: settingsModeSelection.height + anchors.left: parent.left + anchors.leftMargin: model.index * Math.floor(settingsModeSelection.width / 2) + anchors.verticalCenter: parent.verticalCenter + width: Math.floor(0.5 * parent.width) + text: model.text + exclusiveGroup: modeMenuGroup; + checkable: true; + checked: base.currentModeIndex == index + onClicked: base.currentModeIndex = index - onEntered: - { + onHoveredChanged: { + if (hovered) + { + tooltipDelayTimer.item = settingsModeSelection + tooltipDelayTimer.text = model.tooltipText + tooltipDelayTimer.start(); + } + else + { + tooltipDelayTimer.stop(); + base.hideTooltip(); + } + } - if(base.printDuration.valid && !base.printDuration.isTotalDurationZero) - { - var show_data = costSpec.getSpecsData() - - base.showTooltip(parent, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), show_data); + style: ButtonStyle { + background: Rectangle { + border.width: control.checked ? UM.Theme.getSize("default_lining").width * 2 : UM.Theme.getSize("default_lining").width + border.color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_border") : + control.hovered ? UM.Theme.getColor("action_button_hovered_border") : + UM.Theme.getColor("action_button_border") + color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active") : + control.hovered ? UM.Theme.getColor("action_button_hovered") : + UM.Theme.getColor("action_button") + Behavior on color { ColorAnimation { duration: 50; } } + Label { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: UM.Theme.getSize("default_lining").width * 2 + anchors.rightMargin: UM.Theme.getSize("default_lining").width * 2 + color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_text") : + control.hovered ? UM.Theme.getColor("action_button_hovered_text") : + UM.Theme.getColor("action_button_text") + font: UM.Theme.getFont("default") + text: control.text + horizontalAlignment: Text.AlignHCenter + elide: Text.ElideMiddle + } + } + label: Item { } } } - onExited: + } + ExclusiveGroup { id: modeMenuGroup; } + + ListView + { + id: modesList + property var index: 0 + model: modesListModel + delegate: wizardDelegate + anchors.top: parent.top + anchors.left: parent.left + width: parent.width + } + } + + StackView + { + id: sidebarContents + + anchors.bottom: footerSeparator.top + anchors.top: settingsModeSelection.bottom + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + anchors.left: base.left + anchors.right: base.right + visible: !monitoringPrint && !hideSettings + + delegate: StackViewDelegate + { + function transitionFinished(properties) { - base.hideTooltip(); + properties.exitItem.opacity = 1 + } + + pushTransition: StackViewTransition + { + PropertyAnimation + { + target: enterItem + property: "opacity" + from: 0 + to: 1 + duration: 100 + } + PropertyAnimation + { + target: exitItem + property: "opacity" + from: 1 + to: 0 + duration: 100 + } } } } - } - // SaveButton and MonitorButton are actually the bottom footer panels. - // "!monitoringPrint" currently means "show-settings-mode" - SaveButton - { - id: saveButton - implicitWidth: base.width - anchors.top: footerSeparator.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - anchors.bottom: parent.bottom - visible: !monitoringPrint - } - - MonitorButton - { - id: monitorButton - implicitWidth: base.width - anchors.top: footerSeparator.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - anchors.bottom: parent.bottom - visible: monitoringPrint - } - - - SidebarTooltip - { - id: tooltip; - } - - // Setting mode: Recommended or Custom - ListModel - { - id: modesListModel; - } - - SidebarSimple - { - id: sidebarSimple; - visible: false; - - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() - } - - SidebarAdvanced - { - id: sidebarAdvanced; - visible: false; - - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() - } - - Component.onCompleted: - { - modesListModel.append({ - text: catalog.i18nc("@title:tab", "Recommended"), - tooltipText: catalog.i18nc("@tooltip", "Recommended Print Setup

Print with the recommended settings for the selected printer, material and quality."), - item: sidebarSimple - }) - modesListModel.append({ - text: catalog.i18nc("@title:tab", "Custom"), - tooltipText: catalog.i18nc("@tooltip", "Custom Print Setup

Print with finegrained control over every last bit of the slicing process."), - item: sidebarAdvanced - }) - sidebarContents.push({ "item": modesListModel.get(base.currentModeIndex).item, "immediate": true }); - - var index = Math.floor(UM.Preferences.getValue("cura/active_mode")) - if(index) + Loader { - currentModeIndex = index; + id: controlItem + anchors.bottom: footerSeparator.top + anchors.top: headerSeparator.bottom + anchors.left: base.left + anchors.right: base.right + sourceComponent: + { + if(monitoringPrint && connectedPrinter != null) + { + if(connectedPrinter.controlItem != null) + { + return connectedPrinter.controlItem + } + } + return null + } + } + + Loader + { + anchors.bottom: footerSeparator.top + anchors.top: headerSeparator.bottom + anchors.left: base.left + anchors.right: base.right + source: + { + if(controlItem.sourceComponent == null) + { + if(monitoringPrint) + { + return "PrintMonitor.qml" + } else + { + return "SidebarContents.qml" + } + } + else + { + return "" + } + } + } + + Rectangle + { + id: footerSeparator + width: parent.width + height: UM.Theme.getSize("sidebar_lining").height + color: UM.Theme.getColor("sidebar_lining") + anchors.bottom: printSpecs.top + anchors.bottomMargin: Math.floor(UM.Theme.getSize("sidebar_margin").height * 2 + UM.Theme.getSize("progressbar").height + UM.Theme.getFont("default_bold").pixelSize) + } + + Item + { + id: printSpecs + anchors.left: parent.left + anchors.bottom: parent.bottom + anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width + anchors.bottomMargin: UM.Theme.getSize("sidebar_margin").height + height: timeDetails.height + costSpec.height + width: base.width - (saveButton.buttonRowWidth + UM.Theme.getSize("sidebar_margin").width) + visible: !monitoringPrint + clip: true + + Label + { + id: timeDetails + anchors.left: parent.left + anchors.bottom: costSpec.top + font: UM.Theme.getFont("large") + color: UM.Theme.getColor("text_subtext") + text: (!base.printDuration || !base.printDuration.valid) ? catalog.i18nc("@label Hours and minutes", "00h 00min") : base.printDuration.getDisplayString(UM.DurationFormat.Short) + + MouseArea + { + id: timeDetailsMouseArea + anchors.fill: parent + hoverEnabled: true + + onEntered: + { + if(base.printDuration.valid && !base.printDuration.isTotalDurationZero) + { + // All the time information for the different features is achieved + var print_time = PrintInformation.getFeaturePrintTimes(); + var total_seconds = parseInt(base.printDuration.getDisplayString(UM.DurationFormat.Seconds)) + + // A message is created and displayed when the user hover the time label + var tooltip_html = "%1
".arg(catalog.i18nc("@tooltip", "Time specification")); + for(var feature in print_time) + { + if(!print_time[feature].isTotalDurationZero) + { + tooltip_html += "" + + "".arg(print_time[feature].getDisplayString(UM.DurationFormat.ISO8601).slice(0,-3)) + + "".arg(Math.round(100 * parseInt(print_time[feature].getDisplayString(UM.DurationFormat.Seconds)) / total_seconds)) + + ""; + } + } + tooltip_html += "
" + feature + ":  %1  %1%
"; + + base.showTooltip(parent, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), tooltip_html); + } + } + onExited: + { + base.hideTooltip(); + } + } + } + + Label + { + function formatRow(items) + { + var row_html = ""; + for(var item = 0; item < items.length; item++) + { + if (item == 0) + { + row_html += "%1".arg(items[item]); + } + else + { + row_html += "  %1".arg(items[item]); + } + } + row_html += ""; + return row_html; + } + + function getSpecsData() + { + var lengths = []; + var total_length = 0; + var weights = []; + var total_weight = 0; + var costs = []; + var total_cost = 0; + var some_costs_known = false; + var names = []; + if(base.printMaterialLengths) + { + for(var index = 0; index < base.printMaterialLengths.length; index++) + { + if(base.printMaterialLengths[index] > 0) + { + names.push(base.printMaterialNames[index]); + lengths.push(base.printMaterialLengths[index].toFixed(2)); + weights.push(String(Math.floor(base.printMaterialWeights[index]))); + var cost = base.printMaterialCosts[index] == undefined ? 0 : base.printMaterialCosts[index].toFixed(2); + costs.push(cost); + if(cost > 0) + { + some_costs_known = true; + } + + total_length += base.printMaterialLengths[index]; + total_weight += base.printMaterialWeights[index]; + total_cost += base.printMaterialCosts[index]; + } + } + } + if(lengths.length == 0) + { + lengths = ["0.00"]; + weights = ["0"]; + costs = ["0.00"]; + } + + var tooltip_html = "%1
".arg(catalog.i18nc("@label", "Cost specification")); + for(var index = 0; index < lengths.length; index++) + { + tooltip_html += formatRow([ + "%1:".arg(names[index]), + catalog.i18nc("@label m for meter", "%1m").arg(lengths[index]), + catalog.i18nc("@label g for grams", "%1g").arg(weights[index]), + "%1 %2".arg(UM.Preferences.getValue("cura/currency")).arg(costs[index]), + ]); + } + if(lengths.length > 1) + { + tooltip_html += formatRow([ + catalog.i18nc("@label", "Total:"), + catalog.i18nc("@label m for meter", "%1m").arg(total_length.toFixed(2)), + catalog.i18nc("@label g for grams", "%1g").arg(Math.round(total_weight)), + "%1 %2".arg(UM.Preferences.getValue("cura/currency")).arg(total_cost.toFixed(2)), + ]); + } + tooltip_html += "
"; + tooltipText = tooltip_html; + + return tooltipText + } + + id: costSpec + anchors.left: parent.left + anchors.bottom: parent.bottom + font: UM.Theme.getFont("very_small") + color: UM.Theme.getColor("text_subtext") + elide: Text.ElideMiddle + width: parent.width + property string tooltipText + text: + { + var lengths = []; + var weights = []; + var costs = []; + var someCostsKnown = false; + if(base.printMaterialLengths) { + for(var index = 0; index < base.printMaterialLengths.length; index++) + { + if(base.printMaterialLengths[index] > 0) + { + lengths.push(base.printMaterialLengths[index].toFixed(2)); + weights.push(String(Math.floor(base.printMaterialWeights[index]))); + var cost = base.printMaterialCosts[index] == undefined ? 0 : base.printMaterialCosts[index].toFixed(2); + costs.push(cost); + if(cost > 0) + { + someCostsKnown = true; + } + } + } + } + if(lengths.length == 0) + { + lengths = ["0.00"]; + weights = ["0"]; + costs = ["0.00"]; + } + if(someCostsKnown) + { + return catalog.i18nc("@label Print estimates: m for meters, g for grams, %4 is currency and %3 is print cost", "%1m / ~ %2g / ~ %4 %3").arg(lengths.join(" + ")) + .arg(weights.join(" + ")).arg(costs.join(" + ")).arg(UM.Preferences.getValue("cura/currency")); + } + else + { + return catalog.i18nc("@label Print estimates: m for meters, g for grams", "%1m / ~ %2g").arg(lengths.join(" + ")).arg(weights.join(" + ")); + } + } + MouseArea + { + id: costSpecMouseArea + anchors.fill: parent + hoverEnabled: true + + onEntered: + { + + if(base.printDuration.valid && !base.printDuration.isTotalDurationZero) + { + var show_data = costSpec.getSpecsData() + + base.showTooltip(parent, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), show_data); + } + } + onExited: + { + base.hideTooltip(); + } + } + } + } + + // SaveButton and MonitorButton are actually the bottom footer panels. + // "!monitoringPrint" currently means "show-settings-mode" + SaveButton + { + id: saveButton + implicitWidth: base.width + anchors.top: footerSeparator.bottom + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + anchors.bottom: parent.bottom + visible: !monitoringPrint + } + + MonitorButton + { + id: monitorButton + implicitWidth: base.width + anchors.top: footerSeparator.bottom + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + anchors.bottom: parent.bottom + visible: monitoringPrint + } + + + SidebarTooltip + { + id: tooltip; + } + + // Setting mode: Recommended or Custom + ListModel + { + id: modesListModel; + } + + SidebarSimple + { + id: sidebarSimple; + visible: false; + + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() + } + + SidebarAdvanced + { + id: sidebarAdvanced; + visible: false; + + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() + } + + Component.onCompleted: + { + modesListModel.append({ + text: catalog.i18nc("@title:tab", "Recommended"), + tooltipText: catalog.i18nc("@tooltip", "Recommended Print Setup

Print with the recommended settings for the selected printer, material and quality."), + item: sidebarSimple + }) + modesListModel.append({ + text: catalog.i18nc("@title:tab", "Custom"), + tooltipText: catalog.i18nc("@tooltip", "Custom Print Setup

Print with finegrained control over every last bit of the slicing process."), + item: sidebarAdvanced + }) + sidebarContents.push({ "item": modesListModel.get(base.currentModeIndex).item, "immediate": true }); + + var index = Math.floor(UM.Preferences.getValue("cura/active_mode")) + if(index) + { + currentModeIndex = index; + } + } + + UM.SettingPropertyProvider + { + id: machineExtruderCount + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_extruder_count" + watchedProperties: [ "value" ] + storeIndex: 0 + } + + UM.SettingPropertyProvider + { + id: machineHeatedBed + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_heated_bed" + watchedProperties: [ "value" ] + storeIndex: 0 } } - - UM.SettingPropertyProvider - { - id: machineExtruderCount - - containerStackId: Cura.MachineManager.activeMachineId - key: "machine_extruder_count" - watchedProperties: [ "value" ] - storeIndex: 0 - } - - UM.SettingPropertyProvider - { - id: machineHeatedBed - - containerStackId: Cura.MachineManager.activeMachineId - key: "machine_heated_bed" - watchedProperties: [ "value" ] - storeIndex: 0 - } } diff --git a/resources/qml/Topbar.qml b/resources/qml/Topbar.qml index 50e90a9a37..5817aff13b 100644 --- a/resources/qml/Topbar.qml +++ b/resources/qml/Topbar.qml @@ -6,7 +6,7 @@ import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.1 import QtQuick.Layouts 1.1 -import UM 1.2 as UM +import UM 1.4 as UM import Cura 1.0 as Cura import "Menus" @@ -83,34 +83,13 @@ Rectangle style: UM.Theme.styles.topbar_header_tab height: UM.Theme.getSize("sidebar_header").height onClicked: UM.Controller.setActiveStage(model.id) + iconSource: model.stage.iconSource + + property color overlayColor: "transparent" + property string overlayIconSource: "" } } -// Button -// { -// id: showSettings -// height: UM.Theme.getSize("sidebar_header").height -// text: catalog.i18nc("@title:tab", "Prepare") -// checkable: true -// checked: isChecked() -// exclusiveGroup: sidebarHeaderBarGroup -// style: UM.Theme.styles.topbar_header_tab -// -// // We use a Qt.binding to re-bind the checkbox state after manually setting it -// // https://stackoverflow.com/questions/38798450/qt-5-7-qml-why-are-my-checkbox-property-bindings-disappearing -// onClicked: { -// base.stopMonitoringPrint() -// checked = Qt.binding(isChecked) -// } -// -// function isChecked () { -// return !base.monitoringPrint -// } -// -// property color overlayColor: "transparent" -// property string overlayIconSource: "" -// } - // Button // { // id: showMonitor From 2d044a37ae364accfbd86dcf25c026046e0a7228 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 13:33:05 +0100 Subject: [PATCH 25/39] Sidebar and main view via loader --- plugins/MonitorStage/MonitorStage.py | 7 ++++ resources/qml/Cura.qml | 45 ++++++++++---------------- resources/qml/Settings/SettingView.qml | 2 +- resources/qml/Sidebar.qml | 2 +- resources/qml/SidebarHeader.qml | 8 ++--- resources/qml/Topbar.qml | 45 ++++++++++++-------------- 6 files changed, 50 insertions(+), 59 deletions(-) diff --git a/plugins/MonitorStage/MonitorStage.py b/plugins/MonitorStage/MonitorStage.py index 8b98f2872c..7cd6fe5063 100644 --- a/plugins/MonitorStage/MonitorStage.py +++ b/plugins/MonitorStage/MonitorStage.py @@ -1,6 +1,8 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +import os.path from UM.Application import Application +from UM.Resources import Resources from cura.Stages.CuraStage import CuraStage @@ -10,6 +12,11 @@ class MonitorStage(CuraStage): def __init__(self): super().__init__() Application.getInstance().engineCreatedSignal.connect(self._engineCreated) + # TODO: connect output device state to icon source def _engineCreated(self): + # Note: currently the sidebar component for prepare and monitor stages is the same, this will change with the printer output device refactor! + sidebar_component_path = os.path.join(Resources.getPath(Application.getInstance().ResourceTypes.QmlFiles), "Sidebar.qml") + sidebar_component = Application.getInstance().createQmlComponent(sidebar_component_path) + self.addDisplayComponent("sidebar", sidebar_component) self.setIconSource(Application.getInstance().getTheme().getIcon("tab_status_connected")) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 3c9ca25b05..662de05063 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -358,15 +358,13 @@ UM.MainWindow Topbar { id: topbar - anchors.left:parent.left + anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top - monitoringPrint: base.showPrintMonitor - onStartMonitoringPrint: base.showPrintMonitor = true - onStopMonitoringPrint: base.showPrintMonitor = false } - Loader { + Loader + { id: sidebar anchors @@ -382,39 +380,30 @@ UM.MainWindow sourceComponent: UM.Controller.activeStage.sidebarComponent } - Rectangle + Loader { - id: viewportOverlay + id: main - color: UM.Theme.getColor("viewport_overlay") anchors { top: topbar.bottom bottom: parent.bottom - left:parent.left + left: parent.left right: sidebar.left +// horizontalCenter: parent.horizontalCenter +// verticalCenter: parent.verticalCenter +// horizontalCenterOffset: - UM.Theme.getSize("sidebar").width / 2 +// verticalCenterOffset: UM.Theme.getSize("sidebar_header").height / 2 } - visible: opacity > 0 - opacity: base.showPrintMonitor ? 1 : 0 - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.AllButtons +// MouseArea +// { +// anchors.fill: parent +// acceptedButtons: Qt.AllButtons +// onWheel: wheel.accepted = true +// } - onWheel: wheel.accepted = true - } - } - - Loader - { - sourceComponent: Cura.MachineManager.printerOutputDevices.length > 0 ? Cura.MachineManager.printerOutputDevices[0].monitorItem: null - visible: base.showPrintMonitor - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenterOffset: - UM.Theme.getSize("sidebar").width / 2 - anchors.verticalCenterOffset: UM.Theme.getSize("sidebar_header").height / 2 - property real maximumWidth: viewportOverlay.width - property real maximumHeight: viewportOverlay.height + sourceComponent: UM.Controller.activeStage.mainComponent } UM.MessageStack diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 1ebb5cc40e..2079710315 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -24,7 +24,7 @@ Item { id: globalProfileRow height: UM.Theme.getSize("sidebar_setup").height - visible: !sidebar.monitoringPrint && !sidebar.hideSettings +// visible: !sidebar.monitoringPrint && !sidebar.hideSettings anchors { diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index 7429ef26df..992ad1f848 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -26,7 +26,7 @@ Component property var connectedPrinter: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null property int backendState: UM.Backend.state - property bool monitoringPrint: false + property bool monitoringPrint: UM.Controller.activeStage.id == "MonitorStage" property variant printDuration: PrintInformation.currentPrintTime property variant printMaterialLengths: PrintInformation.materialLengths diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml index 3e1e85824a..0f0572a48a 100644 --- a/resources/qml/SidebarHeader.qml +++ b/resources/qml/SidebarHeader.qml @@ -40,7 +40,7 @@ Column id: extruderSelectionRow width: parent.width height: Math.floor(UM.Theme.getSize("sidebar_tabs").height * 2 / 3) - visible: machineExtruderCount.properties.value > 1 && !sidebar.monitoringPrint + visible: machineExtruderCount.properties.value > 1 anchors { @@ -229,7 +229,7 @@ Column { id: materialRow height: UM.Theme.getSize("sidebar_setup").height - visible: Cura.MachineManager.hasMaterials && !sidebar.monitoringPrint && !sidebar.hideSettings + visible: Cura.MachineManager.hasMaterials anchors { @@ -279,7 +279,7 @@ Column { id: variantRow height: UM.Theme.getSize("sidebar_setup").height - visible: Cura.MachineManager.hasVariants && !sidebar.monitoringPrint && !sidebar.hideSettings + visible: Cura.MachineManager.hasVariants anchors { @@ -319,7 +319,7 @@ Column { id: materialInfoRow height: Math.floor(UM.Theme.getSize("sidebar_setup").height / 2) - visible: (Cura.MachineManager.hasVariants || Cura.MachineManager.hasMaterials) && !sidebar.monitoringPrint && !sidebar.hideSettings + visible: Cura.MachineManager.hasVariants || Cura.MachineManager.hasMaterials anchors { diff --git a/resources/qml/Topbar.qml b/resources/qml/Topbar.qml index 5817aff13b..d50cc64608 100644 --- a/resources/qml/Topbar.qml +++ b/resources/qml/Topbar.qml @@ -16,27 +16,22 @@ Rectangle anchors.left: parent.left anchors.right: parent.right height: UM.Theme.getSize("sidebar_header").height - color: base.monitoringPrint ? UM.Theme.getColor("topbar_background_color_monitoring") : UM.Theme.getColor("topbar_background_color") + color: UM.Controller.activeStage.id == "MonitorStage" ? UM.Theme.getColor("topbar_background_color_monitoring") : UM.Theme.getColor("topbar_background_color") property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands - property bool monitoringPrint: false - - // outgoing signal - signal startMonitoringPrint() - signal stopMonitoringPrint() // update monitoring status when event was triggered outside topbar - Component.onCompleted: { - startMonitoringPrint.connect(function () { - base.monitoringPrint = true - UM.Controller.disableModelRendering() - }) - stopMonitoringPrint.connect(function () { - base.monitoringPrint = false - UM.Controller.enableModelRendering() - }) - } +// Component.onCompleted: { +// startMonitoringPrint.connect(function () { +// base.monitoringPrint = true +// UM.Controller.disableModelRendering() +// }) +// stopMonitoringPrint.connect(function () { +// base.monitoringPrint = false +// UM.Controller.enableModelRendering() +// }) +// } UM.I18nCatalog { @@ -79,9 +74,10 @@ Rectangle text: model.name checkable: true checked: model.active - exclusiveGroup: sidebarHeaderBarGroup + exclusiveGroup: topbarMenuGroup style: UM.Theme.styles.topbar_header_tab height: UM.Theme.getSize("sidebar_header").height + width: UM.Theme.getSize("topbar_button").width onClicked: UM.Controller.setActiveStage(model.id) iconSource: model.stage.iconSource @@ -90,6 +86,8 @@ Rectangle } } + ExclusiveGroup { id: topbarMenuGroup } + // Button // { // id: showMonitor @@ -149,8 +147,6 @@ Rectangle // } // } // } - - ExclusiveGroup { id: sidebarHeaderBarGroup } } ToolButton @@ -218,17 +214,16 @@ Rectangle menu: PrinterMenu { } } - //View orientation Item + // View orientation Item Row { id: viewOrientationControl height: 30 - spacing: 2 + visible: UM.Controller.activeStage.id != "MonitorStage" - visible: !base.monitoringPrint - - anchors { + anchors + { verticalCenter: base.verticalCenter right: viewModeButton.right rightMargin: UM.Theme.getSize("default_margin").width + viewModeButton.width @@ -306,7 +301,7 @@ Rectangle } style: UM.Theme.styles.combobox - visible: !base.monitoringPrint + visible: UM.Controller.activeStage.id != "MonitorStage" model: UM.ViewModel { } textRole: "name" From ee643610e5a4de384aa09f5ef5399c94a4972574 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 17:46:18 +0100 Subject: [PATCH 26/39] Fix sidebar loading and unloading depending on active stage --- cura/CuraApplication.py | 1 - cura/Stages/CuraStage.py | 14 +- plugins/MonitorStage/MonitorStage.py | 7 +- plugins/PrepareStage/PrepareStage.py | 7 +- resources/qml/Cura.qml | 10 +- resources/qml/Sidebar.qml | 1145 +++++++++++++------------- resources/qml/SidebarSimple.qml | 20 +- 7 files changed, 601 insertions(+), 603 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index eec6c7f503..c9466a0093 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1,6 +1,5 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. - from PyQt5.QtNetwork import QLocalServer from PyQt5.QtNetwork import QLocalSocket diff --git a/cura/Stages/CuraStage.py b/cura/Stages/CuraStage.py index 6d4e56473d..8b7822ed7a 100644 --- a/cura/Stages/CuraStage.py +++ b/cura/Stages/CuraStage.py @@ -1,18 +1,22 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import pyqtProperty, QObject +from PyQt5.QtCore import pyqtProperty, QUrl, QObject from UM.Stage import Stage class CuraStage(Stage): - def __init__(self): - super().__init__() + def __init__(self, parent = None): + super().__init__(parent) - @pyqtProperty(QObject, constant = True) + @pyqtProperty(str, constant = True) + def stageId(self): + return self.getPluginId() + + @pyqtProperty(QUrl, constant = True) def mainComponent(self): return self.getDisplayComponent("main") - @pyqtProperty(QObject, constant = True) + @pyqtProperty(QUrl, constant = True) def sidebarComponent(self): return self.getDisplayComponent("sidebar") diff --git a/plugins/MonitorStage/MonitorStage.py b/plugins/MonitorStage/MonitorStage.py index 7cd6fe5063..6131e5538b 100644 --- a/plugins/MonitorStage/MonitorStage.py +++ b/plugins/MonitorStage/MonitorStage.py @@ -9,14 +9,13 @@ from cura.Stages.CuraStage import CuraStage ## Stage for monitoring a 3D printing while it's printing. class MonitorStage(CuraStage): - def __init__(self): - super().__init__() + def __init__(self, parent = None): + super().__init__(parent) Application.getInstance().engineCreatedSignal.connect(self._engineCreated) # TODO: connect output device state to icon source def _engineCreated(self): # Note: currently the sidebar component for prepare and monitor stages is the same, this will change with the printer output device refactor! sidebar_component_path = os.path.join(Resources.getPath(Application.getInstance().ResourceTypes.QmlFiles), "Sidebar.qml") - sidebar_component = Application.getInstance().createQmlComponent(sidebar_component_path) - self.addDisplayComponent("sidebar", sidebar_component) + self.addDisplayComponent("sidebar", sidebar_component_path) self.setIconSource(Application.getInstance().getTheme().getIcon("tab_status_connected")) diff --git a/plugins/PrepareStage/PrepareStage.py b/plugins/PrepareStage/PrepareStage.py index 63086b3e93..9d4d632845 100644 --- a/plugins/PrepareStage/PrepareStage.py +++ b/plugins/PrepareStage/PrepareStage.py @@ -9,11 +9,10 @@ from cura.Stages.CuraStage import CuraStage ## Stage for preparing model (slicing). class PrepareStage(CuraStage): - def __init__(self): - super().__init__() + def __init__(self, parent = None): + super().__init__(parent) Application.getInstance().engineCreatedSignal.connect(self._engineCreated) def _engineCreated(self): sidebar_component_path = os.path.join(Resources.getPath(Application.getInstance().ResourceTypes.QmlFiles), "Sidebar.qml") - sidebar_component = Application.getInstance().createQmlComponent(sidebar_component_path) - self.addDisplayComponent("sidebar", sidebar_component) + self.addDisplayComponent("sidebar", sidebar_component_path) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 662de05063..445325df90 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -377,7 +377,8 @@ UM.MainWindow width: UM.Theme.getSize("sidebar").width z: 1 - sourceComponent: UM.Controller.activeStage.sidebarComponent + source: UM.Controller.activeStage.sidebarComponent + asynchronous: true } Loader @@ -390,10 +391,6 @@ UM.MainWindow bottom: parent.bottom left: parent.left right: sidebar.left -// horizontalCenter: parent.horizontalCenter -// verticalCenter: parent.verticalCenter -// horizontalCenterOffset: - UM.Theme.getSize("sidebar").width / 2 -// verticalCenterOffset: UM.Theme.getSize("sidebar_header").height / 2 } // MouseArea @@ -403,7 +400,8 @@ UM.MainWindow // onWheel: wheel.accepted = true // } - sourceComponent: UM.Controller.activeStage.mainComponent + source: UM.Controller.activeStage.mainComponent + asynchronous: true } UM.MessageStack diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index 992ad1f848..a02454d298 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -10,606 +10,601 @@ import UM 1.2 as UM import Cura 1.0 as Cura import "Menus" -Component + +Rectangle { - Rectangle + id: base; + + property int currentModeIndex; + property bool hideSettings: PrintInformation.preSliced + property bool hideView: Cura.MachineManager.activeMachineName == "" + + // Is there an output device for this printer? + property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 + property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands + property var connectedPrinter: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null + property int backendState: UM.Backend.state + + property bool monitoringPrint: UM.Controller.activeStage.stageId == "MonitorStage" + + property variant printDuration: PrintInformation.currentPrintTime + property variant printMaterialLengths: PrintInformation.materialLengths + property variant printMaterialWeights: PrintInformation.materialWeights + property variant printMaterialCosts: PrintInformation.materialCosts + property variant printMaterialNames: PrintInformation.materialNames + + color: UM.Theme.getColor("sidebar") + UM.I18nCatalog { id: catalog; name:"cura"} + + Timer { + id: tooltipDelayTimer + interval: 500 + repeat: false + property var item + property string text + + onTriggered: + { + base.showTooltip(base, {x: 0, y: item.y}, text); + } + } + + function showTooltip(item, position, text) { - id: base; + tooltip.text = text; + position = item.mapToItem(base, position.x - UM.Theme.getSize("default_arrow").width, position.y); + tooltip.show(position); + } - property int currentModeIndex; - property bool hideSettings: PrintInformation.preSliced - property bool hideView: Cura.MachineManager.activeMachineName == "" + function hideTooltip() + { + tooltip.hide(); + } - // Is there an output device for this printer? - property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 - property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands - property var connectedPrinter: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null - property int backendState: UM.Backend.state + function strPadLeft(string, pad, length) { + return (new Array(length + 1).join(pad) + string).slice(-length); + } - property bool monitoringPrint: UM.Controller.activeStage.id == "MonitorStage" + function getPrettyTime(time) + { + var hours = Math.floor(time / 3600) + time -= hours * 3600 + var minutes = Math.floor(time / 60); + time -= minutes * 60 + var seconds = Math.floor(time); - property variant printDuration: PrintInformation.currentPrintTime - property variant printMaterialLengths: PrintInformation.materialLengths - property variant printMaterialWeights: PrintInformation.materialWeights - property variant printMaterialCosts: PrintInformation.materialCosts - property variant printMaterialNames: PrintInformation.materialNames + var finalTime = strPadLeft(hours, "0", 2) + ':' + strPadLeft(minutes,'0',2)+ ':' + strPadLeft(seconds,'0',2); + return finalTime; + } - color: UM.Theme.getColor("sidebar") - UM.I18nCatalog { id: catalog; name:"cura"} + MouseArea + { + anchors.fill: parent + acceptedButtons: Qt.AllButtons; - Timer { - id: tooltipDelayTimer - interval: 500 - repeat: false - property var item - property string text + onWheel: + { + wheel.accepted = true; + } + } - onTriggered: + SidebarHeader { + id: header + width: parent.width + visible: machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariants + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() + } + + Rectangle { + id: headerSeparator + width: parent.width + visible: settingsModeSelection.visible && header.visible + height: visible ? UM.Theme.getSize("sidebar_lining").height : 0 + color: UM.Theme.getColor("sidebar_lining") + anchors.top: header.bottom + anchors.topMargin: visible ? UM.Theme.getSize("sidebar_margin").height : 0 + } + + Label { + id: settingsModeLabel + text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox","Print Setup disabled\nG-code files cannot be modified"); + anchors.left: parent.left + anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width + anchors.top: headerSeparator.bottom + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + width: Math.floor(parent.width * 0.45) + font: UM.Theme.getFont("large") + color: UM.Theme.getColor("text") + visible: !monitoringPrint && !hideView + } + + Rectangle { + id: settingsModeSelection + color: "transparent" + width: Math.floor(parent.width * 0.55) + height: UM.Theme.getSize("sidebar_header_mode_toggle").height + anchors.right: parent.right + anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width + anchors.top: + { + if (settingsModeLabel.contentWidth >= parent.width - width - UM.Theme.getSize("sidebar_margin").width * 2) { - base.showTooltip(base, {x: 0, y: item.y}, text); + return settingsModeLabel.bottom; + } + else + { + return headerSeparator.bottom; } } - - function showTooltip(item, position, text) - { - tooltip.text = text; - position = item.mapToItem(base, position.x - UM.Theme.getSize("default_arrow").width, position.y); - tooltip.show(position); - } - - function hideTooltip() - { - tooltip.hide(); - } - - function strPadLeft(string, pad, length) { - return (new Array(length + 1).join(pad) + string).slice(-length); - } - - function getPrettyTime(time) - { - var hours = Math.floor(time / 3600) - time -= hours * 3600 - var minutes = Math.floor(time / 60); - time -= minutes * 60 - var seconds = Math.floor(time); - - var finalTime = strPadLeft(hours, "0", 2) + ':' + strPadLeft(minutes,'0',2)+ ':' + strPadLeft(seconds,'0',2); - return finalTime; - } - - MouseArea - { - anchors.fill: parent - acceptedButtons: Qt.AllButtons; - - onWheel: - { - wheel.accepted = true; - } - } - - SidebarHeader { - id: header - width: parent.width - visible: machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariantst - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() - } - - Rectangle { - id: headerSeparator - width: parent.width - visible: settingsModeSelection.visible && header.visible - height: visible ? UM.Theme.getSize("sidebar_lining").height : 0 - color: UM.Theme.getColor("sidebar_lining") - anchors.top: header.bottom - anchors.topMargin: visible ? UM.Theme.getSize("sidebar_margin").height : 0 - } - - Label { - id: settingsModeLabel - text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox","Print Setup disabled\nG-code files cannot be modified"); - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width - anchors.top: headerSeparator.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - width: Math.floor(parent.width * 0.45) - font: UM.Theme.getFont("large") - color: UM.Theme.getColor("text") - visible: !monitoringPrint && !hideView - } - - Rectangle { - id: settingsModeSelection - color: "transparent" - width: Math.floor(parent.width * 0.55) - height: UM.Theme.getSize("sidebar_header_mode_toggle").height - anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width - anchors.top: - { - if (settingsModeLabel.contentWidth >= parent.width - width - UM.Theme.getSize("sidebar_margin").width * 2) - { - return settingsModeLabel.bottom; - } - else - { - return headerSeparator.bottom; - } - } - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - visible: !monitoringPrint && !hideSettings && !hideView - Component{ - id: wizardDelegate - Button { - height: settingsModeSelection.height - anchors.left: parent.left - anchors.leftMargin: model.index * Math.floor(settingsModeSelection.width / 2) - anchors.verticalCenter: parent.verticalCenter - width: Math.floor(0.5 * parent.width) - text: model.text - exclusiveGroup: modeMenuGroup; - checkable: true; - checked: base.currentModeIndex == index - onClicked: base.currentModeIndex = index - - onHoveredChanged: { - if (hovered) - { - tooltipDelayTimer.item = settingsModeSelection - tooltipDelayTimer.text = model.tooltipText - tooltipDelayTimer.start(); - } - else - { - tooltipDelayTimer.stop(); - base.hideTooltip(); - } - } - - style: ButtonStyle { - background: Rectangle { - border.width: control.checked ? UM.Theme.getSize("default_lining").width * 2 : UM.Theme.getSize("default_lining").width - border.color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_border") : - control.hovered ? UM.Theme.getColor("action_button_hovered_border") : - UM.Theme.getColor("action_button_border") - color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active") : - control.hovered ? UM.Theme.getColor("action_button_hovered") : - UM.Theme.getColor("action_button") - Behavior on color { ColorAnimation { duration: 50; } } - Label { - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: UM.Theme.getSize("default_lining").width * 2 - anchors.rightMargin: UM.Theme.getSize("default_lining").width * 2 - color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_text") : - control.hovered ? UM.Theme.getColor("action_button_hovered_text") : - UM.Theme.getColor("action_button_text") - font: UM.Theme.getFont("default") - text: control.text - horizontalAlignment: Text.AlignHCenter - elide: Text.ElideMiddle - } - } - label: Item { } - } - } - } - ExclusiveGroup { id: modeMenuGroup; } - - ListView - { - id: modesList - property var index: 0 - model: modesListModel - delegate: wizardDelegate - anchors.top: parent.top + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + visible: !monitoringPrint && !hideSettings && !hideView + Component{ + id: wizardDelegate + Button { + height: settingsModeSelection.height anchors.left: parent.left - width: parent.width - } - } + anchors.leftMargin: model.index * Math.floor(settingsModeSelection.width / 2) + anchors.verticalCenter: parent.verticalCenter + width: Math.floor(0.5 * parent.width) + text: model.text + exclusiveGroup: modeMenuGroup; + checkable: true; + checked: base.currentModeIndex == index + onClicked: base.currentModeIndex = index - StackView - { - id: sidebarContents - - anchors.bottom: footerSeparator.top - anchors.top: settingsModeSelection.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - anchors.left: base.left - anchors.right: base.right - visible: !monitoringPrint && !hideSettings - - delegate: StackViewDelegate - { - function transitionFinished(properties) - { - properties.exitItem.opacity = 1 - } - - pushTransition: StackViewTransition - { - PropertyAnimation + onHoveredChanged: { + if (hovered) { - target: enterItem - property: "opacity" - from: 0 - to: 1 - duration: 100 - } - PropertyAnimation - { - target: exitItem - property: "opacity" - from: 1 - to: 0 - duration: 100 - } - } - } - } - - Loader - { - id: controlItem - anchors.bottom: footerSeparator.top - anchors.top: headerSeparator.bottom - anchors.left: base.left - anchors.right: base.right - sourceComponent: - { - if(monitoringPrint && connectedPrinter != null) - { - if(connectedPrinter.controlItem != null) - { - return connectedPrinter.controlItem - } - } - return null - } - } - - Loader - { - anchors.bottom: footerSeparator.top - anchors.top: headerSeparator.bottom - anchors.left: base.left - anchors.right: base.right - source: - { - if(controlItem.sourceComponent == null) - { - if(monitoringPrint) - { - return "PrintMonitor.qml" - } else - { - return "SidebarContents.qml" - } - } - else - { - return "" - } - } - } - - Rectangle - { - id: footerSeparator - width: parent.width - height: UM.Theme.getSize("sidebar_lining").height - color: UM.Theme.getColor("sidebar_lining") - anchors.bottom: printSpecs.top - anchors.bottomMargin: Math.floor(UM.Theme.getSize("sidebar_margin").height * 2 + UM.Theme.getSize("progressbar").height + UM.Theme.getFont("default_bold").pixelSize) - } - - Item - { - id: printSpecs - anchors.left: parent.left - anchors.bottom: parent.bottom - anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width - anchors.bottomMargin: UM.Theme.getSize("sidebar_margin").height - height: timeDetails.height + costSpec.height - width: base.width - (saveButton.buttonRowWidth + UM.Theme.getSize("sidebar_margin").width) - visible: !monitoringPrint - clip: true - - Label - { - id: timeDetails - anchors.left: parent.left - anchors.bottom: costSpec.top - font: UM.Theme.getFont("large") - color: UM.Theme.getColor("text_subtext") - text: (!base.printDuration || !base.printDuration.valid) ? catalog.i18nc("@label Hours and minutes", "00h 00min") : base.printDuration.getDisplayString(UM.DurationFormat.Short) - - MouseArea - { - id: timeDetailsMouseArea - anchors.fill: parent - hoverEnabled: true - - onEntered: - { - if(base.printDuration.valid && !base.printDuration.isTotalDurationZero) - { - // All the time information for the different features is achieved - var print_time = PrintInformation.getFeaturePrintTimes(); - var total_seconds = parseInt(base.printDuration.getDisplayString(UM.DurationFormat.Seconds)) - - // A message is created and displayed when the user hover the time label - var tooltip_html = "%1
".arg(catalog.i18nc("@tooltip", "Time specification")); - for(var feature in print_time) - { - if(!print_time[feature].isTotalDurationZero) - { - tooltip_html += "" + - "".arg(print_time[feature].getDisplayString(UM.DurationFormat.ISO8601).slice(0,-3)) + - "".arg(Math.round(100 * parseInt(print_time[feature].getDisplayString(UM.DurationFormat.Seconds)) / total_seconds)) + - ""; - } - } - tooltip_html += "
" + feature + ":  %1  %1%
"; - - base.showTooltip(parent, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), tooltip_html); - } - } - onExited: - { - base.hideTooltip(); - } - } - } - - Label - { - function formatRow(items) - { - var row_html = ""; - for(var item = 0; item < items.length; item++) - { - if (item == 0) - { - row_html += "%1".arg(items[item]); - } - else - { - row_html += "  %1".arg(items[item]); - } - } - row_html += ""; - return row_html; - } - - function getSpecsData() - { - var lengths = []; - var total_length = 0; - var weights = []; - var total_weight = 0; - var costs = []; - var total_cost = 0; - var some_costs_known = false; - var names = []; - if(base.printMaterialLengths) - { - for(var index = 0; index < base.printMaterialLengths.length; index++) - { - if(base.printMaterialLengths[index] > 0) - { - names.push(base.printMaterialNames[index]); - lengths.push(base.printMaterialLengths[index].toFixed(2)); - weights.push(String(Math.floor(base.printMaterialWeights[index]))); - var cost = base.printMaterialCosts[index] == undefined ? 0 : base.printMaterialCosts[index].toFixed(2); - costs.push(cost); - if(cost > 0) - { - some_costs_known = true; - } - - total_length += base.printMaterialLengths[index]; - total_weight += base.printMaterialWeights[index]; - total_cost += base.printMaterialCosts[index]; - } - } - } - if(lengths.length == 0) - { - lengths = ["0.00"]; - weights = ["0"]; - costs = ["0.00"]; - } - - var tooltip_html = "%1
".arg(catalog.i18nc("@label", "Cost specification")); - for(var index = 0; index < lengths.length; index++) - { - tooltip_html += formatRow([ - "%1:".arg(names[index]), - catalog.i18nc("@label m for meter", "%1m").arg(lengths[index]), - catalog.i18nc("@label g for grams", "%1g").arg(weights[index]), - "%1 %2".arg(UM.Preferences.getValue("cura/currency")).arg(costs[index]), - ]); - } - if(lengths.length > 1) - { - tooltip_html += formatRow([ - catalog.i18nc("@label", "Total:"), - catalog.i18nc("@label m for meter", "%1m").arg(total_length.toFixed(2)), - catalog.i18nc("@label g for grams", "%1g").arg(Math.round(total_weight)), - "%1 %2".arg(UM.Preferences.getValue("cura/currency")).arg(total_cost.toFixed(2)), - ]); - } - tooltip_html += "
"; - tooltipText = tooltip_html; - - return tooltipText - } - - id: costSpec - anchors.left: parent.left - anchors.bottom: parent.bottom - font: UM.Theme.getFont("very_small") - color: UM.Theme.getColor("text_subtext") - elide: Text.ElideMiddle - width: parent.width - property string tooltipText - text: - { - var lengths = []; - var weights = []; - var costs = []; - var someCostsKnown = false; - if(base.printMaterialLengths) { - for(var index = 0; index < base.printMaterialLengths.length; index++) - { - if(base.printMaterialLengths[index] > 0) - { - lengths.push(base.printMaterialLengths[index].toFixed(2)); - weights.push(String(Math.floor(base.printMaterialWeights[index]))); - var cost = base.printMaterialCosts[index] == undefined ? 0 : base.printMaterialCosts[index].toFixed(2); - costs.push(cost); - if(cost > 0) - { - someCostsKnown = true; - } - } - } - } - if(lengths.length == 0) - { - lengths = ["0.00"]; - weights = ["0"]; - costs = ["0.00"]; - } - if(someCostsKnown) - { - return catalog.i18nc("@label Print estimates: m for meters, g for grams, %4 is currency and %3 is print cost", "%1m / ~ %2g / ~ %4 %3").arg(lengths.join(" + ")) - .arg(weights.join(" + ")).arg(costs.join(" + ")).arg(UM.Preferences.getValue("cura/currency")); + tooltipDelayTimer.item = settingsModeSelection + tooltipDelayTimer.text = model.tooltipText + tooltipDelayTimer.start(); } else { - return catalog.i18nc("@label Print estimates: m for meters, g for grams", "%1m / ~ %2g").arg(lengths.join(" + ")).arg(weights.join(" + ")); - } - } - MouseArea - { - id: costSpecMouseArea - anchors.fill: parent - hoverEnabled: true - - onEntered: - { - - if(base.printDuration.valid && !base.printDuration.isTotalDurationZero) - { - var show_data = costSpec.getSpecsData() - - base.showTooltip(parent, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), show_data); - } - } - onExited: - { + tooltipDelayTimer.stop(); base.hideTooltip(); } } + + style: ButtonStyle { + background: Rectangle { + border.width: control.checked ? UM.Theme.getSize("default_lining").width * 2 : UM.Theme.getSize("default_lining").width + border.color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_border") : + control.hovered ? UM.Theme.getColor("action_button_hovered_border") : + UM.Theme.getColor("action_button_border") + color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active") : + control.hovered ? UM.Theme.getColor("action_button_hovered") : + UM.Theme.getColor("action_button") + Behavior on color { ColorAnimation { duration: 50; } } + Label { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: UM.Theme.getSize("default_lining").width * 2 + anchors.rightMargin: UM.Theme.getSize("default_lining").width * 2 + color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_text") : + control.hovered ? UM.Theme.getColor("action_button_hovered_text") : + UM.Theme.getColor("action_button_text") + font: UM.Theme.getFont("default") + text: control.text + horizontalAlignment: Text.AlignHCenter + elide: Text.ElideMiddle + } + } + label: Item { } + } } } + ExclusiveGroup { id: modeMenuGroup; } - // SaveButton and MonitorButton are actually the bottom footer panels. - // "!monitoringPrint" currently means "show-settings-mode" - SaveButton + ListView { - id: saveButton - implicitWidth: base.width - anchors.top: footerSeparator.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - anchors.bottom: parent.bottom - visible: !monitoringPrint - } - - MonitorButton - { - id: monitorButton - implicitWidth: base.width - anchors.top: footerSeparator.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - anchors.bottom: parent.bottom - visible: monitoringPrint - } - - - SidebarTooltip - { - id: tooltip; - } - - // Setting mode: Recommended or Custom - ListModel - { - id: modesListModel; - } - - SidebarSimple - { - id: sidebarSimple; - visible: false; - - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() - } - - SidebarAdvanced - { - id: sidebarAdvanced; - visible: false; - - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() - } - - Component.onCompleted: - { - modesListModel.append({ - text: catalog.i18nc("@title:tab", "Recommended"), - tooltipText: catalog.i18nc("@tooltip", "Recommended Print Setup

Print with the recommended settings for the selected printer, material and quality."), - item: sidebarSimple - }) - modesListModel.append({ - text: catalog.i18nc("@title:tab", "Custom"), - tooltipText: catalog.i18nc("@tooltip", "Custom Print Setup

Print with finegrained control over every last bit of the slicing process."), - item: sidebarAdvanced - }) - sidebarContents.push({ "item": modesListModel.get(base.currentModeIndex).item, "immediate": true }); - - var index = Math.floor(UM.Preferences.getValue("cura/active_mode")) - if(index) - { - currentModeIndex = index; - } - } - - UM.SettingPropertyProvider - { - id: machineExtruderCount - - containerStackId: Cura.MachineManager.activeMachineId - key: "machine_extruder_count" - watchedProperties: [ "value" ] - storeIndex: 0 - } - - UM.SettingPropertyProvider - { - id: machineHeatedBed - - containerStackId: Cura.MachineManager.activeMachineId - key: "machine_heated_bed" - watchedProperties: [ "value" ] - storeIndex: 0 + id: modesList + property var index: 0 + model: modesListModel + delegate: wizardDelegate + anchors.top: parent.top + anchors.left: parent.left + width: parent.width } } + + StackView + { + id: sidebarContents + + anchors.bottom: footerSeparator.top + anchors.top: settingsModeSelection.bottom + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + anchors.left: base.left + anchors.right: base.right + visible: !monitoringPrint && !hideSettings + + delegate: StackViewDelegate + { + function transitionFinished(properties) + { + properties.exitItem.opacity = 1 + } + + pushTransition: StackViewTransition + { + PropertyAnimation + { + target: enterItem + property: "opacity" + from: 0 + to: 1 + duration: 100 + } + PropertyAnimation + { + target: exitItem + property: "opacity" + from: 1 + to: 0 + duration: 100 + } + } + } + } + + Loader + { + id: controlItem + anchors.bottom: footerSeparator.top + anchors.top: headerSeparator.bottom + anchors.left: base.left + anchors.right: base.right + sourceComponent: + { + if(monitoringPrint && connectedPrinter != null) + { + if(connectedPrinter.controlItem != null) + { + return connectedPrinter.controlItem + } + } + return null + } + } + + Loader + { + anchors.bottom: footerSeparator.top + anchors.top: headerSeparator.bottom + anchors.left: base.left + anchors.right: base.right + source: + { + if(controlItem.sourceComponent == null) + { + if(monitoringPrint) + { + return "PrintMonitor.qml" + } else + { + return "SidebarContents.qml" + } + } + else + { + return "" + } + } + } + + Rectangle + { + id: footerSeparator + width: parent.width + height: UM.Theme.getSize("sidebar_lining").height + color: UM.Theme.getColor("sidebar_lining") + anchors.bottom: printSpecs.top + anchors.bottomMargin: Math.floor(UM.Theme.getSize("sidebar_margin").height * 2 + UM.Theme.getSize("progressbar").height + UM.Theme.getFont("default_bold").pixelSize) + } + + Item + { + id: printSpecs + anchors.left: parent.left + anchors.bottom: parent.bottom + anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width + anchors.bottomMargin: UM.Theme.getSize("sidebar_margin").height + height: timeDetails.height + costSpec.height + width: base.width - (saveButton.buttonRowWidth + UM.Theme.getSize("sidebar_margin").width) + visible: !monitoringPrint + clip: true + + Label + { + id: timeDetails + anchors.left: parent.left + anchors.bottom: costSpec.top + font: UM.Theme.getFont("large") + color: UM.Theme.getColor("text_subtext") + text: (!base.printDuration || !base.printDuration.valid) ? catalog.i18nc("@label Hours and minutes", "00h 00min") : base.printDuration.getDisplayString(UM.DurationFormat.Short) + + MouseArea + { + id: timeDetailsMouseArea + anchors.fill: parent + hoverEnabled: true + + onEntered: + { + if(base.printDuration.valid && !base.printDuration.isTotalDurationZero) + { + // All the time information for the different features is achieved + var print_time = PrintInformation.getFeaturePrintTimes(); + var total_seconds = parseInt(base.printDuration.getDisplayString(UM.DurationFormat.Seconds)) + + // A message is created and displayed when the user hover the time label + var tooltip_html = "%1
".arg(catalog.i18nc("@tooltip", "Time specification")); + for(var feature in print_time) + { + if(!print_time[feature].isTotalDurationZero) + { + tooltip_html += "" + + "".arg(print_time[feature].getDisplayString(UM.DurationFormat.ISO8601).slice(0,-3)) + + "".arg(Math.round(100 * parseInt(print_time[feature].getDisplayString(UM.DurationFormat.Seconds)) / total_seconds)) + + ""; + } + } + tooltip_html += "
" + feature + ":  %1  %1%
"; + + base.showTooltip(parent, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), tooltip_html); + } + } + onExited: + { + base.hideTooltip(); + } + } + } + + Label + { + function formatRow(items) + { + var row_html = ""; + for(var item = 0; item < items.length; item++) + { + if (item == 0) + { + row_html += "%1".arg(items[item]); + } + else + { + row_html += "  %1".arg(items[item]); + } + } + row_html += ""; + return row_html; + } + + function getSpecsData() + { + var lengths = []; + var total_length = 0; + var weights = []; + var total_weight = 0; + var costs = []; + var total_cost = 0; + var some_costs_known = false; + var names = []; + if(base.printMaterialLengths) + { + for(var index = 0; index < base.printMaterialLengths.length; index++) + { + if(base.printMaterialLengths[index] > 0) + { + names.push(base.printMaterialNames[index]); + lengths.push(base.printMaterialLengths[index].toFixed(2)); + weights.push(String(Math.floor(base.printMaterialWeights[index]))); + var cost = base.printMaterialCosts[index] == undefined ? 0 : base.printMaterialCosts[index].toFixed(2); + costs.push(cost); + if(cost > 0) + { + some_costs_known = true; + } + + total_length += base.printMaterialLengths[index]; + total_weight += base.printMaterialWeights[index]; + total_cost += base.printMaterialCosts[index]; + } + } + } + if(lengths.length == 0) + { + lengths = ["0.00"]; + weights = ["0"]; + costs = ["0.00"]; + } + + var tooltip_html = "%1
".arg(catalog.i18nc("@label", "Cost specification")); + for(var index = 0; index < lengths.length; index++) + { + tooltip_html += formatRow([ + "%1:".arg(names[index]), + catalog.i18nc("@label m for meter", "%1m").arg(lengths[index]), + catalog.i18nc("@label g for grams", "%1g").arg(weights[index]), + "%1 %2".arg(UM.Preferences.getValue("cura/currency")).arg(costs[index]), + ]); + } + if(lengths.length > 1) + { + tooltip_html += formatRow([ + catalog.i18nc("@label", "Total:"), + catalog.i18nc("@label m for meter", "%1m").arg(total_length.toFixed(2)), + catalog.i18nc("@label g for grams", "%1g").arg(Math.round(total_weight)), + "%1 %2".arg(UM.Preferences.getValue("cura/currency")).arg(total_cost.toFixed(2)), + ]); + } + tooltip_html += "
"; + tooltipText = tooltip_html; + + return tooltipText + } + + id: costSpec + anchors.left: parent.left + anchors.bottom: parent.bottom + font: UM.Theme.getFont("very_small") + color: UM.Theme.getColor("text_subtext") + elide: Text.ElideMiddle + width: parent.width + property string tooltipText + text: + { + var lengths = []; + var weights = []; + var costs = []; + var someCostsKnown = false; + if(base.printMaterialLengths) { + for(var index = 0; index < base.printMaterialLengths.length; index++) + { + if(base.printMaterialLengths[index] > 0) + { + lengths.push(base.printMaterialLengths[index].toFixed(2)); + weights.push(String(Math.floor(base.printMaterialWeights[index]))); + var cost = base.printMaterialCosts[index] == undefined ? 0 : base.printMaterialCosts[index].toFixed(2); + costs.push(cost); + if(cost > 0) + { + someCostsKnown = true; + } + } + } + } + if(lengths.length == 0) + { + lengths = ["0.00"]; + weights = ["0"]; + costs = ["0.00"]; + } + if(someCostsKnown) + { + return catalog.i18nc("@label Print estimates: m for meters, g for grams, %4 is currency and %3 is print cost", "%1m / ~ %2g / ~ %4 %3").arg(lengths.join(" + ")) + .arg(weights.join(" + ")).arg(costs.join(" + ")).arg(UM.Preferences.getValue("cura/currency")); + } + else + { + return catalog.i18nc("@label Print estimates: m for meters, g for grams", "%1m / ~ %2g").arg(lengths.join(" + ")).arg(weights.join(" + ")); + } + } + MouseArea + { + id: costSpecMouseArea + anchors.fill: parent + hoverEnabled: true + + onEntered: + { + + if(base.printDuration.valid && !base.printDuration.isTotalDurationZero) + { + var show_data = costSpec.getSpecsData() + + base.showTooltip(parent, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), show_data); + } + } + onExited: + { + base.hideTooltip(); + } + } + } + } + + // SaveButton and MonitorButton are actually the bottom footer panels. + // "!monitoringPrint" currently means "show-settings-mode" + SaveButton + { + id: saveButton + implicitWidth: base.width + anchors.top: footerSeparator.bottom + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + anchors.bottom: parent.bottom + visible: !monitoringPrint + } + + MonitorButton + { + id: monitorButton + implicitWidth: base.width + anchors.top: footerSeparator.bottom + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + anchors.bottom: parent.bottom + visible: monitoringPrint + } + + SidebarTooltip + { + id: tooltip; + } + + // Setting mode: Recommended or Custom + ListModel + { + id: modesListModel; + } + + SidebarSimple + { + id: sidebarSimple; + visible: false; + + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() + } + + SidebarAdvanced + { + id: sidebarAdvanced; + visible: false; + + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() + } + + Component.onCompleted: + { + modesListModel.append({ + text: catalog.i18nc("@title:tab", "Recommended"), + tooltipText: catalog.i18nc("@tooltip", "Recommended Print Setup

Print with the recommended settings for the selected printer, material and quality."), + item: sidebarSimple + }) + modesListModel.append({ + text: catalog.i18nc("@title:tab", "Custom"), + tooltipText: catalog.i18nc("@tooltip", "Custom Print Setup

Print with finegrained control over every last bit of the slicing process."), + item: sidebarAdvanced + }) + sidebarContents.push({ "item": modesListModel.get(base.currentModeIndex).item, "immediate": true }); + + var index = Math.floor(UM.Preferences.getValue("cura/active_mode")) + if(index) + { + currentModeIndex = index; + } + } + + UM.SettingPropertyProvider + { + id: machineExtruderCount + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_extruder_count" + watchedProperties: [ "value" ] + storeIndex: 0 + } + + UM.SettingPropertyProvider + { + id: machineHeatedBed + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_heated_bed" + watchedProperties: [ "value" ] + storeIndex: 0 + } } diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index e923963355..4b182c7a72 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -403,8 +403,6 @@ Item } } - - // // Infill // @@ -568,18 +566,24 @@ Item model: infillModel anchors.fill: parent - property int activeIndex: { + function activeIndex () { for (var i = 0; i < infillModel.count; i++) { var density = parseInt(infillDensity.properties.value) var steps = parseInt(infillSteps.properties.value) var infillModelItem = infillModel.get(i) - if (density >= infillModelItem.percentageMin + // TODO: somehow this print causes this method not to crash QML when the sidebar view gets unloaded (when switching states) + // TODO: That should be fixed :P + print("test", density, steps, infillModelItem) + + if (infillModelItem + && density >= infillModelItem.percentageMin && density <= infillModelItem.percentageMax && steps >= infillModelItem.stepsMin - && steps <= infillModelItem.stepsMax){ - return i - } + && steps <= infillModelItem.stepsMax + ){ + return i + } } return -1 } @@ -587,7 +591,7 @@ Item Rectangle { anchors.fill: parent - visible: infillIconList.activeIndex == index + visible: infillIconList.activeIndex() == index border.width: UM.Theme.getSize("default_lining").width border.color: UM.Theme.getColor("quality_slider_unavailable") From 9702ffb79432f0c7ed7242c9d59bee78c4bd2984 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 18:09:02 +0100 Subject: [PATCH 27/39] Small fixes to sidebar layout after refactoring --- resources/qml/Settings/SettingView.qml | 2 +- resources/qml/Sidebar.qml | 14 +++++++++++++- resources/qml/SidebarHeader.qml | 8 ++++---- resources/qml/SidebarSimple.qml | 6 +----- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 2079710315..1ebb5cc40e 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -24,7 +24,7 @@ Item { id: globalProfileRow height: UM.Theme.getSize("sidebar_setup").height -// visible: !sidebar.monitoringPrint && !sidebar.hideSettings + visible: !sidebar.monitoringPrint && !sidebar.hideSettings anchors { diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index a02454d298..6bc0903059 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -10,7 +10,6 @@ import UM 1.2 as UM import Cura 1.0 as Cura import "Menus" - Rectangle { id: base; @@ -92,6 +91,7 @@ Rectangle id: header width: parent.width visible: machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariants + onShowTooltip: base.showTooltip(item, location, text) onHideTooltip: base.hideTooltip() } @@ -106,6 +106,15 @@ Rectangle anchors.topMargin: visible ? UM.Theme.getSize("sidebar_margin").height : 0 } + onCurrentModeIndexChanged: + { + UM.Preferences.setValue("cura/active_mode", currentModeIndex); + if(modesListModel.count > base.currentModeIndex) + { + sidebarContents.push({ "item": modesListModel.get(base.currentModeIndex).item, "replace": true }); + } + } + Label { id: settingsModeLabel text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox","Print Setup disabled\nG-code files cannot be modified"); @@ -540,6 +549,7 @@ Rectangle visible: monitoringPrint } + SidebarTooltip { id: tooltip; @@ -593,6 +603,7 @@ Rectangle UM.SettingPropertyProvider { id: machineExtruderCount + containerStackId: Cura.MachineManager.activeMachineId key: "machine_extruder_count" watchedProperties: [ "value" ] @@ -602,6 +613,7 @@ Rectangle UM.SettingPropertyProvider { id: machineHeatedBed + containerStackId: Cura.MachineManager.activeMachineId key: "machine_heated_bed" watchedProperties: [ "value" ] diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml index 0f0572a48a..3e1e85824a 100644 --- a/resources/qml/SidebarHeader.qml +++ b/resources/qml/SidebarHeader.qml @@ -40,7 +40,7 @@ Column id: extruderSelectionRow width: parent.width height: Math.floor(UM.Theme.getSize("sidebar_tabs").height * 2 / 3) - visible: machineExtruderCount.properties.value > 1 + visible: machineExtruderCount.properties.value > 1 && !sidebar.monitoringPrint anchors { @@ -229,7 +229,7 @@ Column { id: materialRow height: UM.Theme.getSize("sidebar_setup").height - visible: Cura.MachineManager.hasMaterials + visible: Cura.MachineManager.hasMaterials && !sidebar.monitoringPrint && !sidebar.hideSettings anchors { @@ -279,7 +279,7 @@ Column { id: variantRow height: UM.Theme.getSize("sidebar_setup").height - visible: Cura.MachineManager.hasVariants + visible: Cura.MachineManager.hasVariants && !sidebar.monitoringPrint && !sidebar.hideSettings anchors { @@ -319,7 +319,7 @@ Column { id: materialInfoRow height: Math.floor(UM.Theme.getSize("sidebar_setup").height / 2) - visible: Cura.MachineManager.hasVariants || Cura.MachineManager.hasMaterials + visible: (Cura.MachineManager.hasVariants || Cura.MachineManager.hasMaterials) && !sidebar.monitoringPrint && !sidebar.hideSettings anchors { diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index 4b182c7a72..62cf6f9d34 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -572,11 +572,7 @@ Item var steps = parseInt(infillSteps.properties.value) var infillModelItem = infillModel.get(i) - // TODO: somehow this print causes this method not to crash QML when the sidebar view gets unloaded (when switching states) - // TODO: That should be fixed :P - print("test", density, steps, infillModelItem) - - if (infillModelItem + if (infillModelItem != "undefined" && density >= infillModelItem.percentageMin && density <= infillModelItem.percentageMax && steps >= infillModelItem.stepsMin From 28c5debd61e664a03f72ac27b3f715ade8fcf0fc Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 18:11:46 +0100 Subject: [PATCH 28/39] Fix hiding topbar buttons in monitor stage --- resources/qml/Topbar.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/Topbar.qml b/resources/qml/Topbar.qml index d50cc64608..550b2dc007 100644 --- a/resources/qml/Topbar.qml +++ b/resources/qml/Topbar.qml @@ -220,7 +220,7 @@ Rectangle id: viewOrientationControl height: 30 spacing: 2 - visible: UM.Controller.activeStage.id != "MonitorStage" + visible: UM.Controller.activeStage.stageId != "MonitorStage" anchors { @@ -301,7 +301,7 @@ Rectangle } style: UM.Theme.styles.combobox - visible: UM.Controller.activeStage.id != "MonitorStage" + visible: UM.Controller.activeStage.stageId != "MonitorStage" model: UM.ViewModel { } textRole: "name" From 548761fcc35d6a68f94a5d0b817eacb8d8152e36 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 18:15:17 +0100 Subject: [PATCH 29/39] Some cleanup --- plugins/MonitorStage/MonitorStage.py | 37 +++++++++++++++++ resources/qml/Topbar.qml | 62 +--------------------------- 2 files changed, 38 insertions(+), 61 deletions(-) diff --git a/plugins/MonitorStage/MonitorStage.py b/plugins/MonitorStage/MonitorStage.py index 6131e5538b..dd1eca9682 100644 --- a/plugins/MonitorStage/MonitorStage.py +++ b/plugins/MonitorStage/MonitorStage.py @@ -19,3 +19,40 @@ class MonitorStage(CuraStage): sidebar_component_path = os.path.join(Resources.getPath(Application.getInstance().ResourceTypes.QmlFiles), "Sidebar.qml") self.addDisplayComponent("sidebar", sidebar_component_path) self.setIconSource(Application.getInstance().getTheme().getIcon("tab_status_connected")) + +# property string iconSource: +# // { +# // if (!printerConnected) +# // { +# // return UM.Theme.getIcon("tab_status_unknown"); +# // } +# // else if (!printerAcceptsCommands) +# // { +# // return UM.Theme.getIcon("tab_status_unknown"); +# // } +# // +# // if (Cura.MachineManager.printerOutputDevices[0].printerState == "maintenance") +# // { +# // return UM.Theme.getIcon("tab_status_busy"); +# // } +# // +# // switch (Cura.MachineManager.printerOutputDevices[0].jobState) +# // { +# // case "printing": +# // case "pre_print": +# // case "pausing": +# // case "resuming": +# // return UM.Theme.getIcon("tab_status_busy"); +# // case "wait_cleanup": +# // return UM.Theme.getIcon("tab_status_finished"); +# // case "ready": +# // case "": +# // return UM.Theme.getIcon("tab_status_connected") +# // case "paused": +# // return UM.Theme.getIcon("tab_status_paused") +# // case "error": +# // return UM.Theme.getIcon("tab_status_stopped") +# // default: +# // return UM.Theme.getIcon("tab_status_unknown") +# // } +# // } \ No newline at end of file diff --git a/resources/qml/Topbar.qml b/resources/qml/Topbar.qml index 550b2dc007..98f9f94528 100644 --- a/resources/qml/Topbar.qml +++ b/resources/qml/Topbar.qml @@ -16,7 +16,7 @@ Rectangle anchors.left: parent.left anchors.right: parent.right height: UM.Theme.getSize("sidebar_header").height - color: UM.Controller.activeStage.id == "MonitorStage" ? UM.Theme.getColor("topbar_background_color_monitoring") : UM.Theme.getColor("topbar_background_color") + color: UM.Controller.activeStage.stageId == "MonitorStage" ? UM.Theme.getColor("topbar_background_color_monitoring") : UM.Theme.getColor("topbar_background_color") property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands @@ -87,66 +87,6 @@ Rectangle } ExclusiveGroup { id: topbarMenuGroup } - -// Button -// { -// id: showMonitor -// width: UM.Theme.getSize("topbar_button").width -// height: UM.Theme.getSize("sidebar_header").height -// text: catalog.i18nc("@title:tab", "Monitor") -// checkable: true -// checked: isChecked() -// exclusiveGroup: sidebarHeaderBarGroup -// style: UM.Theme.styles.topbar_header_tab_no_overlay -// -// // We use a Qt.binding to re-bind the checkbox state after manually setting it -// // https://stackoverflow.com/questions/38798450/qt-5-7-qml-why-are-my-checkbox-property-bindings-disappearing -// onClicked: { -// base.startMonitoringPrint() -// checked = Qt.binding(isChecked) -// } -// -// function isChecked () { -// return base.monitoringPrint -// } -// -// property string iconSource: -// { -// if (!printerConnected) -// { -// return UM.Theme.getIcon("tab_status_unknown"); -// } -// else if (!printerAcceptsCommands) -// { -// return UM.Theme.getIcon("tab_status_unknown"); -// } -// -// if (Cura.MachineManager.printerOutputDevices[0].printerState == "maintenance") -// { -// return UM.Theme.getIcon("tab_status_busy"); -// } -// -// switch (Cura.MachineManager.printerOutputDevices[0].jobState) -// { -// case "printing": -// case "pre_print": -// case "pausing": -// case "resuming": -// return UM.Theme.getIcon("tab_status_busy"); -// case "wait_cleanup": -// return UM.Theme.getIcon("tab_status_finished"); -// case "ready": -// case "": -// return UM.Theme.getIcon("tab_status_connected") -// case "paused": -// return UM.Theme.getIcon("tab_status_paused") -// case "error": -// return UM.Theme.getIcon("tab_status_stopped") -// default: -// return UM.Theme.getIcon("tab_status_unknown") -// } -// } -// } } ToolButton From 9e3c681d669894c0742a72b00e183a014598428d Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 18:47:11 +0100 Subject: [PATCH 30/39] Fix top bar tab layout when it has an icon --- resources/qml/Topbar.qml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/resources/qml/Topbar.qml b/resources/qml/Topbar.qml index 98f9f94528..9ad91615ad 100644 --- a/resources/qml/Topbar.qml +++ b/resources/qml/Topbar.qml @@ -75,9 +75,8 @@ Rectangle checkable: true checked: model.active exclusiveGroup: topbarMenuGroup - style: UM.Theme.styles.topbar_header_tab + style: (model.stage.iconSource != "") ? UM.Theme.styles.topbar_header_tab_no_overlay : UM.Theme.styles.topbar_header_tab height: UM.Theme.getSize("sidebar_header").height - width: UM.Theme.getSize("topbar_button").width onClicked: UM.Controller.setActiveStage(model.id) iconSource: model.stage.iconSource From f6b570e299a70ee3f96f2798a8becf16b0901283 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Thu, 7 Dec 2017 09:13:46 +0100 Subject: [PATCH 31/39] Back to QUrl, fix monitor view overlay --- plugins/MonitorStage/MonitorMainView.qml | 16 ++++++++++++++++ plugins/MonitorStage/MonitorStage.py | 22 +++++++++++++++++++--- resources/qml/Cura.qml | 13 +++++++------ 3 files changed, 42 insertions(+), 9 deletions(-) create mode 100644 plugins/MonitorStage/MonitorMainView.qml diff --git a/plugins/MonitorStage/MonitorMainView.qml b/plugins/MonitorStage/MonitorMainView.qml new file mode 100644 index 0000000000..b5008ba795 --- /dev/null +++ b/plugins/MonitorStage/MonitorMainView.qml @@ -0,0 +1,16 @@ +// Copyright (c) 2017 Ultimaker B.V. + +import QtQuick 2.2 +import QtQuick.Controls 1.1 + +import UM 1.3 as UM +import Cura 1.0 as Cura + +Loader +{ + property real maximumWidth: parent.width + property real maximumHeight: parent.height + + sourceComponent: Cura.MachineManager.printerOutputDevices.length > 0 ? Cura.MachineManager.printerOutputDevices[0].monitorItem: null + visible: sourceComponent != null +} diff --git a/plugins/MonitorStage/MonitorStage.py b/plugins/MonitorStage/MonitorStage.py index dd1eca9682..f577016b2e 100644 --- a/plugins/MonitorStage/MonitorStage.py +++ b/plugins/MonitorStage/MonitorStage.py @@ -2,6 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. import os.path from UM.Application import Application +from UM.PluginRegistry import PluginRegistry from UM.Resources import Resources from cura.Stages.CuraStage import CuraStage @@ -11,14 +12,29 @@ class MonitorStage(CuraStage): def __init__(self, parent = None): super().__init__(parent) - Application.getInstance().engineCreatedSignal.connect(self._engineCreated) + + # Wait until QML engine is created, otherwise creating the new QML components will fail + Application.getInstance().engineCreatedSignal.connect(self._setComponents) + # TODO: connect output device state to icon source - def _engineCreated(self): + def _setComponents(self): + self._setMainOverlay() + self._setSidebar() + self._setIconSource() + + def _setMainOverlay(self): + main_component_path = os.path.join(PluginRegistry.getInstance().getPluginPath("MonitorStage"), "MonitorMainView.qml") + self.addDisplayComponent("main", main_component_path) + + def _setSidebar(self): # Note: currently the sidebar component for prepare and monitor stages is the same, this will change with the printer output device refactor! sidebar_component_path = os.path.join(Resources.getPath(Application.getInstance().ResourceTypes.QmlFiles), "Sidebar.qml") self.addDisplayComponent("sidebar", sidebar_component_path) - self.setIconSource(Application.getInstance().getTheme().getIcon("tab_status_connected")) + + def _setIconSource(self): + if Application.getInstance().getTheme() is not None: + self.setIconSource(Application.getInstance().getTheme().getIcon("tab_status_connected")) # property string iconSource: # // { diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 445325df90..3e21bf0468 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -393,12 +393,13 @@ UM.MainWindow right: sidebar.left } -// MouseArea -// { -// anchors.fill: parent -// acceptedButtons: Qt.AllButtons -// onWheel: wheel.accepted = true -// } + MouseArea + { + visible: UM.Controller.activeStage.mainComponent != "" + anchors.fill: parent + acceptedButtons: Qt.AllButtons + onWheel: wheel.accepted = true + } source: UM.Controller.activeStage.mainComponent asynchronous: true From dd92d8d5e093c225f4ccf86b8519551ff8fb548a Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Thu, 7 Dec 2017 09:22:02 +0100 Subject: [PATCH 32/39] Re-implement monitor view overlay when there is no monitor component --- plugins/MonitorStage/MonitorMainView.qml | 33 ++++++++++++++++++++---- plugins/MonitorStage/MonitorStage.py | 2 +- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/plugins/MonitorStage/MonitorMainView.qml b/plugins/MonitorStage/MonitorMainView.qml index b5008ba795..038403e6d3 100644 --- a/plugins/MonitorStage/MonitorMainView.qml +++ b/plugins/MonitorStage/MonitorMainView.qml @@ -6,11 +6,34 @@ import QtQuick.Controls 1.1 import UM 1.3 as UM import Cura 1.0 as Cura -Loader +Item { - property real maximumWidth: parent.width - property real maximumHeight: parent.height + // We show a nice overlay on the 3D viewer when the current output device has no monitor view + Rectangle + { + id: viewportOverlay - sourceComponent: Cura.MachineManager.printerOutputDevices.length > 0 ? Cura.MachineManager.printerOutputDevices[0].monitorItem: null - visible: sourceComponent != null + color: UM.Theme.getColor("viewport_overlay") + width: parent.width + height: parent.height + visible: monitorViewComponent.sourceComponent == null ? 1 : 0 + + MouseArea + { + anchors.fill: parent + acceptedButtons: Qt.AllButtons + onWheel: wheel.accepted = true + } + } + + Loader + { + id: monitorViewComponent + + property real maximumWidth: parent.width + property real maximumHeight: parent.height + + sourceComponent: Cura.MachineManager.printerOutputDevices.length > 0 ? Cura.MachineManager.printerOutputDevices[0].monitorItem: null + visible: sourceComponent != null + } } diff --git a/plugins/MonitorStage/MonitorStage.py b/plugins/MonitorStage/MonitorStage.py index f577016b2e..f19d1f895e 100644 --- a/plugins/MonitorStage/MonitorStage.py +++ b/plugins/MonitorStage/MonitorStage.py @@ -28,7 +28,7 @@ class MonitorStage(CuraStage): self.addDisplayComponent("main", main_component_path) def _setSidebar(self): - # Note: currently the sidebar component for prepare and monitor stages is the same, this will change with the printer output device refactor! + # TODO: currently the sidebar component for prepare and monitor stages is the same, this will change with the printer output device refactor! sidebar_component_path = os.path.join(Resources.getPath(Application.getInstance().ResourceTypes.QmlFiles), "Sidebar.qml") self.addDisplayComponent("sidebar", sidebar_component_path) From f91a4db6173bc3e264da53eb8368e78b01104975 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Thu, 7 Dec 2017 10:38:06 +0100 Subject: [PATCH 33/39] Set stage icon for monitor depending on output device state, prevent crash when accessing back-end from unloaded component --- plugins/MonitorStage/MonitorStage.py | 75 ++++++++++++++-------------- resources/qml/Cura.qml | 2 - resources/qml/SaveButton.qml | 2 +- 3 files changed, 38 insertions(+), 41 deletions(-) diff --git a/plugins/MonitorStage/MonitorStage.py b/plugins/MonitorStage/MonitorStage.py index f19d1f895e..0736f49858 100644 --- a/plugins/MonitorStage/MonitorStage.py +++ b/plugins/MonitorStage/MonitorStage.py @@ -16,7 +16,8 @@ class MonitorStage(CuraStage): # Wait until QML engine is created, otherwise creating the new QML components will fail Application.getInstance().engineCreatedSignal.connect(self._setComponents) - # TODO: connect output device state to icon source + # Update the status icon when the output device is changed + Application.getInstance().getOutputDeviceManager().activeDeviceChanged.connect(self._setIconSource) def _setComponents(self): self._setMainOverlay() @@ -34,41 +35,39 @@ class MonitorStage(CuraStage): def _setIconSource(self): if Application.getInstance().getTheme() is not None: - self.setIconSource(Application.getInstance().getTheme().getIcon("tab_status_connected")) + icon_name = self._getActiveOutputDeviceStatusIcon() + self.setIconSource(Application.getInstance().getTheme().getIcon(icon_name)) -# property string iconSource: -# // { -# // if (!printerConnected) -# // { -# // return UM.Theme.getIcon("tab_status_unknown"); -# // } -# // else if (!printerAcceptsCommands) -# // { -# // return UM.Theme.getIcon("tab_status_unknown"); -# // } -# // -# // if (Cura.MachineManager.printerOutputDevices[0].printerState == "maintenance") -# // { -# // return UM.Theme.getIcon("tab_status_busy"); -# // } -# // -# // switch (Cura.MachineManager.printerOutputDevices[0].jobState) -# // { -# // case "printing": -# // case "pre_print": -# // case "pausing": -# // case "resuming": -# // return UM.Theme.getIcon("tab_status_busy"); -# // case "wait_cleanup": -# // return UM.Theme.getIcon("tab_status_finished"); -# // case "ready": -# // case "": -# // return UM.Theme.getIcon("tab_status_connected") -# // case "paused": -# // return UM.Theme.getIcon("tab_status_paused") -# // case "error": -# // return UM.Theme.getIcon("tab_status_stopped") -# // default: -# // return UM.Theme.getIcon("tab_status_unknown") -# // } -# // } \ No newline at end of file + ## Find the correct status icon depending on the active output device state + def _getActiveOutputDeviceStatusIcon(self): + output_device = Application.getInstance().getOutputDeviceManager().getActiveDevice() + + if not output_device: + return "tab_status_unknown" + + if hasattr(output_device, "acceptsCommands") and not output_device.acceptsCommands: + return "tab_status_unknown" + + if not hasattr(output_device, "printerState") or not hasattr(output_device, "jobState"): + return "tab_status_unknown" + + # TODO: refactor to use enum instead of hardcoded strings? + if output_device.printerState == "maintenance": + return "tab_status_busy" + + if output_device.jobState in ["printing", "pre_print", "pausing", "resuming"]: + return "tab_status_busy" + + if output_device.jobState == "wait_cleanup": + return "tab_status_finished" + + if output_device.jobState in ["ready", ""]: + return "tab_status_connected" + + if output_device.jobState == "paused": + return "tab_status_paused" + + if output_device.jobState == "error": + return "tab_status_stopped" + + return "tab_status_unknown" diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 3e21bf0468..0bab1ee5d7 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -378,7 +378,6 @@ UM.MainWindow z: 1 source: UM.Controller.activeStage.sidebarComponent - asynchronous: true } Loader @@ -402,7 +401,6 @@ UM.MainWindow } source: UM.Controller.activeStage.mainComponent - asynchronous: true } UM.MessageStack diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml index b258ecad43..390b868a9b 100644 --- a/resources/qml/SaveButton.qml +++ b/resources/qml/SaveButton.qml @@ -46,7 +46,7 @@ Item { } function sliceOrStopSlicing() { - if ([1, 5].indexOf(UM.Backend.state) != -1) { + if (backend != "undefined" && [1, 5].indexOf(UM.Backend.state) != -1) { backend.forceSlice(); } else { backend.stopSlicing(); From 71ae78f7b6213884ba126d2d0168b95b1c8e8ee2 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Thu, 7 Dec 2017 10:45:54 +0100 Subject: [PATCH 34/39] Fix monitoring sidebar top margin --- resources/qml/Sidebar.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index 6bc0903059..8744b17906 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -282,7 +282,7 @@ Rectangle Loader { anchors.bottom: footerSeparator.top - anchors.top: headerSeparator.bottom + anchors.top: monitoringPrint ? base.top : headerSeparator.bottom anchors.left: base.left anchors.right: base.right source: From 7a6d75fd08cbae3c6e55ff09c1c3f4857c939033 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Thu, 7 Dec 2017 11:02:52 +0100 Subject: [PATCH 35/39] Fix sidebar top margin for um3 network monitor controls --- resources/qml/Sidebar.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index 8744b17906..a459b481db 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -263,7 +263,7 @@ Rectangle { id: controlItem anchors.bottom: footerSeparator.top - anchors.top: headerSeparator.bottom + anchors.top: monitoringPrint ? base.top : headerSeparator.bottom anchors.left: base.left anchors.right: base.right sourceComponent: From faf77b27954491eac9f19122a33241528391c6e1 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Thu, 7 Dec 2017 11:50:01 +0100 Subject: [PATCH 36/39] Cleanup --- resources/qml/Topbar.qml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/resources/qml/Topbar.qml b/resources/qml/Topbar.qml index 9ad91615ad..99910b24a2 100644 --- a/resources/qml/Topbar.qml +++ b/resources/qml/Topbar.qml @@ -21,18 +21,6 @@ Rectangle property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands - // update monitoring status when event was triggered outside topbar -// Component.onCompleted: { -// startMonitoringPrint.connect(function () { -// base.monitoringPrint = true -// UM.Controller.disableModelRendering() -// }) -// stopMonitoringPrint.connect(function () { -// base.monitoringPrint = false -// UM.Controller.enableModelRendering() -// }) -// } - UM.I18nCatalog { id: catalog From acbef72b68e041ff5502f961464e61116611b973 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Thu, 7 Dec 2017 11:52:15 +0100 Subject: [PATCH 37/39] Cleanup --- resources/qml/Cura.qml | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 0bab1ee5d7..f5fa922794 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -18,19 +18,6 @@ UM.MainWindow //: Cura application window title title: catalog.i18nc("@title:window","Ultimaker Cura"); viewportRect: Qt.rect(0, 0, (base.width - sidebar.width) / base.width, 1.0) - property bool showPrintMonitor: false - - Connections - { - target: Printer - onShowPrintMonitor: { - if (show) { - topbar.startMonitoringPrint() - } else { - topbar.stopMonitoringPrint() - } - } - } Component.onCompleted: { From 9bf954643c2fc2b71f938980efb68350a2fbcd6e Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Thu, 7 Dec 2017 11:56:52 +0100 Subject: [PATCH 38/39] Use new method of setting active stage when needing to switch to monitor --- cura/CuraApplication.py | 1 - .../UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py | 2 +- plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py | 6 +++--- plugins/USBPrinting/USBPrinterOutputDevice.py | 4 ++-- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index c9466a0093..a5ae286b1e 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -391,7 +391,6 @@ class CuraApplication(QtApplication): def needToShowUserAgreement(self): return self._need_to_show_user_agreement - def setNeedToShowUserAgreement(self, set_value = True): self._need_to_show_user_agreement = set_value diff --git a/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py b/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py index 44a2e8b743..853ef72f72 100644 --- a/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py +++ b/plugins/UM3NetworkPrinting/NetworkClusterPrinterOutputDevice.py @@ -698,7 +698,7 @@ class NetworkClusterPrinterOutputDevice(NetworkPrinterOutputDevice.NetworkPrinte if self._reply: self._reply.abort() self._stage = OutputStage.ready - Application.getInstance().showPrintMonitor.emit(False) + Application.getInstance().getController().setActiveStage("PrepareStage") @pyqtSlot(int, result=str) def formatDuration(self, seconds): diff --git a/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py b/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py index d8dd780ed5..3a48bab11b 100755 --- a/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py +++ b/plugins/UM3NetworkPrinting/NetworkPrinterOutputDevice.py @@ -672,7 +672,7 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice): Logger.log("d", "Attempting to perform an action without authentication for printer %s. Auth state is %s", self._key, self._authentication_state) return - Application.getInstance().showPrintMonitor.emit(True) + Application.getInstance().getController().setActiveStage("MonitorStage") self._print_finished = True self.writeStarted.emit(self) self._gcode = getattr(Application.getInstance().getController().getScene(), "gcode_list") @@ -767,7 +767,7 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice): if button == QMessageBox.Yes: self.startPrint() else: - Application.getInstance().showPrintMonitor.emit(False) + Application.getInstance().getController().setActiveStage("PrepareStage") # For some unknown reason Cura on OSX will hang if we do the call back code # immediately without first returning and leaving QML's event system. QTimer.singleShot(100, delayedCallback) @@ -850,7 +850,7 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice): self._write_finished = True # post_reply does not always exist, so make sure we unblock writing if self._post_reply: self._finalizePostReply() - Application.getInstance().showPrintMonitor.emit(False) + Application.getInstance().getController().setActiveStage("PrepareStage") ## Attempt to start a new print. # This function can fail to actually start a print due to not being authenticated or another print already diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index 77f45ee6d4..1930f5402b 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -490,7 +490,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): self.setJobName(file_name) self._print_estimated_time = int(Application.getInstance().getPrintInformation().currentPrintTime.getDisplayString(DurationFormat.Format.Seconds)) - Application.getInstance().showPrintMonitor.emit(True) + Application.getInstance().getController().setActiveStage("MonitorStage") self.startPrint() def _setEndstopState(self, endstop_key, value): @@ -698,7 +698,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): self._is_printing = False self._is_paused = False self._updateJobState("ready") - Application.getInstance().showPrintMonitor.emit(False) + Application.getInstance().getController().setActiveStage("PrepareStage") ## Check if the process did not encounter an error yet. def hasError(self): From c6e72abfbe8d8a8a58c7de67070402adb243f1f2 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Thu, 7 Dec 2017 12:00:07 +0100 Subject: [PATCH 39/39] Add switch to monitor support for legacy plugin that emit signal to switch --- resources/qml/Cura.qml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index f5fa922794..aedf5d0fa8 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -18,6 +18,21 @@ UM.MainWindow //: Cura application window title title: catalog.i18nc("@title:window","Ultimaker Cura"); viewportRect: Qt.rect(0, 0, (base.width - sidebar.width) / base.width, 1.0) + property bool showPrintMonitor: false + + // This connection is here to support legacy printer output devices that use the showPrintMonitor signal on Application to switch to the monitor stage + // It should be phased out in newer plugin versions. + Connections + { + target: Printer + onShowPrintMonitor: { + if (show) { + UM.Controller.setActiveStage("MonitorStage") + } else { + UM.Controller.setActiveStage("PrepareStage") + } + } + } Component.onCompleted: {