Merge branch '3.2'

This commit is contained in:
Diego Prado Gesto 2018-01-19 13:50:30 +01:00
commit 0fbcd2d2ec
14 changed files with 179 additions and 128 deletions

View file

@ -279,6 +279,11 @@ class CuraApplication(QtApplication):
# We need them to simplify the switching between materials. # We need them to simplify the switching between materials.
empty_container = ContainerRegistry.getInstance().getEmptyInstanceContainer() empty_container = ContainerRegistry.getInstance().getEmptyInstanceContainer()
empty_definition_changes_container = copy.deepcopy(empty_container)
empty_definition_changes_container.setMetaDataEntry("id", "empty_definition_changes")
empty_definition_changes_container.addMetaDataEntry("type", "definition_changes")
ContainerRegistry.getInstance().addContainer(empty_definition_changes_container)
empty_variant_container = copy.deepcopy(empty_container) empty_variant_container = copy.deepcopy(empty_container)
empty_variant_container.setMetaDataEntry("id", "empty_variant") empty_variant_container.setMetaDataEntry("id", "empty_variant")
empty_variant_container.addMetaDataEntry("type", "variant") empty_variant_container.addMetaDataEntry("type", "variant")

View file

@ -245,6 +245,9 @@ class CuraContainerRegistry(ContainerRegistry):
return {"status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profile {0}", profile_or_list[0].getName())} return {"status": "ok", "message": catalog.i18nc("@info:status", "Successfully imported profile {0}", profile_or_list[0].getName())}
# This message is throw when the profile reader doesn't find any profile in the file
return {"status": "error", "message": catalog.i18nc("@info:status", "File {0} does not contain any valid profile.", file_name)}
# If it hasn't returned by now, none of the plugins loaded the profile successfully. # If it hasn't returned by now, none of the plugins loaded the profile successfully.
return {"status": "error", "message": catalog.i18nc("@info:status", "Profile {0} has an unknown file type or is corrupted.", file_name)} return {"status": "error", "message": catalog.i18nc("@info:status", "Profile {0} has an unknown file type or is corrupted.", file_name)}

View file

@ -63,6 +63,7 @@ class CuraStackBuilder:
next_stack = new_global_stack next_stack = new_global_stack
) )
new_global_stack.addExtruder(new_extruder) new_global_stack.addExtruder(new_extruder)
registry.addContainer(new_extruder)
else: else:
# create extruder stack for each found extruder definition # create extruder stack for each found extruder definition
for extruder_definition in registry.findDefinitionContainers(machine = machine_definition.id): for extruder_definition in registry.findDefinitionContainers(machine = machine_definition.id):
@ -81,6 +82,11 @@ class CuraStackBuilder:
next_stack = new_global_stack next_stack = new_global_stack
) )
new_global_stack.addExtruder(new_extruder) new_global_stack.addExtruder(new_extruder)
registry.addContainer(new_extruder)
# Register the global stack after the extruder stacks are created. This prevents the registry from adding another
# extruder stack because the global stack didn't have one yet (which is enforced since Cura 3.1).
registry.addContainer(new_global_stack)
return new_global_stack return new_global_stack
@ -135,9 +141,7 @@ class CuraStackBuilder:
# Only add the created containers to the registry after we have set all the other # 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 # properties. This makes the create operation more transactional, since any problems
# setting properties will not result in incomplete containers being added. # setting properties will not result in incomplete containers being added.
registry = ContainerRegistry.getInstance() ContainerRegistry.getInstance().addContainer(user_container)
registry.addContainer(stack)
registry.addContainer(user_container)
return stack return stack
@ -181,9 +185,7 @@ class CuraStackBuilder:
if "quality_changes" in kwargs: if "quality_changes" in kwargs:
stack.setQualityChangesById(kwargs["quality_changes"]) stack.setQualityChangesById(kwargs["quality_changes"])
registry = ContainerRegistry.getInstance() ContainerRegistry.getInstance().addContainer(user_container)
registry.addContainer(stack)
registry.addContainer(user_container)
return stack return stack

