diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 464694dbcd..a2dc27f483 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -2,6 +2,8 @@ # Cura is released under the terms of the LGPLv3 or higher. #Type hinting. from typing import Dict + +from PyQt5.QtCore import QObject from PyQt5.QtNetwork import QLocalServer from PyQt5.QtNetwork import QLocalSocket @@ -540,8 +542,6 @@ class CuraApplication(QtApplication): has_user_interaction = True return has_user_interaction - onDiscardOrKeepProfileChangesClosed = pyqtSignal() # Used to notify other managers that the dialog was closed - @pyqtSlot(str) def discardOrKeepProfileChangesClosed(self, option): if option == "discard": @@ -564,7 +564,6 @@ class CuraApplication(QtApplication): user_global_container.update() # notify listeners that quality has changed (after user selected discard or keep) - self.onDiscardOrKeepProfileChangesClosed.emit() self.getMachineManager().activeQualityChanged.emit() @pyqtSlot(int) @@ -862,11 +861,13 @@ class CuraApplication(QtApplication): self._object_manager = ObjectsModel.createObjectsModel() return self._object_manager + @pyqtSlot(result = QObject) def getMultiBuildPlateModel(self, *args): if self._multi_build_plate_model is None: self._multi_build_plate_model = MultiBuildPlateModel(self) return self._multi_build_plate_model + @pyqtSlot(result = QObject) def getBuildPlateModel(self, *args): if self._build_plate_model is None: self._build_plate_model = BuildPlateModel(self) @@ -939,8 +940,8 @@ class CuraApplication(QtApplication): qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, "MachineActionManager", self.getMachineActionManager) qmlRegisterSingletonType(ObjectsModel, "Cura", 1, 0, "ObjectsModel", self.getObjectsModel) - qmlRegisterSingletonType(BuildPlateModel, "Cura", 1, 0, "BuildPlateModel", self.getBuildPlateModel) - qmlRegisterSingletonType(MultiBuildPlateModel, "Cura", 1, 0, "MultiBuildPlateModel", self.getMultiBuildPlateModel) + qmlRegisterType(BuildPlateModel, "Cura", 1, 0, "BuildPlateModel") + qmlRegisterType(MultiBuildPlateModel, "Cura", 1, 0, "MultiBuildPlateModel") qmlRegisterType(InstanceContainer, "Cura", 1, 0, "InstanceContainer") qmlRegisterType(ExtrudersModel, "Cura", 1, 0, "ExtrudersModel") diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 7ca9040d2a..f2446b8017 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -54,12 +54,6 @@ class MachineManager(QObject): self.machine_extruder_material_update_dict = collections.defaultdict(list) - # Used to store the new containers until after confirming the dialog - self._new_variant_container = None # type: Optional[InstanceContainer] - self._new_buildplate_container = None # type: Optional[InstanceContainer] - self._new_material_container = None # type: Optional[InstanceContainer] - self._new_quality_containers = [] # type: List[Dict] - self._error_check_timer = QTimer() self._error_check_timer.setInterval(250) self._error_check_timer.setSingleShot(True) @@ -79,6 +73,9 @@ class MachineManager(QObject): self.globalContainerChanged.connect(self.activeVariantChanged) self.globalContainerChanged.connect(self.activeQualityChanged) + self.globalContainerChanged.connect(self.activeQualityChangesGroupChanged) + self.globalContainerChanged.connect(self.activeQualityGroupChanged) + self._stacks_have_errors = None # type:Optional[bool] self._empty_definition_changes_container = ContainerRegistry.getInstance().findContainers(id = "empty_definition_changes")[0] @@ -101,9 +98,6 @@ class MachineManager(QObject): ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeStackChanged) self.activeStackChanged.connect(self.activeStackValueChanged) - # when a user closed dialog check if any delayed material or variant changes need to be applied - Application.getInstance().onDiscardOrKeepProfileChangesClosed.connect(self._executeDelayedActiveContainerStackChanges) - Preferences.getInstance().addPreference("cura/active_machine", "") self._global_event_keys = set() @@ -169,18 +163,6 @@ class MachineManager(QObject): self.outputDevicesChanged.emit() - @property - def newVariant(self): - return self._new_variant_container - - @property - def newBuildplate(self): - return self._new_buildplate_container - - @property - def newMaterial(self): - return self._new_material_container - @pyqtProperty("QVariantList", notify = outputDevicesChanged) def printerOutputDevices(self): return self._printer_output_devices @@ -242,6 +224,7 @@ class MachineManager(QObject): Application.getInstance().callLater(func) del self.machine_extruder_material_update_dict[self._global_container_stack.getId()] + self.activeQualityGroupChanged.emit() self._error_check_timer.start() ## Update self._stacks_valid according to _checkStacksForErrors and emit if change. @@ -290,26 +273,26 @@ class MachineManager(QObject): material_dict[position] = extruder.material.getMetaDataEntry("base_file") self._current_root_material_id = material_dict global_quality = global_stack.quality - global_quality_changes = global_stack.qualityChanges - - quality_groups = self._application._quality_manager.getQualityGroups(global_stack) quality_type = global_quality.getMetaDataEntry("quality_type") - if not quality_type in quality_groups: - Logger.log("w", "Quality type [%s] not found in available qualities [%s]", quality_type, str(quality_groups.values())) - return - new_quality_group = quality_groups[quality_type] - self._setQualityGroup(new_quality_group) + global_quality_changes = global_stack.qualityChanges + global_quality_changes_name = global_quality_changes.getName() if global_quality_changes.getId() != "empty_quality_changes": quality_changes_groups = self._application._quality_manager.getQualityChangesGroups(global_stack) - if quality_type in quality_changes_groups: - new_quality_changes_group = quality_changes_groups[quality_type] + if global_quality_changes_name in quality_changes_groups: + new_quality_changes_group = quality_changes_groups[global_quality_changes_name] self._setQualityChangesGroup(new_quality_changes_group) + else: + quality_groups = self._application._quality_manager.getQualityGroups(global_stack) + if quality_type not in quality_groups: + Logger.log("w", "Quality type [%s] not found in available qualities [%s]", quality_type, str(quality_groups.values())) + return + new_quality_group = quality_groups[quality_type] + self._setQualityGroup(new_quality_group) @pyqtSlot(str) def setActiveMachine(self, stack_id: str) -> None: self.blurSettings.emit() # Ensure no-one has focus. - self._cancelDelayedActiveContainerStackChanges() container_registry = ContainerRegistry.getInstance() @@ -491,7 +474,7 @@ class MachineManager(QObject): # # \return The layer height of the currently active quality profile. If # there is no quality profile, this returns 0. - @pyqtProperty(float, notify=activeQualityChanged) + @pyqtProperty(float, notify = activeQualityGroupChanged) def activeQualityLayerHeight(self) -> float: if not self._global_container_stack: return 0 @@ -511,34 +494,7 @@ class MachineManager(QObject): return 0 # No quality profile. - @pyqtProperty(str, notify=activeQualityChanged) - def activeQualityId(self) -> str: - if self._active_container_stack: - quality = self._active_container_stack.quality - if isinstance(quality, type(self._empty_quality_container)): - return "" - quality_changes = self._active_container_stack.qualityChanges - if quality and quality_changes: - if isinstance(quality_changes, type(self._empty_quality_changes_container)): - # It's a built-in profile - return quality.getId() - else: - # Custom profile - return quality_changes.getId() - return "" - - @pyqtProperty(str, notify=activeQualityChanged) - def globalQualityId(self) -> str: - if self._global_container_stack: - quality = self._global_container_stack.qualityChanges - if quality and not isinstance(quality, type(self._empty_quality_changes_container)): - return quality.getId() - quality = self._global_container_stack.quality - if quality: - return quality.getId() - return "" - - @pyqtProperty(str, notify=activeVariantChanged) + @pyqtProperty(str, notify = activeVariantChanged) def globalVariantName(self) -> str: if self._global_container_stack: variant = self._global_container_stack.variant @@ -546,21 +502,21 @@ class MachineManager(QObject): return variant.getName() return "" - @pyqtProperty(str, notify = activeQualityChanged) + @pyqtProperty(str, notify = activeQualityGroupChanged) def activeQualityType(self) -> str: + quality_type = "" if self._active_container_stack: - quality = self._active_container_stack.quality - if quality: - return quality.getMetaDataEntry("quality_type") - return "" + if self._current_quality_group: + quality_type = self._current_quality_group.quality_type + return quality_type - @pyqtProperty(bool, notify = activeQualityChanged) + @pyqtProperty(bool, notify = activeQualityGroupChanged) def isActiveQualitySupported(self) -> bool: - if self._active_container_stack: - quality = self._active_container_stack.quality - if quality: - return Util.parseBool(quality.getMetaDataEntry("supported", True)) - return False + is_supported = False + if self._global_container_stack: + if self._current_quality_group: + is_supported = self._current_quality_group.is_available + return is_supported ## Returns whether there is anything unsupported in the current set-up. # @@ -595,61 +551,6 @@ class MachineManager(QObject): if extruder_stack != self._active_container_stack and extruder_stack.getProperty(key, "value") != new_value: extruder_stack.userChanges.setProperty(key, "value", new_value) # TODO: nested property access, should be improved - ## Used to update material and variant in the active container stack with a delay. - # This delay prevents the stack from triggering a lot of signals (eventually resulting in slicing) - # before the user decided to keep or discard any of their changes using the dialog. - # The Application.onDiscardOrKeepProfileChangesClosed signal triggers this method. - def _executeDelayedActiveContainerStackChanges(self): - Logger.log("d", "Applying configuration changes...") - - if self._new_variant_container is not None: - self._active_container_stack.variant = self._new_variant_container - self._new_variant_container = None - - if self._new_buildplate_container is not None: - self._global_container_stack.variant = self._new_buildplate_container - self._new_buildplate_container = None - - if self._new_material_container is not None: - self._active_container_stack.material = self._new_material_container - self._new_material_container = None - - # apply the new quality to all stacks - if self._new_quality_containers: - for new_quality in self._new_quality_containers: - self._replaceQualityOrQualityChangesInStack(new_quality["stack"], new_quality["quality"], postpone_emit = True) - self._replaceQualityOrQualityChangesInStack(new_quality["stack"], new_quality["quality_changes"], postpone_emit = True) - - for new_quality in self._new_quality_containers: - new_quality["stack"].nameChanged.connect(self._onQualityNameChanged) - new_quality["stack"].sendPostponedEmits() # Send the signals that were postponed in _replaceQualityOrQualityChangesInStack - - self._new_quality_containers.clear() - - Logger.log("d", "New configuration applied") - - ## Cancel set changes for material and variant in the active container stack. - # Used for ignoring any changes when switching between printers (setActiveMachine) - def _cancelDelayedActiveContainerStackChanges(self): - self._new_material_container = None - self._new_buildplate_container = None - self._new_variant_container = None - - def _replaceQualityOrQualityChangesInStack(self, stack: "CuraContainerStack", container: "InstanceContainer", postpone_emit = False): - # Disconnect the signal handling from the old container. - container_type = container.getMetaDataEntry("type") - if container_type == "quality": - stack.quality.nameChanged.disconnect(self._onQualityNameChanged) - stack.setQuality(container, postpone_emit = postpone_emit) - stack.quality.nameChanged.connect(self._onQualityNameChanged) - elif container_type == "quality_changes" or container_type is None: - # If the container is an empty container, we need to change the quality_changes. - # Quality can never be set to empty. - stack.qualityChanges.nameChanged.disconnect(self._onQualityNameChanged) - stack.setQualityChanges(container, postpone_emit = postpone_emit) - stack.qualityChanges.nameChanged.connect(self._onQualityNameChanged) - self._onQualityNameChanged() - @pyqtProperty(str, notify = activeVariantChanged) def activeVariantName(self) -> str: if self._active_container_stack: @@ -945,6 +846,7 @@ class MachineManager(QObject): extruder.qualityChanges = self._empty_quality_changes_container self.activeQualityGroupChanged.emit() + self.activeQualityChangesGroupChanged.emit() def _setQualityGroup(self, quality_group, empty_quality_changes = True): self._current_quality_group = quality_group diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index 05ff5050e9..23c19bece8 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -173,7 +173,7 @@ Item Action { id: updateProfileAction; - enabled: !Cura.MachineManager.stacksHaveErrors && Cura.MachineManager.hasUserSettings && !Cura.MachineManager.isReadOnly(Cura.MachineManager.activeQualityId) + enabled: !Cura.MachineManager.stacksHaveErrors && Cura.MachineManager.hasUserSettings && Cura.MachineManager.activeQualityChangesGroup != null text: catalog.i18nc("@action:inmenu menubar:profile","&Update profile with current settings/overrides"); onTriggered: Cura.ContainerManager.updateQualityChanges(); } diff --git a/resources/qml/Menus/BuildplateMenu.qml b/resources/qml/Menus/BuildplateMenu.qml index 8dfc3ced6c..0b67b37cc1 100644 --- a/resources/qml/Menus/BuildplateMenu.qml +++ b/resources/qml/Menus/BuildplateMenu.qml @@ -12,17 +12,19 @@ Menu id: menu title: "Build plate" + property Cura.BuildPlateModel buildPlateModel: CuraApplication.getBuildPlateModel() + Instantiator { - model: Cura.BuildPlateModel + model: menu.buildPlateModel MenuItem { text: model.name checkable: true - checked: model.name == Cura.MachineManager.globalVariantName // TODO + checked: model.name == Cura.MachineManager.globalVariantName exclusiveGroup: group onTriggered: { - Cura.MachineManager.setGlobalVariant(model.container_node); // TODO + Cura.MachineManager.setGlobalVariant(model.container_node); } } diff --git a/resources/qml/Menus/ContextMenu.qml b/resources/qml/Menus/ContextMenu.qml index a37204d255..656e94b336 100644 --- a/resources/qml/Menus/ContextMenu.qml +++ b/resources/qml/Menus/ContextMenu.qml @@ -15,6 +15,8 @@ Menu property bool shouldShowExtruders: machineExtruderCount.properties.value > 1; + property Cura.MultiBuildPlateModel multiBuildPlateModel: CuraApplication.getMultiBuildPlateModel() + // Selection-related actions. MenuItem { action: Cura.Actions.centerSelection; } MenuItem { action: Cura.Actions.deleteSelection; } @@ -45,13 +47,13 @@ Menu Instantiator { - model: Cura.MultiBuildPlateModel + model: base.multiBuildPlateModel MenuItem { enabled: UM.Selection.hasSelection - text: Cura.MultiBuildPlateModel.getItem(index).name; - onTriggered: CuraActions.setBuildPlateForSelection(Cura.MultiBuildPlateModel.getItem(index).buildPlateNumber); + text: base.multiBuildPlateModel.getItem(index).name; + onTriggered: CuraActions.setBuildPlateForSelection(base.multiBuildPlateModel.getItem(index).buildPlateNumber); checkable: true - checked: Cura.MultiBuildPlateModel.selectionBuildPlates.indexOf(Cura.MultiBuildPlateModel.getItem(index).buildPlateNumber) != -1; + checked: base.multiBuildPlateModel.selectionBuildPlates.indexOf(base.multiBuildPlateModel.getItem(index).buildPlateNumber) != -1; visible: UM.Preferences.getValue("cura/use_multi_build_plate") } onObjectAdded: base.insertItem(index, object); @@ -62,7 +64,7 @@ Menu enabled: UM.Selection.hasSelection text: "New build plate"; onTriggered: { - CuraActions.setBuildPlateForSelection(Cura.MultiBuildPlateModel.maxBuildPlate + 1); + CuraActions.setBuildPlateForSelection(base.multiBuildPlateModel.maxBuildPlate + 1); checked = false; } checkable: true diff --git a/resources/qml/Menus/ViewMenu.qml b/resources/qml/Menus/ViewMenu.qml index ff749bec90..fc0339e0c6 100644 --- a/resources/qml/Menus/ViewMenu.qml +++ b/resources/qml/Menus/ViewMenu.qml @@ -13,6 +13,8 @@ Menu id: base enabled: !PrintInformation.preSliced + property Cura.MultiBuildPlateModel multiBuildPlateModel: CuraApplication.getMultiBuildPlateModel() + // main views Instantiator { @@ -53,12 +55,12 @@ Menu visible: UM.Preferences.getValue("cura/use_multi_build_plate") Instantiator { - model: Cura.MultiBuildPlateModel + model: base.multiBuildPlateModel MenuItem { - text: Cura.MultiBuildPlateModel.getItem(index).name; - onTriggered: Cura.SceneController.setActiveBuildPlate(Cura.MultiBuildPlateModel.getItem(index).buildPlateNumber); + text: base.multiBuildPlateModel.getItem(index).name; + onTriggered: Cura.SceneController.setActiveBuildPlate(base.multiBuildPlateModel.getItem(index).buildPlateNumber); checkable: true; - checked: Cura.MultiBuildPlateModel.getItem(index).buildPlateNumber == Cura.MultiBuildPlateModel.activeBuildPlate; + checked: base.multiBuildPlateModel.getItem(index).buildPlateNumber == base.multiBuildPlateModel.activeBuildPlate; exclusiveGroup: buildPlateGroup; visible: UM.Preferences.getValue("cura/use_multi_build_plate") } diff --git a/resources/qml/ObjectsList.qml b/resources/qml/ObjectsList.qml index f51e1081e6..e7dd63ea05 100644 --- a/resources/qml/ObjectsList.qml +++ b/resources/qml/ObjectsList.qml @@ -33,6 +33,8 @@ Rectangle property bool collapsed: true; + property Cura.MultiBuildPlateModel multiBuildPlateModel: CuraApplication.getMultiBuildPlateModel() + SystemPalette { id: palette } Button { @@ -67,7 +69,7 @@ Rectangle Rectangle { height: childrenRect.height - color: Cura.MultiBuildPlateModel.getItem(index).buildPlateNumber == Cura.MultiBuildPlateModel.activeBuildPlate ? palette.highlight : index % 2 ? palette.base : palette.alternateBase + color: multiBuildPlateModel.getItem(index).buildPlateNumber == multiBuildPlateModel.activeBuildPlate ? palette.highlight : index % 2 ? palette.base : palette.alternateBase width: parent.width Label { @@ -75,8 +77,8 @@ Rectangle anchors.left: parent.left anchors.leftMargin: UM.Theme.getSize("default_margin").width width: parent.width - 2 * UM.Theme.getSize("default_margin").width - 30 - text: Cura.MultiBuildPlateModel.getItem(index) ? Cura.MultiBuildPlateModel.getItem(index).name : ""; - color: Cura.MultiBuildPlateModel.activeBuildPlate == index ? palette.highlightedText : palette.text + text: multiBuildPlateModel.getItem(index) ? multiBuildPlateModel.getItem(index).name : ""; + color: multiBuildPlateModel.activeBuildPlate == index ? palette.highlightedText : palette.text elide: Text.ElideRight } @@ -118,13 +120,12 @@ Rectangle ListView { id: buildPlateListView - model: Cura.MultiBuildPlateModel + model: multiBuildPlateModel width: parent.width delegate: buildPlateDelegate } } - Component { id: objectDelegate Rectangle @@ -200,7 +201,6 @@ Rectangle } } - CheckBox { id: filterBuildPlateCheckbox @@ -260,6 +260,4 @@ Rectangle } action: Cura.Actions.arrangeAll; } - - } diff --git a/resources/qml/Preferences/ProfileTab.qml b/resources/qml/Preferences/ProfileTab.qml index 5acbeecda9..b4b2299c15 100644 --- a/resources/qml/Preferences/ProfileTab.qml +++ b/resources/qml/Preferences/ProfileTab.qml @@ -36,8 +36,8 @@ Tab anchors.leftMargin: UM.Theme.getSize("default_margin").width anchors.right: parent.right text: (styleData.value.substr(0,1) == "=") ? catalog.i18nc("@info:status", "Calculated") : styleData.value - font.strikeout: styleData.column == 1 && setting.user_value != "" // TODO && quality == Cura.MachineManager.globalQualityId - font.italic: setting.profile_value_source == "quality_changes" || (setting.user_value != "") // TODO: (setting.user_value != "" && quality == Cura.MachineManager.globalQualityId) + font.strikeout: styleData.column == 1 && setting.user_value != "" && qualityItem.name == Cura.MachineManager.activeQualityOrQualityChangesName + font.italic: setting.profile_value_source == "quality_changes" || (setting.user_value != "" && qualityItem.name == Cura.MachineManager.activeQualityOrQualityChangesName) opacity: font.strikeout ? 0.5 : 1 color: styleData.textColor elide: Text.ElideRight @@ -63,7 +63,7 @@ Tab { role: "user_value" title: catalog.i18nc("@title:column", "Current"); - visible: true // TODO quality == Cura.MachineManager.globalQualityId + visible: qualityItem.name == Cura.MachineManager.activeQualityOrQualityChangesName width: (parent.width * 0.18) | 0 delegate: itemDelegate } diff --git a/resources/qml/Preferences/ProfilesPage.qml b/resources/qml/Preferences/ProfilesPage.qml index 1d29b26e50..6a42034846 100644 --- a/resources/qml/Preferences/ProfilesPage.qml +++ b/resources/qml/Preferences/ProfilesPage.qml @@ -408,14 +408,14 @@ Item height: childrenRect.height Label { - text: base.currentItem.name // TODO + text: base.currentItem.name font: UM.Theme.getFont("large") } } Flow { id: currentSettingsActions - visible: true // TODO //currentItem && currentItem.id == Cura.MachineManager.activeQualityId + visible: base.hasCurrentItem && base.currentItem.name == Cura.MachineManager.activeQualityOrQualityChangesName anchors.left: parent.left anchors.right: parent.right anchors.top: profileName.bottom @@ -424,7 +424,7 @@ Item Button { text: catalog.i18nc("@action:button", "Update profile with current settings/overrides") - enabled: Cura.MachineManager.hasUserSettings && !Cura.MachineManager.isReadOnly(Cura.MachineManager.activeQualityId) + enabled: Cura.MachineManager.hasUserSettings && !base.currentItem.is_read_only onClicked: Cura.ContainerManager.updateQualityChanges() } @@ -453,7 +453,7 @@ Item } Label { id: noCurrentSettingsMessage - visible: currentItem && currentItem.id == Cura.MachineManager.activeQualityId && !Cura.MachineManager.hasUserSettings + visible: base.isCurrentItemActivated && !Cura.MachineManager.hasUserSettings text: catalog.i18nc("@action:label", "Your current settings match the selected profile.") wrapMode: Text.WordWrap width: parent.width diff --git a/resources/qml/SidebarAdvanced.qml b/resources/qml/SidebarAdvanced.qml index ff5f545c80..ae77bc8d1b 100644 --- a/resources/qml/SidebarAdvanced.qml +++ b/resources/qml/SidebarAdvanced.qml @@ -1,7 +1,7 @@ // Copyright (c) 2015 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.7 +import QtQuick 2.8 import QtQuick.Controls 2.0 import "Settings"