From d69e397716120d542a74734f6c7ec100ac663beb Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 4 Dec 2017 10:31:21 +0100 Subject: [PATCH 01/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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/43] 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 9a538b330aeee88494e452ef4133139a14548db9 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 5 Dec 2017 14:14:52 +0100 Subject: [PATCH 16/43] Ignore new plugins --- .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 ab5449b01e75bfa2e2834b2efcd7875707d10dcd Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 09:10:19 +0100 Subject: [PATCH 17/43] 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 18/43] 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 19/43] 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 20/43] 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 21/43] 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 22/43] 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 23/43] 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 24/43] 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 25/43] 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 26/43] 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 9561827bdad91604fc70e0c448c9b8c657c83a83 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 6 Dec 2017 14:12:51 +0100 Subject: [PATCH 27/43] CURA-4680 Checking if there is global stack in the ExtruderManager. Intead of checking for it in all the methods in MachineManager, now the check is done in ExtruderManager when there is no printer in the list. --- cura/Settings/ExtruderManager.py | 8 +++++--- cura/Settings/MachineManager.py | 19 ------------------- 2 files changed, 5 insertions(+), 22 deletions(-) diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index 34b283107d..32e3867300 100755 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -356,14 +356,16 @@ class ExtruderManager(QObject): # \return \type{List[ContainerStack]} a list of def getActiveExtruderStacks(self) -> List["ExtruderStack"]: global_stack = Application.getInstance().getGlobalContainerStack() + if not global_stack: + return None result = [] - machine_extruder_count = global_stack.getProperty("machine_extruder_count", "value") - - if global_stack and global_stack.getId() in self._extruder_trains: + if global_stack.getId() in self._extruder_trains: for extruder in sorted(self._extruder_trains[global_stack.getId()]): result.append(self._extruder_trains[global_stack.getId()][extruder]) + machine_extruder_count = global_stack.getProperty("machine_extruder_count", "value") + return result[:machine_extruder_count] def __globalContainerStackChanged(self) -> None: diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 253c5f793c..afd038b45d 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -499,10 +499,6 @@ class MachineManager(QObject): def activeVariantNames(self) -> List[str]: result = [] - # it can happen when there is no active machine - if self._global_container_stack is None: - return result - active_stacks = ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks() if active_stacks is not None: for stack in active_stacks: @@ -516,10 +512,6 @@ class MachineManager(QObject): def activeMaterialNames(self) -> List[str]: result = [] - # it can happen when there is no active machine - if self._global_container_stack is None: - return result - active_stacks = ExtruderManager.getInstance().getActiveGlobalAndExtruderStacks() if active_stacks is not None: for stack in active_stacks: @@ -541,10 +533,6 @@ class MachineManager(QObject): def allActiveVariantIds(self) -> Dict[str, str]: result = {} - # it can happen when there is no active machine - if self._global_container_stack is None: - return result - active_stacks = ExtruderManager.getInstance().getActiveExtruderStacks() if active_stacks is not None: #If we have a global stack. for stack in active_stacks: @@ -564,14 +552,7 @@ class MachineManager(QObject): def allActiveMaterialIds(self) -> Dict[str, str]: result = {} - # it can happen when there is no active machine - if self._global_container_stack is None: - return result - active_stacks = ExtruderManager.getInstance().getActiveExtruderStacks() - - result[self._global_container_stack.getId()] = self._global_container_stack.material.getId() - if active_stacks is not None: # If we have extruder stacks for stack in active_stacks: material_container = stack.material From 569e040955f9f924040d18b52f3e30f54fe1a196 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 6 Dec 2017 14:45:47 +0100 Subject: [PATCH 28/43] Add more complete version info in project files CURA-4683 --- plugins/3MFWriter/ThreeMFWorkspaceWriter.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/3MFWriter/ThreeMFWorkspaceWriter.py b/plugins/3MFWriter/ThreeMFWorkspaceWriter.py index 9c143f0057..3953bea229 100644 --- a/plugins/3MFWriter/ThreeMFWorkspaceWriter.py +++ b/plugins/3MFWriter/ThreeMFWorkspaceWriter.py @@ -59,7 +59,9 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter): version_file = zipfile.ZipInfo("Cura/version.ini") version_config_parser = configparser.ConfigParser() version_config_parser.add_section("versions") - version_config_parser.set("versions", "cura_version", Application.getStaticVersion()) + version_config_parser.set("versions", "cura_version", Application.getInstance().getVersion()) + version_config_parser.set("versions", "build_type", Application.getInstance().getBuildType()) + version_config_parser.set("versions", "is_debug_mode", Application.getInstance().getIsDebugMode()) version_file_string = StringIO() version_config_parser.write(version_file_string) From 298a659c094bb31e7ad86b4688a40a9a12161dfc Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 6 Dec 2017 14:48:58 +0100 Subject: [PATCH 29/43] Save is_debug_mode as string CURA-4683 --- plugins/3MFWriter/ThreeMFWorkspaceWriter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/3MFWriter/ThreeMFWorkspaceWriter.py b/plugins/3MFWriter/ThreeMFWorkspaceWriter.py index 3953bea229..4f1ff9494f 100644 --- a/plugins/3MFWriter/ThreeMFWorkspaceWriter.py +++ b/plugins/3MFWriter/ThreeMFWorkspaceWriter.py @@ -61,7 +61,7 @@ class ThreeMFWorkspaceWriter(WorkspaceWriter): version_config_parser.add_section("versions") version_config_parser.set("versions", "cura_version", Application.getInstance().getVersion()) version_config_parser.set("versions", "build_type", Application.getInstance().getBuildType()) - version_config_parser.set("versions", "is_debug_mode", Application.getInstance().getIsDebugMode()) + version_config_parser.set("versions", "is_debug_mode", str(Application.getInstance().getIsDebugMode())) version_file_string = StringIO() version_config_parser.write(version_file_string) From ee643610e5a4de384aa09f5ef5399c94a4972574 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 6 Dec 2017 17:46:18 +0100 Subject: [PATCH 30/43] 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 31/43] 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 32/43] 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 33/43] 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 34/43] 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 35/43] 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 36/43] 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 37/43] 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 38/43] 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 39/43] 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 40/43] 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 41/43] 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 42/43] 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 43/43] 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: {