View file

@ -407,6 +407,12 @@ class ExtruderManager(QObject):
extruder_train.setNextStack(global_stack) extruder_train.setNextStack(global_stack)
extruders_changed = True extruders_changed = True
# FIX: We have to remove those settings here because we know that those values have been copied to all
# the extruders at this point.
for key in ("material_diameter", "machine_nozzle_size"):
if global_stack.definitionChanges.hasProperty(key, "value"):
global_stack.definitionChanges.removeInstance(key, postpone_emit = True)
if extruders_changed: if extruders_changed:
self.extrudersChanged.emit(global_stack_id) self.extrudersChanged.emit(global_stack_id)
self.extrudersAdded.emit() self.extrudersAdded.emit()

View file

@ -18,10 +18,6 @@ if TYPE_CHECKING:
from cura.Settings.GlobalStack import GlobalStack from cura.Settings.GlobalStack import GlobalStack
_EXTRUDER_SPECIFIC_DEFINITION_CHANGES_SETTINGS = ["machine_nozzle_size",
"material_diameter"]
## Represents an Extruder and its related containers. ## Represents an Extruder and its related containers.
# #
# #
@ -53,20 +49,38 @@ class ExtruderStack(CuraContainerStack):
# when we are upgrading a definition_changes container file, there is NO guarantee that other files such as # when we are upgrading a definition_changes container file, there is NO guarantee that other files such as
# machine an extruder stack files are upgraded before this, so we cannot read those files assuming they are in # machine an extruder stack files are upgraded before this, so we cannot read those files assuming they are in
# the latest format. # the latest format.
#
# MORE:
# For single-extrusion machines, nozzle size is saved in the global stack, so the nozzle size value should be
# carried to the first extruder.
# For material diameter, it was supposed to be applied to all extruders, so its value should be copied to all
# extruders.
#
keys_to_copy = ["material_diameter"] # material diameter will be copied to all extruders
if self.getMetaDataEntry("position") == "0": if self.getMetaDataEntry("position") == "0":
for key in _EXTRUDER_SPECIFIC_DEFINITION_CHANGES_SETTINGS: keys_to_copy.append("machine_nozzle_size")
setting_value = stack.definitionChanges.getProperty(key, "value")
if setting_value is None:
continue
setting_definition = stack.getSettingDefinition(key) for key in keys_to_copy:
new_instance = SettingInstance(setting_definition, self.definitionChanges) # Only copy the value when this extruder doesn't have the value.
new_instance.setProperty("value", setting_value) if self.definitionChanges.hasProperty(key, "value"):
new_instance.resetState() # Ensure that the state is not seen as a user state. continue
self.definitionChanges.addInstance(new_instance) setting_value = stack.definitionChanges.getProperty(key, "value")
self.definitionChanges.setDirty(True) if setting_value is None:
continue
stack.definitionChanges.removeInstance(key, postpone_emit = True) setting_definition = stack.getSettingDefinition(key)
new_instance = SettingInstance(setting_definition, self.definitionChanges)
new_instance.setProperty("value", setting_value)
new_instance.resetState() # Ensure that the state is not seen as a user state.
self.definitionChanges.addInstance(new_instance)
self.definitionChanges.setDirty(True)
# NOTE: We cannot remove the setting from the global stack's definition changes container because for
# material diameter, it needs to be applied to all extruders, but here we don't know how many extruders
# a machine actually has and how many extruders has already been loaded for that machine, so we have to
# keep this setting for any remaining extruders that haven't been loaded yet.
#
# Those settings will be removed in ExtruderManager which knows all those info.
@override(ContainerStack) @override(ContainerStack)
def getNextStack(self) -> Optional["GlobalStack"]: def getNextStack(self) -> Optional["GlobalStack"]:

View file

