From 48eb8de9a10b870aa3d0f26386a67c9d8f5e47c1 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 1 Jun 2016 13:35:30 +0200 Subject: [PATCH 1/7] Add basic extruder implementation This implementation can load an extruder from a definition container, but doesn't expose anything (yet). It is intended to function in much the same way as a definition model, so it must expose its relevant properties to QML. Contributes to issues CURA-1278 and CURA-351. --- cura/Extruder.py | 85 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 cura/Extruder.py diff --git a/cura/Extruder.py b/cura/Extruder.py new file mode 100644 index 0000000000..f9f957dfe3 --- /dev/null +++ b/cura/Extruder.py @@ -0,0 +1,85 @@ +# Copyright (c) 2016 Ultimaker B.V. +# Cura is released under the terms of the AGPLv3 or higher. + +import re #To parse container registry names to increment the duplicates-resolving number. + +import UM.Settings.ContainerRegistry #To search for nozzles, materials, etc. +import UM.Settings.ContainerStack #To create a container stack for this extruder. + +class Extruder: + ## Creates a new extruder from the specified definition container. + # + # \param definition The definition container defining this extruder. + def __init__(self, definition): + self._definition = definition + + container_registry = UM.Settings.ContainerRegistry.getInstance() + + #Find the nozzles that fit on this extruder. + self._nozzles = container_registry.findInstanceContainers(type = "nozzle", definitions = "*," + definition.getId() + ",*") + self._nozzles += container_registry.findInstanceContainers(type = "nozzle", definitions = "*," + definition.getId()) + self._nozzles += container_registry.findInstanceContainers(type = "nozzle", definitions = definition.getId() + ",*") + self._nozzles += container_registry.findInstanceContainers(type = "nozzle", definitions = definition.getId()) + + #Create a container stack for this extruder. + self._container_stack = UM.Settings.ContainerStack(self._uniqueName(self._definition.getId())) + self._container_stack.addMetaDataEntry("type", "extruder_train") + self._container_stack.addContainer(self._definition) + + #Find the nozzle to use for this extruder. + self._nozzle = container_registry.getEmptyInstanceContainer() + if len(self._nozzles) >= 1: #First add any extruder. Later, overwrite with preference if the preference is valid. + self._nozzle = self._nozzles[0] + preferred_nozzle_id = self._definition.getMetaDataEntry("preferred_nozzle") + if preferred_nozzle_id: + for nozzle in self._nozzles: + if nozzle.getId() == preferred_nozzle_id: + self._nozzle = nozzle + break + self._container_stack.addContainer(self._nozzle) + + #Find a material to use for this nozzle. + self._material = container_registry.getEmptyInstanceContainer() + all_materials = container_registry.findInstanceContainers(type = "material") + if len(all_materials) >= 1: + self._material = all_materials[0] + preferred_material_id = self._definition.getMetaDataEntry("preferred_material") + if preferred_material_id: + preferred_material = container_registry.findInstanceContainers(type = "material", id = preferred_material_id.lower()) + if len(preferred_material) >= 1: + self._material = preferred_material[0] + self._container_stack.addContainer(self._material) + + #Find a quality to use for this extruder. + self._quality = container_registry.getEmptyInstanceContainer() + all_qualities = container_registry.findInstanceContainers(type = "quality") + if len(all_qualities) >= 1: + self._quality = all_qualities[0] + preferred_quality_id = self._definition.getMetaDataEntry("preferred_quality") + if preferred_quality_id: + preferred_quality = container_registry.findInstanceContainers(type = "quality", id = preferred_quality_id.lower()) + if len(preferred_quality) >= 1: + self._quality = preferred_quality[0] + self._container_stack.addContainer(self._quality) + + ## Finds a unique name for an extruder stack. + # + # \param extruder An extruder definition to design a name for. + # \return A name for an extruder stack that is unique and reasonably + # human-readable. + def _uniqueName(self, extruder): + container_registry = UM.Settings.ContainerRegistry.getInstance() + + name = extruder.getName().strip() + num_check = re.compile("(.*?)\s*#\d$").match(name) + if num_check: #There is a number in the name. + name = num_check.group(1) #Filter out the number. + if name == "": #Wait, that deleted everything! + name = "Extruder" + unique_name = name + + i = 1 + while container_registry.findContainers(id = unique_name) or container_registry.findContainers(name = unique_name): #A container already has this name. + i += 1 #Try next numbering. + unique_name = "%s #%d" % (name, i) #Fill name like this: "Extruder #2". + return unique_name \ No newline at end of file From 377fed206c0d581c74b72d534fe8bf988ca3b5ef Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 1 Jun 2016 13:41:25 +0200 Subject: [PATCH 2/7] Remove extruder creation logic from manager This logic is now in cura/Extruder.py. Contributes to issues CURA-1278 and CURA-351. --- cura/ExtruderManager.py | 68 ++++++----------------------------------- 1 file changed, 9 insertions(+), 59 deletions(-) diff --git a/cura/ExtruderManager.py b/cura/ExtruderManager.py index c99ac4be00..924c6a1e21 100644 --- a/cura/ExtruderManager.py +++ b/cura/ExtruderManager.py @@ -3,6 +3,7 @@ import re +from cura.Extruder import Extruder #The individual extruders managed by this manager. from UM.Application import Application #To get the global container stack to find the current machine. from UM.Logger import Logger from UM.Settings.ContainerStack import ContainerStack #To create container stacks for each extruder. @@ -26,72 +27,21 @@ class ExtruderManager: ## (Re)loads all extruders of the currently active machine. # # This looks at the global container stack to see which machine is active. - # Then it loads the extruder definitions for that machine and the variants - # of those definitions. Then it puts the new extruder definitions in the - # appropriate place in the container stacks. + # Then it loads the extruders for that machine and loads each of them in a + # list of extruders. def _reloadExtruders(self): - self._extruderDefinitions = [] - self._nozzles = {} - self._extruderTrains = [] + self._extruders = [] global_container_stack = Application.getInstance().getGlobalContainerStack() if not global_container_stack: #No machine has been added yet. return #Then leave them empty! - #Fill the list of extruder trains. + #Get the extruder definitions belonging to the current machine. machine = global_container_stack.getBottom() extruder_train_ids = machine.getMetaData("machine_extruder_trains") for extruder_train_id in extruder_train_ids: - extruders = ContainerRegistry.getInstance().findDefinitionContainers(id = extruder_train_id) #Should be only 1 definition if IDs are unique, but add the whole list anyway. - if not extruders: #Empty list or error. + extruder_definitions = ContainerRegistry.getInstance().findDefinitionContainers(id = extruder_train_id) #Should be only 1 definition if IDs are unique, but add the whole list anyway. + if not extruder_definitions: #Empty list or error. Logger.log("w", "Machine definition %s refers to an extruder train \"%s\", but no such extruder was found.", machine.getId(), extruder_train_id) continue - self._extruderDefinitions += extruders - - #Fill the nozzles for each of the extruder trains. - for extruder in self._extruderDefinitions: - self._nozzles[extruder.id] = [] - all_nozzles = ContainerRegistry.getInstance().findInstanceContainers(type="nozzle") - for nozzle in all_nozzles: - extruders = nozzle.getMetaDataEntry("definitions").split(",").strip() - for extruder_id in extruders: - self._nozzles[extruder_id] = nozzle - - #Create the extruder train container stacks. - for extruder in self._extruderDefinitions: - self._extruderTrains.append(self._createContainerStack(extruder)) - - ## Creates a container stack for the specified extruder. - # - # This fills in the specified extruder as base definition, then a nozzle - # that fits in that extruder train, then a material that fits through that - # nozzle, then a quality profile that can be used with that material, and - # finally an empty user profile. - # - # \param extruder The extruder to create the container stack for. - # \return A container stack with the specified extruder as base. - def _createContainerStack(self, extruder): - container_stack = ContainerStack(self._uniqueName(extruder)) - #TODO: Fill the container stack. - return container_stack - - ## Finds a unique name for an extruder stack. - # - # \param extruder Extruder to design a name for. - # \return A name for an extruder stack that is unique and reasonably - # human-readable. - def _uniqueName(self, extruder): - container_registry = ContainerRegistry.getInstance() - - name = extruder.getName().strip() - num_check = re.compile("(.*?)\s*#\d$").match(name) - if(num_check): #There is a number in the name. - name = num_check.group(1) #Filter out the number. - if name == "": #Wait, that deleted everything! - name = "Extruder" - unique_name = name - - i = 1 - while(container_registry.findContainers(id = unique_name) or container_registry.findContainers(name = unique_name)): #A container already has this name. - i += 1 #Try next numbering. - unique_name = "%s #%d" % (name, i) #Fill name like this: "Extruder #2". - return unique_name \ No newline at end of file + for extruder_definition in extruder_definitions: + self._extruders.append(Extruder(extruder_definition)) \ No newline at end of file From 60a71fcc332da5f7b20939e7458609b7c2be845c Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 1 Jun 2016 14:54:30 +0200 Subject: [PATCH 3/7] Only list extruders in extruder manager Let the extruders themselves take care of which profiles are attached to each. Contributes to issues CURA-1278 and CURA-351. --- cura/ExtruderManager.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cura/ExtruderManager.py b/cura/ExtruderManager.py index 924c6a1e21..d0dea31206 100644 --- a/cura/ExtruderManager.py +++ b/cura/ExtruderManager.py @@ -18,9 +18,7 @@ from UM.Settings.ContainerRegistry import ContainerRegistry #Finding containers class ExtruderManager: ## Registers listeners and such to listen to changes to the extruders. def __init__(self): - self._extruderDefinitions = [] #Extruder definitions for the current machine. - self._nozzles = {} #Nozzle instances for each extruder. - self._extruderTrains = [] #Container stacks for each of the extruder trains. + self._extruders = [] #Extruders for the current machine. Application.getInstance().getGlobalContainerStack().containersChanged.connect(self._reloadExtruders) #When the current machine changes, we need to reload all extruders belonging to the new machine. From d1566ef637a5c244c0e7a20611f4998a0fd104f3 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 1 Jun 2016 14:55:15 +0200 Subject: [PATCH 4/7] Also reload extruders at init Not a reload really, just a load. Contributes to issues CURA-1278 and CURA-351. --- cura/ExtruderManager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cura/ExtruderManager.py b/cura/ExtruderManager.py index d0dea31206..4d95a9008d 100644 --- a/cura/ExtruderManager.py +++ b/cura/ExtruderManager.py @@ -21,6 +21,7 @@ class ExtruderManager: self._extruders = [] #Extruders for the current machine. Application.getInstance().getGlobalContainerStack().containersChanged.connect(self._reloadExtruders) #When the current machine changes, we need to reload all extruders belonging to the new machine. + self._reloadExtruders() ## (Re)loads all extruders of the currently active machine. # From 91fc90a42323ef64258555c36b3e805881713e22 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 1 Jun 2016 15:05:14 +0200 Subject: [PATCH 5/7] Link extruder stack to global container stack Each extruder stack is linked to the same global container stack. Contributes to issues CURA-1278 and CURA-351. --- cura/Extruder.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cura/Extruder.py b/cura/Extruder.py index f9f957dfe3..8fa1b71ab1 100644 --- a/cura/Extruder.py +++ b/cura/Extruder.py @@ -3,6 +3,7 @@ import re #To parse container registry names to increment the duplicates-resolving number. +import UM.Application #To link the stack to the global container stack. import UM.Settings.ContainerRegistry #To search for nozzles, materials, etc. import UM.Settings.ContainerStack #To create a container stack for this extruder. @@ -62,6 +63,8 @@ class Extruder: self._quality = preferred_quality[0] self._container_stack.addContainer(self._quality) + self._container_stack.setNextStack(UM.Application.getInstance().getGlobalContainerStack()) + ## Finds a unique name for an extruder stack. # # \param extruder An extruder definition to design a name for. From 0e108f0c04b8be53f51ee7ef18ad23946b3d7e5c Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 1 Jun 2016 15:12:20 +0200 Subject: [PATCH 6/7] Document filtering nozzles by extruder better Contributes to issues CURA-1278 and CURA-351. --- cura/Extruder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/Extruder.py b/cura/Extruder.py index 8fa1b71ab1..5f74bd036f 100644 --- a/cura/Extruder.py +++ b/cura/Extruder.py @@ -17,7 +17,7 @@ class Extruder: container_registry = UM.Settings.ContainerRegistry.getInstance() #Find the nozzles that fit on this extruder. - self._nozzles = container_registry.findInstanceContainers(type = "nozzle", definitions = "*," + definition.getId() + ",*") + self._nozzles = container_registry.findInstanceContainers(type = "nozzle", definitions = "*," + definition.getId() + ",*") #Extruder needs to be delimited by either a comma or the end of string. self._nozzles += container_registry.findInstanceContainers(type = "nozzle", definitions = "*," + definition.getId()) self._nozzles += container_registry.findInstanceContainers(type = "nozzle", definitions = definition.getId() + ",*") self._nozzles += container_registry.findInstanceContainers(type = "nozzle", definitions = definition.getId()) From faf647dca05bb39f988637d56f7e2fbdaf5ad769 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 1 Jun 2016 15:14:03 +0200 Subject: [PATCH 7/7] Git ignore resources/firmware It is also compiled and will only be included by including the cura-binary-data repository. However it is also useful to have this in your testing environment. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 72ba4bf565..f60e268711 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ docs/html *.log resources/i18n/en resources/i18n/x-test +resources/firmware # Editors and IDEs. *kdev*