Now loading user settings

CURA-11561
This commit is contained in:
Erwan MATHIEU 2024-02-02 16:05:36 +01:00
parent 733ef4d3d8
commit ab0a52063d
4 changed files with 178 additions and 90 deletions

View file

@ -5,6 +5,7 @@ from configparser import ConfigParser
import zipfile import zipfile
import os import os
import json import json
import re
from typing import cast, Dict, List, Optional, Tuple, Any, Set from typing import cast, Dict, List, Optional, Tuple, Any, Set
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
@ -141,10 +142,13 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
self._old_new_materials: Dict[str, str] = {} self._old_new_materials: Dict[str, str] = {}
self._machine_info = None self._machine_info = None
self._load_profile = False
def _clearState(self): def _clearState(self):
self._id_mapping = {} self._id_mapping = {}
self._old_new_materials = {} self._old_new_materials = {}
self._machine_info = None self._machine_info = None
self._load_profile = False
def getNewId(self, old_id: str): 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. """Get a unique name based on the old_id. This is different from directly calling the registry in that it caches results.
@ -228,7 +232,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
self._resolve_strategies = {k: None for k in resolve_strategy_keys} self._resolve_strategies = {k: None for k in resolve_strategy_keys}
containers_found_dict = {k: False for k in resolve_strategy_keys} containers_found_dict = {k: False for k in resolve_strategy_keys}
# Check whether the file is a PCB # Check whether the file is a PCB, which changes some import options
is_pcb = file_name.endswith('.pcb') is_pcb = file_name.endswith('.pcb')
# #
@ -621,6 +625,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
self._dialog.setHasObjectsOnPlate(Application.getInstance().platformActivity) self._dialog.setHasObjectsOnPlate(Application.getInstance().platformActivity)
self._dialog.setMissingPackagesMetadata(missing_package_metadata) self._dialog.setMissingPackagesMetadata(missing_package_metadata)
self._dialog.setHasVisibleSelectSameProfileChanged(is_pcb) self._dialog.setHasVisibleSelectSameProfileChanged(is_pcb)
self._dialog.setAllowCreatemachine(not is_pcb)
self._dialog.show() self._dialog.show()
# Choosing the initially selected printer in MachineSelector # Choosing the initially selected printer in MachineSelector
@ -652,6 +657,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
self._dialog.setIsNetworkedMachine(is_networked_machine) self._dialog.setIsNetworkedMachine(is_networked_machine)
self._dialog.setIsAbstractMachine(is_abstract_machine) self._dialog.setIsAbstractMachine(is_abstract_machine)
self._dialog.setMachineName(machine_name) self._dialog.setMachineName(machine_name)
self._dialog.updateCompatibleMachine()
self._dialog.setSelectSameProfileChecked(self._dialog.isCompatibleMachine)
# Block until the dialog is closed. # Block until the dialog is closed.
self._dialog.waitForClose() self._dialog.waitForClose()
@ -659,6 +666,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
if self._dialog.getResult() == {}: if self._dialog.getResult() == {}:
return WorkspaceReader.PreReadResult.cancelled return WorkspaceReader.PreReadResult.cancelled
self._load_profile = not is_pcb or self._dialog.selectSameProfileChecked
self._resolve_strategies = self._dialog.getResult() self._resolve_strategies = self._dialog.getResult()
# #
# There can be 3 resolve strategies coming from the dialog: # There can be 3 resolve strategies coming from the dialog:
@ -781,6 +790,9 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
for stack in extruder_stacks: for stack in extruder_stacks:
stack.setNextStack(global_stack, connect_signals = False) stack.setNextStack(global_stack, connect_signals = False)
user_settings = {}
if self._load_profile:
Logger.log("d", "Workspace loading is checking definitions...") Logger.log("d", "Workspace loading is checking definitions...")
# Get all the definition files & check if they exist. If not, add them. # Get all the definition files & check if they exist. If not, add them.
definition_container_files = [name for name in cura_file_names if name.endswith(self._definition_container_suffix)] definition_container_files = [name for name in cura_file_names if name.endswith(self._definition_container_suffix)]
@ -852,13 +864,33 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
self._container_registry.addContainer(material_container) self._container_registry.addContainer(material_container)
Job.yieldThread() Job.yieldThread()
QCoreApplication.processEvents() # Ensure that the GUI does not freeze. 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 <filename> or <message>!",
"Project file <filename>{0}</filename> is corrupt: <message>{1}</message>.",
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 global_stack:
if self._load_profile:
# Handle quality changes if any # Handle quality changes if any
self._processQualityChanges(global_stack) self._processQualityChanges(global_stack)
# Prepare the machine # Prepare the machine
self._applyChangesToMachine(global_stack, extruder_stack_dict) self._applyChangesToMachine(global_stack, extruder_stack_dict)
else:
self._applyUserSettings(global_stack, extruder_stack_dict, user_settings)
Logger.log("d", "Workspace loading is notifying rest of the code of changes...") Logger.log("d", "Workspace loading is notifying rest of the code of changes...")
# Actually change the active machine. # Actually change the active machine.
@ -1181,21 +1213,47 @@ class ThreeMFWorkspaceReader(WorkspaceReader):
material_node = machine_node.variants[extruder_stack.variant.getName()].materials[root_material_id] material_node = machine_node.variants[extruder_stack.variant.getName()].materials[root_material_id]
extruder_stack.material = material_node.container extruder_stack.material = material_node.container
def _applyChangesToMachine(self, global_stack, extruder_stack_dict): def _clearMachineSettings(self, global_stack, extruder_stack_dict):
# Clear all first
self._clearStack(global_stack) self._clearStack(global_stack)
for extruder_stack in extruder_stack_dict.values(): for extruder_stack in extruder_stack_dict.values():
self._clearStack(extruder_stack) self._clearStack(extruder_stack)
self._quality_changes_to_apply = None
self._quality_type_to_apply = None
self._intent_category_to_apply = None
self._user_settings_to_apply = None
def _applyUserSettings(self, global_stack, extruder_stack_dict, user_settings):
# Clear all first
self._clearMachineSettings(global_stack, extruder_stack_dict)
for stack_name, settings in user_settings.items():
if stack_name == 'global':
ThreeMFWorkspaceReader._applyUserSettingsOnStack(global_stack, settings)
else:
extruder_match = re.fullmatch('extruder_([0-9]+)', stack_name)
if extruder_match is not None:
extruder_nr = extruder_match.group(1)
if extruder_nr in extruder_stack_dict:
ThreeMFWorkspaceReader._applyUserSettingsOnStack(extruder_stack_dict[extruder_nr], settings)
@staticmethod
def _applyUserSettingsOnStack(stack, user_settings):
user_settings_container = stack.userChanges
for setting_to_import, setting_value in user_settings.items():
user_settings_container.setProperty(setting_to_import, 'value', setting_value)
def _applyChangesToMachine(self, global_stack, extruder_stack_dict):
# Clear all first
self._clearMachineSettings(global_stack, extruder_stack_dict)
self._applyDefinitionChanges(global_stack, extruder_stack_dict) self._applyDefinitionChanges(global_stack, extruder_stack_dict)
self._applyUserChanges(global_stack, extruder_stack_dict) self._applyUserChanges(global_stack, extruder_stack_dict)
self._applyVariants(global_stack, extruder_stack_dict) self._applyVariants(global_stack, extruder_stack_dict)
self._applyMaterials(global_stack, extruder_stack_dict) self._applyMaterials(global_stack, extruder_stack_dict)
# prepare the quality to select # prepare the quality to select
self._quality_changes_to_apply = None
self._quality_type_to_apply = None
self._intent_category_to_apply = None
if self._machine_info.quality_changes_info is not None: if self._machine_info.quality_changes_info is not None:
self._quality_changes_to_apply = self._machine_info.quality_changes_info.name self._quality_changes_to_apply = self._machine_info.quality_changes_info.name
else: else:

View file

@ -73,6 +73,8 @@ class WorkspaceDialog(QObject):
self._is_networked_machine = False self._is_networked_machine = False
self._is_compatible_machine = False self._is_compatible_machine = False
self._has_visible_select_same_profile = False self._has_visible_select_same_profile = False
self._select_same_profile_checked = True
self._allow_create_machine = True
machineConflictChanged = pyqtSignal() machineConflictChanged = pyqtSignal()
qualityChangesConflictChanged = pyqtSignal() qualityChangesConflictChanged = pyqtSignal()
@ -98,6 +100,7 @@ class WorkspaceDialog(QObject):
missingPackagesChanged = pyqtSignal() missingPackagesChanged = pyqtSignal()
isCompatibleMachineChanged = pyqtSignal() isCompatibleMachineChanged = pyqtSignal()
hasVisibleSelectSameProfileChanged = pyqtSignal() hasVisibleSelectSameProfileChanged = pyqtSignal()
selectSameProfileCheckedChanged = pyqtSignal()
@pyqtProperty(bool, notify = isPrinterGroupChanged) @pyqtProperty(bool, notify = isPrinterGroupChanged)
def isPrinterGroup(self) -> bool: def isPrinterGroup(self) -> bool:
@ -295,17 +298,19 @@ class WorkspaceDialog(QObject):
@pyqtSlot(str) @pyqtSlot(str)
def setMachineToOverride(self, machine_name: str) -> None: def setMachineToOverride(self, machine_name: str) -> None:
self._override_machine = machine_name
self.updateCompatibleMachine()
def updateCompatibleMachine(self):
registry = ContainerRegistry.getInstance() registry = ContainerRegistry.getInstance()
containers_expected = registry.findDefinitionContainers(name=self._machine_type) containers_expected = registry.findDefinitionContainers(name=self._machine_type)
containers_selected = registry.findContainerStacks(id = machine_name) containers_selected = registry.findContainerStacks(id=self._override_machine)
if len(containers_expected) == 1 and len(containers_selected) == 1: if len(containers_expected) == 1 and len(containers_selected) == 1:
new_compatible_machine = (containers_expected[0] == containers_selected[0].definition) new_compatible_machine = (containers_expected[0] == containers_selected[0].definition)
if new_compatible_machine != self._is_compatible_machine: if new_compatible_machine != self._is_compatible_machine:
self._is_compatible_machine = new_compatible_machine self._is_compatible_machine = new_compatible_machine
self.isCompatibleMachineChanged.emit() self.isCompatibleMachineChanged.emit()
self._override_machine = machine_name
@pyqtProperty(bool, notify = isCompatibleMachineChanged) @pyqtProperty(bool, notify = isCompatibleMachineChanged)
def isCompatibleMachine(self) -> bool: def isCompatibleMachine(self) -> bool:
return self._is_compatible_machine return self._is_compatible_machine
@ -319,6 +324,22 @@ class WorkspaceDialog(QObject):
def hasVisibleSelectSameProfile(self): def hasVisibleSelectSameProfile(self):
return self._has_visible_select_same_profile return self._has_visible_select_same_profile
def setSelectSameProfileChecked(self, select_same_profile_checked):
if select_same_profile_checked != self._select_same_profile_checked:
self._select_same_profile_checked = select_same_profile_checked
self.selectSameProfileCheckedChanged.emit()
@pyqtProperty(bool, notify = selectSameProfileCheckedChanged, fset = setSelectSameProfileChecked)
def selectSameProfileChecked(self):
return self._select_same_profile_checked
def setAllowCreatemachine(self, allow_create_machine):
self._allow_create_machine = allow_create_machine
@pyqtProperty(bool, constant = True)
def allowCreateMachine(self):
return self._allow_create_machine
@pyqtSlot() @pyqtSlot()
def closeBackend(self) -> None: def closeBackend(self) -> None:
"""Close the backend: otherwise one could end up with "Slicing...""" """Close the backend: otherwise one could end up with "Slicing..."""