@ -3,6 +3,8 @@
#Type hinting. #Type hinting.
from typing import Union, List, Dict from typing import Union, List, Dict
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Signal import Signal from UM.Signal import Signal
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, QTimer from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, QTimer
@ -75,6 +77,7 @@ class MachineManager(QObject):
self._stacks_have_errors = None self._stacks_have_errors = None
self._empty_definition_changes_container = ContainerRegistry.getInstance().findContainers(id = "empty_definition_changes")[0]
self._empty_variant_container = ContainerRegistry.getInstance().findContainers(id = "empty_variant")[0] self._empty_variant_container = ContainerRegistry.getInstance().findContainers(id = "empty_variant")[0]
self._empty_material_container = ContainerRegistry.getInstance().findContainers(id = "empty_material")[0] self._empty_material_container = ContainerRegistry.getInstance().findContainers(id = "empty_material")[0]
self._empty_quality_container = ContainerRegistry.getInstance().findContainers(id = "empty_quality")[0] self._empty_quality_container = ContainerRegistry.getInstance().findContainers(id = "empty_quality")[0]
@ -1348,6 +1351,68 @@ class MachineManager(QObject):
if containers: if containers:
return containers[0].definition.getId() return containers[0].definition.getId()
## Set the amount of extruders on the active machine (global stack)
# \param extruder_count int the number of extruders to set
def setActiveMachineExtruderCount(self, extruder_count):
extruder_manager = Application.getInstance().getExtruderManager()
definition_changes_container = self._global_container_stack.definitionChanges
if not self._global_container_stack or definition_changes_container == self._empty_definition_changes_container:
return
previous_extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value")
if extruder_count == previous_extruder_count:
return
# reset all extruder number settings whose value is no longer valid
for setting_instance in self._global_container_stack.userChanges.findInstances():
setting_key = setting_instance.definition.key
if not self._global_container_stack.getProperty(setting_key, "type") in ("extruder", "optional_extruder"):
continue
old_value = int(self._global_container_stack.userChanges.getProperty(setting_key, "value"))
if old_value >= extruder_count:
self._global_container_stack.userChanges.removeInstance(setting_key)
Logger.log("d", "Reset [%s] because its old value [%s] is no longer valid ", setting_key, old_value)
# Check to see if any objects are set to print with an extruder that will no longer exist
root_node = Application.getInstance().getController().getScene().getRoot()
for node in DepthFirstIterator(root_node):
if node.getMeshData():
extruder_nr = node.callDecoration("getActiveExtruderPosition")
if extruder_nr is not None and int(extruder_nr) > extruder_count - 1:
node.callDecoration("setActiveExtruder", extruder_manager.getExtruderStack(extruder_count - 1).getId())
definition_changes_container.setProperty("machine_extruder_count", "value", extruder_count)
# Make sure one of the extruder stacks is active
extruder_manager.setActiveExtruderIndex(0)
# Move settable_per_extruder values out of the global container
# After CURA-4482 this should not be the case anymore, but we still want to support older project files.
global_user_container = self._global_container_stack.getTop()
# Make sure extruder_stacks exists
extruder_stacks = []
if previous_extruder_count == 1:
extruder_stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
global_user_container = self._global_container_stack.getTop()
for setting_instance in global_user_container.findInstances():
setting_key = setting_instance.definition.key
settable_per_extruder = self._global_container_stack.getProperty(setting_key, "settable_per_extruder")
if settable_per_extruder:
limit_to_extruder = int(self._global_container_stack.getProperty(setting_key, "limit_to_extruder"))
extruder_stack = extruder_stacks[max(0, limit_to_extruder)]
extruder_stack.getTop().setProperty(setting_key, "value", global_user_container.getProperty(setting_key, "value"))
global_user_container.removeInstance(setting_key)
# Signal that the global stack has changed
Application.getInstance().globalContainerStackChanged.emit()
@staticmethod @staticmethod
def createMachineManager(): def createMachineManager():
return MachineManager() return MachineManager()

View file

