diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 4f02d2dab5..126d8b2864 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -31,6 +31,7 @@ from UM.Settings.ContainerRegistry import ContainerRegistry from UM.i18n import i18nCatalog +from . import ExtruderManager from . import ExtrudersModel from . import PlatformPhysics from . import BuildVolume @@ -352,6 +353,8 @@ class CuraApplication(QtApplication): qmlRegisterSingletonType(MachineManagerModel.MachineManagerModel, "Cura", 1, 0, "MachineManager", MachineManagerModel.createMachineManagerModel) + qmlRegisterSingletonType(ExtruderManager.ExtruderManager, "Cura", 1, 0, "ExtruderManager", + ExtruderManager.createExtruderManager) self.setMainQml(Resources.getPath(self.ResourceTypes.QmlFiles, "Cura.qml")) self._qml_import_paths.append(Resources.getPath(self.ResourceTypes.QmlFiles)) diff --git a/cura/Extruder.py b/cura/Extruder.py deleted file mode 100644 index 6418f008c4..0000000000 --- a/cura/Extruder.py +++ /dev/null @@ -1,211 +0,0 @@ -# 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.Application #To link the stack to the global container stack. -import UM.Logger -import UM.Settings.ContainerRegistry #To search for nozzles, materials, etc. -import UM.Settings.ContainerStack #To create a container stack for this extruder. -import UM.Signal #To notify people of changing extruder stacks. - -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 = "*," + self._definition.getId() + ",*") #Extruder needs to be delimited by either a comma or the end of string. - self._nozzles += container_registry.findInstanceContainers(type = "nozzle", definitions = "*," + self._definition.getId()) - self._nozzles += container_registry.findInstanceContainers(type = "nozzle", definitions = self._definition.getId() + ",*") - self._nozzles += container_registry.findInstanceContainers(type = "nozzle", definitions = self._definition.getId()) - - #Create a container stack for this extruder. - self._name = self._uniqueName(self._definition) - self._container_stack = UM.Settings.ContainerStack(self._name) - 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 self._definition.getMetaDataEntry("has_nozzles", default = "False") == "True": - 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() - if self._definition.getMetaDataEntry("has_materials", default = "False") == "True": - if self._definition.getMetaDataEntry("has_nozzle_materials", default = "False") == "True": - all_materials = container_registry.findInstanceContainers(type = "material", nozzle = self._nozzle.getId()) - else: - 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() - if self._definition.getMetaDataEntry("has_machine_quality"): - 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) - - #Add an empty user profile. - self._user_profile = UM.Settings.InstanceContainer(self._name + "_current_settings") - self._user_profile.addMetaDataEntry("type", "user") - self._container_stack.addContainer(self._user_profile) - container_registry.addContainer(self._user_profile) - - self._container_stack.setNextStack(UM.Application.getInstance().getGlobalContainerStack()) - - container_registry.addContainer(self._container_stack) - - definition_changed = UM.Signal() - material_changed = UM.Signal() - name_changed = UM.Signal() - nozzle_changed = UM.Signal() - quality_changed = UM.Signal() - - ## Gets the definition container of this extruder. - # - # \return The definition container of this extruder. - @property - def definition(self): - return self._definition - - ## Changes the definition container of this extruder. - # - # \param value The new definition for this extruder. - @definition.setter - def definition(self, value): - try: - position = self._container_stack.index(self._definition) - except ValueError: #Definition is not in the list. Big trouble! - UM.Logger.log("e", "I've lost my old extruder definition, so I can't find where to insert the new definition.") - return - self._container_stack.replaceContainer(position, value) - self._definition = value - self.definition_changed.emit() - - ## Gets the currently active material on this extruder. - # - # \return The currently active material on this extruder. - @property - def material(self): - return self._material - - ## Changes the currently active material in this extruder. - # - # \param value The new material to extrude through this extruder. - @material.setter - def material(self, value): - try: - position = self._container_stack.index(self._material) - except ValueError: #Material is not in the list. - UM.Logger.log("e", "I've lost my old material, so I can't find where to insert the new material.") - return - self._container_stack.replaceContainer(position, value) - self._material = value - self.material_changed.emit() - - ## Gets the name of this extruder. - # - # \return The name of this extruder. - @property - def name(self): - return self._name - - ## Changes the name of this extruder. - # - # \param value The new name for this extruder. - @name.setter - def name(self, value): - self._name = value - self._container_stack.setName(value) #Also update in container stack, being defensive. - self.name_changed.emit() - - ## Gets the currently active nozzle on this extruder. - # - # \return The currently active nozzle on this extruder. - @property - def nozzle(self): - return self._nozzle - - ## Changes the currently active nozzle on this extruder. - # - # \param value The new nozzle to use with this extruder. - @nozzle.setter - def nozzle(self, value): - try: - position = self._container_stack.index(self._nozzle) - except ValueError: #Nozzle is not in the list. - UM.Logger.log("e", "I've lost my old nozzle, so I can't find where to insert the new nozzle.") - return - self._container_stack.replaceContainer(position, value) - self._nozzle = value - self.nozzle_changed.emit() - - ## Gets the currently active quality on this extruder. - # - # \return The currently active quality on this extruder. - @property - def quality(self): - return self._quality - - ## Changes the currently active quality to use with this extruder. - # - # \param value The new quality to use with this extruder. - @quality.setter - def quality(self, value): - try: - position = self._container_stack.index(self._quality) - except ValueError: #Quality is not in the list. - UM.Logger.log("e", "I've lost my old quality, so I can't find where to insert the new quality.") - return - self._container_stack.replaceContainer(position, value) - self._quality = value - self.quality_changed.emit() - - ## 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 diff --git a/cura/ExtruderManager.py b/cura/ExtruderManager.py index 7222a1c0f8..4f23a01808 100644 --- a/cura/ExtruderManager.py +++ b/cura/ExtruderManager.py @@ -1,7 +1,8 @@ # Copyright (c) 2016 Ultimaker B.V. # Cura is released under the terms of the AGPLv3 or higher. -from cura.Extruder import Extruder #The individual extruders managed by this manager. +from PyQt5.QtCore import pyqtSignal, pyqtProperty, pyqtSlot, QObject + import UM.Application #To get the global container stack to find the current machine. import UM.Logger import UM.Settings.ContainerRegistry #Finding containers by ID. @@ -13,21 +14,24 @@ import UM.Signal #To notify other components of changes in the extruders. # This finds the extruders that are available for the currently active machine # and makes sure that whenever the machine is swapped, this list is kept up to # date. It also contains and updates the setting stacks for the extruders. -class ExtruderManager: +class ExtruderManager(QObject): ## The singleton instance of this manager. __instance = None ## Signal to notify other components when the list of extruders changes. extrudersChanged = UM.Signal() - ## Registers listeners and such to listen to changes to the extruders. - def __init__(self): - self._extruders = [] #Extruders for the current machine. - self._global_container_stack = None - self._next_item = 0 #For when you use this class as iterator. + ## Notify when the user switches the currently active extruder. + activeExtruderChanged = pyqtSignal() - UM.Application.getInstance().globalContainerStackChanged.connect(self._reconnectExtruderReload) #When the current machine changes, we need to reload all extruders belonging to the new machine. - self._reconnectExtruderReload() + ## Registers listeners and such to listen to changes to the extruders. + def __init__(self, parent = None): + super().__init__(parent) + self._extruder_trains = { } #Extruders for the current machine. + self._next_item = 0 #For when you use this class as iterator. + self._active_extruder_index = 0 + + self._repopulate() ## Gets an instance of this extruder manager. # @@ -45,34 +49,99 @@ class ExtruderManager: def __iter__(self): return iter(self._extruders) - ## When the global container stack changes, this reconnects to the new - # signal for containers changing. - def _reconnectExtruderReload(self): - if self._global_container_stack: - self._global_container_stack.containersChanged.disconnect(self._reloadExtruders) #Disconnect from the old global container stack. - self._global_container_stack = UM.Application.getInstance().getGlobalContainerStack() - self._global_container_stack.containersChanged.connect(self._reloadExtruders) #When the current machine changes, we need to reload all extruders belonging to the new machine. - self._reloadExtruders() + @pyqtProperty(str, notify = activeExtruderChanged) + def activeExtruderStackId(self): + if UM.Application.getInstance().getGlobalContainerStack(): + try: + return self._extruder_trains[UM.Application.getInstance().getGlobalContainerStack().getId()][str(self._active_extruder_index)] + except KeyError: + pass - ## (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 extruders for that machine and loads each of them in a - # list of extruders. - def _reloadExtruders(self, *args): - self._extruders = [] - if not self._global_container_stack: #No machine has been added yet. + @pyqtSlot(int) + def setActiveExtruderIndex(self, index): + self._active_extruder_index = index + self.activeExtruderChanged.emit() + + ## (Re)populates the collections of extruders by machine. + def _repopulate(self): + self._extruder_trains = { } + if not UM.Application.getInstance().getGlobalContainerStack(): #No machine has been added yet. self.extrudersChanged.emit() #Yes, we just cleared the _extruders list! return #Then leave them empty! - #Get the extruder definitions belonging to the current machine. - machine = self._global_container_stack.getBottom() - extruder_train_ids = machine.getMetaDataEntry("machine_extruder_trains", { }) - for _,extruder_train_id in extruder_train_ids.items(): - extruder_definitions = UM.Settings.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. - UM.Logger.log("w", "Machine definition %s refers to an extruder train \"%s\", but no such extruder was found.", machine.getId(), extruder_train_id) + extruder_trains = UM.Settings.ContainerRegistry.getInstance().findContainerStacks(type = "extruder_train") + for extruder_train in extruder_trains: + machine_id = extruder_train.getMetaDataEntry("machine") + if not machine_id: continue - for extruder_definition in extruder_definitions: - self._extruders.append(Extruder(extruder_definition)) - self.extrudersChanged.emit() \ No newline at end of file + if machine_id not in self._extruder_trains: + self._extruder_trains[machine_id] = { } + self._extruder_trains[machine_id][extruder_train.getMetaDataEntry("position")] = extruder_train.getId() + self.extrudersChanged.emit() + + def createExtruderTrain(self, definition, extruder_id): + container_registry = UM.Settings.ContainerRegistry.getInstance() + + #Create a container stack for this extruder. + name = self._uniqueName(extruder_id) + container_stack = UM.Settings.ContainerStack(name) + container_stack.addMetaDataEntry("type", "extruder_train") + container_stack.addContainer(definition) + + """ + Yes, I'm committing this code which needs to be transformed to work later. + #Find the nozzle to use for this extruder. + nozzle = container_registry.getEmptyInstanceContainer() + if definition.getMetaDataEntry("has_nozzles", default = "False") == "True": + 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 = 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() + if self._definition.getMetaDataEntry("has_materials", default = "False") == "True": + if self._definition.getMetaDataEntry("has_nozzle_materials", default = "False") == "True": + all_materials = container_registry.findInstanceContainers(type = "material", nozzle = self._nozzle.getId()) + else: + 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() + if self._definition.getMetaDataEntry("has_machine_quality"): + 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) + """ + + #Add an empty user profile. + user_profile = UM.Settings.InstanceContainer(name + "_current_settings") + user_profile.addMetaDataEntry("type", "user") + container_stack.addContainer(user_profile) + container_registry.addContainer(user_profile) + + container_stack.setNextStack(UM.Application.getInstance().getGlobalContainerStack()) + + container_registry.addContainer(container_stack) + +def createExtruderManager(engine, script_engine): + return ExtruderManager() \ No newline at end of file diff --git a/cura/MachineManagerModel.py b/cura/MachineManagerModel.py index 3a584a2797..3515d613bd 100644 --- a/cura/MachineManagerModel.py +++ b/cura/MachineManagerModel.py @@ -46,20 +46,9 @@ class MachineManagerModel(QObject): activeVariantChanged = pyqtSignal() activeQualityChanged = pyqtSignal() - activeExtruderChanged = pyqtSignal() - globalValueChanged = pyqtSignal() # Emitted whenever a value inside global container is changed. globalValidationChanged = pyqtSignal() # Emitted whenever a validation inside global container is changed. - @pyqtProperty(str, notify=activeExtruderChanged) - def activeExtruderStackId(self): - return self.extrudersIds[str(self._active_extruder_index)] - - @pyqtSlot(int) - def setActiveExtruderIndex(self, index): - self._active_extruder_index = index - self.activeExtruderChanged.emit() - @pyqtProperty("QVariantMap", notify = globalContainerChanged) def extrudersIds(self): ## Find all extruders that reference the new stack @@ -145,26 +134,6 @@ class MachineManagerModel(QObject): new_global_stack.addContainer(current_settings_instance_container) ## Check if the machine has extruder trains - extruder_trains = definition.getMetaDataEntry("machine_extruder_trains", {}) - for extruder in extruder_trains: - extruder_train_stack = UM.Settings.ContainerStack(name + "_extruder_" + extruder) - extruder_train_stack.addMetaDataEntry("type", "extruder") - extruder_train_stack.addMetaDataEntry("machine", name) # What global stack is this extruder linked with? - extruder_train_stack.addMetaDataEntry("position", extruder) # What is the position of the extruder (as defined by machine definition) - extruder_definitions = UM.Settings.ContainerRegistry.getInstance().findDefinitionContainers(id=extruder_trains[extruder]) - if extruder_definitions: - extruder_train_stack.addContainer(extruder_definitions[0]) - current_settings_container_extruder = UM.Settings.InstanceContainer(extruder_train_stack.getName() + "_current_settings") - current_settings_container_extruder.addMetaDataEntry("machine", name) - current_settings_container_extruder.addMetaDataEntry("type", "user") - current_settings_container_extruder.setDefinition(definition) - UM.Settings.ContainerRegistry.getInstance().addContainer(current_settings_container_extruder) - extruder_train_stack.addContainer(current_settings_container_extruder) - extruder_train_stack.setNextStack(new_global_stack) - UM.Settings.ContainerRegistry.getInstance().addContainer(extruder_train_stack) - else: - Logger.log("W", "Unable to find definition for extruder") - Application.getInstance().setGlobalContainerStack(new_global_stack) # Create a name that is not empty and unique diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 0eb13d668c..2e62280865 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -79,7 +79,7 @@ ScrollView { id: provider - containerStackId: Cura.MachineManager.activeMachineId + containerStackId: Cura.ExtruderManager.activeExtruderStackId ? Cura.ExtruderManager.activeExtruderStackId : Cura.MachineManager.activeMachineId key: model.key watchedProperties: [ "value", "enabled", "state", "validationState" ] storeIndex: 0 diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml index 61ccbb998d..b38f65772d 100644 --- a/resources/qml/SidebarHeader.qml +++ b/resources/qml/SidebarHeader.qml @@ -111,7 +111,7 @@ Item onClicked: { base.currentExtruderIndex = index - Cura.MachineManager.setActiveExtruderIndex(index) + Cura.ExtruderManager.setActiveExtruderIndex(index) } style: ButtonStyle {