diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 57bf4be5bb..caf411bc67 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -143,12 +143,14 @@ class ThreeMFWorkspaceReader(WorkspaceReader): self._machine_info = None self._load_profile = False + self._user_settings: Dict[str, Dict[str, Any]] = {} def _clearState(self): self._id_mapping = {} self._old_new_materials = {} self._machine_info = None self._load_profile = False + self._user_settings = {} def getNewId(self, old_id: str): """Get a unique name based on the old_id. This is different from directly calling the registry in that it caches results. @@ -604,6 +606,36 @@ class ThreeMFWorkspaceReader(WorkspaceReader): package_metadata = self._parse_packages_metadata(archive) missing_package_metadata = self._filter_missing_package_metadata(package_metadata) + # Load the user specifically exported settings + self._dialog.exportedSettingModel.clear() + if is_pcb: + try: + self._user_settings = json.loads(archive.open("Cura/user-settings.json").read().decode("utf-8")) + any_extruder_stack = ExtruderManager.getInstance().getExtruderStack(0) + + for stack_name, settings in self._user_settings.items(): + if stack_name == 'global': + self._dialog.exportedSettingModel.addSettingsFromStack(global_stack, i18n_catalog.i18nc("@label", "Global"), settings) + else: + extruder_match = re.fullmatch('extruder_([0-9]+)', stack_name) + if extruder_match is not None: + extruder_nr = int(extruder_match.group(1)) + self._dialog.exportedSettingModel.addSettingsFromStack(any_extruder_stack, + i18n_catalog.i18nc("@label", + "Extruder {0}", extruder_nr + 1), + settings) + except KeyError as e: + # If there is no user settings file, it's not a PCB, so notify user of failure. + Logger.log("w", "File %s is not a valid PCB.", file_name) + message = Message( + i18n_catalog.i18nc("@info:error Don't translate the XML tags or !", + "Project file {0} is corrupt: {1}.", + file_name, str(e)), + title=i18n_catalog.i18nc("@info:title", "Can't Open Project File"), + message_type=Message.MessageType.ERROR) + message.show() + return WorkspaceReader.PreReadResult.failed + # Show the dialog, informing the user what is about to happen. self._dialog.setMachineConflict(machine_conflict) self._dialog.setIsPrinterGroup(is_printer_group) @@ -628,6 +660,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader): self._dialog.setAllowCreatemachine(not is_pcb) self._dialog.show() + # Choosing the initially selected printer in MachineSelector is_networked_machine = False is_abstract_machine = False @@ -790,8 +823,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader): for stack in extruder_stacks: stack.setNextStack(global_stack, connect_signals = False) - user_settings = {} - if self._load_profile: Logger.log("d", "Workspace loading is checking definitions...") # Get all the definition files & check if they exist. If not, add them. @@ -864,23 +895,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader): self._container_registry.addContainer(material_container) Job.yieldThread() QCoreApplication.processEvents() # Ensure that the GUI does not freeze. - else: - Logger.log("d", "Workspace loading user settings...") - try: - user_settings = json.loads(archive.open("Cura/user-settings.json").read().decode("utf-8")) - except KeyError as e: - # If there is no user settings file, it's not a PCB, so notify user of failure. - Logger.log("w", "File %s is not a valid PCB.", file_name) - message = Message( - i18n_catalog.i18nc("@info:error Don't translate the XML tags or !", - "Project file {0} is corrupt: {1}.", - file_name, str(e)), - title=i18n_catalog.i18nc("@info:title", "Can't Open Project File"), - message_type=Message.MessageType.ERROR) - message.show() - self.setWorkspaceName("") - return [], {} - if global_stack: if self._load_profile: @@ -905,7 +919,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader): if not self._load_profile: # Now we have switched, apply the user settings - self._applyUserSettings(global_stack, extruder_stack_dict, user_settings) + self._applyUserSettings(global_stack, extruder_stack_dict, self._user_settings) # Load all the nodes / mesh data of the workspace nodes = self._3mf_mesh_reader.read(file_name) diff --git a/plugins/3MFReader/WorkspaceDialog.py b/plugins/3MFReader/WorkspaceDialog.py index c0006e21a8..1fafcf59f5 100644 --- a/plugins/3MFReader/WorkspaceDialog.py +++ b/plugins/3MFReader/WorkspaceDialog.py @@ -22,6 +22,8 @@ import time from cura.CuraApplication import CuraApplication +from .SpecificSettingsModel import SpecificSettingsModel + i18n_catalog = i18nCatalog("cura") @@ -75,6 +77,7 @@ class WorkspaceDialog(QObject): self._has_visible_select_same_profile = False self._select_same_profile_checked = True self._allow_create_machine = True + self._exported_settings_model = SpecificSettingsModel() machineConflictChanged = pyqtSignal() qualityChangesConflictChanged = pyqtSignal() @@ -340,6 +343,10 @@ class WorkspaceDialog(QObject): def allowCreateMachine(self): return self._allow_create_machine + @pyqtProperty(QObject, constant = True) + def exportedSettingModel(self): + return self._exported_settings_model + @pyqtSlot() def closeBackend(self) -> None: """Close the backend: otherwise one could end up with "Slicing...""" diff --git a/plugins/3MFReader/WorkspaceDialog.qml b/plugins/3MFReader/WorkspaceDialog.qml index c7074ce220..b6a9d59751 100644 --- a/plugins/3MFReader/WorkspaceDialog.qml +++ b/plugins/3MFReader/WorkspaceDialog.qml @@ -1,12 +1,12 @@ // Copyright (c) 2022 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.10 +import QtQuick 2.14 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 import QtQuick.Window 2.2 -import UM 1.5 as UM +import UM 1.6 as UM import Cura 1.1 as Cura UM.Dialog @@ -171,35 +171,68 @@ UM.Dialog { leftLabelText: catalog.i18nc("@action:label", "Name") rightLabelText: manager.qualityName + visible: manager.isCompatibleMachine } WorkspaceRow { leftLabelText: catalog.i18nc("@action:label", "Intent") rightLabelText: manager.intentName + visible: manager.isCompatibleMachine } WorkspaceRow { leftLabelText: catalog.i18nc("@action:label", "Not in profile") rightLabelText: catalog.i18ncp("@action:label", "%1 override", "%1 overrides", manager.numUserSettings).arg(manager.numUserSettings) - visible: manager.numUserSettings != 0 + visible: manager.numUserSettings != 0 && manager.selectSameProfileChecked && manager.isCompatibleMachine } WorkspaceRow { leftLabelText: catalog.i18nc("@action:label", "Derivative from") rightLabelText: catalog.i18ncp("@action:label", "%1, %2 override", "%1, %2 overrides", manager.numSettingsOverridenByQualityChanges).arg(manager.qualityType).arg(manager.numSettingsOverridenByQualityChanges) - visible: manager.numSettingsOverridenByQualityChanges != 0 + visible: manager.numSettingsOverridenByQualityChanges != 0 && manager.selectSameProfileChecked && manager.isCompatibleMachine + } + + WorkspaceRow + { + leftLabelText: catalog.i18nc("@action:label", "Specific settings") + rightLabelText: catalog.i18ncp("@action:label", "%1 override", "%1 overrides", manager.exportedSettingModel.rowCount()).arg(manager.exportedSettingModel.rowCount()) + buttonText: tableViewSpecificSettings.shouldBeVisible ? catalog.i18nc("@action:button", "Hide settings") : catalog.i18nc("@action:button", "Show settings") + visible: !manager.selectSameProfileChecked || !manager.isCompatibleMachine + onButtonClicked: tableViewSpecificSettings.shouldBeVisible = !tableViewSpecificSettings.shouldBeVisible + } + + Cura.TableView + { + id: tableViewSpecificSettings + width: parent.width - parent.leftPadding - UM.Theme.getSize("default_margin").width + height: UM.Theme.getSize("card").height + visible: shouldBeVisible && (!manager.selectSameProfileChecked || !manager.isCompatibleMachine) + property bool shouldBeVisible: false + + columnHeaders: + [ + catalog.i18nc("@title:column", "Applies on"), + catalog.i18nc("@title:column", "Setting"), + catalog.i18nc("@title:column", "Value") + ] + + model: UM.TableModel + { + id: tableModel + headers: ["category", "label", "value"] + rows: manager.exportedSettingModel.items + } } UM.CheckBox { text: catalog.i18nc("@action:checkbox", "Select the same profile") - enabled: manager.isCompatibleMachine onEnabledChanged: manager.selectSameProfileChecked = enabled tooltip: enabled ? "" : catalog.i18nc("@tooltip", "You can use the same profile only if you have the same printer as the project was published with") - visible: manager.hasVisibleSelectSameProfile + visible: manager.hasVisibleSelectSameProfile && manager.isCompatibleMachine checked: manager.selectSameProfileChecked onCheckedChanged: manager.selectSameProfileChecked = checked diff --git a/plugins/3MFReader/WorkspaceRow.qml b/plugins/3MFReader/WorkspaceRow.qml index 8d9f1f25b3..855b8c18b0 100644 --- a/plugins/3MFReader/WorkspaceRow.qml +++ b/plugins/3MFReader/WorkspaceRow.qml @@ -9,26 +9,38 @@ import QtQuick.Window 2.2 import UM 1.5 as UM import Cura 1.1 as Cura -Row +RowLayout { + id: root + property alias leftLabelText: leftLabel.text property alias rightLabelText: rightLabel.text + property alias buttonText: button.text + signal buttonClicked width: parent.width - height: visible ? childrenRect.height : 0 UM.Label { id: leftLabel text: catalog.i18nc("@action:label", "Type") - width: Math.round(parent.width / 4) + Layout.preferredWidth: Math.round(parent.width / 4) wrapMode: Text.WordWrap } + UM.Label { id: rightLabel text: manager.machineType - width: Math.round(parent.width / 3) wrapMode: Text.WordWrap } + + Cura.TertiaryButton + { + id: button + visible: !text.isEmpty + Layout.maximumHeight: leftLabel.implicitHeight + Layout.fillWidth: true + onClicked: root.buttonClicked() + } } \ No newline at end of file