From 4bb67fbd82e65c24b55efcab550a0e943c68fb86 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Wed, 25 Oct 2017 17:04:23 +0200 Subject: [PATCH 01/75] Add setting visibility profile menu button --- resources/qml/Settings/SettingView.qml | 58 +++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 56fd789564..6fa07124f1 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -102,6 +102,60 @@ Item } } + ToolButton + { + id: settingVisibilityProfileMenu + + width: height + height: UM.Theme.getSize("setting_control").height + anchors + { + top: globalProfileRow.bottom + topMargin: UM.Theme.getSize("sidebar_margin").height + right: parent.right + rightMargin: UM.Theme.getSize("sidebar_margin").width + } + style: ButtonStyle + { + background: Rectangle { + color: + { + if(!control.enabled) + { + return UM.Theme.getColor("setting_control_disabled"); + } + return UM.Theme.getColor("setting_control"); + } + + border.width: UM.Theme.getSize("default_lining").width + border.color: + { + if (!control.enabled) + { + return UM.Theme.getColor("setting_control_disabled_border"); + } + else if (control.hovered) + { + return UM.Theme.getColor("setting_control_border_highlight"); + } + return UM.Theme.getColor("setting_control_border"); + } + UM.RecolorImage { + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + width: UM.Theme.getSize("standard_arrow").width + height: UM.Theme.getSize("standard_arrow").height + sourceSize.width: width + sourceSize.height: width + color: control.enabled ? UM.Theme.getColor("setting_category_text") : UM.Theme.getColor("setting_category_disabled_text") + source: UM.Theme.getIcon("menu") + } + } + label: Label{} + } + menu: Menu {} + } + Rectangle { id: filterContainer @@ -128,8 +182,8 @@ Item topMargin: UM.Theme.getSize("sidebar_margin").height left: parent.left leftMargin: UM.Theme.getSize("sidebar_margin").width - right: parent.right - rightMargin: UM.Theme.getSize("sidebar_margin").width + right: settingVisibilityProfileMenu.left + rightMargin: UM.Theme.getSize("default_margin").width } height: visible ? UM.Theme.getSize("setting_control").height : 0 Behavior on height { NumberAnimation { duration: 100 } } From 078fd074e5597120e1855b2f487bdb8722834d14 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 26 Oct 2017 00:00:07 +0200 Subject: [PATCH 02/75] Add initial menu stub --- .../Menus/SettingVisibilityProfilesMenu.qml | 55 +++++++++++++++++ resources/qml/Settings/SettingView.qml | 61 ++++++++++++++----- 2 files changed, 100 insertions(+), 16 deletions(-) create mode 100644 resources/qml/Menus/SettingVisibilityProfilesMenu.qml diff --git a/resources/qml/Menus/SettingVisibilityProfilesMenu.qml b/resources/qml/Menus/SettingVisibilityProfilesMenu.qml new file mode 100644 index 0000000000..e826053a50 --- /dev/null +++ b/resources/qml/Menus/SettingVisibilityProfilesMenu.qml @@ -0,0 +1,55 @@ +// Copyright (c) 2017 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 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: "Visible Settings" + + property bool showingSearchResults + property bool showingAllSettings + + signal showAllSettings() + signal showSettingVisibilityProfile() + + MenuItem + { + text: catalog.i18nc("@action:inmenu", "Normal Set") + checkable: true + checked: !showingSearchResults && !showingAllSettings + exclusiveGroup: group + onTriggered: showAllSettings() + } + MenuSeparator {} + MenuItem + { + text: catalog.i18nc("@action:inmenu", "Search Results") + checkable: true + visible: showingSearchResults + checked: showingSearchResults + exclusiveGroup: group + } + MenuItem + { + text: catalog.i18nc("@action:inmenu", "All Settings") + checkable: true + checked: showingAllSettings + exclusiveGroup: group + onTriggered: showAllSettings() + } + MenuSeparator {} + MenuItem + { + text: catalog.i18nc("@action:inmenu", "Manage Visibility Profiles...") + iconName: "configure" + onTriggered: Cura.Actions.configureSettingVisibility.trigger() + } + + ExclusiveGroup { id: group } +} diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 6fa07124f1..0f7cc3f855 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -15,10 +15,11 @@ Item { id: base; - property Action configureSettings; - property bool findingSettings; - signal showTooltip(Item item, point location, string text); - signal hideTooltip(); + property Action configureSettings + property bool findingSettings + property bool showingAllSettings + signal showTooltip(Item item, point location, string text) + signal hideTooltip() Item { @@ -153,7 +154,26 @@ Item } label: Label{} } - menu: Menu {} + menu: SettingVisibilityProfilesMenu + { + showingSearchResults: findingSettings + showingAllSettings: showingAllSettings + + onShowAllSettings: + { + base.showingAllSettings = true; + base.findingSettings = false; + filter.text = ""; + filter.updateDefinitionModel(); + } + onShowSettingVisibilityProfile: + { + base.showingAllSettings = false; + base.findingSettings = false; + filter.text = ""; + filter.updateDefinitionModel(); + } + } } Rectangle @@ -217,17 +237,9 @@ Item { if(findingSettings) { - expandedCategories = definitionsModel.expanded.slice(); - definitionsModel.expanded = ["*"]; - definitionsModel.showAncestors = true; - definitionsModel.showAll = true; - } - else - { - definitionsModel.expanded = expandedCategories; - definitionsModel.showAncestors = false; - definitionsModel.showAll = false; + showingAllSettings = false; } + updateDefinitionModel(); lastFindingSettings = findingSettings; } } @@ -236,6 +248,23 @@ Item { filter.text = ""; } + + function updateDefinitionModel() + { + if(findingSettings || base.showingAllSettings) + { + expandedCategories = definitionsModel.expanded.slice(); + definitionsModel.expanded = ["*"]; + definitionsModel.showAncestors = true; + definitionsModel.showAll = true; + } + else + { + definitionsModel.expanded = expandedCategories; + definitionsModel.showAncestors = false; + definitionsModel.showAll = false; + } + } } MouseArea @@ -258,7 +287,7 @@ Item anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width + anchors.rightMargin: UM.Theme.getSize("default_margin").width color: UM.Theme.getColor("setting_control_button") hoverColor: UM.Theme.getColor("setting_control_button_hover") From c9ad5eaedb1fd1ac54d5aeac797ab6f5da6bf5f7 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 26 Oct 2017 00:00:39 +0200 Subject: [PATCH 03/75] Add menu icon --- resources/themes/cura-light/icons/menu.svg | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 resources/themes/cura-light/icons/menu.svg diff --git a/resources/themes/cura-light/icons/menu.svg b/resources/themes/cura-light/icons/menu.svg new file mode 100644 index 0000000000..85fbfb072c --- /dev/null +++ b/resources/themes/cura-light/icons/menu.svg @@ -0,0 +1,3 @@ + + + From 5638ebe8cf15e6c7e228bc304a50cd6b978d21b9 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 26 Oct 2017 11:05:59 +0200 Subject: [PATCH 04/75] Tweak appearance of the menu button --- resources/qml/Settings/SettingView.qml | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 0f7cc3f855..7c44f01c3c 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -118,29 +118,7 @@ Item } style: ButtonStyle { - background: Rectangle { - color: - { - if(!control.enabled) - { - return UM.Theme.getColor("setting_control_disabled"); - } - return UM.Theme.getColor("setting_control"); - } - - border.width: UM.Theme.getSize("default_lining").width - border.color: - { - if (!control.enabled) - { - return UM.Theme.getColor("setting_control_disabled_border"); - } - else if (control.hovered) - { - return UM.Theme.getColor("setting_control_border_highlight"); - } - return UM.Theme.getColor("setting_control_border"); - } + background: Item { UM.RecolorImage { anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter @@ -203,7 +181,7 @@ Item left: parent.left leftMargin: UM.Theme.getSize("sidebar_margin").width right: settingVisibilityProfileMenu.left - rightMargin: UM.Theme.getSize("default_margin").width + rightMargin: Math.floor(UM.Theme.getSize("default_margin").width / 2) } height: visible ? UM.Theme.getSize("setting_control").height : 0 Behavior on height { NumberAnimation { duration: 100 } } From 32b0e536211ec61b77d17bfe5e062e409fbdda99 Mon Sep 17 00:00:00 2001 From: Ruben D Date: Sun, 10 Dec 2017 23:46:49 +0100 Subject: [PATCH 05/75] Add more machine information These are some additional settings that a slicer might need to know. Specifically, it needs to know these for proper X3G output. --- resources/definitions/fdmprinter.def.json | 77 +++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index ed8bf59b97..766bc22d3d 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -606,6 +606,73 @@ "settable_per_extruder": false, "settable_per_meshgroup": false }, + "machine_steps_per_mm_x": + { + "label": "Steps per Millimeter (X)", + "description": "How many steps of the stepper motor will result in one millimeter of movement in the X direction.", + "type": "int", + "default_value": 50, + "minimum_value": "0.0000001", + "settable_per_mesh": false, + "settable_per_extruder": true + }, + "machine_steps_per_mm_y": + { + "label": "Steps per Millimeter (Y)", + "description": "How many steps of the stepper motor will result in one millimeter of movement in the Y direction.", + "type": "int", + "default_value": 50, + "minimum_value": "0.0000001", + "settable_per_mesh": false, + "settable_per_extruder": true + }, + "machine_steps_per_mm_z": + { + "label": "Steps per Millimeter (Z)", + "description": "How many steps of the stepper motor will result in one millimeter of movement in the Z direction.", + "type": "int", + "default_value": 50, + "minimum_value": "0.0000001", + "settable_per_mesh": false, + "settable_per_extruder": true + }, + "machine_steps_per_mm_e": + { + "label": "Steps per Millimeter (E)", + "description": "How many steps of the stepper motors will result in one millimeter of extrusion.", + "type": "int", + "default_value": 1600, + "minimum_value": "0.0000001", + "settable_per_mesh": false, + "settable_per_extruder": true + }, + "machine_endstop_positive_direction_x": + { + "label": "X Endstop in Positive Direction", + "description": "Whether the endstop of the X axis is in the positive direction (high X coordinate) or negative (low X coordinate).", + "type": "bool", + "default_value": false, + "settable_per_mesh": false, + "settable_per_extruder": true + }, + "machine_endstop_positive_direction_y": + { + "label": "Y Endstop in Positive Direction", + "description": "Whether the endstop of the Y axis is in the positive direction (high Y coordinate) or negative (low Y coordinate).", + "type": "bool", + "default_value": false, + "settable_per_mesh": false, + "settable_per_extruder": true + }, + "machine_endstop_positive_direction_z": + { + "label": "Z Endstop in Positive Direction", + "description": "Whether the endstop of the Z axis is in the positive direction (high Z coordinate) or negative (low Z coordinate).", + "type": "bool", + "default_value": true, + "settable_per_mesh": false, + "settable_per_extruder": true + }, "machine_minimum_feedrate": { "label": "Minimum Feedrate", @@ -616,6 +683,16 @@ "settable_per_mesh": false, "settable_per_extruder": false, "settable_per_meshgroup": false + }, + "machine_feeder_wheel_diameter": + { + "label": "Feeder Wheel Diameter", + "description": "The diameter of the wheel that drives the material in the feeder.", + "unit": "mm", + "type": "float", + "default_value": 10.0, + "settable_per_mesh": false, + "settable_per_extruder": true } } }, From 9917d567a333166019dc08c778331a93c571770e Mon Sep 17 00:00:00 2001 From: Ruben D Date: Sun, 10 Dec 2017 23:48:11 +0100 Subject: [PATCH 06/75] Correction to M180 settings for X3G These settings are needed for X3G output to have the correct dimensions and feedrate. The steps_per_mm settings are important. --- resources/definitions/m180.def.json | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/resources/definitions/m180.def.json b/resources/definitions/m180.def.json index 1e8ac1767b..1fab24f702 100644 --- a/resources/definitions/m180.def.json +++ b/resources/definitions/m180.def.json @@ -25,8 +25,7 @@ "default_value": true }, "machine_nozzle_size": { - "default_value": 0.4, - "minimum_value": "0.001" + "default_value": 0.4 }, "machine_head_with_fans_polygon": { "default_value": [ @@ -36,6 +35,21 @@ [ 18, 35 ] ] }, + "machine_max_feedrate_z": { + "default_value": 400 + }, + "steps_per_mm_x": { + "default_value": 93 + }, + "steps_per_mm_y": { + "default_value": 93 + }, + "steps_per_mm_z": { + "default_value": 1600 + }, + "steps_per_mm_e": { + "default_value": 92 + }, "gantry_height": { "default_value": 55 }, From 503c612509346d5a215953255d9230c1e2dc9288 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 4 Jan 2018 11:21:44 +0100 Subject: [PATCH 07/75] Add (non-functional) items for changed settings (user-changes) and current profile settings --- .../Menus/SettingVisibilityProfilesMenu.qml | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/resources/qml/Menus/SettingVisibilityProfilesMenu.qml b/resources/qml/Menus/SettingVisibilityProfilesMenu.qml index e826053a50..513930a3ce 100644 --- a/resources/qml/Menus/SettingVisibilityProfilesMenu.qml +++ b/resources/qml/Menus/SettingVisibilityProfilesMenu.qml @@ -18,6 +18,15 @@ Menu signal showAllSettings() signal showSettingVisibilityProfile() + MenuItem + { + text: catalog.i18nc("@action:inmenu", "Search Results") + checkable: true + visible: showingSearchResults + checked: showingSearchResults + exclusiveGroup: group + } + MenuSeparator { visible: showingSearchResults } MenuItem { text: catalog.i18nc("@action:inmenu", "Normal Set") @@ -29,13 +38,22 @@ Menu MenuSeparator {} MenuItem { - text: catalog.i18nc("@action:inmenu", "Search Results") + text: catalog.i18nc("@action:inmenu", "Changed settings") checkable: true - visible: showingSearchResults - checked: showingSearchResults + checked: showingAllSettings exclusiveGroup: group + onTriggered: showAllSettings() } MenuItem + { + text: catalog.i18nc("@action:inmenu", "Settings in profile") + checkable: true + checked: showingAllSettings + exclusiveGroup: group + onTriggered: showAllSettings() + } + MenuSeparator {} + MenuItem { text: catalog.i18nc("@action:inmenu", "All Settings") checkable: true From 9730c25ec6fc1056277136fb99a502e52f7ea961 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 4 Jan 2018 14:29:40 +0100 Subject: [PATCH 08/75] Fix showing the normal set --- resources/qml/Menus/SettingVisibilityProfilesMenu.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/qml/Menus/SettingVisibilityProfilesMenu.qml b/resources/qml/Menus/SettingVisibilityProfilesMenu.qml index 513930a3ce..019d56be16 100644 --- a/resources/qml/Menus/SettingVisibilityProfilesMenu.qml +++ b/resources/qml/Menus/SettingVisibilityProfilesMenu.qml @@ -33,12 +33,13 @@ Menu checkable: true checked: !showingSearchResults && !showingAllSettings exclusiveGroup: group - onTriggered: showAllSettings() + onTriggered: showSettingVisibilityProfile() } MenuSeparator {} MenuItem { text: catalog.i18nc("@action:inmenu", "Changed settings") + enabled: false checkable: true checked: showingAllSettings exclusiveGroup: group @@ -47,6 +48,7 @@ Menu MenuItem { text: catalog.i18nc("@action:inmenu", "Settings in profile") + enabled: false checkable: true checked: showingAllSettings exclusiveGroup: group From 2703474861f50269228fd34f0ef2d4dc5cbb645c Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Sat, 27 Jan 2018 21:49:19 +0100 Subject: [PATCH 09/75] Only select all text when tabbing through fields, not when clicking a field --- resources/qml/Settings/SettingTextField.qml | 33 +++++++++++++++------ 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/resources/qml/Settings/SettingTextField.qml b/resources/qml/Settings/SettingTextField.qml index 9056bbe45a..2226e1f09d 100644 --- a/resources/qml/Settings/SettingTextField.qml +++ b/resources/qml/Settings/SettingTextField.qml @@ -13,11 +13,17 @@ SettingItem property string textBeforeEdit property bool textHasChanged + property bool focusGainedByClick: false onFocusReceived: { textHasChanged = false; textBeforeEdit = focusItem.text; - focusItem.selectAll(); + + if(!focusGainedByClick) + { + // select all text when tabbing through fields (but not when selecting a field with the mouse) + focusItem.selectAll(); + } } contents: Rectangle @@ -92,14 +98,6 @@ SettingItem font: UM.Theme.getFont("default") } - MouseArea - { - id: mouseArea - anchors.fill: parent; - //hoverEnabled: true; - cursorShape: Qt.IBeamCursor - } - TextInput { id: input @@ -141,6 +139,7 @@ SettingItem { base.focusReceived(); } + base.focusGainedByClick = false; } color: !enabled ? UM.Theme.getColor("setting_control_disabled_text") : UM.Theme.getColor("setting_control_text") @@ -177,6 +176,22 @@ SettingItem } when: !input.activeFocus } + + MouseArea + { + id: mouseArea + anchors.fill: parent; + + cursorShape: Qt.IBeamCursor + + onPressed: { + if(!input.activeFocus) { + base.focusGainedByClick = true; + input.forceActiveFocus(); + } + mouse.accepted = false; + } + } } } } From 872efd16b238e17d546db25b6f6fc308b5bc3762 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 8 Feb 2018 17:13:59 +0100 Subject: [PATCH 10/75] Fix selecting presets from menu Switching back to Custom is not yet implemented, and the menu selection does not yet change if the preset is changed in the preferences --- .../Menus/SettingVisibilityProfilesMenu.qml | 52 ++++++++++++++++--- resources/qml/Settings/SettingView.qml | 9 +++- 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/resources/qml/Menus/SettingVisibilityProfilesMenu.qml b/resources/qml/Menus/SettingVisibilityProfilesMenu.qml index 019d56be16..b71520a2ca 100644 --- a/resources/qml/Menus/SettingVisibilityProfilesMenu.qml +++ b/resources/qml/Menus/SettingVisibilityProfilesMenu.qml @@ -16,7 +16,7 @@ Menu property bool showingAllSettings signal showAllSettings() - signal showSettingVisibilityProfile() + signal showSettingVisibilityProfile(string profileName) MenuItem { @@ -27,19 +27,56 @@ Menu exclusiveGroup: group } MenuSeparator { visible: showingSearchResults } + MenuItem { - text: catalog.i18nc("@action:inmenu", "Normal Set") + text: catalog.i18nc("@action:inmenu", "Custom selection") checkable: true checked: !showingSearchResults && !showingAllSettings exclusiveGroup: group - onTriggered: showSettingVisibilityProfile() + onTriggered: showSettingVisibilityProfile("Custom") } - MenuSeparator {} + MenuSeparator { } + + Instantiator + { + model: ListModel + { + id: presetNamesList + Component.onCompleted: + { + // returned value is Dictionary (Ex: {1:"Basic"}, The number 1 is the weight and sort by weight) + var itemsDict = UM.Preferences.getValue("general/visible_settings_preset") + var sorted = []; + for(var key in itemsDict) { + sorted[sorted.length] = key; + } + sorted.sort(); + for(var i = 0; i < sorted.length; i++) { + presetNamesList.append({text: itemsDict[sorted[i]], value: i}); + } + } + } + + MenuItem + { + text: model.text + checkable: true + checked: false + exclusiveGroup: group + onTriggered: showSettingVisibilityProfile(model.text) + } + + onObjectAdded: menu.insertItem(index, object) + onObjectRemoved: menu.removeItem(object) + } + + MenuSeparator { visible: false } MenuItem { text: catalog.i18nc("@action:inmenu", "Changed settings") - enabled: false + visible: false + enabled: true checkable: true checked: showingAllSettings exclusiveGroup: group @@ -48,7 +85,8 @@ Menu MenuItem { text: catalog.i18nc("@action:inmenu", "Settings in profile") - enabled: false + visible: false + enabled: true checkable: true checked: showingAllSettings exclusiveGroup: group @@ -66,7 +104,7 @@ Menu MenuSeparator {} MenuItem { - text: catalog.i18nc("@action:inmenu", "Manage Visibility Profiles...") + text: catalog.i18nc("@action:inmenu", "Manage Setting Visibility...") iconName: "configure" onTriggered: Cura.Actions.configureSettingVisibility.trigger() } diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 78b29d4d5c..f3a4e92d96 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -151,6 +151,10 @@ Item } onShowSettingVisibilityProfile: { + var newVisibleSettings = CuraApplication.getVisibilitySettingPreset(profileName) + UM.Preferences.setValue("general/visible_settings", newVisibleSettings) + UM.Preferences.setValue("general/preset_setting_visibility_choice", profileName) + base.showingAllSettings = false; base.findingSettings = false; filter.text = ""; @@ -243,7 +247,10 @@ Item } else { - definitionsModel.expanded = expandedCategories; + if(expandedCategories) + { + definitionsModel.expanded = expandedCategories; + } definitionsModel.showAncestors = false; definitionsModel.showAll = false; } From 080979caeb50155b2536e1d43e07527408f19351 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 8 Feb 2018 22:57:05 +0100 Subject: [PATCH 11/75] Move setting visibility presets into a model Handling of "Custom" is still under consideration --- cura/CuraApplication.py | 105 ++---------------- .../SettingVisibilityProfilesModel.py | 90 +++++++++++++++ .../Menus/SettingVisibilityProfilesMenu.qml | 32 ++---- .../qml/Preferences/SettingVisibilityPage.qml | 73 +++--------- resources/qml/Settings/SettingView.qml | 4 - 5 files changed, 127 insertions(+), 177 deletions(-) create mode 100644 cura/Settings/SettingVisibilityProfilesModel.py diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 4ff39b84a4..7afea3de13 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -57,6 +57,7 @@ from cura.Settings.QualityAndUserProfilesModel import QualityAndUserProfilesMode from cura.Settings.SettingInheritanceManager import SettingInheritanceManager from cura.Settings.UserProfilesModel import UserProfilesModel from cura.Settings.SimpleModeSettingsManager import SimpleModeSettingsManager +from cura.Settings.SettingVisibilityProfilesModel import SettingVisibilityProfilesModel from . import PlatformPhysics @@ -78,6 +79,7 @@ from cura.Settings.ContainerSettingsModel import ContainerSettingsModel from cura.Settings.MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler from cura.Settings.QualitySettingsModel import QualitySettingsModel from cura.Settings.ContainerManager import ContainerManager +from cura.Settings.SettingVisibilityProfilesModel import SettingVisibilityProfilesModel from cura.ObjectsModel import ObjectsModel from cura.BuildPlateModel import BuildPlateModel @@ -356,19 +358,14 @@ class CuraApplication(QtApplication): preferences.setDefault("local_file/last_used_type", "text/x-gcode") - setting_visibily_preset_names = self.getVisibilitySettingPresetTypes() - preferences.setDefault("general/visible_settings_preset", setting_visibily_preset_names) + default_visibility_profile = SettingVisibilityProfilesModel.getInstance().getItem(0) + + preferences.setDefault("general/visible_settings", ";".join(default_visibility_profile["settings"])) + preferences.setDefault("general/preset_setting_visibility_choice", default_visibility_profile["id"]) preset_setting_visibility_choice = Preferences.getInstance().getValue("general/preset_setting_visibility_choice") - - default_preset_visibility_group_name = "Basic" - if preset_setting_visibility_choice == "" or preset_setting_visibility_choice is None: - if preset_setting_visibility_choice not in setting_visibily_preset_names: - preset_setting_visibility_choice = default_preset_visibility_group_name - - visible_settings = self.getVisibilitySettingPreset(settings_preset_name = preset_setting_visibility_choice) - preferences.setDefault("general/visible_settings", visible_settings) - preferences.setDefault("general/preset_setting_visibility_choice", preset_setting_visibility_choice) + if not SettingVisibilityProfilesModel.getInstance().find("id", preset_setting_visibility_choice): + Preferences.getInstance().setValue("general/preset_setting_visibility_choice", default_visibility_profile["id"]) self.applicationShuttingDown.connect(self.saveSettings) self.engineCreatedSignal.connect(self._onEngineCreated) @@ -382,91 +379,6 @@ class CuraApplication(QtApplication): CuraApplication.Created = True - @pyqtSlot(str, result = str) - def getVisibilitySettingPreset(self, settings_preset_name) -> str: - result = self._loadPresetSettingVisibilityGroup(settings_preset_name) - formatted_preset_settings = self._serializePresetSettingVisibilityData(result) - - return formatted_preset_settings - - ## Serialise the given preset setting visibitlity group dictionary into a string which is concatenated by ";" - # - def _serializePresetSettingVisibilityData(self, settings_data: dict) -> str: - result_string = "" - - for key in settings_data: - result_string += key + ";" - for value in settings_data[key]: - result_string += value + ";" - - return result_string - - ## Load the preset setting visibility group with the given name - # - def _loadPresetSettingVisibilityGroup(self, visibility_preset_name) -> Dict[str, str]: - preset_dir = Resources.getPath(Resources.PresetSettingVisibilityGroups) - - result = {} - right_preset_found = False - - for item in os.listdir(preset_dir): - file_path = os.path.join(preset_dir, item) - if not os.path.isfile(file_path): - continue - - parser = ConfigParser(allow_no_value = True) # accept options without any value, - - try: - parser.read([file_path]) - - if not parser.has_option("general", "name"): - continue - - if parser["general"]["name"] == visibility_preset_name: - right_preset_found = True - for section in parser.sections(): - if section == 'general': - continue - else: - section_settings = [] - for option in parser[section].keys(): - section_settings.append(option) - - result[section] = section_settings - - if right_preset_found: - break - - except Exception as e: - Logger.log("e", "Failed to load setting visibility preset %s: %s", file_path, str(e)) - - return result - - ## Check visibility setting preset folder and returns available types - # - def getVisibilitySettingPresetTypes(self): - preset_dir = Resources.getPath(Resources.PresetSettingVisibilityGroups) - result = {} - - for item in os.listdir(preset_dir): - file_path = os.path.join(preset_dir, item) - if not os.path.isfile(file_path): - continue - - parser = ConfigParser(allow_no_value=True) # accept options without any value, - - try: - parser.read([file_path]) - - if not parser.has_option("general", "name") and not parser.has_option("general", "weight"): - continue - - result[parser["general"]["weight"]] = parser["general"]["name"] - - except Exception as e: - Logger.log("e", "Failed to load setting preset %s: %s", file_path, str(e)) - - return result def _onEngineCreated(self): self._engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider()) @@ -904,6 +816,7 @@ class CuraApplication(QtApplication): qmlRegisterType(MachineNameValidator, "Cura", 1, 0, "MachineNameValidator") qmlRegisterType(UserChangesModel, "Cura", 1, 1, "UserChangesModel") qmlRegisterSingletonType(ContainerManager, "Cura", 1, 0, "ContainerManager", ContainerManager.createContainerManager) + qmlRegisterSingletonType(SettingVisibilityProfilesModel, "Cura", 1, 0, "SettingVisibilityProfilesModel", SettingVisibilityProfilesModel.createSettingVisibilityProfilesModel) # As of Qt5.7, it is necessary to get rid of any ".." in the path for the singleton to work. actions_url = QUrl.fromLocalFile(os.path.abspath(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Actions.qml"))) diff --git a/cura/Settings/SettingVisibilityProfilesModel.py b/cura/Settings/SettingVisibilityProfilesModel.py new file mode 100644 index 0000000000..2bc5f2f382 --- /dev/null +++ b/cura/Settings/SettingVisibilityProfilesModel.py @@ -0,0 +1,90 @@ +# Copyright (c) 2016 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +import os +import urllib +from configparser import ConfigParser + +from PyQt5.QtCore import pyqtProperty, Qt, pyqtSignal, pyqtSlot, QUrl + +from UM.Logger import Logger +from UM.Qt.ListModel import ListModel + +from UM.Resources import Resources +from UM.MimeTypeDatabase import MimeTypeDatabase, MimeTypeNotFoundError + +class SettingVisibilityProfilesModel(ListModel): + IdRole = Qt.UserRole + 1 + NameRole = Qt.UserRole + 2 + SettingsRole = Qt.UserRole + 4 + + def __init__(self, parent = None): + super().__init__(parent) + self.addRoleName(self.IdRole, "id") + self.addRoleName(self.NameRole, "name") + self.addRoleName(self.SettingsRole, "settings") + + self._container_ids = [] + self._containers = [] + + self._populate() + + def _populate(self): + items = [] + for item in Resources.getAllResourcesOfType(Resources.PresetSettingVisibilityGroups): + try: + mime_type = MimeTypeDatabase.getMimeTypeForFile(item) + except MimeTypeNotFoundError: + Logger.log("e", "Could not determine mime type of file %s", item) + continue + + id = urllib.parse.unquote_plus(mime_type.stripExtension(os.path.basename(item))) + + if not os.path.isfile(item): + continue + + parser = ConfigParser(allow_no_value=True) # accept options without any value, + + try: + parser.read([item]) + + if not parser.has_option("general", "name") and not parser.has_option("general", "weight"): + continue + + settings = [] + for section in parser.sections(): + if section == 'general': + continue + + settings.append(section) + for option in parser[section].keys(): + settings.append(option) + + items.append({ + "id": id, + "name": parser["general"]["name"], + "weight": parser["general"]["weight"], + "settings": settings + }) + + except Exception as e: + Logger.log("e", "Failed to load setting preset %s: %s", file_path, str(e)) + + + items.sort(key = lambda k: (k["weight"], k["id"])) + self.setItems(items) + + # Factory function, used by QML + @staticmethod + def createSettingVisibilityProfilesModel(engine, js_engine): + return SettingVisibilityProfilesModel.getInstance() + + ## Get the singleton instance for this class. + @classmethod + def getInstance(cls) -> "SettingVisibilityProfilesModel": + # Note: Explicit use of class name to prevent issues with inheritance. + if not SettingVisibilityProfilesModel.__instance: + SettingVisibilityProfilesModel.__instance = cls() + return SettingVisibilityProfilesModel.__instance + + __instance = None # type: "SettingVisibilityProfilesModel" \ No newline at end of file diff --git a/resources/qml/Menus/SettingVisibilityProfilesMenu.qml b/resources/qml/Menus/SettingVisibilityProfilesMenu.qml index b71520a2ca..764f07ed41 100644 --- a/resources/qml/Menus/SettingVisibilityProfilesMenu.qml +++ b/resources/qml/Menus/SettingVisibilityProfilesMenu.qml @@ -10,13 +10,13 @@ import Cura 1.0 as Cura Menu { id: menu - title: "Visible Settings" + title: catalog.i18nc("@action:inmenu", "Visible Settings") property bool showingSearchResults property bool showingAllSettings signal showAllSettings() - signal showSettingVisibilityProfile(string profileName) + signal showSettingVisibilityProfile() MenuItem { @@ -40,31 +40,21 @@ Menu Instantiator { - model: ListModel - { - id: presetNamesList - Component.onCompleted: - { - // returned value is Dictionary (Ex: {1:"Basic"}, The number 1 is the weight and sort by weight) - var itemsDict = UM.Preferences.getValue("general/visible_settings_preset") - var sorted = []; - for(var key in itemsDict) { - sorted[sorted.length] = key; - } - sorted.sort(); - for(var i = 0; i < sorted.length; i++) { - presetNamesList.append({text: itemsDict[sorted[i]], value: i}); - } - } - } + model: Cura.SettingVisibilityProfilesModel MenuItem { - text: model.text + text: model.name checkable: true checked: false exclusiveGroup: group - onTriggered: showSettingVisibilityProfile(model.text) + onTriggered: + { + UM.Preferences.setValue("general/visible_settings", model.settings.join(";")); + UM.Preferences.setValue("general/preset_setting_visibility_choice", model.id); + + showSettingVisibilityProfile(); + } } onObjectAdded: menu.insertItem(index, object) diff --git a/resources/qml/Preferences/SettingVisibilityPage.qml b/resources/qml/Preferences/SettingVisibilityPage.qml index 0e3069d194..3d94fca6c4 100644 --- a/resources/qml/Preferences/SettingVisibilityPage.qml +++ b/resources/qml/Preferences/SettingVisibilityPage.qml @@ -26,8 +26,8 @@ UM.PreferencesPage UM.Preferences.resetPreference("general/visible_settings") // After calling this function update Setting visibility preset combobox. - // Reset should set "Basic" setting preset - visibilityPreset.setBasicPreset() + // Reset should set default setting preset ("Basic") + visibilityPreset.setDefaultPreset() } resetEnabled: true; @@ -84,7 +84,7 @@ UM.PreferencesPage if (visibilityPreset.currentIndex != visibilityPreset.model.count - 1) { visibilityPreset.currentIndex = visibilityPreset.model.count - 1 - UM.Preferences.setValue("general/preset_setting_visibility_choice", visibilityPreset.model.get(visibilityPreset.currentIndex).text) + UM.Preferences.setValue("general/preset_setting_visibility_choice", visibilityPreset.model.getItem(visibilityPreset.currentIndex).id) } } } @@ -110,83 +110,44 @@ UM.PreferencesPage ComboBox { - property int customOptionValue: 100 - - function setBasicPreset() + function setDefaultPreset() { - var index = 0 - for(var i = 0; i < presetNamesList.count; ++i) - { - if(model.get(i).text == "Basic") - { - index = i; - break; - } - } - - visibilityPreset.currentIndex = index + visibilityPreset.currentIndex = 0 } id: visibilityPreset - width: 150 + width: 150 * screenScaleFactor anchors { top: parent.top right: parent.right } - model: ListModel - { - id: presetNamesList - Component.onCompleted: - { - // returned value is Dictionary (Ex: {1:"Basic"}, The number 1 is the weight and sort by weight) - var itemsDict = UM.Preferences.getValue("general/visible_settings_preset") - var sorted = []; - for(var key in itemsDict) { - sorted[sorted.length] = key; - } - - sorted.sort(); - for(var i = 0; i < sorted.length; i++) { - presetNamesList.append({text: itemsDict[sorted[i]], value: i}); - } - - // By agreement lets "Custom" option will have value 100 - presetNamesList.append({text: "Custom", value: visibilityPreset.customOptionValue}); - } - } + model: Cura.SettingVisibilityProfilesModel + textRole: "name" currentIndex: { // Load previously selected preset. - var text = UM.Preferences.getValue("general/preset_setting_visibility_choice"); - - - - var index = 0; - for(var i = 0; i < presetNamesList.count; ++i) + var index = model.find("id", UM.Preferences.getValue("general/preset_setting_visibility_choice")); + if(index == -1) { - if(model.get(i).text == text) - { - index = i; - break; - } + index = 0; } + return index; } onActivated: { // TODO What to do if user is selected "Custom from Combobox" ? - if (model.get(index).text == "Custom"){ - UM.Preferences.setValue("general/preset_setting_visibility_choice", model.get(index).text) + if (model.getItem(index).id == "custom"){ + UM.Preferences.setValue("general/preset_setting_visibility_choice", model.get(index).id) return } - var newVisibleSettings = CuraApplication.getVisibilitySettingPreset(model.get(index).text) - UM.Preferences.setValue("general/visible_settings", newVisibleSettings) - UM.Preferences.setValue("general/preset_setting_visibility_choice", model.get(index).text) + UM.Preferences.setValue("general/visible_settings", model.getItem(index).settings.join(";")) + UM.Preferences.setValue("general/preset_setting_visibility_choice", model.getItem(index).id) } } @@ -268,7 +229,7 @@ UM.PreferencesPage if (visibilityPreset.currentIndex != visibilityPreset.model.count - 1) { visibilityPreset.currentIndex = visibilityPreset.model.count - 1 - UM.Preferences.setValue("general/preset_setting_visibility_choice", visibilityPreset.model.get(visibilityPreset.currentIndex).text) + UM.Preferences.setValue("general/preset_setting_visibility_choice", visibilityPreset.model.getItem(visibilityPreset.currentIndex).id) } } } diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index f3a4e92d96..6eb37981be 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -151,10 +151,6 @@ Item } onShowSettingVisibilityProfile: { - var newVisibleSettings = CuraApplication.getVisibilitySettingPreset(profileName) - UM.Preferences.setValue("general/visible_settings", newVisibleSettings) - UM.Preferences.setValue("general/preset_setting_visibility_choice", profileName) - base.showingAllSettings = false; base.findingSettings = false; filter.text = ""; From f79e787f2c959c12d0253ce4a5de4a24f519d323 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 8 Feb 2018 23:19:12 +0100 Subject: [PATCH 12/75] Refactor SettingVisibilityProfiles to SettingVisibilityPresets This is closer to the current Cura intend --- cura/CuraApplication.py | 10 +++++----- ...del.py => SettingVisibilityPresetsModel.py} | 18 +++++++++--------- ...nu.qml => SettingVisibilityPresetsMenu.qml} | 2 +- .../qml/Preferences/SettingVisibilityPage.qml | 2 +- resources/qml/Settings/SettingView.qml | 6 +++--- 5 files changed, 19 insertions(+), 19 deletions(-) rename cura/Settings/{SettingVisibilityProfilesModel.py => SettingVisibilityPresetsModel.py} (83%) rename resources/qml/Menus/{SettingVisibilityProfilesMenu.qml => SettingVisibilityPresetsMenu.qml} (98%) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 7afea3de13..19a5b50e65 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -57,7 +57,7 @@ from cura.Settings.QualityAndUserProfilesModel import QualityAndUserProfilesMode from cura.Settings.SettingInheritanceManager import SettingInheritanceManager from cura.Settings.UserProfilesModel import UserProfilesModel from cura.Settings.SimpleModeSettingsManager import SimpleModeSettingsManager -from cura.Settings.SettingVisibilityProfilesModel import SettingVisibilityProfilesModel +from cura.Settings.SettingVisibilityPresetsModel import SettingVisibilityPresetsModel from . import PlatformPhysics @@ -79,7 +79,7 @@ from cura.Settings.ContainerSettingsModel import ContainerSettingsModel from cura.Settings.MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler from cura.Settings.QualitySettingsModel import QualitySettingsModel from cura.Settings.ContainerManager import ContainerManager -from cura.Settings.SettingVisibilityProfilesModel import SettingVisibilityProfilesModel +from cura.Settings.SettingVisibilityPresetsModel import SettingVisibilityPresetsModel from cura.ObjectsModel import ObjectsModel from cura.BuildPlateModel import BuildPlateModel @@ -358,13 +358,13 @@ class CuraApplication(QtApplication): preferences.setDefault("local_file/last_used_type", "text/x-gcode") - default_visibility_profile = SettingVisibilityProfilesModel.getInstance().getItem(0) + default_visibility_profile = SettingVisibilityPresetsModel.getInstance().getItem(0) preferences.setDefault("general/visible_settings", ";".join(default_visibility_profile["settings"])) preferences.setDefault("general/preset_setting_visibility_choice", default_visibility_profile["id"]) preset_setting_visibility_choice = Preferences.getInstance().getValue("general/preset_setting_visibility_choice") - if not SettingVisibilityProfilesModel.getInstance().find("id", preset_setting_visibility_choice): + if not SettingVisibilityPresetsModel.getInstance().find("id", preset_setting_visibility_choice): Preferences.getInstance().setValue("general/preset_setting_visibility_choice", default_visibility_profile["id"]) self.applicationShuttingDown.connect(self.saveSettings) @@ -816,7 +816,7 @@ class CuraApplication(QtApplication): qmlRegisterType(MachineNameValidator, "Cura", 1, 0, "MachineNameValidator") qmlRegisterType(UserChangesModel, "Cura", 1, 1, "UserChangesModel") qmlRegisterSingletonType(ContainerManager, "Cura", 1, 0, "ContainerManager", ContainerManager.createContainerManager) - qmlRegisterSingletonType(SettingVisibilityProfilesModel, "Cura", 1, 0, "SettingVisibilityProfilesModel", SettingVisibilityProfilesModel.createSettingVisibilityProfilesModel) + qmlRegisterSingletonType(SettingVisibilityPresetsModel, "Cura", 1, 0, "SettingVisibilityPresetsModel", SettingVisibilityPresetsModel.createSettingVisibilityPresetsModel) # As of Qt5.7, it is necessary to get rid of any ".." in the path for the singleton to work. actions_url = QUrl.fromLocalFile(os.path.abspath(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Actions.qml"))) diff --git a/cura/Settings/SettingVisibilityProfilesModel.py b/cura/Settings/SettingVisibilityPresetsModel.py similarity index 83% rename from cura/Settings/SettingVisibilityProfilesModel.py rename to cura/Settings/SettingVisibilityPresetsModel.py index 2bc5f2f382..a5b4541ee4 100644 --- a/cura/Settings/SettingVisibilityProfilesModel.py +++ b/cura/Settings/SettingVisibilityPresetsModel.py @@ -13,7 +13,7 @@ from UM.Qt.ListModel import ListModel from UM.Resources import Resources from UM.MimeTypeDatabase import MimeTypeDatabase, MimeTypeNotFoundError -class SettingVisibilityProfilesModel(ListModel): +class SettingVisibilityPresetsModel(ListModel): IdRole = Qt.UserRole + 1 NameRole = Qt.UserRole + 2 SettingsRole = Qt.UserRole + 4 @@ -31,7 +31,7 @@ class SettingVisibilityProfilesModel(ListModel): def _populate(self): items = [] - for item in Resources.getAllResourcesOfType(Resources.PresetSettingVisibilityGroups): + for item in Resources.getAllResourcesOfType(Resources.PresetSettingVisibilityPresets): try: mime_type = MimeTypeDatabase.getMimeTypeForFile(item) except MimeTypeNotFoundError: @@ -76,15 +76,15 @@ class SettingVisibilityProfilesModel(ListModel): # Factory function, used by QML @staticmethod - def createSettingVisibilityProfilesModel(engine, js_engine): - return SettingVisibilityProfilesModel.getInstance() + def createSettingVisibilityPresetsModel(engine, js_engine): + return SettingVisibilityPresetsModel.getInstance() ## Get the singleton instance for this class. @classmethod - def getInstance(cls) -> "SettingVisibilityProfilesModel": + def getInstance(cls) -> "SettingVisibilityPresetsModel": # Note: Explicit use of class name to prevent issues with inheritance. - if not SettingVisibilityProfilesModel.__instance: - SettingVisibilityProfilesModel.__instance = cls() - return SettingVisibilityProfilesModel.__instance + if not SettingVisibilityPresetsModel.__instance: + SettingVisibilityPresetsModel.__instance = cls() + return SettingVisibilityPresetsModel.__instance - __instance = None # type: "SettingVisibilityProfilesModel" \ No newline at end of file + __instance = None # type: "SettingVisibilityPresetsModel" \ No newline at end of file diff --git a/resources/qml/Menus/SettingVisibilityProfilesMenu.qml b/resources/qml/Menus/SettingVisibilityPresetsMenu.qml similarity index 98% rename from resources/qml/Menus/SettingVisibilityProfilesMenu.qml rename to resources/qml/Menus/SettingVisibilityPresetsMenu.qml index 764f07ed41..6fcadeac2c 100644 --- a/resources/qml/Menus/SettingVisibilityProfilesMenu.qml +++ b/resources/qml/Menus/SettingVisibilityPresetsMenu.qml @@ -40,7 +40,7 @@ Menu Instantiator { - model: Cura.SettingVisibilityProfilesModel + model: Cura.SettingVisibilityPresetsModel MenuItem { diff --git a/resources/qml/Preferences/SettingVisibilityPage.qml b/resources/qml/Preferences/SettingVisibilityPage.qml index 3d94fca6c4..64c997e5f7 100644 --- a/resources/qml/Preferences/SettingVisibilityPage.qml +++ b/resources/qml/Preferences/SettingVisibilityPage.qml @@ -123,7 +123,7 @@ UM.PreferencesPage right: parent.right } - model: Cura.SettingVisibilityProfilesModel + model: Cura.SettingVisibilityPresetsModel textRole: "name" currentIndex: diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 6eb37981be..e2e99fbc3d 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -110,7 +110,7 @@ Item ToolButton { - id: settingVisibilityProfileMenu + id: settingVisibilityMenu width: height height: UM.Theme.getSize("setting_control").height @@ -137,7 +137,7 @@ Item } label: Label{} } - menu: SettingVisibilityProfilesMenu + menu: SettingVisibilityPresetsMenu { showingSearchResults: findingSettings showingAllSettings: showingAllSettings @@ -185,7 +185,7 @@ Item topMargin: UM.Theme.getSize("sidebar_margin").height left: parent.left leftMargin: UM.Theme.getSize("sidebar_margin").width - right: settingVisibilityProfileMenu.left + right: settingVisibilityMenu.left rightMargin: Math.floor(UM.Theme.getSize("default_margin").width / 2) } height: visible ? UM.Theme.getSize("setting_control").height : 0 From 4c1002bf47f9ae901a3ecae59c34391b4cb8158b Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 9 Feb 2018 09:19:15 +0100 Subject: [PATCH 13/75] Move setting visibility resource type from Uranium to Cura --- cura/CuraApplication.py | 4 +++- cura/Settings/SettingVisibilityPresetsModel.py | 5 ++++- .../advanced.cfg | 0 .../basic.cfg | 0 .../expert.cfg | 0 5 files changed, 7 insertions(+), 2 deletions(-) rename resources/{preset_setting_visibility_groups => setting_visibility}/advanced.cfg (100%) rename resources/{preset_setting_visibility_groups => setting_visibility}/basic.cfg (100%) rename resources/{preset_setting_visibility_groups => setting_visibility}/expert.cfg (100%) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 19a5b50e65..05ed178bf9 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -130,6 +130,7 @@ class CuraApplication(QtApplication): MachineStack = Resources.UserType + 7 ExtruderStack = Resources.UserType + 8 DefinitionChangesContainer = Resources.UserType + 9 + SettingVisibilityPreset = Resources.UserType + 10 Q_ENUMS(ResourceTypes) @@ -183,6 +184,7 @@ class CuraApplication(QtApplication): Resources.addStorageType(self.ResourceTypes.ExtruderStack, "extruders") Resources.addStorageType(self.ResourceTypes.MachineStack, "machine_instances") Resources.addStorageType(self.ResourceTypes.DefinitionChangesContainer, "definition_changes") + Resources.addStorageType(self.ResourceTypes.SettingVisibilityPreset, "setting_visibility") ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer, "quality") ContainerRegistry.getInstance().addResourceType(self.ResourceTypes.QualityInstanceContainer, "quality_changes") @@ -360,8 +362,8 @@ class CuraApplication(QtApplication): default_visibility_profile = SettingVisibilityPresetsModel.getInstance().getItem(0) + preferences.addPreference("general/preset_setting_visibility_choice", default_visibility_profile["id"]) preferences.setDefault("general/visible_settings", ";".join(default_visibility_profile["settings"])) - preferences.setDefault("general/preset_setting_visibility_choice", default_visibility_profile["id"]) preset_setting_visibility_choice = Preferences.getInstance().getValue("general/preset_setting_visibility_choice") if not SettingVisibilityPresetsModel.getInstance().find("id", preset_setting_visibility_choice): diff --git a/cura/Settings/SettingVisibilityPresetsModel.py b/cura/Settings/SettingVisibilityPresetsModel.py index a5b4541ee4..6052fd2509 100644 --- a/cura/Settings/SettingVisibilityPresetsModel.py +++ b/cura/Settings/SettingVisibilityPresetsModel.py @@ -13,6 +13,9 @@ from UM.Qt.ListModel import ListModel from UM.Resources import Resources from UM.MimeTypeDatabase import MimeTypeDatabase, MimeTypeNotFoundError +import cura.CuraApplication + + class SettingVisibilityPresetsModel(ListModel): IdRole = Qt.UserRole + 1 NameRole = Qt.UserRole + 2 @@ -31,7 +34,7 @@ class SettingVisibilityPresetsModel(ListModel): def _populate(self): items = [] - for item in Resources.getAllResourcesOfType(Resources.PresetSettingVisibilityPresets): + for item in Resources.getAllResourcesOfType(cura.CuraApplication.CuraApplication.ResourceTypes.SettingVisibilityPreset): try: mime_type = MimeTypeDatabase.getMimeTypeForFile(item) except MimeTypeNotFoundError: diff --git a/resources/preset_setting_visibility_groups/advanced.cfg b/resources/setting_visibility/advanced.cfg similarity index 100% rename from resources/preset_setting_visibility_groups/advanced.cfg rename to resources/setting_visibility/advanced.cfg diff --git a/resources/preset_setting_visibility_groups/basic.cfg b/resources/setting_visibility/basic.cfg similarity index 100% rename from resources/preset_setting_visibility_groups/basic.cfg rename to resources/setting_visibility/basic.cfg diff --git a/resources/preset_setting_visibility_groups/expert.cfg b/resources/setting_visibility/expert.cfg similarity index 100% rename from resources/preset_setting_visibility_groups/expert.cfg rename to resources/setting_visibility/expert.cfg From 74fe281e1db3b3fbf1dc3bf4a3cd041d03acc079 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 9 Feb 2018 17:04:08 +0100 Subject: [PATCH 14/75] Sync currently selected preset between visibility-page and -menu --- cura/CuraApplication.py | 5 --- .../Settings/SettingVisibilityPresetsModel.py | 32 ++++++++++++-- plugins/3MFReader/ThreeMFWorkspaceReader.py | 2 +- resources/qml/Cura.qml | 5 ++- .../Menus/SettingVisibilityPresetsMenu.qml | 40 ++++++----------- .../qml/Preferences/SettingVisibilityPage.qml | 43 +++++++++---------- resources/qml/Settings/SettingView.qml | 16 ++++++- 7 files changed, 82 insertions(+), 61 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 05ed178bf9..87c24d788b 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -362,13 +362,8 @@ class CuraApplication(QtApplication): default_visibility_profile = SettingVisibilityPresetsModel.getInstance().getItem(0) - preferences.addPreference("general/preset_setting_visibility_choice", default_visibility_profile["id"]) preferences.setDefault("general/visible_settings", ";".join(default_visibility_profile["settings"])) - preset_setting_visibility_choice = Preferences.getInstance().getValue("general/preset_setting_visibility_choice") - if not SettingVisibilityPresetsModel.getInstance().find("id", preset_setting_visibility_choice): - Preferences.getInstance().setValue("general/preset_setting_visibility_choice", default_visibility_profile["id"]) - self.applicationShuttingDown.connect(self.saveSettings) self.engineCreatedSignal.connect(self._onEngineCreated) diff --git a/cura/Settings/SettingVisibilityPresetsModel.py b/cura/Settings/SettingVisibilityPresetsModel.py index 6052fd2509..307cbc3602 100644 --- a/cura/Settings/SettingVisibilityPresetsModel.py +++ b/cura/Settings/SettingVisibilityPresetsModel.py @@ -9,7 +9,7 @@ from PyQt5.QtCore import pyqtProperty, Qt, pyqtSignal, pyqtSlot, QUrl from UM.Logger import Logger from UM.Qt.ListModel import ListModel - +from UM.Preferences import Preferences from UM.Resources import Resources from UM.MimeTypeDatabase import MimeTypeDatabase, MimeTypeNotFoundError @@ -27,11 +27,18 @@ class SettingVisibilityPresetsModel(ListModel): self.addRoleName(self.NameRole, "name") self.addRoleName(self.SettingsRole, "settings") - self._container_ids = [] - self._containers = [] - self._populate() + preferences = Preferences.getInstance() + preferences.addPreference("cura/active_setting_visibility_preset", "custom") + + self._active_preset = Preferences.getInstance().getValue("cura/active_setting_visibility_preset") + if self.find("id", self._active_preset) < 0: + self._active_preset = "custom" + + self.activePresetChanged.emit() + + def _populate(self): items = [] for item in Resources.getAllResourcesOfType(cura.CuraApplication.CuraApplication.ResourceTypes.SettingVisibilityPreset): @@ -77,6 +84,23 @@ class SettingVisibilityPresetsModel(ListModel): items.sort(key = lambda k: (k["weight"], k["id"])) self.setItems(items) + @pyqtSlot(str) + def setActivePreset(self, preset_id): + if preset_id != "custom" and self.find("id", preset_id) == -1: + Logger.log("w", "Tried to set active preset to unknown id %s", preset_id) + return + + Preferences.getInstance().setValue("cura/active_setting_visibility_preset", preset_id); + + self._active_preset = preset_id + self.activePresetChanged.emit() + + activePresetChanged = pyqtSignal() + + @pyqtProperty(str, notify = activePresetChanged) + def activePreset(self): + return self._active_preset + # Factory function, used by QML @staticmethod def createSettingVisibilityPresetsModel(engine, js_engine): diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index a2e02fa9d4..bef387d667 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -453,7 +453,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader): Logger.log("w", "Workspace did not contain visible settings. Leaving visibility unchanged") else: global_preferences.setValue("general/visible_settings", visible_settings) - global_preferences.setValue("general/preset_setting_visibility_choice", "Custom") + global_preferences.setValue("cura/active_setting_visibility_preset", "custom") categories_expanded = temp_preferences.getValue("cura/categories_expanded") if categories_expanded is None: diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 6f649a7273..6d48ee34c3 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -625,7 +625,10 @@ UM.MainWindow { preferences.visible = true; preferences.setPage(1); - preferences.getCurrentItem().scrollToSection(source.key); + if(source && source.key) + { + preferences.getCurrentItem().scrollToSection(source.key); + } } } diff --git a/resources/qml/Menus/SettingVisibilityPresetsMenu.qml b/resources/qml/Menus/SettingVisibilityPresetsMenu.qml index 6fcadeac2c..72ca93a27e 100644 --- a/resources/qml/Menus/SettingVisibilityPresetsMenu.qml +++ b/resources/qml/Menus/SettingVisibilityPresetsMenu.qml @@ -32,9 +32,13 @@ Menu { text: catalog.i18nc("@action:inmenu", "Custom selection") checkable: true - checked: !showingSearchResults && !showingAllSettings + checked: !showingSearchResults && !showingAllSettings && Cura.SettingVisibilityPresetsModel.activePreset == "custom" exclusiveGroup: group - onTriggered: showSettingVisibilityProfile("Custom") + onTriggered: + { + Cura.SettingVisibilityPresetsModel.setActivePreset("custom"); + showSettingVisibilityProfile(); + } } MenuSeparator { } @@ -46,12 +50,13 @@ Menu { text: model.name checkable: true - checked: false + checked: model.id == Cura.SettingVisibilityPresetsModel.activePreset exclusiveGroup: group onTriggered: { + Cura.SettingVisibilityPresetsModel.setActivePreset(model.id); + UM.Preferences.setValue("general/visible_settings", model.settings.join(";")); - UM.Preferences.setValue("general/preset_setting_visibility_choice", model.id); showSettingVisibilityProfile(); } @@ -61,27 +66,6 @@ Menu onObjectRemoved: menu.removeItem(object) } - MenuSeparator { visible: false } - MenuItem - { - text: catalog.i18nc("@action:inmenu", "Changed settings") - visible: false - enabled: true - checkable: true - checked: showingAllSettings - exclusiveGroup: group - onTriggered: showAllSettings() - } - MenuItem - { - text: catalog.i18nc("@action:inmenu", "Settings in profile") - visible: false - enabled: true - checkable: true - checked: showingAllSettings - exclusiveGroup: group - onTriggered: showAllSettings() - } MenuSeparator {} MenuItem { @@ -89,7 +73,11 @@ Menu checkable: true checked: showingAllSettings exclusiveGroup: group - onTriggered: showAllSettings() + onTriggered: + { + Cura.SettingVisibilityPresetsModel.setActivePreset("custom"); + showAllSettings(); + } } MenuSeparator {} MenuItem diff --git a/resources/qml/Preferences/SettingVisibilityPage.qml b/resources/qml/Preferences/SettingVisibilityPage.qml index 64c997e5f7..fa1b1a1be1 100644 --- a/resources/qml/Preferences/SettingVisibilityPage.qml +++ b/resources/qml/Preferences/SettingVisibilityPage.qml @@ -37,6 +37,8 @@ UM.PreferencesPage id: base; anchors.fill: parent; + property bool inhibitSwitchToCustom: false + CheckBox { id: toggleVisibleSettings @@ -84,7 +86,7 @@ UM.PreferencesPage if (visibilityPreset.currentIndex != visibilityPreset.model.count - 1) { visibilityPreset.currentIndex = visibilityPreset.model.count - 1 - UM.Preferences.setValue("general/preset_setting_visibility_choice", visibilityPreset.model.getItem(visibilityPreset.currentIndex).id) + UM.Preferences.setValue("cura/active_setting_visibility_preset", visibilityPreset.model.getItem(visibilityPreset.currentIndex).id) } } } @@ -129,7 +131,7 @@ UM.PreferencesPage currentIndex: { // Load previously selected preset. - var index = model.find("id", UM.Preferences.getValue("general/preset_setting_visibility_choice")); + var index = model.find("id", model.activePreset); if(index == -1) { index = 0; @@ -140,14 +142,12 @@ UM.PreferencesPage onActivated: { - // TODO What to do if user is selected "Custom from Combobox" ? - if (model.getItem(index).id == "custom"){ - UM.Preferences.setValue("general/preset_setting_visibility_choice", model.get(index).id) - return - } + base.inhibitSwitchToCustom = true; + model.setActivePreset(model.getItem(index).id); - UM.Preferences.setValue("general/visible_settings", model.getItem(index).settings.join(";")) - UM.Preferences.setValue("general/preset_setting_visibility_choice", model.getItem(index).id) + UM.Preferences.setValue("general/visible_settings", model.getItem(index).settings.join(";")); + UM.Preferences.setValue("cura/active_setting_visibility_preset", model.getItem(index).id); + base.inhibitSwitchToCustom = false; } } @@ -177,7 +177,16 @@ UM.PreferencesPage exclude: ["machine_settings", "command_line_settings"] showAncestors: true expanded: ["*"] - visibilityHandler: UM.SettingPreferenceVisibilityHandler { } + visibilityHandler: UM.SettingPreferenceVisibilityHandler + { + onVisibilityChanged: + { + if(Cura.SettingVisibilityPresetsModel.activePreset != "" && !base.inhibitSwitchToCustom) + { + Cura.SettingVisibilityPresetsModel.setActivePreset("custom"); + } + } + } } delegate: Loader @@ -220,19 +229,7 @@ UM.PreferencesPage { id: settingVisibilityItem; - UM.SettingVisibilityItem { - - // after changing any visibility of settings, set the preset to the "Custom" option - visibilityChangeCallback : function() - { - // If already "Custom" then don't do nothing - if (visibilityPreset.currentIndex != visibilityPreset.model.count - 1) - { - visibilityPreset.currentIndex = visibilityPreset.model.count - 1 - UM.Preferences.setValue("general/preset_setting_visibility_choice", visibilityPreset.model.getItem(visibilityPreset.currentIndex).id) - } - } - } + UM.SettingVisibilityItem { } } } } \ No newline at end of file diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index e2e99fbc3d..98753d0b4c 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -18,6 +18,7 @@ Item property Action configureSettings property bool findingSettings property bool showingAllSettings + property bool inhibitSwitchToCustom: false signal showTooltip(Item item, point location, string text) signal hideTooltip() @@ -559,7 +560,15 @@ Item //: Settings context menu action visible: !findingSettings; text: catalog.i18nc("@action:menu", "Hide this setting"); - onTriggered: definitionsModel.hide(contextMenu.key); + onTriggered: + { + definitionsModel.hide(contextMenu.key); + // visible settings have changed, so we're no longer showing a preset + if (Cura.SettingVisibilityPresetsModel.activePreset != "") + { + Cura.SettingVisibilityPresetsModel.setActivePreset("custom"); + } + } } MenuItem { @@ -586,6 +595,11 @@ Item { definitionsModel.show(contextMenu.key); } + // visible settings have changed, so we're no longer showing a preset + if (Cura.SettingVisibilityPresetsModel.activePreset != "") + { + Cura.SettingVisibilityPresetsModel.setActivePreset("custom"); + } } } MenuItem From 61bbfcda3ab59b555618fa07e07f37c516cef284 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 9 Feb 2018 19:28:00 +0100 Subject: [PATCH 15/75] Add back "custom" option to the setting visibility page --- .../qml/Preferences/SettingVisibilityPage.qml | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/resources/qml/Preferences/SettingVisibilityPage.qml b/resources/qml/Preferences/SettingVisibilityPage.qml index fa1b1a1be1..bc271971b4 100644 --- a/resources/qml/Preferences/SettingVisibilityPage.qml +++ b/resources/qml/Preferences/SettingVisibilityPage.qml @@ -125,28 +125,45 @@ UM.PreferencesPage right: parent.right } - model: Cura.SettingVisibilityPresetsModel - textRole: "name" + model: ListModel + { + id: visibilityPresetsModel + Component.onCompleted: + { + visibilityPresetsModel.append({text: catalog.i18nc("@action:inmenu", "Custom selection"), id: "custom"}); + + var presets = Cura.SettingVisibilityPresetsModel; + for(var i = 0; i < presets.rowCount(); i++) + { + visibilityPresetsModel.append({text: presets.getItem(i)["name"], id: presets.getItem(i)["id"]}); + } + } + } currentIndex: { // Load previously selected preset. - var index = model.find("id", model.activePreset); + var index = Cura.SettingVisibilityPresetsModel.find("id", Cura.SettingVisibilityPresetsModel.activePreset); if(index == -1) { - index = 0; + return 0; } - return index; + return index + 1; // "Custom selection" entry is added in front, so index is off by 1 } onActivated: { base.inhibitSwitchToCustom = true; - model.setActivePreset(model.getItem(index).id); + var preset_id = visibilityPresetsModel.get(index).id; + Cura.SettingVisibilityPresetsModel.setActivePreset(preset_id); - UM.Preferences.setValue("general/visible_settings", model.getItem(index).settings.join(";")); - UM.Preferences.setValue("cura/active_setting_visibility_preset", model.getItem(index).id); + UM.Preferences.setValue("cura/active_setting_visibility_preset", preset_id); + if (preset_id != "custom") + { + UM.Preferences.setValue("general/visible_settings", Cura.SettingVisibilityPresetsModel.getItem(index - 1).settings.join(";")); + // "Custom selection" entry is added in front, so index is off by 1 + } base.inhibitSwitchToCustom = false; } } From 3f9d92ade090fed2356e3ace748165e6b644d7e7 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 9 Feb 2018 20:22:46 +0100 Subject: [PATCH 16/75] Remove "Search Results" item It makes no sense to have it; instead none of the other menuitems are selected when search results are shown. --- resources/qml/Menus/SettingVisibilityPresetsMenu.qml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/resources/qml/Menus/SettingVisibilityPresetsMenu.qml b/resources/qml/Menus/SettingVisibilityPresetsMenu.qml index 72ca93a27e..d9ffd630e1 100644 --- a/resources/qml/Menus/SettingVisibilityPresetsMenu.qml +++ b/resources/qml/Menus/SettingVisibilityPresetsMenu.qml @@ -18,16 +18,6 @@ Menu signal showAllSettings() signal showSettingVisibilityProfile() - MenuItem - { - text: catalog.i18nc("@action:inmenu", "Search Results") - checkable: true - visible: showingSearchResults - checked: showingSearchResults - exclusiveGroup: group - } - MenuSeparator { visible: showingSearchResults } - MenuItem { text: catalog.i18nc("@action:inmenu", "Custom selection") From 112bb260acb8655e929cf377b18f505d62608eb1 Mon Sep 17 00:00:00 2001 From: Simon Lundell Date: Sun, 18 Feb 2018 11:44:04 +0100 Subject: [PATCH 17/75] Do not send new G-CODEs when the RX buffer is filling up. This is in an attempt to fix #1777 where smoothieware based printers would halt in the middle of the print. --- plugins/USBPrinting/USBPrinterOutputDevice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index b53f502d81..c177ad64d6 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -304,7 +304,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): self._sendCommand(self._command_queue.get()) elif self._paused: pass # Nothing to do! - else: + elif self._serial.in_waiting < 256: # Do not send new G-CODE when the RX buffer is filling up self._sendNextGcodeLine() elif b"resend" in line.lower() or b"rs" in line: # A resend can be requested either by Resend, resend or rs. From d62dbe51876da7c8715b54ec1b30774914a782fa Mon Sep 17 00:00:00 2001 From: Simon Lundell Date: Fri, 23 Feb 2018 16:12:01 +0100 Subject: [PATCH 18/75] Stop sending empty commands as this confuses the communication with e.g. Smoothieware. See PR #3346 and bug #1777 --- plugins/USBPrinting/USBPrinterOutputDevice.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index c177ad64d6..a25bfcb5a2 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -266,7 +266,6 @@ class USBPrinterOutputDevice(PrinterOutputDevice): command = (command + "\n").encode() if not command.endswith(b"\n"): command += b"\n" - self._serial.write(b"\n") self._serial.write(command) def _update(self): From 946a09eb62e1bbf3fe623e47da2de61de3686d37 Mon Sep 17 00:00:00 2001 From: Simon Lundell Date: Fri, 23 Feb 2018 16:14:57 +0100 Subject: [PATCH 19/75] Parse heatbed temperatures even if no extruder temperature is sent. Attempts to fix bug #3332 --- plugins/USBPrinting/USBPrinterOutputDevice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index a25bfcb5a2..e34736c241 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -280,7 +280,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): self.sendCommand("M105") self._last_temperature_request = time() - if b"ok T:" in line or line.startswith(b"T:"): # Temperature message + if b"ok T:" in line or line.startswith(b"T:") or b"ok B:" in line or line.startswith(b"B:"): # Temperature message. 'T:' for extruder and 'B:' for bed extruder_temperature_matches = re.findall(b"T(\d*): ?([\d\.]+) ?\/?([\d\.]+)?", line) # Update all temperature values for match, extruder in zip(extruder_temperature_matches, self._printers[0].extruders): From b1be25e8d8142d59cddb5d5ec505808810598c66 Mon Sep 17 00:00:00 2001 From: Simon Lundell Date: Fri, 23 Feb 2018 18:13:42 +0100 Subject: [PATCH 20/75] Start the device thread GCODE streaming after the main thread has sent those first few lines of codes. This fixes an issue where a race between the main thread and device thread, caused both to try to send the first lines of codes and the line numbering were then messed up. This caused the printer to send a resend request for a line whose number Cura did not recognize, and the printing would wait forever to start. --- plugins/USBPrinting/USBPrinterOutputDevice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index e34736c241..942e8d5306 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -198,7 +198,6 @@ class USBPrinterOutputDevice(PrinterOutputDevice): # Reset line number. If this is not done, first line is sometimes ignored self._gcode.insert(0, "M110") self._gcode_position = 0 - self._is_printing = True self._print_start_time = time() self._print_estimated_time = int(Application.getInstance().getPrintInformation().currentPrintTime.getDisplayString(DurationFormat.Format.Seconds)) @@ -206,6 +205,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): for i in range(0, 4): # Push first 4 entries before accepting other inputs self._sendNextGcodeLine() + self._is_printing = True self.writeFinished.emit(self) def _autoDetectFinished(self, job: AutoDetectBaudJob): From b3f0292ce60c2920a9117de8fb102dc40f19aa51 Mon Sep 17 00:00:00 2001 From: Simon Lundell Date: Fri, 23 Feb 2018 18:19:11 +0100 Subject: [PATCH 21/75] Cancel the print when the printer sends message (b'!!') about a fatal error. --- plugins/USBPrinting/USBPrinterOutputDevice.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index 942e8d5306..8cb477d10d 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -298,6 +298,9 @@ class USBPrinterOutputDevice(PrinterOutputDevice): self._printers[0].updateTargetBedTemperature(float(match[1])) if self._is_printing: + if line.startswith(b'!!'): + Logger.log('e', "Printer signals fatal error. Cancelling print. {}".format(line)) + self.cancelPrint() if b"ok" in line: if not self._command_queue.empty(): self._sendCommand(self._command_queue.get()) From 5214ef3bde8e632908d2b346e2a96c6f8a3df766 Mon Sep 17 00:00:00 2001 From: Simon Lundell Date: Fri, 23 Feb 2018 18:20:47 +0100 Subject: [PATCH 22/75] Add a retry when probing for printers on discovered ports. This is to accomodate for printers that needs a few seconds to initialize before they open the port. --- plugins/USBPrinting/AutoDetectBaudJob.py | 61 +++++++++++++----------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/plugins/USBPrinting/AutoDetectBaudJob.py b/plugins/USBPrinting/AutoDetectBaudJob.py index 72f4f20262..50bb831ba8 100644 --- a/plugins/USBPrinting/AutoDetectBaudJob.py +++ b/plugins/USBPrinting/AutoDetectBaudJob.py @@ -22,6 +22,7 @@ class AutoDetectBaudJob(Job): def run(self): Logger.log("d", "Auto detect baud rate started.") timeout = 3 + tries = 2 programmer = Stk500v2() serial = None @@ -31,36 +32,38 @@ class AutoDetectBaudJob(Job): except: programmer.close() - for baud_rate in self._all_baud_rates: - Logger.log("d", "Checking {serial} if baud rate {baud_rate} works".format(serial= self._serial_port, baud_rate = baud_rate)) + for retry in range(tries): + for baud_rate in self._all_baud_rates: + Logger.log("d", "Checking {serial} if baud rate {baud_rate} works".format(serial= self._serial_port, baud_rate = baud_rate)) - if serial is None: - try: - serial = Serial(str(self._serial_port), baud_rate, timeout = timeout, writeTimeout = timeout) - except SerialException as e: - Logger.logException("w", "Unable to create serial") - continue - else: - # We already have a serial connection, just change the baud rate. - try: - serial.baudrate = baud_rate - except: - continue - sleep(1.5) # Ensure that we are not talking to the boot loader. 1.5 seconds seems to be the magic number - successful_responses = 0 - - serial.write(b"\n") # Ensure we clear out previous responses - serial.write(b"M105\n") - - timeout_time = time() + timeout - - while timeout_time > time(): - line = serial.readline() - if b"ok T:" in line: - successful_responses += 1 - if successful_responses >= 3: - self.setResult(baud_rate) - return + if serial is None: + try: + serial = Serial(str(self._serial_port), baud_rate, timeout = timeout, writeTimeout = timeout) + except SerialException as e: + Logger.logException("w", "Unable to create serial") + continue + else: + # We already have a serial connection, just change the baud rate. + try: + serial.baudrate = baud_rate + except: + continue + sleep(1.5) # Ensure that we are not talking to the boot loader. 1.5 seconds seems to be the magic number + successful_responses = 0 + serial.write(b"\n") # Ensure we clear out previous responses serial.write(b"M105\n") + + timeout_time = time() + timeout + + while timeout_time > time(): + line = serial.readline() + if b"ok T:" in line: + successful_responses += 1 + if successful_responses >= 3: + self.setResult(baud_rate) + return + + serial.write(b"M105\n") + sleep(15) # Give the printer some time to init and try again. self.setResult(None) # Unable to detect the correct baudrate. From a024be78dc4a74d6ef9ae0776533b42e33d1d443 Mon Sep 17 00:00:00 2001 From: Simon Lundell Date: Fri, 23 Feb 2018 18:54:31 +0100 Subject: [PATCH 23/75] Reverting this commit as this was not the correct way to fix it. Revert "Do not send new G-CODEs when the RX buffer is filling up. This is in an attempt to fix #1777 where smoothieware based printers would halt in the middle of the print." This reverts commit 112bb260acb8655e929cf377b18f505d62608eb1. --- plugins/USBPrinting/USBPrinterOutputDevice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index 8cb477d10d..4f5fdc340a 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -306,7 +306,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): self._sendCommand(self._command_queue.get()) elif self._paused: pass # Nothing to do! - elif self._serial.in_waiting < 256: # Do not send new G-CODE when the RX buffer is filling up + else: self._sendNextGcodeLine() elif b"resend" in line.lower() or b"rs" in line: # A resend can be requested either by Resend, resend or rs. From d9eb4406bfa3560e0f2042d506705968ccab1d55 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 5 Mar 2018 23:25:33 +0100 Subject: [PATCH 24/75] Speed up showing all settings --- resources/qml/Settings/SettingView.qml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 16b0691a56..73a76a028f 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -235,12 +235,13 @@ Item function updateDefinitionModel() { - if(findingSettings || base.showingAllSettings) + if(findingSettings || showingAllSettings) { expandedCategories = definitionsModel.expanded.slice(); - definitionsModel.expanded = ["*"]; + definitionsModel.expanded = [""]; // keep categories closed while to prevent render while making settings visible one by one definitionsModel.showAncestors = true; definitionsModel.showAll = true; + definitionsModel.expanded = ["*"]; } else { From 7a56c4f63180b1293d8f580b9f9775d7a6d60dfa Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 5 Mar 2018 23:50:42 +0100 Subject: [PATCH 25/75] Fix activating "All settings" menuitem --- resources/qml/Menus/SettingVisibilityPresetsMenu.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/qml/Menus/SettingVisibilityPresetsMenu.qml b/resources/qml/Menus/SettingVisibilityPresetsMenu.qml index d9ffd630e1..17204d6b6c 100644 --- a/resources/qml/Menus/SettingVisibilityPresetsMenu.qml +++ b/resources/qml/Menus/SettingVisibilityPresetsMenu.qml @@ -65,7 +65,6 @@ Menu exclusiveGroup: group onTriggered: { - Cura.SettingVisibilityPresetsModel.setActivePreset("custom"); showAllSettings(); } } From 6c1aee2c471f7d01d5d055083462467c64e6c007 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 5 Mar 2018 23:55:23 +0100 Subject: [PATCH 26/75] Update copyright --- cura/Settings/SettingVisibilityPresetsModel.py | 2 +- resources/qml/Menus/SettingVisibilityPresetsMenu.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/Settings/SettingVisibilityPresetsModel.py b/cura/Settings/SettingVisibilityPresetsModel.py index 307cbc3602..9ec8875a39 100644 --- a/cura/Settings/SettingVisibilityPresetsModel.py +++ b/cura/Settings/SettingVisibilityPresetsModel.py @@ -1,4 +1,4 @@ -# Copyright (c) 2016 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import os diff --git a/resources/qml/Menus/SettingVisibilityPresetsMenu.qml b/resources/qml/Menus/SettingVisibilityPresetsMenu.qml index 17204d6b6c..de9eac08cf 100644 --- a/resources/qml/Menus/SettingVisibilityPresetsMenu.qml +++ b/resources/qml/Menus/SettingVisibilityPresetsMenu.qml @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Ultimaker B.V. +// Copyright (c) 2018 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.2 From 711d60e8c7e2948f80e50f7c339dcfa27e21d6ee Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 9 Mar 2018 16:30:37 +0100 Subject: [PATCH 27/75] Allow switching back to the "Custom" set --- .../Settings/SettingVisibilityPresetsModel.py | 27 ++++++++++++++++--- .../Menus/SettingVisibilityPresetsMenu.qml | 2 ++ .../qml/Preferences/SettingVisibilityPage.qml | 5 ++++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/cura/Settings/SettingVisibilityPresetsModel.py b/cura/Settings/SettingVisibilityPresetsModel.py index 9ec8875a39..e5a2e24412 100644 --- a/cura/Settings/SettingVisibilityPresetsModel.py +++ b/cura/Settings/SettingVisibilityPresetsModel.py @@ -29,10 +29,12 @@ class SettingVisibilityPresetsModel(ListModel): self._populate() - preferences = Preferences.getInstance() - preferences.addPreference("cura/active_setting_visibility_preset", "custom") + self._preferences = Preferences.getInstance() + self._preferences.addPreference("cura/active_setting_visibility_preset", "custom") # Preference to store which preset is currently selected + self._preferences.addPreference("cura/custom_visible_settings", "") # Preference that stores the "custom" set so it can always be restored (even after a restart) + self._preferences.preferenceChanged.connect(self._onPreferencesChanged) - self._active_preset = Preferences.getInstance().getValue("cura/active_setting_visibility_preset") + self._active_preset = self._preferences.getValue("cura/active_setting_visibility_preset") if self.find("id", self._active_preset) < 0: self._active_preset = "custom" @@ -90,7 +92,12 @@ class SettingVisibilityPresetsModel(ListModel): Logger.log("w", "Tried to set active preset to unknown id %s", preset_id) return - Preferences.getInstance().setValue("cura/active_setting_visibility_preset", preset_id); + if preset_id == "custom" and self._active_preset == "custom": + # Copy current visibility set to custom visibility set preference so it can be restored later + visibility_string = self._preferences.getValue("general/visible_settings") + self._preferences.setValue("cura/custom_visible_settings", visibility_string) + + self._preferences.setValue("cura/active_setting_visibility_preset", preset_id) self._active_preset = preset_id self.activePresetChanged.emit() @@ -101,6 +108,18 @@ class SettingVisibilityPresetsModel(ListModel): def activePreset(self): return self._active_preset + def _onPreferencesChanged(self, name): + if name != "general/visible_settings": + return + + if self._active_preset != "custom": + return + + # Copy current visibility set to custom visibility set preference so it can be restored later + visibility_string = self._preferences.getValue("general/visible_settings") + self._preferences.setValue("cura/custom_visible_settings", visibility_string) + + # Factory function, used by QML @staticmethod def createSettingVisibilityPresetsModel(engine, js_engine): diff --git a/resources/qml/Menus/SettingVisibilityPresetsMenu.qml b/resources/qml/Menus/SettingVisibilityPresetsMenu.qml index de9eac08cf..19c36e6118 100644 --- a/resources/qml/Menus/SettingVisibilityPresetsMenu.qml +++ b/resources/qml/Menus/SettingVisibilityPresetsMenu.qml @@ -27,6 +27,8 @@ Menu onTriggered: { Cura.SettingVisibilityPresetsModel.setActivePreset("custom"); + // Restore custom set from preference + UM.Preferences.setValue("general/visible_settings", UM.Preferences.getValue("cura/custom_visible_settings")); showSettingVisibilityProfile(); } } diff --git a/resources/qml/Preferences/SettingVisibilityPage.qml b/resources/qml/Preferences/SettingVisibilityPage.qml index bc271971b4..f0c24e2cbe 100644 --- a/resources/qml/Preferences/SettingVisibilityPage.qml +++ b/resources/qml/Preferences/SettingVisibilityPage.qml @@ -164,6 +164,11 @@ UM.PreferencesPage UM.Preferences.setValue("general/visible_settings", Cura.SettingVisibilityPresetsModel.getItem(index - 1).settings.join(";")); // "Custom selection" entry is added in front, so index is off by 1 } + else + { + // Restore custom set from preference + UM.Preferences.setValue("general/visible_settings", UM.Preferences.getValue("cura/custom_visible_settings")); + } base.inhibitSwitchToCustom = false; } } From 7123441db81819f8d46158e8b227ae18ab71fbbf Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 9 Mar 2018 16:51:46 +0100 Subject: [PATCH 28/75] Fix keeping settings visible when showing all settings --- resources/qml/Settings/SettingView.qml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 73a76a028f..1acc1845e5 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -18,7 +18,6 @@ Item property Action configureSettings property bool findingSettings property bool showingAllSettings - property bool inhibitSwitchToCustom: false signal showTooltip(Item item, point location, string text) signal hideTooltip() @@ -559,13 +558,13 @@ Item MenuItem { //: Settings context menu action - visible: !findingSettings; + visible: !(findingSettings || showingAllSettings); text: catalog.i18nc("@action:menu", "Hide this setting"); onTriggered: { definitionsModel.hide(contextMenu.key); // visible settings have changed, so we're no longer showing a preset - if (Cura.SettingVisibilityPresetsModel.activePreset != "") + if (Cura.SettingVisibilityPresetsModel.activePreset != "" && !showingAllSettings) { Cura.SettingVisibilityPresetsModel.setActivePreset("custom"); } @@ -585,7 +584,7 @@ Item return catalog.i18nc("@action:menu", "Keep this setting visible"); } } - visible: findingSettings; + visible: (findingSettings || showingAllSettings); onTriggered: { if (contextMenu.settingVisible) @@ -597,7 +596,7 @@ Item definitionsModel.show(contextMenu.key); } // visible settings have changed, so we're no longer showing a preset - if (Cura.SettingVisibilityPresetsModel.activePreset != "") + if (Cura.SettingVisibilityPresetsModel.activePreset != "" && !showingAllSettings) { Cura.SettingVisibilityPresetsModel.setActivePreset("custom"); } From e62ed62bdda71cd162dbb80cfeaa586a46e217bc Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 9 Mar 2018 16:57:09 +0100 Subject: [PATCH 29/75] Remove duplicate import --- cura/CuraApplication.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 903533aecf..64ef37b71d 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -70,7 +70,6 @@ from cura.Machines.Models.BrandMaterialsModel import BrandMaterialsModel from cura.Settings.SettingInheritanceManager import SettingInheritanceManager from cura.Settings.SimpleModeSettingsManager import SimpleModeSettingsManager -from cura.Settings.SettingVisibilityPresetsModel import SettingVisibilityPresetsModel from cura.Machines.VariantManager import VariantManager from cura.Machines.Models.QualityManagementModel import QualityManagementModel From 4a16d04dd79fe8639f8381d44297e0bf3f74e8de Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Wed, 14 Mar 2018 10:50:54 +0100 Subject: [PATCH 30/75] CURA-5053 Changed printer type checking "Still hard coded but at least doesn't use `elif` because `elif`/`else if` should not exist in any programming language." - Grumpy Pedants & Ian >:[ --- .../PrinterOutput/NetworkedPrinterOutputDevice.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py index a4934b7f74..eefbd9ae12 100644 --- a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py +++ b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py @@ -56,12 +56,15 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): self._connection_state_before_timeout = None # type: Optional[ConnectionState] printer_type = self._properties.get(b"machine", b"").decode("utf-8") - if printer_type.startswith("9511"): - self._printer_type = "ultimaker3_extended" - elif printer_type.startswith("9066"): - self._printer_type = "ultimaker3" - else: - self._printer_type = "unknown" + printer_type_identifiers = { + "9066": "ultimaker3", + "9511": "ultimaker3_extended" + } + self._printer_type = "Unknown" + for key, value in printer_type_identifiers.items(): + if printer_type.startswith(key): + self._printer_type = value + break def requestWrite(self, nodes, file_name=None, filter_by_machine=False, file_handler=None, **kwargs) -> None: raise NotImplementedError("requestWrite needs to be implemented") From f5f8bf19ecdd0e463b8eb3626639637cfd76e8d8 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 14 Mar 2018 11:05:32 +0100 Subject: [PATCH 31/75] Fix QML warnings --- resources/qml/SidebarHeader.qml | 7 ++++++- resources/qml/WorkspaceSummaryDialog.qml | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml index 74e189789d..dcb351e866 100644 --- a/resources/qml/SidebarHeader.qml +++ b/resources/qml/SidebarHeader.qml @@ -347,7 +347,8 @@ Column id: materialSelection property var activeExtruder: Cura.MachineManager.activeStack - property var currentRootMaterialName: activeExtruder.material.name + property var hasActiveExtruder: activeExtruder != null + property var currentRootMaterialName: hasActiveExtruder ? activeExtruder.material.name : "" text: currentRootMaterialName tooltip: currentRootMaterialName @@ -366,6 +367,10 @@ Column property var valueWarning: ! Cura.MachineManager.isActiveQualitySupported function isMaterialSupported () { + if (!hasActiveExtruder) + { + return false; + } return Cura.ContainerManager.getContainerMetaDataEntry(activeExtruder.material.id, "compatible") == "True" } } diff --git a/resources/qml/WorkspaceSummaryDialog.qml b/resources/qml/WorkspaceSummaryDialog.qml index 12eba13dd9..cf19e45fdd 100644 --- a/resources/qml/WorkspaceSummaryDialog.qml +++ b/resources/qml/WorkspaceSummaryDialog.qml @@ -101,7 +101,7 @@ UM.Dialog } Label { - text: Cura.MachineManager.activeMachine.definition.name + text: (Cura.MachineManager.activeMachine == null) ? "" : Cura.MachineManager.activeMachine.definition.name width: (parent.width / 3) | 0 } } From 9f40e3925dcb02028fee554a78cdadf52ac4f47e Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Wed, 14 Mar 2018 11:07:38 +0100 Subject: [PATCH 32/75] CURA-5090 added timer to SettingInheritanceManager._update --- cura/Settings/SettingInheritanceManager.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/cura/Settings/SettingInheritanceManager.py b/cura/Settings/SettingInheritanceManager.py index 0d4cd02cdb..e317b20f68 100644 --- a/cura/Settings/SettingInheritanceManager.py +++ b/cura/Settings/SettingInheritanceManager.py @@ -1,7 +1,7 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal +from PyQt5.QtCore import QObject, QTimer, pyqtProperty, pyqtSignal from UM.FlameProfiler import pyqtSlot from UM.Application import Application from UM.Logger import Logger @@ -30,6 +30,11 @@ class SettingInheritanceManager(QObject): ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderChanged) self._onActiveExtruderChanged() + self._update_timer = QTimer() + self._update_timer.setInterval(500) + self._update_timer.setSingleShot(True) + self._update_timer.timeout.connect(self._update) + settingsWithIntheritanceChanged = pyqtSignal() ## Get the keys of all children settings with an override. @@ -226,9 +231,7 @@ class SettingInheritanceManager(QObject): self._onActiveExtruderChanged() def _onContainersChanged(self, container): - # TODO: Multiple container changes in sequence now cause quite a few recalculations. - # This isn't that big of an issue, but it could be in the future. - self._update() + self._update_timer.start() @staticmethod def createSettingInheritanceManager(engine=None, script_engine=None): From c69987b8240ba82464bb04a4e5c7057f664a5c22 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 14 Mar 2018 11:26:22 +0100 Subject: [PATCH 33/75] CURA-4870 Reintroduce the "Check compatibility" link in the sidebar --- resources/qml/SidebarHeader.qml | 68 +++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml index 74e189789d..5591f49797 100644 --- a/resources/qml/SidebarHeader.qml +++ b/resources/qml/SidebarHeader.qml @@ -468,6 +468,74 @@ Column } } + // Material info row + Item + { + id: materialInfoRow + height: Math.round(UM.Theme.getSize("sidebar_setup").height / 2) + visible: (Cura.MachineManager.hasVariants || Cura.MachineManager.hasMaterials) && !sidebar.monitoringPrint && !sidebar.hideSettings + + anchors + { + left: parent.left + leftMargin: UM.Theme.getSize("sidebar_margin").width + right: parent.right + rightMargin: UM.Theme.getSize("sidebar_margin").width + } + + Item { + height: UM.Theme.getSize("sidebar_setup").height + anchors.right: parent.right + width: Math.round(parent.width * 0.7 + UM.Theme.getSize("sidebar_margin").width) + + UM.RecolorImage { + id: warningImage + anchors.right: materialInfoLabel.left + anchors.rightMargin: UM.Theme.getSize("default_margin").width + anchors.verticalCenter: parent.Bottom + source: UM.Theme.getIcon("warning") + width: UM.Theme.getSize("section_icon").width + height: UM.Theme.getSize("section_icon").height + color: UM.Theme.getColor("material_compatibility_warning") + visible: !Cura.MachineManager.isCurrentSetupSupported + } + + Label { + id: materialInfoLabel + wrapMode: Text.WordWrap + text: "" + catalog.i18nc("@label", "Check compatibility") + "" + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + linkColor: UM.Theme.getColor("text_link") + verticalAlignment: Text.AlignTop + anchors.top: parent.top + anchors.right: parent.right + anchors.bottom: parent.bottom + + MouseArea { + anchors.fill: parent + hoverEnabled: true + onClicked: { + // open the material URL with web browser + var version = UM.Application.version; + var machineName = Cura.MachineManager.activeMachine.definition.id; + var url = "https://ultimaker.com/materialcompatibility/" + version + "/" + machineName + "?utm_source=cura&utm_medium=software&utm_campaign=resources"; + Qt.openUrlExternally(url); + } + onEntered: { + var content = catalog.i18nc("@tooltip", "Click to check the material compatibility on Ultimaker.com."); + base.showTooltip( + materialInfoRow, + Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), + catalog.i18nc("@tooltip", content) + ); + } + onExited: base.hideTooltip(); + } + } + } + } + UM.SettingPropertyProvider { id: machineExtruderCount From 8dd2243399b509858c7b0753c4cb11a94ca4cf3e Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 14 Mar 2018 11:32:36 +0100 Subject: [PATCH 34/75] Fixed stupid mistake that made all snapshots be taken from the back of model --- cura/Snapshot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/Snapshot.py b/cura/Snapshot.py index afc8818116..1f2a24aecd 100644 --- a/cura/Snapshot.py +++ b/cura/Snapshot.py @@ -66,7 +66,7 @@ class Snapshot: size = max(bbox.width, bbox.height, bbox.depth * 0.5) # Looking from this direction (x, y, z) in OGL coordinates - looking_from_offset = Vector(1, 1, -2) + looking_from_offset = Vector(-1, 1, 2) if size > 0: # determine the watch distance depending on the size looking_from_offset = looking_from_offset * size * 1.3 From 160a59191b883a6de4567b7e18ab8acf68c587e7 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Wed, 14 Mar 2018 11:45:00 +0100 Subject: [PATCH 35/75] Show Tooltip for the unavailable slider's area CURA-5044 --- resources/qml/SidebarSimple.qml | 69 +++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index a7c8a1b8c5..ca3f5fb5d8 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -243,6 +243,75 @@ Item anchors.top: parent.top anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + // This Item is used only for tooltip, for slider area which is unavailable + Item{ + + function showTooltip (showTooltip) { + if(showTooltip){ + var content = catalog.i18nc("@tooltip","This quality profile is not available for you current material and nozzle configuration. Please change these to enable this quality profile") + base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("sidebar_margin").width, customisedSettings.height), content) + } + else + base.hideTooltip(); + } + + id: unavailableLineToolTip + height: 20 // hovered area height + z: parent.z + 1 // should be higher, otherwise the area can be hovered + x: 0 + anchors.verticalCenter: qualitySlider.verticalCenter + + Rectangle{ + id: leftArea + width: { + if(qualityModel.availableTotalTicks == 0) + return 0 + + return qualityModel.qualitySliderStepWidth * qualityModel.qualitySliderAvailableMin - 10 + } + height: parent.height + color: "transparent" + + MouseArea { + anchors.fill: parent + hoverEnabled: true + enabled: Cura.SimpleModeSettingsManager.isProfileUserCreated == false + onEntered: unavailableLineToolTip.showTooltip(true) + onExited: unavailableLineToolTip.showTooltip(false) + } + } + + Rectangle{ + id: rightArea + width: { + if(qualityModel.availableTotalTicks == 0) + return 0 + + return qualityModel.qualitySliderMarginRight - 10 + } + height: parent.height + color: "transparent" + x: { + if(qualityModel.availableTotalTicks == 0){ + return 0 + } + + var leftUnavailableArea = qualityModel.qualitySliderStepWidth * qualityModel.qualitySliderAvailableMin + var totalGap = qualityModel.qualitySliderStepWidth * (qualityModel.availableTotalTicks -1) + leftUnavailableArea + 10 + + return totalGap + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + enabled: Cura.SimpleModeSettingsManager.isProfileUserCreated == false + onEntered: unavailableLineToolTip.showTooltip(true) + onExited: unavailableLineToolTip.showTooltip(false) + } + } + } + // Draw Unavailable line Rectangle { From 23a2b6e0f7ef86454904e67590b10725c6bf5d4b Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 14 Mar 2018 11:54:10 +0100 Subject: [PATCH 36/75] CURA-4870 Don't show the configuration selector if the printer is not a network printer (for example don't show if is USB) --- resources/qml/MachineSelection.qml | 2 +- resources/qml/Menus/ConfigurationMenu/SyncButton.qml | 12 ++++++++---- resources/qml/Sidebar.qml | 3 ++- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/resources/qml/MachineSelection.qml b/resources/qml/MachineSelection.qml index 4bddd20b2b..b3f9629703 100644 --- a/resources/qml/MachineSelection.qml +++ b/resources/qml/MachineSelection.qml @@ -12,7 +12,7 @@ import "Menus" ToolButton { id: base - property var isNetworkPrinter: Cura.MachineManager.activeMachineNetworkKey != "" + property bool isNetworkPrinter: Cura.MachineManager.activeMachineNetworkKey != "" property var printerStatus: Cura.MachineManager.printerOutputDevices.length != 0 ? "connected" : "disconnected" text: isNetworkPrinter ? Cura.MachineManager.activeMachineNetworkGroupName : Cura.MachineManager.activeMachineName diff --git a/resources/qml/Menus/ConfigurationMenu/SyncButton.qml b/resources/qml/Menus/ConfigurationMenu/SyncButton.qml index a2d1d53b78..f0394cc107 100644 --- a/resources/qml/Menus/ConfigurationMenu/SyncButton.qml +++ b/resources/qml/Menus/ConfigurationMenu/SyncButton.qml @@ -17,11 +17,15 @@ Button width: parent.width height: parent.height - function updateOnSync() { - if (outputDevice != undefined) { - for (var index in outputDevice.uniqueConfigurations) { + function updateOnSync() + { + if (outputDevice != undefined) + { + for (var index in outputDevice.uniqueConfigurations) + { var configuration = outputDevice.uniqueConfigurations[index] - if (Cura.MachineManager.matchesConfiguration(configuration)) { + if (Cura.MachineManager.matchesConfiguration(configuration)) + { base.matched = true; return; } diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index 47882c9ecc..5211ee5a1d 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -19,6 +19,7 @@ Rectangle property bool hideView: Cura.MachineManager.activeMachineName == "" // Is there an output device for this printer? + property bool isNetworkPrinter: Cura.MachineManager.activeMachineNetworkKey != "" property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands property var connectedPrinter: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null @@ -106,7 +107,7 @@ Rectangle ConfigurationSelection { id: configSelection - visible: printerConnected && !sidebar.monitoringPrint && !sidebar.hideSettings + visible: isNetworkPrinter && !sidebar.monitoringPrint && !sidebar.hideSettings width: visible ? Math.round(base.width * 0.15) : 0 height: UM.Theme.getSize("sidebar_header").height anchors.top: base.top From b8c32eb166a58c8fba86d97aa8a94c19d113791d Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 14 Mar 2018 12:59:31 +0100 Subject: [PATCH 37/75] Fix typo in ConfigurationListView.qml --- resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml index 4a2d4cd062..52fd0e6556 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml @@ -59,7 +59,7 @@ Column section.criteria: ViewSection.FullString section.delegate: sectionHeading - model: (ouputDevice != null) ? outputDevice.uniqueConfigurations : [] + model: (outputDevice != null) ? outputDevice.uniqueConfigurations : [] delegate: ConfigurationItem { width: parent.width - UM.Theme.getSize("default_margin").width From 53626d3c67100682b97dff30e9cfe17440e25cf4 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 14 Mar 2018 13:02:51 +0100 Subject: [PATCH 38/75] Also force update validationState CURA-5048 --- cura/Settings/MachineManager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index b41cdc9799..019354ad40 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -882,7 +882,7 @@ class MachineManager(QObject): @pyqtSlot() def forceUpdateAllSettings(self): with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue): - property_names = ["value", "resolve"] + property_names = ["value", "resolve", "validationState"] for container in [self._global_container_stack] + list(self._global_container_stack.extruders.values()): for setting_key in container.getAllKeys(): container.propertiesChanged.emit(setting_key, property_names) From fcd50b0cf21a1e0755213685f7a89fa4f61964be Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 14 Mar 2018 13:10:56 +0100 Subject: [PATCH 39/75] Fix code style CURA-5044 --- resources/qml/SidebarSimple.qml | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index ca3f5fb5d8..2ee0a9e9f1 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -244,15 +244,17 @@ Item anchors.topMargin: UM.Theme.getSize("sidebar_margin").height // This Item is used only for tooltip, for slider area which is unavailable - Item{ - - function showTooltip (showTooltip) { - if(showTooltip){ - var content = catalog.i18nc("@tooltip","This quality profile is not available for you current material and nozzle configuration. Please change these to enable this quality profile") - base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("sidebar_margin").width, customisedSettings.height), content) + Item + { + function showTooltip (showTooltip) + { + if (showTooltip) { + var content = catalog.i18nc("@tooltip", "This quality profile is not available for you current material and nozzle configuration. Please change these to enable this quality profile") + base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("sidebar_margin").width, customisedSettings.height), content) + } + else { + base.hideTooltip() } - else - base.hideTooltip(); } id: unavailableLineToolTip @@ -261,7 +263,8 @@ Item x: 0 anchors.verticalCenter: qualitySlider.verticalCenter - Rectangle{ + Rectangle + { id: leftArea width: { if(qualityModel.availableTotalTicks == 0) @@ -272,7 +275,8 @@ Item height: parent.height color: "transparent" - MouseArea { + MouseArea + { anchors.fill: parent hoverEnabled: true enabled: Cura.SimpleModeSettingsManager.isProfileUserCreated == false @@ -281,7 +285,8 @@ Item } } - Rectangle{ + Rectangle + { id: rightArea width: { if(qualityModel.availableTotalTicks == 0) @@ -292,7 +297,7 @@ Item height: parent.height color: "transparent" x: { - if(qualityModel.availableTotalTicks == 0){ + if (qualityModel.availableTotalTicks == 0) { return 0 } From d6b270954adb4c116c947f95e644f4643768662a Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 14 Mar 2018 13:11:22 +0100 Subject: [PATCH 40/75] Fix the case when no quality is available CURA-5044 The tooltip should also show up if there is no quality available. --- resources/qml/SidebarSimple.qml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index 2ee0a9e9f1..41ecb529eb 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -266,10 +266,11 @@ Item Rectangle { id: leftArea - width: { - if(qualityModel.availableTotalTicks == 0) - return 0 - + width: + { + if (qualityModel.availableTotalTicks == 0) { + return qualityModel.qualitySliderStepWidth * qualityModel.totalTicks + } return qualityModel.qualitySliderStepWidth * qualityModel.qualitySliderAvailableMin - 10 } height: parent.height From 05c59ddaa763277de44e56d0855e827fe5a357ee Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Wed, 14 Mar 2018 13:29:48 +0100 Subject: [PATCH 41/75] CURA-4946 Handle gcode with wrong quality definition Some gcode has its quality definition set to, say, UM2 extended, which is _actually_ just UM2 gcode. Thus, we check if the definition in the profile matches the current machine or what it in theory should be, either one being valid. See comments in code for details. --- cura/Settings/CuraContainerRegistry.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/cura/Settings/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py index 81cbabc0c9..07ba2db1e5 100644 --- a/cura/Settings/CuraContainerRegistry.py +++ b/cura/Settings/CuraContainerRegistry.py @@ -212,12 +212,20 @@ class CuraContainerRegistry(ContainerRegistry): return { "status": "error", "message": catalog.i18nc("@info:status Don't translate the XML tags or !", "This profile {0} contains incorrect data, could not import it.", file_name)} profile_definition = global_profile.getMetaDataEntry("definition") - expected_machine_definition = "fdmprinter" - if parseBool(global_container_stack.getMetaDataEntry("has_machine_quality", "False")): - expected_machine_definition = global_container_stack.getMetaDataEntry("quality_definition") - if not expected_machine_definition: - expected_machine_definition = global_container_stack.definition.getId() - if expected_machine_definition is not None and profile_definition is not None and profile_definition != expected_machine_definition: + + # Make sure we have a profile_definition in the file: + if profile_definition is None: + break + + # Get the expected machine definition. + # i.e.: We expect gcode for a UM2 Extended to be defined as normal UM2 gcode... + expected_machine_definition = getMachineDefinitionIDForQualitySearch(global_container_stack.definition) + + # ...but that's not always the case for Cura 3.1 and older, so also get the current machine: + current_machine_definition = global_container_stack.definition.getId() + + # And check if the profile_definition matches either one (showing error if not): + if profile_definition not in (expected_machine_definition, current_machine_definition): Logger.log("e", "Profile [%s] is for machine [%s] but the current active machine is [%s]. Will not import the profile", file_name, profile_definition, expected_machine_definition) return { "status": "error", "message": catalog.i18nc("@info:status Don't translate the XML tags or !", "The machine defined in profile {0} ({1}) doesn't match with your current machine ({2}), could not import it.", file_name, profile_definition, expected_machine_definition)} From 474db38ddf1c7f0193fa8486c0294e2eaf9f44e9 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 14 Mar 2018 13:30:20 +0100 Subject: [PATCH 42/75] Fix failing test for um3_extended 0.8 variant --- resources/variants/ultimaker3_bb0.8.inst.cfg | 1 - tests/TestProfileRequirements.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/resources/variants/ultimaker3_bb0.8.inst.cfg b/resources/variants/ultimaker3_bb0.8.inst.cfg index 41c6419ec1..ef6dc625ac 100644 --- a/resources/variants/ultimaker3_bb0.8.inst.cfg +++ b/resources/variants/ultimaker3_bb0.8.inst.cfg @@ -56,7 +56,6 @@ retraction_amount = 4.5 retraction_count_max = 15 retraction_extrusion_window = =retraction_amount retraction_hop = 2 -retraction_hop_enabled = True retraction_hop_only_when_collides = True retraction_min_travel = 5 retraction_prime_speed = 15 diff --git a/tests/TestProfileRequirements.py b/tests/TestProfileRequirements.py index a91a08172c..edeec909f2 100644 --- a/tests/TestProfileRequirements.py +++ b/tests/TestProfileRequirements.py @@ -22,4 +22,4 @@ def test_ultimaker3extended_variants(um3_file, um3e_file): um3.read_file(open(os.path.join(directory, um3_file))) um3e = configparser.ConfigParser() um3e.read_file(open(os.path.join(directory, um3e_file))) - assert um3["values"] == um3e["values"] \ No newline at end of file + assert um3["values"] == um3e["values"] From 191faaba19b71cabacfde4c391961863493fe87f Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 14 Mar 2018 13:49:13 +0100 Subject: [PATCH 43/75] Fix machine definition in quality importing CURA-4946 --- cura/Settings/CuraContainerRegistry.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/cura/Settings/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py index 07ba2db1e5..4e8ece0504 100644 --- a/cura/Settings/CuraContainerRegistry.py +++ b/cura/Settings/CuraContainerRegistry.py @@ -230,6 +230,11 @@ class CuraContainerRegistry(ContainerRegistry): return { "status": "error", "message": catalog.i18nc("@info:status Don't translate the XML tags or !", "The machine defined in profile {0} ({1}) doesn't match with your current machine ({2}), could not import it.", file_name, profile_definition, expected_machine_definition)} + # Fix the global quality profile's definition field in case it's not correct + global_profile.setMetaDataEntry("definition", expected_machine_definition) + quality_name = global_profile.getName() + quality_type = global_profile.getMetaDataEntry("quality_type") + name_seed = os.path.splitext(os.path.basename(file_name))[0] new_name = self.uniqueName(name_seed) @@ -244,11 +249,11 @@ class CuraContainerRegistry(ContainerRegistry): for idx, extruder in enumerate(global_container_stack.extruders.values()): profile_id = ContainerRegistry.getInstance().uniqueName(global_container_stack.getId() + "_extruder_" + str(idx + 1)) profile = InstanceContainer(profile_id) - profile.setName(global_profile.getName()) + profile.setName(quality_name) profile.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) profile.addMetaDataEntry("type", "quality_changes") - profile.addMetaDataEntry("definition", global_profile.getMetaDataEntry("definition")) - profile.addMetaDataEntry("quality_type", global_profile.getMetaDataEntry("quality_type")) + profile.addMetaDataEntry("definition", expected_machine_definition) + profile.addMetaDataEntry("quality_type", quality_type) profile.addMetaDataEntry("position", "0") profile.setDirty(True) if idx == 0: From 1111fbaa78113071e13b477373af1ad0ca22454a Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 14 Mar 2018 13:53:04 +0100 Subject: [PATCH 44/75] CURA-4870 Changing the version upgrade to include new metadata entries for the container stacks. --- .../VersionUpgrade32to33.py | 24 +++++++++++++++++++ .../VersionUpgrade32to33/__init__.py | 11 +++++++++ 2 files changed, 35 insertions(+) diff --git a/plugins/VersionUpgrade/VersionUpgrade32to33/VersionUpgrade32to33.py b/plugins/VersionUpgrade/VersionUpgrade32to33/VersionUpgrade32to33.py index de2240a7c6..620f367e25 100644 --- a/plugins/VersionUpgrade/VersionUpgrade32to33/VersionUpgrade32to33.py +++ b/plugins/VersionUpgrade/VersionUpgrade32to33/VersionUpgrade32to33.py @@ -56,6 +56,8 @@ _EXTRUDER_TO_POSITION = { ## Upgrades configurations from the state they were in at version 3.2 to the # state they should be in at version 3.3. class VersionUpgrade32to33(VersionUpgrade): + + temporary_group_name_counter = 1 ## Gets the version number from a CFG file in Uranium's 3.2 format. # # Since the format may change, this is implemented for the 3.2 format only @@ -74,6 +76,28 @@ class VersionUpgrade32to33(VersionUpgrade): setting_version = int(parser.get("metadata", "setting_version", fallback = 0)) return format_version * 1000000 + setting_version + ## Upgrades a container stack from version 3.2 to 3.3. + # + # \param serialised The serialised form of a container stack. + # \param filename The name of the file to upgrade. + def upgradeStack(self, serialized, filename): + parser = configparser.ConfigParser(interpolation = None) + parser.read_string(serialized) + + if "metadata" in parser and "um_network_key" in parser["metadata"]: + if "hidden" not in parser["metadata"]: + parser["metadata"]["hidden"] = "False" + if "connect_group_name" not in parser["metadata"]: + parser["metadata"]["connect_group_name"] = "Temporary group name #" + str(self.temporary_group_name_counter) + self.temporary_group_name_counter += 1 + + #Update version number. + parser["general"]["version"] = "4" + + result = io.StringIO() + parser.write(result) + return [filename], [result.getvalue()] + ## Upgrades non-quality-changes instance containers to have the new version # number. def upgradeInstanceContainer(self, serialized, filename): diff --git a/plugins/VersionUpgrade/VersionUpgrade32to33/__init__.py b/plugins/VersionUpgrade/VersionUpgrade32to33/__init__.py index c411b4190e..72ff6e1de9 100644 --- a/plugins/VersionUpgrade/VersionUpgrade32to33/__init__.py +++ b/plugins/VersionUpgrade/VersionUpgrade32to33/__init__.py @@ -9,11 +9,22 @@ def getMetaData(): return { "version_upgrade": { # From To Upgrade function + ("machine_stack", 3000004): ("machine_stack", 4000004, upgrade.upgradeStack), + ("extruder_train", 3000004): ("extruder_train", 4000004, upgrade.upgradeStack), + ("definition_changes", 2000004): ("definition_changes", 3000004, upgrade.upgradeInstanceContainer), ("quality_changes", 2000004): ("quality_changes", 3000004, upgrade.upgradeQualityChanges), ("user", 2000004): ("user", 3000004, upgrade.upgradeInstanceContainer) }, "sources": { + "machine_stack": { + "get_version": upgrade.getCfgVersion, + "location": {"./machine_instances"} + }, + "extruder_train": { + "get_version": upgrade.getCfgVersion, + "location": {"./extruders"} + }, "definition_changes": { "get_version": upgrade.getCfgVersion, "location": {"./definition_changes"} From fa9dc7a1df691a282a3ebacc5b5d7bc0ba817ea3 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 14 Mar 2018 14:20:03 +0100 Subject: [PATCH 45/75] Fixed send custom firmware --- plugins/USBPrinting/USBPrinterOutputDevice.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index 5d99f61a51..a17b2af3c3 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -116,7 +116,8 @@ class USBPrinterOutputDevice(PrinterOutputDevice): @pyqtSlot(str) def updateFirmware(self, file): - self._firmware_location = file + # the file path is qurl encoded. + self._firmware_location = file.replace("file://", "") self.showFirmwareInterface() self.setFirmwareUpdateState(FirmwareUpdateState.updating) self._update_firmware_thread.start() From 0655ef0bc52efa03b6892d417e79e3dbe26d638e Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 14 Mar 2018 14:22:19 +0100 Subject: [PATCH 46/75] Fix quality profile import CURA-4946 --- cura/Settings/CuraContainerRegistry.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cura/Settings/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py index 4e8ece0504..ebe8007955 100644 --- a/cura/Settings/CuraContainerRegistry.py +++ b/cura/Settings/CuraContainerRegistry.py @@ -204,7 +204,7 @@ class CuraContainerRegistry(ContainerRegistry): global_profile = profile_or_list[0] else: for profile in profile_or_list: - if not profile.getMetaDataEntry("extruder"): + if not profile.getMetaDataEntry("position"): global_profile = profile break if not global_profile: @@ -296,7 +296,7 @@ class CuraContainerRegistry(ContainerRegistry): else: #More extruders in the imported file than in the machine. continue #Delete the additional profiles. - result = self._configureProfile(profile, profile_id, new_name) + result = self._configureProfile(profile, profile_id, new_name, expected_machine_definition) if result is not None: return {"status": "error", "message": catalog.i18nc( "@info:status Don't translate the XML tags or !", @@ -324,7 +324,7 @@ class CuraContainerRegistry(ContainerRegistry): # \param new_name The new name for the profile. # # \return None if configuring was successful or an error message if an error occurred. - def _configureProfile(self, profile: InstanceContainer, id_seed: str, new_name: str) -> Optional[str]: + def _configureProfile(self, profile: InstanceContainer, id_seed: str, new_name: str, machine_definition_id: str) -> Optional[str]: profile.setDirty(True) # Ensure the profiles are correctly saved new_id = self.createUniqueName("quality_changes", "", id_seed, catalog.i18nc("@label", "Custom profile")) @@ -334,6 +334,7 @@ class CuraContainerRegistry(ContainerRegistry): # Set the unique Id to the profile, so it's generating a new one even if the user imports the same profile # It also solves an issue with importing profiles from G-Codes profile.setMetaDataEntry("id", new_id) + profile.setMetaDataEntry("definition", machine_definition_id) if "type" in profile.getMetaData(): profile.setMetaDataEntry("type", "quality_changes") From 877d061a1b19b16cda66c81989a6000c9b1018b5 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 14 Mar 2018 14:23:40 +0100 Subject: [PATCH 47/75] Catch FileNotFoundError when updating custom firmware In case there is a bug there, we won't crash the thread. --- plugins/USBPrinting/USBPrinterOutputDevice.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index a17b2af3c3..14098b66f8 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -127,9 +127,11 @@ class USBPrinterOutputDevice(PrinterOutputDevice): if self._connection_state != ConnectionState.closed: self.close() - hex_file = intelHex.readHex(self._firmware_location) - if len(hex_file) == 0: - Logger.log("e", "Unable to read provided hex file. Could not update firmware") + try: + hex_file = intelHex.readHex(self._firmware_location) + assert len(hex_file) > 0 + except (FileNotFoundError, AssertionError): + Logger.log("e", "Unable to read provided hex file. Could not update firmware.") self.setFirmwareUpdateState(FirmwareUpdateState.firmware_not_found_error) return From 22573a685d5cd771202e950c2c0e2dc48da25fcc Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 14 Mar 2018 14:31:07 +0100 Subject: [PATCH 48/75] Fix quality definition comparison in quality importing CURA-4946 --- cura/Machines/QualityManager.py | 18 ++++++++++-------- cura/Settings/CuraContainerRegistry.py | 16 ++++++++++------ cura/Settings/MachineManager.py | 2 +- plugins/3MFReader/ThreeMFReader.py | 2 +- plugins/3MFReader/ThreeMFWorkspaceReader.py | 2 +- 5 files changed, 23 insertions(+), 17 deletions(-) diff --git a/cura/Machines/QualityManager.py b/cura/Machines/QualityManager.py index efb940b857..8d972c9192 100644 --- a/cura/Machines/QualityManager.py +++ b/cura/Machines/QualityManager.py @@ -16,6 +16,7 @@ from .QualityGroup import QualityGroup from .QualityNode import QualityNode if TYPE_CHECKING: + from UM.Settings.DefinitionContainer import DefinitionContainer from cura.Settings.GlobalStack import GlobalStack from .QualityChangesGroup import QualityChangesGroup @@ -178,7 +179,7 @@ class QualityManager(QObject): # Returns a dict of "custom profile name" -> QualityChangesGroup def getQualityChangesGroups(self, machine: "GlobalStack") -> dict: - machine_definition_id = getMachineDefinitionIDForQualitySearch(machine) + machine_definition_id = getMachineDefinitionIDForQualitySearch(machine.definition) machine_node = self._machine_quality_type_to_quality_changes_dict.get(machine_definition_id) if not machine_node: @@ -206,7 +207,7 @@ class QualityManager(QObject): # For more details, see QualityGroup. # def getQualityGroups(self, machine: "GlobalStack") -> dict: - machine_definition_id = getMachineDefinitionIDForQualitySearch(machine) + machine_definition_id = getMachineDefinitionIDForQualitySearch(machine.definition) # This determines if we should only get the global qualities for the global stack and skip the global qualities for the extruder stacks has_variant_materials = parseBool(machine.getMetaDataEntry("has_variant_materials", False)) @@ -315,7 +316,7 @@ class QualityManager(QObject): return quality_group_dict def getQualityGroupsForMachineDefinition(self, machine: "GlobalStack") -> dict: - machine_definition_id = getMachineDefinitionIDForQualitySearch(machine) + machine_definition_id = getMachineDefinitionIDForQualitySearch(machine.definition) # To find the quality container for the GlobalStack, check in the following fall-back manner: # (1) the machine-specific node @@ -460,7 +461,7 @@ class QualityManager(QObject): quality_changes.addMetaDataEntry("position", extruder_stack.getMetaDataEntry("position")) # If the machine specifies qualities should be filtered, ensure we match the current criteria. - machine_definition_id = getMachineDefinitionIDForQualitySearch(machine) + machine_definition_id = getMachineDefinitionIDForQualitySearch(machine.definition) quality_changes.setDefinition(machine_definition_id) quality_changes.addMetaDataEntry("setting_version", self._application.SettingVersion) @@ -480,12 +481,13 @@ class QualityManager(QObject): # Example: for an Ultimaker 3 Extended, it has "quality_definition = ultimaker3". This means Ultimaker 3 Extended # shares the same set of qualities profiles as Ultimaker 3. # -def getMachineDefinitionIDForQualitySearch(machine: "GlobalStack", default_definition_id: str = "fdmprinter") -> str: +def getMachineDefinitionIDForQualitySearch(machine_definition: "DefinitionContainer", + default_definition_id: str = "fdmprinter") -> str: machine_definition_id = default_definition_id - if parseBool(machine.getMetaDataEntry("has_machine_quality", False)): + if parseBool(machine_definition.getMetaDataEntry("has_machine_quality", False)): # Only use the machine's own quality definition ID if this machine has machine quality. - machine_definition_id = machine.getMetaDataEntry("quality_definition") + machine_definition_id = machine_definition.getMetaDataEntry("quality_definition") if machine_definition_id is None: - machine_definition_id = machine.definition.getId() + machine_definition_id = machine_definition.getId() return machine_definition_id diff --git a/cura/Settings/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py index ebe8007955..0cf1c7399f 100644 --- a/cura/Settings/CuraContainerRegistry.py +++ b/cura/Settings/CuraContainerRegistry.py @@ -216,16 +216,21 @@ class CuraContainerRegistry(ContainerRegistry): # Make sure we have a profile_definition in the file: if profile_definition is None: break + machine_definition = self.findDefinitionContainers(id = profile_definition) + if not machine_definition: + Logger.log("e", "Incorrect profile [%s]. Unknown machine type [%s]", file_name, profile_definition) + return {"status": "error", + "message": catalog.i18nc("@info:status Don't translate the XML tags or !", "This profile {0} contains incorrect data, could not import it.", file_name) + } + machine_definition = machine_definition[0] # Get the expected machine definition. # i.e.: We expect gcode for a UM2 Extended to be defined as normal UM2 gcode... + profile_definition = getMachineDefinitionIDForQualitySearch(machine_definition) expected_machine_definition = getMachineDefinitionIDForQualitySearch(global_container_stack.definition) - # ...but that's not always the case for Cura 3.1 and older, so also get the current machine: - current_machine_definition = global_container_stack.definition.getId() - # And check if the profile_definition matches either one (showing error if not): - if profile_definition not in (expected_machine_definition, current_machine_definition): + if profile_definition != expected_machine_definition: Logger.log("e", "Profile [%s] is for machine [%s] but the current active machine is [%s]. Will not import the profile", file_name, profile_definition, expected_machine_definition) return { "status": "error", "message": catalog.i18nc("@info:status Don't translate the XML tags or !", "The machine defined in profile {0} ({1}) doesn't match with your current machine ({2}), could not import it.", file_name, profile_definition, expected_machine_definition)} @@ -345,9 +350,8 @@ class CuraContainerRegistry(ContainerRegistry): if not quality_type: return catalog.i18nc("@info:status", "Profile is missing a quality type.") - quality_type_criteria = {"quality_type": quality_type} global_stack = Application.getInstance().getGlobalContainerStack() - definition_id = getMachineDefinitionIDForQualitySearch(global_stack) + definition_id = getMachineDefinitionIDForQualitySearch(global_stack.definition) profile.setDefinition(definition_id) # Check to make sure the imported profile actually makes sense in context of the current configuration. diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 019354ad40..3af6f70e5f 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -628,7 +628,7 @@ class MachineManager(QObject): @pyqtProperty(str, notify = globalContainerChanged) def activeQualityDefinitionId(self) -> str: if self._global_container_stack: - return getMachineDefinitionIDForQualitySearch(self._global_container_stack) + return getMachineDefinitionIDForQualitySearch(self._global_container_stack.definition) return "" ## Gets how the active definition calls variants diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py index ec590a0212..3a1298bdba 100755 --- a/plugins/3MFReader/ThreeMFReader.py +++ b/plugins/3MFReader/ThreeMFReader.py @@ -122,7 +122,7 @@ class ThreeMFReader(MeshReader): um_node.callDecoration("setActiveExtruder", default_stack.getId()) # Get the definition & set it - definition_id = getMachineDefinitionIDForQualitySearch(global_container_stack) + definition_id = getMachineDefinitionIDForQualitySearch(global_container_stack.definition) um_node.callDecoration("getStack").getTop().setDefinition(definition_id) setting_container = um_node.callDecoration("getStack").getTop() diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index f20b9ab181..f5daa77bb0 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -719,7 +719,7 @@ class ThreeMFWorkspaceReader(WorkspaceReader): # Get the correct extruder definition IDs for quality changes from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch - machine_definition_id_for_quality = getMachineDefinitionIDForQualitySearch(global_stack) + machine_definition_id_for_quality = getMachineDefinitionIDForQualitySearch(global_stack.definition) machine_definition_for_quality = self._container_registry.findDefinitionContainers(id = machine_definition_id_for_quality)[0] quality_changes_info = self._machine_info.quality_changes_info From 962c5f32604b314aa4cd71a7b13350a23ac32025 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 14 Mar 2018 14:39:06 +0100 Subject: [PATCH 49/75] Added bit of defensive coding to cluster output device --- plugins/UM3NetworkPrinting/ClusterUM3OutputDevice.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/ClusterUM3OutputDevice.py index 70a5607071..d69d787eab 100644 --- a/plugins/UM3NetworkPrinting/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/ClusterUM3OutputDevice.py @@ -372,11 +372,17 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): print_job.updateState(data["status"]) print_job.updateOwner(data["owner"]) - def _updatePrinter(self, printer, data): + def _updatePrinter(self, printer, data) -> None: # For some unknown reason the cluster wants UUID for everything, except for sending a job directly to a printer. # Then we suddenly need the unique name. So in order to not have to mess up all the other code, we save a mapping. self._printer_uuid_to_unique_name_mapping[data["uuid"]] = data["unique_name"] - machine_definition = ContainerRegistry.getInstance().findDefinitionContainers(name = data["machine_variant"])[0] + + definitions = ContainerRegistry.getInstance().findDefinitionContainers(name = data["machine_variant"]) + if not definitions: + Logger.log("w", "Unable to find definition for machine variant %s", data["machine_variant"]) + return + + machine_definition = definitions[0] printer.updateName(data["friendly_name"]) printer.updateKey(data["uuid"]) From 241611546e643ab50a2145b7186e6d3c5d4a2f1b Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 14 Mar 2018 14:41:43 +0100 Subject: [PATCH 50/75] Fix quality details view CURA-5054 --- cura/Machines/Models/QualitySettingsModel.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cura/Machines/Models/QualitySettingsModel.py b/cura/Machines/Models/QualitySettingsModel.py index 4470ffc80e..b38f6f65c8 100644 --- a/cura/Machines/Models/QualitySettingsModel.py +++ b/cura/Machines/Models/QualitySettingsModel.py @@ -87,9 +87,11 @@ class QualitySettingsModel(ListModel): if self._selected_position == self.GLOBAL_STACK_POSITION: quality_node = quality_group.node_for_global else: - quality_node = quality_group.nodes_for_extruders.get(self._selected_position) + quality_node = quality_group.nodes_for_extruders.get(str(self._selected_position)) settings_keys = quality_group.getAllKeys() - quality_containers = [quality_node.getContainer()] + quality_containers = [] + if quality_node is not None: + quality_containers.append(quality_node.getContainer()) # Here, if the user has selected a quality changes, then "quality_changes_group" will not be None, and we fetch # the settings in that quality_changes_group. @@ -97,7 +99,7 @@ class QualitySettingsModel(ListModel): if self._selected_position == self.GLOBAL_STACK_POSITION: quality_changes_node = quality_changes_group.node_for_global else: - quality_changes_node = quality_changes_group.nodes_for_extruders.get(self._selected_position) + quality_changes_node = quality_changes_group.nodes_for_extruders.get(str(self._selected_position)) if quality_changes_node is not None: # it can be None if number of extruders are changed during runtime try: quality_containers.insert(0, quality_changes_node.getContainer()) From ad3c5466d1078640f3b509f570aa3b0fde99ec6b Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 14 Mar 2018 14:45:01 +0100 Subject: [PATCH 51/75] Added bunch of typing to clusteroutput device --- .../ClusterUM3OutputDevice.py | 51 +++++++++---------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/plugins/UM3NetworkPrinting/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/ClusterUM3OutputDevice.py index d69d787eab..c19c86d6ce 100644 --- a/plugins/UM3NetworkPrinting/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/ClusterUM3OutputDevice.py @@ -22,7 +22,7 @@ from PyQt5.QtCore import pyqtSlot, QUrl, pyqtSignal, pyqtProperty, QObject from time import time from datetime import datetime -from typing import Optional +from typing import Optional, Dict, List import json import os @@ -79,7 +79,6 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): self._latest_reply_handler = None - def requestWrite(self, nodes, file_name=None, filter_by_machine=False, file_handler=None, **kwargs): self.writeStarted.emit(self) @@ -116,7 +115,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): @pyqtSlot() @pyqtSlot(str) - def sendPrintJob(self, target_printer = ""): + def sendPrintJob(self, target_printer: str = ""): Logger.log("i", "Sending print job to printer.") if self._sending_gcode: self._error_message = Message( @@ -157,11 +156,11 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): return True @pyqtProperty(QObject, notify=activePrinterChanged) - def activePrinter(self) -> Optional["PrinterOutputModel"]: + def activePrinter(self) -> Optional[PrinterOutputModel]: return self._active_printer @pyqtSlot(QObject) - def setActivePrinter(self, printer): + def setActivePrinter(self, printer: Optional[PrinterOutputModel]): if self._active_printer != printer: if self._active_printer and self._active_printer.camera: self._active_printer.camera.stop() @@ -173,7 +172,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): self._compressing_gcode = False self._sending_gcode = False - def _onUploadPrintJobProgress(self, bytes_sent, bytes_total): + def _onUploadPrintJobProgress(self, bytes_sent:int, bytes_total:int): if bytes_total > 0: new_progress = bytes_sent / bytes_total * 100 # Treat upload progress as response. Uploading can take more than 10 seconds, so if we don't, we can get @@ -186,7 +185,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): self._progress_message.setProgress(0) self._progress_message.hide() - def _progressMessageActionTriggered(self, message_id=None, action_id=None): + def _progressMessageActionTriggered(self, message_id: Optional[str]=None, action_id: Optional[str]=None) -> None: if action_id == "Abort": Logger.log("d", "User aborted sending print to remote.") self._progress_message.hide() @@ -202,29 +201,29 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): @pyqtSlot() - def openPrintJobControlPanel(self): + def openPrintJobControlPanel(self) -> None: Logger.log("d", "Opening print job control panel...") QDesktopServices.openUrl(QUrl("http://" + self._address + "/print_jobs")) @pyqtSlot() - def openPrinterControlPanel(self): + def openPrinterControlPanel(self) -> None: Logger.log("d", "Opening printer control panel...") QDesktopServices.openUrl(QUrl("http://" + self._address + "/printers")) @pyqtProperty("QVariantList", notify=printJobsChanged) - def printJobs(self): + def printJobs(self)-> List[PrintJobOutputModel] : return self._print_jobs @pyqtProperty("QVariantList", notify=printJobsChanged) - def queuedPrintJobs(self): + def queuedPrintJobs(self) -> List[PrintJobOutputModel]: return [print_job for print_job in self._print_jobs if print_job.assignedPrinter is None or print_job.state == "queued"] @pyqtProperty("QVariantList", notify=printJobsChanged) - def activePrintJobs(self): + def activePrintJobs(self) -> List[PrintJobOutputModel]: return [print_job for print_job in self._print_jobs if print_job.assignedPrinter is not None and print_job.state != "queued"] @pyqtProperty("QVariantList", notify=clusterPrintersChanged) - def connectedPrintersTypeCount(self): + def connectedPrintersTypeCount(self) -> List[PrinterOutputModel]: printer_count = {} for printer in self._printers: if printer.type in printer_count: @@ -237,22 +236,22 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): return result @pyqtSlot(int, result=str) - def formatDuration(self, seconds): + def formatDuration(self, seconds: int) -> str: return Duration(seconds).getDisplayString(DurationFormat.Format.Short) @pyqtSlot(int, result=str) - def getTimeCompleted(self, time_remaining): + def getTimeCompleted(self, time_remaining: int) -> str: current_time = time() datetime_completed = datetime.fromtimestamp(current_time + time_remaining) return "{hour:02d}:{minute:02d}".format(hour=datetime_completed.hour, minute=datetime_completed.minute) @pyqtSlot(int, result=str) - def getDateCompleted(self, time_remaining): + def getDateCompleted(self, time_remaining: int) -> str: current_time = time() datetime_completed = datetime.fromtimestamp(current_time + time_remaining) return (datetime_completed.strftime("%a %b ") + "{day}".format(day=datetime_completed.day)).upper() - def _printJobStateChanged(self): + def _printJobStateChanged(self) -> None: username = self._getUserName() if username is None: @@ -275,13 +274,13 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): # Keep a list of all completed jobs so we know if something changed next time. self._finished_jobs = finished_jobs - def _update(self): + def _update(self) -> None: if not super()._update(): return self.get("printers/", onFinished=self._onGetPrintersDataFinished) self.get("print_jobs/", onFinished=self._onGetPrintJobsFinished) - def _onGetPrintJobsFinished(self, reply: QNetworkReply): + def _onGetPrintJobsFinished(self, reply: QNetworkReply) -> None: if not checkValidGetReply(reply): return @@ -323,7 +322,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): if job_list_changed: self.printJobsChanged.emit() # Do a single emit for all print job changes. - def _onGetPrintersDataFinished(self, reply: QNetworkReply): + def _onGetPrintersDataFinished(self, reply: QNetworkReply) -> None: if not checkValidGetReply(reply): return @@ -352,27 +351,27 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): if removed_printers or printer_list_changed: self.printersChanged.emit() - def _createPrinterModel(self, data): + def _createPrinterModel(self, data: Dict) -> PrinterOutputModel: printer = PrinterOutputModel(output_controller=ClusterUM3PrinterOutputController(self), number_of_extruders=self._number_of_extruders) printer.setCamera(NetworkCamera("http://" + data["ip_address"] + ":8080/?action=stream")) self._printers.append(printer) return printer - def _createPrintJobModel(self, data): + def _createPrintJobModel(self, data: Dict) -> PrintJobOutputModel: print_job = PrintJobOutputModel(output_controller=ClusterUM3PrinterOutputController(self), key=data["uuid"], name= data["name"]) print_job.stateChanged.connect(self._printJobStateChanged) self._print_jobs.append(print_job) return print_job - def _updatePrintJob(self, print_job, data): + def _updatePrintJob(self, print_job: PrintJobOutputModel, data: Dict) -> None: print_job.updateTimeTotal(data["time_total"]) print_job.updateTimeElapsed(data["time_elapsed"]) print_job.updateState(data["status"]) print_job.updateOwner(data["owner"]) - def _updatePrinter(self, printer, data) -> None: + def _updatePrinter(self, printer: PrinterOutputModel, data: Dict) -> None: # For some unknown reason the cluster wants UUID for everything, except for sending a job directly to a printer. # Then we suddenly need the unique name. So in order to not have to mess up all the other code, we save a mapping. self._printer_uuid_to_unique_name_mapping[data["uuid"]] = data["unique_name"] @@ -427,7 +426,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): brand=brand, color=color, name=name) extruder.updateActiveMaterial(material) - def _removeJob(self, job): + def _removeJob(self, job: PrintJobOutputModel): if job not in self._print_jobs: return False @@ -438,7 +437,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): return True - def _removePrinter(self, printer): + def _removePrinter(self, printer: PrinterOutputModel): self._printers.remove(printer) if self._active_printer == printer: self._active_printer = None From 61ce0c3154a2398f775b0a0c92865adf6b19f05f Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 14 Mar 2018 15:15:43 +0100 Subject: [PATCH 52/75] CURA-4870 Make the configuration pop-up open and close with one click. Before it was the case in which sometimes the user needs two clicks for that. Also collapse the dropdown when the configuration is applied. --- .../ConfigurationListView.qml | 1 + .../ConfigurationSelection.qml | 62 +++++++++---------- .../Menus/ConfigurationMenu/SyncButton.qml | 5 -- 3 files changed, 32 insertions(+), 36 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml index 52fd0e6556..999fecd7fd 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml @@ -66,6 +66,7 @@ Column configuration: modelData onActivateConfiguration: { + switchPopupState() Cura.MachineManager.applyRemoteConfiguration(configuration) } } diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationSelection.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationSelection.qml index eb0d5f5cff..a3cf10168b 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationSelection.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationSelection.qml @@ -13,54 +13,54 @@ Item id: configurationSelector property var connectedDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null property var panelWidth: control.width - property var panelVisible: false - SyncButton { - onClicked: configurationSelector.state == "open" ? configurationSelector.state = "closed" : configurationSelector.state = "open" + function switchPopupState() + { + popup.opened ? popup.close() : popup.open() + } + + SyncButton + { + id: syncButton + onClicked: switchPopupState() outputDevice: connectedDevice } - Popup { + Popup + { + // TODO Change once updating to Qt5.10 - This property is already in 5.10 but is manually implemented until upgrade + property bool opened: false id: popup clip: true + closePolicy: Popup.CloseOnPressOutsideParent y: configurationSelector.height - UM.Theme.getSize("default_lining").height x: configurationSelector.width - width width: panelWidth - visible: panelVisible && connectedDevice != null + visible: opened padding: UM.Theme.getSize("default_lining").width - contentItem: ConfigurationListView { + transformOrigin: Popup.Top + contentItem: ConfigurationListView + { id: configList width: panelWidth - 2 * popup.padding outputDevice: connectedDevice } - background: Rectangle { + background: Rectangle + { color: UM.Theme.getColor("setting_control") border.color: UM.Theme.getColor("setting_control_border") } - } - - states: [ - // This adds a second state to the container where the rectangle is farther to the right - State { - name: "open" - PropertyChanges { - target: popup - height: configList.computedHeight - } - }, - State { - name: "closed" - PropertyChanges { - target: popup - height: 0 - } - } - ] - transitions: [ - // This adds a transition that defaults to applying to all state changes - Transition { + exit: Transition + { // This applies a default NumberAnimation to any changes a state change makes to x or y properties - NumberAnimation { properties: "height"; duration: 200; easing.type: Easing.InOutQuad; } + NumberAnimation { property: "visible"; duration: 75; } } - ] + enter: Transition + { + // This applies a default NumberAnimation to any changes a state change makes to x or y properties + NumberAnimation { property: "visible"; duration: 75; } + } + onClosed: opened = false + onOpened: opened = true + } } \ No newline at end of file diff --git a/resources/qml/Menus/ConfigurationMenu/SyncButton.qml b/resources/qml/Menus/ConfigurationMenu/SyncButton.qml index f0394cc107..c292a792db 100644 --- a/resources/qml/Menus/ConfigurationMenu/SyncButton.qml +++ b/resources/qml/Menus/ConfigurationMenu/SyncButton.qml @@ -86,11 +86,6 @@ Button label: Label {} } - onClicked: - { - panelVisible = !panelVisible - } - Connections { target: outputDevice onUniqueConfigurationsChanged: { From e3dd7a449d396cadd97835cb531fe74dc0264b13 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Wed, 14 Mar 2018 16:09:59 +0100 Subject: [PATCH 53/75] CURA-5090 speedups by using qtimers on updating mostly visual elements --- cura/BuildVolume.py | 2 +- cura/CuraApplication.py | 15 +++++++++++--- cura/Machines/Models/MultiBuildPlateModel.py | 12 +++++++++-- cura/ObjectsModel.py | 17 ++++++++++++++-- cura/Scene/ConvexHullDecorator.py | 21 +++++++++++++++++++- plugins/SimulationView/SimulationView.py | 7 ++++--- 6 files changed, 62 insertions(+), 12 deletions(-) diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index 0b81a5183f..d93ce1107d 100755 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -136,6 +136,7 @@ class BuildVolume(SceneNode): if active_extruder_changed is not None: node.callDecoration("getActiveExtruderChangedSignal").disconnect(self._updateDisallowedAreasAndRebuild) node.decoratorsChanged.disconnect(self._updateNodeListeners) + self._updateDisallowedAreasAndRebuild() # make sure we didn't miss anything before we updated the node listeners self._scene_objects = new_scene_objects self._onSettingPropertyChanged("print_sequence", "value") # Create fake event, so right settings are triggered. @@ -150,7 +151,6 @@ class BuildVolume(SceneNode): active_extruder_changed = node.callDecoration("getActiveExtruderChangedSignal") if active_extruder_changed is not None: active_extruder_changed.connect(self._updateDisallowedAreasAndRebuild) - self._updateDisallowedAreasAndRebuild() def setWidth(self, width): if width is not None: diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 84ec787cd7..99cc30a12b 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -4,7 +4,7 @@ #Type hinting. from typing import Dict -from PyQt5.QtCore import QObject +from PyQt5.QtCore import QObject, QTimer from PyQt5.QtNetwork import QLocalServer from PyQt5.QtNetwork import QLocalSocket @@ -283,10 +283,15 @@ class CuraApplication(QtApplication): self._preferred_mimetype = "" self._i18n_catalog = i18nCatalog("cura") - self.getController().getScene().sceneChanged.connect(self.updatePlatformActivity) + self._update_platform_activity_timer = QTimer() + self._update_platform_activity_timer.setInterval(500) + self._update_platform_activity_timer.setSingleShot(True) + self._update_platform_activity_timer.timeout.connect(self.updatePlatformActivity) + + self.getController().getScene().sceneChanged.connect(self.updatePlatformActivityDelayed) self.getController().toolOperationStopped.connect(self._onToolOperationStopped) self.getController().contextMenuRequested.connect(self._onContextMenuRequested) - self.getCuraSceneController().activeBuildPlateChanged.connect(self.updatePlatformActivity) + self.getCuraSceneController().activeBuildPlateChanged.connect(self.updatePlatformActivityDelayed) Resources.addType(self.ResourceTypes.QmlFiles, "qml") Resources.addType(self.ResourceTypes.Firmware, "firmware") @@ -1061,6 +1066,10 @@ class CuraApplication(QtApplication): def getSceneBoundingBoxString(self): return self._i18n_catalog.i18nc("@info 'width', 'depth' and 'height' are variable names that must NOT be translated; just translate the format of ##x##x## mm.", "%(width).1f x %(depth).1f x %(height).1f mm") % {'width' : self._scene_bounding_box.width.item(), 'depth': self._scene_bounding_box.depth.item(), 'height' : self._scene_bounding_box.height.item()} + def updatePlatformActivityDelayed(self, node = None): + if node is not None and node.getMeshData() is not None: + self._update_platform_activity_timer.start() + ## Update scene bounding box for current build plate def updatePlatformActivity(self, node = None): count = 0 diff --git a/cura/Machines/Models/MultiBuildPlateModel.py b/cura/Machines/Models/MultiBuildPlateModel.py index f0f4997014..958e93837a 100644 --- a/cura/Machines/Models/MultiBuildPlateModel.py +++ b/cura/Machines/Models/MultiBuildPlateModel.py @@ -1,7 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import pyqtSignal, pyqtProperty +from PyQt5.QtCore import QTimer, pyqtSignal, pyqtProperty from UM.Application import Application from UM.Scene.Selection import Selection @@ -21,8 +21,13 @@ class MultiBuildPlateModel(ListModel): def __init__(self, parent = None): super().__init__(parent) + self._update_timer = QTimer() + self._update_timer.setInterval(100) + self._update_timer.setSingleShot(True) + self._update_timer.timeout.connect(self._updateSelectedObjectBuildPlateNumbers) + self._application = Application.getInstance() - self._application.getController().getScene().sceneChanged.connect(self._updateSelectedObjectBuildPlateNumbers) + self._application.getController().getScene().sceneChanged.connect(self._updateSelectedObjectBuildPlateNumbersDelayed) Selection.selectionChanged.connect(self._updateSelectedObjectBuildPlateNumbers) self._max_build_plate = 1 # default @@ -45,6 +50,9 @@ class MultiBuildPlateModel(ListModel): def activeBuildPlate(self): return self._active_build_plate + def _updateSelectedObjectBuildPlateNumbersDelayed(self, *args): + self._update_timer.start() + def _updateSelectedObjectBuildPlateNumbers(self, *args): result = set() for node in Selection.getAllSelectedObjects(): diff --git a/cura/ObjectsModel.py b/cura/ObjectsModel.py index f02e8b4db5..cfe4320e28 100644 --- a/cura/ObjectsModel.py +++ b/cura/ObjectsModel.py @@ -1,3 +1,8 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from PyQt5.QtCore import QTimer + from UM.Application import Application from UM.Qt.ListModel import ListModel from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator @@ -14,8 +19,13 @@ class ObjectsModel(ListModel): def __init__(self): super().__init__() - Application.getInstance().getController().getScene().sceneChanged.connect(self._update) - Preferences.getInstance().preferenceChanged.connect(self._update) + Application.getInstance().getController().getScene().sceneChanged.connect(self._updateDelayed) + Preferences.getInstance().preferenceChanged.connect(self._updateDelayed) + + self._update_timer = QTimer() + self._update_timer.setInterval(100) + self._update_timer.setSingleShot(True) + self._update_timer.timeout.connect(self._update) self._build_plate_number = -1 @@ -23,6 +33,9 @@ class ObjectsModel(ListModel): self._build_plate_number = nr self._update() + def _updateDelayed(self, *args): + self._update_timer.start() + def _update(self, *args): nodes = [] filter_current_build_plate = Preferences.getInstance().getValue("view/filter_current_build_plate") diff --git a/cura/Scene/ConvexHullDecorator.py b/cura/Scene/ConvexHullDecorator.py index 3a563c2764..66bc8a7fc3 100644 --- a/cura/Scene/ConvexHullDecorator.py +++ b/cura/Scene/ConvexHullDecorator.py @@ -1,6 +1,8 @@ # Copyright (c) 2016 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from PyQt5.QtCore import QTimer + from UM.Application import Application from UM.Math.Polygon import Polygon from UM.Scene.SceneNodeDecorator import SceneNodeDecorator @@ -22,6 +24,10 @@ class ConvexHullDecorator(SceneNodeDecorator): self._global_stack = None + # Make sure the timer is created on the main thread + self._recompute_convex_hull_timer = None + Application.getInstance().callLater(self.createRecomputeConvexHullTimer) + self._raft_thickness = 0.0 # For raft thickness, DRY self._build_volume = Application.getInstance().getBuildVolume() @@ -33,6 +39,12 @@ class ConvexHullDecorator(SceneNodeDecorator): self._onGlobalStackChanged() + def createRecomputeConvexHullTimer(self): + self._recompute_convex_hull_timer = QTimer() + self._recompute_convex_hull_timer.setInterval(200) + self._recompute_convex_hull_timer.setSingleShot(True) + self._recompute_convex_hull_timer.timeout.connect(self.recomputeConvexHull) + def setNode(self, node): previous_node = self._node # Disconnect from previous node signals @@ -99,6 +111,12 @@ class ConvexHullDecorator(SceneNodeDecorator): return self._compute2DConvexHull() return None + def recomputeConvexHullDelayed(self): + if self._recompute_convex_hull_timer is not None: + self._recompute_convex_hull_timer.start() + else: + self.recomputeConvexHull() + def recomputeConvexHull(self): controller = Application.getInstance().getController() root = controller.getScene().getRoot() @@ -279,7 +297,8 @@ class ConvexHullDecorator(SceneNodeDecorator): def _onChanged(self, *args): self._raft_thickness = self._build_volume.getRaftThickness() - self.recomputeConvexHull() + if not args or args[0] == self._node: + self.recomputeConvexHullDelayed() def _onGlobalStackChanged(self): if self._global_stack: diff --git a/plugins/SimulationView/SimulationView.py b/plugins/SimulationView/SimulationView.py index 35ce9cc37a..5c3dca9fae 100644 --- a/plugins/SimulationView/SimulationView.py +++ b/plugins/SimulationView/SimulationView.py @@ -158,9 +158,10 @@ class SimulationView(View): return self._nozzle_node def _onSceneChanged(self, node): - self.setActivity(False) - self.calculateMaxLayers() - self.calculateMaxPathsOnLayer(self._current_layer_num) + if node.getMeshData() is not None: + self.setActivity(False) + self.calculateMaxLayers() + self.calculateMaxPathsOnLayer(self._current_layer_num) def isBusy(self): return self._busy From 6909c9263a050010f1f69a5b66930ad50641ce4c Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Wed, 14 Mar 2018 16:32:53 +0100 Subject: [PATCH 54/75] CURA-4400 fix setting extruder number on a group printed with mixed extruders --- cura/CuraActions.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cura/CuraActions.py b/cura/CuraActions.py index f517ec4217..75338f17b6 100644 --- a/cura/CuraActions.py +++ b/cura/CuraActions.py @@ -109,10 +109,6 @@ class CuraActions(QObject): nodes_to_change = [] for node in Selection.getAllSelectedObjects(): - # Do not change any nodes that already have the right extruder set. - if node.callDecoration("getActiveExtruder") == extruder_id: - continue - # If the node is a group, apply the active extruder to all children of the group. if node.callDecoration("isGroup"): for grouped_node in BreadthFirstIterator(node): @@ -125,6 +121,10 @@ class CuraActions(QObject): nodes_to_change.append(grouped_node) continue + # Do not change any nodes that already have the right extruder set. + if node.callDecoration("getActiveExtruder") == extruder_id: + continue + nodes_to_change.append(node) if not nodes_to_change: From 2fdd51fc231cadb2c1ee8d6eacfccf2b1fd7c105 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 14 Mar 2018 16:47:01 +0100 Subject: [PATCH 55/75] CURA-4870 Bind the network information with the output devices changed signal. --- 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 3af6f70e5f..c79d352dcb 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -467,13 +467,13 @@ class MachineManager(QObject): return self._global_container_stack.getId() return "" - @pyqtProperty(str, notify = globalContainerChanged) + @pyqtProperty(str, notify = outputDevicesChanged) def activeMachineNetworkKey(self) -> str: if self._global_container_stack: return self._global_container_stack.getMetaDataEntry("um_network_key") return "" - @pyqtProperty(str, notify = globalContainerChanged) + @pyqtProperty(str, notify = outputDevicesChanged) def activeMachineNetworkGroupName(self) -> str: if self._global_container_stack: return self._global_container_stack.getMetaDataEntry("connect_group_name") From e6c5bd28b2de4b4759cbd3e643882b927675a2e5 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 14 Mar 2018 16:53:51 +0100 Subject: [PATCH 56/75] CURA-4870 Use the visible property instead of a Qt5.10 existing 'opened' property. This is needed to avoid a crashing for those that are using 5.10 running from source. --- .../ConfigurationMenu/ConfigurationSelection.qml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationSelection.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationSelection.qml index a3cf10168b..d7ee2c68ee 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationSelection.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationSelection.qml @@ -16,7 +16,7 @@ Item function switchPopupState() { - popup.opened ? popup.close() : popup.open() + popup.visible ? popup.close() : popup.open() } SyncButton @@ -28,15 +28,14 @@ Item Popup { - // TODO Change once updating to Qt5.10 - This property is already in 5.10 but is manually implemented until upgrade - property bool opened: false + // TODO Change once updating to Qt5.10 - The 'opened' property is in 5.10 but the behavior is now implemented with the visible property id: popup clip: true closePolicy: Popup.CloseOnPressOutsideParent y: configurationSelector.height - UM.Theme.getSize("default_lining").height x: configurationSelector.width - width width: panelWidth - visible: opened + visible: false padding: UM.Theme.getSize("default_lining").width transformOrigin: Popup.Top contentItem: ConfigurationListView @@ -60,7 +59,7 @@ Item // This applies a default NumberAnimation to any changes a state change makes to x or y properties NumberAnimation { property: "visible"; duration: 75; } } - onClosed: opened = false - onOpened: opened = true + onClosed: visible = false + onOpened: visible = true } } \ No newline at end of file From 1cf832653ce5012de855bc2e3d5833c968ecf7a6 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 14 Mar 2018 17:00:39 +0100 Subject: [PATCH 57/75] CURA-4870 Make the configuration selector only available for network printers. Show the connection icon also for printers connected via USB or Octoprint. --- resources/qml/MachineSelection.qml | 37 +++++++++++++++--------------- resources/qml/Sidebar.qml | 8 ++++--- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/resources/qml/MachineSelection.qml b/resources/qml/MachineSelection.qml index b3f9629703..357e7870a7 100644 --- a/resources/qml/MachineSelection.qml +++ b/resources/qml/MachineSelection.qml @@ -10,17 +10,22 @@ import UM 1.2 as UM import Cura 1.0 as Cura import "Menus" -ToolButton { +ToolButton +{ id: base property bool isNetworkPrinter: Cura.MachineManager.activeMachineNetworkKey != "" + property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 property var printerStatus: Cura.MachineManager.printerOutputDevices.length != 0 ? "connected" : "disconnected" text: isNetworkPrinter ? Cura.MachineManager.activeMachineNetworkGroupName : Cura.MachineManager.activeMachineName tooltip: Cura.MachineManager.activeMachineName - style: ButtonStyle { - background: Rectangle { - color: { + style: ButtonStyle + { + background: Rectangle + { + color: + { if (control.pressed) { return UM.Theme.getColor("sidebar_header_active"); } @@ -33,7 +38,8 @@ ToolButton { } Behavior on color { ColorAnimation { duration: 50; } } - UM.RecolorImage { + UM.RecolorImage + { id: downArrow anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right @@ -46,18 +52,21 @@ ToolButton { source: UM.Theme.getIcon("arrow_bottom") } - PrinterStatusIcon { + PrinterStatusIcon + { id: printerStatusIcon - visible: isNetworkPrinter + visible: printerConnected status: printerStatus - anchors { + anchors + { verticalCenter: parent.verticalCenter left: parent.left leftMargin: UM.Theme.getSize("sidebar_margin").width } } - Label { + Label + { id: sidebarComboBoxLabel color: UM.Theme.getColor("sidebar_header_text_active") text: control.text; @@ -74,14 +83,4 @@ ToolButton { } menu: PrinterMenu { } - - // Make the toolbutton react when the outputdevice changes - Connections - { - target: Cura.MachineManager - onOutputDevicesChanged: - { - base.isNetworkPrinter = Cura.MachineManager.activeMachineNetworkKey != "" - } - } } diff --git a/resources/qml/Sidebar.qml b/resources/qml/Sidebar.qml index 5211ee5a1d..4744bbfda0 100644 --- a/resources/qml/Sidebar.qml +++ b/resources/qml/Sidebar.qml @@ -87,7 +87,8 @@ Rectangle } } - MachineSelection { + MachineSelection + { id: machineSelection width: base.width - configSelection.width - separator.width height: UM.Theme.getSize("sidebar_header").height @@ -105,9 +106,10 @@ Rectangle anchors.left: machineSelection.right } - ConfigurationSelection { + ConfigurationSelection + { id: configSelection - visible: isNetworkPrinter && !sidebar.monitoringPrint && !sidebar.hideSettings + visible: isNetworkPrinter width: visible ? Math.round(base.width * 0.15) : 0 height: UM.Theme.getSize("sidebar_header").height anchors.top: base.top From a477b8cdc245b6f0f6572997a7c126031d100bdd Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 15 Mar 2018 09:57:46 +0100 Subject: [PATCH 58/75] Fix malyan m180 definitions CURA-4696 --- resources/definitions/malyan_m180.def.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/definitions/malyan_m180.def.json b/resources/definitions/malyan_m180.def.json index c1a424f0c9..11b61328ed 100644 --- a/resources/definitions/malyan_m180.def.json +++ b/resources/definitions/malyan_m180.def.json @@ -38,16 +38,16 @@ "machine_max_feedrate_z": { "default_value": 400 }, - "steps_per_mm_x": { + "machine_steps_per_mm_x": { "default_value": 93 }, - "steps_per_mm_y": { + "machine_steps_per_mm_y": { "default_value": 93 }, - "steps_per_mm_z": { + "machine_steps_per_mm_z": { "default_value": 1600 }, - "steps_per_mm_e": { + "machine_steps_per_mm_e": { "default_value": 92 }, "gantry_height": { From 0155a20994daedf9acf12080ba82a0c879e2ca23 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 15 Mar 2018 10:33:06 +0100 Subject: [PATCH 59/75] CURA-4870 Fix visibility in the status icon --- resources/qml/MachineSelection.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/MachineSelection.qml b/resources/qml/MachineSelection.qml index 357e7870a7..b959e20bb7 100644 --- a/resources/qml/MachineSelection.qml +++ b/resources/qml/MachineSelection.qml @@ -55,7 +55,7 @@ ToolButton PrinterStatusIcon { id: printerStatusIcon - visible: printerConnected + visible: printerConnected || isNetworkPrinter status: printerStatus anchors { From 496c8f2f7911af896de05728711f2f9ae25a1e92 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 15 Mar 2018 10:43:05 +0100 Subject: [PATCH 60/75] Cleanup MachineManagementModel --- .../Machines/Models/MachineManagementModel.py | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/cura/Machines/Models/MachineManagementModel.py b/cura/Machines/Models/MachineManagementModel.py index 481a692675..7dc51f07f7 100644 --- a/cura/Machines/Models/MachineManagementModel.py +++ b/cura/Machines/Models/MachineManagementModel.py @@ -3,7 +3,7 @@ from UM.Qt.ListModel import ListModel -from PyQt5.QtCore import pyqtSlot, pyqtProperty, Qt, pyqtSignal +from PyQt5.QtCore import Qt from UM.Settings.ContainerRegistry import ContainerRegistry from UM.Settings.ContainerStack import ContainerStack @@ -11,6 +11,7 @@ from UM.Settings.ContainerStack import ContainerStack from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") + # # This the QML model for the quality management page. # @@ -39,7 +40,7 @@ class MachineManagementModel(ListModel): ## Handler for container added/removed events from registry def _onContainerChanged(self, container): # We only need to update when the added / removed container is a stack. - if isinstance(container, ContainerStack): + if isinstance(container, ContainerStack) and container.getMetaDataEntry("type") == "machine": self._update() ## Private convenience function to reset & repopulate the model. @@ -47,7 +48,9 @@ class MachineManagementModel(ListModel): items = [] # Get first the network enabled printers - network_filter_printers = {"type": "machine", "um_network_key": "*", "hidden": "False"} + network_filter_printers = {"type": "machine", + "um_network_key": "*", + "hidden": "False"} self._network_container_stacks = ContainerRegistry.getInstance().findContainerStacks(**network_filter_printers) self._network_container_stacks.sort(key = lambda i: i.getMetaDataEntry("connect_group_name")) @@ -57,11 +60,11 @@ class MachineManagementModel(ListModel): metadata["definition_name"] = container.getBottom().getName() items.append({"name": metadata["connect_group_name"], - "id": container.getId(), - "metadata": metadata, - "group": catalog.i18nc("@info:title", "Network enabled printers")}) + "id": container.getId(), + "metadata": metadata, + "group": catalog.i18nc("@info:title", "Network enabled printers")}) - # Get now the local printes + # Get now the local printers local_filter_printers = {"type": "machine", "um_network_key": None} self._local_container_stacks = ContainerRegistry.getInstance().findContainerStacks(**local_filter_printers) self._local_container_stacks.sort(key = lambda i: i.getName()) @@ -72,8 +75,8 @@ class MachineManagementModel(ListModel): metadata["definition_name"] = container.getBottom().getName() items.append({"name": container.getName(), - "id": container.getId(), - "metadata": metadata, - "group": catalog.i18nc("@info:title", "Local printers")}) + "id": container.getId(), + "metadata": metadata, + "group": catalog.i18nc("@info:title", "Local printers")}) self.setItems(items) From 15ff4045bf6957cd0b38b55dc7a09000165c7613 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 15 Mar 2018 11:02:29 +0100 Subject: [PATCH 61/75] CURA-4870 When removing a network connected printer, also remove all the other machines that were (possibly) created in the background so that there is no orphan containers. --- cura/Settings/MachineManager.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index c79d352dcb..100c7c3c31 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -470,13 +470,13 @@ class MachineManager(QObject): @pyqtProperty(str, notify = outputDevicesChanged) def activeMachineNetworkKey(self) -> str: if self._global_container_stack: - return self._global_container_stack.getMetaDataEntry("um_network_key") + return self._global_container_stack.getMetaDataEntry("um_network_key", "") return "" @pyqtProperty(str, notify = outputDevicesChanged) def activeMachineNetworkGroupName(self) -> str: if self._global_container_stack: - return self._global_container_stack.getMetaDataEntry("connect_group_name") + return self._global_container_stack.getMetaDataEntry("connect_group_name", "") return "" @pyqtProperty(QObject, notify = globalContainerChanged) @@ -662,12 +662,22 @@ class MachineManager(QObject): if other_machine_stacks: self.setActiveMachine(other_machine_stacks[0]["id"]) + metadata = ContainerRegistry.getInstance().findContainerStacksMetadata(id = machine_id)[0] + network_key = metadata["um_network_key"] if "um_network_key" in metadata else None ExtruderManager.getInstance().removeMachineExtruders(machine_id) containers = ContainerRegistry.getInstance().findInstanceContainersMetadata(type = "user", machine = machine_id) for container in containers: ContainerRegistry.getInstance().removeContainer(container["id"]) ContainerRegistry.getInstance().removeContainer(machine_id) + # If the printer that is being removed is a network printer, the hidden printers have to be also removed + if network_key: + metadata_filter = {"um_network_key": network_key} + hidden_containers = ContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter) + if hidden_containers: + # This reuses the method and remove all printers recursively + self.removeMachine(hidden_containers[0].getId()) + @pyqtProperty(bool, notify = globalContainerChanged) def hasMaterials(self) -> bool: if self._global_container_stack: From 097c97b6f8640df7151fa47c5ca5e0e591e803a8 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Thu, 15 Mar 2018 14:33:27 +0100 Subject: [PATCH 62/75] Fix: Refresh list of available printers in network after clicking refresh button. --- plugins/UM3NetworkPrinting/UM3OutputDevicePlugin.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/UM3NetworkPrinting/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/UM3OutputDevicePlugin.py index 5ff5eb9e3e..089b9038f7 100644 --- a/plugins/UM3NetworkPrinting/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/UM3OutputDevicePlugin.py @@ -82,6 +82,9 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self._zero_conf_browser.cancel() self._zero_conf_browser = None # Force the old ServiceBrowser to be destroyed. + for instance_name in list(self._discovered_devices): + self._onRemoveDevice(instance_name) + self._zero_conf = Zeroconf() self._zero_conf_browser = ServiceBrowser(self._zero_conf, u'_ultimaker._tcp.local.', [self._appendServiceChangedRequest]) From 1f2602a2f4c32447b421d7de4e43b59544639361 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 15 Mar 2018 10:41:50 +0100 Subject: [PATCH 63/75] Move SettingVisibilityPresetsModel to Machines.Models CURA-5088 --- cura/CuraApplication.py | 6 +----- .../Models}/SettingVisibilityPresetsModel.py | 0 2 files changed, 1 insertion(+), 5 deletions(-) rename cura/{Settings => Machines/Models}/SettingVisibilityPresetsModel.py (100%) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index b7731c5c8c..243bb2eb8a 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1,9 +1,6 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -#Type hinting. -from typing import Dict - from PyQt5.QtCore import QObject, QTimer from PyQt5.QtNetwork import QLocalServer from PyQt5.QtNetwork import QLocalSocket @@ -91,7 +88,7 @@ from cura.Settings.UserChangesModel import UserChangesModel from cura.Settings.ExtrudersModel import ExtrudersModel from cura.Settings.MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler from cura.Settings.ContainerManager import ContainerManager -from cura.Settings.SettingVisibilityPresetsModel import SettingVisibilityPresetsModel +from cura.Machines.Models.SettingVisibilityPresetsModel import SettingVisibilityPresetsModel from cura.ObjectsModel import ObjectsModel @@ -101,7 +98,6 @@ from PyQt5.QtGui import QColor, QIcon from PyQt5.QtWidgets import QMessageBox from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType -from configparser import ConfigParser import sys import os.path import numpy diff --git a/cura/Settings/SettingVisibilityPresetsModel.py b/cura/Machines/Models/SettingVisibilityPresetsModel.py similarity index 100% rename from cura/Settings/SettingVisibilityPresetsModel.py rename to cura/Machines/Models/SettingVisibilityPresetsModel.py From 83175b00c282a6f63943522ff70e7447775b74f1 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 15 Mar 2018 10:59:23 +0100 Subject: [PATCH 64/75] Change SettingVisibilityPresetsModel to non-singleton CURA-5088 --- cura/CuraApplication.py | 19 +++++++++++++------ .../Models/SettingVisibilityPresetsModel.py | 16 ---------------- .../Menus/SettingVisibilityPresetsMenu.qml | 11 ++++++----- .../qml/Preferences/SettingVisibilityPage.qml | 17 +++++++++-------- resources/qml/Settings/SettingView.qml | 11 ++++++----- 5 files changed, 34 insertions(+), 40 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 243bb2eb8a..1b5de89c2b 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -65,6 +65,8 @@ from cura.Machines.Models.QualityManagementModel import QualityManagementModel from cura.Machines.Models.QualitySettingsModel import QualitySettingsModel from cura.Machines.Models.MachineManagementModel import MachineManagementModel +from cura.Machines.Models.SettingVisibilityPresetsModel import SettingVisibilityPresetsModel + from cura.Machines.MachineErrorChecker import MachineErrorChecker from cura.Settings.SettingInheritanceManager import SettingInheritanceManager @@ -88,7 +90,6 @@ from cura.Settings.UserChangesModel import UserChangesModel from cura.Settings.ExtrudersModel import ExtrudersModel from cura.Settings.MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler from cura.Settings.ContainerManager import ContainerManager -from cura.Machines.Models.SettingVisibilityPresetsModel import SettingVisibilityPresetsModel from cura.ObjectsModel import ObjectsModel @@ -222,6 +223,7 @@ class CuraApplication(QtApplication): self._object_manager = None self._build_plate_model = None self._multi_build_plate_model = None + self._setting_visibility_presets_model = None self._setting_inheritance_manager = None self._simple_mode_settings_manager = None self._cura_scene_controller = None @@ -377,10 +379,6 @@ class CuraApplication(QtApplication): preferences.setDefault("local_file/last_used_type", "text/x-gcode") - default_visibility_profile = SettingVisibilityPresetsModel.getInstance().getItem(0) - - preferences.setDefault("general/visible_settings", ";".join(default_visibility_profile["settings"])) - self.applicationShuttingDown.connect(self.saveSettings) self.engineCreatedSignal.connect(self._onEngineCreated) @@ -683,6 +681,11 @@ class CuraApplication(QtApplication): self._print_information = PrintInformation.PrintInformation() self._cura_actions = CuraActions.CuraActions(self) + # Initialize setting visibility presets model + self._setting_visibility_presets_model = SettingVisibilityPresetsModel(self) + default_visibility_profile = self._setting_visibility_presets_model.getItem(0) + Preferences.getInstance().setDefault("general/visible_settings", ";".join(default_visibility_profile["settings"])) + # Detect in which mode to run and execute that mode if self.getCommandLineOption("headless", False): self.runWithoutGUI() @@ -765,6 +768,10 @@ class CuraApplication(QtApplication): def hasGui(self): return self._use_gui + @pyqtSlot(result = QObject) + def getSettingVisibilityPresetsModel(self, *args) -> SettingVisibilityPresetsModel: + return self._setting_visibility_presets_model + def getMachineErrorChecker(self, *args) -> MachineErrorChecker: return self._machine_error_checker @@ -891,11 +898,11 @@ class CuraApplication(QtApplication): qmlRegisterType(NozzleModel, "Cura", 1, 0, "NozzleModel") qmlRegisterType(MaterialSettingsVisibilityHandler, "Cura", 1, 0, "MaterialSettingsVisibilityHandler") + qmlRegisterType(SettingVisibilityPresetsModel, "Cura", 1, 0, "SettingVisibilityPresetsModel") qmlRegisterType(QualitySettingsModel, "Cura", 1, 0, "QualitySettingsModel") qmlRegisterType(MachineNameValidator, "Cura", 1, 0, "MachineNameValidator") qmlRegisterType(UserChangesModel, "Cura", 1, 0, "UserChangesModel") qmlRegisterSingletonType(ContainerManager, "Cura", 1, 0, "ContainerManager", ContainerManager.createContainerManager) - qmlRegisterSingletonType(SettingVisibilityPresetsModel, "Cura", 1, 0, "SettingVisibilityPresetsModel", SettingVisibilityPresetsModel.createSettingVisibilityPresetsModel) # As of Qt5.7, it is necessary to get rid of any ".." in the path for the singleton to work. actions_url = QUrl.fromLocalFile(os.path.abspath(Resources.getPath(CuraApplication.ResourceTypes.QmlFiles, "Actions.qml"))) diff --git a/cura/Machines/Models/SettingVisibilityPresetsModel.py b/cura/Machines/Models/SettingVisibilityPresetsModel.py index e5a2e24412..ace78ae093 100644 --- a/cura/Machines/Models/SettingVisibilityPresetsModel.py +++ b/cura/Machines/Models/SettingVisibilityPresetsModel.py @@ -118,19 +118,3 @@ class SettingVisibilityPresetsModel(ListModel): # Copy current visibility set to custom visibility set preference so it can be restored later visibility_string = self._preferences.getValue("general/visible_settings") self._preferences.setValue("cura/custom_visible_settings", visibility_string) - - - # Factory function, used by QML - @staticmethod - def createSettingVisibilityPresetsModel(engine, js_engine): - return SettingVisibilityPresetsModel.getInstance() - - ## Get the singleton instance for this class. - @classmethod - def getInstance(cls) -> "SettingVisibilityPresetsModel": - # Note: Explicit use of class name to prevent issues with inheritance. - if not SettingVisibilityPresetsModel.__instance: - SettingVisibilityPresetsModel.__instance = cls() - return SettingVisibilityPresetsModel.__instance - - __instance = None # type: "SettingVisibilityPresetsModel" \ No newline at end of file diff --git a/resources/qml/Menus/SettingVisibilityPresetsMenu.qml b/resources/qml/Menus/SettingVisibilityPresetsMenu.qml index 19c36e6118..d39d65e96c 100644 --- a/resources/qml/Menus/SettingVisibilityPresetsMenu.qml +++ b/resources/qml/Menus/SettingVisibilityPresetsMenu.qml @@ -12,6 +12,7 @@ Menu id: menu title: catalog.i18nc("@action:inmenu", "Visible Settings") + property QtObject settingVisibilityPresetsModel: CuraApplication.getSettingVisibilityPresetsModel() property bool showingSearchResults property bool showingAllSettings @@ -22,11 +23,11 @@ Menu { text: catalog.i18nc("@action:inmenu", "Custom selection") checkable: true - checked: !showingSearchResults && !showingAllSettings && Cura.SettingVisibilityPresetsModel.activePreset == "custom" + checked: !showingSearchResults && !showingAllSettings && settingVisibilityPresetsModel.activePreset == "custom" exclusiveGroup: group onTriggered: { - Cura.SettingVisibilityPresetsModel.setActivePreset("custom"); + settingVisibilityPresetsModel.setActivePreset("custom"); // Restore custom set from preference UM.Preferences.setValue("general/visible_settings", UM.Preferences.getValue("cura/custom_visible_settings")); showSettingVisibilityProfile(); @@ -36,17 +37,17 @@ Menu Instantiator { - model: Cura.SettingVisibilityPresetsModel + model: settingVisibilityPresetsModel MenuItem { text: model.name checkable: true - checked: model.id == Cura.SettingVisibilityPresetsModel.activePreset + checked: model.id == settingVisibilityPresetsModel.activePreset exclusiveGroup: group onTriggered: { - Cura.SettingVisibilityPresetsModel.setActivePreset(model.id); + settingVisibilityPresetsModel.setActivePreset(model.id); UM.Preferences.setValue("general/visible_settings", model.settings.join(";")); diff --git a/resources/qml/Preferences/SettingVisibilityPage.qml b/resources/qml/Preferences/SettingVisibilityPage.qml index f0c24e2cbe..7f6a58367d 100644 --- a/resources/qml/Preferences/SettingVisibilityPage.qml +++ b/resources/qml/Preferences/SettingVisibilityPage.qml @@ -13,6 +13,8 @@ UM.PreferencesPage { title: catalog.i18nc("@title:tab", "Setting Visibility"); + property QtObject settingVisibilityPresetsModel: CuraApplication.getSettingVisibilityPresetsModel() + property int scrollToIndex: 0 signal scrollToSection( string key ) @@ -132,10 +134,9 @@ UM.PreferencesPage { visibilityPresetsModel.append({text: catalog.i18nc("@action:inmenu", "Custom selection"), id: "custom"}); - var presets = Cura.SettingVisibilityPresetsModel; - for(var i = 0; i < presets.rowCount(); i++) + for(var i = 0; i < settingVisibilityPresetsModel.rowCount(); i++) { - visibilityPresetsModel.append({text: presets.getItem(i)["name"], id: presets.getItem(i)["id"]}); + visibilityPresetsModel.append({text: settingVisibilityPresetsModel.getItem(i)["name"], id: settingVisibilityPresetsModel.getItem(i)["id"]}); } } } @@ -143,7 +144,7 @@ UM.PreferencesPage currentIndex: { // Load previously selected preset. - var index = Cura.SettingVisibilityPresetsModel.find("id", Cura.SettingVisibilityPresetsModel.activePreset); + var index = settingVisibilityPresetsModel.find("id", settingVisibilityPresetsModel.activePreset); if(index == -1) { return 0; @@ -156,12 +157,12 @@ UM.PreferencesPage { base.inhibitSwitchToCustom = true; var preset_id = visibilityPresetsModel.get(index).id; - Cura.SettingVisibilityPresetsModel.setActivePreset(preset_id); + settingVisibilityPresetsModel.setActivePreset(preset_id); UM.Preferences.setValue("cura/active_setting_visibility_preset", preset_id); if (preset_id != "custom") { - UM.Preferences.setValue("general/visible_settings", Cura.SettingVisibilityPresetsModel.getItem(index - 1).settings.join(";")); + UM.Preferences.setValue("general/visible_settings", settingVisibilityPresetsModel.getItem(index - 1).settings.join(";")); // "Custom selection" entry is added in front, so index is off by 1 } else @@ -203,9 +204,9 @@ UM.PreferencesPage { onVisibilityChanged: { - if(Cura.SettingVisibilityPresetsModel.activePreset != "" && !base.inhibitSwitchToCustom) + if(settingVisibilityPresetsModel.activePreset != "" && !base.inhibitSwitchToCustom) { - Cura.SettingVisibilityPresetsModel.setActivePreset("custom"); + settingVisibilityPresetsModel.setActivePreset("custom"); } } } diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 235dfac91a..a6d7b3a71e 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -15,6 +15,7 @@ Item { id: base; + property QtObject settingVisibilityPresetsModel: CuraApplication.getSettingVisibilityPresetsModel() property Action configureSettings property bool findingSettings property bool showingAllSettings @@ -562,9 +563,9 @@ Item { definitionsModel.hide(contextMenu.key); // visible settings have changed, so we're no longer showing a preset - if (Cura.SettingVisibilityPresetsModel.activePreset != "" && !showingAllSettings) + if (settingVisibilityPresetsModel.activePreset != "" && !showingAllSettings) { - Cura.SettingVisibilityPresetsModel.setActivePreset("custom"); + settingVisibilityPresetsModel.setActivePreset("custom"); } } } @@ -594,16 +595,16 @@ Item definitionsModel.show(contextMenu.key); } // visible settings have changed, so we're no longer showing a preset - if (Cura.SettingVisibilityPresetsModel.activePreset != "" && !showingAllSettings) + if (settingVisibilityPresetsModel.activePreset != "" && !showingAllSettings) { - Cura.SettingVisibilityPresetsModel.setActivePreset("custom"); + settingVisibilityPresetsModel.setActivePreset("custom"); } } } MenuItem { //: Settings context menu action - text: catalog.i18nc("@action:menu", "Configure setting visiblity..."); + text: catalog.i18nc("@action:menu", "Configure setting visibility..."); onTriggered: Cura.Actions.configureSettingVisibility.trigger(contextMenu); } From 8e39849aadd34fcd93a37dffbdae837bd700ca21 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 15 Mar 2018 12:04:14 +0100 Subject: [PATCH 65/75] Refactor setting visibility preset CURA-5088 --- .../Models/SettingVisibilityPresetsModel.py | 140 ++++++++++++------ .../Menus/SettingVisibilityPresetsMenu.qml | 23 +-- .../qml/Preferences/SettingVisibilityPage.qml | 60 ++------ 3 files changed, 109 insertions(+), 114 deletions(-) diff --git a/cura/Machines/Models/SettingVisibilityPresetsModel.py b/cura/Machines/Models/SettingVisibilityPresetsModel.py index ace78ae093..e281d81c39 100644 --- a/cura/Machines/Models/SettingVisibilityPresetsModel.py +++ b/cura/Machines/Models/SettingVisibilityPresetsModel.py @@ -1,11 +1,12 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from typing import Optional import os -import urllib +import urllib.parse from configparser import ConfigParser -from PyQt5.QtCore import pyqtProperty, Qt, pyqtSignal, pyqtSlot, QUrl +from PyQt5.QtCore import pyqtProperty, Qt, pyqtSignal, pyqtSlot from UM.Logger import Logger from UM.Qt.ListModel import ListModel @@ -13,13 +14,14 @@ from UM.Preferences import Preferences from UM.Resources import Resources from UM.MimeTypeDatabase import MimeTypeDatabase, MimeTypeNotFoundError -import cura.CuraApplication +from UM.i18n import i18nCatalog +catalog = i18nCatalog("cura") class SettingVisibilityPresetsModel(ListModel): IdRole = Qt.UserRole + 1 NameRole = Qt.UserRole + 2 - SettingsRole = Qt.UserRole + 4 + SettingsRole = Qt.UserRole + 3 def __init__(self, parent = None): super().__init__(parent) @@ -28,39 +30,51 @@ class SettingVisibilityPresetsModel(ListModel): self.addRoleName(self.SettingsRole, "settings") self._populate() + basic_item = self.items[1] + basic_visibile_settings = ";".join(basic_item["settings"]) self._preferences = Preferences.getInstance() - self._preferences.addPreference("cura/active_setting_visibility_preset", "custom") # Preference to store which preset is currently selected - self._preferences.addPreference("cura/custom_visible_settings", "") # Preference that stores the "custom" set so it can always be restored (even after a restart) + # Preference to store which preset is currently selected + self._preferences.addPreference("cura/active_setting_visibility_preset", "basic") + # Preference that stores the "custom" set so it can always be restored (even after a restart) + self._preferences.addPreference("cura/custom_visible_settings", basic_visibile_settings) self._preferences.preferenceChanged.connect(self._onPreferencesChanged) - self._active_preset = self._preferences.getValue("cura/active_setting_visibility_preset") - if self.find("id", self._active_preset) < 0: - self._active_preset = "custom" + self._active_preset_item = self._getItem(self._preferences.getValue("cura/active_setting_visibility_preset")) + # Initialize visible settings if it is not done yet + visible_settings = self._preferences.getValue("general/visible_settings") + if not visible_settings: + self._preferences.setValue("general/visible_settings", ";".join(self._active_preset_item["settings"])) self.activePresetChanged.emit() + def _getItem(self, item_id: str) -> Optional[dict]: + result = None + for item in self.items: + if item["id"] == item_id: + result = item + break + return result def _populate(self): + from cura.CuraApplication import CuraApplication items = [] - for item in Resources.getAllResourcesOfType(cura.CuraApplication.CuraApplication.ResourceTypes.SettingVisibilityPreset): + for file_path in Resources.getAllResourcesOfType(CuraApplication.ResourceTypes.SettingVisibilityPreset): try: - mime_type = MimeTypeDatabase.getMimeTypeForFile(item) + mime_type = MimeTypeDatabase.getMimeTypeForFile(file_path) except MimeTypeNotFoundError: - Logger.log("e", "Could not determine mime type of file %s", item) + Logger.log("e", "Could not determine mime type of file %s", file_path) continue - id = urllib.parse.unquote_plus(mime_type.stripExtension(os.path.basename(item))) - - if not os.path.isfile(item): + item_id = urllib.parse.unquote_plus(mime_type.stripExtension(os.path.basename(file_path))) + if not os.path.isfile(file_path): + Logger.log("e", "[%s] is not a file", file_path) continue - parser = ConfigParser(allow_no_value=True) # accept options without any value, - + parser = ConfigParser(allow_no_value = True) # accept options without any value, try: - parser.read([item]) - - if not parser.has_option("general", "name") and not parser.has_option("general", "weight"): + parser.read([file_path]) + if not parser.has_option("general", "name") or not parser.has_option("general", "weight"): continue settings = [] @@ -73,48 +87,90 @@ class SettingVisibilityPresetsModel(ListModel): settings.append(option) items.append({ - "id": id, - "name": parser["general"]["name"], + "id": item_id, + "name": catalog.i18nc("@action:inmenu", parser["general"]["name"]), "weight": parser["general"]["weight"], - "settings": settings + "settings": settings, }) - except Exception as e: - Logger.log("e", "Failed to load setting preset %s: %s", file_path, str(e)) + except Exception: + Logger.logException("e", "Failed to load setting preset %s", file_path) + items.sort(key = lambda k: (int(k["weight"]), k["id"])) + # Put "custom" at the top + items.insert(0, {"id": "custom", + "name": "Custom selection", + "weight": -100, + "settings": []}) - items.sort(key = lambda k: (k["weight"], k["id"])) self.setItems(items) @pyqtSlot(str) - def setActivePreset(self, preset_id): - if preset_id != "custom" and self.find("id", preset_id) == -1: - Logger.log("w", "Tried to set active preset to unknown id %s", preset_id) + def setActivePreset(self, preset_id: str): + if preset_id == self._active_preset_item["id"]: + Logger.log("d", "Same setting visibility preset [%s] selected, do nothing.", preset_id) return - if preset_id == "custom" and self._active_preset == "custom": - # Copy current visibility set to custom visibility set preference so it can be restored later - visibility_string = self._preferences.getValue("general/visible_settings") - self._preferences.setValue("cura/custom_visible_settings", visibility_string) + preset_item = None + for item in self.items: + if item["id"] == preset_id: + preset_item = item + break + if preset_item is None: + Logger.log("w", "Tried to set active preset to unknown id [%s]", preset_id) + return + + need_to_save_to_custom = self._active_preset_item["id"] == "custom" and preset_id != "custom" + if need_to_save_to_custom: + # Save the current visibility settings to custom + current_visibility_string = self._preferences.getValue("general/visible_settings") + if current_visibility_string: + self._preferences.setValue("cura/custom_visible_settings", current_visibility_string) + + new_visibility_string = ";".join(preset_item["settings"]) + if preset_id == "custom": + # Get settings from the stored custom data + new_visibility_string = self._preferences.getValue("cura/custom_visible_settings") + if new_visibility_string is None: + new_visibility_string = self._preferences.getValue("general/visible_settings") + self._preferences.setValue("general/visible_settings", new_visibility_string) self._preferences.setValue("cura/active_setting_visibility_preset", preset_id) - - self._active_preset = preset_id + self._active_preset_item = preset_item self.activePresetChanged.emit() activePresetChanged = pyqtSignal() @pyqtProperty(str, notify = activePresetChanged) - def activePreset(self): - return self._active_preset + def activePreset(self) -> str: + return self._active_preset_item["id"] - def _onPreferencesChanged(self, name): + def _onPreferencesChanged(self, name: str): if name != "general/visible_settings": return - if self._active_preset != "custom": + # Find the preset that matches with the current visible settings setup + visibility_string = self._preferences.getValue("general/visible_settings") + if not visibility_string: return - # Copy current visibility set to custom visibility set preference so it can be restored later - visibility_string = self._preferences.getValue("general/visible_settings") - self._preferences.setValue("cura/custom_visible_settings", visibility_string) + visibility_set = set(visibility_string.split(";")) + matching_preset_item = None + for item in self.items: + if item["id"] == "custom": + continue + if set(item["settings"]) == visibility_set: + matching_preset_item = item + break + + if matching_preset_item is None: + # The new visibility setup is "custom" should be custom + if self._active_preset_item["id"] == "custom": + # We are already in custom, just save the settings + self._preferences.setValue("cura/custom_visible_settings", visibility_string) + else: + self._active_preset_item = self.items[0] # 0 is custom + self.activePresetChanged.emit() + else: + self._active_preset_item = matching_preset_item + self.activePresetChanged.emit() diff --git a/resources/qml/Menus/SettingVisibilityPresetsMenu.qml b/resources/qml/Menus/SettingVisibilityPresetsMenu.qml index d39d65e96c..0753c83b17 100644 --- a/resources/qml/Menus/SettingVisibilityPresetsMenu.qml +++ b/resources/qml/Menus/SettingVisibilityPresetsMenu.qml @@ -1,8 +1,8 @@ // Copyright (c) 2018 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.2 -import QtQuick.Controls 1.1 +import QtQuick 2.7 +import QtQuick.Controls 1.4 import UM 1.2 as UM import Cura 1.0 as Cura @@ -19,22 +19,6 @@ Menu signal showAllSettings() signal showSettingVisibilityProfile() - MenuItem - { - text: catalog.i18nc("@action:inmenu", "Custom selection") - checkable: true - checked: !showingSearchResults && !showingAllSettings && settingVisibilityPresetsModel.activePreset == "custom" - exclusiveGroup: group - onTriggered: - { - settingVisibilityPresetsModel.setActivePreset("custom"); - // Restore custom set from preference - UM.Preferences.setValue("general/visible_settings", UM.Preferences.getValue("cura/custom_visible_settings")); - showSettingVisibilityProfile(); - } - } - MenuSeparator { } - Instantiator { model: settingVisibilityPresetsModel @@ -48,9 +32,6 @@ Menu onTriggered: { settingVisibilityPresetsModel.setActivePreset(model.id); - - UM.Preferences.setValue("general/visible_settings", model.settings.join(";")); - showSettingVisibilityProfile(); } } diff --git a/resources/qml/Preferences/SettingVisibilityPage.qml b/resources/qml/Preferences/SettingVisibilityPage.qml index 7f6a58367d..b6b1c133ed 100644 --- a/resources/qml/Preferences/SettingVisibilityPage.qml +++ b/resources/qml/Preferences/SettingVisibilityPage.qml @@ -29,8 +29,7 @@ UM.PreferencesPage // After calling this function update Setting visibility preset combobox. // Reset should set default setting preset ("Basic") - visibilityPreset.setDefaultPreset() - + visibilityPreset.currentIndex = 1 } resetEnabled: true; @@ -39,8 +38,6 @@ UM.PreferencesPage id: base; anchors.fill: parent; - property bool inhibitSwitchToCustom: false - CheckBox { id: toggleVisibleSettings @@ -114,11 +111,6 @@ UM.PreferencesPage ComboBox { - function setDefaultPreset() - { - visibilityPreset.currentIndex = 0 - } - id: visibilityPreset width: 150 * screenScaleFactor anchors @@ -127,50 +119,25 @@ UM.PreferencesPage right: parent.right } - model: ListModel - { - id: visibilityPresetsModel - Component.onCompleted: - { - visibilityPresetsModel.append({text: catalog.i18nc("@action:inmenu", "Custom selection"), id: "custom"}); - - for(var i = 0; i < settingVisibilityPresetsModel.rowCount(); i++) - { - visibilityPresetsModel.append({text: settingVisibilityPresetsModel.getItem(i)["name"], id: settingVisibilityPresetsModel.getItem(i)["id"]}); - } - } - } + model: settingVisibilityPresetsModel + textRole: "name" currentIndex: { // Load previously selected preset. - var index = settingVisibilityPresetsModel.find("id", settingVisibilityPresetsModel.activePreset); - if(index == -1) + var index = settingVisibilityPresetsModel.find("id", settingVisibilityPresetsModel.activePreset) + if (index == -1) { - return 0; + return 0 } - return index + 1; // "Custom selection" entry is added in front, so index is off by 1 + return index } onActivated: { - base.inhibitSwitchToCustom = true; - var preset_id = visibilityPresetsModel.get(index).id; + var preset_id = settingVisibilityPresetsModel.getItem(index).id; settingVisibilityPresetsModel.setActivePreset(preset_id); - - UM.Preferences.setValue("cura/active_setting_visibility_preset", preset_id); - if (preset_id != "custom") - { - UM.Preferences.setValue("general/visible_settings", settingVisibilityPresetsModel.getItem(index - 1).settings.join(";")); - // "Custom selection" entry is added in front, so index is off by 1 - } - else - { - // Restore custom set from preference - UM.Preferences.setValue("general/visible_settings", UM.Preferences.getValue("cura/custom_visible_settings")); - } - base.inhibitSwitchToCustom = false; } } @@ -200,16 +167,7 @@ UM.PreferencesPage exclude: ["machine_settings", "command_line_settings"] showAncestors: true expanded: ["*"] - visibilityHandler: UM.SettingPreferenceVisibilityHandler - { - onVisibilityChanged: - { - if(settingVisibilityPresetsModel.activePreset != "" && !base.inhibitSwitchToCustom) - { - settingVisibilityPresetsModel.setActivePreset("custom"); - } - } - } + visibilityHandler: UM.SettingPreferenceVisibilityHandler {} } delegate: Loader From cbe929242ec59b227dc03079272978e1327ec786 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 15 Mar 2018 14:44:01 +0100 Subject: [PATCH 66/75] CURA-4870 Update the names of the groups in the container stacks when there is temporary name after upgrading from 3.2 --- .../NetworkedPrinterOutputDevice.py | 16 ++++++++++++++++ cura/Settings/MachineManager.py | 2 -- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py index eefbd9ae12..0dca149e5a 100644 --- a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py +++ b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py @@ -3,6 +3,7 @@ from UM.Application import Application from UM.Logger import Logger +from UM.Settings.ContainerRegistry import ContainerRegistry from cura.PrinterOutputDevice import PrinterOutputDevice, ConnectionState @@ -254,6 +255,21 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): self._last_manager_create_time = time() self._manager.authenticationRequired.connect(self._onAuthenticationRequired) + self._checkCorrectGroupName() + + ## This method checks if the name of the group stored in the definition container is correct. + # After updating from 3.2 to 3.3 some group names may be temporary. If there is a mismatch in the name of the group + # then all the container stacks are updated, both the current and the hidden ones. + def _checkCorrectGroupName(self): + global_container_stack = Application.getInstance().getGlobalContainerStack() + if global_container_stack and self.getId() == global_container_stack.getMetaDataEntry("um_network_key"): + # Check if the connect_group_name is correct. If not, update all the containers connected to the same printer + if global_container_stack.getMetaDataEntry("connect_group_name") != self.name: + metadata_filter = {"um_network_key": global_container_stack.getMetaDataEntry("um_network_key")} + hidden_containers = ContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter) + for container in hidden_containers: + container.setMetaDataEntry("connect_group_name", self.name) + def _registerOnFinishedCallback(self, reply: QNetworkReply, onFinished: Optional[Callable[[Any, QNetworkReply], None]]) -> None: if onFinished is not None: self._onFinishedCallbacks[reply.url().toString() + str(reply.operation())] = onFinished diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 100c7c3c31..1cf3fae161 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -10,7 +10,6 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Signal import Signal from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, QTimer -import UM.FlameProfiler from UM.FlameProfiler import pyqtSlot from UM import Util @@ -24,7 +23,6 @@ from UM.Settings.SettingFunction import SettingFunction from UM.Signal import postponeSignals, CompressTechnique from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch -from cura.Machines.VariantManager import VariantType from cura.PrinterOutputDevice import PrinterOutputDevice from cura.PrinterOutput.ConfigurationModel import ConfigurationModel from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationModel From 98b325c9d5334b5a6a0eff6c7918207c6a054d21 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 15 Mar 2018 14:54:09 +0100 Subject: [PATCH 67/75] Fix material serialization crash --- plugins/XmlMaterialProfile/XmlMaterialProfile.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/plugins/XmlMaterialProfile/XmlMaterialProfile.py b/plugins/XmlMaterialProfile/XmlMaterialProfile.py index 8b17721794..5ff6838373 100644 --- a/plugins/XmlMaterialProfile/XmlMaterialProfile.py +++ b/plugins/XmlMaterialProfile/XmlMaterialProfile.py @@ -208,14 +208,9 @@ class XmlMaterialProfile(InstanceContainer): machine_variant_map = {} variant_manager = CuraApplication.getInstance().getVariantManager() - material_manager = CuraApplication.getInstance().getMaterialManager() root_material_id = self.getMetaDataEntry("base_file") # if basefile is self.getId, this is a basefile. - material_group = material_manager.getMaterialGroup(root_material_id) - - all_containers = [] - for node in [material_group.root_material_node] + material_group.derived_material_node_list: - all_containers.append(node.getContainer()) + all_containers = registry.findInstanceContainers(base_file = root_material_id) for container in all_containers: definition_id = container.getMetaDataEntry("definition") @@ -242,7 +237,7 @@ class XmlMaterialProfile(InstanceContainer): for definition_id, container in machine_container_map.items(): definition_id = container.getMetaDataEntry("definition") - definition_metadata = ContainerRegistry.getInstance().findDefinitionContainersMetadata(id = definition_id)[0] + definition_metadata = registry.findDefinitionContainersMetadata(id = definition_id)[0] product = definition_id for product_name, product_id_list in product_id_map.items(): From 05cd937df35335744524cd5268f089a1296ebf0c Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Thu, 15 Mar 2018 15:00:13 +0100 Subject: [PATCH 68/75] CURA-4400 optional_extruders cannot be set to disabled extruders anymore --- cura/Settings/ExtrudersModel.py | 1 + resources/qml/Settings/SettingExtruder.qml | 3 ++- .../qml/Settings/SettingOptionalExtruder.qml | 24 ++++++++++++++++--- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/cura/Settings/ExtrudersModel.py b/cura/Settings/ExtrudersModel.py index 4ee5ab3c3b..f179dabd5a 100644 --- a/cura/Settings/ExtrudersModel.py +++ b/cura/Settings/ExtrudersModel.py @@ -210,6 +210,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): item = { "id": "", "name": catalog.i18nc("@menuitem", "Not overridden"), + "enabled": True, "color": "#ffffff", "index": -1, "definition": "" diff --git a/resources/qml/Settings/SettingExtruder.qml b/resources/qml/Settings/SettingExtruder.qml index 2ddbb135c7..38b1c2cab0 100644 --- a/resources/qml/Settings/SettingExtruder.qml +++ b/resources/qml/Settings/SettingExtruder.qml @@ -215,7 +215,8 @@ SettingItem { text: model.name renderType: Text.NativeRendering - color: { + color: + { if (model.enabled) { UM.Theme.getColor("setting_control_text") } else { diff --git a/resources/qml/Settings/SettingOptionalExtruder.qml b/resources/qml/Settings/SettingOptionalExtruder.qml index f49b7035d7..a370ec6259 100644 --- a/resources/qml/Settings/SettingOptionalExtruder.qml +++ b/resources/qml/Settings/SettingOptionalExtruder.qml @@ -27,8 +27,19 @@ SettingItem onActivated: { - forceActiveFocus(); - propertyProvider.setPropertyValue("value", model.getItem(index).index); + if (model.getItem(index).enabled) + { + forceActiveFocus(); + propertyProvider.setPropertyValue("value", model.getItem(index).index); + } else + { + if (propertyProvider.properties.value == -1) + { + control.currentIndex = model.rowCount() - 1; // we know the last item is "Not overriden" + } else { + control.currentIndex = propertyProvider.properties.value; // revert to the old value + } + } } onActiveFocusChanged: @@ -192,7 +203,14 @@ SettingItem { text: model.name renderType: Text.NativeRendering - color: UM.Theme.getColor("setting_control_text") + color: + { + if (model.enabled) { + UM.Theme.getColor("setting_control_text") + } else { + UM.Theme.getColor("action_button_disabled_text"); + } + } font: UM.Theme.getFont("default") elide: Text.ElideRight verticalAlignment: Text.AlignVCenter From dcb68bb33ef2e65fc3b63c7ac1196fd45105822c Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 15 Mar 2018 15:13:35 +0100 Subject: [PATCH 69/75] CURA-4870 Move the checkCorrectGroupName to the machine manager, where it must belong to. --- .../NetworkedPrinterOutputDevice.py | 17 +++-------------- cura/Settings/MachineManager.py | 12 ++++++++++++ 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py index 0dca149e5a..1537d51919 100644 --- a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py +++ b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py @@ -4,6 +4,7 @@ from UM.Application import Application from UM.Logger import Logger from UM.Settings.ContainerRegistry import ContainerRegistry +from cura.CuraApplication import CuraApplication from cura.PrinterOutputDevice import PrinterOutputDevice, ConnectionState @@ -255,20 +256,8 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): self._last_manager_create_time = time() self._manager.authenticationRequired.connect(self._onAuthenticationRequired) - self._checkCorrectGroupName() - - ## This method checks if the name of the group stored in the definition container is correct. - # After updating from 3.2 to 3.3 some group names may be temporary. If there is a mismatch in the name of the group - # then all the container stacks are updated, both the current and the hidden ones. - def _checkCorrectGroupName(self): - global_container_stack = Application.getInstance().getGlobalContainerStack() - if global_container_stack and self.getId() == global_container_stack.getMetaDataEntry("um_network_key"): - # Check if the connect_group_name is correct. If not, update all the containers connected to the same printer - if global_container_stack.getMetaDataEntry("connect_group_name") != self.name: - metadata_filter = {"um_network_key": global_container_stack.getMetaDataEntry("um_network_key")} - hidden_containers = ContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter) - for container in hidden_containers: - container.setMetaDataEntry("connect_group_name", self.name) + machine_manager = CuraApplication.getInstance().getMachineManager() + machine_manager.checkCorrectGroupName(self.getId(), self.name) def _registerOnFinishedCallback(self, reply: QNetworkReply, onFinished: Optional[Callable[[Any, QNetworkReply], None]]) -> None: if onFinished is not None: diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 1cf3fae161..3a6015e90f 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -1201,6 +1201,18 @@ class MachineManager(QObject): if machine.getMetaDataEntry(key) == value: machine.setMetaDataEntry(key, new_value) + ## This method checks if the name of the group stored in the definition container is correct. + # After updating from 3.2 to 3.3 some group names may be temporary. If there is a mismatch in the name of the group + # then all the container stacks are updated, both the current and the hidden ones. + def checkCorrectGroupName(self, device_id: str, group_name: str): + if self._global_container_stack and device_id == self.activeMachineNetworkKey: + # Check if the connect_group_name is correct. If not, update all the containers connected to the same printer + if self.activeMachineNetworkGroupName != group_name: + metadata_filter = {"um_network_key": self.activeMachineNetworkKey} + hidden_containers = ContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter) + for container in hidden_containers: + container.setMetaDataEntry("connect_group_name", group_name) + @pyqtSlot("QVariant") def setGlobalVariant(self, container_node): self.blurSettings.emit() From 08f43f6b2e719cad4ac0c42efdfaa7d82af0dc37 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 15 Mar 2018 15:27:13 +0100 Subject: [PATCH 70/75] Fix profile ordering in profile importing CURA-5054 --- cura/Settings/CuraContainerRegistry.py | 28 ++++++++++++++++---------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/cura/Settings/CuraContainerRegistry.py b/cura/Settings/CuraContainerRegistry.py index 0cf1c7399f..ab48eaddd2 100644 --- a/cura/Settings/CuraContainerRegistry.py +++ b/cura/Settings/CuraContainerRegistry.py @@ -173,12 +173,13 @@ class CuraContainerRegistry(ContainerRegistry): plugin_registry = PluginRegistry.getInstance() extension = file_name.split(".")[-1] - global_container_stack = Application.getInstance().getGlobalContainerStack() - if not global_container_stack: + global_stack = Application.getInstance().getGlobalContainerStack() + if not global_stack: return - machine_extruders = list(ExtruderManager.getInstance().getMachineExtruders(global_container_stack.getId())) - machine_extruders.sort(key = lambda k: k.getMetaDataEntry("position")) + machine_extruders = [] + for position in sorted(global_stack.extruders): + machine_extruders.append(global_stack.extruders[position]) for plugin_id, meta_data in self._getIOPlugins("profile_reader"): if meta_data["profile_reader"][0]["extension"] != extension: @@ -200,13 +201,18 @@ class CuraContainerRegistry(ContainerRegistry): # First check if this profile is suitable for this machine global_profile = None + extruder_profiles = [] if len(profile_or_list) == 1: global_profile = profile_or_list[0] else: for profile in profile_or_list: if not profile.getMetaDataEntry("position"): global_profile = profile - break + else: + extruder_profiles.append(profile) + extruder_profiles = sorted(extruder_profiles, key = lambda x: int(x.getMetaDataEntry("position"))) + profile_or_list = [global_profile] + extruder_profiles + if not global_profile: Logger.log("e", "Incorrect profile [%s]. Could not find global profile", file_name) return { "status": "error", @@ -227,7 +233,7 @@ class CuraContainerRegistry(ContainerRegistry): # Get the expected machine definition. # i.e.: We expect gcode for a UM2 Extended to be defined as normal UM2 gcode... profile_definition = getMachineDefinitionIDForQualitySearch(machine_definition) - expected_machine_definition = getMachineDefinitionIDForQualitySearch(global_container_stack.definition) + expected_machine_definition = getMachineDefinitionIDForQualitySearch(global_stack.definition) # And check if the profile_definition matches either one (showing error if not): if profile_definition != expected_machine_definition: @@ -251,8 +257,8 @@ class CuraContainerRegistry(ContainerRegistry): if len(profile_or_list) == 1: global_profile = profile_or_list[0] extruder_profiles = [] - for idx, extruder in enumerate(global_container_stack.extruders.values()): - profile_id = ContainerRegistry.getInstance().uniqueName(global_container_stack.getId() + "_extruder_" + str(idx + 1)) + for idx, extruder in enumerate(global_stack.extruders.values()): + profile_id = ContainerRegistry.getInstance().uniqueName(global_stack.getId() + "_extruder_" + str(idx + 1)) profile = InstanceContainer(profile_id) profile.setName(quality_name) profile.addMetaDataEntry("setting_version", CuraApplication.SettingVersion) @@ -264,12 +270,12 @@ class CuraContainerRegistry(ContainerRegistry): if idx == 0: # move all per-extruder settings to the first extruder's quality_changes for qc_setting_key in global_profile.getAllKeys(): - settable_per_extruder = global_container_stack.getProperty(qc_setting_key, + settable_per_extruder = global_stack.getProperty(qc_setting_key, "settable_per_extruder") if settable_per_extruder: setting_value = global_profile.getProperty(qc_setting_key, "value") - setting_definition = global_container_stack.getSettingDefinition(qc_setting_key) + setting_definition = global_stack.getSettingDefinition(qc_setting_key) new_instance = SettingInstance(setting_definition, profile) new_instance.setProperty("value", setting_value) new_instance.resetState() # Ensure that the state is not seen as a user state. @@ -286,7 +292,7 @@ class CuraContainerRegistry(ContainerRegistry): for profile_index, profile in enumerate(profile_or_list): if profile_index == 0: # This is assumed to be the global profile - profile_id = (global_container_stack.getBottom().getId() + "_" + name_seed).lower().replace(" ", "_") + profile_id = (global_stack.getBottom().getId() + "_" + name_seed).lower().replace(" ", "_") elif profile_index < len(machine_extruders) + 1: # This is assumed to be an extruder profile From 4b060f297f2a386ad8193f6795c1608ce5ec5830 Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Thu, 15 Mar 2018 15:44:27 +0100 Subject: [PATCH 71/75] CURA-4400 grey out disabled extruder in context menu --- resources/qml/Menus/ContextMenu.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Menus/ContextMenu.qml b/resources/qml/Menus/ContextMenu.qml index 83302f9463..e35aef5f20 100644 --- a/resources/qml/Menus/ContextMenu.qml +++ b/resources/qml/Menus/ContextMenu.qml @@ -31,7 +31,7 @@ Menu MenuItem { text: "%1: %2 - %3".arg(model.name).arg(model.material).arg(model.variant) visible: base.shouldShowExtruders - enabled: UM.Selection.hasSelection + enabled: UM.Selection.hasSelection && model.enabled checkable: true checked: Cura.ExtruderManager.selectedObjectExtruders.indexOf(model.id) != -1 onTriggered: CuraActions.setExtruderForSelection(model.id) From 8af82cc3f459e39abedd534e4295b0fd8ac1f04a Mon Sep 17 00:00:00 2001 From: Jack Ha Date: Thu, 15 Mar 2018 15:54:44 +0100 Subject: [PATCH 72/75] CURA-4400 prevent disabling last enabled extruder --- cura/Settings/MachineManager.py | 9 ++++++++- resources/qml/Cura.qml | 1 + resources/qml/SidebarHeader.qml | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 3a6015e90f..50c3c53734 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -145,6 +145,7 @@ class MachineManager(QObject): activeStackValueChanged = pyqtSignal() # Emitted whenever a value inside the active stack is changed. activeStackValidationChanged = pyqtSignal() # Emitted whenever a validation inside active container is changed stacksValidationChanged = pyqtSignal() # Emitted whenever a validation is changed + numberExtrudersEnabledChanged = pyqtSignal() # Emitted when the number of extruders that are enabled changed blurSettings = pyqtSignal() # Emitted to force fields in the advanced sidebar to un-focus, so they update properly @@ -880,7 +881,13 @@ class MachineManager(QObject): for position, extruder in self._global_container_stack.extruders.items(): if extruder.isEnabled: extruder_count += 1 - definition_changes_container.setProperty("extruders_enabled_count", "value", extruder_count) + if self.numberExtrudersEnabled != extruder_count: + definition_changes_container.setProperty("extruders_enabled_count", "value", extruder_count) + self.numberExtrudersEnabledChanged.emit() + + @pyqtProperty(int, notify = numberExtrudersEnabledChanged) + def numberExtrudersEnabled(self): + return self._global_container_stack.definitionChanges.getProperty("extruders_enabled_count", "value") @pyqtProperty(str, notify = extruderChanged) def defaultExtruderPosition(self): diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index cff4399073..c4ebb790e8 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -217,6 +217,7 @@ UM.MainWindow text: catalog.i18nc("@action:inmenu", "Disable Extruder") onTriggered: Cura.MachineManager.setExtruderEnabled(model.index, false) visible: Cura.MachineManager.getExtruder(model.index).isEnabled + enabled: Cura.MachineManager.numberExtrudersEnabled > 1 } } diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml index 5cd0446b36..92af6e9cc9 100644 --- a/resources/qml/SidebarHeader.qml +++ b/resources/qml/SidebarHeader.qml @@ -178,6 +178,7 @@ Column text: catalog.i18nc("@action:inmenu", "Disable Extruder") onTriggered: Cura.MachineManager.setExtruderEnabled(model.index, false) visible: extruder_enabled + enabled: Cura.MachineManager.numberExtrudersEnabled > 1 } } From b1c1b04f0d96c5b43795f44581691f8f3d634f7c Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 15 Mar 2018 16:08:40 +0100 Subject: [PATCH 73/75] Restore change to SettingView removeUnusedValue --- resources/qml/Settings/SettingView.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index a6d7b3a71e..cf9697210b 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -440,6 +440,7 @@ Item key: model.key ? model.key : "" watchedProperties: [ "value", "enabled", "state", "validationState", "settable_per_extruder", "resolve" ] storeIndex: 0 + removeUnusedValue: model.resolve == undefined } Connections From f127660cfc84819d61a58727fd7e7fe96c1f4b8d Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 15 Mar 2018 16:12:32 +0100 Subject: [PATCH 74/75] Remove whitespace at the end of the lines Someone put tabs there. --- resources/definitions/ultimaker3.def.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/definitions/ultimaker3.def.json b/resources/definitions/ultimaker3.def.json index 57cfbe960f..ef41686752 100644 --- a/resources/definitions/ultimaker3.def.json +++ b/resources/definitions/ultimaker3.def.json @@ -110,9 +110,9 @@ "material_bed_temperature": { "maximum_value": "115" }, "material_bed_temperature_layer_0": { "maximum_value": "115" }, "material_standby_temperature": { "value": "100" }, - "meshfix_maximum_resolution": { "value": "0.04" }, + "meshfix_maximum_resolution": { "value": "0.04" }, "multiple_mesh_overlap": { "value": "0" }, - "optimize_wall_printing_order": { "value": "True" }, + "optimize_wall_printing_order": { "value": "True" }, "prime_tower_enable": { "default_value": true }, "raft_airgap": { "value": "0" }, "raft_base_thickness": { "value": "0.3" }, From 3ce51bb25554ef70c695302d36927fbbde7a4761 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 15 Mar 2018 16:14:20 +0100 Subject: [PATCH 75/75] CURA-4870 Correctly align the name with the connection icon when the printer is connected to USB --- resources/qml/MachineSelection.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/MachineSelection.qml b/resources/qml/MachineSelection.qml index b959e20bb7..d075486eb2 100644 --- a/resources/qml/MachineSelection.qml +++ b/resources/qml/MachineSelection.qml @@ -71,8 +71,8 @@ ToolButton color: UM.Theme.getColor("sidebar_header_text_active") text: control.text; elide: Text.ElideRight; - anchors.left: isNetworkPrinter ? printerStatusIcon.right : parent.left; - anchors.leftMargin: isNetworkPrinter ? UM.Theme.getSize("sidebar_lining").width : UM.Theme.getSize("sidebar_margin").width + anchors.left: printerStatusIcon.visible ? printerStatusIcon.right : parent.left; + anchors.leftMargin: printerStatusIcon.visible ? UM.Theme.getSize("sidebar_lining").width : UM.Theme.getSize("sidebar_margin").width anchors.right: downArrow.left; anchors.rightMargin: control.rightMargin; anchors.verticalCenter: parent.verticalCenter;