From 3cc31fd9c2e97e6c64e9c8198e7337013c8a77dd Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 22 Jun 2016 17:39:50 +0200 Subject: [PATCH 001/236] When saving containers, use the mime type's preferred suffix instead of a hardcoded value Contributes to CURA-342 --- cura/CuraApplication.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 5f5880e3d5..9ab580eaf2 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -251,7 +251,8 @@ class CuraApplication(QtApplication): Logger.logException("e", "An exception occurred when serializing container %s", instance.getId()) continue - file_name = urllib.parse.quote_plus(instance.getId()) + ".inst.cfg" + mime_type = ContainerRegistry.getMimeTypeForContainer(type(instance)) + file_name = urllib.parse.quote_plus(instance.getId()) + "." + mime_type.preferredSuffix instance_type = instance.getMetaDataEntry("type") path = None if instance_type == "material": @@ -279,7 +280,8 @@ class CuraApplication(QtApplication): Logger.logException("e", "An exception occurred when serializing container %s", instance.getId()) continue - file_name = urllib.parse.quote_plus(stack.getId()) + ".stack.cfg" + mime_type = ContainerRegistry.getMimeTypeForContainer(type(stack)) + file_name = urllib.parse.quote_plus(stack.getId()) + "." + mime_type.preferredSuffix stack_type = stack.getMetaDataEntry("type", None) path = None if not stack_type or stack_type == "machine": From a3ea042d4b1374a99d9870147b6c39e3ccd449c5 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Wed, 22 Jun 2016 17:40:17 +0200 Subject: [PATCH 002/236] Implement serialization of XmlMaterialProfile Contributes to CURA-342 --- .../XmlMaterialProfile/XmlMaterialProfile.py | 151 +++++++++++++++++- 1 file changed, 148 insertions(+), 3 deletions(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 89c7d76e9f..a1a992334e 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -3,10 +3,13 @@ import math import copy +import io import xml.etree.ElementTree as ET from UM.Logger import Logger +import UM.Dictionary + import UM.Settings # The namespace is prepended to the tag name but between {}. @@ -20,7 +23,131 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): super().__init__(container_id, *args, **kwargs) def serialize(self): - raise NotImplementedError("Writing material profiles has not yet been implemented") + if self.getDefinition().id != "fdmprinter": + # Since we create an instance of XmlMaterialProfile for each machine and nozzle in the profile, + # we should only serialize the "base" material definition, since that can then take care of + # serializing the machine/nozzle specific profiles. + raise NotImplementedError("Cannot serialize non-root XML materials") + + builder = ET.TreeBuilder() + + root = builder.start("fdmmaterial", { "xmlns": "http://www.ultimaker.com/material"}) + + ## Begin Metadata Block + builder.start("metadata") + + metadata = copy.deepcopy(self.getMetaData()) + properties = metadata.pop("properties", {}) + + # Metadata properties that should not be serialized. + metadata.pop("status", "") + metadata.pop("variant", "") + metadata.pop("type", "") + + ## Begin Name Block + builder.start("name") + + builder.start("brand") + builder.data(metadata.pop("brand", "")) + builder.end("brand") + + builder.start("material") + builder.data(self.getName()) + metadata.pop("material", "") + builder.end("material") + + builder.start("color") + builder.data(metadata.pop("color_name", "")) + builder.end("color") + + builder.end("name") + ## End Name Block + + for key, value in metadata.items(): + builder.start(key) + builder.data(value) + builder.end(key) + + builder.end("metadata") + ## End Metadata Block + + ## Begin Properties Block + builder.start("properties") + + for key, value in properties.items(): + builder.start(key) + builder.data(value) + builder.end(key) + + builder.end("properties") + ## End Properties Block + + ## Begin Settings Block + builder.start("settings") + + for instance in self.findInstances(): + builder.start("setting", { "key": UM.Dictionary.findKey(self.__material_property_setting_map, instance.definition.key) }) + builder.data(str(instance.value)) + builder.end("setting") + + # Find all machine sub-profiles corresponding to this material and add them to this profile. + machines = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = self.getId() + "_*") + for machine in machines: + if machine.getMetaDataEntry("variant"): + # Since the list includes variant-specific containers but we do not yet want to add those, we just skip them. + continue + + builder.start("machine") + + definition = machine.getDefinition() + builder.start("machine_identifier", { "manufacturer": definition.getMetaDataEntry("manufacturer", ""), "product": UM.Dictionary.findKey(self.__product_id_map, definition.id) }) + builder.end("machine_identifier") + + for instance in machine.findInstances(): + if self.getInstance(instance.definition.key) and self.getProperty(instance.definition.key, "value") == instance.value: + # If the settings match that of the base profile, just skip since we inherit the base profile. + continue + + builder.start("setting", { "key": UM.Dictionary.findKey(self.__material_property_setting_map, instance.definition.key) }) + builder.data(str(instance.value)) + builder.end("setting") + + # Find all hotend sub-profiles corresponding to this material and machine and add them to this profile. + hotends = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = machine.getId() + "_*") + for hotend in hotends: + variant_containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = hotend.getMetaDataEntry("variant")) + if variant_containers: + builder.start("hotend", { "id": variant_containers[0].getName() }) + + for instance in hotend.findInstances(): + if self.getInstance(instance.definition.key) and self.getProperty(instance.definition.key, "value") == instance.value: + # If the settings match that of the base profile, just skip since we inherit the base profile. + continue + + if machine.getInstance(instance.definition.key) and machine.getProperty(instance.definition.key, "value") == instance.value: + # If the settings match that of the machine profile, just skip since we inherit the machine profile. + continue + + builder.start("setting", { "key": UM.Dictionary.findKey(self.__material_property_setting_map, instance.definition.key) }) + builder.data(str(instance.value)) + builder.end("setting") + + builder.end("hotend") + + builder.end("machine") + + builder.end("settings") + ## End Settings Block + + builder.end("fdmmaterial") + + root = builder.close() + _indent(root) + stream = io.StringIO() + tree = ET.ElementTree(root) + tree.write(stream, "unicode", True) + + return stream.getvalue() def deserialize(self, serialized): data = ET.fromstring(serialized) @@ -28,7 +155,7 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): self.addMetaDataEntry("type", "material") # TODO: Add material verfication - self.addMetaDataEntry("status", "Unknown") + self.addMetaDataEntry("status", "unknown") metadata = data.iterfind("./um:metadata/*", self.__namespaces) for entry in metadata: @@ -39,7 +166,7 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): material = entry.find("./um:material", self.__namespaces) color = entry.find("./um:color", self.__namespaces) - self.setName("{0} {1} ({2})".format(brand.text, material.text, color.text)) + self.setName(material.text) self.addMetaDataEntry("brand", brand.text) self.addMetaDataEntry("material", material.text) @@ -83,6 +210,8 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): else: Logger.log("d", "Unsupported material setting %s", key) + self._dirty = False + machines = data.iterfind("./um:settings/um:machine", self.__namespaces) for machine in machines: machine_setting_values = {} @@ -187,3 +316,19 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): __namespaces = { "um": "http://www.ultimaker.com/material" } + +## Helper function for pretty-printing XML because ETree is stupid +def _indent(elem, level = 0): + i = "\n" + level * " " + if len(elem): + if not elem.text or not elem.text.strip(): + elem.text = i + " " + if not elem.tail or not elem.tail.strip(): + elem.tail = i + for elem in elem: + _indent(elem, level + 1) + if not elem.tail or not elem.tail.strip(): + elem.tail = i + else: + if level and (not elem.tail or not elem.tail.strip()): + elem.tail = i From f71ddc4b9ff3e300284d43af18eda5113b9122f0 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 23 Jun 2016 17:31:50 +0200 Subject: [PATCH 003/236] Start implementing view and edit support in the materials page Contributes to CURA-342 --- resources/qml/Preferences/MaterialView.qml | 183 ++++++++++++++++++++ resources/qml/Preferences/MaterialsPage.qml | 165 +++++------------- 2 files changed, 222 insertions(+), 126 deletions(-) create mode 100644 resources/qml/Preferences/MaterialView.qml diff --git a/resources/qml/Preferences/MaterialView.qml b/resources/qml/Preferences/MaterialView.qml new file mode 100644 index 0000000000..ac3e4e1e42 --- /dev/null +++ b/resources/qml/Preferences/MaterialView.qml @@ -0,0 +1,183 @@ +// Copyright (c) 2016 Ultimaker B.V. +// Uranium is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.1 +import QtQuick.Controls 1.1 +import QtQuick.Dialogs 1.2 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +TabView +{ + id: base + + property QtObject properties; + + property bool editingEnabled; + + property string currency: UM.Preferences.getValue("general/currency") ? UM.Preferences.getValue("general/currency") : "€" + + Tab + { + title: "Information" + + ScrollView + { + anchors.fill: parent + anchors.margins: UM.Theme.getSize("default_margin").width + + Flow + { + id: containerGrid + + width: base.width - UM.Theme.getSize("default_margin").width * 4; + + property real firstColumnWidth: width * 0.5 + property real secondColumnWidth: width * 0.4 + + property real rowHeight: textField.height; + + Label { width: parent.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Brand") } + TextField { id: textField; width: parent.secondColumnWidth; text: properties.supplier; readOnly: !base.editingEnabled; } + + Label { width: parent.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Material Type") } + TextField { width: parent.secondColumnWidth; text: properties.material_type; readOnly: !base.editingEnabled; } + + Label { width: parent.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Color") } + + Row + { + width: parent.secondColumnWidth; + height: parent.rowHeight; + spacing: UM.Theme.getSize("default_margin").width/2 + + Rectangle + { + id: colorSelector + color: properties.color_code + width: colorLabel.height * 0.75 + height: colorLabel.height * 0.75 + border.width: UM.Theme.getSize("default_lining").height + + anchors.verticalCenter: parent.verticalCenter + + MouseArea { anchors.fill: parent; onClicked: colorDialog.open(); enabled: base.editingEnabled } + } + TextField { id: colorLabel; text: properties.color_name; readOnly: !base.editingEnabled } + + ColorDialog { id: colorDialog; color: properties.color_code; onAccepted: colorSelector.color = color } + } + + Item { width: parent.width; height: UM.Theme.getSize("default_margin").height } + + Label { width: parent.width; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: "" + catalog.i18nc("@label", "Properties") + "" } + + Label { width: parent.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Density") } + ReadOnlySpinBox + { + width: parent.secondColumnWidth; + value: properties.density; + decimals: 2 + suffix: "g/cm" + stepSize: 0.01 + readOnly: !base.editingEnabled; + } + + Label { width: parent.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Diameter") } + ReadOnlySpinBox + { + width: parent.secondColumnWidth; + value: properties.diameter; + decimals: 2 + suffix: "mm³" + stepSize: 0.01 + readOnly: !base.editingEnabled; + } + + Label { width: parent.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament Cost") } + ReadOnlySpinBox + { + width: parent.secondColumnWidth; + value: properties.spool_cost; + prefix: base.currency + readOnly: !base.editingEnabled; + } + + Label { width: parent.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament weight") } + ReadOnlySpinBox + { + width: parent.secondColumnWidth; + value: properties.spool_weight; + suffix: "g"; + stepSize: 10 + readOnly: !base.editingEnabled; + } + + Label { width: parent.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament length") } + ReadOnlySpinBox + { + width: parent.secondColumnWidth; + value: parseFloat(properties.spool_length); + suffix: "m"; + readOnly: !base.editingEnabled; + } + + Label { width: parent.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Cost per Meter (Approx.)") } + ReadOnlySpinBox + { + width: parent.secondColumnWidth; + value: parseFloat(properties.cost_per_meter); + suffix: catalog.i18nc("@label", "%1/m".arg(base.currency)); + readOnly: !base.editingEnabled; + } + + Item { width: parent.width; height: UM.Theme.getSize("default_margin").height } + + Label { width: parent.width; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Description") } + + TextArea + { + text: properties.description; + width: parent.firstColumnWidth + parent.secondColumnWidth + wrapMode: Text.WordWrap + + readOnly: !base.editingEnabled; + } + + Label { width: parent.width; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Adhesion Information") } + + TextArea + { + text: properties.adhesion_info; + width: parent.firstColumnWidth + parent.secondColumnWidth + wrapMode: Text.WordWrap + + readOnly: !base.editingEnabled; + } + } + } + } + + Tab + { + title: catalog.i18nc("@label", "Print settings") + anchors.margins: UM.Theme.getSize("default_margin").height + + ScrollView + { + anchors.fill: parent; + + ListView + { + model: UM.SettingDefinitionsModel + { + containerId: Cura.MachineManager.activeDefinitionId + visibilityHandler: UM.SettingPreferenceVisibilityHandler { } + } + + delegate: Cura.SettingDelegate { } + } + } + } +} diff --git a/resources/qml/Preferences/MaterialsPage.qml b/resources/qml/Preferences/MaterialsPage.qml index af0f0c1bd2..36451e1717 100644 --- a/resources/qml/Preferences/MaterialsPage.qml +++ b/resources/qml/Preferences/MaterialsPage.qml @@ -52,134 +52,45 @@ UM.ManagementPage scrollviewCaption: " " detailsVisible: true - property string currency: UM.Preferences.getValue("general/currency") - Item { UM.I18nCatalog { id: catalog; name: "cura"; } visible: base.currentItem != null anchors.fill: parent - Label { id: profileName; text: materialProperties.name; font: UM.Theme.getFont("large"); width: parent.width; } + Item + { + id: profileName - TabView { - id: scrollView - anchors.left: parent.left - anchors.right: parent.right - anchors.top: profileName.bottom - anchors.topMargin: UM.Theme.getSize("default_margin").height - anchors.bottom: parent.bottom + width: parent.width; + height: childrenRect.height - Tab { - title: "Information" - anchors.margins: UM.Theme.getSize("default_margin").height + Label { text: materialProperties.name; font: UM.Theme.getFont("large"); } + Button + { + id: editButton + anchors.right: parent.right; + text: catalog.i18nc("@action:button", "Edit"); + iconName: "document-edit"; - Flow { - id: containerGrid - - width: scrollView.width; - property real columnWidth: width / 2 - - Label { width: parent.columnWidth; text: catalog.i18nc("@label", "Profile Type") } - Label { width: parent.columnWidth; text: materialProperties.profile_type } - - Label { width: parent.columnWidth; text: catalog.i18nc("@label", "Supplier") } - Label { width: parent.columnWidth; text: materialProperties.supplier } - - Label { width: parent.columnWidth; text: catalog.i18nc("@label", "Material Type") } - Label { width: parent.columnWidth; text: materialProperties.material_type } - - Label { width: parent.columnWidth; text: catalog.i18nc("@label", "Color") } - - Row { - width: parent.columnWidth; - spacing: UM.Theme.getSize("default_margin").width/2 - Rectangle { - color: materialProperties.color_code - width: colorLabel.height - height: colorLabel.height - border.width: UM.Theme.getSize("default_lining").height - } - Label { id: colorLabel; text: materialProperties.color_name } - } - - Item { width: parent.width; height: UM.Theme.getSize("default_margin").height } - - Label { width: parent.width; text: "" + catalog.i18nc("@label", "Properties") + "" } - - Label { width: parent.columnWidth; text: catalog.i18nc("@label", "Density") } - Label { width: parent.columnWidth; text: materialProperties.density } - - Label { width: parent.columnWidth; text: catalog.i18nc("@label", "Diameter") } - Label { width: parent.columnWidth; text: materialProperties.diameter } - - Label { - text: catalog.i18nc("@label", "Filament cost") - width: parent.columnWidth; - height: spoolCostInput.height - verticalAlignment: Text.AlignVCenter - } - - Row { - width: parent.columnWidth; - Label { - text: base.currency ? base.currency + " " : " " - anchors.verticalCenter: parent.verticalCenter - } - TextField { - id: spoolCostInput - text: materialProperties.spool_cost - } - } - - Label { width: parent.columnWidth; text: catalog.i18nc("@label", "Filament weight") } - Label { width: parent.columnWidth; text: materialProperties.spool_weight + " " + "g" } - - Label { width: parent.columnWidth; text: catalog.i18nc("@label", "Filament length") } - Label { width: parent.columnWidth; text: materialProperties.spool_length + " " + "m" } - - Label { width: parent.columnWidth; text: catalog.i18nc("@label", "Cost per meter") } - Label { width: parent.columnWidth; text: catalog.i18nc("@label", "approx. %1 %2/m").arg(materialProperties.cost_per_meter).arg(base.currency); } - - Item { width: parent.width; height: UM.Theme.getSize("default_margin").height } - - Label { - text: materialProperties.description ? "" + catalog.i18nc("@label", "Information") + "
" + materialProperties.description : ""; - width: parent.width - wrapMode: Text.WordWrap - } - Label { - text: materialProperties.adhesion_info ? "" + catalog.i18nc("@label", "Adhesion") + "
" + materialProperties.adhesion_info : ""; - width: parent.width - wrapMode: Text.WordWrap - } - } + checkable: true } - Tab { - title: catalog.i18nc("@label", "Print settings") - anchors.margins: UM.Theme.getSize("default_margin").height + } - Grid { - columns: 2 - spacing: UM.Theme.getSize("default_margin").width - - Column { - Repeater { - model: base.currentItem ? base.currentItem.settings : null - Label { - text: modelData.name.toString(); - elide: Text.ElideMiddle; - } - } - } - Column { - Repeater { - model: base.currentItem ? base.currentItem.settings : null - Label { text: modelData.value.toString() + " " + modelData.unit.toString(); } - } - } - } + MaterialView + { + anchors + { + left: parent.left + right: parent.right + top: profileName.bottom + topMargin: UM.Theme.getSize("default_margin").height + bottom: parent.bottom } + + editingEnabled: editButton.checked; + + properties: materialProperties } QtObject @@ -194,13 +105,15 @@ UM.ManagementPage property string color_name: "Yellow"; property color color_code: "yellow"; - property string density: "Unknown"; - property string diameter: "Unknown"; + property real density: 0.0; + onDensityChanged: console.log(density); + property real diameter: 0.0; + onDiameterChanged: console.log(diameter); - property string spool_cost: "Unknown"; - property string spool_weight: "Unknown"; - property string spool_length: "Unknown"; - property string cost_per_meter: "Unknown"; + property real spool_cost: 0.0; + property real spool_weight: 0.0; + property real spool_length: 0.0; + property real cost_per_meter: 0.0; property string description: ""; property string adhesion_info: ""; @@ -228,13 +141,13 @@ UM.ManagementPage if(currentItem.metadata.properties != undefined && currentItem.metadata.properties != null) { - materialProperties.density = currentItem.metadata.properties.density ? currentItem.metadata.properties.density : "Unknown"; - materialProperties.diameter = currentItem.metadata.properties.diameter ? currentItem.metadata.properties.diameter : "Unknown"; + materialProperties.density = currentItem.metadata.properties.density ? currentItem.metadata.properties.density : 0.0; + materialProperties.diameter = currentItem.metadata.properties.diameter ? currentItem.metadata.properties.diameter : 0.0; } else { - materialProperties.density = "Unknown"; - materialProperties.diameter = "Unknown"; + materialProperties.density = 0.0; + materialProperties.diameter = 0.0; } } } From 1cb836aea5f55ac1778e005609a94c456180ef73 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 23 Jun 2016 17:33:55 +0200 Subject: [PATCH 004/236] Add a ReadOnlySpinBox control that provides a spinBox with a readOnly property Contributes to CURA-342 --- resources/qml/Preferences/ReadOnlySpinBox.qml | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 resources/qml/Preferences/ReadOnlySpinBox.qml diff --git a/resources/qml/Preferences/ReadOnlySpinBox.qml b/resources/qml/Preferences/ReadOnlySpinBox.qml new file mode 100644 index 0000000000..5c1a3cbe19 --- /dev/null +++ b/resources/qml/Preferences/ReadOnlySpinBox.qml @@ -0,0 +1,23 @@ +// Copyright (c) 2016 Ultimaker B.V. +// Uranium is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.1 +import QtQuick.Controls 1.1 +import QtQuick.Dialogs 1.2 + +// Provides a SpinBox with the same readOnly property as a TextField +SpinBox +{ + id: base + property bool readOnly: false + + Keys.enabled: !readOnly + MouseArea + { + acceptedButtons: Qt.AllButtons; + anchors.fill: parent; + enabled: parent.readOnly; + onWheel: wheel.accepted = true; + cursorShape: enabled ? Qt.ArrowCursor : Qt.IBeamCursor; + } +} From b2ef607cb6c251e9f1cead83e31e8e00581bbc6f Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 23 Jun 2016 17:34:35 +0200 Subject: [PATCH 005/236] Fix the material setting list to use a simple placeholder Contributes to CURA-342 --- resources/qml/Preferences/MaterialView.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/qml/Preferences/MaterialView.qml b/resources/qml/Preferences/MaterialView.qml index ac3e4e1e42..41afae9eac 100644 --- a/resources/qml/Preferences/MaterialView.qml +++ b/resources/qml/Preferences/MaterialView.qml @@ -174,9 +174,10 @@ TabView { containerId: Cura.MachineManager.activeDefinitionId visibilityHandler: UM.SettingPreferenceVisibilityHandler { } + expanded: ["*"] } - delegate: Cura.SettingDelegate { } + delegate: Label { text: model.label } } } } From 6a1942c254cc241da90712744522571ee71f1c05 Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Thu, 23 Jun 2016 19:40:00 +0200 Subject: [PATCH 006/236] GCodeWriter: Exporting the data as done on regualar exports --- plugins/GCodeWriter/GCodeWriter.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/plugins/GCodeWriter/GCodeWriter.py b/plugins/GCodeWriter/GCodeWriter.py index d304f0d046..c80d8e610a 100644 --- a/plugins/GCodeWriter/GCodeWriter.py +++ b/plugins/GCodeWriter/GCodeWriter.py @@ -23,7 +23,7 @@ class GCodeWriter(MeshWriter): # It can only read settings with the same version as the version it was # written with. If the file format is changed in a way that breaks reverse # compatibility, increment this version number! - version = 1 + version = 2 ## Dictionary that defines how characters are escaped when embedded in # g-code. @@ -68,23 +68,21 @@ class GCodeWriter(MeshWriter): 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() + global_stack = Application.getInstance().getGlobalContainerStack() + container_with_settings = global_stack.getContainers()[1] + serialized = container_with_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) + serialized = pattern.sub(lambda m: GCodeWriter.escape_characters[re.escape(m.group(0))], serialized) # 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 + for pos in range(0, len(serialized), 80 - prefix_length): + result += prefix + serialized[pos : pos + 80 - prefix_length] + "\n" + serialized = result - return serialised \ No newline at end of file + return serialized \ No newline at end of file From 1fdf835c19bf8687c7ff1998d306a1301b13c768 Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Thu, 23 Jun 2016 19:40:52 +0200 Subject: [PATCH 007/236] GCodeProfileReader: Increasing the setting version --- plugins/GCodeProfileReader/GCodeProfileReader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/GCodeProfileReader/GCodeProfileReader.py b/plugins/GCodeProfileReader/GCodeProfileReader.py index 5dcea88aed..5915395005 100644 --- a/plugins/GCodeProfileReader/GCodeProfileReader.py +++ b/plugins/GCodeProfileReader/GCodeProfileReader.py @@ -22,7 +22,7 @@ class GCodeProfileReader(ProfileReader): # It can only read settings with the same version as the version it was # written with. If the file format is changed in a way that breaks reverse # compatibility, increment this version number! - version = 1 + version = 2 ## Dictionary that defines how characters are escaped when embedded in # g-code. From d49ba8011775957eebc63f17aed808216629da1b Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Fri, 24 Jun 2016 16:00:38 +0200 Subject: [PATCH 008/236] GCodeWriter: Getting always the correct container with the currently used profile. --- plugins/GCodeWriter/GCodeWriter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/GCodeWriter/GCodeWriter.py b/plugins/GCodeWriter/GCodeWriter.py index c80d8e610a..4eb1f89134 100644 --- a/plugins/GCodeWriter/GCodeWriter.py +++ b/plugins/GCodeWriter/GCodeWriter.py @@ -69,8 +69,8 @@ class GCodeWriter(MeshWriter): prefix_length = len(prefix) global_stack = Application.getInstance().getGlobalContainerStack() - container_with_settings = global_stack.getContainers()[1] - serialized = container_with_settings.serialize() + container_with_profile = global_stack.findContainer({"type": "quality"}) + serialized = container_with_profile.serialize() # Escape characters that have a special meaning in g-code comments. pattern = re.compile("|".join(GCodeWriter.escape_characters.keys())) From 93cdce7b3360b2b285ca32f8b7cf7ba7522381a9 Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Fri, 24 Jun 2016 16:01:47 +0200 Subject: [PATCH 009/236] GCodeProfileReader: Fixing read of profiles from GCode --- plugins/GCodeProfileReader/GCodeProfileReader.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/GCodeProfileReader/GCodeProfileReader.py b/plugins/GCodeProfileReader/GCodeProfileReader.py index 5915395005..1ce1473582 100644 --- a/plugins/GCodeProfileReader/GCodeProfileReader.py +++ b/plugins/GCodeProfileReader/GCodeProfileReader.py @@ -75,15 +75,16 @@ class GCodeProfileReader(ProfileReader): # Create an empty profile - the id will be changed later profile = InstanceContainer("") - profile.addMetaDataEntry("type", "quality") try: profile.deserialize(serialized) except Exception as e: # Not a valid g-code file. Logger.log("e", "Unable to serialise the profile: %s", str(e)) return None + profile.addMetaDataEntry("type", "quality") + #Creating a unique name using the filename of the GCode - new_name = catalog.i18nc("@label", "Custom profile (%s)") %(os.path.splitext(os.path.basename(file_name))[0]) + new_name = catalog.i18nc("@label", "G-Code-imported profile") profile.setName(new_name) profile._id = new_name From 68a8bcb009562434be867ef2c473ebd131e5a18d Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Mon, 27 Jun 2016 13:33:38 +0200 Subject: [PATCH 010/236] Fix width of materials properties tab --- resources/qml/Preferences/MaterialView.qml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/resources/qml/Preferences/MaterialView.qml b/resources/qml/Preferences/MaterialView.qml index 41afae9eac..c662895584 100644 --- a/resources/qml/Preferences/MaterialView.qml +++ b/resources/qml/Preferences/MaterialView.qml @@ -2,7 +2,7 @@ // Uranium is released under the terms of the AGPLv3 or higher. import QtQuick 2.1 -import QtQuick.Controls 1.1 +import QtQuick.Controls 1.3 import QtQuick.Dialogs 1.2 import UM 1.2 as UM @@ -27,13 +27,15 @@ TabView anchors.fill: parent anchors.margins: UM.Theme.getSize("default_margin").width + horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff + Flow { id: containerGrid - width: base.width - UM.Theme.getSize("default_margin").width * 4; + width: base.width; - property real firstColumnWidth: width * 0.5 + property real firstColumnWidth: width * 0.45 property real secondColumnWidth: width * 0.4 property real rowHeight: textField.height; From 214ffbb0bd1edd2dd0c519abd1a209978737b0d3 Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Mon, 27 Jun 2016 15:22:45 +0200 Subject: [PATCH 011/236] JSON fix: all retraction settings now only retrievable per extruder (CURA-303) --- resources/definitions/fdmprinter.def.json | 30 +++++++++++++++-------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 1a3f8f144a..a867f9aad8 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -914,7 +914,8 @@ "description": "Retract the filament when the nozzle is moving over a non-printed area. ", "type": "bool", "default_value": true, - "settable_per_mesh": true + "settable_per_mesh": false, + "settable_per_extruder": true }, "retraction_amount": { "label": "Retraction Distance", @@ -925,7 +926,8 @@ "minimum_value_warning": "-0.0001", "maximum_value_warning": "10.0", "enabled": "retraction_enable", - "settable_per_mesh": true + "settable_per_mesh": false, + "settable_per_extruder": true }, "retraction_speed": { "label": "Retraction Speed", @@ -937,7 +939,8 @@ "maximum_value": "299792458000", "maximum_value_warning": "100", "enabled": "retraction_enable", - "settable_per_mesh": true, + "settable_per_mesh": false, + "settable_per_extruder": true, "children": { "retraction_retract_speed": { "label": "Retraction Retract Speed", @@ -950,7 +953,8 @@ "maximum_value_warning": "100", "enabled": "retraction_enable", "value": "retraction_speed", - "settable_per_mesh": true + "settable_per_mesh": false, + "settable_per_extruder": true }, "retraction_prime_speed": { "label": "Retraction Prime Speed", @@ -963,7 +967,8 @@ "maximum_value_warning": "100", "enabled": "retraction_enable", "value": "retraction_speed", - "settable_per_mesh": true + "settable_per_mesh": false, + "settable_per_extruder": true } } }, @@ -976,7 +981,8 @@ "minimum_value_warning": "-0.0001", "maximum_value_warning": "5.0", "enabled": "retraction_enable", - "settable_per_mesh": true + "settable_per_mesh": false, + "settable_per_extruder": true }, "retraction_min_travel": { "label": "Retraction Minimum Travel", @@ -988,7 +994,8 @@ "minimum_value": "0", "maximum_value_warning": "10", "enabled": "retraction_enable", - "settable_per_mesh": true + "settable_per_mesh": false, + "settable_per_extruder": true }, "retraction_count_max": { "label": "Maximum Retraction Count", @@ -998,7 +1005,8 @@ "maximum_value_warning": "100", "type": "int", "enabled": "retraction_enable", - "settable_per_mesh": true + "settable_per_mesh": false, + "settable_per_extruder": true }, "retraction_extrusion_window": { "label": "Minimum Extrusion Distance Window", @@ -1010,7 +1018,8 @@ "maximum_value_warning": "retraction_amount * 2", "value": "retraction_amount", "enabled": "retraction_enable", - "settable_per_mesh": true + "settable_per_mesh": false, + "settable_per_extruder": true }, "retraction_hop": { "label": "Z Hop when Retracting", @@ -1021,7 +1030,8 @@ "minimum_value_warning": "-0.0001", "maximum_value_warning": "10", "enabled": "retraction_enable", - "settable_per_mesh": true + "settable_per_mesh": false, + "settable_per_extruder": true }, "material_standby_temperature": { From a746710e26a57be7ae4a641d46efe648381f28e2 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 28 Jun 2016 17:30:46 +0200 Subject: [PATCH 012/236] Introduce a MaterialSettingsVisibilityHandler that will only show those settings relevant for materials Contriubtes to CURA-342 --- cura/CuraApplication.py | 3 +++ .../MaterialSettingsVisibilityHandler.py | 19 +++++++++++++++++++ cura/Settings/__init__.py | 2 ++ 3 files changed, 24 insertions(+) create mode 100644 cura/Settings/MaterialSettingsVisibilityHandler.py create mode 100644 cura/Settings/__init__.py diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index c12ce3b8e5..9970f0efce 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -46,6 +46,8 @@ from . import MachineManagerModel from . import ContainerSettingsModel from . import MachineActionManager +import cura.Settings + from PyQt5.QtCore import pyqtSlot, QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS from PyQt5.QtGui import QColor, QIcon from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType @@ -424,6 +426,7 @@ class CuraApplication(QtApplication): qmlRegisterType(ExtrudersModel.ExtrudersModel, "Cura", 1, 0, "ExtrudersModel") qmlRegisterType(ContainerSettingsModel.ContainerSettingsModel, "Cura", 1, 0, "ContainerSettingsModel") + qmlRegisterType(cura.Settings.MaterialSettingsVisibilityHandler, "Cura", 1, 0, "MaterialSettingsVisibilityHandler") qmlRegisterSingletonType(QUrl.fromLocalFile(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Actions.qml")), "Cura", 1, 0, "Actions") diff --git a/cura/Settings/MaterialSettingsVisibilityHandler.py b/cura/Settings/MaterialSettingsVisibilityHandler.py new file mode 100644 index 0000000000..7286f509bf --- /dev/null +++ b/cura/Settings/MaterialSettingsVisibilityHandler.py @@ -0,0 +1,19 @@ +# Copyright (c) 2016 Ultimaker B.V. +# Uranium is released under the terms of the AGPLv3 or higher. + +import UM.Settings.Models + +class MaterialSettingsVisibilityHandler(UM.Settings.Models.SettingVisibilityHandler): + def __init__(self, parent = None, *args, **kwargs): + super().__init__(parent = parent, *args, **kwargs) + + material_settings = set([ + "material_print_temperature", + "material_bed_temperature", + "material_standby_temperature", + "cool_fan_speed", + "retraction_amount", + "retraction_speed", + ]) + + self.setVisible(material_settings) diff --git a/cura/Settings/__init__.py b/cura/Settings/__init__.py new file mode 100644 index 0000000000..d6fe389478 --- /dev/null +++ b/cura/Settings/__init__.py @@ -0,0 +1,2 @@ + +from .MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler From 842015ea10aeba8c60bd08613b6ba6562e362950 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 28 Jun 2016 17:32:50 +0200 Subject: [PATCH 013/236] Move all the menus to individual QML files to make it easier to reuse them Contributes to CURA-342 --- resources/qml/Cura.qml | 32 +--- resources/qml/Menus/MaterialMenu.qml | 57 +++++++ resources/qml/Menus/NozzleMenu.qml | 37 +++++ resources/qml/Menus/PrinterMenu.qml | 38 +++++ resources/qml/Menus/ProfileMenu.qml | 86 +++++++++++ resources/qml/Menus/RecentFilesMenu.qml | 37 +++++ resources/qml/Menus/ViewMenu.qml | 29 ++++ resources/qml/SidebarHeader.qml | 190 +----------------------- 8 files changed, 293 insertions(+), 213 deletions(-) create mode 100644 resources/qml/Menus/MaterialMenu.qml create mode 100644 resources/qml/Menus/NozzleMenu.qml create mode 100644 resources/qml/Menus/PrinterMenu.qml create mode 100644 resources/qml/Menus/ProfileMenu.qml create mode 100644 resources/qml/Menus/RecentFilesMenu.qml create mode 100644 resources/qml/Menus/ViewMenu.qml diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index a27c232e5e..c41d80461a 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -10,6 +10,8 @@ import QtQuick.Dialogs 1.1 import UM 1.2 as UM import Cura 1.0 as Cura +import "Menus" + UM.MainWindow { id: base @@ -417,14 +419,9 @@ UM.MainWindow { id: view_panel - //anchors.left: parent.left; - //anchors.right: parent.right; - //anchors.bottom: parent.bottom anchors.top: viewModeButton.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height; anchors.left: viewModeButton.left; - //anchors.bottom: buttons.top; - //anchors.bottomMargin: UM.Theme.getSize("default_margin").height; height: childrenRect.height; @@ -434,7 +431,6 @@ UM.MainWindow Button { id: openFileButton; - //style: UM.Backend.progress < 0 ? UM.Theme.styles.open_file_button : UM.Theme.styles.tool_button; text: catalog.i18nc("@action:button","Open File"); iconSource: UM.Theme.getIcon("load") style: UM.Theme.styles.tool_button @@ -442,9 +438,7 @@ UM.MainWindow anchors { top: parent.top; - //topMargin: UM.Theme.getSize("loadfile_margin").height left: parent.left; - //leftMargin: UM.Theme.getSize("loadfile_margin").width } action: Cura.Actions.open; } @@ -484,27 +478,7 @@ UM.MainWindow style: UM.Theme.styles.tool_button; tooltip: ''; - menu: Menu - { - id: viewMenu; - Instantiator - { - id: viewMenuInstantiator - model: UM.ViewModel { } - MenuItem - { - text: model.name - checkable: true; - checked: model.active - exclusiveGroup: viewMenuGroup; - onTriggered: UM.Controller.setActiveView(model.id); - } - onObjectAdded: viewMenu.insertItem(index, object) - onObjectRemoved: viewMenu.removeItem(object) - } - - ExclusiveGroup { id: viewMenuGroup; } - } + menu: ViewMenu { } } Toolbar diff --git a/resources/qml/Menus/MaterialMenu.qml b/resources/qml/Menus/MaterialMenu.qml new file mode 100644 index 0000000000..4af8246930 --- /dev/null +++ b/resources/qml/Menus/MaterialMenu.qml @@ -0,0 +1,57 @@ +// Copyright (c) 2016 Ultimaker B.V. +// Cura is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 1.1 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +Menu +{ + id: menu + title: "Material" + + Instantiator + { + model: UM.InstanceContainersModel + { + filter: + { + var result = { "type": "material" } + if(Cura.MachineManager.filterMaterialsByMachine) + { + result.definition = Cura.MachineManager.activeDefinitionId + if(Cura.MachineManager.hasVariants) + { + result.variant = Cura.MachineManager.activeVariantId + } + } + else + { + result.definition = "fdmprinter" + } + return result + } + } + MenuItem + { + text: model.name; + checkable: true; + checked: model.id == Cura.MachineManager.activeMaterialId; + exclusiveGroup: group; + onTriggered: + { + Cura.MachineManager.setActiveMaterial(model.id); + } + } + onObjectAdded: menu.insertItem(index, object) + onObjectRemoved: menu.removeItem(object) + } + + ExclusiveGroup { id: group } + + MenuSeparator { } + + MenuItem { action: Cura.Actions.manageMaterials } +} diff --git a/resources/qml/Menus/NozzleMenu.qml b/resources/qml/Menus/NozzleMenu.qml new file mode 100644 index 0000000000..506a9a2a01 --- /dev/null +++ b/resources/qml/Menus/NozzleMenu.qml @@ -0,0 +1,37 @@ +// Copyright (c) 2016 Ultimaker B.V. +// Cura is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 1.1 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +Menu +{ + id: menu + title: "Nozzle" + + Instantiator + { + model: UM.InstanceContainersModel + { + filter: + { + "type": "variant", + "definition": Cura.MachineManager.activeDefinitionId //Only show variants of this machine + } + } + MenuItem { + text: model.name; + checkable: true; + checked: model.id == Cura.MachineManager.activeVariantId; + exclusiveGroup: group + onTriggered: Cura.MachineManager.setActiveVariant(model.id) + } + onObjectAdded: menu.insertItem(index, object) + onObjectRemoved: menu.removeItem(object) + } + + ExclusiveGroup { id: group } +} diff --git a/resources/qml/Menus/PrinterMenu.qml b/resources/qml/Menus/PrinterMenu.qml new file mode 100644 index 0000000000..408e8bd585 --- /dev/null +++ b/resources/qml/Menus/PrinterMenu.qml @@ -0,0 +1,38 @@ +// Copyright (c) 2016 Ultimaker B.V. +// Cura is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 1.1 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +Menu +{ + id: menu; + + Instantiator + { + model: UM.ContainerStacksModel + { + filter: {"type": "machine"} + } + MenuItem + { + text: model.name; + checkable: true; + checked: Cura.MachineManager.activeMachineId == model.id + exclusiveGroup: group; + onTriggered: Cura.MachineManager.setActiveMachine(model.id); + } + onObjectAdded: menu.insertItem(index, object) + onObjectRemoved: menu.removeItem(object) + } + + ExclusiveGroup { id: group; } + + MenuSeparator { } + + MenuItem { action: Cura.Actions.addMachine; } + MenuItem { action: Cura.Actions.configureMachines; } +} diff --git a/resources/qml/Menus/ProfileMenu.qml b/resources/qml/Menus/ProfileMenu.qml new file mode 100644 index 0000000000..213f8c2629 --- /dev/null +++ b/resources/qml/Menus/ProfileMenu.qml @@ -0,0 +1,86 @@ +// Copyright (c) 2016 Ultimaker B.V. +// Cura is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 1.1 + +import UM 1.2 as UM +import Cura 1.0 as Cura + + Menu +{ + id: menu + + Instantiator + { + model: UM.InstanceContainersModel { filter: menu.getFilter({ "read_only": true }); } + + MenuItem + { + text: model.name + checkable: true + checked: Cura.MachineManager.activeQualityId == model.id + exclusiveGroup: group + onTriggered: Cura.MachineManager.setActiveQuality(model.id) + } + + onObjectAdded: menu.insertItem(index, object); + onObjectRemoved: menu.removeItem(object); + } + + MenuSeparator { id: customSeparator } + + Instantiator + { + model: UM.InstanceContainersModel + { + id: customProfilesModel; + filter: menu.getFilter({ "read_only": false }); + onRowsInserted: customSeparator.visible = rowCount() > 1 + onRowsRemoved: customSeparator.visible = rowCount() > 1 + onModelReset: customSeparator.visible = rowCount() > 1 + } + + MenuItem + { + text: model.name + checkable: true + checked: Cura.MachineManager.activeQualityId == model.id + exclusiveGroup: group + onTriggered: Cura.MachineManager.setActiveQuality(model.id) + } + + onObjectAdded: menu.insertItem(index, object); + onObjectRemoved: menu.removeItem(object); + } + + ExclusiveGroup { id: group; } + + MenuSeparator { id: profileMenuSeparator } + + MenuItem { action: Cura.Actions.addProfile } + MenuItem { action: Cura.Actions.updateProfile } + MenuItem { action: Cura.Actions.resetProfile } + MenuSeparator { } + MenuItem { action: Cura.Actions.manageProfiles } + + function getFilter(initial_conditions) + { + var result = initial_conditions; + result.type = "quality" + + if(Cura.MachineManager.filterQualityByMachine) + { + result.definition = Cura.MachineManager.activeDefinitionId; + if(Cura.MachineManager.hasMaterials) + { + result.material = Cura.MachineManager.activeMaterialId; + } + } + else + { + result.definition = "fdmprinter" + } + return result + } +} diff --git a/resources/qml/Menus/RecentFilesMenu.qml b/resources/qml/Menus/RecentFilesMenu.qml new file mode 100644 index 0000000000..c47fc5715b --- /dev/null +++ b/resources/qml/Menus/RecentFilesMenu.qml @@ -0,0 +1,37 @@ +// Copyright (c) 2016 Ultimaker B.V. +// Cura is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 1.1 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +Menu +{ + id: menu + title: catalog.i18nc("@title:menu menubar:file", "Open &Recent") + iconName: "document-open-recent"; + + enabled: Printer.recentFiles.length > 0; + + Instantiator + { + model: Printer.recentFiles + MenuItem + { + text: + { + var path = modelData.toString() + return (index + 1) + ". " + path.slice(path.lastIndexOf("/") + 1); + } + onTriggered: { + UM.MeshFileHandler.readLocalFile(modelData); + var meshName = backgroundItem.getMeshName(modelData.toString()) + backgroundItem.hasMesh(decodeURIComponent(meshName)) + } + } + onObjectAdded: menu.insertItem(index, object) + onObjectRemoved: menu.removeItem(object) + } +} diff --git a/resources/qml/Menus/ViewMenu.qml b/resources/qml/Menus/ViewMenu.qml new file mode 100644 index 0000000000..74579932e0 --- /dev/null +++ b/resources/qml/Menus/ViewMenu.qml @@ -0,0 +1,29 @@ +// Copyright (c) 2016 Ultimaker B.V. +// Cura is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 1.1 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +Menu +{ + title: catalog.i18nc("@title:menu menubar:toplevel", "&View"); + id: menu + Instantiator + { + model: UM.ViewModel { } + MenuItem + { + text: model.name; + checkable: true; + checked: model.active; + exclusiveGroup: group; + onTriggered: UM.Controller.setActiveView(model.id); + } + onObjectAdded: menu.insertItem(index, object) + onObjectRemoved: menu.removeItem(object) + } + ExclusiveGroup { id: group; } +} diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml index a2304d1d3e..4aad341ee0 100644 --- a/resources/qml/SidebarHeader.qml +++ b/resources/qml/SidebarHeader.qml @@ -8,6 +8,8 @@ import QtQuick.Controls.Styles 1.1 import UM 1.2 as UM import Cura 1.0 as Cura +import "Menus" + Column { id: base; @@ -56,34 +58,7 @@ Column width: parent.width * 0.55 + UM.Theme.getSize("default_margin").width - menu: Menu - { - id: machineSelectionMenu - Instantiator - { - model: UM.ContainerStacksModel - { - filter: {"type": "machine"} - } - MenuItem - { - text: model.name; - checkable: true; - checked: Cura.MachineManager.activeMachineId == model.id - exclusiveGroup: machineSelectionMenuGroup; - onTriggered: Cura.MachineManager.setActiveMachine(model.id); - } - onObjectAdded: machineSelectionMenu.insertItem(index, object) - onObjectRemoved: machineSelectionMenu.removeItem(object) - } - - ExclusiveGroup { id: machineSelectionMenuGroup; } - - MenuSeparator { } - - MenuItem { action: Cura.Actions.addMachine; } - MenuItem { action: Cura.Actions.configureMachines; } - } + menu: PrinterMenu { } } } @@ -235,37 +210,7 @@ Column anchors.left: parent.left style: UM.Theme.styles.sidebar_header_button - menu: Menu - { - id: variantsSelectionMenu - Instantiator - { - id: variantSelectionInstantiator - model: UM.InstanceContainersModel - { - filter: - { - "type": "variant", - "definition": Cura.MachineManager.activeDefinitionId //Only show variants of this machine - } - } - MenuItem - { - text: model.name; - checkable: true; - checked: model.id == Cura.MachineManager.activeVariantId; - exclusiveGroup: variantSelectionMenuGroup; - onTriggered: - { - Cura.MachineManager.setActiveVariant(model.id); - } - } - onObjectAdded: variantsSelectionMenu.insertItem(index, object) - onObjectRemoved: variantsSelectionMenu.removeItem(object) - } - - ExclusiveGroup { id: variantSelectionMenuGroup; } - } + menu: NozzleMenu { } } ToolButton { @@ -280,49 +225,7 @@ Column anchors.right: parent.right style: UM.Theme.styles.sidebar_header_button - menu: Menu - { - id: materialSelectionMenu - Instantiator - { - id: materialSelectionInstantiator - model: UM.InstanceContainersModel - { - filter: - { - var result = { "type": "material" } - if(Cura.MachineManager.filterMaterialsByMachine) - { - result.definition = Cura.MachineManager.activeDefinitionId - if(Cura.MachineManager.hasVariants) - { - result.variant = Cura.MachineManager.activeVariantId - } - } - else - { - result.definition = "fdmprinter" - } - return result - } - } - MenuItem - { - text: model.name; - checkable: true; - checked: model.id == Cura.MachineManager.activeMaterialId; - exclusiveGroup: materialSelectionMenuGroup; - onTriggered: - { - Cura.MachineManager.setActiveMaterial(model.id); - } - } - onObjectAdded: materialSelectionMenu.insertItem(index, object) - onObjectRemoved: materialSelectionMenu.removeItem(object) - } - - ExclusiveGroup { id: materialSelectionMenuGroup; } - } + menu: MaterialMenu { } } } } @@ -359,88 +262,7 @@ Column tooltip: Cura.MachineManager.activeQualityName style: UM.Theme.styles.sidebar_header_button - menu: Menu - { - id: profileSelectionMenu - Instantiator - { - id: profileSelectionInstantiator - model: UM.InstanceContainersModel - { - filter: - { - var result = { "type": "quality" }; - if(Cura.MachineManager.filterQualityByMachine) - { - result.definition = Cura.MachineManager.activeDefinitionId; - if(Cura.MachineManager.hasMaterials) - { - result.material = Cura.MachineManager.activeMaterialId; - } - } - else - { - result.definition = "fdmprinter" - } - return result - } - } - property int separatorIndex: -1 - - Loader { - property QtObject model_data: model - property int model_index: index - sourceComponent: menuItemDelegate - } - onObjectAdded: - { - //Insert a separator between readonly and custom profiles - if(separatorIndex < 0 && index > 0) - { - if(model.getItem(index-1).readOnly != model.getItem(index).readOnly) - { - profileSelectionMenu.insertSeparator(index); - separatorIndex = index; - } - } - //Because of the separator, custom profiles move one index lower - profileSelectionMenu.insertItem((model.getItem(index).readOnly) ? index : index + 1, object.item); - } - onObjectRemoved: - { - //When adding a profile, the menu is rebuilt by removing all items. - //If a separator was added, we need to remove that too. - if(separatorIndex >= 0) - { - profileSelectionMenu.removeItem(profileSelectionMenu.items[separatorIndex]) - separatorIndex = -1; - } - profileSelectionMenu.removeItem(object.item); - } - } - ExclusiveGroup { id: profileSelectionMenuGroup; } - - Component - { - id: menuItemDelegate - MenuItem - { - id: item - text: model_data ? model_data.name : "" - checkable: true - checked: model_data != null ? Cura.MachineManager.activeQualityId == model_data.id : false - exclusiveGroup: profileSelectionMenuGroup; - onTriggered: Cura.MachineManager.setActiveQuality(model_data.id) - } - } - - MenuSeparator { } - MenuItem { action: Cura.Actions.addProfile } - MenuItem { action: Cura.Actions.updateProfile } - MenuItem { action: Cura.Actions.resetProfile } - MenuSeparator { } - MenuItem { action: Cura.Actions.manageProfiles } - } + menu: ProfileMenu { } UM.SimpleButton { From 1a56da464c73e3b6cd48433619a79ed110b5c1a1 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 28 Jun 2016 17:42:04 +0200 Subject: [PATCH 014/236] Introduce a "Manage Materials" action Contributes to CURA-342 --- resources/qml/Actions.qml | 9 +++++++++ resources/qml/Cura.qml | 10 ++++++++++ 2 files changed, 19 insertions(+) diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index 7e03bd7102..45ba20c973 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -39,6 +39,8 @@ Item property alias resetProfile: resetProfileAction; property alias manageProfiles: manageProfilesAction; + property alias manageMaterials: manageMaterialsAction; + property alias preferences: preferencesAction; property alias showEngineLog: showEngineLogAction; @@ -107,6 +109,13 @@ Item iconName: "configure"; } + Action + { + id: manageMaterialsAction + text: catalog.i18nc("@action:inmenu", "Manage Materials...") + iconName: "configure" + } + Action { id: updateProfileAction; diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index c41d80461a..b754d3a1d7 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -582,6 +582,16 @@ UM.MainWindow } } + Connections + { + target: Cura.Actions.manageMaterials + onTriggered: + { + preferences.visible = true; + preferences.setPage(3) + } + } + Connections { target: Cura.Actions.configureSettingVisibility From 0270aecdc342c170dcd8f1000c831bb52322bab2 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 28 Jun 2016 17:43:28 +0200 Subject: [PATCH 015/236] Replace separate machine/profile menus in menubar with a single "Settings" menu Contributes to CURA-342 --- resources/qml/Actions.qml | 3 +- resources/qml/Cura.qml | 218 ++++++-------------------------------- 2 files changed, 37 insertions(+), 184 deletions(-) diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index 45ba20c973..67bc5fe149 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -92,7 +92,7 @@ Item Action { id: preferencesAction; - text: catalog.i18nc("@action:inmenu menubar:settings","&Preferences..."); + text: catalog.i18nc("@action:inmenu","Configure Cura..."); iconName: "configure"; } @@ -282,5 +282,6 @@ Item { id: configureSettingVisibilityAction text: catalog.i18nc("@action:menu", "Configure setting visiblity..."); + iconName: "configure" } } diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index b754d3a1d7..5ec6a37ea5 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -52,41 +52,13 @@ UM.MainWindow Menu { id: fileMenu - //: File menu title: catalog.i18nc("@title:menu menubar:toplevel","&File"); MenuItem { action: Cura.Actions.open; } - Menu - { - id: recentFilesMenu; - title: catalog.i18nc("@title:menu menubar:file", "Open &Recent") - iconName: "document-open-recent"; - - enabled: Printer.recentFiles.length > 0; - - Instantiator - { - model: Printer.recentFiles - MenuItem - { - text: - { - var path = modelData.toString() - return (index + 1) + ". " + path.slice(path.lastIndexOf("/") + 1); - } - onTriggered: { - UM.MeshFileHandler.readLocalFile(modelData); - var meshName = backgroundItem.getMeshName(modelData.toString()) - backgroundItem.hasMesh(decodeURIComponent(meshName)) - } - } - onObjectAdded: recentFilesMenu.insertItem(index, object) - onObjectRemoved: recentFilesMenu.removeItem(object) - } - } + RecentFilesMenu { } MenuSeparator { } @@ -127,7 +99,6 @@ UM.MainWindow Menu { - //: Edit menu title: catalog.i18nc("@title:menu menubar:toplevel","&Edit"); MenuItem { action: Cura.Actions.undo; } @@ -143,173 +114,45 @@ UM.MainWindow MenuItem { action: Cura.Actions.unGroupObjects;} } + ViewMenu { title: catalog.i18nc("@title:menu", "&View") } + Menu { - title: catalog.i18nc("@title:menu menubar:toplevel","&View"); - id: top_view_menu - Instantiator - { - model: UM.ViewModel { } - MenuItem - { - text: model.name; - checkable: true; - checked: model.active; - exclusiveGroup: view_menu_top_group; - onTriggered: UM.Controller.setActiveView(model.id); - } - onObjectAdded: top_view_menu.insertItem(index, object) - onObjectRemoved: top_view_menu.removeItem(object) - } - ExclusiveGroup { id: view_menu_top_group; } - } - Menu - { - id: machineMenu; - //: Machine menu - title: catalog.i18nc("@title:menu menubar:toplevel","&Printer"); + id: settingsMenu + title: catalog.i18nc("@title:menu", "&Settings") + + PrinterMenu { title: catalog.i18nc("@title:menu menubar:toplevel", "&Printer") } Instantiator { - model: UM.ContainerStacksModel - { - filter: {"type": "machine"} + model: Cura.ExtrudersModel { } + Menu { + title: model.name + + NozzleMenu { title: catalog.i18nc("@title:menu", "&Nozzle"); visible: Cura.MachineManager.hasVariants } + MaterialMenu { title: catalog.i18nc("@title:menu", "&Material"); visible: Cura.MachineManager.hasMaterials } + ProfileMenu { title: catalog.i18nc("@title:menu", "&Profile"); } + + MenuSeparator { } + + MenuItem { text: "Set as Active Extruder" } } - MenuItem - { - text: model.name; - checkable: true; - checked: Cura.MachineManager.activeMachineId == model.id - exclusiveGroup: machineMenuGroup; - onTriggered: Cura.MachineManager.setActiveMachine(model.id); - } - onObjectAdded: machineMenu.insertItem(index, object) - onObjectRemoved: machineMenu.removeItem(object) + onObjectAdded: settingsMenu.insertItem(index, object) + onObjectRemoved: settingsMenu.removeItem(object) } - ExclusiveGroup { id: machineMenuGroup; } + NozzleMenu { title: catalog.i18nc("@title:menu", "&Nozzle"); visible: machineExtruderCount.properties.value <= 1 && Cura.MachineManager.hasVariants } + MaterialMenu { title: catalog.i18nc("@title:menu", "&Material"); visible: machineExtruderCount.properties.value <= 1 && Cura.MachineManager.hasMaterials } + ProfileMenu { title: catalog.i18nc("@title:menu", "&Profile"); visible: machineExtruderCount.properties.value <= 1 } MenuSeparator { } - Instantiator - { - model: UM.InstanceContainersModel - { - filter: - { - "type": "variant", - "definition": Cura.MachineManager.activeDefinitionId //Only show variants of this machine - } - } - MenuItem { - text: model.name; - checkable: true; - checked: model.id == Cura.MachineManager.activeVariantId; - exclusiveGroup: machineVariantsGroup; - onTriggered: Cura.MachineManager.setActiveVariant(model.id) - } - onObjectAdded: machineMenu.insertItem(index, object) - onObjectRemoved: machineMenu.removeItem(object) - } - - ExclusiveGroup { id: machineVariantsGroup; } - - MenuSeparator { visible: Cura.MachineManager.hasVariants; } - - MenuItem { action: Cura.Actions.addMachine; } - MenuItem { action: Cura.Actions.configureMachines; } - } - - Menu - { - id: profileMenu - title: catalog.i18nc("@title:menu menubar:toplevel", "P&rofile") - - Instantiator - { - id: profileMenuInstantiator - model: UM.InstanceContainersModel - { - filter: - { - var result = { "type": "quality" }; - if(Cura.MachineManager.filterQualityByMachine) - { - result.definition = Cura.MachineManager.activeDefinitionId; - if(Cura.MachineManager.hasMaterials) - { - result.material = Cura.MachineManager.activeMaterialId; - } - } - else - { - result.definition = "fdmprinter" - } - return result - } - } - property int separatorIndex: -1 - - Loader { - property QtObject model_data: model - property int model_index: index - sourceComponent: profileMenuItemDelegate - } - - onObjectAdded: - { - //Insert a separator between readonly and custom profiles - if(separatorIndex < 0 && index > 0) { - if(model.getItem(index-1).readOnly != model.getItem(index).readOnly) { - profileMenu.insertSeparator(index); - separatorIndex = index; - } - } - //Because of the separator, custom profiles move one index lower - profileMenu.insertItem((model.getItem(index).readOnly) ? index : index + 1, object.item); - } - onObjectRemoved: - { - //When adding a profile, the menu is rebuild by removing all items. - //If a separator was added, we need to remove that too. - if(separatorIndex >= 0) - { - profileMenu.removeItem(profileMenu.items[separatorIndex]) - separatorIndex = -1; - } - profileMenu.removeItem(object.item); - } - } - - ExclusiveGroup { id: profileMenuGroup; } - - Component - { - id: profileMenuItemDelegate - MenuItem - { - id: item - text: model_data ? model_data.name : "" - checkable: true - checked: Cura.MachineManager.activeQualityId == model_data.id - exclusiveGroup: profileMenuGroup - onTriggered: Cura.MachineManager.setActiveQuality(model_data.id) - } - } - - MenuSeparator { id: profileMenuSeparator } - - MenuItem { action: Cura.Actions.addProfile } - MenuItem { action: Cura.Actions.updateProfile } - MenuItem { action: Cura.Actions.resetProfile } - MenuSeparator { } - MenuItem { action: Cura.Actions.manageProfiles } + MenuItem { action: Cura.Actions.configureSettingVisibility } } Menu { id: extension_menu - //: Extensions menu title: catalog.i18nc("@title:menu menubar:toplevel","E&xtensions"); Instantiator @@ -343,8 +186,7 @@ UM.MainWindow Menu { - //: Settings menu - title: catalog.i18nc("@title:menu menubar:toplevel","&Settings"); + title: catalog.i18nc("@title:menu menubar:toplevel","P&references"); MenuItem { action: Cura.Actions.preferences; } } @@ -362,6 +204,16 @@ UM.MainWindow } } + UM.SettingPropertyProvider + { + id: machineExtruderCount + + containerStackId: Cura.MachineManager.activeMachineId + key: "machine_extruder_count" + watchedProperties: [ "value" ] + storeIndex: 0 + } + Item { id: contentItem; From 47079dc2dd41c810bf1532643cc58c2f9f53870d Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 28 Jun 2016 17:43:52 +0200 Subject: [PATCH 016/236] Start enabling some material management things --- resources/qml/Preferences/MaterialsPage.qml | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/resources/qml/Preferences/MaterialsPage.qml b/resources/qml/Preferences/MaterialsPage.qml index 36451e1717..cf721aa749 100644 --- a/resources/qml/Preferences/MaterialsPage.qml +++ b/resources/qml/Preferences/MaterialsPage.qml @@ -13,6 +13,7 @@ UM.ManagementPage id: base; title: catalog.i18nc("@title:tab", "Materials"); + addText: catalog.i18nc("@action:button", "Duplicate") model: UM.InstanceContainersModel { @@ -33,6 +34,8 @@ UM.ManagementPage } return result } + + sectionProperty: "brand" } activeId: Cura.MachineManager.activeMaterialId @@ -45,13 +48,19 @@ UM.ManagementPage return -1; } - addEnabled: false - removeEnabled: false - renameEnabled: false + onActivateObject: Cura.MachineManager.setActiveMaterial(currentItem.id) - scrollviewCaption: " " + activateEnabled: currentItem != null ? currentItem.id != Cura.MachineManager.activeMaterialId : false; + addEnabled: currentItem != null; + removeEnabled: currentItem != null ? !currentItem.readOnly : false; + renameEnabled: currentItem != null ? !currentItem.readOnly : false; + + scrollviewCaption: "Printer: %1, Nozzle: %2".arg(Cura.MachineManager.activeMachineName).arg(Cura.MachineManager.activeVariantName) detailsVisible: true + section.property: "section" + section.delegate: Label { text: section } + Item { UM.I18nCatalog { id: catalog; name: "cura"; } @@ -91,6 +100,7 @@ UM.ManagementPage editingEnabled: editButton.checked; properties: materialProperties + containerId: base.currentItem.id } QtObject @@ -106,9 +116,7 @@ UM.ManagementPage property color color_code: "yellow"; property real density: 0.0; - onDensityChanged: console.log(density); property real diameter: 0.0; - onDiameterChanged: console.log(diameter); property real spool_cost: 0.0; property real spool_weight: 0.0; From f7e4b91569ad31e8ed4ec4228e71a1fff9eea9d2 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 28 Jun 2016 17:44:19 +0200 Subject: [PATCH 017/236] Update styling of Material management page Contributes to CURA-342 --- resources/qml/Preferences/MaterialView.qml | 102 +++++++++++++++------ 1 file changed, 72 insertions(+), 30 deletions(-) diff --git a/resources/qml/Preferences/MaterialView.qml b/resources/qml/Preferences/MaterialView.qml index c662895584..cf6e6e38e6 100644 --- a/resources/qml/Preferences/MaterialView.qml +++ b/resources/qml/Preferences/MaterialView.qml @@ -14,19 +14,26 @@ TabView property QtObject properties; - property bool editingEnabled; - + property bool editingEnabled: false; property string currency: UM.Preferences.getValue("general/currency") ? UM.Preferences.getValue("general/currency") : "€" + property real firstColumnWidth: width * 0.45 + property real secondColumnWidth: width * 0.45 + property string containerId: "" Tab { title: "Information" + anchors + { + leftMargin: UM.Theme.getSize("default_margin").width + topMargin: UM.Theme.getSize("default_margin").height + bottomMargin: UM.Theme.getSize("default_margin").height + rightMargin: 0 + } ScrollView { anchors.fill: parent - anchors.margins: UM.Theme.getSize("default_margin").width - horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff Flow @@ -35,22 +42,19 @@ TabView width: base.width; - property real firstColumnWidth: width * 0.45 - property real secondColumnWidth: width * 0.4 - property real rowHeight: textField.height; - Label { width: parent.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Brand") } - TextField { id: textField; width: parent.secondColumnWidth; text: properties.supplier; readOnly: !base.editingEnabled; } + Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Brand") } + TextField { id: textField; width: base.secondColumnWidth; text: properties.supplier; readOnly: !base.editingEnabled; } - Label { width: parent.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Material Type") } - TextField { width: parent.secondColumnWidth; text: properties.material_type; readOnly: !base.editingEnabled; } + Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Material Type") } + TextField { width: base.secondColumnWidth; text: properties.material_type; readOnly: !base.editingEnabled; } - Label { width: parent.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Color") } + Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Color") } Row { - width: parent.secondColumnWidth; + width: base.secondColumnWidth; height: parent.rowHeight; spacing: UM.Theme.getSize("default_margin").width/2 @@ -75,10 +79,10 @@ TabView Label { width: parent.width; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: "" + catalog.i18nc("@label", "Properties") + "" } - Label { width: parent.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Density") } + Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Density") } ReadOnlySpinBox { - width: parent.secondColumnWidth; + width: base.secondColumnWidth; value: properties.density; decimals: 2 suffix: "g/cm" @@ -86,10 +90,10 @@ TabView readOnly: !base.editingEnabled; } - Label { width: parent.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Diameter") } + Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Diameter") } ReadOnlySpinBox { - width: parent.secondColumnWidth; + width: base.secondColumnWidth; value: properties.diameter; decimals: 2 suffix: "mm³" @@ -97,38 +101,38 @@ TabView readOnly: !base.editingEnabled; } - Label { width: parent.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament Cost") } + Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament Cost") } ReadOnlySpinBox { - width: parent.secondColumnWidth; + width: base.secondColumnWidth; value: properties.spool_cost; prefix: base.currency readOnly: !base.editingEnabled; } - Label { width: parent.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament weight") } + Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament weight") } ReadOnlySpinBox { - width: parent.secondColumnWidth; + width: base.secondColumnWidth; value: properties.spool_weight; suffix: "g"; stepSize: 10 readOnly: !base.editingEnabled; } - Label { width: parent.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament length") } + Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament length") } ReadOnlySpinBox { - width: parent.secondColumnWidth; + width: base.secondColumnWidth; value: parseFloat(properties.spool_length); suffix: "m"; readOnly: !base.editingEnabled; } - Label { width: parent.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Cost per Meter (Approx.)") } + Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Cost per Meter (Approx.)") } ReadOnlySpinBox { - width: parent.secondColumnWidth; + width: base.secondColumnWidth; value: parseFloat(properties.cost_per_meter); suffix: catalog.i18nc("@label", "%1/m".arg(base.currency)); readOnly: !base.editingEnabled; @@ -141,7 +145,7 @@ TabView TextArea { text: properties.description; - width: parent.firstColumnWidth + parent.secondColumnWidth + width: base.firstColumnWidth + base.secondColumnWidth wrapMode: Text.WordWrap readOnly: !base.editingEnabled; @@ -152,7 +156,7 @@ TabView TextArea { text: properties.adhesion_info; - width: parent.firstColumnWidth + parent.secondColumnWidth + width: base.firstColumnWidth + base.secondColumnWidth wrapMode: Text.WordWrap readOnly: !base.editingEnabled; @@ -164,7 +168,13 @@ TabView Tab { title: catalog.i18nc("@label", "Print settings") - anchors.margins: UM.Theme.getSize("default_margin").height + anchors + { + leftMargin: UM.Theme.getSize("default_margin").width + topMargin: UM.Theme.getSize("default_margin").height + bottomMargin: UM.Theme.getSize("default_margin").height + rightMargin: 0 + } ScrollView { @@ -175,11 +185,43 @@ TabView model: UM.SettingDefinitionsModel { containerId: Cura.MachineManager.activeDefinitionId - visibilityHandler: UM.SettingPreferenceVisibilityHandler { } + visibilityHandler: Cura.MaterialSettingsVisibilityHandler { } expanded: ["*"] } - delegate: Label { text: model.label } + delegate: UM.TooltipArea + { + width: childrenRect.width + height: childrenRect.height + text: model.description + Label + { + id: label + width: base.firstColumnWidth; + height: spinBox.height + text: model.label + } + ReadOnlySpinBox + { + id: spinBox + anchors.left: label.right + value: parseFloat(provider.properties.value); + width: base.secondColumnWidth; + readOnly: !base.editingEnabled + suffix: model.unit + maximumValue: 99999 + decimals: model.unit == "mm" ? 2 : 0 + } + + UM.SettingPropertyProvider + { + id: provider + + containerStackId: Cura.MachineManager.activeMachineId + key: model.key + watchedProperties: [ "value" ] + } + } } } } From 911f80f2378dd57531a5be43b919496e90878cd4 Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Wed, 29 Jun 2016 14:52:39 +0200 Subject: [PATCH 018/236] JSON refactor: OCD told me to mode the 'label' property to the top of each machine setting --- resources/definitions/fdmprinter.def.json | 44 +++++++++++------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 4d0a235ce5..e6686f38fd 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -29,19 +29,19 @@ { "machine_show_variants": { + "label": "Show machine variants", "description": "Whether to show the different variants of this machine, which are described in separate json files.", "default_value": false, "type": "bool", - "label": "Show machine variants", "settable_per_mesh": false, "settable_per_extruder": false, "settable_per_meshgroup": false }, "machine_start_gcode": { + "label": "Start GCode", "description": "Gcode commands to be executed at the very start - separated by \\n.", "default_value": "G28 ;Home\nG1 Z15.0 F6000 ;Move the platform down 15mm\n;Prime the extruder\nG92 E0\nG1 F200 E3\nG92 E0", - "label": "Start GCode", "type": "str", "settable_per_mesh": false, "settable_per_extruder": false, @@ -49,9 +49,9 @@ }, "machine_end_gcode": { + "label": "End GCode", "description": "Gcode commands to be executed at the very end - separated by \\n.", "default_value": "M104 S0\nM140 S0\n;Retract the filament\nG92 E1\nG1 E-1 F300\nG28 X0 Y0\nM84", - "label": "End GCode", "type": "str", "settable_per_mesh": false, "settable_per_extruder": false, @@ -59,8 +59,8 @@ }, "material_bed_temp_wait": { - "description": "Whether to insert a command to wait until the bed temperature is reached at the start.", "label": "Wait for bed heatup", + "description": "Whether to insert a command to wait until the bed temperature is reached at the start.", "default_value": true, "type": "bool", "settable_per_mesh": false, @@ -69,49 +69,49 @@ }, "material_print_temp_prepend": { + "label": "Wait for material heatup", "description": "Whether to include nozzle temperature commands at the start of the gcode. When the start_gcode already contains nozzle temperature commands Cura frontend will automatically disable this setting.", "default_value": true, "type": "bool", - "label": "Wait for material heatup", "settable_per_mesh": false, "settable_per_extruder": false, "settable_per_meshgroup": false }, "machine_width": { + "label": "Machine width", "description": "The width (X-direction) of the printable area.", "default_value": 100, "type": "float", - "label": "Machine width", "settable_per_mesh": false, "settable_per_extruder": false, "settable_per_meshgroup": false }, "machine_depth": { + "label": "Machine depth", "description": "The depth (Y-direction) of the printable area.", "default_value": 100, "type": "float", - "label": "Machine depth", "settable_per_mesh": false, "settable_per_extruder": false, "settable_per_meshgroup": false }, "machine_height": { + "label": "Machine height", "description": "The height (Z-direction) of the printable area.", "default_value": 100, "type": "float", - "label": "Machine height", "settable_per_mesh": false, "settable_per_extruder": false, "settable_per_meshgroup": false }, "machine_heated_bed": { + "label": "Has heated bed", "description": "Whether the machine has a heated bed present.", "default_value": false, - "label": "Has heated bed", "type": "bool", "settable_per_mesh": false, "settable_per_extruder": false, @@ -119,28 +119,28 @@ }, "machine_center_is_zero": { + "label": "Is center origin", "description": "Whether the X/Y coordinates of the zero position of the printer is at the center of the printable area.", "default_value": false, "type": "bool", - "label": "Is center origin", "settable_per_mesh": false, "settable_per_extruder": false, "settable_per_meshgroup": false }, "machine_extruder_count": { + "label": "Number extruders", "description": "Number of extruder trains. An extruder train is the combination of a feeder, bowden tube, and nozzle.", "default_value": 1, "type": "int", - "label": "Number extruders", "settable_per_mesh": false, "settable_per_extruder": false, "settable_per_meshgroup": false }, "machine_nozzle_tip_outer_diameter": { - "description": "The outer diameter of the tip of the nozzle.", "label": "Outer nozzle diameter", + "description": "The outer diameter of the tip of the nozzle.", "default_value": 1, "type": "float", "settable_per_mesh": false, @@ -150,74 +150,75 @@ }, "machine_nozzle_head_distance": { + "label": "Nozzle length", "description": "The height difference between the tip of the nozzle and the lowest part of the print head.", "default_value": 3, "type": "float", - "label": "Nozzle length", "settable_per_mesh": false, "settable_per_extruder": false, "settable_per_meshgroup": false }, "machine_nozzle_expansion_angle": { + "label": "Nozzle angle", "description": "The angle between the horizontal plane and the conical part right above the tip of the nozzle.", "default_value": 45, "type": "int", - "label": "Nozzle angle", "settable_per_mesh": false, "settable_per_extruder": false, "settable_per_meshgroup": false }, "machine_heat_zone_length": { + "label": "Heat zone length", "description": "The distance from the tip of the nozzle in which heat from the nozzle is transfered to the filament.", "default_value": 16, "type": "float", - "label": "Heat zone length", "settable_per_mesh": false, "settable_per_extruder": true, "settable_per_meshgroup": false }, "machine_nozzle_heat_up_speed": { + "label": "Heat up speed", "description": "The speed (°C/s) by which the nozzle heats up averaged over the window of normal printing temperatures and the standby temperature.", "default_value": 2.0, "type": "float", - "label": "Heat up speed", "settable_per_mesh": false, "settable_per_extruder": true }, "machine_nozzle_cool_down_speed": { + "label": "Cool down speed", "description": "The speed (°C/s) by which the nozzle cools down averaged over the window of normal printing temperatures and the standby temperature.", "default_value": 2.0, "type": "float", - "label": "Cool down speed", "settable_per_mesh": false, "settable_per_extruder": true }, "machine_gcode_flavor": { + "label": "Gcode flavour", "description": "The type of gcode to be generated.", "default_value": "RepRap", "type": "str", - "label": "Gcode flavour", "settable_per_mesh": false, "settable_per_extruder": false, "settable_per_meshgroup": false }, "machine_disallowed_areas": { + "label": "Disallowed areas", "description": "A list of polygons with areas the print head is not allowed to enter.", "type": "polygons", "default_value": [], - "label": "Disallowed areas", "settable_per_mesh": false, "settable_per_extruder": false, "settable_per_meshgroup": false }, "machine_head_polygon": { + "label": "Machine head polygon", "description": "A 2D silhouette of the print head (fan caps excluded).", "type": "polygon", "default_value": @@ -239,13 +240,13 @@ 1 ] ], - "label": "Machine head polygon", "settable_per_mesh": false, "settable_per_extruder": false, "settable_per_meshgroup": false }, "machine_head_with_fans_polygon": { + "label": "Machine head & Fan polygon", "description": "A 2D silhouette of the print head (fan caps included).", "type": "polygon", "default_value": @@ -267,16 +268,15 @@ -10 ] ], - "label": "Machine head & Fan polygon", "settable_per_mesh": false, "settable_per_extruder": false, "settable_per_meshgroup": false }, "gantry_height": { + "label": "Gantry height", "description": "The height difference between the tip of the nozzle and the gantry system (X and Y axes).", "default_value": 99999999999, - "label": "Gantry height", "type": "float", "settable_per_mesh": false, "settable_per_extruder": false, From 48852381037af33ec8e6dd9860b626fa9ef72f03 Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Wed, 29 Jun 2016 14:53:13 +0200 Subject: [PATCH 019/236] JSON feat: machine_min_cool_heat_time_window (CURA-1783) --- resources/definitions/fdmprinter.def.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index e6686f38fd..d187dc66be 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -183,6 +183,7 @@ "label": "Heat up speed", "description": "The speed (°C/s) by which the nozzle heats up averaged over the window of normal printing temperatures and the standby temperature.", "default_value": 2.0, + "unit": "°C/s", "type": "float", "settable_per_mesh": false, "settable_per_extruder": true @@ -192,6 +193,17 @@ "label": "Cool down speed", "description": "The speed (°C/s) by which the nozzle cools down averaged over the window of normal printing temperatures and the standby temperature.", "default_value": 2.0, + "unit": "°C/s", + "type": "float", + "settable_per_mesh": false, + "settable_per_extruder": true + }, + "machine_min_cool_heat_time_window": + { + "label": "Minimal Time Standby Temperature", + "description": "The minimal time an extruder has to be inactive before the nozzle is cooled. Only when an extruder is not used for longer than this time will it be allowed to cool down to the standby temperature.", + "default_value": 50.0, + "unit": "s", "type": "float", "settable_per_mesh": false, "settable_per_extruder": true From cd8bc3b60d79125de0afc3592a2b5351bfc4ff59 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 30 Jun 2016 01:53:48 +0200 Subject: [PATCH 020/236] Introduce a ContainerManager class to handle container actions like duplicate Because MachineManager is getting rather large Contributes to CURA-342 --- cura/ContainerManager.py | 34 ++++++++++++++++++++++++++++++++++ cura/CuraApplication.py | 3 +++ 2 files changed, 37 insertions(+) create mode 100644 cura/ContainerManager.py diff --git a/cura/ContainerManager.py b/cura/ContainerManager.py new file mode 100644 index 0000000000..9639dd1064 --- /dev/null +++ b/cura/ContainerManager.py @@ -0,0 +1,34 @@ +# Copyright (c) 2016 Ultimaker B.V. +# Cura is released under the terms of the AGPLv3 or higher. + +from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal + +import UM.Settings + +from UM.i18n import i18nCatalog +catalog = i18nCatalog("cura") + +class ContainerManager(QObject): + def __init__(self, parent = None): + super().__init__(parent) + + @pyqtSlot(str) + def duplicateContainer(self, container_id): + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = container_id) + if not containers: + return + + new_name = UM.Settings.ContainerRegistry.getInstance().uniqueName(containers[0].getName()) + new_material = containers[0].duplicate(new_name) + UM.Settings.ContainerRegistry.getInstance().addContainer(new_material) + + @pyqtSlot(str, str) + def renameContainer(self, container_id, new_name): + pass + + @pyqtSlot(str) + def removeContainer(self, container_id): + pass + +def createContainerManager(engine, js_engine): + return ContainerManager() diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 9970f0efce..420ae276e9 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -45,6 +45,7 @@ from . import CuraSplashScreen from . import MachineManagerModel from . import ContainerSettingsModel from . import MachineActionManager +from . import ContainerManager import cura.Settings @@ -428,6 +429,8 @@ class CuraApplication(QtApplication): qmlRegisterType(ContainerSettingsModel.ContainerSettingsModel, "Cura", 1, 0, "ContainerSettingsModel") qmlRegisterType(cura.Settings.MaterialSettingsVisibilityHandler, "Cura", 1, 0, "MaterialSettingsVisibilityHandler") + qmlRegisterSingletonType(ContainerManager.ContainerManager, "Cura", 1, 0, "ContainerManager", ContainerManager.createContainerManager) + qmlRegisterSingletonType(QUrl.fromLocalFile(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Actions.qml")), "Cura", 1, 0, "Actions") engine.rootContext().setContextProperty("ExtruderManager", ExtruderManager.ExtruderManager.getInstance()) From 3798a6e26a7904b95e0d1a43b8d8951bdff7ae21 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 30 Jun 2016 01:54:25 +0200 Subject: [PATCH 021/236] Update MaterialsPage with the new way of handling buttons Contributes to CURA-342 --- resources/qml/Preferences/MaterialsPage.qml | 39 ++++++++++++++++----- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/resources/qml/Preferences/MaterialsPage.qml b/resources/qml/Preferences/MaterialsPage.qml index cf721aa749..797b698cd1 100644 --- a/resources/qml/Preferences/MaterialsPage.qml +++ b/resources/qml/Preferences/MaterialsPage.qml @@ -13,7 +13,6 @@ UM.ManagementPage id: base; title: catalog.i18nc("@title:tab", "Materials"); - addText: catalog.i18nc("@action:button", "Duplicate") model: UM.InstanceContainersModel { @@ -48,19 +47,43 @@ UM.ManagementPage return -1; } - onActivateObject: Cura.MachineManager.setActiveMaterial(currentItem.id) - - activateEnabled: currentItem != null ? currentItem.id != Cura.MachineManager.activeMaterialId : false; - addEnabled: currentItem != null; - removeEnabled: currentItem != null ? !currentItem.readOnly : false; - renameEnabled: currentItem != null ? !currentItem.readOnly : false; - scrollviewCaption: "Printer: %1, Nozzle: %2".arg(Cura.MachineManager.activeMachineName).arg(Cura.MachineManager.activeVariantName) detailsVisible: true section.property: "section" section.delegate: Label { text: section } + buttons: [ + Button + { + text: catalog.i18nc("@action:button", "Activate"); + iconName: "list-activate"; + enabled: base.currentItem != null && base.currentItem.id != Cura.MachineManager.activeMaterialId + onClicked: Cura.MachineManager.setActiveMaterial(base.currentItem.id) + }, + Button + { + text: catalog.i18nc("@action:button", "Duplicate"); + iconName: "list-add"; + enabled: base.currentItem + onClicked: Cura.ContainerManager.duplicateContainer(base.currentItem.id) + }, + Button + { + text: catalog.i18nc("@action:button", "Remove"); + iconName: "list-remove"; + enabled: base.currentItem && !base.currentItem.readOnly +// onClicked: Cura.ContainerManager.removeContainer() + }, + Button + { + text: catalog.i18nc("@action:button", "Rename"); + iconName: "edit-rename"; + enabled: base.currentItem && !base.currentItem.readOnly +// onClicked: Cura.ContainerManager.renameContainer() + } + ] + Item { UM.I18nCatalog { id: catalog; name: "cura"; } From 419326132fd81e168d4e9affb827cd9ac9fcc9d1 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 30 Jun 2016 01:54:58 +0200 Subject: [PATCH 022/236] Use ContainerPropertyProvider to provide properties for the materials page Contributes to CURA-342 --- resources/qml/Preferences/MaterialView.qml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/resources/qml/Preferences/MaterialView.qml b/resources/qml/Preferences/MaterialView.qml index cf6e6e38e6..94aa2ca5d6 100644 --- a/resources/qml/Preferences/MaterialView.qml +++ b/resources/qml/Preferences/MaterialView.qml @@ -211,16 +211,11 @@ TabView suffix: model.unit maximumValue: 99999 decimals: model.unit == "mm" ? 2 : 0 + + onEditingFinished: provider.setPropertyValue("value", value) } - UM.SettingPropertyProvider - { - id: provider - - containerStackId: Cura.MachineManager.activeMachineId - key: model.key - watchedProperties: [ "value" ] - } + UM.ContainerPropertyProvider { id: provider; containerId: base.containerId; watchedProperties: [ "value" ]; key: model.key } } } } From ee0160075e3d392f5414a584fb9305cd2fb4730c Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 30 Jun 2016 01:55:53 +0200 Subject: [PATCH 023/236] Override InstanceContainer::duplicate in XmlMAterialProfile So that we can set a new GUID for the material on duplicate Contributes to CURA-342 --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index a1a992334e..353c02e2ac 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -5,6 +5,7 @@ import math import copy import io import xml.etree.ElementTree as ET +import uuid from UM.Logger import Logger @@ -22,6 +23,11 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): def __init__(self, container_id, *args, **kwargs): super().__init__(container_id, *args, **kwargs) + def duplicate(self, new_id, new_name = None): + result = super().duplicate(self.getMetaDataEntry("brand") + "_" + new_id, new_name) + result.setMetaDataEntry("GUID", str(uuid.uuid4())) + return result + def serialize(self): if self.getDefinition().id != "fdmprinter": # Since we create an instance of XmlMaterialProfile for each machine and nozzle in the profile, From 26f7ba0a74647658520f4fcb63be713e59606a9b Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 30 Jun 2016 01:57:58 +0200 Subject: [PATCH 024/236] Fix up XmlMaterialProfile::serialize so we can properly serialize duplicated materials We now use GUID to look up all containers belonging to a material. In addition, we handle the multiple containers better Contributes to CURA-342 --- .../XmlMaterialProfile/XmlMaterialProfile.py | 103 ++++++++++++------ 1 file changed, 69 insertions(+), 34 deletions(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 353c02e2ac..53659f14bb 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -28,8 +28,25 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): result.setMetaDataEntry("GUID", str(uuid.uuid4())) return result + def setProperty(self, key, property_name, property_value, container = None): + super().setProperty(key, property_name, property_value) + + for container in UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(GUID = self.getMetaDataEntry("GUID")): + container._dirty = True + def serialize(self): - if self.getDefinition().id != "fdmprinter": + registry = UM.Settings.ContainerRegistry.getInstance() + + all_containers = registry.findInstanceContainers(GUID = self.getMetaDataEntry("GUID")) + most_generic = all_containers[0] + for container in all_containers: + # Find the "most generic" version of this material + # This is a bit of a nasty implementation because of the naive assumption that anything with a shorter + # id is more generic. It holds for the current implementation though. + if len(most_generic.id) > len(container.id): + most_generic = container + + if most_generic and self.id != most_generic.id: # Since we create an instance of XmlMaterialProfile for each machine and nozzle in the profile, # we should only serialize the "base" material definition, since that can then take care of # serializing the machine/nozzle specific profiles. @@ -91,54 +108,63 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): ## Begin Settings Block builder.start("settings") - for instance in self.findInstances(): - builder.start("setting", { "key": UM.Dictionary.findKey(self.__material_property_setting_map, instance.definition.key) }) - builder.data(str(instance.value)) - builder.end("setting") + if self.getDefinition().id == "fdmprinter": + for instance in self.findInstances(): + self._addSettingElement(builder, instance) - # Find all machine sub-profiles corresponding to this material and add them to this profile. - machines = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = self.getId() + "_*") - for machine in machines: - if machine.getMetaDataEntry("variant"): - # Since the list includes variant-specific containers but we do not yet want to add those, we just skip them. + machine_container_map = {} + machine_nozzle_map = {} + + for container in all_containers: + definition_id = container.getDefinition().id + if definition_id == "fdmprinter": + continue + + if definition_id not in machine_container_map: + machine_container_map[definition_id] = container + elif len(container.id) < len(machine_container_map[definition_id].id): + machine_container_map[definition_id] = container + + variant = container.getMetaDataEntry("variant") + if variant: + if variant not in machine_nozzle_map: + machine_nozzle_map[definition_id] = [] + machine_nozzle_map[definition_id].append(container) + + for definition_id, container in machine_container_map.items(): + definition = container.getDefinition() + try: + product = UM.Dictionary.findKey(self.__product_id_map, definition_id) + except ValueError: continue builder.start("machine") - - definition = machine.getDefinition() - builder.start("machine_identifier", { "manufacturer": definition.getMetaDataEntry("manufacturer", ""), "product": UM.Dictionary.findKey(self.__product_id_map, definition.id) }) + builder.start("machine_identifier", { "manufacturer": definition.getMetaDataEntry("manufacturer", ""), "product": product}) builder.end("machine_identifier") - for instance in machine.findInstances(): - if self.getInstance(instance.definition.key) and self.getProperty(instance.definition.key, "value") == instance.value: + for instance in container.findInstances(): + if self.getDefinition().id == "fdmprinter" and self.getInstance(instance.definition.key) and self.getProperty(instance.definition.key, "value") == instance.value: # If the settings match that of the base profile, just skip since we inherit the base profile. continue - builder.start("setting", { "key": UM.Dictionary.findKey(self.__material_property_setting_map, instance.definition.key) }) - builder.data(str(instance.value)) - builder.end("setting") + self._addSettingElement(builder, instance) # Find all hotend sub-profiles corresponding to this material and machine and add them to this profile. - hotends = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = machine.getId() + "_*") - for hotend in hotends: - variant_containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = hotend.getMetaDataEntry("variant")) - if variant_containers: - builder.start("hotend", { "id": variant_containers[0].getName() }) + for hotend in machine_nozzle_map[definition_id]: + variant_containers = registry.findInstanceContainers(id = hotend.getMetaDataEntry("variant")) + if not variant_containers: + continue - for instance in hotend.findInstances(): - if self.getInstance(instance.definition.key) and self.getProperty(instance.definition.key, "value") == instance.value: - # If the settings match that of the base profile, just skip since we inherit the base profile. - continue + builder.start("hotend", { "id": variant_containers[0].getName() }) - if machine.getInstance(instance.definition.key) and machine.getProperty(instance.definition.key, "value") == instance.value: - # If the settings match that of the machine profile, just skip since we inherit the machine profile. - continue + for instance in hotend.findInstances(): + if container.getInstance(instance.definition.key) and container.getProperty(instance.definition.key, "value") == instance.value: + # If the settings match that of the machine profile, just skip since we inherit the machine profile. + continue - builder.start("setting", { "key": UM.Dictionary.findKey(self.__material_property_setting_map, instance.definition.key) }) - builder.data(str(instance.value)) - builder.end("setting") + self._addSettingElement(builder, instance) - builder.end("hotend") + builder.end("hotend") builder.end("machine") @@ -297,6 +323,15 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): new_hotend_material._dirty = False UM.Settings.ContainerRegistry.getInstance().addContainer(new_hotend_material) + def _addSettingElement(self, builder, instance): + try: + key = UM.Dictionary.findKey(self.__material_property_setting_map, instance.definition.key) + except ValueError: + return + + builder.start("setting", { "key": key }) + builder.data(str(instance.value)) + builder.end("setting") # Map XML file setting names to internal names __material_property_setting_map = { From 551dc7d757b7256f4843666a134a9e0a1f8217de Mon Sep 17 00:00:00 2001 From: Simon Edwards Date: Thu, 30 Jun 2016 09:32:46 +0200 Subject: [PATCH 025/236] Fixes for the problem where duplicated objects were broken, missing hull 'shadows' or would fly off the build plate. Contributes to CURA-1504 Improve performance/simplicity by using Convex Hulls in Scene Graph --- cura/CuraApplication.py | 4 ++-- cura/PlatformPhysics.py | 24 +++++++++--------------- cura/ZOffsetDecorator.py | 3 +++ 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 9d068d1214..de4c6e0681 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -546,12 +546,12 @@ class CuraApplication(QtApplication): for _ in range(count): if node.getParent() and node.getParent().callDecoration("isGroup"): new_node = copy.deepcopy(node.getParent()) #Copy the group node. - new_node.callDecoration("setConvexHull",None) + new_node.callDecoration("recomputeConvexHull") op.addOperation(AddSceneNodeOperation(new_node,node.getParent().getParent())) else: new_node = copy.deepcopy(node) - new_node.callDecoration("setConvexHull", None) + new_node.callDecoration("recomputeConvexHull") op.addOperation(AddSceneNodeOperation(new_node, node.getParent())) op.push() diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py index c43d0d09d7..e4844baf31 100644 --- a/cura/PlatformPhysics.py +++ b/cura/PlatformPhysics.py @@ -7,7 +7,6 @@ from UM.Scene.SceneNode import SceneNode from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator from UM.Math.Vector import Vector from UM.Math.AxisAlignedBox import AxisAlignedBox -from UM.Application import Application from UM.Scene.Selection import Selection from UM.Preferences import Preferences @@ -16,8 +15,6 @@ from cura.ConvexHullDecorator import ConvexHullDecorator from . import PlatformPhysicsOperation from . import ZOffsetDecorator -import copy - class PlatformPhysics: def __init__(self, controller, volume): super().__init__() @@ -100,18 +97,15 @@ class PlatformPhysics: # continue # Get the overlap distance for both convex hulls. If this returns None, there is no intersection. - try: - head_hull = node.callDecoration("getConvexHullHead") - if head_hull: - overlap = head_hull.intersectsPolygon(other_node.callDecoration("getConvexHull")) - if not overlap: - other_head_hull = other_node.callDecoration("getConvexHullHead") - if other_head_hull: - overlap = node.callDecoration("getConvexHull").intersectsPolygon(other_head_hull) - else: - overlap = node.callDecoration("getConvexHull").intersectsPolygon(other_node.callDecoration("getConvexHull")) - except: - overlap = None #It can sometimes occur that the calculated convex hull has no size, in which case there is no overlap. + head_hull = node.callDecoration("getConvexHullHead") + if head_hull: + overlap = head_hull.intersectsPolygon(other_node.callDecoration("getConvexHull")) + if not overlap: + other_head_hull = other_node.callDecoration("getConvexHullHead") + if other_head_hull: + overlap = node.callDecoration("getConvexHull").intersectsPolygon(other_head_hull) + else: + overlap = node.callDecoration("getConvexHull").intersectsPolygon(other_node.callDecoration("getConvexHull")) if overlap is None: continue diff --git a/cura/ZOffsetDecorator.py b/cura/ZOffsetDecorator.py index 5c3c9e219b..c2912454de 100644 --- a/cura/ZOffsetDecorator.py +++ b/cura/ZOffsetDecorator.py @@ -10,3 +10,6 @@ class ZOffsetDecorator(SceneNodeDecorator): def getZOffset(self): return self._z_offset + + def __deepcopy__(self, memo): + return ZOffsetDecorator() From 7d962e97c4da17f6587869adfeb08ed2ac589510 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 30 Jun 2016 10:18:10 +0200 Subject: [PATCH 026/236] Change monitor progress to percentages CURA-1036 --- resources/qml/MonitorButton.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/MonitorButton.qml b/resources/qml/MonitorButton.qml index ca6b216a0a..e52fd53dca 100644 --- a/resources/qml/MonitorButton.qml +++ b/resources/qml/MonitorButton.qml @@ -65,7 +65,7 @@ Rectangle color: printerConnected ? Cura.MachineManager.printerOutputDevices[0].jobState == "paused" ? UM.Theme.getColor("status_paused") : UM.Theme.getColor("status_ready") : UM.Theme.getColor("status_offline") font: UM.Theme.getFont("large") - text: Math.round(progress * 100) + "%"; + text: Math.round(progress) + "%"; visible: printerConnected } @@ -83,7 +83,7 @@ Rectangle Rectangle { - width: Math.max(parent.width * base.progress) + width: Math.max(parent.width * base.progress / 100) height: parent.height color: printerConnected ? Cura.MachineManager.printerOutputDevices[0].jobState == "paused" ? UM.Theme.getColor("status_paused") : UM.Theme.getColor("status_ready") : UM.Theme.getColor("status_offline") radius: UM.Theme.getSize("progressbar_radius").width From 7ec605579e86871def6fa4dabd4d5501eeef0c13 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 30 Jun 2016 10:20:19 +0200 Subject: [PATCH 027/236] Introduce printing/busy status and make monitor i18n-aware CURA-1036 --- resources/qml/MonitorButton.qml | 28 +++++++++++++++++++++------- resources/qml/Sidebar.qml | 10 ++++------ 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/resources/qml/MonitorButton.qml b/resources/qml/MonitorButton.qml index e52fd53dca..efffd890a1 100644 --- a/resources/qml/MonitorButton.qml +++ b/resources/qml/MonitorButton.qml @@ -18,6 +18,20 @@ Rectangle property real progress: printerConnected ? Cura.MachineManager.printerOutputDevices[0].progress : 0; property int backendState: UM.Backend.state; + property variant statusColor: + { + if(!printerConnected) + return UM.Theme.getColor("status_offline") + else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing") + return UM.Theme.getColor("status_busy") + else if(Cura.MachineManager.printerOutputDevices[0].jobState == "ready") + return UM.Theme.getColor("status_ready") + else if(Cura.MachineManager.printerOutputDevices[0].jobState == "paused") + return UM.Theme.getColor("status_paused") + else if (Cura.MachineManager.printerOutputDevices[0].jobState != "error") + return UM.Theme.getColor("status_error") + } + property bool activity: Printer.getPlatformActivity; property int totalHeight: childrenRect.height + UM.Theme.getSize("default_margin").height property string fileBaseName @@ -25,17 +39,17 @@ Rectangle { if(!printerConnected) { - return "Please check your printer connections" + return catalog.i18nc("@label:", "Please check your printer connections") } else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing") { - return "Printing..." + return catalog.i18nc("@label:", "Printing...") } else if(Cura.MachineManager.printerOutputDevices[0].jobState == "paused") { - return "Paused" + return catalog.i18nc("@label:", "Paused") } else if(Cura.MachineManager.printerOutputDevices[0].jobState == "pre_print") { - return "Preparing..." + return catalog.i18nc("@label:", "Preparing...") } else { @@ -52,7 +66,7 @@ Rectangle anchors.left: parent.left anchors.leftMargin: UM.Theme.getSize("default_margin").width - color: printerConnected ? Cura.MachineManager.printerOutputDevices[0].jobState == "paused" ? UM.Theme.getColor("status_paused") : UM.Theme.getColor("status_ready") : UM.Theme.getColor("status_offline") + color: base.statusColor font: UM.Theme.getFont("large") text: statusText; } @@ -63,7 +77,7 @@ Rectangle anchors.top: parent.top anchors.right: progressBar.right - color: printerConnected ? Cura.MachineManager.printerOutputDevices[0].jobState == "paused" ? UM.Theme.getColor("status_paused") : UM.Theme.getColor("status_ready") : UM.Theme.getColor("status_offline") + color: base.statusColor font: UM.Theme.getFont("large") text: Math.round(progress) + "%"; visible: printerConnected @@ -85,7 +99,7 @@ Rectangle { width: Math.max(parent.width * base.progress / 100) height: parent.height - color: printerConnected ? Cura.MachineManager.printerOutputDevices[0].jobState == "paused" ? UM.Theme.getColor("status_paused") : UM.Theme.getColor("status_ready") : UM.Theme.getColor("status_offline") + color: base.statusColor radius: UM.Theme.getSize("progressbar_radius").width } } diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index e1516214f1..822fdf8ca1 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -95,15 +95,13 @@ Rectangle onClicked: monitoringPrint = true iconSource: { if(!printerConnected) - { return UM.Theme.getIcon("tab_monitor") - } else if(Cura.MachineManager.printerOutputDevices[0].jobState == "paused") - { + else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing") + return UM.Theme.getIcon("tab_monitor_busy") + else if(Cura.MachineManager.printerOutputDevices[0].jobState == "paused") return UM.Theme.getIcon("tab_monitor_paused") - } else if (Cura.MachineManager.printerOutputDevices[0].jobState != "error") - { + else if (Cura.MachineManager.printerOutputDevices[0].jobState != "error") return UM.Theme.getIcon("tab_monitor_connected") - } } checkable: true exclusiveGroup: sidebarHeaderBarGroup From 3ea4983a23623f1a5318269222c138af3f48c153 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 30 Jun 2016 11:02:15 +0200 Subject: [PATCH 028/236] Move message stack "above" the viewport overlay CURA-1036 --- resources/qml/Cura.qml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 2b9746cb28..e475007a5d 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -407,17 +407,6 @@ UM.MainWindow } } - UM.MessageStack - { - anchors - { - horizontalCenter: parent.horizontalCenter - horizontalCenterOffset: -(UM.Theme.getSize("sidebar").width/ 2) - top: parent.verticalCenter; - bottom: parent.bottom; - } - } - Loader { id: view_panel @@ -576,6 +565,17 @@ UM.MainWindow visible: base.monitoringPrint source: Cura.MachineManager.printerOutputDevices.length > 0 ? Cura.MachineManager.printerOutputDevices[0].cameraImage : "" } + + UM.MessageStack + { + anchors + { + horizontalCenter: parent.horizontalCenter + horizontalCenterOffset: -(UM.Theme.getSize("sidebar").width/ 2) + top: parent.verticalCenter; + bottom: parent.bottom; + } + } } } From dc2384c92d7327df9116ed63e2246cca7d01f1f6 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 30 Jun 2016 11:15:32 +0200 Subject: [PATCH 029/236] Disable monitor buttons when there is no job running CURA-1036 --- resources/qml/MonitorButton.qml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/resources/qml/MonitorButton.qml b/resources/qml/MonitorButton.qml index efffd890a1..10fc44b3f0 100644 --- a/resources/qml/MonitorButton.qml +++ b/resources/qml/MonitorButton.qml @@ -109,8 +109,9 @@ Rectangle id: abortButton visible: printerConnected - height: UM.Theme.getSize("save_button_save_to_button").height + enabled: Cura.MachineManager.printerOutputDevices[0].jobState == "paused" || Cura.MachineManager.printerOutputDevices[0].jobState == "printing" + height: UM.Theme.getSize("save_button_save_to_button").height anchors.top: progressBar.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.right: parent.right @@ -154,9 +155,10 @@ Rectangle { id: pauseButton - height: UM.Theme.getSize("save_button_save_to_button").height visible: printerConnected + enabled: Cura.MachineManager.printerOutputDevices[0].jobState == "paused" || Cura.MachineManager.printerOutputDevices[0].jobState == "printing" + height: UM.Theme.getSize("save_button_save_to_button").height anchors.top: progressBar.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.right: abortButton.left From 343280103f130bd72e48b9b1348844c8eb4f9d11 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 30 Jun 2016 15:07:17 +0200 Subject: [PATCH 030/236] Fix GUID of materials Contributes to CURA-342 --- resources/materials/generic_abs.xml.fdm_material | 2 +- resources/materials/generic_cpe.xml.fdm_material | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/materials/generic_abs.xml.fdm_material b/resources/materials/generic_abs.xml.fdm_material index 654b06d221..82b2f1f963 100644 --- a/resources/materials/generic_abs.xml.fdm_material +++ b/resources/materials/generic_abs.xml.fdm_material @@ -9,7 +9,7 @@ Generic PLA profile. Serves as an example file, data in this file is not correct ABS Generic - 506c9f0d-e3aa-4bd4-b2d2-23e2425b1aa9 + 60636bb4-518f-42e7-8237-fe77b194ebe0 0 #FF0000 diff --git a/resources/materials/generic_cpe.xml.fdm_material b/resources/materials/generic_cpe.xml.fdm_material index bbe6e328d2..8ac4dd8c71 100644 --- a/resources/materials/generic_cpe.xml.fdm_material +++ b/resources/materials/generic_cpe.xml.fdm_material @@ -9,7 +9,7 @@ Generic PLA profile. Serves as an example file, data in this file is not correct CPE Generic - 506c9f0d-e3aa-4bd4-b2d2-23e2425b1aa9 + 12f41353-1a33-415e-8b4f-a775a6c70cc6 0 #0000FF From 8badb061da85a96def826e902e3af8d23043711d Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 30 Jun 2016 15:08:00 +0200 Subject: [PATCH 031/236] Check for definition_id, not variant name Since the dict is supposed to be per definition Contributes to CURA-342 --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 53659f14bb..b1b065db70 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -127,7 +127,7 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): variant = container.getMetaDataEntry("variant") if variant: - if variant not in machine_nozzle_map: + if definition_id not in machine_nozzle_map: machine_nozzle_map[definition_id] = [] machine_nozzle_map[definition_id].append(container) From 92d3bea2290a6b76c33195c0c5a39186621812c4 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 30 Jun 2016 17:31:19 +0200 Subject: [PATCH 032/236] Add a setContainerMetaDataEntry to ContainerManager So that we can set metadata from QML Contributes to CURA-342 --- cura/ContainerManager.py | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/cura/ContainerManager.py b/cura/ContainerManager.py index 9639dd1064..b2b8e36770 100644 --- a/cura/ContainerManager.py +++ b/cura/ContainerManager.py @@ -12,15 +12,17 @@ class ContainerManager(QObject): def __init__(self, parent = None): super().__init__(parent) + self._registry = UM.Settings.ContainerRegistry.getInstance() + @pyqtSlot(str) def duplicateContainer(self, container_id): - containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = container_id) + containers = self._registry.findInstanceContainers(id = container_id) if not containers: return - new_name = UM.Settings.ContainerRegistry.getInstance().uniqueName(containers[0].getName()) + new_name = self_registry.uniqueName(containers[0].getName()) new_material = containers[0].duplicate(new_name) - UM.Settings.ContainerRegistry.getInstance().addContainer(new_material) + self._registry.addContainer(new_material) @pyqtSlot(str, str) def renameContainer(self, container_id, new_name): @@ -30,5 +32,31 @@ class ContainerManager(QObject): def removeContainer(self, container_id): pass + @pyqtSlot(str, str, str) + def setContainerMetaDataEntry(self, container_id, entry_name, entry_value): + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = container_id) + if not containers: + return + + container = containers[0] + + entries = entry_name.split("/") + entry_name = entries.pop() + + if entries: + root_name = entries.pop(0) + root = container.getMetaDataEntry(root_name) + + item = root + for entry in entries: + item = item.get(entries.pop(0), { }) + + item[entry_name] = entry_value + + entry_name = root_name + entry_value = root + + containers[0].setMetaDataEntry(entry_name, entry_value) + def createContainerManager(engine, js_engine): return ContainerManager() From ded4c89cb5b193d11bb0bb6ee25dba31ea0356fc Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 30 Jun 2016 17:32:10 +0200 Subject: [PATCH 033/236] Ensure metadata changes are propagated to the related containers Now we properly serialize the new metadata Contributes to CURA-342 --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index b1b065db70..ec7673f7f8 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -28,6 +28,12 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): result.setMetaDataEntry("GUID", str(uuid.uuid4())) return result + def setMetaDataEntry(self, key, value): + super().setMetaDataEntry(key, value) + + for container in UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(GUID = self.getMetaDataEntry("GUID")): + container.setMetaData(copy.deepcopy(self._metadata)) + def setProperty(self, key, property_name, property_value, container = None): super().setProperty(key, property_name, property_value) @@ -75,8 +81,7 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): builder.end("brand") builder.start("material") - builder.data(self.getName()) - metadata.pop("material", "") + builder.data(metadata.pop("material", "")) builder.end("material") builder.start("color") @@ -128,8 +133,8 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): variant = container.getMetaDataEntry("variant") if variant: if definition_id not in machine_nozzle_map: - machine_nozzle_map[definition_id] = [] - machine_nozzle_map[definition_id].append(container) + machine_nozzle_map[definition_id] = {} + machine_nozzle_map[definition_id][variant] = container for definition_id, container in machine_container_map.items(): definition = container.getDefinition() @@ -150,7 +155,7 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): self._addSettingElement(builder, instance) # Find all hotend sub-profiles corresponding to this material and machine and add them to this profile. - for hotend in machine_nozzle_map[definition_id]: + for hotend_id, hotend in machine_nozzle_map[definition_id].items(): variant_containers = registry.findInstanceContainers(id = hotend.getMetaDataEntry("variant")) if not variant_containers: continue From eb48a4e7687e8850964d35e4b33e7de2d5a36012 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 30 Jun 2016 17:32:43 +0200 Subject: [PATCH 034/236] Set the new metadata when editing a material Contributes to CURA-342 --- resources/qml/Preferences/MaterialView.qml | 31 +++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/resources/qml/Preferences/MaterialView.qml b/resources/qml/Preferences/MaterialView.qml index 94aa2ca5d6..b187fcb469 100644 --- a/resources/qml/Preferences/MaterialView.qml +++ b/resources/qml/Preferences/MaterialView.qml @@ -45,10 +45,23 @@ TabView property real rowHeight: textField.height; Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Brand") } - TextField { id: textField; width: base.secondColumnWidth; text: properties.supplier; readOnly: !base.editingEnabled; } + TextField + { + id: textField; + width: base.secondColumnWidth; + text: properties.supplier; + readOnly: !base.editingEnabled; + onEditingFinished: Cura.ContainerManager.setContainerMetaDataEntry(base.containerId, "brand", text) + } Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Material Type") } - TextField { width: base.secondColumnWidth; text: properties.material_type; readOnly: !base.editingEnabled; } + TextField + { + width: base.secondColumnWidth; + text: properties.material_type; + readOnly: !base.editingEnabled; + onEditingFinished: Cura.ContainerManager.setContainerMetaDataEntry(base.containerId, "material", text) + } Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Color") } @@ -62,6 +75,8 @@ TabView { id: colorSelector color: properties.color_code + onColorChanged: Cura.ContainerManager.setContainerMetaDataEntry(base.containerId, "color_code", color) + width: colorLabel.height * 0.75 height: colorLabel.height * 0.75 border.width: UM.Theme.getSize("default_lining").height @@ -70,7 +85,13 @@ TabView MouseArea { anchors.fill: parent; onClicked: colorDialog.open(); enabled: base.editingEnabled } } - TextField { id: colorLabel; text: properties.color_name; readOnly: !base.editingEnabled } + TextField + { + id: colorLabel; + text: properties.color_name; + readOnly: !base.editingEnabled + onEditingFinished: Cura.ContainerManager.setContainerMetaDataEntry(base.containerId, "color_name", text) + } ColorDialog { id: colorDialog; color: properties.color_code; onAccepted: colorSelector.color = color } } @@ -88,6 +109,8 @@ TabView suffix: "g/cm" stepSize: 0.01 readOnly: !base.editingEnabled; + + onEditingFinished: Cura.ContainerManager.setContainerMetaDataEntry(base.containerId, "properties/density", value) } Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Diameter") } @@ -99,6 +122,8 @@ TabView suffix: "mm³" stepSize: 0.01 readOnly: !base.editingEnabled; + + onEditingFinished: Cura.ContainerManager.setContainerMetaDataEntry(base.containerId, "properties/diameter", value) } Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament Cost") } From a7939c6f3db3b3598e601fbbdee6f168d8b6b99e Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 30 Jun 2016 18:53:50 +0200 Subject: [PATCH 035/236] Fix empty material/quality profiles when switching variants CURA-1278 --- cura/MachineManagerModel.py | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/cura/MachineManagerModel.py b/cura/MachineManagerModel.py index 14763f14db..43b4ab521d 100644 --- a/cura/MachineManagerModel.py +++ b/cura/MachineManagerModel.py @@ -401,9 +401,9 @@ class MachineManagerModel(QObject): preferred_material = None if old_material: - preferred_material = old_material.getId() + preferred_material_name = old_material.getName() - self.setActiveMaterial(self._updateMaterialContainer(self._global_container_stack.getBottom(), containers[0], preferred_material).id) + self.setActiveMaterial(self._updateMaterialContainer(self._global_container_stack.getBottom(), containers[0], preferred_material_name).id) @pyqtSlot(str) def setActiveQuality(self, quality_id): @@ -519,7 +519,7 @@ class MachineManagerModel(QObject): return self._empty_variant_container - def _updateMaterialContainer(self, definition, variant_container = None, preferred_material = None): + def _updateMaterialContainer(self, definition, variant_container = None, preferred_material_name = None): if not definition.getMetaDataEntry("has_materials"): return self._empty_material_container @@ -533,15 +533,26 @@ class MachineManagerModel(QObject): else: search_criteria["definition"] = "fdmprinter" - if not preferred_material: + if preferred_material_name: + search_criteria["name"] = preferred_material_name + else: preferred_material = definition.getMetaDataEntry("preferred_material") - if preferred_material: - search_criteria["id"] = preferred_material + if preferred_material: + search_criteria["id"] = preferred_material containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**search_criteria) if containers: return containers[0] + if "name" in search_criteria or "id" in search_criteria: + # If a material by this name can not be found, try a wider set of search criteria + search_criteria.pop("name", None) + search_criteria.pop("id", None) + + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**search_criteria) + if containers: + return containers[0] + return self._empty_material_container def _updateQualityContainer(self, definition, material_container = None, preferred_quality_name = None): @@ -566,6 +577,15 @@ class MachineManagerModel(QObject): if containers: return containers[0] + if "name" in search_criteria or "id" in search_criteria: + # If a quality by this name can not be found, try a wider set of search criteria + search_criteria.pop("name", None) + search_criteria.pop("id", None) + + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**search_criteria) + if containers: + return containers[0] + return self._empty_quality_container def createMachineManagerModel(engine, script_engine): From 58ae1e923c1ad043a4328cb4677b1e90839ac3e5 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 30 Jun 2016 19:13:46 +0200 Subject: [PATCH 036/236] Prevent warning when no printer is connected. CURA-1036 --- resources/qml/MonitorButton.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/MonitorButton.qml b/resources/qml/MonitorButton.qml index 10fc44b3f0..b8bfbab869 100644 --- a/resources/qml/MonitorButton.qml +++ b/resources/qml/MonitorButton.qml @@ -109,7 +109,7 @@ Rectangle id: abortButton visible: printerConnected - enabled: Cura.MachineManager.printerOutputDevices[0].jobState == "paused" || Cura.MachineManager.printerOutputDevices[0].jobState == "printing" + enabled: printerConnected && Cura.MachineManager.printerOutputDevices[0].jobState == "paused" || Cura.MachineManager.printerOutputDevices[0].jobState == "printing" height: UM.Theme.getSize("save_button_save_to_button").height anchors.top: progressBar.bottom @@ -156,7 +156,7 @@ Rectangle id: pauseButton visible: printerConnected - enabled: Cura.MachineManager.printerOutputDevices[0].jobState == "paused" || Cura.MachineManager.printerOutputDevices[0].jobState == "printing" + enabled: printerConnected && Cura.MachineManager.printerOutputDevices[0].jobState == "paused" || Cura.MachineManager.printerOutputDevices[0].jobState == "printing" height: UM.Theme.getSize("save_button_save_to_button").height anchors.top: progressBar.bottom From bcffa3f09a2df57cf578bfe55e6130d6ee2fc041 Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Fri, 1 Jul 2016 00:39:04 +0200 Subject: [PATCH 037/236] JSON fix: support roof extruder could be chosen even when roofs weren't enabled (CURA-1723) just putting this commit under that issue so that it will be reviewed... --- resources/definitions/fdmprinter.def.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index d187dc66be..91b5014794 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -2793,7 +2793,7 @@ "type": "extruder", "default_value": "0", "value": "support_extruder_nr", - "enabled": "support_enable", + "enabled": "support_enable and support_roof_enable", "settable_per_mesh": false, "settable_per_extruder": false } From c8e61a326728c2fa02214130d746ef4a7cfc9698 Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Fri, 1 Jul 2016 00:56:48 +0200 Subject: [PATCH 038/236] JSON feat: cubic isometric infill (CURA-1723) --- resources/definitions/fdmprinter.def.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 91b5014794..b2f41e6860 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -717,7 +717,7 @@ "type": "float", "default_value": 2, "minimum_value": "0", - "value": "0 if infill_sparse_density == 0 else (infill_line_width * 100) / infill_sparse_density * (2 if infill_pattern == \"grid\" else (3 if infill_pattern == \"triangles\" else 1))", + "value": "0 if infill_sparse_density == 0 else (infill_line_width * 100) / infill_sparse_density * (2 if infill_pattern == \"grid\" else (3 if infill_pattern == \"triangles\" else (3 if infill_pattern == \"cubic\" else 1)))", "settable_per_mesh": true } } @@ -725,12 +725,13 @@ "infill_pattern": { "label": "Infill Pattern", - "description": "The pattern of the infill material of the print. The line and zig zag infill swap direction on alternate layers, reducing material cost. The grid, triangle and concentric patterns are fully printed every layer.", + "description": "The pattern of the infill material of the print. The line and zig zag infill swap direction on alternate layers, reducing material cost. The grid, cubic, triangle and concentric patterns are fully printed every layer.", "type": "enum", "options": { "grid": "Grid", "lines": "Lines", + "cubic": "Cubic", "triangles": "Triangles", "concentric": "Concentric", "zigzag": "Zig Zag" From aaae2aa3d586f43ad632d6851a0fe1ebd8ad2b11 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 1 Jul 2016 09:51:54 +0200 Subject: [PATCH 039/236] Minor check machine action GUI improvements CURA-1385 --- plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml index 1c3ac84010..4b25d3130d 100644 --- a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml +++ b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml @@ -52,7 +52,6 @@ Cura.MachineAction onClicked: { checkupContent.visible = true - startCheckButton.enabled = false manager.startCheck() } } @@ -94,7 +93,7 @@ Cura.MachineAction anchors.left: connectionLabel.right anchors.top: parent.top wrapMode: Text.WordWrap - text: Cura.USBPrinterManager.connectedPrinterList.rowCount() > 0 || base.addOriginalProgress.checkUp[0] ? catalog.i18nc("@info:status","Done"):catalog.i18nc("@info:status","Incomplete") + text: Cura.USBPrinterManager.connectedPrinterList.rowCount() > 0 ? catalog.i18nc("@info:status","Connected"): catalog.i18nc("@info:status","Not connected") } ////////////////////////////////////////////////////////// Label From fea534509ee514bcf222100785bfc3f8ef90e43a Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 1 Jul 2016 10:12:11 +0200 Subject: [PATCH 040/236] Fix two "critical errors" on startup CuraMachineManager.isGlobalStackValid would return None on startup, which qml does not like. CURA-1585 --- cura/MachineManagerModel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/MachineManagerModel.py b/cura/MachineManagerModel.py index 43b4ab521d..76786efe61 100644 --- a/cura/MachineManagerModel.py +++ b/cura/MachineManagerModel.py @@ -213,7 +213,7 @@ class MachineManagerModel(QObject): # Calling _checkStackForErrors on every change is simply too expensive @pyqtProperty(bool, notify = globalValidationChanged) def isGlobalStackValid(self): - return self._global_stack_valid + return bool(self._global_stack_valid) @pyqtProperty(str, notify = activeStackChanged) def activeUserProfileId(self): From 91bfd2de2778622d6fd7193fb02d698b6b8494bb Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 1 Jul 2016 11:04:23 +0200 Subject: [PATCH 041/236] Force focus instead of requesting it. Fixes CURA-1778 --- resources/qml/SidebarHeader.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml index b31b28779d..ac6c491262 100644 --- a/resources/qml/SidebarHeader.qml +++ b/resources/qml/SidebarHeader.qml @@ -117,6 +117,7 @@ Column onGlobalContainerChanged: { base.currentExtruderIndex = -1; + forceActiveFocus() ExtruderManager.setActiveExtruderIndex(0); } } @@ -134,7 +135,7 @@ Column onClicked: { - focus = true; //Changing focus applies the currently-being-typed values so it can change the displayed setting values. + forceActiveFocus() //Changing focus applies the currently-being-typed values so it can change the displayed setting values. base.currentExtruderIndex = index; ExtruderManager.setActiveExtruderIndex(index); } From b06e61727c86c6cf43d62a515b248b69302d34de Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 1 Jul 2016 11:39:04 +0200 Subject: [PATCH 042/236] ZOffset decorator is now correctly copied CURA-1504 --- cura/ZOffsetDecorator.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cura/ZOffsetDecorator.py b/cura/ZOffsetDecorator.py index c2912454de..66dddfd390 100644 --- a/cura/ZOffsetDecorator.py +++ b/cura/ZOffsetDecorator.py @@ -12,4 +12,6 @@ class ZOffsetDecorator(SceneNodeDecorator): return self._z_offset def __deepcopy__(self, memo): - return ZOffsetDecorator() + copied_decorator = ZOffsetDecorator() + copied_decorator.setZOffset(self.getZOffset()) + return copied_decorator From c9b2a1d93846157eefd016e728997072c06af08e Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 1 Jul 2016 11:51:46 +0200 Subject: [PATCH 043/236] Properly prevent warning when no printer is connected. CURA-1036 --- resources/qml/MonitorButton.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/MonitorButton.qml b/resources/qml/MonitorButton.qml index b8bfbab869..739364fb46 100644 --- a/resources/qml/MonitorButton.qml +++ b/resources/qml/MonitorButton.qml @@ -109,7 +109,7 @@ Rectangle id: abortButton visible: printerConnected - enabled: printerConnected && Cura.MachineManager.printerOutputDevices[0].jobState == "paused" || Cura.MachineManager.printerOutputDevices[0].jobState == "printing" + enabled: printerConnected && (Cura.MachineManager.printerOutputDevices[0].jobState == "paused" || Cura.MachineManager.printerOutputDevices[0].jobState == "printing") height: UM.Theme.getSize("save_button_save_to_button").height anchors.top: progressBar.bottom @@ -156,7 +156,7 @@ Rectangle id: pauseButton visible: printerConnected - enabled: printerConnected && Cura.MachineManager.printerOutputDevices[0].jobState == "paused" || Cura.MachineManager.printerOutputDevices[0].jobState == "printing" + enabled: printerConnected && (Cura.MachineManager.printerOutputDevices[0].jobState == "paused" || Cura.MachineManager.printerOutputDevices[0].jobState == "printing") height: UM.Theme.getSize("save_button_save_to_button").height anchors.top: progressBar.bottom From 6800991fa17585f3128ac2461688f40389d0282e Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 1 Jul 2016 14:32:40 +0200 Subject: [PATCH 044/236] Fix USBPrinterOutputDevice to work with the Print Monitor CURA-1036 --- plugins/USBPrinting/ControlWindow.qml | 72 ----------------- plugins/USBPrinting/USBPrinterOutputDevice.py | 78 ++++++++++--------- 2 files changed, 43 insertions(+), 107 deletions(-) delete mode 100644 plugins/USBPrinting/ControlWindow.qml diff --git a/plugins/USBPrinting/ControlWindow.qml b/plugins/USBPrinting/ControlWindow.qml deleted file mode 100644 index f0036ab6f9..0000000000 --- a/plugins/USBPrinting/ControlWindow.qml +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 2015 Ultimaker B.V. -// Cura is released under the terms of the AGPLv3 or higher. - -import QtQuick 2.1 -import QtQuick.Controls 1.1 -import QtQuick.Layouts 1.1 -import QtQuick.Window 2.1 - -import UM 1.1 as UM - -UM.Dialog -{ - width: 500 * Screen.devicePixelRatio; - height: 100 * Screen.devicePixelRatio; - modality: Qt.NonModal - - title: catalog.i18nc("@title:window", "Print via USB") - - Column - { - anchors.fill: parent; - Row - { - spacing: UM.Theme.getSize("default_margin").width; - Label - { - //: USB Printing dialog label, %1 is head temperature - text: catalog.i18nc("@label","Extruder Temperature %1").arg(manager.hotendTemperatures[0]) - } - Label - { - //: USB Printing dialog label, %1 is bed temperature - text: catalog.i18nc("@label","Bed Temperature %1").arg(manager.bedTemperature) - } - Label - { - text: "" + manager.error - } - - UM.I18nCatalog{id: catalog; name:"cura"} - - } - - ProgressBar - { - id: prog; - anchors.left: parent.left; - anchors.right: parent.right; - - minimumValue: 0; - maximumValue: 100; - value: manager.progress - } - } - - rightButtons: [ - Button - { - //: USB Printing dialog start print button - text: catalog.i18nc("@action:button","Print"); - onClicked: { manager.startPrint() } - enabled: manager.progress == 0 ? true : false - }, - Button - { - //: USB Printing dialog cancel print button - text: catalog.i18nc("@action:button","Cancel"); - onClicked: { manager.cancelPrint() } - enabled: manager.progress == 0 ? false: true - } - ] -} diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index 58b75c2987..526002aa3a 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -51,13 +51,14 @@ class USBPrinterOutputDevice(PrinterOutputDevice): self._update_firmware_thread = threading.Thread(target= self._updateFirmware) self._update_firmware_thread.daemon = True self.firmwareUpdateComplete.connect(self._onFirmwareUpdateComplete) - + self._heatup_wait_start_time = time.time() ## Queue for commands that need to be send. Used when command is sent when a print is active. self._command_queue = queue.Queue() self._is_printing = False + self._is_paused = False ## Set when print is started in order to check running time. self._print_start_time = None @@ -80,13 +81,15 @@ class USBPrinterOutputDevice(PrinterOutputDevice): # In order to keep the connection alive we request the temperature every so often from a different extruder. # This index is the extruder we requested data from the last time. - self._temperature_requested_extruder_index = 0 + self._temperature_requested_extruder_index = 0 + + self._current_z = 0 self._updating_firmware = False self._firmware_file_name = None - self._control_view = None + self._error_message = None onError = pyqtSignal() @@ -120,10 +123,10 @@ class USBPrinterOutputDevice(PrinterOutputDevice): def _homeBed(self): self._sendCommand("G28 Z") - @pyqtSlot() def startPrint(self): self.writeStarted.emit(self) gcode_list = getattr( Application.getInstance().getController().getScene(), "gcode_list") + self._updateJobState("printing") self.printGCode(gcode_list) def _moveHead(self, x, y, z, speed): @@ -135,6 +138,8 @@ class USBPrinterOutputDevice(PrinterOutputDevice): # \param gcode_list List with gcode (strings). def printGCode(self, gcode_list): if self._progress or self._connection_state != ConnectionState.connected: + self._error_message = Message(i18n_catalog.i18nc("@info:status", "Printer is busy or not connected. Unable to start a new job.")) + self._error_message.show() Logger.log("d", "Printer is busy or not connected, aborting print") self.writeError.emit(self) return @@ -344,23 +349,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): self._setErrorState("Unexpected error while writing serial port %s " % e) self.close() - def createControlInterface(self): - if self._control_view is None: - Logger.log("d", "Creating control interface for printer connection") - path = QUrl.fromLocalFile(os.path.join(PluginRegistry.getInstance().getPluginPath("USBPrinting"), "ControlWindow.qml")) - component = QQmlComponent(Application.getInstance()._engine, path) - self._control_context = QQmlContext(Application.getInstance()._engine.rootContext()) - self._control_context.setContextProperty("manager", self) - self._control_view = component.create(self._control_context) - - ## Show control interface. - # This will create the view if its not already created. - def showControlInterface(self): - if self._control_view is None: - self.createControlInterface() - self._control_view.show() - - ## Send a command to printer. + ## Send a command to printer. # \param cmd string with g-code def sendCommand(self, cmd): if self._progress: @@ -371,11 +360,12 @@ class USBPrinterOutputDevice(PrinterOutputDevice): ## Set the error state with a message. # \param error String with the error message. def _setErrorState(self, error): + self._updateJobState("error") self._error_state = error self.onError.emit() def requestWrite(self, node, file_name = None, filter_by_machine = False): - self.showControlInterface() + self.startPrint() def _setEndstopState(self, endstop_key, value): if endstop_key == b"x_min": @@ -391,14 +381,14 @@ class USBPrinterOutputDevice(PrinterOutputDevice): self.endstopStateChanged.emit("z_min", value) self._z_min_endstop_pressed = value - ## Listen thread function. + ## Listen thread function. def _listen(self): Logger.log("i", "Printer connection listen thread started for %s" % self._serial_port) temperature_request_timeout = time.time() ok_timeout = time.time() while self._connection_state == ConnectionState.connected: line = self._readline() - if line is None: + if line is None: break # None is only returned when something went wrong. Stop listening if time.time() > temperature_request_timeout: @@ -423,7 +413,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): self._setErrorState(line[6:]) elif b" T:" in line or line.startswith(b"T:"): # Temperature message - try: + try: self._setHotendTemperature(self._temperature_requested_extruder_index, float(re.search(b"T: *([0-9\.]*)", line).group(1))) except: pass @@ -445,6 +435,8 @@ class USBPrinterOutputDevice(PrinterOutputDevice): ok_timeout = time.time() + 5 if not self._command_queue.empty(): self._sendCommand(self._command_queue.get()) + elif self._is_paused: + line = b"" # Force getting temperature as keep alive else: self._sendNextGcodeLine() elif b"resend" in line.lower() or b"rs" in line: # Because a resend can be asked with "resend" and "rs" @@ -454,13 +446,14 @@ class USBPrinterOutputDevice(PrinterOutputDevice): if b"rs" in line: self._gcode_position = int(line.split()[1]) - else: # Request the temperature on comm timeout (every 2 seconds) when we are not printing.) - if line == b"": - if self._num_extruders > 0: - self._temperature_requested_extruder_index = (self._temperature_requested_extruder_index + 1) % self._num_extruders - self.sendCommand("M105 T%d" % self._temperature_requested_extruder_index) - else: - self.sendCommand("M105") + # Request the temperature on comm timeout (every 2 seconds) when we are not printing.) + if line == b"": + if self._num_extruders > 0: + self._temperature_requested_extruder_index = (self._temperature_requested_extruder_index + 1) % self._num_extruders + self.sendCommand("M105 T%d" % self._temperature_requested_extruder_index) + else: + self.sendCommand("M105") + Logger.log("i", "Printer connection listen thread stopped for %s" % self._serial_port) ## Send next Gcode in the gcode list @@ -487,10 +480,22 @@ class USBPrinterOutputDevice(PrinterOutputDevice): checksum = functools.reduce(lambda x,y: x^y, map(ord, "N%d%s" % (self._gcode_position, line))) self._sendCommand("N%d%s*%d" % (self._gcode_position, line, checksum)) - self._gcode_position += 1 + self._gcode_position += 1 self.setProgress((self._gcode_position / len(self._gcode)) * 100) self.progressChanged.emit() + ## Set the state of the print. + # Sent from the print monitor + def _setJobState(self, job_state): + if job_state == "pause": + self._is_paused = True + self._updateJobState("paused") + elif job_state == "print": + self._is_paused = False + self._updateJobState("printing") + elif job_state == "abort": + self.cancelPrint() + ## Set the progress of the print. # It will be normalized (based on max_progress) to range 0 - 100 def setProgress(self, progress, max_progress = 100): @@ -498,16 +503,19 @@ class USBPrinterOutputDevice(PrinterOutputDevice): self.progressChanged.emit() ## Cancel the current print. Printer connection wil continue to listen. - @pyqtSlot() def cancelPrint(self): self._gcode_position = 0 self.setProgress(0) self._gcode = [] - # Turn of temperatures + # Turn off temperatures, fan and steppers self._sendCommand("M140 S0") self._sendCommand("M104 S0") + self._sendCommand("M107") + self._sendCommand("M84") self._is_printing = False + self._is_paused = False + self._updateJobState("ready") ## Check if the process did not encounter an error yet. def hasError(self): From 087ebc0768b9ba98d48253e5147e37eb18050333 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 1 Jul 2016 15:06:49 +0200 Subject: [PATCH 045/236] Automatically show the Print Monitor when starting a print CURA-1036 --- cura/CuraApplication.py | 2 ++ plugins/USBPrinting/USBPrinterOutputDevice.py | 2 ++ resources/qml/Sidebar.qml | 8 +++++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index de4c6e0681..72d19d65a1 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -242,6 +242,8 @@ class CuraApplication(QtApplication): def _onEngineCreated(self): self._engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider()) + showPrintMonitor = pyqtSignal(bool, arguments = ["show"]) + ## Cura has multiple locations where instance containers need to be saved, so we need to handle this differently. # # Note that the AutoSave plugin also calls this method. diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index 526002aa3a..0b545314aa 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -365,6 +365,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): self.onError.emit() def requestWrite(self, node, file_name = None, filter_by_machine = False): + Application.getInstance().showPrintMonitor.emit(True) self.startPrint() def _setEndstopState(self, endstop_key, value): @@ -516,6 +517,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): self._is_printing = False self._is_paused = False self._updateJobState("ready") + Application.getInstance().showPrintMonitor.emit(False) ## Check if the process did not encounter an error yet. def hasError(self): diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index 822fdf8ca1..25931932d3 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -15,6 +15,11 @@ Rectangle property int currentModeIndex; property bool monitoringPrint: false + Connections + { + target: Printer + onShowPrintMonitor: base.monitoringPrint = show + } // Is there an output device for this printer? property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 @@ -83,7 +88,7 @@ Rectangle onClicked: monitoringPrint = false iconSource: UM.Theme.getIcon("tab_settings"); checkable: true - checked: true + checked: !monitoringPrint exclusiveGroup: sidebarHeaderBarGroup style: UM.Theme.styles.sidebar_header_tab @@ -104,6 +109,7 @@ Rectangle return UM.Theme.getIcon("tab_monitor_connected") } checkable: true + checked: monitoringPrint exclusiveGroup: sidebarHeaderBarGroup style: UM.Theme.styles.sidebar_header_tab From b7906c9ce7c5a9b033299de7700bf110431ee90f Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 1 Jul 2016 15:26:24 +0200 Subject: [PATCH 046/236] Fix warning about missing color in theme CURA-1036 --- resources/qml/MonitorButton.qml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/resources/qml/MonitorButton.qml b/resources/qml/MonitorButton.qml index 739364fb46..8a232e62f1 100644 --- a/resources/qml/MonitorButton.qml +++ b/resources/qml/MonitorButton.qml @@ -28,8 +28,10 @@ Rectangle return UM.Theme.getColor("status_ready") else if(Cura.MachineManager.printerOutputDevices[0].jobState == "paused") return UM.Theme.getColor("status_paused") - else if (Cura.MachineManager.printerOutputDevices[0].jobState != "error") - return UM.Theme.getColor("status_error") + else if (Cura.MachineManager.printerOutputDevices[0].jobState == "error") + return UM.Theme.getColor("status_stopped") + else + return UM.Theme.getColor("text") } property bool activity: Printer.getPlatformActivity; From 19a718205dbb30dacc6ecfe40be5526c8bc1c671 Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Fri, 1 Jul 2016 21:27:36 +0200 Subject: [PATCH 047/236] =?UTF-8?q?BQ=20Hephestos2=20-=20Preheating=20temp?= =?UTF-8?q?erature=20fix=20When=20starting=20a=20print=20with=20the=20"cus?= =?UTF-8?q?tom"=20GCode=20by=20BQ,=20the=20printer=20resets=20the=20nozzle?= =?UTF-8?q?=20temperature=20to=20210=C2=B0C=20when=20printing=20via=20SD?= =?UTF-8?q?=20card=20(no=20matter=20what=20you=20set=20on=20Cura)=20and=20?= =?UTF-8?q?only=20preheats=20the=20nozzle=20to=20180=C2=B0C=20when=20print?= =?UTF-8?q?ing=20via=20USB.=20So=20currently=20the=20printer=20begins=20to?= =?UTF-8?q?=20print=20via=20USB=20at=20180=C2=B0C=20and=20reaches=20the=20?= =?UTF-8?q?correct=20temperature=20eg.g=20while=20printing=20the=20brim.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resources/definitions/bq_hephestos_2.def.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/definitions/bq_hephestos_2.def.json b/resources/definitions/bq_hephestos_2.def.json index e4e58cb6c2..db6a2971e6 100644 --- a/resources/definitions/bq_hephestos_2.def.json +++ b/resources/definitions/bq_hephestos_2.def.json @@ -14,7 +14,7 @@ }, "overrides": { - "machine_start_gcode": { "default_value": "; -- START GCODE --\nM800 ; Custom GCODE to fire start print procedure\n; -- end of START GCODE --" }, + "machine_start_gcode": { "default_value": "; -- START GCODE --\nM800 ; Custom GCODE to fire start print procedure\nM109 S{print_temperature} ;Makes sure the temperature is correct before printing\n; -- end of START GCODE --" }, "machine_end_gcode": { "default_value": "; -- END GCODE --\nM801 ; Custom GCODE to fire end print procedure\n; -- end of END GCODE --" }, "machine_width": { "default_value": 210 }, "machine_depth": { "default_value": 297 }, From ec41fce7bdd5d828c6834966aef75aefac7679cc Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Sun, 3 Jul 2016 13:26:51 +0200 Subject: [PATCH 048/236] USBPrinting: Let's "Print via USB" Already made that correction in the past. When print monitor was merged it appeared again. --- plugins/USBPrinting/USBPrinterOutputDevice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index 0b545314aa..2938604eed 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -26,8 +26,8 @@ class USBPrinterOutputDevice(PrinterOutputDevice): def __init__(self, serial_port): super().__init__(serial_port) self.setName(catalog.i18nc("@item:inmenu", "USB printing")) - self.setShortDescription(catalog.i18nc("@action:button", "Print with USB")) - self.setDescription(catalog.i18nc("@info:tooltip", "Print with USB")) + self.setShortDescription(catalog.i18nc("@action:button", "Print via USB")) + self.setDescription(catalog.i18nc("@info:tooltip", "Print via USB")) self.setIconName("print") self._serial = None From a38606da4a159389824fabdc7cebf07b9df5557a Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Sun, 3 Jul 2016 15:00:26 +0200 Subject: [PATCH 049/236] SplashScreen: Using system-default fontfamily Contributes to CURA-1676 --- cura/CuraSplashScreen.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cura/CuraSplashScreen.py b/cura/CuraSplashScreen.py index f2810d359b..3f24502bcd 100644 --- a/cura/CuraSplashScreen.py +++ b/cura/CuraSplashScreen.py @@ -25,10 +25,14 @@ class CuraSplashScreen(QSplashScreen): if buildtype: version[0] += " (%s)" %(buildtype) - painter.setFont(QFont("Proxima Nova Rg", 20 )) + used_font = QFont() # Using system-default font here + used_font.setPointSize(20) + painter.setFont(used_font) painter.drawText(0, 0, 330 * self._scale, 230 * self._scale, Qt.AlignHCenter | Qt.AlignBottom, version[0]) if len(version) > 1: - painter.setFont(QFont("Proxima Nova Rg", 12 )) + used_font = QFont() # Using system-default font here + used_font.setPointSize(12) + painter.setFont(used_font) painter.drawText(0, 0, 330 * self._scale, 255 * self._scale, Qt.AlignHCenter | Qt.AlignBottom, version[1]) painter.restore() From cd14a150403b7fed88a0dd052389819b27be2f34 Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Sun, 3 Jul 2016 15:17:40 +0200 Subject: [PATCH 050/236] Using the correct placeholder Thanks to Ruben! --- resources/definitions/bq_hephestos_2.def.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/definitions/bq_hephestos_2.def.json b/resources/definitions/bq_hephestos_2.def.json index db6a2971e6..374e41f93c 100644 --- a/resources/definitions/bq_hephestos_2.def.json +++ b/resources/definitions/bq_hephestos_2.def.json @@ -14,7 +14,7 @@ }, "overrides": { - "machine_start_gcode": { "default_value": "; -- START GCODE --\nM800 ; Custom GCODE to fire start print procedure\nM109 S{print_temperature} ;Makes sure the temperature is correct before printing\n; -- end of START GCODE --" }, + "machine_start_gcode": { "default_value": "; -- START GCODE --\nM800 ; Custom GCODE to fire start print procedure\nM109 S{material_print_temperature} ;Makes sure the temperature is correct before printing\n; -- end of START GCODE --" }, "machine_end_gcode": { "default_value": "; -- END GCODE --\nM801 ; Custom GCODE to fire end print procedure\n; -- end of END GCODE --" }, "machine_width": { "default_value": 210 }, "machine_depth": { "default_value": 297 }, From 0c23c26ac91b7bcdccfa7525686e8317bcaa14f9 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Sun, 3 Jul 2016 16:01:57 +0200 Subject: [PATCH 051/236] Code style CURA-1676 --- cura/CuraSplashScreen.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/cura/CuraSplashScreen.py b/cura/CuraSplashScreen.py index 3f24502bcd..1df2c39da7 100644 --- a/cura/CuraSplashScreen.py +++ b/cura/CuraSplashScreen.py @@ -25,14 +25,13 @@ class CuraSplashScreen(QSplashScreen): if buildtype: version[0] += " (%s)" %(buildtype) - used_font = QFont() # Using system-default font here - used_font.setPointSize(20) - painter.setFont(used_font) + font = QFont() # Using system-default font here + font.setPointSize(20) + painter.setFont(font) painter.drawText(0, 0, 330 * self._scale, 230 * self._scale, Qt.AlignHCenter | Qt.AlignBottom, version[0]) if len(version) > 1: - used_font = QFont() # Using system-default font here - used_font.setPointSize(12) - painter.setFont(used_font) + font.setPointSize(12) + painter.setFont(font) painter.drawText(0, 0, 330 * self._scale, 255 * self._scale, Qt.AlignHCenter | Qt.AlignBottom, version[1]) painter.restore() From 41147b5128a06db4cda27107d70ebe6d2ef102b0 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 4 Jul 2016 10:26:59 +0200 Subject: [PATCH 052/236] Machine action labels are now translatable CURA-1385 --- plugins/UltimakerMachineActions/BedLevelMachineAction.py | 8 +++++--- .../UltimakerMachineActions/UMOCheckupMachineAction.py | 6 +++++- .../UpgradeFirmwareMachineAction.py | 5 ++++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/plugins/UltimakerMachineActions/BedLevelMachineAction.py b/plugins/UltimakerMachineActions/BedLevelMachineAction.py index b6b52c552e..7dad841340 100644 --- a/plugins/UltimakerMachineActions/BedLevelMachineAction.py +++ b/plugins/UltimakerMachineActions/BedLevelMachineAction.py @@ -1,14 +1,16 @@ from cura.MachineAction import MachineAction +from cura.PrinterOutputDevice import PrinterOutputDevice -from PyQt5.QtCore import pyqtSlot +from PyQt5.QtCore import pyqtSlot from UM.Application import Application +from UM.i18n import i18nCatalog +catalog = i18nCatalog("cura") -from cura.PrinterOutputDevice import PrinterOutputDevice class BedLevelMachineAction(MachineAction): def __init__(self): - super().__init__("BedLevel", "Level bed") + super().__init__("BedLevel", catalog.i18nc("@action", "Level bed")) self._qml_url = "BedLevelMachineAction.qml" self._bed_level_position = 0 diff --git a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py index 0c8bf3480f..f13257e8a9 100644 --- a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py +++ b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py @@ -3,9 +3,13 @@ from cura.PrinterOutputDevice import PrinterOutputDevice from UM.Application import Application from PyQt5.QtCore import pyqtSlot, pyqtSignal, pyqtProperty +from UM.i18n import i18nCatalog +catalog = i18nCatalog("cura") + + class UMOCheckupMachineAction(MachineAction): def __init__(self): - super().__init__("UMOCheckup", "Checkup") + super().__init__("UMOCheckup", catalog.i18nc("@action", "Checkup")) self._qml_url = "UMOCheckupMachineAction.qml" self._hotend_target_temp = 180 self._bed_target_temp = 60 diff --git a/plugins/UltimakerMachineActions/UpgradeFirmwareMachineAction.py b/plugins/UltimakerMachineActions/UpgradeFirmwareMachineAction.py index 7d696a871e..53476207fd 100644 --- a/plugins/UltimakerMachineActions/UpgradeFirmwareMachineAction.py +++ b/plugins/UltimakerMachineActions/UpgradeFirmwareMachineAction.py @@ -1,6 +1,9 @@ from cura.MachineAction import MachineAction +from UM.i18n import i18nCatalog +catalog = i18nCatalog("cura") + class UpgradeFirmwareMachineAction(MachineAction): def __init__(self): - super().__init__("UpgradeFirmware", "Upgrade Firmware") + super().__init__("UpgradeFirmware", catalog.i18nc("@action", "Upgrade Firmware")) self._qml_url = "UpgradeFirmwareMachineAction.qml" \ No newline at end of file From 53087bd2785ed026c910a2502945ed274be6cb66 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 4 Jul 2016 10:28:37 +0200 Subject: [PATCH 053/236] Added stubs for UMO upgrade selection CURA-1385 --- .../UMOUpgradeSelection.py | 10 ++++++++ .../UMOUpgradeSelectionMachineAction.qml | 23 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 plugins/UltimakerMachineActions/UMOUpgradeSelection.py create mode 100644 plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml diff --git a/plugins/UltimakerMachineActions/UMOUpgradeSelection.py b/plugins/UltimakerMachineActions/UMOUpgradeSelection.py new file mode 100644 index 0000000000..56540b591e --- /dev/null +++ b/plugins/UltimakerMachineActions/UMOUpgradeSelection.py @@ -0,0 +1,10 @@ +from cura.MachineAction import MachineAction + +from UM.i18n import i18nCatalog +catalog = i18nCatalog("cura") + + +class UMOUpgradeSelection(MachineAction): + def __init__(self): + super().__init__("UMOUpgradeSelection", catalog.i18nc("@action", "Select upgrades")) + self._qml_url = "UMOUpgradeSelectionMachineAction.qml" \ No newline at end of file diff --git a/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml b/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml new file mode 100644 index 0000000000..bc0f1fe3b4 --- /dev/null +++ b/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml @@ -0,0 +1,23 @@ +// Copyright (c) 2016 Ultimaker B.V. +// Cura is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Layouts 1.1 +import QtQuick.Window 2.1 + +import UM 1.2 as UM +import Cura 1.0 as Cura + + +Cura.MachineAction +{ + anchors.fill: parent; + Item + { + id: bedLevelMachineAction + anchors.fill: parent; + + UM.I18nCatalog { id: catalog; name: "cura"; } + } +} \ No newline at end of file From c9b1c36cc3d32c375c1b9fcf69ec9ce2fa61d9a2 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Mon, 4 Jul 2016 11:00:54 +0200 Subject: [PATCH 054/236] Add some documentation to XmlMaterialProfile Contributes to CURA-342 --- .../XmlMaterialProfile/XmlMaterialProfile.py | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index ec7673f7f8..b996be602f 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -13,33 +13,32 @@ import UM.Dictionary import UM.Settings -# The namespace is prepended to the tag name but between {}. -# We are only interested in the actual tag name, so discard everything -# before the last } -def _tag_without_namespace(element): - return element.tag[element.tag.rfind("}") + 1:] - +## Handles serializing and deserializing material containers from an XML file class XmlMaterialProfile(UM.Settings.InstanceContainer): def __init__(self, container_id, *args, **kwargs): super().__init__(container_id, *args, **kwargs) + ## Overridden from InstanceContainer def duplicate(self, new_id, new_name = None): result = super().duplicate(self.getMetaDataEntry("brand") + "_" + new_id, new_name) result.setMetaDataEntry("GUID", str(uuid.uuid4())) return result + ## Overridden from InstanceContainer def setMetaDataEntry(self, key, value): super().setMetaDataEntry(key, value) for container in UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(GUID = self.getMetaDataEntry("GUID")): container.setMetaData(copy.deepcopy(self._metadata)) + ## Overridden from InstanceContainer def setProperty(self, key, property_name, property_value, container = None): super().setProperty(key, property_name, property_value) for container in UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(GUID = self.getMetaDataEntry("GUID")): container._dirty = True + ## Overridden from InstanceContainer def serialize(self): registry = UM.Settings.ContainerRegistry.getInstance() @@ -186,6 +185,7 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): return stream.getvalue() + ## Overridden from InstanceContainer def deserialize(self, serialized): data = ET.fromstring(serialized) @@ -359,6 +359,7 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): "Ultimaker Original+": "ultimaker_original_plus" } + # Map of recognised namespaces with a proper prefix. __namespaces = { "um": "http://www.ultimaker.com/material" } @@ -378,3 +379,10 @@ def _indent(elem, level = 0): else: if level and (not elem.tail or not elem.tail.strip()): elem.tail = i + + +# The namespace is prepended to the tag name but between {}. +# We are only interested in the actual tag name, so discard everything +# before the last } +def _tag_without_namespace(element): + return element.tag[element.tag.rfind("}") + 1:] From 1242d6bf52e3ed375f2a4387874d970bf2d3b6e1 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 4 Jul 2016 11:24:31 +0200 Subject: [PATCH 055/236] Added UMO upgrade selection CURA-1385 --- .../UMOUpgradeSelection.py | 39 ++++++++++++++++++- .../UMOUpgradeSelectionMachineAction.qml | 16 ++++++++ plugins/UltimakerMachineActions/__init__.py | 3 +- .../definitions/ultimaker_original.def.json | 2 +- 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/plugins/UltimakerMachineActions/UMOUpgradeSelection.py b/plugins/UltimakerMachineActions/UMOUpgradeSelection.py index 56540b591e..2e13ec50d5 100644 --- a/plugins/UltimakerMachineActions/UMOUpgradeSelection.py +++ b/plugins/UltimakerMachineActions/UMOUpgradeSelection.py @@ -1,10 +1,47 @@ from cura.MachineAction import MachineAction +from PyQt5.QtCore import pyqtSlot, pyqtSignal, pyqtProperty from UM.i18n import i18nCatalog +from UM.Application import Application catalog = i18nCatalog("cura") +import UM.Settings.InstanceContainer class UMOUpgradeSelection(MachineAction): def __init__(self): super().__init__("UMOUpgradeSelection", catalog.i18nc("@action", "Select upgrades")) - self._qml_url = "UMOUpgradeSelectionMachineAction.qml" \ No newline at end of file + self._qml_url = "UMOUpgradeSelectionMachineAction.qml" + + heatedBedChanged = pyqtSignal() + + @pyqtProperty(bool, notify = heatedBedChanged) + def hasHeatedBed(self): + global_container_stack = Application.getInstance().getGlobalContainerStack() + return global_container_stack.getProperty("machine_heated_bed", "value") + + @pyqtSlot() + def addHeatedBed(self): + global_container_stack = Application.getInstance().getGlobalContainerStack() + if global_container_stack: + variant = global_container_stack.findContainer({"type": "variant"}) + if variant: + if variant.getId() == "empty_variant": + variant_index = global_container_stack.getContainerIndex(variant) + stack_name = global_container_stack.getName() + new_variant = UM.Settings.InstanceContainer(stack_name + "_variant") + new_variant.addMetaDataEntry("type", "variant") + new_variant.setDefinition(global_container_stack.getBottom()) + UM.Settings.ContainerRegistry.getInstance().addContainer(new_variant) + global_container_stack.replaceContainer(variant_index, new_variant) + variant = new_variant + variant.setProperty("machine_heated_bed", "value", True) + self.heatedBedChanged.emit() + + @pyqtSlot() + def removeHeatedBed(self): + global_container_stack = Application.getInstance().getGlobalContainerStack() + if global_container_stack: + variant = global_container_stack.findContainer({"type": "variant"}) + if variant: + variant.setProperty("machine_heated_bed", "value", False) + self.heatedBedChanged.emit() \ No newline at end of file diff --git a/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml b/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml index bc0f1fe3b4..53a7e47a6b 100644 --- a/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml +++ b/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml @@ -19,5 +19,21 @@ Cura.MachineAction anchors.fill: parent; UM.I18nCatalog { id: catalog; name: "cura"; } + Column + { + anchors.fill: parent; + Label + { + width: parent.width + wrapMode: Text.WordWrap + text: catalog.i18nc("@label","Please select any upgrades made to this ultimaker original"); + } + CheckBox + { + text: catalog.i18nc("@label", "Self-built heated bed") + checked: manager.hasHeatedBed + onClicked: manager.hasHeatedBed ? manager.removeHeatedBed() : manager.addHeatedBed() + } + } } } \ No newline at end of file diff --git a/plugins/UltimakerMachineActions/__init__.py b/plugins/UltimakerMachineActions/__init__.py index 39734d00e3..fb0b2b1f64 100644 --- a/plugins/UltimakerMachineActions/__init__.py +++ b/plugins/UltimakerMachineActions/__init__.py @@ -4,6 +4,7 @@ from . import BedLevelMachineAction from . import UpgradeFirmwareMachineAction from . import UMOCheckupMachineAction +from . import UMOUpgradeSelection from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") @@ -20,4 +21,4 @@ def getMetaData(): } def register(app): - return { "machine_action": [BedLevelMachineAction.BedLevelMachineAction(), UpgradeFirmwareMachineAction.UpgradeFirmwareMachineAction(), UMOCheckupMachineAction.UMOCheckupMachineAction()]} + return { "machine_action": [BedLevelMachineAction.BedLevelMachineAction(), UpgradeFirmwareMachineAction.UpgradeFirmwareMachineAction(), UMOCheckupMachineAction.UMOCheckupMachineAction(), UMOUpgradeSelection.UMOUpgradeSelection()]} diff --git a/resources/definitions/ultimaker_original.def.json b/resources/definitions/ultimaker_original.def.json index e95431c99e..60eb8dcd00 100644 --- a/resources/definitions/ultimaker_original.def.json +++ b/resources/definitions/ultimaker_original.def.json @@ -14,7 +14,7 @@ "has_materials": true, "preferred_material": "*pla*", "preferred_quality": "*normal*", - "supported_actions":["UMOCheckup", "UpgradeFirmware", "BedLevel"] + "supported_actions":["UMOCheckup", "UpgradeFirmware", "BedLevel", "UMOUpgradeSelection"] }, "overrides": { From df3275a10a7b64f4622abd1e76e95cd9a537a05b Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Mon, 4 Jul 2016 11:43:27 +0200 Subject: [PATCH 056/236] Update Machine and Profile pages with the changes made to ManagementPage Since all the individual properties are removed and we now just need to handle the buttons ourself. Contributes to CURA-342 --- resources/qml/Preferences/MachinesPage.qml | 37 +++++++-- resources/qml/Preferences/ProfilesPage.qml | 94 +++++++++++++--------- 2 files changed, 84 insertions(+), 47 deletions(-) diff --git a/resources/qml/Preferences/MachinesPage.qml b/resources/qml/Preferences/MachinesPage.qml index dd9a7b42a7..87b4a9e540 100644 --- a/resources/qml/Preferences/MachinesPage.qml +++ b/resources/qml/Preferences/MachinesPage.qml @@ -27,14 +27,35 @@ UM.ManagementPage return -1; } - onAddObject: Printer.requestAddPrinter() - onRemoveObject: confirmDialog.open(); - onRenameObject: renameDialog.open(); - onActivateObject: Cura.MachineManager.setActiveMachine(base.currentItem.id) - - removeEnabled: base.currentItem != null && model.rowCount() > 1 - renameEnabled: base.currentItem != null - activateEnabled: base.currentItem != null && base.currentItem.id != Cura.MachineManager.activeMachineId + buttons: [ + Button + { + text: catalog.i18nc("@action:button", "Activate"); + iconName: "list-activate"; + enabled: base.currentItem != null && base.currentItem.id != Cura.MachineManager.activeMaterialId + onClicked: Cura.MachineManager.setActiveMachine(base.currentItem.id) + }, + Button + { + text: catalog.i18nc("@action:button", "Add"); + iconName: "list-add"; + onClicked: Printer.requestAddPrinter() + }, + Button + { + text: catalog.i18nc("@action:button", "Remove"); + iconName: "list-remove"; + enabled: base.currentItem != null && model.rowCount() > 1 + onClicked: confirmDialog.open(); + }, + Button + { + text: catalog.i18nc("@action:button", "Rename"); + iconName: "edit-rename"; + enabled: base.currentItem != null + onClicked: renameDialog.open(); + } + ] Item { diff --git a/resources/qml/Preferences/ProfilesPage.qml b/resources/qml/Preferences/ProfilesPage.qml index 1f90d7c889..d6dd66e8a8 100644 --- a/resources/qml/Preferences/ProfilesPage.qml +++ b/resources/qml/Preferences/ProfilesPage.qml @@ -13,7 +13,6 @@ UM.ManagementPage id: base; title: catalog.i18nc("@title:tab", "Profiles"); - addText: base.currentItem && (base.currentItem.id == Cura.MachineManager.activeQualityId) ? catalog.i18nc("@label", "Create") : catalog.i18nc("@label", "Duplicate") model: UM.InstanceContainersModel { @@ -60,27 +59,62 @@ UM.ManagementPage return -1; } - onActivateObject: Cura.MachineManager.setActiveQuality(currentItem.id) - onAddObject: { - var selectedContainer; - if (objectList.currentItem.id == Cura.MachineManager.activeQualityId) { - selectedContainer = Cura.MachineManager.newQualityContainerFromQualityAndUser(); - } else { - selectedContainer = Cura.MachineManager.duplicateContainer(base.currentItem.id); + buttons: [ + Button + { + text: catalog.i18nc("@action:button", "Activate"); + iconName: "list-activate"; + enabled: base.currentItem != null ? base.currentItem.id != Cura.MachineManager.activeQualityId : false; + onClicked: Cura.MachineManager.setActiveQuality(base.currentItem.id) + }, + Button + { + text: base.currentItem && (base.currentItem.id == Cura.MachineManager.activeQualityId) ? catalog.i18nc("@label", "Create") : catalog.i18nc("@label", "Duplicate") + iconName: "list-add"; + + onClicked: + { + var selectedContainer; + if (objectList.currentItem.id == Cura.MachineManager.activeQualityId) { + selectedContainer = Cura.MachineManager.newQualityContainerFromQualityAndUser(); + } else { + selectedContainer = Cura.MachineManager.duplicateContainer(base.currentItem.id); + } + base.selectContainer(selectedContainer); + + renameDialog.removeWhenRejected = true; + renameDialog.open(); + renameDialog.selectText(); + } + }, + Button + { + text: catalog.i18nc("@action:button", "Remove"); + iconName: "list-remove"; + enabled: base.currentItem != null ? !base.currentItem.readOnly : false; + onClicked: confirmDialog.open(); + }, + Button + { + text: catalog.i18nc("@action:button", "Rename"); + iconName: "edit-rename"; + enabled: base.currentItem != null ? !base.currentItem.readOnly : false; + onClicked: { renameDialog.removeWhenRejected = false; renameDialog.open(); renameDialog.selectText(); } + }, + Button + { + text: catalog.i18nc("@action:button", "Import"); + iconName: "document-import"; + onClicked: importDialog.open(); + }, + Button + { + text: catalog.i18nc("@action:button", "Export") + iconName: "document-export" + onClicked: exportDialog.open() + enabled: currentItem != null } - base.selectContainer(selectedContainer); - - renameDialog.removeWhenRejected = true; - renameDialog.open(); - renameDialog.selectText(); - } - onRemoveObject: confirmDialog.open(); - onRenameObject: { renameDialog.removeWhenRejected = false; renameDialog.open(); renameDialog.selectText(); } - - activateEnabled: currentItem != null ? currentItem.id != Cura.MachineManager.activeQualityId : false; - addEnabled: currentItem != null; - removeEnabled: currentItem != null ? !currentItem.readOnly : false; - renameEnabled: currentItem != null ? !currentItem.readOnly : false; + ] scrollviewCaption: catalog.i18nc("@label %1 is printer name","Printer: %1").arg(Cura.MachineManager.activeMachineName) @@ -211,24 +245,6 @@ UM.ManagementPage } } - buttons: Row { - - Button - { - text: catalog.i18nc("@action:button", "Import"); - iconName: "document-import"; - onClicked: importDialog.open(); - } - - Button - { - text: catalog.i18nc("@action:button", "Export") - iconName: "document-export" - onClicked: exportDialog.open() - enabled: currentItem != null - } - } - Item { UM.I18nCatalog { id: catalog; name: "uranium"; } From a1a751831708804d3e94fbb6824be9749f84d3a2 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 4 Jul 2016 11:49:27 +0200 Subject: [PATCH 057/236] Switch materials/nozzle when the printer signals a material/nozzle change CURA-491 --- cura/MachineManagerModel.py | 50 ++++++++++++++++++++++++++++++++++--- cura/PrinterOutputDevice.py | 36 ++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 3 deletions(-) diff --git a/cura/MachineManagerModel.py b/cura/MachineManagerModel.py index 76786efe61..aa8b08cf7b 100644 --- a/cura/MachineManagerModel.py +++ b/cura/MachineManagerModel.py @@ -4,6 +4,7 @@ from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal from UM.Application import Application from UM.Preferences import Preferences +from UM.Logger import Logger import UM.Settings from UM.Settings.Validator import ValidatorState @@ -51,6 +52,7 @@ class MachineManagerModel(QObject): active_machine_id = Preferences.getInstance().getValue("cura/active_machine") + self._printer_output_devices = [] Application.getInstance().getOutputDeviceManager().outputDevicesChanged.connect(self._onOutputDevicesChanged) if active_machine_id != "": @@ -72,8 +74,53 @@ class MachineManagerModel(QObject): outputDevicesChanged = pyqtSignal() def _onOutputDevicesChanged(self): + for printer_output_device in self._printer_output_devices: + printer_output_device.HotendIdChanged.disconnect(self._onHotendIdChanged) + printer_output_device.MaterialIdChanged.disconnect(self._onMaterialIdChanged) + + self._printer_output_devices.clear() + + for printer_output_device in Application.getInstance().getOutputDeviceManager().getOutputDevices(): + if isinstance(printer_output_device, PrinterOutputDevice): + self._printer_output_devices.append(printer_output_device) + printer_output_device.HotendIdChanged.connect(self._onHotendIdChanged) + printer_output_device.MaterialIdChanged.connect(self._onMaterialIdChanged) + self.outputDevicesChanged.emit() + @pyqtProperty("QVariantList", notify = outputDevicesChanged) + def printerOutputDevices(self): + return self._printer_output_devices + + def _onHotendIdChanged(self, index, hotend_id): + if not self._global_container_stack: + return + + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "variant", definition = self._global_container_stack.getBottom().getId(), name = hotend_id) + if containers: + ExtruderManager.ExtruderManager.getInstance().setActiveExtruderIndex(index) + Logger.log("d", "Setting hotend variant of hotend %d to %s" % (index, containers[0].getId())) + self._updateVariantContainer(containers[0]) + + def _onMaterialIdChanged(self, index, material_id): + # TODO: fix this + if not self._global_container_stack: + return + + if self._global_container_stack.getMetaDataEntry("has_machine_materials", False): + definition_id = "fdmprinter" + else: + definition_id = self._global_container_stack.getBottom().getId() + + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "material", defintion = definition_id, GUID = material_id) + if containers: + ExtruderManager.ExtruderManager.getInstance().setActiveExtruderIndex(index) + Logger.log("d", "Setting material of hotend %d to %s" % (index, containers[0].getId())) + self._updateMaterialContainer(containers[0]) + else: + Logger.log("w", "No material definition found for printer definition %s and GUID %s" % (definition_id, material_id)) + + def _onGlobalPropertyChanged(self, key, property_name): if property_name == "value": self.globalValueChanged.emit() @@ -165,9 +212,6 @@ class MachineManagerModel(QObject): Application.getInstance().setGlobalContainerStack(new_global_stack) - @pyqtProperty("QVariantList", notify = outputDevicesChanged) - def printerOutputDevices(self): - return [printer_output_device for printer_output_device in Application.getInstance().getOutputDeviceManager().getOutputDevices() if isinstance(printer_output_device, PrinterOutputDevice)] ## Create a name that is not empty and unique # \param container_type \type{string} Type of the container (machine, quality, ...) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 7f6e51e1fd..6504de9cdd 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -24,6 +24,8 @@ class PrinterOutputDevice(QObject, OutputDevice): self._num_extruders = 1 self._hotend_temperatures = [0] * self._num_extruders self._target_hotend_temperatures = [0] * self._num_extruders + self._material_ids = [""] * self._num_extruders + self._hotend_ids = [""] * self._num_extruders self._progress = 0 self._head_x = 0 self._head_y = 0 @@ -57,6 +59,12 @@ class PrinterOutputDevice(QObject, OutputDevice): # Signal to be emitted when head position is changed (x,y,z) headPositionChanged = pyqtSignal() + # Signal to be emitted when either of the material ids is changed + MaterialIdChanged = pyqtSignal(int, str, arguments = ["index", "id"]) + + # Signal to be emitted when either of the hotend ids is changed + HotendIdChanged = pyqtSignal(int, str, arguments = ["index", "id"]) + # Signal that is emitted every time connection state is changed. # it also sends it's own device_id (for convenience sake) connectionStateChanged = pyqtSignal(str) @@ -212,6 +220,34 @@ class PrinterOutputDevice(QObject, OutputDevice): self._hotend_temperatures[index] = temperature self.hotendTemperaturesChanged.emit() + @pyqtProperty("QVariantList", notify = MaterialIdChanged) + def materialIds(self): + return self._material_ids + + ## Protected setter for the current material id. + # /param index Index of the extruder + # /param material_id id of the material + def _setMaterialId(self, index, material_id): + if material_id and material_id != "" and material_id != self._material_ids[index]: + Logger.log("d", "Setting material id of hotend %d to %s" % (index, material_id)) + self._material_ids[index] = material_id + self.MaterialIdChanged.emit(index, material_id) + + + @pyqtProperty("QVariantList", notify = HotendIdChanged) + def hotendIds(self): + return self._hotend_ids + + ## Protected setter for the current hotend id. + # /param index Index of the extruder + # /param hotend_id id of the hotend + def _setHotendId(self, index, hotend_id): + if hotend_id and hotend_id != "" and hotend_id != self._hotend_ids[index]: + Logger.log("d", "Setting hotend id of hotend %d to %s" % (index, hotend_id)) + self._hotend_ids[index] = hotend_id + self.HotendIdChanged.emit(index, hotend_id) + + ## Attempt to establish connection def connect(self): raise NotImplementedError("connect needs to be implemented") From 8e58e88511057e11fb92556edb47bbeb0cba69cf Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 4 Jul 2016 11:50:15 +0200 Subject: [PATCH 058/236] Update GUIDs to match the GUIDs in the official repository CURA-491 --- resources/materials/generic_abs.xml.fdm_material | 2 +- resources/materials/generic_cpe.xml.fdm_material | 2 +- resources/materials/generic_pla.xml.fdm_material | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/materials/generic_abs.xml.fdm_material b/resources/materials/generic_abs.xml.fdm_material index 654b06d221..82b2f1f963 100644 --- a/resources/materials/generic_abs.xml.fdm_material +++ b/resources/materials/generic_abs.xml.fdm_material @@ -9,7 +9,7 @@ Generic PLA profile. Serves as an example file, data in this file is not correct ABS Generic - 506c9f0d-e3aa-4bd4-b2d2-23e2425b1aa9 + 60636bb4-518f-42e7-8237-fe77b194ebe0 0 #FF0000 diff --git a/resources/materials/generic_cpe.xml.fdm_material b/resources/materials/generic_cpe.xml.fdm_material index bbe6e328d2..8ac4dd8c71 100644 --- a/resources/materials/generic_cpe.xml.fdm_material +++ b/resources/materials/generic_cpe.xml.fdm_material @@ -9,7 +9,7 @@ Generic PLA profile. Serves as an example file, data in this file is not correct CPE Generic - 506c9f0d-e3aa-4bd4-b2d2-23e2425b1aa9 + 12f41353-1a33-415e-8b4f-a775a6c70cc6 0 #0000FF diff --git a/resources/materials/generic_pla.xml.fdm_material b/resources/materials/generic_pla.xml.fdm_material index 40432d5849..2f8fa165cf 100644 --- a/resources/materials/generic_pla.xml.fdm_material +++ b/resources/materials/generic_pla.xml.fdm_material @@ -9,7 +9,7 @@ Generic PLA profile. Serves as an example file, data in this file is not correct PLA Generic - 506c9f0d-e3aa-4bd4-b2d2-23e2425b1aa9 + 86a89ceb-4159-47f6-ab97-e9953803d70f 0 #00FF00 From 0e28b331fb3f0949f01834c924bc9d439f96aa57 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Mon, 4 Jul 2016 12:07:29 +0200 Subject: [PATCH 059/236] Move Cura setting related classes to cura/Settings Contributes to CURA-342 --- cura/{ => Settings}/ContainerManager.py | 0 cura/{ => Settings}/ContainerSettingsModel.py | 0 cura/{ => Settings}/CuraContainerRegistry.py | 0 cura/{ => Settings}/ExtruderManager.py | 0 cura/{ => Settings}/ExtrudersModel.py | 0 cura/{ => Settings}/SettingOverrideDecorator.py | 0 cura/Settings/__init__.py | 9 +++++++++ 7 files changed, 9 insertions(+) rename cura/{ => Settings}/ContainerManager.py (100%) rename cura/{ => Settings}/ContainerSettingsModel.py (100%) rename cura/{ => Settings}/CuraContainerRegistry.py (100%) rename cura/{ => Settings}/ExtruderManager.py (100%) rename cura/{ => Settings}/ExtrudersModel.py (100%) rename cura/{ => Settings}/SettingOverrideDecorator.py (100%) diff --git a/cura/ContainerManager.py b/cura/Settings/ContainerManager.py similarity index 100% rename from cura/ContainerManager.py rename to cura/Settings/ContainerManager.py diff --git a/cura/ContainerSettingsModel.py b/cura/Settings/ContainerSettingsModel.py similarity index 100% rename from cura/ContainerSettingsModel.py rename to cura/Settings/ContainerSettingsModel.py diff --git a/cura/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py similarity index 100% rename from cura/CuraContainerRegistry.py rename to cura/Settings/CuraContainerRegistry.py diff --git a/cura/ExtruderManager.py b/cura/Settings/ExtruderManager.py similarity index 100% rename from cura/ExtruderManager.py rename to cura/Settings/ExtruderManager.py diff --git a/cura/ExtrudersModel.py b/cura/Settings/ExtrudersModel.py similarity index 100% rename from cura/ExtrudersModel.py rename to cura/Settings/ExtrudersModel.py diff --git a/cura/SettingOverrideDecorator.py b/cura/Settings/SettingOverrideDecorator.py similarity index 100% rename from cura/SettingOverrideDecorator.py rename to cura/Settings/SettingOverrideDecorator.py diff --git a/cura/Settings/__init__.py b/cura/Settings/__init__.py index d6fe389478..c3d45dc81e 100644 --- a/cura/Settings/__init__.py +++ b/cura/Settings/__init__.py @@ -1,2 +1,11 @@ +# Copyright (c) 2016 Ultimaker B.V. +# Cura is released under the terms of the AGPLv3 or higher. from .MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler +from .ContainerManager import ContainerManager +from .ContainerSettingsModel import ContainerSettingsModel +from .CuraContainerRegistry import CuraContainerRegistry +from .ExtruderManager import ExtruderManager +from .ExtrudersModel import ExtrudersModel +from .MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler +from .SettingOverrideDecorator import SettingOverrideDecorator From a882c43b2c399899f01c8e475b54ec75f998f3d1 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Mon, 4 Jul 2016 12:08:08 +0200 Subject: [PATCH 060/236] Move MachineManager to cura/Settings and rename it to MachineManager Contributes to CURA-342 --- .../MachineManager.py} | 34 +++++++++---------- cura/Settings/__init__.py | 1 + 2 files changed, 18 insertions(+), 17 deletions(-) rename cura/{MachineManagerModel.py => Settings/MachineManager.py} (95%) diff --git a/cura/MachineManagerModel.py b/cura/Settings/MachineManager.py similarity index 95% rename from cura/MachineManagerModel.py rename to cura/Settings/MachineManager.py index 76786efe61..6ab19509ba 100644 --- a/cura/MachineManagerModel.py +++ b/cura/Settings/MachineManager.py @@ -2,21 +2,20 @@ # Cura is released under the terms of the AGPLv3 or higher. from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal + from UM.Application import Application from UM.Preferences import Preferences import UM.Settings -from UM.Settings.Validator import ValidatorState -from UM.Settings.InstanceContainer import InstanceContainer from cura.PrinterOutputDevice import PrinterOutputDevice -from UM.Settings.ContainerStack import ContainerStack + from . import ExtruderManager + from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") - -class MachineManagerModel(QObject): +class MachineManager(QObject): def __init__(self, parent = None): super().__init__(parent) @@ -27,7 +26,7 @@ class MachineManagerModel(QObject): self._global_stack_valid = None self._onGlobalContainerChanged() - ExtruderManager.ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged) + ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged) self.globalContainerChanged.connect(self._onActiveExtruderStackChanged) self._onActiveExtruderStackChanged() @@ -35,13 +34,13 @@ class MachineManagerModel(QObject): self.globalContainerChanged.connect(self.activeMaterialChanged) self.globalContainerChanged.connect(self.activeVariantChanged) self.globalContainerChanged.connect(self.activeQualityChanged) - ExtruderManager.ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeMaterialChanged) - ExtruderManager.ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeVariantChanged) - ExtruderManager.ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeQualityChanged) + ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeMaterialChanged) + ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeVariantChanged) + ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeQualityChanged) self.globalContainerChanged.connect(self.activeStackChanged) self.globalValueChanged.connect(self.activeStackChanged) - ExtruderManager.ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeStackChanged) + ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeStackChanged) self._empty_variant_container = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = "empty_variant")[0] self._empty_material_container = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = "empty_material")[0] @@ -80,7 +79,7 @@ class MachineManagerModel(QObject): if property_name == "validationState": if self._global_stack_valid: changed_validation_state = self._active_container_stack.getProperty(key, property_name) - if changed_validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError): + if changed_validation_state in (UM.Settings.ValidatorState.Exception, UM.Settings.ValidatorState.MaximumError, UM.Settings.ValidatorState.MinimumError): self._global_stack_valid = False self.globalValidationChanged.emit() else: @@ -109,7 +108,7 @@ class MachineManagerModel(QObject): self._active_container_stack.containersChanged.disconnect(self._onInstanceContainersChanged) self._active_container_stack.propertyChanged.disconnect(self._onGlobalPropertyChanged) - self._active_container_stack = ExtruderManager.ExtruderManager.getInstance().getActiveExtruderStack() + self._active_container_stack = ExtruderManager.getInstance().getActiveExtruderStack() if self._active_container_stack: self._active_container_stack.containersChanged.connect(self._onInstanceContainersChanged) self._active_container_stack.propertyChanged.connect(self._onGlobalPropertyChanged) @@ -161,7 +160,7 @@ class MachineManagerModel(QObject): new_global_stack.addContainer(quality_instance_container) new_global_stack.addContainer(current_settings_instance_container) - ExtruderManager.ExtruderManager.getInstance().addMachineExtruders(definition) + ExtruderManager.getInstance().addMachineExtruders(definition) Application.getInstance().setGlobalContainerStack(new_global_stack) @@ -185,7 +184,7 @@ class MachineManagerModel(QObject): for key in stack.getAllKeys(): validation_state = stack.getProperty(key, "validationState") - if validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError): + if validation_state in (UM.Settings.ValidatorState.Exception, UM.Settings.ValidatorState.MaximumError, UM.Settings.ValidatorState.MinimumError): return True return False @@ -502,6 +501,10 @@ class MachineManagerModel(QObject): if containers: return containers[0].getBottom().getId() + @staticmethod + def createMachineManager(engine, script_engine): + return MachineManager() + def _updateVariantContainer(self, definition): if not definition.getMetaDataEntry("has_variants"): return self._empty_variant_container @@ -587,6 +590,3 @@ class MachineManagerModel(QObject): return containers[0] return self._empty_quality_container - -def createMachineManagerModel(engine, script_engine): - return MachineManagerModel() diff --git a/cura/Settings/__init__.py b/cura/Settings/__init__.py index c3d45dc81e..da8f36c040 100644 --- a/cura/Settings/__init__.py +++ b/cura/Settings/__init__.py @@ -7,5 +7,6 @@ from .ContainerSettingsModel import ContainerSettingsModel from .CuraContainerRegistry import CuraContainerRegistry from .ExtruderManager import ExtruderManager from .ExtrudersModel import ExtrudersModel +from .MachineManager import MachineManager from .MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler from .SettingOverrideDecorator import SettingOverrideDecorator From ce3a5f7e483a831c0f0536aaee6a15861805baaf Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Mon, 4 Jul 2016 12:08:38 +0200 Subject: [PATCH 061/236] Update references to moved classes so things work again Contributes to CURA-342 --- cura/CuraApplication.py | 19 +++++++------------ cura/Settings/ContainerManager.py | 5 +++-- cura/Settings/ContainerSettingsModel.py | 2 +- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 3664563590..4883194803 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -32,8 +32,6 @@ from UM.Settings.ContainerRegistry import ContainerRegistry from UM.i18n import i18nCatalog -from . import ExtruderManager -from . import ExtrudersModel from . import PlatformPhysics from . import BuildVolume from . import CameraAnimation @@ -42,11 +40,8 @@ from . import CuraActions from . import MultiMaterialDecorator from . import ZOffsetDecorator from . import CuraSplashScreen -from . import MachineManagerModel -from . import ContainerSettingsModel from . import CameraImageProvider from . import MachineActionManager -from . import ContainerManager import cura.Settings @@ -373,9 +368,9 @@ class CuraApplication(QtApplication): self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Loading interface...")) # Initialise extruder so as to listen to global container stack changes before the first global container stack is set. - ExtruderManager.ExtruderManager.getInstance() - qmlRegisterSingletonType(MachineManagerModel.MachineManagerModel, "Cura", 1, 0, "MachineManager", - MachineManagerModel.createMachineManagerModel) + cura.Settings.ExtruderManager.getInstance() + qmlRegisterSingletonType(cura.Settings.MachineManager, "Cura", 1, 0, "MachineManager", + cura.Settings.MachineManager.createMachineManager) qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, "MachineActionManager", self.getMachineActionManager) self.setMainQml(Resources.getPath(self.ResourceTypes.QmlFiles, "Cura.qml")) @@ -426,16 +421,16 @@ class CuraApplication(QtApplication): qmlRegisterUncreatableType(CuraApplication, "Cura", 1, 0, "ResourceTypes", "Just an Enum type") - qmlRegisterType(ExtrudersModel.ExtrudersModel, "Cura", 1, 0, "ExtrudersModel") + qmlRegisterType(cura.Settings.ExtrudersModel, "Cura", 1, 0, "ExtrudersModel") - qmlRegisterType(ContainerSettingsModel.ContainerSettingsModel, "Cura", 1, 0, "ContainerSettingsModel") + qmlRegisterType(cura.Settings.ContainerSettingsModel, "Cura", 1, 0, "ContainerSettingsModel") qmlRegisterType(cura.Settings.MaterialSettingsVisibilityHandler, "Cura", 1, 0, "MaterialSettingsVisibilityHandler") - qmlRegisterSingletonType(ContainerManager.ContainerManager, "Cura", 1, 0, "ContainerManager", ContainerManager.createContainerManager) + qmlRegisterSingletonType(cura.Settings.ContainerManager, "Cura", 1, 0, "ContainerManager", cura.Settings.ContainerManager.createContainerManager) qmlRegisterSingletonType(QUrl.fromLocalFile(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Actions.qml")), "Cura", 1, 0, "Actions") - engine.rootContext().setContextProperty("ExtruderManager", ExtruderManager.ExtruderManager.getInstance()) + engine.rootContext().setContextProperty("ExtruderManager", cura.Settings.ExtruderManager.getInstance()) for path in Resources.getAllResourcesOfType(CuraApplication.ResourceTypes.QmlFiles): type_name = os.path.splitext(os.path.basename(path))[0] diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index b2b8e36770..c3ee7e1645 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -58,5 +58,6 @@ class ContainerManager(QObject): containers[0].setMetaDataEntry(entry_name, entry_value) -def createContainerManager(engine, js_engine): - return ContainerManager() + @staticmethod + def createContainerManager(engine, js_engine): + return ContainerManager() diff --git a/cura/Settings/ContainerSettingsModel.py b/cura/Settings/ContainerSettingsModel.py index 2ff1a5f401..9ec19ed7fb 100644 --- a/cura/Settings/ContainerSettingsModel.py +++ b/cura/Settings/ContainerSettingsModel.py @@ -90,4 +90,4 @@ class ContainerSettingsModel(ListModel): containersChanged = pyqtSignal() @pyqtProperty("QVariantList", fset = setContainers, notify = containersChanged) def containers(self): - return self.container_ids \ No newline at end of file + return self.container_ids From f679b1ef6835db53d7c9739347e6a420e7803797 Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Mon, 4 Jul 2016 12:24:26 +0200 Subject: [PATCH 062/236] JSON fix: merge went wrong: forgotten comma --- resources/definitions/fdmprinter.def.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index b843f6c194..b5d3796e74 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -1040,7 +1040,7 @@ "default_value": false, "enabled": "retraction_enable", "settable_per_mesh": false, - "settable_per_extruder": true + "settable_per_extruder": true, "children": { "retraction_hop_only_when_collides": { "label": "Z Hop Only Over Printed Parts", From 6933430185ffc93ae3dcbfa7d110c93561c636b3 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 4 Jul 2016 12:47:16 +0200 Subject: [PATCH 063/236] Flow MachineActions on the manage Printes page. CURA-1385 --- resources/qml/Preferences/MachinesPage.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/qml/Preferences/MachinesPage.qml b/resources/qml/Preferences/MachinesPage.qml index e214034659..74089ec9e6 100644 --- a/resources/qml/Preferences/MachinesPage.qml +++ b/resources/qml/Preferences/MachinesPage.qml @@ -50,10 +50,11 @@ UM.ManagementPage elide: Text.ElideRight } - Row + Flow { id: machineActions anchors.left: parent.left + anchors.right: parent.right anchors.top: machineName.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height From 6fbe354839eaeae4bd67b91c848016bae02448d6 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Mon, 4 Jul 2016 13:03:10 +0200 Subject: [PATCH 064/236] Fix ExtrudersModel to use the right location for ExtruderManager Contributes to CURA-342 --- cura/Settings/ExtrudersModel.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cura/Settings/ExtrudersModel.py b/cura/Settings/ExtrudersModel.py index c8c5a21274..0f2511452a 100644 --- a/cura/Settings/ExtrudersModel.py +++ b/cura/Settings/ExtrudersModel.py @@ -3,9 +3,10 @@ from PyQt5.QtCore import Qt, pyqtSignal, pyqtProperty -import cura.ExtruderManager import UM.Qt.ListModel +from . import ExtruderManager + ## Model that holds extruders. # # This model is designed for use by any list of extruders, but specifically @@ -49,7 +50,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): self._active_extruder_stack = None #Listen to changes. - manager = cura.ExtruderManager.ExtruderManager.getInstance() + manager = ExtruderManager.getInstance() manager.extrudersChanged.connect(self._updateExtruders) #When the list of extruders changes in general. UM.Application.getInstance().globalContainerStackChanged.connect(self._updateExtruders) #When the current machine changes. self._updateExtruders() @@ -69,7 +70,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): return self._add_global def _onActiveExtruderChanged(self): - manager = cura.ExtruderManager.ExtruderManager.getInstance() + manager = ExtruderManager.getInstance() active_extruder_stack = manager.getActiveExtruderStack() if self._active_extruder_stack != active_extruder_stack: if self._active_extruder_stack: @@ -93,7 +94,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): # This should be called whenever the list of extruders changes. def _updateExtruders(self): self.clear() - manager = cura.ExtruderManager.ExtruderManager.getInstance() + manager = ExtruderManager.getInstance() global_container_stack = UM.Application.getInstance().getGlobalContainerStack() if not global_container_stack: return #There is no machine to get the extruders of. From 0e098e38ebd9b96ca5f87512d89a4d7896f0f7ab Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 4 Jul 2016 13:10:29 +0200 Subject: [PATCH 065/236] More fixes for changed setting object location --- cura_app.py | 4 ++-- plugins/CuraEngineBackend/CuraEngineBackend.py | 2 +- plugins/CuraEngineBackend/StartSliceJob.py | 2 +- plugins/SolidView/SolidView.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cura_app.py b/cura_app.py index 3548acedb6..4df40344b9 100755 --- a/cura_app.py +++ b/cura_app.py @@ -35,7 +35,7 @@ sys.excepthook = exceptHook import Arcus #@UnusedImport from UM.Platform import Platform import cura.CuraApplication -import cura.CuraContainerRegistry +import cura.Settings.CuraContainerRegistry if Platform.isWindows() and hasattr(sys, "frozen"): dirpath = os.path.expanduser("~/AppData/Local/cura/") @@ -44,7 +44,7 @@ if Platform.isWindows() and hasattr(sys, "frozen"): sys.stderr = open(os.path.join(dirpath, "stderr.log"), "w") # Force an instance of CuraContainerRegistry to be created and reused later. -cura.CuraContainerRegistry.CuraContainerRegistry.getInstance() +cura.Settings.CuraContainerRegistry.getInstance() app = cura.CuraApplication.CuraApplication.getInstance() app.run() diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index c91e414a13..d222e37bd9 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -13,7 +13,7 @@ from UM.Resources import Resources from UM.Settings.Validator import ValidatorState #To find if a setting is in an error state. We can't slice then. from UM.Platform import Platform -from cura.ExtruderManager import ExtruderManager +from cura.Settings.ExtruderManager import ExtruderManager from cura.OneAtATimeIterator import OneAtATimeIterator from . import ProcessSlicedLayersJob diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index 3d2eb0ed4a..6ed103d0b5 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -15,7 +15,7 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Settings.Validator import ValidatorState from cura.OneAtATimeIterator import OneAtATimeIterator -from cura.ExtruderManager import ExtruderManager +from cura.Settings.ExtruderManager import ExtruderManager class StartJobResult(IntEnum): Finished = 1 diff --git a/plugins/SolidView/SolidView.py b/plugins/SolidView/SolidView.py index ddbc06d119..2af4108f23 100644 --- a/plugins/SolidView/SolidView.py +++ b/plugins/SolidView/SolidView.py @@ -10,7 +10,7 @@ from UM.View.Renderer import Renderer from UM.View.GL.OpenGL import OpenGL -from cura.ExtrudersModel import ExtrudersModel +from cura.Settings.ExtrudersModel import ExtrudersModel import math From 90fa0f05645c7e37577ea14d675deb5aba97bf4b Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 4 Jul 2016 13:14:40 +0200 Subject: [PATCH 066/236] Make Select Upgrades page look like the other pages CURA-1385 --- .../UMOUpgradeSelectionMachineAction.qml | 49 ++++++++++++------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml b/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml index 53a7e47a6b..1381daa270 100644 --- a/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml +++ b/plugins/UltimakerMachineActions/UMOUpgradeSelectionMachineAction.qml @@ -15,25 +15,38 @@ Cura.MachineAction anchors.fill: parent; Item { - id: bedLevelMachineAction - anchors.fill: parent; + id: upgradeSelectionMachineAction + anchors.fill: parent + + Label + { + id: pageTitle + width: parent.width + text: catalog.i18nc("@title", "Check Printer") + wrapMode: Text.WordWrap + font.pointSize: 18; + } + + Label + { + id: pageDescription + anchors.top: pageTitle.bottom + anchors.topMargin: UM.Theme.getSize("default_margin").height + width: parent.width + wrapMode: Text.WordWrap + text: catalog.i18nc("@label","Please select any upgrades made to this Ultimaker Original"); + } + + CheckBox + { + anchors.top: pageDescription.bottom + anchors.topMargin: UM.Theme.getSize("default_margin").height + + text: catalog.i18nc("@label", "Self-built heated bed") + checked: manager.hasHeatedBed + onClicked: manager.hasHeatedBed ? manager.removeHeatedBed() : manager.addHeatedBed() + } UM.I18nCatalog { id: catalog; name: "cura"; } - Column - { - anchors.fill: parent; - Label - { - width: parent.width - wrapMode: Text.WordWrap - text: catalog.i18nc("@label","Please select any upgrades made to this ultimaker original"); - } - CheckBox - { - text: catalog.i18nc("@label", "Self-built heated bed") - checked: manager.hasHeatedBed - onClicked: manager.hasHeatedBed ? manager.removeHeatedBed() : manager.addHeatedBed() - } - } } } \ No newline at end of file From 6f3fa19890b7708eb40f2b6039de7c6ba95761e0 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Mon, 4 Jul 2016 13:13:44 +0200 Subject: [PATCH 067/236] Fix up plugins that use ExtruderManager Since it has been moved to cura.Settings --- plugins/CuraEngineBackend/CuraEngineBackend.py | 8 ++++---- plugins/CuraEngineBackend/StartSliceJob.py | 7 ++++--- plugins/SolidView/SolidView.py | 4 ++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index d222e37bd9..a70683bd21 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -13,7 +13,7 @@ from UM.Resources import Resources from UM.Settings.Validator import ValidatorState #To find if a setting is in an error state. We can't slice then. from UM.Platform import Platform -from cura.Settings.ExtruderManager import ExtruderManager +import cura.Settings from cura.OneAtATimeIterator import OneAtATimeIterator from . import ProcessSlicedLayersJob @@ -63,7 +63,7 @@ class CuraEngineBackend(Backend): self._onGlobalStackChanged() self._active_extruder_stack = None - ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderChanged) + cura.Settings.ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderChanged) self._onActiveExtruderChanged() #When you update a setting and other settings get changed through inheritance, many propertyChanged signals are fired. @@ -379,8 +379,8 @@ class CuraEngineBackend(Backend): self._active_extruder_stack.propertyChanged.disconnect(self._onSettingChanged) self._active_extruder_stack.containersChanged.disconnect(self._onChanged) - self._active_extruder_stack = ExtruderManager.getInstance().getActiveExtruderStack() + self._active_extruder_stack = cura.Settings.ExtruderManager.getInstance().getActiveExtruderStack() if self._active_extruder_stack: self._active_extruder_stack.propertyChanged.connect(self._onSettingChanged) # Note: Only starts slicing when the value changed. self._active_extruder_stack.containersChanged.connect(self._onChanged) - self._onChanged() \ No newline at end of file + self._onChanged() diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index 6ed103d0b5..cdfcecb6b9 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -15,7 +15,8 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Settings.Validator import ValidatorState from cura.OneAtATimeIterator import OneAtATimeIterator -from cura.Settings.ExtruderManager import ExtruderManager + +import cura.Settings class StartJobResult(IntEnum): Finished = 1 @@ -128,7 +129,7 @@ class StartSliceJob(Job): self._buildGlobalSettingsMessage(stack) - for extruder_stack in ExtruderManager.getInstance().getMachineExtruders(stack.getBottom().getId()): + for extruder_stack in cura.Settings.ExtruderManager.getInstance().getMachineExtruders(stack.getBottom().getId()): self._buildExtruderMessage(extruder_stack) for group in object_groups: @@ -208,4 +209,4 @@ class StartSliceJob(Job): setting = message.addRepeatedMessage("settings") setting.name = key setting.value = str(stack.getProperty(key, "value")).encode("utf-8") - Job.yieldThread() \ No newline at end of file + Job.yieldThread() diff --git a/plugins/SolidView/SolidView.py b/plugins/SolidView/SolidView.py index 2af4108f23..15cf25e65e 100644 --- a/plugins/SolidView/SolidView.py +++ b/plugins/SolidView/SolidView.py @@ -10,7 +10,7 @@ from UM.View.Renderer import Renderer from UM.View.GL.OpenGL import OpenGL -from cura.Settings.ExtrudersModel import ExtrudersModel +import cura.Settings import math @@ -24,7 +24,7 @@ class SolidView(View): self._enabled_shader = None self._disabled_shader = None - self._extruders_model = ExtrudersModel() + self._extruders_model = cura.Settings.ExtrudersModel() def beginRendering(self): scene = self.getController().getScene() From 24c5a39962d369eca85f37c1ac481d9c0e9c3358 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 4 Jul 2016 13:43:13 +0200 Subject: [PATCH 068/236] Fix logic and typo for material container filtering CURA-491 --- cura/MachineManagerModel.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cura/MachineManagerModel.py b/cura/MachineManagerModel.py index aa8b08cf7b..9a08aaa348 100644 --- a/cura/MachineManagerModel.py +++ b/cura/MachineManagerModel.py @@ -107,12 +107,11 @@ class MachineManagerModel(QObject): if not self._global_container_stack: return + definition_id = "fdmprinter" if self._global_container_stack.getMetaDataEntry("has_machine_materials", False): - definition_id = "fdmprinter" - else: definition_id = self._global_container_stack.getBottom().getId() - containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "material", defintion = definition_id, GUID = material_id) + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "material", definition = definition_id, GUID = material_id) if containers: ExtruderManager.ExtruderManager.getInstance().setActiveExtruderIndex(index) Logger.log("d", "Setting material of hotend %d to %s" % (index, containers[0].getId())) From 3d86ffd57542afa863778dc408e6b446224f4258 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 4 Jul 2016 15:14:36 +0200 Subject: [PATCH 069/236] Fix switching materials/nozzles in Cura when the materials/nozzles in the printer change CURA-491 --- cura/MachineManagerModel.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cura/MachineManagerModel.py b/cura/MachineManagerModel.py index 9a08aaa348..d9903744ee 100644 --- a/cura/MachineManagerModel.py +++ b/cura/MachineManagerModel.py @@ -98,9 +98,9 @@ class MachineManagerModel(QObject): containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "variant", definition = self._global_container_stack.getBottom().getId(), name = hotend_id) if containers: - ExtruderManager.ExtruderManager.getInstance().setActiveExtruderIndex(index) Logger.log("d", "Setting hotend variant of hotend %d to %s" % (index, containers[0].getId())) - self._updateVariantContainer(containers[0]) + ExtruderManager.ExtruderManager.getInstance().setActiveExtruderIndex(index) + self.setActiveVariant(containers[0].getId()) def _onMaterialIdChanged(self, index, material_id): # TODO: fix this @@ -113,9 +113,9 @@ class MachineManagerModel(QObject): containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "material", definition = definition_id, GUID = material_id) if containers: - ExtruderManager.ExtruderManager.getInstance().setActiveExtruderIndex(index) Logger.log("d", "Setting material of hotend %d to %s" % (index, containers[0].getId())) - self._updateMaterialContainer(containers[0]) + ExtruderManager.ExtruderManager.getInstance().setActiveExtruderIndex(index) + self.setActiveMaterial(containers[0].getId()) else: Logger.log("w", "No material definition found for printer definition %s and GUID %s" % (definition_id, material_id)) From bff23598c699ef21b21325ab800d9ef2d37bb005 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 4 Jul 2016 16:23:07 +0200 Subject: [PATCH 070/236] Fix simple mode infill to work with multiple extruders CURA-1778 --- cura/MachineManagerModel.py | 7 +++++++ resources/qml/SidebarSimple.qml | 3 ++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/cura/MachineManagerModel.py b/cura/MachineManagerModel.py index 76786efe61..7aa99e1b09 100644 --- a/cura/MachineManagerModel.py +++ b/cura/MachineManagerModel.py @@ -236,6 +236,13 @@ class MachineManagerModel(QObject): return "" + @pyqtProperty(str, notify = activeStackChanged) + def activeStackId(self): + if self._active_container_stack: + return self._active_container_stack.getId() + + return "" + @pyqtProperty(str, notify = activeMaterialChanged) def activeMaterialName(self): if self._active_container_stack: diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index 56c4385297..a393500fb7 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -371,7 +371,7 @@ Item { id: infillDensity - containerStackId: Cura.MachineManager.activeMachineId + containerStackId: Cura.MachineManager.activeStackId key: "infill_sparse_density" watchedProperties: [ "value" ] storeIndex: 0 @@ -406,6 +406,7 @@ Item watchedProperties: [ "value" ] storeIndex: 0 } + UM.SettingPropertyProvider { id: supportExtruderNr From dd249206ac15db31eda2cd0fc5990c6871619387 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 4 Jul 2016 16:33:32 +0200 Subject: [PATCH 071/236] Fix codestyle CURA-491 --- cura/MachineManagerModel.py | 8 ++++---- cura/PrinterOutputDevice.py | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cura/MachineManagerModel.py b/cura/MachineManagerModel.py index d9903744ee..92ce86804d 100644 --- a/cura/MachineManagerModel.py +++ b/cura/MachineManagerModel.py @@ -75,16 +75,16 @@ class MachineManagerModel(QObject): def _onOutputDevicesChanged(self): for printer_output_device in self._printer_output_devices: - printer_output_device.HotendIdChanged.disconnect(self._onHotendIdChanged) - printer_output_device.MaterialIdChanged.disconnect(self._onMaterialIdChanged) + printer_output_device.hotendIdChanged.disconnect(self._onHotendIdChanged) + printer_output_device.materialIdChanged.disconnect(self._onMaterialIdChanged) self._printer_output_devices.clear() for printer_output_device in Application.getInstance().getOutputDeviceManager().getOutputDevices(): if isinstance(printer_output_device, PrinterOutputDevice): self._printer_output_devices.append(printer_output_device) - printer_output_device.HotendIdChanged.connect(self._onHotendIdChanged) - printer_output_device.MaterialIdChanged.connect(self._onMaterialIdChanged) + printer_output_device.hotendIdChanged.connect(self._onHotendIdChanged) + printer_output_device.materialIdChanged.connect(self._onMaterialIdChanged) self.outputDevicesChanged.emit() diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 6504de9cdd..212ed86ab3 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -60,10 +60,10 @@ class PrinterOutputDevice(QObject, OutputDevice): headPositionChanged = pyqtSignal() # Signal to be emitted when either of the material ids is changed - MaterialIdChanged = pyqtSignal(int, str, arguments = ["index", "id"]) + materialIdChanged = pyqtSignal(int, str, arguments = ["index", "id"]) # Signal to be emitted when either of the hotend ids is changed - HotendIdChanged = pyqtSignal(int, str, arguments = ["index", "id"]) + hotendIdChanged = pyqtSignal(int, str, arguments = ["index", "id"]) # Signal that is emitted every time connection state is changed. # it also sends it's own device_id (for convenience sake) @@ -220,7 +220,7 @@ class PrinterOutputDevice(QObject, OutputDevice): self._hotend_temperatures[index] = temperature self.hotendTemperaturesChanged.emit() - @pyqtProperty("QVariantList", notify = MaterialIdChanged) + @pyqtProperty("QVariantList", notify = materialIdChanged) def materialIds(self): return self._material_ids @@ -231,10 +231,10 @@ class PrinterOutputDevice(QObject, OutputDevice): if material_id and material_id != "" and material_id != self._material_ids[index]: Logger.log("d", "Setting material id of hotend %d to %s" % (index, material_id)) self._material_ids[index] = material_id - self.MaterialIdChanged.emit(index, material_id) + self.materialIdChanged.emit(index, material_id) - @pyqtProperty("QVariantList", notify = HotendIdChanged) + @pyqtProperty("QVariantList", notify = hotendIdChanged) def hotendIds(self): return self._hotend_ids @@ -245,7 +245,7 @@ class PrinterOutputDevice(QObject, OutputDevice): if hotend_id and hotend_id != "" and hotend_id != self._hotend_ids[index]: Logger.log("d", "Setting hotend id of hotend %d to %s" % (index, hotend_id)) self._hotend_ids[index] = hotend_id - self.HotendIdChanged.emit(index, hotend_id) + self.hotendIdChanged.emit(index, hotend_id) ## Attempt to establish connection From 84b736df07e8699faa5ca8667191529d76f30486 Mon Sep 17 00:00:00 2001 From: "U-ULTIMAKER\\j.ha" Date: Mon, 4 Jul 2016 17:08:35 +0200 Subject: [PATCH 072/236] first commit in Cura! fix CURA-1085, time estimate is reset before every slice action --- plugins/CuraEngineBackend/CuraEngineBackend.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index c91e414a13..89be5fef08 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -126,6 +126,8 @@ class CuraEngineBackend(Backend): ## Perform a slice of the scene. def slice(self): + self.printDurationMessage.emit(0, 0) + self._stored_layer_data = [] if not self._enabled or not self._global_container_stack: #We shouldn't be slicing. From 318182495ae4874a6fa18b8ffbb992782f1ade80 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 4 Jul 2016 17:11:03 +0200 Subject: [PATCH 073/236] We now recieve material estimation per extruder CURA-1687 --- cura/PrintInformation.py | 21 ++++++++++--------- plugins/CuraEngineBackend/Cura.proto | 11 +++++++--- .../CuraEngineBackend/CuraEngineBackend.py | 12 +++++++---- plugins/SliceInfoPlugin/SliceInfo.py | 2 +- resources/qml/JobSpecs.qml | 2 +- 5 files changed, 29 insertions(+), 19 deletions(-) diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py index f1eb93de0e..5432da5dcc 100644 --- a/cura/PrintInformation.py +++ b/cura/PrintInformation.py @@ -44,7 +44,7 @@ class PrintInformation(QObject): self._current_print_time = Duration(None, self) - self._material_amount = -1 + self._material_amounts = [] self._backend = Application.getInstance().getBackend() if self._backend: @@ -62,21 +62,22 @@ class PrintInformation(QObject): def currentPrintTime(self): return self._current_print_time - materialAmountChanged = pyqtSignal() + materialAmountsChanged = pyqtSignal() - @pyqtProperty(float, notify = materialAmountChanged) - def materialAmount(self): - return self._material_amount + @pyqtProperty("QVariantList", notify = materialAmountsChanged) + def materialAmounts(self): + return self._material_amounts - def _onPrintDurationMessage(self, time, amount): - #if self._slice_pass == self.SlicePass.CurrentSettings: - self._current_print_time.setDuration(time) + def _onPrintDurationMessage(self, total_time, material_amounts): + self._current_print_time.setDuration(total_time) self.currentPrintTimeChanged.emit() # Material amount is sent as an amount of mm^3, so calculate length from that r = Application.getInstance().getGlobalContainerStack().getProperty("material_diameter", "value") / 2 - self._material_amount = round((amount / (math.pi * r ** 2)) / 1000, 2) - self.materialAmountChanged.emit() + self._material_amounts = [] + for amount in material_amounts: + self._material_amounts.append(round((amount / (math.pi * r ** 2)) / 1000, 2)) + self.materialAmountsChanged.emit() @pyqtSlot(str) def setJobName(self, name): diff --git a/plugins/CuraEngineBackend/Cura.proto b/plugins/CuraEngineBackend/Cura.proto index 38753fd804..5f95a4d4a8 100644 --- a/plugins/CuraEngineBackend/Cura.proto +++ b/plugins/CuraEngineBackend/Cura.proto @@ -65,10 +65,15 @@ message GCodeLayer { bytes data = 2; } -message ObjectPrintTime { // The print time for the whole print and material estimates for the first extruder + +message PrintTimeMaterialEstimates { // The print time for the whole print and material estimates for the extruder + float time = 1; // Total time estimate + repeated MaterialEstimates materialEstimates = 2; // materialEstimates data +} + +message MaterialEstimates { int64 id = 1; - float time = 2; // Total time estimate - float material_amount = 3; // material used in the first extruder + float material_amount = 2; // material used in the extruder } message SettingList { diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index c91e414a13..82815bccb7 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -79,7 +79,8 @@ class CuraEngineBackend(Backend): self._message_handlers["cura.proto.Progress"] = self._onProgressMessage self._message_handlers["cura.proto.GCodeLayer"] = self._onGCodeLayerMessage self._message_handlers["cura.proto.GCodePrefix"] = self._onGCodePrefixMessage - self._message_handlers["cura.proto.ObjectPrintTime"] = self._onObjectPrintTimeMessage + self._message_handlers["cura.proto.PrintTimeMaterialEstimates"] = self._onPrintTimeMaterialEstimates + #self._message_handlers["cura.proto.ObjectPrintTime"] = self._onObjectPrintTimeMessage self._message_handlers["cura.proto.SlicingFinished"] = self._onSlicingFinishedMessage self._start_slice_job = None @@ -294,9 +295,12 @@ class CuraEngineBackend(Backend): ## Called when a print time message is received from the engine. # # \param message The protobuf message containing the print time and - # material amount. - def _onObjectPrintTimeMessage(self, message): - self.printDurationMessage.emit(message.time, message.material_amount) + # material amount per extruder + def _onPrintTimeMaterialEstimates(self, message): + material_amounts = [] + for index in range(message.repeatedMessageCount("materialEstimates")): + material_amounts.append(message.getRepeatedMessage("materialEstimates", index).material_amount) + self.printDurationMessage.emit(message.time, material_amounts) ## Creates a new socket connection. def _createSocket(self): diff --git a/plugins/SliceInfoPlugin/SliceInfo.py b/plugins/SliceInfoPlugin/SliceInfo.py index 50b6275bf0..863eaa5166 100644 --- a/plugins/SliceInfoPlugin/SliceInfo.py +++ b/plugins/SliceInfoPlugin/SliceInfo.py @@ -54,7 +54,7 @@ class SliceInfo(Extension): # Get total material used (in mm^3) print_information = Application.getInstance().getPrintInformation() material_radius = 0.5 * global_container_stack.getProperty("material_diameter", "value") - material_used = math.pi * material_radius * material_radius * print_information.materialAmount #Volume of material used + material_used = math.pi * material_radius * material_radius * print_information.materialAmounts #Volume of material used # Get model information (bounding boxes, hashes and transformation matrix) models_info = [] diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index e73bf145de..0dec471a1c 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -24,7 +24,7 @@ Rectangle { UM.I18nCatalog { id: catalog; name:"cura"} property variant printDuration: PrintInformation.currentPrintTime - property real printMaterialAmount: PrintInformation.materialAmount + property real printMaterialAmount: PrintInformation.materialAmounts[0] height: childrenRect.height color: "transparent" From 7b4137ce205b90c45ce3753ae3c5b5ec17a77b35 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 4 Jul 2016 18:35:47 +0200 Subject: [PATCH 074/236] Tweak color and icon logic (add "pre_print" state) CURA-1036 --- resources/qml/MonitorButton.qml | 2 +- resources/qml/Sidebar.qml | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/resources/qml/MonitorButton.qml b/resources/qml/MonitorButton.qml index 8a232e62f1..bbb39b188e 100644 --- a/resources/qml/MonitorButton.qml +++ b/resources/qml/MonitorButton.qml @@ -22,7 +22,7 @@ Rectangle { if(!printerConnected) return UM.Theme.getColor("status_offline") - else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing") + else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing" || Cura.MachineManager.printerOutputDevices[0].jobState == "pre_print") return UM.Theme.getColor("status_busy") else if(Cura.MachineManager.printerOutputDevices[0].jobState == "ready") return UM.Theme.getColor("status_ready") diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index 25931932d3..78930c8d90 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -101,12 +101,16 @@ Rectangle iconSource: { if(!printerConnected) return UM.Theme.getIcon("tab_monitor") - else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing") + else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing" || Cura.MachineManager.printerOutputDevices[0].jobState == "pre_print") return UM.Theme.getIcon("tab_monitor_busy") + else if(Cura.MachineManager.printerOutputDevices[0].jobState == "ready") + return UM.Theme.getIcon("tab_monitor_connected") else if(Cura.MachineManager.printerOutputDevices[0].jobState == "paused") return UM.Theme.getIcon("tab_monitor_paused") - else if (Cura.MachineManager.printerOutputDevices[0].jobState != "error") - return UM.Theme.getIcon("tab_monitor_connected") + else if (Cura.MachineManager.printerOutputDevices[0].jobState == "error") + return UM.Theme.getIcon("tab_monitor_stopped") + else + return UM.Theme.getIcon("tab_monitor") } checkable: true checked: monitoringPrint From 793f7824c73614f037822905c5b97bf37a42a5ff Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 4 Jul 2016 19:14:48 +0200 Subject: [PATCH 075/236] Quick fix to restore slicing This list of zeros may have to match the number of extruders CURA-1687 --- plugins/CuraEngineBackend/CuraEngineBackend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 646400c2c2..6af51c4cc6 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -127,7 +127,7 @@ class CuraEngineBackend(Backend): ## Perform a slice of the scene. def slice(self): - self.printDurationMessage.emit(0, 0) + self.printDurationMessage.emit(0, [0]) self._stored_layer_data = [] From 44e4fc383ef5ce5850af3a509220f334e976f8bb Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 5 Jul 2016 09:37:01 +0200 Subject: [PATCH 076/236] Changing active extruder no longer trigger re-slice CURA-1729 --- plugins/CuraEngineBackend/CuraEngineBackend.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 82815bccb7..c871d35ea7 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -386,5 +386,4 @@ class CuraEngineBackend(Backend): self._active_extruder_stack = ExtruderManager.getInstance().getActiveExtruderStack() if self._active_extruder_stack: self._active_extruder_stack.propertyChanged.connect(self._onSettingChanged) # Note: Only starts slicing when the value changed. - self._active_extruder_stack.containersChanged.connect(self._onChanged) - self._onChanged() \ No newline at end of file + self._active_extruder_stack.containersChanged.connect(self._onChanged) \ No newline at end of file From fc99cac05eeded3a9cf9fad0fa865595cd817541 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 5 Jul 2016 09:39:18 +0200 Subject: [PATCH 077/236] Removed update firmware from extensions; This is now handled by machine actions --- plugins/USBPrinting/USBPrinterOutputDeviceManager.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugins/USBPrinting/USBPrinterOutputDeviceManager.py b/plugins/USBPrinting/USBPrinterOutputDeviceManager.py index 760b1b1564..3f739a8629 100644 --- a/plugins/USBPrinting/USBPrinterOutputDeviceManager.py +++ b/plugins/USBPrinting/USBPrinterOutputDeviceManager.py @@ -41,10 +41,6 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension): self._check_updates = True self._firmware_view = None - ## Add menu item to top menu of the application. - self.setMenuName(i18n_catalog.i18nc("@title:menu","Firmware")) - self.addMenuItem(i18n_catalog.i18nc("@item:inmenu", "Update Firmware"), self.updateAllFirmware) - Application.getInstance().applicationShuttingDown.connect(self.stop) self.addUSBOutputDeviceSignal.connect(self.addOutputDevice) #Because the model needs to be created in the same thread as the QMLEngine, we use a signal. From aba0392728d38accda9cb60b367401d6682973d6 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 5 Jul 2016 10:46:38 +0200 Subject: [PATCH 078/236] Saving g-code no longer crashes --- plugins/SliceInfoPlugin/SliceInfo.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/SliceInfoPlugin/SliceInfo.py b/plugins/SliceInfoPlugin/SliceInfo.py index 863eaa5166..f19a20c2b0 100644 --- a/plugins/SliceInfoPlugin/SliceInfo.py +++ b/plugins/SliceInfoPlugin/SliceInfo.py @@ -54,7 +54,9 @@ class SliceInfo(Extension): # Get total material used (in mm^3) print_information = Application.getInstance().getPrintInformation() material_radius = 0.5 * global_container_stack.getProperty("material_diameter", "value") - material_used = math.pi * material_radius * material_radius * print_information.materialAmounts #Volume of material used + + # TODO; Send material per extruder instead of mashing it on a pile + material_used = math.pi * material_radius * material_radius * sum(print_information.materialAmounts) #Volume of all materials used # Get model information (bounding boxes, hashes and transformation matrix) models_info = [] From ff9e4c9bb7e6f706f11ab0c75d7db679c7f2e6bf Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 5 Jul 2016 16:44:13 +0200 Subject: [PATCH 079/236] Add some more documentation to ContainerManager Contributes to CURA-341 --- cura/Settings/ContainerManager.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index c3ee7e1645..0853d8534b 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -8,13 +8,26 @@ import UM.Settings from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") +## Manager class that contains common actions to deal with containers in Cura. +# +# This is primarily intended as a class to be able to perform certain actions +# from within QML. We want to be able to trigger things like removing a container +# when a certain action happens. This can be done through this class. class ContainerManager(QObject): def __init__(self, parent = None): super().__init__(parent) self._registry = UM.Settings.ContainerRegistry.getInstance() - @pyqtSlot(str) + ## Create a duplicate of the specified container + # + # This will create and add a duplicate of the container corresponding + # to the container ID. + # + # \param container_id \type{str} The ID of the container to duplicate. + # + # \return The ID of the new container, or an empty string if duplication failed. + @pyqtSlot(str, result = str) def duplicateContainer(self, container_id): containers = self._registry.findInstanceContainers(id = container_id) if not containers: @@ -32,6 +45,16 @@ class ContainerManager(QObject): def removeContainer(self, container_id): pass + ## Set a metadata entry of the specified container. + # + # This will set the specified entry of the container's metadata to the specified + # value. Note that entries containing dictionaries can have their entries changed + # by using "/" as a separator. For example, to change an entry "foo" in a + # dictionary entry "bar", you can specify "bar/foo" as entry name. + # + # \param container_id \type{str} The ID of the container to change. + # \param entry_name \type{str} The name of the metadata entry to change. + # \param entry_value The new value of the entry. @pyqtSlot(str, str, str) def setContainerMetaDataEntry(self, container_id, entry_name, entry_value): containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = container_id) @@ -58,6 +81,7 @@ class ContainerManager(QObject): containers[0].setMetaDataEntry(entry_name, entry_value) + # Factory function, used by QML @staticmethod def createContainerManager(engine, js_engine): return ContainerManager() From ce065d110d8598e8ce8118035a8b4cd5f13bc145 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 5 Jul 2016 16:45:23 +0200 Subject: [PATCH 080/236] Finish implementation of ContainerManager Contributes to CURA-341 --- cura/Settings/ContainerManager.py | 116 +++++++++++++++++++++++++++--- 1 file changed, 105 insertions(+), 11 deletions(-) diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index 0853d8534b..fae5eb0b9d 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -29,21 +29,115 @@ class ContainerManager(QObject): # \return The ID of the new container, or an empty string if duplication failed. @pyqtSlot(str, result = str) def duplicateContainer(self, container_id): - containers = self._registry.findInstanceContainers(id = container_id) + containers = self._registry.findContainers(None, id = container_id) if not containers: - return + return "" - new_name = self_registry.uniqueName(containers[0].getName()) - new_material = containers[0].duplicate(new_name) - self._registry.addContainer(new_material) + container = containers[0] - @pyqtSlot(str, str) - def renameContainer(self, container_id, new_name): - pass + new_container = None + new_name = self._registry.uniqueName(container.getName()) + if hasattr(container, "duplicate"): + new_container = container.duplicate(new_name) + else: + new_container = container.__class__(new_name) + new_container.deserialize(container.serialize()) + new_container.setName(new_name) - @pyqtSlot(str) + if new_container: + self._registry.addContainer(new_container) + + return new_container.getId() + + ## Change the name of a specified container to a new name. + # + # \param container_id \type{str} The ID of the container to change the name of. + # \param new_id \type{str} The new ID of the container. + # \param new_name \type{str} The new name of the specified container. + # + # \return True if successful, False if not. + @pyqtSlot(str, str, str, result = bool) + def renameContainer(self, container_id, new_id, new_name): + containers = self._registry.findContainers(None, id = container_id) + if not containers: + return False + + container = containers[0] + # First, remove the container from the registry. This will clean up any files related to the container. + self._registry.removeContainer(container) + + # Ensure we have a unique name for the container + new_name = self._registry.uniqueName(new_name) + + # Then, update the name and ID of the container + container.setName(new_name) + container._id = new_id # TODO: Find a nicer way to set a new, unique ID + + # Finally, re-add the container so it will be properly serialized again. + self._registry.addContainer(container) + + return True + + ## Remove the specified container. + # + # \param container_id \type{str} The ID of the container to remove. + # + # \return True if the container was successfully removed, False if not. + @pyqtSlot(str, result = bool) def removeContainer(self, container_id): - pass + containers = self._registry.findContainers(None, id = container_id) + if not containers: + return False + + self._registry.removeContainer(containers[0]) + + return True + + ## Merge a container with another. + # + # This will try to merge one container into the other, by going through the container + # and setting the right properties on the other container. + # + # \param merge_into_id \type{str} The ID of the container to merge into. + # \param merge_id \type{str} The ID of the container to merge. + # + # \return True if successfully merged, False if not. + @pyqtSlot(str, result = bool) + def mergeContainers(self, merge_into_id, merge_id): + containers = self._registry.findContainers(None, id = container_id) + if not containers: + return False + + merge_into = containers[0] + + containers = self._registry.findContainers(None, id = container_id) + if not containers: + return False + + merge = containers[0] + + if type(merge) != type(merge_into): + return False + + for key in merge.getAllKeys(): + merge_into.setProperty(key, "value", merge.getProperty(key, "value")) + + return True + + ## Clear the contents of a container. + # + # \param container_id \type{str} The ID of the container to clear. + # + # \return True if successful, False if not. + @pyqtSlot(str, result = bool) + def clearContainer(self, container_id): + containers = self._registry.findContainers(None, id = container_id) + if not containers: + return False + + containers[0].clear() + + return True ## Set a metadata entry of the specified container. # @@ -57,7 +151,7 @@ class ContainerManager(QObject): # \param entry_value The new value of the entry. @pyqtSlot(str, str, str) def setContainerMetaDataEntry(self, container_id, entry_name, entry_value): - containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = container_id) + containers = UM.Settings.ContainerRegistry.getInstance().findContainers(None, id = container_id) if not containers: return From e80a999740ba333046fdbd0b55d74b4bbe1b4888 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 5 Jul 2016 16:46:52 +0200 Subject: [PATCH 081/236] Fix up readOnly property of XmlMaterialProfile Contributes to CURA-341 --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index b996be602f..0fd3ded228 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -24,6 +24,13 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): result.setMetaDataEntry("GUID", str(uuid.uuid4())) return result + ## Overridden from InstanceContainer + def setReadOnly(self, read_only): + super().setReadOnly(read_only) + + for container in UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(GUID = self.getMetaDataEntry("GUID")): + container._read_only = read_only + ## Overridden from InstanceContainer def setMetaDataEntry(self, key, value): super().setMetaDataEntry(key, value) From 827c7b84fd15fd026475e6db444fab659d2926b8 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 5 Jul 2016 16:47:26 +0200 Subject: [PATCH 082/236] Do not try to set a property of a read-only material Contributes to CURA-341 --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 0fd3ded228..d827f506e3 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -40,6 +40,9 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): ## Overridden from InstanceContainer def setProperty(self, key, property_name, property_value, container = None): + if self.isReadOnly(): + return + super().setProperty(key, property_name, property_value) for container in UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(GUID = self.getMetaDataEntry("GUID")): From aba60b0105b1e5a74f647d2280c3766ec2e724a8 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 5 Jul 2016 16:48:43 +0200 Subject: [PATCH 083/236] Remove "Rename" button from Materials page and make the other ones work Since the name is generated from the metadata Contributes to CURA-341 --- resources/qml/Preferences/MaterialsPage.qml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/resources/qml/Preferences/MaterialsPage.qml b/resources/qml/Preferences/MaterialsPage.qml index 797b698cd1..a40d4c1e3b 100644 --- a/resources/qml/Preferences/MaterialsPage.qml +++ b/resources/qml/Preferences/MaterialsPage.qml @@ -65,22 +65,15 @@ UM.ManagementPage { text: catalog.i18nc("@action:button", "Duplicate"); iconName: "list-add"; - enabled: base.currentItem + enabled: base.currentItem != null onClicked: Cura.ContainerManager.duplicateContainer(base.currentItem.id) }, Button { text: catalog.i18nc("@action:button", "Remove"); iconName: "list-remove"; - enabled: base.currentItem && !base.currentItem.readOnly -// onClicked: Cura.ContainerManager.removeContainer() - }, - Button - { - text: catalog.i18nc("@action:button", "Rename"); - iconName: "edit-rename"; - enabled: base.currentItem && !base.currentItem.readOnly -// onClicked: Cura.ContainerManager.renameContainer() + enabled: base.currentItem != null && !base.currentItem.readOnly + onClicked: confirmDialog.open() } ] @@ -149,6 +142,13 @@ UM.ManagementPage property string description: ""; property string adhesion_info: ""; } + + UM.ConfirmRemoveDialog + { + id: confirmDialog + object: base.currentItem != null ? base.currentItem.name : "" + onYes: Cura.ContainerManager.removeContainer(base.currentItem.id) + } } onCurrentItemChanged: From 2bc3a1077654e255433612f69f4d83d23110728a Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 5 Jul 2016 16:51:02 +0200 Subject: [PATCH 084/236] Properly handle machines that have no variants Contributes to CURA-341 --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index d827f506e3..b0aa93afe6 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -139,10 +139,11 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): elif len(container.id) < len(machine_container_map[definition_id].id): machine_container_map[definition_id] = container + if definition_id not in machine_nozzle_map: + machine_nozzle_map[definition_id] = {} + variant = container.getMetaDataEntry("variant") if variant: - if definition_id not in machine_nozzle_map: - machine_nozzle_map[definition_id] = {} machine_nozzle_map[definition_id][variant] = container for definition_id, container in machine_container_map.items(): From 750ecfc2e185c9e281af1aa95e877bdc89ef473b Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Tue, 5 Jul 2016 16:51:28 +0200 Subject: [PATCH 085/236] Remove some now-useless code relating to spool weight Contributes to CURA-341 --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index b0aa93afe6..8e3298597c 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -233,17 +233,6 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): diameter = float(property_values.get("diameter", 2.85)) # In mm density = float(property_values.get("density", 1.3)) # In g/cm3 - weight_per_cm = (math.pi * (diameter / 20) ** 2 * 0.1) * density - - spool_weight = property_values.get("spool_weight") - spool_length = property_values.get("spool_length") - if spool_weight: - length = float(spool_weight) / weight_per_cm - property_values["spool_length"] = str(length / 100) - elif spool_length: - weight = (float(spool_length) * 100) * weight_per_cm - property_values["spool_weight"] = str(weight) - self.addMetaDataEntry("properties", property_values) self.setDefinition(UM.Settings.ContainerRegistry.getInstance().findDefinitionContainers(id = "fdmprinter")[0]) @@ -360,6 +349,7 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): } # Map XML file product names to internal ids + # TODO: Move this to definition's metadata __product_id_map = { "Ultimaker2": "ultimaker2", "Ultimaker2+": "ultimaker2_plus", From be4cf0083512a1e5cc67a27bfe9c692f4f7a7b7e Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Tue, 5 Jul 2016 18:22:56 +0200 Subject: [PATCH 086/236] BQ Hephestos2: Heat up nozzle while leveling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Saves time and preheats the nozzle to the correct temperature (set by Cura). Btw. the BQ support explained me the reason why they are assuming 210°C needs to set here. The reason is that the printer is just meant to be used with PLA, so they are only expecting to use exactly 210°C. Just explained them why this is incorrect when think about the possibility that Cura can set the temperature on it's own. --- resources/definitions/bq_hephestos_2.def.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/definitions/bq_hephestos_2.def.json b/resources/definitions/bq_hephestos_2.def.json index 374e41f93c..51777ff83e 100644 --- a/resources/definitions/bq_hephestos_2.def.json +++ b/resources/definitions/bq_hephestos_2.def.json @@ -14,7 +14,7 @@ }, "overrides": { - "machine_start_gcode": { "default_value": "; -- START GCODE --\nM800 ; Custom GCODE to fire start print procedure\nM109 S{material_print_temperature} ;Makes sure the temperature is correct before printing\n; -- end of START GCODE --" }, + "machine_start_gcode": { "default_value": "; -- START GCODE --\nM104 S{material_print_temperature} ; Heat up extruder while leveling\nM800 ; Custom GCODE to fire start print procedure\nM109 S{material_print_temperature} ; Makes sure the temperature is correct before printing\n; -- end of START GCODE --" }, "machine_end_gcode": { "default_value": "; -- END GCODE --\nM801 ; Custom GCODE to fire end print procedure\n; -- end of END GCODE --" }, "machine_width": { "default_value": 210 }, "machine_depth": { "default_value": 297 }, From 25c81ce1f86a3be41720b9e5689a490aed7ea7c8 Mon Sep 17 00:00:00 2001 From: Thomas Karl Pietrowski Date: Tue, 5 Jul 2016 18:55:13 +0200 Subject: [PATCH 087/236] Just a little typo --- plugins/SliceInfoPlugin/SliceInfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/SliceInfoPlugin/SliceInfo.py b/plugins/SliceInfoPlugin/SliceInfo.py index f19a20c2b0..2751173a1c 100644 --- a/plugins/SliceInfoPlugin/SliceInfo.py +++ b/plugins/SliceInfoPlugin/SliceInfo.py @@ -55,7 +55,7 @@ class SliceInfo(Extension): print_information = Application.getInstance().getPrintInformation() material_radius = 0.5 * global_container_stack.getProperty("material_diameter", "value") - # TODO; Send material per extruder instead of mashing it on a pile + # TODO: Send material per extruder instead of mashing it on a pile material_used = math.pi * material_radius * material_radius * sum(print_information.materialAmounts) #Volume of all materials used # Get model information (bounding boxes, hashes and transformation matrix) From c06e5f4d39af1faedf24fd26b25beeb2f4dd1a9b Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Tue, 5 Jul 2016 21:58:47 +0200 Subject: [PATCH 088/236] Remove unused name/id when importing a profile from a gcode file A unique name will be set from the filename by ContainerRegistry CURA-1615 --- plugins/GCodeProfileReader/GCodeProfileReader.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/plugins/GCodeProfileReader/GCodeProfileReader.py b/plugins/GCodeProfileReader/GCodeProfileReader.py index 1ce1473582..1e649b7dd4 100644 --- a/plugins/GCodeProfileReader/GCodeProfileReader.py +++ b/plugins/GCodeProfileReader/GCodeProfileReader.py @@ -73,7 +73,7 @@ class GCodeProfileReader(ProfileReader): serialized = pattern.sub(lambda m: GCodeProfileReader.escape_characters[re.escape(m.group(0))], serialized) Logger.log("i", "Serialized the following from %s: %s" %(file_name, repr(serialized))) - # Create an empty profile - the id will be changed later + # Create an empty profile - the id and name will be changed by the ContainerRegistry profile = InstanceContainer("") try: profile.deserialize(serialized) @@ -83,9 +83,4 @@ class GCodeProfileReader(ProfileReader): profile.addMetaDataEntry("type", "quality") - #Creating a unique name using the filename of the GCode - new_name = catalog.i18nc("@label", "G-Code-imported profile") - profile.setName(new_name) - profile._id = new_name - return profile From 59020fd98cb3a9f351d4d054270f6a746e0950f9 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 6 Jul 2016 11:20:06 +0200 Subject: [PATCH 089/236] CHeckup action now correctly resets every time you start it CURA-1385 --- .../UltimakerMachineActions/UMOCheckupMachineAction.py | 10 +++++++++- .../UMOCheckupMachineAction.qml | 7 +++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py index f13257e8a9..861477d61d 100644 --- a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py +++ b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py @@ -43,7 +43,6 @@ class UMOCheckupMachineAction(MachineAction): if self._output_device is None and self._check_started: self.startCheck() - def _getPrinterOutputDevices(self): return [printer_output_device for printer_output_device in Application.getInstance().getOutputDeviceManager().getOutputDevices() if @@ -63,6 +62,7 @@ class UMOCheckupMachineAction(MachineAction): self._output_device = None self._check_started = False + self.checkStartedChanged.emit() # Ensure everything is reset (and right signals are emitted again) self._bed_test_completed = False @@ -79,6 +79,7 @@ class UMOCheckupMachineAction(MachineAction): @pyqtProperty(bool, notify = onBedTestCompleted) def bedTestCompleted(self): + print("zomg?") return self._bed_test_completed @pyqtProperty(bool, notify = onHotendTestCompleted) @@ -137,9 +138,16 @@ class UMOCheckupMachineAction(MachineAction): self._z_min_endstop_test_completed = True self.onZMinEndstopTestCompleted.emit() + checkStartedChanged = pyqtSignal() + + @pyqtProperty(bool, notify = checkStartedChanged) + def checkStarted(self): + return self._check_started + @pyqtSlot() def startCheck(self): self._check_started = True + self.checkStartedChanged.emit() output_devices = self._getPrinterOutputDevices() if output_devices: self._output_device = output_devices[0] diff --git a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml index 4b25d3130d..d2bc90b601 100644 --- a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml +++ b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml @@ -32,7 +32,7 @@ Cura.MachineAction anchors.topMargin: UM.Theme.getSize("default_margin").height width: parent.width wrapMode: Text.WordWrap - text: catalog.i18nc("@label","It's a good idea to do a few sanity checks on your Ultimaker. You can skip this step if you know your machine is functional"); + text: catalog.i18nc("@label", "It's a good idea to do a few sanity checks on your Ultimaker. You can skip this step if you know your machine is functional"); } Item @@ -51,7 +51,7 @@ Cura.MachineAction text: catalog.i18nc("@action:button","Start Printer Check"); onClicked: { - checkupContent.visible = true + //checkupContent.visible = true manager.startCheck() } } @@ -73,7 +73,7 @@ Cura.MachineAction id: checkupContent anchors.top: startStopButtons.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height - visible: false + visible: manager.checkStarted width: parent.width height: 250 ////////////////////////////////////////////////////////// @@ -188,7 +188,6 @@ Cura.MachineAction onClicked: { manager.heatupHotend() - nozzleTempStatus.text = catalog.i18nc("@info:progress","Checking") } } } From a65bb1432995fbccd40fbc280a608399baa5274f Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 6 Jul 2016 12:56:05 +0200 Subject: [PATCH 090/236] Capitalise setting label This is in line with the rest of the setting labels. --- resources/definitions/fdmprinter.def.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index b5d3796e74..c153ca3b07 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -410,7 +410,7 @@ }, "skin_line_width": { - "label": "Top/bottom Line Width", + "label": "Top/Bottom Line Width", "description": "Width of a single top/bottom line.", "unit": "mm", "minimum_value": "0.0001", From d42a97f036d20c1215e534087b153d7615cb5344 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 6 Jul 2016 13:22:09 +0200 Subject: [PATCH 091/236] Fixed firmware upgrade for um2+ CURA-1761 --- plugins/USBPrinting/USBPrinterOutputDeviceManager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/USBPrinting/USBPrinterOutputDeviceManager.py b/plugins/USBPrinting/USBPrinterOutputDeviceManager.py index 3f739a8629..fc10c217a0 100644 --- a/plugins/USBPrinting/USBPrinterOutputDeviceManager.py +++ b/plugins/USBPrinting/USBPrinterOutputDeviceManager.py @@ -152,7 +152,7 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension): "ultimaker_original_plus" : "MarlinUltimaker-UMOP-{baudrate}.hex", "ultimaker2" : "MarlinUltimaker2.hex", "ultimaker2_go" : "MarlinUltimaker2go.hex", - "ultimaker2plus" : "MarlinUltimaker2plus.hex", + "ultimaker2_plus" : "MarlinUltimaker2plus.hex", "ultimaker2_extended" : "MarlinUltimaker2extended.hex", "ultimaker2_extended_plus" : "MarlinUltimaker2extended-plus.hex", } From 0e6754c0e2f6a9adab7ecfd70fd63bc9e65538ce Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Wed, 6 Jul 2016 14:06:14 +0200 Subject: [PATCH 092/236] Update UM2 Extended build volume height to value published in marketing materials --- resources/definitions/ultimaker2_extended.def.json | 2 +- resources/definitions/ultimaker2_extended_plus.def.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/definitions/ultimaker2_extended.def.json b/resources/definitions/ultimaker2_extended.def.json index cead008643..fb443365f2 100644 --- a/resources/definitions/ultimaker2_extended.def.json +++ b/resources/definitions/ultimaker2_extended.def.json @@ -16,7 +16,7 @@ "overrides": { "machine_height": { - "default_value": 315 + "default_value": 305 } } } diff --git a/resources/definitions/ultimaker2_extended_plus.def.json b/resources/definitions/ultimaker2_extended_plus.def.json index 23b308461d..f4190be83d 100644 --- a/resources/definitions/ultimaker2_extended_plus.def.json +++ b/resources/definitions/ultimaker2_extended_plus.def.json @@ -15,7 +15,7 @@ "overrides": { "machine_height": { - "default_value": 313 + "default_value": 305 }, "machine_show_variants": { "default_value": true From a5ba68bae3502a21ced78a19ad35b1394292c034 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 6 Jul 2016 14:09:08 +0200 Subject: [PATCH 093/236] Ensure that the display matches with the backend active extruder data --- cura/ExtruderManager.py | 4 ++++ resources/qml/SidebarHeader.qml | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/cura/ExtruderManager.py b/cura/ExtruderManager.py index 5d0ad612cf..16eaf7f8c4 100644 --- a/cura/ExtruderManager.py +++ b/cura/ExtruderManager.py @@ -66,6 +66,10 @@ class ExtruderManager(QObject): self._active_extruder_index = index self.activeExtruderChanged.emit() + @pyqtProperty(int, notify = activeExtruderChanged) + def activeExtruderIndex(self): + return self._active_extruder_index + def getActiveExtruderStack(self): global_container_stack = UM.Application.getInstance().getGlobalContainerStack() if global_container_stack: diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml index ac6c491262..82dba70b92 100644 --- a/resources/qml/SidebarHeader.qml +++ b/resources/qml/SidebarHeader.qml @@ -13,7 +13,7 @@ Column id: base; property int totalHeightHeader: childrenRect.height - property int currentExtruderIndex: -1; + property int currentExtruderIndex:ExtruderManager.activeExtruderIndex; spacing: UM.Theme.getSize("default_margin").height @@ -118,7 +118,7 @@ Column { base.currentExtruderIndex = -1; forceActiveFocus() - ExtruderManager.setActiveExtruderIndex(0); + ExtruderManager.setActiveExtruderIndex(base.currentExtruderIndex); } } From b3a6fafd97f5a55993f34941355fd99be0918007 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 6 Jul 2016 14:30:34 +0200 Subject: [PATCH 094/236] Set default extruder index to -1 (so global is default) --- cura/ExtruderManager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/ExtruderManager.py b/cura/ExtruderManager.py index 16eaf7f8c4..e937714d85 100644 --- a/cura/ExtruderManager.py +++ b/cura/ExtruderManager.py @@ -22,7 +22,7 @@ class ExtruderManager(QObject): def __init__(self, parent = None): super().__init__(parent) self._extruder_trains = { } #Per machine, a dictionary of extruder container stack IDs. - self._active_extruder_index = 0 + self._active_extruder_index = -1 UM.Application.getInstance().globalContainerStackChanged.connect(self._addCurrentMachineExtruders) ## Gets the unique identifier of the currently active extruder stack. From bdd3ecbc1af8fca469ebecc93da747d19d0ec0ed Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Wed, 6 Jul 2016 14:32:37 +0200 Subject: [PATCH 095/236] Update GUID for PLA to match the GUID in the official repository CURA-491 --- resources/materials/generic_pla.xml.fdm_material | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/materials/generic_pla.xml.fdm_material b/resources/materials/generic_pla.xml.fdm_material index 2f8fa165cf..40432d5849 100644 --- a/resources/materials/generic_pla.xml.fdm_material +++ b/resources/materials/generic_pla.xml.fdm_material @@ -9,7 +9,7 @@ Generic PLA profile. Serves as an example file, data in this file is not correct PLA Generic - 86a89ceb-4159-47f6-ab97-e9953803d70f + 506c9f0d-e3aa-4bd4-b2d2-23e2425b1aa9 0 #00FF00 From 2be8111f1bf28a47c539c8bf8feea340dd7cb15e Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Wed, 6 Jul 2016 16:21:03 +0200 Subject: [PATCH 096/236] Restart timer after slicing is performed when not enabled. CURA-1502 --- plugins/CuraEngineBackend/CuraEngineBackend.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 9e37fa9049..40729eb48b 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -127,13 +127,15 @@ class CuraEngineBackend(Backend): ## Perform a slice of the scene. def slice(self): + if not self._enabled or not self._global_container_stack: #We shouldn't be slicing. + # try again in a short time + self._change_timer.start() + return + self.printDurationMessage.emit(0, [0]) self._stored_layer_data = [] - if not self._enabled or not self._global_container_stack: #We shouldn't be slicing. - return - if self._slicing: #We were already slicing. Stop the old job. self._terminate() From 57d0ad1bd0f70a68d3a0ca88f2952c641cd31f55 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 6 Jul 2016 16:32:24 +0200 Subject: [PATCH 097/236] Made exception handling of slice info plugin way more robust CURA-1831 --- plugins/SliceInfoPlugin/SliceInfo.py | 145 ++++++++++++++------------- 1 file changed, 75 insertions(+), 70 deletions(-) diff --git a/plugins/SliceInfoPlugin/SliceInfo.py b/plugins/SliceInfoPlugin/SliceInfo.py index 2751173a1c..487b077d2b 100644 --- a/plugins/SliceInfoPlugin/SliceInfo.py +++ b/plugins/SliceInfoPlugin/SliceInfo.py @@ -45,74 +45,79 @@ class SliceInfo(Extension): Preferences.getInstance().setValue("info/asked_send_slice_info", True) def _onWriteStarted(self, output_device): - if not Preferences.getInstance().getValue("info/send_slice_info"): - Logger.log("d", "'info/send_slice_info' is turned off.") - return # Do nothing, user does not want to send data - - global_container_stack = Application.getInstance().getGlobalContainerStack() - - # Get total material used (in mm^3) - print_information = Application.getInstance().getPrintInformation() - material_radius = 0.5 * global_container_stack.getProperty("material_diameter", "value") - - # TODO: Send material per extruder instead of mashing it on a pile - material_used = math.pi * material_radius * material_radius * sum(print_information.materialAmounts) #Volume of all materials used - - # Get model information (bounding boxes, hashes and transformation matrix) - models_info = [] - for node in DepthFirstIterator(Application.getInstance().getController().getScene().getRoot()): - if type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None: - if not getattr(node, "_outside_buildarea", False): - model_info = {} - model_info["hash"] = node.getMeshData().getHash() - model_info["bounding_box"] = {} - model_info["bounding_box"]["minimum"] = {} - model_info["bounding_box"]["minimum"]["x"] = node.getBoundingBox().minimum.x - model_info["bounding_box"]["minimum"]["y"] = node.getBoundingBox().minimum.y - model_info["bounding_box"]["minimum"]["z"] = node.getBoundingBox().minimum.z - - model_info["bounding_box"]["maximum"] = {} - model_info["bounding_box"]["maximum"]["x"] = node.getBoundingBox().maximum.x - model_info["bounding_box"]["maximum"]["y"] = node.getBoundingBox().maximum.y - model_info["bounding_box"]["maximum"]["z"] = node.getBoundingBox().maximum.z - model_info["transformation"] = str(node.getWorldTransformation().getData()) - - models_info.append(model_info) - - # Bundle the collected data - submitted_data = { - "processor": platform.processor(), - "machine": platform.machine(), - "platform": platform.platform(), - "settings": global_container_stack.serialize(), # global_container with references on used containers - "version": Application.getInstance().getVersion(), - "modelhash": "None", - "printtime": print_information.currentPrintTime.getDisplayString(), - "filament": material_used, - "language": Preferences.getInstance().getValue("general/language"), - "materials_profiles ": {} - } - for container in global_container_stack.getContainers(): - container_id = container.getId() - try: - container_serialized = container.serialize() - except NotImplementedError: - Logger.log("w", "Container %s could not be serialized!", container_id) - continue - - if container_serialized: - submitted_data["settings_%s" %(container_id)] = container_serialized # This can be anything, eg. INI, JSON, etc. - else: - Logger.log("i", "No data found in %s to be serialized!", container_id) - - # Convert data to bytes - submitted_data = urllib.parse.urlencode(submitted_data) - binary_data = submitted_data.encode("utf-8") - - # Submit data try: - f = urllib.request.urlopen(self.info_url, data = binary_data, timeout = 1) - Logger.log("i", "Sent anonymous slice info to %s", self.info_url) - f.close() - except Exception as e: - Logger.logException("e", e) + if not Preferences.getInstance().getValue("info/send_slice_info"): + Logger.log("d", "'info/send_slice_info' is turned off.") + return # Do nothing, user does not want to send data + + global_container_stack = Application.getInstance().getGlobalContainerStack() + + # Get total material used (in mm^3) + print_information = Application.getInstance().getPrintInformation() + material_radius = 0.5 * global_container_stack.getProperty("material_diameter", "value") + + # TODO: Send material per extruder instead of mashing it on a pile + material_used = math.pi * material_radius * material_radius * sum(print_information.materialAmounts) #Volume of all materials used + + # Get model information (bounding boxes, hashes and transformation matrix) + models_info = [] + for node in DepthFirstIterator(Application.getInstance().getController().getScene().getRoot()): + if type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None: + if not getattr(node, "_outside_buildarea", False): + model_info = {} + model_info["hash"] = node.getMeshData().getHash() + model_info["bounding_box"] = {} + model_info["bounding_box"]["minimum"] = {} + model_info["bounding_box"]["minimum"]["x"] = node.getBoundingBox().minimum.x + model_info["bounding_box"]["minimum"]["y"] = node.getBoundingBox().minimum.y + model_info["bounding_box"]["minimum"]["z"] = node.getBoundingBox().minimum.z + + model_info["bounding_box"]["maximum"] = {} + model_info["bounding_box"]["maximum"]["x"] = node.getBoundingBox().maximum.x + model_info["bounding_box"]["maximum"]["y"] = node.getBoundingBox().maximum.y + model_info["bounding_box"]["maximum"]["z"] = node.getBoundingBox().maximum.z + model_info["transformation"] = str(node.getWorldTransformation().getData()) + + models_info.append(model_info) + + # Bundle the collected data + submitted_data = { + "processor": platform.processor(), + "machine": platform.machine(), + "platform": platform.platform(), + "settings": global_container_stack.serialize(), # global_container with references on used containers + "version": Application.getInstance().getVersion(), + "modelhash": "None", + "printtime": print_information.currentPrintTime.getDisplayString(), + "filament": material_used, + "language": Preferences.getInstance().getValue("general/language"), + "materials_profiles ": {} + } + for container in global_container_stack.getContainers(): + container_id = container.getId() + try: + container_serialized = container.serialize() + except NotImplementedError: + Logger.log("w", "Container %s could not be serialized!", container_id) + continue + + if container_serialized: + submitted_data["settings_%s" %(container_id)] = container_serialized # This can be anything, eg. INI, JSON, etc. + else: + Logger.log("i", "No data found in %s to be serialized!", container_id) + + # Convert data to bytes + submitted_data = urllib.parse.urlencode(submitted_data) + binary_data = submitted_data.encode("utf-8") + + # Submit data + try: + f = urllib.request.urlopen(self.info_url, data = binary_data, timeout = 1) + Logger.log("i", "Sent anonymous slice info to %s", self.info_url) + f.close() + except Exception as e: + Logger.logException("e", "An exception occurred while trying to send slice information") + except: + # We really can't afford to have a mistake here, as this would break the sending of g-code to a device + # (Either saving or directly to a printer). The functionality of the slice data is not *that* important. + pass \ No newline at end of file From a3618c243d8b2c3585af0fb10528c675d5d48758 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 6 Jul 2016 16:38:35 +0200 Subject: [PATCH 098/236] Added deepcopy function CURA-1578 --- cura/MultiMaterialDecorator.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cura/MultiMaterialDecorator.py b/cura/MultiMaterialDecorator.py index c702ecef13..5fee777309 100644 --- a/cura/MultiMaterialDecorator.py +++ b/cura/MultiMaterialDecorator.py @@ -5,4 +5,7 @@ class MultiMaterialDecorator(SceneNodeDecorator): super().__init__() def isMultiMaterial(self): - return True \ No newline at end of file + return True + + def __deepcopy__(self, memo): + return MultiMaterialDecorator() \ No newline at end of file From 8476090ed237ee2e2c0dbc97b1dd727b787598f7 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 7 Jul 2016 01:36:52 +0200 Subject: [PATCH 099/236] Also expose the main Cura application object as CuraApplication to QML This way we can start cleaning things up and moving away from the "Printer" name Contributes to CURA-341 --- cura/CuraApplication.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 4883194803..eb9c5e6d3b 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -414,6 +414,7 @@ class CuraApplication(QtApplication): # \param engine The QML engine. def registerObjects(self, engine): engine.rootContext().setContextProperty("Printer", self) + engine.rootContext().setContextProperty("CuraApplication", self) self._print_information = PrintInformation.PrintInformation() engine.rootContext().setContextProperty("PrintInformation", self._print_information) self._cura_actions = CuraActions.CuraActions(self) From 6e153414a4f89158844478d5a1563a43f352cace Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 7 Jul 2016 01:37:54 +0200 Subject: [PATCH 100/236] Do not try to write to read-only containers Contributes to CURA-341 --- cura/Settings/ContainerManager.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index fae5eb0b9d..050e61a023 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -18,6 +18,7 @@ class ContainerManager(QObject): super().__init__(parent) self._registry = UM.Settings.ContainerRegistry.getInstance() + self._container_name_filters = {} ## Create a duplicate of the specified container # @@ -89,7 +90,7 @@ class ContainerManager(QObject): if not containers: return False - self._registry.removeContainer(containers[0]) + self._registry.removeContainer(containers[0].getId()) return True @@ -135,6 +136,9 @@ class ContainerManager(QObject): if not containers: return False + if containers[0].isReadOnly(): + return False + containers[0].clear() return True @@ -149,14 +153,19 @@ class ContainerManager(QObject): # \param container_id \type{str} The ID of the container to change. # \param entry_name \type{str} The name of the metadata entry to change. # \param entry_value The new value of the entry. - @pyqtSlot(str, str, str) + # + # \return True if successful, False if not. + @pyqtSlot(str, str, str, result = bool) def setContainerMetaDataEntry(self, container_id, entry_name, entry_value): containers = UM.Settings.ContainerRegistry.getInstance().findContainers(None, id = container_id) if not containers: - return + return False container = containers[0] + if container.isReadOnly(): + return False + entries = entry_name.split("/") entry_name = entries.pop() From 1f6e99f641181a5b18a20de90ce33759622c40cb Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 7 Jul 2016 01:39:38 +0200 Subject: [PATCH 101/236] Add methods to get name filters, import and export containers to ContainerManager We should start replacing the other ways of import/export with this since this uses the containerregistry provided contaienr types instead of needing a different plugin type. Contributes to CURA-341 --- cura/Settings/ContainerManager.py | 184 +++++++++++++++++++++++++++++- 1 file changed, 182 insertions(+), 2 deletions(-) diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index 050e61a023..6f272c65d8 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -1,9 +1,19 @@ # Copyright (c) 2016 Ultimaker B.V. # Cura is released under the terms of the AGPLv3 or higher. -from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal +import os.path +import urllib +from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal, QUrl +from PyQt5.QtWidgets import QMessageBox + +import UM.PluginRegistry import UM.Settings +import UM.SaveFile +import UM.Platform +import UM.MimeTypeDatabase + +from UM.MimeTypeDatabase import MimeTypeNotFoundError from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") @@ -182,7 +192,177 @@ class ContainerManager(QObject): entry_name = root_name entry_value = root - containers[0].setMetaDataEntry(entry_name, entry_value) + container.setMetaDataEntry(entry_name, entry_value) + + return True + + ## Find instance containers matching certain criteria. + # + # This effectively forwards to ContainerRegistry::findInstanceContainers. + # + # \param criteria A dict of key - value pairs to search for. + # + # \return A list of container IDs that match the given criteria. + @pyqtSlot("QVariantMap", result = "QVariantList") + def findInstanceContainers(self, criteria): + result = [] + for entry in self._registry.findInstanceContainers(**criteria): + result.append(entry.getId()) + + return result + + ## Get a list of string that can be used as name filters for a Qt File Dialog + # + # This will go through the list of available container types and generate a list of strings + # out of that. The strings are formatted as "description (*.extension)" and can be directly + # passed to a nameFilters property of a Qt File Dialog. + # + # \param type_name Which types of containers to list. These types correspond to the "type" + # key of the plugin metadata. + # + # \return A string list with name filters. + @pyqtSlot(str, result = "QStringList") + def getContainerNameFilters(self, type_name): + if not self._container_name_filters: + self._updateContainerNameFilters() + + filters = [] + for filter_string, entry in self._container_name_filters.items(): + if not type_name or entry["type"] == type_name: + filters.append(filter_string) + + return filters + + ## Export a container to a file + # + # \param container_id The ID of the container to export + # \param file_type The type of file to save as. Should be in the form of "description (*.extension, *.ext)" + # \param file_url The URL where to save the file. + # + # \return A dictionary containing a key "status" with a status code and a key "message" with a message + # explaining the status. + # The status code can be one of "error", "cancelled", "success" + @pyqtSlot(str, str, QUrl, result = "QVariantMap") + def exportContainer(self, container_id, file_type, file_url): + if not container_id or not file_type or not file_url: + return { "status": "error", "message": "Invalid arguments"} + + if isinstance(file_url, QUrl): + file_url = file_url.toLocalFile() + + if not file_url: + return { "status": "error", "message": "Invalid path"} + + mime_type = None + if not file_type in self._container_name_filters: + try: + mime_type = UM.MimeTypeDatabase.getMimeTypeForFile(file_url) + except MimeTypeNotFoundError: + return { "status": "error", "message": "Unknown File Type" } + else: + mime_type = self._container_name_filters[file_type]["mime"] + + containers = UM.Settings.ContainerRegistry.getInstance().findContainers(None, id = container_id) + if not containers: + return { "status": "error", "message": "Container not found"} + container = containers[0] + + for suffix in mime_type.suffixes: + if file_url.endswith(suffix): + break + else: + file_url += "." + mime_type.preferredSuffix + + if not UM.Platform.isWindows(): + if os.path.exists(file_url): + result = QMessageBox.question(None, catalog.i18nc("@title:window", "File Already Exists"), + catalog.i18nc("@label", "The file {0} already exists. Are you sure you want to overwrite it?").format(file_url)) + if result == QMessageBox.No: + return { "status": "cancelled", "message": "User cancelled"} + + try: + contents = container.serialize() + except NotImplementedError: + return { "status": "error", "message": "Unable to serialize container"} + + with UM.SaveFile(file_url, "w") as f: + f.write(contents) + + return { "status": "success", "message": "Succesfully exported container"} + + ## Imports a profile from a file + # + # \param file_url A URL that points to the file to import. + # + # \return \type{Dict} dict with a 'status' key containing the string 'success' or 'error', and a 'message' key + # containing a message for the user + @pyqtSlot(QUrl, result = "QVariantMap") + def importContainer(self, file_url): + if not file_url: + return { "status": "error", "message": "Invalid path"} + + if isinstance(file_url, QUrl): + file_url = file_url.toLocalFile() + + if not file_url or not os.path.exists(file_url): + return { "status": "error", "message": "Invalid path" } + + try: + mime_type = UM.MimeTypeDatabase.getMimeTypeForFile(file_url) + except MimeTypeNotFoundError: + return { "status": "error", "message": "Could not determine mime type of file" } + + container_type = UM.Settings.ContainerRegistry.getContainerForMimeType(mime_type) + if not container_type: + return { "status": "error", "message": "Could not find a container to handle the specified file."} + + container_id = urllib.parse.unquote_plus(mime_type.stripExtension(os.path.basename(file_url))) + container_id = UM.Settings.ContainerRegistry.getInstance().uniqueName(container_id) + + container = container_type(container_id) + + try: + with open(file_url, "rt") as f: + container.deserialize(f.read()) + except PermissionError: + return { "status": "error", "message": "Permission denied when trying to read the file"} + + container.setName(container_id) + + UM.Settings.ContainerRegistry.getInstance().addContainer(container) + + return { "status": "success", "message": "Successfully imported container {0}".format(container.getName()) } + + def _updateContainerNameFilters(self): + self._container_name_filters = {} + for plugin_id, container_type in UM.Settings.ContainerRegistry.getContainerTypes(): + serialize_type = "" + try: + plugin_metadata = UM.PluginRegistry.getInstance().getMetaData(plugin_id) + if plugin_metadata: + serialize_type = plugin_metadata["settings_container"]["type"] + else: + continue + except KeyError as e: + continue + + mime_type = UM.Settings.ContainerRegistry.getMimeTypeForContainer(container_type) + + entry = { + "type": serialize_type, + "mime": mime_type, + "container": container_type + } + + suffix_list = "*." + mime_type.preferredSuffix + for suffix in mime_type.suffixes: + if suffix == mime_type.preferredSuffix: + continue + + suffix_list += ", *." + suffix + + name_filter = "{0} ({1})".format(mime_type.comment, suffix_list) + self._container_name_filters[name_filter] = entry # Factory function, used by QML @staticmethod From f21079b763b06ed32438a2f1987844ab5c2b1344 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 7 Jul 2016 01:41:00 +0200 Subject: [PATCH 102/236] Store the base material file as a metadata property of XmlMaterialProfile And use the base_file property to check if we should serialize or not. Contributes to CURA-342 --- .../XmlMaterialProfile/XmlMaterialProfile.py | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 8e3298597c..6c70930bae 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -52,16 +52,8 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): def serialize(self): registry = UM.Settings.ContainerRegistry.getInstance() - all_containers = registry.findInstanceContainers(GUID = self.getMetaDataEntry("GUID")) - most_generic = all_containers[0] - for container in all_containers: - # Find the "most generic" version of this material - # This is a bit of a nasty implementation because of the naive assumption that anything with a shorter - # id is more generic. It holds for the current implementation though. - if len(most_generic.id) > len(container.id): - most_generic = container - - if most_generic and self.id != most_generic.id: + base_file = self.getMetaDataEntry("base_file", "") + if base_file and self.id != base_file: # Since we create an instance of XmlMaterialProfile for each machine and nozzle in the profile, # we should only serialize the "base" material definition, since that can then take care of # serializing the machine/nozzle specific profiles. @@ -81,6 +73,7 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): metadata.pop("status", "") metadata.pop("variant", "") metadata.pop("type", "") + metadata.pop("base_file", "") ## Begin Name Block builder.start("name") @@ -129,6 +122,7 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): machine_container_map = {} machine_nozzle_map = {} + all_containers = registry.findInstanceContainers(GUID = self.getMetaDataEntry("GUID")) for container in all_containers: definition_id = container.getDefinition().id if definition_id == "fdmprinter": @@ -136,8 +130,6 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): if definition_id not in machine_container_map: machine_container_map[definition_id] = container - elif len(container.id) < len(machine_container_map[definition_id].id): - machine_container_map[definition_id] = container if definition_id not in machine_nozzle_map: machine_nozzle_map[definition_id] = {} @@ -145,6 +137,9 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): variant = container.getMetaDataEntry("variant") if variant: machine_nozzle_map[definition_id][variant] = container + continue + + machine_container_map[definition_id] = container for definition_id, container in machine_container_map.items(): definition = container.getDefinition() @@ -278,6 +273,7 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): new_material.setName(self.getName()) new_material.setMetaData(copy.deepcopy(self.getMetaData())) new_material.setDefinition(definition) + new_material.addMetaDataEntry("base_file", self.id) for key, value in global_setting_values.items(): new_material.setProperty(key, "value", value, definition) @@ -308,6 +304,7 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): new_hotend_material.setName(self.getName()) new_hotend_material.setMetaData(copy.deepcopy(self.getMetaData())) new_hotend_material.setDefinition(definition) + new_hotend_material.addMetaDataEntry("base_file", self.id) new_hotend_material.addMetaDataEntry("variant", variant_containers[0].id) From fdab77ff6ac2f9336879134cbc0371c804bac47f Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 7 Jul 2016 01:42:05 +0200 Subject: [PATCH 103/236] Also duplicate the base file when duplicating a material profile This way serialization will always be correctly performed and we can generate a machine/variant specifc ID for the duplicate. Contributes to CURA-341 --- .../XmlMaterialProfile/XmlMaterialProfile.py | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 6c70930bae..709eb8c276 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -20,8 +20,27 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): ## Overridden from InstanceContainer def duplicate(self, new_id, new_name = None): - result = super().duplicate(self.getMetaDataEntry("brand") + "_" + new_id, new_name) - result.setMetaDataEntry("GUID", str(uuid.uuid4())) + base_file = self.getMetaDataEntry("base_file", "") + new_uuid = str(uuid.uuid4()) + + if base_file: + containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = base_file) + if containers: + new_basefile = containers[0].duplicate(self.getMetaDataEntry("brand") + "_" + new_id, new_name) + new_basefile.setMetaDataEntry("GUID", new_uuid) + base_file = new_basefile.id + UM.Settings.ContainerRegistry.getInstance().addContainer(new_basefile) + + new_id = self.getMetaDataEntry("brand") + "_" + new_id + "_" + self.getDefinition().getId() + variant = self.getMetaDataEntry("variant") + if variant: + variant_containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = variant) + if variant_containers: + new_id += "_" + variant_containers[0].getName().replace(" ", "_") + + result = super().duplicate(new_id, new_name) + result.setMetaDataEntry("GUID", new_uuid) + result.setMetaDataEntry("base_file", base_file) return result ## Overridden from InstanceContainer @@ -33,6 +52,9 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): ## Overridden from InstanceContainer def setMetaDataEntry(self, key, value): + if self.isReadOnly(): + return + super().setMetaDataEntry(key, value) for container in UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(GUID = self.getMetaDataEntry("GUID")): From 19fd2795e1cd909bb969a4c4e514d8cb1fd884f5 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 7 Jul 2016 01:42:39 +0200 Subject: [PATCH 104/236] Mark XmlMaterialProfile as type "material" so the import/export code can find it Contributes to CURA-341 --- plugins/XmlMaterialProfile/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/XmlMaterialProfile/__init__.py b/plugins/XmlMaterialProfile/__init__.py index 041a3f6346..213b9a358a 100644 --- a/plugins/XmlMaterialProfile/__init__.py +++ b/plugins/XmlMaterialProfile/__init__.py @@ -17,6 +17,7 @@ def getMetaData(): "api": 3 }, "settings_container": { + "type": "material", "mimetype": "application/x-ultimaker-material-profile" } } From 0da07b55ce7c91286f5ea667bd69a2e5229907cb Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 7 Jul 2016 01:43:18 +0200 Subject: [PATCH 105/236] Display brand and color name in the material menu if they are different than "generic" Contributes to CURA-341 --- resources/qml/Menus/MaterialMenu.qml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/resources/qml/Menus/MaterialMenu.qml b/resources/qml/Menus/MaterialMenu.qml index 4af8246930..be2ef4a551 100644 --- a/resources/qml/Menus/MaterialMenu.qml +++ b/resources/qml/Menus/MaterialMenu.qml @@ -36,7 +36,22 @@ Menu } MenuItem { - text: model.name; + text: + { + var result = model.name + + if(model.metadata.brand != undefined && model.metadata.brand != "Generic") + { + result = model.metadata.brand + " " + result + } + + if(model.metadata.color_name != undefined && model.metadata.color_name != "Generic") + { + result = result + " (%1)".arg(model.metadata.color_name) + } + + return result + } checkable: true; checked: model.id == Cura.MachineManager.activeMaterialId; exclusiveGroup: group; From 17ff92bdc9fb9afb62f748b89ca778cc7c97c677 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 7 Jul 2016 01:43:44 +0200 Subject: [PATCH 106/236] Add support for import/export to the Materials page Contributes to CURA-341 --- resources/qml/Preferences/MaterialsPage.qml | 113 +++++++++++++++++++- 1 file changed, 110 insertions(+), 3 deletions(-) diff --git a/resources/qml/Preferences/MaterialsPage.qml b/resources/qml/Preferences/MaterialsPage.qml index a40d4c1e3b..35fa7256ea 100644 --- a/resources/qml/Preferences/MaterialsPage.qml +++ b/resources/qml/Preferences/MaterialsPage.qml @@ -66,7 +66,23 @@ UM.ManagementPage text: catalog.i18nc("@action:button", "Duplicate"); iconName: "list-add"; enabled: base.currentItem != null - onClicked: Cura.ContainerManager.duplicateContainer(base.currentItem.id) + onClicked: + { + var material_id = Cura.ContainerManager.duplicateContainer(base.currentItem.id) + if(material_id == "") + { + return + } + + if(Cura.MachineManager.filterQualityByMachine) + { + var quality_id = Cura.ContainerManager.duplicateContainer(Cura.MachineManager.activeQualityId) + Cura.ContainerManager.setContainerMetaDataEntry(quality_id, "material", material_id) + Cura.MachineManager.setActiveQuality(quality_id) + } + + Cura.MachineManager.setActiveMaterial(material_id) + } }, Button { @@ -74,6 +90,19 @@ UM.ManagementPage iconName: "list-remove"; enabled: base.currentItem != null && !base.currentItem.readOnly onClicked: confirmDialog.open() + }, + Button + { + text: catalog.i18nc("@action:button", "Import"); + iconName: "document-import"; + onClicked: importDialog.open(); + }, + Button + { + text: catalog.i18nc("@action:button", "Export") + iconName: "document-export" + onClicked: exportDialog.open() + enabled: currentItem != null } ] @@ -98,6 +127,8 @@ UM.ManagementPage text: catalog.i18nc("@action:button", "Edit"); iconName: "document-edit"; + enabled: base.currentItem != null && !base.currentItem.readOnly + checkable: true } } @@ -113,7 +144,7 @@ UM.ManagementPage bottom: parent.bottom } - editingEnabled: editButton.checked; + editingEnabled: base.currentItem != null && !base.currentItem.readOnly && editButton.checked; properties: materialProperties containerId: base.currentItem.id @@ -147,7 +178,83 @@ UM.ManagementPage { id: confirmDialog object: base.currentItem != null ? base.currentItem.name : "" - onYes: Cura.ContainerManager.removeContainer(base.currentItem.id) + onYes: + { + var containers = Cura.ContainerManager.findInstanceContainers({"GUID": base.currentItem.metadata.GUID}) + for(var i in containers) + { + Cura.ContainerManager.removeContainer(containers[i]) + } + } + } + + FileDialog + { + id: importDialog; + title: catalog.i18nc("@title:window", "Import Material"); + selectExisting: true; + nameFilters: Cura.ContainerManager.getContainerNameFilters("material") + folder: CuraApplication.getDefaultPath() + onAccepted: + { + var result = Cura.ContainerManager.importContainer(fileUrl) + + messageDialog.title = catalog.i18nc("@title:window", "Import Material") + messageDialog.text = catalog.i18nc("@info:status", "Could not import material %1: %2").arg(fileUrl).arg(result.message) + if(result.status == "success") + { + messageDialog.icon = StandardIcon.Information + messageDialog.text = catalog.i18nc("@info:status", "Successfully imported material %1").arg(fileUrl) + } + else if(result.status == "duplicate") + { + messageDialog.icon = StandardIcon.Warning + } + else + { + messageDialog.icon = StandardIcon.Critical + } + messageDialog.open() + } + } + + FileDialog + { + id: exportDialog; + title: catalog.i18nc("@title:window", "Export Material"); + selectExisting: false; + nameFilters: Cura.ContainerManager.getContainerNameFilters("material") + folder: CuraApplication.getDefaultPath() + onAccepted: + { + if(base.currentItem.metadata.base_file) + { + var result = Cura.ContainerManager.exportContainer(base.currentItem.metadata.base_file, selectedNameFilter, fileUrl) + } + else + { + var result = Cura.ContainerManager.exportContainer(base.currentItem.id, selectedNameFilter, fileUrl) + } + + messageDialog.title = catalog.i18nc("@title:window", "Export Material") + if(result.status == "error") + { + messageDialog.icon = StandardIcon.Critical + messageDialog.text = catalog.i18nc("@info:status", "Failed to export material to %1: %2").arg(fileUrl).arg(result.message) + messageDialog.open() + } + else if(result.status == "success") + { + messageDialog.icon = StandardIcon.Information + messageDialog.text = catalog.i18nc("@info:status", "Successfully exported material to %1").arg(fileUrl) + messageDialog.open() + } + } + } + + MessageDialog + { + id: messageDialog } } From 35efea3d11b8f84f142d436f6d26b2f3c02986a8 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 7 Jul 2016 09:52:53 +0200 Subject: [PATCH 107/236] Show "ready" state when a printer is connected but jobstate is not yet set CURA-1036 --- resources/qml/MonitorButton.qml | 2 +- resources/qml/Sidebar.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/MonitorButton.qml b/resources/qml/MonitorButton.qml index bbb39b188e..af163a39e2 100644 --- a/resources/qml/MonitorButton.qml +++ b/resources/qml/MonitorButton.qml @@ -24,7 +24,7 @@ Rectangle return UM.Theme.getColor("status_offline") else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing" || Cura.MachineManager.printerOutputDevices[0].jobState == "pre_print") return UM.Theme.getColor("status_busy") - else if(Cura.MachineManager.printerOutputDevices[0].jobState == "ready") + else if(Cura.MachineManager.printerOutputDevices[0].jobState == "ready" || Cura.MachineManager.printerOutputDevices[0].jobState == "") return UM.Theme.getColor("status_ready") else if(Cura.MachineManager.printerOutputDevices[0].jobState == "paused") return UM.Theme.getColor("status_paused") diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index 78930c8d90..d9b3b56109 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -103,7 +103,7 @@ Rectangle return UM.Theme.getIcon("tab_monitor") else if(Cura.MachineManager.printerOutputDevices[0].jobState == "printing" || Cura.MachineManager.printerOutputDevices[0].jobState == "pre_print") return UM.Theme.getIcon("tab_monitor_busy") - else if(Cura.MachineManager.printerOutputDevices[0].jobState == "ready") + else if(Cura.MachineManager.printerOutputDevices[0].jobState == "ready" || Cura.MachineManager.printerOutputDevices[0].jobState == "") return UM.Theme.getIcon("tab_monitor_connected") else if(Cura.MachineManager.printerOutputDevices[0].jobState == "paused") return UM.Theme.getIcon("tab_monitor_paused") From 2ba2599d6b860c724cd4668c65face16c17884f0 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 7 Jul 2016 10:23:01 +0200 Subject: [PATCH 108/236] Remove debug statement and commented-out code CURA-1385 --- plugins/UltimakerMachineActions/UMOCheckupMachineAction.py | 1 - plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml | 1 - 2 files changed, 2 deletions(-) diff --git a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py index 861477d61d..4a44f50bf4 100644 --- a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py +++ b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py @@ -79,7 +79,6 @@ class UMOCheckupMachineAction(MachineAction): @pyqtProperty(bool, notify = onBedTestCompleted) def bedTestCompleted(self): - print("zomg?") return self._bed_test_completed @pyqtProperty(bool, notify = onHotendTestCompleted) diff --git a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml index d2bc90b601..c85a9c74e4 100644 --- a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml +++ b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml @@ -51,7 +51,6 @@ Cura.MachineAction text: catalog.i18nc("@action:button","Start Printer Check"); onClicked: { - //checkupContent.visible = true manager.startCheck() } } From 8c22efc4dd62ed72a81405e58048ba5eaf54b228 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 7 Jul 2016 10:38:08 +0200 Subject: [PATCH 109/236] Fix UMO Checkup button size CURA-1385 --- .../UMOCheckupMachineAction.qml | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml index c85a9c74e4..5d5a102e7d 100644 --- a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml +++ b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml @@ -156,6 +156,7 @@ Cura.MachineAction { id: nozzleTempLabel width: checkupMachineAction.leftRow + height: nozzleTempButton.height anchors.left: parent.left anchors.top: endstopZLabel.bottom wrapMode: Text.WordWrap @@ -174,15 +175,12 @@ Cura.MachineAction { id: nozzleTempButton width: checkupMachineAction.rightRow * 0.3 - height: nozzleTemp.height + height: childrenRect.height anchors.top: nozzleTempLabel.top anchors.left: bedTempStatus.right anchors.leftMargin: UM.Theme.getSize("default_margin").width/2 Button { - height: nozzleTemp.height - 2 - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter text: catalog.i18nc("@action:button","Start Heating") onClicked: { @@ -206,10 +204,11 @@ Cura.MachineAction { id: bedTempLabel width: checkupMachineAction.leftRow + height: bedTempButton.height anchors.left: parent.left anchors.top: nozzleTempLabel.bottom wrapMode: Text.WordWrap - text: catalog.i18nc("@label","bed temperature check:") + text: catalog.i18nc("@label","Bed temperature check:") } Label @@ -225,15 +224,12 @@ Cura.MachineAction { id: bedTempButton width: checkupMachineAction.rightRow * 0.3 - height: bedTemp.height + height: childrenRect.height anchors.top: bedTempLabel.top anchors.left: bedTempStatus.right anchors.leftMargin: UM.Theme.getSize("default_margin").width/2 Button { - height: bedTemp.height - 2 - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter text: catalog.i18nc("@action:button","Start Heating") onClicked: { From 464fe111239cb52602ca5ae30fa2b846e4e1da6a Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Thu, 7 Jul 2016 11:04:34 +0200 Subject: [PATCH 110/236] Fixed profile file case-sensitivity. CURA-1720 --- cura/CuraContainerRegistry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/CuraContainerRegistry.py b/cura/CuraContainerRegistry.py index 2ecb22d670..af7ca2e87e 100644 --- a/cura/CuraContainerRegistry.py +++ b/cura/CuraContainerRegistry.py @@ -53,7 +53,7 @@ class CuraContainerRegistry(ContainerRegistry): def _containerExists(self, container_type, container_name): container_class = ContainerStack if container_type == "machine" else InstanceContainer - return self.findContainers(container_class, id = container_name, type = container_type) or \ + return self.findContainers(container_class, id = container_name, type = container_type, ignore_case = True) or \ self.findContainers(container_class, name = container_name, type = container_type) ## Exports an profile to a file From 8e63016ef3d506b069a9d9a8f7c0553e53adf659 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 7 Jul 2016 12:07:16 +0200 Subject: [PATCH 111/236] Add logging to explain why ContainerManager's methods do not complete successfully Contributes to CURA-341 --- cura/Settings/ContainerManager.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index 6f272c65d8..eb9dab0ad7 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -12,6 +12,7 @@ import UM.Settings import UM.SaveFile import UM.Platform import UM.MimeTypeDatabase +import UM.Logger from UM.MimeTypeDatabase import MimeTypeNotFoundError @@ -42,12 +43,15 @@ class ContainerManager(QObject): def duplicateContainer(self, container_id): containers = self._registry.findContainers(None, id = container_id) if not containers: + UM.Logger.log("w", "Could duplicate container %s because it was not found.", container_id) return "" container = containers[0] new_container = None new_name = self._registry.uniqueName(container.getName()) + # Only InstanceContainer has a duplicate method at the moment. + # So fall back to serialize/deserialize when no duplicate method exists. if hasattr(container, "duplicate"): new_container = container.duplicate(new_name) else: @@ -71,6 +75,7 @@ class ContainerManager(QObject): def renameContainer(self, container_id, new_id, new_name): containers = self._registry.findContainers(None, id = container_id) if not containers: + UM.Logger.log("w", "Could rename container %s because it was not found.", container_id) return False container = containers[0] @@ -98,6 +103,7 @@ class ContainerManager(QObject): def removeContainer(self, container_id): containers = self._registry.findContainers(None, id = container_id) if not containers: + UM.Logger.log("w", "Could remove container %s because it was not found.", container_id) return False self._registry.removeContainer(containers[0].getId()) @@ -115,19 +121,22 @@ class ContainerManager(QObject): # \return True if successfully merged, False if not. @pyqtSlot(str, result = bool) def mergeContainers(self, merge_into_id, merge_id): - containers = self._registry.findContainers(None, id = container_id) + containers = self._registry.findContainers(None, id = merge_into_id) if not containers: + UM.Logger.log("w", "Could merge into container %s because it was not found.", merge_into_id) return False merge_into = containers[0] - containers = self._registry.findContainers(None, id = container_id) + containers = self._registry.findContainers(None, id = merge_id) if not containers: + UM.Logger.log("w", "Could not merge container %s because it was not found", merge_id) return False merge = containers[0] if type(merge) != type(merge_into): + UM.Logger.log("w", "Cannot merge two containers of different types") return False for key in merge.getAllKeys(): @@ -144,9 +153,11 @@ class ContainerManager(QObject): def clearContainer(self, container_id): containers = self._registry.findContainers(None, id = container_id) if not containers: + UM.Logger.log("w", "Could clear container %s because it was not found.", container_id) return False if containers[0].isReadOnly(): + UM.Logger.log("w", "Cannot clear read-only container %s", container_id) return False containers[0].clear() @@ -169,11 +180,13 @@ class ContainerManager(QObject): def setContainerMetaDataEntry(self, container_id, entry_name, entry_value): containers = UM.Settings.ContainerRegistry.getInstance().findContainers(None, id = container_id) if not containers: + UM.Logger.log("w", "Could set metadata of container %s because it was not found.", container_id) return False container = containers[0] if container.isReadOnly(): + UM.Logger.log("w", "Cannot set metadata of read-only container %s.", container_id) return False entries = entry_name.split("/") From 3d1bbeca3815598bd16b3e489c31a22021c5076e Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 7 Jul 2016 13:43:45 +0200 Subject: [PATCH 112/236] Restarting machine check properly re-enables endstop check CURA-1385 --- plugins/USBPrinting/USBPrinterOutputDevice.py | 11 +++++++---- .../UMOCheckupMachineAction.py | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index 2938604eed..da3dfa2bde 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -37,9 +37,8 @@ class USBPrinterOutputDevice(PrinterOutputDevice): self._connect_thread = threading.Thread(target = self._connect) self._connect_thread.daemon = True - self._end_stop_thread = threading.Thread(target = self._pollEndStop) - self._end_stop_thread.daemon = True - self._poll_endstop = -1 + self._end_stop_thread = None + self._poll_endstop = False # The baud checking is done by sending a number of m105 commands to the printer and waiting for a readable # response. If the baudrate is correct, this should make sense, else we get giberish. @@ -221,13 +220,17 @@ class USBPrinterOutputDevice(PrinterOutputDevice): @pyqtSlot() def startPollEndstop(self): - if self._poll_endstop == -1: + if not self._poll_endstop: self._poll_endstop = True + if self._end_stop_thread is None: + self._end_stop_thread = threading.Thread(target=self._pollEndStop) + self._end_stop_thread.daemon = True self._end_stop_thread.start() @pyqtSlot() def stopPollEndstop(self): self._poll_endstop = False + self._end_stop_thread = None def _pollEndStop(self): while self._connection_state == ConnectionState.connected and self._poll_endstop: diff --git a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py index 4a44f50bf4..201e415e15 100644 --- a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py +++ b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py @@ -157,7 +157,7 @@ class UMOCheckupMachineAction(MachineAction): self._output_device.bedTemperatureChanged.connect(self._onBedTemperatureChanged) self._output_device.hotendTemperaturesChanged.connect(self._onHotendTemperatureChanged) self._output_device.endstopStateChanged.connect(self._onEndstopStateChanged) - except AttributeError: # Connection is probably not a USB connection. Something went pretty wrong if this happens. + except AttributeError as e: # Connection is probably not a USB connection. Something went pretty wrong if this happens. pass @pyqtSlot() From 2f4e63a944281ef49da3212f60cafff70797ed6f Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 19 Apr 2016 11:46:30 +0200 Subject: [PATCH 113/236] Add boilerplate version upgrade plug-in This plug-in does nothing at the moment. It merely says that it is able to upgrade configuration from version 2.1 to 2.2, but then raises exceptions when you actually try to use it. This is by design. I will now implement the functions that do the conversion. Contributes to issue CURA-844. --- .../VersionUpgrade21to22/MachineInstance.py | 21 +++++++++ .../VersionUpgrade21to22/Preferences.py | 20 +++++++++ .../VersionUpgrade21to22/Profile.py | 20 +++++++++ .../VersionUpgrade21to22.py | 45 +++++++++++++++++++ .../VersionUpgrade21to22/__init__.py | 35 +++++++++++++++ 5 files changed, 141 insertions(+) create mode 100644 plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py create mode 100644 plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py create mode 100644 plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py create mode 100644 plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py create mode 100644 plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py new file mode 100644 index 0000000000..0fafe94218 --- /dev/null +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -0,0 +1,21 @@ +# Copyright (c) 2015 Ultimaker B.V. +# Cura is released under the terms of the AGPLv3 or higher. + +## Creates a new machine instance instance by parsing a serialised machine +# instance in version 1 of the file format. +# +# \param serialised The serialised form of a machine instance in version 1. +# \return A machine instance instance, or None if the file format is +# incorrect. +def importVersion1(serialised): + return None #Not implemented yet. + +## A representation of a machine instance used as intermediary form for +# conversion from one format to the other. +class MachineInstance: + ## Serialises this machine instance as file format version 2. + # + # \return A serialised form of this machine instance, serialised in + # version 2 of the file format. + def exportVersion2(): + raise Exception("Not implemented yet.") \ No newline at end of file diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py new file mode 100644 index 0000000000..7027ce960f --- /dev/null +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py @@ -0,0 +1,20 @@ +# Copyright (c) 2015 Ultimaker B.V. +# Cura is released under the terms of the AGPLv3 or higher. + +## Creates a new preferences instance by parsing a serialised preferences file +# in version 2 of the file format. +# +# \param serialised The serialised form of a preferences file in version 2. +# \return A preferences instance, or None if the file format is incorrect. +def importVersion2(serialised): + return None #Not implemented yet. + +## A representation of a preferences file used as intermediary form for +# conversion from one format to the other. +class Preferences: + ## Serialises this preferences file as file format version 3. + # + # \return A serialised form of this preferences file, serialised in + # version 3 of the file format. + def exportVersion3(): + raise Exception("Not implemented yet.") \ No newline at end of file diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py new file mode 100644 index 0000000000..50d5b82b31 --- /dev/null +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py @@ -0,0 +1,20 @@ +# Copyright (c) 2015 Ultimaker B.V. +# Cura is released under the terms of the AGPLv3 or higher. + +## Creates a new profile instance by parsing a serialised profile in version 1 +# of the file format. +# +# \param serialised The serialised form of a profile in version 1. +# \return A profile instance, or None if the file format is incorrect. +def importVersion1(serialised): + return None #Not implemented yet. + +## A representation of a profile used as intermediary form for conversion from +# one format to the other. +class Profile: + ## Serialises this profile as file format version 2. + # + # \return A serialised form of this profile, serialised in version 2 of + # the file format. + def exportVersion2(): + raise Exception("Not implemented yet.") \ No newline at end of file diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py new file mode 100644 index 0000000000..5a333c8f5b --- /dev/null +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -0,0 +1,45 @@ +# Copyright (c) 2015 Ultimaker B.V. +# Cura is released under the terms of the AGPLv3 or higher. + +from UM.VersionUpgrade import VersionUpgrade #Superclass of the plugin. + +from . import MachineInstance #To upgrade machine instances. +from . import Preferences #To upgrade preferences. +from . import Profile #To upgrade profiles. + +## Converts configuration from Cura 2.1's file formats to Cura 2.2's. +# +# It converts the machine instances, preferences and profiles. +class VersionUpgrade21to22(VersionUpgrade): + ## Converts machine instances from format version 1 to version 2. + # + # \param serialised The serialised machine instance in version 1. + # \return The serialised machine instance in version 2, or None if the + # input was not of the correct format. + def upgradeMachineInstance(self, serialised): + machine_instance = MachineInstance.importVersion1(serialised) + if not machine_instance: #Invalid file format. + return None + return machine_instance.exportVersion2() + + ## Converts preferences from format version 2 to version 3. + # + # \param serialised The serialised preferences file in version 2. + # \return The serialised preferences file in version 3, or None if the + # input was not of the correct format. + def upgradePreferences(self, serialised): + preferences = Preferences.importVersion2(serialised) + if not preferences: #Invalid file format. + return None + return preferences.exportVersion3() + + ## Converts profiles from format version 1 to version 2. + # + # \param serialised The serialised profile in version 1. + # \return The serialised profile in version 2, or None if the input was + # not of the correct format. + def upgradeProfile(self, serialised): + profile = Profile.importVersion1(serialised) + if not profile: #Invalid file format. + return None + return profile.exportVersion2() \ No newline at end of file diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py b/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py new file mode 100644 index 0000000000..76546d1dd2 --- /dev/null +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py @@ -0,0 +1,35 @@ +# Copyright (c) 2015 Ultimaker B.V. +# Cura is released under the terms of the AGPLv3 or higher. + +from . import VersionUpgrade21to22 + +from UM.i18n import i18nCatalog +catalog = i18nCatalog("cura") + +def getMetaData(): + return { + "plugin": { + "name": catalog.i18nc("@label", "Version Upgrade 2.1 to 2.2"), + "author": "Ultimaker", + "version": "1.0", + "description": catalog.i18nc("@info:whatsthis", "Upgrades configurations from Cura 2.1 to Cura 2.2."), + "api": 2 + }, + "version_upgrade": { + "profile": { + "from": 1, + "to": 2 + }, + "preferences": { + "from": 2, + "to": 3 + }, + "machine_instance": { + "from": 1, + "to": 2 + } + } + } + +def register(app): + return { "version_upgrade": VersionUpgrade21to22.VersionUpgrade21to22() } From b643fe8fd0c8643a359c98d40aa3d6d9b511c67f Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 19 Apr 2016 13:44:16 +0200 Subject: [PATCH 114/236] Translucent translation of machine instances Machine instances are translucently translated from version 1 to version 2. No setting changes are applied yet, nor has the format itself changed. Contributes to issue CURA-844. --- .../VersionUpgrade21to22/MachineInstance.py | 61 ++++++++++++++++++- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py index 0fafe94218..f924f4756d 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -1,6 +1,11 @@ # Copyright (c) 2015 Ultimaker B.V. # Cura is released under the terms of the AGPLv3 or higher. +import UM.SettingsError #To indicate that a file is of incorrect format. + +import configparser #To read config files. +import io #To write config files to strings as if they were files. + ## Creates a new machine instance instance by parsing a serialised machine # instance in version 1 of the file format. # @@ -8,14 +13,66 @@ # \return A machine instance instance, or None if the file format is # incorrect. def importVersion1(serialised): - return None #Not implemented yet. + return MachineInstance(serialised) ## A representation of a machine instance used as intermediary form for # conversion from one format to the other. class MachineInstance: + ## Reads version 1 of the file format, storing it in memory. + # + # \param serialised A string with the contents of a machine instance file. + def __init__(self, serialised): + config = configparser.ConfigParser(interpolation = None) + config.read_string(serialised) #Read the input string as config file. + + #Checking file correctness. + if not config.has_section("general"): + raise SettingsError.InvalidFormatError("general") + if not config.has_option("general", "version"): + raise SettingsError.InvalidFormatError("general/version") + if not config.has_option("general", "name"): + raise SettingsError.InvalidFormatError("general/name") + if not config.has_option("general", "type"): + raise SettingsError.InvalidFormatError("general/type") + if int(config.get("general", "version")) != 1: #Explicitly hard-code version 1, since if this number changes the programmer MUST change this entire function. + raise SettingsError.InvalidVersionError("Version upgrade intermediary version 1") + + self._type_name = config.get("general", "type") + self._variant_name = config.get("general", "variant", fallback = None) + self._name = config.get("general", "name") + self._key = config.get("general", "key", fallback = None) + self._active_profile_name = config.get("general", "active_profile", fallback = None) + self._active_material_name = config.get("general", "material", fallback = None) + + self._machine_setting_overrides = {} + for key, value in config["machine_settings"].items(): + self._machine_setting_overrides[key] = value + ## Serialises this machine instance as file format version 2. # # \return A serialised form of this machine instance, serialised in # version 2 of the file format. def exportVersion2(): - raise Exception("Not implemented yet.") \ No newline at end of file + config = configparser.ConfigParser(interpolation = None) #Build a config file in the form of version 2. + + config.add_section("general") + config.set("general", "name", self._name) + config.set("general", "type", self._type_name) + config.set("general", "version", 2) #Hard-code version 2, since if this number changes the programmer MUST change this entire function. + if self._variant_name: + config.set("general", "variant", self._variant_name) + if self._key: + config.set("general", "key", self._key) + if self._active_profile_name: + config.set("general", "active_profile", self._active_profile_name) + if self._active_material_name: + config.set("general", "material", self._active_material_name) + + config.add_section("machine_settings") + for key, value in self._machine_setting_overrides.items(): + #TODO: Filter these through a translation dictionary. + config.set("machine_settings", key, value) + + output = io.StringIO() + config.write(output) + return output.getvalue() \ No newline at end of file From 242559bd7ebc5c72c67d8dc7749ec7ea406f7a04 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 19 Apr 2016 13:53:56 +0200 Subject: [PATCH 115/236] Remove preferences upgrade from 2.1->2.2 No preferences will need to be upgraded. Contributes to issue CURA-844. --- .../VersionUpgrade21to22/Preferences.py | 20 ------------------- .../VersionUpgrade21to22.py | 13 +----------- .../VersionUpgrade21to22/__init__.py | 4 ---- 3 files changed, 1 insertion(+), 36 deletions(-) delete mode 100644 plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py deleted file mode 100644 index 7027ce960f..0000000000 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) 2015 Ultimaker B.V. -# Cura is released under the terms of the AGPLv3 or higher. - -## Creates a new preferences instance by parsing a serialised preferences file -# in version 2 of the file format. -# -# \param serialised The serialised form of a preferences file in version 2. -# \return A preferences instance, or None if the file format is incorrect. -def importVersion2(serialised): - return None #Not implemented yet. - -## A representation of a preferences file used as intermediary form for -# conversion from one format to the other. -class Preferences: - ## Serialises this preferences file as file format version 3. - # - # \return A serialised form of this preferences file, serialised in - # version 3 of the file format. - def exportVersion3(): - raise Exception("Not implemented yet.") \ No newline at end of file diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py index 5a333c8f5b..dc7012e86b 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -9,7 +9,7 @@ from . import Profile #To upgrade profiles. ## Converts configuration from Cura 2.1's file formats to Cura 2.2's. # -# It converts the machine instances, preferences and profiles. +# It converts the machine instances and profiles. class VersionUpgrade21to22(VersionUpgrade): ## Converts machine instances from format version 1 to version 2. # @@ -22,17 +22,6 @@ class VersionUpgrade21to22(VersionUpgrade): return None return machine_instance.exportVersion2() - ## Converts preferences from format version 2 to version 3. - # - # \param serialised The serialised preferences file in version 2. - # \return The serialised preferences file in version 3, or None if the - # input was not of the correct format. - def upgradePreferences(self, serialised): - preferences = Preferences.importVersion2(serialised) - if not preferences: #Invalid file format. - return None - return preferences.exportVersion3() - ## Converts profiles from format version 1 to version 2. # # \param serialised The serialised profile in version 1. diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py b/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py index 76546d1dd2..a8e976853f 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py @@ -20,10 +20,6 @@ def getMetaData(): "from": 1, "to": 2 }, - "preferences": { - "from": 2, - "to": 3 - }, "machine_instance": { "from": 1, "to": 2 From 00f356f06e47778ad49b7e9e3cadc378cc6a324b Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 19 Apr 2016 15:24:28 +0200 Subject: [PATCH 116/236] Add translation of speed_support_lines This setting had its key changed to speed_support_infill Contributes to issue CURA-844. --- .../VersionUpgrade/VersionUpgrade21to22/MachineInstance.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py index f924f4756d..ae8cb68cc2 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -50,6 +50,8 @@ class MachineInstance: ## Serialises this machine instance as file format version 2. # + # This is where the actual translation happens in this case. + # # \return A serialised form of this machine instance, serialised in # version 2 of the file format. def exportVersion2(): @@ -70,7 +72,8 @@ class MachineInstance: config.add_section("machine_settings") for key, value in self._machine_setting_overrides.items(): - #TODO: Filter these through a translation dictionary. + if key == "speed_support_lines": #Setting key was changed for 2.2. + key = "speed_support_infill" config.set("machine_settings", key, value) output = io.StringIO() From 9bc5f979029d1350772f1079891951e1b8c30b42 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 19 Apr 2016 15:59:19 +0200 Subject: [PATCH 117/236] Implement conversion of profiles 2.1->2.2 This works more or less the same as for machine instances. The code was copied from version 2.1 of the unserialise function of profiles, and optimised a bit. The output function is written from scratch. It has some code duplication. Maybe we have to do something about that. Contributes to issue CURA-844. --- .../VersionUpgrade21to22/Profile.py | 99 ++++++++++++++++++- 1 file changed, 96 insertions(+), 3 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py index 50d5b82b31..f5bda015d5 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py @@ -1,20 +1,113 @@ -# Copyright (c) 2015 Ultimaker B.V. +# Copyright (c) 2016 Ultimaker B.V. # Cura is released under the terms of the AGPLv3 or higher. +import UM.SettingsError #To indicate that a file is of incorrect format. + +import configparser #To read config files. +import io #To write config files to strings as if they were files. + ## Creates a new profile instance by parsing a serialised profile in version 1 # of the file format. # # \param serialised The serialised form of a profile in version 1. # \return A profile instance, or None if the file format is incorrect. def importVersion1(serialised): - return None #Not implemented yet. + return Profile(serialised) ## A representation of a profile used as intermediary form for conversion from # one format to the other. class Profile: + ## Reads version 1 of the file format, storing it in memory. + # + # \param serialised A string with the contents of a machine instance file. + def __init__(self, serialised): + parser = configparser.ConfigParser(interpolation = None) + parser.read_string(serialised) + + #Check correctness. + if not parser.has_section("general"): + raise SettingsError.InvalidFormatError("general") + if not parser.has_option("general", "version") or int(parser.get("general", "version")) != 1: #Hard-coded profile version here. If this number changes the entire function needs to change. + raise SettingsError.InvalidVersionError("Version upgrade intermediary version 1") + + #Parse the general section. + self._name = parser.get("general", "name") + self._type = parser.get("general", "type", fallback = None) + if "weight" in parser["general"]: + self._weight = int(parser.get("general", "weight")) + else: + self._weight = None + self._machine_type_id = parser.get("general", "machine_type", fallback = None) + self._machine_variant_name = parser.get("general", "machine_variant", fallback = None) + self._machine_instance_name = parser.get("general", "machine_instance", fallback = None) + if "material" in parser["general"]: + self._material_name = parser.get("general", "material") + elif self._type == "material": + self._material_name = parser.get("general", "name", fallback = None) + else: + self._material_name = None + + #Parse the settings. + self._settings = {} + if parser.has_section("settings"): + for key, value in parser["settings"].items(): + self._settings[key] = value + + #Parse the defaults and the disabled defaults. + self._changed_settings_defaults = {} + if parser.has_section("defaults"): + for key, value in parser["defaults"].items(): + self._changed_settings_defaults[key] = value + self._disabled_settings_defaults = [] + if parser.has_section("disabled_defaults"): + disabled_defaults_string = parser.get("disabled_defaults", "values") + for item in disabled_defaults_string.split(","): + if item != "": + self._disabled_settings_defaults.append(item) + ## Serialises this profile as file format version 2. # # \return A serialised form of this profile, serialised in version 2 of # the file format. def exportVersion2(): - raise Exception("Not implemented yet.") \ No newline at end of file + config = configparser.ConfigParser(interpolation = None) + + config.add_section("general") + config.set("general", "version", "2") #Hard-coded profile version 2 + config.set("general", "name", self._name) + if self._type: + config.set("general", "type", self._type) + if self._weight: + config.set("general", "weight", self._weight) + if self._machine_type_id: + config.set("general", "machine_type", self._machine_type_id) + if self._machine_variant_name: + config.set("general", "machine_variant", self._machine_variant_name) + if self._machine_instance_name: + config.set("general", "machine_instance", self._machine_instance_name) + if self._material_name and self._type != "material": + config.set("general", "material", self._material_name) + + if self._settings: + config.add_section("settings") + for key, value in self._settings.items(): + if key == "speed_support_lines": #Setting key was changed for 2.2. + key = "speed_support_infill" + config.set("settings", key, value) + + if self._changed_settings_defaults: + config.add_section("defaults") + for key, value in self._changed_settings_defaults.items(): + if key == "speed_support_lines": #Setting key was changed for 2.2. + key = "speed_support_infill" + config.set("defaults", key, value) + + if self._disabled_settings_defaults: + config.add_section("disabled_defaults") + disabled_defaults_string = str(self._disabled_settings_defaults[0]) #Must be at least 1 item, otherwise we wouldn't enter this if statement. + for item in self._disabled_settings_defaults[1:]: + disabled_defaults_string += "," + str(item) + + output = io.StringIO() + config.write(output) + return output.getvalue() \ No newline at end of file From 0d553c10f17204625115bd77a127c52bd9041770 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 19 Apr 2016 16:05:38 +0200 Subject: [PATCH 118/236] Fix import of SettingsError It is in a submodule. Contributes to issue CURA-844. --- plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py | 2 +- plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py index ae8cb68cc2..2291adf92b 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -1,7 +1,7 @@ # Copyright (c) 2015 Ultimaker B.V. # Cura is released under the terms of the AGPLv3 or higher. -import UM.SettingsError #To indicate that a file is of incorrect format. +import UM.Settings.SettingsError #To indicate that a file is of incorrect format. import configparser #To read config files. import io #To write config files to strings as if they were files. diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py index f5bda015d5..8fd48745b1 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py @@ -1,7 +1,7 @@ # Copyright (c) 2016 Ultimaker B.V. # Cura is released under the terms of the AGPLv3 or higher. -import UM.SettingsError #To indicate that a file is of incorrect format. +import UM.Settings.SettingsError #To indicate that a file is of incorrect format. import configparser #To read config files. import io #To write config files to strings as if they were files. From 724f9ce0106a598421a57c3bf24efd3579fd28f3 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 19 Apr 2016 16:08:47 +0200 Subject: [PATCH 119/236] Return None if config file wasn't correct This was actually specified in the original function description. Contributes to issue CURA-844. --- .../VersionUpgrade/VersionUpgrade21to22/MachineInstance.py | 5 ++++- plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py index 2291adf92b..1a6f7b9da9 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -13,7 +13,10 @@ import io #To write config files to strings as if they were files. # \return A machine instance instance, or None if the file format is # incorrect. def importVersion1(serialised): - return MachineInstance(serialised) + try: + return MachineInstance(serialised) + except configparser.Error, SettingsError.InvalidFormatError, SettingsError.InvalidVersionError: + return None ## A representation of a machine instance used as intermediary form for # conversion from one format to the other. diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py index 8fd48745b1..52180b72e0 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py @@ -12,7 +12,10 @@ import io #To write config files to strings as if they were files. # \param serialised The serialised form of a profile in version 1. # \return A profile instance, or None if the file format is incorrect. def importVersion1(serialised): - return Profile(serialised) + try: + return Profile(serialised) + except configparser.Error, SettingsError.InvalidFormatError, SettingsError.InvalidVersionError: + return None ## A representation of a profile used as intermediary form for conversion from # one format to the other. From ccf9796b5108294c04e7c58b6b3e1f2130bf3244 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 19 Apr 2016 16:09:32 +0200 Subject: [PATCH 120/236] Remove superfluous import Preferences doesn't exist any more. Contributes to issue CURA-844. --- .../VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py index dc7012e86b..385001a3e6 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -4,7 +4,6 @@ from UM.VersionUpgrade import VersionUpgrade #Superclass of the plugin. from . import MachineInstance #To upgrade machine instances. -from . import Preferences #To upgrade preferences. from . import Profile #To upgrade profiles. ## Converts configuration from Cura 2.1's file formats to Cura 2.2's. From 816e94c7603c5e2c63dbbf90182e748d3f75c9ed Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 19 Apr 2016 16:12:13 +0200 Subject: [PATCH 121/236] Fix multiple exceptions Turns out that this syntax needs to be surrounded in brackets. Contributes to issue CURA-844. --- plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py | 2 +- plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py index 1a6f7b9da9..8799c32b46 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -15,7 +15,7 @@ import io #To write config files to strings as if they were files. def importVersion1(serialised): try: return MachineInstance(serialised) - except configparser.Error, SettingsError.InvalidFormatError, SettingsError.InvalidVersionError: + except (configparser.Error, SettingsError.InvalidFormatError, SettingsError.InvalidVersionError): return None ## A representation of a machine instance used as intermediary form for diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py index 52180b72e0..65fc243de6 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py @@ -14,7 +14,7 @@ import io #To write config files to strings as if they were files. def importVersion1(serialised): try: return Profile(serialised) - except configparser.Error, SettingsError.InvalidFormatError, SettingsError.InvalidVersionError: + except (configparser.Error, SettingsError.InvalidFormatError, SettingsError.InvalidVersionError): return None ## A representation of a profile used as intermediary form for conversion from From aeecdd9e0e95d62e76dffc36cb23ddc7c83014fc Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 19 Apr 2016 16:31:22 +0200 Subject: [PATCH 122/236] Add self to parameters of function Yeah this was a method, not a static method. Contributes to issue CURA-844. --- plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py index 8799c32b46..5b29800d5f 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -57,7 +57,7 @@ class MachineInstance: # # \return A serialised form of this machine instance, serialised in # version 2 of the file format. - def exportVersion2(): + def exportVersion2(self): config = configparser.ConfigParser(interpolation = None) #Build a config file in the form of version 2. config.add_section("general") From 149e873eb6ed8b2d99289cbdf7f42225b82f795a Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 19 Apr 2016 17:26:28 +0200 Subject: [PATCH 123/236] Pass strings to writing configparser When setting fields in configparser, it must be a string. No ints. Contributes to issue CURA-844. --- .../VersionUpgrade/VersionUpgrade21to22/MachineInstance.py | 4 ++-- plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py index 5b29800d5f..038d35bee0 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -63,7 +63,7 @@ class MachineInstance: config.add_section("general") config.set("general", "name", self._name) config.set("general", "type", self._type_name) - config.set("general", "version", 2) #Hard-code version 2, since if this number changes the programmer MUST change this entire function. + config.set("general", "version", "2") #Hard-code version 2, since if this number changes the programmer MUST change this entire function. if self._variant_name: config.set("general", "variant", self._variant_name) if self._key: @@ -77,7 +77,7 @@ class MachineInstance: for key, value in self._machine_setting_overrides.items(): if key == "speed_support_lines": #Setting key was changed for 2.2. key = "speed_support_infill" - config.set("machine_settings", key, value) + config.set("machine_settings", key, str(value)) output = io.StringIO() config.write(output) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py index 65fc243de6..d9e00bf85d 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py @@ -96,14 +96,14 @@ class Profile: for key, value in self._settings.items(): if key == "speed_support_lines": #Setting key was changed for 2.2. key = "speed_support_infill" - config.set("settings", key, value) + config.set("settings", key, str(value)) if self._changed_settings_defaults: config.add_section("defaults") for key, value in self._changed_settings_defaults.items(): if key == "speed_support_lines": #Setting key was changed for 2.2. key = "speed_support_infill" - config.set("defaults", key, value) + config.set("defaults", key, str(value)) if self._disabled_settings_defaults: config.add_section("disabled_defaults") From 17f47033373727473d8c0c457cb7dc05a224d291 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 19 Apr 2016 17:28:26 +0200 Subject: [PATCH 124/236] Fix self parameter It must have access to self to get the values to store in the file. Contributes to issue CURA-844. --- plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py index d9e00bf85d..fe0a48f6d0 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py @@ -72,7 +72,7 @@ class Profile: # # \return A serialised form of this profile, serialised in version 2 of # the file format. - def exportVersion2(): + def exportVersion2(self): config = configparser.ConfigParser(interpolation = None) config.add_section("general") From f7be4831b856d64655d4fcaee84edb7f9f09276c Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 20 Apr 2016 12:04:22 +0200 Subject: [PATCH 125/236] Add translation for combing Combing was made into an enum instead of a boolean. Contributes to issue CURA-844. --- .../VersionUpgrade/VersionUpgrade21to22/MachineInstance.py | 2 ++ plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py index 038d35bee0..5e90e27f17 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -77,6 +77,8 @@ class MachineInstance: for key, value in self._machine_setting_overrides.items(): if key == "speed_support_lines": #Setting key was changed for 2.2. key = "speed_support_infill" + if key == "retraction_combing": #Combing was made into an enum instead of a boolean. + value = "off" if (value == "False") else "all" config.set("machine_settings", key, str(value)) output = io.StringIO() diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py index fe0a48f6d0..a70293f8b9 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py @@ -96,6 +96,8 @@ class Profile: for key, value in self._settings.items(): if key == "speed_support_lines": #Setting key was changed for 2.2. key = "speed_support_infill" + if key == "retraction_combing": #Combing was made into an enum instead of a boolean. + value = "off" if (value == "False") else "all" config.set("settings", key, str(value)) if self._changed_settings_defaults: @@ -103,6 +105,8 @@ class Profile: for key, value in self._changed_settings_defaults.items(): if key == "speed_support_lines": #Setting key was changed for 2.2. key = "speed_support_infill" + if key == "retraction_combing": #Combing was made into an enum instead of a boolean. + value = "off" if (value == "False") else "all" config.set("defaults", key, str(value)) if self._disabled_settings_defaults: From cce642a3e1ae2b4500fb5e4ea335254b2c636678 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 20 Apr 2016 12:58:17 +0200 Subject: [PATCH 126/236] Extrapolate settings translations It is now in one place. Or rather, two: There is another function to translate only setting names. Contributes to issue CURA-844. --- .../VersionUpgrade21to22/MachineInstance.py | 6 ++-- .../VersionUpgrade21to22/Profile.py | 12 +++---- .../VersionUpgrade21to22.py | 33 ++++++++++++++++++- 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py index 5e90e27f17..4b9d7b8be5 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -58,6 +58,7 @@ class MachineInstance: # \return A serialised form of this machine instance, serialised in # version 2 of the file format. def exportVersion2(self): + import VersionUpgrade21to22 #Import here to prevent circular dependencies. config = configparser.ConfigParser(interpolation = None) #Build a config file in the form of version 2. config.add_section("general") @@ -73,12 +74,9 @@ class MachineInstance: if self._active_material_name: config.set("general", "material", self._active_material_name) + VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._machine_setting_overrides) config.add_section("machine_settings") for key, value in self._machine_setting_overrides.items(): - if key == "speed_support_lines": #Setting key was changed for 2.2. - key = "speed_support_infill" - if key == "retraction_combing": #Combing was made into an enum instead of a boolean. - value = "off" if (value == "False") else "all" config.set("machine_settings", key, str(value)) output = io.StringIO() diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py index a70293f8b9..8a461dad6a 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py @@ -73,6 +73,7 @@ class Profile: # \return A serialised form of this profile, serialised in version 2 of # the file format. def exportVersion2(self): + import VersionUpgrade21to22 #Import here to prevent circular dependencies. config = configparser.ConfigParser(interpolation = None) config.add_section("general") @@ -92,24 +93,19 @@ class Profile: config.set("general", "material", self._material_name) if self._settings: + VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._settings) config.add_section("settings") for key, value in self._settings.items(): - if key == "speed_support_lines": #Setting key was changed for 2.2. - key = "speed_support_infill" - if key == "retraction_combing": #Combing was made into an enum instead of a boolean. - value = "off" if (value == "False") else "all" config.set("settings", key, str(value)) if self._changed_settings_defaults: + VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._changed_settings_defaults) config.add_section("defaults") for key, value in self._changed_settings_defaults.items(): - if key == "speed_support_lines": #Setting key was changed for 2.2. - key = "speed_support_infill" - if key == "retraction_combing": #Combing was made into an enum instead of a boolean. - value = "off" if (value == "False") else "all" config.set("defaults", key, str(value)) if self._disabled_settings_defaults: + VersionUpgrade21to22.VersionUpgrade21to22.translateSettingNames(self._disabled_settings_defaults) config.add_section("disabled_defaults") disabled_defaults_string = str(self._disabled_settings_defaults[0]) #Must be at least 1 item, otherwise we wouldn't enter this if statement. for item in self._disabled_settings_defaults[1:]: diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py index 385001a3e6..cc248d3d51 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -30,4 +30,35 @@ class VersionUpgrade21to22(VersionUpgrade): profile = Profile.importVersion1(serialised) if not profile: #Invalid file format. return None - return profile.exportVersion2() \ No newline at end of file + return profile.exportVersion2() + + ## Translates settings for the change from Cura 2.1 to 2.2. + # + # Each setting is changed in-place in the provided dictionary. This changes + # the input parameter. + # + # \param settings A dictionary of settings (as key-value pairs) to update. + # \return The same dictionary. + @staticmethod + def translateSettings(settings): + for key, value in settings.items(): + if key == "speed_support_lines": #Setting key was changed for 2.2. + del settings[key] + settings["speed_support_infill"] = value + if key == "retraction_combing": #Combing was made into an enum instead of a boolean. + settings[key] = "off" if (value == "False") else "all" + return settings + + ## Translates setting names for the change from Cura 2.1 to 2.2. + # + # The setting names are changed in-place in the provided list. This changes + # the input parameter. + # + # \param settings A list of setting names to update. + # \return The same list. + @staticmethod + def translateSettingNames(settings): + for i in range(0, len(settings)): + if settings[i] == "speed_support_lines": + settings[i] = "speed_support_infill" + return settings \ No newline at end of file From 41ee575cc1bfdc769bde94d9d81d8146498e46ce Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 28 Apr 2016 12:03:12 +0200 Subject: [PATCH 127/236] Correct copyright year These files were made in 2016. Contributes to issue CURA-844. --- plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py | 2 +- .../VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py | 2 +- plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py index 4b9d7b8be5..e4f067b5f3 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015 Ultimaker B.V. +# Copyright (c) 2016 Ultimaker B.V. # Cura is released under the terms of the AGPLv3 or higher. import UM.Settings.SettingsError #To indicate that a file is of incorrect format. diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py index cc248d3d51..17f0154168 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015 Ultimaker B.V. +# Copyright (c) 2016 Ultimaker B.V. # Cura is released under the terms of the AGPLv3 or higher. from UM.VersionUpgrade import VersionUpgrade #Superclass of the plugin. diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py b/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py index a8e976853f..36b6d6609a 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015 Ultimaker B.V. +# Copyright (c) 2016 Ultimaker B.V. # Cura is released under the terms of the AGPLv3 or higher. from . import VersionUpgrade21to22 From 0e92929be486666f25fc79e0deb0a8aa929cdf95 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 28 Apr 2016 12:07:51 +0200 Subject: [PATCH 128/236] Codestyle: Wrap doxygen documentation at 80 chars Contributes to issue CURA-844. --- .../VersionUpgrade21to22/VersionUpgrade21to22.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py index 17f0154168..0118a96aa8 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -34,8 +34,8 @@ class VersionUpgrade21to22(VersionUpgrade): ## Translates settings for the change from Cura 2.1 to 2.2. # - # Each setting is changed in-place in the provided dictionary. This changes - # the input parameter. + # Each setting is changed in-place in the provided dictionary. This + # changes the input parameter. # # \param settings A dictionary of settings (as key-value pairs) to update. # \return The same dictionary. From 7ad2fbc95d3ed440aac561557d833f3614944dcf Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 28 Apr 2016 12:15:43 +0200 Subject: [PATCH 129/236] Codestyle: Start comments with space We didn't really discuss this one, but apparently it's in PEP8 so I'd better do it. Contributes to issue CURA-844. --- .../VersionUpgrade21to22/MachineInstance.py | 12 ++++++------ .../VersionUpgrade/VersionUpgrade21to22/Profile.py | 14 +++++++------- .../VersionUpgrade21to22/VersionUpgrade21to22.py | 12 ++++++------ 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py index e4f067b5f3..10eb3a0607 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -26,9 +26,9 @@ class MachineInstance: # \param serialised A string with the contents of a machine instance file. def __init__(self, serialised): config = configparser.ConfigParser(interpolation = None) - config.read_string(serialised) #Read the input string as config file. + config.read_string(serialised) # Read the input string as config file. - #Checking file correctness. + # Checking file correctness. if not config.has_section("general"): raise SettingsError.InvalidFormatError("general") if not config.has_option("general", "version"): @@ -37,7 +37,7 @@ class MachineInstance: raise SettingsError.InvalidFormatError("general/name") if not config.has_option("general", "type"): raise SettingsError.InvalidFormatError("general/type") - if int(config.get("general", "version")) != 1: #Explicitly hard-code version 1, since if this number changes the programmer MUST change this entire function. + if int(config.get("general", "version")) != 1: # Explicitly hard-code version 1, since if this number changes the programmer MUST change this entire function. raise SettingsError.InvalidVersionError("Version upgrade intermediary version 1") self._type_name = config.get("general", "type") @@ -58,13 +58,13 @@ class MachineInstance: # \return A serialised form of this machine instance, serialised in # version 2 of the file format. def exportVersion2(self): - import VersionUpgrade21to22 #Import here to prevent circular dependencies. - config = configparser.ConfigParser(interpolation = None) #Build a config file in the form of version 2. + import VersionUpgrade21to22 # Import here to prevent circular dependencies. + config = configparser.ConfigParser(interpolation = None) # Build a config file in the form of version 2. config.add_section("general") config.set("general", "name", self._name) config.set("general", "type", self._type_name) - config.set("general", "version", "2") #Hard-code version 2, since if this number changes the programmer MUST change this entire function. + config.set("general", "version", "2") # Hard-code version 2, since if this number changes the programmer MUST change this entire function. if self._variant_name: config.set("general", "variant", self._variant_name) if self._key: diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py index 8a461dad6a..c2eed5d47f 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py @@ -27,13 +27,13 @@ class Profile: parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) - #Check correctness. + # Check correctness. if not parser.has_section("general"): raise SettingsError.InvalidFormatError("general") if not parser.has_option("general", "version") or int(parser.get("general", "version")) != 1: #Hard-coded profile version here. If this number changes the entire function needs to change. raise SettingsError.InvalidVersionError("Version upgrade intermediary version 1") - #Parse the general section. + # Parse the general section. self._name = parser.get("general", "name") self._type = parser.get("general", "type", fallback = None) if "weight" in parser["general"]: @@ -50,13 +50,13 @@ class Profile: else: self._material_name = None - #Parse the settings. + # Parse the settings. self._settings = {} if parser.has_section("settings"): for key, value in parser["settings"].items(): self._settings[key] = value - #Parse the defaults and the disabled defaults. + # Parse the defaults and the disabled defaults. self._changed_settings_defaults = {} if parser.has_section("defaults"): for key, value in parser["defaults"].items(): @@ -73,11 +73,11 @@ class Profile: # \return A serialised form of this profile, serialised in version 2 of # the file format. def exportVersion2(self): - import VersionUpgrade21to22 #Import here to prevent circular dependencies. + import VersionUpgrade21to22 # Import here to prevent circular dependencies. config = configparser.ConfigParser(interpolation = None) config.add_section("general") - config.set("general", "version", "2") #Hard-coded profile version 2 + config.set("general", "version", "2") # Hard-coded profile version 2 config.set("general", "name", self._name) if self._type: config.set("general", "type", self._type) @@ -107,7 +107,7 @@ class Profile: if self._disabled_settings_defaults: VersionUpgrade21to22.VersionUpgrade21to22.translateSettingNames(self._disabled_settings_defaults) config.add_section("disabled_defaults") - disabled_defaults_string = str(self._disabled_settings_defaults[0]) #Must be at least 1 item, otherwise we wouldn't enter this if statement. + disabled_defaults_string = str(self._disabled_settings_defaults[0]) # Must be at least 1 item, otherwise we wouldn't enter this if statement. for item in self._disabled_settings_defaults[1:]: disabled_defaults_string += "," + str(item) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py index 0118a96aa8..076bd7cc5e 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -1,10 +1,10 @@ # Copyright (c) 2016 Ultimaker B.V. # Cura is released under the terms of the AGPLv3 or higher. -from UM.VersionUpgrade import VersionUpgrade #Superclass of the plugin. +from UM.VersionUpgrade import VersionUpgrade # Superclass of the plugin. -from . import MachineInstance #To upgrade machine instances. -from . import Profile #To upgrade profiles. +from . import MachineInstance # To upgrade machine instances. +from . import Profile # To upgrade profiles. ## Converts configuration from Cura 2.1's file formats to Cura 2.2's. # @@ -28,7 +28,7 @@ class VersionUpgrade21to22(VersionUpgrade): # not of the correct format. def upgradeProfile(self, serialised): profile = Profile.importVersion1(serialised) - if not profile: #Invalid file format. + if not profile: # Invalid file format. return None return profile.exportVersion2() @@ -42,10 +42,10 @@ class VersionUpgrade21to22(VersionUpgrade): @staticmethod def translateSettings(settings): for key, value in settings.items(): - if key == "speed_support_lines": #Setting key was changed for 2.2. + if key == "speed_support_lines": # Setting key was changed for 2.2. del settings[key] settings["speed_support_infill"] = value - if key == "retraction_combing": #Combing was made into an enum instead of a boolean. + if key == "retraction_combing": # Combing was made into an enum instead of a boolean. settings[key] = "off" if (value == "False") else "all" return settings From fdf37c2ab01dbff71005373dfda9afdf53ca67e8 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 28 Apr 2016 12:21:32 +0200 Subject: [PATCH 130/236] Codestyle: Start comments with space Forgot this one. Contributes to issue CURA-844. --- plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py index c2eed5d47f..5c4afb864b 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py @@ -30,7 +30,7 @@ class Profile: # Check correctness. if not parser.has_section("general"): raise SettingsError.InvalidFormatError("general") - if not parser.has_option("general", "version") or int(parser.get("general", "version")) != 1: #Hard-coded profile version here. If this number changes the entire function needs to change. + if not parser.has_option("general", "version") or int(parser.get("general", "version")) != 1: # Hard-coded profile version here. If this number changes the entire function needs to change. raise SettingsError.InvalidVersionError("Version upgrade intermediary version 1") # Parse the general section. From f2f993916d72e19c207d24537aef459aacf1b943 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 28 Apr 2016 12:27:39 +0200 Subject: [PATCH 131/236] Rename import/export functions They no longer mention the version number in their function names. I'd rather have named them import/export but that gave a name clash with Python's 'import' keyword. Contributes to issue CURA-844. --- .../VersionUpgrade21to22/MachineInstance.py | 4 ++-- plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py | 4 ++-- .../VersionUpgrade21to22/VersionUpgrade21to22.py | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py index 10eb3a0607..06438d994d 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -12,7 +12,7 @@ import io #To write config files to strings as if they were files. # \param serialised The serialised form of a machine instance in version 1. # \return A machine instance instance, or None if the file format is # incorrect. -def importVersion1(serialised): +def importFrom(serialised): try: return MachineInstance(serialised) except (configparser.Error, SettingsError.InvalidFormatError, SettingsError.InvalidVersionError): @@ -57,7 +57,7 @@ class MachineInstance: # # \return A serialised form of this machine instance, serialised in # version 2 of the file format. - def exportVersion2(self): + def exportTo(self): import VersionUpgrade21to22 # Import here to prevent circular dependencies. config = configparser.ConfigParser(interpolation = None) # Build a config file in the form of version 2. diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py index 5c4afb864b..22b68bdbbf 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py @@ -11,7 +11,7 @@ import io #To write config files to strings as if they were files. # # \param serialised The serialised form of a profile in version 1. # \return A profile instance, or None if the file format is incorrect. -def importVersion1(serialised): +def importFrom(serialised): try: return Profile(serialised) except (configparser.Error, SettingsError.InvalidFormatError, SettingsError.InvalidVersionError): @@ -72,7 +72,7 @@ class Profile: # # \return A serialised form of this profile, serialised in version 2 of # the file format. - def exportVersion2(self): + def exportTo(self): import VersionUpgrade21to22 # Import here to prevent circular dependencies. config = configparser.ConfigParser(interpolation = None) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py index 076bd7cc5e..9312da690d 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -16,10 +16,10 @@ class VersionUpgrade21to22(VersionUpgrade): # \return The serialised machine instance in version 2, or None if the # input was not of the correct format. def upgradeMachineInstance(self, serialised): - machine_instance = MachineInstance.importVersion1(serialised) + machine_instance = MachineInstance.importFrom(serialised) if not machine_instance: #Invalid file format. return None - return machine_instance.exportVersion2() + return machine_instance.exportTo() ## Converts profiles from format version 1 to version 2. # @@ -27,10 +27,10 @@ class VersionUpgrade21to22(VersionUpgrade): # \return The serialised profile in version 2, or None if the input was # not of the correct format. def upgradeProfile(self, serialised): - profile = Profile.importVersion1(serialised) + profile = Profile.importFrom(serialised) if not profile: # Invalid file format. return None - return profile.exportVersion2() + return profile.exportTo() ## Translates settings for the change from Cura 2.1 to 2.2. # From f7ca48987703007896b6170790d03438369451dd Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 28 Apr 2016 12:29:14 +0200 Subject: [PATCH 132/236] Rename exportTo -> export Because exportTo doesn't make sense if there is no parameter to which we're exporting. Contributes to issue CURA-844. --- .../VersionUpgrade/VersionUpgrade21to22/MachineInstance.py | 2 +- plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py | 2 +- .../VersionUpgrade21to22/VersionUpgrade21to22.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py index 06438d994d..bf01c877ff 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -57,7 +57,7 @@ class MachineInstance: # # \return A serialised form of this machine instance, serialised in # version 2 of the file format. - def exportTo(self): + def export(self): import VersionUpgrade21to22 # Import here to prevent circular dependencies. config = configparser.ConfigParser(interpolation = None) # Build a config file in the form of version 2. diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py index 22b68bdbbf..52518f3502 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py @@ -72,7 +72,7 @@ class Profile: # # \return A serialised form of this profile, serialised in version 2 of # the file format. - def exportTo(self): + def export(self): import VersionUpgrade21to22 # Import here to prevent circular dependencies. config = configparser.ConfigParser(interpolation = None) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py index 9312da690d..303a6c0634 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -19,7 +19,7 @@ class VersionUpgrade21to22(VersionUpgrade): machine_instance = MachineInstance.importFrom(serialised) if not machine_instance: #Invalid file format. return None - return machine_instance.exportTo() + return machine_instance.export() ## Converts profiles from format version 1 to version 2. # @@ -30,7 +30,7 @@ class VersionUpgrade21to22(VersionUpgrade): profile = Profile.importFrom(serialised) if not profile: # Invalid file format. return None - return profile.exportTo() + return profile.export() ## Translates settings for the change from Cura 2.1 to 2.2. # From 28da3c1a4ff5b1bef294fea0281a1bd1ac8ae5a6 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 28 Apr 2016 13:10:18 +0200 Subject: [PATCH 133/236] Improve documentation for translate function It says now that the function updates the settings to what they should be in the new version. Contributes to issue CURA-844. --- .../VersionUpgrade21to22/VersionUpgrade21to22.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py index 303a6c0634..4a8a4652ad 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -32,10 +32,11 @@ class VersionUpgrade21to22(VersionUpgrade): return None return profile.export() - ## Translates settings for the change from Cura 2.1 to 2.2. + ## Updates settings for the change from Cura 2.1 to 2.2. # - # Each setting is changed in-place in the provided dictionary. This - # changes the input parameter. + # The keys and values of settings are changed to what they should be in + # the new version. Each setting is changed in-place in the provided + # dictionary. This changes the input parameter. # # \param settings A dictionary of settings (as key-value pairs) to update. # \return The same dictionary. From 6572d939bf841da1a63791f8cf21050bdbd71601 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 28 Apr 2016 14:09:40 +0200 Subject: [PATCH 134/236] Convert for+if to list comprehension It's a simple filter, so why not? Contributes to issue CURA-844. --- plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py index 52518f3502..d3c2ba3233 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py @@ -64,9 +64,7 @@ class Profile: self._disabled_settings_defaults = [] if parser.has_section("disabled_defaults"): disabled_defaults_string = parser.get("disabled_defaults", "values") - for item in disabled_defaults_string.split(","): - if item != "": - self._disabled_settings_defaults.append(item) + self._disabled_settings_defaults = [item for item in disabled_defaults_string.split(",") if item != ""] # Split by comma. ## Serialises this profile as file format version 2. # From 8a44705413d3a01e897d4a922e7c1383b60a2927 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 23 Jun 2016 10:52:42 +0200 Subject: [PATCH 135/236] Update metadata with dynamic config types After settings rework, we decided to make the upgrade plug-ins define their own configuration types. This is basically the definition for these configuration types. Only the get_version function is not yet implemented. Contributes to issue CURA-844. --- .../VersionUpgrade21to22/__init__.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py b/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py index 36b6d6609a..2607b92e6a 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py @@ -16,13 +16,23 @@ def getMetaData(): "api": 2 }, "version_upgrade": { + # From To Upgrade function + ("profile", 1): ("instance_container", 2, VersionUpgrade21to22.upgradeProfile), + ("machine_instance", 1): ("container_stack", 2, VersionUpgrade21to22.upgradeMachineInstance), + ("preferences", 1): ("preferences", 2, VersionUpgrade21to22.upgradePreferences) + }, + "sources": { "profile": { - "from": 1, - "to": 2 + "get_version": VersionUpgrade21to22.getCfgVersion, + "location": {"./profiles"} }, "machine_instance": { - "from": 1, - "to": 2 + "get_version": VersionUpgrade21to22.getCfgVersion, + "location": {"./machine_instances"} + }, + "preferences": { + "get_version": VersionUpgrade21to22.getCfgVersion, + "location": {"."} } } } From 5143d0b9f15b5d61ad081d852d347a4da7c5b8a4 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 23 Jun 2016 12:03:44 +0200 Subject: [PATCH 136/236] Implement getCfgVersion This code is basically moved from Uranium to here. This is needed to allow for upgrade plug-ins to define their own configuration types. Contributes to issue CURA-844. --- .../VersionUpgrade21to22/VersionUpgrade21to22.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py index 4a8a4652ad..77669f2b56 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -1,11 +1,25 @@ # Copyright (c) 2016 Ultimaker B.V. # Cura is released under the terms of the AGPLv3 or higher. +import configparser #To get version numbers from config files. + from UM.VersionUpgrade import VersionUpgrade # Superclass of the plugin. from . import MachineInstance # To upgrade machine instances. from . import Profile # To upgrade profiles. +## Gets the version number from a config file. +# +# In all config files that concern this version upgrade, the version +# number is stored in general/version, so get the data from that key. +# +# \param serialised The contents of a config file. +# \return \type{int} The version number of that config file. +def getCfgVersion(serialised): + parser = configparser.ConfigParser(interpolation = None) + parser.read_string(serialised) + return int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised. + ## Converts configuration from Cura 2.1's file formats to Cura 2.2's. # # It converts the machine instances and profiles. From 68afd08afd93b64ebdb18d9e60188b710449d80c Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 23 Jun 2016 12:42:27 +0200 Subject: [PATCH 137/236] Fix links to upgrade functions These are bound methods of an instance. Contributes to issue CURA-844. --- .../VersionUpgrade21to22/__init__.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py b/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py index 2607b92e6a..53d3814a94 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py @@ -6,6 +6,8 @@ from . import VersionUpgrade21to22 from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") +upgrade = VersionUpgrade21to22.VersionUpgrade21to22() + def getMetaData(): return { "plugin": { @@ -17,25 +19,25 @@ def getMetaData(): }, "version_upgrade": { # From To Upgrade function - ("profile", 1): ("instance_container", 2, VersionUpgrade21to22.upgradeProfile), - ("machine_instance", 1): ("container_stack", 2, VersionUpgrade21to22.upgradeMachineInstance), - ("preferences", 1): ("preferences", 2, VersionUpgrade21to22.upgradePreferences) + ("profile", 1): ("instance_container", 2, upgrade.upgradeProfile), + ("machine_instance", 1): ("container_stack", 2, upgrade.upgradeMachineInstance), + ("preferences", 1): ("preferences", 2, upgrade.upgradePreferences) }, "sources": { "profile": { - "get_version": VersionUpgrade21to22.getCfgVersion, + "get_version": upgrade.getCfgVersion, "location": {"./profiles"} }, "machine_instance": { - "get_version": VersionUpgrade21to22.getCfgVersion, + "get_version": upgrade.getCfgVersion, "location": {"./machine_instances"} }, "preferences": { - "get_version": VersionUpgrade21to22.getCfgVersion, + "get_version": upgrade.getCfgVersion, "location": {"."} } } } def register(app): - return { "version_upgrade": VersionUpgrade21to22.VersionUpgrade21to22() } + return { "version_upgrade": upgrade } From c1b738953b621b09664a4b77802bc6e5f6fdff94 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 23 Jun 2016 12:47:12 +0200 Subject: [PATCH 138/236] Align documentation better I just couldn't stand it... 'I have to sort my books', she cried With self-indulgent glee With senseless, narcissistic pride: 'I'm just so OCD' 'How random, guys', I smiled and said Then left without a peep And washed my hands until they bled And cried myself to sleep Contributes to issue CURA-844. --- plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py b/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py index 53d3814a94..9e683be321 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py @@ -18,7 +18,7 @@ def getMetaData(): "api": 2 }, "version_upgrade": { - # From To Upgrade function + # From To Upgrade function ("profile", 1): ("instance_container", 2, upgrade.upgradeProfile), ("machine_instance", 1): ("container_stack", 2, upgrade.upgradeMachineInstance), ("preferences", 1): ("preferences", 2, upgrade.upgradePreferences) From 66df680e1b3d641b3ede9e58cc2f30c8f19328cb Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 23 Jun 2016 12:53:53 +0200 Subject: [PATCH 139/236] Move getCfgVersion back inside class To ask the cfgversion from the actual plug-in is more object-oriented programming. Contributes to issue CURA-844. --- .../VersionUpgrade21to22.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py index 77669f2b56..b2404fb8f0 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -8,22 +8,22 @@ from UM.VersionUpgrade import VersionUpgrade # Superclass of the plugin. from . import MachineInstance # To upgrade machine instances. from . import Profile # To upgrade profiles. -## Gets the version number from a config file. -# -# In all config files that concern this version upgrade, the version -# number is stored in general/version, so get the data from that key. -# -# \param serialised The contents of a config file. -# \return \type{int} The version number of that config file. -def getCfgVersion(serialised): - parser = configparser.ConfigParser(interpolation = None) - parser.read_string(serialised) - return int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised. - ## Converts configuration from Cura 2.1's file formats to Cura 2.2's. # # It converts the machine instances and profiles. class VersionUpgrade21to22(VersionUpgrade): + ## Gets the version number from a config file. + # + # In all config files that concern this version upgrade, the version + # number is stored in general/version, so get the data from that key. + # + # \param serialised The contents of a config file. + # \return \type{int} The version number of that config file. + def getCfgVersion(self, serialised): + parser = configparser.ConfigParser(interpolation = None) + parser.read_string(serialised) + return int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised. + ## Converts machine instances from format version 1 to version 2. # # \param serialised The serialised machine instance in version 1. From 24946d3f13205a854bf9ced48f127701ff4d6622 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 23 Jun 2016 16:50:24 +0200 Subject: [PATCH 140/236] Fix references to exception classes These were moved to VersionUpgrade module. Contributes to issue CURA-844. --- .../VersionUpgrade21to22/MachineInstance.py | 16 ++++++++-------- .../VersionUpgrade21to22/Profile.py | 16 +++++++++------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py index bf01c877ff..ceb40fadd9 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -1,7 +1,7 @@ # Copyright (c) 2016 Ultimaker B.V. # Cura is released under the terms of the AGPLv3 or higher. -import UM.Settings.SettingsError #To indicate that a file is of incorrect format. +import UM.VersionUpgrade #To indicate that a file is of incorrect format. import configparser #To read config files. import io #To write config files to strings as if they were files. @@ -15,7 +15,7 @@ import io #To write config files to strings as if they were files. def importFrom(serialised): try: return MachineInstance(serialised) - except (configparser.Error, SettingsError.InvalidFormatError, SettingsError.InvalidVersionError): + except (configparser.Error, UM.VersionUpgrade.FormatException, UM.VersionUpgrade.InvalidVersionException): return None ## A representation of a machine instance used as intermediary form for @@ -30,15 +30,15 @@ class MachineInstance: # Checking file correctness. if not config.has_section("general"): - raise SettingsError.InvalidFormatError("general") + raise UM.VersionUpgrade.FormatException("No \"general\" section.") if not config.has_option("general", "version"): - raise SettingsError.InvalidFormatError("general/version") + raise UM.VersionUpgrade.FormatException("No \"version\" in \"general\" section.") if not config.has_option("general", "name"): - raise SettingsError.InvalidFormatError("general/name") + raise UM.VersionUpgrade.FormatException("No \"name\" in \"general\" section.") if not config.has_option("general", "type"): - raise SettingsError.InvalidFormatError("general/type") + raise UM.VersionUpgrade.FormatException("No \"type\" in \"general\" section.") if int(config.get("general", "version")) != 1: # Explicitly hard-code version 1, since if this number changes the programmer MUST change this entire function. - raise SettingsError.InvalidVersionError("Version upgrade intermediary version 1") + raise UM.VersionUpgrade.InvalidVersionException("The version of this machine instance is wrong. It must be 1.") self._type_name = config.get("general", "type") self._variant_name = config.get("general", "variant", fallback = None) @@ -58,7 +58,6 @@ class MachineInstance: # \return A serialised form of this machine instance, serialised in # version 2 of the file format. def export(self): - import VersionUpgrade21to22 # Import here to prevent circular dependencies. config = configparser.ConfigParser(interpolation = None) # Build a config file in the form of version 2. config.add_section("general") @@ -74,6 +73,7 @@ class MachineInstance: if self._active_material_name: config.set("general", "material", self._active_material_name) + import VersionUpgrade21to22 # Import here to prevent circular dependencies. VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._machine_setting_overrides) config.add_section("machine_settings") for key, value in self._machine_setting_overrides.items(): diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py index d3c2ba3233..15cc01c2ea 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py @@ -1,11 +1,11 @@ # Copyright (c) 2016 Ultimaker B.V. # Cura is released under the terms of the AGPLv3 or higher. -import UM.Settings.SettingsError #To indicate that a file is of incorrect format. - import configparser #To read config files. import io #To write config files to strings as if they were files. +import UM.VersionUpgrade + ## Creates a new profile instance by parsing a serialised profile in version 1 # of the file format. # @@ -14,7 +14,7 @@ import io #To write config files to strings as if they were files. def importFrom(serialised): try: return Profile(serialised) - except (configparser.Error, SettingsError.InvalidFormatError, SettingsError.InvalidVersionError): + except (configparser.Error, UM.VersionUpgrade.FormatException, UM.VersionUpgrade.InvalidVersionException): return None ## A representation of a profile used as intermediary form for conversion from @@ -29,9 +29,11 @@ class Profile: # Check correctness. if not parser.has_section("general"): - raise SettingsError.InvalidFormatError("general") - if not parser.has_option("general", "version") or int(parser.get("general", "version")) != 1: # Hard-coded profile version here. If this number changes the entire function needs to change. - raise SettingsError.InvalidVersionError("Version upgrade intermediary version 1") + raise UM.VersionUpgrade.FormatException("No \"general\" section.") + if not parser.has_option("general", "version"): + raise UM.VersionUpgrade.FormatException("No \"version\" in the \"general\" section.") + if int(parser.get("general", "version")) != 1: # Hard-coded profile version here. If this number changes the entire function needs to change. + raise UM.VersionUpgrade.InvalidVersionException("The version of this profile is wrong. It must be 1.") # Parse the general section. self._name = parser.get("general", "name") @@ -71,7 +73,6 @@ class Profile: # \return A serialised form of this profile, serialised in version 2 of # the file format. def export(self): - import VersionUpgrade21to22 # Import here to prevent circular dependencies. config = configparser.ConfigParser(interpolation = None) config.add_section("general") @@ -90,6 +91,7 @@ class Profile: if self._material_name and self._type != "material": config.set("general", "material", self._material_name) + import VersionUpgrade21to22 # Import here to prevent circular dependencies. if self._settings: VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._settings) config.add_section("settings") From 11d59709efc5136fa5ea48b7b6e019d779eddf88 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 23 Jun 2016 16:54:32 +0200 Subject: [PATCH 141/236] Add stub preferences converter Needs to be implemented like the rest of them. Contributes to issue CURA-844. --- .../VersionUpgrade21to22/VersionUpgrade21to22.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py index b2404fb8f0..adbde2ff0d 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -35,6 +35,14 @@ class VersionUpgrade21to22(VersionUpgrade): return None return machine_instance.export() + ## Converts preferences from format version 1 to version 2. + # + # \param serialised The serialised preferences file in version 1. + # \return The serialised preferences in version 2, or None if the input + # was not of the correct format. + def upgradePreferences(self, serialised): + return serialised #TODO + ## Converts profiles from format version 1 to version 2. # # \param serialised The serialised profile in version 1. From f04430ba5732234ec6a2a21230c041a127fd825c Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 23 Jun 2016 16:56:57 +0200 Subject: [PATCH 142/236] Increment plug-in API version It is now settings-rework-aware! Contributes to issue CURA-844. --- plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py b/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py index 9e683be321..98593785c6 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py @@ -15,7 +15,7 @@ def getMetaData(): "author": "Ultimaker", "version": "1.0", "description": catalog.i18nc("@info:whatsthis", "Upgrades configurations from Cura 2.1 to Cura 2.2."), - "api": 2 + "api": 3 }, "version_upgrade": { # From To Upgrade function From 04974a4308e45193c113dce7d3f9199def6d9446 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 24 Jun 2016 13:46:08 +0200 Subject: [PATCH 143/236] Correct preferences version number The old version was 2, so the new one needs to be 3. Contributes to issue CURA-844. --- plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py b/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py index 98593785c6..02207bbd50 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py @@ -21,7 +21,7 @@ def getMetaData(): # From To Upgrade function ("profile", 1): ("instance_container", 2, upgrade.upgradeProfile), ("machine_instance", 1): ("container_stack", 2, upgrade.upgradeMachineInstance), - ("preferences", 1): ("preferences", 2, upgrade.upgradePreferences) + ("preferences", 2): ("preferences", 3, upgrade.upgradePreferences) }, "sources": { "profile": { From e02a633ef268a58a0054c0f9ab1a03dacdb3919f Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 24 Jun 2016 15:49:31 +0200 Subject: [PATCH 144/236] Add preferences converter Currently it removes the expanded categories setting, and translates the setting names in the visibility. Contributes to issue CURA-844. --- .../VersionUpgrade21to22/Preferences.py | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py new file mode 100644 index 0000000000..0ec76eba53 --- /dev/null +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py @@ -0,0 +1,61 @@ +# Copyright (c) 2016 Ultimaker B.V. +# Cura is released under the terms of the AGPLv3 or higher. + +import configparser #To read config files. +import io #To output config files to string. + +import UM.VersionUpgrade #To indicate that a file is of the wrong format. + +## Creates a new preferences instance by parsing a serialised preferences file +# in version 1 of the file format. +# +# \param serialised The serialised form of a preferences file in version 1. +# \return A representation of those preferences, or None if the file format is +# incorrect. +def importFrom(serialised): + try: + return Preferences(serialised) + except (configparser.Error, UM.VersionUpgrade.FormatException, UM.VersionUpgrade.InvalidVersionException): + return None + +## A representation of preferences files as intermediary form for conversion +# from one format to the other. +class Preferences: + ## Reads version 2 of the preferences file format, storing it in memory. + # + # \param serialised A serialised version 2 preferences file. + def __init__(self, serialised): + self._config = configparser.ConfigParser(interpolation = None) + self._config.read_string(serialised) + + #Checking file correctness. + if not self._config.has_section("general"): + raise UM.VersionUpgrade.FormatException("No \"general\" section.") + if not self._config.has_option("general", "version"): + raise UM.VersionUpgrade.FormatException("No \"version\" in \"general\" section.") + if int(self._config.get("general", "version")) != 2: # Explicitly hard-code version 2, since if this number changes the programmer MUST change this entire function. + raise UM.VersionUpgrade.InvalidVersionException("The version of this preferences file is wrong. It must be 2.") + + ## Serialises these preferences as a preferences file of version 3. + # + # This is where the actual translation happens. + # + # \return A serialised version of a preferences file in version 3. + def export(self): + #Reset the cura/categories_expanded property since it works differently now. + if self._config.has_section("cura") and self._config.has_option("cura", "categories_expanded"): + self._config.remove_option("cura", "categories_expanded") + + #Translate the setting names in the visible settings. + if self._config.has_section("machines") and self._config.has_option("machines", "setting_visibility"): + visible_settings = self._config.get("machines", "setting_visibility") + visible_settings = visible_settings.split(",") + import VersionUpgrade21to22 #Import here to prevent a circular dependency. + visible_settings = VersionUpgrade21to22.translateSettingNames(visible_settings) + visible_settings = visible_settings.join(",") + self._config.set("machines", "setting_visibility", value = visible_settings) + + #Output the result as a string. + output = io.StringIO() + self._config.write(output) + return output.getvalue() \ No newline at end of file From ec5aee253d2ad1d78741e8ed796f491f583095bf Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 24 Jun 2016 16:04:02 +0200 Subject: [PATCH 145/236] Also increment the version number Of course. Duh. Contributes to issue CURA-844. --- plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py index 0ec76eba53..f4c5e89b92 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py @@ -55,6 +55,9 @@ class Preferences: visible_settings = visible_settings.join(",") self._config.set("machines", "setting_visibility", value = visible_settings) + #Update the version number itself. + self._config.set("general", "version", value = "3") + #Output the result as a string. output = io.StringIO() self._config.write(output) From 51aa82bd6c61ceb75c074b36d349e87fa2478282 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 24 Jun 2016 16:18:04 +0200 Subject: [PATCH 146/236] Use preferences upgrader Instead of the placeholder. Contributes to issue CURA-844. --- .../VersionUpgrade21to22/VersionUpgrade21to22.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py index adbde2ff0d..a06369f301 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -6,6 +6,7 @@ import configparser #To get version numbers from config files. from UM.VersionUpgrade import VersionUpgrade # Superclass of the plugin. from . import MachineInstance # To upgrade machine instances. +from . import Preferences #To upgrade preferences. from . import Profile # To upgrade profiles. ## Converts configuration from Cura 2.1's file formats to Cura 2.2's. @@ -35,13 +36,16 @@ class VersionUpgrade21to22(VersionUpgrade): return None return machine_instance.export() - ## Converts preferences from format version 1 to version 2. + ## Converts preferences from format version 2 to version 3. # - # \param serialised The serialised preferences file in version 1. - # \return The serialised preferences in version 2, or None if the input + # \param serialised The serialised preferences file in version 2. + # \return The serialised preferences in version 3, or None if the input # was not of the correct format. def upgradePreferences(self, serialised): - return serialised #TODO + preferences = Preferences.importFrom(serialised) + if not preferences: #Invalid file format. + return None + return preferences.export() ## Converts profiles from format version 1 to version 2. # From a5abfe29cdf78443e8c04f49ab061d1473947ebe Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 24 Jun 2016 16:19:06 +0200 Subject: [PATCH 147/236] Fix call to translateSettingNames It's two modules deeper! Bit of weird magic due to how our plug-in import system works. Contributes to issue CURA-844. --- plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py index f4c5e89b92..93c12116ef 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py @@ -51,7 +51,7 @@ class Preferences: visible_settings = self._config.get("machines", "setting_visibility") visible_settings = visible_settings.split(",") import VersionUpgrade21to22 #Import here to prevent a circular dependency. - visible_settings = VersionUpgrade21to22.translateSettingNames(visible_settings) + VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettingNames(visible_settings) visible_settings = visible_settings.join(",") self._config.set("machines", "setting_visibility", value = visible_settings) From 20d776c0d4d047b4beadc3e4a012ade99a46fd9a Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 24 Jun 2016 16:21:14 +0200 Subject: [PATCH 148/236] Fix joining strings on comma The call worked a bit differently. Contributes to issue CURA-844. --- plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py index 93c12116ef..87077b1fde 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py @@ -52,7 +52,7 @@ class Preferences: visible_settings = visible_settings.split(",") import VersionUpgrade21to22 #Import here to prevent a circular dependency. VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettingNames(visible_settings) - visible_settings = visible_settings.join(",") + visible_settings = ",".join(visible_settings) self._config.set("machines", "setting_visibility", value = visible_settings) #Update the version number itself. From 07b6507133a1c1ed142eb0c8718ee2264be9ab65 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 24 Jun 2016 16:52:04 +0200 Subject: [PATCH 149/236] Fix calls to translateSettings and translateSettingNames They are one module deeper, due to the way that plug-ins are imported by Uranium. Contributes to issue CURA-844. --- .../VersionUpgrade/VersionUpgrade21to22/MachineInstance.py | 2 +- plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py index ceb40fadd9..04bc8aefd5 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -74,7 +74,7 @@ class MachineInstance: config.set("general", "material", self._active_material_name) import VersionUpgrade21to22 # Import here to prevent circular dependencies. - VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._machine_setting_overrides) + VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._machine_setting_overrides) config.add_section("machine_settings") for key, value in self._machine_setting_overrides.items(): config.set("machine_settings", key, str(value)) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py index 15cc01c2ea..4d447e5587 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py @@ -93,19 +93,19 @@ class Profile: import VersionUpgrade21to22 # Import here to prevent circular dependencies. if self._settings: - VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._settings) + VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._settings) config.add_section("settings") for key, value in self._settings.items(): config.set("settings", key, str(value)) if self._changed_settings_defaults: - VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._changed_settings_defaults) + VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._changed_settings_defaults) config.add_section("defaults") for key, value in self._changed_settings_defaults.items(): config.set("defaults", key, str(value)) if self._disabled_settings_defaults: - VersionUpgrade21to22.VersionUpgrade21to22.translateSettingNames(self._disabled_settings_defaults) + VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettingNames(self._disabled_settings_defaults) config.add_section("disabled_defaults") disabled_defaults_string = str(self._disabled_settings_defaults[0]) # Must be at least 1 item, otherwise we wouldn't enter this if statement. for item in self._disabled_settings_defaults[1:]: From 922b0df60bdb68e7871c90dd79df3d9108ea95a6 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 30 Jun 2016 14:21:07 +0200 Subject: [PATCH 150/236] Don't read machine instances as preferences Machine instances have the exact same file structure as preferences, except that machine instances require a name field (was already correctly implemented), but preferences didn't require it. This now forbids preferences to have a name field, so that the distinction between the two can be made. Contributes to issue CURA-844. --- plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py index 87077b1fde..961f4f963e 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py @@ -35,6 +35,8 @@ class Preferences: raise UM.VersionUpgrade.FormatException("No \"version\" in \"general\" section.") if int(self._config.get("general", "version")) != 2: # Explicitly hard-code version 2, since if this number changes the programmer MUST change this entire function. raise UM.VersionUpgrade.InvalidVersionException("The version of this preferences file is wrong. It must be 2.") + if self._config.has_option("general", "name"): #This is probably a machine instance. + raise UM.VersionUpgrade.FormatException("There is a \"name\" field in this configuration file. I suspect it is not a preferences file.") ## Serialises these preferences as a preferences file of version 3. # From e1db3e5316ffac5a97d99586a5c08e0dee57335d Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 1 Jul 2016 14:01:06 +0200 Subject: [PATCH 151/236] Convert instance profiles as profiles Treated in the same way. Contributes to issue CURA-844. --- plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py b/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py index 02207bbd50..50b6dcd48e 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py @@ -26,7 +26,7 @@ def getMetaData(): "sources": { "profile": { "get_version": upgrade.getCfgVersion, - "location": {"./profiles"} + "location": {"./profiles", "./instance_profiles"} }, "machine_instance": { "get_version": upgrade.getCfgVersion, From d1188899a7d4de87ad7bede251a22194be97d44e Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 1 Jul 2016 14:38:51 +0200 Subject: [PATCH 152/236] Translate active machine setting It was in machines/active_instance. Now it's in cura/active_machine. The setting value remains the same. Contributes to issue CURA-844. --- plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py index 961f4f963e..18391880e0 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py @@ -57,6 +57,12 @@ class Preferences: visible_settings = ",".join(visible_settings) self._config.set("machines", "setting_visibility", value = visible_settings) + #Translate the active_instance key. + if self._config.has_section("machines") and self._config.has_option("machines", "active_instance"): + active_machine = self._config.get("machines", "active_instance") + self._config.remove_option("machines", "active_instance") + self._config.set("cura", "active_machine", active_machine) + #Update the version number itself. self._config.set("general", "version", value = "3") From 2de811accfd94cb55d3b1da8eb85cf7392161f09 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 1 Jul 2016 15:07:20 +0200 Subject: [PATCH 153/236] Update how machine instances are translated to stacks Lots of things have changed in how this works. Sadly, I can't translate things like the material, from PLA to ultimaker2_plus_0.4mm_pla. Not without implementing the entire container registry in the plug-in anyway. Contributes to issue CURA-844. --- .../VersionUpgrade21to22/MachineInstance.py | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py index 04bc8aefd5..84b38b9955 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -62,22 +62,26 @@ class MachineInstance: config.add_section("general") config.set("general", "name", self._name) + config.set("general", "id", self._name) config.set("general", "type", self._type_name) config.set("general", "version", "2") # Hard-code version 2, since if this number changes the programmer MUST change this entire function. - if self._variant_name: - config.set("general", "variant", self._variant_name) - if self._key: - config.set("general", "key", self._key) - if self._active_profile_name: - config.set("general", "active_profile", self._active_profile_name) - if self._active_material_name: - config.set("general", "material", self._active_material_name) + + containers = [] + containers.append(self._name + "_current_settings") + containers.append("empty") #The dependencies of the active profile, material and variant changed, so there is no 1:1 relation possible here. + containers.append("empty") + containers.append("empty") + containers.append(self._type_name) + config.set("general", "containers", ",".join(containers)) + + config.add_section("metadata") + config.set("metadata", "type", "machine") import VersionUpgrade21to22 # Import here to prevent circular dependencies. VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._machine_setting_overrides) - config.add_section("machine_settings") + config.add_section("values") for key, value in self._machine_setting_overrides.items(): - config.set("machine_settings", key, str(value)) + config.set("values", key, str(value)) output = io.StringIO() config.write(output) From fbffff4c8dd2a01110ab478180bb939371527b9b Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 1 Jul 2016 15:28:35 +0200 Subject: [PATCH 154/236] Write list creation as literal Contributes to issue CURA-844. --- .../VersionUpgrade21to22/MachineInstance.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py index 84b38b9955..b64e87f5b4 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -66,12 +66,13 @@ class MachineInstance: config.set("general", "type", self._type_name) config.set("general", "version", "2") # Hard-code version 2, since if this number changes the programmer MUST change this entire function. - containers = [] - containers.append(self._name + "_current_settings") - containers.append("empty") #The dependencies of the active profile, material and variant changed, so there is no 1:1 relation possible here. - containers.append("empty") - containers.append("empty") - containers.append(self._type_name) + containers = [ + self._name + "_current_settings", + "empty", #The dependencies of the active profile, material and variant changed, so there is no 1:1 relation possible here. + "empty", + "empty", + self._type_name + ] config.set("general", "containers", ",".join(containers)) config.add_section("metadata") From 28cc1e8cf7eebc1d4d8be6e406a564d83f272d4e Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 1 Jul 2016 15:37:12 +0200 Subject: [PATCH 155/236] Translate material, variant and profile along They might not exist any more because the relation of profiles to printers changed from 2.1 to 2.2 for some machines (notably the UM2+ variants). But then it'll just make it empty when loading. Contributes to issue CURA-844. --- .../VersionUpgrade/VersionUpgrade21to22/MachineInstance.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py index b64e87f5b4..da58737d60 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -68,9 +68,9 @@ class MachineInstance: containers = [ self._name + "_current_settings", - "empty", #The dependencies of the active profile, material and variant changed, so there is no 1:1 relation possible here. - "empty", - "empty", + self._active_profile_name, + self._active_material_name, + self._variant_name, self._type_name ] config.set("general", "containers", ",".join(containers)) From 002f43598b8706e568b242e834c8127b46eb6e9e Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 1 Jul 2016 16:48:18 +0200 Subject: [PATCH 156/236] Correct storing upgraded config location Two big things had to happen for this: The resource types of quality and machine stack have to be defined before the initialisation of the version upgrade manager, since the version upgrade manager needs to know those storage locations at initialisation. But the storage location is still prefaced with '.config/UM' at this point, so instead we pass on the resource type (but the resource type still has to be defined before the init). The other big change is that we now have to name the configuration type 'quality' and 'machine_stack' instead of 'instance_container' and 'container_stack' to coincide with the resource type. This allows us to be more consistent in later plug-ins when we also have to upgrade other instance container types. Contributes to issue CURA-844. --- cura/CuraApplication.py | 44 ++++++++++++------- .../VersionUpgrade21to22/__init__.py | 8 ++-- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 72d19d65a1..88ced6f68e 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -19,7 +19,7 @@ from UM.JobQueue import JobQueue from UM.SaveFile import SaveFile from UM.Scene.Selection import Selection from UM.Scene.GroupDecorator import GroupDecorator -import UM.Settings.Validator +from UM.Settings.Validator import Validator from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation @@ -99,7 +99,32 @@ class CuraApplication(QtApplication): SettingDefinition.addSupportedProperty("settable_per_extruder", DefinitionPropertyType.Any, default = True) SettingDefinition.addSupportedProperty("settable_per_meshgroup", DefinitionPropertyType.Any, default = True) SettingDefinition.addSupportedProperty("settable_globally", DefinitionPropertyType.Any, default = True) - SettingDefinition.addSettingType("extruder", int, str, UM.Settings.Validator) + SettingDefinition.addSettingType("extruder", int, str, Validator) + + ## Add the 4 types of profiles to storage. + Resources.addStorageType(self.ResourceTypes.QualityInstanceContainer, "quality") + Resources.addStorageType(self.ResourceTypes.VariantInstanceContainer, "variants") + Resources.addStorageType(self.ResourceTypes.MaterialInstanceContainer, "materials") + Resources.addStorageType(self.ResourceTypes.UserInstanceContainer, "user") + Resources.addStorageType(self.ResourceTypes.ExtruderStack, "extruders") + Resources.addStorageType(self.ResourceTypes.MachineStack, "machine_instances") + + ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer) + ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.VariantInstanceContainer) + ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MaterialInstanceContainer) + ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.UserInstanceContainer) + ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.ExtruderStack) + ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MachineStack) + + ## Initialise the version upgrade manager with Cura's storage paths. + import UM.VersionUpgradeManager #Needs to be here to prevent circular dependencies. + self._version_upgrade_manager = UM.VersionUpgradeManager.VersionUpgradeManager( + { + ("quality", UM.Settings.InstanceContainer.Version): (self.ResourceTypes.QualityInstanceContainer, "application/x-uranium-instancecontainer"), + ("machine_stack", UM.Settings.ContainerStack.Version): (self.ResourceTypes.MachineStack, "application/x-uranium-containerstack"), + ("preferences", UM.Preferences.Version): (Resources.Preferences, "application/x-uranium-preferences") + } + ) self._machine_action_manager = MachineActionManager.MachineActionManager() @@ -142,21 +167,6 @@ class CuraApplication(QtApplication): self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Loading machines...")) - ## Add the 4 types of profiles to storage. - Resources.addStorageType(self.ResourceTypes.QualityInstanceContainer, "quality") - Resources.addStorageType(self.ResourceTypes.VariantInstanceContainer, "variants") - Resources.addStorageType(self.ResourceTypes.MaterialInstanceContainer, "materials") - Resources.addStorageType(self.ResourceTypes.UserInstanceContainer, "user") - Resources.addStorageType(self.ResourceTypes.ExtruderStack, "extruders") - Resources.addStorageType(self.ResourceTypes.MachineStack, "machine_instances") - - ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer) - ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.VariantInstanceContainer) - ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MaterialInstanceContainer) - ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.UserInstanceContainer) - ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.ExtruderStack) - ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.MachineStack) - # Add empty variant, material and quality containers. # Since they are empty, they should never be serialized and instead just programmatically created. # We need them to simplify the switching between materials. diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py b/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py index 50b6dcd48e..86cfda6b90 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py @@ -18,10 +18,10 @@ def getMetaData(): "api": 3 }, "version_upgrade": { - # From To Upgrade function - ("profile", 1): ("instance_container", 2, upgrade.upgradeProfile), - ("machine_instance", 1): ("container_stack", 2, upgrade.upgradeMachineInstance), - ("preferences", 2): ("preferences", 3, upgrade.upgradePreferences) + # From To Upgrade function + ("profile", 1): ("quality", 2, upgrade.upgradeProfile), + ("machine_instance", 1): ("machine_stack", 2, upgrade.upgradeMachineInstance), + ("preferences", 2): ("preferences", 3, upgrade.upgradePreferences) }, "sources": { "profile": { From 7f5b656c68000c31985d8783650d60b2aa3b7a11 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 1 Jul 2016 17:17:57 +0200 Subject: [PATCH 157/236] Update translation of metadata A few fields are different now. Contributes to issue CURA-844. --- plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py index 4d447e5587..4383b50f4e 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py @@ -83,11 +83,11 @@ class Profile: if self._weight: config.set("general", "weight", self._weight) if self._machine_type_id: - config.set("general", "machine_type", self._machine_type_id) + config.set("general", "definition", self._machine_type_id) + else: + config.set("general", "definition", "fdmprinter") if self._machine_variant_name: - config.set("general", "machine_variant", self._machine_variant_name) - if self._machine_instance_name: - config.set("general", "machine_instance", self._machine_instance_name) + config.set("general", "variant", self._machine_variant_name) if self._material_name and self._type != "material": config.set("general", "material", self._material_name) From a87e756a42998ad378c249860623af16e6e7078d Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 1 Jul 2016 17:25:33 +0200 Subject: [PATCH 158/236] Translate machine names Some names might be changed. I know of at least one: ultimaker2plus -> ultimaker2_plus. Contributes to issue CURA-844. --- plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py | 3 ++- .../VersionUpgrade21to22/VersionUpgrade21to22.py | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py index 4383b50f4e..d5f38585c9 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py @@ -83,7 +83,8 @@ class Profile: if self._weight: config.set("general", "weight", self._weight) if self._machine_type_id: - config.set("general", "definition", self._machine_type_id) + translated_machine = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translatePrinters([self._machine_type_id])[0] + config.set("general", "definition", translated_machine) else: config.set("general", "definition", "fdmprinter") if self._machine_variant_name: diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py index a06369f301..8a166ba160 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -58,6 +58,12 @@ class VersionUpgrade21to22(VersionUpgrade): return None return profile.export() + @staticmethod + def translatePrinters(printers): + for index, printer in enumerate(printers): + if printer == "ultimaker2plus": + printers[index] = "ultimaker2_plus" + ## Updates settings for the change from Cura 2.1 to 2.2. # # The keys and values of settings are changed to what they should be in From 39212a601e408df59750bea859c7cef6b4a40e69 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 1 Jul 2016 17:35:52 +0200 Subject: [PATCH 159/236] Fix import Didn't see this due to the sea of errors that it gives. Contributes to issue CURA-844. --- plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py index d5f38585c9..ce6bac4c40 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py @@ -73,6 +73,8 @@ class Profile: # \return A serialised form of this profile, serialised in version 2 of # the file format. def export(self): + import VersionUpgrade21to22 # Import here to prevent circular dependencies. + config = configparser.ConfigParser(interpolation = None) config.add_section("general") @@ -92,7 +94,6 @@ class Profile: if self._material_name and self._type != "material": config.set("general", "material", self._material_name) - import VersionUpgrade21to22 # Import here to prevent circular dependencies. if self._settings: VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._settings) config.add_section("settings") From 86544d41726359ae8d8166f6cd1a4ea06d8fc471 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 1 Jul 2016 19:02:35 +0200 Subject: [PATCH 160/236] Fix translate function It was expected to return a list of translated names, even though it actually translates in-place. Contributes to issue CURA-844. --- .../VersionUpgrade21to22/VersionUpgrade21to22.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py index 8a166ba160..f54609256d 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -58,11 +58,16 @@ class VersionUpgrade21to22(VersionUpgrade): return None return profile.export() + ## Translates printer names that have changed since the last version. + # + # \param printers A list of printer names in the old version. + # \return The same list, but with printer names translated. @staticmethod def translatePrinters(printers): for index, printer in enumerate(printers): if printer == "ultimaker2plus": printers[index] = "ultimaker2_plus" + return printers ## Updates settings for the change from Cura 2.1 to 2.2. # From 7939a03114f006b2fb439b2b42a4a08bb0cf1076 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 1 Jul 2016 19:05:19 +0200 Subject: [PATCH 161/236] Fix translating current_settings It is not entirely accurate in the translated version, since the new current_settings is not machine-dependent any more. However, without information on the original file name, this is as good as it gets, since that instance profile there only mentions the machine it is dependent on in the file name. Contributes to issue CURA-844. --- plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py index da58737d60..894e9a8c7a 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -67,7 +67,7 @@ class MachineInstance: config.set("general", "version", "2") # Hard-code version 2, since if this number changes the programmer MUST change this entire function. containers = [ - self._name + "_current_settings", + self._name, self._active_profile_name, self._active_material_name, self._variant_name, From f07598a2287fc809a03bbd0878a58141fa1ffeff Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Sun, 3 Jul 2016 23:21:43 +0200 Subject: [PATCH 162/236] Translate profile names too Not all profile name translations are entered yet, I think. I just did the material ones. Contributes to issue CURA-844. --- .../VersionUpgrade21to22/MachineInstance.py | 8 +++++--- .../VersionUpgrade21to22.py | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py index 894e9a8c7a..7582befca6 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -66,10 +66,13 @@ class MachineInstance: config.set("general", "type", self._type_name) config.set("general", "version", "2") # Hard-code version 2, since if this number changes the programmer MUST change this entire function. + import VersionUpgrade21to22 # Import here to prevent circular dependencies. + active_profile = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateProfile(self._active_profile_name) + active_material = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateProfile(self._active_material_name) containers = [ self._name, - self._active_profile_name, - self._active_material_name, + active_profile, + active_material, self._variant_name, self._type_name ] @@ -78,7 +81,6 @@ class MachineInstance: config.add_section("metadata") config.set("metadata", "type", "machine") - import VersionUpgrade21to22 # Import here to prevent circular dependencies. VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._machine_setting_overrides) config.add_section("values") for key, value in self._machine_setting_overrides.items(): diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py index f54609256d..641931c5a4 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -9,6 +9,13 @@ from . import MachineInstance # To upgrade machine instances. from . import Preferences #To upgrade preferences. from . import Profile # To upgrade profiles. +## How to translate profile names from the old version to the new. +_profile_translation = { + "PLA": "generic_pla", + "ABS": "generic_abs", + "CPE": "generic_cpe" +} + ## Converts configuration from Cura 2.1's file formats to Cura 2.2's. # # It converts the machine instances and profiles. @@ -69,6 +76,17 @@ class VersionUpgrade21to22(VersionUpgrade): printers[index] = "ultimaker2_plus" return printers + ## Translates a built-in profile name that might have changed since the + # last version. + # + # \param profile A profile name in the old version. + # \return The corresponding profile name in the new version. + @staticmethod + def translateProfile(profile): + if profile in _profile_translation: + return _profile_translation[profile] + return profile + ## Updates settings for the change from Cura 2.1 to 2.2. # # The keys and values of settings are changed to what they should be in From 88b36ad3d7b837dc63590831cddecccd7cf07b21 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Sun, 3 Jul 2016 23:31:10 +0200 Subject: [PATCH 163/236] Make translateSettingName use dictionary look-up This solution is a bit neater in code. It makes the function perform a single purpose, since it no longer translates a list of setting names but just one. Also it now neatly puts the translations in a separate, easy-to-modify dict. Only disadvantage is when simple key look-up is not sufficient, such as when renaming lots of settings at once, where substring matching would make the code a bit shorter. But we shouldn't do such a rename anyway. Contributes to issue CURA-844. --- .../VersionUpgrade21to22/Preferences.py | 3 ++- .../VersionUpgrade21to22.py | 24 +++++++++---------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py index 18391880e0..37cb3ccb9f 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py @@ -53,7 +53,8 @@ class Preferences: visible_settings = self._config.get("machines", "setting_visibility") visible_settings = visible_settings.split(",") import VersionUpgrade21to22 #Import here to prevent a circular dependency. - VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettingNames(visible_settings) + visible_settings = [VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettingName(setting_name) + for setting_name in visible_settings] visible_settings = ",".join(visible_settings) self._config.set("machines", "setting_visibility", value = visible_settings) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py index 641931c5a4..e1632e0d85 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -16,6 +16,10 @@ _profile_translation = { "CPE": "generic_cpe" } +_setting_name_translation = { + "speed_support_lines": "speed_support_infill" +} + ## Converts configuration from Cura 2.1's file formats to Cura 2.2's. # # It converts the machine instances and profiles. @@ -85,7 +89,7 @@ class VersionUpgrade21to22(VersionUpgrade): def translateProfile(profile): if profile in _profile_translation: return _profile_translation[profile] - return profile + return profile #Doesn't need to be translated. ## Updates settings for the change from Cura 2.1 to 2.2. # @@ -105,16 +109,12 @@ class VersionUpgrade21to22(VersionUpgrade): settings[key] = "off" if (value == "False") else "all" return settings - ## Translates setting names for the change from Cura 2.1 to 2.2. + ## Translates a setting name for the change from Cura 2.1 to 2.2. # - # The setting names are changed in-place in the provided list. This changes - # the input parameter. - # - # \param settings A list of setting names to update. - # \return The same list. + # \param setting The name of a setting in Cura 2.1. + # \return The name of the corresponding setting in Cura 2.2. @staticmethod - def translateSettingNames(settings): - for i in range(0, len(settings)): - if settings[i] == "speed_support_lines": - settings[i] = "speed_support_infill" - return settings \ No newline at end of file + def translateSettingName(setting): + if setting in _setting_name_translation: + return _setting_name_translation[setting] + return setting #Doesn't need to be translated. \ No newline at end of file From b5efb2eee846616d2d822c7325601b5ca83f8431 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Sun, 3 Jul 2016 23:37:11 +0200 Subject: [PATCH 164/236] Make profile translation use new translateSettingName Forgot about this one. Contributes to issue CURA-844. --- plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py index ce6bac4c40..3f49a3862d 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py @@ -107,10 +107,11 @@ class Profile: config.set("defaults", key, str(value)) if self._disabled_settings_defaults: - VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettingNames(self._disabled_settings_defaults) + disabled_settings_defaults = [VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettingName(setting) + for setting in self._disabled_settings_defaults] config.add_section("disabled_defaults") - disabled_defaults_string = str(self._disabled_settings_defaults[0]) # Must be at least 1 item, otherwise we wouldn't enter this if statement. - for item in self._disabled_settings_defaults[1:]: + disabled_defaults_string = str(disabled_settings_defaults[0]) # Must be at least 1 item, otherwise we wouldn't enter this if statement. + for item in disabled_settings_defaults[1:]: disabled_defaults_string += "," + str(item) output = io.StringIO() From f13db7de108b53320bbde9382a4d95e0c2311a1e Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Sun, 3 Jul 2016 23:58:38 +0200 Subject: [PATCH 165/236] Make translatePrinter use dict A translation dictionary makes it much easier to edit the translations. Also this now just translates one printer, instead of a list. Contributes to issue CURA-844. --- .../VersionUpgrade21to22/Profile.py | 2 +- .../VersionUpgrade21to22.py | 22 ++++++++++++------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py index 3f49a3862d..f194fe8101 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py @@ -85,7 +85,7 @@ class Profile: if self._weight: config.set("general", "weight", self._weight) if self._machine_type_id: - translated_machine = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translatePrinters([self._machine_type_id])[0] + translated_machine = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translatePrinter(self._machine_type_id) config.set("general", "definition", translated_machine) else: config.set("general", "definition", "fdmprinter") diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py index e1632e0d85..71e5f9efbe 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -9,6 +9,11 @@ from . import MachineInstance # To upgrade machine instances. from . import Preferences #To upgrade preferences. from . import Profile # To upgrade profiles. +## How to translate printer names from the old version to the new. +_printer_translation = { + "ultimaker2plus": "ultimaker2_plus" +} + ## How to translate profile names from the old version to the new. _profile_translation = { "PLA": "generic_pla", @@ -16,6 +21,7 @@ _profile_translation = { "CPE": "generic_cpe" } +## How to translate setting names from the old version to the new. _setting_name_translation = { "speed_support_lines": "speed_support_infill" } @@ -69,16 +75,16 @@ class VersionUpgrade21to22(VersionUpgrade): return None return profile.export() - ## Translates printer names that have changed since the last version. + ## Translates a printer name that might have changed since the last + # version. # - # \param printers A list of printer names in the old version. - # \return The same list, but with printer names translated. + # \param printer A printer name in Cura 2.1. + # \return The name of the corresponding printer in Cura 2.2. @staticmethod - def translatePrinters(printers): - for index, printer in enumerate(printers): - if printer == "ultimaker2plus": - printers[index] = "ultimaker2_plus" - return printers + def translatePrinter(printer): + if printer in _printer_translation: + return _printer_translation[printer] + return printer #Doesn't need to be translated. ## Translates a built-in profile name that might have changed since the # last version. From 439629d0b51f1cedea337cb4058d99df8d2cbf8e Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 4 Jul 2016 00:11:51 +0200 Subject: [PATCH 166/236] Translate variants and machine names I'm not quite pleased with the variant translation being inside this function, so I'll move it to VersionUpgrade21to22 soon I think. Contributes to issue CURA-844. --- .../VersionUpgrade21to22/MachineInstance.py | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py index 7582befca6..63e853d649 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -67,14 +67,40 @@ class MachineInstance: config.set("general", "version", "2") # Hard-code version 2, since if this number changes the programmer MUST change this entire function. import VersionUpgrade21to22 # Import here to prevent circular dependencies. + type_name = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translatePrinter(self._type_name) active_profile = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateProfile(self._active_profile_name) active_material = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateProfile(self._active_material_name) + if type_name == "ultimaker2_plus": + if self._variant_name == "0.25 mm": + variant = "ultimaker2_plus_0.25" + elif self._variant_name == "0.4 mm": + variant = "ultimaker2_plus_0.4" + elif self._variant_name == "0.6 mm": + variant = "ultimaker2_plus_0.6" + elif self._variant_name == "0.8 mm": + variant = "ultimaker2_plus_0.8" + else: + variant = self._variant_name + elif type_name == "ultimaker2_extended_plus": + if self._variant_name == "0.25 mm": + variant = "ultimaker2_extended_plus_0.25" + elif self._variant_name == "0.4 mm": + variant = "ultimaker2_extended_plus_0.4" + elif self._variant_name == "0.6 mm": + variant = "ultimaker2_extended_plus_0.6" + elif self._variant_name == "0.8 mm": + variant = "ultimaker2_extended_plus_0.8" + else: + variant = self._variant_name + else: + variant = self._variant_name + containers = [ self._name, active_profile, active_material, - self._variant_name, - self._type_name + variant, + type_name ] config.set("general", "containers", ",".join(containers)) From 93041191c25dcf9af9c29b3869080fcdac3df58d Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 4 Jul 2016 00:18:52 +0200 Subject: [PATCH 167/236] Move translateVariant to VersionUpgrade21to22 Also make it a dictionary look-up, like the rest, instead of a series of if-statements. Contributes to issue CURA-844. --- .../VersionUpgrade21to22/MachineInstance.py | 25 +-------------- .../VersionUpgrade21to22.py | 31 ++++++++++++++++++- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py index 63e853d649..98545969cd 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -70,30 +70,7 @@ class MachineInstance: type_name = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translatePrinter(self._type_name) active_profile = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateProfile(self._active_profile_name) active_material = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateProfile(self._active_material_name) - if type_name == "ultimaker2_plus": - if self._variant_name == "0.25 mm": - variant = "ultimaker2_plus_0.25" - elif self._variant_name == "0.4 mm": - variant = "ultimaker2_plus_0.4" - elif self._variant_name == "0.6 mm": - variant = "ultimaker2_plus_0.6" - elif self._variant_name == "0.8 mm": - variant = "ultimaker2_plus_0.8" - else: - variant = self._variant_name - elif type_name == "ultimaker2_extended_plus": - if self._variant_name == "0.25 mm": - variant = "ultimaker2_extended_plus_0.25" - elif self._variant_name == "0.4 mm": - variant = "ultimaker2_extended_plus_0.4" - elif self._variant_name == "0.6 mm": - variant = "ultimaker2_extended_plus_0.6" - elif self._variant_name == "0.8 mm": - variant = "ultimaker2_extended_plus_0.8" - else: - variant = self._variant_name - else: - variant = self._variant_name + variant = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateVariant(self._variant_name, type_name) containers = [ self._name, diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py index 71e5f9efbe..a1761a7498 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -26,6 +26,23 @@ _setting_name_translation = { "speed_support_lines": "speed_support_infill" } +## How to translate variants of specific machines from the old version to the +# new. +_variant_translation = { + "ultimaker2_plus": { + "0.25 mm": "ultimaker2_plus_0.25", + "0.4 mm": "ultimaker2_plus_0.4", + "0.6 mm": "ultimaker2_plus_0.6", + "0.8 mm": "ultimaker2_plus_0.8" + }, + "ultimaker2_extended_plus": { + "0.25 mm": "ultimaker2_extended_plus_0.25", + "0.4 mm": "ultimaker2_extended_plus_0.4", + "0.6 mm": "ultimaker2_extended_plus_0.6", + "0.8 mm": "ultimaker2_extended_plus_0.8" + } +} + ## Converts configuration from Cura 2.1's file formats to Cura 2.2's. # # It converts the machine instances and profiles. @@ -123,4 +140,16 @@ class VersionUpgrade21to22(VersionUpgrade): def translateSettingName(setting): if setting in _setting_name_translation: return _setting_name_translation[setting] - return setting #Doesn't need to be translated. \ No newline at end of file + return setting #Doesn't need to be translated. + + ## Translates a variant name for the change from Cura 2.1 to 2.2 + # + # \param variant The name of a variant in Cura 2.1. + # \param machine The name of the machine this variant is part of in Cura + # 2.2's naming. + # \return The name of the corresponding variant in Cura 2.2. + @staticmethod + def translateVariant(variant, machine): + if machine in _variant_translation and variant in _variant_translation[machine]: + return _variant_translation[machine][variant] + return variant \ No newline at end of file From c50619e3631dc9878d83edd2f26ce28498895ab4 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 4 Jul 2016 01:08:24 +0200 Subject: [PATCH 168/236] Add additional settings transformations since 2.1 These should be all the settings that were changed since Cura 2.1. Contributes to issue CURA-844. --- .../VersionUpgrade21to22.py | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py index a1761a7498..4f7d43b5c6 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -23,6 +23,10 @@ _profile_translation = { ## How to translate setting names from the old version to the new. _setting_name_translation = { + "remove_overlapping_walls_0_enabled": "travel_compensate_overlapping_walls_0_enabled", + "remove_overlapping_walls_enabled": "travel_compensate_overlapping_walls_enabled", + "remove_overlapping_walls_x_enabled": "travel_compensate_overlapping_walls_x_enabled", + "retraction_hop": "retraction_hop_enabled", "speed_support_lines": "speed_support_infill" } @@ -125,11 +129,25 @@ class VersionUpgrade21to22(VersionUpgrade): @staticmethod def translateSettings(settings): for key, value in settings.items(): - if key == "speed_support_lines": # Setting key was changed for 2.2. + if key == "fill_perimeter_gaps": #Setting is removed. + del settings[key] + elif key == "remove_overlapping_walls_0_enabled": #Setting is functionally replaced. + del settings[key] + settings["travel_compensate_overlapping_walls_0_enabled"] = value + elif key == "remove_overlapping_walls_enabled": #Setting is functionally replaced. + del settings[key] + settings["travel_compensate_overlapping_walls_enabled"] = value + elif key == "remove_overlapping_walls_x_enabled": #Setting is functionally replaced. + del settings[key] + settings["travel_compensate_overlapping_walls_x_enabled"] = value + elif key == "retraction_combing": #Combing was made into an enum instead of a boolean. + settings[key] = "off" if (value == "False") else "all" + elif key == "retraction_hop": #Setting key was changed. + del settings[key] + settings["retraction_hop_enabled"] = value + elif key == "speed_support_lines": #Setting key was changed. del settings[key] settings["speed_support_infill"] = value - if key == "retraction_combing": # Combing was made into an enum instead of a boolean. - settings[key] = "off" if (value == "False") else "all" return settings ## Translates a setting name for the change from Cura 2.1 to 2.2. From b841738b76a1e38e3e8971ac86b6df4552c7d44b Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 4 Jul 2016 01:18:26 +0200 Subject: [PATCH 169/236] Translate variants in profile Contributes to issue CURA-844. --- plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py index f194fe8101..915ecceec6 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py @@ -78,7 +78,7 @@ class Profile: config = configparser.ConfigParser(interpolation = None) config.add_section("general") - config.set("general", "version", "2") # Hard-coded profile version 2 + config.set("general", "version", "2") #Hard-coded profile version 2. config.set("general", "name", self._name) if self._type: config.set("general", "type", self._type) @@ -90,7 +90,10 @@ class Profile: else: config.set("general", "definition", "fdmprinter") if self._machine_variant_name: - config.set("general", "variant", self._machine_variant_name) + if self._machine_type_id: + config.set("general", "variant", VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateVariant(self._machine_variant_name, self._machine_type_id)) + else: + config.set("general", "variant", self._machine_variant_name) if self._material_name and self._type != "material": config.set("general", "material", self._material_name) @@ -110,7 +113,7 @@ class Profile: disabled_settings_defaults = [VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettingName(setting) for setting in self._disabled_settings_defaults] config.add_section("disabled_defaults") - disabled_defaults_string = str(disabled_settings_defaults[0]) # Must be at least 1 item, otherwise we wouldn't enter this if statement. + disabled_defaults_string = str(disabled_settings_defaults[0]) #Must be at least 1 item, otherwise we wouldn't enter this if statement. for item in disabled_settings_defaults[1:]: disabled_defaults_string += "," + str(item) From 8f34186a9b491070321f06896e12d67faf20a2d1 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 4 Jul 2016 01:27:13 +0200 Subject: [PATCH 170/236] Rename settings section to values It should be called 'values' in the new version. Contributes to issue CURA-844. --- plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py index 915ecceec6..9ebf415f78 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py @@ -99,9 +99,9 @@ class Profile: if self._settings: VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._settings) - config.add_section("settings") + config.add_section("values") for key, value in self._settings.items(): - config.set("settings", key, str(value)) + config.set("values", key, str(value)) if self._changed_settings_defaults: VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._changed_settings_defaults) From 19b4ec655efb87dd71aa82a99916954208cc8a85 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 4 Jul 2016 01:36:48 +0200 Subject: [PATCH 171/236] Move some things to metadata section Why there is a difference between 'general' and 'metadata', only His Noodleness knows. Also put in a default for the type. It should apparently be 'quality' unless it is a user profile. Contributes to issue CURA-844. --- .../VersionUpgrade21to22/Profile.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py index 9ebf415f78..1b2f90d917 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py @@ -80,22 +80,26 @@ class Profile: config.add_section("general") config.set("general", "version", "2") #Hard-coded profile version 2. config.set("general", "name", self._name) - if self._type: - config.set("general", "type", self._type) - if self._weight: - config.set("general", "weight", self._weight) if self._machine_type_id: translated_machine = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translatePrinter(self._machine_type_id) config.set("general", "definition", translated_machine) else: config.set("general", "definition", "fdmprinter") + + config.add_section("metadata") + if self._type: + config.set("metadata", "type", self._type) + else: + config.set("metadata", "type", "quality") + if self._weight: + config.set("metadata", "weight", self._weight) if self._machine_variant_name: if self._machine_type_id: - config.set("general", "variant", VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateVariant(self._machine_variant_name, self._machine_type_id)) + config.set("metadata", "variant", VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateVariant(self._machine_variant_name, self._machine_type_id)) else: - config.set("general", "variant", self._machine_variant_name) + config.set("metadata", "variant", self._machine_variant_name) if self._material_name and self._type != "material": - config.set("general", "material", self._material_name) + config.set("metadata", "material", self._material_name) if self._settings: VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettings(self._settings) From 1b0974ba9f862942d58d6b7b655d4a4815d4b033 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 5 Jul 2016 09:54:39 +0200 Subject: [PATCH 172/236] Rename translation dicts to plural form This is more in line with the rest of the code. Contributes to issue CURA-844. --- .../VersionUpgrade21to22.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py index 4f7d43b5c6..e798517e15 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -10,19 +10,19 @@ from . import Preferences #To upgrade preferences. from . import Profile # To upgrade profiles. ## How to translate printer names from the old version to the new. -_printer_translation = { +_printer_translations = { "ultimaker2plus": "ultimaker2_plus" } ## How to translate profile names from the old version to the new. -_profile_translation = { +_profile_translations = { "PLA": "generic_pla", "ABS": "generic_abs", "CPE": "generic_cpe" } ## How to translate setting names from the old version to the new. -_setting_name_translation = { +_setting_name_translations = { "remove_overlapping_walls_0_enabled": "travel_compensate_overlapping_walls_0_enabled", "remove_overlapping_walls_enabled": "travel_compensate_overlapping_walls_enabled", "remove_overlapping_walls_x_enabled": "travel_compensate_overlapping_walls_x_enabled", @@ -32,7 +32,7 @@ _setting_name_translation = { ## How to translate variants of specific machines from the old version to the # new. -_variant_translation = { +_variant_translations = { "ultimaker2_plus": { "0.25 mm": "ultimaker2_plus_0.25", "0.4 mm": "ultimaker2_plus_0.4", @@ -103,8 +103,8 @@ class VersionUpgrade21to22(VersionUpgrade): # \return The name of the corresponding printer in Cura 2.2. @staticmethod def translatePrinter(printer): - if printer in _printer_translation: - return _printer_translation[printer] + if printer in _printer_translations: + return _printer_translations[printer] return printer #Doesn't need to be translated. ## Translates a built-in profile name that might have changed since the @@ -114,8 +114,8 @@ class VersionUpgrade21to22(VersionUpgrade): # \return The corresponding profile name in the new version. @staticmethod def translateProfile(profile): - if profile in _profile_translation: - return _profile_translation[profile] + if profile in _profile_translations: + return _profile_translations[profile] return profile #Doesn't need to be translated. ## Updates settings for the change from Cura 2.1 to 2.2. @@ -156,8 +156,8 @@ class VersionUpgrade21to22(VersionUpgrade): # \return The name of the corresponding setting in Cura 2.2. @staticmethod def translateSettingName(setting): - if setting in _setting_name_translation: - return _setting_name_translation[setting] + if setting in _setting_name_translations: + return _setting_name_translations[setting] return setting #Doesn't need to be translated. ## Translates a variant name for the change from Cura 2.1 to 2.2 @@ -168,6 +168,6 @@ class VersionUpgrade21to22(VersionUpgrade): # \return The name of the corresponding variant in Cura 2.2. @staticmethod def translateVariant(variant, machine): - if machine in _variant_translation and variant in _variant_translation[machine]: - return _variant_translation[machine][variant] + if machine in _variant_translations and variant in _variant_translations[machine]: + return _variant_translations[machine][variant] return variant \ No newline at end of file From e6efba38687ed9f0b7253a6d8f3b95799de8a5d7 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 7 Jul 2016 15:13:58 +0200 Subject: [PATCH 173/236] Make version upgrade also translate file names This was required since Cura 2.1 produced files with the same filename (bar extension). This then resulted in two containers with the same ID. If you had bad luck, an instance container was chosen as global container (depending on which was first in the unordered dictionary). This gives the current settings the postfix _current_settings, fixing that issue. Contributes to issue CURA-844. --- .../VersionUpgrade21to22/MachineInstance.py | 18 ++++++---- .../VersionUpgrade21to22/Preferences.py | 15 +++++--- .../VersionUpgrade21to22/Profile.py | 21 +++++++---- .../VersionUpgrade21to22.py | 35 +++++++++++-------- 4 files changed, 55 insertions(+), 34 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py index 98545969cd..46003d4d21 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -10,11 +10,12 @@ import io #To write config files to strings as if they were files. # instance in version 1 of the file format. # # \param serialised The serialised form of a machine instance in version 1. +# \param filename The supposed file name of this machine instance. # \return A machine instance instance, or None if the file format is # incorrect. -def importFrom(serialised): +def importFrom(serialised, filename): try: - return MachineInstance(serialised) + return MachineInstance(serialised, filename) except (configparser.Error, UM.VersionUpgrade.FormatException, UM.VersionUpgrade.InvalidVersionException): return None @@ -24,7 +25,10 @@ class MachineInstance: ## Reads version 1 of the file format, storing it in memory. # # \param serialised A string with the contents of a machine instance file. - def __init__(self, serialised): + # \param filename The supposed file name of this machine instance. + def __init__(self, serialised, filename): + self._filename = filename + config = configparser.ConfigParser(interpolation = None) config.read_string(serialised) # Read the input string as config file. @@ -55,8 +59,8 @@ class MachineInstance: # # This is where the actual translation happens in this case. # - # \return A serialised form of this machine instance, serialised in - # version 2 of the file format. + # \return A tuple containing the new filename and a serialised form of + # this machine instance, serialised in version 2 of the file format. def export(self): config = configparser.ConfigParser(interpolation = None) # Build a config file in the form of version 2. @@ -73,7 +77,7 @@ class MachineInstance: variant = VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateVariant(self._variant_name, type_name) containers = [ - self._name, + self._name + "_current_settings", active_profile, active_material, variant, @@ -91,4 +95,4 @@ class MachineInstance: output = io.StringIO() config.write(output) - return output.getvalue() \ No newline at end of file + return self._filename, output.getvalue() \ No newline at end of file diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py index 37cb3ccb9f..2fcacedbf6 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py @@ -10,11 +10,12 @@ import UM.VersionUpgrade #To indicate that a file is of the wrong format. # in version 1 of the file format. # # \param serialised The serialised form of a preferences file in version 1. +# \param filename The supposed filename of the preferences file. # \return A representation of those preferences, or None if the file format is # incorrect. -def importFrom(serialised): +def importFrom(serialised, filename): try: - return Preferences(serialised) + return Preferences(serialised, filename) except (configparser.Error, UM.VersionUpgrade.FormatException, UM.VersionUpgrade.InvalidVersionException): return None @@ -24,7 +25,10 @@ class Preferences: ## Reads version 2 of the preferences file format, storing it in memory. # # \param serialised A serialised version 2 preferences file. - def __init__(self, serialised): + # \param filename The supposed filename of the preferences file. + def __init__(self, serialised, filename): + self._filename = filename + self._config = configparser.ConfigParser(interpolation = None) self._config.read_string(serialised) @@ -42,7 +46,8 @@ class Preferences: # # This is where the actual translation happens. # - # \return A serialised version of a preferences file in version 3. + # \return A tuple containing the new filename and a serialised version of + # a preferences file in version 3. def export(self): #Reset the cura/categories_expanded property since it works differently now. if self._config.has_section("cura") and self._config.has_option("cura", "categories_expanded"): @@ -70,4 +75,4 @@ class Preferences: #Output the result as a string. output = io.StringIO() self._config.write(output) - return output.getvalue() \ No newline at end of file + return self._filename, output.getvalue() \ No newline at end of file diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py index 1b2f90d917..2f911d755d 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py @@ -10,10 +10,11 @@ import UM.VersionUpgrade # of the file format. # # \param serialised The serialised form of a profile in version 1. +# \param filename The supposed filename of the profile. # \return A profile instance, or None if the file format is incorrect. -def importFrom(serialised): +def importFrom(serialised, filename): try: - return Profile(serialised) + return Profile(serialised, filename) except (configparser.Error, UM.VersionUpgrade.FormatException, UM.VersionUpgrade.InvalidVersionException): return None @@ -22,8 +23,11 @@ def importFrom(serialised): class Profile: ## Reads version 1 of the file format, storing it in memory. # - # \param serialised A string with the contents of a machine instance file. - def __init__(self, serialised): + # \param serialised A string with the contents of a profile. + # \param filename The supposed filename of the profile. + def __init__(self, serialised, filename): + self._filename = filename + parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) @@ -70,11 +74,14 @@ class Profile: ## Serialises this profile as file format version 2. # - # \return A serialised form of this profile, serialised in version 2 of - # the file format. + # \return A tuple containing the new filename and a serialised form of + # this profile, serialised in version 2 of the file format. def export(self): import VersionUpgrade21to22 # Import here to prevent circular dependencies. + if self._name == "Current settings": + self._filename += "_current_settings" #This resolves a duplicate ID arising from how Cura 2.1 stores its current settings. + config = configparser.ConfigParser(interpolation = None) config.add_section("general") @@ -123,4 +130,4 @@ class Profile: output = io.StringIO() config.write(output) - return output.getvalue() \ No newline at end of file + return self._filename, output.getvalue() \ No newline at end of file diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py index e798517e15..ae2356d720 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -66,34 +66,39 @@ class VersionUpgrade21to22(VersionUpgrade): ## Converts machine instances from format version 1 to version 2. # # \param serialised The serialised machine instance in version 1. - # \return The serialised machine instance in version 2, or None if the - # input was not of the correct format. - def upgradeMachineInstance(self, serialised): - machine_instance = MachineInstance.importFrom(serialised) + # \param filename The supposed file name of the machine instance. + # \return A tuple containing the new filename and the serialised machine + # instance in version 2, or None if the input was not of the correct + # format. + def upgradeMachineInstance(self, serialised, filename): + machine_instance = MachineInstance.importFrom(serialised, filename) if not machine_instance: #Invalid file format. - return None + return filename, None return machine_instance.export() ## Converts preferences from format version 2 to version 3. # # \param serialised The serialised preferences file in version 2. - # \return The serialised preferences in version 3, or None if the input - # was not of the correct format. - def upgradePreferences(self, serialised): - preferences = Preferences.importFrom(serialised) + # \param filename THe supposed file name of the preferences file. + # \return A tuple containing the new filename and the serialised + # preferences in version 3, or None if the input was not of the correct + # format. + def upgradePreferences(self, serialised, filename): + preferences = Preferences.importFrom(serialised, filename) if not preferences: #Invalid file format. - return None + return filename, None return preferences.export() ## Converts profiles from format version 1 to version 2. # # \param serialised The serialised profile in version 1. - # \return The serialised profile in version 2, or None if the input was - # not of the correct format. - def upgradeProfile(self, serialised): - profile = Profile.importFrom(serialised) + # \param filename The supposed file name of the profile. + # \return A tuple containing the new filename and the serialised profile + # in version 2, or None if the input was not of the correct format. + def upgradeProfile(self, serialised, filename): + profile = Profile.importFrom(serialised, filename) if not profile: # Invalid file format. - return None + return filename, None return profile.export() ## Translates a printer name that might have changed since the last From 101b22528be3fbd6d7142206896eb1ed3e0c9d16 Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Thu, 7 Jul 2016 15:15:22 +0200 Subject: [PATCH 174/236] JSOn feat: nozzle startup prime location (CURA-1816) --- resources/definitions/fdmprinter.def.json | 45 +++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index b5d3796e74..8bdbacc4ae 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -315,6 +315,51 @@ "settable_per_mesh": false, "settable_per_extruder": false, "settable_per_meshgroup": false + }, + "extruder_prime_pos_x": + { + "label": "Extruder Prime X Position", + "description": "The x coordinate of the position where the nozzle primes at the start of printing.", + "type": "float", + "unit": "mm", + "default_value": 0, + "minimum_value_warning": "-1000", + "maximum_value_warning": "1000", + "settable_per_mesh": false, + "settable_per_extruder": true + }, + "extruder_prime_pos_y": + { + "label": "Extruder Prime Y Position", + "description": "The y coordinate of the position where the nozzle primes at the start of printing.", + "type": "float", + "unit": "mm", + "default_value": 0, + "minimum_value_warning": "-1000", + "maximum_value_warning": "1000", + "settable_per_mesh": false, + "settable_per_extruder": true + }, + "extruder_prime_pos_z": + { + "label": "Extruder Prime Z Position", + "description": "The z coordinate of the position where the nozzle primes at the start of printing.", + "type": "float", + "unit": "mm", + "default_value": 0, + "minimum_value_warning": "-1000", + "maximum_value_warning": "1000", + "settable_per_mesh": false, + "settable_per_extruder": true + }, + "extruder_prime_pos_abs": + { + "label": "Absolute Extruder Prime Position", + "description": "Make the extruder prime position absolute rather than relative to the last-known location of the head.", + "type": "bool", + "default_value": false, + "settable_per_mesh": false, + "settable_per_extruder": true } } }, From 8f5e56c66ec39502b569c20e147ff93fba006d9c Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 7 Jul 2016 15:22:27 +0200 Subject: [PATCH 175/236] Make documentation specify filename without extension Contributes to issue CURA-844. --- .../VersionUpgrade21to22/MachineInstance.py | 6 ++++-- .../VersionUpgrade/VersionUpgrade21to22/Preferences.py | 6 ++++-- plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py | 4 ++-- .../VersionUpgrade21to22/VersionUpgrade21to22.py | 9 ++++++--- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py index 46003d4d21..a7534dc862 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -10,7 +10,8 @@ import io #To write config files to strings as if they were files. # instance in version 1 of the file format. # # \param serialised The serialised form of a machine instance in version 1. -# \param filename The supposed file name of this machine instance. +# \param filename The supposed file name of this machine instance, without +# extension. # \return A machine instance instance, or None if the file format is # incorrect. def importFrom(serialised, filename): @@ -24,7 +25,8 @@ def importFrom(serialised, filename): class MachineInstance: ## Reads version 1 of the file format, storing it in memory. # - # \param serialised A string with the contents of a machine instance file. + # \param serialised A string with the contents of a machine instance file, + # without extension. # \param filename The supposed file name of this machine instance. def __init__(self, serialised, filename): self._filename = filename diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py index 2fcacedbf6..9f6a36d87a 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py @@ -10,7 +10,8 @@ import UM.VersionUpgrade #To indicate that a file is of the wrong format. # in version 1 of the file format. # # \param serialised The serialised form of a preferences file in version 1. -# \param filename The supposed filename of the preferences file. +# \param filename The supposed filename of the preferences file, without +# extension. # \return A representation of those preferences, or None if the file format is # incorrect. def importFrom(serialised, filename): @@ -25,7 +26,8 @@ class Preferences: ## Reads version 2 of the preferences file format, storing it in memory. # # \param serialised A serialised version 2 preferences file. - # \param filename The supposed filename of the preferences file. + # \param filename The supposed filename of the preferences file, without + # extension. def __init__(self, serialised, filename): self._filename = filename diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py index 2f911d755d..621f346887 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py @@ -10,7 +10,7 @@ import UM.VersionUpgrade # of the file format. # # \param serialised The serialised form of a profile in version 1. -# \param filename The supposed filename of the profile. +# \param filename The supposed filename of the profile, without extension. # \return A profile instance, or None if the file format is incorrect. def importFrom(serialised, filename): try: @@ -24,7 +24,7 @@ class Profile: ## Reads version 1 of the file format, storing it in memory. # # \param serialised A string with the contents of a profile. - # \param filename The supposed filename of the profile. + # \param filename The supposed filename of the profile, without extension. def __init__(self, serialised, filename): self._filename = filename diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py index ae2356d720..a45a4a6e79 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -66,7 +66,8 @@ class VersionUpgrade21to22(VersionUpgrade): ## Converts machine instances from format version 1 to version 2. # # \param serialised The serialised machine instance in version 1. - # \param filename The supposed file name of the machine instance. + # \param filename The supposed file name of the machine instance, without + # extension. # \return A tuple containing the new filename and the serialised machine # instance in version 2, or None if the input was not of the correct # format. @@ -79,7 +80,8 @@ class VersionUpgrade21to22(VersionUpgrade): ## Converts preferences from format version 2 to version 3. # # \param serialised The serialised preferences file in version 2. - # \param filename THe supposed file name of the preferences file. + # \param filename THe supposed file name of the preferences file, without + # extension. # \return A tuple containing the new filename and the serialised # preferences in version 3, or None if the input was not of the correct # format. @@ -92,7 +94,8 @@ class VersionUpgrade21to22(VersionUpgrade): ## Converts profiles from format version 1 to version 2. # # \param serialised The serialised profile in version 1. - # \param filename The supposed file name of the profile. + # \param filename The supposed file name of the profile, without + # extension. # \return A tuple containing the new filename and the serialised profile # in version 2, or None if the input was not of the correct format. def upgradeProfile(self, serialised, filename): From 9d1ea28b49e4ec13196dd0050cbb12c9f3ef57fd Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 7 Jul 2016 15:50:46 +0200 Subject: [PATCH 176/236] Remove old commented-out code The function it calls no longer exists. The protobuf message it listens to was renamed and the renamed message is properly listened to in the line above. Contributes to issue CURA-1687. --- plugins/CuraEngineBackend/CuraEngineBackend.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 40729eb48b..3bcc56a79c 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -80,7 +80,6 @@ class CuraEngineBackend(Backend): self._message_handlers["cura.proto.GCodeLayer"] = self._onGCodeLayerMessage self._message_handlers["cura.proto.GCodePrefix"] = self._onGCodePrefixMessage self._message_handlers["cura.proto.PrintTimeMaterialEstimates"] = self._onPrintTimeMaterialEstimates - #self._message_handlers["cura.proto.ObjectPrintTime"] = self._onObjectPrintTimeMessage self._message_handlers["cura.proto.SlicingFinished"] = self._onSlicingFinishedMessage self._start_slice_job = None From a76f6375c74ea69b514a26a220c22de6679976fa Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 7 Jul 2016 15:56:14 +0200 Subject: [PATCH 177/236] Added way to stop the heating of bed / hotend in checkup CURA-1385 --- .../UMOCheckupMachineAction.py | 10 +++++++ .../UMOCheckupMachineAction.qml | 29 ++++++++++++++++--- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py index 201e415e15..ac4dd99bad 100644 --- a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py +++ b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py @@ -160,6 +160,16 @@ class UMOCheckupMachineAction(MachineAction): except AttributeError as e: # Connection is probably not a USB connection. Something went pretty wrong if this happens. pass + @pyqtSlot() + def cooldownHotend(self): + if self._output_device is not None: + self._output_device.setTargetHotendTemperature(0, 0) + + @pyqtSlot() + def cooldownBed(self): + if self._output_device is not None: + self._output_device.setTargetBedTemperature(0) + @pyqtSlot() def heatupHotend(self): if self._output_device is not None: diff --git a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml index 5d5a102e7d..43e1fa8f69 100644 --- a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml +++ b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml @@ -15,6 +15,8 @@ Cura.MachineAction anchors.fill: parent; property int leftRow: checkupMachineAction.width * 0.40 property int rightRow: checkupMachineAction.width * 0.60 + property bool heatupHotendStarted: false + property bool heatupBedStarted: false UM.I18nCatalog { id: catalog; name:"cura"} Label { @@ -51,6 +53,8 @@ Cura.MachineAction text: catalog.i18nc("@action:button","Start Printer Check"); onClicked: { + checkupMachineAction.heatupHotendStarted = false + checkupMachineAction.heatupBedStarted = false manager.startCheck() } } @@ -181,10 +185,19 @@ Cura.MachineAction anchors.leftMargin: UM.Theme.getSize("default_margin").width/2 Button { - text: catalog.i18nc("@action:button","Start Heating") + text: checkupMachineAction.heatupHotendStarted ? catalog.i18nc("@action:button","Stop Heating") : catalog.i18nc("@action:button","Start Heating") + // onClicked: { - manager.heatupHotend() + if (checkupMachineAction.heatupHotendStarted) + { + manager.cooldownHotend() + checkupMachineAction.heatupHotendStarted = false + } else + { + manager.heatupHotend() + checkupMachineAction.heatupHotendStarted = true + } } } } @@ -230,10 +243,18 @@ Cura.MachineAction anchors.leftMargin: UM.Theme.getSize("default_margin").width/2 Button { - text: catalog.i18nc("@action:button","Start Heating") + text: checkupMachineAction.heatupBedStarted ?catalog.i18nc("@action:button","Stop Heating") : catalog.i18nc("@action:button","Start Heating") onClicked: { - manager.heatupBed() + if (checkupMachineAction.heatupBedStarted) + { + manager.cooldownBed() + checkupMachineAction.heatupBedStarted = false + } else + { + manager.heatupBed() + checkupMachineAction.heatupBedStarted = true + } } } } From 342d4f1b82754e3b6834e2c0ae3cb3a3e5f456f3 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Thu, 7 Jul 2016 16:23:27 +0200 Subject: [PATCH 178/236] Changed tooltip extremely small. CURA-1671 --- plugins/LayerView/LayerView.py | 2 -- resources/qml/Preferences/GeneralPage.qml | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/LayerView/LayerView.py b/plugins/LayerView/LayerView.py index cd7a17a357..9725f2292d 100644 --- a/plugins/LayerView/LayerView.py +++ b/plugins/LayerView/LayerView.py @@ -135,8 +135,6 @@ class LayerView(View): self.currentLayerNumChanged.emit() - currentLayerNumChanged = Signal() - def calculateMaxLayers(self): scene = self.getController().getScene() renderer = self.getRenderer() # TODO: @UnusedVariable diff --git a/resources/qml/Preferences/GeneralPage.qml b/resources/qml/Preferences/GeneralPage.qml index 2886f94d92..7e5b4efadc 100644 --- a/resources/qml/Preferences/GeneralPage.qml +++ b/resources/qml/Preferences/GeneralPage.qml @@ -246,7 +246,7 @@ UM.PreferencesPage UM.TooltipArea { width: childrenRect.width height: childrenRect.height - text: catalog.i18nc("@info:tooltip","Should objects be scaled up if they are extremely small?") + text: catalog.i18nc("@info:tooltip","An object may appear extremely small if its unit is for example in meters rather than millimeters. Should these objects be scaled up?") CheckBox { From 6374eee7b45e4065b1a95c26674b8f27dd954eeb Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Thu, 7 Jul 2016 16:55:56 +0200 Subject: [PATCH 179/236] Fix LayerView cursor issue. CURA-1381 --- plugins/LayerView/LayerView.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/LayerView/LayerView.qml b/plugins/LayerView/LayerView.qml index 4779d74720..a7e9a7c67d 100644 --- a/plugins/LayerView/LayerView.qml +++ b/plugins/LayerView/LayerView.qml @@ -69,7 +69,7 @@ Item // Ensure that the cursor is at the first position. On some systems the text isnt fully visible // Seems to have to do something with different dpi densities that QML doesn't quite handle. // Another option would be to increase the size even further, but that gives pretty ugly results. - onTextChanged: cursorPosition = 0 + onEditingFinished: cursorPosition = 0 style: TextFieldStyle { textColor: UM.Theme.getColor("setting_control_text"); From 0477955974cbdbca249438e1dc16ab536a88cd6f Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 7 Jul 2016 17:02:29 +0200 Subject: [PATCH 180/236] Sidebar stuff is now properly done in a loader (and in seperate files) CURA-1036 --- resources/qml/PrintMonitor.qml | 118 ++++++++++++++++++++++++++++++ resources/qml/Sidebar.qml | 112 +--------------------------- resources/qml/SidebarContents.qml | 43 +++++++++++ 3 files changed, 164 insertions(+), 109 deletions(-) create mode 100644 resources/qml/PrintMonitor.qml create mode 100644 resources/qml/SidebarContents.qml diff --git a/resources/qml/PrintMonitor.qml b/resources/qml/PrintMonitor.qml new file mode 100644 index 0000000000..c8f56a5b98 --- /dev/null +++ b/resources/qml/PrintMonitor.qml @@ -0,0 +1,118 @@ +// Copyright (c) 2016 Ultimaker B.V. +// Cura is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Controls.Styles 1.1 +import QtQuick.Layouts 1.1 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +Column +{ + id: printMonitor + + Loader + { + sourceComponent: monitorSection + property string label: catalog.i18nc("@label", "Temperatures") + } + Repeater + { + model: machineExtruderCount.properties.value + delegate: Loader + { + sourceComponent: monitorItem + property string label: machineExtruderCount.properties.value > 1 ? catalog.i18nc("@label", "Hotend Temperature %1").arg(index + 1) : catalog.i18nc("@label", "Hotend Temperature") + property string value: printerConnected ? Math.round(Cura.MachineManager.printerOutputDevices[0].hotendTemperatures[index]) + "°C" : "" + } + } + Repeater + { + model: machineHeatedBed.properties.value == "True" ? 1 : 0 + delegate: Loader + { + sourceComponent: monitorItem + property string label: catalog.i18nc("@label", "Bed Temperature") + property string value: printerConnected ? Math.round(Cura.MachineManager.printerOutputDevices[0].bedTemperature) + "°C" : "" + } + } + + Loader + { + sourceComponent: monitorSection + property string label: catalog.i18nc("@label", "Active print") + } + Loader + { + sourceComponent: monitorItem + property string label: catalog.i18nc("@label", "Job Name") + property string value: printerConnected ? Cura.MachineManager.printerOutputDevices[0].jobName : "" + } + Loader + { + sourceComponent: monitorItem + property string label: catalog.i18nc("@label", "Printing Time") + property string value: printerConnected ? getPrettyTime(Cura.MachineManager.printerOutputDevices[0].timeTotal) : "" + } + Loader + { + sourceComponent: monitorItem + property string label: catalog.i18nc("@label", "Estimated time left") + property string value: printerConnected ? getPrettyTime(Cura.MachineManager.printerOutputDevices[0].timeTotal - Cura.MachineManager.printerOutputDevices[0].timeElapsed) : "" + } + Loader + { + sourceComponent: monitorItem + property string label: catalog.i18nc("@label", "Current Layer") + property string value: printerConnected ? "0" : "" + } + + Component + { + id: monitorItem + + Row + { + height: UM.Theme.getSize("setting_control").height + Label + { + text: label + color: printerConnected ? UM.Theme.getColor("setting_control_text") : UM.Theme.getColor("setting_control_disabled_text") + font: UM.Theme.getFont("default") + width: base.width * 0.4 + elide: Text.ElideRight + anchors.verticalCenter: parent.verticalCenter + } + Label + { + text: value + color: printerConnected ? UM.Theme.getColor("setting_control_text") : UM.Theme.getColor("setting_control_disabled_text") + font: UM.Theme.getFont("default") + anchors.verticalCenter: parent.verticalCenter + } + } + } + Component + { + id: monitorSection + + Rectangle + { + color: UM.Theme.getColor("setting_category") + width: base.width - 2 * UM.Theme.getSize("default_margin").width + height: UM.Theme.getSize("section").height + + Label + { + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: UM.Theme.getSize("default_margin").width + text: label + font: UM.Theme.getFont("setting_category") + color: UM.Theme.getColor("setting_category_text") + } + } + } +} \ No newline at end of file diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index d9b3b56109..ebf75e7a49 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -285,122 +285,16 @@ Rectangle } } - // Item that shows the print monitor properties - Column + Loader { - id: printMonitor - anchors.bottom: footerSeparator.top anchors.top: monitorLabel.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.left: base.left anchors.leftMargin: UM.Theme.getSize("default_margin").width anchors.right: base.right - visible: monitoringPrint - - Loader - { - sourceComponent: monitorSection - property string label: catalog.i18nc("@label", "Temperatures") - } - Repeater - { - model: machineExtruderCount.properties.value - delegate: Loader - { - sourceComponent: monitorItem - property string label: machineExtruderCount.properties.value > 1 ? catalog.i18nc("@label", "Hotend Temperature %1").arg(index + 1) : catalog.i18nc("@label", "Hotend Temperature") - property string value: printerConnected ? Math.round(Cura.MachineManager.printerOutputDevices[0].hotendTemperatures[index]) + "°C" : "" - } - } - Repeater - { - model: machineHeatedBed.properties.value == "True" ? 1 : 0 - delegate: Loader - { - sourceComponent: monitorItem - property string label: catalog.i18nc("@label", "Bed Temperature") - property string value: printerConnected ? Math.round(Cura.MachineManager.printerOutputDevices[0].bedTemperature) + "°C" : "" - } - } - - Loader - { - sourceComponent: monitorSection - property string label: catalog.i18nc("@label", "Active print") - } - Loader - { - sourceComponent: monitorItem - property string label: catalog.i18nc("@label", "Job Name") - property string value: printerConnected ? Cura.MachineManager.printerOutputDevices[0].jobName : "" - } - Loader - { - sourceComponent: monitorItem - property string label: catalog.i18nc("@label", "Printing Time") - property string value: printerConnected ? getPrettyTime(Cura.MachineManager.printerOutputDevices[0].timeTotal) : "" - } - Loader - { - sourceComponent: monitorItem - property string label: catalog.i18nc("@label", "Estimated time left") - property string value: printerConnected ? getPrettyTime(Cura.MachineManager.printerOutputDevices[0].timeTotal - Cura.MachineManager.printerOutputDevices[0].timeElapsed) : "" - } - Loader - { - sourceComponent: monitorItem - property string label: catalog.i18nc("@label", "Current Layer") - property string value: printerConnected ? "0" : "" - } - - Component - { - id: monitorItem - - Row - { - height: UM.Theme.getSize("setting_control").height - Label - { - text: label - color: printerConnected ? UM.Theme.getColor("setting_control_text") : UM.Theme.getColor("setting_control_disabled_text") - font: UM.Theme.getFont("default") - width: base.width * 0.4 - elide: Text.ElideRight - anchors.verticalCenter: parent.verticalCenter - } - Label - { - text: value - color: printerConnected ? UM.Theme.getColor("setting_control_text") : UM.Theme.getColor("setting_control_disabled_text") - font: UM.Theme.getFont("default") - anchors.verticalCenter: parent.verticalCenter - } - } - } - Component - { - id: monitorSection - - Rectangle - { - color: UM.Theme.getColor("setting_category") - width: base.width - 2 * UM.Theme.getSize("default_margin").width - height: UM.Theme.getSize("section").height - - Label - { - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("default_margin").width - text: label - font: UM.Theme.getFont("setting_category") - color: UM.Theme.getColor("setting_category_text") - } - } - } - } + source: monitoringPrint ? "PrintMonitor.qml": "SidebarContents.qml" + } Rectangle { diff --git a/resources/qml/SidebarContents.qml b/resources/qml/SidebarContents.qml new file mode 100644 index 0000000000..c53818a9ce --- /dev/null +++ b/resources/qml/SidebarContents.qml @@ -0,0 +1,43 @@ +// Copyright (c) 2016 Ultimaker B.V. +// Cura is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 1.1 +import QtQuick.Controls.Styles 1.1 +import QtQuick.Layouts 1.1 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +StackView +{ + id: sidebarContents + + delegate: StackViewDelegate + { + function transitionFinished(properties) + { + properties.exitItem.opacity = 1 + } + + pushTransition: StackViewTransition + { + PropertyAnimation + { + target: enterItem + property: "opacity" + from: 0 + to: 1 + duration: 100 + } + PropertyAnimation + { + target: exitItem + property: "opacity" + from: 1 + to: 0 + duration: 100 + } + } + } +} \ No newline at end of file From 66dd4a7a508ce7b5bb12569a1f045da1d50a8132 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 7 Jul 2016 17:08:03 +0200 Subject: [PATCH 181/236] Add exception logging CURA-1385 --- .../UltimakerMachineActions/UMOCheckupMachineAction.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py index ac4dd99bad..b374138791 100644 --- a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py +++ b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py @@ -3,6 +3,7 @@ from cura.PrinterOutputDevice import PrinterOutputDevice from UM.Application import Application from PyQt5.QtCore import pyqtSlot, pyqtSignal, pyqtProperty +from UM.Logger import Logger from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") @@ -57,8 +58,9 @@ class UMOCheckupMachineAction(MachineAction): self._output_device.endstopStateChanged.disconnect(self._onEndstopStateChanged) try: self._output_device.stopPollEndstop() - except AttributeError: # Connection is probably not a USB connection. Something went pretty wrong if this happens. - pass + except AttributeError as e: # Connection is probably not a USB connection. Something went pretty wrong if this happens. + Logger.log("e", "An exception occurred while stopping end stop polling: %s" % str(e)) + self._output_device = None self._check_started = False @@ -158,7 +160,7 @@ class UMOCheckupMachineAction(MachineAction): self._output_device.hotendTemperaturesChanged.connect(self._onHotendTemperatureChanged) self._output_device.endstopStateChanged.connect(self._onEndstopStateChanged) except AttributeError as e: # Connection is probably not a USB connection. Something went pretty wrong if this happens. - pass + Logger.log("e", "An exception occurred while starting end stop polling: %s" % str(e)) @pyqtSlot() def cooldownHotend(self): From 571523e0efa64d5906d869bbd04a0d392f43706c Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 7 Jul 2016 17:30:39 +0200 Subject: [PATCH 182/236] Ensure an XML material always has a description and adhesion_info metadata entry Contributes to CURA-342 --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 709eb8c276..ec75b7253c 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -241,6 +241,12 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): self.addMetaDataEntry(tag_name, entry.text) + if not "description" in self.getMetaData(): + self.addMetaDataEntry("description", "") + + if not "adhesion_info" in self.getMetaData(): + self.addMetaDataEntry("adhesion_info", "") + property_values = {} properties = data.iterfind("./um:properties/*", self.__namespaces) for entry in properties: From 975106b90e6c14fc101482c194820d8fc6bc30de Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 7 Jul 2016 17:31:12 +0200 Subject: [PATCH 183/236] Also update description and adhesion info when they change Contributes to CURA-342 --- resources/qml/Preferences/MaterialView.qml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/resources/qml/Preferences/MaterialView.qml b/resources/qml/Preferences/MaterialView.qml index b187fcb469..c79b8867c1 100644 --- a/resources/qml/Preferences/MaterialView.qml +++ b/resources/qml/Preferences/MaterialView.qml @@ -174,6 +174,8 @@ TabView wrapMode: Text.WordWrap readOnly: !base.editingEnabled; + + onEditingFinished: Cura.ContainerManager.setContainerMetaDataEntry(base.containerId, "description", text) } Label { width: parent.width; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Adhesion Information") } @@ -185,6 +187,7 @@ TabView wrapMode: Text.WordWrap readOnly: !base.editingEnabled; + onEditingFinished: Cura.ContainerManager.setContainerMetaDataEntry(base.containerId, "adhesion_info", text) } } } From 7df5008d3c02827ce1b48aa37a0c930c45ac3dce Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 7 Jul 2016 17:32:11 +0200 Subject: [PATCH 184/236] Disable spool weight etc. fields since they are not yet functional Contributes to CURA-342 --- resources/qml/Preferences/MaterialView.qml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/resources/qml/Preferences/MaterialView.qml b/resources/qml/Preferences/MaterialView.qml index c79b8867c1..6d5f218c06 100644 --- a/resources/qml/Preferences/MaterialView.qml +++ b/resources/qml/Preferences/MaterialView.qml @@ -127,40 +127,40 @@ TabView } Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament Cost") } - ReadOnlySpinBox + SpinBox { width: base.secondColumnWidth; value: properties.spool_cost; prefix: base.currency - readOnly: !base.editingEnabled; + enabled: false } Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament weight") } - ReadOnlySpinBox + SpinBox { width: base.secondColumnWidth; value: properties.spool_weight; suffix: "g"; stepSize: 10 - readOnly: !base.editingEnabled; + enabled: false } Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament length") } - ReadOnlySpinBox + SpinBox { width: base.secondColumnWidth; value: parseFloat(properties.spool_length); suffix: "m"; - readOnly: !base.editingEnabled; + enabled: false } Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Cost per Meter (Approx.)") } - ReadOnlySpinBox + SpinBox { width: base.secondColumnWidth; value: parseFloat(properties.cost_per_meter); suffix: catalog.i18nc("@label", "%1/m".arg(base.currency)); - readOnly: !base.editingEnabled; + enabled: false } Item { width: parent.width; height: UM.Theme.getSize("default_margin").height } From 6b9689c2c79a108ed3715e23aaeddde9d25953af Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Thu, 7 Jul 2016 17:32:49 +0200 Subject: [PATCH 185/236] Use clearer styling of disabled entry fields on the materials page Contributes to CURA-342 --- resources/qml/Preferences/MaterialView.qml | 11 +++-- resources/qml/Preferences/ReadOnlySpinBox.qml | 47 +++++++++++++++---- .../qml/Preferences/ReadOnlyTextArea.qml | 46 ++++++++++++++++++ .../qml/Preferences/ReadOnlyTextField.qml | 46 ++++++++++++++++++ 4 files changed, 136 insertions(+), 14 deletions(-) create mode 100644 resources/qml/Preferences/ReadOnlyTextArea.qml create mode 100644 resources/qml/Preferences/ReadOnlyTextField.qml diff --git a/resources/qml/Preferences/MaterialView.qml b/resources/qml/Preferences/MaterialView.qml index 6d5f218c06..67a149f446 100644 --- a/resources/qml/Preferences/MaterialView.qml +++ b/resources/qml/Preferences/MaterialView.qml @@ -45,7 +45,7 @@ TabView property real rowHeight: textField.height; Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Brand") } - TextField + ReadOnlyTextField { id: textField; width: base.secondColumnWidth; @@ -55,7 +55,7 @@ TabView } Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Material Type") } - TextField + ReadOnlyTextField { width: base.secondColumnWidth; text: properties.material_type; @@ -85,7 +85,7 @@ TabView MouseArea { anchors.fill: parent; onClicked: colorDialog.open(); enabled: base.editingEnabled } } - TextField + ReadOnlyTextField { id: colorLabel; text: properties.color_name; @@ -167,7 +167,7 @@ TabView Label { width: parent.width; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Description") } - TextArea + ReadOnlyTextArea { text: properties.description; width: base.firstColumnWidth + base.secondColumnWidth @@ -180,13 +180,14 @@ TabView Label { width: parent.width; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Adhesion Information") } - TextArea + ReadOnlyTextArea { text: properties.adhesion_info; width: base.firstColumnWidth + base.secondColumnWidth wrapMode: Text.WordWrap readOnly: !base.editingEnabled; + onEditingFinished: Cura.ContainerManager.setContainerMetaDataEntry(base.containerId, "adhesion_info", text) } } diff --git a/resources/qml/Preferences/ReadOnlySpinBox.qml b/resources/qml/Preferences/ReadOnlySpinBox.qml index 5c1a3cbe19..8692f55708 100644 --- a/resources/qml/Preferences/ReadOnlySpinBox.qml +++ b/resources/qml/Preferences/ReadOnlySpinBox.qml @@ -5,19 +5,48 @@ import QtQuick 2.1 import QtQuick.Controls 1.1 import QtQuick.Dialogs 1.2 -// Provides a SpinBox with the same readOnly property as a TextField -SpinBox +Item { id: base + + property alias value: spinBox.value + property alias minimumValue: spinBox.minimumValue + property alias maximumValue: spinBox.maximumValue + property alias stepSize: spinBox.stepSize + property alias prefix: spinBox.prefix + property alias suffix: spinBox.suffix + property alias decimals: spinBox.decimals + + signal editingFinished(); + property bool readOnly: false - Keys.enabled: !readOnly - MouseArea + width: spinBox.width + height: spinBox.height + + SpinBox { - acceptedButtons: Qt.AllButtons; - anchors.fill: parent; - enabled: parent.readOnly; - onWheel: wheel.accepted = true; - cursorShape: enabled ? Qt.ArrowCursor : Qt.IBeamCursor; + id: spinBox + + enabled: !base.readOnly + opacity: base.readOnly ? 0.5 : 1.0 + + anchors.fill: parent + + onEditingFinished: base.editingFinished() } + + Label + { + visible: base.readOnly + text: base.prefix + base.value.toFixed(spinBox.decimals) + base.suffix + + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: spinBox.__style ? spinBox.__style.padding.left : 0 + + color: palette.buttonText + } + + SystemPalette { id: palette } } diff --git a/resources/qml/Preferences/ReadOnlyTextArea.qml b/resources/qml/Preferences/ReadOnlyTextArea.qml new file mode 100644 index 0000000000..cbef8fa46b --- /dev/null +++ b/resources/qml/Preferences/ReadOnlyTextArea.qml @@ -0,0 +1,46 @@ +// Copyright (c) 2016 Ultimaker B.V. +// Uranium is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.1 +import QtQuick.Controls 1.1 +import QtQuick.Dialogs 1.2 + +Item +{ + id: base + + property alias text: textArea.text + property alias wrapMode: textArea.wrapMode + + signal editingFinished(); + + property bool readOnly: false + + width: textArea.width + height: textArea.height + + TextArea + { + id: textArea + + enabled: !base.readOnly + opacity: base.readOnly ? 0.5 : 1.0 + + anchors.fill: parent + + onEditingFinished: base.editingFinished() + } + + Label + { + visible: base.readOnly + text: textArea.text + + anchors.fill: parent + anchors.margins: textArea.__style ? textArea.__style.textMargin : 4 + + color: palette.buttonText + } + + SystemPalette { id: palette } +} diff --git a/resources/qml/Preferences/ReadOnlyTextField.qml b/resources/qml/Preferences/ReadOnlyTextField.qml new file mode 100644 index 0000000000..28c714259b --- /dev/null +++ b/resources/qml/Preferences/ReadOnlyTextField.qml @@ -0,0 +1,46 @@ +// Copyright (c) 2016 Ultimaker B.V. +// Uranium is released under the terms of the AGPLv3 or higher. + +import QtQuick 2.1 +import QtQuick.Controls 1.1 +import QtQuick.Dialogs 1.2 + +Item +{ + id: base + + property alias text: textField.text + + signal editingFinished(); + + property bool readOnly: false + + width: textField.width + height: textField.height + + TextField + { + id: textField + + enabled: !base.readOnly + opacity: base.readOnly ? 0.5 : 1.0 + + anchors.fill: parent + + onEditingFinished: base.editingFinished() + } + + Label + { + visible: base.readOnly + text: textField.text + + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: textField.__panel ? textField.__panel.leftMargin : 0 + + color: palette.buttonText + } + + SystemPalette { id: palette } +} From a774f06237bc119fe3abcd301fcd64f3be8970b7 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 7 Jul 2016 17:54:43 +0200 Subject: [PATCH 186/236] Hide checks if the printer is not connected, hide bed temperature if no bed is installed CURA-1385 --- .../UMOCheckupMachineAction.py | 11 ++++++++++- .../UMOCheckupMachineAction.qml | 18 +++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py index b374138791..b46c92a82c 100644 --- a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py +++ b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.py @@ -79,6 +79,8 @@ class UMOCheckupMachineAction(MachineAction): self._z_min_endstop_test_completed = False self.onZMinEndstopTestCompleted.emit() + self.heatedBedChanged.emit() + @pyqtProperty(bool, notify = onBedTestCompleted) def bedTestCompleted(self): return self._bed_test_completed @@ -180,4 +182,11 @@ class UMOCheckupMachineAction(MachineAction): @pyqtSlot() def heatupBed(self): if self._output_device is not None: - self._output_device.setTargetBedTemperature(self._bed_target_temp) \ No newline at end of file + self._output_device.setTargetBedTemperature(self._bed_target_temp) + + heatedBedChanged = pyqtSignal() + + @pyqtProperty(bool, notify = heatedBedChanged) + def hasHeatedBed(self): + global_container_stack = Application.getInstance().getGlobalContainerStack() + return global_container_stack.getProperty("machine_heated_bed", "value") \ No newline at end of file diff --git a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml index 43e1fa8f69..86429ed119 100644 --- a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml +++ b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml @@ -17,6 +17,8 @@ Cura.MachineAction property int rightRow: checkupMachineAction.width * 0.60 property bool heatupHotendStarted: false property bool heatupBedStarted: false + property bool usbConnected: Cura.USBPrinterManager.connectedPrinterList.rowCount() > 0 + UM.I18nCatalog { id: catalog; name:"cura"} Label { @@ -96,7 +98,7 @@ Cura.MachineAction anchors.left: connectionLabel.right anchors.top: parent.top wrapMode: Text.WordWrap - text: Cura.USBPrinterManager.connectedPrinterList.rowCount() > 0 ? catalog.i18nc("@info:status","Connected"): catalog.i18nc("@info:status","Not connected") + text: checkupMachineAction.usbConnected ? catalog.i18nc("@info:status","Connected"): catalog.i18nc("@info:status","Not connected") } ////////////////////////////////////////////////////////// Label @@ -107,6 +109,7 @@ Cura.MachineAction anchors.top: connectionLabel.bottom wrapMode: Text.WordWrap text: catalog.i18nc("@label","Min endstop X: ") + visible: checkupMachineAction.usbConnected } Label { @@ -116,6 +119,7 @@ Cura.MachineAction anchors.top: connectionLabel.bottom wrapMode: Text.WordWrap text: manager.xMinEndstopTestCompleted ? catalog.i18nc("@info:status","Works") : catalog.i18nc("@info:status","Not checked") + visible: checkupMachineAction.usbConnected } ////////////////////////////////////////////////////////////// Label @@ -126,6 +130,7 @@ Cura.MachineAction anchors.top: endstopXLabel.bottom wrapMode: Text.WordWrap text: catalog.i18nc("@label","Min endstop Y: ") + visible: checkupMachineAction.usbConnected } Label { @@ -135,6 +140,7 @@ Cura.MachineAction anchors.top: endstopXLabel.bottom wrapMode: Text.WordWrap text: manager.yMinEndstopTestCompleted ? catalog.i18nc("@info:status","Works") : catalog.i18nc("@info:status","Not checked") + visible: checkupMachineAction.usbConnected } ///////////////////////////////////////////////////////////////////// Label @@ -145,6 +151,7 @@ Cura.MachineAction anchors.top: endstopYLabel.bottom wrapMode: Text.WordWrap text: catalog.i18nc("@label","Min endstop Z: ") + visible: checkupMachineAction.usbConnected } Label { @@ -154,6 +161,7 @@ Cura.MachineAction anchors.top: endstopYLabel.bottom wrapMode: Text.WordWrap text: manager.zMinEndstopTestCompleted ? catalog.i18nc("@info:status","Works") : catalog.i18nc("@info:status","Not checked") + visible: checkupMachineAction.usbConnected } //////////////////////////////////////////////////////////// Label @@ -165,6 +173,7 @@ Cura.MachineAction anchors.top: endstopZLabel.bottom wrapMode: Text.WordWrap text: catalog.i18nc("@label","Nozzle temperature check: ") + visible: checkupMachineAction.usbConnected } Label { @@ -174,6 +183,7 @@ Cura.MachineAction anchors.left: nozzleTempLabel.right wrapMode: Text.WordWrap text: catalog.i18nc("@info:status","Not checked") + visible: checkupMachineAction.usbConnected } Item { @@ -183,6 +193,7 @@ Cura.MachineAction anchors.top: nozzleTempLabel.top anchors.left: bedTempStatus.right anchors.leftMargin: UM.Theme.getSize("default_margin").width/2 + visible: checkupMachineAction.usbConnected Button { text: checkupMachineAction.heatupHotendStarted ? catalog.i18nc("@action:button","Stop Heating") : catalog.i18nc("@action:button","Start Heating") @@ -211,6 +222,7 @@ Cura.MachineAction wrapMode: Text.WordWrap text: manager.hotendTemperature + "°C" font.bold: true + visible: checkupMachineAction.usbConnected } ///////////////////////////////////////////////////////////////////////////// Label @@ -222,6 +234,7 @@ Cura.MachineAction anchors.top: nozzleTempLabel.bottom wrapMode: Text.WordWrap text: catalog.i18nc("@label","Bed temperature check:") + visible: checkupMachineAction.usbConnected && manager.hasHeatedBed } Label @@ -232,6 +245,7 @@ Cura.MachineAction anchors.left: bedTempLabel.right wrapMode: Text.WordWrap text: manager.bedTestCompleted ? catalog.i18nc("@info:status","Not checked"): catalog.i18nc("@info:status","Checked") + visible: checkupMachineAction.usbConnected && manager.hasHeatedBed } Item { @@ -241,6 +255,7 @@ Cura.MachineAction anchors.top: bedTempLabel.top anchors.left: bedTempStatus.right anchors.leftMargin: UM.Theme.getSize("default_margin").width/2 + visible: checkupMachineAction.usbConnected && manager.hasHeatedBed Button { text: checkupMachineAction.heatupBedStarted ?catalog.i18nc("@action:button","Stop Heating") : catalog.i18nc("@action:button","Start Heating") @@ -268,6 +283,7 @@ Cura.MachineAction wrapMode: Text.WordWrap text: manager.bedTemperature + "°C" font.bold: true + visible: checkupMachineAction.usbConnected && manager.hasHeatedBed } Label { From c36af13dafa600c588eeda126b1aa3eae1fc4f31 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 7 Jul 2016 18:25:20 +0200 Subject: [PATCH 187/236] Fix PerObjectSettings, which were broken due to the material editing merge CURA-342 --- .../PerObjectSettingsTool/PerObjectSettingVisibilityHandler.py | 2 +- plugins/PerObjectSettingsTool/PerObjectSettingsTool.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingVisibilityHandler.py b/plugins/PerObjectSettingsTool/PerObjectSettingVisibilityHandler.py index 57bff4f0de..181c3dea18 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingVisibilityHandler.py +++ b/plugins/PerObjectSettingsTool/PerObjectSettingVisibilityHandler.py @@ -5,7 +5,7 @@ from UM.Logger import Logger import UM.Settings.Models -from cura.SettingOverrideDecorator import SettingOverrideDecorator +from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator ## The per object setting visibility handler ensures that only setting defintions that have a matching instance Container # are returned as visible. diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py b/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py index f80d9cbf9c..8b51852c15 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsTool.py @@ -5,7 +5,7 @@ from UM.Tool import Tool from UM.Scene.Selection import Selection from UM.Application import Application from UM.Preferences import Preferences -from cura.SettingOverrideDecorator import SettingOverrideDecorator +from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator ## This tool allows the user to add & change settings per node in the scene. # The settings per object are kept in a ContainerStack, which is linked to a node by decorator. From d6c25fcd10add0351a9663374e5dab96bc2cfdf2 Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Thu, 7 Jul 2016 21:04:23 +0200 Subject: [PATCH 188/236] JSON feat: infill mesh (CURA-833) --- resources/definitions/fdmprinter.def.json | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 50c3676540..0dd44a0647 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -3042,6 +3042,17 @@ "settable_per_extruder": false, "settable_per_meshgroup": false }, + "infill_mesh": + { + "label": "Infill Mesh", + "description": "Use this mesh to modify the infill of other meshes with which it overlaps. Replaces infill regions of other meshes with regions for this mesh.", + "type": "bool", + "default_value": false, + "settable_per_mesh": true, + "settable_per_extruder": false, + "settable_per_meshgroup": false, + "settable_globally": false + }, "magic_mesh_surface_mode": { "label": "Surface Mode", From 4349f6c75b7db8be90ec6bccd00a2b2a4035cbbb Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Thu, 7 Jul 2016 21:18:25 +0200 Subject: [PATCH 189/236] fix: make infill_mesh visible by default because it's the only setting which is only visible in the per-object settings menu which is not normally visible (CURA-833) Because it should never show up in the normal global/extruder settings tab, people might forget about this setting. It's one of the reasons per-object settings is awesome ^^. --- cura/CuraApplication.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 88ced6f68e..de933ecb88 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -233,6 +233,7 @@ class CuraApplication(QtApplication): meshfix blackmagic print_sequence + infill_mesh dual experimental """.replace("\n", ";").replace(" ", "")) From 91082404be25681aeb53f3600615f6d77586a1c8 Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Thu, 7 Jul 2016 21:18:46 +0200 Subject: [PATCH 190/236] JSON feat: infill_mesh_order (CURA-833) --- resources/definitions/fdmprinter.def.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 0dd44a0647..a2ac13e0e2 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -3053,6 +3053,19 @@ "settable_per_meshgroup": false, "settable_globally": false }, + "infill_mesh_order": + { + "label": "Infill Mesh Order", + "description": "Determines which infill mesh is inside the infill of another infill mesh. An infill mesh with a higher order will modify the infill of infill meshes with lower order and normal meshes.", + "default_value": 0, + "minimum_value_warning": "0", + "maximum_value_warning": "50", + "type": "int", + "settable_per_mesh": true, + "settable_per_extruder": false, + "settable_per_meshgroup": false, + "settable_globally": false + }, "magic_mesh_surface_mode": { "label": "Surface Mode", From 9c492342cb170385cc8c909d005b509e6970b04a Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 7 Jul 2016 22:17:35 +0200 Subject: [PATCH 191/236] Show each material length individually CURA-1687 --- resources/qml/JobSpecs.qml | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index 0dec471a1c..b2ba575761 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -24,7 +24,7 @@ Rectangle { UM.I18nCatalog { id: catalog; name:"cura"} property variant printDuration: PrintInformation.currentPrintTime - property real printMaterialAmount: PrintInformation.materialAmounts[0] + property variant printMaterialAmounts: PrintInformation.materialAmounts height: childrenRect.height color: "transparent" @@ -192,7 +192,20 @@ Rectangle { anchors.verticalCenter: parent.verticalCenter font: UM.Theme.getFont("small") color: UM.Theme.getColor("text_subtext") - text: catalog.i18nc("@label", "%1 m").arg(base.printMaterialAmount > 0 ? base.printMaterialAmount : 0) + text: + { + var material_lengths; + if(base.printMaterialAmounts) { + material_lengths = "" + for(var index = 0; index < base.printMaterialAmounts.length; index++) { + material_lengths += base.printMaterialAmounts[index].toFixed(2).toString() + " + " + } + material_lengths = material_lengths.substr(0, material_lengths.length - 3) + } else { + material_lengths = "0.00" + } + return catalog.i18nc("@label", "%1 m").arg(material_lengths) + } } } } From 08f2143790b6e2e44a4a4c3b072787c08c9822d1 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 7 Jul 2016 23:58:14 +0200 Subject: [PATCH 192/236] Simplify showing each material length individually CURA-1687 --- resources/qml/JobSpecs.qml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index b2ba575761..db58b5ee4e 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -194,17 +194,15 @@ Rectangle { color: UM.Theme.getColor("text_subtext") text: { - var material_lengths; + var amounts = []; if(base.printMaterialAmounts) { - material_lengths = "" for(var index = 0; index < base.printMaterialAmounts.length; index++) { - material_lengths += base.printMaterialAmounts[index].toFixed(2).toString() + " + " + amounts.push(base.printMaterialAmounts[index].toFixed(2)); } - material_lengths = material_lengths.substr(0, material_lengths.length - 3) } else { - material_lengths = "0.00" + amounts = ["0.00"]; } - return catalog.i18nc("@label", "%1 m").arg(material_lengths) + return catalog.i18nc("@label", "%1 m").arg(amounts.join(" + ")); } } } From 6cdab33eb6a77e13e15bd731c1407944a8fb2493 Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Thu, 7 Jul 2016 23:59:19 +0200 Subject: [PATCH 193/236] JSON fix: negative angles for conical overhang are possible (CURA-1412) --- resources/definitions/fdmprinter.def.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index d810a2dbcb..bf39807ba3 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -3160,7 +3160,8 @@ "description": "The maximum angle of overhangs after the they have been made printable. At a value of 0° all overhangs are replaced by a piece of model connected to the build plate, 90° will not change the model in any way.", "unit": "°", "type": "float", - "minimum_value": "0", + "minimum_value": "-89", + "minimum_value_warning": "0", "maximum_value": "89", "default_value": 50, "enabled": "conical_overhang_enabled" From 2f3ffe2a18341391a0d1166962573b68d94ed339 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 8 Jul 2016 11:22:07 +0200 Subject: [PATCH 194/236] Fix tabs when automatically switching to the monitor tab (on print start) CURA-1036 --- resources/qml/Sidebar.qml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index ebf75e7a49..53a2375394 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -18,7 +18,12 @@ Rectangle Connections { target: Printer - onShowPrintMonitor: base.monitoringPrint = show + onShowPrintMonitor: + { + base.monitoringPrint = show; + showSettings.checked = !show; + showMonitor.checked = show; + } } // Is there an output device for this printer? @@ -83,6 +88,7 @@ Rectangle anchors.right: parent.right Button { + id: showSettings width: (parent.width - UM.Theme.getSize("default_margin").width) / 2 height: UM.Theme.getSize("sidebar_header").height onClicked: monitoringPrint = false @@ -95,6 +101,7 @@ Rectangle } Button { + id: showMonitor width: (parent.width - UM.Theme.getSize("default_margin").width) / 2 height: UM.Theme.getSize("sidebar_header").height onClicked: monitoringPrint = true From 6eb502730c0530f7f7e8905d07272a54338c2a14 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Wed, 6 Jul 2016 16:30:34 +0200 Subject: [PATCH 195/236] Add reusable messagebox based on QML MessageDialog Contributes to CURA-1730 and CURA-1850 --- cura/CuraApplication.py | 16 ++++++++++++++++ resources/qml/Cura.qml | 29 +++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index e34a537953..feaa700d48 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -47,6 +47,7 @@ import cura.Settings from PyQt5.QtCore import pyqtSlot, QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS from PyQt5.QtGui import QColor, QIcon +from PyQt5.QtWidgets import QMessageBox from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType import platform @@ -155,6 +156,8 @@ class CuraApplication(QtApplication): self._cura_actions = None self._started = False + self._message_box_callback = None + self._i18n_catalog = i18nCatalog("cura") self.getController().getScene().sceneChanged.connect(self.updatePlatformActivity) @@ -251,6 +254,19 @@ class CuraApplication(QtApplication): def _onEngineCreated(self): self._engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider()) + ## A reusable dialogbox + # + showMessageBox = pyqtSignal(str, str, str, str, int, int, arguments = ["title", "text", "informativeText", "detailedText", "buttons", "icon"]) + def messageBox(self, title, text, informativeText = "", detailedText = "", buttons = QMessageBox.Ok, icon = QMessageBox.NoIcon, callback = None): + self._message_box_callback = callback + self.showMessageBox.emit(title, text, informativeText, detailedText, buttons, icon) + + @pyqtSlot(int) + def messageBoxClosed(self, button): + if self._message_box_callback: + self._message_box_callback(button) + self._message_box_callback = None + showPrintMonitor = pyqtSignal(bool, arguments = ["show"]) ## Cura has multiple locations where instance containers need to be saved, so we need to handle this differently. diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index e805991df4..f70dffa3cc 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -683,6 +683,35 @@ UM.MainWindow } } + MessageDialog + { + id: messageDialog + modality: Qt.ApplicationModal + onAccepted: Printer.messageBoxClosed(clickedButton) + onApply: Printer.messageBoxClosed(clickedButton) + onDiscard: Printer.messageBoxClosed(clickedButton) + onHelp: Printer.messageBoxClosed(clickedButton) + onNo: Printer.messageBoxClosed(clickedButton) + onRejected: Printer.messageBoxClosed(clickedButton) + onReset: Printer.messageBoxClosed(clickedButton) + onYes: Printer.messageBoxClosed(clickedButton) + } + + Connections + { + target: Printer + onShowMessageBox: + { + messageDialog.title = title + messageDialog.text = text + messageDialog.informativeText = informativeText + messageDialog.detailedText = detailedText + messageDialog.standardButtons = buttons + messageDialog.icon = icon + messageDialog.visible = true + } + } + Connections { target: Cura.Actions.addMachine From 4a9de156cb7ef5fb73c1aec48dcc50043e3f97e2 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Wed, 6 Jul 2016 16:31:27 +0200 Subject: [PATCH 196/236] Ask the user about keeping current settings when switching quality profiles CURA-1730 --- cura/Settings/MachineManager.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index c217c7087a..773d65a7db 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -2,6 +2,7 @@ # Cura is released under the terms of the AGPLv3 or higher. from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal +from PyQt5.QtWidgets import QMessageBox from UM.Application import Application from UM.Preferences import Preferences @@ -461,10 +462,31 @@ class MachineManager(QObject): return old_quality = self._active_container_stack.findContainer({"type": "quality"}) - if old_quality: + if old_quality and old_quality != containers[0]: quality_index = self._active_container_stack.getContainerIndex(old_quality) + self._active_container_stack.replaceContainer(quality_index, containers[0]) + if self.hasUserSettings and Preferences.getInstance().getValue("cura/active_mode") == 1: + # Ask the user if the user profile should be cleared or not (discarding the current settings) + # In Simple Mode we assume the user always wants to keep the (limited) current settings + details = catalog.i18nc("@label", "You made changes to the following setting(s):") + user_settings = self._active_container_stack.getTop().findInstances(**{}) + for setting in user_settings: + details = details + "\n " + setting.definition.label + + Application.getInstance().messageBox(catalog.i18nc("@window:title", "Switched profiles"), catalog.i18nc("@label", "Do you want to transfer your changed settings to this profile?"), + catalog.i18nc("@label", "If you transfer your settings they will override settings in the profile."), details, + buttons = QMessageBox.Yes + QMessageBox.No, icon = QMessageBox.Question, callback = self._keepUserSettingsDialogCallback) + + def _keepUserSettingsDialogCallback(self, button): + if button == QMessageBox.Yes: + # Yes, keep the settings in the user profile with this profile + pass + elif button == QMessageBox.No: + # No, discard the settings in the user profile + self.clearUserSettings() + @pyqtProperty(str, notify = activeVariantChanged) def activeVariantName(self): if self._active_container_stack: From 475277069f8facc8641b688feb52292d82fe4501 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 7 Jul 2016 08:14:49 +0200 Subject: [PATCH 197/236] Add optional arguments to reusable messagebox Contributes to CURA-1850 --- cura/CuraApplication.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index feaa700d48..0d31155590 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -157,6 +157,7 @@ class CuraApplication(QtApplication): self._started = False self._message_box_callback = None + self._message_box_callback_arguments = [] self._i18n_catalog = i18nCatalog("cura") @@ -257,15 +258,17 @@ class CuraApplication(QtApplication): ## A reusable dialogbox # showMessageBox = pyqtSignal(str, str, str, str, int, int, arguments = ["title", "text", "informativeText", "detailedText", "buttons", "icon"]) - def messageBox(self, title, text, informativeText = "", detailedText = "", buttons = QMessageBox.Ok, icon = QMessageBox.NoIcon, callback = None): + def messageBox(self, title, text, informativeText = "", detailedText = "", buttons = QMessageBox.Ok, icon = QMessageBox.NoIcon, callback = None, callback_arguments = []): self._message_box_callback = callback + self._message_box_callback_arguments = callback_arguments self.showMessageBox.emit(title, text, informativeText, detailedText, buttons, icon) @pyqtSlot(int) def messageBoxClosed(self, button): if self._message_box_callback: - self._message_box_callback(button) + self._message_box_callback(button, *self._message_box_callback_arguments) self._message_box_callback = None + self._message_box_callback_arguments = [] showPrintMonitor = pyqtSignal(bool, arguments = ["show"]) From c129b4b8b5e94f7f9f8aca1b1a926ec460d42edb Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 7 Jul 2016 08:16:02 +0200 Subject: [PATCH 198/236] Let the user opt out of changing to the material/nozzle reported by the printer CURA-1850 --- cura/Settings/MachineManager.py | 66 +++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 7 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 773d65a7db..15b5265850 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -98,12 +98,38 @@ class MachineManager(QObject): containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "variant", definition = self._global_container_stack.getBottom().getId(), name = hotend_id) if containers: - Logger.log("d", "Setting hotend variant of hotend %d to %s" % (index, containers[0].getId())) - ExtruderManager.ExtruderManager.getInstance().setActiveExtruderIndex(index) - self.setActiveVariant(containers[0].getId()) + old_index = extruder_manager.activeExtruderIndex + if old_index != index: + extruder_manager.setActiveExtruderIndex(index) + else: + old_index = None + + if self.activeVariantId != containers[0].getId(): + Application.getInstance().messageBox(catalog.i18nc("@window:title", "Changes on the Printer"), catalog.i18nc("@label", "Do you want to change the hotend to match the hotend in your printer?"), + catalog.i18nc("@label", "The hotend on your printer was changed. For best results always slice for the hotend that is inserted in your printer."), + buttons = QMessageBox.Yes + QMessageBox.No, icon = QMessageBox.Question, callback = self._hotendChangedDialogCallback, callback_arguments = [index, containers[0].getId()]) + if old_index is not None: + extruder_manager.setActiveExtruderIndex(old_index) + + else: + Logger.log("w", "No variant found for printer definition %s with id %s" % (definition_id, variant_id)) + + def _hotendChangedDialogCallback(self, button, index, hotend_id): + Logger.log("d", "Setting hotend variant of hotend %d to %s" % (index, containers[0].getId())) + + extruder_manager = ExtruderManager.ExtruderManager.getInstance() + old_index = extruder_manager.activeExtruderIndex + if old_index != index: + extruder_manager.setActiveExtruderIndex(index) + else: + old_index = None + + self.setActiveVariant(containers[0].getId()) + + if old_index is not None: + extruder_manager.setActiveExtruderIndex(old_index) def _onMaterialIdChanged(self, index, material_id): - # TODO: fix this if not self._global_container_stack: return @@ -113,12 +139,38 @@ class MachineManager(QObject): containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "material", definition = definition_id, GUID = material_id) if containers: - Logger.log("d", "Setting material of hotend %d to %s" % (index, containers[0].getId())) - ExtruderManager.ExtruderManager.getInstance().setActiveExtruderIndex(index) - self.setActiveMaterial(containers[0].getId()) + extruder_manager = ExtruderManager.ExtruderManager.getInstance() + old_index = extruder_manager.activeExtruderIndex + if old_index != index: + extruder_manager.setActiveExtruderIndex(index) + else: + old_index = None + + if self.activeMaterialId != containers[0].getId(): + Application.getInstance().messageBox(catalog.i18nc("@window:title", "Changes on the Printer"), catalog.i18nc("@label", "Do you want to change the material to match the material in your printer?"), + catalog.i18nc("@label", "The material on your printer was changed. For best results always slice for the material that is inserted in your printer."), + buttons = QMessageBox.Yes + QMessageBox.No, icon = QMessageBox.Question, callback = self._materialIdChangedDialogCallback, callback_arguments = [index, containers[0].getId()]) + if old_index is not None: + extruder_manager.setActiveExtruderIndex(old_index) + else: Logger.log("w", "No material definition found for printer definition %s and GUID %s" % (definition_id, material_id)) + def _materialIdChangedDialogCallback(self, button, index, material_id): + Logger.log("d", "Setting material of hotend %d to %s" % (index, material_id)) + + extruder_manager = ExtruderManager.ExtruderManager.getInstance() + old_index = extruder_manager.activeExtruderIndex + if old_index != index: + extruder_manager.setActiveExtruderIndex(index) + else: + old_index = None + + self.setActiveMaterial(material_id) + + if old_index is not None: + extruder_manager.setActiveExtruderIndex(old_index) + def _onGlobalPropertyChanged(self, key, property_name): if property_name == "value": From bf529c642572e1965216f2ca707fd6242ffeec36 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 7 Jul 2016 11:46:07 +0200 Subject: [PATCH 199/236] Apply a flood control on messages about materials/hotends changed on the printer CURA-1850 --- cura/Settings/MachineManager.py | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 15b5265850..1c8734c80d 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -17,6 +17,8 @@ from . import ExtruderManager from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") +import time + class MachineManager(QObject): def __init__(self, parent = None): super().__init__(parent) @@ -60,6 +62,10 @@ class MachineManager(QObject): self.setActiveMachine(active_machine_id) pass + self._auto_change_material_hotend_flood_window = 10 + self._auto_change_material_hotend_flood_time = 0 + self._auto_change_material_hotend_flood_last_choice = None + globalContainerChanged = pyqtSignal() activeMaterialChanged = pyqtSignal() activeVariantChanged = pyqtSignal() @@ -98,6 +104,7 @@ class MachineManager(QObject): containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "variant", definition = self._global_container_stack.getBottom().getId(), name = hotend_id) if containers: + extruder_manager = ExtruderManager.ExtruderManager.getInstance() old_index = extruder_manager.activeExtruderIndex if old_index != index: extruder_manager.setActiveExtruderIndex(index) @@ -105,9 +112,12 @@ class MachineManager(QObject): old_index = None if self.activeVariantId != containers[0].getId(): - Application.getInstance().messageBox(catalog.i18nc("@window:title", "Changes on the Printer"), catalog.i18nc("@label", "Do you want to change the hotend to match the hotend in your printer?"), - catalog.i18nc("@label", "The hotend on your printer was changed. For best results always slice for the hotend that is inserted in your printer."), - buttons = QMessageBox.Yes + QMessageBox.No, icon = QMessageBox.Question, callback = self._hotendChangedDialogCallback, callback_arguments = [index, containers[0].getId()]) + if time.time() - self._auto_change_material_hotend_flood_time > self._auto_change_material_hotend_flood_window: + Application.getInstance().messageBox(catalog.i18nc("@window:title", "Changes on the Printer"), catalog.i18nc("@label", "Do you want to change the hotend to match the hotend in your printer?"), + catalog.i18nc("@label", "The hotend on your printer was changed. For best results always slice for the hotend that is inserted in your printer."), + buttons = QMessageBox.Yes + QMessageBox.No, icon = QMessageBox.Question, callback = self._hotendChangedDialogCallback, callback_arguments = [index, containers[0].getId()]) + else: + self._hotendChangedDialogCallback(self._auto_change_material_hotend_flood_last_choice, index, containers[0].getId()) if old_index is not None: extruder_manager.setActiveExtruderIndex(old_index) @@ -115,6 +125,9 @@ class MachineManager(QObject): Logger.log("w", "No variant found for printer definition %s with id %s" % (definition_id, variant_id)) def _hotendChangedDialogCallback(self, button, index, hotend_id): + self._auto_change_material_hotend_flood_time = time.time() + self._auto_change_material_hotend_flood_last_choice = button + Logger.log("d", "Setting hotend variant of hotend %d to %s" % (index, containers[0].getId())) extruder_manager = ExtruderManager.ExtruderManager.getInstance() @@ -147,9 +160,12 @@ class MachineManager(QObject): old_index = None if self.activeMaterialId != containers[0].getId(): - Application.getInstance().messageBox(catalog.i18nc("@window:title", "Changes on the Printer"), catalog.i18nc("@label", "Do you want to change the material to match the material in your printer?"), - catalog.i18nc("@label", "The material on your printer was changed. For best results always slice for the material that is inserted in your printer."), - buttons = QMessageBox.Yes + QMessageBox.No, icon = QMessageBox.Question, callback = self._materialIdChangedDialogCallback, callback_arguments = [index, containers[0].getId()]) + if time.time() - self._auto_change_material_hotend_flood_time > self._auto_change_material_hotend_flood_window: + Application.getInstance().messageBox(catalog.i18nc("@window:title", "Changes on the Printer"), catalog.i18nc("@label", "Do you want to change the material to match the material in your printer?"), + catalog.i18nc("@label", "The material on your printer was changed. For best results always slice for the material that is inserted in your printer."), + buttons = QMessageBox.Yes + QMessageBox.No, icon = QMessageBox.Question, callback = self._materialIdChangedDialogCallback, callback_arguments = [index, containers[0].getId()]) + else: + self._materialIdChangedDialogCallback(self._auto_change_material_hotend_flood_last_choice, index, containers[0].getId()) if old_index is not None: extruder_manager.setActiveExtruderIndex(old_index) @@ -157,6 +173,9 @@ class MachineManager(QObject): Logger.log("w", "No material definition found for printer definition %s and GUID %s" % (definition_id, material_id)) def _materialIdChangedDialogCallback(self, button, index, material_id): + self._auto_change_material_hotend_flood_time = time.time() + self._auto_change_material_hotend_flood_last_choice = button + Logger.log("d", "Setting material of hotend %d to %s" % (index, material_id)) extruder_manager = ExtruderManager.ExtruderManager.getInstance() From fc74b538771514fb0525031b110a88ca04c7fc28 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 8 Jul 2016 11:44:20 +0200 Subject: [PATCH 200/236] Add documentation CURA-1850 --- cura/Settings/MachineManager.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 1c8734c80d..c55baab055 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -62,9 +62,9 @@ class MachineManager(QObject): self.setActiveMachine(active_machine_id) pass - self._auto_change_material_hotend_flood_window = 10 - self._auto_change_material_hotend_flood_time = 0 - self._auto_change_material_hotend_flood_last_choice = None + self._auto_change_material_hotend_flood_window = 10 # The minimum number of seconds between asking if the material or hotend on the machine should be used + self._auto_change_material_hotend_flood_time = 0 # The last timestamp (in seconds) when the user was asked about changing the material or hotend to whatis loaded on the machine + self._auto_change_material_hotend_flood_last_choice = None # The last choice that was made, so we can apply that choice again globalContainerChanged = pyqtSignal() activeMaterialChanged = pyqtSignal() From 21ae73fca01503180417784698a96a9a102ef692 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 8 Jul 2016 11:53:33 +0200 Subject: [PATCH 201/236] Capitalise Euclidean dimension names X, Y and Z. Contributes to issue CURA-1816. --- resources/definitions/fdmprinter.def.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index bf39807ba3..79d08749cb 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -319,7 +319,7 @@ "extruder_prime_pos_x": { "label": "Extruder Prime X Position", - "description": "The x coordinate of the position where the nozzle primes at the start of printing.", + "description": "The X coordinate of the position where the nozzle primes at the start of printing.", "type": "float", "unit": "mm", "default_value": 0, @@ -331,7 +331,7 @@ "extruder_prime_pos_y": { "label": "Extruder Prime Y Position", - "description": "The y coordinate of the position where the nozzle primes at the start of printing.", + "description": "The Y coordinate of the position where the nozzle primes at the start of printing.", "type": "float", "unit": "mm", "default_value": 0, @@ -343,7 +343,7 @@ "extruder_prime_pos_z": { "label": "Extruder Prime Z Position", - "description": "The z coordinate of the position where the nozzle primes at the start of printing.", + "description": "The Z coordinate of the position where the nozzle primes at the start of printing.", "type": "float", "unit": "mm", "default_value": 0, From 27d47b5c9c5301e8db5569025634f57933b8a3bf Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 8 Jul 2016 13:22:40 +0200 Subject: [PATCH 202/236] Fixes some issues due to merge --- cura/Settings/MachineManager.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index c55baab055..e0158f2ac4 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -104,7 +104,7 @@ class MachineManager(QObject): containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "variant", definition = self._global_container_stack.getBottom().getId(), name = hotend_id) if containers: - extruder_manager = ExtruderManager.ExtruderManager.getInstance() + extruder_manager = ExtruderManager.getInstance() old_index = extruder_manager.activeExtruderIndex if old_index != index: extruder_manager.setActiveExtruderIndex(index) @@ -122,22 +122,22 @@ class MachineManager(QObject): extruder_manager.setActiveExtruderIndex(old_index) else: - Logger.log("w", "No variant found for printer definition %s with id %s" % (definition_id, variant_id)) + Logger.log("w", "No variant found for printer definition %s with id %s" % (self._global_container_stack.getBottom().getId(), hotend_id)) def _hotendChangedDialogCallback(self, button, index, hotend_id): self._auto_change_material_hotend_flood_time = time.time() self._auto_change_material_hotend_flood_last_choice = button - Logger.log("d", "Setting hotend variant of hotend %d to %s" % (index, containers[0].getId())) + Logger.log("d", "Setting hotend variant of hotend %d to %s" % (index, hotend_id)) - extruder_manager = ExtruderManager.ExtruderManager.getInstance() + extruder_manager = ExtruderManager.getInstance() old_index = extruder_manager.activeExtruderIndex if old_index != index: extruder_manager.setActiveExtruderIndex(index) else: old_index = None - self.setActiveVariant(containers[0].getId()) + self.setActiveVariant(hotend_id) if old_index is not None: extruder_manager.setActiveExtruderIndex(old_index) @@ -152,7 +152,7 @@ class MachineManager(QObject): containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(type = "material", definition = definition_id, GUID = material_id) if containers: - extruder_manager = ExtruderManager.ExtruderManager.getInstance() + extruder_manager = ExtruderManager.getInstance() old_index = extruder_manager.activeExtruderIndex if old_index != index: extruder_manager.setActiveExtruderIndex(index) @@ -178,7 +178,7 @@ class MachineManager(QObject): Logger.log("d", "Setting material of hotend %d to %s" % (index, material_id)) - extruder_manager = ExtruderManager.ExtruderManager.getInstance() + extruder_manager = ExtruderManager.getInstance() old_index = extruder_manager.activeExtruderIndex if old_index != index: extruder_manager.setActiveExtruderIndex(index) @@ -513,7 +513,6 @@ class MachineManager(QObject): containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = variant_id) if not containers or not self._active_container_stack: return - old_variant = self._active_container_stack.findContainer({"type": "variant"}) old_material = self._active_container_stack.findContainer({"type": "material"}) if old_variant: @@ -523,7 +522,6 @@ class MachineManager(QObject): preferred_material = None if old_material: preferred_material_name = old_material.getName() - self.setActiveMaterial(self._updateMaterialContainer(self._global_container_stack.getBottom(), containers[0], preferred_material_name).id) @pyqtSlot(str) From 6e7c263557a2cab1bbad19a9f564ad85597c0828 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 8 Jul 2016 13:31:54 +0200 Subject: [PATCH 203/236] Improved logging --- cura/Settings/MachineManager.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index e0158f2ac4..89703ace6c 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -507,6 +507,8 @@ class MachineManager(QObject): preferred_quality_name = old_quality.getName() self.setActiveQuality(self._updateQualityContainer(self._global_container_stack.getBottom(), containers[0], preferred_quality_name).id) + else: + Logger.log("w", "While trying to set the active material, no material was found to replace.") @pyqtSlot(str) def setActiveVariant(self, variant_id): @@ -523,6 +525,8 @@ class MachineManager(QObject): if old_material: preferred_material_name = old_material.getName() self.setActiveMaterial(self._updateMaterialContainer(self._global_container_stack.getBottom(), containers[0], preferred_material_name).id) + else: + Logger.log("w", "While trying to set the active variant, no variant was found to replace.") @pyqtSlot(str) def setActiveQuality(self, quality_id): @@ -547,6 +551,8 @@ class MachineManager(QObject): Application.getInstance().messageBox(catalog.i18nc("@window:title", "Switched profiles"), catalog.i18nc("@label", "Do you want to transfer your changed settings to this profile?"), catalog.i18nc("@label", "If you transfer your settings they will override settings in the profile."), details, buttons = QMessageBox.Yes + QMessageBox.No, icon = QMessageBox.Question, callback = self._keepUserSettingsDialogCallback) + else: + Logger.log("w", "While trying to set the active quality, no quality was found to replace.") def _keepUserSettingsDialogCallback(self, button): if button == QMessageBox.Yes: From 31f97e0f61a2e435dba546f5d67b33e16c5f2829 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 8 Jul 2016 13:33:09 +0200 Subject: [PATCH 204/236] Another set of fixes due to merge issues --- cura/Settings/MachineManager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 89703ace6c..8c13458679 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -417,7 +417,7 @@ class MachineManager(QObject): if containers: new_name = self._createUniqueName("quality", "", containers[0].getName(), catalog.i18nc("@label", "Custom profile")) - new_container = InstanceContainer("") + new_container = UM.Settings.InstanceContainer("") ## Copy all values new_container.deserialize(containers[0].serialize()) @@ -444,7 +444,7 @@ class MachineManager(QObject): # As we also want the id of the container to be changed (so that profile name is the name of the file # on disk. We need to create a new instance and remove it (so the old file of the container is removed) # If we don't do that, we might get duplicates & other weird issues. - new_container = InstanceContainer("") + new_container = UM.Settings.InstanceContainer("") new_container.deserialize(containers[0].serialize()) # Actually set the name From b6e4bcb58f1118550b579e9336b9eb6b4cae3b11 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 8 Jul 2016 13:37:09 +0200 Subject: [PATCH 205/236] Updated documentation CURA-537 --- cura/Settings/MachineManager.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 8c13458679..64eac750e9 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -599,7 +599,7 @@ class MachineManager(QObject): @pyqtSlot(str) def removeMachine(self, machine_id): - # If the machine that is being removed is the currently active machine, set another machine as the active machine + # If the machine that is being removed is the currently active machine, set another machine as the active machine. activate_new_machine = (self._global_container_stack and self._global_container_stack.getId() == machine_id) current_settings_id = machine_id + "_current_settings" @@ -628,6 +628,8 @@ class MachineManager(QObject): return False + ## Property to indicate if a machine has "specialized" material profiles. + # Some machines have their own material profiles that "override" the default catch all profiles. @pyqtProperty(bool, notify = globalContainerChanged) def filterMaterialsByMachine(self): if self._global_container_stack: @@ -635,13 +637,17 @@ class MachineManager(QObject): return False + ## Property to indicate if a machine has "specialized" quality profiles. + # Some machines have their own quality profiles that "override" the default catch all profiles. @pyqtProperty(bool, notify = globalContainerChanged) def filterQualityByMachine(self): if self._global_container_stack: return bool(self._global_container_stack.getMetaDataEntry("has_machine_quality", False)) - return False + ## Get the Definition ID of a machine (specified by ID) + # \param machine_id string machine id to get the definition ID of + # \returns DefinitionID (string) if found, None otherwise @pyqtSlot(str, result = str) def getDefinitionByMachineId(self, machine_id): containers = UM.Settings.ContainerRegistry.getInstance().findContainerStacks(id=machine_id) From 1ced206c5d04c6104f0b1471ad5eba25d4b661d4 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 8 Jul 2016 14:12:44 +0200 Subject: [PATCH 206/236] Codestyle --- plugins/3MFReader/ThreeMFReader.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py index 84f71cf253..da8d9551b5 100644 --- a/plugins/3MFReader/ThreeMFReader.py +++ b/plugins/3MFReader/ThreeMFReader.py @@ -53,7 +53,6 @@ class ThreeMFReader(MeshReader): triangles = entry.findall(".//3mf:triangle", self._namespaces) mesh_builder.reserveFaceCount(len(triangles)) - #for triangle in object.mesh.triangles.triangle: for triangle in triangles: v1 = int(triangle.get("v1")) v2 = int(triangle.get("v2")) @@ -67,9 +66,9 @@ class ThreeMFReader(MeshReader): # Rotate the model; We use a different coordinate frame. rotation = Matrix() - rotation.setByRotationAxis(-0.5 * math.pi, Vector(1,0,0)) + rotation.setByRotationAxis(-0.5 * math.pi, Vector(1, 0, 0)) - #TODO: We currently do not check for normals and simply recalculate them. + # TODO: We currently do not check for normals and simply recalculate them. mesh_builder.calculateNormals() node.setMeshData(mesh_builder.build().getTransformed(rotation)) @@ -108,11 +107,11 @@ class ThreeMFReader(MeshReader): Job.yieldThread() - #If there is more then one object, group them. + # If there is more then one object, group them. if len(objects) > 1: group_decorator = GroupDecorator() result.addDecorator(group_decorator) except Exception as e: - Logger.log("e" ,"exception occured in 3mf reader: %s" , e) + Logger.log("e", "exception occured in 3mf reader: %s", e) return result From 19933cb4e79b9a35dac5847ca17478f1dc5c65fd Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 8 Jul 2016 15:17:28 +0200 Subject: [PATCH 207/236] Improved robustness of Platform physics by checking if the hulls are calculated before trying to intersect them. fixes CURA-1827 --- cura/PlatformPhysics.py | 22 ++++++++++------------ plugins/3MFReader/ThreeMFReader.py | 2 +- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py index e4844baf31..0c9c933899 100644 --- a/cura/PlatformPhysics.py +++ b/cura/PlatformPhysics.py @@ -63,9 +63,6 @@ class PlatformPhysics: elif bbox.bottom < z_offset: move_vector = move_vector.set(y=(-bbox.bottom) - z_offset) - #if not Float.fuzzyCompare(bbox.bottom, 0.0): - # pass#move_vector.setY(-bbox.bottom) - # If there is no convex hull for the node, start calculating it and continue. if not node.getDecorator(ConvexHullDecorator): node.addDecorator(ConvexHullDecorator()) @@ -78,24 +75,18 @@ class PlatformPhysics: if other_node is root or type(other_node) is not SceneNode or other_node is node: continue - # Ignore colissions of a group with it's own children + # Ignore collisions of a group with it's own children if other_node in node.getAllChildren() or node in other_node.getAllChildren(): continue - # Ignore colissions within a group + # Ignore collisions within a group if other_node.getParent().callDecoration("isGroup") is not None or node.getParent().callDecoration("isGroup") is not None: continue - #if node.getParent().callDecoration("isGroup") is other_node.getParent().callDecoration("isGroup"): - # continue # Ignore nodes that do not have the right properties set. if not other_node.callDecoration("getConvexHull") or not other_node.getBoundingBox(): continue - # Check to see if the bounding boxes intersect. If not, we can ignore the node as there is no way the hull intersects. - #if node.getBoundingBox().intersectsBox(other_node.getBoundingBox()) == AxisAlignedBox.IntersectionResult.NoIntersection: - # continue - # Get the overlap distance for both convex hulls. If this returns None, there is no intersection. head_hull = node.callDecoration("getConvexHullHead") if head_hull: @@ -105,7 +96,14 @@ class PlatformPhysics: if other_head_hull: overlap = node.callDecoration("getConvexHull").intersectsPolygon(other_head_hull) else: - overlap = node.callDecoration("getConvexHull").intersectsPolygon(other_node.callDecoration("getConvexHull")) + own_convex_hull = node.callDecoration("getConvexHull") + other_convex_hull = other_node.callDecoration("getConvexHull") + if own_convex_hull and other_convex_hull: + overlap = own_convex_hull.intersectsPolygon(other_convex_hull) + else: + # This can happen in some cases if the object is not yet done with being loaded. + # Simply waiting for the next tick seems to resolve this correctly. + overlap = None if overlap is None: continue diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py index da8d9551b5..123e3ced14 100644 --- a/plugins/3MFReader/ThreeMFReader.py +++ b/plugins/3MFReader/ThreeMFReader.py @@ -70,7 +70,7 @@ class ThreeMFReader(MeshReader): # TODO: We currently do not check for normals and simply recalculate them. mesh_builder.calculateNormals() - + node.setMeshData(mesh_builder.build().getTransformed(rotation)) node.setSelectable(True) From 8a4ea5c710e9687d2286b91b176f3ccb36e44e76 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 8 Jul 2016 15:26:41 +0200 Subject: [PATCH 208/236] Added warnings to top/bottom thickness if it exceeds machine height CURA-1706 --- resources/definitions/fdmprinter.def.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 79d08749cb..43014a32fc 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -582,6 +582,7 @@ "default_value": 0.8, "minimum_value": "0", "minimum_value_warning": "0.6", + "maximum_value_warning": "machine_height", "type": "float", "settable_per_mesh": true, "children": @@ -593,7 +594,7 @@ "unit": "mm", "default_value": 0.8, "minimum_value": "0", - "maximum_value_warning": "100", + "maximum_value_warning": "machine_height", "type": "float", "value": "top_bottom_thickness", "settable_per_mesh": true, @@ -621,6 +622,7 @@ "minimum_value": "0", "type": "float", "value": "top_bottom_thickness", + "maximum_value_warning": "machine_height", "settable_per_mesh": true, "children": { From d2ad1ad153fb952963d7a2ea730c550609b1f433 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 8 Jul 2016 15:52:31 +0200 Subject: [PATCH 209/236] Added more logging --- cura/CuraApplication.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 0d31155590..7a12df320c 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -612,6 +612,7 @@ class CuraApplication(QtApplication): ## Delete all nodes containing mesh data in the scene. @pyqtSlot() def deleteAll(self): + Logger.log("i", "Clearing scene") if not self.getController().getToolsEnabled(): return @@ -635,6 +636,7 @@ class CuraApplication(QtApplication): ## Reset all translation on nodes with mesh data. @pyqtSlot() def resetAllTranslation(self): + Logger.log("i", "Resetting all scene translations") nodes = [] for node in DepthFirstIterator(self.getController().getScene().getRoot()): if type(node) is not SceneNode: @@ -657,6 +659,7 @@ class CuraApplication(QtApplication): ## Reset all transformations on nodes with mesh data. @pyqtSlot() def resetAll(self): + Logger.log("i", "Resetting all scene transformations") nodes = [] for node in DepthFirstIterator(self.getController().getScene().getRoot()): if type(node) is not SceneNode: @@ -680,6 +683,7 @@ class CuraApplication(QtApplication): ## Reload all mesh data on the screen from file. @pyqtSlot() def reloadAll(self): + Logger.log("i", "Reloading all loaded mesh data.") nodes = [] for node in DepthFirstIterator(self.getController().getScene().getRoot()): if type(node) is not SceneNode or not node.getMeshData(): From 1d2f7b3c3651000c8a2c936ba536474dd0e86bf8 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 8 Jul 2016 15:54:39 +0200 Subject: [PATCH 210/236] Added logging to indicate reload all failed CURA-1622 --- cura/CuraApplication.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 7a12df320c..c96bea352b 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -695,15 +695,14 @@ class CuraApplication(QtApplication): return for node in nodes: - if not node.getMeshData(): - continue - file_name = node.getMeshData().getFileName() if file_name: job = ReadMeshJob(file_name) job._node = node job.finished.connect(self._reloadMeshFinished) job.start() + else: + Logger.log("w", "Unable to reload data because we don't have a filename.") ## Get logging data of the backend engine # \returns \type{string} Logging data From 25d13969ee8642b0791554b49a41c36d1d4ad464 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 8 Jul 2016 15:58:33 +0200 Subject: [PATCH 211/236] Filename is now correctly set for 3mf CURA-1622 --- plugins/3MFReader/ThreeMFReader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py index 123e3ced14..2a51e442da 100644 --- a/plugins/3MFReader/ThreeMFReader.py +++ b/plugins/3MFReader/ThreeMFReader.py @@ -70,7 +70,7 @@ class ThreeMFReader(MeshReader): # TODO: We currently do not check for normals and simply recalculate them. mesh_builder.calculateNormals() - + mesh_builder.setFileName(file_name) node.setMeshData(mesh_builder.build().getTransformed(rotation)) node.setSelectable(True) From a854554d94ee7f072ef4ba952891b3e4d3adc55a Mon Sep 17 00:00:00 2001 From: Thomas-Karl Pietrowski Date: Fri, 8 Jul 2016 16:56:12 +0200 Subject: [PATCH 212/236] CURA-1831: Adding only the basic workaround for OSX --- plugins/SliceInfoPlugin/SliceInfo.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/plugins/SliceInfoPlugin/SliceInfo.py b/plugins/SliceInfoPlugin/SliceInfo.py index 487b077d2b..d3b93aacac 100644 --- a/plugins/SliceInfoPlugin/SliceInfo.py +++ b/plugins/SliceInfoPlugin/SliceInfo.py @@ -9,6 +9,7 @@ from UM.Scene.SceneNode import SceneNode from UM.Message import Message from UM.i18n import i18nCatalog from UM.Logger import Logger +from UM.Platform import Platform import collections import json @@ -18,6 +19,7 @@ import platform import math import urllib.request import urllib.parse +import ssl catalog = i18nCatalog("cura") @@ -111,8 +113,13 @@ class SliceInfo(Extension): binary_data = submitted_data.encode("utf-8") # Submit data + kwoptions = {"data" : binary_data, + "timeout" : 1 + } + if Platform.isOSX(): + kwoptions["context"] = ssl._create_unverified_context() try: - f = urllib.request.urlopen(self.info_url, data = binary_data, timeout = 1) + f = urllib.request.urlopen(self.info_url, **kwoptions) Logger.log("i", "Sent anonymous slice info to %s", self.info_url) f.close() except Exception as e: From f30bfafd4d468cb992f19da04fe2a33f870ea3ef Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 8 Jul 2016 13:58:56 +0200 Subject: [PATCH 213/236] Select a material for the extruder that is intended for the correct machine CURA-1878 --- cura/Settings/ExtruderManager.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index e937714d85..6c856a4605 100644 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -169,7 +169,16 @@ class ExtruderManager(QObject): material = materials[0] preferred_material_id = machine_definition.getMetaDataEntry("preferred_material") if preferred_material_id: - preferred_materials = container_registry.findInstanceContainers(id = preferred_material_id, type = "material") + search_criteria = { "type": "material", "id": preferred_material_id} + 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: material = preferred_materials[0] else: From c01b7091c8c4d84182adc74ee7e6f802ef826f05 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Sat, 9 Jul 2016 18:14:16 +0200 Subject: [PATCH 214/236] Wait until the nozzle temperature is reached before starting to print CURA-1794 --- resources/definitions/fdmprinter.def.json | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 43014a32fc..b3fb5edab4 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -67,9 +67,19 @@ "settable_per_extruder": false, "settable_per_meshgroup": false }, + "material_print_temp_wait": + { + "label": "Wait for nozzle heatup", + "description": "Whether to wait until the nozzle temperature is reached at the start.", + "default_value": true, + "type": "bool", + "settable_per_mesh": false, + "settable_per_extruder": false, + "settable_per_meshgroup": false + }, "material_print_temp_prepend": { - "label": "Wait for material heatup", + "label": "Include material temperatures", "description": "Whether to include nozzle temperature commands at the start of the gcode. When the start_gcode already contains nozzle temperature commands Cura frontend will automatically disable this setting.", "default_value": true, "type": "bool", From 6fbac2f7ee2431c833834ba10b7d67ab5c0b6a26 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 11 Jul 2016 00:49:18 +0200 Subject: [PATCH 215/236] Better fallbacks if MachineInstance missing profiles They now fallback to "empty" so that it can still construct a valid instance, albeit with a missing profile (such as material), if some profile is missing from the original machine instance file. Contributes to issue CURA-844. --- .../VersionUpgrade21to22/MachineInstance.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py index a7534dc862..197aa9fcb9 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -47,11 +47,11 @@ class MachineInstance: raise UM.VersionUpgrade.InvalidVersionException("The version of this machine instance is wrong. It must be 1.") self._type_name = config.get("general", "type") - self._variant_name = config.get("general", "variant", fallback = None) - self._name = config.get("general", "name") + self._variant_name = config.get("general", "variant", fallback = "empty") + self._name = config.get("general", "name", fallback = "") self._key = config.get("general", "key", fallback = None) - self._active_profile_name = config.get("general", "active_profile", fallback = None) - self._active_material_name = config.get("general", "material", fallback = None) + self._active_profile_name = config.get("general", "active_profile", fallback = "empty") + self._active_material_name = config.get("general", "material", fallback = "empty") self._machine_setting_overrides = {} for key, value in config["machine_settings"].items(): From f52f713694e55a88a7bb5fce1841eebb76313498 Mon Sep 17 00:00:00 2001 From: Simon Edwards Date: Fri, 8 Jul 2016 10:17:21 +0200 Subject: [PATCH 216/236] Tightended up the signals associated with the extruders. Bug fixed. CURA-1585 Profile dropdown options non functional. --- cura/Settings/ExtruderManager.py | 44 ++++++++++------- cura/Settings/ExtrudersModel.py | 81 +++++++++++++++++--------------- cura/Settings/MachineManager.py | 2 - 3 files changed, 70 insertions(+), 57 deletions(-) diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index 6c856a4605..5062fc93b4 100644 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -23,7 +23,8 @@ class ExtruderManager(QObject): super().__init__(parent) self._extruder_trains = { } #Per machine, a dictionary of extruder container stack IDs. self._active_extruder_index = -1 - UM.Application.getInstance().globalContainerStackChanged.connect(self._addCurrentMachineExtruders) + UM.Application.getInstance().globalContainerStackChanged.connect(self.__globalContainerStackChanged) + self._addCurrentMachineExtruders() ## Gets the unique identifier of the currently active extruder stack. # @@ -78,38 +79,41 @@ class ExtruderManager(QObject): if global_definition_container.getId() in self._extruder_trains: if str(self._active_extruder_index) in self._extruder_trains[global_definition_container.getId()]: return self._extruder_trains[global_definition_container.getId()][str(self._active_extruder_index)] - + return None ## Adds all extruders of a specific machine definition to the extruder # manager. # # \param machine_definition The machine to add the extruders for. def addMachineExtruders(self, machine_definition): + changed = False machine_id = machine_definition.getId() if machine_id not in self._extruder_trains: self._extruder_trains[machine_id] = { } + changed = True container_registry = UM.Settings.ContainerRegistry.getInstance() - if not container_registry: #Then we shouldn't have any machine definition either. In any case, there are no extruder trains then so bye bye. - return + if container_registry: - #Add the extruder trains that don't exist yet. - for extruder_definition in container_registry.findDefinitionContainers(machine = machine_definition.getId()): - position = extruder_definition.getMetaDataEntry("position", None) - if not position: - UM.Logger.log("w", "Extruder definition %s specifies no position metadata entry.", extruder_definition.getId()) - if not container_registry.findContainerStacks(machine = machine_id, position = position): #Doesn't exist yet. - self.createExtruderTrain(extruder_definition, machine_definition, position) + #Add the extruder trains that don't exist yet. + for extruder_definition in container_registry.findDefinitionContainers(machine = machine_definition.getId()): + position = extruder_definition.getMetaDataEntry("position", None) + if not position: + UM.Logger.log("w", "Extruder definition %s specifies no position metadata entry.", extruder_definition.getId()) + if not container_registry.findContainerStacks(machine = machine_id, position = position): #Doesn't exist yet. + self.createExtruderTrain(extruder_definition, machine_definition, position) + changed = True - #Gets the extruder trains that we just created as well as any that still existed. - extruder_trains = container_registry.findContainerStacks(type = "extruder_train", machine = machine_definition.getId()) - for extruder_train in extruder_trains: - self._extruder_trains[machine_id][extruder_train.getMetaDataEntry("position")] = extruder_train + #Gets the extruder trains that we just created as well as any that still existed. + extruder_trains = container_registry.findContainerStacks(type = "extruder_train", machine = machine_definition.getId()) + for extruder_train in extruder_trains: + self._extruder_trains[machine_id][extruder_train.getMetaDataEntry("position")] = extruder_train - #Ensure that the extruder train stacks are linked to global stack. - extruder_train.setNextStack(UM.Application.getInstance().getGlobalContainerStack()) + #Ensure that the extruder train stacks are linked to global stack. + extruder_train.setNextStack(UM.Application.getInstance().getGlobalContainerStack()) + changed = True - if extruder_trains: + if changed: self.extrudersChanged.emit(machine_definition) ## Creates a container stack for an extruder train. @@ -227,6 +231,10 @@ class ExtruderManager(QObject): for name in self._extruder_trains[machine_id]: yield self._extruder_trains[machine_id][name] + def __globalContainerStackChanged(self): + self._addCurrentMachineExtruders() + self.activeExtruderChanged.emit() + ## Adds the extruders of the currently active machine. def _addCurrentMachineExtruders(self): global_stack = UM.Application.getInstance().getGlobalContainerStack() diff --git a/cura/Settings/ExtrudersModel.py b/cura/Settings/ExtrudersModel.py index 0f2511452a..66462296d3 100644 --- a/cura/Settings/ExtrudersModel.py +++ b/cura/Settings/ExtrudersModel.py @@ -52,7 +52,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): #Listen to changes. manager = ExtruderManager.getInstance() manager.extrudersChanged.connect(self._updateExtruders) #When the list of extruders changes in general. - UM.Application.getInstance().globalContainerStackChanged.connect(self._updateExtruders) #When the current machine changes. + self._updateExtruders() manager.activeExtruderChanged.connect(self._onActiveExtruderChanged) @@ -65,6 +65,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): self.addGlobalChanged.emit() addGlobalChanged = pyqtSignal() + @pyqtProperty(bool, fset = setAddGlobal, notify = addGlobalChanged) def addGlobal(self): return self._add_global @@ -93,42 +94,48 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): # # This should be called whenever the list of extruders changes. def _updateExtruders(self): - self.clear() - manager = ExtruderManager.getInstance() + changed = False + + if self.rowCount() != 0: + self.clear() + changed = True + global_container_stack = UM.Application.getInstance().getGlobalContainerStack() - if not global_container_stack: - return #There is no machine to get the extruders of. + if global_container_stack: + if self._add_global: + material = global_container_stack.findContainer({ "type": "material" }) + colour = material.getMetaDataEntry("color_code", default = self.defaultColours[0]) if material else self.defaultColours[0] + item = { + "id": global_container_stack.getId(), + "name": "Global", + "colour": colour, + "index": -1 + } + self.appendItem(item) + changed = True - if self._add_global: - material = global_container_stack.findContainer({ "type": "material" }) - colour = material.getMetaDataEntry("color_code", default = self.defaultColours[0]) if material else self.defaultColours[0] - item = { - "id": global_container_stack.getId(), - "name": "Global", - "colour": colour, - "index": -1 - } - self.appendItem(item) + manager = ExtruderManager.getInstance() + for extruder in manager.getMachineExtruders(global_container_stack.getBottom().getId()): + extruder_name = extruder.getName() + material = extruder.findContainer({ "type": "material" }) + if material: + extruder_name = "%s (%s)" % (material.getName(), extruder_name) + position = extruder.getBottom().getMetaDataEntry("position", default = "0") #Position in the definition. + try: + position = int(position) + except ValueError: #Not a proper int. + position = -1 + default_colour = self.defaultColours[position] if position >= 0 and position < len(self.defaultColours) else self.defaultColours[0] + colour = material.getMetaDataEntry("color_code", default = default_colour) if material else default_colour + item = { #Construct an item with only the relevant information. + "id": extruder.getId(), + "name": extruder_name, + "colour": colour, + "index": position + } + self.appendItem(item) + changed = True - for extruder in manager.getMachineExtruders(global_container_stack.getBottom().getId()): - extruder_name = extruder.getName() - material = extruder.findContainer({ "type": "material" }) - if material: - extruder_name = "%s (%s)" % (material.getName(), extruder_name) - position = extruder.getBottom().getMetaDataEntry("position", default = "0") #Position in the definition. - try: - position = int(position) - except ValueError: #Not a proper int. - position = -1 - default_colour = self.defaultColours[position] if position >= 0 and position < len(self.defaultColours) else defaultColours[0] - colour = material.getMetaDataEntry("color_code", default = default_colour) if material else default_colour - item = { #Construct an item with only the relevant information. - "id": extruder.getId(), - "name": extruder_name, - "colour": colour, - "index": position - } - self.appendItem(item) - - self.sort(lambda item: item["index"]) - self.modelChanged.emit() + if changed: + self.sort(lambda item: item["index"]) + self.modelChanged.emit() diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 64eac750e9..42802fc8cb 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -11,7 +11,6 @@ from UM.Logger import Logger import UM.Settings from cura.PrinterOutputDevice import PrinterOutputDevice - from . import ExtruderManager from UM.i18n import i18nCatalog @@ -31,7 +30,6 @@ class MachineManager(QObject): self._onGlobalContainerChanged() ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged) - self.globalContainerChanged.connect(self._onActiveExtruderStackChanged) self._onActiveExtruderStackChanged() ## When the global container is changed, active material probably needs to be updated. From 0745b3bbf5e71b48620df3e188ac1f54640f4ab4 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Mon, 11 Jul 2016 10:29:21 +0200 Subject: [PATCH 217/236] Unbreak LayerView. CURA-1381 --- plugins/LayerView/LayerView.qml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/LayerView/LayerView.qml b/plugins/LayerView/LayerView.qml index a7e9a7c67d..fef0c52c12 100644 --- a/plugins/LayerView/LayerView.qml +++ b/plugins/LayerView/LayerView.qml @@ -54,9 +54,13 @@ Item horizontalAlignment: TextInput.AlignRight; onEditingFinished: { + // Ensure that the cursor is at the first position. On some systems the text isn't fully visible + // Seems to have to do something with different dpi densities that QML doesn't quite handle. + // Another option would be to increase the size even further, but that gives pretty ugly results. + cursorPosition = 0; if(valueLabel.text != '') { - slider.value = valueLabel.text - 1 + slider.value = valueLabel.text - 1; } } validator: IntValidator { bottom: 1; top: slider.maximumValue + 1; } @@ -66,10 +70,6 @@ Item anchors.verticalCenter: parent.verticalCenter; width: Math.max(UM.Theme.getSize("line").width * maxValue.length + 2, 20); - // Ensure that the cursor is at the first position. On some systems the text isnt fully visible - // Seems to have to do something with different dpi densities that QML doesn't quite handle. - // Another option would be to increase the size even further, but that gives pretty ugly results. - onEditingFinished: cursorPosition = 0 style: TextFieldStyle { textColor: UM.Theme.getColor("setting_control_text"); From 898c621b6f1910349be7fd64a6903016a01f56e0 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Mon, 11 Jul 2016 11:36:06 +0200 Subject: [PATCH 218/236] Recreate editingFinished signal behaviour without using TextArea::editingFinished Apparently, editingFinished was only added in Qt 5.6, so we cannot use it. So we have to manually recreate the behaviour. Contributes to CURA-342 --- .../qml/Preferences/ReadOnlyTextArea.qml | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/resources/qml/Preferences/ReadOnlyTextArea.qml b/resources/qml/Preferences/ReadOnlyTextArea.qml index cbef8fa46b..080aec5f53 100644 --- a/resources/qml/Preferences/ReadOnlyTextArea.qml +++ b/resources/qml/Preferences/ReadOnlyTextArea.qml @@ -3,7 +3,6 @@ import QtQuick 2.1 import QtQuick.Controls 1.1 -import QtQuick.Dialogs 1.2 Item { @@ -28,7 +27,23 @@ Item anchors.fill: parent - onEditingFinished: base.editingFinished() + Keys.onReturnPressed: + { + base.editingFinished() + } + + Keys.onEnterPressed: + { + base.editingFinished() + } + + onActiveFocusChanged: + { + if(!activeFocus) + { + base.editingFinished() + } + } } Label From 2a949237c1423f851ab5a7ab02d9e28d12791ea7 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 11 Jul 2016 12:49:32 +0200 Subject: [PATCH 219/236] Do not ask the user about keeping current settings when creating a profile from the current changes. CURA-1885 --- cura/Settings/MachineManager.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 42802fc8cb..6159283353 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -403,8 +403,8 @@ class MachineManager(QObject): if new_container_id == "": return self.blurSettings.emit() + self.updateQualityContainerFromUserContainer(new_container_id) self.setActiveQuality(new_container_id) - self.updateQualityContainerFromUserContainer() return new_container_id @pyqtSlot(str, result=str) @@ -477,12 +477,23 @@ class MachineManager(QObject): self.setActiveQuality(containers[0].getId()) self.activeQualityChanged.emit() - @pyqtSlot() - def updateQualityContainerFromUserContainer(self): + @pyqtSlot(str) + def updateQualityContainerFromUserContainer(self, quality_id = None): if not self._active_container_stack: return + + if quality_id: + quality = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(id = quality_id, type = "quality") + if quality: + quality = quality[0] + else: + quality = self._active_container_stack.findContainer({"type": "quality"}) + + if not quality: + return + user_settings = self._active_container_stack.getTop() - quality = self._active_container_stack.findContainer({"type": "quality"}) + for key in user_settings.getAllKeys(): quality.setProperty(key, "value", user_settings.getProperty(key, "value")) self.clearUserSettings() # As all users settings are noq a quality, remove them. From 9efc2ee97319c94e04fb957b51ee01b0aa6040d0 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Mon, 11 Jul 2016 13:36:23 +0200 Subject: [PATCH 220/236] Added message when switching to one_at_a_time. CURA-1787 --- cura/BuildVolume.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index efc306c32d..091ba8bb42 100644 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -1,6 +1,7 @@ # Copyright (c) 2015 Ultimaker B.V. # Cura is released under the terms of the AGPLv3 or higher. +from UM.i18n import i18nCatalog from UM.Scene.SceneNode import SceneNode from UM.Application import Application from UM.Resources import Resources @@ -9,9 +10,11 @@ from UM.Math.Vector import Vector from UM.Math.Color import Color from UM.Math.AxisAlignedBox import AxisAlignedBox from UM.Math.Polygon import Polygon +from UM.Message import Message from UM.View.RenderBatch import RenderBatch from UM.View.GL.OpenGL import OpenGL +catalog = i18nCatalog("cura") import numpy @@ -189,6 +192,11 @@ class BuildVolume(SceneNode): if setting_key == "print_sequence": if Application.getInstance().getGlobalContainerStack().getProperty("print_sequence", "value") == "one_at_a_time": self._height = self._active_container_stack.getProperty("gantry_height", "value") + Message(catalog.i18nc( + "@info:status", + "The build volume height has been reduced due to the value of the" + " \"Print Sequence\" setting to prevent the gantry from colliding" + " with printed objects."), lifetime=10).show() else: self._height = self._active_container_stack.getProperty("machine_height", "value") self.rebuild() From f424097387f75ec9a6671b5e261262be9a3ff256 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 11 Jul 2016 14:01:32 +0200 Subject: [PATCH 221/236] Improve defaults for raft settings These better defaults and formulas are coming from Paul, one of our material testers. --- resources/definitions/fdmprinter.def.json | 32 ++++++++++++++--------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index b3fb5edab4..077b9d2162 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -1912,7 +1912,7 @@ "unit": "mm", "type": "float", "default_value": 0.5, - "value": "layer_height_0", + "value": "0 if adhesion_type == \"raft\" else layer_height_0", "minimum_value": "0", "maximum_value_warning": "10.0", "settable_per_mesh": false, @@ -2405,7 +2405,7 @@ "description": "If the raft is enabled, this is the extra raft area around the object which is also given a raft. Increasing this margin will create a stronger raft while using more material and leaving less area for your print.", "unit": "mm", "type": "float", - "default_value": 5, + "default_value": 15, "minimum_value_warning": "0", "maximum_value_warning": "10", "enabled": "adhesion_type == \"raft\"" @@ -2455,6 +2455,7 @@ "unit": "mm", "type": "float", "default_value": 0.1, + "value": "layer_height", "minimum_value": "0", "maximum_value_warning": "2.0", "enabled": "adhesion_type == \"raft\"", @@ -2467,7 +2468,8 @@ "description": "Width of the lines in the top surface of the raft. These can be thin lines so that the top of the raft becomes smooth.", "unit": "mm", "type": "float", - "default_value": 0.3, + "default_value": 0.4, + "value": "line_width", "minimum_value": "0.0001", "maximum_value_warning": "machine_nozzle_size * 2", "enabled": "adhesion_type == \"raft\"", @@ -2480,7 +2482,7 @@ "description": "The distance between the raft lines for the top raft layers. The spacing should be equal to the line width, so that the surface is solid.", "unit": "mm", "type": "float", - "default_value": 0.3, + "default_value": 0.4, "minimum_value": "0.0001", "maximum_value_warning": "5.0", "enabled": "adhesion_type == \"raft\"", @@ -2494,7 +2496,8 @@ "description": "Layer thickness of the middle raft layer.", "unit": "mm", "type": "float", - "default_value": 0.27, + "default_value": 0.15, + "value": "layer_height * 1.5", "minimum_value": "0", "maximum_value_warning": "5.0", "enabled": "adhesion_type == \"raft\"", @@ -2507,7 +2510,7 @@ "description": "Width of the lines in the middle raft layer. Making the second layer extrude more causes the lines to stick to the bed.", "unit": "mm", "type": "float", - "default_value": 1, + "default_value": 0.7, "value": "line_width * 2", "minimum_value": "0.0001", "maximum_value_warning": "machine_nozzle_size * 2", @@ -2521,7 +2524,8 @@ "description": "The distance between the raft lines for the middle raft layer. The spacing of the middle should be quite wide, while being dense enough to support the top raft layers.", "unit": "mm", "type": "float", - "default_value": 1.0, + "default_value": 0.9, + "value": "raft_interface_line_width + 0.2", "minimum_value": "0", "maximum_value_warning": "15.0", "enabled": "adhesion_type == \"raft\"", @@ -2535,6 +2539,7 @@ "unit": "mm", "type": "float", "default_value": 0.3, + "value": "layer_height_0 * 1.2", "minimum_value": "0", "maximum_value_warning": "5.0", "enabled": "adhesion_type == \"raft\"", @@ -2547,10 +2552,10 @@ "description": "Width of the lines in the base raft layer. These should be thick lines to assist in bed adhesion.", "unit": "mm", "type": "float", - "default_value": 1, + "default_value": 0.8, "minimum_value": "0.0001", - "value": "line_width * 2", - "maximum_value_warning": "machine_nozzle_size * 2", + "value": "machine_nozzle_size * 2", + "maximum_value_warning": "machine_nozzle_size * 3", "enabled": "adhesion_type == \"raft\"", "settable_per_mesh": false, "settable_per_extruder": true @@ -2561,7 +2566,8 @@ "description": "The distance between the raft lines for the base raft layer. Wide spacing makes for easy removal of the raft from the build plate.", "unit": "mm", "type": "float", - "default_value": 3.0, + "default_value": 1.6, + "value": "raft_base_line_width * 2", "minimum_value": "0.0001", "maximum_value_warning": "100", "enabled": "adhesion_type == \"raft\"", @@ -2605,12 +2611,12 @@ "description": "The speed at which the middle raft layer is printed. This should be printed quite slowly, as the volume of material coming out of the nozzle is quite high.", "unit": "mm/s", "type": "float", - "default_value": 20, + "default_value": 15, + "value": "raft_speed * 0.75", "minimum_value": "0.1", "maximum_value": "299792458000", "maximum_value_warning": "150", "enabled": "adhesion_type == \"raft\"", - "value": "raft_speed", "settable_per_mesh": false, "settable_per_extruder": true }, From 4aa95ab31746bf7e007670aa0c9a8212fcf4a74c Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 11 Jul 2016 14:54:08 +0200 Subject: [PATCH 222/236] Prevent showing custom profiles twice and fix separator in the profiles dropdown CURA-1887 --- resources/qml/Menus/ProfileMenu.qml | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/resources/qml/Menus/ProfileMenu.qml b/resources/qml/Menus/ProfileMenu.qml index 213f8c2629..68f83305d7 100644 --- a/resources/qml/Menus/ProfileMenu.qml +++ b/resources/qml/Menus/ProfileMenu.qml @@ -13,7 +13,10 @@ import Cura 1.0 as Cura Instantiator { - model: UM.InstanceContainersModel { filter: menu.getFilter({ "read_only": true }); } + model: UM.InstanceContainersModel + { + filter: menu.getFilter({ "read_only": true }); + } MenuItem { @@ -32,13 +35,11 @@ import Cura 1.0 as Cura Instantiator { + id: customProfileInstantiator model: UM.InstanceContainersModel { - id: customProfilesModel; filter: menu.getFilter({ "read_only": false }); - onRowsInserted: customSeparator.visible = rowCount() > 1 - onRowsRemoved: customSeparator.visible = rowCount() > 1 - onModelReset: customSeparator.visible = rowCount() > 1 + onModelReset: customSeparator.visible = rowCount() > 0 } MenuItem @@ -50,8 +51,16 @@ import Cura 1.0 as Cura onTriggered: Cura.MachineManager.setActiveQuality(model.id) } - onObjectAdded: menu.insertItem(index, object); - onObjectRemoved: menu.removeItem(object); + onObjectAdded: + { + customSeparator.visible = model.rowCount() > 0; + menu.insertItem(index, object); + } + onObjectRemoved: + { + customSeparator.visible = model.rowCount() > 0; + menu.removeItem(object); + } } ExclusiveGroup { id: group; } From f49528b71fc416a96b094426c17b93710cc458ee Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Mon, 11 Jul 2016 14:54:50 +0200 Subject: [PATCH 223/236] Do not try to set base_file metadata if it was never set before Contributes to CURA-341 --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index ec75b7253c..4b7d5ebc41 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -20,7 +20,7 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): ## Overridden from InstanceContainer def duplicate(self, new_id, new_name = None): - base_file = self.getMetaDataEntry("base_file", "") + base_file = self.getMetaDataEntry("base_file", None) new_uuid = str(uuid.uuid4()) if base_file: @@ -40,7 +40,8 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): result = super().duplicate(new_id, new_name) result.setMetaDataEntry("GUID", new_uuid) - result.setMetaDataEntry("base_file", base_file) + if result.getMetaDataEntry("base_file", None): + result.setMetaDataEntry("base_file", base_file) return result ## Overridden from InstanceContainer From 57f63bfc73c8406468676d59d5eee6898f950c0b Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Mon, 11 Jul 2016 14:55:17 +0200 Subject: [PATCH 224/236] Update material container's name when its "material" metadata key changes Contributes to CURA-342 --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 4b7d5ebc41..84b78c1d6a 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -58,8 +58,13 @@ class XmlMaterialProfile(UM.Settings.InstanceContainer): super().setMetaDataEntry(key, value) + if key == "material": + self.setName(value) + for container in UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(GUID = self.getMetaDataEntry("GUID")): container.setMetaData(copy.deepcopy(self._metadata)) + if key == "material": + container.setName(value) ## Overridden from InstanceContainer def setProperty(self, key, property_name, property_value, container = None): From d6ba70c55263566ab7af2a77bd941f6b43f605be Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Mon, 11 Jul 2016 14:56:27 +0200 Subject: [PATCH 225/236] Do not try to set a material's metadata when we know it has not changed Contributes to CURA-342 --- resources/qml/Preferences/MaterialView.qml | 28 ++++++++++++++-------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/resources/qml/Preferences/MaterialView.qml b/resources/qml/Preferences/MaterialView.qml index 67a149f446..9dbb5c6d15 100644 --- a/resources/qml/Preferences/MaterialView.qml +++ b/resources/qml/Preferences/MaterialView.qml @@ -51,7 +51,7 @@ TabView width: base.secondColumnWidth; text: properties.supplier; readOnly: !base.editingEnabled; - onEditingFinished: Cura.ContainerManager.setContainerMetaDataEntry(base.containerId, "brand", text) + onEditingFinished: base.setMetaDataEntry("brand", properties.supplier, text) } Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Material Type") } @@ -60,7 +60,7 @@ TabView width: base.secondColumnWidth; text: properties.material_type; readOnly: !base.editingEnabled; - onEditingFinished: Cura.ContainerManager.setContainerMetaDataEntry(base.containerId, "material", text) + onEditingFinished: base.setMetaDataEntry("material", properties.material_type, text) } Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Color") } @@ -75,7 +75,6 @@ TabView { id: colorSelector color: properties.color_code - onColorChanged: Cura.ContainerManager.setContainerMetaDataEntry(base.containerId, "color_code", color) width: colorLabel.height * 0.75 height: colorLabel.height * 0.75 @@ -90,15 +89,15 @@ TabView id: colorLabel; text: properties.color_name; readOnly: !base.editingEnabled - onEditingFinished: Cura.ContainerManager.setContainerMetaDataEntry(base.containerId, "color_name", text) + onEditingFinished: base.setMetaDataEntry("color_name", properties.color_name, text) } - ColorDialog { id: colorDialog; color: properties.color_code; onAccepted: colorSelector.color = color } + ColorDialog { id: colorDialog; color: properties.color_code; onAccepted: base.setMetaDataEntry("color_code", properties.color_code, color) } } Item { width: parent.width; height: UM.Theme.getSize("default_margin").height } - Label { width: parent.width; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: "" + catalog.i18nc("@label", "Properties") + "" } + Label { width: parent.width; height: parent.rowHeight; font.bold: true; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Properties") } Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Density") } ReadOnlySpinBox @@ -110,7 +109,7 @@ TabView stepSize: 0.01 readOnly: !base.editingEnabled; - onEditingFinished: Cura.ContainerManager.setContainerMetaDataEntry(base.containerId, "properties/density", value) + onEditingFinished: base.setMetaDataEntry("properties/density", properties.density, value) } Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Diameter") } @@ -123,7 +122,7 @@ TabView stepSize: 0.01 readOnly: !base.editingEnabled; - onEditingFinished: Cura.ContainerManager.setContainerMetaDataEntry(base.containerId, "properties/diameter", value) + onEditingFinished: base.setMetaDataEntry("properties/diameter", properties.diameter, value) } Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament Cost") } @@ -175,7 +174,7 @@ TabView readOnly: !base.editingEnabled; - onEditingFinished: Cura.ContainerManager.setContainerMetaDataEntry(base.containerId, "description", text) + onEditingFinished: base.setMetaDataEntry("description", properties.description, text) } Label { width: parent.width; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Adhesion Information") } @@ -188,7 +187,7 @@ TabView readOnly: !base.editingEnabled; - onEditingFinished: Cura.ContainerManager.setContainerMetaDataEntry(base.containerId, "adhesion_info", text) + onEditingFinished: base.setMetaDataEntry("adhesion_info", properties.adhesion_info, text) } } } @@ -249,4 +248,13 @@ TabView } } } + + // Tiny convenience function to check if a value really changed before trying to set it. + function setMetaDataEntry(entry_name, old_value, new_value) + { + if(old_value != new_value) + { + Cura.ContainerManager.setContainerMetaDataEntry(base.containerId, entry_name, new_value) + } + } } From 4d2d8896049652b5df94aaee72faad111cfb28fd Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Mon, 11 Jul 2016 14:56:59 +0200 Subject: [PATCH 226/236] Do not complain when currentItem is null Contributes to CURA-342 --- resources/qml/Preferences/MaterialsPage.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Preferences/MaterialsPage.qml b/resources/qml/Preferences/MaterialsPage.qml index 35fa7256ea..bacb4f73bb 100644 --- a/resources/qml/Preferences/MaterialsPage.qml +++ b/resources/qml/Preferences/MaterialsPage.qml @@ -147,7 +147,7 @@ UM.ManagementPage editingEnabled: base.currentItem != null && !base.currentItem.readOnly && editButton.checked; properties: materialProperties - containerId: base.currentItem.id + containerId: base.currentItem != null ? base.currentItem.id : "" } QtObject From 2798b73d3234328023a2ab796a90f726e4c5a106 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Mon, 11 Jul 2016 15:12:08 +0200 Subject: [PATCH 227/236] Also display build volume message _onGlobalContainerStackChanged CURA-1787 --- cura/BuildVolume.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index 091ba8bb42..b438365f78 100644 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -165,6 +165,13 @@ class BuildVolume(SceneNode): def getBoundingBox(self): return self._volume_aabb + def _buildVolumeMessage(self): + Message(catalog.i18nc( + "@info:status", + "The build volume height has been reduced due to the value of the" + " \"Print Sequence\" setting to prevent the gantry from colliding" + " with printed objects."), lifetime=10).show() + def _onGlobalContainerStackChanged(self): if self._active_container_stack: self._active_container_stack.propertyChanged.disconnect(self._onSettingPropertyChanged) @@ -177,6 +184,7 @@ class BuildVolume(SceneNode): self._width = self._active_container_stack.getProperty("machine_width", "value") if self._active_container_stack.getProperty("print_sequence", "value") == "one_at_a_time": self._height = self._active_container_stack.getProperty("gantry_height", "value") + self._buildVolumeMessage() else: self._height = self._active_container_stack.getProperty("machine_height", "value") self._depth = self._active_container_stack.getProperty("machine_depth", "value") @@ -192,11 +200,7 @@ class BuildVolume(SceneNode): if setting_key == "print_sequence": if Application.getInstance().getGlobalContainerStack().getProperty("print_sequence", "value") == "one_at_a_time": self._height = self._active_container_stack.getProperty("gantry_height", "value") - Message(catalog.i18nc( - "@info:status", - "The build volume height has been reduced due to the value of the" - " \"Print Sequence\" setting to prevent the gantry from colliding" - " with printed objects."), lifetime=10).show() + self._buildVolumeMessage() else: self._height = self._active_container_stack.getProperty("machine_height", "value") self.rebuild() From 99c2e3b1d16edf565c621366a1411b70ef568704 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Mon, 11 Jul 2016 15:36:26 +0200 Subject: [PATCH 228/236] Ensure machine, material and quality name changes are properly handled Contributes to CURA-342 Fixes CURA-1669 --- cura/Settings/MachineManager.py | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 6159283353..2448139897 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -206,18 +206,32 @@ class MachineManager(QObject): def _onGlobalContainerChanged(self): if self._global_container_stack: + self._global_container_stack.nameChanged.disconnect(self._onMachineNameChanged) self._global_container_stack.containersChanged.disconnect(self._onInstanceContainersChanged) self._global_container_stack.propertyChanged.disconnect(self._onGlobalPropertyChanged) + material = self._global_container_stack.findContainer({"type": "material"}) + material.nameChanged.disconnect(self._onMaterialNameChanged) + + quality = self._global_container_stack.findContainer({"type": "quality"}) + quality.nameChanged.disconnect(self._onQualityNameChanged) + self._global_container_stack = Application.getInstance().getGlobalContainerStack() self.globalContainerChanged.emit() if self._global_container_stack: Preferences.getInstance().setValue("cura/active_machine", self._global_container_stack.getId()) + self._global_container_stack.nameChanged.connect(self._onMachineNameChanged) self._global_container_stack.containersChanged.connect(self._onInstanceContainersChanged) self._global_container_stack.propertyChanged.connect(self._onGlobalPropertyChanged) self._global_stack_valid = not self._checkStackForErrors(self._global_container_stack) + material = self._global_container_stack.findContainer({"type": "material"}) + material.nameChanged.connect(self._onMaterialNameChanged) + + quality = self._global_container_stack.findContainer({"type": "quality"}) + quality.nameChanged.connect(self._onQualityNameChanged) + def _onActiveExtruderStackChanged(self): self.blurSettings.emit() # Ensure no-one has focus. if self._active_container_stack and self._active_container_stack != self._global_container_stack: @@ -508,9 +522,13 @@ class MachineManager(QObject): old_material = self._active_container_stack.findContainer({"type":"material"}) old_quality = self._active_container_stack.findContainer({"type": "quality"}) if old_material: + old_material.nameChanged.disconnect(self._onMaterialNameChanged) + material_index = self._active_container_stack.getContainerIndex(old_material) self._active_container_stack.replaceContainer(material_index, containers[0]) + containers[0].nameChanged.connect(self._onMaterialNameChanged) + preferred_quality_name = None if old_quality: preferred_quality_name = old_quality.getName() @@ -545,10 +563,14 @@ class MachineManager(QObject): old_quality = self._active_container_stack.findContainer({"type": "quality"}) if old_quality and old_quality != containers[0]: + old_quality.nameChanged.disconnect(self._onQualityNameChanged) + quality_index = self._active_container_stack.getContainerIndex(old_quality) self._active_container_stack.replaceContainer(quality_index, containers[0]) + containers[0].nameChanged.connect(self._onQualityNameChanged) + if self.hasUserSettings and Preferences.getInstance().getValue("cura/active_mode") == 1: # Ask the user if the user profile should be cleared or not (discarding the current settings) # In Simple Mode we assume the user always wants to keep the (limited) current settings @@ -752,3 +774,15 @@ class MachineManager(QObject): return containers[0] return self._empty_quality_container + + def _onMachineNameChanged(self): + print("machine name changed") + self.globalContainerChanged.emit() + + def _onMaterialNameChanged(self): + print("material name changed") + self.activeMaterialChanged.emit() + + def _onQualityNameChanged(self): + print("quality name changed") + self.activeQualityChanged.emit() From 7a63f957cb104f903d54d59fb628dbdbdd856c62 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 11 Jul 2016 15:48:41 +0200 Subject: [PATCH 229/236] More gracefully handle cases where no profile is selected in the profiles list --- resources/qml/Preferences/ProfilesPage.qml | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/resources/qml/Preferences/ProfilesPage.qml b/resources/qml/Preferences/ProfilesPage.qml index d6dd66e8a8..5a56ac8dc3 100644 --- a/resources/qml/Preferences/ProfilesPage.qml +++ b/resources/qml/Preferences/ProfilesPage.qml @@ -140,7 +140,7 @@ UM.ManagementPage Row { id: currentSettingsActions - visible: currentItem.id == Cura.MachineManager.activeQualityId + visible: currentItem && currentItem.id == Cura.MachineManager.activeQualityId anchors.left: parent.left anchors.top: profileName.bottom @@ -173,14 +173,14 @@ UM.ManagementPage Label { id: defaultsMessage - visible: !currentItem.metadata.has_settings + visible: currentItem && !currentItem.metadata.has_settings text: catalog.i18nc("@action:label", "This profile has no settings and uses the defaults specified by the printer.") wrapMode: Text.WordWrap width: parent.width } Label { id: noCurrentSettingsMessage - visible: currentItem.id == Cura.MachineManager.activeQualityId && !Cura.MachineManager.hasUserSettings + visible: currentItem && currentItem.id == Cura.MachineManager.activeQualityId && !Cura.MachineManager.hasUserSettings text: catalog.i18nc("@action:label", "Your current settings match the selected profile.") wrapMode: Text.WordWrap width: parent.width @@ -197,7 +197,17 @@ UM.ManagementPage anchors.bottom: parent.bottom ListView { - model: Cura.ContainerSettingsModel{ containers: (currentItem.id == Cura.MachineManager.activeQualityId) ? [base.currentItem.id, Cura.MachineManager.activeUserProfileId] : [base.currentItem.id] } + model: Cura.ContainerSettingsModel{ containers: + { + if (!currentItem) { + return [] + } else if (currentItem.id == Cura.MachineManager.activeQualityId) { + return [base.currentItem.id, Cura.MachineManager.activeUserProfileId] + } else { + return [base.currentItem.id] + } + } + } delegate: Row { property variant setting: model spacing: UM.Theme.getSize("default_margin").width/2 @@ -221,7 +231,7 @@ UM.ManagementPage } } header: Row { - visible: currentItem.id == Cura.MachineManager.activeQualityId + visible: currentItem && currentItem.id == Cura.MachineManager.activeQualityId spacing: UM.Theme.getSize("default_margin").width Label { text: catalog.i18nc("@action:label", "Profile:") @@ -231,7 +241,7 @@ UM.ManagementPage } Label { text: catalog.i18nc("@action:label", "Current:") - visible: currentItem.id == Cura.MachineManager.activeQualityId + visible: currentItem && currentItem.id == Cura.MachineManager.activeQualityId font.bold: true } } From c3d395b71dc5b2fccaa8d321c89b3341b84c4396 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 11 Jul 2016 16:29:28 +0200 Subject: [PATCH 230/236] Define extruder_prime_pos_* in fdmextruder too We need to have it in both, because it needs to be changed by the inheriting definitions (so it needs to be defined in fdmextruder) but it also needs to be defined for single-extrusion printers (so it needs to be in fdmprinter). I copied these definitions from fdmprinter. Contributes to issue CURA-1816. --- resources/definitions/fdmextruder.def.json | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/resources/definitions/fdmextruder.def.json b/resources/definitions/fdmextruder.def.json index 06a736330c..bb14732493 100644 --- a/resources/definitions/fdmextruder.def.json +++ b/resources/definitions/fdmextruder.def.json @@ -144,6 +144,42 @@ "settable_per_extruder": true, "settable_per_meshgroup": false, "settable_globally": false + }, + "extruder_prime_pos_x": + { + "label": "Extruder Prime X Position", + "description": "The X coordinate of the position where the nozzle primes at the start of printing.", + "type": "float", + "unit": "mm", + "default_value": 0, + "minimum_value_warning": "-1000", + "maximum_value_warning": "1000", + "settable_per_mesh": false, + "settable_per_extruder": true + }, + "extruder_prime_pos_y": + { + "label": "Extruder Prime Y Position", + "description": "The Y coordinate of the position where the nozzle primes at the start of printing.", + "type": "float", + "unit": "mm", + "default_value": 0, + "minimum_value_warning": "-1000", + "maximum_value_warning": "1000", + "settable_per_mesh": false, + "settable_per_extruder": true + }, + "extruder_prime_pos_z": + { + "label": "Extruder Prime Z Position", + "description": "The Z coordinate of the position where the nozzle primes at the start of printing.", + "type": "float", + "unit": "mm", + "default_value": 0, + "minimum_value_warning": "-1000", + "maximum_value_warning": "1000", + "settable_per_mesh": false, + "settable_per_extruder": true } } } From a7585a4b9a10e83f181fce8adee12cee3b0851e5 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Mon, 11 Jul 2016 16:40:57 +0200 Subject: [PATCH 231/236] Ignore default container types when creating name filters for containers Contributes to CURA-342 --- cura/Settings/ContainerManager.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cura/Settings/ContainerManager.py b/cura/Settings/ContainerManager.py index eb9dab0ad7..9184db109a 100644 --- a/cura/Settings/ContainerManager.py +++ b/cura/Settings/ContainerManager.py @@ -349,6 +349,10 @@ class ContainerManager(QObject): def _updateContainerNameFilters(self): self._container_name_filters = {} for plugin_id, container_type in UM.Settings.ContainerRegistry.getContainerTypes(): + # Ignore default container types since those are not plugins + if container_type in (UM.Settings.InstanceContainer, UM.Settings.ContainerStack, UM.Settings.DefinitionContainer): + continue + serialize_type = "" try: plugin_metadata = UM.PluginRegistry.getInstance().getMetaData(plugin_id) From 5393beb8de1ae4ce8799949ce4b6e25a8b82c290 Mon Sep 17 00:00:00 2001 From: Arjen Hiemstra Date: Mon, 11 Jul 2016 16:41:39 +0200 Subject: [PATCH 232/236] Fix binding loop warnings that would sometimes occur when editing Brand entry Contributes to CURA-342 --- resources/qml/Preferences/MaterialsPage.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/Preferences/MaterialsPage.qml b/resources/qml/Preferences/MaterialsPage.qml index bacb4f73bb..4e0d64ca5e 100644 --- a/resources/qml/Preferences/MaterialsPage.qml +++ b/resources/qml/Preferences/MaterialsPage.qml @@ -129,7 +129,7 @@ UM.ManagementPage enabled: base.currentItem != null && !base.currentItem.readOnly - checkable: true + checkable: enabled } } @@ -144,7 +144,7 @@ UM.ManagementPage bottom: parent.bottom } - editingEnabled: base.currentItem != null && !base.currentItem.readOnly && editButton.checked; + editingEnabled: editButton.checkable && editButton.checked; properties: materialProperties containerId: base.currentItem != null ? base.currentItem.id : "" From 2c09fc4a6723239cd744b4aa9f28007dea5852fd Mon Sep 17 00:00:00 2001 From: Tim Kuipers Date: Mon, 11 Jul 2016 16:43:55 +0200 Subject: [PATCH 233/236] JSON fix: infill mesh order defaults to higher than for normal meshes (CURA-833) --- resources/definitions/fdmprinter.def.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 077b9d2162..d8c335f0b3 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -3076,7 +3076,8 @@ "label": "Infill Mesh Order", "description": "Determines which infill mesh is inside the infill of another infill mesh. An infill mesh with a higher order will modify the infill of infill meshes with lower order and normal meshes.", "default_value": 0, - "minimum_value_warning": "0", + "value": "1 if infill_mesh else 0", + "minimum_value_warning": "1", "maximum_value_warning": "50", "type": "int", "settable_per_mesh": true, From 151bf1f8de6cf240327ae700d1ebf7a774b7b39b Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 11 Jul 2016 17:25:59 +0200 Subject: [PATCH 234/236] Make style of sections on the Materials page match sections on the profiles page CURA-342 --- resources/qml/Preferences/MaterialsPage.qml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/resources/qml/Preferences/MaterialsPage.qml b/resources/qml/Preferences/MaterialsPage.qml index bacb4f73bb..55f29fad4f 100644 --- a/resources/qml/Preferences/MaterialsPage.qml +++ b/resources/qml/Preferences/MaterialsPage.qml @@ -51,7 +51,13 @@ UM.ManagementPage detailsVisible: true section.property: "section" - section.delegate: Label { text: section } + section.delegate: Label + { + text: section + font.bold: true + anchors.left: parent.left; + anchors.leftMargin: UM.Theme.getSize("default_lining").width; + } buttons: [ Button From 1b2514ae2e6e002a1190679843af60aa1ec1d5e9 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 11 Jul 2016 18:10:25 +0200 Subject: [PATCH 235/236] Fix setting the active container stack on start When starting Cura, no nozzle, material or quality would seem to be selected. This commit makes sure _active_container_stack gets properly initialised. I'll hijack CURA-1844 for this. --- cura/Settings/MachineManager.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 2448139897..f670bf2bf2 100644 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -217,6 +217,8 @@ class MachineManager(QObject): quality.nameChanged.disconnect(self._onQualityNameChanged) self._global_container_stack = Application.getInstance().getGlobalContainerStack() + self._active_container_stack = self._global_container_stack + self.globalContainerChanged.emit() if self._global_container_stack: From a7cfe1b5afc7c5b1e5f11afdccbcbc6658d38432 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 11 Jul 2016 21:15:09 +0200 Subject: [PATCH 236/236] Emphasize settings that have a value in the user or quality profile (in a subtle way) --- resources/qml/Settings/SettingItem.qml | 5 +++-- resources/themes/cura/theme.json | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/resources/qml/Settings/SettingItem.qml b/resources/qml/Settings/SettingItem.qml index 1e69704995..5e37288f4a 100644 --- a/resources/qml/Settings/SettingItem.qml +++ b/resources/qml/Settings/SettingItem.qml @@ -64,7 +64,7 @@ Item { return tooltip } - MouseArea + MouseArea { id: mouse; @@ -114,7 +114,8 @@ Item { elide: Text.ElideMiddle; color: UM.Theme.getColor("setting_control_text"); - font: UM.Theme.getFont("default"); + // emphasize the setting if it has a value in the user or quality profile + font: base.stackLevel != undefined && base.stackLevel <= 1 ? UM.Theme.getFont("default_italic") : UM.Theme.getFont("default") } Row diff --git a/resources/themes/cura/theme.json b/resources/themes/cura/theme.json index 5bc5e99765..053e5e3d84 100644 --- a/resources/themes/cura/theme.json +++ b/resources/themes/cura/theme.json @@ -14,6 +14,11 @@ "bold": true, "family": "Open Sans" }, + "default_italic": { + "size": 1.15, + "italic": true, + "family": "Open Sans" + }, "small": { "size": 1.0, "bold": true,