@ -390,6 +390,13 @@ UM.Dialog
} }
} }
function accept() {
manager.closeBackend();
manager.onOkButtonClicked();
base.visible = false;
base.accept();
}
function reject() { function reject() {
manager.onCancelButtonClicked(); manager.onCancelButtonClicked();
base.visible = false; base.visible = false;

View file

@ -431,7 +431,6 @@ class CuraEngineBackend(QObject, Backend):
# cached layer data is removed so the previous data is not rendered - CURA-4821 # cached layer data is removed so the previous data is not rendered - CURA-4821
if source.callDecoration("isBlockSlicing") and source.callDecoration("getLayerData"): if source.callDecoration("isBlockSlicing") and source.callDecoration("getLayerData"):
if self._stored_optimized_layer_data: if self._stored_optimized_layer_data:
print(self._stored_optimized_layer_data)
del self._stored_optimized_layer_data[source.callDecoration("getBuildPlateNumber")] del self._stored_optimized_layer_data[source.callDecoration("getBuildPlateNumber")]
build_plate_changed = set() build_plate_changed = set()

View file

@ -49,7 +49,6 @@ class FirmwareUpdateChecker(Extension):
def _onContainerAdded(self, container): def _onContainerAdded(self, container):
# Only take care when a new GlobalStack was added # Only take care when a new GlobalStack was added
if isinstance(container, GlobalStack): if isinstance(container, GlobalStack):
Logger.log("i", "You have a '%s' in printer list. Let's check the firmware!", container.getId())
self.checkFirmwareVersion(container, True) self.checkFirmwareVersion(container, True)
## Connect with software.ultimaker.com, load latest.version and check version info. ## Connect with software.ultimaker.com, load latest.version and check version info.

View file

@ -44,6 +44,8 @@ class FirmwareUpdateCheckerJob(Job):
# Now we just do that if the active printer is Ultimaker 3 or Ultimaker 3 Extended or any # Now we just do that if the active printer is Ultimaker 3 or Ultimaker 3 Extended or any
# other Ultimaker 3 that will come in the future # other Ultimaker 3 that will come in the future
if len(machine_name_parts) >= 2 and machine_name_parts[:2] == ["ultimaker", "3"]: if len(machine_name_parts) >= 2 and machine_name_parts[:2] == ["ultimaker", "3"]:
Logger.log("i", "You have a UM3 in printer list. Let's check the firmware!")
# Nothing to parse, just get the string # Nothing to parse, just get the string
# TODO: In the future may be done by parsing a JSON file with diferent version for each printer model # TODO: In the future may be done by parsing a JSON file with diferent version for each printer model
current_version = reader(current_version_file).readline().rstrip() current_version = reader(current_version_file).readline().rstrip()

View file

@ -107,7 +107,7 @@ class GCodeWriter(MeshWriter):
prefix_length = len(prefix) prefix_length = len(prefix)
container_with_profile = stack.qualityChanges container_with_profile = stack.qualityChanges
if not container_with_profile: if container_with_profile.getId() == "empty_quality_changes":
Logger.log("e", "No valid quality profile found, not writing settings to GCode!") Logger.log("e", "No valid quality profile found, not writing settings to GCode!")
return "" return ""
@ -123,9 +123,9 @@ class GCodeWriter(MeshWriter):
serialized = flat_global_container.serialize() serialized = flat_global_container.serialize()
data = {"global_quality": serialized} data = {"global_quality": serialized}
for extruder in sorted(ExtruderManager.getInstance().getMachineExtruders(stack.getId()), key = lambda k: k.getMetaDataEntry("position")): for extruder in sorted(stack.extruders.values(), key = lambda k: k.getMetaDataEntry("position")):
extruder_quality = extruder.qualityChanges extruder_quality = extruder.qualityChanges
if not extruder_quality: if extruder_quality.getId() == "empty_quality_changes":
Logger.log("w", "No extruder quality profile found, not writing quality for extruder %s to file!", extruder.getId()) Logger.log("w", "No extruder quality profile found, not writing quality for extruder %s to file!", extruder.getId())
continue continue
flat_extruder_quality = self._createFlattenedContainerInstance(extruder.getTop(), extruder_quality) flat_extruder_quality = self._createFlattenedContainerInstance(extruder.getTop(), extruder_quality)

View file

@ -105,60 +105,9 @@ class MachineSettingsAction(MachineAction):
@pyqtSlot(int) @pyqtSlot(int)
def setMachineExtruderCount(self, extruder_count): def setMachineExtruderCount(self, extruder_count):
extruder_manager = Application.getInstance().getExtruderManager() # Note: this method was in this class before, but since it's quite generic and other plugins also need it
# it was moved to the machine manager instead. Now this method just calls the machine manager.
definition_changes_container = self._global_container_stack.definitionChanges Application.getInstance().getMachineManager().setActiveMachineExtruderCount(extruder_count)
if not self._global_container_stack or definition_changes_container == self._empty_container:
return
previous_extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value")
if extruder_count == previous_extruder_count:
return
# reset all extruder number settings whose value is no longer valid
for setting_instance in self._global_container_stack.userChanges.findInstances():
setting_key = setting_instance.definition.key
if not self._global_container_stack.getProperty(setting_key, "type") in ("extruder", "optional_extruder"):
continue
old_value = int(self._global_container_stack.userChanges.getProperty(setting_key, "value"))
if old_value >= extruder_count:
self._global_container_stack.userChanges.removeInstance(setting_key)
Logger.log("d", "Reset [%s] because its old value [%s] is no longer valid ", setting_key, old_value)
# Check to see if any objects are set to print with an extruder that will no longer exist
root_node = Application.getInstance().getController().getScene().getRoot()
for node in DepthFirstIterator(root_node):
if node.getMeshData():
extruder_nr = node.callDecoration("getActiveExtruderPosition")
if extruder_nr is not None and int(extruder_nr) > extruder_count - 1:
node.callDecoration("setActiveExtruder", extruder_manager.getExtruderStack(extruder_count - 1).getId())
definition_changes_container.setProperty("machine_extruder_count", "value", extruder_count)
# Make sure one of the extruder stacks is active
extruder_manager.setActiveExtruderIndex(0)
# Move settable_per_extruder values out of the global container
# After CURA-4482 this should not be the case anymore, but we still want to support older project files.
global_user_container = self._global_container_stack.getTop()
if previous_extruder_count == 1:
extruder_stacks = ExtruderManager.getInstance().getActiveExtruderStacks()
global_user_container = self._global_container_stack.getTop()
for setting_instance in global_user_container.findInstances():
setting_key = setting_instance.definition.key
settable_per_extruder = self._global_container_stack.getProperty(setting_key, "settable_per_extruder")
if settable_per_extruder:
limit_to_extruder = int(self._global_container_stack.getProperty(setting_key, "limit_to_extruder"))
extruder_stack = extruder_stacks[max(0, limit_to_extruder)]
extruder_stack.getTop().setProperty(setting_key, "value", global_user_container.getProperty(setting_key, "value"))
global_user_container.removeInstance(setting_key)
self.forceUpdate()
@pyqtSlot() @pyqtSlot()
def forceUpdate(self): def forceUpdate(self):

View file

@ -390,7 +390,7 @@ Cura.MachineAction
visible: Cura.MachineManager.hasMaterials visible: Cura.MachineManager.hasMaterials
sourceComponent: numericTextFieldWithUnit sourceComponent: numericTextFieldWithUnit
property string settingKey: "material_diameter" property string settingKey: "material_diameter"
property string label: catalog.i18nc("@label", "Material diameter") property string label: catalog.i18nc("@label", "Compatible material diameter")
property string unit: catalog.i18nc("@label", "mm") property string unit: catalog.i18nc("@label", "mm")
property string tooltip: catalog.i18nc("@tooltip", "The nominal diameter of filament supported by the printer. The exact diameter will be overridden by the material and/or the profile.") property string tooltip: catalog.i18nc("@tooltip", "The nominal diameter of filament supported by the printer. The exact diameter will be overridden by the material and/or the profile.")
function afterOnEditingFinished() function afterOnEditingFinished()

View file

@ -26,33 +26,54 @@ UM.Dialog
minimumHeight: maximumHeight minimumHeight: maximumHeight
minimumWidth: maximumWidth minimumWidth: maximumWidth
modality: UM.Application.platform == "linux" ? Qt.NonModal : Qt.WindowModal; modality: UM.Application.platform == "linux" ? Qt.NonModal : Qt.WindowModal
property var fileUrl property var fileUrl
function loadProjectFile(projectFile) // load the entire project
{ function loadProjectFile() {
UM.WorkspaceFileHandler.readLocalFile(projectFile);
var meshName = backgroundItem.getMeshName(projectFile.toString()); // update preference
backgroundItem.hasMesh(decodeURIComponent(meshName)); if (rememberChoiceCheckBox.checked) {
} UM.Preferences.setValue("cura/choice_on_open_project", "open_as_project")
function loadModelFiles(fileUrls)
{
for (var i in fileUrls)
{
CuraApplication.readLocalFile(fileUrls[i]);
} }
var meshName = backgroundItem.getMeshName(fileUrls[0].toString()); UM.WorkspaceFileHandler.readLocalFile(base.fileUrl)
backgroundItem.hasMesh(decodeURIComponent(meshName)); var meshName = backgroundItem.getMeshName(base.fileUrl.toString())
backgroundItem.hasMesh(decodeURIComponent(meshName))
base.hide()
} }
onVisibleChanged: // load the project file as separated models
{ function loadModelFiles() {
if (visible)
{ // update preference
if (rememberChoiceCheckBox.checked) {
UM.Preferences.setValue("cura/choice_on_open_project", "open_as_model")
}
CuraApplication.readLocalFile(base.fileUrl)
var meshName = backgroundItem.getMeshName(base.fileUrl.toString())
backgroundItem.hasMesh(decodeURIComponent(meshName))
base.hide()
}
// override UM.Dialog accept
function accept () {
var openAsPreference = UM.Preferences.getValue("cura/choice_on_open_project")
// when hitting 'enter', we always open as project unless open_as_model was explicitly stored as preference
if (openAsPreference == "open_as_model") {
loadModelFiles()
} else {
loadProjectFile()
}
}
onVisibleChanged: {
if (visible) {
var rememberMyChoice = UM.Preferences.getValue("cura/choice_on_open_project") != "always_ask"; var rememberMyChoice = UM.Preferences.getValue("cura/choice_on_open_project") != "always_ask";
rememberChoiceCheckBox.checked = rememberMyChoice; rememberChoiceCheckBox.checked = rememberMyChoice;
} }
@ -90,47 +111,26 @@ UM.Dialog
} }
// Buttons // Buttons
Item Item {
{
id: buttonBar id: buttonBar
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
height: childrenRect.height height: childrenRect.height
Button Button {
{
id: openAsProjectButton id: openAsProjectButton
text: catalog.i18nc("@action:button", "Open as project"); text: catalog.i18nc("@action:button", "Open as project")
anchors.right: importModelsButton.left anchors.right: importModelsButton.left
anchors.rightMargin: UM.Theme.getSize("default_margin").width anchors.rightMargin: UM.Theme.getSize("default_margin").width
isDefault: true isDefault: true
onClicked: onClicked: loadProjectFile()
{
// update preference
if (rememberChoiceCheckBox.checked)
UM.Preferences.setValue("cura/choice_on_open_project", "open_as_project");
// load this file as project
base.hide();
loadProjectFile(base.fileUrl);
}
} }
Button Button {
{
id: importModelsButton id: importModelsButton
text: catalog.i18nc("@action:button", "Import models"); text: catalog.i18nc("@action:button", "Import models")
anchors.right: parent.right anchors.right: parent.right
onClicked: onClicked: loadModelFiles()
{
// update preference
if (rememberChoiceCheckBox.checked)
UM.Preferences.setValue("cura/choice_on_open_project", "open_as_model");
// load models from this project file
base.hide();
loadModelFiles([base.fileUrl]);
}
} }
} }
} }