View file

@ -120,13 +120,17 @@ UM.Dialog
minDropDownWidth: machineSelector.width minDropDownWidth: machineSelector.width
buttons: [ Component
{
id: componentNewPrinter
Cura.SecondaryButton Cura.SecondaryButton
{ {
id: createNewPrinter id: createNewPrinter
text: catalog.i18nc("@button", "Create new") text: catalog.i18nc("@button", "Create new")
fixedWidthMode: true fixedWidthMode: true
width: parent.width - leftPadding * 1.5 width: parent.width - leftPadding * 1.5
visible: manager.allowCreateMachine
onClicked: onClicked:
{ {
toggleContent() toggleContent()
@ -136,7 +140,9 @@ UM.Dialog
manager.setIsNetworkedMachine(false) manager.setIsNetworkedMachine(false)
} }
} }
] }
buttons: manager.allowCreateMachine ? [componentNewPrinter.createObject()] : []
onSelectPrinter: function(machine) onSelectPrinter: function(machine)
{ {
@ -191,9 +197,12 @@ UM.Dialog
{ {
text: catalog.i18nc("@action:checkbox", "Select the same profile") text: catalog.i18nc("@action:checkbox", "Select the same profile")
enabled: manager.isCompatibleMachine enabled: manager.isCompatibleMachine
onEnabledChanged: checked = enabled 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") 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
checked: manager.selectSameProfileChecked
onCheckedChanged: manager.selectSameProfileChecked = checked
} }
} }

View file

@ -105,7 +105,7 @@ class SettingsExportModel(QObject):
@staticmethod @staticmethod
def _exportSettings(settings_stack): def _exportSettings(settings_stack):
user_settings_container = settings_stack.getTop() user_settings_container = settings_stack.userChanges
user_keys = user_settings_container.getAllKeys() user_keys = user_settings_container.getAllKeys()
settings_export = [] settings_export = []