def createGlobalStack(cls, new_stack_id: str, definition: DefinitionContainer, **kwargs) -> GlobalStack: stack = GlobalStack(new_stack_id) stack.setDefinition(definition) user_container = InstanceContainer(new_stack_id + "_user") user_container.addMetaDataEntry("type", "user") user_container.addMetaDataEntry("machine", new_stack_id) user_container.setDefinition(definition) stack.setUserChanges(user_container) # Important! The order here matters, because that allows the stack to # assume the material and variant have already been set. if "definition_changes" in kwargs: stack.setDefinitionChangesById(kwargs["definition_changes"]) if "variant" in kwargs: stack.setVariantById(kwargs["variant"]) if "material" in kwargs: stack.setMaterialById(kwargs["material"]) if "quality" in kwargs: stack.setQualityById(kwargs["quality"]) if "quality_changes" in kwargs: stack.setQualityChangesById(kwargs["quality_changes"]) registry = ContainerRegistry.getInstance() registry.addContainer(stack) registry.addContainer(user_container) return stack
def test_roundtrip_instance(tmpdir, process_count, loaded_container_registry): instance_container = InstanceContainer("test_container") instance_container.setName("Test Instance Container") instance_container.setDefinition("inherits") instance_container.setMetaDataEntry("test", "test") instance_container.setProperty("test_setting_1", "value", 20) temp_file = tmpdir.join("instance_container_test") mp_run(process_count, write_data, temp_file, instance_container) assert len(list(tmpdir.listdir())) == 1 results = mp_run(process_count, read_data, temp_file) for result in results: deserialized_container = InstanceContainer("test_container") deserialized_container.setDefinition("inherits") with unittest.mock.patch( "UM.Settings.ContainerRegistry.ContainerRegistry.getInstance", unittest.mock.MagicMock( return_value=loaded_container_registry)): deserialized_container.deserialize(result) assert deserialized_container.getName() == instance_container.getName() assert deserialized_container.getMetaData( ) == instance_container.getMetaData() assert deserialized_container.getProperty( "test_setting_1", "value") == instance_container.getProperty("test_setting_1", "value")
def test_roundtrip_instance(tmpdir, process_count, loaded_container_registry): definition = loaded_container_registry.findDefinitionContainers( id="inherits")[0] instance_container = InstanceContainer("test_container") instance_container.setName("Test Instance Container") instance_container.setDefinition(definition) instance_container.addMetaDataEntry("test", "test") instance_container.setProperty("test_setting_1", "value", 20) temp_file = tmpdir.join("instance_container_test") mp_run(process_count, write_data, temp_file, instance_container) assert len(list(tmpdir.listdir())) == 1 results = mp_run(process_count, read_data, temp_file) for result in results: deserialized_container = InstanceContainer("test_container") deserialized_container.setDefinition(definition) deserialized_container.deserialize(result) assert deserialized_container.getName() == instance_container.getName() assert deserialized_container.getMetaData( ) == instance_container.getMetaData() assert deserialized_container.getProperty( "test_setting_1", "value") == instance_container.getProperty("test_setting_1", "value")
def _createQualityChanges( self, quality_type: str, new_name: str, machine: "GlobalStack", extruder_stack: Optional["ExtruderStack"]) -> "InstanceContainer": base_id = machine.definition.getId( ) if extruder_stack is None else extruder_stack.getId() new_id = base_id + "_" + new_name new_id = new_id.lower().replace(" ", "_") new_id = self._container_registry.uniqueName(new_id) # Create a new quality_changes container for the quality. quality_changes = InstanceContainer(new_id) quality_changes.setName(new_name) quality_changes.addMetaDataEntry("type", "quality_changes") quality_changes.addMetaDataEntry("quality_type", quality_type) # If we are creating a container for an extruder, ensure we add that to the container if extruder_stack is not None: quality_changes.addMetaDataEntry( "position", extruder_stack.getMetaDataEntry("position")) # If the machine specifies qualities should be filtered, ensure we match the current criteria. machine_definition_id = getMachineDefinitionIDForQualitySearch( machine.definition) quality_changes.setDefinition(machine_definition_id) quality_changes.addMetaDataEntry("setting_version", self._application.SettingVersion) return quality_changes
def _createQualityChanges( self, quality_type: str, intent_category: Optional[str], new_name: str, machine: "GlobalStack", extruder_stack: Optional["ExtruderStack"]) -> "InstanceContainer": container_registry = cura.CuraApplication.CuraApplication.getInstance( ).getContainerRegistry() base_id = machine.definition.getId( ) if extruder_stack is None else extruder_stack.getId() new_id = base_id + "_" + new_name new_id = new_id.lower().replace(" ", "_") new_id = container_registry.uniqueName(new_id) # Create a new quality_changes container for the quality. quality_changes = InstanceContainer(new_id) quality_changes.setName(new_name) quality_changes.setMetaDataEntry("type", "quality_changes") quality_changes.setMetaDataEntry("quality_type", quality_type) if intent_category is not None: quality_changes.setMetaDataEntry("intent_category", intent_category) # If we are creating a container for an extruder, ensure we add that to the container. if extruder_stack is not None: quality_changes.setMetaDataEntry( "position", extruder_stack.getMetaDataEntry("position")) # If the machine specifies qualities should be filtered, ensure we match the current criteria. machine_definition_id = ContainerTree.getInstance().machines[ machine.definition.getId()].quality_definition quality_changes.setDefinition(machine_definition_id) quality_changes.setMetaDataEntry( "setting_version", cura.CuraApplication.CuraApplication.getInstance().SettingVersion) return quality_changes
def convertUserContainerToQuality(self): if not self._global_container_stack: return new_quality_container = InstanceContainer("") name = self._createUniqueName("quality", "", self.activeQualityName, catalog.i18nc("@label", "Custom profile")) user_settings = self._global_container_stack.getTop() ## Copy all values new_quality_container.deserialize(user_settings.serialize()) ## If the currently active machine does not have quality profiles of its own, # make the new quality profile available for all machines that don't have # unique quality profiles (including the current machine) if not self.filterQualityByMachine: new_quality_container.setDefinition(UM.Settings.ContainerRegistry.getInstance().findDefinitionContainers(id = "fdmprinter")[0]) ## Change type / id / name new_quality_container.setMetaDataEntry("type", "quality") new_quality_container.setMetaDataEntry("read_only", False) new_quality_container.setName(name) new_quality_container._id = name UM.Settings.ContainerRegistry.getInstance().addContainer(new_quality_container) self.clearUserSettings() # As all users settings are now transfered to the new quality profile, remove them. self.setActiveQuality(name) return name
def test_roundtrip_instance(tmpdir, process_count, loaded_container_registry): definition = loaded_container_registry.findDefinitionContainers(id = "inherits")[0] instance_container = InstanceContainer("test_container") instance_container.setName("Test Instance Container") instance_container.setDefinition(definition) instance_container.addMetaDataEntry("test", "test") instance_container.setProperty("test_setting_1", "value", 20) temp_file = tmpdir.join("instance_container_test") mp_run(process_count, write_data, temp_file, instance_container) assert len(list(tmpdir.listdir())) == 1 results = mp_run(process_count, read_data, temp_file) for result in results: deserialized_container = InstanceContainer("test_container") deserialized_container.setDefinition(definition) deserialized_container.deserialize(result) assert deserialized_container.getName() == instance_container.getName() assert deserialized_container.getMetaData() == instance_container.getMetaData() assert deserialized_container.getProperty("test_setting_1", "value") == instance_container.getProperty("test_setting_1", "value")
def convertUserContainerToQuality(self): if not self._global_container_stack: return new_quality_container = InstanceContainer("") name = self._createUniqueName( "quality", "", self.activeQualityName, catalog.i18nc("@label", "Custom profile")) user_settings = self._global_container_stack.getTop() ## Copy all values new_quality_container.deserialize(user_settings.serialize()) ## If the currently active machine does not have quality profiles of its own, # make the new quality profile available for all machines that don't have # unique quality profiles (including the current machine) if not self.filterQualityByMachine: new_quality_container.setDefinition( UM.Settings.ContainerRegistry.getInstance( ).findDefinitionContainers(id="fdmprinter")[0]) ## Change type / id / name new_quality_container.setMetaDataEntry("type", "quality") new_quality_container.setMetaDataEntry("read_only", False) new_quality_container.setName(name) new_quality_container._id = name UM.Settings.ContainerRegistry.getInstance().addContainer( new_quality_container) self.clearUserSettings( ) # As all users settings are now transfered to the new quality profile, remove them. self.setActiveQuality(name) return name
def _serialiseSettings(self, settings): prefix = ";SETTING_" + str( GCodeWriter.version) + " " # The prefix to put before each line. prefix_length = len(prefix) all_settings = InstanceContainer( "G-code-imported-profile" ) #Create a new 'profile' with ALL settings so that the slice can be precisely reproduced. all_settings.setDefinition(settings.getBottom()) for key in settings.getAllKeys(): all_settings.setProperty( key, "value", settings.getProperty(key, "value") ) #Just copy everything over to the setting instance. serialised = all_settings.serialize() # Escape characters that have a special meaning in g-code comments. pattern = re.compile("|".join(GCodeWriter.escape_characters.keys())) # Perform the replacement with a regular expression. serialised = pattern.sub( lambda m: GCodeWriter.escape_characters[re.escape(m.group(0))], serialised) # Introduce line breaks so that each comment is no longer than 80 characters. Prepend each line with the prefix. result = "" # Lines have 80 characters, so the payload of each line is 80 - prefix. for pos in range(0, len(serialised), 80 - prefix_length): result += prefix + serialised[pos:pos + 80 - prefix_length] + "\n" serialised = result return serialised
def createGlobalStack(cls, new_stack_id: str, definition: DefinitionContainer, **kwargs) -> GlobalStack: stack = GlobalStack(new_stack_id) stack.setDefinition(definition) user_container = InstanceContainer(new_stack_id + "_user") user_container.addMetaDataEntry("type", "user") user_container.addMetaDataEntry("machine", new_stack_id) from cura.CuraApplication import CuraApplication user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) user_container.setDefinition(definition) stack.setUserChanges(user_container) # Important! The order here matters, because that allows the stack to # assume the material and variant have already been set. if "definition_changes" in kwargs: stack.setDefinitionChangesById(kwargs["definition_changes"]) if "variant" in kwargs: stack.setVariantById(kwargs["variant"]) if "material" in kwargs: stack.setMaterialById(kwargs["material"]) if "quality" in kwargs: stack.setQualityById(kwargs["quality"]) if "quality_changes" in kwargs: stack.setQualityChangesById(kwargs["quality_changes"]) registry = ContainerRegistry.getInstance() registry.addContainer(stack) registry.addContainer(user_container) return stack
def _createQualityChanges(self, quality_container, new_name, machine_definition, extruder_id): base_id = machine_definition.getId( ) if extruder_id is None else extruder_id # Create a new quality_changes container for the quality. quality_changes = InstanceContainer( self._createUniqueId(base_id, new_name)) quality_changes.setName(new_name) quality_changes.addMetaDataEntry("type", "quality_changes") quality_changes.addMetaDataEntry( "quality_type", quality_container.getMetaDataEntry("quality_type")) # If we are creating a container for an extruder, ensure we add that to the container if extruder_id is not None: quality_changes.addMetaDataEntry("extruder", extruder_id) # If the machine specifies qualities should be filtered, ensure we match the current criteria. if not machine_definition.getMetaDataEntry("has_machine_quality"): quality_changes.setDefinition( self._container_registry.findContainers(id="fdmprinter")[0]) else: quality_changes.setDefinition( QualityManager.getInstance().getParentMachineDefinition( machine_definition)) return quality_changes
def _configureProfile(self, profile: InstanceContainer, id_seed: str, new_name: str) -> Optional[str]: profile.setDirty(True) # Ensure the profiles are correctly saved new_id = self.createUniqueName("quality_changes", "", id_seed, catalog.i18nc("@label", "Custom profile")) profile.setMetaDataEntry("id", new_id) profile.setName(new_name) # Set the unique Id to the profile, so it's generating a new one even if the user imports the same profile # It also solves an issue with importing profiles from G-Codes profile.setMetaDataEntry("id", new_id) if "type" in profile.getMetaData(): profile.setMetaDataEntry("type", "quality_changes") else: profile.addMetaDataEntry("type", "quality_changes") quality_type = profile.getMetaDataEntry("quality_type") if not quality_type: return catalog.i18nc("@info:status", "Profile is missing a quality type.") quality_type_criteria = {"quality_type": quality_type} if self._machineHasOwnQualities(): profile.setDefinition(self._activeQualityDefinition().getId()) if self._machineHasOwnMaterials(): active_material_id = self._activeMaterialId() if active_material_id and active_material_id != "empty": # only update if there is an active material profile.addMetaDataEntry("material", active_material_id) quality_type_criteria["material"] = active_material_id quality_type_criteria["definition"] = profile.getDefinition().getId() else: profile.setDefinition("fdmprinter") quality_type_criteria["definition"] = "fdmprinter" machine_definition = Application.getInstance().getGlobalContainerStack().getBottom() del quality_type_criteria["definition"] # materials = None if "material" in quality_type_criteria: # materials = ContainerRegistry.getInstance().findInstanceContainers(id = quality_type_criteria["material"]) del quality_type_criteria["material"] # Do not filter quality containers here with materials because we are trying to import a profile, so it should # NOT be restricted by the active materials on the current machine. materials = None # Check to make sure the imported profile actually makes sense in context of the current configuration. # This prevents issues where importing a "draft" profile for a machine without "draft" qualities would report as # successfully imported but then fail to show up. from cura.QualityManager import QualityManager qualities = QualityManager.getInstance()._getFilteredContainersForStack(machine_definition, materials, **quality_type_criteria) if not qualities: return catalog.i18nc("@info:status", "Could not find a quality type {0} for the current configuration.", quality_type) ContainerRegistry.getInstance().addContainer(profile) return None
def updateQualityChanges(self) -> bool: application = cura.CuraApplication.CuraApplication.getInstance() global_stack = application.getMachineManager().activeMachine if not global_stack: return False application.getMachineManager().blurSettings.emit() current_quality_changes_name = global_stack.qualityChanges.getName() current_quality_type = global_stack.quality.getMetaDataEntry( "quality_type") extruder_stacks = list(global_stack.extruders.values()) container_registry = cura.CuraApplication.CuraApplication.getInstance( ).getContainerRegistry() machine_definition_id = ContainerTree.getInstance().machines[ global_stack.definition.getId()].quality_definition for stack in [global_stack] + extruder_stacks: # Find the quality_changes container for this stack and merge the contents of the top container into it. quality_changes = stack.qualityChanges if quality_changes.getId() == "empty_quality_changes": quality_changes = InstanceContainer( container_registry.uniqueName( (stack.getId() + "_" + current_quality_changes_name).lower().replace( " ", "_"))) quality_changes.setName(current_quality_changes_name) quality_changes.setMetaDataEntry("type", "quality_changes") quality_changes.setMetaDataEntry("quality_type", current_quality_type) if stack.getMetaDataEntry( "position") is not None: # Extruder stacks. quality_changes.setMetaDataEntry( "position", stack.getMetaDataEntry("position")) quality_changes.setMetaDataEntry( "intent_category", stack.quality.getMetaDataEntry("intent_category", "default")) quality_changes.setMetaDataEntry("setting_version", application.SettingVersion) quality_changes.setDefinition(machine_definition_id) container_registry.addContainer(quality_changes) stack.qualityChanges = quality_changes if not quality_changes or container_registry.isReadOnly( quality_changes.getId()): Logger.log( "e", "Could not update quality of a nonexistant or read only quality profile in stack %s", stack.getId()) continue self._performMerge(quality_changes, stack.getTop()) cura.CuraApplication.CuraApplication.getInstance().getMachineManager( ).activeQualityChangesGroupChanged.emit() return True
def _createDefinitionChangesContainer(self, container_stack, container_name, container_index = None): definition_changes_container = InstanceContainer(container_name) definition = container_stack.getBottom() definition_changes_container.setDefinition(definition) definition_changes_container.addMetaDataEntry("type", "definition_changes") self._container_registry.addContainer(definition_changes_container) container_stack.definitionChanges = definition_changes_container return definition_changes_container
def _configureProfile(self, profile: InstanceContainer, id_seed: str, new_name: str) -> Optional[str]: profile.setDirty(True) # Ensure the profiles are correctly saved new_id = self.createUniqueName("quality_changes", "", id_seed, catalog.i18nc("@label", "Custom profile")) profile._id = new_id profile.setName(new_name) if "type" in profile.getMetaData(): profile.setMetaDataEntry("type", "quality_changes") else: profile.addMetaDataEntry("type", "quality_changes") quality_type = profile.getMetaDataEntry("quality_type") if not quality_type: return catalog.i18nc("@info:status", "Profile is missing a quality type.") quality_type_criteria = {"quality_type": quality_type} if self._machineHasOwnQualities(): profile.setDefinition(self._activeQualityDefinition().getId()) if self._machineHasOwnMaterials(): active_material_id = self._activeMaterialId() if active_material_id and active_material_id != "empty": # only update if there is an active material profile.addMetaDataEntry("material", active_material_id) quality_type_criteria["material"] = active_material_id quality_type_criteria["definition"] = profile.getDefinition().getId() else: profile.setDefinition(fdmprinter) quality_type_criteria["definition"] = "fdmprinter" machine_definition = Application.getInstance().getGlobalContainerStack().getBottom() del quality_type_criteria["definition"] # materials = None if "material" in quality_type_criteria: # materials = ContainerRegistry.getInstance().findInstanceContainers(id = quality_type_criteria["material"]) del quality_type_criteria["material"] # Do not filter quality containers here with materials because we are trying to import a profile, so it should # NOT be restricted by the active materials on the current machine. materials = None # Check to make sure the imported profile actually makes sense in context of the current configuration. # This prevents issues where importing a "draft" profile for a machine without "draft" qualities would report as # successfully imported but then fail to show up. from cura.QualityManager import QualityManager qualities = QualityManager.getInstance()._getFilteredContainersForStack(machine_definition, materials, **quality_type_criteria) if not qualities: return catalog.i18nc("@info:status", "Could not find a quality type {0} for the current configuration.", quality_type) ContainerRegistry.getInstance().addContainer(profile) return None
def _createDefinitionChangesContainer(self, global_container_stack, container_index = None): definition_changes_container = InstanceContainer(global_container_stack.getName() + "_settings") definition = global_container_stack.getBottom() definition_changes_container.setDefinition(definition) definition_changes_container.addMetaDataEntry("type", "definition_changes") self._container_registry.addContainer(definition_changes_container) # Insert definition_changes between the definition and the variant global_container_stack.insertContainer(-1, definition_changes_container) return definition_changes_container
def _createDefinitionChangesContainer(self, global_container_stack, container_index = None): definition_changes_container = InstanceContainer(global_container_stack.getName() + "_settings") definition = global_container_stack.getBottom() definition_changes_container.setDefinition(definition) definition_changes_container.addMetaDataEntry("type", "definition_changes") self._container_registry.addContainer(definition_changes_container) # Insert definition_changes between the definition and the variant global_container_stack.insertContainer(-1, definition_changes_container) return definition_changes_container
def _createFlattenedContainerInstance(self, instance_container1, instance_container2): flat_container = InstanceContainer(instance_container2.getName()) flat_container.setDefinition(instance_container2.getDefinition()) flat_container.setMetaData(instance_container2.getMetaData()) for key in instance_container2.getAllKeys(): flat_container.setProperty(key, "value", instance_container2.getProperty(key, "value")) for key in instance_container1.getAllKeys(): flat_container.setProperty(key, "value", instance_container1.getProperty(key, "value")) return flat_container
def _createDefinitionChangesContainer(self, container_stack, container_name, container_index = None): definition_changes_container = InstanceContainer(container_name) definition = container_stack.getBottom() definition_changes_container.setDefinition(definition) definition_changes_container.addMetaDataEntry("type", "definition_changes") definition_changes_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) self._container_registry.addContainer(definition_changes_container) container_stack.definitionChanges = definition_changes_container return definition_changes_container
def createExtruderStack(cls, new_stack_id: str, definition: DefinitionContainerInterface, machine_definition_id: str, **kwargs) -> ExtruderStack: stack = ExtruderStack(new_stack_id) stack.setName(definition.getName()) stack.setDefinition(definition) stack.addMetaDataEntry("position", definition.getMetaDataEntry("position")) if "next_stack" in kwargs: # Add stacks before containers are added, since they may trigger a setting update. stack.setNextStack(kwargs["next_stack"]) user_container = InstanceContainer(new_stack_id + "_user") user_container.addMetaDataEntry("type", "user") user_container.addMetaDataEntry("extruder", new_stack_id) from cura.CuraApplication import CuraApplication user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) user_container.setDefinition(machine_definition_id) stack.setUserChanges(user_container) # Important! The order here matters, because that allows the stack to # assume the material and variant have already been set. if "definition_changes" in kwargs: stack.setDefinitionChangesById(kwargs["definition_changes"]) else: stack.setDefinitionChanges( cls.createDefinitionChangesContainer( stack, new_stack_id + "_settings")) if "variant" in kwargs: stack.setVariantById(kwargs["variant"]) if "material" in kwargs: stack.setMaterialById(kwargs["material"]) if "quality" in kwargs: stack.setQualityById(kwargs["quality"]) if "quality_changes" in kwargs: stack.setQualityChangesById(kwargs["quality_changes"]) # Only add the created containers to the registry after we have set all the other # properties. This makes the create operation more transactional, since any problems # setting properties will not result in incomplete containers being added. registry = ContainerRegistry.getInstance() registry.addContainer(stack) registry.addContainer(user_container) return stack
def _configureProfile(self, profile: InstanceContainer, id_seed: str, new_name: str) -> Optional[str]: profile.setReadOnly(False) profile.setDirty(True) # Ensure the profiles are correctly saved new_id = self.createUniqueName( "quality_changes", "", id_seed, catalog.i18nc("@label", "Custom profile")) profile._id = new_id profile.setName(new_name) if "type" in profile.getMetaData(): profile.setMetaDataEntry("type", "quality_changes") else: profile.addMetaDataEntry("type", "quality_changes") quality_type = profile.getMetaDataEntry("quality_type") if not quality_type: return catalog.i18nc("@info:status", "Profile is missing a quality type.") quality_type_criteria = {"quality_type": quality_type} if self._machineHasOwnQualities(): profile.setDefinition(self._activeQualityDefinition()) if self._machineHasOwnMaterials(): active_material_id = self._activeMaterialId() if active_material_id: # only update if there is an active material profile.addMetaDataEntry("material", active_material_id) quality_type_criteria["material"] = active_material_id quality_type_criteria["definition"] = profile.getDefinition( ).getId() else: profile.setDefinition( ContainerRegistry.getInstance().findDefinitionContainers( id="fdmprinter")[0]) quality_type_criteria["definition"] = "fdmprinter" # Check to make sure the imported profile actually makes sense in context of the current configuration. # This prevents issues where importing a "draft" profile for a machine without "draft" qualities would report as # successfully imported but then fail to show up. qualities = self.findInstanceContainers(**quality_type_criteria) if not qualities: return catalog.i18nc( "@info:status", "Could not find a quality type {0} for the current configuration.", quality_type) ContainerRegistry.getInstance().addContainer(profile) return None
def createDefinitionChangesContainer(cls, container_stack, container_name): from cura.CuraApplication import CuraApplication unique_container_name = ContainerRegistry.getInstance().uniqueName(container_name) definition_changes_container = InstanceContainer(unique_container_name) definition_changes_container.setDefinition(container_stack.getBottom().getId()) definition_changes_container.addMetaDataEntry("type", "definition_changes") definition_changes_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) ContainerRegistry.getInstance().addContainer(definition_changes_container) container_stack.definitionChanges = definition_changes_container return definition_changes_container
def _configureProfile(self, profile: InstanceContainer, id_seed: str, new_name: str, machine_definition_id: str) -> Optional[str]: profile.setDirty(True) # Ensure the profiles are correctly saved new_id = self.createUniqueName( "quality_changes", "", id_seed, catalog.i18nc("@label", "Custom profile")) profile.setMetaDataEntry("id", new_id) profile.setName(new_name) # Set the unique Id to the profile, so it's generating a new one even if the user imports the same profile # It also solves an issue with importing profiles from G-Codes profile.setMetaDataEntry("id", new_id) profile.setMetaDataEntry("definition", machine_definition_id) if "type" in profile.getMetaData(): profile.setMetaDataEntry("type", "quality_changes") else: profile.setMetaDataEntry("type", "quality_changes") quality_type = profile.getMetaDataEntry("quality_type") if not quality_type: return catalog.i18nc("@info:status", "Profile is missing a quality type.") global_stack = Application.getInstance().getGlobalContainerStack() if global_stack is None: return None definition_id = getMachineDefinitionIDForQualitySearch( global_stack.definition) profile.setDefinition(definition_id) # Check to make sure the imported profile actually makes sense in context of the current configuration. # This prevents issues where importing a "draft" profile for a machine without "draft" qualities would report as # successfully imported but then fail to show up. quality_manager = cura.CuraApplication.CuraApplication.getInstance( )._quality_manager quality_group_dict = quality_manager.getQualityGroupsForMachineDefinition( global_stack) if quality_type not in quality_group_dict: return catalog.i18nc( "@info:status", "Could not find a quality type {0} for the current configuration.", quality_type) ContainerRegistry.getInstance().addContainer(profile) return None
def createDefinitionChangesContainer(cls, container_stack, container_name, container_index = None): from cura.CuraApplication import CuraApplication unique_container_name = ContainerRegistry.getInstance().uniqueName(container_name) definition_changes_container = InstanceContainer(unique_container_name) definition = container_stack.getBottom() definition_changes_container.setDefinition(definition) definition_changes_container.addMetaDataEntry("type", "definition_changes") definition_changes_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) ContainerRegistry.getInstance().addContainer(definition_changes_container) container_stack.definitionChanges = definition_changes_container return definition_changes_container
def createUserChangesContainer(cls, container_name: str, definition_id: str, stack_id: str, is_global_stack: bool) -> "InstanceContainer": from cura.CuraApplication import CuraApplication unique_container_name = ContainerRegistry.getInstance().uniqueName(container_name) container = InstanceContainer(unique_container_name) container.setDefinition(definition_id) container.addMetaDataEntry("type", "user") container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) metadata_key_to_add = "machine" if is_global_stack else "extruder" container.addMetaDataEntry(metadata_key_to_add, stack_id) return container
def _createFlattenedContainerInstance(self, instance_container1, instance_container2): flat_container = InstanceContainer(instance_container2.getName()) # The metadata includes id, name and definition flat_container.setMetaData(copy.deepcopy(instance_container2.getMetaData())) if instance_container1.getDefinition(): flat_container.setDefinition(instance_container1.getDefinition().getId()) for key in instance_container2.getAllKeys(): flat_container.setProperty(key, "value", instance_container2.getProperty(key, "value")) for key in instance_container1.getAllKeys(): flat_container.setProperty(key, "value", instance_container1.getProperty(key, "value")) return flat_container
def createDefinitionChangesContainer(cls, container_stack, container_name): from cura.CuraApplication import CuraApplication application = CuraApplication.getInstance() registry = application.getContainerRegistry() unique_container_name = registry.uniqueName(container_name) definition_changes_container = InstanceContainer(unique_container_name) definition_changes_container.setDefinition(container_stack.getBottom().getId()) definition_changes_container.setMetaDataEntry("type", "definition_changes") definition_changes_container.setMetaDataEntry("setting_version", CuraApplication.SettingVersion) registry.addContainer(definition_changes_container) container_stack.definitionChanges = definition_changes_container return definition_changes_container
def _createFlattenedContainerInstance(self, instance_container1, instance_container2): flat_container = InstanceContainer(instance_container2.getName()) # The metadata includes id, name and definition flat_container.setMetaData(copy.deepcopy(instance_container2.getMetaData())) if instance_container1.getDefinition(): flat_container.setDefinition(instance_container1.getDefinition().getId()) for key in instance_container2.getAllKeys(): flat_container.setProperty(key, "value", instance_container2.getProperty(key, "value")) for key in instance_container1.getAllKeys(): flat_container.setProperty(key, "value", instance_container1.getProperty(key, "value")) return flat_container
def _configureProfile(self, profile: InstanceContainer, id_seed: str, new_name: str, machine_definition_id: str) -> Optional[str]: """Update an imported profile to match the current machine configuration. :param profile: The profile to configure. :param id_seed: The base ID for the profile. May be changed so it does not conflict with existing containers. :param new_name: The new name for the profile. :return: None if configuring was successful or an error message if an error occurred. """ profile.setDirty(True) # Ensure the profiles are correctly saved new_id = self.createUniqueName("quality_changes", "", id_seed, catalog.i18nc("@label", "Custom profile")) profile.setMetaDataEntry("id", new_id) profile.setName(new_name) # Set the unique Id to the profile, so it's generating a new one even if the user imports the same profile # It also solves an issue with importing profiles from G-Codes profile.setMetaDataEntry("id", new_id) profile.setMetaDataEntry("definition", machine_definition_id) if "type" in profile.getMetaData(): profile.setMetaDataEntry("type", "quality_changes") else: profile.setMetaDataEntry("type", "quality_changes") quality_type = profile.getMetaDataEntry("quality_type") if not quality_type: return catalog.i18nc("@info:status", "Profile is missing a quality type.") global_stack = cura.CuraApplication.CuraApplication.getInstance().getGlobalContainerStack() if global_stack is None: return None definition_id = ContainerTree.getInstance().machines[global_stack.definition.getId()].quality_definition profile.setDefinition(definition_id) # Check to make sure the imported profile actually makes sense in context of the current configuration. # This prevents issues where importing a "draft" profile for a machine without "draft" qualities would report as # successfully imported but then fail to show up. quality_group_dict = ContainerTree.getInstance().getCurrentQualityGroups() # "not_supported" profiles can be imported. if quality_type != empty_quality_container.getMetaDataEntry("quality_type") and quality_type not in quality_group_dict: return catalog.i18nc("@info:status", "Could not find a quality type {0} for the current configuration.", quality_type) ContainerRegistry.getInstance().addContainer(profile) return None
def createUserChangesContainer(cls, container_name: str, definition_id: str, stack_id: str, is_global_stack: bool) -> "InstanceContainer": from cura.CuraApplication import CuraApplication application = CuraApplication.getInstance() registry = application.getContainerRegistry() unique_container_name = registry.uniqueName(container_name) container = InstanceContainer(unique_container_name) container.setDefinition(definition_id) container.setMetaDataEntry("type", "user") container.setMetaDataEntry("setting_version", CuraApplication.SettingVersion) metadata_key_to_add = "machine" if is_global_stack else "extruder" container.setMetaDataEntry(metadata_key_to_add, stack_id) return container
def createExtruderStack(cls, new_stack_id: str, definition: DefinitionContainerInterface, machine_definition_id: str, **kwargs) -> ExtruderStack: stack = ExtruderStack(new_stack_id) stack.setName(definition.getName()) stack.setDefinition(definition) stack.addMetaDataEntry("position", definition.getMetaDataEntry("position")) if "next_stack" in kwargs: # Add stacks before containers are added, since they may trigger a setting update. stack.setNextStack(kwargs["next_stack"]) user_container = InstanceContainer(new_stack_id + "_user") user_container.addMetaDataEntry("type", "user") user_container.addMetaDataEntry("extruder", new_stack_id) from cura.CuraApplication import CuraApplication user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) user_container.setDefinition(machine_definition_id) stack.setUserChanges(user_container) # Important! The order here matters, because that allows the stack to # assume the material and variant have already been set. if "definition_changes" in kwargs: stack.setDefinitionChangesById(kwargs["definition_changes"]) else: stack.setDefinitionChanges(cls.createDefinitionChangesContainer(stack, new_stack_id + "_settings")) if "variant" in kwargs: stack.setVariantById(kwargs["variant"]) if "material" in kwargs: stack.setMaterialById(kwargs["material"]) if "quality" in kwargs: stack.setQualityById(kwargs["quality"]) if "quality_changes" in kwargs: stack.setQualityChangesById(kwargs["quality_changes"]) # Only add the created containers to the registry after we have set all the other # properties. This makes the create operation more transactional, since any problems # setting properties will not result in incomplete containers being added. registry = ContainerRegistry.getInstance() registry.addContainer(stack) registry.addContainer(user_container) return stack
def _createQualityChanges(self, quality_container, new_name, machine_definition, extruder_id): base_id = machine_definition.getId() if extruder_id is None else extruder_id # Create a new quality_changes container for the quality. quality_changes = InstanceContainer(self._createUniqueId(base_id, new_name)) quality_changes.setName(new_name) quality_changes.addMetaDataEntry("type", "quality_changes") quality_changes.addMetaDataEntry("quality_type", quality_container.getMetaDataEntry("quality_type")) # If we are creating a container for an extruder, ensure we add that to the container if extruder_id is not None: quality_changes.addMetaDataEntry("extruder", extruder_id) # If the machine specifies qualities should be filtered, ensure we match the current criteria. if not machine_definition.getMetaDataEntry("has_machine_quality"): quality_changes.setDefinition(self._container_registry.findContainers(id = "fdmprinter")[0]) else: quality_changes.setDefinition(QualityManager.getInstance().getParentMachineDefinition(machine_definition)) return quality_changes
def createGlobalStack(cls, new_stack_id: str, definition: DefinitionContainerInterface, **kwargs) -> GlobalStack: stack = GlobalStack(new_stack_id) stack.setDefinition(definition) user_container = InstanceContainer(new_stack_id + "_user") user_container.addMetaDataEntry("type", "user") user_container.addMetaDataEntry("machine", new_stack_id) from cura.CuraApplication import CuraApplication user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) user_container.setDefinition(definition.getId()) stack.setUserChanges(user_container) # Important! The order here matters, because that allows the stack to # assume the material and variant have already been set. if "definition_changes" in kwargs: stack.setDefinitionChangesById(kwargs["definition_changes"]) else: stack.setDefinitionChanges( cls.createDefinitionChangesContainer( stack, new_stack_id + "_settings")) if "variant" in kwargs: stack.setVariantById(kwargs["variant"]) if "material" in kwargs: stack.setMaterialById(kwargs["material"]) if "quality" in kwargs: stack.setQualityById(kwargs["quality"]) if "quality_changes" in kwargs: stack.setQualityChangesById(kwargs["quality_changes"]) registry = ContainerRegistry.getInstance() registry.addContainer(stack) registry.addContainer(user_container) return stack
def _configureProfile(self, profile: InstanceContainer, id_seed: str, new_name: str) -> Optional[str]: profile.setReadOnly(False) profile.setDirty(True) # Ensure the profiles are correctly saved new_id = self.createUniqueName("quality_changes", "", id_seed, catalog.i18nc("@label", "Custom profile")) profile._id = new_id profile.setName(new_name) if "type" in profile.getMetaData(): profile.setMetaDataEntry("type", "quality_changes") else: profile.addMetaDataEntry("type", "quality_changes") quality_type = profile.getMetaDataEntry("quality_type") if not quality_type: return catalog.i18nc("@info:status", "Profile is missing a quality type.") quality_type_criteria = {"quality_type": quality_type} if self._machineHasOwnQualities(): profile.setDefinition(self._activeQualityDefinition()) if self._machineHasOwnMaterials(): active_material_id = self._activeMaterialId() if active_material_id: # only update if there is an active material profile.addMetaDataEntry("material", active_material_id) quality_type_criteria["material"] = active_material_id quality_type_criteria["definition"] = profile.getDefinition().getId() else: profile.setDefinition(ContainerRegistry.getInstance().findDefinitionContainers(id="fdmprinter")[0]) quality_type_criteria["definition"] = "fdmprinter" # Check to make sure the imported profile actually makes sense in context of the current configuration. # This prevents issues where importing a "draft" profile for a machine without "draft" qualities would report as # successfully imported but then fail to show up. qualities = self.findInstanceContainers(**quality_type_criteria) if not qualities: return catalog.i18nc("@info:status", "Could not find a quality type {0} for the current configuration.", quality_type) ContainerRegistry.getInstance().addContainer(profile) return None
def _configureProfile(self, profile: InstanceContainer, id_seed: str, new_name: str, machine_definition_id: str) -> Optional[str]: profile.setDirty(True) # Ensure the profiles are correctly saved new_id = self.createUniqueName("quality_changes", "", id_seed, catalog.i18nc("@label", "Custom profile")) profile.setMetaDataEntry("id", new_id) profile.setName(new_name) # Set the unique Id to the profile, so it's generating a new one even if the user imports the same profile # It also solves an issue with importing profiles from G-Codes profile.setMetaDataEntry("id", new_id) profile.setMetaDataEntry("definition", machine_definition_id) if "type" in profile.getMetaData(): profile.setMetaDataEntry("type", "quality_changes") else: profile.setMetaDataEntry("type", "quality_changes") quality_type = profile.getMetaDataEntry("quality_type") if not quality_type: return catalog.i18nc("@info:status", "Profile is missing a quality type.") global_stack = Application.getInstance().getGlobalContainerStack() if global_stack is None: return None definition_id = getMachineDefinitionIDForQualitySearch(global_stack.definition) profile.setDefinition(definition_id) # Check to make sure the imported profile actually makes sense in context of the current configuration. # This prevents issues where importing a "draft" profile for a machine without "draft" qualities would report as # successfully imported but then fail to show up. quality_manager = cura.CuraApplication.CuraApplication.getInstance()._quality_manager quality_group_dict = quality_manager.getQualityGroupsForMachineDefinition(global_stack) if quality_type not in quality_group_dict: return catalog.i18nc("@info:status", "Could not find a quality type {0} for the current configuration.", quality_type) ContainerRegistry.getInstance().addContainer(profile) return None
def _createQualityChanges(self, quality_type: str, new_name: str, machine: "GlobalStack", extruder_stack: Optional["ExtruderStack"]) -> "InstanceContainer": base_id = machine.definition.getId() if extruder_stack is None else extruder_stack.getId() new_id = base_id + "_" + new_name new_id = new_id.lower().replace(" ", "_") new_id = self._container_registry.uniqueName(new_id) # Create a new quality_changes container for the quality. quality_changes = InstanceContainer(new_id) quality_changes.setName(new_name) quality_changes.setMetaDataEntry("type", "quality_changes") quality_changes.setMetaDataEntry("quality_type", quality_type) # If we are creating a container for an extruder, ensure we add that to the container if extruder_stack is not None: quality_changes.setMetaDataEntry("position", extruder_stack.getMetaDataEntry("position")) # If the machine specifies qualities should be filtered, ensure we match the current criteria. machine_definition_id = getMachineDefinitionIDForQualitySearch(machine.definition) quality_changes.setDefinition(machine_definition_id) quality_changes.setMetaDataEntry("setting_version", self._application.SettingVersion) return quality_changes
def _serialiseSettings(self, settings): prefix = ";SETTING_" + str(GCodeWriter.version) + " " # The prefix to put before each line. prefix_length = len(prefix) all_settings = InstanceContainer("G-code-imported-profile") #Create a new 'profile' with ALL settings so that the slice can be precisely reproduced. all_settings.setDefinition(settings.getBottom()) for key in settings.getAllKeys(): all_settings.setProperty(key, "value", settings.getProperty(key, "value")) #Just copy everything over to the setting instance. serialised = all_settings.serialize() # Escape characters that have a special meaning in g-code comments. pattern = re.compile("|".join(GCodeWriter.escape_characters.keys())) # Perform the replacement with a regular expression. serialised = pattern.sub(lambda m: GCodeWriter.escape_characters[re.escape(m.group(0))], serialised) # Introduce line breaks so that each comment is no longer than 80 characters. Prepend each line with the prefix. result = "" # Lines have 80 characters, so the payload of each line is 80 - prefix. for pos in range(0, len(serialised), 80 - prefix_length): result += prefix + serialised[pos : pos + 80 - prefix_length] + "\n" serialised = result return serialised
def read(self, file_name): if file_name.split(".")[-1] != "ini": return None global_container_stack = Application.getInstance( ).getGlobalContainerStack() if not global_container_stack: return None multi_extrusion = global_container_stack.getProperty( "machine_extruder_count", "value") > 1 if multi_extrusion: Logger.log( "e", "Unable to import legacy profile %s. Multi extrusion is not supported", file_name) raise Exception( "Unable to import legacy profile. Multi extrusion is not supported" ) Logger.log("i", "Importing legacy profile from file " + file_name + ".") container_registry = ContainerRegistry.getInstance() profile_id = container_registry.uniqueName("Imported Legacy Profile") profile = InstanceContainer(profile_id) # Create an empty profile. parser = configparser.ConfigParser(interpolation=None) try: parser.read([file_name]) # Parse the INI file. except Exception as e: Logger.log("e", "Unable to open legacy profile %s: %s", file_name, str(e)) return None # Legacy Cura saved the profile under the section "profile_N" where N is the ID of a machine, except when you export in which case it saves it in the section "profile". # Since importing multiple machine profiles is out of scope, just import the first section we find. section = "" for found_section in parser.sections(): if found_section.startswith("profile"): section = found_section break if not section: # No section starting with "profile" was found. Probably not a proper INI file. return None try: with open( os.path.join( PluginRegistry.getInstance().getPluginPath( "LegacyProfileReader"), "DictionaryOfDoom.json"), "r", -1, "utf-8") as f: dict_of_doom = json.load(f) # Parse the Dictionary of Doom. except IOError as e: Logger.log("e", "Could not open DictionaryOfDoom.json for reading: %s", str(e)) return None except Exception as e: Logger.log("e", "Could not parse DictionaryOfDoom.json: %s", str(e)) return None defaults = self.prepareDefaults(dict_of_doom) legacy_settings = self.prepareLocals( parser, section, defaults) #Gets the settings from the legacy profile. #Check the target version in the Dictionary of Doom with this application version. if "target_version" not in dict_of_doom: Logger.log( "e", "Dictionary of Doom has no target version. Is it the correct JSON file?" ) return None if InstanceContainer.Version != dict_of_doom["target_version"]: Logger.log( "e", "Dictionary of Doom of legacy profile reader (version %s) is not in sync with the current instance container version (version %s)!", dict_of_doom["target_version"], str(InstanceContainer.Version)) return None if "translation" not in dict_of_doom: Logger.log( "e", "Dictionary of Doom has no translation. Is it the correct JSON file?" ) return None current_printer_definition = global_container_stack.definition profile.setDefinition(current_printer_definition.getId()) for new_setting in dict_of_doom[ "translation"]: # Evaluate all new settings that would get a value from the translations. old_setting_expression = dict_of_doom["translation"][new_setting] compiled = compile(old_setting_expression, new_setting, "eval") try: new_value = eval( compiled, {"math": math}, legacy_settings ) # Pass the legacy settings as local variables to allow access to in the evaluation. value_using_defaults = eval( compiled, {"math": math}, defaults ) #Evaluate again using only the default values to try to see if they are default. except Exception: # Probably some setting name that was missing or something else that went wrong in the ini file. Logger.log( "w", "Setting " + new_setting + " could not be set because the evaluation failed. Something is probably missing from the imported legacy profile." ) continue definitions = current_printer_definition.findDefinitions( key=new_setting) if definitions: if new_value != value_using_defaults and definitions[ 0].default_value != new_value: # Not equal to the default in the new Cura OR the default in the legacy Cura. profile.setProperty( new_setting, "value", new_value) # Store the setting in the profile! if len(profile.getAllKeys()) == 0: Logger.log( "i", "A legacy profile was imported but everything evaluates to the defaults, creating an empty profile." ) profile.addMetaDataEntry("type", "profile") # don't know what quality_type it is based on, so use "normal" by default profile.addMetaDataEntry("quality_type", "normal") profile.setName(profile_id) profile.setDirty(True) #Serialise and deserialise in order to perform the version upgrade. parser = configparser.ConfigParser(interpolation=None) data = profile.serialize() parser.read_string(data) parser["general"]["version"] = "1" if parser.has_section("values"): parser["settings"] = parser["values"] del parser["values"] stream = io.StringIO() parser.write(stream) data = stream.getvalue() profile.deserialize(data) #We need to return one extruder stack and one global stack. global_container_id = container_registry.uniqueName( "Global Imported Legacy Profile") global_profile = profile.duplicate( new_id=global_container_id, new_name=profile_id ) #Needs to have the same name as the extruder profile. global_profile.setDirty(True) #Only the extruder stack has an extruder metadata entry. profile.addMetaDataEntry( "extruder", ExtruderManager.getInstance().getActiveExtruderStack().definition. getId()) #Split all settings into per-extruder and global settings. for setting_key in profile.getAllKeys(): settable_per_extruder = global_container_stack.getProperty( setting_key, "settable_per_extruder") if settable_per_extruder: global_profile.removeInstance(setting_key) else: profile.removeInstance(setting_key) return [global_profile, profile]
class Script: def __init__(self): super().__init__() self._settings = None self._stack = None setting_data = self.getSettingData() self._stack = ContainerStack(stack_id = str(id(self))) self._stack.setDirty(False) # This stack does not need to be saved. ## Check if the definition of this script already exists. If not, add it to the registry. if "key" in setting_data: definitions = ContainerRegistry.getInstance().findDefinitionContainers(id = setting_data["key"]) if definitions: # Definition was found self._definition = definitions[0] else: self._definition = DefinitionContainer(setting_data["key"]) self._definition.deserialize(json.dumps(setting_data)) ContainerRegistry.getInstance().addContainer(self._definition) self._stack.addContainer(self._definition) self._instance = InstanceContainer(container_id="ScriptInstanceContainer") self._instance.setDefinition(self._definition.getId()) self._instance.addMetaDataEntry("setting_version", self._definition.getMetaDataEntry("setting_version", default = 0)) self._stack.addContainer(self._instance) self._stack.propertyChanged.connect(self._onPropertyChanged) ContainerRegistry.getInstance().addContainer(self._stack) settingsLoaded = Signal() valueChanged = Signal() # Signal emitted whenever a value of a setting is changed def _onPropertyChanged(self, key, property_name): if property_name == "value": self.valueChanged.emit() # Property changed: trigger reslice # To do this we use the global container stack propertyChanged. # Reslicing is necessary for setting changes in this plugin, because the changes # are applied only once per "fresh" gcode global_container_stack = Application.getInstance().getGlobalContainerStack() global_container_stack.propertyChanged.emit(key, property_name) ## Needs to return a dict that can be used to construct a settingcategory file. # See the example script for an example. # It follows the same style / guides as the Uranium settings. # Scripts can either override getSettingData directly, or use getSettingDataString # to return a string that will be parsed as json. The latter has the benefit over # returning a dict in that the order of settings is maintained. def getSettingData(self): setting_data = self.getSettingDataString() if type(setting_data) == str: setting_data = json.loads(setting_data, object_pairs_hook = collections.OrderedDict) return setting_data def getSettingDataString(self): raise NotImplementedError() def getDefinitionId(self): if self._stack: return self._stack.getBottom().getId() def getStackId(self): if self._stack: return self._stack.getId() ## Convenience function that retrieves value of a setting from the stack. def getSettingValueByKey(self, key): return self._stack.getProperty(key, "value") ## Convenience function that finds the value in a line of g-code. # When requesting key = x from line "G1 X100" the value 100 is returned. def getValue(self, line, key, default = None): if not key in line or (';' in line and line.find(key) > line.find(';')): return default sub_part = line[line.find(key) + 1:] m = re.search('^-?[0-9]+\.?[0-9]*', sub_part) if m is None: return default try: return float(m.group(0)) except: return default ## This is called when the script is executed. # It gets a list of g-code strings and needs to return a (modified) list. def execute(self, data): raise NotImplementedError()
def _serialiseSettings(self, stack): """Serialises a container stack to prepare it for writing at the end of the g-code. The settings are serialised, and special characters (including newline) are escaped. :param stack: A container stack to serialise. :return: A serialised string of the settings. """ container_registry = self._application.getContainerRegistry() prefix = self._setting_keyword + str( GCodeWriter.version) + " " # The prefix to put before each line. prefix_length = len(prefix) quality_type = stack.quality.getMetaDataEntry("quality_type") container_with_profile = stack.qualityChanges machine_definition_id_for_quality = ContainerTree.getInstance( ).machines[stack.definition.getId()].quality_definition if container_with_profile.getId() == "empty_quality_changes": # If the global quality changes is empty, create a new one quality_name = container_registry.uniqueName( stack.quality.getName()) quality_id = container_registry.uniqueName( (stack.definition.getId() + "_" + quality_name).lower().replace(" ", "_")) container_with_profile = InstanceContainer(quality_id) container_with_profile.setName(quality_name) container_with_profile.setMetaDataEntry("type", "quality_changes") container_with_profile.setMetaDataEntry("quality_type", quality_type) if stack.getMetaDataEntry( "position" ) is not None: # For extruder stacks, the quality changes should include an intent category. container_with_profile.setMetaDataEntry( "intent_category", stack.intent.getMetaDataEntry("intent_category", "solid")) container_with_profile.setDefinition( machine_definition_id_for_quality) container_with_profile.setMetaDataEntry( "setting_version", stack.quality.getMetaDataEntry("setting_version")) flat_global_container = self._createFlattenedContainerInstance( stack.userChanges, container_with_profile) # If the quality changes is not set, we need to set type manually if flat_global_container.getMetaDataEntry("type", None) is None: flat_global_container.setMetaDataEntry("type", "quality_changes") # Ensure that quality_type is set. (Can happen if we have empty quality changes). if flat_global_container.getMetaDataEntry("quality_type", None) is None: flat_global_container.setMetaDataEntry( "quality_type", stack.quality.getMetaDataEntry("quality_type", "normal")) # Get the machine definition ID for quality profiles flat_global_container.setMetaDataEntry( "definition", machine_definition_id_for_quality) serialized = flat_global_container.serialize() data = {"global_quality": serialized} all_setting_keys = flat_global_container.getAllKeys() for extruder in stack.extruderList: extruder_quality = extruder.qualityChanges if extruder_quality.getId() == "empty_quality_changes": # Same story, if quality changes is empty, create a new one quality_name = container_registry.uniqueName( stack.quality.getName()) quality_id = container_registry.uniqueName( (stack.definition.getId() + "_" + quality_name).lower().replace(" ", "_")) extruder_quality = InstanceContainer(quality_id) extruder_quality.setName(quality_name) extruder_quality.setMetaDataEntry("type", "quality_changes") extruder_quality.setMetaDataEntry("quality_type", quality_type) extruder_quality.setDefinition( machine_definition_id_for_quality) extruder_quality.setMetaDataEntry( "setting_version", stack.quality.getMetaDataEntry("setting_version")) flat_extruder_quality = self._createFlattenedContainerInstance( extruder.userChanges, extruder_quality) # If the quality changes is not set, we need to set type manually if flat_extruder_quality.getMetaDataEntry("type", None) is None: flat_extruder_quality.setMetaDataEntry("type", "quality_changes") # Ensure that extruder is set. (Can happen if we have empty quality changes). if flat_extruder_quality.getMetaDataEntry("position", None) is None: flat_extruder_quality.setMetaDataEntry( "position", extruder.getMetaDataEntry("position")) # Ensure that quality_type is set. (Can happen if we have empty quality changes). if flat_extruder_quality.getMetaDataEntry("quality_type", None) is None: flat_extruder_quality.setMetaDataEntry( "quality_type", extruder.quality.getMetaDataEntry("quality_type", "normal")) # Change the default definition flat_extruder_quality.setMetaDataEntry( "definition", machine_definition_id_for_quality) extruder_serialized = flat_extruder_quality.serialize() data.setdefault("extruder_quality", []).append(extruder_serialized) all_setting_keys.update(flat_extruder_quality.getAllKeys()) # Check if there is any profiles if not all_setting_keys: Logger.log( "i", "No custom settings found, not writing settings to g-code.") return "" json_string = json.dumps(data) # Escape characters that have a special meaning in g-code comments. pattern = re.compile("|".join(GCodeWriter.escape_characters.keys())) # Perform the replacement with a regular expression. escaped_string = pattern.sub( lambda m: GCodeWriter.escape_characters[re.escape(m.group(0))], json_string) # Introduce line breaks so that each comment is no longer than 80 characters. Prepend each line with the prefix. result = "" # Lines have 80 characters, so the payload of each line is 80 - prefix. for pos in range(0, len(escaped_string), 80 - prefix_length): result += prefix + escaped_string[pos:pos + 80 - prefix_length] + "\n" return result
def createExtruderTrain(self, extruder_definition: DefinitionContainerInterface, machine_definition: DefinitionContainerInterface, position, machine_id: str) -> None: # Cache some things. container_registry = ContainerRegistry.getInstance() machine_definition_id = Application.getInstance().getMachineManager().getQualityDefinitionId(machine_definition) # Create a container stack for this extruder. extruder_stack_id = container_registry.uniqueName(extruder_definition.getId()) container_stack = ContainerStack(extruder_stack_id) container_stack.setName(extruder_definition.getName()) # Take over the display name to display the stack with. container_stack.addMetaDataEntry("type", "extruder_train") container_stack.addMetaDataEntry("machine", machine_id) container_stack.addMetaDataEntry("position", position) container_stack.addContainer(extruder_definition) # Find the variant to use for this extruder. variant = container_registry.findInstanceContainers(id = "empty_variant")[0] if machine_definition.getMetaDataEntry("has_variants"): # First add any variant. Later, overwrite with preference if the preference is valid. variants = container_registry.findInstanceContainers(definition = machine_definition_id, type = "variant") if len(variants) >= 1: variant = variants[0] preferred_variant_id = machine_definition.getMetaDataEntry("preferred_variant") if preferred_variant_id: preferred_variants = container_registry.findInstanceContainers(id = preferred_variant_id, definition = machine_definition_id, type = "variant") if len(preferred_variants) >= 1: variant = preferred_variants[0] else: Logger.log("w", "The preferred variant \"%s\" of machine %s doesn't exist or is not a variant profile.", preferred_variant_id, machine_id) # And leave it at the default variant. container_stack.addContainer(variant) # Find a material to use for this variant. material = container_registry.findInstanceContainers(id = "empty_material")[0] if machine_definition.getMetaDataEntry("has_materials"): # First add any material. Later, overwrite with preference if the preference is valid. machine_has_variant_materials = machine_definition.getMetaDataEntry("has_variant_materials", default = False) if machine_has_variant_materials or machine_has_variant_materials == "True": materials = container_registry.findInstanceContainers(type = "material", definition = machine_definition_id, variant = variant.getId()) else: materials = container_registry.findInstanceContainers(type = "material", definition = machine_definition_id) if len(materials) >= 1: material = materials[0] preferred_material_id = machine_definition.getMetaDataEntry("preferred_material") if preferred_material_id: global_stack = ContainerRegistry.getInstance().findContainerStacks(id = machine_id) if global_stack: approximate_material_diameter = str(round(global_stack[0].getProperty("material_diameter", "value"))) else: approximate_material_diameter = str(round(machine_definition.getProperty("material_diameter", "value"))) search_criteria = { "type": "material", "id": preferred_material_id, "approximate_diameter": approximate_material_diameter} if machine_definition.getMetaDataEntry("has_machine_materials"): search_criteria["definition"] = machine_definition_id if machine_definition.getMetaDataEntry("has_variants") and variant: search_criteria["variant"] = variant.id else: search_criteria["definition"] = "fdmprinter" preferred_materials = container_registry.findInstanceContainers(**search_criteria) if len(preferred_materials) >= 1: # In some cases we get multiple materials. In that case, prefer materials that are marked as read only. read_only_preferred_materials = [preferred_material for preferred_material in preferred_materials if preferred_material.isReadOnly()] if len(read_only_preferred_materials) >= 1: material = read_only_preferred_materials[0] else: material = preferred_materials[0] else: Logger.log("w", "The preferred material \"%s\" of machine %s doesn't exist or is not a material profile.", preferred_material_id, machine_id) # And leave it at the default material. container_stack.addContainer(material) # Find a quality to use for this extruder. quality = container_registry.getEmptyInstanceContainer() search_criteria = { "type": "quality" } if machine_definition.getMetaDataEntry("has_machine_quality"): search_criteria["definition"] = machine_definition_id if machine_definition.getMetaDataEntry("has_materials") and material: search_criteria["material"] = material.id else: search_criteria["definition"] = "fdmprinter" preferred_quality = machine_definition.getMetaDataEntry("preferred_quality") if preferred_quality: search_criteria["id"] = preferred_quality containers = ContainerRegistry.getInstance().findInstanceContainers(**search_criteria) if not containers and preferred_quality: Logger.log("w", "The preferred quality \"%s\" of machine %s doesn't exist or is not a quality profile.", preferred_quality, machine_id) search_criteria.pop("id", None) containers = ContainerRegistry.getInstance().findInstanceContainers(**search_criteria) if containers: quality = containers[0] container_stack.addContainer(quality) empty_quality_changes = container_registry.findInstanceContainers(id = "empty_quality_changes")[0] container_stack.addContainer(empty_quality_changes) user_profile = container_registry.findInstanceContainers(type = "user", extruder = extruder_stack_id) if user_profile: # There was already a user profile, loaded from settings. user_profile = user_profile[0] else: user_profile = InstanceContainer(extruder_stack_id + "_current_settings") # Add an empty user profile. user_profile.addMetaDataEntry("type", "user") user_profile.addMetaDataEntry("extruder", extruder_stack_id) from cura.CuraApplication import CuraApplication user_profile.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) user_profile.setDefinition(machine_definition) container_registry.addContainer(user_profile) container_stack.addContainer(user_profile) # regardless of what the next stack is, we have to set it again, because of signal routing. container_stack.setNextStack(Application.getInstance().getGlobalContainerStack()) container_registry.addContainer(container_stack)
def createExtruderTrain(self, extruder_definition: DefinitionContainerInterface, machine_definition: DefinitionContainerInterface, position, machine_id: str) -> None: # Cache some things. container_registry = ContainerRegistry.getInstance() machine_definition_id = Application.getInstance().getMachineManager().getQualityDefinitionId(machine_definition) # Create a container stack for this extruder. extruder_stack_id = container_registry.uniqueName(extruder_definition.getId()) container_stack = ContainerStack(extruder_stack_id) container_stack.setName(extruder_definition.getName()) # Take over the display name to display the stack with. container_stack.addMetaDataEntry("type", "extruder_train") container_stack.addMetaDataEntry("machine", machine_id) container_stack.addMetaDataEntry("position", position) container_stack.addContainer(extruder_definition) # Find the variant to use for this extruder. variant = container_registry.findInstanceContainers(id = "empty_variant")[0] if machine_definition.getMetaDataEntry("has_variants"): # First add any variant. Later, overwrite with preference if the preference is valid. variants = container_registry.findInstanceContainers(definition = machine_definition_id, type = "variant") if len(variants) >= 1: variant = variants[0] preferred_variant_id = machine_definition.getMetaDataEntry("preferred_variant") if preferred_variant_id: preferred_variants = container_registry.findInstanceContainers(id = preferred_variant_id, definition = machine_definition_id, type = "variant") if len(preferred_variants) >= 1: variant = preferred_variants[0] else: Logger.log("w", "The preferred variant \"%s\" of machine %s doesn't exist or is not a variant profile.", preferred_variant_id, machine_id) # And leave it at the default variant. container_stack.addContainer(variant) # Find a material to use for this variant. material = container_registry.findInstanceContainers(id = "empty_material")[0] if machine_definition.getMetaDataEntry("has_materials"): # First add any material. Later, overwrite with preference if the preference is valid. machine_has_variant_materials = machine_definition.getMetaDataEntry("has_variant_materials", default = False) if machine_has_variant_materials or machine_has_variant_materials == "True": materials = container_registry.findInstanceContainers(type = "material", definition = machine_definition_id, variant = variant.getId()) else: materials = container_registry.findInstanceContainers(type = "material", definition = machine_definition_id) if len(materials) >= 1: material = materials[0] preferred_material_id = machine_definition.getMetaDataEntry("preferred_material") if preferred_material_id: global_stack = ContainerRegistry.getInstance().findContainerStacks(id = machine_id) if global_stack: approximate_material_diameter = str(round(global_stack[0].getProperty("material_diameter", "value"))) else: approximate_material_diameter = str(round(machine_definition.getProperty("material_diameter", "value"))) search_criteria = { "type": "material", "id": preferred_material_id, "approximate_diameter": approximate_material_diameter} if machine_definition.getMetaDataEntry("has_machine_materials"): search_criteria["definition"] = machine_definition_id if machine_definition.getMetaDataEntry("has_variants") and variant: search_criteria["variant"] = variant.id else: search_criteria["definition"] = "fdmprinter" preferred_materials = container_registry.findInstanceContainers(**search_criteria) if len(preferred_materials) >= 1: # In some cases we get multiple materials. In that case, prefer materials that are marked as read only. read_only_preferred_materials = [preferred_material for preferred_material in preferred_materials if preferred_material.isReadOnly()] if len(read_only_preferred_materials) >= 1: material = read_only_preferred_materials[0] else: material = preferred_materials[0] else: Logger.log("w", "The preferred material \"%s\" of machine %s doesn't exist or is not a material profile.", preferred_material_id, machine_id) # And leave it at the default material. container_stack.addContainer(material) # Find a quality to use for this extruder. quality = container_registry.getEmptyInstanceContainer() search_criteria = { "type": "quality" } if machine_definition.getMetaDataEntry("has_machine_quality"): search_criteria["definition"] = machine_definition_id if machine_definition.getMetaDataEntry("has_materials") and material: search_criteria["material"] = material.id else: search_criteria["definition"] = "fdmprinter" preferred_quality = machine_definition.getMetaDataEntry("preferred_quality") if preferred_quality: search_criteria["id"] = preferred_quality containers = ContainerRegistry.getInstance().findInstanceContainers(**search_criteria) if not containers and preferred_quality: Logger.log("w", "The preferred quality \"%s\" of machine %s doesn't exist or is not a quality profile.", preferred_quality, machine_id) search_criteria.pop("id", None) containers = ContainerRegistry.getInstance().findInstanceContainers(**search_criteria) if containers: quality = containers[0] container_stack.addContainer(quality) empty_quality_changes = container_registry.findInstanceContainers(id = "empty_quality_changes")[0] container_stack.addContainer(empty_quality_changes) user_profile = container_registry.findInstanceContainers(type = "user", extruder = extruder_stack_id) if user_profile: # There was already a user profile, loaded from settings. user_profile = user_profile[0] else: user_profile = InstanceContainer(extruder_stack_id + "_current_settings") # Add an empty user profile. user_profile.addMetaDataEntry("type", "user") user_profile.addMetaDataEntry("extruder", extruder_stack_id) from cura.CuraApplication import CuraApplication user_profile.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) user_profile.setDefinition(machine_definition) container_registry.addContainer(user_profile) container_stack.addContainer(user_profile) # regardless of what the next stack is, we have to set it again, because of signal routing. container_stack.setNextStack(Application.getInstance().getGlobalContainerStack()) container_registry.addContainer(container_stack)
def addExtruderStackForSingleExtrusionMachine(self, machine, extruder_id): new_extruder_id = extruder_id extruder_definitions = self.findDefinitionContainers(id = new_extruder_id) if not extruder_definitions: Logger.log("w", "Could not find definition containers for extruder %s", new_extruder_id) return extruder_definition = extruder_definitions[0] unique_name = self.uniqueName(machine.getName() + " " + new_extruder_id) extruder_stack = ExtruderStack.ExtruderStack(unique_name) extruder_stack.setName(extruder_definition.getName()) extruder_stack.setDefinition(extruder_definition) extruder_stack.addMetaDataEntry("position", extruder_definition.getMetaDataEntry("position")) extruder_stack.setNextStack(machine) # create empty user changes container otherwise user_container = InstanceContainer(extruder_stack.id + "_user") user_container.addMetaDataEntry("type", "user") user_container.addMetaDataEntry("machine", extruder_stack.getId()) from cura.CuraApplication import CuraApplication user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) user_container.setDefinition(machine.definition) if machine.userChanges: # for the newly created extruder stack, we need to move all "per-extruder" settings to the user changes # container to the extruder stack. for user_setting_key in machine.userChanges.getAllKeys(): settable_per_extruder = machine.getProperty(user_setting_key, "settable_per_extruder") if settable_per_extruder: user_container.addInstance(machine.userChanges.getInstance(user_setting_key)) machine.userChanges.removeInstance(user_setting_key, postpone_emit = True) extruder_stack.setUserChanges(user_container) self.addContainer(user_container) variant_id = "default" if machine.variant.getId() not in ("empty", "empty_variant"): variant_id = machine.variant.getId() else: variant_id = "empty_variant" extruder_stack.setVariantById(variant_id) material_id = "default" if machine.material.getId() not in ("empty", "empty_material"): material_id = machine.material.getId() else: material_id = "empty_material" extruder_stack.setMaterialById(material_id) quality_id = "default" if machine.quality.getId() not in ("empty", "empty_quality"): quality_id = machine.quality.getId() else: quality_id = "empty_quality" extruder_stack.setQualityById(quality_id) if machine.qualityChanges.getId() not in ("empty", "empty_quality_changes"): extruder_quality_changes_container = self.findInstanceContainers(name = machine.qualityChanges.getName(), extruder = extruder_id) if extruder_quality_changes_container: extruder_quality_changes_container = extruder_quality_changes_container[0] quality_changes_id = extruder_quality_changes_container.getId() extruder_stack.setQualityChangesById(quality_changes_id) else: # Some extruder quality_changes containers can be created at runtime as files in the qualities # folder. Those files won't be loaded in the registry immediately. So we also need to search # the folder to see if the quality_changes exists. extruder_quality_changes_container = self._findQualityChangesContainerInCuraFolder(machine.qualityChanges.getName()) if extruder_quality_changes_container: quality_changes_id = extruder_quality_changes_container.getId() extruder_stack.setQualityChangesById(quality_changes_id) if not extruder_quality_changes_container: Logger.log("w", "Could not find quality_changes named [%s] for extruder [%s]", machine.qualityChanges.getName(), extruder_stack.getId()) else: extruder_stack.setQualityChangesById("empty_quality_changes") self.addContainer(extruder_stack) return extruder_stack
def addExtruderStackForSingleExtrusionMachine( self, machine, extruder_id, new_global_quality_changes=None, create_new_ids=True): new_extruder_id = extruder_id application = cura.CuraApplication.CuraApplication.getInstance() extruder_definitions = self.findDefinitionContainers( id=new_extruder_id) if not extruder_definitions: Logger.log("w", "Could not find definition containers for extruder %s", new_extruder_id) return extruder_definition = extruder_definitions[0] unique_name = self.uniqueName( machine.getName() + " " + new_extruder_id ) if create_new_ids else machine.getName() + " " + new_extruder_id extruder_stack = ExtruderStack.ExtruderStack(unique_name) extruder_stack.setName(extruder_definition.getName()) extruder_stack.setDefinition(extruder_definition) extruder_stack.setMetaDataEntry( "position", extruder_definition.getMetaDataEntry("position")) # create a new definition_changes container for the extruder stack definition_changes_id = self.uniqueName( extruder_stack.getId() + "_settings" ) if create_new_ids else extruder_stack.getId() + "_settings" definition_changes_name = definition_changes_id definition_changes = InstanceContainer(definition_changes_id, parent=application) definition_changes.setName(definition_changes_name) definition_changes.setMetaDataEntry("setting_version", application.SettingVersion) definition_changes.setMetaDataEntry("type", "definition_changes") definition_changes.setMetaDataEntry("definition", extruder_definition.getId()) # move definition_changes settings if exist for setting_key in definition_changes.getAllKeys(): if machine.definition.getProperty(setting_key, "settable_per_extruder"): setting_value = machine.definitionChanges.getProperty( setting_key, "value") if setting_value is not None: # move it to the extruder stack's definition_changes setting_definition = machine.getSettingDefinition( setting_key) new_instance = SettingInstance(setting_definition, definition_changes) new_instance.setProperty("value", setting_value) new_instance.resetState( ) # Ensure that the state is not seen as a user state. definition_changes.addInstance(new_instance) definition_changes.setDirty(True) machine.definitionChanges.removeInstance( setting_key, postpone_emit=True) self.addContainer(definition_changes) extruder_stack.setDefinitionChanges(definition_changes) # create empty user changes container otherwise user_container_id = self.uniqueName( extruder_stack.getId() + "_user") if create_new_ids else extruder_stack.getId() + "_user" user_container_name = user_container_id user_container = InstanceContainer(user_container_id, parent=application) user_container.setName(user_container_name) user_container.setMetaDataEntry("type", "user") user_container.setMetaDataEntry("machine", machine.getId()) user_container.setMetaDataEntry("setting_version", application.SettingVersion) user_container.setDefinition(machine.definition.getId()) user_container.setMetaDataEntry( "position", extruder_stack.getMetaDataEntry("position")) if machine.userChanges: # For the newly created extruder stack, we need to move all "per-extruder" settings to the user changes # container to the extruder stack. for user_setting_key in machine.userChanges.getAllKeys(): settable_per_extruder = machine.getProperty( user_setting_key, "settable_per_extruder") if settable_per_extruder: setting_value = machine.getProperty( user_setting_key, "value") setting_definition = machine.getSettingDefinition( user_setting_key) new_instance = SettingInstance(setting_definition, definition_changes) new_instance.setProperty("value", setting_value) new_instance.resetState( ) # Ensure that the state is not seen as a user state. user_container.addInstance(new_instance) user_container.setDirty(True) machine.userChanges.removeInstance(user_setting_key, postpone_emit=True) self.addContainer(user_container) extruder_stack.setUserChanges(user_container) empty_variant = application.empty_variant_container empty_material = application.empty_material_container empty_quality = application.empty_quality_container if machine.variant.getId() not in ("empty", "empty_variant"): variant = machine.variant else: variant = empty_variant extruder_stack.variant = variant if machine.material.getId() not in ("empty", "empty_material"): material = machine.material else: material = empty_material extruder_stack.material = material if machine.quality.getId() not in ("empty", "empty_quality"): quality = machine.quality else: quality = empty_quality extruder_stack.quality = quality machine_quality_changes = machine.qualityChanges if new_global_quality_changes is not None: machine_quality_changes = new_global_quality_changes if machine_quality_changes.getId() not in ("empty", "empty_quality_changes"): extruder_quality_changes_container = self.findInstanceContainers( name=machine_quality_changes.getName(), extruder=extruder_id) if extruder_quality_changes_container: extruder_quality_changes_container = extruder_quality_changes_container[ 0] quality_changes_id = extruder_quality_changes_container.getId() extruder_stack.qualityChanges = self.findInstanceContainers( id=quality_changes_id)[0] else: # Some extruder quality_changes containers can be created at runtime as files in the qualities # folder. Those files won't be loaded in the registry immediately. So we also need to search # the folder to see if the quality_changes exists. extruder_quality_changes_container = self._findQualityChangesContainerInCuraFolder( machine_quality_changes.getName()) if extruder_quality_changes_container: quality_changes_id = extruder_quality_changes_container.getId( ) extruder_quality_changes_container.setMetaDataEntry( "position", extruder_definition.getMetaDataEntry("position")) extruder_stack.qualityChanges = self.findInstanceContainers( id=quality_changes_id)[0] else: # If we still cannot find a quality changes container for the extruder, create a new one container_name = machine_quality_changes.getName() container_id = self.uniqueName(extruder_stack.getId() + "_qc_" + container_name) extruder_quality_changes_container = InstanceContainer( container_id, parent=application) extruder_quality_changes_container.setName(container_name) extruder_quality_changes_container.setMetaDataEntry( "type", "quality_changes") extruder_quality_changes_container.setMetaDataEntry( "setting_version", application.SettingVersion) extruder_quality_changes_container.setMetaDataEntry( "position", extruder_definition.getMetaDataEntry("position")) extruder_quality_changes_container.setMetaDataEntry( "quality_type", machine_quality_changes.getMetaDataEntry( "quality_type")) extruder_quality_changes_container.setDefinition( machine_quality_changes.getDefinition().getId()) self.addContainer(extruder_quality_changes_container) extruder_stack.qualityChanges = extruder_quality_changes_container if not extruder_quality_changes_container: Logger.log( "w", "Could not find quality_changes named [%s] for extruder [%s]", machine_quality_changes.getName(), extruder_stack.getId()) else: # Move all per-extruder settings to the extruder's quality changes for qc_setting_key in machine_quality_changes.getAllKeys(): settable_per_extruder = machine.getProperty( qc_setting_key, "settable_per_extruder") if settable_per_extruder: setting_value = machine_quality_changes.getProperty( qc_setting_key, "value") setting_definition = machine.getSettingDefinition( qc_setting_key) new_instance = SettingInstance(setting_definition, definition_changes) new_instance.setProperty("value", setting_value) new_instance.resetState( ) # Ensure that the state is not seen as a user state. extruder_quality_changes_container.addInstance( new_instance) extruder_quality_changes_container.setDirty(True) machine_quality_changes.removeInstance( qc_setting_key, postpone_emit=True) else: extruder_stack.qualityChanges = self.findInstanceContainers( id="empty_quality_changes")[0] self.addContainer(extruder_stack) # Also need to fix the other qualities that are suitable for this machine. Those quality changes may still have # per-extruder settings in the container for the machine instead of the extruder. if machine_quality_changes.getId() not in ("empty", "empty_quality_changes"): quality_changes_machine_definition_id = machine_quality_changes.getDefinition( ).getId() else: whole_machine_definition = machine.definition machine_entry = machine.definition.getMetaDataEntry("machine") if machine_entry is not None: container_registry = ContainerRegistry.getInstance() whole_machine_definition = container_registry.findDefinitionContainers( id=machine_entry)[0] quality_changes_machine_definition_id = "fdmprinter" if whole_machine_definition.getMetaDataEntry( "has_machine_quality"): quality_changes_machine_definition_id = machine.definition.getMetaDataEntry( "quality_definition", whole_machine_definition.getId()) qcs = self.findInstanceContainers( type="quality_changes", definition=quality_changes_machine_definition_id) qc_groups = {} # map of qc names -> qc containers for qc in qcs: qc_name = qc.getName() if qc_name not in qc_groups: qc_groups[qc_name] = [] qc_groups[qc_name].append(qc) # Try to find from the quality changes cura directory too quality_changes_container = self._findQualityChangesContainerInCuraFolder( machine_quality_changes.getName()) if quality_changes_container: qc_groups[qc_name].append(quality_changes_container) for qc_name, qc_list in qc_groups.items(): qc_dict = {"global": None, "extruders": []} for qc in qc_list: extruder_position = qc.getMetaDataEntry("position") if extruder_position is not None: qc_dict["extruders"].append(qc) else: qc_dict["global"] = qc if qc_dict["global"] is not None and len( qc_dict["extruders"]) == 1: # Move per-extruder settings for qc_setting_key in qc_dict["global"].getAllKeys(): settable_per_extruder = machine.getProperty( qc_setting_key, "settable_per_extruder") if settable_per_extruder: setting_value = qc_dict["global"].getProperty( qc_setting_key, "value") setting_definition = machine.getSettingDefinition( qc_setting_key) new_instance = SettingInstance(setting_definition, definition_changes) new_instance.setProperty("value", setting_value) new_instance.resetState( ) # Ensure that the state is not seen as a user state. qc_dict["extruders"][0].addInstance(new_instance) qc_dict["extruders"][0].setDirty(True) qc_dict["global"].removeInstance(qc_setting_key, postpone_emit=True) # Set next stack at the end extruder_stack.setNextStack(machine) return extruder_stack
def read(self, file_name): if file_name.split(".")[-1] != "ini": return None global_container_stack = Application.getInstance().getGlobalContainerStack() if not global_container_stack: return None multi_extrusion = global_container_stack.getProperty("machine_extruder_count", "value") > 1 if multi_extrusion: Logger.log("e", "Unable to import legacy profile %s. Multi extrusion is not supported", file_name) raise Exception("Unable to import legacy profile. Multi extrusion is not supported") Logger.log("i", "Importing legacy profile from file " + file_name + ".") container_registry = ContainerRegistry.getInstance() profile_id = container_registry.uniqueName("Imported Legacy Profile") profile = InstanceContainer(profile_id) # Create an empty profile. parser = configparser.ConfigParser(interpolation = None) try: parser.read([file_name]) # Parse the INI file. except Exception as e: Logger.log("e", "Unable to open legacy profile %s: %s", file_name, str(e)) return None # Legacy Cura saved the profile under the section "profile_N" where N is the ID of a machine, except when you export in which case it saves it in the section "profile". # Since importing multiple machine profiles is out of scope, just import the first section we find. section = "" for found_section in parser.sections(): if found_section.startswith("profile"): section = found_section break if not section: # No section starting with "profile" was found. Probably not a proper INI file. return None try: with open(os.path.join(PluginRegistry.getInstance().getPluginPath("LegacyProfileReader"), "DictionaryOfDoom.json"), "r", encoding = "utf-8") as f: dict_of_doom = json.load(f) # Parse the Dictionary of Doom. except IOError as e: Logger.log("e", "Could not open DictionaryOfDoom.json for reading: %s", str(e)) return None except Exception as e: Logger.log("e", "Could not parse DictionaryOfDoom.json: %s", str(e)) return None defaults = self.prepareDefaults(dict_of_doom) legacy_settings = self.prepareLocals(parser, section, defaults) #Gets the settings from the legacy profile. #Check the target version in the Dictionary of Doom with this application version. if "target_version" not in dict_of_doom: Logger.log("e", "Dictionary of Doom has no target version. Is it the correct JSON file?") return None if InstanceContainer.Version != dict_of_doom["target_version"]: Logger.log("e", "Dictionary of Doom of legacy profile reader (version %s) is not in sync with the current instance container version (version %s)!", dict_of_doom["target_version"], str(InstanceContainer.Version)) return None if "translation" not in dict_of_doom: Logger.log("e", "Dictionary of Doom has no translation. Is it the correct JSON file?") return None current_printer_definition = global_container_stack.definition quality_definition = current_printer_definition.getMetaDataEntry("quality_definition") if not quality_definition: quality_definition = current_printer_definition.getId() profile.setDefinition(quality_definition) for new_setting in dict_of_doom["translation"]: # Evaluate all new settings that would get a value from the translations. old_setting_expression = dict_of_doom["translation"][new_setting] compiled = compile(old_setting_expression, new_setting, "eval") try: new_value = eval(compiled, {"math": math}, legacy_settings) # Pass the legacy settings as local variables to allow access to in the evaluation. value_using_defaults = eval(compiled, {"math": math}, defaults) #Evaluate again using only the default values to try to see if they are default. except Exception: # Probably some setting name that was missing or something else that went wrong in the ini file. Logger.log("w", "Setting " + new_setting + " could not be set because the evaluation failed. Something is probably missing from the imported legacy profile.") continue definitions = current_printer_definition.findDefinitions(key = new_setting) if definitions: if new_value != value_using_defaults and definitions[0].default_value != new_value: # Not equal to the default in the new Cura OR the default in the legacy Cura. profile.setProperty(new_setting, "value", new_value) # Store the setting in the profile! if len(profile.getAllKeys()) == 0: Logger.log("i", "A legacy profile was imported but everything evaluates to the defaults, creating an empty profile.") profile.addMetaDataEntry("type", "profile") # don't know what quality_type it is based on, so use "normal" by default profile.addMetaDataEntry("quality_type", "normal") profile.setName(profile_id) profile.setDirty(True) #Serialise and deserialise in order to perform the version upgrade. parser = configparser.ConfigParser(interpolation=None) data = profile.serialize() parser.read_string(data) parser["general"]["version"] = "1" if parser.has_section("values"): parser["settings"] = parser["values"] del parser["values"] stream = io.StringIO() parser.write(stream) data = stream.getvalue() profile.deserialize(data) # The definition can get reset to fdmprinter during the deserialization's upgrade. Here we set the definition # again. profile.setDefinition(quality_definition) #We need to return one extruder stack and one global stack. global_container_id = container_registry.uniqueName("Global Imported Legacy Profile") global_profile = profile.duplicate(new_id = global_container_id, new_name = profile_id) #Needs to have the same name as the extruder profile. global_profile.setDirty(True) profile_definition = "fdmprinter" from UM.Util import parseBool if parseBool(global_container_stack.getMetaDataEntry("has_machine_quality", "False")): profile_definition = global_container_stack.getMetaDataEntry("quality_definition") if not profile_definition: profile_definition = global_container_stack.definition.getId() global_profile.setDefinition(profile_definition) return [global_profile]
def addExtruderStackForSingleExtrusionMachine(self, machine, extruder_id, new_global_quality_changes = None, create_new_ids = True): new_extruder_id = extruder_id application = CuraApplication.getInstance() extruder_definitions = self.findDefinitionContainers(id = new_extruder_id) if not extruder_definitions: Logger.log("w", "Could not find definition containers for extruder %s", new_extruder_id) return extruder_definition = extruder_definitions[0] unique_name = self.uniqueName(machine.getName() + " " + new_extruder_id) if create_new_ids else machine.getName() + " " + new_extruder_id extruder_stack = ExtruderStack.ExtruderStack(unique_name) extruder_stack.setName(extruder_definition.getName()) extruder_stack.setDefinition(extruder_definition) extruder_stack.addMetaDataEntry("position", extruder_definition.getMetaDataEntry("position")) # create a new definition_changes container for the extruder stack definition_changes_id = self.uniqueName(extruder_stack.getId() + "_settings") if create_new_ids else extruder_stack.getId() + "_settings" definition_changes_name = definition_changes_id definition_changes = InstanceContainer(definition_changes_id, parent = application) definition_changes.setName(definition_changes_name) definition_changes.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) definition_changes.addMetaDataEntry("type", "definition_changes") definition_changes.addMetaDataEntry("definition", extruder_definition.getId()) # move definition_changes settings if exist for setting_key in definition_changes.getAllKeys(): if machine.definition.getProperty(setting_key, "settable_per_extruder"): setting_value = machine.definitionChanges.getProperty(setting_key, "value") if setting_value is not None: # move it to the extruder stack's definition_changes setting_definition = machine.getSettingDefinition(setting_key) new_instance = SettingInstance(setting_definition, definition_changes) new_instance.setProperty("value", setting_value) new_instance.resetState() # Ensure that the state is not seen as a user state. definition_changes.addInstance(new_instance) definition_changes.setDirty(True) machine.definitionChanges.removeInstance(setting_key, postpone_emit = True) self.addContainer(definition_changes) extruder_stack.setDefinitionChanges(definition_changes) # create empty user changes container otherwise user_container_id = self.uniqueName(extruder_stack.getId() + "_user") if create_new_ids else extruder_stack.getId() + "_user" user_container_name = user_container_id user_container = InstanceContainer(user_container_id, parent = application) user_container.setName(user_container_name) user_container.addMetaDataEntry("type", "user") user_container.addMetaDataEntry("machine", machine.getId()) user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) user_container.setDefinition(machine.definition.getId()) user_container.setMetaDataEntry("position", extruder_stack.getMetaDataEntry("position")) if machine.userChanges: # for the newly created extruder stack, we need to move all "per-extruder" settings to the user changes # container to the extruder stack. for user_setting_key in machine.userChanges.getAllKeys(): settable_per_extruder = machine.getProperty(user_setting_key, "settable_per_extruder") if settable_per_extruder: setting_value = machine.getProperty(user_setting_key, "value") setting_definition = machine.getSettingDefinition(user_setting_key) new_instance = SettingInstance(setting_definition, definition_changes) new_instance.setProperty("value", setting_value) new_instance.resetState() # Ensure that the state is not seen as a user state. user_container.addInstance(new_instance) user_container.setDirty(True) machine.userChanges.removeInstance(user_setting_key, postpone_emit = True) self.addContainer(user_container) extruder_stack.setUserChanges(user_container) empty_variant = application.empty_variant_container empty_material = application.empty_material_container empty_quality = application.empty_quality_container if machine.variant.getId() not in ("empty", "empty_variant"): variant = machine.variant else: variant = empty_variant extruder_stack.variant = variant if machine.material.getId() not in ("empty", "empty_material"): material = machine.material else: material = empty_material extruder_stack.material = material if machine.quality.getId() not in ("empty", "empty_quality"): quality = machine.quality else: quality = empty_quality extruder_stack.quality = quality machine_quality_changes = machine.qualityChanges if new_global_quality_changes is not None: machine_quality_changes = new_global_quality_changes if machine_quality_changes.getId() not in ("empty", "empty_quality_changes"): extruder_quality_changes_container = self.findInstanceContainers(name = machine_quality_changes.getName(), extruder = extruder_id) if extruder_quality_changes_container: extruder_quality_changes_container = extruder_quality_changes_container[0] quality_changes_id = extruder_quality_changes_container.getId() extruder_stack.qualityChanges = self.findInstanceContainers(id = quality_changes_id)[0] else: # Some extruder quality_changes containers can be created at runtime as files in the qualities # folder. Those files won't be loaded in the registry immediately. So we also need to search # the folder to see if the quality_changes exists. extruder_quality_changes_container = self._findQualityChangesContainerInCuraFolder(machine_quality_changes.getName()) if extruder_quality_changes_container: quality_changes_id = extruder_quality_changes_container.getId() extruder_quality_changes_container.addMetaDataEntry("position", extruder_definition.getMetaDataEntry("position")) extruder_stack.qualityChanges = self.findInstanceContainers(id = quality_changes_id)[0] else: # if we still cannot find a quality changes container for the extruder, create a new one container_name = machine_quality_changes.getName() container_id = self.uniqueName(extruder_stack.getId() + "_qc_" + container_name) extruder_quality_changes_container = InstanceContainer(container_id, parent = application) extruder_quality_changes_container.setName(container_name) extruder_quality_changes_container.addMetaDataEntry("type", "quality_changes") extruder_quality_changes_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) extruder_quality_changes_container.addMetaDataEntry("position", extruder_definition.getMetaDataEntry("position")) extruder_quality_changes_container.addMetaDataEntry("quality_type", machine_quality_changes.getMetaDataEntry("quality_type")) extruder_quality_changes_container.setDefinition(machine_quality_changes.getDefinition().getId()) self.addContainer(extruder_quality_changes_container) extruder_stack.qualityChanges = extruder_quality_changes_container if not extruder_quality_changes_container: Logger.log("w", "Could not find quality_changes named [%s] for extruder [%s]", machine_quality_changes.getName(), extruder_stack.getId()) else: # move all per-extruder settings to the extruder's quality changes for qc_setting_key in machine_quality_changes.getAllKeys(): settable_per_extruder = machine.getProperty(qc_setting_key, "settable_per_extruder") if settable_per_extruder: setting_value = machine_quality_changes.getProperty(qc_setting_key, "value") setting_definition = machine.getSettingDefinition(qc_setting_key) new_instance = SettingInstance(setting_definition, definition_changes) new_instance.setProperty("value", setting_value) new_instance.resetState() # Ensure that the state is not seen as a user state. extruder_quality_changes_container.addInstance(new_instance) extruder_quality_changes_container.setDirty(True) machine_quality_changes.removeInstance(qc_setting_key, postpone_emit=True) else: extruder_stack.qualityChanges = self.findInstanceContainers(id = "empty_quality_changes")[0] self.addContainer(extruder_stack) # Also need to fix the other qualities that are suitable for this machine. Those quality changes may still have # per-extruder settings in the container for the machine instead of the extruder. if machine_quality_changes.getId() not in ("empty", "empty_quality_changes"): quality_changes_machine_definition_id = machine_quality_changes.getDefinition().getId() else: whole_machine_definition = machine.definition machine_entry = machine.definition.getMetaDataEntry("machine") if machine_entry is not None: container_registry = ContainerRegistry.getInstance() whole_machine_definition = container_registry.findDefinitionContainers(id = machine_entry)[0] quality_changes_machine_definition_id = "fdmprinter" if whole_machine_definition.getMetaDataEntry("has_machine_quality"): quality_changes_machine_definition_id = machine.definition.getMetaDataEntry("quality_definition", whole_machine_definition.getId()) qcs = self.findInstanceContainers(type = "quality_changes", definition = quality_changes_machine_definition_id) qc_groups = {} # map of qc names -> qc containers for qc in qcs: qc_name = qc.getName() if qc_name not in qc_groups: qc_groups[qc_name] = [] qc_groups[qc_name].append(qc) # try to find from the quality changes cura directory too quality_changes_container = self._findQualityChangesContainerInCuraFolder(machine_quality_changes.getName()) if quality_changes_container: qc_groups[qc_name].append(quality_changes_container) for qc_name, qc_list in qc_groups.items(): qc_dict = {"global": None, "extruders": []} for qc in qc_list: extruder_position = qc.getMetaDataEntry("position") if extruder_position is not None: qc_dict["extruders"].append(qc) else: qc_dict["global"] = qc if qc_dict["global"] is not None and len(qc_dict["extruders"]) == 1: # move per-extruder settings for qc_setting_key in qc_dict["global"].getAllKeys(): settable_per_extruder = machine.getProperty(qc_setting_key, "settable_per_extruder") if settable_per_extruder: setting_value = qc_dict["global"].getProperty(qc_setting_key, "value") setting_definition = machine.getSettingDefinition(qc_setting_key) new_instance = SettingInstance(setting_definition, definition_changes) new_instance.setProperty("value", setting_value) new_instance.resetState() # Ensure that the state is not seen as a user state. qc_dict["extruders"][0].addInstance(new_instance) qc_dict["extruders"][0].setDirty(True) qc_dict["global"].removeInstance(qc_setting_key, postpone_emit=True) # Set next stack at the end extruder_stack.setNextStack(machine) return extruder_stack
class Script: def __init__(self): super().__init__() self._settings = None self._stack = None setting_data = self.getSettingData() self._stack = ContainerStack(stack_id=id(self)) self._stack.setDirty(False) # This stack does not need to be saved. ## Check if the definition of this script already exists. If not, add it to the registry. if "key" in setting_data: definitions = ContainerRegistry.getInstance().findDefinitionContainers(id = setting_data["key"]) if definitions: # Definition was found self._definition = definitions[0] else: self._definition = DefinitionContainer(setting_data["key"]) self._definition.deserialize(json.dumps(setting_data)) ContainerRegistry.getInstance().addContainer(self._definition) self._stack.addContainer(self._definition) self._instance = InstanceContainer(container_id="ScriptInstanceContainer") self._instance.setDefinition(self._definition) self._stack.addContainer(self._instance) ContainerRegistry.getInstance().addContainer(self._stack) settingsLoaded = Signal() ## Needs to return a dict that can be used to construct a settingcategory file. # See the example script for an example. # It follows the same style / guides as the Uranium settings. def getSettingData(self): raise NotImplementedError() def getDefinitionId(self): if self._stack: return self._stack.getBottom().getId() def getStackId(self): if self._stack: return self._stack.getId() ## Convenience function that retrieves value of a setting from the stack. def getSettingValueByKey(self, key): return self._stack.getProperty(key, "value") ## Convenience function that finds the value in a line of g-code. # When requesting key = x from line "G1 X100" the value 100 is returned. def getValue(self, line, key, default = None): if not key in line or (';' in line and line.find(key) > line.find(';')): return default sub_part = line[line.find(key) + 1:] m = re.search('^[0-9]+\.?[0-9]*', sub_part) if m is None: return default try: return float(m.group(0)) except: return default ## This is called when the script is executed. # It gets a list of g-code strings and needs to return a (modified) list. def execute(self, data): raise NotImplementedError()
class Script: def __init__(self) -> None: super().__init__() self._stack = None # type: Optional[ContainerStack] self._definition = None # type: Optional[DefinitionContainerInterface] self._instance = None # type: Optional[InstanceContainer] def initialize(self) -> None: setting_data = self.getSettingData() self._stack = ContainerStack(stack_id=str(id(self))) self._stack.setDirty(False) # This stack does not need to be saved. ## Check if the definition of this script already exists. If not, add it to the registry. if "key" in setting_data: definitions = ContainerRegistry.getInstance().findDefinitionContainers(id=setting_data["key"]) if definitions: # Definition was found self._definition = definitions[0] else: self._definition = DefinitionContainer(setting_data["key"]) try: self._definition.deserialize(json.dumps(setting_data)) ContainerRegistry.getInstance().addContainer(self._definition) except ContainerFormatError: self._definition = None return if self._definition is None: return self._stack.addContainer(self._definition) self._instance = InstanceContainer(container_id="ScriptInstanceContainer") self._instance.setDefinition(self._definition.getId()) self._instance.setMetaDataEntry("setting_version", self._definition.getMetaDataEntry("setting_version", default=0)) self._stack.addContainer(self._instance) self._stack.propertyChanged.connect(self._onPropertyChanged) ContainerRegistry.getInstance().addContainer(self._stack) settingsLoaded = Signal() valueChanged = Signal() # Signal emitted whenever a value of a setting is changed def _onPropertyChanged(self, key: str, property_name: str) -> None: if property_name == "value": self.valueChanged.emit() # Property changed: trigger reslice # To do this we use the global container stack propertyChanged. # Re-slicing is necessary for setting changes in this plugin, because the changes # are applied only once per "fresh" gcode global_container_stack = Application.getInstance().getGlobalContainerStack() if global_container_stack is not None: global_container_stack.propertyChanged.emit(key, property_name) ## Needs to return a dict that can be used to construct a settingcategory file. # See the example script for an example. # It follows the same style / guides as the Uranium settings. # Scripts can either override getSettingData directly, or use getSettingDataString # to return a string that will be parsed as json. The latter has the benefit over # returning a dict in that the order of settings is maintained. def getSettingData(self) -> Dict[str, Any]: setting_data_as_string = self.getSettingDataString() setting_data = json.loads(setting_data_as_string, object_pairs_hook = collections.OrderedDict) return setting_data def getSettingDataString(self) -> str: raise NotImplementedError() def getDefinitionId(self) -> Optional[str]: if self._stack: bottom = self._stack.getBottom() if bottom is not None: return bottom.getId() return None def getStackId(self) -> Optional[str]: if self._stack: return self._stack.getId() return None ## Convenience function that retrieves value of a setting from the stack. def getSettingValueByKey(self, key: str) -> Any: if self._stack is not None: return self._stack.getProperty(key, "value") return None ## Convenience function that finds the value in a line of g-code. # When requesting key = x from line "G1 X100" the value 100 is returned. def getValue(self, line: str, key: str, default = None) -> Any: if not key in line or (';' in line and line.find(key) > line.find(';')): return default sub_part = line[line.find(key) + 1:] m = re.search('^-?[0-9]+\.?[0-9]*', sub_part) if m is None: return default try: return int(m.group(0)) except ValueError: #Not an integer. try: return float(m.group(0)) except ValueError: #Not a number at all. return default ## Convenience function to produce a line of g-code. # # You can put in an original g-code line and it'll re-use all the values # in that line. # All other keyword parameters are put in the result in g-code's format. # For instance, if you put ``G=1`` in the parameters, it will output # ``G1``. If you put ``G=1, X=100`` in the parameters, it will output # ``G1 X100``. The parameters G and M will always be put first. The # parameters T and S will be put second (or first if there is no G or M). # The rest of the parameters will be put in arbitrary order. # \param line The original g-code line that must be modified. If not # provided, an entirely new g-code line will be produced. # \return A line of g-code with the desired parameters filled in. def putValue(self, line: str = "", **kwargs) -> str: #Strip the comment. comment = "" if ";" in line: comment = line[line.find(";"):] line = line[:line.find(";")] #Strip the comment. #Parse the original g-code line. for part in line.split(" "): if part == "": continue parameter = part[0] if parameter in kwargs: continue #Skip this one. The user-provided parameter overwrites the one in the line. value = part[1:] kwargs[parameter] = value #Write the new g-code line. result = "" priority_parameters = ["G", "M", "T", "S", "F", "X", "Y", "Z", "E"] #First some parameters that get priority. In order of priority! for priority_key in priority_parameters: if priority_key in kwargs: if result != "": result += " " result += priority_key + str(kwargs[priority_key]) del kwargs[priority_key] for key, value in kwargs.items(): if result != "": result += " " result += key + str(value) #Put the comment back in. if comment != "": if result != "": result += " " result += ";" + comment return result ## This is called when the script is executed. # It gets a list of g-code strings and needs to return a (modified) list. def execute(self, data: List[str]) -> List[str]: raise NotImplementedError()
def read(self, file_name): if file_name.split(".")[-1] != "ini": return None global_container_stack = Application.getInstance( ).getGlobalContainerStack() if not global_container_stack: return None multi_extrusion = global_container_stack.getProperty( "machine_extruder_count", "value") > 1 if multi_extrusion: Logger.log( "e", "Unable to import legacy profile %s. Multi extrusion is not supported", file_name) raise Exception( "Unable to import legacy profile. Multi extrusion is not supported" ) Logger.log("i", "Importing legacy profile from file " + file_name + ".") profile = InstanceContainer( "Imported Legacy Profile") # Create an empty profile. parser = configparser.ConfigParser(interpolation=None) try: with open(file_name) as f: parser.readfp(f) #Parse the INI file. except Exception as e: Logger.log("e", "Unable to open legacy profile %s: %s", file_name, str(e)) return None #Legacy Cura saved the profile under the section "profile_N" where N is the ID of a machine, except when you export in which case it saves it in the section "profile". #Since importing multiple machine profiles is out of scope, just import the first section we find. section = "" for found_section in parser.sections(): if found_section.startswith("profile"): section = found_section break if not section: #No section starting with "profile" was found. Probably not a proper INI file. return None try: with open( os.path.join( PluginRegistry.getInstance().getPluginPath( "LegacyProfileReader"), "DictionaryOfDoom.json"), "r", -1, "utf-8") as f: dict_of_doom = json.load(f) #Parse the Dictionary of Doom. except IOError as e: Logger.log("e", "Could not open DictionaryOfDoom.json for reading: %s", str(e)) return None except Exception as e: Logger.log("e", "Could not parse DictionaryOfDoom.json: %s", str(e)) return None defaults = self.prepareDefaults(dict_of_doom) legacy_settings = self.prepareLocals( parser, section, defaults) #Gets the settings from the legacy profile. #Check the target version in the Dictionary of Doom with this application version. if "target_version" not in dict_of_doom: Logger.log( "e", "Dictionary of Doom has no target version. Is it the correct JSON file?" ) return None if InstanceContainer.Version != dict_of_doom["target_version"]: Logger.log( "e", "Dictionary of Doom of legacy profile reader (version %s) is not in sync with the current instance container version (version %s)!", dict_of_doom["target_version"], str(InstanceContainer.Version)) return None if "translation" not in dict_of_doom: Logger.log( "e", "Dictionary of Doom has no translation. Is it the correct JSON file?" ) return None current_printer_definition = global_container_stack.getBottom() profile.setDefinition(current_printer_definition) for new_setting in dict_of_doom[ "translation"]: #Evaluate all new settings that would get a value from the translations. old_setting_expression = dict_of_doom["translation"][new_setting] compiled = compile(old_setting_expression, new_setting, "eval") try: new_value = eval( compiled, {"math": math}, legacy_settings ) #Pass the legacy settings as local variables to allow access to in the evaluation. value_using_defaults = eval( compiled, {"math": math}, defaults ) #Evaluate again using only the default values to try to see if they are default. except Exception: #Probably some setting name that was missing or something else that went wrong in the ini file. Logger.log( "w", "Setting " + new_setting + " could not be set because the evaluation failed. Something is probably missing from the imported legacy profile." ) continue definitions = current_printer_definition.findDefinitions( key=new_setting) if definitions: if new_value != value_using_defaults and definitions[ 0].default_value != new_value: # Not equal to the default in the new Cura OR the default in the legacy Cura. profile.setProperty( new_setting, "value", new_value) # Store the setting in the profile! if len(profile.getAllKeys()) == 0: Logger.log( "i", "A legacy profile was imported but everything evaluates to the defaults, creating an empty profile." ) profile.setDirty(True) profile.addMetaDataEntry("type", "quality_changes") profile.addMetaDataEntry("quality_type", "normal") return profile
def read(self, file_name): if file_name.split(".")[-1] != "ini": return None global_container_stack = Application.getInstance().getGlobalContainerStack() if not global_container_stack: return None multi_extrusion = global_container_stack.getProperty("machine_extruder_count", "value") > 1 if multi_extrusion: Logger.log("e", "Unable to import legacy profile %s. Multi extrusion is not supported", file_name) raise Exception("Unable to import legacy profile. Multi extrusion is not supported") Logger.log("i", "Importing legacy profile from file " + file_name + ".") profile = InstanceContainer("Imported Legacy Profile") # Create an empty profile. parser = configparser.ConfigParser(interpolation = None) try: with open(file_name) as f: parser.readfp(f) # Parse the INI file. except Exception as e: Logger.log("e", "Unable to open legacy profile %s: %s", file_name, str(e)) return None # Legacy Cura saved the profile under the section "profile_N" where N is the ID of a machine, except when you export in which case it saves it in the section "profile". # Since importing multiple machine profiles is out of scope, just import the first section we find. section = "" for found_section in parser.sections(): if found_section.startswith("profile"): section = found_section break if not section: # No section starting with "profile" was found. Probably not a proper INI file. return None try: with open(os.path.join(PluginRegistry.getInstance().getPluginPath("LegacyProfileReader"), "DictionaryOfDoom.json"), "r", -1, "utf-8") as f: dict_of_doom = json.load(f) # Parse the Dictionary of Doom. except IOError as e: Logger.log("e", "Could not open DictionaryOfDoom.json for reading: %s", str(e)) return None except Exception as e: Logger.log("e", "Could not parse DictionaryOfDoom.json: %s", str(e)) return None defaults = self.prepareDefaults(dict_of_doom) legacy_settings = self.prepareLocals(parser, section, defaults) #Gets the settings from the legacy profile. #Check the target version in the Dictionary of Doom with this application version. if "target_version" not in dict_of_doom: Logger.log("e", "Dictionary of Doom has no target version. Is it the correct JSON file?") return None if InstanceContainer.Version != dict_of_doom["target_version"]: Logger.log("e", "Dictionary of Doom of legacy profile reader (version %s) is not in sync with the current instance container version (version %s)!", dict_of_doom["target_version"], str(InstanceContainer.Version)) return None if "translation" not in dict_of_doom: Logger.log("e", "Dictionary of Doom has no translation. Is it the correct JSON file?") return None current_printer_definition = global_container_stack.getBottom() profile.setDefinition(current_printer_definition) for new_setting in dict_of_doom["translation"]: # Evaluate all new settings that would get a value from the translations. old_setting_expression = dict_of_doom["translation"][new_setting] compiled = compile(old_setting_expression, new_setting, "eval") try: new_value = eval(compiled, {"math": math}, legacy_settings) # Pass the legacy settings as local variables to allow access to in the evaluation. value_using_defaults = eval(compiled, {"math": math}, defaults) #Evaluate again using only the default values to try to see if they are default. except Exception: # Probably some setting name that was missing or something else that went wrong in the ini file. Logger.log("w", "Setting " + new_setting + " could not be set because the evaluation failed. Something is probably missing from the imported legacy profile.") continue definitions = current_printer_definition.findDefinitions(key = new_setting) if definitions: if new_value != value_using_defaults and definitions[0].default_value != new_value: # Not equal to the default in the new Cura OR the default in the legacy Cura. profile.setProperty(new_setting, "value", new_value) # Store the setting in the profile! if len(profile.getAllKeys()) == 0: Logger.log("i", "A legacy profile was imported but everything evaluates to the defaults, creating an empty profile.") profile.setDirty(True) profile.addMetaDataEntry("type", "quality_changes") profile.addMetaDataEntry("quality_type", "normal") return profile
class Script: def __init__(self): super().__init__() self._settings = None self._stack = None setting_data = self.getSettingData() self._stack = ContainerStack(stack_id=str(id(self))) self._stack.setDirty(False) # This stack does not need to be saved. ## Check if the definition of this script already exists. If not, add it to the registry. if "key" in setting_data: definitions = ContainerRegistry.getInstance( ).findDefinitionContainers(id=setting_data["key"]) if definitions: # Definition was found self._definition = definitions[0] else: self._definition = DefinitionContainer(setting_data["key"]) try: self._definition.deserialize(json.dumps(setting_data)) ContainerRegistry.getInstance().addContainer( self._definition) except ContainerFormatError: self._definition = None return self._stack.addContainer(self._definition) self._instance = InstanceContainer( container_id="ScriptInstanceContainer") self._instance.setDefinition(self._definition.getId()) self._instance.addMetaDataEntry( "setting_version", self._definition.getMetaDataEntry("setting_version", default=0)) self._stack.addContainer(self._instance) self._stack.propertyChanged.connect(self._onPropertyChanged) ContainerRegistry.getInstance().addContainer(self._stack) settingsLoaded = Signal() valueChanged = Signal( ) # Signal emitted whenever a value of a setting is changed def _onPropertyChanged(self, key, property_name): if property_name == "value": self.valueChanged.emit() # Property changed: trigger reslice # To do this we use the global container stack propertyChanged. # Reslicing is necessary for setting changes in this plugin, because the changes # are applied only once per "fresh" gcode global_container_stack = Application.getInstance( ).getGlobalContainerStack() global_container_stack.propertyChanged.emit(key, property_name) ## Needs to return a dict that can be used to construct a settingcategory file. # See the example script for an example. # It follows the same style / guides as the Uranium settings. # Scripts can either override getSettingData directly, or use getSettingDataString # to return a string that will be parsed as json. The latter has the benefit over # returning a dict in that the order of settings is maintained. def getSettingData(self): setting_data = self.getSettingDataString() if type(setting_data) == str: setting_data = json.loads( setting_data, object_pairs_hook=collections.OrderedDict) return setting_data def getSettingDataString(self): raise NotImplementedError() def getDefinitionId(self): if self._stack: return self._stack.getBottom().getId() def getStackId(self): if self._stack: return self._stack.getId() ## Convenience function that retrieves value of a setting from the stack. def getSettingValueByKey(self, key): return self._stack.getProperty(key, "value") ## Convenience function that finds the value in a line of g-code. # When requesting key = x from line "G1 X100" the value 100 is returned. def getValue(self, line, key, default=None): if not key in line or (';' in line and line.find(key) > line.find(';')): return default sub_part = line[line.find(key) + 1:] m = re.search('^-?[0-9]+\.?[0-9]*', sub_part) if m is None: return default try: return float(m.group(0)) except: return default ## Convenience function to produce a line of g-code. # # You can put in an original g-code line and it'll re-use all the values # in that line. # All other keyword parameters are put in the result in g-code's format. # For instance, if you put ``G=1`` in the parameters, it will output # ``G1``. If you put ``G=1, X=100`` in the parameters, it will output # ``G1 X100``. The parameters G and M will always be put first. The # parameters T and S will be put second (or first if there is no G or M). # The rest of the parameters will be put in arbitrary order. # \param line The original g-code line that must be modified. If not # provided, an entirely new g-code line will be produced. # \return A line of g-code with the desired parameters filled in. def putValue(self, line="", **kwargs): #Strip the comment. comment = "" if ";" in line: comment = line[line.find(";"):] line = line[:line.find(";")] #Strip the comment. #Parse the original g-code line. for part in line.split(" "): if part == "": continue parameter = part[0] if parameter in kwargs: continue #Skip this one. The user-provided parameter overwrites the one in the line. value = part[1:] kwargs[parameter] = value #Write the new g-code line. result = "" priority_parameters = [ "G", "M", "T", "S", "F", "X", "Y", "Z", "E" ] #First some parameters that get priority. In order of priority! for priority_key in priority_parameters: if priority_key in kwargs: if result != "": result += " " result += priority_key + str(kwargs[priority_key]) del kwargs[priority_key] for key, value in kwargs.items(): if result != "": result += " " result += key + str(value) #Put the comment back in. if comment != "": if result != "": result += " " result += ";" + comment return result ## This is called when the script is executed. # It gets a list of g-code strings and needs to return a (modified) list. def execute(self, data): raise NotImplementedError()
def addExtruderStackForSingleExtrusionMachine(self, machine, extruder_id): new_extruder_id = extruder_id extruder_definitions = self.findDefinitionContainers(id = new_extruder_id) if not extruder_definitions: Logger.log("w", "Could not find definition containers for extruder %s", new_extruder_id) return extruder_definition = extruder_definitions[0] unique_name = self.uniqueName(machine.getName() + " " + new_extruder_id) extruder_stack = ExtruderStack.ExtruderStack(unique_name) extruder_stack.setName(extruder_definition.getName()) extruder_stack.setDefinition(extruder_definition) extruder_stack.addMetaDataEntry("position", extruder_definition.getMetaDataEntry("position")) # create empty user changes container otherwise user_container = InstanceContainer(extruder_stack.id + "_user") user_container.addMetaDataEntry("type", "user") user_container.addMetaDataEntry("machine", extruder_stack.getId()) from cura.CuraApplication import CuraApplication user_container.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) user_container.setDefinition(machine.definition.getId()) if machine.userChanges: # for the newly created extruder stack, we need to move all "per-extruder" settings to the user changes # container to the extruder stack. for user_setting_key in machine.userChanges.getAllKeys(): settable_per_extruder = machine.getProperty(user_setting_key, "settable_per_extruder") if settable_per_extruder: user_container.addInstance(machine.userChanges.getInstance(user_setting_key)) machine.userChanges.removeInstance(user_setting_key, postpone_emit = True) self.addContainer(user_container) extruder_stack.setUserChanges(user_container) variant_id = "default" if machine.variant.getId() not in ("empty", "empty_variant"): variant_id = machine.variant.getId() else: variant_id = "empty_variant" extruder_stack.setVariantById(variant_id) material_id = "default" if machine.material.getId() not in ("empty", "empty_material"): material_id = machine.material.getId() else: material_id = "empty_material" extruder_stack.setMaterialById(material_id) quality_id = "default" if machine.quality.getId() not in ("empty", "empty_quality"): quality_id = machine.quality.getId() else: quality_id = "empty_quality" extruder_stack.setQualityById(quality_id) if machine.qualityChanges.getId() not in ("empty", "empty_quality_changes"): extruder_quality_changes_container = self.findInstanceContainers(name = machine.qualityChanges.getName(), extruder = extruder_id) if extruder_quality_changes_container: extruder_quality_changes_container = extruder_quality_changes_container[0] quality_changes_id = extruder_quality_changes_container.getId() extruder_stack.setQualityChangesById(quality_changes_id) else: # Some extruder quality_changes containers can be created at runtime as files in the qualities # folder. Those files won't be loaded in the registry immediately. So we also need to search # the folder to see if the quality_changes exists. extruder_quality_changes_container = self._findQualityChangesContainerInCuraFolder(machine.qualityChanges.getName()) if extruder_quality_changes_container: quality_changes_id = extruder_quality_changes_container.getId() extruder_stack.setQualityChangesById(quality_changes_id) if not extruder_quality_changes_container: Logger.log("w", "Could not find quality_changes named [%s] for extruder [%s]", machine.qualityChanges.getName(), extruder_stack.getId()) else: extruder_stack.setQualityChangesById("empty_quality_changes") self.addContainer(extruder_stack) # Set next stack at the end extruder_stack.setNextStack(machine) return extruder_stack