From 7516fb6056bbd182f20af6739a4ee90834304612 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Tue, 13 Dec 2016 14:16:04 +0100 Subject: [PATCH 01/24] Store cost & weight as preference values independent of material definition Users need to be able to set these values independent of the xml definition, as it is not a material property but something that depends on the reseller etc. It must also be settable for read-only materials. --- cura/CuraApplication.py | 4 + resources/qml/Preferences/MaterialView.qml | 87 ++++++++++++++------- resources/qml/Preferences/MaterialsPage.qml | 2 + 3 files changed, 65 insertions(+), 28 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 2ab7837352..41b1625e61 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -217,6 +217,10 @@ class CuraApplication(QtApplication): Preferences.getInstance().addPreference("mesh/scale_tiny_meshes", True) Preferences.getInstance().addPreference("cura/dialog_on_project_save", True) Preferences.getInstance().addPreference("cura/asked_dialog_on_project_save", False) + + Preferences.getInstance().addPreference("cura/currency", "€") + Preferences.getInstance().addPreference("cura/material_settings", "{}") + for key in [ "dialog_load_path", # dialog_save_path is in LocalFileOutputDevicePlugin "dialog_profile_path", diff --git a/resources/qml/Preferences/MaterialView.qml b/resources/qml/Preferences/MaterialView.qml index 7ea363454b..d3efef353c 100644 --- a/resources/qml/Preferences/MaterialView.qml +++ b/resources/qml/Preferences/MaterialView.qml @@ -15,10 +15,11 @@ TabView property QtObject properties; property bool editingEnabled: false; - property string currency: UM.Preferences.getValue("general/currency") ? UM.Preferences.getValue("general/currency") : "€" + property string currency: UM.Preferences.getValue("cura/currency") ? UM.Preferences.getValue("cura/currency") : "€" property real firstColumnWidth: width * 0.45 property real secondColumnWidth: width * 0.45 property string containerId: "" + property var materialPreferenceValues: UM.Preferences.getValue("cura/material_settings") ? JSON.parse(UM.Preferences.getValue("cura/material_settings")) : {} Tab { @@ -112,12 +113,12 @@ TabView Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Density") } ReadOnlySpinBox { - width: base.secondColumnWidth; - value: properties.density; + width: base.secondColumnWidth + value: properties.density decimals: 2 - suffix: "g/cm³" + suffix: " g/cm³" stepSize: 0.01 - readOnly: !base.editingEnabled; + readOnly: !base.editingEnabled onEditingFinished: base.setMetaDataEntry("properties/density", properties.density, value) } @@ -125,12 +126,12 @@ TabView Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Diameter") } ReadOnlySpinBox { - width: base.secondColumnWidth; - value: properties.diameter; + width: base.secondColumnWidth + value: properties.diameter decimals: 2 - suffix: "mm" + suffix: " mm" stepSize: 0.01 - readOnly: !base.editingEnabled; + readOnly: !base.editingEnabled onEditingFinished: base.setMetaDataEntry("properties/diameter", properties.diameter, value) } @@ -138,38 +139,42 @@ TabView Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament Cost") } SpinBox { - width: base.secondColumnWidth; - value: properties.spool_cost; - prefix: base.currency - enabled: false + width: base.secondColumnWidth + value: base.getMaterialPreferenceValue(properties.guid, "spool_cost") + prefix: base.currency + " " + decimals: 2 + maximumValue: 1000 + onEditingFinished: base.setMaterialPreferenceValue(properties.guid, "spool_cost", parseFloat(value)) } Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament weight") } SpinBox { - width: base.secondColumnWidth; - value: properties.spool_weight; - suffix: "g"; - stepSize: 10 - enabled: false + width: base.secondColumnWidth + value: base.getMaterialPreferenceValue(properties.guid, "spool_weight") + suffix: " g" + stepSize: 100 + decimals: 0 + maximumValue: 10000 + onEditingFinished: base.setMaterialPreferenceValue(properties.guid, "spool_weight", parseFloat(value)) } Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament length") } - SpinBox + Label { - width: base.secondColumnWidth; - value: parseFloat(properties.spool_length); - suffix: "m"; - enabled: false + width: base.secondColumnWidth + text: "%1 m".arg(properties.spool_length) + verticalAlignment: Qt.AlignVCenter + height: parent.rowHeight } Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Cost per Meter (Approx.)") } - SpinBox + Label { - width: base.secondColumnWidth; - value: parseFloat(properties.cost_per_meter); - suffix: catalog.i18nc("@label", "%1/m".arg(base.currency)); - enabled: false + width: base.secondColumnWidth + text: "%1 %2/m".arg(parseFloat(properties.cost_per_meter).toFixed(2)).arg(base.currency) + verticalAlignment: Qt.AlignVCenter + height: parent.rowHeight } Item { width: parent.width; height: UM.Theme.getSize("default_margin").height } @@ -268,6 +273,32 @@ TabView } } + function setMaterialPreferenceValue(material_guid, entry_name, new_value) + { + if(!(material_guid in materialPreferenceValues)) + { + materialPreferenceValues[material_guid] = {}; + } + if(entry_name in materialPreferenceValues[material_guid] && materialPreferenceValues[material_guid][entry_name] == new_value) + { + // value has not changed + return + } + materialPreferenceValues[material_guid][entry_name] = new_value; + + // store preference + UM.Preferences.setValue("cura/material_settings", JSON.stringify(materialPreferenceValues)); + } + + function getMaterialPreferenceValue(material_guid, entry_name) + { + if(material_guid in materialPreferenceValues && entry_name in materialPreferenceValues[material_guid]) + { + return materialPreferenceValues[material_guid][entry_name]; + } + return 0; + } + function setName(old_value, new_value) { if(old_value != new_value) diff --git a/resources/qml/Preferences/MaterialsPage.qml b/resources/qml/Preferences/MaterialsPage.qml index 264bc182e6..0c780d165d 100644 --- a/resources/qml/Preferences/MaterialsPage.qml +++ b/resources/qml/Preferences/MaterialsPage.qml @@ -219,6 +219,7 @@ UM.ManagementPage { id: materialProperties + property string guid: "00000000-0000-0000-0000-000000000000" property string name: "Unknown"; property string profile_type: "Unknown"; property string supplier: "Unknown"; @@ -344,6 +345,7 @@ UM.ManagementPage return } materialProperties.name = currentItem.name; + materialProperties.guid = Cura.ContainerManager.getContainerMetaDataEntry(base.currentItem.id, "GUID"); if(currentItem.metadata != undefined && currentItem.metadata != null) { From a06f66305f35a13e938c77652b54763e8bb69363 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Tue, 13 Dec 2016 14:46:03 +0100 Subject: [PATCH 02/24] Calculate spool length and cost per meter --- resources/qml/Preferences/MaterialView.qml | 26 ++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/resources/qml/Preferences/MaterialView.qml b/resources/qml/Preferences/MaterialView.qml index d3efef353c..acdf076706 100644 --- a/resources/qml/Preferences/MaterialView.qml +++ b/resources/qml/Preferences/MaterialView.qml @@ -21,6 +21,28 @@ TabView property string containerId: "" property var materialPreferenceValues: UM.Preferences.getValue("cura/material_settings") ? JSON.parse(UM.Preferences.getValue("cura/material_settings")) : {} + property double spoolLength: + { + if (properties.diameter == 0 || properties.density == 0 || getMaterialPreferenceValue(properties.guid, "spool_weight") == 0) + { + return 0; + } + print(properties.diameter / 2); + var area = Math.PI * Math.pow(properties.diameter / 2, 2); // in mm2 + var volume = (getMaterialPreferenceValue(properties.guid, "spool_weight") / properties.density); // in cm3 + return volume / area; // in m + } + + property real costPerMeter: + { + if (spoolLength == 0) + { + return 0; + } + return getMaterialPreferenceValue(properties.guid, "spool_cost") / spoolLength; + } + + Tab { title: catalog.i18nc("@title","Information") @@ -163,7 +185,7 @@ TabView Label { width: base.secondColumnWidth - text: "%1 m".arg(properties.spool_length) + text: "%1 m".arg(Math.round(base.spoolLength)) verticalAlignment: Qt.AlignVCenter height: parent.rowHeight } @@ -172,7 +194,7 @@ TabView Label { width: base.secondColumnWidth - text: "%1 %2/m".arg(parseFloat(properties.cost_per_meter).toFixed(2)).arg(base.currency) + text: "%1 %2/m".arg(base.costPerMeter.toFixed(2)).arg(base.currency) verticalAlignment: Qt.AlignVCenter height: parent.rowHeight } From 4429825d155a2fa7f9996864dac8899717bda39f Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Tue, 13 Dec 2016 14:53:21 +0100 Subject: [PATCH 03/24] Show the calculated values are an approximation --- resources/qml/Preferences/MaterialView.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/Preferences/MaterialView.qml b/resources/qml/Preferences/MaterialView.qml index acdf076706..3e05ef0746 100644 --- a/resources/qml/Preferences/MaterialView.qml +++ b/resources/qml/Preferences/MaterialView.qml @@ -185,7 +185,7 @@ TabView Label { width: base.secondColumnWidth - text: "%1 m".arg(Math.round(base.spoolLength)) + text: "~ %1 m".arg(Math.round(base.spoolLength)) verticalAlignment: Qt.AlignVCenter height: parent.rowHeight } @@ -194,7 +194,7 @@ TabView Label { width: base.secondColumnWidth - text: "%1 %2/m".arg(base.costPerMeter.toFixed(2)).arg(base.currency) + text: "~ %1 %2/m".arg(base.costPerMeter.toFixed(2)).arg(base.currency) verticalAlignment: Qt.AlignVCenter height: parent.rowHeight } From ac34fe89eeee9017f993e8a41d3069ceefc39b37 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Tue, 13 Dec 2016 15:02:36 +0100 Subject: [PATCH 04/24] Make currency preference settable --- resources/qml/Preferences/GeneralPage.qml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/resources/qml/Preferences/GeneralPage.qml b/resources/qml/Preferences/GeneralPage.qml index eab5dbe938..69342563a0 100644 --- a/resources/qml/Preferences/GeneralPage.qml +++ b/resources/qml/Preferences/GeneralPage.qml @@ -128,6 +128,19 @@ UM.PreferencesPage currentIndex -= 1; } } + + Label + { + id: currencyLabel + text: catalog.i18nc("@label","Currency:") + anchors.verticalCenter: languageComboBox.verticalCenter + } + TextField + { + id: currencyField + text: UM.Preferences.getValue("cura/currency") + onTextChanged: UM.Preferences.setValue("cura/currency", text) + } } Label From 27cf300ba6b1b561bb1673ee4cabf27adc52f204 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Tue, 13 Dec 2016 15:59:00 +0100 Subject: [PATCH 05/24] Remove lingering debug statement --- resources/qml/Preferences/MaterialView.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/qml/Preferences/MaterialView.qml b/resources/qml/Preferences/MaterialView.qml index 3e05ef0746..d430f8deb3 100644 --- a/resources/qml/Preferences/MaterialView.qml +++ b/resources/qml/Preferences/MaterialView.qml @@ -27,7 +27,6 @@ TabView { return 0; } - print(properties.diameter / 2); var area = Math.PI * Math.pow(properties.diameter / 2, 2); // in mm2 var volume = (getMaterialPreferenceValue(properties.guid, "spool_weight") / properties.density); // in cm3 return volume / area; // in m From a83c397d690da8691692ecbc0c2920835f2b72e9 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Tue, 13 Dec 2016 16:22:29 +0100 Subject: [PATCH 06/24] Show material cost in Job Specs area... ...if weight/price information is available --- cura/PrintInformation.py | 30 ++++++++++++++++++++++- resources/qml/JobSpecs.qml | 49 +++++++++++++++++++++++++++++--------- 2 files changed, 67 insertions(+), 12 deletions(-) diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py index b65101ecc7..cabba1bb25 100644 --- a/cura/PrintInformation.py +++ b/cura/PrintInformation.py @@ -12,6 +12,7 @@ import cura.Settings.ExtruderManager import math import os.path import unicodedata +import json ## A class for processing and calculating minimum, current and maximum print time as well as managing the job name # @@ -48,6 +49,7 @@ class PrintInformation(QObject): self._material_lengths = [] self._material_weights = [] + self._material_costs = [] self._backend = Application.getInstance().getBackend() if self._backend: @@ -77,6 +79,12 @@ class PrintInformation(QObject): def materialWeights(self): return self._material_weights + materialCostsChanged = pyqtSignal() + + @pyqtProperty("QVariantList", notify = materialCostsChanged) + def materialCosts(self): + return self._material_costs + def _onPrintDurationMessage(self, total_time, material_amounts): self._current_print_time.setDuration(total_time) self.currentPrintTimeChanged.emit() @@ -85,20 +93,40 @@ class PrintInformation(QObject): r = Application.getInstance().getGlobalContainerStack().getProperty("material_diameter", "value") / 2 self._material_lengths = [] self._material_weights = [] + self._material_costs = [] + + material_preference_values = json.loads(Preferences.getInstance().getValue("cura/material_settings")) + extruder_stacks = list(cura.Settings.ExtruderManager.getInstance().getMachineExtruders(Application.getInstance().getGlobalContainerStack().getId())) for index, amount in enumerate(material_amounts): ## Find the right extruder stack. As the list isn't sorted because it's a annoying generator, we do some # list comprehension filtering to solve this for us. + material = None if extruder_stacks: # Multi extrusion machine extruder_stack = [extruder for extruder in extruder_stacks if extruder.getMetaDataEntry("position") == str(index)][0] density = extruder_stack.getMetaDataEntry("properties", {}).get("density", 0) + material = extruder_stack.findContainer({"type": "material"}) else: # Machine with no extruder stacks density = Application.getInstance().getGlobalContainerStack().getMetaDataEntry("properties", {}).get("density", 0) + material = Application.getInstance().getGlobalContainerStack().findContainer({"type": "material"}) - self._material_weights.append(float(amount) * float(density) / 1000) + weight = float(amount) * float(density) / 1000 + cost = 0 + if material: + material_guid = material.getMetaDataEntry("GUID") + if material_guid in material_preference_values: + weight_per_spool = float(material_preference_values[material_guid]["spool_weight"]) + cost_per_spool = float(material_preference_values[material_guid]["spool_cost"]) + + cost = cost_per_spool * weight / weight_per_spool + + self._material_weights.append(weight) self._material_lengths.append(round((amount / (math.pi * r ** 2)) / 1000, 2)) + self._material_costs.append(cost) + self.materialLengthsChanged.emit() self.materialWeightsChanged.emit() + self.materialCostsChanged.emit() @pyqtSlot(str) def setJobName(self, name): diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index 78f184f13c..00d22ae8a8 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -26,6 +26,7 @@ Rectangle { property variant printDuration: PrintInformation.currentPrintTime property variant printMaterialLengths: PrintInformation.materialLengths property variant printMaterialWeights: PrintInformation.materialWeights + property variant printMaterialCosts: PrintInformation.materialCosts height: childrenRect.height color: "transparent" @@ -133,7 +134,8 @@ Rectangle { } } - Label{ + Label + { id: boundingSpec anchors.top: jobNameRow.bottom anchors.right: parent.right @@ -144,17 +146,20 @@ Rectangle { text: Printer.getSceneBoundingBoxString } - Rectangle { + Rectangle + { id: specsRow anchors.top: boundingSpec.bottom anchors.right: parent.right height: UM.Theme.getSize("jobspecs_line").height - Item{ + Item + { width: parent.width height: parent.height - UM.RecolorImage { + UM.RecolorImage + { id: timeIcon anchors.right: timeSpec.left anchors.rightMargin: UM.Theme.getSize("default_margin").width/2 @@ -166,7 +171,8 @@ Rectangle { color: UM.Theme.getColor("text_subtext") source: UM.Theme.getIcon("print_time") } - Label{ + Label + { id: timeSpec anchors.right: lengthIcon.left anchors.rightMargin: UM.Theme.getSize("default_margin").width @@ -175,7 +181,8 @@ Rectangle { color: UM.Theme.getColor("text_subtext") text: (!base.printDuration || !base.printDuration.valid) ? catalog.i18nc("@label", "00h 00min") : base.printDuration.getDisplayString(UM.DurationFormat.Short) } - UM.RecolorImage { + UM.RecolorImage + { id: lengthIcon anchors.right: lengthSpec.left anchors.rightMargin: UM.Theme.getSize("default_margin").width/2 @@ -187,7 +194,8 @@ Rectangle { color: UM.Theme.getColor("text_subtext") source: UM.Theme.getIcon("category_material") } - Label{ + Label + { id: lengthSpec anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter @@ -197,19 +205,38 @@ Rectangle { { var lengths = []; var weights = []; + var costs = []; + var someCostsKnown = false; if(base.printMaterialLengths) { - for(var index = 0; index < base.printMaterialLengths.length; index++) { - if(base.printMaterialLengths[index] > 0) { + for(var index = 0; index < base.printMaterialLengths.length; index++) + { + if(base.printMaterialLengths[index] > 0) + { lengths.push(base.printMaterialLengths[index].toFixed(2)); weights.push(String(Math.floor(base.printMaterialWeights[index]))); + costs.push(base.printMaterialCosts[index].toFixed(2)); + if(base.printMaterialCosts[index] > 0) + { + someCostsKnown = true; + } } } } - if(lengths.length == 0) { + if(lengths.length == 0) + { lengths = ["0.00"]; weights = ["0"]; + costs = ["0.00"]; + } + if(someCostsKnown) + { + return catalog.i18nc("@label", "%1 m / ~ %2 g / ~ %4 %3").arg(lengths.join(" + ")) + .arg(weights.join(" + ")).arg(costs.join(" + ")).arg(UM.Preferences.getValue("cura/currency")); + } + else + { + return catalog.i18nc("@label", "%1 m / ~ %2 g").arg(lengths.join(" + ")).arg(weights.join(" + ")); } - return catalog.i18nc("@label", "%1 m / ~ %2 g").arg(lengths.join(" + ")).arg(weights.join(" + ")); } } } From 6479f2f9388585540d12045b34435ba72d0c4500 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Tue, 3 Jan 2017 11:11:57 +0100 Subject: [PATCH 07/24] Remove redundant "(Approx.)" It is made redundant by the ~, and Approx. is an ugly abbreviation anyway --- resources/qml/Preferences/MaterialView.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Preferences/MaterialView.qml b/resources/qml/Preferences/MaterialView.qml index d430f8deb3..ee07586073 100644 --- a/resources/qml/Preferences/MaterialView.qml +++ b/resources/qml/Preferences/MaterialView.qml @@ -189,7 +189,7 @@ TabView height: parent.rowHeight } - Label { width: base.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") } Label { width: base.secondColumnWidth From ebbe37a6cdadf0cf16cf05be9ebe489b8115d694 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Tue, 3 Jan 2017 11:23:31 +0100 Subject: [PATCH 08/24] Remove "Edit" button from materials page The "Edit" button is an unnecessary barrier. Read-only materials are still uneditable. --- resources/qml/Preferences/MaterialsPage.qml | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/resources/qml/Preferences/MaterialsPage.qml b/resources/qml/Preferences/MaterialsPage.qml index 0c780d165d..6072541976 100644 --- a/resources/qml/Preferences/MaterialsPage.qml +++ b/resources/qml/Preferences/MaterialsPage.qml @@ -185,17 +185,6 @@ UM.ManagementPage height: childrenRect.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"; - - enabled: base.currentItem != null && !base.currentItem.readOnly - - checkable: enabled - } } MaterialView @@ -209,7 +198,7 @@ UM.ManagementPage bottom: parent.bottom } - editingEnabled: editButton.checkable && editButton.checked; + editingEnabled: base.currentItem != null && !base.currentItem.readOnly properties: materialProperties containerId: base.currentItem != null ? base.currentItem.id : "" From 4ba8b4e7c80c44487490cec3484f774a6db0d7db Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 9 Jan 2017 14:08:50 +0100 Subject: [PATCH 09/24] Update spool length and cost per meter when editing values in materials dialog --- resources/qml/Preferences/MaterialView.qml | 77 ++++++++++++++++------ 1 file changed, 57 insertions(+), 20 deletions(-) diff --git a/resources/qml/Preferences/MaterialView.qml b/resources/qml/Preferences/MaterialView.qml index ee07586073..11df30e116 100644 --- a/resources/qml/Preferences/MaterialView.qml +++ b/resources/qml/Preferences/MaterialView.qml @@ -21,30 +21,13 @@ TabView property string containerId: "" property var materialPreferenceValues: UM.Preferences.getValue("cura/material_settings") ? JSON.parse(UM.Preferences.getValue("cura/material_settings")) : {} - property double spoolLength: - { - if (properties.diameter == 0 || properties.density == 0 || getMaterialPreferenceValue(properties.guid, "spool_weight") == 0) - { - return 0; - } - var area = Math.PI * Math.pow(properties.diameter / 2, 2); // in mm2 - var volume = (getMaterialPreferenceValue(properties.guid, "spool_weight") / properties.density); // in cm3 - return volume / area; // in m - } - - property real costPerMeter: - { - if (spoolLength == 0) - { - return 0; - } - return getMaterialPreferenceValue(properties.guid, "spool_cost") / spoolLength; - } - + property double spoolLength: calculateSpoolLength() + property real costPerMeter: calculateCostPerMeter() Tab { title: catalog.i18nc("@title","Information") + anchors { leftMargin: UM.Theme.getSize("default_margin").width @@ -134,6 +117,7 @@ TabView Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Density") } ReadOnlySpinBox { + id: densitySpinBox width: base.secondColumnWidth value: properties.density decimals: 2 @@ -142,11 +126,13 @@ TabView readOnly: !base.editingEnabled onEditingFinished: base.setMetaDataEntry("properties/density", properties.density, value) + onValueChanged: updateCostPerMeter() } Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Diameter") } ReadOnlySpinBox { + id: diameterSpinBox width: base.secondColumnWidth value: properties.diameter decimals: 2 @@ -155,29 +141,36 @@ TabView readOnly: !base.editingEnabled onEditingFinished: base.setMetaDataEntry("properties/diameter", properties.diameter, value) + onValueChanged: updateCostPerMeter() } Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament Cost") } SpinBox { + id: spoolCostSpinBox width: base.secondColumnWidth value: base.getMaterialPreferenceValue(properties.guid, "spool_cost") prefix: base.currency + " " decimals: 2 maximumValue: 1000 + onEditingFinished: base.setMaterialPreferenceValue(properties.guid, "spool_cost", parseFloat(value)) + onValueChanged: updateCostPerMeter() } Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament weight") } SpinBox { + id: spoolWeightSpinBox width: base.secondColumnWidth value: base.getMaterialPreferenceValue(properties.guid, "spool_weight") suffix: " g" stepSize: 100 decimals: 0 maximumValue: 10000 + onEditingFinished: base.setMaterialPreferenceValue(properties.guid, "spool_weight", parseFloat(value)) + onValueChanged: updateCostPerMeter() } Label { width: base.firstColumnWidth; height: parent.rowHeight; verticalAlignment: Qt.AlignVCenter; text: catalog.i18nc("@label", "Filament length") } @@ -226,6 +219,12 @@ TabView onEditingFinished: base.setMetaDataEntry("adhesion_info", properties.adhesion_info, text) } } + function updateCostPerMeter() + { + base.spoolLength = calculateSpoolLength(diameterSpinBox.value, densitySpinBox.value, spoolWeightSpinBox.value); + base.costPerMeter = calculateCostPerMeter(spoolCostSpinBox.value); + } + } } @@ -285,6 +284,44 @@ TabView } } + function calculateSpoolLength(diameter, density, spoolWeight) + { + if(!diameter) + { + diameter = properties.diameter; + } + if(!density) + { + density = properties.density; + } + if(!spoolWeight) + { + spoolWeight = base.getMaterialPreferenceValue(properties.guid, "spool_weight"); + } + + if (diameter == 0 || density == 0 || spoolWeight == 0) + { + return 0; + } + var area = Math.PI * Math.pow(diameter / 2, 2); // in mm2 + var volume = (spoolWeight / density); // in cm3 + return volume / area; // in m + } + + function calculateCostPerMeter(spoolCost) + { + if(!spoolCost) + { + spoolCost = base.getMaterialPreferenceValue(properties.guid, "spool_cost"); + } + + if (spoolLength == 0) + { + return 0; + } + return spoolCost / spoolLength; + } + // Tiny convenience function to check if a value really changed before trying to set it. function setMetaDataEntry(entry_name, old_value, new_value) { From c48f02a7ebc29d57d35cf40bb167392b64b3917d Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 9 Jan 2017 14:19:22 +0100 Subject: [PATCH 10/24] Fix horizontal scrolling/flicking of materialview (probably windows-only) --- 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 11df30e116..17f76466ab 100644 --- a/resources/qml/Preferences/MaterialView.qml +++ b/resources/qml/Preferences/MaterialView.qml @@ -40,6 +40,7 @@ TabView { anchors.fill: parent horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff + flickableItem.flickableDirection: Flickable.VerticalFlick Flow { @@ -219,12 +220,12 @@ TabView onEditingFinished: base.setMetaDataEntry("adhesion_info", properties.adhesion_info, text) } } + function updateCostPerMeter() { base.spoolLength = calculateSpoolLength(diameterSpinBox.value, densitySpinBox.value, spoolWeightSpinBox.value); base.costPerMeter = calculateCostPerMeter(spoolCostSpinBox.value); } - } } From 6f06e9b3202b08b681d33f13be32a01cfc8e14e8 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 9 Jan 2017 15:24:20 +0100 Subject: [PATCH 11/24] Update material costs in slice info area when changing material settings (without reslicing) --- cura/PrintInformation.py | 43 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py index cabba1bb25..b9593d57ee 100644 --- a/cura/PrintInformation.py +++ b/cura/PrintInformation.py @@ -6,6 +6,7 @@ from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty, pyqtSlot from UM.Application import Application from UM.Qt.Duration import Duration from UM.Preferences import Preferences +from UM.Settings import ContainerRegistry import cura.Settings.ExtruderManager @@ -61,6 +62,12 @@ class PrintInformation(QObject): Application.getInstance().globalContainerStackChanged.connect(self._setAbbreviatedMachineName) Application.getInstance().fileLoaded.connect(self.setJobName) + Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged) + + self._active_material_container = None + Application.getInstance().getMachineManager().activeMaterialChanged.connect(self._onActiveMaterialChanged) + self._onActiveMaterialChanged() + currentPrintTimeChanged = pyqtSignal() @pyqtProperty(Duration, notify = currentPrintTimeChanged) @@ -89,6 +96,10 @@ class PrintInformation(QObject): self._current_print_time.setDuration(total_time) self.currentPrintTimeChanged.emit() + self._material_amounts = material_amounts + self._calculateInformation() + + def _calculateInformation(self): # 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_lengths = [] @@ -98,7 +109,7 @@ class PrintInformation(QObject): material_preference_values = json.loads(Preferences.getInstance().getValue("cura/material_settings")) extruder_stacks = list(cura.Settings.ExtruderManager.getInstance().getMachineExtruders(Application.getInstance().getGlobalContainerStack().getId())) - for index, amount in enumerate(material_amounts): + for index, amount in enumerate(self._material_amounts): ## Find the right extruder stack. As the list isn't sorted because it's a annoying generator, we do some # list comprehension filtering to solve this for us. material = None @@ -115,10 +126,15 @@ class PrintInformation(QObject): if material: material_guid = material.getMetaDataEntry("GUID") if material_guid in material_preference_values: - weight_per_spool = float(material_preference_values[material_guid]["spool_weight"]) - cost_per_spool = float(material_preference_values[material_guid]["spool_cost"]) + material_values = material_preference_values[material_guid] - cost = cost_per_spool * weight / weight_per_spool + weight_per_spool = float(material_values["spool_weight"] if material_values and "spool_weight" in material_values else 0) + cost_per_spool = float(material_values["spool_cost"] if material_values and "spool_cost" in material_values else 0) + + if weight_per_spool != 0: + cost = cost_per_spool * weight / weight_per_spool + else: + cost = 0 self._material_weights.append(weight) self._material_lengths.append(round((amount / (math.pi * r ** 2)) / 1000, 2)) @@ -128,6 +144,25 @@ class PrintInformation(QObject): self.materialWeightsChanged.emit() self.materialCostsChanged.emit() + def _onPreferencesChanged(self, preference): + if preference != "cura/material_settings": + return + + self._calculateInformation() + + def _onActiveMaterialChanged(self): + if self._active_material_container: + self._active_material_container.metaDataChanged.disconnect(self._onMaterialMetaDataChanged) + + active_material_id = Application.getInstance().getMachineManager().activeMaterialId + self._active_material_container = ContainerRegistry.getInstance().findInstanceContainers(id=active_material_id)[0] + + if self._active_material_container: + self._active_material_container.metaDataChanged.connect(self._onMaterialMetaDataChanged) + + def _onMaterialMetaDataChanged(self): + self._calculateInformation() + @pyqtSlot(str) def setJobName(self, name): # Ensure that we don't use entire path but only filename From f6110623ce7cd2f07c7fc5d6f14d598dcbd16ac5 Mon Sep 17 00:00:00 2001 From: Chris Petersen Date: Wed, 18 Jan 2017 01:15:59 -0800 Subject: [PATCH 12/24] Fix typos in kossel_pro gcode variable usage Replace double-braces with single-braces so that `material_bed_temperature` and `material_print_temperature` are interpolated properly. --- resources/definitions/kossel_pro.def.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/definitions/kossel_pro.def.json b/resources/definitions/kossel_pro.def.json index a0774b3a06..e8403a1727 100644 --- a/resources/definitions/kossel_pro.def.json +++ b/resources/definitions/kossel_pro.def.json @@ -45,7 +45,7 @@ "default_value": "RepRap (Marlin/Sprinter)" }, "machine_start_gcode": { - "default_value": "; info: M303 E0 S200 C8 ; Pid auto-tune \n\nM140 S{{material_bed_temperature}}; Start heating up the base\nG28 ; Home to top 3 endstops\n; Autolevel and adjust first layer\n; Adjust this value to fit your own printer! (positive is thicker)\n; This default value is intentionally very high to accommodate the\n; variety of print heads used with this printer. Many of you will\n; need tiny values like Z0 or Z0.1. Use feeler gauges to dial this\n; in as accurately as possible.\nG29 Z10\n\n; Squirt and wipe ;\nM109 S220 ; Wait for the temp to hit 220\nG00 X125 Y-60 Z0.1 ;\nG92 E0 ;\nG01 E25 F100 ; Extrude a little bit to replace oozage from auto levelling\nG01 X90 Y-50 F6000 ;\nG01 Z5 ;\n\n; Set the extruder to the requested print temperature\nM104 S{{material_print_temperature}}\n" + "default_value": "; info: M303 E0 S200 C8 ; Pid auto-tune \n\nM140 S{material_bed_temperature}; Start heating up the base\nG28 ; Home to top 3 endstops\n; Autolevel and adjust first layer\n; Adjust this value to fit your own printer! (positive is thicker)\n; This default value is intentionally very high to accommodate the\n; variety of print heads used with this printer. Many of you will\n; need tiny values like Z0 or Z0.1. Use feeler gauges to dial this\n; in as accurately as possible.\nG29 Z10\n\n; Squirt and wipe ;\nM109 S220 ; Wait for the temp to hit 220\nG00 X125 Y-60 Z0.1 ;\nG92 E0 ;\nG01 E25 F100 ; Extrude a little bit to replace oozage from auto levelling\nG01 X90 Y-50 F6000 ;\nG01 Z5 ;\n\n; Set the extruder to the requested print temperature\nM104 S{material_print_temperature}\n" }, "machine_end_gcode": { "default_value": "M104 S0 ; turn off temperature\nM140 S0 ; turn off bed\nG28 ; home all axes\nM84 ; disable motors\n" @@ -54,4 +54,4 @@ "default_value": "elliptic" } } -} \ No newline at end of file +} From c017013aaee66412519334ea03b948beb50d1eec Mon Sep 17 00:00:00 2001 From: Mark Burton Date: Thu, 19 Jan 2017 11:48:17 +0000 Subject: [PATCH 13/24] Added min_infill_area setting. This allows the user to specify in mm^2 the minimum area of infill regions. Areas smaller than this will be merged into the surrounding skin rather than being filled with infill. --- resources/definitions/fdmprinter.def.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 6627754274..3be63b555d 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -1230,6 +1230,16 @@ "default_value": true, "enabled": "infill_sparse_density > 0", "settable_per_mesh": true + }, + "min_infill_area": + { + "label": "Min Infill Area", + "description": "Don't generate areas of infill smaller than this (use skin instead).", + "unit": "mm²", + "type": "float", + "minimum_value": "0", + "default_value": 0, + "settable_per_mesh": true } } }, From 3d58bd4d4934bdb19c71400dca38bd8a60febdab Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 23 Jan 2017 15:06:49 +0100 Subject: [PATCH 14/24] Workspace & load image dialog can now be resized CURA-3247 --- plugins/ImageReader/ConfigUI.qml | 2 -- resources/qml/WorkspaceSummaryDialog.qml | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/plugins/ImageReader/ConfigUI.qml b/plugins/ImageReader/ConfigUI.qml index 1df57d74c0..893f8e248c 100644 --- a/plugins/ImageReader/ConfigUI.qml +++ b/plugins/ImageReader/ConfigUI.qml @@ -12,11 +12,9 @@ UM.Dialog { width: 350 * Screen.devicePixelRatio; minimumWidth: 350 * Screen.devicePixelRatio; - maximumWidth: 350 * Screen.devicePixelRatio; height: 250 * Screen.devicePixelRatio; minimumHeight: 250 * Screen.devicePixelRatio; - maximumHeight: 250 * Screen.devicePixelRatio; title: catalog.i18nc("@title:window", "Convert Image...") diff --git a/resources/qml/WorkspaceSummaryDialog.qml b/resources/qml/WorkspaceSummaryDialog.qml index 2072ddb1e7..4c0a7bf86d 100644 --- a/resources/qml/WorkspaceSummaryDialog.qml +++ b/resources/qml/WorkspaceSummaryDialog.qml @@ -15,11 +15,10 @@ UM.Dialog width: 550 minimumWidth: 550 - maximumWidth: 550 height: 350 minimumHeight: 350 - maximumHeight: 350 + property int spacerHeight: 10 property bool dontShowAgain: true From 1727b6dbe1aa9478e3d6bb5534fa0d29129e80b4 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 23 Jan 2017 17:18:27 +0100 Subject: [PATCH 15/24] Add entry that we switched Linux to AppImage format It was deemed worthy to mention. Contributes to issue CURA-3048. --- plugins/ChangeLogPlugin/ChangeLog.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/ChangeLogPlugin/ChangeLog.txt b/plugins/ChangeLogPlugin/ChangeLog.txt index c98f0a7c6b..265a076d25 100644 --- a/plugins/ChangeLogPlugin/ChangeLog.txt +++ b/plugins/ChangeLogPlugin/ChangeLog.txt @@ -98,6 +98,9 @@ Use a mesh to specify a volume within which to classify nothing as overhang for *Delta printer support This release adds support for printers with elliptic buildplates. This feature has not been extensively tested so please let us know if it works or get involved in improving it. +*AppImage for Linux +The Linux distribution is now in AppImage format, which makes Cura easier to install. + *bugfixes The user is now notified when a new version of Cura is available. When searching in the setting visibility preferences, the category for each setting is always displayed. From 32823a01836ef2774c442b4ce8a40947d376dd47 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 23 Jan 2017 20:06:06 +0100 Subject: [PATCH 16/24] Add viewport colors to cura theme --- resources/themes/cura/theme.json | 39 +++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/resources/themes/cura/theme.json b/resources/themes/cura/theme.json index d5a95a7104..31caeeabd4 100644 --- a/resources/themes/cura/theme.json +++ b/resources/themes/cura/theme.json @@ -192,7 +192,44 @@ "status_ready": [0, 205, 0, 255], "status_busy": [12, 169, 227, 255], "status_paused": [255, 140, 0, 255], - "status_stopped": [236, 82, 80, 255] + "status_stopped": [236, 82, 80, 255], + + "disabled_axis": [127, 127, 127, 255], + "x_axis": [255, 0, 0, 255], + "y_axis": [0, 0, 255, 255], + "z_axis": [0, 255, 0, 255], + "all_axis": [255, 255, 255, 255], + + "viewport_background": [245, 245, 245, 255], + "volume_outline": [12, 169, 227, 255], + "buildplate": [244, 244, 244, 255], + "buildplate_alt": [204, 204, 204, 255], + + "convex_hull": [35, 35, 35, 127], + "disallowed_area": [0, 0, 0, 40], + "error_area": [255, 0, 0, 127], + + "model_default": [255, 201, 36, 255], + "model_overhang": [255, 0, 0, 255], + "model_unslicable": [122, 122, 122, 255], + "model_unslicable_alt": [172, 172, 127, 255], + "model_selection_outline": [12, 169, 227, 255], + + "xray": [26, 26, 62, 255], + "xray_error": [255, 0, 0, 255], + + "layerview_ghost": [32, 32, 32, 96], + "layerview_none": [255, 255, 255, 255], + "layerview_inset_0": [255, 0, 0, 255], + "layerview_inset_x": [0, 255, 0, 255], + "layerview_skin": [255, 255, 0, 255], + "layerview_support": [0, 255, 255, 255], + "layerview_skirt": [0, 255, 255, 255], + "layerview_infill": [255, 192, 0, 255], + "layerview_support_infill": [0, 255, 255, 255], + "layerview_move_combing": [0, 0, 255, 255], + "layerview_move_retraction": [128, 128, 255, 255], + "layerview_support_interface": [64, 192, 255, 255] }, "sizes": { From 45c808342389ef38405c4be512dc2d8c8a152f6a Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 23 Jan 2017 20:08:45 +0100 Subject: [PATCH 17/24] Use themeable colors in Solid, Xray & Layer view --- cura/BuildVolume.py | 76 +++++++++++++++++--------- cura/ConvexHullNode.py | 5 +- plugins/LayerView/LayerView.py | 5 +- plugins/SolidView/SolidView.py | 7 ++- plugins/XRayView/XRayView.py | 8 ++- plugins/XRayView/xray_composite.shader | 4 +- 6 files changed, 72 insertions(+), 33 deletions(-) diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index 1691361629..707a5df2a7 100644 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -34,16 +34,18 @@ PRIME_CLEARANCE = 6.5 ## Build volume is a special kind of node that is responsible for rendering the printable area & disallowed areas. class BuildVolume(SceneNode): - VolumeOutlineColor = Color(12, 169, 227, 255) - XAxisColor = Color(255, 0, 0, 255) - YAxisColor = Color(0, 0, 255, 255) - ZAxisColor = Color(0, 255, 0, 255) - raftThicknessChanged = Signal() def __init__(self, parent = None): super().__init__(parent) + self._volume_outline_color = None + self._x_axis_color = None + self._y_axis_color = None + self._z_axis_color = None + self._disallowed_area_color = None + self._error_area_color = None + self._width = 0 self._height = 0 self._depth = 0 @@ -75,6 +77,9 @@ class BuildVolume(SceneNode): Application.getInstance().globalContainerStackChanged.connect(self._onStackChanged) self._onStackChanged() + self._engine_ready = False + Application.getInstance().engineCreatedSignal.connect(self._onEngineCreated) + self._has_errors = False Application.getInstance().getController().getScene().sceneChanged.connect(self._onSceneChanged) @@ -99,6 +104,7 @@ class BuildVolume(SceneNode): # but it does not update the disallowed areas after material change Application.getInstance().getMachineManager().activeStackChanged.connect(self._onStackChanged) + def _onSceneChanged(self, source): if self._global_container_stack: self._change_timer.start() @@ -158,6 +164,9 @@ class BuildVolume(SceneNode): if not self._shader: self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "default.shader")) self._grid_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "grid.shader")) + theme = Application.getInstance().getTheme() + self._grid_shader.setUniformValue("u_gridColor0", Color(*theme.getColor("buildplate").getRgb())) + self._grid_shader.setUniformValue("u_gridColor1", Color(*theme.getColor("buildplate_alt").getRgb())) renderer.queueNode(self, mode = RenderBatch.RenderMode.Lines) renderer.queueNode(self, mesh = self._origin_mesh) @@ -176,6 +185,18 @@ class BuildVolume(SceneNode): if not self._width or not self._height or not self._depth: return + if not Application.getInstance()._engine: + return + + if not self._volume_outline_color: + theme = Application.getInstance().getTheme() + self._volume_outline_color = Color(*theme.getColor("volume_outline").getRgb()) + self._x_axis_color = Color(*theme.getColor("x_axis").getRgb()) + self._y_axis_color = Color(*theme.getColor("y_axis").getRgb()) + self._z_axis_color = Color(*theme.getColor("z_axis").getRgb()) + self._disallowed_area_color = Color(*theme.getColor("disallowed_area").getRgb()) + self._error_area_color = Color(*theme.getColor("error_area").getRgb()) + min_w = -self._width / 2 max_w = self._width / 2 min_h = 0.0 @@ -188,20 +209,20 @@ class BuildVolume(SceneNode): if self._shape != "elliptic": # Outline 'cube' of the build volume mb = MeshBuilder() - mb.addLine(Vector(min_w, min_h, min_d), Vector(max_w, min_h, min_d), color = self.VolumeOutlineColor) - mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, max_h, min_d), color = self.VolumeOutlineColor) - mb.addLine(Vector(min_w, max_h, min_d), Vector(max_w, max_h, min_d), color = self.VolumeOutlineColor) - mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, max_h, min_d), color = self.VolumeOutlineColor) + mb.addLine(Vector(min_w, min_h, min_d), Vector(max_w, min_h, min_d), color = self._volume_outline_color) + mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, max_h, min_d), color = self._volume_outline_color) + mb.addLine(Vector(min_w, max_h, min_d), Vector(max_w, max_h, min_d), color = self._volume_outline_color) + mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, max_h, min_d), color = self._volume_outline_color) - mb.addLine(Vector(min_w, min_h, max_d), Vector(max_w, min_h, max_d), color = self.VolumeOutlineColor) - mb.addLine(Vector(min_w, min_h, max_d), Vector(min_w, max_h, max_d), color = self.VolumeOutlineColor) - mb.addLine(Vector(min_w, max_h, max_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor) - mb.addLine(Vector(max_w, min_h, max_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor) + mb.addLine(Vector(min_w, min_h, max_d), Vector(max_w, min_h, max_d), color = self._volume_outline_color) + mb.addLine(Vector(min_w, min_h, max_d), Vector(min_w, max_h, max_d), color = self._volume_outline_color) + mb.addLine(Vector(min_w, max_h, max_d), Vector(max_w, max_h, max_d), color = self._volume_outline_color) + mb.addLine(Vector(max_w, min_h, max_d), Vector(max_w, max_h, max_d), color = self._volume_outline_color) - mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, min_h, max_d), color = self.VolumeOutlineColor) - mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, min_h, max_d), color = self.VolumeOutlineColor) - mb.addLine(Vector(min_w, max_h, min_d), Vector(min_w, max_h, max_d), color = self.VolumeOutlineColor) - mb.addLine(Vector(max_w, max_h, min_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor) + mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, min_h, max_d), color = self._volume_outline_color) + mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, min_h, max_d), color = self._volume_outline_color) + mb.addLine(Vector(min_w, max_h, min_d), Vector(min_w, max_h, max_d), color = self._volume_outline_color) + mb.addLine(Vector(max_w, max_h, min_d), Vector(max_w, max_h, max_d), color = self._volume_outline_color) self.setMeshData(mb.build()) @@ -228,8 +249,8 @@ class BuildVolume(SceneNode): aspect = self._depth / self._width scale_matrix.compose(scale = Vector(1, 1, aspect)) mb = MeshBuilder() - mb.addArc(max_w, Vector.Unit_Y, center = (0, min_h - z_fight_distance, 0), color = self.VolumeOutlineColor) - mb.addArc(max_w, Vector.Unit_Y, center = (0, max_h, 0), color = self.VolumeOutlineColor) + mb.addArc(max_w, Vector.Unit_Y, center = (0, min_h - z_fight_distance, 0), color = self._volume_outline_color) + mb.addArc(max_w, Vector.Unit_Y, center = (0, max_h, 0), color = self._volume_outline_color) self.setMeshData(mb.build().getTransformed(scale_matrix)) # Build plate grid mesh @@ -260,21 +281,21 @@ class BuildVolume(SceneNode): height = self._origin_line_width, depth = self._origin_line_width, center = origin + Vector(self._origin_line_length / 2, 0, 0), - color = self.XAxisColor + color = self._x_axis_color ) mb.addCube( width = self._origin_line_width, height = self._origin_line_length, depth = self._origin_line_width, center = origin + Vector(0, self._origin_line_length / 2, 0), - color = self.YAxisColor + color = self._y_axis_color ) mb.addCube( width = self._origin_line_width, height = self._origin_line_width, depth = self._origin_line_length, center = origin - Vector(0, 0, self._origin_line_length / 2), - color = self.ZAxisColor + color = self._z_axis_color ) self._origin_mesh = mb.build() @@ -282,7 +303,7 @@ class BuildVolume(SceneNode): disallowed_area_size = 0 if self._disallowed_areas: mb = MeshBuilder() - color = Color(0.0, 0.0, 0.0, 0.15) + color = self._disallowed_area_color for polygon in self._disallowed_areas: points = polygon.getPoints() if len(points) == 0: @@ -311,7 +332,7 @@ class BuildVolume(SceneNode): if self._error_areas: mb = MeshBuilder() for error_area in self._error_areas: - color = Color(1.0, 0.0, 0.0, 0.5) + color = self._error_area_color points = error_area.getPoints() first = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._clamp(points[0][1], min_d, max_d)) @@ -398,7 +419,12 @@ class BuildVolume(SceneNode): self._updateDisallowedAreas() self._updateRaftThickness() - self.rebuild() + if self._engine_ready: + self.rebuild() + + def _onEngineCreated(self): + self._engine_ready = True + self.rebuild() def _onSettingPropertyChanged(self, setting_key, property_name): if property_name != "value": diff --git a/cura/ConvexHullNode.py b/cura/ConvexHullNode.py index 8e5acf9518..7282b0ffb2 100644 --- a/cura/ConvexHullNode.py +++ b/cura/ConvexHullNode.py @@ -1,6 +1,7 @@ # Copyright (c) 2015 Ultimaker B.V. # Cura is released under the terms of the AGPLv3 or higher. +from UM.Application import Application from UM.Scene.SceneNode import SceneNode from UM.Resources import Resources from UM.Math.Color import Color @@ -23,7 +24,7 @@ class ConvexHullNode(SceneNode): self._original_parent = parent # Color of the drawn convex hull - self._color = Color(0.4, 0.4, 0.4, 1.0) + self._color = None # The y-coordinate of the convex hull mesh. Must not be 0, to prevent z-fighting. self._mesh_height = 0.1 @@ -72,7 +73,7 @@ class ConvexHullNode(SceneNode): return True def _onNodeDecoratorsChanged(self, node): - self._color = Color(35, 35, 35, 0.5) + self._color = Color(*Application.getInstance().getTheme().getColor("convex_hull").getRgb()) convex_hull_head = self._node.callDecoration("getConvexHullHead") if convex_hull_head: diff --git a/plugins/LayerView/LayerView.py b/plugins/LayerView/LayerView.py index 0bae9c891c..bb315e5b57 100644 --- a/plugins/LayerView/LayerView.py +++ b/plugins/LayerView/LayerView.py @@ -110,7 +110,7 @@ class LayerView(View): if not self._ghost_shader: self._ghost_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "color.shader")) - self._ghost_shader.setUniformValue("u_color", Color(32, 32, 32, 96)) + self._ghost_shader.setUniformValue("u_color", Color(*Application.getInstance().getTheme().getColor("layerview_ghost").getRgb())) for node in DepthFirstIterator(scene.getRoot()): # We do not want to render ConvexHullNode as it conflicts with the bottom layers. @@ -194,6 +194,9 @@ class LayerView(View): if not self._layerview_composite_shader: self._layerview_composite_shader = OpenGL.getInstance().createShaderProgram(os.path.join(PluginRegistry.getInstance().getPluginPath("LayerView"), "layerview_composite.shader")) + theme = Application.getInstance().getTheme() + self._layerview_composite_shader.setUniformValue("u_background_color", Color(*theme.getColor("viewport_background").getRgb())) + self._layerview_composite_shader.setUniformValue("u_outline_color", Color(*theme.getColor("model_selection_outline").getRgb())) if not self._composite_pass: self._composite_pass = self.getRenderer().getRenderPass("composite") diff --git a/plugins/SolidView/SolidView.py b/plugins/SolidView/SolidView.py index 8277813c92..065b73b24f 100644 --- a/plugins/SolidView/SolidView.py +++ b/plugins/SolidView/SolidView.py @@ -11,7 +11,7 @@ from UM.View.Renderer import Renderer from UM.Settings.Validator import ValidatorState from UM.View.GL.OpenGL import OpenGL - +import UM.Qt.Bindings.Theme import cura.Settings from cura.Settings.ExtruderManager import ExtruderManager @@ -38,8 +38,9 @@ class SolidView(View): if not self._disabled_shader: self._disabled_shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "striped.shader")) - self._disabled_shader.setUniformValue("u_diffuseColor1", [0.48, 0.48, 0.48, 1.0]) - self._disabled_shader.setUniformValue("u_diffuseColor2", [0.68, 0.68, 0.68, 1.0]) + theme = Application.getInstance().getTheme() + self._disabled_shader.setUniformValue("u_diffuseColor1", theme.getColor("model_unslicable").getRgbF()) + self._disabled_shader.setUniformValue("u_diffuseColor2", theme.getColor("model_unslicable_alt").getRgbF()) self._disabled_shader.setUniformValue("u_width", 50.0) multi_extrusion = False diff --git a/plugins/XRayView/XRayView.py b/plugins/XRayView/XRayView.py index 9913ee786f..931ecb1975 100644 --- a/plugins/XRayView/XRayView.py +++ b/plugins/XRayView/XRayView.py @@ -3,6 +3,8 @@ import os.path +from UM.Application import Application +from UM.Math.Color import Color from UM.PluginRegistry import PluginRegistry from UM.Event import Event from UM.View.View import View @@ -31,7 +33,7 @@ class XRayView(View): if not self._xray_shader: self._xray_shader = OpenGL.getInstance().createShaderProgram(os.path.join(PluginRegistry.getInstance().getPluginPath("XRayView"), "xray.shader")) - self._xray_shader.setUniformValue("u_color", [0.1, 0.1, 0.2, 1.0]) + self._xray_shader.setUniformValue("u_color", Color(*Application.getInstance().getTheme().getColor("xray").getRgb())) for node in BreadthFirstIterator(scene.getRoot()): if not node.render(renderer): @@ -58,6 +60,10 @@ class XRayView(View): if not self._xray_composite_shader: self._xray_composite_shader = OpenGL.getInstance().createShaderProgram(os.path.join(PluginRegistry.getInstance().getPluginPath("XRayView"), "xray_composite.shader")) + theme = Application.getInstance().getTheme() + self._xray_composite_shader.setUniformValue("u_background_color", Color(*theme.getColor("viewport_background").getRgb())) + self._xray_composite_shader.setUniformValue("u_error_color", Color(*theme.getColor("xray_error").getRgb())) + self._xray_composite_shader.setUniformValue("u_outline_color", Color(*theme.getColor("model_selection_outline").getRgb())) if not self._composite_pass: self._composite_pass = self.getRenderer().getRenderPass("composite") diff --git a/plugins/XRayView/xray_composite.shader b/plugins/XRayView/xray_composite.shader index f6e67c1d0f..e7a38950bf 100644 --- a/plugins/XRayView/xray_composite.shader +++ b/plugins/XRayView/xray_composite.shader @@ -22,6 +22,7 @@ fragment = uniform float u_outline_strength; uniform vec4 u_outline_color; uniform vec4 u_error_color; + uniform vec4 u_background_color; const vec3 x_axis = vec3(1.0, 0.0, 0.0); const vec3 y_axis = vec3(0.0, 1.0, 0.0); @@ -37,7 +38,7 @@ fragment = kernel[3] = 1.0; kernel[4] = -4.0; kernel[5] = 1.0; kernel[6] = 0.0; kernel[7] = 1.0; kernel[8] = 0.0; - vec4 result = vec4(0.965, 0.965, 0.965, 1.0); + vec4 result = u_background_color; vec4 layer0 = texture2D(u_layer0, v_uvs); result = layer0 * layer0.a + result * (1.0 - layer0.a); @@ -70,6 +71,7 @@ fragment = u_layer0 = 0 u_layer1 = 1 u_layer2 = 2 +u_background_color = [0.965, 0.965, 0.965, 1.0] u_outline_strength = 1.0 u_outline_color = [0.05, 0.66, 0.89, 1.0] u_error_color = [1.0, 0.0, 0.0, 1.0] From 5e121e4039feb2359e29908cf304944303137ec1 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 23 Jan 2017 22:11:38 +0100 Subject: [PATCH 18/24] Use themeable colors for layerdata --- cura/LayerPolygon.py | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/cura/LayerPolygon.py b/cura/LayerPolygon.py index cb00bd0c60..3c228610bc 100644 --- a/cura/LayerPolygon.py +++ b/cura/LayerPolygon.py @@ -1,4 +1,5 @@ from UM.Math.Color import Color +from UM.Application import Application import numpy @@ -37,7 +38,7 @@ class LayerPolygon: # Buffering the colors shouldn't be necessary as it is not # re-used and can save alot of memory usage. - self._color_map = self.__color_map * [1, 1, 1, self._extruder] # The alpha component is used to store the extruder nr + self._color_map = LayerPolygon.getColorMap() * [1, 1, 1, self._extruder] # The alpha component is used to store the extruder nr self._colors = self._color_map[self._types] # When type is used as index returns true if type == LayerPolygon.InfillType or type == LayerPolygon.SkinType or type == LayerPolygon.SupportInfillType @@ -172,17 +173,25 @@ class LayerPolygon: return normals - # Should be generated in better way, not hardcoded. - __color_map = numpy.array([ - [1.0, 1.0, 1.0, 1.0], # NoneType - [1.0, 0.0, 0.0, 1.0], # Inset0Type - [0.0, 1.0, 0.0, 1.0], # InsetXType - [1.0, 1.0, 0.0, 1.0], # SkinType - [0.0, 1.0, 1.0, 1.0], # SupportType - [0.0, 1.0, 1.0, 1.0], # SkirtType - [1.0, 0.75, 0.0, 1.0], # InfillType - [0.0, 1.0, 1.0, 1.0], # SupportInfillType - [0.0, 0.0, 1.0, 1.0], # MoveCombingType - [0.5, 0.5, 1.0, 1.0], # MoveRetractionType - [0.25, 0.75, 1.0, 1.0] # SupportInterfaceType - ]) \ No newline at end of file + __color_map = None + + ## Gets the instance of the VersionUpgradeManager, or creates one. + @classmethod + def getColorMap(cls): + if cls.__color_map is None: + theme = Application.getInstance().getTheme() + cls.__color_map = numpy.array([ + theme.getColor("layerview_none").getRgbF(), # NoneType + theme.getColor("layerview_inset_0").getRgbF(), # Inset0Type + theme.getColor("layerview_inset_x").getRgbF(), # InsetXType + theme.getColor("layerview_skin").getRgbF(), # SkinType + theme.getColor("layerview_support").getRgbF(), # SupportType + theme.getColor("layerview_skirt").getRgbF(), # SkirtType + theme.getColor("layerview_infill").getRgbF(), # InfillType + theme.getColor("layerview_support_infill").getRgbF(), # SupportInfillType + theme.getColor("layerview_move_combining").getRgbF(), # MoveCombingType + theme.getColor("layerview_move_retraction").getRgbF(), # MoveRetractionType + theme.getColor("layerview_support_interface").getRgbF() # SupportInterfaceType + ]) + + return cls.__color_map From b0ea72834f6884d98b5a9e1273b7aefacf10c210 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Tue, 24 Jan 2017 11:47:02 +0100 Subject: [PATCH 19/24] Remove unused import Remnant from another, less tidy, approach --- plugins/SolidView/SolidView.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/SolidView/SolidView.py b/plugins/SolidView/SolidView.py index 065b73b24f..893c4ed180 100644 --- a/plugins/SolidView/SolidView.py +++ b/plugins/SolidView/SolidView.py @@ -11,7 +11,7 @@ from UM.View.Renderer import Renderer from UM.Settings.Validator import ValidatorState from UM.View.GL.OpenGL import OpenGL -import UM.Qt.Bindings.Theme + import cura.Settings from cura.Settings.ExtruderManager import ExtruderManager From 7020e018432bf2ad8242ddb06838b51ca25ff335 Mon Sep 17 00:00:00 2001 From: Mark Burton Date: Tue, 24 Jan 2017 10:50:28 +0000 Subject: [PATCH 20/24] Don't minimise minimum! --- 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 3be63b555d..14f65137e9 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -1233,7 +1233,7 @@ }, "min_infill_area": { - "label": "Min Infill Area", + "label": "Minimum Infill Area", "description": "Don't generate areas of infill smaller than this (use skin instead).", "unit": "mm²", "type": "float", From 4e8fb254f67790c6f97e12f1d365521ce0235933 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 24 Jan 2017 13:14:10 +0100 Subject: [PATCH 21/24] Only emit signal when the active extruder actually changes --- cura/Settings/ExtruderManager.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index 81579f74d0..e57ff3115e 100644 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -96,8 +96,9 @@ class ExtruderManager(QObject): # \param index The index of the new active extruder. @pyqtSlot(int) def setActiveExtruderIndex(self, index): - self._active_extruder_index = index - self.activeExtruderChanged.emit() + if self._active_extruder_index != index: + self._active_extruder_index = index + self.activeExtruderChanged.emit() @pyqtProperty(int, notify = activeExtruderChanged) def activeExtruderIndex(self): From b284e8508a06ceb6e557246ecbb212770fcdeb3a Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 24 Jan 2017 13:29:33 +0100 Subject: [PATCH 22/24] Added extra checks to prevent unneeded signal emits --- plugins/3MFReader/WorkspaceDialog.py | 60 +++++++++++++++++----------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/plugins/3MFReader/WorkspaceDialog.py b/plugins/3MFReader/WorkspaceDialog.py index 8e16404b0a..1bae9575f2 100644 --- a/plugins/3MFReader/WorkspaceDialog.py +++ b/plugins/3MFReader/WorkspaceDialog.py @@ -70,8 +70,9 @@ class WorkspaceDialog(QObject): return self._variant_type def setVariantType(self, variant_type): - self._variant_type = variant_type - self.variantTypeChanged.emit() + if self._variant_type != variant_type: + self._variant_type = variant_type + self.variantTypeChanged.emit() @pyqtProperty(str, notify=machineTypeChanged) def machineType(self): @@ -82,8 +83,9 @@ class WorkspaceDialog(QObject): self.machineTypeChanged.emit() def setNumUserSettings(self, num_user_settings): - self._num_user_settings = num_user_settings - self.numVisibleSettingsChanged.emit() + if self._num_user_settings != num_user_settings: + self._num_user_settings = num_user_settings + self.numVisibleSettingsChanged.emit() @pyqtProperty(int, notify=numUserSettingsChanged) def numUserSettings(self): @@ -94,40 +96,45 @@ class WorkspaceDialog(QObject): return self._objects_on_plate def setHasObjectsOnPlate(self, objects_on_plate): - self._objects_on_plate = objects_on_plate - self.objectsOnPlateChanged.emit() + if self._objects_on_plate != objects_on_plate: + self._objects_on_plate = objects_on_plate + self.objectsOnPlateChanged.emit() @pyqtProperty("QVariantList", notify = materialLabelsChanged) def materialLabels(self): return self._material_labels def setMaterialLabels(self, material_labels): - self._material_labels = material_labels - self.materialLabelsChanged.emit() + if self._material_labels != material_labels: + self._material_labels = material_labels + self.materialLabelsChanged.emit() @pyqtProperty("QVariantList", notify=extrudersChanged) def extruders(self): return self._extruders def setExtruders(self, extruders): - self._extruders = extruders - self.extrudersChanged.emit() + if self._extruders != extruders: + self._extruders = extruders + self.extrudersChanged.emit() @pyqtProperty(str, notify = machineNameChanged) def machineName(self): return self._machine_name def setMachineName(self, machine_name): - self._machine_name = machine_name - self.machineNameChanged.emit() + if self._machine_name != machine_name: + self._machine_name = machine_name + self.machineNameChanged.emit() @pyqtProperty(str, notify=qualityTypeChanged) def qualityType(self): return self._quality_type def setQualityType(self, quality_type): - self._quality_type = quality_type - self.qualityTypeChanged.emit() + if self._quality_type != quality_type: + self._quality_type = quality_type + self.qualityTypeChanged.emit() @pyqtProperty(int, notify=numSettingsOverridenByQualityChangesChanged) def numSettingsOverridenByQualityChanges(self): @@ -142,8 +149,9 @@ class WorkspaceDialog(QObject): return self._quality_name def setQualityName(self, quality_name): - self._quality_name = quality_name - self.qualityNameChanged.emit() + if self._quality_name != quality_name: + self._quality_name = quality_name + self.qualityNameChanged.emit() @pyqtProperty(str, notify=activeModeChanged) def activeMode(self): @@ -165,8 +173,9 @@ class WorkspaceDialog(QObject): return self._num_visible_settings def setNumVisibleSettings(self, num_visible_settings): - self._num_visible_settings = num_visible_settings - self.numVisibleSettingsChanged.emit() + if self._num_visible_settings != num_visible_settings: + self._num_visible_settings = num_visible_settings + self.numVisibleSettingsChanged.emit() @pyqtProperty(bool, notify = machineConflictChanged) def machineConflict(self): @@ -191,16 +200,19 @@ class WorkspaceDialog(QObject): Application.getInstance().getBackend().close() def setMaterialConflict(self, material_conflict): - self._has_material_conflict = material_conflict - self.materialConflictChanged.emit() + if self._has_material_conflict != material_conflict: + self._has_material_conflict = material_conflict + self.materialConflictChanged.emit() def setMachineConflict(self, machine_conflict): - self._has_machine_conflict = machine_conflict - self.machineConflictChanged.emit() + if self._has_machine_conflict != machine_conflict: + self._has_machine_conflict = machine_conflict + self.machineConflictChanged.emit() def setQualityChangesConflict(self, quality_changes_conflict): - self._has_quality_changes_conflict = quality_changes_conflict - self.qualityChangesConflictChanged.emit() + if self._has_quality_changes_conflict != quality_changes_conflict: + self._has_quality_changes_conflict = quality_changes_conflict + self.qualityChangesConflictChanged.emit() def getResult(self): if "machine" in self._result and not self._has_machine_conflict: From f0333e2184d4c979aa2d6614770585e2ca968c2e Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Tue, 24 Jan 2017 23:18:46 +0100 Subject: [PATCH 23/24] Fix typo --- cura/LayerPolygon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/LayerPolygon.py b/cura/LayerPolygon.py index 3c228610bc..70b17cff75 100644 --- a/cura/LayerPolygon.py +++ b/cura/LayerPolygon.py @@ -189,7 +189,7 @@ class LayerPolygon: theme.getColor("layerview_skirt").getRgbF(), # SkirtType theme.getColor("layerview_infill").getRgbF(), # InfillType theme.getColor("layerview_support_infill").getRgbF(), # SupportInfillType - theme.getColor("layerview_move_combining").getRgbF(), # MoveCombingType + theme.getColor("layerview_move_combing").getRgbF(), # MoveCombingType theme.getColor("layerview_move_retraction").getRgbF(), # MoveRetractionType theme.getColor("layerview_support_interface").getRgbF() # SupportInterfaceType ]) From 878ccc679c7a9d2eaeed1dfe3383c19abc9a9c8b Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Tue, 24 Jan 2017 23:24:52 +0100 Subject: [PATCH 24/24] Add a legend to Layer View --- cura/CuraApplication.py | 5 +++ plugins/LayerView/LayerView.py | 24 +++++++++++++ resources/qml/Cura.qml | 12 +++++++ resources/qml/Legend.qml | 66 ++++++++++++++++++++++++++++++++++ 4 files changed, 107 insertions(+) create mode 100644 resources/qml/Legend.qml diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 3286a69bbb..5fadd85e46 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -315,6 +315,11 @@ class CuraApplication(QtApplication): showPrintMonitor = pyqtSignal(bool, arguments = ["show"]) + def setViewLegendItems(self, items): + self.viewLegendItemsChanged.emit(items) + + viewLegendItemsChanged = pyqtSignal("QVariantList", arguments = ["items"]) + ## 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/LayerView/LayerView.py b/plugins/LayerView/LayerView.py index bb315e5b57..df0c92f87e 100644 --- a/plugins/LayerView/LayerView.py +++ b/plugins/LayerView/LayerView.py @@ -60,6 +60,8 @@ class LayerView(View): self._proxy = LayerViewProxy.LayerViewProxy() self._controller.getScene().getRoot().childrenChanged.connect(self._onSceneChanged) + self._legend_items = None + Preferences.getInstance().addPreference("view/top_layer_count", 5) Preferences.getInstance().addPreference("view/only_show_top_layers", False) Preferences.getInstance().preferenceChanged.connect(self._onPreferencesChanged) @@ -206,6 +208,8 @@ class LayerView(View): self._old_composite_shader = self._composite_pass.getCompositeShader() self._composite_pass.setCompositeShader(self._layerview_composite_shader) + Application.getInstance().setViewLegendItems(self._getLegendItems()) + elif event.type == Event.ViewDeactivateEvent: self._wireprint_warning_message.hide() Application.getInstance().globalContainerStackChanged.disconnect(self._onGlobalStackChanged) @@ -215,6 +219,8 @@ class LayerView(View): self._composite_pass.setLayerBindings(self._old_layer_bindings) self._composite_pass.setCompositeShader(self._old_composite_shader) + Application.getInstance().setViewLegendItems([]) + def _onGlobalStackChanged(self): if self._global_container_stack: self._global_container_stack.propertyChanged.disconnect(self._onPropertyChanged) @@ -264,6 +270,24 @@ class LayerView(View): self._startUpdateTopLayers() + def _getLegendItems(self): + if self._legend_items is None: + theme = Application.getInstance().getTheme() + self._legend_items = [ + {"color": theme.getColor("layerview_inset_0").name(), "title": catalog.i18nc("@label:layerview polygon type", "Outer Wall")}, # Inset0Type + {"color": theme.getColor("layerview_inset_x").name(), "title": catalog.i18nc("@label:layerview polygon type", "Inner Wall")}, # InsetXType + {"color": theme.getColor("layerview_skin").name(), "title": catalog.i18nc("@label:layerview polygon type", "Top / Bottom")}, # SkinType + {"color": theme.getColor("layerview_infill").name(), "title": catalog.i18nc("@label:layerview polygon type", "Infill")}, # InfillType + {"color": theme.getColor("layerview_support").name(), "title": catalog.i18nc("@label:layerview polygon type", "Support Skin")}, # SupportType + {"color": theme.getColor("layerview_support_infill").name(), "title": catalog.i18nc("@label:layerview polygon type", "Support Infill")}, # SupportInfillType + {"color": theme.getColor("layerview_support_interface").name(), "title": catalog.i18nc("@label:layerview polygon type", "Support Interface")}, # SupportInterfaceType + {"color": theme.getColor("layerview_skirt").name(), "title": catalog.i18nc("@label:layerview polygon type", "Build Plate Adhesion")}, # SkirtType + {"color": theme.getColor("layerview_move_combing").name(), "title": catalog.i18nc("@label:layerview polygon type", "Travel Move")}, # MoveCombingType + {"color": theme.getColor("layerview_move_retraction").name(), "title": catalog.i18nc("@label:layerview polygon type", "Retraction Move")}, # MoveRetractionType + #{"color": theme.getColor("layerview_none").name(), "title": catalog.i18nc("@label:layerview polygon type", "Unknown")} # NoneType + ] + return self._legend_items + class _CreateTopLayersJob(Job): def __init__(self, scene, layer_number, solid_layers): diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 1383338144..264bec6d9a 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -306,6 +306,18 @@ UM.MainWindow } } + Legend + { + id: legend + anchors + { + top: parent.top + topMargin: UM.Theme.getSize("default_margin").height + right: sidebar.left + rightMargin: UM.Theme.getSize("default_margin").width + } + } + JobSpecs { id: jobSpecs diff --git a/resources/qml/Legend.qml b/resources/qml/Legend.qml new file mode 100644 index 0000000000..353747ef67 --- /dev/null +++ b/resources/qml/Legend.qml @@ -0,0 +1,66 @@ +// Copyright (c) 2015 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.1 as UM +import Cura 1.0 as Cura + +Rectangle { + id: base + + UM.I18nCatalog { id: catalog; name:"cura"} + + width: childrenRect.width + height: childrenRect.height + color: "transparent" + + Connections + { + target: Printer + onViewLegendItemsChanged: + { + legendItemRepeater.model = items + } + } + + Column + { + Repeater + { + id: legendItemRepeater + + Item { + anchors.right: parent.right + height: childrenRect.height + width: childrenRect.width + + Rectangle { + id: swatch + + anchors.right: parent.right + anchors.verticalCenter: label.verticalCenter + height: UM.Theme.getSize("setting_control").height / 2 + width: height + + color: modelData.color + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("text_subtext") + } + Label { + id: label + + text: modelData.title + font: UM.Theme.getFont("small") + color: UM.Theme.getColor("text_subtext") + + anchors.right: swatch.left + anchors.rightMargin: UM.Theme.getSize("default_margin").width / 2 + } + } + } + } +}