From 193f11385168d1b0ca015127cfc46ac607a1d2a6 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 8 Oct 2018 10:33:01 +0200 Subject: [PATCH 0001/1240] Clean up Cura.qml: - Create a Skeleton folder where the main parts of the application will be stored. - Separate the top menus to a different file. Contributes to CURA-5784. --- cura/CuraApplication.py | 4 +- resources/qml/Cura.qml | 259 ++------------------- resources/qml/Skeleton/ApplicationMenu.qml | 234 +++++++++++++++++++ 3 files changed, 251 insertions(+), 246 deletions(-) create mode 100644 resources/qml/Skeleton/ApplicationMenu.qml diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 67bdd5805e..1990fd1b36 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -617,9 +617,7 @@ class CuraApplication(QtApplication): self._message_box_callback(button, *self._message_box_callback_arguments) self._message_box_callback = None self._message_box_callback_arguments = [] - - showPrintMonitor = pyqtSignal(bool, arguments = ["show"]) - + def setSaveDataEnabled(self, enabled: bool) -> None: self._save_data_enabled = enabled diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index b3367471ad..380e425ef5 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.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.7 @@ -11,28 +11,21 @@ import UM 1.3 as UM import Cura 1.1 as Cura import "Menus" +import "Skeleton" UM.MainWindow { id: base - //: Cura application window title - title: catalog.i18nc("@title:window","Ultimaker Cura"); - viewportRect: Qt.rect(0, 0, (base.width - sidebar.width) / base.width, 1.0) - property bool showPrintMonitor: false + // Cura application window title + title: catalog.i18nc("@title:window", "Ultimaker Cura") + viewportRect: Qt.rect(0, 0, (base.width - sidebar.width) / base.width, 1.0) backgroundColor: UM.Theme.getColor("viewport_background") - // This connection is here to support legacy printer output devices that use the showPrintMonitor signal on Application to switch to the monitor stage - // It should be phased out in newer plugin versions. - Connections + + UM.I18nCatalog { - target: CuraApplication - onShowPrintMonitor: { - if (show) { - UM.Controller.setActiveStage("MonitorStage") - } else { - UM.Controller.setActiveStage("PrepareStage") - } - } + id: catalog + name:"cura" } onWidthChanged: @@ -72,12 +65,12 @@ UM.MainWindow Item { - id: backgroundItem; - anchors.fill: parent; - UM.I18nCatalog{id: catalog; name:"cura"} + id: backgroundItem + anchors.fill: parent signal hasMesh(string name) //this signal sends the filebase name so it can be used for the JobSpecs.qml - function getMeshName(path){ + function getMeshName(path) + { //takes the path the complete path of the meshname and returns only the filebase var fileName = path.slice(path.lastIndexOf("/") + 1) var fileBase = fileName.slice(0, fileName.indexOf(".")) @@ -85,238 +78,18 @@ UM.MainWindow } //DeleteSelection on the keypress backspace event - Keys.onPressed: { + Keys.onPressed: + { if (event.key == Qt.Key_Backspace) { Cura.Actions.deleteSelection.trigger() } } - UM.ApplicationMenu + ApplicationMenu { id: menu window: base - - Menu - { - id: fileMenu - title: catalog.i18nc("@title:menu menubar:toplevel","&File"); - MenuItem - { - id: newProjectMenu - action: Cura.Actions.newProject; - } - - MenuItem - { - id: openMenu - action: Cura.Actions.open; - } - - RecentFilesMenu { } - - MenuItem - { - id: saveWorkspaceMenu - text: catalog.i18nc("@title:menu menubar:file","&Save...") - onTriggered: - { - var args = { "filter_by_machine": false, "file_type": "workspace", "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml" }; - if(UM.Preferences.getValue("cura/dialog_on_project_save")) - { - saveWorkspaceDialog.args = args; - saveWorkspaceDialog.open() - } - else - { - UM.OutputDeviceManager.requestWriteToDevice("local_file", PrintInformation.jobName, args) - } - } - } - - MenuSeparator { } - - MenuItem - { - id: saveAsMenu - text: catalog.i18nc("@title:menu menubar:file", "&Export...") - onTriggered: - { - var localDeviceId = "local_file"; - UM.OutputDeviceManager.requestWriteToDevice(localDeviceId, PrintInformation.jobName, { "filter_by_machine": false, "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml"}); - } - } - - MenuItem - { - id: exportSelectionMenu - text: catalog.i18nc("@action:inmenu menubar:file", "Export Selection..."); - enabled: UM.Selection.hasSelection; - iconName: "document-save-as"; - onTriggered: UM.OutputDeviceManager.requestWriteSelectionToDevice("local_file", PrintInformation.jobName, { "filter_by_machine": false, "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml"}); - } - - MenuSeparator { } - - MenuItem - { - id: reloadAllMenu - action: Cura.Actions.reloadAll; - } - - MenuSeparator { } - - MenuItem { action: Cura.Actions.quit; } - } - - Menu - { - title: catalog.i18nc("@title:menu menubar:toplevel","&Edit"); - - MenuItem { action: Cura.Actions.undo; } - MenuItem { action: Cura.Actions.redo; } - MenuSeparator { } - MenuItem { action: Cura.Actions.selectAll; } - MenuItem { action: Cura.Actions.arrangeAll; } - MenuItem { action: Cura.Actions.deleteSelection; } - MenuItem { action: Cura.Actions.deleteAll; } - MenuItem { action: Cura.Actions.resetAllTranslation; } - MenuItem { action: Cura.Actions.resetAll; } - MenuSeparator { } - MenuItem { action: Cura.Actions.groupObjects;} - MenuItem { action: Cura.Actions.mergeObjects;} - MenuItem { action: Cura.Actions.unGroupObjects;} - } - - ViewMenu { title: catalog.i18nc("@title:menu", "&View") } - - Menu - { - id: settingsMenu - title: catalog.i18nc("@title:menu", "&Settings") - - PrinterMenu { title: catalog.i18nc("@title:menu menubar:settings", "&Printer") } - - Instantiator - { - model: Cura.ExtrudersModel { simpleNames: true } - Menu { - title: model.name - - NozzleMenu { title: Cura.MachineManager.activeDefinitionVariantsName; visible: Cura.MachineManager.hasVariants; extruderIndex: index } - MaterialMenu { title: catalog.i18nc("@title:menu", "&Material"); visible: Cura.MachineManager.hasMaterials; extruderIndex: index } - - MenuSeparator - { - visible: Cura.MachineManager.hasVariants || Cura.MachineManager.hasMaterials - } - - MenuItem - { - text: catalog.i18nc("@action:inmenu", "Set as Active Extruder") - onTriggered: Cura.MachineManager.setExtruderIndex(model.index) - } - - MenuItem - { - text: catalog.i18nc("@action:inmenu", "Enable Extruder") - onTriggered: Cura.MachineManager.setExtruderEnabled(model.index, true) - visible: !Cura.MachineManager.getExtruder(model.index).isEnabled - } - - MenuItem - { - 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 - } - - } - onObjectAdded: settingsMenu.insertItem(index, object) - onObjectRemoved: settingsMenu.removeItem(object) - } - - // TODO Only show in dev mode. Remove check when feature ready - BuildplateMenu { title: catalog.i18nc("@title:menu", "&Build plate"); visible: CuraSDKVersion == "dev" ? Cura.MachineManager.hasVariantBuildplates : false } - ProfileMenu { title: catalog.i18nc("@title:settings", "&Profile"); } - - MenuSeparator { } - - MenuItem { action: Cura.Actions.configureSettingVisibility } - } - - Menu - { - id: extension_menu - title: catalog.i18nc("@title:menu menubar:toplevel","E&xtensions"); - - Instantiator - { - id: extensions - model: UM.ExtensionModel { } - - Menu - { - id: sub_menu - title: model.name; - visible: actions != null - enabled: actions != null - Instantiator - { - model: actions - MenuItem - { - text: model.text - onTriggered: extensions.model.subMenuTriggered(name, model.text) - } - onObjectAdded: sub_menu.insertItem(index, object) - onObjectRemoved: sub_menu.removeItem(object) - } - } - - onObjectAdded: extension_menu.insertItem(index, object) - onObjectRemoved: extension_menu.removeItem(object) - } - } - - Menu - { - id: plugin_menu - title: catalog.i18nc("@title:menu menubar:toplevel", "&Toolbox") - - MenuItem { action: Cura.Actions.browsePackages } - } - - Menu - { - id: preferencesMenu - title: catalog.i18nc("@title:menu menubar:toplevel","P&references"); - - MenuItem { action: Cura.Actions.preferences; } - } - - Menu - { - id: helpMenu - title: catalog.i18nc("@title:menu menubar:toplevel","&Help"); - - MenuItem { action: Cura.Actions.showProfileFolder; } - MenuItem { action: Cura.Actions.documentation; } - MenuItem { action: Cura.Actions.reportBug; } - MenuSeparator { } - MenuItem { action: Cura.Actions.about; } - } - } - - UM.SettingPropertyProvider - { - id: machineExtruderCount - - containerStack: Cura.MachineManager.activeMachine - key: "machine_extruder_count" - watchedProperties: [ "value" ] - storeIndex: 0 } Item diff --git a/resources/qml/Skeleton/ApplicationMenu.qml b/resources/qml/Skeleton/ApplicationMenu.qml new file mode 100644 index 0000000000..9abaa80f2a --- /dev/null +++ b/resources/qml/Skeleton/ApplicationMenu.qml @@ -0,0 +1,234 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.1 +import QtQuick.Dialogs 1.2 + +import UM 1.3 as UM +import Cura 1.1 as Cura + +import "../Menus" + +UM.ApplicationMenu +{ + id: menu + + Menu + { + id: fileMenu + title: catalog.i18nc("@title:menu menubar:toplevel","&File") + + MenuItem + { + id: newProjectMenu + action: Cura.Actions.newProject + } + + MenuItem + { + id: openMenu + action: Cura.Actions.open + } + + RecentFilesMenu { } + + MenuItem + { + id: saveWorkspaceMenu + text: catalog.i18nc("@title:menu menubar:file","&Save...") + onTriggered: + { + var args = { "filter_by_machine": false, "file_type": "workspace", "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml" }; + if(UM.Preferences.getValue("cura/dialog_on_project_save")) + { + saveWorkspaceDialog.args = args + saveWorkspaceDialog.open() + } + else + { + UM.OutputDeviceManager.requestWriteToDevice("local_file", PrintInformation.jobName, args) + } + } + } + + MenuSeparator { } + + MenuItem + { + id: saveAsMenu + text: catalog.i18nc("@title:menu menubar:file", "&Export...") + onTriggered: + { + var localDeviceId = "local_file" + UM.OutputDeviceManager.requestWriteToDevice(localDeviceId, PrintInformation.jobName, { "filter_by_machine": false, "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml"}) + } + } + + MenuItem + { + id: exportSelectionMenu + text: catalog.i18nc("@action:inmenu menubar:file", "Export Selection...") + enabled: UM.Selection.hasSelection + iconName: "document-save-as" + onTriggered: UM.OutputDeviceManager.requestWriteSelectionToDevice("local_file", PrintInformation.jobName, { "filter_by_machine": false, "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml"}) + } + + MenuSeparator { } + + MenuItem + { + id: reloadAllMenu + action: Cura.Actions.reloadAll + } + + MenuSeparator { } + + MenuItem { action: Cura.Actions.quit } + } + + Menu + { + title: catalog.i18nc("@title:menu menubar:toplevel","&Edit") + + MenuItem { action: Cura.Actions.undo } + MenuItem { action: Cura.Actions.redo } + MenuSeparator { } + MenuItem { action: Cura.Actions.selectAll } + MenuItem { action: Cura.Actions.arrangeAll } + MenuItem { action: Cura.Actions.deleteSelection } + MenuItem { action: Cura.Actions.deleteAll } + MenuItem { action: Cura.Actions.resetAllTranslation } + MenuItem { action: Cura.Actions.resetAll } + MenuSeparator { } + MenuItem { action: Cura.Actions.groupObjects } + MenuItem { action: Cura.Actions.mergeObjects } + MenuItem { action: Cura.Actions.unGroupObjects } + } + + ViewMenu { title: catalog.i18nc("@title:menu", "&View") } + + Menu + { + id: settingsMenu + title: catalog.i18nc("@title:menu", "&Settings") + + PrinterMenu { title: catalog.i18nc("@title:menu menubar:settings", "&Printer") } + + Instantiator + { + model: Cura.ExtrudersModel { simpleNames: true } + Menu { + title: model.name + + NozzleMenu { title: Cura.MachineManager.activeDefinitionVariantsName; visible: Cura.MachineManager.hasVariants; extruderIndex: index } + MaterialMenu { title: catalog.i18nc("@title:menu", "&Material"); visible: Cura.MachineManager.hasMaterials; extruderIndex: index } + + MenuSeparator + { + visible: Cura.MachineManager.hasVariants || Cura.MachineManager.hasMaterials + } + + MenuItem + { + text: catalog.i18nc("@action:inmenu", "Set as Active Extruder") + onTriggered: Cura.MachineManager.setExtruderIndex(model.index) + } + + MenuItem + { + text: catalog.i18nc("@action:inmenu", "Enable Extruder") + onTriggered: Cura.MachineManager.setExtruderEnabled(model.index, true) + visible: !Cura.MachineManager.getExtruder(model.index).isEnabled + } + + MenuItem + { + 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 + } + + } + onObjectAdded: settingsMenu.insertItem(index, object) + onObjectRemoved: settingsMenu.removeItem(object) + } + + // TODO Only show in dev mode. Remove check when feature ready + BuildplateMenu + { + title: catalog.i18nc("@title:menu", "&Build plate") + visible: CuraSDKVersion == "dev" && Cura.MachineManager.hasVariantBuildplates + } + ProfileMenu { title: catalog.i18nc("@title:settings", "&Profile") } + + MenuSeparator { } + + MenuItem { action: Cura.Actions.configureSettingVisibility } + } + + Menu + { + id: extension_menu + title: catalog.i18nc("@title:menu menubar:toplevel","E&xtensions") + + Instantiator + { + id: extensions + model: UM.ExtensionModel { } + + Menu + { + id: sub_menu + title: model.name; + visible: actions != null + enabled: actions != null + Instantiator + { + model: actions + MenuItem + { + text: model.text + onTriggered: extensions.model.subMenuTriggered(name, model.text) + } + onObjectAdded: sub_menu.insertItem(index, object) + onObjectRemoved: sub_menu.removeItem(object) + } + } + + onObjectAdded: extension_menu.insertItem(index, object) + onObjectRemoved: extension_menu.removeItem(object) + } + } + + Menu + { + id: plugin_menu + title: catalog.i18nc("@title:menu menubar:toplevel", "&Toolbox") + + MenuItem { action: Cura.Actions.browsePackages } + } + + Menu + { + id: preferencesMenu + title: catalog.i18nc("@title:menu menubar:toplevel","P&references") + + MenuItem { action: Cura.Actions.preferences } + } + + Menu + { + id: helpMenu + title: catalog.i18nc("@title:menu menubar:toplevel","&Help") + + MenuItem { action: Cura.Actions.showProfileFolder } + MenuItem { action: Cura.Actions.documentation } + MenuItem { action: Cura.Actions.reportBug } + MenuSeparator { } + MenuItem { action: Cura.Actions.about } + } +} \ No newline at end of file From 8bdd27183fcb318cf76c1c5f5aab1b0e7507e4f7 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 8 Oct 2018 10:59:46 +0200 Subject: [PATCH 0002/1240] Move TopBar to the Skeleton folder. Clean up the Cura.qml even more by moving some components to the ApplicationMenu, where they are called. Contributes to CURA-5784. --- resources/qml/Cura.qml | 35 -- .../{ => Dialogs}/WorkspaceSummaryDialog.qml | 0 resources/qml/Skeleton/ApplicationMenu.qml | 413 ++++++++++-------- resources/qml/{ => Skeleton}/Topbar.qml | 3 +- 4 files changed, 229 insertions(+), 222 deletions(-) rename resources/qml/{ => Dialogs}/WorkspaceSummaryDialog.qml (100%) rename resources/qml/{ => Skeleton}/Topbar.qml (99%) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 380e425ef5..18280ea4ae 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -183,7 +183,6 @@ UM.MainWindow bottom: parent.bottom; left: parent.left; } - } Topbar @@ -340,13 +339,6 @@ UM.MainWindow } } - WorkspaceSummaryDialog - { - id: saveWorkspaceDialog - property var args - onYes: UM.OutputDeviceManager.requestWriteToDevice("local_file", PrintInformation.jobName, args) - } - Connections { target: Cura.Actions.preferences @@ -359,33 +351,6 @@ UM.MainWindow onShowPreferencesWindow: preferences.visible = true } - MessageDialog - { - id: newProjectDialog - modality: Qt.ApplicationModal - title: catalog.i18nc("@title:window", "New project") - text: catalog.i18nc("@info:question", "Are you sure you want to start a new project? This will clear the build plate and any unsaved settings.") - standardButtons: StandardButton.Yes | StandardButton.No - icon: StandardIcon.Question - onYes: - { - CuraApplication.deleteAll(); - Cura.Actions.resetProfile.trigger(); - } - } - - Connections - { - target: Cura.Actions.newProject - onTriggered: - { - if(Printer.platformActivity || Cura.MachineManager.hasUserSettings) - { - newProjectDialog.visible = true - } - } - } - Connections { target: Cura.Actions.addProfile diff --git a/resources/qml/WorkspaceSummaryDialog.qml b/resources/qml/Dialogs/WorkspaceSummaryDialog.qml similarity index 100% rename from resources/qml/WorkspaceSummaryDialog.qml rename to resources/qml/Dialogs/WorkspaceSummaryDialog.qml diff --git a/resources/qml/Skeleton/ApplicationMenu.qml b/resources/qml/Skeleton/ApplicationMenu.qml index 9abaa80f2a..97d8f7ca7e 100644 --- a/resources/qml/Skeleton/ApplicationMenu.qml +++ b/resources/qml/Skeleton/ApplicationMenu.qml @@ -11,224 +11,267 @@ import UM 1.3 as UM import Cura 1.1 as Cura import "../Menus" +import "../Dialogs" -UM.ApplicationMenu +Item { id: menu + width: applicationMenu.width + height: applicationMenu.height + property alias window: applicationMenu.window - Menu + UM.ApplicationMenu { - id: fileMenu - title: catalog.i18nc("@title:menu menubar:toplevel","&File") + id: applicationMenu - MenuItem + Menu { - id: newProjectMenu - action: Cura.Actions.newProject - } + id: fileMenu + title: catalog.i18nc("@title:menu menubar:toplevel","&File") - MenuItem - { - id: openMenu - action: Cura.Actions.open - } - - RecentFilesMenu { } - - MenuItem - { - id: saveWorkspaceMenu - text: catalog.i18nc("@title:menu menubar:file","&Save...") - onTriggered: + MenuItem { - var args = { "filter_by_machine": false, "file_type": "workspace", "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml" }; - if(UM.Preferences.getValue("cura/dialog_on_project_save")) + id: newProjectMenu + action: Cura.Actions.newProject + } + + MenuItem + { + id: openMenu + action: Cura.Actions.open + } + + RecentFilesMenu { } + + MenuItem + { + id: saveWorkspaceMenu + text: catalog.i18nc("@title:menu menubar:file","&Save...") + onTriggered: { - saveWorkspaceDialog.args = args - saveWorkspaceDialog.open() - } - else - { - UM.OutputDeviceManager.requestWriteToDevice("local_file", PrintInformation.jobName, args) + var args = { "filter_by_machine": false, "file_type": "workspace", "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml" }; + if(UM.Preferences.getValue("cura/dialog_on_project_save")) + { + saveWorkspaceDialog.args = args + saveWorkspaceDialog.open() + } + else + { + UM.OutputDeviceManager.requestWriteToDevice("local_file", PrintInformation.jobName, args) + } } } - } - MenuSeparator { } + MenuSeparator { } - MenuItem - { - id: saveAsMenu - text: catalog.i18nc("@title:menu menubar:file", "&Export...") - onTriggered: + MenuItem { - var localDeviceId = "local_file" - UM.OutputDeviceManager.requestWriteToDevice(localDeviceId, PrintInformation.jobName, { "filter_by_machine": false, "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml"}) + id: saveAsMenu + text: catalog.i18nc("@title:menu menubar:file", "&Export...") + onTriggered: + { + var localDeviceId = "local_file" + UM.OutputDeviceManager.requestWriteToDevice(localDeviceId, PrintInformation.jobName, { "filter_by_machine": false, "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml"}) + } } - } - MenuItem - { - id: exportSelectionMenu - text: catalog.i18nc("@action:inmenu menubar:file", "Export Selection...") - enabled: UM.Selection.hasSelection - iconName: "document-save-as" - onTriggered: UM.OutputDeviceManager.requestWriteSelectionToDevice("local_file", PrintInformation.jobName, { "filter_by_machine": false, "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml"}) - } - - MenuSeparator { } - - MenuItem - { - id: reloadAllMenu - action: Cura.Actions.reloadAll - } - - MenuSeparator { } - - MenuItem { action: Cura.Actions.quit } - } - - Menu - { - title: catalog.i18nc("@title:menu menubar:toplevel","&Edit") - - MenuItem { action: Cura.Actions.undo } - MenuItem { action: Cura.Actions.redo } - MenuSeparator { } - MenuItem { action: Cura.Actions.selectAll } - MenuItem { action: Cura.Actions.arrangeAll } - MenuItem { action: Cura.Actions.deleteSelection } - MenuItem { action: Cura.Actions.deleteAll } - MenuItem { action: Cura.Actions.resetAllTranslation } - MenuItem { action: Cura.Actions.resetAll } - MenuSeparator { } - MenuItem { action: Cura.Actions.groupObjects } - MenuItem { action: Cura.Actions.mergeObjects } - MenuItem { action: Cura.Actions.unGroupObjects } - } - - ViewMenu { title: catalog.i18nc("@title:menu", "&View") } - - Menu - { - id: settingsMenu - title: catalog.i18nc("@title:menu", "&Settings") - - PrinterMenu { title: catalog.i18nc("@title:menu menubar:settings", "&Printer") } - - Instantiator - { - model: Cura.ExtrudersModel { simpleNames: true } - Menu { - title: model.name - - NozzleMenu { title: Cura.MachineManager.activeDefinitionVariantsName; visible: Cura.MachineManager.hasVariants; extruderIndex: index } - MaterialMenu { title: catalog.i18nc("@title:menu", "&Material"); visible: Cura.MachineManager.hasMaterials; extruderIndex: index } - - MenuSeparator - { - visible: Cura.MachineManager.hasVariants || Cura.MachineManager.hasMaterials - } - - MenuItem - { - text: catalog.i18nc("@action:inmenu", "Set as Active Extruder") - onTriggered: Cura.MachineManager.setExtruderIndex(model.index) - } - - MenuItem - { - text: catalog.i18nc("@action:inmenu", "Enable Extruder") - onTriggered: Cura.MachineManager.setExtruderEnabled(model.index, true) - visible: !Cura.MachineManager.getExtruder(model.index).isEnabled - } - - MenuItem - { - 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 - } - - } - onObjectAdded: settingsMenu.insertItem(index, object) - onObjectRemoved: settingsMenu.removeItem(object) - } - - // TODO Only show in dev mode. Remove check when feature ready - BuildplateMenu - { - title: catalog.i18nc("@title:menu", "&Build plate") - visible: CuraSDKVersion == "dev" && Cura.MachineManager.hasVariantBuildplates - } - ProfileMenu { title: catalog.i18nc("@title:settings", "&Profile") } - - MenuSeparator { } - - MenuItem { action: Cura.Actions.configureSettingVisibility } - } - - Menu - { - id: extension_menu - title: catalog.i18nc("@title:menu menubar:toplevel","E&xtensions") - - Instantiator - { - id: extensions - model: UM.ExtensionModel { } - - Menu + MenuItem { - id: sub_menu - title: model.name; - visible: actions != null - enabled: actions != null - Instantiator - { - model: actions + id: exportSelectionMenu + text: catalog.i18nc("@action:inmenu menubar:file", "Export Selection...") + enabled: UM.Selection.hasSelection + iconName: "document-save-as" + onTriggered: UM.OutputDeviceManager.requestWriteSelectionToDevice("local_file", PrintInformation.jobName, { "filter_by_machine": false, "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml"}) + } + + MenuSeparator { } + + MenuItem + { + id: reloadAllMenu + action: Cura.Actions.reloadAll + } + + MenuSeparator { } + + MenuItem { action: Cura.Actions.quit } + } + + Menu + { + title: catalog.i18nc("@title:menu menubar:toplevel","&Edit") + + MenuItem { action: Cura.Actions.undo } + MenuItem { action: Cura.Actions.redo } + MenuSeparator { } + MenuItem { action: Cura.Actions.selectAll } + MenuItem { action: Cura.Actions.arrangeAll } + MenuItem { action: Cura.Actions.deleteSelection } + MenuItem { action: Cura.Actions.deleteAll } + MenuItem { action: Cura.Actions.resetAllTranslation } + MenuItem { action: Cura.Actions.resetAll } + MenuSeparator { } + MenuItem { action: Cura.Actions.groupObjects } + MenuItem { action: Cura.Actions.mergeObjects } + MenuItem { action: Cura.Actions.unGroupObjects } + } + + ViewMenu { title: catalog.i18nc("@title:menu", "&View") } + + Menu + { + id: settingsMenu + title: catalog.i18nc("@title:menu", "&Settings") + + PrinterMenu { title: catalog.i18nc("@title:menu menubar:settings", "&Printer") } + + Instantiator + { + model: Cura.ExtrudersModel { simpleNames: true } + Menu { + title: model.name + + NozzleMenu { title: Cura.MachineManager.activeDefinitionVariantsName; visible: Cura.MachineManager.hasVariants; extruderIndex: index } + MaterialMenu { title: catalog.i18nc("@title:menu", "&Material"); visible: Cura.MachineManager.hasMaterials; extruderIndex: index } + + MenuSeparator + { + visible: Cura.MachineManager.hasVariants || Cura.MachineManager.hasMaterials + } + MenuItem { - text: model.text - onTriggered: extensions.model.subMenuTriggered(name, model.text) + text: catalog.i18nc("@action:inmenu", "Set as Active Extruder") + onTriggered: Cura.MachineManager.setExtruderIndex(model.index) } - onObjectAdded: sub_menu.insertItem(index, object) - onObjectRemoved: sub_menu.removeItem(object) + + MenuItem + { + text: catalog.i18nc("@action:inmenu", "Enable Extruder") + onTriggered: Cura.MachineManager.setExtruderEnabled(model.index, true) + visible: !Cura.MachineManager.getExtruder(model.index).isEnabled + } + + MenuItem + { + 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 + } + } + onObjectAdded: settingsMenu.insertItem(index, object) + onObjectRemoved: settingsMenu.removeItem(object) } - onObjectAdded: extension_menu.insertItem(index, object) - onObjectRemoved: extension_menu.removeItem(object) + // TODO Only show in dev mode. Remove check when feature ready + BuildplateMenu + { + title: catalog.i18nc("@title:menu", "&Build plate") + visible: CuraSDKVersion == "dev" && Cura.MachineManager.hasVariantBuildplates + } + ProfileMenu { title: catalog.i18nc("@title:settings", "&Profile") } + + MenuSeparator { } + + MenuItem { action: Cura.Actions.configureSettingVisibility } + } + + Menu + { + id: extension_menu + title: catalog.i18nc("@title:menu menubar:toplevel","E&xtensions") + + Instantiator + { + id: extensions + model: UM.ExtensionModel { } + + Menu + { + id: sub_menu + title: model.name; + visible: actions != null + enabled: actions != null + Instantiator + { + model: actions + MenuItem + { + text: model.text + onTriggered: extensions.model.subMenuTriggered(name, model.text) + } + onObjectAdded: sub_menu.insertItem(index, object) + onObjectRemoved: sub_menu.removeItem(object) + } + } + + onObjectAdded: extension_menu.insertItem(index, object) + onObjectRemoved: extension_menu.removeItem(object) + } + } + + Menu + { + id: plugin_menu + title: catalog.i18nc("@title:menu menubar:toplevel", "&Toolbox") + + MenuItem { action: Cura.Actions.browsePackages } + } + + Menu + { + id: preferencesMenu + title: catalog.i18nc("@title:menu menubar:toplevel","P&references") + + MenuItem { action: Cura.Actions.preferences } + } + + Menu + { + id: helpMenu + title: catalog.i18nc("@title:menu menubar:toplevel","&Help") + + MenuItem { action: Cura.Actions.showProfileFolder } + MenuItem { action: Cura.Actions.documentation } + MenuItem { action: Cura.Actions.reportBug } + MenuSeparator { } + MenuItem { action: Cura.Actions.about } } } - Menu + WorkspaceSummaryDialog { - id: plugin_menu - title: catalog.i18nc("@title:menu menubar:toplevel", "&Toolbox") - - MenuItem { action: Cura.Actions.browsePackages } + id: saveWorkspaceDialog + property var args + onYes: UM.OutputDeviceManager.requestWriteToDevice("local_file", PrintInformation.jobName, args) } - Menu + MessageDialog { - id: preferencesMenu - title: catalog.i18nc("@title:menu menubar:toplevel","P&references") - - MenuItem { action: Cura.Actions.preferences } + id: newProjectDialog + modality: Qt.ApplicationModal + title: catalog.i18nc("@title:window", "New project") + text: catalog.i18nc("@info:question", "Are you sure you want to start a new project? This will clear the build plate and any unsaved settings.") + standardButtons: StandardButton.Yes | StandardButton.No + icon: StandardIcon.Question + onYes: + { + CuraApplication.deleteAll(); + Cura.Actions.resetProfile.trigger(); + } } - Menu + Connections { - id: helpMenu - title: catalog.i18nc("@title:menu menubar:toplevel","&Help") - - MenuItem { action: Cura.Actions.showProfileFolder } - MenuItem { action: Cura.Actions.documentation } - MenuItem { action: Cura.Actions.reportBug } - MenuSeparator { } - MenuItem { action: Cura.Actions.about } + target: Cura.Actions.newProject + onTriggered: + { + if(Printer.platformActivity || Cura.MachineManager.hasUserSettings) + { + newProjectDialog.visible = true + } + } } } \ No newline at end of file diff --git a/resources/qml/Topbar.qml b/resources/qml/Skeleton/Topbar.qml similarity index 99% rename from resources/qml/Topbar.qml rename to resources/qml/Skeleton/Topbar.qml index 17819dbd27..d27d9865b8 100644 --- a/resources/qml/Topbar.qml +++ b/resources/qml/Skeleton/Topbar.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 @@ -8,7 +8,6 @@ import QtQuick.Layouts 1.1 import UM 1.4 as UM import Cura 1.0 as Cura -import "Menus" Rectangle { From 3acfdadd12c8a0f5b136cb94d011cf6ccfebc693 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 8 Oct 2018 11:31:53 +0200 Subject: [PATCH 0003/1240] Organize the components in a Column. Separate the TopHeader and place it below the application menu, using all the width. Contributes to CURA-5784. --- resources/qml/Cura.qml | 104 +++++++++--------- .../Skeleton/{Topbar.qml => TopHeader.qml} | 0 2 files changed, 53 insertions(+), 51 deletions(-) rename resources/qml/Skeleton/{Topbar.qml => TopHeader.qml} (100%) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 18280ea4ae..62ec4799c9 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -63,7 +63,7 @@ UM.MainWindow CuraApplication.purgeWindows() } - Item + Column { id: backgroundItem anchors.fill: parent @@ -92,19 +92,25 @@ UM.MainWindow window: base } + TopHeader + { + id: topHeader + anchors.left: parent.left + anchors.right: parent.right + } + Item { - id: contentItem; + id: contentItem - y: menu.height - width: parent.width; - height: parent.height - menu.height; + width: parent.width + height: parent.height - menu.height - topHeader.height Keys.forwardTo: menu DropArea { - anchors.fill: parent; + anchors.fill: parent onDropped: { if (drop.urls.length > 0) @@ -137,69 +143,61 @@ UM.MainWindow id: jobSpecs anchors { - bottom: parent.bottom; - right: sidebar.left; - bottomMargin: UM.Theme.getSize("default_margin").height; - rightMargin: UM.Theme.getSize("default_margin").width; + bottom: parent.bottom + right: sidebar.left + bottomMargin: UM.Theme.getSize("default_margin").height + rightMargin: UM.Theme.getSize("default_margin").width } } Button { - id: openFileButton; - text: catalog.i18nc("@action:button","Open File"); + id: openFileButton + text: catalog.i18nc("@action:button","Open File") iconSource: UM.Theme.getIcon("load") style: UM.Theme.styles.tool_button tooltip: "" anchors { - top: topbar.bottom; - topMargin: UM.Theme.getSize("default_margin").height; - left: parent.left; + top: parent.top + topMargin: UM.Theme.getSize("default_margin").height + left: parent.left } - action: Cura.Actions.open; + action: Cura.Actions.open } Toolbar { - id: toolbar; + id: toolbar property int mouseX: base.mouseX property int mouseY: base.mouseY anchors { - top: openFileButton.bottom; - topMargin: UM.Theme.getSize("window_margin").height; - left: parent.left; + top: openFileButton.bottom + topMargin: UM.Theme.getSize("window_margin").height + left: parent.left } } ObjectsList { - id: objectsList; - visible: UM.Preferences.getValue("cura/use_multi_build_plate"); + id: objectsList + visible: UM.Preferences.getValue("cura/use_multi_build_plate") anchors { - bottom: parent.bottom; - left: parent.left; + bottom: parent.bottom + left: parent.left } } - Topbar - { - id: topbar - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - } - Loader { id: main anchors { - top: topbar.bottom + top: parent.top bottom: parent.bottom left: parent.left right: sidebar.left @@ -220,26 +218,30 @@ UM.MainWindow { id: sidebar - property bool collapsed: false; - property var initialWidth: UM.Theme.getSize("sidebar").width; + property bool collapsed: false + property var initialWidth: UM.Theme.getSize("sidebar").width - function callExpandOrCollapse() { - if (collapsed) { - sidebar.visible = true; - sidebar.initialWidth = UM.Theme.getSize("sidebar").width; - viewportRect = Qt.rect(0, 0, (base.width - sidebar.width) / base.width, 1.0); + function callExpandOrCollapse() + { + if (collapsed) + { + sidebar.visible = true + sidebar.initialWidth = UM.Theme.getSize("sidebar").width + viewportRect = Qt.rect(0, 0, (base.width - sidebar.width) / base.width, 1.0) expandSidebarAnimation.start(); - } else { - viewportRect = Qt.rect(0, 0, 1, 1.0); - collapseSidebarAnimation.start(); } - collapsed = !collapsed; - UM.Preferences.setValue("cura/sidebar_collapsed", collapsed); + else + { + viewportRect = Qt.rect(0, 0, 1, 1.0) + collapseSidebarAnimation.start() + } + collapsed = !collapsed + UM.Preferences.setValue("cura/sidebar_collapsed", collapsed) } anchors { - top: topbar.top + top: parent.top bottom: parent.bottom } @@ -265,13 +267,13 @@ UM.MainWindow Component.onCompleted: { - var sidebar_collapsed = UM.Preferences.getValue("cura/sidebar_collapsed"); + var sidebar_collapsed = UM.Preferences.getValue("cura/sidebar_collapsed") if (sidebar_collapsed) { - sidebar.collapsed = true; + sidebar.collapsed = true viewportRect = Qt.rect(0, 0, 1, 1.0) - collapseSidebarAnimation.start(); + collapseSidebarAnimation.start() } } @@ -290,8 +292,8 @@ UM.MainWindow { horizontalCenter: parent.horizontalCenter horizontalCenterOffset: -(Math.round(UM.Theme.getSize("sidebar").width / 2)) - top: parent.verticalCenter; - bottom: parent.bottom; + top: parent.verticalCenter + bottom: parent.bottom bottomMargin: UM.Theme.getSize("default_margin").height } } diff --git a/resources/qml/Skeleton/Topbar.qml b/resources/qml/Skeleton/TopHeader.qml similarity index 100% rename from resources/qml/Skeleton/Topbar.qml rename to resources/qml/Skeleton/TopHeader.qml From 075a9a161fb77c999ad432666cd9b6d943f52cad Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 8 Oct 2018 12:57:16 +0200 Subject: [PATCH 0004/1240] Create a component for the Views selector. This component contains the list of the views and also the shortcuts for the camera position. The theme colors, sizes and the styles have been updated. Contributes to CURA-5784. --- .../resources/qml/ToolboxTabButton.qml | 6 +- resources/qml/Cura.qml | 12 + .../ConfigurationMenu/ConfigurationItem.qml | 4 +- resources/qml/Skeleton/ApplicationViews.qml | 108 +++++++++ resources/qml/Skeleton/OrientationViews.qml | 56 +++++ resources/qml/Skeleton/TopHeader.qml | 208 ++---------------- resources/themes/cura-dark/theme.json | 15 +- resources/themes/cura-light/styles.qml | 106 ++------- resources/themes/cura-light/theme.json | 24 +- 9 files changed, 235 insertions(+), 304 deletions(-) create mode 100644 resources/qml/Skeleton/ApplicationViews.qml create mode 100644 resources/qml/Skeleton/OrientationViews.qml diff --git a/plugins/Toolbox/resources/qml/ToolboxTabButton.qml b/plugins/Toolbox/resources/qml/ToolboxTabButton.qml index 22fb6d73ca..58149e419a 100644 --- a/plugins/Toolbox/resources/qml/ToolboxTabButton.qml +++ b/plugins/Toolbox/resources/qml/ToolboxTabButton.qml @@ -32,15 +32,15 @@ Button { if(control.hovered) { - return UM.Theme.getColor("topbar_button_text_hovered"); + return UM.Theme.getColor("toolbox_header_button_text_hovered"); } if(control.active) { - return UM.Theme.getColor("topbar_button_text_active"); + return UM.Theme.getColor("toolbox_header_button_text_active"); } else { - return UM.Theme.getColor("topbar_button_text_inactive"); + return UM.Theme.getColor("toolbox_header_button_text_inactive"); } } font: control.enabled ? (control.active ? UM.Theme.getFont("medium_bold") : UM.Theme.getFont("medium")) : UM.Theme.getFont("default_italic") diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 62ec4799c9..b7245b1b65 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -191,6 +191,18 @@ UM.MainWindow } } + ApplicationViews + { + id: applicationViews + + visible: UM.Controller.activeStage.stageId != "MonitorStage" + anchors + { + top: parent.top + right: sidebar.left + } + } + Loader { id: main diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 942dd81d9c..517b69428e 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -77,8 +77,8 @@ Rectangle UM.RecolorImage { id: buildplateIcon anchors.left: parent.left - width: UM.Theme.getSize("topbar_button_icon").width - height: UM.Theme.getSize("topbar_button_icon").height + width: UM.Theme.getSize("topheader_button_icon").width + height: UM.Theme.getSize("topheader_button_icon").height sourceSize.width: width sourceSize.height: height source: UM.Theme.getIcon("buildplate") diff --git a/resources/qml/Skeleton/ApplicationViews.qml b/resources/qml/Skeleton/ApplicationViews.qml new file mode 100644 index 0000000000..8e0b6efc0b --- /dev/null +++ b/resources/qml/Skeleton/ApplicationViews.qml @@ -0,0 +1,108 @@ +// 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.Controls.Styles 1.1 +import QtQuick.Layouts 1.1 + +import UM 1.4 as UM +import Cura 1.0 as Cura + +// This item contains the views selector, a combobox that is dinamically created from +// the list of available Views (packages that create different visualizactions of the +// scene. Aside the selector, there is a row of buttons that change the orientation of the view. +Item +{ + id: applicationViewsSelector + + height: UM.Theme.getSize("views_selector").height + + OrientationViews + { + id: orientationViews + + anchors { + verticalCenter: parent.verticalCenter + right: viewModeButton.left + rightMargin: UM.Theme.getSize("default_margin").width + } + } + + ComboBox + { + id: viewModeButton + + anchors { + verticalCenter: parent.verticalCenter + right: parent.right + rightMargin: UM.Theme.getSize("default_margin").width + } + + style: UM.Theme.styles.combobox + + model: UM.ViewModel { } + textRole: "name" + + // update the model's active index + function updateItemActiveFlags () + { + currentIndex = getActiveIndex() + for (var i = 0; i < model.rowCount(); i++) + { + model.getItem(i).active = (i == currentIndex) + } + } + + // get the index of the active model item on start + function getActiveIndex () + { + for (var i = 0; i < model.rowCount(); i++) + { + if (model.getItem(i).active) { + return i + } + } + return 0 + } + + // set the active index + function setActiveIndex (index) + { + UM.Controller.setActiveView(index) + // the connection to UM.ActiveView will trigger update so there is no reason to call it manually here + } + + onCurrentIndexChanged: + { + if (model.getItem(currentIndex).id != undefined) + { + viewModeButton.setActiveIndex(model.getItem(currentIndex).id) + } + } + currentIndex: getActiveIndex() + + // watch the active view proxy for changes made from the menu item + Connections + { + target: UM.ActiveView + onActiveViewChanged: viewModeButton.updateItemActiveFlags() + } + } + + Loader + { + id: viewPanel + + anchors.top: viewModeButton.bottom + anchors.topMargin: UM.Theme.getSize("default_margin").height + anchors.right: viewModeButton.right + + property var buttonTarget: Qt.point(viewModeButton.x + Math.round(viewModeButton.width / 2), viewModeButton.y + Math.round(viewModeButton.height / 2)) + + height: childrenRect.height + width: childrenRect.width + + source: UM.ActiveView.valid ? UM.ActiveView.activeViewPanel : "" + } +} diff --git a/resources/qml/Skeleton/OrientationViews.qml b/resources/qml/Skeleton/OrientationViews.qml new file mode 100644 index 0000000000..d1b370ed71 --- /dev/null +++ b/resources/qml/Skeleton/OrientationViews.qml @@ -0,0 +1,56 @@ +// 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.Controls.Styles 1.1 + +import UM 1.4 as UM + +// View orientation Item +Row +{ + id: viewOrientationControl + + spacing: 2 * screenScaleFactor + + // #1 3d view + Button + { + iconSource: UM.Theme.getIcon("view_3d") + style: UM.Theme.styles.small_tool_button + onClicked:UM.Controller.rotateView("3d", 0) + } + + // #2 Front view + Button + { + iconSource: UM.Theme.getIcon("view_front") + style: UM.Theme.styles.small_tool_button + onClicked: UM.Controller.rotateView("home", 0) + } + + // #3 Top view + Button + { + iconSource: UM.Theme.getIcon("view_top") + style: UM.Theme.styles.small_tool_button + onClicked: UM.Controller.rotateView("y", 90) + } + + // #4 Left view + Button + { + iconSource: UM.Theme.getIcon("view_left") + style: UM.Theme.styles.small_tool_button + onClicked: UM.Controller.rotateView("x", 90) + } + + // #5 Right view + Button + { + iconSource: UM.Theme.getIcon("view_right") + style: UM.Theme.styles.small_tool_button + onClicked: UM.Controller.rotateView("x", -90) + } +} diff --git a/resources/qml/Skeleton/TopHeader.qml b/resources/qml/Skeleton/TopHeader.qml index d27d9865b8..bdc9afd0cc 100644 --- a/resources/qml/Skeleton/TopHeader.qml +++ b/resources/qml/Skeleton/TopHeader.qml @@ -12,38 +12,9 @@ import Cura 1.0 as Cura Rectangle { id: base - anchors.left: parent.left - anchors.right: parent.right - height: UM.Theme.getSize("sidebar_header").height - color: UM.Controller.activeStage.stageId == "MonitorStage" ? UM.Theme.getColor("topbar_background_color_monitoring") : UM.Theme.getColor("topbar_background_color") - property bool printerConnected: Cura.MachineManager.printerConnected - property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands - - property int rightMargin: UM.Theme.getSize("sidebar").width + UM.Theme.getSize("default_margin").width; - property int allItemsWidth: 0; - - function updateMarginsAndSizes() { - if (UM.Preferences.getValue("cura/sidebar_collapsed")) - { - rightMargin = UM.Theme.getSize("default_margin").width; - } - else - { - rightMargin = UM.Theme.getSize("sidebar").width + UM.Theme.getSize("default_margin").width; - } - allItemsWidth = ( - logo.width + UM.Theme.getSize("topbar_logo_right_margin").width + - UM.Theme.getSize("topbar_logo_right_margin").width + stagesMenuContainer.width + - UM.Theme.getSize("default_margin").width + viewModeButton.width + - rightMargin); - } - - UM.I18nCatalog - { - id: catalog - name:"cura" - } + height: UM.Theme.getSize("topheader").height + color: UM.Theme.getColor("topheader_background") Image { @@ -52,36 +23,36 @@ Rectangle anchors.leftMargin: UM.Theme.getSize("default_margin").width anchors.verticalCenter: parent.verticalCenter - source: UM.Theme.getImage("logo"); - width: UM.Theme.getSize("logo").width; - height: UM.Theme.getSize("logo").height; + source: UM.Theme.getImage("logo") + width: UM.Theme.getSize("logo").width + height: UM.Theme.getSize("logo").height - sourceSize.width: width; - sourceSize.height: height; + sourceSize.width: width + sourceSize.height: height } Row { id: stagesMenuContainer anchors.left: logo.right - anchors.leftMargin: UM.Theme.getSize("topbar_logo_right_margin").width + anchors.leftMargin: UM.Theme.getSize("topheader_logo_right_margin").width spacing: UM.Theme.getSize("default_margin").width - // The topbar is dynamically filled with all available stages + // The topheader is dynamically filled with all available stages Repeater { id: stagesMenu - model: UM.StageModel{} + model: UM.StageModel { } delegate: Button { text: model.name checkable: true checked: model.active - exclusiveGroup: topbarMenuGroup - style: (model.stage.iconSource != "") ? UM.Theme.styles.topbar_header_tab_no_overlay : UM.Theme.styles.topbar_header_tab - height: UM.Theme.getSize("sidebar_header").height + exclusiveGroup: topheaderMenuGroup + style: UM.Theme.styles.topheader_tab + height: UM.Theme.getSize("topheader").height onClicked: UM.Controller.setActiveStage(model.id) iconSource: model.stage.iconSource @@ -90,157 +61,6 @@ Rectangle } } - ExclusiveGroup { id: topbarMenuGroup } + ExclusiveGroup { id: topheaderMenuGroup } } - - // View orientation Item - Row - { - id: viewOrientationControl - height: 30 - - spacing: 2 - visible: UM.Controller.activeStage.stageId != "MonitorStage" - - anchors - { - verticalCenter: base.verticalCenter - right: viewModeButton.left - rightMargin: UM.Theme.getSize("default_margin").width - } - - // #1 3d view - Button - { - iconSource: UM.Theme.getIcon("view_3d") - style: UM.Theme.styles.small_tool_button - anchors.verticalCenter: viewOrientationControl.verticalCenter - onClicked:UM.Controller.rotateView("3d", 0) - visible: base.width - allItemsWidth - 4 * this.width > 0 - } - - // #2 Front view - Button - { - iconSource: UM.Theme.getIcon("view_front") - style: UM.Theme.styles.small_tool_button - anchors.verticalCenter: viewOrientationControl.verticalCenter - onClicked: UM.Controller.rotateView("home", 0); - visible: base.width - allItemsWidth - 3 * this.width > 0 - } - - // #3 Top view - Button - { - iconSource: UM.Theme.getIcon("view_top") - style: UM.Theme.styles.small_tool_button - anchors.verticalCenter: viewOrientationControl.verticalCenter - onClicked: UM.Controller.rotateView("y", 90) - visible: base.width - allItemsWidth - 2 * this.width > 0 - } - - // #4 Left view - Button - { - iconSource: UM.Theme.getIcon("view_left") - style: UM.Theme.styles.small_tool_button - anchors.verticalCenter: viewOrientationControl.verticalCenter - onClicked: UM.Controller.rotateView("x", 90) - visible: base.width - allItemsWidth - 1 * this.width > 0 - } - - // #5 Right view - Button - { - iconSource: UM.Theme.getIcon("view_right") - style: UM.Theme.styles.small_tool_button - anchors.verticalCenter: viewOrientationControl.verticalCenter - onClicked: UM.Controller.rotateView("x", -90) - visible: base.width - allItemsWidth > 0 - } - } - - ComboBox - { - id: viewModeButton - - anchors { - verticalCenter: parent.verticalCenter - right: parent.right - rightMargin: rightMargin - } - - style: UM.Theme.styles.combobox - visible: UM.Controller.activeStage.stageId != "MonitorStage" - - model: UM.ViewModel { } - textRole: "name" - - // update the model's active index - function updateItemActiveFlags () { - currentIndex = getActiveIndex() - for (var i = 0; i < model.rowCount(); i++) { - model.getItem(i).active = (i == currentIndex) - } - } - - // get the index of the active model item on start - function getActiveIndex () { - for (var i = 0; i < model.rowCount(); i++) { - if (model.getItem(i).active) { - return i - } - } - return 0 - } - - // set the active index - function setActiveIndex (index) { - UM.Controller.setActiveView(index) - // the connection to UM.ActiveView will trigger update so there is no reason to call it manually here - } - - onCurrentIndexChanged: - { - if (model.getItem(currentIndex).id != undefined) - viewModeButton.setActiveIndex(model.getItem(currentIndex).id) - } - currentIndex: getActiveIndex() - - // watch the active view proxy for changes made from the menu item - Connections - { - target: UM.ActiveView - onActiveViewChanged: viewModeButton.updateItemActiveFlags() - } - } - - Loader - { - id: view_panel - - anchors.top: viewModeButton.bottom - anchors.topMargin: UM.Theme.getSize("default_margin").height - anchors.right: viewModeButton.right - - property var buttonTarget: Qt.point(viewModeButton.x + Math.round(viewModeButton.width / 2), viewModeButton.y + Math.round(viewModeButton.height / 2)) - - height: childrenRect.height - width: childrenRect.width - - source: UM.ActiveView.valid ? UM.ActiveView.activeViewPanel : ""; - } - - // Expand or collapse sidebar - Connections - { - target: Cura.Actions.expandSidebar - onTriggered: updateMarginsAndSizes() - } - - Component.onCompleted: - { - updateMarginsAndSizes(); - } - } diff --git a/resources/themes/cura-dark/theme.json b/resources/themes/cura-dark/theme.json index 26e6c2ac8b..fa0da2851e 100644 --- a/resources/themes/cura-dark/theme.json +++ b/resources/themes/cura-dark/theme.json @@ -15,12 +15,11 @@ "border": [127, 127, 127, 255], "secondary": [241, 242, 242, 255], - "topbar_background_color": [0, 0, 0, 0], - "topbar_background_color_monitoring": [31, 36, 39, 255], + "topheader_background": [31, 36, 39, 255], - "topbar_button_text_active": [255, 255, 255, 255], - "topbar_button_text_inactive": [128, 128, 128, 255], - "topbar_button_text_hovered": [255, 255, 255, 255], + "topheader_button_text_active": [255, 255, 255, 255], + "topheader_button_text_inactive": [128, 128, 128, 255], + "topheader_button_text_hovered": [255, 255, 255, 255], "text": [255, 255, 255, 204], "text_detail": [255, 255, 255, 172], @@ -221,6 +220,10 @@ "quality_slider_available": [255, 255, 255, 255], "quality_slider_handle": [255, 255, 255, 255], "quality_slider_handle_hover": [127, 127, 127, 255], - "quality_slider_text": [255, 255, 255, 255] + "quality_slider_text": [255, 255, 255, 255], + + "toolbox_header_button_text_active": [255, 255, 255, 255], + "toolbox_header_button_text_inactive": [128, 128, 128, 255], + "toolbox_header_button_text_hovered": [255, 255, 255, 255] } } diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index b71ddd2d86..b54d12e07b 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -90,85 +90,11 @@ QtObject { } } - property Component topbar_header_tab_no_overlay: Component { - ButtonStyle { - background: Rectangle { - implicitHeight: Theme.getSize("topbar_button").height - implicitWidth: Theme.getSize("topbar_button").width - color: "transparent" - anchors.fill: parent - - Rectangle - { - id: underline - - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - width: parent.width - height: Theme.getSize("sidebar_header_highlight").height - color: control.checked ? UM.Theme.getColor("sidebar_header_highlight") : UM.Theme.getColor("sidebar_header_highlight_hover") - visible: control.hovered || control.checked - } - } - - label: Rectangle { - implicitHeight: Theme.getSize("topbar_button_icon").height - implicitWidth: Theme.getSize("topbar_button").width - color: "transparent" - anchors.fill: parent - - Item - { - anchors.centerIn: parent - width: Math.round(textLabel.width + icon.width + Theme.getSize("default_margin").width / 2) - Label - { - id: textLabel - text: control.text - anchors.right: icon.visible ? icon.left : parent.right - anchors.rightMargin: icon.visible ? Math.round(Theme.getSize("default_margin").width / 2) : 0 - anchors.verticalCenter: parent.verticalCenter; - font: control.checked ? UM.Theme.getFont("large") : UM.Theme.getFont("large_nonbold") - color: - { - if(control.hovered) - { - return UM.Theme.getColor("topbar_button_text_hovered"); - } - if(control.checked) - { - return UM.Theme.getColor("topbar_button_text_active"); - } - else - { - return UM.Theme.getColor("topbar_button_text_inactive"); - } - } - } - Image - { - id: icon - visible: control.iconSource != "" - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - opacity: !control.enabled ? 0.2 : 1.0 - source: control.iconSource - width: visible ? Theme.getSize("topbar_button_icon").width : 0 - height: Theme.getSize("topbar_button_icon").height - - sourceSize: Theme.getSize("topbar_button_icon") - } - } - } - } - } - - property Component topbar_header_tab: Component { + property Component topheader_tab: Component { ButtonStyle { background: Item { - implicitHeight: Theme.getSize("topbar_button").height - implicitWidth: Theme.getSize("topbar_button").width + Theme.getSize("topbar_button_icon").width + implicitHeight: Theme.getSize("topheader_button").height + implicitWidth: Theme.getSize("topheader_button").width + Theme.getSize("topheader_button_icon").width Rectangle { id: buttonFace; @@ -182,7 +108,7 @@ QtObject { anchors.horizontalCenter: parent.horizontalCenter anchors.bottom: parent.bottom - width: Theme.getSize("topbar_button").width + Theme.getSize("topbar_button_icon").width + width: Theme.getSize("topheader_button").width + Theme.getSize("topheader_button_icon").width height: Theme.getSize("sidebar_header_highlight").height color: control.checked ? UM.Theme.getColor("sidebar_header_highlight") : UM.Theme.getColor("sidebar_header_highlight_hover") visible: control.hovered || control.checked @@ -192,14 +118,14 @@ QtObject { label: Item { - implicitHeight: Theme.getSize("topbar_button_icon").height - implicitWidth: Theme.getSize("topbar_button").width + Theme.getSize("topbar_button_icon").width + implicitHeight: Theme.getSize("topheader_button_icon").height + implicitWidth: Theme.getSize("topheader_button").width + Theme.getSize("topheader_button_icon").width Item { anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter; width: childrenRect.width - height: Theme.getSize("topbar_button_icon").height + height: Theme.getSize("topheader_button_icon").height Label { id: button_label @@ -210,15 +136,15 @@ QtObject { { if(control.hovered) { - return UM.Theme.getColor("topbar_button_text_hovered"); + return UM.Theme.getColor("topheader_button_text_hovered"); } if(control.checked) { - return UM.Theme.getColor("topbar_button_text_active"); + return UM.Theme.getColor("topheader_button_text_active"); } else { - return UM.Theme.getColor("topbar_button_text_inactive"); + return UM.Theme.getColor("topheader_button_text_inactive"); } } } @@ -231,10 +157,10 @@ QtObject { color: UM.Theme.getColor("text_emphasis") opacity: !control.enabled ? 0.2 : 1.0 source: control.iconSource - width: visible ? Theme.getSize("topbar_button_icon").width : 0 - height: Theme.getSize("topbar_button_icon").height + width: visible ? Theme.getSize("topheader_button_icon").width : 0 + height: Theme.getSize("topheader_button_icon").height - sourceSize: Theme.getSize("topbar_button_icon") + sourceSize: Theme.getSize("topheader_button_icon") } UM.RecolorImage { @@ -245,10 +171,10 @@ QtObject { color: control.overlayColor opacity: !control.enabled ? 0.2 : 1.0 source: control.overlayIconSource - width: visible ? Theme.getSize("topbar_button_icon").width : 0 - height: Theme.getSize("topbar_button_icon").height + width: visible ? Theme.getSize("topheader_button_icon").width : 0 + height: Theme.getSize("topheader_button_icon").height - sourceSize: Theme.getSize("topbar_button_icon") + sourceSize: Theme.getSize("topheader_button_icon") } } } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 43d892c34c..74644c4de9 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -79,12 +79,11 @@ "border": [127, 127, 127, 255], "secondary": [245, 245, 245, 255], - "topbar_background_color": [255, 255, 255, 0], - "topbar_background_color_monitoring": [255, 255, 255, 255], + "topheader_background": [255, 255, 255, 255], - "topbar_button_text_active": [0, 0, 0, 255], - "topbar_button_text_inactive": [128, 128, 128, 255], - "topbar_button_text_hovered": [0, 0, 0, 255], + "topheader_button_text_active": [0, 0, 0, 255], + "topheader_button_text_inactive": [128, 128, 128, 255], + "topheader_button_text_hovered": [0, 0, 0, 255], "text": [0, 0, 0, 255], "text_detail": [174, 174, 174, 128], @@ -317,6 +316,10 @@ "printer_config_matched": [12, 169, 227, 255], "printer_config_mismatch": [127, 127, 127, 255], + "toolbox_header_button_text_active": [0, 0, 0, 255], + "toolbox_header_button_text_inactive": [128, 128, 128, 255], + "toolbox_header_button_text_hovered": [0, 0, 0, 255], + "favorites_header_bar": [245, 245, 245, 255], "favorites_header_hover": [245, 245, 245, 255], "favorites_header_text": [31, 36, 39, 255], @@ -332,6 +335,13 @@ "sizes": { "window_minimum_size": [70, 50], + "topheader": [0.0, 4.0], + "topheader_logo_right_margin": [3, 0], + "topheader_button": [8, 4], + "topheader_button_icon": [1.2, 1.2], + + "views_selector": [0.0, 4.0], + "default_lining": [0.08, 0.08], "default_arrow": [0.8, 0.8], "logo": [7.6, 1.6], @@ -390,10 +400,6 @@ "printer_status_icon": [1.8, 1.8], "printer_sync_icon": [1.2, 1.2], - "topbar_logo_right_margin": [3, 0], - "topbar_button": [8, 4], - "topbar_button_icon": [1.2, 1.2], - "button_tooltip": [1.0, 1.3], "button_tooltip_arrow": [0.25, 0.25], From 41add97b135482b19b23246793d52d32b992a7d0 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 8 Oct 2018 14:50:13 +0200 Subject: [PATCH 0005/1240] Add Toolbox shortcut to the TopHeader and align the stage tabs to the center. Contributes to CURA-5784. --- resources/qml/Cura.qml | 13 ----------- resources/qml/Skeleton/ApplicationMenu.qml | 27 +++++++++++++++++++--- resources/qml/Skeleton/TopHeader.qml | 26 +++++++++++++++++---- resources/themes/cura-light/theme.json | 1 - 4 files changed, 46 insertions(+), 21 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index b7245b1b65..445bfb6ec9 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -422,19 +422,6 @@ UM.MainWindow } } - UM.ExtensionModel { - id: curaExtensions - } - - // show the plugin browser dialog - Connections - { - target: Cura.Actions.browsePackages - onTriggered: { - curaExtensions.callExtensionMethod("Toolbox", "browsePackages") - } - } - Timer { id: createProfileTimer diff --git a/resources/qml/Skeleton/ApplicationMenu.qml b/resources/qml/Skeleton/ApplicationMenu.qml index 97d8f7ca7e..a8774e8ceb 100644 --- a/resources/qml/Skeleton/ApplicationMenu.qml +++ b/resources/qml/Skeleton/ApplicationMenu.qml @@ -180,7 +180,7 @@ Item Menu { - id: extension_menu + id: extensionMenu title: catalog.i18nc("@title:menu menubar:toplevel","E&xtensions") Instantiator @@ -207,8 +207,8 @@ Item } } - onObjectAdded: extension_menu.insertItem(index, object) - onObjectRemoved: extension_menu.removeItem(object) + onObjectAdded: extensionMenu.insertItem(index, object) + onObjectRemoved: extensionMenu.removeItem(object) } } @@ -241,6 +241,10 @@ Item } } + // ############################################################################################### + // Definition of other components that are linked to the menus + // ############################################################################################### + WorkspaceSummaryDialog { id: saveWorkspaceDialog @@ -263,6 +267,14 @@ Item } } + UM.ExtensionModel { + id: curaExtensions + } + + // ############################################################################################### + // Definition of all the connections + // ############################################################################################### + Connections { target: Cura.Actions.newProject @@ -274,4 +286,13 @@ Item } } } + + // show the plugin browser dialog + Connections + { + target: Cura.Actions.browsePackages + onTriggered: { + curaExtensions.callExtensionMethod("Toolbox", "browsePackages") + } + } } \ No newline at end of file diff --git a/resources/qml/Skeleton/TopHeader.qml b/resources/qml/Skeleton/TopHeader.qml index bdc9afd0cc..bf92fbb41d 100644 --- a/resources/qml/Skeleton/TopHeader.qml +++ b/resources/qml/Skeleton/TopHeader.qml @@ -33,15 +33,19 @@ Rectangle Row { - id: stagesMenuContainer - anchors.left: logo.right - anchors.leftMargin: UM.Theme.getSize("topheader_logo_right_margin").width + id: stagesListContainer + + anchors + { + horizontalCenter: parent.horizontalCenter + leftMargin: UM.Theme.getSize("default_margin").width + } spacing: UM.Theme.getSize("default_margin").width // The topheader is dynamically filled with all available stages Repeater { - id: stagesMenu + id: stagesHeader model: UM.StageModel { } @@ -63,4 +67,18 @@ Rectangle ExclusiveGroup { id: topheaderMenuGroup } } + + // Shortcut button to quick access the Toolbox + Button + { + id: toolboxShortcutButton + text: catalog.i18nc("@action:button", "Toolbox") + anchors + { + right: parent.right + leftMargin: UM.Theme.getSize("default_margin").width + } + style: UM.Theme.styles.topheader_tab + action: Cura.Actions.browsePackages + } } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 74644c4de9..f5372bbb0f 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -336,7 +336,6 @@ "window_minimum_size": [70, 50], "topheader": [0.0, 4.0], - "topheader_logo_right_margin": [3, 0], "topheader_button": [8, 4], "topheader_button_icon": [1.2, 1.2], From 2ffcf03f43e7c68a57471123a2a221be13d964d1 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 8 Oct 2018 17:35:24 +0200 Subject: [PATCH 0006/1240] Create an AvatarImage component that will show the profile image of the user. Still WIP. Create an AccountWidget (WIP) that shows the avatar image and the dropdown menu to manage accounts. Contributes to CURA-5784. --- resources/qml/Skeleton/ApplicationViews.qml | 2 + resources/qml/Skeleton/TopHeader.qml | 17 ++++- .../qml/Skeleton/components/AccountWidget.qml | 71 ++++++++++++++++++ .../qml/Skeleton/components/AvatarImage.qml | 42 +++++++++++ .../{ => components}/OrientationViews.qml | 0 .../themes/cura-light/icons/circle_mask.svg | 64 ++++++++++++++++ .../cura-light/images/avatar_default.png | Bin 0 -> 3115 bytes 7 files changed, 193 insertions(+), 3 deletions(-) create mode 100644 resources/qml/Skeleton/components/AccountWidget.qml create mode 100644 resources/qml/Skeleton/components/AvatarImage.qml rename resources/qml/Skeleton/{ => components}/OrientationViews.qml (100%) create mode 100644 resources/themes/cura-light/icons/circle_mask.svg create mode 100644 resources/themes/cura-light/images/avatar_default.png diff --git a/resources/qml/Skeleton/ApplicationViews.qml b/resources/qml/Skeleton/ApplicationViews.qml index 8e0b6efc0b..25c2ff9983 100644 --- a/resources/qml/Skeleton/ApplicationViews.qml +++ b/resources/qml/Skeleton/ApplicationViews.qml @@ -9,6 +9,8 @@ import QtQuick.Layouts 1.1 import UM 1.4 as UM import Cura 1.0 as Cura +import "components" + // This item contains the views selector, a combobox that is dinamically created from // the list of available Views (packages that create different visualizactions of the // scene. Aside the selector, there is a row of buttons that change the orientation of the view. diff --git a/resources/qml/Skeleton/TopHeader.qml b/resources/qml/Skeleton/TopHeader.qml index bf92fbb41d..c2e03aa04b 100644 --- a/resources/qml/Skeleton/TopHeader.qml +++ b/resources/qml/Skeleton/TopHeader.qml @@ -4,11 +4,12 @@ import QtQuick 2.2 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.1 -import QtQuick.Layouts 1.1 import UM 1.4 as UM import Cura 1.0 as Cura +import "components" + Rectangle { id: base @@ -75,10 +76,20 @@ Rectangle text: catalog.i18nc("@action:button", "Toolbox") anchors { - right: parent.right - leftMargin: UM.Theme.getSize("default_margin").width + right: accountWidget.left + rightMargin: UM.Theme.getSize("default_margin").width } style: UM.Theme.styles.topheader_tab action: Cura.Actions.browsePackages } + + AccountWidget + { + id: accountWidget + anchors + { + right: parent.right + rightMargin: UM.Theme.getSize("default_margin").width + } + } } diff --git a/resources/qml/Skeleton/components/AccountWidget.qml b/resources/qml/Skeleton/components/AccountWidget.qml new file mode 100644 index 0000000000..3d33aeaf27 --- /dev/null +++ b/resources/qml/Skeleton/components/AccountWidget.qml @@ -0,0 +1,71 @@ +// 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 UM 1.4 as UM + +Item +{ + id: base + height: UM.Theme.getSize("topheader").height + width: UM.Theme.getSize("topheader").height + + AvatarImage + { + id: avatar + width: Math.round(0.8 * parent.width) + height: Math.round(0.8 * parent.height) + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + } + + MouseArea + { + anchors.fill: avatar + onClicked: + { + // Collapse/Expand the dropdown panel + accountManagementPanel.visible = !accountManagementPanel.visible + } + } + + UM.PointingRectangle + { + id: accountManagementPanel + + width: 250 + height: 250 + + anchors + { + top: parent.bottom + topMargin: UM.Theme.getSize("default_margin").height + right: parent.right + } + + target: Qt.point(parent.width / 2, parent.bottom) + arrowSize: UM.Theme.getSize("default_arrow").width + + visible: false + opacity: visible ? 1 : 0 + Behavior on opacity { NumberAnimation { duration: 100 } } + + color: UM.Theme.getColor("tool_panel_background") + borderColor: UM.Theme.getColor("lining") + borderWidth: UM.Theme.getSize("default_lining").width + + Loader + { + id: panel + sourceComponent: login + } + } + + Component + { + id: login + Label {text: "HOLA"} + } +} \ No newline at end of file diff --git a/resources/qml/Skeleton/components/AvatarImage.qml b/resources/qml/Skeleton/components/AvatarImage.qml new file mode 100644 index 0000000000..52e2757cbf --- /dev/null +++ b/resources/qml/Skeleton/components/AvatarImage.qml @@ -0,0 +1,42 @@ +// Copyright (c) 2018 Ultimaker B.V. +import QtQuick 2.7 +import QtQuick.Controls 2.1 +import QtGraphicalEffects 1.0 + +import UM 1.4 as UM + +Item +{ + id: avatar + + Image + { + id: profileImage + source: UM.Theme.getImage("avatar_default") + sourceSize: Qt.size(parent.width, parent.height) + width: parent.width + height: parent.height + fillMode: Image.PreserveAspectCrop + visible: false + } + + UM.RecolorImage + { + id: profileImageMask + source: UM.Theme.getIcon("circle_mask") + sourceSize: Qt.size(parent.width, parent.height) + width: parent.width + height: parent.height + color: UM.Theme.getColor("topheader_background") + visible: false + } + + OpacityMask + { + anchors.fill: profileImage + source: profileImage + maskSource: profileImageMask + cached: true + invert: false + } +} \ No newline at end of file diff --git a/resources/qml/Skeleton/OrientationViews.qml b/resources/qml/Skeleton/components/OrientationViews.qml similarity index 100% rename from resources/qml/Skeleton/OrientationViews.qml rename to resources/qml/Skeleton/components/OrientationViews.qml diff --git a/resources/themes/cura-light/icons/circle_mask.svg b/resources/themes/cura-light/icons/circle_mask.svg new file mode 100644 index 0000000000..d5bfe53a97 --- /dev/null +++ b/resources/themes/cura-light/icons/circle_mask.svg @@ -0,0 +1,64 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/resources/themes/cura-light/images/avatar_default.png b/resources/themes/cura-light/images/avatar_default.png new file mode 100644 index 0000000000000000000000000000000000000000..0c306680f721dba0de73fb7be90fe5c548608692 GIT binary patch literal 3115 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 Date: Tue, 9 Oct 2018 15:58:13 +0200 Subject: [PATCH 0007/1240] Add the callout panel to the account panel, with the two components that will show up when the user is logged in or when not. Contributes to CURA-5784. --- .../components => Accounts}/AccountWidget.qml | 32 ++++++---- .../components => Accounts}/AvatarImage.qml | 4 +- .../qml/Accounts/GeneralManagementWidget.qml | 23 +++++++ .../qml/Accounts/UserManagementWidget.qml | 33 ++++++++++ resources/qml/Cura.qml | 1 + resources/qml/Skeleton/ApplicationViews.qml | 2 +- resources/qml/Skeleton/TopHeader.qml | 2 +- resources/qml/components/ActionButton.qml | 61 +++++++++++++++++++ .../components/OrientationViews.qml | 0 9 files changed, 143 insertions(+), 15 deletions(-) rename resources/qml/{Skeleton/components => Accounts}/AccountWidget.qml (60%) rename resources/qml/{Skeleton/components => Accounts}/AvatarImage.qml (88%) create mode 100644 resources/qml/Accounts/GeneralManagementWidget.qml create mode 100644 resources/qml/Accounts/UserManagementWidget.qml create mode 100644 resources/qml/components/ActionButton.qml rename resources/qml/{Skeleton => }/components/OrientationViews.qml (100%) diff --git a/resources/qml/Skeleton/components/AccountWidget.qml b/resources/qml/Accounts/AccountWidget.qml similarity index 60% rename from resources/qml/Skeleton/components/AccountWidget.qml rename to resources/qml/Accounts/AccountWidget.qml index 3d33aeaf27..89f9acd8cc 100644 --- a/resources/qml/Skeleton/components/AccountWidget.qml +++ b/resources/qml/Accounts/AccountWidget.qml @@ -5,10 +5,14 @@ import QtQuick 2.2 import QtQuick.Controls 1.1 import UM 1.4 as UM +import Cura 1.1 as Cura Item { - id: base + id: accountWidget + property var profile: Cura.API.account.userProfile + property var loggedIn: Cura.API.account.isLoggedIn + property var logoutCallback: Cura.API.account.logout height: UM.Theme.getSize("topheader").height width: UM.Theme.getSize("topheader").height @@ -19,24 +23,21 @@ Item height: Math.round(0.8 * parent.height) anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter + source: loggedIn ? profile["profile_image_url"] : UM.Theme.getImage("avatar_default") } MouseArea { - anchors.fill: avatar - onClicked: - { - // Collapse/Expand the dropdown panel - accountManagementPanel.visible = !accountManagementPanel.visible - } + anchors.fill: parent + onClicked: accountManagementPanel.visible = !accountManagementPanel.visible // Collapse/Expand the dropdown panel } UM.PointingRectangle { id: accountManagementPanel - width: 250 - height: 250 + width: panel.width + height: panel.height anchors { @@ -56,16 +57,23 @@ Item borderColor: UM.Theme.getColor("lining") borderWidth: UM.Theme.getSize("default_lining").width + // Shows the user management options or general options to create account Loader { id: panel - sourceComponent: login + sourceComponent: loggedIn ? userManagementWidget : generalManagementWidget } } Component { - id: login - Label {text: "HOLA"} + id: userManagementWidget + UserManagementWidget { } + } + + Component + { + id: generalManagementWidget + GeneralManagementWidget { } } } \ No newline at end of file diff --git a/resources/qml/Skeleton/components/AvatarImage.qml b/resources/qml/Accounts/AvatarImage.qml similarity index 88% rename from resources/qml/Skeleton/components/AvatarImage.qml rename to resources/qml/Accounts/AvatarImage.qml index 52e2757cbf..18ac19a45d 100644 --- a/resources/qml/Skeleton/components/AvatarImage.qml +++ b/resources/qml/Accounts/AvatarImage.qml @@ -9,10 +9,12 @@ Item { id: avatar + property var source + Image { id: profileImage - source: UM.Theme.getImage("avatar_default") + source: avatar.source ? avatar.source : UM.Theme.getImage("avatar_default") sourceSize: Qt.size(parent.width, parent.height) width: parent.width height: parent.height diff --git a/resources/qml/Accounts/GeneralManagementWidget.qml b/resources/qml/Accounts/GeneralManagementWidget.qml new file mode 100644 index 0000000000..2bb0c89f98 --- /dev/null +++ b/resources/qml/Accounts/GeneralManagementWidget.qml @@ -0,0 +1,23 @@ +// 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 UM 1.4 as UM +import Cura 1.1 as Cura + +import "../components" + +Column +{ + ActionButton + { + text: catalog.i18nc("@button", "Sign In") + color: "transparent" + hoverColor: "transparent" + textColor: UM.Theme.getColor("text") + textHoverColor: UM.Theme.getColor("text_link") + onClicked: Cura.API.account.login() + } +} \ No newline at end of file diff --git a/resources/qml/Accounts/UserManagementWidget.qml b/resources/qml/Accounts/UserManagementWidget.qml new file mode 100644 index 0000000000..24f4966679 --- /dev/null +++ b/resources/qml/Accounts/UserManagementWidget.qml @@ -0,0 +1,33 @@ +// 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 UM 1.4 as UM +import Cura 1.1 as Cura + +import "../components" + +Column +{ + ActionButton + { + text: catalog.i18nc("@button", "Manage Profile") + color: "transparent" + hoverColor: "transparent" + textColor: UM.Theme.getColor("text") + textHoverColor: UM.Theme.getColor("text_link") + onClicked: Qt.openUrlExternally("https://account.ultimaker.com") + } + + ActionButton + { + text: catalog.i18nc("@button", "Sign Out") + color: "transparent" + hoverColor: "transparent" + textColor: UM.Theme.getColor("text") + textHoverColor: UM.Theme.getColor("text_link") + onClicked: Cura.API.account.logout() + } +} \ No newline at end of file diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 445bfb6ec9..16bae8aa3b 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -105,6 +105,7 @@ UM.MainWindow width: parent.width height: parent.height - menu.height - topHeader.height + z: topHeader.z - 1 Keys.forwardTo: menu diff --git a/resources/qml/Skeleton/ApplicationViews.qml b/resources/qml/Skeleton/ApplicationViews.qml index 25c2ff9983..b3d08ab627 100644 --- a/resources/qml/Skeleton/ApplicationViews.qml +++ b/resources/qml/Skeleton/ApplicationViews.qml @@ -9,7 +9,7 @@ import QtQuick.Layouts 1.1 import UM 1.4 as UM import Cura 1.0 as Cura -import "components" +import "../components" // This item contains the views selector, a combobox that is dinamically created from // the list of available Views (packages that create different visualizactions of the diff --git a/resources/qml/Skeleton/TopHeader.qml b/resources/qml/Skeleton/TopHeader.qml index c2e03aa04b..fedb00898f 100644 --- a/resources/qml/Skeleton/TopHeader.qml +++ b/resources/qml/Skeleton/TopHeader.qml @@ -8,7 +8,7 @@ import QtQuick.Controls.Styles 1.1 import UM 1.4 as UM import Cura 1.0 as Cura -import "components" +import "../Accounts" Rectangle { diff --git a/resources/qml/components/ActionButton.qml b/resources/qml/components/ActionButton.qml new file mode 100644 index 0000000000..02c279a458 --- /dev/null +++ b/resources/qml/components/ActionButton.qml @@ -0,0 +1,61 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 2.1 +import QtQuick.Layouts 1.3 + +import UM 1.1 as UM + +Button +{ + id: button + property alias cursorShape: mouseArea.cursorShape + property var iconSource: "" + property var color: UM.Theme.getColor("primary") + property var hoverColor: UM.Theme.getColor("primary_hover") + property var disabledColor: color + property var textColor: UM.Theme.getColor("button_text") + property var textHoverColor: UM.Theme.getColor("button_text_hover") + property var textDisabledColor: textColor + property var textFont: UM.Theme.getFont("action_button") + + contentItem: Row + { + UM.RecolorImage + { + id: buttonIcon + source: button.iconSource + width: 16 * screenScaleFactor + height: 16 * screenScaleFactor + sourceSize.width: width + sourceSize.height: height + color: button.hovered ? button.textHoverColor : button.textColor + visible: button.iconSource != "" + } + + Label + { + id: buttonText + text: button.text + color: button.enabled ? (button.hovered ? button.textHoverColor : button.textColor): button.textDisabledColor + font: button.textFont + visible: button.text != "" + renderType: Text.NativeRendering + } + } + + background: Rectangle + { + color: button.enabled ? (button.hovered ? button.hoverColor : button.color) : button.disabledColor + } + + MouseArea + { + id: mouseArea + anchors.fill: parent + onPressed: mouse.accepted = false + hoverEnabled: true + cursorShape: button.enabled ? (hovered ? Qt.PointingHandCursor : Qt.ArrowCursor) : Qt.ForbiddenCursor + } +} diff --git a/resources/qml/Skeleton/components/OrientationViews.qml b/resources/qml/components/OrientationViews.qml similarity index 100% rename from resources/qml/Skeleton/components/OrientationViews.qml rename to resources/qml/components/OrientationViews.qml From 49e96980f124381410b3e6f34cd1c9c1737178d0 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 9 Oct 2018 17:11:01 +0200 Subject: [PATCH 0008/1240] Store the dialogs in a folder called Dialogs. Contributes to CURA-5784. --- resources/qml/{Accounts => Account}/AccountWidget.qml | 0 resources/qml/{Accounts => Account}/AvatarImage.qml | 0 resources/qml/{Accounts => Account}/GeneralManagementWidget.qml | 0 resources/qml/{Accounts => Account}/UserManagementWidget.qml | 0 resources/qml/Cura.qml | 1 + resources/qml/{ => Dialogs}/AboutDialog.qml | 0 resources/qml/{ => Dialogs}/AddMachineDialog.qml | 0 resources/qml/{ => Dialogs}/AskOpenAsProjectOrModelsDialog.qml | 0 .../qml/{ => Dialogs}/DiscardOrKeepProfileChangesDialog.qml | 0 .../qml/{ => Dialogs}/OpenFilesIncludingProjectsDialog.qml | 0 resources/qml/Menus/RecentFilesMenu.qml | 2 ++ resources/qml/Skeleton/TopHeader.qml | 2 +- 12 files changed, 4 insertions(+), 1 deletion(-) rename resources/qml/{Accounts => Account}/AccountWidget.qml (100%) rename resources/qml/{Accounts => Account}/AvatarImage.qml (100%) rename resources/qml/{Accounts => Account}/GeneralManagementWidget.qml (100%) rename resources/qml/{Accounts => Account}/UserManagementWidget.qml (100%) rename resources/qml/{ => Dialogs}/AboutDialog.qml (100%) rename resources/qml/{ => Dialogs}/AddMachineDialog.qml (100%) rename resources/qml/{ => Dialogs}/AskOpenAsProjectOrModelsDialog.qml (100%) rename resources/qml/{ => Dialogs}/DiscardOrKeepProfileChangesDialog.qml (100%) rename resources/qml/{ => Dialogs}/OpenFilesIncludingProjectsDialog.qml (100%) diff --git a/resources/qml/Accounts/AccountWidget.qml b/resources/qml/Account/AccountWidget.qml similarity index 100% rename from resources/qml/Accounts/AccountWidget.qml rename to resources/qml/Account/AccountWidget.qml diff --git a/resources/qml/Accounts/AvatarImage.qml b/resources/qml/Account/AvatarImage.qml similarity index 100% rename from resources/qml/Accounts/AvatarImage.qml rename to resources/qml/Account/AvatarImage.qml diff --git a/resources/qml/Accounts/GeneralManagementWidget.qml b/resources/qml/Account/GeneralManagementWidget.qml similarity index 100% rename from resources/qml/Accounts/GeneralManagementWidget.qml rename to resources/qml/Account/GeneralManagementWidget.qml diff --git a/resources/qml/Accounts/UserManagementWidget.qml b/resources/qml/Account/UserManagementWidget.qml similarity index 100% rename from resources/qml/Accounts/UserManagementWidget.qml rename to resources/qml/Account/UserManagementWidget.qml diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 16bae8aa3b..2af085fee7 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -10,6 +10,7 @@ import QtQuick.Dialogs 1.2 import UM 1.3 as UM import Cura 1.1 as Cura +import "Dialogs" import "Menus" import "Skeleton" diff --git a/resources/qml/AboutDialog.qml b/resources/qml/Dialogs/AboutDialog.qml similarity index 100% rename from resources/qml/AboutDialog.qml rename to resources/qml/Dialogs/AboutDialog.qml diff --git a/resources/qml/AddMachineDialog.qml b/resources/qml/Dialogs/AddMachineDialog.qml similarity index 100% rename from resources/qml/AddMachineDialog.qml rename to resources/qml/Dialogs/AddMachineDialog.qml diff --git a/resources/qml/AskOpenAsProjectOrModelsDialog.qml b/resources/qml/Dialogs/AskOpenAsProjectOrModelsDialog.qml similarity index 100% rename from resources/qml/AskOpenAsProjectOrModelsDialog.qml rename to resources/qml/Dialogs/AskOpenAsProjectOrModelsDialog.qml diff --git a/resources/qml/DiscardOrKeepProfileChangesDialog.qml b/resources/qml/Dialogs/DiscardOrKeepProfileChangesDialog.qml similarity index 100% rename from resources/qml/DiscardOrKeepProfileChangesDialog.qml rename to resources/qml/Dialogs/DiscardOrKeepProfileChangesDialog.qml diff --git a/resources/qml/OpenFilesIncludingProjectsDialog.qml b/resources/qml/Dialogs/OpenFilesIncludingProjectsDialog.qml similarity index 100% rename from resources/qml/OpenFilesIncludingProjectsDialog.qml rename to resources/qml/Dialogs/OpenFilesIncludingProjectsDialog.qml diff --git a/resources/qml/Menus/RecentFilesMenu.qml b/resources/qml/Menus/RecentFilesMenu.qml index 12f53fb517..0f1f67b6fa 100644 --- a/resources/qml/Menus/RecentFilesMenu.qml +++ b/resources/qml/Menus/RecentFilesMenu.qml @@ -7,6 +7,8 @@ import QtQuick.Controls 1.1 import UM 1.3 as UM import Cura 1.0 as Cura +import "../Dialogs" + Menu { id: menu diff --git a/resources/qml/Skeleton/TopHeader.qml b/resources/qml/Skeleton/TopHeader.qml index fedb00898f..5013c12703 100644 --- a/resources/qml/Skeleton/TopHeader.qml +++ b/resources/qml/Skeleton/TopHeader.qml @@ -8,7 +8,7 @@ import QtQuick.Controls.Styles 1.1 import UM 1.4 as UM import Cura 1.0 as Cura -import "../Accounts" +import "../Account" Rectangle { From 0df7fa1a6cb4e2ac0284fb9300c3025a692bab0a Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 10 Oct 2018 11:04:08 +0200 Subject: [PATCH 0009/1240] Add style to the top header tabs. Contributes to CURA-5784. --- resources/qml/Skeleton/TopHeader.qml | 3 +- resources/themes/cura-light/images/logo.svg | 200 +++++++++++++++----- resources/themes/cura-light/styles.qml | 109 ++++------- resources/themes/cura-light/theme.json | 12 +- 4 files changed, 195 insertions(+), 129 deletions(-) diff --git a/resources/qml/Skeleton/TopHeader.qml b/resources/qml/Skeleton/TopHeader.qml index 5013c12703..8cb568df4d 100644 --- a/resources/qml/Skeleton/TopHeader.qml +++ b/resources/qml/Skeleton/TopHeader.qml @@ -41,7 +41,6 @@ Rectangle horizontalCenter: parent.horizontalCenter leftMargin: UM.Theme.getSize("default_margin").width } - spacing: UM.Theme.getSize("default_margin").width // The topheader is dynamically filled with all available stages Repeater @@ -52,7 +51,7 @@ Rectangle delegate: Button { - text: model.name + text: model.name.toUpperCase() checkable: true checked: model.active exclusiveGroup: topheaderMenuGroup diff --git a/resources/themes/cura-light/images/logo.svg b/resources/themes/cura-light/images/logo.svg index 5fa5895443..89692f8295 100644 --- a/resources/themes/cura-light/images/logo.svg +++ b/resources/themes/cura-light/images/logo.svg @@ -1,4 +1,6 @@ + + + id="svg8" + inkscape:version="0.92.2 (5c3e80d, 2017-08-06)" + sodipodi:docname="logo.svg"> + + + id="metadata5"> @@ -26,47 +49,124 @@ - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index b54d12e07b..ba0350fd4b 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -90,92 +90,57 @@ QtObject { } } - property Component topheader_tab: Component { - ButtonStyle { - background: Item { - implicitHeight: Theme.getSize("topheader_button").height - implicitWidth: Theme.getSize("topheader_button").width + Theme.getSize("topheader_button_icon").width + property Component topheader_tab: Component + { + ButtonStyle + { + // This property will be back-propagated when the width of the label is calculated + property var buttonWidth: 0 - Rectangle { - id: buttonFace; - anchors.fill: parent; + background: Rectangle + { + id: buttonFace + implicitHeight: control.height + implicitWidth: buttonWidth + color: control.checked ? UM.Theme.getColor("topheader_button_background_active") : UM.Theme.getColor("topheader_button_background_inactive") - color: "transparent" - Behavior on color { ColorAnimation { duration: 50; } } - - Rectangle { - id: underline; - - anchors.horizontalCenter: parent.horizontalCenter - anchors.bottom: parent.bottom - width: Theme.getSize("topheader_button").width + Theme.getSize("topheader_button_icon").width - height: Theme.getSize("sidebar_header_highlight").height - color: control.checked ? UM.Theme.getColor("sidebar_header_highlight") : UM.Theme.getColor("sidebar_header_highlight_hover") - visible: control.hovered || control.checked - } - } + Behavior on color { ColorAnimation { duration: 50 } } } label: Item { - implicitHeight: Theme.getSize("topheader_button_icon").height - implicitWidth: Theme.getSize("topheader_button").width + Theme.getSize("topheader_button_icon").width - Item + id: contents + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + height: control.height + width: buttonLabel.width + 4 * UM.Theme.getSize("default_margin").width + + Label { + id: buttonLabel + text: control.text + anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter; - width: childrenRect.width - height: Theme.getSize("topheader_button_icon").height - Label + font: UM.Theme.getFont("medium_bold") + color: { - id: button_label - text: control.text; - anchors.verticalCenter: parent.verticalCenter; - font: control.checked ? UM.Theme.getFont("large") : UM.Theme.getFont("large_nonbold") - color: + if (control.checked) { - if(control.hovered) + return UM.Theme.getColor("topheader_button_text_active") + } + else + { + if (control.hovered) { - return UM.Theme.getColor("topheader_button_text_hovered"); - } - if(control.checked) - { - return UM.Theme.getColor("topheader_button_text_active"); - } - else - { - return UM.Theme.getColor("topheader_button_text_inactive"); + return UM.Theme.getColor("topheader_button_text_hovered") } + return UM.Theme.getColor("topheader_button_text_inactive") } } - UM.RecolorImage - { - visible: control.iconSource != "" - id: icon - anchors.left: button_label.right - anchors.leftMargin: (icon.visible || overlayIcon.visible) ? Theme.getSize("default_margin").width : 0 - color: UM.Theme.getColor("text_emphasis") - opacity: !control.enabled ? 0.2 : 1.0 - source: control.iconSource - width: visible ? Theme.getSize("topheader_button_icon").width : 0 - height: Theme.getSize("topheader_button_icon").height - - sourceSize: Theme.getSize("topheader_button_icon") - } - UM.RecolorImage - { - id: overlayIcon - anchors.left: button_label.right - anchors.leftMargin: (icon.visible || overlayIcon.visible) ? Theme.getSize("default_margin").width : 0 - visible: control.overlayIconSource != "" && control.iconSource != "" - color: control.overlayColor - opacity: !control.enabled ? 0.2 : 1.0 - source: control.overlayIconSource - width: visible ? Theme.getSize("topheader_button_icon").width : 0 - height: Theme.getSize("topheader_button_icon").height - - sourceSize: Theme.getSize("topheader_button_icon") - } + } + Component.onCompleted: + { + buttonWidth = width } } } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index f5372bbb0f..e49824ae74 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -79,11 +79,13 @@ "border": [127, 127, 127, 255], "secondary": [245, 245, 245, 255], - "topheader_background": [255, 255, 255, 255], + "topheader_background": [12, 169, 227, 255], - "topheader_button_text_active": [0, 0, 0, 255], - "topheader_button_text_inactive": [128, 128, 128, 255], + "topheader_button_text_active": [12, 169, 227, 255], + "topheader_button_text_inactive": [255, 255, 255, 255], "topheader_button_text_hovered": [0, 0, 0, 255], + "topheader_button_background_active": [255, 255, 255, 255], + "topheader_button_background_inactive": [255, 255, 255, 0], "text": [0, 0, 0, 255], "text_detail": [174, 174, 174, 128], @@ -335,7 +337,7 @@ "sizes": { "window_minimum_size": [70, 50], - "topheader": [0.0, 4.0], + "topheader": [0.0, 4.5], "topheader_button": [8, 4], "topheader_button_icon": [1.2, 1.2], @@ -343,7 +345,7 @@ "default_lining": [0.08, 0.08], "default_arrow": [0.8, 0.8], - "logo": [7.6, 1.6], + "logo": [8, 2.4], "default_margin": [1.0, 1.0], "wide_margin": [2.0, 2.0], From b89c220a4d43e423432af9b4b032de077727fcbf Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 10 Oct 2018 11:17:09 +0200 Subject: [PATCH 0010/1240] Add linings between tabs in the top header. Contributes to CURA-5784. --- resources/themes/cura-light/styles.qml | 34 ++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index ba0350fd4b..b08d9e8728 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -97,14 +97,38 @@ QtObject { // This property will be back-propagated when the width of the label is calculated property var buttonWidth: 0 - background: Rectangle + background: Item { - id: buttonFace implicitHeight: control.height - implicitWidth: buttonWidth - color: control.checked ? UM.Theme.getColor("topheader_button_background_active") : UM.Theme.getColor("topheader_button_background_inactive") + implicitWidth: buttonWidth + 2 * UM.Theme.getSize("default_lining").width - Behavior on color { ColorAnimation { duration: 50 } } + Rectangle + { + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + implicitHeight: parent.height - 2 * UM.Theme.getSize("default_margin").width + implicitWidth: UM.Theme.getSize("default_lining").width + color: UM.Theme.getColor("topheader_button_background_active") + visible: !control.checked + } + Rectangle + { + id: buttonFace + implicitHeight: parent.height + implicitWidth: parent.width + color: control.checked ? UM.Theme.getColor("topheader_button_background_active") : UM.Theme.getColor("topheader_button_background_inactive") + + Behavior on color { ColorAnimation { duration: 50 } } + } + Rectangle + { + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + implicitHeight: parent.height - 2 * UM.Theme.getSize("default_margin").width + implicitWidth: UM.Theme.getSize("default_lining").width + color: UM.Theme.getColor("topheader_button_background_active") + visible: !control.checked + } } label: Item From b3fc76092d4b957ac0462910a49b2244a982654b Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 10 Oct 2018 14:01:34 +0200 Subject: [PATCH 0011/1240] Removed unused EngineLog --- resources/qml/Actions.qml | 9 ------- resources/qml/Cura.qml | 5 ---- resources/qml/EngineLog.qml | 53 ------------------------------------- 3 files changed, 67 deletions(-) delete mode 100644 resources/qml/EngineLog.qml diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index 7d898eed2c..1b1881ab76 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -58,7 +58,6 @@ Item property alias preferences: preferencesAction; - property alias showEngineLog: showEngineLogAction; property alias showProfileFolder: showProfileFolderAction; property alias documentation: documentationAction; property alias reportBug: reportBugAction; @@ -396,14 +395,6 @@ Item shortcut: StandardKey.New } - Action - { - id: showEngineLogAction; - text: catalog.i18nc("@action:inmenu menubar:help","Show Engine &Log..."); - iconName: "view-list-text"; - shortcut: StandardKey.WhatsThis; - } - Action { id: showProfileFolderAction; diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 2af085fee7..0a6e054a5f 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -655,11 +655,6 @@ UM.MainWindow } } - EngineLog - { - id: engineLog; - } - Connections { target: Cura.Actions.showProfileFolder diff --git a/resources/qml/EngineLog.qml b/resources/qml/EngineLog.qml deleted file mode 100644 index 965587b59e..0000000000 --- a/resources/qml/EngineLog.qml +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2015 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.Layouts 1.1 - -import UM 1.1 as UM - -UM.Dialog -{ - id: dialog; - - //: Engine Log dialog title - title: catalog.i18nc("@title:window","Engine Log"); - - modality: Qt.NonModal; - - TextArea - { - id: textArea - anchors.fill: parent; - - Timer - { - id: updateTimer; - interval: 1000; - running: false; - repeat: true; - onTriggered: textArea.text = CuraApplication.getEngineLog(); - } - UM.I18nCatalog{id: catalog; name:"cura"} - } - - rightButtons: Button - { - //: Close engine log button - text: catalog.i18nc("@action:button","Close"); - onClicked: dialog.visible = false; - } - - onVisibleChanged: - { - if(visible) - { - textArea.text = CuraApplication.getEngineLog(); - updateTimer.start(); - } else - { - updateTimer.stop(); - } - } -} From 1ed3558b9a0489a8c479e30170040ec74ec7cb2b Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 10 Oct 2018 14:21:09 +0200 Subject: [PATCH 0012/1240] Move ViewOrientationControl to lower left corner This also renames the component to better reflect what it is (the old name implied that it had something to do with orientation views, wheres it's actually a set of controls to change orientation) CURA-5772 --- resources/qml/Cura.qml | 17 ++++++++++++++++- resources/qml/Skeleton/ApplicationViews.qml | 10 ---------- ...ionViews.qml => ViewOrientationControls.qml} | 3 ++- 3 files changed, 18 insertions(+), 12 deletions(-) rename resources/qml/components/{OrientationViews.qml => ViewOrientationControls.qml} (95%) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 0a6e054a5f..50419067b4 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -13,6 +13,7 @@ import Cura 1.1 as Cura import "Dialogs" import "Menus" import "Skeleton" +import "components" UM.MainWindow { @@ -197,7 +198,7 @@ UM.MainWindow { id: applicationViews - visible: UM.Controller.activeStage.stageId != "MonitorStage" + anchors { top: parent.top @@ -311,6 +312,20 @@ UM.MainWindow bottomMargin: UM.Theme.getSize("default_margin").height } } + + + ViewOrientationControls + { + id: viewOrientationControls + + anchors + { + left: parent.left + margins: UM.Theme.getSize("default_margin").width + + bottom: parent.bottom + } + } } } diff --git a/resources/qml/Skeleton/ApplicationViews.qml b/resources/qml/Skeleton/ApplicationViews.qml index b3d08ab627..94315a73fb 100644 --- a/resources/qml/Skeleton/ApplicationViews.qml +++ b/resources/qml/Skeleton/ApplicationViews.qml @@ -20,16 +20,6 @@ Item height: UM.Theme.getSize("views_selector").height - OrientationViews - { - id: orientationViews - - anchors { - verticalCenter: parent.verticalCenter - right: viewModeButton.left - rightMargin: UM.Theme.getSize("default_margin").width - } - } ComboBox { diff --git a/resources/qml/components/OrientationViews.qml b/resources/qml/components/ViewOrientationControls.qml similarity index 95% rename from resources/qml/components/OrientationViews.qml rename to resources/qml/components/ViewOrientationControls.qml index d1b370ed71..bf56ef2997 100644 --- a/resources/qml/components/OrientationViews.qml +++ b/resources/qml/components/ViewOrientationControls.qml @@ -13,7 +13,8 @@ Row id: viewOrientationControl spacing: 2 * screenScaleFactor - + height: childrenRect.height + width: childrenRect.width // #1 3d view Button { From 5befc0b256f93a4ad5d0199eccfcd5a023aa2be9 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 10 Oct 2018 16:34:55 +0200 Subject: [PATCH 0013/1240] Restyle the dropdown of the accounts, with information about the user and available operations that can be done when the user is logged in or not. Contributes to CURA-5784. --- resources/qml/Account/AccountDetails.qml | 77 +++++++++++++++++++ resources/qml/Account/AccountWidget.qml | 23 ++---- resources/qml/Account/AvatarImage.qml | 12 +++ .../qml/Account/GeneralManagementWidget.qml | 23 ------ resources/qml/Account/GeneralOperations.qml | 35 +++++++++ .../qml/Account/UserManagementWidget.qml | 33 -------- resources/qml/Account/UserOperations.qml | 35 +++++++++ resources/qml/Skeleton/TopHeader.qml | 24 +++++- resources/qml/components/ActionButton.qml | 4 + .../cura-light/icons/circle_outline.svg | 64 +++++++++++++++ resources/themes/cura-light/styles.qml | 8 ++ resources/themes/cura-light/theme.json | 5 ++ 12 files changed, 267 insertions(+), 76 deletions(-) create mode 100644 resources/qml/Account/AccountDetails.qml delete mode 100644 resources/qml/Account/GeneralManagementWidget.qml create mode 100644 resources/qml/Account/GeneralOperations.qml delete mode 100644 resources/qml/Account/UserManagementWidget.qml create mode 100644 resources/qml/Account/UserOperations.qml create mode 100644 resources/themes/cura-light/icons/circle_outline.svg diff --git a/resources/qml/Account/AccountDetails.qml b/resources/qml/Account/AccountDetails.qml new file mode 100644 index 0000000000..ab1ccc15df --- /dev/null +++ b/resources/qml/Account/AccountDetails.qml @@ -0,0 +1,77 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 2.1 + +import UM 1.4 as UM +import Cura 1.1 as Cura + +import "../components" + +Column +{ + property var profile: null + property var loggedIn: false + + padding: 2 * UM.Theme.getSize("default_margin").height + spacing: 2 * UM.Theme.getSize("default_margin").height + + AvatarImage + { + id: avatar + width: 75 * screenScaleFactor + height: 75 * screenScaleFactor + anchors.horizontalCenter: parent.horizontalCenter + source: loggedIn ? profile["profile_image_url"] : UM.Theme.getImage("avatar_default") + outlineColor: loggedIn ? UM.Theme.getColor("account_widget_ouline_active") : UM.Theme.getColor("account_widget_ouline_inactive") + } + + Label + { + id: message + anchors.horizontalCenter: parent.horizontalCenter + visible: !loggedIn + text: catalog.i18nc("@label", "Please login or create an account to 
enjoy all features of Ultimaker Cura") + } + + Column + { + id: userInformation + anchors.horizontalCenter: parent.horizontalCenter + visible: loggedIn + + Label + { + anchors.horizontalCenter: parent.horizontalCenter + text: loggedIn ? profile["username"] : "" + font: UM.Theme.getFont("large") + } + + Label + { + anchors.horizontalCenter: parent.horizontalCenter + text: "email.address@hardcoded.is" + font: UM.Theme.getFont("default") + } + } + + Loader + { + id: accountEntryPoints + anchors.horizontalCenter: parent.horizontalCenter + sourceComponent: loggedIn ? userOperations : generalOperations + } + + Component + { + id: userOperations + UserOperations { } + } + + Component + { + id: generalOperations + GeneralOperations { } + } +} \ No newline at end of file diff --git a/resources/qml/Account/AccountWidget.qml b/resources/qml/Account/AccountWidget.qml index 89f9acd8cc..1d9541de2c 100644 --- a/resources/qml/Account/AccountWidget.qml +++ b/resources/qml/Account/AccountWidget.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 2.1 import UM 1.4 as UM import Cura 1.1 as Cura @@ -12,7 +12,6 @@ Item id: accountWidget property var profile: Cura.API.account.userProfile property var loggedIn: Cura.API.account.isLoggedIn - property var logoutCallback: Cura.API.account.logout height: UM.Theme.getSize("topheader").height width: UM.Theme.getSize("topheader").height @@ -24,6 +23,7 @@ Item anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter source: loggedIn ? profile["profile_image_url"] : UM.Theme.getImage("avatar_default") + outlineColor: loggedIn ? UM.Theme.getColor("account_widget_ouline_active") : UM.Theme.getColor("account_widget_ouline_inactive") } MouseArea @@ -58,22 +58,11 @@ Item borderWidth: UM.Theme.getSize("default_lining").width // Shows the user management options or general options to create account - Loader + AccountDetails { id: panel - sourceComponent: loggedIn ? userManagementWidget : generalManagementWidget + profile: Cura.API.account.userProfile + loggedIn: Cura.API.account.isLoggedIn } } - - Component - { - id: userManagementWidget - UserManagementWidget { } - } - - Component - { - id: generalManagementWidget - GeneralManagementWidget { } - } } \ No newline at end of file diff --git a/resources/qml/Account/AvatarImage.qml b/resources/qml/Account/AvatarImage.qml index 18ac19a45d..96c3cd2330 100644 --- a/resources/qml/Account/AvatarImage.qml +++ b/resources/qml/Account/AvatarImage.qml @@ -10,6 +10,8 @@ Item id: avatar property var source + property var fallbackSource: UM.Theme.getImage("avatar_default") + property var outlineColor: UM.Theme.getColor("account_widget_ouline_active") Image { @@ -41,4 +43,14 @@ Item cached: true invert: false } + + UM.RecolorImage + { + id: profileImageOutline + source: UM.Theme.getIcon("circle_outline") + sourceSize: Qt.size(parent.width, parent.height) + width: parent.width + height: parent.height + color: avatar.outlineColor + } } \ No newline at end of file diff --git a/resources/qml/Account/GeneralManagementWidget.qml b/resources/qml/Account/GeneralManagementWidget.qml deleted file mode 100644 index 2bb0c89f98..0000000000 --- a/resources/qml/Account/GeneralManagementWidget.qml +++ /dev/null @@ -1,23 +0,0 @@ -// 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 UM 1.4 as UM -import Cura 1.1 as Cura - -import "../components" - -Column -{ - ActionButton - { - text: catalog.i18nc("@button", "Sign In") - color: "transparent" - hoverColor: "transparent" - textColor: UM.Theme.getColor("text") - textHoverColor: UM.Theme.getColor("text_link") - onClicked: Cura.API.account.login() - } -} \ No newline at end of file diff --git a/resources/qml/Account/GeneralOperations.qml b/resources/qml/Account/GeneralOperations.qml new file mode 100644 index 0000000000..d224de44bb --- /dev/null +++ b/resources/qml/Account/GeneralOperations.qml @@ -0,0 +1,35 @@ +// 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 UM 1.4 as UM +import Cura 1.1 as Cura + +import "../components" + +Row +{ + spacing: UM.Theme.getSize("default_margin").width + + ActionButton + { + width: UM.Theme.getSize("account_button").width + height: UM.Theme.getSize("account_button").height + text: catalog.i18nc("@button", "Create account") + color: UM.Theme.getColor("secondary") + hoverColor: UM.Theme.getColor("secondary") + textColor: UM.Theme.getColor("text_link") + textHoverColor: UM.Theme.getColor("text") + onClicked: Qt.openUrlExternally("https://account.ultimaker.com") + } + + ActionButton + { + width: UM.Theme.getSize("account_button").width + height: UM.Theme.getSize("account_button").height + text: catalog.i18nc("@button", "Login") + onClicked: Cura.API.account.login() + } +} \ No newline at end of file diff --git a/resources/qml/Account/UserManagementWidget.qml b/resources/qml/Account/UserManagementWidget.qml deleted file mode 100644 index 24f4966679..0000000000 --- a/resources/qml/Account/UserManagementWidget.qml +++ /dev/null @@ -1,33 +0,0 @@ -// 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 UM 1.4 as UM -import Cura 1.1 as Cura - -import "../components" - -Column -{ - ActionButton - { - text: catalog.i18nc("@button", "Manage Profile") - color: "transparent" - hoverColor: "transparent" - textColor: UM.Theme.getColor("text") - textHoverColor: UM.Theme.getColor("text_link") - onClicked: Qt.openUrlExternally("https://account.ultimaker.com") - } - - ActionButton - { - text: catalog.i18nc("@button", "Sign Out") - color: "transparent" - hoverColor: "transparent" - textColor: UM.Theme.getColor("text") - textHoverColor: UM.Theme.getColor("text_link") - onClicked: Cura.API.account.logout() - } -} \ No newline at end of file diff --git a/resources/qml/Account/UserOperations.qml b/resources/qml/Account/UserOperations.qml new file mode 100644 index 0000000000..1704254ed3 --- /dev/null +++ b/resources/qml/Account/UserOperations.qml @@ -0,0 +1,35 @@ +// 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 UM 1.4 as UM +import Cura 1.1 as Cura + +import "../components" + +Row +{ + spacing: UM.Theme.getSize("default_margin").width + + ActionButton + { + width: UM.Theme.getSize("account_button").width + height: UM.Theme.getSize("account_button").height + text: catalog.i18nc("@button", "Manage account") + color: UM.Theme.getColor("secondary") + hoverColor: UM.Theme.getColor("secondary") + textColor: UM.Theme.getColor("text_link") + textHoverColor: UM.Theme.getColor("text") + onClicked: Qt.openUrlExternally("https://account.ultimaker.com") + } + + ActionButton + { + width: UM.Theme.getSize("account_button").width + height: UM.Theme.getSize("account_button").height + text: catalog.i18nc("@button", "Logout") + onClicked: Cura.API.account.logout() + } +} \ No newline at end of file diff --git a/resources/qml/Skeleton/TopHeader.qml b/resources/qml/Skeleton/TopHeader.qml index 8cb568df4d..c24fe86af0 100644 --- a/resources/qml/Skeleton/TopHeader.qml +++ b/resources/qml/Skeleton/TopHeader.qml @@ -1,13 +1,14 @@ // Copyright (c) 2018 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.2 +import QtQuick 2.7 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.1 import UM 1.4 as UM import Cura 1.0 as Cura +import "../components" import "../Account" Rectangle @@ -72,13 +73,30 @@ Rectangle Button { id: toolboxShortcutButton - text: catalog.i18nc("@action:button", "Toolbox") anchors { right: accountWidget.left rightMargin: UM.Theme.getSize("default_margin").width + verticalCenter: parent.verticalCenter + } + style: ButtonStyle + { + background: Rectangle + { + color: control.hovered ? UM.Theme.getColor("secondary") : UM.Theme.getColor("topheader_button_background_active") + radius: 2 * screenScaleFactor + } + + label: Label + { + text: catalog.i18nc("@action:button", "Toolbox") + color: UM.Theme.getColor("topheader_button_text_active") + font: UM.Theme.getFont("action_button") + renderType: Text.NativeRendering + anchors.verticalCenter: control.verticalCenter + } + } - style: UM.Theme.styles.topheader_tab action: Cura.Actions.browsePackages } diff --git a/resources/qml/components/ActionButton.qml b/resources/qml/components/ActionButton.qml index 02c279a458..b49e3f1dcb 100644 --- a/resources/qml/components/ActionButton.qml +++ b/resources/qml/components/ActionButton.qml @@ -19,6 +19,7 @@ Button property var textHoverColor: UM.Theme.getColor("button_text_hover") property var textDisabledColor: textColor property var textFont: UM.Theme.getFont("action_button") + property var cornerRadius: 2 * screenScaleFactor contentItem: Row { @@ -32,6 +33,7 @@ Button sourceSize.height: height color: button.hovered ? button.textHoverColor : button.textColor visible: button.iconSource != "" + anchors.verticalCenter: parent.verticalCenter } Label @@ -42,12 +44,14 @@ Button font: button.textFont visible: button.text != "" renderType: Text.NativeRendering + anchors.verticalCenter: parent.verticalCenter } } background: Rectangle { color: button.enabled ? (button.hovered ? button.hoverColor : button.color) : button.disabledColor + radius: cornerRadius } MouseArea diff --git a/resources/themes/cura-light/icons/circle_outline.svg b/resources/themes/cura-light/icons/circle_outline.svg new file mode 100644 index 0000000000..4d0b0e4eb0 --- /dev/null +++ b/resources/themes/cura-light/icons/circle_outline.svg @@ -0,0 +1,64 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index b08d9e8728..939399efc8 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -90,6 +90,14 @@ QtObject { } } + property Component action_button: Component + { + ButtonStyle + { + + } + } + property Component topheader_tab: Component { ButtonStyle diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index e49824ae74..285d2724a0 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -87,6 +87,9 @@ "topheader_button_background_active": [255, 255, 255, 255], "topheader_button_background_inactive": [255, 255, 255, 0], + "account_widget_ouline_active": [9, 140, 188, 255], + "account_widget_ouline_inactive": [175, 175, 175, 255], + "text": [0, 0, 0, 255], "text_detail": [174, 174, 174, 128], "text_link": [12, 169, 227, 255], @@ -341,6 +344,8 @@ "topheader_button": [8, 4], "topheader_button_icon": [1.2, 1.2], + "account_button": [12, 3], + "views_selector": [0.0, 4.0], "default_lining": [0.08, 0.08], From 85436c1469142301ea02cc647df70e42a9eaba7c Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 11 Oct 2018 10:29:59 +0200 Subject: [PATCH 0014/1240] Move machine selector to own component & new location This also simplifies & cleans up the actually used QML CURA-5772 --- resources/qml/Cura.qml | 8 ++++ resources/qml/PrepareSidebar.qml | 39 ++------------- .../MachineAndConfigurationSelector.qml | 48 +++++++++++++++++++ 3 files changed, 60 insertions(+), 35 deletions(-) create mode 100644 resources/qml/components/MachineAndConfigurationSelector.qml diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 50419067b4..f040469916 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -169,6 +169,14 @@ UM.MainWindow action: Cura.Actions.open } + MachineAndConfigurationSelector + { + anchors.left: openFileButton.right + //anchors.right: sidebar.left + anchors.leftMargin: UM.Theme.getSize("default_margin").height + anchors.top: openFileButton.top + } + Toolbar { id: toolbar diff --git a/resources/qml/PrepareSidebar.qml b/resources/qml/PrepareSidebar.qml index fe0fb033f7..a44f3fad7d 100644 --- a/resources/qml/PrepareSidebar.qml +++ b/resources/qml/PrepareSidebar.qml @@ -9,6 +9,8 @@ import UM 1.2 as UM import Cura 1.0 as Cura import "Menus" import "Menus/ConfigurationMenu" +import "components" + Rectangle { @@ -19,7 +21,6 @@ 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.printerConnected property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands property var connectedPrinter: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null @@ -85,41 +86,11 @@ Rectangle } } - MachineSelection - { - id: machineSelection - width: base.width - configSelection.width - separator.width - height: UM.Theme.getSize("sidebar_header").height - anchors.top: base.top - anchors.left: parent.left - } - - Rectangle - { - id: separator - visible: configSelection.visible - width: visible ? Math.round(UM.Theme.getSize("sidebar_lining_thin").height / 2) : 0 - height: UM.Theme.getSize("sidebar_header").height - color: UM.Theme.getColor("sidebar_lining_thin") - anchors.left: machineSelection.right - } - - ConfigurationSelection - { - id: configSelection - visible: isNetworkPrinter && printerConnected - width: visible ? Math.round(base.width * 0.15) : 0 - height: UM.Theme.getSize("sidebar_header").height - anchors.top: base.top - anchors.right: parent.right - panelWidth: base.width - } - SidebarHeader { id: header width: parent.width visible: !hideSettings && (machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariants) - anchors.top: machineSelection.bottom + anchors.top: parent.top onShowTooltip: base.showTooltip(item, location, text) onHideTooltip: base.hideTooltip() @@ -160,10 +131,9 @@ Rectangle } // Settings mode selection toggle - Rectangle + Item { id: settingsModeSelection - color: "transparent" width: Math.round(parent.width * 0.55) height: UM.Theme.getSize("sidebar_header_mode_toggle").height @@ -328,7 +298,6 @@ Rectangle anchors.bottomMargin: UM.Theme.getSize("sidebar_margin").height height: timeDetails.height + costSpec.height width: base.width - (saveButton.buttonRowWidth + UM.Theme.getSize("sidebar_margin").width) - clip: true Label { diff --git a/resources/qml/components/MachineAndConfigurationSelector.qml b/resources/qml/components/MachineAndConfigurationSelector.qml new file mode 100644 index 0000000000..5b919c42d8 --- /dev/null +++ b/resources/qml/components/MachineAndConfigurationSelector.qml @@ -0,0 +1,48 @@ +// Copyright (c) 2017 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 + +import UM 1.2 as UM +import Cura 1.0 as Cura +import "../Menus" +import "../Menus/ConfigurationMenu" +import ".." + +Rectangle +{ + color: UM.Theme.getColor("sidebar_lining_thin") + + implicitHeight: UM.Theme.getSize("sidebar_header").height + implicitWidth: UM.Theme.getSize("sidebar").width + + property bool isNetworkPrinter: Cura.MachineManager.activeMachineNetworkKey != "" + property bool printerConnected: Cura.MachineManager.printerConnected + + MachineSelection + { + id: machineSelection + anchors + { + left: parent.left + right: configSelection.left + rightMargin: UM.Theme.getSize("sidebar_lining_thin").height / 2 + top: parent.top + bottom: parent.bottom + } + } + + ConfigurationSelection + { + id: configSelection + visible: isNetworkPrinter && printerConnected + width: visible ? Math.round(parent.width * 0.15) : 0 + panelWidth: parent.width + anchors + { + right: parent.right + top: parent.top + bottom: parent.bottom + } + } +} \ No newline at end of file From cc6d5617eefe9431012bbf98496743c4c11723c4 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 11 Oct 2018 10:41:29 +0200 Subject: [PATCH 0015/1240] Update documentation & typing CURA-5772 --- cura/Stages/CuraStage.py | 19 ++++++++++++++----- resources/qml/PrepareSidebar.qml | 6 ++++-- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/cura/Stages/CuraStage.py b/cura/Stages/CuraStage.py index b2f6d61799..774a5a6e76 100644 --- a/cura/Stages/CuraStage.py +++ b/cura/Stages/CuraStage.py @@ -5,19 +5,28 @@ from PyQt5.QtCore import pyqtProperty, QUrl from UM.Stage import Stage +# Since Cura has a few pre-defined "space claims" for the locations of certain components, we've provided some structure +# to indicate this. +# * The StageMenuComponent is the horizontal area below the stage bar. This should be used to show stage specific +# buttons and elements. This component will be drawn over the bar & main component. +# * The MainComponent is the component that will be drawn starting from the bottom of the stageBar and fills the rest +# of the screen. class CuraStage(Stage): - - def __init__(self, parent = None): + def __init__(self, parent = None) -> None: super().__init__(parent) @pyqtProperty(str, constant = True) - def stageId(self): + def stageId(self) -> str: return self.getPluginId() @pyqtProperty(QUrl, constant = True) - def mainComponent(self): + def mainComponent(self) -> QUrl: return self.getDisplayComponent("main") @pyqtProperty(QUrl, constant = True) - def sidebarComponent(self): + def sidebarComponent(self) -> QUrl: return self.getDisplayComponent("sidebar") + + @pyqtProperty(QUrl, constant=True) + def stageMenuComponent(self) -> QUrl: + return self.getDisplayComponent("menu") \ No newline at end of file diff --git a/resources/qml/PrepareSidebar.qml b/resources/qml/PrepareSidebar.qml index a44f3fad7d..7c99e6e145 100644 --- a/resources/qml/PrepareSidebar.qml +++ b/resources/qml/PrepareSidebar.qml @@ -86,7 +86,8 @@ Rectangle } } - SidebarHeader { + SidebarHeader + { id: header width: parent.width visible: !hideSettings && (machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariants) @@ -96,7 +97,8 @@ Rectangle onHideTooltip: base.hideTooltip() } - Rectangle { + Rectangle + { id: headerSeparator width: parent.width visible: settingsModeSelection.visible && header.visible From f18d490ca1037296b4c507481058f1372cbe2ca3 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 11 Oct 2018 13:08:57 +0200 Subject: [PATCH 0016/1240] Move extruderIcon to it's own location This also simplifies the qml that actually handles the display CURA-5772 --- resources/qml/ExtruderIcon.qml | 56 ++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 resources/qml/ExtruderIcon.qml diff --git a/resources/qml/ExtruderIcon.qml b/resources/qml/ExtruderIcon.qml new file mode 100644 index 0000000000..2c2edcb492 --- /dev/null +++ b/resources/qml/ExtruderIcon.qml @@ -0,0 +1,56 @@ +import QtQuick 2.7 +import QtQuick.Controls 1.1 +import UM 1.2 as UM + +Item +{ + + id: extruderIconItem + + implicitWidth: UM.Theme.getSize("button").width + implicitHeight: implicitWidth + + property bool checked: true + property alias material_color: materialColorCircle.color + property alias text_color: extruderNumberText.color + + UM.RecolorImage + { + id: mainCircle + anchors.fill: parent + + sourceSize.width: parent.width + sourceSize.height: parent.width + source: UM.Theme.getIcon("extruder_button") + color: extruderNumberText.color + } + + Label + { + id: extruderNumberText + anchors.centerIn: parent + text: index + 1; + font: UM.Theme.getFont("default_bold") + } + + // Material colour circle + // Only draw the filling colour of the material inside the SVG border. + Rectangle + { + id: materialColorCircle + + anchors + { + right: parent.right + } + + width: Math.round(parent.width * 0.35) + height: Math.round(parent.height * 0.35) + radius: Math.round(width / 2) + + border.width: 1 + border.color: UM.Theme.getColor("extruder_button_material_border") + + opacity: !extruderIconItem.checked ? 0.6 : 1.0 + } +} \ No newline at end of file From a861b88de11ed317f32523e233eb0a3a0252aca9 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 11 Oct 2018 15:03:37 +0200 Subject: [PATCH 0017/1240] Move most of the sidebarHeader items to materialAndVariantSelector CURA-5772 --- resources/qml/MaterialAndVariantSelector.qml | 349 +++++++++++++++++++ 1 file changed, 349 insertions(+) create mode 100644 resources/qml/MaterialAndVariantSelector.qml diff --git a/resources/qml/MaterialAndVariantSelector.qml b/resources/qml/MaterialAndVariantSelector.qml new file mode 100644 index 0000000000..3d2ef2e98f --- /dev/null +++ b/resources/qml/MaterialAndVariantSelector.qml @@ -0,0 +1,349 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 1.1 +import QtQuick.Controls.Styles 1.1 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +import "Menus" // TODO: This needs to be fixed in the qmldir! + +Rectangle +{ + implicitWidth: parent.width + implicitHeight: parent.height + + id: base + + color: "purple" + // Height has an extra 2x margin for the top & bottom margin. + height: childrenRect.height + 2 * UM.Theme.getSize("default_margin").width + + Cura.ExtrudersModel { id: extrudersModel; } + + ListView + { + // Horizontal list that shows the extruders + id: extrudersList + visible: extrudersModel.items.length > 1 + property var index: 0 + + height: UM.Theme.getSize("sidebar_header_mode_tabs").height + boundsBehavior: Flickable.StopAtBounds + + anchors + { + left: parent.left + right: parent.right + top: parent.top + margins: UM.Theme.getSize("sidebar_margin").width + } + + ExclusiveGroup { id: extruderMenuGroup; } + + orientation: ListView.Horizontal + + model: extrudersModel + + Connections + { + target: Cura.MachineManager + onGlobalContainerChanged: forceActiveFocus() // Changing focus applies the currently-being-typed values so it can change the displayed setting values. + } + + delegate: Button + { + height: parent.height + width: Math.round(ListView.view.width / extrudersModel.rowCount()) + + text: model.name + tooltip: model.name + exclusiveGroup: extruderMenuGroup + checked: Cura.ExtruderManager.activeExtruderIndex == index + + property bool extruder_enabled: true + + MouseArea // TODO; This really should be fixed. It makes absolutely no sense to have a button AND a mouse area. + { + anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton + onClicked: { + switch (mouse.button) { + case Qt.LeftButton: + extruder_enabled = Cura.MachineManager.getExtruder(model.index).isEnabled + if (extruder_enabled) + { + forceActiveFocus(); // Changing focus applies the currently-being-typed values so it can change the displayed setting values. + Cura.ExtruderManager.setActiveExtruderIndex(index); + } + break; + case Qt.RightButton: + extruder_enabled = Cura.MachineManager.getExtruder(model.index).isEnabled + extruderMenu.popup(); + break; + } + + } + } + + Menu + { + id: extruderMenu + + MenuItem + { + text: catalog.i18nc("@action:inmenu", "Enable Extruder") + onTriggered: Cura.MachineManager.setExtruderEnabled(model.index, true) + visible: !extruder_enabled // using an intermediate variable prevents an empty popup that occured now and then + } + + MenuItem + { + text: catalog.i18nc("@action:inmenu", "Disable Extruder") + onTriggered: Cura.MachineManager.setExtruderEnabled(model.index, false) + visible: extruder_enabled + enabled: Cura.MachineManager.numberExtrudersEnabled > 1 + } + } + + style: ButtonStyle + { + background: Rectangle + { + anchors.fill: parent + border.width: control.checked ? UM.Theme.getSize("default_lining").width * 2 : UM.Theme.getSize("default_lining").width + border.color: { + if (Cura.MachineManager.getExtruder(index).isEnabled) + { + if(control.checked || control.pressed) + { + return UM.Theme.getColor("action_button_active_border"); + } else if (control.hovered) + { + return UM.Theme.getColor("action_button_hovered_border") + } + return UM.Theme.getColor("action_button_border") + } + return UM.Theme.getColor("action_button_disabled_border") + } + color: { + if (Cura.MachineManager.getExtruder(index).isEnabled) + { + if(control.checked || control.pressed) + { + return UM.Theme.getColor("action_button_active"); + } else if (control.hovered) + { + return UM.Theme.getColor("action_button_hovered") + } + return UM.Theme.getColor("action_button") + } + return UM.Theme.getColor("action_button_disabled") + } + Behavior on color { ColorAnimation { duration: 50; } } + + Item + { + id: extruderButtonFace + anchors.centerIn: parent + width: childrenRect.width + + Label + { + // Static text that holds the "Extruder" label + id: extruderStaticText + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + + color: { + if (Cura.MachineManager.getExtruder(index).isEnabled) + { + if(control.checked || control.pressed) + { + return UM.Theme.getColor("action_button_active_text"); + } else if (control.hovered) + { + return UM.Theme.getColor("action_button_hovered_text") + } + return UM.Theme.getColor("action_button_text") + } + return UM.Theme.getColor("action_button_disabled_text") + } + + font: UM.Theme.getFont("large_nonbold") + text: catalog.i18nc("@label", "Extruder") + visible: width < (control.width - extruderIconItem.width - UM.Theme.getSize("default_margin").width) + elide: Text.ElideRight + } + + ExtruderIcon + { + // Round icon with the extruder number and material color indicator. + anchors.verticalCenter: parent.verticalCenter + anchors.left: extruderStaticText.right + width: control.height - Math.round(UM.Theme.getSize("default_margin").width / 2) + height: width + + checked: control.checked + material_color: model.color + text_color: extruderStaticText.color + } + } + } + + label: Item {} + } + } + } + + Item + { + id: materialRow + height: UM.Theme.getSize("sidebar_setup").height + visible: Cura.MachineManager.hasMaterials + + anchors + { + left: parent.left + right: parent.right + top: extrudersList.bottom + margins: UM.Theme.getSize("sidebar_margin").width + } + + Label + { + id: materialLabel + text: catalog.i18nc("@label", "Material"); + width: Math.round(parent.width * 0.45 - UM.Theme.getSize("default_margin").width) + height: parent.height + verticalAlignment: Text.AlignVCenter + font: UM.Theme.getFont("default"); + color: UM.Theme.getColor("text"); + } + + ToolButton + { + id: materialSelection + + property var activeExtruder: Cura.MachineManager.activeStack + property var hasActiveExtruder: activeExtruder != null + property var currentRootMaterialName: hasActiveExtruder ? activeExtruder.material.name : "" + + text: currentRootMaterialName + tooltip: currentRootMaterialName + visible: Cura.MachineManager.hasMaterials + + enabled: !extrudersList.visible || Cura.ExtruderManager.activeExtruderIndex > -1 + + height: UM.Theme.getSize("setting_control").height + width: Math.round(parent.width * 0.7) + UM.Theme.getSize("sidebar_margin").width + anchors.right: parent.right + style: UM.Theme.styles.sidebar_header_button + activeFocusOnPress: true; + menu: MaterialMenu + { + extruderIndex: Cura.ExtruderManager.activeExtruderIndex + } + + property var valueError: hasActiveExtruder ? Cura.ContainerManager.getContainerMetaDataEntry(activeExtruder.material.id, "compatible", "") != "True" : true + property var valueWarning: ! Cura.MachineManager.isActiveQualitySupported + } + } + + Item + { + id: variantRow + height: UM.Theme.getSize("sidebar_setup").height + visible: Cura.MachineManager.hasVariants + + anchors + { + left: parent.left + right: parent.right + top: materialRow.bottom + margins: UM.Theme.getSize("sidebar_margin").width + } + + Label + { + id: variantLabel + text: Cura.MachineManager.activeDefinitionVariantsName; + width: Math.round(parent.width * 0.45 - UM.Theme.getSize("default_margin").width) + height: parent.height + verticalAlignment: Text.AlignVCenter + font: UM.Theme.getFont("default"); + color: UM.Theme.getColor("text"); + } + + ToolButton + { + id: variantSelection + text: Cura.MachineManager.activeVariantName + tooltip: Cura.MachineManager.activeVariantName; + visible: Cura.MachineManager.hasVariants + + height: UM.Theme.getSize("setting_control").height + width: Math.round(parent.width * 0.7 + UM.Theme.getSize("sidebar_margin").width) + anchors.right: parent.right + style: UM.Theme.styles.sidebar_header_button + activeFocusOnPress: true; + + menu: NozzleMenu { extruderIndex: Cura.ExtruderManager.activeExtruderIndex } + } + } + + Item + { + id: materialCompatibilityLink + height: UM.Theme.getSize("sidebar_setup").height + + anchors.right: parent.right + anchors.top: variantRow.bottom + anchors.margins: UM.Theme.getSize("sidebar_margin").width + UM.RecolorImage + { + id: warningImage + + anchors.right: materialInfoLabel.left + anchors.rightMargin: UM.Theme.getSize("default_margin").width + + source: UM.Theme.getIcon("warning") + width: UM.Theme.getSize("section_icon").width + height: UM.Theme.getSize("section_icon").height + + sourceSize.width: width + sourceSize.height: 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.right: parent.right + + MouseArea + { + anchors.fill: parent + + onClicked: { + // open the material URL with web browser + Qt.openUrlExternally("https://ultimaker.com/incoming-links/cura/material-compatibilty"); + } + } + } + } + +} \ No newline at end of file From 9ab0fdfb68e12576977e226103c88f428f4cbce3 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 11 Oct 2018 15:03:37 +0200 Subject: [PATCH 0018/1240] Move most of the sidebarHeader items to materialAndVariantSelector CURA-5772 --- resources/qml/MaterialAndVariantSelector.qml | 349 +++++++++++++++++++ 1 file changed, 349 insertions(+) create mode 100644 resources/qml/MaterialAndVariantSelector.qml diff --git a/resources/qml/MaterialAndVariantSelector.qml b/resources/qml/MaterialAndVariantSelector.qml new file mode 100644 index 0000000000..8acaba9ec7 --- /dev/null +++ b/resources/qml/MaterialAndVariantSelector.qml @@ -0,0 +1,349 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 1.1 +import QtQuick.Controls.Styles 1.1 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +import "Menus" // TODO: This needs to be fixed in the qmldir! + +Rectangle +{ + implicitWidth: parent.width + implicitHeight: parent.height + + id: base + color: UM.Theme.getColor("sidebar") + + // Height has an extra 2x margin for the top & bottom margin. + height: childrenRect.height + 2 * UM.Theme.getSize("default_margin").width + + Cura.ExtrudersModel { id: extrudersModel; } + + ListView + { + // Horizontal list that shows the extruders + id: extrudersList + visible: extrudersModel.items.length > 1 + property var index: 0 + + height: UM.Theme.getSize("sidebar_header_mode_tabs").height + boundsBehavior: Flickable.StopAtBounds + + anchors + { + left: parent.left + right: parent.right + top: parent.top + margins: UM.Theme.getSize("sidebar_margin").width + } + + ExclusiveGroup { id: extruderMenuGroup; } + + orientation: ListView.Horizontal + + model: extrudersModel + + Connections + { + target: Cura.MachineManager + onGlobalContainerChanged: forceActiveFocus() // Changing focus applies the currently-being-typed values so it can change the displayed setting values. + } + + delegate: Button + { + height: parent.height + width: Math.round(ListView.view.width / extrudersModel.rowCount()) + + text: model.name + tooltip: model.name + exclusiveGroup: extruderMenuGroup + checked: Cura.ExtruderManager.activeExtruderIndex == index + + property bool extruder_enabled: true + + MouseArea // TODO; This really should be fixed. It makes absolutely no sense to have a button AND a mouse area. + { + anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton + onClicked: { + switch (mouse.button) { + case Qt.LeftButton: + extruder_enabled = Cura.MachineManager.getExtruder(model.index).isEnabled + if (extruder_enabled) + { + forceActiveFocus(); // Changing focus applies the currently-being-typed values so it can change the displayed setting values. + Cura.ExtruderManager.setActiveExtruderIndex(index); + } + break; + case Qt.RightButton: + extruder_enabled = Cura.MachineManager.getExtruder(model.index).isEnabled + extruderMenu.popup(); + break; + } + + } + } + + Menu + { + id: extruderMenu + + MenuItem + { + text: catalog.i18nc("@action:inmenu", "Enable Extruder") + onTriggered: Cura.MachineManager.setExtruderEnabled(model.index, true) + visible: !extruder_enabled // using an intermediate variable prevents an empty popup that occured now and then + } + + MenuItem + { + text: catalog.i18nc("@action:inmenu", "Disable Extruder") + onTriggered: Cura.MachineManager.setExtruderEnabled(model.index, false) + visible: extruder_enabled + enabled: Cura.MachineManager.numberExtrudersEnabled > 1 + } + } + + style: ButtonStyle + { + background: Rectangle + { + anchors.fill: parent + border.width: control.checked ? UM.Theme.getSize("default_lining").width * 2 : UM.Theme.getSize("default_lining").width + border.color: { + if (Cura.MachineManager.getExtruder(index).isEnabled) + { + if(control.checked || control.pressed) + { + return UM.Theme.getColor("action_button_active_border"); + } else if (control.hovered) + { + return UM.Theme.getColor("action_button_hovered_border") + } + return UM.Theme.getColor("action_button_border") + } + return UM.Theme.getColor("action_button_disabled_border") + } + color: { + if (Cura.MachineManager.getExtruder(index).isEnabled) + { + if(control.checked || control.pressed) + { + return UM.Theme.getColor("action_button_active"); + } else if (control.hovered) + { + return UM.Theme.getColor("action_button_hovered") + } + return UM.Theme.getColor("action_button") + } + return UM.Theme.getColor("action_button_disabled") + } + Behavior on color { ColorAnimation { duration: 50; } } + + Item + { + id: extruderButtonFace + anchors.centerIn: parent + width: childrenRect.width + + Label + { + // Static text that holds the "Extruder" label + id: extruderStaticText + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + + color: { + if (Cura.MachineManager.getExtruder(index).isEnabled) + { + if(control.checked || control.pressed) + { + return UM.Theme.getColor("action_button_active_text"); + } else if (control.hovered) + { + return UM.Theme.getColor("action_button_hovered_text") + } + return UM.Theme.getColor("action_button_text") + } + return UM.Theme.getColor("action_button_disabled_text") + } + + font: UM.Theme.getFont("large_nonbold") + text: catalog.i18nc("@label", "Extruder") + visible: width < (control.width - extruderIconItem.width - UM.Theme.getSize("default_margin").width) + elide: Text.ElideRight + } + + ExtruderIcon + { + // Round icon with the extruder number and material color indicator. + anchors.verticalCenter: parent.verticalCenter + anchors.left: extruderStaticText.right + width: control.height - Math.round(UM.Theme.getSize("default_margin").width / 2) + height: width + + checked: control.checked + material_color: model.color + text_color: extruderStaticText.color + } + } + } + + label: Item {} + } + } + } + + Item + { + id: materialRow + height: UM.Theme.getSize("sidebar_setup").height + visible: Cura.MachineManager.hasMaterials + + anchors + { + left: parent.left + right: parent.right + top: extrudersList.bottom + margins: UM.Theme.getSize("sidebar_margin").width + } + + Label + { + id: materialLabel + text: catalog.i18nc("@label", "Material"); + width: Math.round(parent.width * 0.45 - UM.Theme.getSize("default_margin").width) + height: parent.height + verticalAlignment: Text.AlignVCenter + font: UM.Theme.getFont("default"); + color: UM.Theme.getColor("text"); + } + + ToolButton + { + id: materialSelection + + property var activeExtruder: Cura.MachineManager.activeStack + property var hasActiveExtruder: activeExtruder != null + property var currentRootMaterialName: hasActiveExtruder ? activeExtruder.material.name : "" + + text: currentRootMaterialName + tooltip: currentRootMaterialName + visible: Cura.MachineManager.hasMaterials + + enabled: !extrudersList.visible || Cura.ExtruderManager.activeExtruderIndex > -1 + + height: UM.Theme.getSize("setting_control").height + width: Math.round(parent.width * 0.7) + UM.Theme.getSize("sidebar_margin").width + anchors.right: parent.right + style: UM.Theme.styles.sidebar_header_button + activeFocusOnPress: true; + menu: MaterialMenu + { + extruderIndex: Cura.ExtruderManager.activeExtruderIndex + } + + property var valueError: hasActiveExtruder ? Cura.ContainerManager.getContainerMetaDataEntry(activeExtruder.material.id, "compatible", "") != "True" : true + property var valueWarning: ! Cura.MachineManager.isActiveQualitySupported + } + } + + Item + { + id: variantRow + height: UM.Theme.getSize("sidebar_setup").height + visible: Cura.MachineManager.hasVariants + + anchors + { + left: parent.left + right: parent.right + top: materialRow.bottom + margins: UM.Theme.getSize("sidebar_margin").width + } + + Label + { + id: variantLabel + text: Cura.MachineManager.activeDefinitionVariantsName; + width: Math.round(parent.width * 0.45 - UM.Theme.getSize("default_margin").width) + height: parent.height + verticalAlignment: Text.AlignVCenter + font: UM.Theme.getFont("default"); + color: UM.Theme.getColor("text"); + } + + ToolButton + { + id: variantSelection + text: Cura.MachineManager.activeVariantName + tooltip: Cura.MachineManager.activeVariantName; + visible: Cura.MachineManager.hasVariants + + height: UM.Theme.getSize("setting_control").height + width: Math.round(parent.width * 0.7 + UM.Theme.getSize("sidebar_margin").width) + anchors.right: parent.right + style: UM.Theme.styles.sidebar_header_button + activeFocusOnPress: true; + + menu: NozzleMenu { extruderIndex: Cura.ExtruderManager.activeExtruderIndex } + } + } + + Item + { + id: materialCompatibilityLink + height: UM.Theme.getSize("sidebar_setup").height + + anchors.right: parent.right + anchors.top: variantRow.bottom + anchors.margins: UM.Theme.getSize("sidebar_margin").width + UM.RecolorImage + { + id: warningImage + + anchors.right: materialInfoLabel.left + anchors.rightMargin: UM.Theme.getSize("default_margin").width + + source: UM.Theme.getIcon("warning") + width: UM.Theme.getSize("section_icon").width + height: UM.Theme.getSize("section_icon").height + + sourceSize.width: width + sourceSize.height: 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.right: parent.right + + MouseArea + { + anchors.fill: parent + + onClicked: { + // open the material URL with web browser + Qt.openUrlExternally("https://ultimaker.com/incoming-links/cura/material-compatibilty"); + } + } + } + } + +} \ No newline at end of file From bb7582159910728976e4e020d0b61de2b0b08ce2 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 11 Oct 2018 13:44:10 +0200 Subject: [PATCH 0019/1240] Fix some code style Contributes to issue CURA-5784. --- resources/qml/Cura.qml | 22 ++++++++++++--------- resources/qml/Skeleton/ApplicationMenu.qml | 12 +++++------ resources/qml/Skeleton/ApplicationViews.qml | 16 ++++++++------- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index f040469916..8369c2a743 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -27,21 +27,25 @@ UM.MainWindow UM.I18nCatalog { id: catalog - name:"cura" + name: "cura" } onWidthChanged: { // If slidebar is collapsed then it should be invisible // otherwise after the main_window resize the sidebar will be fully re-drawn - if (sidebar.collapsed){ - if (sidebar.visible == true){ + if (sidebar.collapsed) + { + if (sidebar.visible == true) + { sidebar.visible = false sidebar.initialWidth = 0 } } - else{ - if (sidebar.visible == false){ + else + { + if (sidebar.visible == false) + { sidebar.visible = true sidebar.initialWidth = UM.Theme.getSize("sidebar").width } @@ -156,7 +160,7 @@ UM.MainWindow Button { id: openFileButton - text: catalog.i18nc("@action:button","Open File") + text: catalog.i18nc("@action:button", "Open File") iconSource: UM.Theme.getIcon("load") style: UM.Theme.styles.tool_button tooltip: "" @@ -352,10 +356,10 @@ UM.MainWindow { //; Remove & re-add the general page as we want to use our own instead of uranium standard. removePage(0); - insertPage(0, catalog.i18nc("@title:tab","General"), Qt.resolvedUrl("Preferences/GeneralPage.qml")); + insertPage(0, catalog.i18nc("@title:tab", "General"), Qt.resolvedUrl("Preferences/GeneralPage.qml")); removePage(1); - insertPage(1, catalog.i18nc("@title:tab","Settings"), Qt.resolvedUrl("Preferences/SettingVisibilityPage.qml")); + insertPage(1, catalog.i18nc("@title:tab", "Settings"), Qt.resolvedUrl("Preferences/SettingVisibilityPage.qml")); insertPage(2, catalog.i18nc("@title:tab", "Printers"), Qt.resolvedUrl("Preferences/MachinesPage.qml")); @@ -528,7 +532,7 @@ UM.MainWindow id: openDialog; //: File open dialog title - title: catalog.i18nc("@title:window","Open file(s)") + title: catalog.i18nc("@title:window", "Open file(s)") modality: UM.Application.platform == "linux" ? Qt.NonModal : Qt.WindowModal; selectMultiple: true nameFilters: UM.MeshFileHandler.supportedReadFileTypes; diff --git a/resources/qml/Skeleton/ApplicationMenu.qml b/resources/qml/Skeleton/ApplicationMenu.qml index a8774e8ceb..00859e2a45 100644 --- a/resources/qml/Skeleton/ApplicationMenu.qml +++ b/resources/qml/Skeleton/ApplicationMenu.qml @@ -27,7 +27,7 @@ Item Menu { id: fileMenu - title: catalog.i18nc("@title:menu menubar:toplevel","&File") + title: catalog.i18nc("@title:menu menubar:toplevel", "&File") MenuItem { @@ -46,7 +46,7 @@ Item MenuItem { id: saveWorkspaceMenu - text: catalog.i18nc("@title:menu menubar:file","&Save...") + text: catalog.i18nc("@title:menu menubar:file", "&Save...") onTriggered: { var args = { "filter_by_machine": false, "file_type": "workspace", "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml" }; @@ -99,7 +99,7 @@ Item Menu { - title: catalog.i18nc("@title:menu menubar:toplevel","&Edit") + title: catalog.i18nc("@title:menu menubar:toplevel", "&Edit") MenuItem { action: Cura.Actions.undo } MenuItem { action: Cura.Actions.redo } @@ -181,7 +181,7 @@ Item Menu { id: extensionMenu - title: catalog.i18nc("@title:menu menubar:toplevel","E&xtensions") + title: catalog.i18nc("@title:menu menubar:toplevel", "E&xtensions") Instantiator { @@ -223,7 +223,7 @@ Item Menu { id: preferencesMenu - title: catalog.i18nc("@title:menu menubar:toplevel","P&references") + title: catalog.i18nc("@title:menu menubar:toplevel", "P&references") MenuItem { action: Cura.Actions.preferences } } @@ -231,7 +231,7 @@ Item Menu { id: helpMenu - title: catalog.i18nc("@title:menu menubar:toplevel","&Help") + title: catalog.i18nc("@title:menu menubar:toplevel", "&Help") MenuItem { action: Cura.Actions.showProfileFolder } MenuItem { action: Cura.Actions.documentation } diff --git a/resources/qml/Skeleton/ApplicationViews.qml b/resources/qml/Skeleton/ApplicationViews.qml index 94315a73fb..eb8ab16fae 100644 --- a/resources/qml/Skeleton/ApplicationViews.qml +++ b/resources/qml/Skeleton/ApplicationViews.qml @@ -11,9 +11,9 @@ import Cura 1.0 as Cura import "../components" -// This item contains the views selector, a combobox that is dinamically created from -// the list of available Views (packages that create different visualizactions of the -// scene. Aside the selector, there is a row of buttons that change the orientation of the view. +// This item contains the views selector, a combobox that is dynamically created from +// the list of available Views (packages that create different visualizations of the +// scene). Aside from the selector, there is a row of buttons that change the orientation of the view. Item { id: applicationViewsSelector @@ -25,7 +25,8 @@ Item { id: viewModeButton - anchors { + anchors + { verticalCenter: parent.verticalCenter right: parent.right rightMargin: UM.Theme.getSize("default_margin").width @@ -51,15 +52,16 @@ Item { for (var i = 0; i < model.rowCount(); i++) { - if (model.getItem(i).active) { - return i + if (model.getItem(i).active) + { + return i; } } return 0 } // set the active index - function setActiveIndex (index) + function setActiveIndex(index) { UM.Controller.setActiveView(index) // the connection to UM.ActiveView will trigger update so there is no reason to call it manually here From 10de312647a1e38db0ae6c24b0d06b3e807f7f37 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 12 Oct 2018 13:28:08 +0200 Subject: [PATCH 0020/1240] Added QMLDIR so we can specify cura specific QML components for plugins This ensures that we can be way more explicit with what QML components we see as re-usable CURA-5772 --- resources/qml/qmldir | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 resources/qml/qmldir diff --git a/resources/qml/qmldir b/resources/qml/qmldir new file mode 100644 index 0000000000..5083b5e80a --- /dev/null +++ b/resources/qml/qmldir @@ -0,0 +1,5 @@ +module Cura + +MachineAndConfigurationSelector 1.0 components/MachineAndConfigurationSelector.qml +MaterialAndVariantSelector 1.0 MaterialAndVariantSelector.qml +ProfileAndSettingComponent 1.0 ProfileAndSettingComponent.qml \ No newline at end of file From 7d5472b09fe0a0fc524b36ab9c9d85e54c68d241 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 12 Oct 2018 13:29:57 +0200 Subject: [PATCH 0021/1240] Moved the prepareMenu into the prepare stage plugin CURA-5772 --- plugins/PrepareStage/PrepareMenu.qml | 42 ++++++++++++++++++++++++++++ plugins/PrepareStage/PrepareStage.py | 6 +++- 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 plugins/PrepareStage/PrepareMenu.qml diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml new file mode 100644 index 0000000000..eb09326524 --- /dev/null +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -0,0 +1,42 @@ +import QtQuick 2.7 + +import QtQuick.Controls 1.4 + +import UM 1.3 as UM +import Cura 1.1 as Cura + + +Row +{ + spacing: UM.Theme.getSize("default_margin").width + + UM.I18nCatalog + { + id: catalog + name:"cura" + } + + Button + { + id: openFileButton; + text: catalog.i18nc("@action:button", "Open File"); + iconSource: UM.Theme.getIcon("load") + style: UM.Theme.styles.tool_button + tooltip: "" + action: Cura.Actions.open; + } + + Cura.MachineAndConfigurationSelector + { + } + + Cura.MaterialAndVariantSelector + { + width: UM.Theme.getSize("sidebar").width + } + + Cura.ProfileAndSettingComponent + { + width: UM.Theme.getSize("sidebar").width + } +} \ No newline at end of file diff --git a/plugins/PrepareStage/PrepareStage.py b/plugins/PrepareStage/PrepareStage.py index c3c9f0a1f8..b22f3385b8 100644 --- a/plugins/PrepareStage/PrepareStage.py +++ b/plugins/PrepareStage/PrepareStage.py @@ -2,13 +2,14 @@ # Cura is released under the terms of the LGPLv3 or higher. import os.path from UM.Application import Application +from UM.PluginRegistry import PluginRegistry from UM.Resources import Resources from cura.Stages.CuraStage import CuraStage + ## Stage for preparing model (slicing). class PrepareStage(CuraStage): - def __init__(self, parent = None): super().__init__(parent) Application.getInstance().engineCreatedSignal.connect(self._engineCreated) @@ -16,4 +17,7 @@ class PrepareStage(CuraStage): def _engineCreated(self): sidebar_component_path = os.path.join(Resources.getPath(Application.getInstance().ResourceTypes.QmlFiles), "PrepareSidebar.qml") + + menu_component_path = os.path.join(PluginRegistry.getInstance().getPluginPath("PrepareStage"), "PrepareMenu.qml") + self.addDisplayComponent("menu", menu_component_path) self.addDisplayComponent("sidebar", sidebar_component_path) From 11a08d0e472612b7c5377273cf2435c1a17cb3c4 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 12 Oct 2018 13:32:23 +0200 Subject: [PATCH 0022/1240] Move whole bunch of QML components to their new locations The tooltips are still not working correctly, it might be that it needs rework CURA-5772 --- resources/qml/Cura.qml | 149 +++++--- resources/qml/ExtruderIcon.qml | 1 - resources/qml/MaterialAndVariantSelector.qml | 2 +- ...bar.qml => ProfileAndSettingComponent.qml} | 330 ++++++------------ resources/qml/ProgressAndSaveWidget.qml | 237 +++++++++++++ resources/qml/SaveButton.qml | 165 +++++---- resources/qml/Settings/SettingView.qml | 9 +- resources/qml/SidebarHeader.qml | 3 +- resources/qml/Skeleton/TopHeader.qml | 3 +- 9 files changed, 536 insertions(+), 363 deletions(-) rename resources/qml/{PrepareSidebar.qml => ProfileAndSettingComponent.qml} (66%) create mode 100644 resources/qml/ProgressAndSaveWidget.qml diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 8369c2a743..f89f8d63dd 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -21,36 +21,45 @@ UM.MainWindow // Cura application window title title: catalog.i18nc("@title:window", "Ultimaker Cura") - viewportRect: Qt.rect(0, 0, (base.width - sidebar.width) / base.width, 1.0) + //viewportRect: Qt.rect(0, 0, (base.width - sidebar.width) / base.width, 1.0) + viewportRect: Qt.rect(0, 0, 1.0, 1.0) backgroundColor: UM.Theme.getColor("viewport_background") UM.I18nCatalog { id: catalog - name: "cura" + name:"cura" } - onWidthChanged: + function showTooltip(item, position, text) + { + tooltip.text = text; + position = item.mapToItem(backgroundItem, position.x - UM.Theme.getSize("default_arrow").width, position.y); + tooltip.show(position); + } + + function hideTooltip() + { + tooltip.hide(); + } + + /*onWidthChanged: { // If slidebar is collapsed then it should be invisible // otherwise after the main_window resize the sidebar will be fully re-drawn - if (sidebar.collapsed) - { - if (sidebar.visible == true) - { + if (sidebar.collapsed){ + if (sidebar.visible == true){ sidebar.visible = false sidebar.initialWidth = 0 } } - else - { - if (sidebar.visible == false) - { + else{ + if (sidebar.visible == false){ sidebar.visible = true sidebar.initialWidth = UM.Theme.getSize("sidebar").width } } - } + }*/ Component.onCompleted: { @@ -69,11 +78,16 @@ UM.MainWindow CuraApplication.purgeWindows() } - Column + Item { id: backgroundItem anchors.fill: parent + SidebarTooltip + { + id: tooltip + } + signal hasMesh(string name) //this signal sends the filebase name so it can be used for the JobSpecs.qml function getMeshName(path) { @@ -94,26 +108,34 @@ UM.MainWindow ApplicationMenu { - id: menu + id: applicationMenu window: base } TopHeader { id: topHeader - anchors.left: parent.left - anchors.right: parent.right + anchors + { + left: parent.left + right: parent.right + top: applicationMenu.bottom + } } Item { id: contentItem - width: parent.width - height: parent.height - menu.height - topHeader.height - z: topHeader.z - 1 + anchors + { + top: topHeader.bottom + bottom: parent.bottom + left: parent.left + right: parent.right + } - Keys.forwardTo: menu + Keys.forwardTo: applicationMenu DropArea { @@ -145,41 +167,39 @@ UM.MainWindow } } + Loader + { + // The stage menu is, as the name implies, a menu that is defined by the active stage. + // Note that this menu does not need to be set at all! It's perfectly acceptable to have a stage + // without this menu! + id: stageMenu + + anchors + { + left: parent.left + right: parent.right + top: parent.top + margins: UM.Theme.getSize("default_margin").height + } + + height: 50 + + source: UM.Controller.activeStage.stageMenuComponent + } + JobSpecs { id: jobSpecs anchors { bottom: parent.bottom - right: sidebar.left + //right: sidebar.left bottomMargin: UM.Theme.getSize("default_margin").height rightMargin: UM.Theme.getSize("default_margin").width } } - Button - { - id: openFileButton - text: catalog.i18nc("@action:button", "Open File") - iconSource: UM.Theme.getIcon("load") - style: UM.Theme.styles.tool_button - tooltip: "" - anchors - { - top: parent.top - topMargin: UM.Theme.getSize("default_margin").height - left: parent.left - } - action: Cura.Actions.open - } - MachineAndConfigurationSelector - { - anchors.left: openFileButton.right - //anchors.right: sidebar.left - anchors.leftMargin: UM.Theme.getSize("default_margin").height - anchors.top: openFileButton.top - } Toolbar { @@ -189,7 +209,7 @@ UM.MainWindow property int mouseY: base.mouseY anchors { - top: openFileButton.bottom + top: stageMenu.bottom topMargin: UM.Theme.getSize("window_margin").height left: parent.left } @@ -206,7 +226,7 @@ UM.MainWindow } } - ApplicationViews + /*ApplicationViews { id: applicationViews @@ -216,7 +236,7 @@ UM.MainWindow top: parent.top right: sidebar.left } - } + }*/ Loader { @@ -227,7 +247,7 @@ UM.MainWindow top: parent.top bottom: parent.bottom left: parent.left - right: sidebar.left + right: parent.right } MouseArea @@ -241,7 +261,7 @@ UM.MainWindow source: UM.Controller.activeStage.mainComponent } - Loader + /*Loader { id: sidebar @@ -311,7 +331,7 @@ UM.MainWindow acceptedButtons: Qt.AllButtons onWheel: wheel.accepted = true } - } + }*/ UM.MessageStack { @@ -338,15 +358,33 @@ UM.MainWindow bottom: parent.bottom } } + + + ProgressAndSaveWidget + { + anchors.right: parent.right + anchors.bottom: parent.bottom + width: UM.Theme.getSize("sidebar").width + anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width + anchors.bottomMargin: UM.Theme.getSize("sidebar_margin").height + onShowTooltip: + { + base.showTooltip(item, location, text) + } + onHideTooltip: + { + base.hideTooltip() + } + } } } // Expand or collapse sidebar - Connections + /*Connections { target: Cura.Actions.expandSidebar onTriggered: sidebar.callExpandOrCollapse() - } + }*/ UM.PreferencesDialog { @@ -356,10 +394,10 @@ UM.MainWindow { //; Remove & re-add the general page as we want to use our own instead of uranium standard. removePage(0); - insertPage(0, catalog.i18nc("@title:tab", "General"), Qt.resolvedUrl("Preferences/GeneralPage.qml")); + insertPage(0, catalog.i18nc("@title:tab","General"), Qt.resolvedUrl("Preferences/GeneralPage.qml")); removePage(1); - insertPage(1, catalog.i18nc("@title:tab", "Settings"), Qt.resolvedUrl("Preferences/SettingVisibilityPage.qml")); + insertPage(1, catalog.i18nc("@title:tab","Settings"), Qt.resolvedUrl("Preferences/SettingVisibilityPage.qml")); insertPage(2, catalog.i18nc("@title:tab", "Printers"), Qt.resolvedUrl("Preferences/MachinesPage.qml")); @@ -532,7 +570,7 @@ UM.MainWindow id: openDialog; //: File open dialog title - title: catalog.i18nc("@title:window", "Open file(s)") + title: catalog.i18nc("@title:window","Open file(s)") modality: UM.Application.platform == "linux" ? Qt.NonModal : Qt.WindowModal; selectMultiple: true nameFilters: UM.MeshFileHandler.supportedReadFileTypes; @@ -638,7 +676,8 @@ UM.MainWindow modality: Qt.ApplicationModal } - MessageDialog { + MessageDialog + { id: infoMultipleFilesWithGcodeDialog title: catalog.i18nc("@title:window", "Open File(s)") icon: StandardIcon.Information diff --git a/resources/qml/ExtruderIcon.qml b/resources/qml/ExtruderIcon.qml index 2c2edcb492..580ff5dce9 100644 --- a/resources/qml/ExtruderIcon.qml +++ b/resources/qml/ExtruderIcon.qml @@ -4,7 +4,6 @@ import UM 1.2 as UM Item { - id: extruderIconItem implicitWidth: UM.Theme.getSize("button").width diff --git a/resources/qml/MaterialAndVariantSelector.qml b/resources/qml/MaterialAndVariantSelector.qml index 325ffa85a5..fff3ef1100 100644 --- a/resources/qml/MaterialAndVariantSelector.qml +++ b/resources/qml/MaterialAndVariantSelector.qml @@ -17,7 +17,7 @@ Rectangle id: base color: UM.Theme.getColor("sidebar") - + // Height has an extra 2x margin for the top & bottom margin. height: childrenRect.height + 2 * UM.Theme.getSize("default_margin").width diff --git a/resources/qml/PrepareSidebar.qml b/resources/qml/ProfileAndSettingComponent.qml similarity index 66% rename from resources/qml/PrepareSidebar.qml rename to resources/qml/ProfileAndSettingComponent.qml index 7c99e6e145..33c2f37fb1 100644 --- a/resources/qml/PrepareSidebar.qml +++ b/resources/qml/ProfileAndSettingComponent.qml @@ -16,14 +16,10 @@ Rectangle { id: base + height: childrenRect.height + property int currentModeIndex: -1 property bool hideSettings: PrintInformation.preSliced - property bool hideView: Cura.MachineManager.activeMachineName == "" - - // Is there an output device for this printer? - property bool printerConnected: Cura.MachineManager.printerConnected - property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands - property var connectedPrinter: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null property variant printDuration: PrintInformation.currentPrintTime property variant printMaterialLengths: PrintInformation.materialLengths @@ -34,7 +30,8 @@ Rectangle color: UM.Theme.getColor("sidebar") UM.I18nCatalog { id: catalog; name:"cura"} - Timer { + Timer + { id: tooltipDelayTimer interval: 500 repeat: false @@ -59,7 +56,8 @@ Rectangle tooltip.hide(); } - function strPadLeft(string, pad, length) { + function strPadLeft(string, pad, length) + { return (new Array(length + 1).join(pad) + string).slice(-length); } @@ -86,35 +84,9 @@ Rectangle } } - SidebarHeader - { - id: header - width: parent.width - visible: !hideSettings && (machineExtruderCount.properties.value > 1 || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariants) - anchors.top: parent.top - - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() - } - - Rectangle - { - id: headerSeparator - width: parent.width - visible: settingsModeSelection.visible && header.visible - height: visible ? UM.Theme.getSize("sidebar_lining").height : 0 - color: UM.Theme.getColor("sidebar_lining") - anchors.top: header.bottom - anchors.topMargin: visible ? UM.Theme.getSize("sidebar_margin").height : 0 - } - onCurrentModeIndexChanged: { UM.Preferences.setValue("cura/active_mode", currentModeIndex); - if(modesListModel.count > base.currentModeIndex) - { - sidebarContents.replace(modesListModel.get(base.currentModeIndex).item, { "replace": true }) - } } Label @@ -122,176 +94,129 @@ Rectangle id: settingsModeLabel text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox", "Print Setup disabled\nG-code files cannot be modified") renderType: Text.NativeRendering - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width - anchors.top: hideSettings ? machineSelection.bottom : headerSeparator.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + + anchors + { + left: parent.left + top: parent.top + margins: UM.Theme.getSize("sidebar_margin").width + } + width: Math.round(parent.width * 0.45) + height: contentHeight font: UM.Theme.getFont("large") color: UM.Theme.getColor("text") - visible: !hideView } - // Settings mode selection toggle - Item - { - id: settingsModeSelection + ListView + { + // Settings mode selection toggle + id: settingsModeSelection + model: modesListModel width: Math.round(parent.width * 0.55) height: UM.Theme.getSize("sidebar_header_mode_toggle").height anchors.right: parent.right anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - anchors.top: - { - if (settingsModeLabel.contentWidth >= parent.width - width - UM.Theme.getSize("sidebar_margin").width * 2) - { - return settingsModeLabel.bottom; - } - else - { - return headerSeparator.bottom; - } - } - visible: !hideSettings && !hideView - - Component - { - id: wizardDelegate - - Button - { - id: control - - height: settingsModeSelection.height - width: Math.round(parent.width / 2) - - anchors.left: parent.left - anchors.leftMargin: model.index * Math.round(settingsModeSelection.width / 2) - anchors.verticalCenter: parent.verticalCenter - - ButtonGroup.group: modeMenuGroup - - checkable: true - checked: base.currentModeIndex == index - onClicked: base.currentModeIndex = index - - onHoveredChanged: - { - if (hovered) - { - tooltipDelayTimer.item = settingsModeSelection - tooltipDelayTimer.text = model.tooltipText - tooltipDelayTimer.start() - } - else - { - tooltipDelayTimer.stop() - base.hideTooltip() - } - } - - background: Rectangle - { - border.width: control.checked ? UM.Theme.getSize("default_lining").width * 2 : UM.Theme.getSize("default_lining").width - border.color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_border") : control.hovered ? UM.Theme.getColor("action_button_hovered_border"): UM.Theme.getColor("action_button_border") - - // for some reason, QtQuick decided to use the color of the background property as text color for the contentItem, so here it is - color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active") : control.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button") - } - - contentItem: Label - { - text: model.text - font: UM.Theme.getFont("default") - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - renderType: Text.NativeRendering - elide: Text.ElideRight - color: - { - if(control.pressed) - { - return UM.Theme.getColor("action_button_active_text"); - } - else if(control.hovered) - { - return UM.Theme.getColor("action_button_hovered_text"); - } - return UM.Theme.getColor("action_button_text"); - } - } - } - } + anchors.top: settingsModeLabel.top ButtonGroup { id: modeMenuGroup } - ListView + delegate: Button { - id: modesList - model: modesListModel - delegate: wizardDelegate - anchors.top: parent.top + id: control + + height: settingsModeSelection.height + width: Math.round(parent.width / 2) + anchors.left: parent.left - width: parent.width - } - } + anchors.leftMargin: model.index * Math.round(settingsModeSelection.width / 2) + anchors.verticalCenter: parent.verticalCenter - StackView - { - id: sidebarContents + ButtonGroup.group: modeMenuGroup - anchors.bottom: footerSeparator.top - anchors.top: settingsModeSelection.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - anchors.left: base.left - anchors.right: base.right - visible: !hideSettings + checkable: true + checked: base.currentModeIndex == index + onClicked: base.currentModeIndex = index - replaceEnter: Transition { - PropertyAnimation { - property: "opacity" - from: 0 - to:1 - duration: 100 + onHoveredChanged: + { + if (hovered) + { + tooltipDelayTimer.item = settingsModeSelection + tooltipDelayTimer.text = model.tooltipText + tooltipDelayTimer.start() + } + else + { + tooltipDelayTimer.stop() + base.hideTooltip() + } + } + + background: Rectangle + { + border.width: control.checked ? UM.Theme.getSize("default_lining").width * 2 : UM.Theme.getSize("default_lining").width + border.color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_border") : control.hovered ? UM.Theme.getColor("action_button_hovered_border"): UM.Theme.getColor("action_button_border") + + // for some reason, QtQuick decided to use the color of the background property as text color for the contentItem, so here it is + color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active") : control.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button") + } + + contentItem: Label + { + text: model.text + font: UM.Theme.getFont("default") + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + renderType: Text.NativeRendering + elide: Text.ElideRight + color: + { + if(control.pressed) + { + return UM.Theme.getColor("action_button_active_text"); + } + else if(control.hovered) + { + return UM.Theme.getColor("action_button_hovered_text"); + } + return UM.Theme.getColor("action_button_text"); + } } } - - replaceExit: Transition { - PropertyAnimation { - property: "opacity" - from: 1 - to:0 - duration: 100 - } - } - } - - Loader - { - anchors.bottom: footerSeparator.top - anchors.top: headerSeparator.bottom - anchors.left: base.left - anchors.right: base.right - source: "SidebarContents.qml" - } - - Rectangle - { - id: footerSeparator - width: parent.width - height: UM.Theme.getSize("sidebar_lining").height - color: UM.Theme.getColor("sidebar_lining") - anchors.bottom: printSpecs.top - anchors.bottomMargin: Math.round(UM.Theme.getSize("sidebar_margin").height * 2 + UM.Theme.getSize("progressbar").height + UM.Theme.getFont("default_bold").pixelSize) } Item + { + id: sidebarContents + anchors.top: settingsModeSelection.bottom + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + anchors.left: parent.left + anchors.right: parent.right + height: 500 + + // We load both of them at once (instead of using a loader) because the advanced sidebar can take + // quite some time to load. So in this case we sacrifice memory for speed. + SidebarAdvanced + { + anchors.fill: parent + visible: currentModeIndex == 1 + } + + SidebarSimple + { + anchors.fill: parent + visible: currentModeIndex != 1 + } + } + + /*Item { id: printSpecs anchors.left: parent.left @@ -499,22 +424,23 @@ Rectangle } } } - } + }*/ // SaveButton is actually the bottom footer panel. - SaveButton + /*SaveButton { id: saveButton implicitWidth: base.width anchors.top: footerSeparator.bottom anchors.topMargin: UM.Theme.getSize("sidebar_margin").height anchors.bottom: parent.bottom - } + }*/ + /* SidebarTooltip { id: tooltip - } + }*/ // Setting mode: Recommended or Custom ListModel @@ -522,35 +448,15 @@ Rectangle id: modesListModel } - SidebarSimple - { - id: sidebarSimple - visible: false - - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() - } - - SidebarAdvanced - { - id: sidebarAdvanced - visible: false - - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() - } - Component.onCompleted: { modesListModel.append({ text: catalog.i18nc("@title:tab", "Recommended"), - tooltipText: catalog.i18nc("@tooltip", "Recommended Print Setup

Print with the recommended settings for the selected printer, material and quality."), - item: sidebarSimple + tooltipText: catalog.i18nc("@tooltip", "Recommended Print Setup

Print with the recommended settings for the selected printer, material and quality.") }) modesListModel.append({ text: catalog.i18nc("@title:tab", "Custom"), - tooltipText: catalog.i18nc("@tooltip", "Custom Print Setup

Print with finegrained control over every last bit of the slicing process."), - item: sidebarAdvanced + tooltipText: catalog.i18nc("@tooltip", "Custom Print Setup

Print with finegrained control over every last bit of the slicing process.") }) var index = Math.round(UM.Preferences.getValue("cura/active_mode")) @@ -564,24 +470,4 @@ Rectangle currentModeIndex = 0; } } - - UM.SettingPropertyProvider - { - id: machineExtruderCount - - containerStack: Cura.MachineManager.activeMachine - key: "machine_extruder_count" - watchedProperties: [ "value" ] - storeIndex: 0 - } - - UM.SettingPropertyProvider - { - id: machineHeatedBed - - containerStack: Cura.MachineManager.activeMachine - key: "machine_heated_bed" - watchedProperties: [ "value" ] - storeIndex: 0 - } } diff --git a/resources/qml/ProgressAndSaveWidget.qml b/resources/qml/ProgressAndSaveWidget.qml new file mode 100644 index 0000000000..88819af759 --- /dev/null +++ b/resources/qml/ProgressAndSaveWidget.qml @@ -0,0 +1,237 @@ +// Copyright (c) 2017 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 2.0 +import QtQuick.Layouts 1.3 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +Rectangle +{ + id: base + + // We need a whole lot of print duration information. + property variant printDuration: PrintInformation.currentPrintTime + + // This widget doesn't show tooltips by itself. Instead it emits signals so others can do something with it. + signal showTooltip(Item item, point location, string text) + signal hideTooltip() + + // Also add an extra margin, as we ant some breathing room around the edges. + height: childrenRect.height + UM.Theme.getSize("sidebar_margin").height + Label + { + id: timeDetails + anchors.left: parent.left + anchors.bottom: costSpec.top + anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width + + font: UM.Theme.getFont("large") + color: UM.Theme.getColor("text_subtext") + text: (!base.printDuration || !base.printDuration.valid) ? catalog.i18nc("@label Hours and minutes", "00h 00min") : base.printDuration.getDisplayString(UM.DurationFormat.Short) + renderType: Text.NativeRendering + + MouseArea + { + id: timeDetailsMouseArea + anchors.fill: parent + hoverEnabled: true + + onEntered: + { + if(base.printDuration.valid && !base.printDuration.isTotalDurationZero) + { + // All the time information for the different features is achieved + var print_time = PrintInformation.getFeaturePrintTimes(); + var total_seconds = parseInt(base.printDuration.getDisplayString(UM.DurationFormat.Seconds)) + + // A message is created and displayed when the user hover the time label + var tooltip_html = "%1
".arg(catalog.i18nc("@tooltip", "Time specification")); + for(var feature in print_time) + { + if(!print_time[feature].isTotalDurationZero) + { + tooltip_html += "" + + "".arg(print_time[feature].getDisplayString(UM.DurationFormat.ISO8601).slice(0,-3)) + + "".arg(Math.round(100 * parseInt(print_time[feature].getDisplayString(UM.DurationFormat.Seconds)) / total_seconds)) + + ""; + } + } + tooltip_html += "
" + feature + ":  %1  %1%
"; + //print("trying to show tooltip", base, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), tooltip_html) + base.showTooltip(parent, Qt.point(0, 0), tooltip_html); + } + } + onExited: + { + base.hideTooltip(); + } + } + } + + Label + { + function formatRow(items) + { + var row_html = ""; + for(var item = 0; item < items.length; item++) + { + if (item == 0) + { + row_html += "%1".arg(items[item]); + } + else + { + row_html += "  %1".arg(items[item]); + } + } + row_html += ""; + return row_html; + } + + function getSpecsData() + { + var lengths = []; + var total_length = 0; + var weights = []; + var total_weight = 0; + var costs = []; + var total_cost = 0; + var some_costs_known = false; + var names = []; + if(base.printMaterialLengths) + { + for(var index = 0; index < base.printMaterialLengths.length; index++) + { + if(base.printMaterialLengths[index] > 0) + { + names.push(base.printMaterialNames[index]); + lengths.push(base.printMaterialLengths[index].toFixed(2)); + weights.push(String(Math.round(base.printMaterialWeights[index]))); + var cost = base.printMaterialCosts[index] == undefined ? 0 : base.printMaterialCosts[index].toFixed(2); + costs.push(cost); + if(cost > 0) + { + some_costs_known = true; + } + + total_length += base.printMaterialLengths[index]; + total_weight += base.printMaterialWeights[index]; + total_cost += base.printMaterialCosts[index]; + } + } + } + if(lengths.length == 0) + { + lengths = ["0.00"]; + weights = ["0"]; + costs = ["0.00"]; + } + + var tooltip_html = "%1
".arg(catalog.i18nc("@label", "Cost specification")); + for(var index = 0; index < lengths.length; index++) + { + tooltip_html += formatRow([ + "%1:".arg(names[index]), + catalog.i18nc("@label m for meter", "%1m").arg(lengths[index]), + catalog.i18nc("@label g for grams", "%1g").arg(weights[index]), + "%1 %2".arg(UM.Preferences.getValue("cura/currency")).arg(costs[index]), + ]); + } + if(lengths.length > 1) + { + tooltip_html += formatRow([ + catalog.i18nc("@label", "Total:"), + catalog.i18nc("@label m for meter", "%1m").arg(total_length.toFixed(2)), + catalog.i18nc("@label g for grams", "%1g").arg(Math.round(total_weight)), + "%1 %2".arg(UM.Preferences.getValue("cura/currency")).arg(total_cost.toFixed(2)), + ]); + } + tooltip_html += "
"; + tooltipText = tooltip_html; + + return tooltipText + } + + id: costSpec + + anchors.left: parent.left + anchors.bottom: parent.bottom + anchors.bottomMargin: UM.Theme.getSize("sidebar_margin").height + anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width + + font: UM.Theme.getFont("very_small") + renderType: Text.NativeRendering + color: UM.Theme.getColor("text_subtext") + elide: Text.ElideMiddle + width: parent.width + property string tooltipText + text: + { + var lengths = []; + var weights = []; + var costs = []; + var someCostsKnown = false; + if(base.printMaterialLengths) { + for(var index = 0; index < base.printMaterialLengths.length; index++) + { + if(base.printMaterialLengths[index] > 0) + { + lengths.push(base.printMaterialLengths[index].toFixed(2)); + weights.push(String(Math.round(base.printMaterialWeights[index]))); + var cost = base.printMaterialCosts[index] == undefined ? 0 : base.printMaterialCosts[index].toFixed(2); + costs.push(cost); + if(cost > 0) + { + someCostsKnown = true; + } + } + } + } + if(lengths.length == 0) + { + lengths = ["0.00"]; + weights = ["0"]; + costs = ["0.00"]; + } + var result = lengths.join(" + ") + "m / ~ " + weights.join(" + ") + "g"; + if(someCostsKnown) + { + result += " / ~ " + costs.join(" + ") + " " + UM.Preferences.getValue("cura/currency"); + } + return result; + } + + MouseArea + { + id: costSpecMouseArea + anchors.fill: parent + hoverEnabled: true + + onEntered: + { + + if(base.printDuration.valid && !base.printDuration.isTotalDurationZero) + { + var show_data = costSpec.getSpecsData() + + base.showTooltip(parent, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), show_data); + } + } + onExited: + { + base.hideTooltip(); + } + } + } + + SaveButton + { + id: saveButton + width: parent.width + height: 100 + anchors.bottom: parent.bottom + } +} \ No newline at end of file diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml index 2a0a523026..8c561b0d00 100644 --- a/resources/qml/SaveButton.qml +++ b/resources/qml/SaveButton.qml @@ -9,7 +9,9 @@ import QtQuick.Layouts 1.1 import UM 1.1 as UM import Cura 1.0 as Cura -Item { +// This widget does so much more than "just" being a save button, so it should be refactored at some point in time. +Item +{ id: base; UM.I18nCatalog { id: catalog; name:"cura"} @@ -27,10 +29,6 @@ Item { return catalog.i18nc("@label:PrintjobStatus", "Please load a 3D model"); } - if (base.backendState == "undefined") { - return "" - } - switch(base.backendState) { case 1: @@ -48,19 +46,23 @@ Item { } } - function sliceOrStopSlicing() { + function sliceOrStopSlicing() + { try { - if ([1, 5].indexOf(base.backendState) != -1) { + if ([1, 5].indexOf(base.backendState) != -1) + { CuraApplication.backend.forceSlice(); } else { CuraApplication.backend.stopSlicing(); } - } catch (e) { + } catch (e) + { console.log('Could not start or stop slicing', e) } } - Label { + Label + { id: statusLabel width: parent.width - 2 * UM.Theme.getSize("sidebar_margin").width anchors.top: parent.top @@ -72,7 +74,8 @@ Item { text: statusText; } - Rectangle { + Rectangle + { id: progressBar width: parent.width - 2 * UM.Theme.getSize("sidebar_margin").width height: UM.Theme.getSize("progressbar").height @@ -83,32 +86,37 @@ Item { radius: UM.Theme.getSize("progressbar_radius").width color: UM.Theme.getColor("progressbar_background") - Rectangle { + Rectangle + { width: Math.max(parent.width * base.progress) height: parent.height color: UM.Theme.getColor("progressbar_control") radius: UM.Theme.getSize("progressbar_radius").width - visible: (base.backendState != "undefined" && base.backendState == 2) ? true : false + visible: base.backendState == 2 } } // Shortcut for "save as/print/..." - Action { + Action + { shortcut: "Ctrl+P" onTriggered: { // only work when the button is enabled - if (saveToButton.enabled) { + if (saveToButton.enabled) + { saveToButton.clicked(); } // prepare button - if (prepareButton.enabled) { + if (prepareButton.enabled) + { sliceOrStopSlicing(); } } } - Item { + Item + { id: saveRow width: { // using childrenRect.width directly causes a binding loop, because setting the width affects the childrenRect @@ -129,7 +137,8 @@ Item { anchors.right: parent.right clip: true - Row { + Row + { id: additionalComponentsRow anchors.top: parent.top anchors.right: saveToButton.visible ? saveToButton.left : (prepareButton.visible ? prepareButton.left : parent.right) @@ -138,24 +147,30 @@ Item { spacing: UM.Theme.getSize("default_margin").width } - Component.onCompleted: { + Component.onCompleted: + { saveRow.addAdditionalComponents("saveButton") } - Connections { + Connections + { target: CuraApplication onAdditionalComponentsChanged: saveRow.addAdditionalComponents("saveButton") } - function addAdditionalComponents (areaId) { - if(areaId == "saveButton") { - for (var component in CuraApplication.additionalComponents["saveButton"]) { + function addAdditionalComponents (areaId) + { + if(areaId == "saveButton") + { + for (var component in CuraApplication.additionalComponents["saveButton"]) + { CuraApplication.additionalComponents["saveButton"][component].parent = additionalComponentsRow } } } - Connections { + Connections + { target: UM.Preferences onPreferenceChanged: { @@ -166,13 +181,14 @@ Item { } // Prepare button, only shows if auto_slice is off - Button { + Button + { id: prepareButton tooltip: [1, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@info:tooltip","Slice current printjob") : catalog.i18nc("@info:tooltip","Cancel slicing process") // 1 = not started, 2 = Processing - enabled: base.backendState != "undefined" && ([1, 2].indexOf(base.backendState) != -1) && base.activity - visible: base.backendState != "undefined" && !autoSlice && ([1, 2, 4].indexOf(base.backendState) != -1) && base.activity + enabled: ([1, 2].indexOf(base.backendState) != -1) && base.activity + visible: !autoSlice && ([1, 2, 4].indexOf(base.backendState) != -1) && base.activity property bool autoSlice height: UM.Theme.getSize("save_button_save_to_button").height @@ -236,11 +252,12 @@ Item { text: control.text; } } - label: Item { } + label: Item {} } } - Button { + Button + { id: saveToButton tooltip: UM.OutputDeviceManager.activeDeviceDescription; @@ -262,7 +279,8 @@ Item { { "filter_by_machine": true, "preferred_mimetypes": Cura.MachineManager.activeMachine.preferred_output_file_formats }); } - style: ButtonStyle { + style: ButtonStyle + { background: Rectangle { border.width: UM.Theme.getSize("default_lining").width @@ -296,17 +314,7 @@ Item { Label { id: actualLabel anchors.centerIn: parent - color: - { - if(!control.enabled) - return UM.Theme.getColor("action_button_disabled_text"); - else if(control.pressed) - return UM.Theme.getColor("print_button_ready_text"); - else if(control.hovered) - return UM.Theme.getColor("print_button_ready_text"); - else - return UM.Theme.getColor("print_button_ready_text"); - } + color:control.enabled ? UM.Theme.getColor("print_button_ready_text") : UM.Theme.getColor("action_button_disabled_text") font: UM.Theme.getFont("action_button") text: control.text; } @@ -324,36 +332,49 @@ Item { anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width width: UM.Theme.getSize("save_button_save_to_button").height height: UM.Theme.getSize("save_button_save_to_button").height + // 3 = Done, 5 = Disabled - enabled: base.backendState != "undefined" && (base.backendState == 3 || base.backendState == 5) && base.activity == true - visible: base.backendState != "undefined" && (devicesModel.deviceCount > 1) && (base.backendState == 3 || base.backendState == 5) && base.activity == true + enabled: (base.backendState == 3 || base.backendState == 5) && base.activity == true + visible: (devicesModel.deviceCount > 1) && (base.backendState == 3 || base.backendState == 5) && base.activity == true - style: ButtonStyle { - background: Rectangle { + style: ButtonStyle + { + background: Rectangle + { id: deviceSelectionIcon border.width: UM.Theme.getSize("default_lining").width border.color: { if(!control.enabled) - return UM.Theme.getColor("action_button_disabled_border"); - else if(control.pressed) - return UM.Theme.getColor("print_button_ready_pressed_border"); - else if(control.hovered) - return UM.Theme.getColor("print_button_ready_hovered_border"); - else - return UM.Theme.getColor("print_button_ready_border"); + { + return UM.Theme.getColor("action_button_disabled_border") + } else if(control.pressed) + { + return UM.Theme.getColor("print_button_ready_pressed_border") + } else if(control.hovered) + { + return UM.Theme.getColor("print_button_ready_hovered_border") + } else + { + return UM.Theme.getColor("print_button_ready_border") + } } color: { if(!control.enabled) - return UM.Theme.getColor("action_button_disabled"); - else if(control.pressed) - return UM.Theme.getColor("print_button_ready_pressed"); - else if(control.hovered) - return UM.Theme.getColor("print_button_ready_hovered"); - else - return UM.Theme.getColor("print_button_ready"); + { + return UM.Theme.getColor("action_button_disabled") + } else if(control.pressed) + { + return UM.Theme.getColor("print_button_ready_pressed") + } else if(control.hovered) + { + return UM.Theme.getColor("print_button_ready_hovered") + } else + { + return UM.Theme.getColor("print_button_ready") + } } Behavior on color { ColorAnimation { duration: 50; } } anchors.left: parent.left @@ -361,40 +382,34 @@ Item { width: parent.height height: parent.height - UM.RecolorImage { + 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: height - color: - { - if(!control.enabled) - return UM.Theme.getColor("action_button_disabled_text"); - else if(control.pressed) - return UM.Theme.getColor("print_button_ready_text"); - else if(control.hovered) - return UM.Theme.getColor("print_button_ready_text"); - else - return UM.Theme.getColor("print_button_ready_text"); - } + color: control.enabled ? UM.Theme.getColor("print_button_ready_text") : UM.Theme.getColor("action_button_disabled_text") source: UM.Theme.getIcon("arrow_bottom"); } } - label: Label{ } } - menu: Menu { + menu: Menu + { id: devicesMenu; - Instantiator { + Instantiator + { model: devicesModel; - MenuItem { + MenuItem + { text: model.description checkable: true; checked: model.id == UM.OutputDeviceManager.activeDevice; exclusiveGroup: devicesMenuGroup; - onTriggered: { + onTriggered: + { UM.OutputDeviceManager.setActiveDevice(model.id); } } diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index da50b430ac..5a6811926e 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -25,7 +25,6 @@ Item { id: globalProfileRow height: UM.Theme.getSize("sidebar_setup").height - visible: !sidebar.hideSettings anchors { @@ -54,7 +53,6 @@ Item id: globalProfileSelection text: generateActiveQualityText() - enabled: !header.currentExtruderVisible || header.currentExtruderIndex > -1 width: Math.round(parent.width * 0.55) height: UM.Theme.getSize("setting_control").height anchors.left: globalProfileLabel.right @@ -177,9 +175,7 @@ Item right: settingVisibilityMenu.left 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 } } - + height: UM.Theme.getSize("setting_control").height Timer { id: settingsSearchTimer @@ -292,8 +288,7 @@ Item anchors.bottom: parent.bottom; anchors.right: parent.right; anchors.left: parent.left; - anchors.topMargin: filterContainer.visible ? UM.Theme.getSize("sidebar_margin").height : 0 - Behavior on anchors.topMargin { NumberAnimation { duration: 100 } } + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height style: UM.Theme.styles.scrollview; flickableItem.flickableDirection: Flickable.VerticalFlick; diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml index df4f493ea5..15ca60e6e3 100644 --- a/resources/qml/SidebarHeader.qml +++ b/resources/qml/SidebarHeader.qml @@ -90,7 +90,8 @@ Column } } - Rectangle { + Rectangle + { id: headerSeparator width: parent.width visible: printerTypeSelectionRow.visible diff --git a/resources/qml/Skeleton/TopHeader.qml b/resources/qml/Skeleton/TopHeader.qml index c24fe86af0..f08a0aaca3 100644 --- a/resources/qml/Skeleton/TopHeader.qml +++ b/resources/qml/Skeleton/TopHeader.qml @@ -15,7 +15,8 @@ Rectangle { id: base - height: UM.Theme.getSize("topheader").height + implicitHeight: UM.Theme.getSize("topheader").height + implicitWidth: UM.Theme.getSize("topheader").width color: UM.Theme.getColor("topheader_background") Image From 2fce1e433a4c732462310c24c63970752e6fcd43 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 15 Oct 2018 09:49:08 +0200 Subject: [PATCH 0023/1240] Fix the tooltips for time estimations CURA-5772 --- resources/qml/ProgressAndSaveWidget.qml | 3 +-- resources/qml/SidebarTooltip.qml | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/resources/qml/ProgressAndSaveWidget.qml b/resources/qml/ProgressAndSaveWidget.qml index 88819af759..2fa5cf6149 100644 --- a/resources/qml/ProgressAndSaveWidget.qml +++ b/resources/qml/ProgressAndSaveWidget.qml @@ -60,8 +60,7 @@ Rectangle } } tooltip_html += ""; - //print("trying to show tooltip", base, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), tooltip_html) - base.showTooltip(parent, Qt.point(0, 0), tooltip_html); + base.showTooltip(parent, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), tooltip_html); } } onExited: diff --git a/resources/qml/SidebarTooltip.qml b/resources/qml/SidebarTooltip.qml index 29199481f6..4fa4ef9dd7 100644 --- a/resources/qml/SidebarTooltip.qml +++ b/resources/qml/SidebarTooltip.qml @@ -36,7 +36,7 @@ UM.PointingRectangle { } } base.opacity = 1; - target = Qt.point(40 , position.y + Math.round(UM.Theme.getSize("tooltip_arrow_margins").height / 2)) + target = Qt.point(position.x + 1, position.y + Math.round(UM.Theme.getSize("tooltip_arrow_margins").height / 2)) } function hide() { From fc1faf79f5dec5dc9271e5ee7fe89079161dae5a Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 15 Oct 2018 09:52:48 +0200 Subject: [PATCH 0024/1240] Cleanup unused code CURA-5772 --- resources/qml/Cura.qml | 118 +---------------------------------------- 1 file changed, 2 insertions(+), 116 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index f89f8d63dd..8955cb9598 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -21,7 +21,6 @@ UM.MainWindow // Cura application window title title: catalog.i18nc("@title:window", "Ultimaker Cura") - //viewportRect: Qt.rect(0, 0, (base.width - sidebar.width) / base.width, 1.0) viewportRect: Qt.rect(0, 0, 1.0, 1.0) backgroundColor: UM.Theme.getColor("viewport_background") @@ -43,24 +42,6 @@ UM.MainWindow tooltip.hide(); } - /*onWidthChanged: - { - // If slidebar is collapsed then it should be invisible - // otherwise after the main_window resize the sidebar will be fully re-drawn - if (sidebar.collapsed){ - if (sidebar.visible == true){ - sidebar.visible = false - sidebar.initialWidth = 0 - } - } - else{ - if (sidebar.visible == false){ - sidebar.visible = true - sidebar.initialWidth = UM.Theme.getSize("sidebar").width - } - } - }*/ - Component.onCompleted: { CuraApplication.setMinimumWindowSize(UM.Theme.getSize("window_minimum_size")) @@ -193,14 +174,11 @@ UM.MainWindow anchors { bottom: parent.bottom - //right: sidebar.left bottomMargin: UM.Theme.getSize("default_margin").height rightMargin: UM.Theme.getSize("default_margin").width } } - - Toolbar { id: toolbar @@ -226,18 +204,6 @@ UM.MainWindow } } - /*ApplicationViews - { - id: applicationViews - - - anchors - { - top: parent.top - right: sidebar.left - } - }*/ - Loader { id: main @@ -261,78 +227,6 @@ UM.MainWindow source: UM.Controller.activeStage.mainComponent } - /*Loader - { - id: sidebar - - property bool collapsed: false - property var initialWidth: UM.Theme.getSize("sidebar").width - - function callExpandOrCollapse() - { - if (collapsed) - { - sidebar.visible = true - sidebar.initialWidth = UM.Theme.getSize("sidebar").width - viewportRect = Qt.rect(0, 0, (base.width - sidebar.width) / base.width, 1.0) - expandSidebarAnimation.start(); - } - else - { - viewportRect = Qt.rect(0, 0, 1, 1.0) - collapseSidebarAnimation.start() - } - collapsed = !collapsed - UM.Preferences.setValue("cura/sidebar_collapsed", collapsed) - } - - anchors - { - top: parent.top - bottom: parent.bottom - } - - width: initialWidth - x: base.width - sidebar.width - source: UM.Controller.activeStage.sidebarComponent - - NumberAnimation { - id: collapseSidebarAnimation - target: sidebar - properties: "x" - to: base.width - duration: 100 - } - - NumberAnimation { - id: expandSidebarAnimation - target: sidebar - properties: "x" - to: base.width - sidebar.width - duration: 100 - } - - Component.onCompleted: - { - var sidebar_collapsed = UM.Preferences.getValue("cura/sidebar_collapsed") - - if (sidebar_collapsed) - { - sidebar.collapsed = true - viewportRect = Qt.rect(0, 0, 1, 1.0) - collapseSidebarAnimation.start() - } - } - - MouseArea - { - visible: UM.Controller.activeStage.sidebarComponent != "" - anchors.fill: parent - acceptedButtons: Qt.AllButtons - onWheel: wheel.accepted = true - } - }*/ - UM.MessageStack { anchors @@ -345,7 +239,6 @@ UM.MainWindow } } - ViewOrientationControls { id: viewOrientationControls @@ -359,7 +252,6 @@ UM.MainWindow } } - ProgressAndSaveWidget { anchors.right: parent.right @@ -379,13 +271,6 @@ UM.MainWindow } } - // Expand or collapse sidebar - /*Connections - { - target: Cura.Actions.expandSidebar - onTriggered: sidebar.callExpandOrCollapse() - }*/ - UM.PreferencesDialog { id: preferences @@ -509,7 +394,8 @@ UM.MainWindow } } - ContextMenu { + ContextMenu + { id: contextMenu } From 8cac5e1de214b34ad39328fe93b29a9bfe270afe Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 15 Oct 2018 10:26:27 +0200 Subject: [PATCH 0025/1240] Removed the components qml dir We can consider putting components into their own folder, but we first should have a clear definition of what they are / should be. CURA-5772 --- resources/qml/Account/AccountDetails.qml | 2 -- resources/qml/Account/GeneralOperations.qml | 6 ++---- resources/qml/Account/UserOperations.qml | 6 ++---- resources/qml/{components => }/ActionButton.qml | 0 resources/qml/Cura.qml | 1 - .../{components => }/MachineAndConfigurationSelector.qml | 5 ++--- resources/qml/ProfileAndSettingComponent.qml | 2 -- resources/qml/Skeleton/ApplicationViews.qml | 1 - resources/qml/Skeleton/TopHeader.qml | 1 - resources/qml/{components => }/ViewOrientationControls.qml | 0 resources/qml/qmldir | 5 +++-- 11 files changed, 9 insertions(+), 20 deletions(-) rename resources/qml/{components => }/ActionButton.qml (100%) rename resources/qml/{components => }/MachineAndConfigurationSelector.qml (94%) rename resources/qml/{components => }/ViewOrientationControls.qml (100%) diff --git a/resources/qml/Account/AccountDetails.qml b/resources/qml/Account/AccountDetails.qml index ab1ccc15df..4d6b0a314b 100644 --- a/resources/qml/Account/AccountDetails.qml +++ b/resources/qml/Account/AccountDetails.qml @@ -7,8 +7,6 @@ import QtQuick.Controls 2.1 import UM 1.4 as UM import Cura 1.1 as Cura -import "../components" - Column { property var profile: null diff --git a/resources/qml/Account/GeneralOperations.qml b/resources/qml/Account/GeneralOperations.qml index d224de44bb..1f3cd51f26 100644 --- a/resources/qml/Account/GeneralOperations.qml +++ b/resources/qml/Account/GeneralOperations.qml @@ -7,13 +7,11 @@ import QtQuick.Controls 1.1 import UM 1.4 as UM import Cura 1.1 as Cura -import "../components" - Row { spacing: UM.Theme.getSize("default_margin").width - ActionButton + Cura.ActionButton { width: UM.Theme.getSize("account_button").width height: UM.Theme.getSize("account_button").height @@ -25,7 +23,7 @@ Row onClicked: Qt.openUrlExternally("https://account.ultimaker.com") } - ActionButton + Cura.ActionButton { width: UM.Theme.getSize("account_button").width height: UM.Theme.getSize("account_button").height diff --git a/resources/qml/Account/UserOperations.qml b/resources/qml/Account/UserOperations.qml index 1704254ed3..ffcf518b6e 100644 --- a/resources/qml/Account/UserOperations.qml +++ b/resources/qml/Account/UserOperations.qml @@ -7,13 +7,11 @@ import QtQuick.Controls 1.1 import UM 1.4 as UM import Cura 1.1 as Cura -import "../components" - Row { spacing: UM.Theme.getSize("default_margin").width - ActionButton + Cura.ActionButton { width: UM.Theme.getSize("account_button").width height: UM.Theme.getSize("account_button").height @@ -25,7 +23,7 @@ Row onClicked: Qt.openUrlExternally("https://account.ultimaker.com") } - ActionButton + Cura.ActionButton { width: UM.Theme.getSize("account_button").width height: UM.Theme.getSize("account_button").height diff --git a/resources/qml/components/ActionButton.qml b/resources/qml/ActionButton.qml similarity index 100% rename from resources/qml/components/ActionButton.qml rename to resources/qml/ActionButton.qml diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 8955cb9598..304f78e633 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -13,7 +13,6 @@ import Cura 1.1 as Cura import "Dialogs" import "Menus" import "Skeleton" -import "components" UM.MainWindow { diff --git a/resources/qml/components/MachineAndConfigurationSelector.qml b/resources/qml/MachineAndConfigurationSelector.qml similarity index 94% rename from resources/qml/components/MachineAndConfigurationSelector.qml rename to resources/qml/MachineAndConfigurationSelector.qml index 5b919c42d8..15b9ca6dd6 100644 --- a/resources/qml/components/MachineAndConfigurationSelector.qml +++ b/resources/qml/MachineAndConfigurationSelector.qml @@ -5,9 +5,8 @@ import QtQuick 2.7 import UM 1.2 as UM import Cura 1.0 as Cura -import "../Menus" -import "../Menus/ConfigurationMenu" -import ".." +import "Menus" +import "Menus/ConfigurationMenu" Rectangle { diff --git a/resources/qml/ProfileAndSettingComponent.qml b/resources/qml/ProfileAndSettingComponent.qml index 33c2f37fb1..dbded90576 100644 --- a/resources/qml/ProfileAndSettingComponent.qml +++ b/resources/qml/ProfileAndSettingComponent.qml @@ -9,8 +9,6 @@ import UM 1.2 as UM import Cura 1.0 as Cura import "Menus" import "Menus/ConfigurationMenu" -import "components" - Rectangle { diff --git a/resources/qml/Skeleton/ApplicationViews.qml b/resources/qml/Skeleton/ApplicationViews.qml index eb8ab16fae..d91837a2db 100644 --- a/resources/qml/Skeleton/ApplicationViews.qml +++ b/resources/qml/Skeleton/ApplicationViews.qml @@ -9,7 +9,6 @@ import QtQuick.Layouts 1.1 import UM 1.4 as UM import Cura 1.0 as Cura -import "../components" // This item contains the views selector, a combobox that is dynamically created from // the list of available Views (packages that create different visualizations of the diff --git a/resources/qml/Skeleton/TopHeader.qml b/resources/qml/Skeleton/TopHeader.qml index f08a0aaca3..7344d01436 100644 --- a/resources/qml/Skeleton/TopHeader.qml +++ b/resources/qml/Skeleton/TopHeader.qml @@ -8,7 +8,6 @@ import QtQuick.Controls.Styles 1.1 import UM 1.4 as UM import Cura 1.0 as Cura -import "../components" import "../Account" Rectangle diff --git a/resources/qml/components/ViewOrientationControls.qml b/resources/qml/ViewOrientationControls.qml similarity index 100% rename from resources/qml/components/ViewOrientationControls.qml rename to resources/qml/ViewOrientationControls.qml diff --git a/resources/qml/qmldir b/resources/qml/qmldir index 5083b5e80a..b9306b14bc 100644 --- a/resources/qml/qmldir +++ b/resources/qml/qmldir @@ -1,5 +1,6 @@ module Cura -MachineAndConfigurationSelector 1.0 components/MachineAndConfigurationSelector.qml +MachineAndConfigurationSelector 1.0 MachineAndConfigurationSelector.qml MaterialAndVariantSelector 1.0 MaterialAndVariantSelector.qml -ProfileAndSettingComponent 1.0 ProfileAndSettingComponent.qml \ No newline at end of file +ProfileAndSettingComponent 1.0 ProfileAndSettingComponent.qml +ActionButton 1.0 ActionButton.qml \ No newline at end of file From d2baef97c4e2d014884e22537330e4ecc4b34cf5 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 15 Oct 2018 11:07:03 +0200 Subject: [PATCH 0026/1240] Fix tooltips for settings CURA-5772 --- plugins/PrepareStage/PrepareMenu.qml | 12 +- resources/qml/Cura.qml | 8 +- resources/qml/ProfileAndSettingComponent.qml | 246 +------------------ 3 files changed, 24 insertions(+), 242 deletions(-) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index eb09326524..353c4df51a 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -10,6 +10,10 @@ Row { spacing: UM.Theme.getSize("default_margin").width + // This widget doesn't show tooltips by itself. Instead it emits signals so others can do something with it. + signal showTooltip(Item item, point location, string text) + signal hideTooltip() + UM.I18nCatalog { id: catalog @@ -18,12 +22,12 @@ Row Button { - id: openFileButton; - text: catalog.i18nc("@action:button", "Open File"); + id: openFileButton + text: catalog.i18nc("@action:button", "Open File") iconSource: UM.Theme.getIcon("load") style: UM.Theme.styles.tool_button tooltip: "" - action: Cura.Actions.open; + action: Cura.Actions.open } Cura.MachineAndConfigurationSelector @@ -38,5 +42,7 @@ Row Cura.ProfileAndSettingComponent { width: UM.Theme.getSize("sidebar").width + onShowTooltip: parent.showTooltip(item, location, text) + onHideTooltip: parent.hideTooltip() } } \ No newline at end of file diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 304f78e633..97cdc94d64 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -163,10 +163,16 @@ UM.MainWindow } height: 50 - source: UM.Controller.activeStage.stageMenuComponent } + Connections + { + target: stageMenu.item + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltop: base.hideTooltip() + } + JobSpecs { id: jobSpecs diff --git a/resources/qml/ProfileAndSettingComponent.qml b/resources/qml/ProfileAndSettingComponent.qml index dbded90576..b367c1130f 100644 --- a/resources/qml/ProfileAndSettingComponent.qml +++ b/resources/qml/ProfileAndSettingComponent.qml @@ -28,6 +28,10 @@ Rectangle color: UM.Theme.getColor("sidebar") UM.I18nCatalog { id: catalog; name:"cura"} + // This widget doesn't show tooltips by itself. Instead it emits signals so others can do something with it. + signal showTooltip(Item item, point location, string text) + signal hideTooltip() + Timer { id: tooltipDelayTimer @@ -42,18 +46,6 @@ Rectangle } } - function showTooltip(item, position, text) - { - tooltip.text = text; - position = item.mapToItem(base, position.x - UM.Theme.getSize("default_arrow").width, position.y); - tooltip.show(position); - } - - function hideTooltip() - { - tooltip.hide(); - } - function strPadLeft(string, pad, length) { return (new Array(length + 1).join(pad) + string).slice(-length); @@ -205,241 +197,19 @@ Rectangle { anchors.fill: parent visible: currentModeIndex == 1 + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() } SidebarSimple { anchors.fill: parent visible: currentModeIndex != 1 + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() } } - /*Item - { - id: printSpecs - anchors.left: parent.left - anchors.bottom: parent.bottom - anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width - anchors.bottomMargin: UM.Theme.getSize("sidebar_margin").height - height: timeDetails.height + costSpec.height - width: base.width - (saveButton.buttonRowWidth + UM.Theme.getSize("sidebar_margin").width) - - Label - { - id: timeDetails - anchors.left: parent.left - anchors.bottom: costSpec.top - font: UM.Theme.getFont("large") - color: UM.Theme.getColor("text_subtext") - text: (!base.printDuration || !base.printDuration.valid) ? catalog.i18nc("@label Hours and minutes", "00h 00min") : base.printDuration.getDisplayString(UM.DurationFormat.Short) - renderType: Text.NativeRendering - - MouseArea - { - id: timeDetailsMouseArea - anchors.fill: parent - hoverEnabled: true - - onEntered: - { - if(base.printDuration.valid && !base.printDuration.isTotalDurationZero) - { - // All the time information for the different features is achieved - var print_time = PrintInformation.getFeaturePrintTimes(); - var total_seconds = parseInt(base.printDuration.getDisplayString(UM.DurationFormat.Seconds)) - - // A message is created and displayed when the user hover the time label - var tooltip_html = "%1
".arg(catalog.i18nc("@tooltip", "Time specification")); - for(var feature in print_time) - { - if(!print_time[feature].isTotalDurationZero) - { - tooltip_html += "" + - "".arg(print_time[feature].getDisplayString(UM.DurationFormat.ISO8601).slice(0,-3)) + - "".arg(Math.round(100 * parseInt(print_time[feature].getDisplayString(UM.DurationFormat.Seconds)) / total_seconds)) + - ""; - } - } - tooltip_html += "
" + feature + ":  %1  %1%
"; - - base.showTooltip(parent, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), tooltip_html); - } - } - onExited: - { - base.hideTooltip(); - } - } - } - - Label - { - function formatRow(items) - { - var row_html = ""; - for(var item = 0; item < items.length; item++) - { - if (item == 0) - { - row_html += "%1".arg(items[item]); - } - else - { - row_html += "  %1".arg(items[item]); - } - } - row_html += ""; - return row_html; - } - - function getSpecsData() - { - var lengths = []; - var total_length = 0; - var weights = []; - var total_weight = 0; - var costs = []; - var total_cost = 0; - var some_costs_known = false; - var names = []; - if(base.printMaterialLengths) - { - for(var index = 0; index < base.printMaterialLengths.length; index++) - { - if(base.printMaterialLengths[index] > 0) - { - names.push(base.printMaterialNames[index]); - lengths.push(base.printMaterialLengths[index].toFixed(2)); - weights.push(String(Math.round(base.printMaterialWeights[index]))); - var cost = base.printMaterialCosts[index] == undefined ? 0 : base.printMaterialCosts[index].toFixed(2); - costs.push(cost); - if(cost > 0) - { - some_costs_known = true; - } - - total_length += base.printMaterialLengths[index]; - total_weight += base.printMaterialWeights[index]; - total_cost += base.printMaterialCosts[index]; - } - } - } - if(lengths.length == 0) - { - lengths = ["0.00"]; - weights = ["0"]; - costs = ["0.00"]; - } - - var tooltip_html = "%1
".arg(catalog.i18nc("@label", "Cost specification")); - for(var index = 0; index < lengths.length; index++) - { - tooltip_html += formatRow([ - "%1:".arg(names[index]), - catalog.i18nc("@label m for meter", "%1m").arg(lengths[index]), - catalog.i18nc("@label g for grams", "%1g").arg(weights[index]), - "%1 %2".arg(UM.Preferences.getValue("cura/currency")).arg(costs[index]), - ]); - } - if(lengths.length > 1) - { - tooltip_html += formatRow([ - catalog.i18nc("@label", "Total:"), - catalog.i18nc("@label m for meter", "%1m").arg(total_length.toFixed(2)), - catalog.i18nc("@label g for grams", "%1g").arg(Math.round(total_weight)), - "%1 %2".arg(UM.Preferences.getValue("cura/currency")).arg(total_cost.toFixed(2)), - ]); - } - tooltip_html += "
"; - tooltipText = tooltip_html; - - return tooltipText - } - - id: costSpec - anchors.left: parent.left - anchors.bottom: parent.bottom - font: UM.Theme.getFont("very_small") - renderType: Text.NativeRendering - color: UM.Theme.getColor("text_subtext") - elide: Text.ElideMiddle - width: parent.width - property string tooltipText - text: - { - var lengths = []; - var weights = []; - var costs = []; - var someCostsKnown = false; - if(base.printMaterialLengths) { - for(var index = 0; index < base.printMaterialLengths.length; index++) - { - if(base.printMaterialLengths[index] > 0) - { - lengths.push(base.printMaterialLengths[index].toFixed(2)); - weights.push(String(Math.round(base.printMaterialWeights[index]))); - var cost = base.printMaterialCosts[index] == undefined ? 0 : base.printMaterialCosts[index].toFixed(2); - costs.push(cost); - if(cost > 0) - { - someCostsKnown = true; - } - } - } - } - if(lengths.length == 0) - { - lengths = ["0.00"]; - weights = ["0"]; - costs = ["0.00"]; - } - var result = lengths.join(" + ") + "m / ~ " + weights.join(" + ") + "g"; - if(someCostsKnown) - { - result += " / ~ " + costs.join(" + ") + " " + UM.Preferences.getValue("cura/currency"); - } - return result; - } - MouseArea - { - id: costSpecMouseArea - anchors.fill: parent - hoverEnabled: true - - onEntered: - { - - if(base.printDuration.valid && !base.printDuration.isTotalDurationZero) - { - var show_data = costSpec.getSpecsData() - - base.showTooltip(parent, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), show_data); - } - } - onExited: - { - base.hideTooltip(); - } - } - } - }*/ - - // SaveButton is actually the bottom footer panel. - /*SaveButton - { - id: saveButton - implicitWidth: base.width - anchors.top: footerSeparator.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - anchors.bottom: parent.bottom - }*/ - - /* - SidebarTooltip - { - id: tooltip - }*/ - // Setting mode: Recommended or Custom ListModel { From 0a1dad92ea50beeab3bdf7fc487e4d48e1169142 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 15 Oct 2018 13:41:44 +0200 Subject: [PATCH 0027/1240] Change AvatarImage so it uses aliases instead of properties I've changed this because it makes for much cleaner QML CURA-5772 --- resources/qml/Account/AvatarImage.qml | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/resources/qml/Account/AvatarImage.qml b/resources/qml/Account/AvatarImage.qml index 96c3cd2330..0e0d182018 100644 --- a/resources/qml/Account/AvatarImage.qml +++ b/resources/qml/Account/AvatarImage.qml @@ -7,19 +7,19 @@ import UM 1.4 as UM Item { + // This item shows the provided image while applying a round mask to it. + // It also shows a round border around it. The color is defined by the outlineColor property. + id: avatar - property var source - property var fallbackSource: UM.Theme.getImage("avatar_default") - property var outlineColor: UM.Theme.getColor("account_widget_ouline_active") + property alias source: profileImage.source + property alias outlineColor: profileImageOutline.color Image { id: profileImage - source: avatar.source ? avatar.source : UM.Theme.getImage("avatar_default") - sourceSize: Qt.size(parent.width, parent.height) - width: parent.width - height: parent.height + source: UM.Theme.getImage("avatar_default") + anchors.fill: parent fillMode: Image.PreserveAspectCrop visible: false } @@ -29,8 +29,7 @@ Item id: profileImageMask source: UM.Theme.getIcon("circle_mask") sourceSize: Qt.size(parent.width, parent.height) - width: parent.width - height: parent.height + anchors.fill: parent color: UM.Theme.getColor("topheader_background") visible: false } @@ -49,8 +48,7 @@ Item id: profileImageOutline source: UM.Theme.getIcon("circle_outline") sourceSize: Qt.size(parent.width, parent.height) - width: parent.width - height: parent.height - color: avatar.outlineColor + anchors.fill: parent + color: UM.Theme.getColor("account_widget_ouline_active") } } \ No newline at end of file From 62c99804381a0d8647791b888cf163499a22a5ca Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 15 Oct 2018 14:07:01 +0200 Subject: [PATCH 0028/1240] Move SidebarTooltip so that it's no longer ocluded by other objects CURA-5772 --- resources/qml/Cura.qml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 97cdc94d64..bf71bd8d94 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -63,11 +63,6 @@ UM.MainWindow id: backgroundItem anchors.fill: parent - SidebarTooltip - { - id: tooltip - } - signal hasMesh(string name) //this signal sends the filebase name so it can be used for the JobSpecs.qml function getMeshName(path) { @@ -274,6 +269,11 @@ UM.MainWindow } } } + + SidebarTooltip + { + id: tooltip + } } UM.PreferencesDialog From f6ae19003ba77a5da9d8a8a90016422f0b9ef297 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 15 Oct 2018 14:07:30 +0200 Subject: [PATCH 0029/1240] The AccountDetails Panel is now an actual popup This ensures that it's always visible (previously it was ocluded by the StageMenu) CURA-5772 --- resources/qml/Account/AccountWidget.qml | 65 ++++++++++++------------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/resources/qml/Account/AccountWidget.qml b/resources/qml/Account/AccountWidget.qml index 1d9541de2c..1b4e6de1b2 100644 --- a/resources/qml/Account/AccountWidget.qml +++ b/resources/qml/Account/AccountWidget.qml @@ -7,62 +7,59 @@ import QtQuick.Controls 2.1 import UM 1.4 as UM import Cura 1.1 as Cura -Item +Button { id: accountWidget property var profile: Cura.API.account.userProfile property var loggedIn: Cura.API.account.isLoggedIn - height: UM.Theme.getSize("topheader").height - width: UM.Theme.getSize("topheader").height + + implicitHeight: UM.Theme.getSize("topheader").height + implicitWidth: UM.Theme.getSize("topheader").height + AvatarImage { id: avatar + + width: Math.round(0.8 * parent.width) height: Math.round(0.8 * parent.height) anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter + source: loggedIn ? profile["profile_image_url"] : UM.Theme.getImage("avatar_default") outlineColor: loggedIn ? UM.Theme.getColor("account_widget_ouline_active") : UM.Theme.getColor("account_widget_ouline_inactive") } - MouseArea + background: Item {} + + onClicked: popup.open() + + Popup { - anchors.fill: parent - onClicked: accountManagementPanel.visible = !accountManagementPanel.visible // Collapse/Expand the dropdown panel - } + id: popup - UM.PointingRectangle - { - id: accountManagementPanel + y: parent.height + UM.Theme.getSize("default_arrow").height + x: (parent.width - width) - width: panel.width - height: panel.height - - anchors - { - top: parent.bottom - topMargin: UM.Theme.getSize("default_margin").height - right: parent.right - } - - target: Qt.point(parent.width / 2, parent.bottom) - arrowSize: UM.Theme.getSize("default_arrow").width - - visible: false - opacity: visible ? 1 : 0 - Behavior on opacity { NumberAnimation { duration: 100 } } - - color: UM.Theme.getColor("tool_panel_background") - borderColor: UM.Theme.getColor("lining") - borderWidth: UM.Theme.getSize("default_lining").width - - // Shows the user management options or general options to create account - AccountDetails + contentItem: AccountDetails { id: panel profile: Cura.API.account.userProfile loggedIn: Cura.API.account.isLoggedIn } + + background: UM.PointingRectangle + { + opacity: visible ? 1 : 0 + Behavior on opacity { NumberAnimation { duration: 100 } } + color: UM.Theme.getColor("tool_panel_background") + borderColor: UM.Theme.getColor("lining") + borderWidth: UM.Theme.getSize("default_lining").width + + target: Qt.point(width - (accountWidget.width / 2), -10) + + arrowSize: UM.Theme.getSize("default_arrow").width + } } -} \ No newline at end of file +} From 4e432a5f06c5726f4c83e8127636470867274631 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 15 Oct 2018 14:21:03 +0200 Subject: [PATCH 0030/1240] Remove SidebarHeader It has been replaced by others, so we don't need it anymore CURA-5772 --- resources/qml/SidebarHeader.qml | 618 -------------------------------- 1 file changed, 618 deletions(-) delete mode 100644 resources/qml/SidebarHeader.qml diff --git a/resources/qml/SidebarHeader.qml b/resources/qml/SidebarHeader.qml deleted file mode 100644 index 15ca60e6e3..0000000000 --- a/resources/qml/SidebarHeader.qml +++ /dev/null @@ -1,618 +0,0 @@ -// Copyright (c) 2017 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.7 -import QtQuick.Controls 1.1 -import QtQuick.Controls.Styles 1.1 - -import UM 1.2 as UM -import Cura 1.0 as Cura - -import "Menus" - -Column -{ - id: base; - - property int currentExtruderIndex: Cura.ExtruderManager.activeExtruderIndex; - property bool currentExtruderVisible: extrudersList.visible; - property bool printerConnected: Cura.MachineManager.printerConnected - property bool hasManyPrinterTypes: - { - if (printerConnected) - { - if (Cura.MachineManager.printerOutputDevices[0].connectedPrintersTypeCount != null) - { - return Cura.MachineManager.printerOutputDevices[0].connectedPrintersTypeCount.length > 1; - } - } - return false; - } - property bool buildplateCompatibilityError: !Cura.MachineManager.variantBuildplateCompatible && !Cura.MachineManager.variantBuildplateUsable - property bool buildplateCompatibilityWarning: Cura.MachineManager.variantBuildplateUsable - - spacing: Math.round(UM.Theme.getSize("sidebar_margin").width * 0.9) - - signal showTooltip(Item item, point location, string text) - signal hideTooltip() - - Item - { - id: initialSeparator - anchors - { - left: parent.left - right: parent.right - } - visible: printerTypeSelectionRow.visible || buildplateRow.visible || extruderSelectionRow.visible - height: UM.Theme.getSize("default_lining").height - width: height - } - - // Printer Type Row - Item - { - id: printerTypeSelectionRow - height: UM.Theme.getSize("sidebar_setup").height - visible: printerConnected && hasManyPrinterTypes && !sidebar.hideSettings - - anchors - { - left: parent.left - leftMargin: UM.Theme.getSize("sidebar_margin").width - right: parent.right - rightMargin: UM.Theme.getSize("sidebar_margin").width - } - - Label - { - id: configurationLabel - text: catalog.i18nc("@label", "Printer type"); - width: Math.round(parent.width * 0.4 - UM.Theme.getSize("default_margin").width) - height: parent.height - verticalAlignment: Text.AlignVCenter - font: UM.Theme.getFont("default"); - color: UM.Theme.getColor("text"); - } - - ToolButton - { - id: printerTypeSelection - text: Cura.MachineManager.activeMachineDefinitionName - tooltip: Cura.MachineManager.activeMachineDefinitionName - height: UM.Theme.getSize("setting_control").height - width: Math.round(parent.width * 0.7) + UM.Theme.getSize("sidebar_margin").width - anchors.right: parent.right - style: UM.Theme.styles.sidebar_header_button - activeFocusOnPress: true; - - menu: PrinterTypeMenu { } - } - } - - Rectangle - { - id: headerSeparator - width: parent.width - visible: printerTypeSelectionRow.visible - height: visible ? UM.Theme.getSize("sidebar_lining").height : 0 - color: UM.Theme.getColor("sidebar_lining") - } - - // Extruder Row - Item - { - id: extruderSelectionRow - width: parent.width - height: Math.round(UM.Theme.getSize("sidebar_tabs").height * 2 / 3) - visible: machineExtruderCount.properties.value > 1 - - anchors - { - left: parent.left - leftMargin: Math.round(UM.Theme.getSize("sidebar_margin").width * 0.7) - right: parent.right - rightMargin: Math.round(UM.Theme.getSize("sidebar_margin").width * 0.7) - topMargin: UM.Theme.getSize("sidebar_margin").height - } - - ListView - { - id: extrudersList - property var index: 0 - - height: UM.Theme.getSize("sidebar_header_mode_tabs").height - width: Math.round(parent.width) - boundsBehavior: Flickable.StopAtBounds - - anchors - { - left: parent.left - leftMargin: Math.round(UM.Theme.getSize("default_margin").width / 2) - right: parent.right - rightMargin: Math.round(UM.Theme.getSize("default_margin").width / 2) - verticalCenter: parent.verticalCenter - } - - ExclusiveGroup { id: extruderMenuGroup; } - - orientation: ListView.Horizontal - - model: Cura.ExtrudersModel { id: extrudersModel; } - - Connections - { - target: Cura.MachineManager - onGlobalContainerChanged: forceActiveFocus() // Changing focus applies the currently-being-typed values so it can change the displayed setting values. - } - - delegate: Button - { - height: ListView.view.height - width: Math.round(ListView.view.width / extrudersModel.rowCount()) - - text: model.name - tooltip: model.name - exclusiveGroup: extruderMenuGroup - checked: base.currentExtruderIndex == index - - property bool extruder_enabled: true - - MouseArea - { - anchors.fill: parent - acceptedButtons: Qt.LeftButton | Qt.RightButton - onClicked: { - switch (mouse.button) { - case Qt.LeftButton: - extruder_enabled = Cura.MachineManager.getExtruder(model.index).isEnabled - if (extruder_enabled) - { - forceActiveFocus(); // Changing focus applies the currently-being-typed values so it can change the displayed setting values. - Cura.ExtruderManager.setActiveExtruderIndex(index); - } - break; - case Qt.RightButton: - extruder_enabled = Cura.MachineManager.getExtruder(model.index).isEnabled - extruderMenu.popup(); - break; - } - - } - } - - Menu - { - id: extruderMenu - - MenuItem { - text: catalog.i18nc("@action:inmenu", "Enable Extruder") - onTriggered: Cura.MachineManager.setExtruderEnabled(model.index, true) - visible: !extruder_enabled // using an intermediate variable prevents an empty popup that occured now and then - } - - MenuItem { - text: catalog.i18nc("@action:inmenu", "Disable Extruder") - onTriggered: Cura.MachineManager.setExtruderEnabled(model.index, false) - visible: extruder_enabled - enabled: Cura.MachineManager.numberExtrudersEnabled > 1 - } - } - - style: ButtonStyle - { - background: Item - { - function buttonBackgroundColor(index) - { - var extruder = Cura.MachineManager.getExtruder(index) - if (extruder.isEnabled) { - return (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active") : - control.hovered ? UM.Theme.getColor("action_button_hovered") : - UM.Theme.getColor("action_button") - } else { - return UM.Theme.getColor("action_button_disabled") - } - } - - function buttonBorderColor(index) - { - var extruder = Cura.MachineManager.getExtruder(index) - if (extruder.isEnabled) { - return (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_border") : - control.hovered ? UM.Theme.getColor("action_button_hovered_border") : - UM.Theme.getColor("action_button_border") - } else { - return UM.Theme.getColor("action_button_disabled_border") - } - } - - function buttonColor(index) { - var extruder = Cura.MachineManager.getExtruder(index); - if (extruder.isEnabled) - { - return ( - control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_text") : - control.hovered ? UM.Theme.getColor("action_button_hovered_text") : - UM.Theme.getColor("action_button_text"); - } else { - return UM.Theme.getColor("action_button_disabled_text"); - } - } - - Rectangle - { - anchors.fill: parent - border.width: control.checked ? UM.Theme.getSize("default_lining").width * 2 : UM.Theme.getSize("default_lining").width - border.color: buttonBorderColor(index) - color: buttonBackgroundColor(index) - Behavior on color { ColorAnimation { duration: 50; } } - } - - Item - { - id: extruderButtonFace - anchors.centerIn: parent - - width: { - var extruderTextWidth = extruderStaticText.visible ? extruderStaticText.width : 0; - var iconWidth = extruderIconItem.width; - return Math.round(extruderTextWidth + iconWidth + UM.Theme.getSize("default_margin").width / 2); - } - - // Static text "Extruder" - Label - { - id: extruderStaticText - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - - color: buttonColor(index) - - font: UM.Theme.getFont("large_nonbold") - text: catalog.i18nc("@label", "Extruder") - visible: width < (control.width - extruderIconItem.width - UM.Theme.getSize("default_margin").width) - elide: Text.ElideRight - } - - // Everything for the extruder icon - Item - { - id: extruderIconItem - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - - property var sizeToUse: - { - var minimumWidth = control.width < UM.Theme.getSize("button").width ? control.width : UM.Theme.getSize("button").width; - var minimumHeight = control.height < UM.Theme.getSize("button").height ? control.height : UM.Theme.getSize("button").height; - var minimumSize = minimumWidth < minimumHeight ? minimumWidth : minimumHeight; - minimumSize -= Math.round(UM.Theme.getSize("default_margin").width / 2); - return minimumSize; - } - - width: sizeToUse - height: sizeToUse - - UM.RecolorImage { - id: mainCircle - anchors.fill: parent - - sourceSize.width: parent.width - sourceSize.height: parent.width - source: UM.Theme.getIcon("extruder_button") - - color: extruderNumberText.color - } - - Label - { - id: extruderNumberText - anchors.centerIn: parent - text: index + 1; - color: buttonColor(index) - font: UM.Theme.getFont("default_bold") - } - - // Material colour circle - // Only draw the filling colour of the material inside the SVG border. - Rectangle - { - id: materialColorCircle - - anchors - { - right: parent.right - top: parent.top - rightMargin: Math.round(parent.sizeToUse * 0.01) - topMargin: Math.round(parent.sizeToUse * 0.05) - } - - color: model.color - - width: Math.round(parent.width * 0.35) - height: Math.round(parent.height * 0.35) - radius: Math.round(width / 2) - - border.width: 1 - border.color: UM.Theme.getColor("extruder_button_material_border") - - opacity: !control.checked ? 0.6 : 1.0 - } - } - } - } - label: Item {} - } - } - } - } - - Item - { - id: variantRowSpacer - height: Math.round(UM.Theme.getSize("sidebar_margin").height / 4) - width: height - visible: !extruderSelectionRow.visible && !initialSeparator.visible - } - - // Material Row - Item - { - id: materialRow - height: UM.Theme.getSize("sidebar_setup").height - visible: Cura.MachineManager.hasMaterials && !sidebar.hideSettings - - anchors - { - left: parent.left - leftMargin: UM.Theme.getSize("sidebar_margin").width - right: parent.right - rightMargin: UM.Theme.getSize("sidebar_margin").width - } - - Label - { - id: materialLabel - text: catalog.i18nc("@label", "Material"); - width: Math.round(parent.width * 0.45 - UM.Theme.getSize("default_margin").width) - height: parent.height - verticalAlignment: Text.AlignVCenter - font: UM.Theme.getFont("default"); - color: UM.Theme.getColor("text"); - } - - ToolButton - { - id: materialSelection - - property var activeExtruder: Cura.MachineManager.activeStack - property var hasActiveExtruder: activeExtruder != null - property var currentRootMaterialName: hasActiveExtruder ? activeExtruder.material.name : "" - - text: currentRootMaterialName - tooltip: currentRootMaterialName - visible: Cura.MachineManager.hasMaterials - enabled: !extrudersList.visible || base.currentExtruderIndex > -1 - height: UM.Theme.getSize("setting_control").height - width: Math.round(parent.width * 0.7) + UM.Theme.getSize("sidebar_margin").width - anchors.right: parent.right - style: UM.Theme.styles.sidebar_header_button - activeFocusOnPress: true; - menu: MaterialMenu - { - extruderIndex: base.currentExtruderIndex - } - - property var valueError: !isMaterialSupported() - property var valueWarning: ! Cura.MachineManager.isActiveQualitySupported - - function isMaterialSupported () - { - if (!hasActiveExtruder) - { - return false; - } - return Cura.ContainerManager.getContainerMetaDataEntry(activeExtruder.material.id, "compatible", "") == "True" - } - } - } - - //Variant row - Item - { - id: variantRow - height: UM.Theme.getSize("sidebar_setup").height - visible: Cura.MachineManager.hasVariants && !sidebar.hideSettings - - anchors - { - left: parent.left - leftMargin: UM.Theme.getSize("sidebar_margin").width - right: parent.right - rightMargin: UM.Theme.getSize("sidebar_margin").width - } - - Label - { - id: variantLabel - text: Cura.MachineManager.activeDefinitionVariantsName; - width: Math.round(parent.width * 0.45 - UM.Theme.getSize("default_margin").width) - height: parent.height - verticalAlignment: Text.AlignVCenter - font: UM.Theme.getFont("default"); - color: UM.Theme.getColor("text"); - } - - ToolButton - { - id: variantSelection - text: Cura.MachineManager.activeVariantName - tooltip: Cura.MachineManager.activeVariantName; - visible: Cura.MachineManager.hasVariants - - height: UM.Theme.getSize("setting_control").height - width: Math.round(parent.width * 0.7 + UM.Theme.getSize("sidebar_margin").width) - anchors.right: parent.right - style: UM.Theme.styles.sidebar_header_button - activeFocusOnPress: true; - - menu: NozzleMenu { extruderIndex: base.currentExtruderIndex } - } - } - - Rectangle - { - id: buildplateSeparator - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width - width: parent.width - 2 * UM.Theme.getSize("sidebar_margin").width - visible: buildplateRow.visible - height: visible ? UM.Theme.getSize("sidebar_lining_thin").height : 0 - color: UM.Theme.getColor("sidebar_lining") - } - - //Buildplate row - Item - { - id: buildplateRow - height: UM.Theme.getSize("sidebar_setup").height - // TODO Only show in dev mode. Remove check when feature ready - visible: CuraSDKVersion == "dev" ? Cura.MachineManager.hasVariantBuildplates && !sidebar.hideSettings : false - - anchors - { - left: parent.left - leftMargin: UM.Theme.getSize("sidebar_margin").width - right: parent.right - rightMargin: UM.Theme.getSize("sidebar_margin").width - } - - Label - { - id: bulidplateLabel - text: catalog.i18nc("@label", "Build plate"); - width: Math.floor(parent.width * 0.45 - UM.Theme.getSize("default_margin").width) - height: parent.height - verticalAlignment: Text.AlignVCenter - font: UM.Theme.getFont("default"); - color: UM.Theme.getColor("text"); - } - - ToolButton - { - id: buildplateSelection - text: Cura.MachineManager.activeVariantBuildplateName - tooltip: Cura.MachineManager.activeVariantBuildplateName - visible: Cura.MachineManager.hasVariantBuildplates - - height: UM.Theme.getSize("setting_control").height - width: Math.floor(parent.width * 0.7 + UM.Theme.getSize("sidebar_margin").width) - anchors.right: parent.right - style: UM.Theme.styles.sidebar_header_button - activeFocusOnPress: true; - - menu: BuildplateMenu {} - - property var valueError: !Cura.MachineManager.variantBuildplateCompatible && !Cura.MachineManager.variantBuildplateUsable - property var valueWarning: Cura.MachineManager.variantBuildplateUsable - } - } - - // Material info row - Item - { - id: materialInfoRow - height: Math.round(UM.Theme.getSize("sidebar_setup").height / 2) - visible: (Cura.MachineManager.hasVariants || Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariantBuildplates) && !sidebar.hideSettings - - anchors - { - left: parent.left - leftMargin: UM.Theme.getSize("sidebar_margin").width - right: parent.right - rightMargin: UM.Theme.getSize("sidebar_margin").width - } - - // TODO This was added to replace the buildplate selector. Remove this component when the feature is ready - Label - { - id: materialCompatibilityLabel - y: -Math.round(UM.Theme.getSize("sidebar_margin").height / 3) - anchors.left: parent.left - width: parent.width - materialCompatibilityLink.width - text: catalog.i18nc("@label", "Use glue with this material combination") - font: UM.Theme.getFont("very_small") - color: UM.Theme.getColor("text") - visible: CuraSDKVersion == "dev" ? false : buildplateCompatibilityError || buildplateCompatibilityWarning - wrapMode: Text.WordWrap - opacity: 0.5 - } - - Item - { - id: materialCompatibilityLink - height: UM.Theme.getSize("sidebar_setup").height - anchors.right: parent.right - width: childrenRect.width + UM.Theme.getSize("default_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 - sourceSize.width: width - sourceSize.height: height - color: UM.Theme.getColor("material_compatibility_warning") - visible: !Cura.MachineManager.isCurrentSetupSupported || buildplateCompatibilityError || buildplateCompatibilityWarning - } - - 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 url = "https://ultimaker.com/incoming-links/cura/material-compatibilty" - 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 - - containerStack: Cura.MachineManager.activeMachine - key: "machine_extruder_count" - watchedProperties: [ "value" ] - storeIndex: 0 - } - - UM.I18nCatalog { id: catalog; name:"cura" } -} From 1e3a9ff57cabc85ef2cea5c0346d620e57f48493 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 15 Oct 2018 14:23:42 +0200 Subject: [PATCH 0031/1240] Removed the expandSidebar action Since we don't even have a sidebar anymore, there is no point in expanding it. --- resources/qml/Actions.qml | 9 --------- resources/qml/Menus/ViewMenu.qml | 6 ++---- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index 1b1881ab76..30295f6e9b 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -23,8 +23,6 @@ Item property alias viewLeftSideCamera: viewLeftSideCameraAction; property alias viewRightSideCamera: viewRightSideCameraAction; - property alias expandSidebar: expandSidebarAction; - property alias deleteSelection: deleteSelectionAction; property alias centerSelection: centerSelectionAction; property alias multiplySelection: multiplySelectionAction; @@ -415,11 +413,4 @@ Item text: catalog.i18nc("@action:menu", "Browse packages...") iconName: "plugins_browse" } - - Action - { - id: expandSidebarAction; - text: catalog.i18nc("@action:inmenu menubar:view","Expand/Collapse Sidebar"); - shortcut: "Ctrl+E"; - } } diff --git a/resources/qml/Menus/ViewMenu.qml b/resources/qml/Menus/ViewMenu.qml index 9a2e603673..593bd63bcc 100644 --- a/resources/qml/Menus/ViewMenu.qml +++ b/resources/qml/Menus/ViewMenu.qml @@ -44,7 +44,8 @@ Menu MenuItem { action: Cura.Actions.viewRightSideCamera; } } - MenuSeparator { + MenuSeparator + { visible: UM.Preferences.getValue("cura/use_multi_build_plate") } @@ -72,8 +73,5 @@ Menu MenuSeparator {} - MenuItem { action: Cura.Actions.expandSidebar; } - - MenuSeparator {} MenuItem { action: Cura.Actions.toggleFullScreen; } } From a473a46a35c360605226c0cf71f7efed51ced0fc Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 15 Oct 2018 15:07:46 +0200 Subject: [PATCH 0032/1240] Add a bit more documentation to explain what the elements do CURA-5772 --- resources/qml/Cura.qml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index bf71bd8d94..df5995de39 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -114,6 +114,7 @@ UM.MainWindow DropArea { + // The drop area is here to handle files being dropped onto Cura. anchors.fill: parent onDropped: { @@ -181,6 +182,8 @@ UM.MainWindow Toolbar { + // The toolbar is the left bar that is populated by all the tools (which are dynamicly populated by + // plugins) id: toolbar property int mouseX: base.mouseX @@ -206,6 +209,7 @@ UM.MainWindow Loader { + // A stage can control this area. If nothing is set, it will therefor show the 3D view. id: main anchors From a7edd893d796df3c9537ca58c4794f1aaac7b43e Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 15 Oct 2018 15:08:58 +0200 Subject: [PATCH 0033/1240] Change the order in which ViewOrientationControls and ProgressAndSaveWidget are drawn. This will ensure that if a stage sets a mainComponent that it won't have any other items drawn over it. CURA-5772 --- resources/qml/Cura.qml | 60 +++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index df5995de39..39443f9b0b 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -207,6 +207,36 @@ UM.MainWindow } } + ViewOrientationControls + { + id: viewOrientationControls + + anchors + { + left: parent.left + margins: UM.Theme.getSize("default_margin").width + + bottom: parent.bottom + } + } + + ProgressAndSaveWidget + { + anchors.right: parent.right + anchors.bottom: parent.bottom + width: UM.Theme.getSize("sidebar").width + anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width + anchors.bottomMargin: UM.Theme.getSize("sidebar_margin").height + onShowTooltip: + { + base.showTooltip(item, location, text) + } + onHideTooltip: + { + base.hideTooltip() + } + } + Loader { // A stage can control this area. If nothing is set, it will therefor show the 3D view. @@ -242,36 +272,6 @@ UM.MainWindow bottomMargin: UM.Theme.getSize("default_margin").height } } - - ViewOrientationControls - { - id: viewOrientationControls - - anchors - { - left: parent.left - margins: UM.Theme.getSize("default_margin").width - - bottom: parent.bottom - } - } - - ProgressAndSaveWidget - { - anchors.right: parent.right - anchors.bottom: parent.bottom - width: UM.Theme.getSize("sidebar").width - anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width - anchors.bottomMargin: UM.Theme.getSize("sidebar_margin").height - onShowTooltip: - { - base.showTooltip(item, location, text) - } - onHideTooltip: - { - base.hideTooltip() - } - } } SidebarTooltip From 1c70a62df67918acb63ff233daa650c506f2ec7c Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 15 Oct 2018 15:10:50 +0200 Subject: [PATCH 0034/1240] Fix typo Derp. CURA-5772 --- resources/qml/Cura.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 39443f9b0b..517affe1b1 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -166,7 +166,7 @@ UM.MainWindow { target: stageMenu.item onShowTooltip: base.showTooltip(item, location, text) - onHideTooltop: base.hideTooltip() + onHideTooltip: base.hideTooltip() } JobSpecs From cd5a0a84a80ae0de2ed518cc18e3e19a67c88f79 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 15 Oct 2018 15:14:05 +0200 Subject: [PATCH 0035/1240] Horizontally align the prepare menu bar CURA-5772 --- plugins/PrepareStage/PrepareMenu.qml | 51 +++++++++++++++------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index 353c4df51a..ea247bf258 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -6,10 +6,9 @@ import UM 1.3 as UM import Cura 1.1 as Cura -Row +Item { - spacing: UM.Theme.getSize("default_margin").width - + id: prepareMenu // This widget doesn't show tooltips by itself. Instead it emits signals so others can do something with it. signal showTooltip(Item item, point location, string text) signal hideTooltip() @@ -20,29 +19,35 @@ Row name:"cura" } - Button + Row { - id: openFileButton - text: catalog.i18nc("@action:button", "Open File") - iconSource: UM.Theme.getIcon("load") - style: UM.Theme.styles.tool_button - tooltip: "" - action: Cura.Actions.open - } + spacing: UM.Theme.getSize("default_margin").width + anchors.horizontalCenter: parent.horizontalCenter - Cura.MachineAndConfigurationSelector - { - } + Button + { + id: openFileButton + text: catalog.i18nc("@action:button", "Open File") + iconSource: UM.Theme.getIcon("load") + style: UM.Theme.styles.tool_button + tooltip: "" + action: Cura.Actions.open + } - Cura.MaterialAndVariantSelector - { - width: UM.Theme.getSize("sidebar").width - } + Cura.MachineAndConfigurationSelector + { + } - Cura.ProfileAndSettingComponent - { - width: UM.Theme.getSize("sidebar").width - onShowTooltip: parent.showTooltip(item, location, text) - onHideTooltip: parent.hideTooltip() + Cura.MaterialAndVariantSelector + { + width: UM.Theme.getSize("sidebar").width + } + + Cura.ProfileAndSettingComponent + { + width: UM.Theme.getSize("sidebar").width + onShowTooltip: prepareMenu.showTooltip(item, location, text) + onHideTooltip: prepareMenu.hideTooltip() + } } } \ No newline at end of file From 2455961ac7c63dba6fa5970506fd0b3e79310c91 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 15 Oct 2018 15:16:28 +0200 Subject: [PATCH 0036/1240] Vertically allign the toolbar CURA-5772 --- resources/qml/Cura.qml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 517affe1b1..bb94435c69 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -189,9 +189,9 @@ UM.MainWindow property int mouseX: base.mouseX property int mouseY: base.mouseY - anchors { - top: stageMenu.bottom - topMargin: UM.Theme.getSize("window_margin").height + anchors + { + verticalCenter: parent.verticalCenter left: parent.left } } From 553e29b9b801d8c6679382f2fe29b4a17a4da72d Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 15 Oct 2018 15:32:50 +0200 Subject: [PATCH 0037/1240] Fix QML warnings CURA-5772 --- resources/qml/Cura.qml | 6 +++--- resources/qml/MaterialAndVariantSelector.qml | 5 ++++- resources/qml/ProgressAndSaveWidget.qml | 2 +- resources/qml/Skeleton/TopHeader.qml | 1 - 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index bb94435c69..a3746a557b 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -159,7 +159,7 @@ UM.MainWindow } height: 50 - source: UM.Controller.activeStage.stageMenuComponent + source: UM.Controller.activeStage != null ? UM.Controller.activeStage.stageMenuComponent : "" } Connections @@ -252,13 +252,13 @@ UM.MainWindow MouseArea { - visible: UM.Controller.activeStage.mainComponent != "" + visible: parent.source != "" anchors.fill: parent acceptedButtons: Qt.AllButtons onWheel: wheel.accepted = true } - source: UM.Controller.activeStage.mainComponent + source: UM.Controller.activeStage != null ? UM.Controller.activeStage.mainComponent : "" } UM.MessageStack diff --git a/resources/qml/MaterialAndVariantSelector.qml b/resources/qml/MaterialAndVariantSelector.qml index fff3ef1100..31a898f8ae 100644 --- a/resources/qml/MaterialAndVariantSelector.qml +++ b/resources/qml/MaterialAndVariantSelector.qml @@ -174,15 +174,18 @@ Rectangle font: UM.Theme.getFont("large_nonbold") text: catalog.i18nc("@label", "Extruder") - visible: width < (control.width - extruderIconItem.width - UM.Theme.getSize("default_margin").width) + visible: width < (control.width - extruderIcon.width - UM.Theme.getSize("default_margin").width) elide: Text.ElideRight } ExtruderIcon { // Round icon with the extruder number and material color indicator. + id: extruderIcon + anchors.verticalCenter: parent.verticalCenter anchors.left: extruderStaticText.right + anchors.leftMargin: UM.Theme.getSize("default_margin").width width: control.height - Math.round(UM.Theme.getSize("default_margin").width / 2) height: width diff --git a/resources/qml/ProgressAndSaveWidget.qml b/resources/qml/ProgressAndSaveWidget.qml index 2fa5cf6149..1e82681164 100644 --- a/resources/qml/ProgressAndSaveWidget.qml +++ b/resources/qml/ProgressAndSaveWidget.qml @@ -20,7 +20,7 @@ Rectangle signal hideTooltip() // Also add an extra margin, as we ant some breathing room around the edges. - height: childrenRect.height + UM.Theme.getSize("sidebar_margin").height + height: saveButton.height + UM.Theme.getSize("sidebar_margin").height Label { id: timeDetails diff --git a/resources/qml/Skeleton/TopHeader.qml b/resources/qml/Skeleton/TopHeader.qml index 7344d01436..867a6bafbb 100644 --- a/resources/qml/Skeleton/TopHeader.qml +++ b/resources/qml/Skeleton/TopHeader.qml @@ -93,7 +93,6 @@ Rectangle color: UM.Theme.getColor("topheader_button_text_active") font: UM.Theme.getFont("action_button") renderType: Text.NativeRendering - anchors.verticalCenter: control.verticalCenter } } From 304a9e11f3978dd4f2a30c649598583e0ddee5ee Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 17 Oct 2018 09:50:24 +0200 Subject: [PATCH 0038/1240] Moved view selection back into Cura QML It didn't make a whole lot of sense for it to be a seperate component, since it's probably going to change in the future CURA-5772 --- resources/qml/Cura.qml | 77 +++++++++++++++ resources/qml/Skeleton/ApplicationViews.qml | 101 -------------------- 2 files changed, 77 insertions(+), 101 deletions(-) delete mode 100644 resources/qml/Skeleton/ApplicationViews.qml diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index a3746a557b..a999e7deac 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -220,6 +220,83 @@ UM.MainWindow } } + ComboBox + { + // This item contains the views selector, a combobox that is dynamically created from + // the list of available Views (packages that create different visualizations of the + // scene). + id: viewModeButton + + anchors.left: viewOrientationControls.right + anchors.bottom: viewOrientationControls.bottom + + style: UM.Theme.styles.combobox + + model: UM.ViewModel { } + textRole: "name" + + // update the model's active index + function updateItemActiveFlags () + { + currentIndex = getActiveIndex() + for (var i = 0; i < model.rowCount(); i++) + { + model.getItem(i).active = (i == currentIndex) + } + } + + // get the index of the active model item on start + function getActiveIndex () + { + for (var i = 0; i < model.rowCount(); i++) + { + if (model.getItem(i).active) + { + return i; + } + } + return 0 + } + + // set the active index + function setActiveIndex(index) + { + UM.Controller.setActiveView(index) + // the connection to UM.ActiveView will trigger update so there is no reason to call it manually here + } + + onCurrentIndexChanged: + { + if (model.getItem(currentIndex).id != undefined) + { + viewModeButton.setActiveIndex(model.getItem(currentIndex).id) + } + } + currentIndex: getActiveIndex() + + // watch the active view proxy for changes made from the menu item + Connections + { + target: UM.ActiveView + onActiveViewChanged: viewModeButton.updateItemActiveFlags() + } + } + Loader + { + id: viewPanel + + anchors.bottom: viewModeButton.top + anchors.topMargin: UM.Theme.getSize("default_margin").height + anchors.right: viewModeButton.right + + property var buttonTarget: Qt.point(viewModeButton.x + Math.round(viewModeButton.width / 2), viewModeButton.y + Math.round(viewModeButton.height / 2)) + + height: childrenRect.height + width: childrenRect.width + + source: UM.ActiveView.valid ? UM.ActiveView.activeViewPanel : "" + } + ProgressAndSaveWidget { anchors.right: parent.right diff --git a/resources/qml/Skeleton/ApplicationViews.qml b/resources/qml/Skeleton/ApplicationViews.qml deleted file mode 100644 index d91837a2db..0000000000 --- a/resources/qml/Skeleton/ApplicationViews.qml +++ /dev/null @@ -1,101 +0,0 @@ -// 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.Controls.Styles 1.1 -import QtQuick.Layouts 1.1 - -import UM 1.4 as UM -import Cura 1.0 as Cura - - -// This item contains the views selector, a combobox that is dynamically created from -// the list of available Views (packages that create different visualizations of the -// scene). Aside from the selector, there is a row of buttons that change the orientation of the view. -Item -{ - id: applicationViewsSelector - - height: UM.Theme.getSize("views_selector").height - - - ComboBox - { - id: viewModeButton - - anchors - { - verticalCenter: parent.verticalCenter - right: parent.right - rightMargin: UM.Theme.getSize("default_margin").width - } - - style: UM.Theme.styles.combobox - - model: UM.ViewModel { } - textRole: "name" - - // update the model's active index - function updateItemActiveFlags () - { - currentIndex = getActiveIndex() - for (var i = 0; i < model.rowCount(); i++) - { - model.getItem(i).active = (i == currentIndex) - } - } - - // get the index of the active model item on start - function getActiveIndex () - { - for (var i = 0; i < model.rowCount(); i++) - { - if (model.getItem(i).active) - { - return i; - } - } - return 0 - } - - // set the active index - function setActiveIndex(index) - { - UM.Controller.setActiveView(index) - // the connection to UM.ActiveView will trigger update so there is no reason to call it manually here - } - - onCurrentIndexChanged: - { - if (model.getItem(currentIndex).id != undefined) - { - viewModeButton.setActiveIndex(model.getItem(currentIndex).id) - } - } - currentIndex: getActiveIndex() - - // watch the active view proxy for changes made from the menu item - Connections - { - target: UM.ActiveView - onActiveViewChanged: viewModeButton.updateItemActiveFlags() - } - } - - Loader - { - id: viewPanel - - anchors.top: viewModeButton.bottom - anchors.topMargin: UM.Theme.getSize("default_margin").height - anchors.right: viewModeButton.right - - property var buttonTarget: Qt.point(viewModeButton.x + Math.round(viewModeButton.width / 2), viewModeButton.y + Math.round(viewModeButton.height / 2)) - - height: childrenRect.height - width: childrenRect.width - - source: UM.ActiveView.valid ? UM.ActiveView.activeViewPanel : "" - } -} From 7fb72a1a58ad0d7522f53dbd00f57e44f829590c Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 17 Oct 2018 16:13:43 +0200 Subject: [PATCH 0039/1240] Change Skeleton folder name to MainWindow. This folder is intended to store the files that construct the main window. All the parts. The rest of the components are stored in more specific folders. Contributes to CURA-5784. --- resources/qml/Cura.qml | 2 +- resources/qml/{Skeleton => MainWindow}/ApplicationMenu.qml | 0 resources/qml/{Skeleton => MainWindow}/TopHeader.qml | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename resources/qml/{Skeleton => MainWindow}/ApplicationMenu.qml (100%) rename resources/qml/{Skeleton => MainWindow}/TopHeader.qml (100%) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index a999e7deac..a58e7ef6fc 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -12,7 +12,7 @@ import Cura 1.1 as Cura import "Dialogs" import "Menus" -import "Skeleton" +import "MainWindow" UM.MainWindow { diff --git a/resources/qml/Skeleton/ApplicationMenu.qml b/resources/qml/MainWindow/ApplicationMenu.qml similarity index 100% rename from resources/qml/Skeleton/ApplicationMenu.qml rename to resources/qml/MainWindow/ApplicationMenu.qml diff --git a/resources/qml/Skeleton/TopHeader.qml b/resources/qml/MainWindow/TopHeader.qml similarity index 100% rename from resources/qml/Skeleton/TopHeader.qml rename to resources/qml/MainWindow/TopHeader.qml From 54554bff8920ee3e9ab85df0302a4d9e0d030722 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 18 Oct 2018 09:51:04 +0200 Subject: [PATCH 0040/1240] Adjust colors to work with dark theme. Contributes to CURA-5784. --- resources/qml/Account/AccountDetails.qml | 3 + .../cura-dark/icons/tab_status_unknown.svg | 13 ---- resources/themes/cura-dark/images/logo.svg | 72 ------------------- resources/themes/cura-dark/theme.json | 2 +- 4 files changed, 4 insertions(+), 86 deletions(-) delete mode 100644 resources/themes/cura-dark/icons/tab_status_unknown.svg delete mode 100644 resources/themes/cura-dark/images/logo.svg diff --git a/resources/qml/Account/AccountDetails.qml b/resources/qml/Account/AccountDetails.qml index 4d6b0a314b..0ce82570a5 100644 --- a/resources/qml/Account/AccountDetails.qml +++ b/resources/qml/Account/AccountDetails.qml @@ -31,6 +31,7 @@ Column anchors.horizontalCenter: parent.horizontalCenter visible: !loggedIn text: catalog.i18nc("@label", "Please login or create an account to 
enjoy all features of Ultimaker Cura") + color: UM.Theme.getColor("text") } Column @@ -44,6 +45,7 @@ Column anchors.horizontalCenter: parent.horizontalCenter text: loggedIn ? profile["username"] : "" font: UM.Theme.getFont("large") + color: UM.Theme.getColor("text") } Label @@ -51,6 +53,7 @@ Column anchors.horizontalCenter: parent.horizontalCenter text: "email.address@hardcoded.is" font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") } } diff --git a/resources/themes/cura-dark/icons/tab_status_unknown.svg b/resources/themes/cura-dark/icons/tab_status_unknown.svg deleted file mode 100644 index d20218bc00..0000000000 --- a/resources/themes/cura-dark/icons/tab_status_unknown.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - Unknown - Created with Sketch. - - - - - - - - \ No newline at end of file diff --git a/resources/themes/cura-dark/images/logo.svg b/resources/themes/cura-dark/images/logo.svg deleted file mode 100644 index 92ffe4ca0c..0000000000 --- a/resources/themes/cura-dark/images/logo.svg +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - diff --git a/resources/themes/cura-dark/theme.json b/resources/themes/cura-dark/theme.json index fa0da2851e..99737ff8bf 100644 --- a/resources/themes/cura-dark/theme.json +++ b/resources/themes/cura-dark/theme.json @@ -17,7 +17,7 @@ "topheader_background": [31, 36, 39, 255], - "topheader_button_text_active": [255, 255, 255, 255], + "topheader_button_text_active": [31, 36, 39, 255], "topheader_button_text_inactive": [128, 128, 128, 255], "topheader_button_text_hovered": [255, 255, 255, 255], From 11693aad36a86ca56843f9bcf5bbbd787f8685b7 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 18 Oct 2018 09:51:42 +0200 Subject: [PATCH 0041/1240] Change the behaviour of the Popup, that now closes when clicking the widget if it is open. Contributes to CURA-5784. --- resources/qml/Account/AccountWidget.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/qml/Account/AccountWidget.qml b/resources/qml/Account/AccountWidget.qml index 1b4e6de1b2..d1e285e1da 100644 --- a/resources/qml/Account/AccountWidget.qml +++ b/resources/qml/Account/AccountWidget.qml @@ -33,7 +33,7 @@ Button background: Item {} - onClicked: popup.open() + onClicked: popup.opened ? popup.close() : popup.open() Popup { @@ -42,6 +42,8 @@ Button y: parent.height + UM.Theme.getSize("default_arrow").height x: (parent.width - width) + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent + contentItem: AccountDetails { id: panel From a4c609d514a7687b318613d862894c67dce83b1d Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 18 Oct 2018 14:28:00 +0200 Subject: [PATCH 0042/1240] Remove email label from the account widget. It's not needed and it's not currently public data that is published with the user token. Contributes to CURA-5784. --- cura/OAuth2/Models.py | 2 ++ resources/qml/Account/AccountDetails.qml | 28 +++--------------------- 2 files changed, 5 insertions(+), 25 deletions(-) diff --git a/cura/OAuth2/Models.py b/cura/OAuth2/Models.py index 83fc22554f..0515e789e6 100644 --- a/cura/OAuth2/Models.py +++ b/cura/OAuth2/Models.py @@ -1,4 +1,6 @@ # Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + from typing import Optional diff --git a/resources/qml/Account/AccountDetails.qml b/resources/qml/Account/AccountDetails.qml index 0ce82570a5..27860cfae5 100644 --- a/resources/qml/Account/AccountDetails.qml +++ b/resources/qml/Account/AccountDetails.qml @@ -27,36 +27,14 @@ Column Label { - id: message + id: information anchors.horizontalCenter: parent.horizontalCenter visible: !loggedIn - text: catalog.i18nc("@label", "Please login or create an account to 
enjoy all features of Ultimaker Cura") + text: loggedIn ? profile["username"] : catalog.i18nc("@label", "Please login or create an account to 
enjoy all features of Ultimaker Cura") + font: loggedIn ? UM.Theme.getFont("large") : UM.Theme.getFont("default") color: UM.Theme.getColor("text") } - Column - { - id: userInformation - anchors.horizontalCenter: parent.horizontalCenter - visible: loggedIn - - Label - { - anchors.horizontalCenter: parent.horizontalCenter - text: loggedIn ? profile["username"] : "" - font: UM.Theme.getFont("large") - color: UM.Theme.getColor("text") - } - - Label - { - anchors.horizontalCenter: parent.horizontalCenter - text: "email.address@hardcoded.is" - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - } - } - Loader { id: accountEntryPoints From 1bcd134f85c2f70df31f8e61706cec8636ed3790 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 18 Oct 2018 14:30:11 +0200 Subject: [PATCH 0043/1240] Fix code style Most stuff here was recently changed. Some of it was where I found a pattern in something that was likely copy-pasted from somewhere else, so I did a global search and replace on that. Contributes to issue CURA-5784. --- .../FirmwareUpdaterMachineAction.qml | 2 +- plugins/ImageReader/ConfigUI.qml | 2 +- plugins/ModelChecker/ModelChecker.qml | 2 +- .../PostProcessingPlugin.qml | 2 +- plugins/PrepareStage/PrepareMenu.qml | 2 +- plugins/Toolbox/resources/qml/Toolbox.qml | 2 +- .../resources/qml/DiscoverUM3Action.qml | 2 +- .../resources/qml/UM3InfoComponents.qml | 2 +- .../UMOCheckupMachineAction.qml | 2 +- plugins/UserAgreement/UserAgreement.qml | 2 +- resources/qml/Account/AccountWidget.qml | 2 +- resources/qml/Actions.qml | 4 +- resources/qml/Cura.qml | 6 +- resources/qml/Dialogs/AboutDialog.qml | 52 +-- resources/qml/JobSpecs.qml | 4 +- resources/qml/MainWindow/ApplicationMenu.qml | 9 +- resources/qml/MaterialAndVariantSelector.qml | 25 +- resources/qml/MonitorButton.qml | 2 +- resources/qml/MonitorSidebar.qml | 6 +- resources/qml/Preferences/GeneralPage.qml | 4 +- resources/qml/ProfileAndSettingComponent.qml | 8 +- resources/qml/ProgressAndSaveWidget.qml | 5 +- resources/qml/SaveButton.qml | 100 +++- resources/themes/cura-light/styles.qml | 429 +++++++++++------- 24 files changed, 433 insertions(+), 243 deletions(-) diff --git a/plugins/FirmwareUpdater/FirmwareUpdaterMachineAction.qml b/plugins/FirmwareUpdater/FirmwareUpdaterMachineAction.qml index 9a56dbb20a..b5b6c15f50 100644 --- a/plugins/FirmwareUpdater/FirmwareUpdaterMachineAction.qml +++ b/plugins/FirmwareUpdater/FirmwareUpdaterMachineAction.qml @@ -22,7 +22,7 @@ Cura.MachineAction { id: firmwareUpdaterMachineAction anchors.fill: parent; - UM.I18nCatalog { id: catalog; name:"cura"} + UM.I18nCatalog { id: catalog; name: "cura"} spacing: UM.Theme.getSize("default_margin").height Label diff --git a/plugins/ImageReader/ConfigUI.qml b/plugins/ImageReader/ConfigUI.qml index 12c6aa8dde..b9ff2e4453 100644 --- a/plugins/ImageReader/ConfigUI.qml +++ b/plugins/ImageReader/ConfigUI.qml @@ -20,7 +20,7 @@ UM.Dialog GridLayout { - UM.I18nCatalog{id: catalog; name:"cura"} + UM.I18nCatalog{id: catalog; name: "cura"} anchors.fill: parent; Layout.fillWidth: true columnSpacing: 16 * screenScaleFactor diff --git a/plugins/ModelChecker/ModelChecker.qml b/plugins/ModelChecker/ModelChecker.qml index 98db233bf8..5e41591d6b 100644 --- a/plugins/ModelChecker/ModelChecker.qml +++ b/plugins/ModelChecker/ModelChecker.qml @@ -16,7 +16,7 @@ Button { id: modelCheckerButton - UM.I18nCatalog{id: catalog; name:"cura"} + UM.I18nCatalog{id: catalog; name: "cura"} visible: manager.hasWarnings tooltip: catalog.i18nc("@info:tooltip", "Some things could be problematic in this print. Click to see tips for adjustment.") diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml index d492e06462..10cc4cc082 100644 --- a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml +++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml @@ -31,7 +31,7 @@ UM.Dialog Item { - UM.I18nCatalog{id: catalog; name:"cura"} + UM.I18nCatalog{id: catalog; name: "cura"} id: base property int columnWidth: Math.round((base.width / 2) - UM.Theme.getSize("default_margin").width) property int textMargin: Math.round(UM.Theme.getSize("default_margin").width / 2) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index ea247bf258..208e7d18df 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -16,7 +16,7 @@ Item UM.I18nCatalog { id: catalog - name:"cura" + name: "cura" } Row diff --git a/plugins/Toolbox/resources/qml/Toolbox.qml b/plugins/Toolbox/resources/qml/Toolbox.qml index 4fb8192d81..29b026058c 100644 --- a/plugins/Toolbox/resources/qml/Toolbox.qml +++ b/plugins/Toolbox/resources/qml/Toolbox.qml @@ -24,7 +24,7 @@ Window UM.I18nCatalog { id: catalog - name:"cura" + name: "cura" } Item { diff --git a/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml b/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml index b5b80a3010..773c38a454 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml @@ -54,7 +54,7 @@ Cura.MachineAction spacing: UM.Theme.getSize("default_margin").height SystemPalette { id: palette } - UM.I18nCatalog { id: catalog; name:"cura" } + UM.I18nCatalog { id: catalog; name: "cura" } Label { id: pageTitle diff --git a/plugins/UM3NetworkPrinting/resources/qml/UM3InfoComponents.qml b/plugins/UM3NetworkPrinting/resources/qml/UM3InfoComponents.qml index a19d1be60d..d84c58daef 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/UM3InfoComponents.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/UM3InfoComponents.qml @@ -121,5 +121,5 @@ Item } } - UM.I18nCatalog{id: catalog; name:"cura"} + UM.I18nCatalog{id: catalog; name: "cura"} } diff --git a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml index 4a1d42e248..2a01cfaa40 100644 --- a/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml +++ b/plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml @@ -19,7 +19,7 @@ Cura.MachineAction property bool heatupBedStarted: false property bool printerConnected: Cura.MachineManager.printerConnected - UM.I18nCatalog { id: catalog; name:"cura"} + UM.I18nCatalog { id: catalog; name: "cura"} Label { id: pageTitle diff --git a/plugins/UserAgreement/UserAgreement.qml b/plugins/UserAgreement/UserAgreement.qml index 4ee03f4ad5..2e5893fc41 100644 --- a/plugins/UserAgreement/UserAgreement.qml +++ b/plugins/UserAgreement/UserAgreement.qml @@ -36,7 +36,7 @@ UM.Dialog width: parent.width anchors.bottomMargin: UM.Theme.getSize("default_margin").height - UM.I18nCatalog { id: catalog; name:"cura" } + UM.I18nCatalog { id: catalog; name: "cura" } Button { diff --git a/resources/qml/Account/AccountWidget.qml b/resources/qml/Account/AccountWidget.qml index d1e285e1da..a6a79a7d29 100644 --- a/resources/qml/Account/AccountWidget.qml +++ b/resources/qml/Account/AccountWidget.qml @@ -40,7 +40,7 @@ Button id: popup y: parent.height + UM.Theme.getSize("default_arrow").height - x: (parent.width - width) + x: parent.width - width closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index 30295f6e9b..a2b58063b4 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Ultimaker B.V. +// Copyright (c) 2018 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. pragma Singleton @@ -67,7 +67,7 @@ Item property alias browsePackages: browsePackagesAction - UM.I18nCatalog{id: catalog; name:"cura"} + UM.I18nCatalog{id: catalog; name: "cura"} Action { diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index a58e7ef6fc..2654eb3c20 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -26,7 +26,7 @@ UM.MainWindow UM.I18nCatalog { id: catalog - name:"cura" + name: "cura" } function showTooltip(item, position, text) @@ -236,7 +236,7 @@ UM.MainWindow textRole: "name" // update the model's active index - function updateItemActiveFlags () + function updateItemActiveFlags() { currentIndex = getActiveIndex() for (var i = 0; i < model.rowCount(); i++) @@ -316,7 +316,7 @@ UM.MainWindow Loader { - // A stage can control this area. If nothing is set, it will therefor show the 3D view. + // A stage can control this area. If nothing is set, it will therefore show the 3D view. id: main anchors diff --git a/resources/qml/Dialogs/AboutDialog.qml b/resources/qml/Dialogs/AboutDialog.qml index 9a7e53260b..25c9bbf74b 100644 --- a/resources/qml/Dialogs/AboutDialog.qml +++ b/resources/qml/Dialogs/AboutDialog.qml @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Ultimaker B.V. +// Copyright (c) 2018 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.2 @@ -45,7 +45,7 @@ UM.Dialog anchors.topMargin: ((base.minimumWidth - width) / 2) | 0 anchors.horizontalCenter: parent.horizontalCenter - UM.I18nCatalog{id: catalog; name:"cura"} + UM.I18nCatalog{id: catalog; name: "cura"} } Label @@ -129,32 +129,32 @@ UM.Dialog } Component.onCompleted: { - projectsModel.append({ name:"Cura", description: catalog.i18nc("@label", "Graphical user interface"), license: "LGPLv3", url: "https://github.com/Ultimaker/Cura" }); - projectsModel.append({ name:"Uranium", description: catalog.i18nc("@label", "Application framework"), license: "LGPLv3", url: "https://github.com/Ultimaker/Uranium" }); - projectsModel.append({ name:"CuraEngine", description: catalog.i18nc("@label", "G-code generator"), license: "AGPLv3", url: "https://github.com/Ultimaker/CuraEngine" }); - projectsModel.append({ name:"libArcus", description: catalog.i18nc("@label", "Interprocess communication library"), license: "LGPLv3", url: "https://github.com/Ultimaker/libArcus" }); + projectsModel.append({ name: "Cura", description: catalog.i18nc("@label", "Graphical user interface"), license: "LGPLv3", url: "https://github.com/Ultimaker/Cura" }); + projectsModel.append({ name: "Uranium", description: catalog.i18nc("@label", "Application framework"), license: "LGPLv3", url: "https://github.com/Ultimaker/Uranium" }); + projectsModel.append({ name: "CuraEngine", description: catalog.i18nc("@label", "G-code generator"), license: "AGPLv3", url: "https://github.com/Ultimaker/CuraEngine" }); + projectsModel.append({ name: "libArcus", description: catalog.i18nc("@label", "Interprocess communication library"), license: "LGPLv3", url: "https://github.com/Ultimaker/libArcus" }); - projectsModel.append({ name:"Python", description: catalog.i18nc("@label", "Programming language"), license: "Python", url: "http://python.org/" }); - projectsModel.append({ name:"Qt5", description: catalog.i18nc("@label", "GUI framework"), license: "LGPLv3", url: "https://www.qt.io/" }); - projectsModel.append({ name:"PyQt", description: catalog.i18nc("@label", "GUI framework bindings"), license: "GPL", url: "https://riverbankcomputing.com/software/pyqt" }); - projectsModel.append({ name:"SIP", description: catalog.i18nc("@label", "C/C++ Binding library"), license: "GPL", url: "https://riverbankcomputing.com/software/sip" }); - projectsModel.append({ name:"Protobuf", description: catalog.i18nc("@label", "Data interchange format"), license: "BSD", url: "https://developers.google.com/protocol-buffers" }); - projectsModel.append({ name:"SciPy", description: catalog.i18nc("@label", "Support library for scientific computing"), license: "BSD-new", url: "https://www.scipy.org/" }); - projectsModel.append({ name:"NumPy", description: catalog.i18nc("@label", "Support library for faster math"), license: "BSD", url: "http://www.numpy.org/" }); - projectsModel.append({ name:"NumPy-STL", description: catalog.i18nc("@label", "Support library for handling STL files"), license: "BSD", url: "https://github.com/WoLpH/numpy-stl" }); - projectsModel.append({ name:"Shapely", description: catalog.i18nc("@label", "Support library for handling planar objects"), license: "BSD", url: "https://github.com/Toblerity/Shapely" }); - projectsModel.append({ name:"Trimesh", description: catalog.i18nc("@label", "Support library for handling triangular meshes"), license: "MIT", url: "https://trimsh.org" }); - projectsModel.append({ name:"NetworkX", description: catalog.i18nc("@label", "Support library for analysis of complex networks"), license: "3-clause BSD", url: "https://networkx.github.io/" }); - projectsModel.append({ name:"libSavitar", description: catalog.i18nc("@label", "Support library for handling 3MF files"), license: "LGPLv3", url: "https://github.com/ultimaker/libsavitar" }); - projectsModel.append({ name:"libCharon", description: catalog.i18nc("@label", "Support library for file metadata and streaming"), license: "LGPLv3", url: "https://github.com/ultimaker/libcharon" }); - projectsModel.append({ name:"PySerial", description: catalog.i18nc("@label", "Serial communication library"), license: "Python", url: "http://pyserial.sourceforge.net/" }); - projectsModel.append({ name:"python-zeroconf", description: catalog.i18nc("@label", "ZeroConf discovery library"), license: "LGPL", url: "https://github.com/jstasiak/python-zeroconf" }); - projectsModel.append({ name:"Clipper", description: catalog.i18nc("@label", "Polygon clipping library"), license: "Boost", url: "http://www.angusj.com/delphi/clipper.php" }); - projectsModel.append({ name:"Requests", description: catalog.i18nc("@Label", "Python HTTP library"), license: "GPL", url: "http://docs.python-requests.org" }); + projectsModel.append({ name: "Python", description: catalog.i18nc("@label", "Programming language"), license: "Python", url: "http://python.org/" }); + projectsModel.append({ name: "Qt5", description: catalog.i18nc("@label", "GUI framework"), license: "LGPLv3", url: "https://www.qt.io/" }); + projectsModel.append({ name: "PyQt", description: catalog.i18nc("@label", "GUI framework bindings"), license: "GPL", url: "https://riverbankcomputing.com/software/pyqt" }); + projectsModel.append({ name: "SIP", description: catalog.i18nc("@label", "C/C++ Binding library"), license: "GPL", url: "https://riverbankcomputing.com/software/sip" }); + projectsModel.append({ name: "Protobuf", description: catalog.i18nc("@label", "Data interchange format"), license: "BSD", url: "https://developers.google.com/protocol-buffers" }); + projectsModel.append({ name: "SciPy", description: catalog.i18nc("@label", "Support library for scientific computing"), license: "BSD-new", url: "https://www.scipy.org/" }); + projectsModel.append({ name: "NumPy", description: catalog.i18nc("@label", "Support library for faster math"), license: "BSD", url: "http://www.numpy.org/" }); + projectsModel.append({ name: "NumPy-STL", description: catalog.i18nc("@label", "Support library for handling STL files"), license: "BSD", url: "https://github.com/WoLpH/numpy-stl" }); + projectsModel.append({ name: "Shapely", description: catalog.i18nc("@label", "Support library for handling planar objects"), license: "BSD", url: "https://github.com/Toblerity/Shapely" }); + projectsModel.append({ name: "Trimesh", description: catalog.i18nc("@label", "Support library for handling triangular meshes"), license: "MIT", url: "https://trimsh.org" }); + projectsModel.append({ name: "NetworkX", description: catalog.i18nc("@label", "Support library for analysis of complex networks"), license: "3-clause BSD", url: "https://networkx.github.io/" }); + projectsModel.append({ name: "libSavitar", description: catalog.i18nc("@label", "Support library for handling 3MF files"), license: "LGPLv3", url: "https://github.com/ultimaker/libsavitar" }); + projectsModel.append({ name: "libCharon", description: catalog.i18nc("@label", "Support library for file metadata and streaming"), license: "LGPLv3", url: "https://github.com/ultimaker/libcharon" }); + projectsModel.append({ name: "PySerial", description: catalog.i18nc("@label", "Serial communication library"), license: "Python", url: "http://pyserial.sourceforge.net/" }); + projectsModel.append({ name: "python-zeroconf", description: catalog.i18nc("@label", "ZeroConf discovery library"), license: "LGPL", url: "https://github.com/jstasiak/python-zeroconf" }); + projectsModel.append({ name: "Clipper", description: catalog.i18nc("@label", "Polygon clipping library"), license: "Boost", url: "http://www.angusj.com/delphi/clipper.php" }); + projectsModel.append({ name: "Requests", description: catalog.i18nc("@Label", "Python HTTP library"), license: "GPL", url: "http://docs.python-requests.org" }); - projectsModel.append({ name:"Noto Sans", description: catalog.i18nc("@label", "Font"), license: "Apache 2.0", url: "https://www.google.com/get/noto/" }); - projectsModel.append({ name:"Font-Awesome-SVG-PNG", description: catalog.i18nc("@label", "SVG icons"), license: "SIL OFL 1.1", url: "https://github.com/encharm/Font-Awesome-SVG-PNG" }); - projectsModel.append({ name:"AppImageKit", description: catalog.i18nc("@label", "Linux cross-distribution application deployment"), license: "MIT", url: "https://github.com/AppImage/AppImageKit" }); + projectsModel.append({ name: "Noto Sans", description: catalog.i18nc("@label", "Font"), license: "Apache 2.0", url: "https://www.google.com/get/noto/" }); + projectsModel.append({ name: "Font-Awesome-SVG-PNG", description: catalog.i18nc("@label", "SVG icons"), license: "SIL OFL 1.1", url: "https://github.com/encharm/Font-Awesome-SVG-PNG" }); + projectsModel.append({ name: "AppImageKit", description: catalog.i18nc("@label", "Linux cross-distribution application deployment"), license: "MIT", url: "https://github.com/AppImage/AppImageKit" }); } } } diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index 1a5b604886..45111992c1 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.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 @@ -15,7 +15,7 @@ Item { property bool activity: CuraApplication.platformActivity property string fileBaseName: PrintInformation.baseName - UM.I18nCatalog { id: catalog; name:"cura"} + UM.I18nCatalog { id: catalog; name: "cura"} height: childrenRect.height diff --git a/resources/qml/MainWindow/ApplicationMenu.qml b/resources/qml/MainWindow/ApplicationMenu.qml index 00859e2a45..e538e966fc 100644 --- a/resources/qml/MainWindow/ApplicationMenu.qml +++ b/resources/qml/MainWindow/ApplicationMenu.qml @@ -128,7 +128,8 @@ Item Instantiator { model: Cura.ExtrudersModel { simpleNames: true } - Menu { + Menu + { title: model.name NozzleMenu { title: Cura.MachineManager.activeDefinitionVariantsName; visible: Cura.MachineManager.hasVariants; extruderIndex: index } @@ -267,7 +268,8 @@ Item } } - UM.ExtensionModel { + UM.ExtensionModel + { id: curaExtensions } @@ -291,7 +293,8 @@ Item Connections { target: Cura.Actions.browsePackages - onTriggered: { + onTriggered: + { curaExtensions.callExtensionMethod("Toolbox", "browsePackages") } } diff --git a/resources/qml/MaterialAndVariantSelector.qml b/resources/qml/MaterialAndVariantSelector.qml index 31a898f8ae..7dfc3737c0 100644 --- a/resources/qml/MaterialAndVariantSelector.qml +++ b/resources/qml/MaterialAndVariantSelector.qml @@ -21,7 +21,7 @@ Rectangle // Height has an extra 2x margin for the top & bottom margin. height: childrenRect.height + 2 * UM.Theme.getSize("default_margin").width - Cura.ExtrudersModel { id: extrudersModel; } + Cura.ExtrudersModel { id: extrudersModel } ListView { @@ -41,7 +41,7 @@ Rectangle margins: UM.Theme.getSize("sidebar_margin").width } - ExclusiveGroup { id: extruderMenuGroup; } + ExclusiveGroup { id: extruderMenuGroup } orientation: ListView.Horizontal @@ -69,8 +69,10 @@ Rectangle { anchors.fill: parent acceptedButtons: Qt.LeftButton | Qt.RightButton - onClicked: { - switch (mouse.button) { + onClicked: + { + switch (mouse.button) + { case Qt.LeftButton: extruder_enabled = Cura.MachineManager.getExtruder(model.index).isEnabled if (extruder_enabled) @@ -114,7 +116,8 @@ Rectangle { anchors.fill: parent border.width: control.checked ? UM.Theme.getSize("default_lining").width * 2 : UM.Theme.getSize("default_lining").width - border.color: { + border.color: + { if (Cura.MachineManager.getExtruder(index).isEnabled) { if(control.checked || control.pressed) @@ -128,13 +131,15 @@ Rectangle } return UM.Theme.getColor("action_button_disabled_border") } - color: { + color: + { if (Cura.MachineManager.getExtruder(index).isEnabled) { if(control.checked || control.pressed) { return UM.Theme.getColor("action_button_active"); - } else if (control.hovered) + } + else if (control.hovered) { return UM.Theme.getColor("action_button_hovered") } @@ -157,13 +162,15 @@ Rectangle anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left - color: { + color: + { if (Cura.MachineManager.getExtruder(index).isEnabled) { if(control.checked || control.pressed) { return UM.Theme.getColor("action_button_active_text"); - } else if (control.hovered) + } + else if (control.hovered) { return UM.Theme.getColor("action_button_hovered_text") } diff --git a/resources/qml/MonitorButton.qml b/resources/qml/MonitorButton.qml index aa40de11e4..efee50a2b3 100644 --- a/resources/qml/MonitorButton.qml +++ b/resources/qml/MonitorButton.qml @@ -13,7 +13,7 @@ import Cura 1.0 as Cura Item { id: base; - UM.I18nCatalog { id: catalog; name:"cura"} + UM.I18nCatalog { id: catalog; name: "cura"} height: childrenRect.height + UM.Theme.getSize("sidebar_margin").height diff --git a/resources/qml/MonitorSidebar.qml b/resources/qml/MonitorSidebar.qml index 80bd5c1a2e..179fcafb53 100644 --- a/resources/qml/MonitorSidebar.qml +++ b/resources/qml/MonitorSidebar.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.7 @@ -31,7 +31,7 @@ Rectangle property variant printMaterialNames: PrintInformation.materialNames color: UM.Theme.getColor("sidebar") - UM.I18nCatalog { id: catalog; name:"cura"} + UM.I18nCatalog { id: catalog; name: "cura"} Timer { id: tooltipDelayTimer @@ -70,7 +70,7 @@ Rectangle time -= minutes * 60 var seconds = Math.floor(time); - var finalTime = strPadLeft(hours, "0", 2) + ':' + strPadLeft(minutes,'0',2)+ ':' + strPadLeft(seconds,'0',2); + var finalTime = strPadLeft(hours, "0", 2) + ":" + strPadLeft(minutes, "0", 2) + ":" + strPadLeft(seconds, "0", 2); return finalTime; } diff --git a/resources/qml/Preferences/GeneralPage.qml b/resources/qml/Preferences/GeneralPage.qml index 64c1246929..0e8dd1b426 100644 --- a/resources/qml/Preferences/GeneralPage.qml +++ b/resources/qml/Preferences/GeneralPage.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.1 @@ -134,7 +134,7 @@ UM.PreferencesPage UM.PluginsModel { id: plugins } //: Language selection label - UM.I18nCatalog{id: catalog; name:"cura"} + UM.I18nCatalog{id: catalog; name: "cura"} Label { diff --git a/resources/qml/ProfileAndSettingComponent.qml b/resources/qml/ProfileAndSettingComponent.qml index b367c1130f..e5a29dffad 100644 --- a/resources/qml/ProfileAndSettingComponent.qml +++ b/resources/qml/ProfileAndSettingComponent.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.7 @@ -26,7 +26,7 @@ Rectangle property variant printMaterialNames: PrintInformation.materialNames color: UM.Theme.getColor("sidebar") - UM.I18nCatalog { id: catalog; name:"cura"} + UM.I18nCatalog { id: catalog; name: "cura"} // This widget doesn't show tooltips by itself. Instead it emits signals so others can do something with it. signal showTooltip(Item item, point location, string text) @@ -59,7 +59,7 @@ Rectangle time -= minutes * 60 var seconds = Math.floor(time); - var finalTime = strPadLeft(hours, "0", 2) + ':' + strPadLeft(minutes,'0',2)+ ':' + strPadLeft(seconds,'0',2); + var finalTime = strPadLeft(hours, "0", 2) + ":" + strPadLeft(minutes, "0", 2) + ":" + strPadLeft(seconds, "0", 2); return finalTime; } @@ -152,7 +152,7 @@ Rectangle background: Rectangle { border.width: control.checked ? UM.Theme.getSize("default_lining").width * 2 : UM.Theme.getSize("default_lining").width - border.color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_border") : control.hovered ? UM.Theme.getColor("action_button_hovered_border"): UM.Theme.getColor("action_button_border") + border.color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_border") : control.hovered ? UM.Theme.getColor("action_button_hovered_border") : UM.Theme.getColor("action_button_border") // for some reason, QtQuick decided to use the color of the background property as text color for the contentItem, so here it is color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active") : control.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button") diff --git a/resources/qml/ProgressAndSaveWidget.qml b/resources/qml/ProgressAndSaveWidget.qml index 1e82681164..6e8eb59482 100644 --- a/resources/qml/ProgressAndSaveWidget.qml +++ b/resources/qml/ProgressAndSaveWidget.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.7 @@ -173,7 +173,8 @@ Rectangle var weights = []; var costs = []; var someCostsKnown = false; - if(base.printMaterialLengths) { + if(base.printMaterialLengths) + { for(var index = 0; index < base.printMaterialLengths.length; index++) { if(base.printMaterialLengths[index] > 0) diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml index 8c561b0d00..5e41dd3007 100644 --- a/resources/qml/SaveButton.qml +++ b/resources/qml/SaveButton.qml @@ -13,7 +13,7 @@ import Cura 1.0 as Cura Item { id: base; - UM.I18nCatalog { id: catalog; name:"cura"} + UM.I18nCatalog { id: catalog; name: "cura"} property real progress: UM.Backend.progress property int backendState: UM.Backend.state @@ -36,7 +36,7 @@ Item case 2: return catalog.i18nc("@label:PrintjobStatus", "Slicing..."); case 3: - return catalog.i18nc("@label:PrintjobStatus %1 is target operation","Ready to %1").arg(UM.OutputDeviceManager.activeDeviceShortDescription); + return catalog.i18nc("@label:PrintjobStatus %1 is target operation", "Ready to %1").arg(UM.OutputDeviceManager.activeDeviceShortDescription); case 4: return catalog.i18nc("@label:PrintjobStatus", "Unable to Slice"); case 5: @@ -48,16 +48,20 @@ Item function sliceOrStopSlicing() { - try { + try + { if ([1, 5].indexOf(base.backendState) != -1) { CuraApplication.backend.forceSlice(); - } else { + } + else + { CuraApplication.backend.stopSlicing(); } - } catch (e) + } + catch (e) { - console.log('Could not start or stop slicing', e) + console.log("Could not start or stop slicing.", e) } } @@ -203,50 +207,76 @@ Item sliceOrStopSlicing(); } - style: ButtonStyle { + style: ButtonStyle + { background: Rectangle { border.width: UM.Theme.getSize("default_lining").width border.color: { if(!control.enabled) + { return UM.Theme.getColor("action_button_disabled_border"); + } else if(control.pressed) + { return UM.Theme.getColor("action_button_active_border"); + } else if(control.hovered) + { return UM.Theme.getColor("action_button_hovered_border"); + } else + { return UM.Theme.getColor("action_button_border"); + } } color: { if(!control.enabled) + { return UM.Theme.getColor("action_button_disabled"); + } else if(control.pressed) + { return UM.Theme.getColor("action_button_active"); + } else if(control.hovered) + { return UM.Theme.getColor("action_button_hovered"); + } else + { return UM.Theme.getColor("action_button"); + } } Behavior on color { ColorAnimation { duration: 50; } } implicitWidth: actualLabel.contentWidth + (UM.Theme.getSize("sidebar_margin").width * 2) - Label { + Label + { id: actualLabel anchors.centerIn: parent color: { if(!control.enabled) + { return UM.Theme.getColor("action_button_disabled_text"); + } else if(control.pressed) + { return UM.Theme.getColor("action_button_active_text"); + } else if(control.hovered) + { return UM.Theme.getColor("action_button_hovered_text"); + } else + { return UM.Theme.getColor("action_button_text"); + } } font: UM.Theme.getFont("action_button") text: control.text; @@ -287,43 +317,61 @@ Item border.color: { if(!control.enabled) + { return UM.Theme.getColor("action_button_disabled_border"); + } else if(control.pressed) + { return UM.Theme.getColor("print_button_ready_pressed_border"); + } else if(control.hovered) + { return UM.Theme.getColor("print_button_ready_hovered_border"); + } else + { return UM.Theme.getColor("print_button_ready_border"); + } } color: { if(!control.enabled) + { return UM.Theme.getColor("action_button_disabled"); + } else if(control.pressed) + { return UM.Theme.getColor("print_button_ready_pressed"); + } else if(control.hovered) + { return UM.Theme.getColor("print_button_ready_hovered"); + } else + { return UM.Theme.getColor("print_button_ready"); + } } Behavior on color { ColorAnimation { duration: 50; } } implicitWidth: actualLabel.contentWidth + (UM.Theme.getSize("sidebar_margin").width * 2) - Label { + Label + { id: actualLabel anchors.centerIn: parent - color:control.enabled ? UM.Theme.getColor("print_button_ready_text") : UM.Theme.getColor("action_button_disabled_text") + color: control.enabled ? UM.Theme.getColor("print_button_ready_text") : UM.Theme.getColor("action_button_disabled_text") font: UM.Theme.getFont("action_button") - text: control.text; + text: control.text } } label: Item { } } } - Button { + Button + { id: deviceSelectionMenu tooltip: catalog.i18nc("@info:tooltip","Select the active output device"); anchors.top: parent.top @@ -349,13 +397,16 @@ Item if(!control.enabled) { return UM.Theme.getColor("action_button_disabled_border") - } else if(control.pressed) + } + else if(control.pressed) { return UM.Theme.getColor("print_button_ready_pressed_border") - } else if(control.hovered) + } + else if(control.hovered) { return UM.Theme.getColor("print_button_ready_hovered_border") - } else + } + else { return UM.Theme.getColor("print_button_ready_border") } @@ -365,13 +416,16 @@ Item if(!control.enabled) { return UM.Theme.getColor("action_button_disabled") - } else if(control.pressed) + } + else if(control.pressed) { return UM.Theme.getColor("print_button_ready_pressed") - } else if(control.hovered) + } + else if(control.hovered) { return UM.Theme.getColor("print_button_ready_hovered") - } else + } + else { return UM.Theme.getColor("print_button_ready") } @@ -391,7 +445,7 @@ Item sourceSize.width: width sourceSize.height: height color: control.enabled ? UM.Theme.getColor("print_button_ready_text") : UM.Theme.getColor("action_button_disabled_text") - source: UM.Theme.getIcon("arrow_bottom"); + source: UM.Theme.getIcon("arrow_bottom") } } } @@ -406,8 +460,8 @@ Item { text: model.description checkable: true; - checked: model.id == UM.OutputDeviceManager.activeDevice; - exclusiveGroup: devicesMenuGroup; + checked: model.id == UM.OutputDeviceManager.activeDevice + exclusiveGroup: devicesMenuGroup onTriggered: { UM.OutputDeviceManager.setActiveDevice(model.id); @@ -416,9 +470,9 @@ Item onObjectAdded: devicesMenu.insertItem(index, object) onObjectRemoved: devicesMenu.removeItem(object) } - ExclusiveGroup { id: devicesMenuGroup; } + ExclusiveGroup { id: devicesMenuGroup } } } - UM.OutputDevicesModel { id: devicesModel; } + UM.OutputDevicesModel { id: devicesModel } } } diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index 939399efc8..58e6d2cf4d 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.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.1 @@ -7,10 +7,14 @@ import QtQuick.Controls.Styles 1.1 import UM 1.1 as UM -QtObject { - property Component sidebar_header_button: Component { - ButtonStyle { - background: Rectangle { +QtObject +{ + property Component sidebar_header_button: Component + { + ButtonStyle + { + background: Rectangle + { color: { if(control.enabled) @@ -61,7 +65,8 @@ QtObject { return Theme.getColor("setting_control_disabled_border"); } } - UM.RecolorImage { + UM.RecolorImage + { id: downArrow anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right @@ -73,7 +78,8 @@ QtObject { color: control.enabled ? Theme.getColor("setting_category_text") : Theme.getColor("setting_category_disabled_text") source: Theme.getIcon("arrow_bottom") } - Label { + Label + { id: sidebarComboBoxLabel color: control.enabled ? Theme.getColor("setting_control_text") : Theme.getColor("setting_control_disabled_text") text: control.text; @@ -158,33 +164,37 @@ QtObject { { if (control.checked) { - return UM.Theme.getColor("topheader_button_text_active") + return UM.Theme.getColor("topheader_button_text_active"); } else { if (control.hovered) { - return UM.Theme.getColor("topheader_button_text_hovered") + return UM.Theme.getColor("topheader_button_text_hovered"); } - return UM.Theme.getColor("topheader_button_text_inactive") + return UM.Theme.getColor("topheader_button_text_inactive"); } } } Component.onCompleted: { - buttonWidth = width + buttonWidth = width; } } } } - property Component tool_button: Component { - ButtonStyle { - background: Item { + property Component tool_button: Component + { + ButtonStyle + { + background: Item + { implicitWidth: Theme.getSize("button").width; implicitHeight: Theme.getSize("button").height; - UM.PointingRectangle { + UM.PointingRectangle + { id: button_tooltip anchors.left: parent.right @@ -203,7 +213,8 @@ QtObject { Behavior on width { NumberAnimation { duration: 100; } } Behavior on opacity { NumberAnimation { duration: 100; } } - Label { + Label + { id: button_tip anchors.horizontalCenter: parent.horizontalCenter @@ -215,7 +226,8 @@ QtObject { } } - Rectangle { + Rectangle + { id: buttonFace; anchors.fill: parent; @@ -249,7 +261,8 @@ QtObject { border.width: (control.hasOwnProperty("needBorder") && control.needBorder) ? 2 * screenScaleFactor : 0 border.color: Theme.getColor("tool_button_border") - UM.RecolorImage { + UM.RecolorImage + { id: tool_button_arrow anchors.right: parent.right; anchors.rightMargin: Theme.getSize("button").width - Math.round(Theme.getSize("button_icon").width / 4) @@ -284,8 +297,10 @@ QtObject { } } - label: Item { - UM.RecolorImage { + label: Item + { + UM.RecolorImage + { anchors.centerIn: parent; opacity: !control.enabled ? 0.2 : 1.0 source: control.iconSource; @@ -317,13 +332,17 @@ QtObject { } } - property Component small_tool_button: Component { - ButtonStyle { - background: Item { + property Component small_tool_button: Component + { + ButtonStyle + { + background: Item + { implicitWidth: Theme.getSize("small_button").width; implicitHeight: Theme.getSize("small_button").height; - Rectangle { + Rectangle + { id: smallButtonFace; anchors.fill: parent; @@ -357,7 +376,8 @@ QtObject { border.width: (control.hasOwnProperty("needBorder") && control.needBorder) ? 2 * screenScaleFactor : 0 border.color: Theme.getColor("tool_button_border") - UM.RecolorImage { + UM.RecolorImage + { id: smallToolButtonArrow width: 5 @@ -389,13 +409,15 @@ QtObject { } } - label: Item { - UM.RecolorImage { - anchors.centerIn: parent; + label: Item + { + UM.RecolorImage + { + anchors.centerIn: parent opacity: !control.enabled ? 0.2 : 1.0 source: control.iconSource; - width: Theme.getSize("small_button_icon").width; - height: Theme.getSize("small_button_icon").height; + width: Theme.getSize("small_button_icon").width + height: Theme.getSize("small_button_icon").height color: { if(control.checkable && control.checked && control.hovered) @@ -422,14 +444,18 @@ QtObject { } } - property Component progressbar: Component{ - ProgressBarStyle { - background: Rectangle { + property Component progressbar: Component + { + ProgressBarStyle + { + background: Rectangle + { implicitWidth: Theme.getSize("message").width - (Theme.getSize("default_margin").width * 2) implicitHeight: Theme.getSize("progressbar").height color: control.hasOwnProperty("backgroundColor") ? control.backgroundColor : Theme.getColor("progressbar_background") } - progress: Rectangle { + progress: Rectangle + { color: { if(control.indeterminate) @@ -446,14 +472,16 @@ QtObject { } } radius: Theme.getSize("progressbar_radius").width - Rectangle{ + Rectangle + { radius: Theme.getSize("progressbar_radius").width color: control.hasOwnProperty("controlColor") ? control.controlColor : Theme.getColor("progressbar_control") width: Theme.getSize("progressbar_control").width height: Theme.getSize("progressbar_control").height visible: control.indeterminate - SequentialAnimation on x { + SequentialAnimation on x + { id: xAnim property int animEndPoint: Theme.getSize("message").width - Math.round((Theme.getSize("default_margin").width * 2.5)) - Theme.getSize("progressbar_control").width running: control.indeterminate && control.visible @@ -466,59 +494,88 @@ QtObject { } } - property Component sidebar_category: Component { - ButtonStyle { - background: Rectangle { - anchors.fill: parent; + property Component sidebar_category: Component + { + ButtonStyle + { + background: Rectangle + { + anchors.fill: parent anchors.left: parent.left anchors.leftMargin: Theme.getSize("sidebar_margin").width anchors.right: parent.right anchors.rightMargin: Theme.getSize("sidebar_margin").width - implicitHeight: Theme.getSize("section").height; - color: { - if(control.color) { + implicitHeight: Theme.getSize("section").height + color: + { + if(control.color) + { return control.color; - } else if(!control.enabled) { + } + else if(!control.enabled) + { return Theme.getColor("setting_category_disabled"); - } else if(control.hovered && control.checkable && control.checked) { + } + else if(control.hovered && control.checkable && control.checked) + { return Theme.getColor("setting_category_active_hover"); - } else if(control.pressed || (control.checkable && control.checked)) { + } + else if(control.pressed || (control.checkable && control.checked)) + { return Theme.getColor("setting_category_active"); - } else if(control.hovered) { + } + else if(control.hovered) + { return Theme.getColor("setting_category_hover"); - } else { + } + else + { return Theme.getColor("setting_category"); } } Behavior on color { ColorAnimation { duration: 50; } } - Rectangle { + Rectangle + { height: Theme.getSize("default_lining").height width: parent.width anchors.bottom: parent.bottom - color: { - if(!control.enabled) { + color: + { + if(!control.enabled) + { return Theme.getColor("setting_category_disabled_border"); - } else if((control.hovered || control.activeFocus) && control.checkable && control.checked) { + } + else if((control.hovered || control.activeFocus) && control.checkable && control.checked) + { return Theme.getColor("setting_category_active_hover_border"); - } else if(control.pressed || (control.checkable && control.checked)) { + } + else if(control.pressed || (control.checkable && control.checked)) + { return Theme.getColor("setting_category_active_border"); - } else if(control.hovered || control.activeFocus) { + } + else if(control.hovered || control.activeFocus) + { return Theme.getColor("setting_category_hover_border"); - } else { + } + else + { return Theme.getColor("setting_category_border"); } } } } - label: Item { - anchors.fill: parent; + label: Item + { + anchors.fill: parent anchors.left: parent.left - Item{ - id: icon; + Item + { + id: icon anchors.left: parent.left height: parent.height width: Theme.getSize("section_icon_column").width - UM.RecolorImage { + UM.RecolorImage + { anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.leftMargin: Theme.getSize("sidebar_margin").width @@ -553,15 +610,17 @@ QtObject { } } - Label { - anchors { - left: icon.right; - leftMargin: Theme.getSize("default_margin").width; - right: parent.right; - verticalCenter: parent.verticalCenter; + Label + { + anchors + { + left: icon.right + leftMargin: Theme.getSize("default_margin").width + right: parent.right + verticalCenter: parent.verticalCenter } - text: control.text; - font: Theme.getFont("setting_category"); + text: control.text + font: Theme.getFont("setting_category") color: { if(!control.enabled) @@ -585,10 +644,11 @@ QtObject { return Theme.getColor("setting_category_text"); } } - fontSizeMode: Text.HorizontalFit; + fontSizeMode: Text.HorizontalFit minimumPointSize: 8 } - UM.RecolorImage { + UM.RecolorImage + { id: category_arrow anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right @@ -626,20 +686,24 @@ QtObject { } } - property Component scrollview: Component { - ScrollViewStyle { + property Component scrollview: Component + { + ScrollViewStyle + { decrementControl: Item { } incrementControl: Item { } transientScrollBars: false - scrollBarBackground: Rectangle { + scrollBarBackground: Rectangle + { implicitWidth: Theme.getSize("scrollbar").width radius: Math.round(implicitWidth / 2) color: Theme.getColor("scrollbar_background"); } - handle: Rectangle { + handle: Rectangle + { id: scrollViewHandle implicitWidth: Theme.getSize("scrollbar").width; radius: Math.round(implicitWidth / 2) @@ -650,10 +714,13 @@ QtObject { } } - property Component combobox: Component { - ComboBoxStyle { + property Component combobox: Component + { + ComboBoxStyle + { - background: Rectangle { + background: Rectangle + { implicitHeight: Theme.getSize("setting_control").height; implicitWidth: Theme.getSize("setting_control").width; @@ -664,28 +731,31 @@ QtObject { border.color: control.hovered ? Theme.getColor("setting_control_border_highlight") : Theme.getColor("setting_control_border"); } - label: Item { + label: Item + { - Label { - anchors.left: parent.left; + Label + { + anchors.left: parent.left anchors.leftMargin: Theme.getSize("default_lining").width - anchors.right: downArrow.left; - anchors.rightMargin: Theme.getSize("default_lining").width; - anchors.verticalCenter: parent.verticalCenter; + anchors.right: downArrow.left + anchors.rightMargin: Theme.getSize("default_lining").width + anchors.verticalCenter: parent.verticalCenter - text: control.currentText; + text: control.currentText font: Theme.getFont("default"); - color: !enabled ? Theme.getColor("setting_control_disabled_text") : Theme.getColor("setting_control_text"); + color: !enabled ? Theme.getColor("setting_control_disabled_text") : Theme.getColor("setting_control_text") - elide: Text.ElideRight; - verticalAlignment: Text.AlignVCenter; + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter } - UM.RecolorImage { + UM.RecolorImage + { id: downArrow - anchors.right: parent.right; - anchors.rightMargin: Theme.getSize("default_lining").width * 2; - anchors.verticalCenter: parent.verticalCenter; + anchors.right: parent.right + anchors.rightMargin: Theme.getSize("default_lining").width * 2 + anchors.verticalCenter: parent.verticalCenter source: Theme.getIcon("arrow_bottom") width: Theme.getSize("standard_arrow").width @@ -700,19 +770,24 @@ QtObject { } // Combobox with items with colored rectangles - property Component combobox_color: Component { + property Component combobox_color: Component + { - ComboBoxStyle { + ComboBoxStyle + { - background: Rectangle { + background: Rectangle + { color: !enabled ? UM.Theme.getColor("setting_control_disabled") : control._hovered ? UM.Theme.getColor("setting_control_highlight") : UM.Theme.getColor("setting_control") border.width: UM.Theme.getSize("default_lining").width border.color: !enabled ? UM.Theme.getColor("setting_control_disabled_border") : control._hovered ? UM.Theme.getColor("setting_control_border_highlight") : UM.Theme.getColor("setting_control_border") } - label: Item { + label: Item + { - Label { + Label + { anchors.left: parent.left anchors.leftMargin: UM.Theme.getSize("default_lining").width anchors.right: swatch.left @@ -727,7 +802,8 @@ QtObject { verticalAlignment: Text.AlignVCenter } - Rectangle { + Rectangle + { id: swatch height: Math.round(UM.Theme.getSize("setting_control").height / 2) width: height @@ -740,7 +816,8 @@ QtObject { color: (control.color_override !== "") ? control.color_override : control.color } - UM.RecolorImage { + UM.RecolorImage + { id: downArrow anchors.right: parent.right anchors.rightMargin: UM.Theme.getSize("default_lining").width * 2 @@ -758,22 +835,26 @@ QtObject { } } - property Component checkbox: Component { - CheckBoxStyle { + property Component checkbox: Component + { + CheckBoxStyle + { background: Item { } - indicator: Rectangle { - implicitWidth: Theme.getSize("checkbox").width; - implicitHeight: Theme.getSize("checkbox").height; + indicator: Rectangle + { + implicitWidth: Theme.getSize("checkbox").width + implicitHeight: Theme.getSize("checkbox").height - color: (control.hovered || control._hovered) ? Theme.getColor("checkbox_hover") : Theme.getColor("checkbox"); + color: (control.hovered || control._hovered) ? Theme.getColor("checkbox_hover") : Theme.getColor("checkbox") Behavior on color { ColorAnimation { duration: 50; } } radius: control.exclusiveGroup ? Math.round(Theme.getSize("checkbox").width / 2) : 0 - border.width: Theme.getSize("default_lining").width; - border.color: (control.hovered || control._hovered) ? Theme.getColor("checkbox_border_hover") : Theme.getColor("checkbox_border"); + border.width: Theme.getSize("default_lining").width + border.color: (control.hovered || control._hovered) ? Theme.getColor("checkbox_border_hover") : Theme.getColor("checkbox_border") - UM.RecolorImage { + UM.RecolorImage + { anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter width: Math.round(parent.width / 2.5) @@ -786,7 +867,8 @@ QtObject { Behavior on opacity { NumberAnimation { duration: 100; } } } } - label: Label { + label: Label + { text: control.text color: Theme.getColor("checkbox_text") font: Theme.getFont("default") @@ -795,12 +877,15 @@ QtObject { } } - property Component partially_checkbox: Component { - CheckBoxStyle { + property Component partially_checkbox: Component + { + CheckBoxStyle + { background: Item { } - indicator: Rectangle { - implicitWidth: Theme.getSize("checkbox").width; - implicitHeight: Theme.getSize("checkbox").height; + indicator: Rectangle + { + implicitWidth: Theme.getSize("checkbox").width + implicitHeight: Theme.getSize("checkbox").height color: (control.hovered || control._hovered) ? Theme.getColor("checkbox_hover") : Theme.getColor("checkbox"); Behavior on color { ColorAnimation { duration: 50; } } @@ -810,7 +895,8 @@ QtObject { border.width: Theme.getSize("default_lining").width; border.color: (control.hovered || control._hovered) ? Theme.getColor("checkbox_border_hover") : Theme.getColor("checkbox_border"); - UM.RecolorImage { + UM.RecolorImage + { anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter width: Math.round(parent.width / 2.5) @@ -818,50 +904,60 @@ QtObject { sourceSize.width: width sourceSize.height: width color: Theme.getColor("checkbox_mark") - source: { - if (control.checkbox_state == 2){ - return Theme.getIcon("solid") + source: + { + if (control.checkbox_state == 2) + { + return Theme.getIcon("solid"); } - else{ - return control.exclusiveGroup ? Theme.getIcon("dot") : Theme.getIcon("check") + else + { + return control.exclusiveGroup ? Theme.getIcon("dot") : Theme.getIcon("check"); } } opacity: control.checked Behavior on opacity { NumberAnimation { duration: 100; } } } } - label: Label { - text: control.text; - color: Theme.getColor("checkbox_text"); - font: Theme.getFont("default"); + label: Label + { + text: control.text + color: Theme.getColor("checkbox_text") + font: Theme.getFont("default") } } } - property Component slider: Component { - SliderStyle { - groove: Rectangle { - implicitWidth: control.width; - implicitHeight: Theme.getSize("slider_groove").height; + property Component slider: Component + { + SliderStyle + { + groove: Rectangle + { + implicitWidth: control.width + implicitHeight: Theme.getSize("slider_groove").height - color: Theme.getColor("slider_groove"); - border.width: Theme.getSize("default_lining").width; - border.color: Theme.getColor("slider_groove_border"); + color: Theme.getColor("slider_groove") + border.width: Theme.getSize("default_lining").width + border.color: Theme.getColor("slider_groove_border") - radius: Math.round(width / 2); + radius: Math.round(width / 2) - Rectangle { - anchors { - left: parent.left; - top: parent.top; - bottom: parent.bottom; + Rectangle + { + anchors + { + left: parent.left + top: parent.top + bottom: parent.bottom } color: Theme.getColor("slider_groove_fill"); width: Math.round((control.value / (control.maximumValue - control.minimumValue)) * parent.width); radius: Math.round(width / 2); } } - handle: Rectangle { + handle: Rectangle + { width: Theme.getSize("slider_handle").width; height: Theme.getSize("slider_handle").height; color: control.hovered ? Theme.getColor("slider_handle_hover") : Theme.getColor("slider_handle"); @@ -873,11 +969,13 @@ QtObject { } } - property Component text_field: Component { - TextFieldStyle { - textColor: Theme.getColor("setting_control_text"); + property Component text_field: Component + { + TextFieldStyle + { + textColor: Theme.getColor("setting_control_text") placeholderTextColor: Theme.getColor("setting_control_text") - font: Theme.getFont("default"); + font: Theme.getFont("default") background: Rectangle { @@ -889,7 +987,8 @@ QtObject { color: Theme.getColor("setting_validation_ok"); - Label { + Label + { anchors.right: parent.right; anchors.rightMargin: Theme.getSize("setting_unit_margin").width; anchors.verticalCenter: parent.verticalCenter; @@ -902,7 +1001,8 @@ QtObject { } } - property Component sidebar_action_button: Component { + property Component sidebar_action_button: Component + { ButtonStyle { background: Rectangle @@ -911,26 +1011,42 @@ QtObject { border.color: { if(!control.enabled) + { return UM.Theme.getColor("action_button_disabled_border"); + } else if(control.pressed) + { return UM.Theme.getColor("action_button_active_border"); + } else if(control.hovered) + { return UM.Theme.getColor("action_button_hovered_border"); + } else + { return UM.Theme.getColor("action_button_border"); + } } color: { if(!control.enabled) + { return UM.Theme.getColor("action_button_disabled"); + } else if(control.pressed) + { return UM.Theme.getColor("action_button_active"); + } else if(control.hovered) + { return UM.Theme.getColor("action_button_hovered"); + } else + { return UM.Theme.getColor("action_button"); + } } - Behavior on color { ColorAnimation { duration: 50; } } + Behavior on color { ColorAnimation { duration: 50 } } implicitWidth: actualLabel.contentWidth + (UM.Theme.getSize("sidebar_margin").width * 2) @@ -941,13 +1057,21 @@ QtObject { color: { if(!control.enabled) + { return UM.Theme.getColor("action_button_disabled_text"); + } else if(control.pressed) + { return UM.Theme.getColor("action_button_active_text"); + } else if(control.hovered) + { return UM.Theme.getColor("action_button_hovered_text"); + } else + { return UM.Theme.getColor("action_button_text"); + } } font: UM.Theme.getFont("action_button") text: control.text @@ -957,7 +1081,8 @@ QtObject { } } - property Component toolbox_action_button: Component { + property Component toolbox_action_button: Component + { ButtonStyle { background: Rectangle @@ -968,17 +1093,17 @@ QtObject { { if (control.installed) { - return UM.Theme.getColor("action_button_disabled") + return UM.Theme.getColor("action_button_disabled"); } else { if (control.hovered) { - return UM.Theme.getColor("primary_hover") + return UM.Theme.getColor("primary_hover"); } else { - return UM.Theme.getColor("primary") + return UM.Theme.getColor("primary"); } } @@ -991,17 +1116,17 @@ QtObject { { if (control.installed) { - return UM.Theme.getColor("action_button_disabled_text") + return UM.Theme.getColor("action_button_disabled_text"); } else { if (control.hovered) { - return UM.Theme.getColor("button_text_hover") + return UM.Theme.getColor("button_text_hover"); } else { - return UM.Theme.getColor("button_text") + return UM.Theme.getColor("button_text"); } } } From 237c360a88897a34ea7abf365c584b9ee04180a9 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 18 Oct 2018 15:18:00 +0200 Subject: [PATCH 0044/1240] The size of the avatar image is now themed. Some other small fixes in the AccountDetails componenent were done. Contributes to CURA-5784. --- resources/qml/Account/AccountDetails.qml | 8 ++++---- resources/qml/Account/AvatarImage.qml | 2 ++ resources/themes/cura-light/styles.qml | 2 +- resources/themes/cura-light/theme.json | 2 ++ 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/resources/qml/Account/AccountDetails.qml b/resources/qml/Account/AccountDetails.qml index 27860cfae5..10ee5016cd 100644 --- a/resources/qml/Account/AccountDetails.qml +++ b/resources/qml/Account/AccountDetails.qml @@ -18,8 +18,8 @@ Column AvatarImage { id: avatar - width: 75 * screenScaleFactor - height: 75 * screenScaleFactor + width: UM.Theme.getSize("avatar_image").width + height: UM.Theme.getSize("avatar_image").height anchors.horizontalCenter: parent.horizontalCenter source: loggedIn ? profile["profile_image_url"] : UM.Theme.getImage("avatar_default") outlineColor: loggedIn ? UM.Theme.getColor("account_widget_ouline_active") : UM.Theme.getColor("account_widget_ouline_inactive") @@ -29,8 +29,8 @@ Column { id: information anchors.horizontalCenter: parent.horizontalCenter - visible: !loggedIn - text: loggedIn ? profile["username"] : catalog.i18nc("@label", "Please login or create an account to 
enjoy all features of Ultimaker Cura") + horizontalAlignment: Text.AlignHCenter + text: loggedIn ? profile["username"] : catalog.i18nc("@label", "Please log in or create an account to\nenjoy all features of Ultimaker Cura.") font: loggedIn ? UM.Theme.getFont("large") : UM.Theme.getFont("default") color: UM.Theme.getColor("text") } diff --git a/resources/qml/Account/AvatarImage.qml b/resources/qml/Account/AvatarImage.qml index 0e0d182018..333bce29d7 100644 --- a/resources/qml/Account/AvatarImage.qml +++ b/resources/qml/Account/AvatarImage.qml @@ -1,4 +1,6 @@ // Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + import QtQuick 2.7 import QtQuick.Controls 2.1 import QtGraphicalEffects 1.0 diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index 939399efc8..1164b84332 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.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.1 diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 285d2724a0..43a0b628b5 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -488,6 +488,8 @@ "toolbox_action_button": [8.0, 2.5], "toolbox_loader": [2.0, 2.0], + "avatar_image": [6.8, 6.8], + "drop_shadow_radius": [1.0, 1.0] } } From 119638efa1fa71492648b0305df87bdea23bad12 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 18 Oct 2018 15:35:21 +0200 Subject: [PATCH 0045/1240] Adjust outline color for the avatar image. Following the colors in the designs. Contributes to CURA-5784. --- resources/themes/cura-light/theme.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 43a0b628b5..16cb17756d 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -88,7 +88,7 @@ "topheader_button_background_inactive": [255, 255, 255, 0], "account_widget_ouline_active": [9, 140, 188, 255], - "account_widget_ouline_inactive": [175, 175, 175, 255], + "account_widget_ouline_inactive": [229, 229, 229, 255], "text": [0, 0, 0, 255], "text_detail": [174, 174, 174, 128], From 0a07f48375bb47976d32194ece31c0d5c7038dbe Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 18 Oct 2018 15:38:24 +0200 Subject: [PATCH 0046/1240] Fix typo in a theme's key. Contributes to CURA-5784. --- resources/qml/Account/AccountDetails.qml | 2 +- resources/qml/Account/AccountWidget.qml | 2 +- resources/themes/cura-light/theme.json | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/qml/Account/AccountDetails.qml b/resources/qml/Account/AccountDetails.qml index 10ee5016cd..18f11622fd 100644 --- a/resources/qml/Account/AccountDetails.qml +++ b/resources/qml/Account/AccountDetails.qml @@ -22,7 +22,7 @@ Column height: UM.Theme.getSize("avatar_image").height anchors.horizontalCenter: parent.horizontalCenter source: loggedIn ? profile["profile_image_url"] : UM.Theme.getImage("avatar_default") - outlineColor: loggedIn ? UM.Theme.getColor("account_widget_ouline_active") : UM.Theme.getColor("account_widget_ouline_inactive") + outlineColor: loggedIn ? UM.Theme.getColor("account_widget_outline_active") : UM.Theme.getColor("account_widget_outline_inactive") } Label diff --git a/resources/qml/Account/AccountWidget.qml b/resources/qml/Account/AccountWidget.qml index a6a79a7d29..6374aaad69 100644 --- a/resources/qml/Account/AccountWidget.qml +++ b/resources/qml/Account/AccountWidget.qml @@ -28,7 +28,7 @@ Button anchors.horizontalCenter: parent.horizontalCenter source: loggedIn ? profile["profile_image_url"] : UM.Theme.getImage("avatar_default") - outlineColor: loggedIn ? UM.Theme.getColor("account_widget_ouline_active") : UM.Theme.getColor("account_widget_ouline_inactive") + outlineColor: loggedIn ? UM.Theme.getColor("account_widget_outline_active") : UM.Theme.getColor("account_widget_outline_inactive") } background: Item {} diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 16cb17756d..07eab72afb 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -87,8 +87,8 @@ "topheader_button_background_active": [255, 255, 255, 255], "topheader_button_background_inactive": [255, 255, 255, 0], - "account_widget_ouline_active": [9, 140, 188, 255], - "account_widget_ouline_inactive": [229, 229, 229, 255], + "account_widget_outline_active": [9, 140, 188, 255], + "account_widget_outline_inactive": [229, 229, 229, 255], "text": [0, 0, 0, 255], "text_detail": [174, 174, 174, 128], From fdfa81b2b8cf69d78897961f6985bf6ed8b941e0 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 18 Oct 2018 16:06:21 +0200 Subject: [PATCH 0047/1240] Make some items' size dependent on parent's. Also make the ActionButton more themable. Contributes to CURA-5784. --- resources/qml/Account/AccountDetails.qml | 1 + resources/qml/Account/AccountWidget.qml | 1 - resources/qml/ActionButton.qml | 21 +++++++++++---------- resources/themes/cura-light/theme.json | 2 ++ 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/resources/qml/Account/AccountDetails.qml b/resources/qml/Account/AccountDetails.qml index 18f11622fd..cc065fc0dd 100644 --- a/resources/qml/Account/AccountDetails.qml +++ b/resources/qml/Account/AccountDetails.qml @@ -30,6 +30,7 @@ Column id: information anchors.horizontalCenter: parent.horizontalCenter horizontalAlignment: Text.AlignHCenter + renderType: Text.NativeRendering text: loggedIn ? profile["username"] : catalog.i18nc("@label", "Please log in or create an account to\nenjoy all features of Ultimaker Cura.") font: loggedIn ? UM.Theme.getFont("large") : UM.Theme.getFont("default") color: UM.Theme.getColor("text") diff --git a/resources/qml/Account/AccountWidget.qml b/resources/qml/Account/AccountWidget.qml index 6374aaad69..70ee6c86da 100644 --- a/resources/qml/Account/AccountWidget.qml +++ b/resources/qml/Account/AccountWidget.qml @@ -21,7 +21,6 @@ Button { id: avatar - width: Math.round(0.8 * parent.width) height: Math.round(0.8 * parent.height) anchors.verticalCenter: parent.verticalCenter diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index b49e3f1dcb..44299d3734 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -11,28 +11,28 @@ Button { id: button property alias cursorShape: mouseArea.cursorShape - property var iconSource: "" + property alias iconSource: buttonIcon.source + property alias textFont: buttonText.font + property alias cornerRadius: backgroundRect.radius property var color: UM.Theme.getColor("primary") property var hoverColor: UM.Theme.getColor("primary_hover") property var disabledColor: color property var textColor: UM.Theme.getColor("button_text") property var textHoverColor: UM.Theme.getColor("button_text_hover") property var textDisabledColor: textColor - property var textFont: UM.Theme.getFont("action_button") - property var cornerRadius: 2 * screenScaleFactor contentItem: Row { UM.RecolorImage { id: buttonIcon - source: button.iconSource - width: 16 * screenScaleFactor - height: 16 * screenScaleFactor + source: "" + height: Math.round(0.6 * parent.height) + width: height sourceSize.width: width sourceSize.height: height color: button.hovered ? button.textHoverColor : button.textColor - visible: button.iconSource != "" + visible: source != "" anchors.verticalCenter: parent.verticalCenter } @@ -41,8 +41,8 @@ Button id: buttonText text: button.text color: button.enabled ? (button.hovered ? button.textHoverColor : button.textColor): button.textDisabledColor - font: button.textFont - visible: button.text != "" + font: UM.Theme.getFont("action_button") + visible: text != "" renderType: Text.NativeRendering anchors.verticalCenter: parent.verticalCenter } @@ -50,8 +50,9 @@ Button background: Rectangle { + id: backgroundRect color: button.enabled ? (button.hovered ? button.hoverColor : button.color) : button.disabledColor - radius: cornerRadius + radius: UM.Theme.getSize("action_button_radius").width } MouseArea diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 07eab72afb..3325312e8b 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -490,6 +490,8 @@ "avatar_image": [6.8, 6.8], + "action_button_radius": [0.25, 0.25], + "drop_shadow_radius": [1.0, 1.0] } } From 1cc7e0e586604ddf16b2931e1bcffbadb53c7f80 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 18 Oct 2018 16:33:13 +0200 Subject: [PATCH 0048/1240] Set the height of the stage menu in the theme instead of hardcoded. I also adjusted a bit the design to fulfill the requirments from UX/UI team. Contributes to CURA-5772. --- cura/Stages/CuraStage.py | 5 +++-- plugins/PrepareStage/PrepareMenu.qml | 8 +++++++- resources/qml/Cura.qml | 17 ++++++++++++++--- resources/themes/cura-light/theme.json | 2 ++ 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/cura/Stages/CuraStage.py b/cura/Stages/CuraStage.py index 774a5a6e76..e8537fb6b9 100644 --- a/cura/Stages/CuraStage.py +++ b/cura/Stages/CuraStage.py @@ -1,5 +1,6 @@ -# Copyright (c) 2017 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. + from PyQt5.QtCore import pyqtProperty, QUrl from UM.Stage import Stage @@ -27,6 +28,6 @@ class CuraStage(Stage): def sidebarComponent(self) -> QUrl: return self.getDisplayComponent("sidebar") - @pyqtProperty(QUrl, constant=True) + @pyqtProperty(QUrl, constant = True) def stageMenuComponent(self) -> QUrl: return self.getDisplayComponent("menu") \ No newline at end of file diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index 208e7d18df..10ff0b2310 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -21,7 +21,6 @@ Item Row { - spacing: UM.Theme.getSize("default_margin").width anchors.horizontalCenter: parent.horizontalCenter Button @@ -34,6 +33,13 @@ Item action: Cura.Actions.open } + Item + { + id: spacing + width: UM.Theme.getSize("default_margin").width + height: parent.height + } + Cura.MachineAndConfigurationSelector { } diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 2654eb3c20..d9113ca448 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -20,7 +20,6 @@ UM.MainWindow // Cura application window title title: catalog.i18nc("@title:window", "Ultimaker Cura") - viewportRect: Qt.rect(0, 0, 1.0, 1.0) backgroundColor: UM.Theme.getColor("viewport_background") UM.I18nCatalog @@ -143,6 +142,19 @@ UM.MainWindow } } + Rectangle + { + anchors + { + left: parent.left + right: parent.right + top: parent.top + } + visible: stageMenu.source != "" + height: Math.round(UM.Theme.getSize("stage_menu").height / 2) + color: UM.Theme.getColor("topheader_background") + } + Loader { // The stage menu is, as the name implies, a menu that is defined by the active stage. @@ -155,10 +167,9 @@ UM.MainWindow left: parent.left right: parent.right top: parent.top - margins: UM.Theme.getSize("default_margin").height } - height: 50 + height: UM.Theme.getSize("stage_menu").height source: UM.Controller.activeStage != null ? UM.Controller.activeStage.stageMenuComponent : "" } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 3325312e8b..d26c872b00 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -344,6 +344,8 @@ "topheader_button": [8, 4], "topheader_button_icon": [1.2, 1.2], + "stage_menu": [0.0, 4.5], + "account_button": [12, 3], "views_selector": [0.0, 4.0], From 8df3eb33d45fd89bc4e7b0789ba03cfdc1020c4f Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 18 Oct 2018 16:51:32 +0200 Subject: [PATCH 0049/1240] Minor changes: remove empty lines, small changes in the anchors, ... Contributes to CURA-5772. --- resources/qml/Account/AccountDetails.qml | 2 +- resources/qml/Account/AccountWidget.qml | 1 - resources/qml/Cura.qml | 9 +-------- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/resources/qml/Account/AccountDetails.qml b/resources/qml/Account/AccountDetails.qml index cc065fc0dd..9cd9b91949 100644 --- a/resources/qml/Account/AccountDetails.qml +++ b/resources/qml/Account/AccountDetails.qml @@ -38,7 +38,7 @@ Column Loader { - id: accountEntryPoints + id: accountOperations anchors.horizontalCenter: parent.horizontalCenter sourceComponent: loggedIn ? userOperations : generalOperations } diff --git a/resources/qml/Account/AccountWidget.qml b/resources/qml/Account/AccountWidget.qml index 70ee6c86da..d6d4728e7e 100644 --- a/resources/qml/Account/AccountWidget.qml +++ b/resources/qml/Account/AccountWidget.qml @@ -16,7 +16,6 @@ Button implicitHeight: UM.Theme.getSize("topheader").height implicitWidth: UM.Theme.getSize("topheader").height - AvatarImage { id: avatar diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index d9113ca448..52ff99ad9b 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -187,7 +187,6 @@ UM.MainWindow { bottom: parent.bottom bottomMargin: UM.Theme.getSize("default_margin").height - rightMargin: UM.Theme.getSize("default_margin").width } } @@ -330,13 +329,7 @@ UM.MainWindow // A stage can control this area. If nothing is set, it will therefore show the 3D view. id: main - anchors - { - top: parent.top - bottom: parent.bottom - left: parent.left - right: parent.right - } + anchors.fill: parent MouseArea { From 93b04190f803b16e8409ac94f843f2605a347ed8 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 18 Oct 2018 17:13:15 +0200 Subject: [PATCH 0050/1240] Change name of some components to make them more indicative of what they do. Contributes to CURA-5772. --- plugins/PrepareStage/PrepareMenu.qml | 23 +++++++-- ...or.qml => CustomConfigurationSelector.qml} | 0 .../qml/MachineAndConfigurationSelector.qml | 47 ------------------- ...chineSelection.qml => MachineSelector.qml} | 0 ...ion.qml => QuickConfigurationSelector.qml} | 0 ...ngComponent.qml => PrintSetupSelector.qml} | 0 resources/qml/qmldir | 7 +-- 7 files changed, 23 insertions(+), 54 deletions(-) rename resources/qml/{MaterialAndVariantSelector.qml => CustomConfigurationSelector.qml} (100%) delete mode 100644 resources/qml/MachineAndConfigurationSelector.qml rename resources/qml/{MachineSelection.qml => MachineSelector.qml} (100%) rename resources/qml/Menus/ConfigurationMenu/{ConfigurationSelection.qml => QuickConfigurationSelector.qml} (100%) rename resources/qml/{ProfileAndSettingComponent.qml => PrintSetupSelector.qml} (100%) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index 10ff0b2310..8d0f03ec2f 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -13,6 +13,9 @@ Item signal showTooltip(Item item, point location, string text) signal hideTooltip() + property bool isNetworkPrinter: Cura.MachineManager.activeMachineNetworkKey != "" + property bool printerConnected: Cura.MachineManager.printerConnected + UM.I18nCatalog { id: catalog @@ -37,19 +40,31 @@ Item { id: spacing width: UM.Theme.getSize("default_margin").width - height: parent.height + height: prepareMenu.height } - Cura.MachineAndConfigurationSelector + Cura.MachineSelector { + id: machineSelection + width: UM.Theme.getSize("sidebar").width + height: prepareMenu.height } - Cura.MaterialAndVariantSelector + Cura.QuickConfigurationSelector + { + id: configSelection + visible: isNetworkPrinter && printerConnected + width: visible ? Math.round(machineSelection.width * 0.15) : 0 + panelWidth: machineSelection.width + height: prepareMenu.height + } + + Cura.CustomConfigurationSelector { width: UM.Theme.getSize("sidebar").width } - Cura.ProfileAndSettingComponent + Cura.PrintSetupSelector { width: UM.Theme.getSize("sidebar").width onShowTooltip: prepareMenu.showTooltip(item, location, text) diff --git a/resources/qml/MaterialAndVariantSelector.qml b/resources/qml/CustomConfigurationSelector.qml similarity index 100% rename from resources/qml/MaterialAndVariantSelector.qml rename to resources/qml/CustomConfigurationSelector.qml diff --git a/resources/qml/MachineAndConfigurationSelector.qml b/resources/qml/MachineAndConfigurationSelector.qml deleted file mode 100644 index 15b9ca6dd6..0000000000 --- a/resources/qml/MachineAndConfigurationSelector.qml +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2017 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.7 - -import UM 1.2 as UM -import Cura 1.0 as Cura -import "Menus" -import "Menus/ConfigurationMenu" - -Rectangle -{ - color: UM.Theme.getColor("sidebar_lining_thin") - - implicitHeight: UM.Theme.getSize("sidebar_header").height - implicitWidth: UM.Theme.getSize("sidebar").width - - property bool isNetworkPrinter: Cura.MachineManager.activeMachineNetworkKey != "" - property bool printerConnected: Cura.MachineManager.printerConnected - - MachineSelection - { - id: machineSelection - anchors - { - left: parent.left - right: configSelection.left - rightMargin: UM.Theme.getSize("sidebar_lining_thin").height / 2 - top: parent.top - bottom: parent.bottom - } - } - - ConfigurationSelection - { - id: configSelection - visible: isNetworkPrinter && printerConnected - width: visible ? Math.round(parent.width * 0.15) : 0 - panelWidth: parent.width - anchors - { - right: parent.right - top: parent.top - bottom: parent.bottom - } - } -} \ No newline at end of file diff --git a/resources/qml/MachineSelection.qml b/resources/qml/MachineSelector.qml similarity index 100% rename from resources/qml/MachineSelection.qml rename to resources/qml/MachineSelector.qml diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationSelection.qml b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml similarity index 100% rename from resources/qml/Menus/ConfigurationMenu/ConfigurationSelection.qml rename to resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml diff --git a/resources/qml/ProfileAndSettingComponent.qml b/resources/qml/PrintSetupSelector.qml similarity index 100% rename from resources/qml/ProfileAndSettingComponent.qml rename to resources/qml/PrintSetupSelector.qml diff --git a/resources/qml/qmldir b/resources/qml/qmldir index b9306b14bc..10e103d9ef 100644 --- a/resources/qml/qmldir +++ b/resources/qml/qmldir @@ -1,6 +1,7 @@ module Cura -MachineAndConfigurationSelector 1.0 MachineAndConfigurationSelector.qml -MaterialAndVariantSelector 1.0 MaterialAndVariantSelector.qml -ProfileAndSettingComponent 1.0 ProfileAndSettingComponent.qml +MachineSelector 1.0 MachineSelector.qml +QuickConfigurationSelector 1.0 QuickConfigurationSelector.qml +CustomConfigurationSelector 1.0 CustomConfigurationSelector.qml +PrintSetupSelector 1.0 PrintSetupSelector.qml ActionButton 1.0 ActionButton.qml \ No newline at end of file From 84adfa14ee73ef09831197c464ca847b5543f1de Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 18 Oct 2018 17:26:10 +0200 Subject: [PATCH 0051/1240] Add license to the top of a file Contributes to CURA-5772. --- resources/qml/ExtruderIcon.qml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/resources/qml/ExtruderIcon.qml b/resources/qml/ExtruderIcon.qml index 580ff5dce9..e4161e0058 100644 --- a/resources/qml/ExtruderIcon.qml +++ b/resources/qml/ExtruderIcon.qml @@ -1,3 +1,6 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + import QtQuick 2.7 import QtQuick.Controls 1.1 import UM 1.2 as UM @@ -47,7 +50,7 @@ Item height: Math.round(parent.height * 0.35) radius: Math.round(width / 2) - border.width: 1 + border.width: UM.Theme.getSize("default_lining").width border.color: UM.Theme.getColor("extruder_button_material_border") opacity: !extruderIconItem.checked ? 0.6 : 1.0 From bf6817ce7a68c2e7f0b347ca98062e913a7a35e4 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 18 Oct 2018 17:38:23 +0200 Subject: [PATCH 0052/1240] Reduce the size of the ApplicationMenu.qml file by moving some components out to different files. Contributes to CURA-5772. --- resources/qml/MainWindow/ApplicationMenu.qml | 136 +------------------ resources/qml/Menus/FileMenu.qml | 81 +++++++++++ resources/qml/Menus/SettingsMenu.qml | 69 ++++++++++ 3 files changed, 153 insertions(+), 133 deletions(-) create mode 100644 resources/qml/Menus/FileMenu.qml create mode 100644 resources/qml/Menus/SettingsMenu.qml diff --git a/resources/qml/MainWindow/ApplicationMenu.qml b/resources/qml/MainWindow/ApplicationMenu.qml index e538e966fc..d3bc115419 100644 --- a/resources/qml/MainWindow/ApplicationMenu.qml +++ b/resources/qml/MainWindow/ApplicationMenu.qml @@ -24,78 +24,7 @@ Item { id: applicationMenu - Menu - { - id: fileMenu - title: catalog.i18nc("@title:menu menubar:toplevel", "&File") - - MenuItem - { - id: newProjectMenu - action: Cura.Actions.newProject - } - - MenuItem - { - id: openMenu - action: Cura.Actions.open - } - - RecentFilesMenu { } - - MenuItem - { - id: saveWorkspaceMenu - text: catalog.i18nc("@title:menu menubar:file", "&Save...") - onTriggered: - { - var args = { "filter_by_machine": false, "file_type": "workspace", "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml" }; - if(UM.Preferences.getValue("cura/dialog_on_project_save")) - { - saveWorkspaceDialog.args = args - saveWorkspaceDialog.open() - } - else - { - UM.OutputDeviceManager.requestWriteToDevice("local_file", PrintInformation.jobName, args) - } - } - } - - MenuSeparator { } - - MenuItem - { - id: saveAsMenu - text: catalog.i18nc("@title:menu menubar:file", "&Export...") - onTriggered: - { - var localDeviceId = "local_file" - UM.OutputDeviceManager.requestWriteToDevice(localDeviceId, PrintInformation.jobName, { "filter_by_machine": false, "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml"}) - } - } - - MenuItem - { - id: exportSelectionMenu - text: catalog.i18nc("@action:inmenu menubar:file", "Export Selection...") - enabled: UM.Selection.hasSelection - iconName: "document-save-as" - onTriggered: UM.OutputDeviceManager.requestWriteSelectionToDevice("local_file", PrintInformation.jobName, { "filter_by_machine": false, "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml"}) - } - - MenuSeparator { } - - MenuItem - { - id: reloadAllMenu - action: Cura.Actions.reloadAll - } - - MenuSeparator { } - - MenuItem { action: Cura.Actions.quit } - } + FileMenu { title: catalog.i18nc("@title:menu menubar:toplevel", "&File") } Menu { @@ -116,68 +45,9 @@ Item MenuItem { action: Cura.Actions.unGroupObjects } } - ViewMenu { title: catalog.i18nc("@title:menu", "&View") } + ViewMenu { title: catalog.i18nc("@title:menu menubar:toplevel", "&View") } - Menu - { - id: settingsMenu - title: catalog.i18nc("@title:menu", "&Settings") - - PrinterMenu { title: catalog.i18nc("@title:menu menubar:settings", "&Printer") } - - Instantiator - { - model: Cura.ExtrudersModel { simpleNames: true } - Menu - { - title: model.name - - NozzleMenu { title: Cura.MachineManager.activeDefinitionVariantsName; visible: Cura.MachineManager.hasVariants; extruderIndex: index } - MaterialMenu { title: catalog.i18nc("@title:menu", "&Material"); visible: Cura.MachineManager.hasMaterials; extruderIndex: index } - - MenuSeparator - { - visible: Cura.MachineManager.hasVariants || Cura.MachineManager.hasMaterials - } - - MenuItem - { - text: catalog.i18nc("@action:inmenu", "Set as Active Extruder") - onTriggered: Cura.MachineManager.setExtruderIndex(model.index) - } - - MenuItem - { - text: catalog.i18nc("@action:inmenu", "Enable Extruder") - onTriggered: Cura.MachineManager.setExtruderEnabled(model.index, true) - visible: !Cura.MachineManager.getExtruder(model.index).isEnabled - } - - MenuItem - { - 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 - } - - } - onObjectAdded: settingsMenu.insertItem(index, object) - onObjectRemoved: settingsMenu.removeItem(object) - } - - // TODO Only show in dev mode. Remove check when feature ready - BuildplateMenu - { - title: catalog.i18nc("@title:menu", "&Build plate") - visible: CuraSDKVersion == "dev" && Cura.MachineManager.hasVariantBuildplates - } - ProfileMenu { title: catalog.i18nc("@title:settings", "&Profile") } - - MenuSeparator { } - - MenuItem { action: Cura.Actions.configureSettingVisibility } - } + SettingsMenu { title: catalog.i18nc("@title:menu menubar:toplevel", "&Settings") } Menu { diff --git a/resources/qml/Menus/FileMenu.qml b/resources/qml/Menus/FileMenu.qml new file mode 100644 index 0000000000..955ac89693 --- /dev/null +++ b/resources/qml/Menus/FileMenu.qml @@ -0,0 +1,81 @@ +// 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 UM 1.2 as UM +import Cura 1.0 as Cura + +Menu +{ + id: base + title: catalog.i18nc("@title:menu menubar:toplevel", "&File") + + MenuItem + { + id: newProjectMenu + action: Cura.Actions.newProject + } + + MenuItem + { + id: openMenu + action: Cura.Actions.open + } + + RecentFilesMenu { } + + MenuItem + { + id: saveWorkspaceMenu + text: catalog.i18nc("@title:menu menubar:file", "&Save...") + onTriggered: + { + var args = { "filter_by_machine": false, "file_type": "workspace", "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml" }; + if(UM.Preferences.getValue("cura/dialog_on_project_save")) + { + saveWorkspaceDialog.args = args + saveWorkspaceDialog.open() + } + else + { + UM.OutputDeviceManager.requestWriteToDevice("local_file", PrintInformation.jobName, args) + } + } + } + + MenuSeparator { } + + MenuItem + { + id: saveAsMenu + text: catalog.i18nc("@title:menu menubar:file", "&Export...") + onTriggered: + { + var localDeviceId = "local_file" + UM.OutputDeviceManager.requestWriteToDevice(localDeviceId, PrintInformation.jobName, { "filter_by_machine": false, "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml"}) + } + } + + MenuItem + { + id: exportSelectionMenu + text: catalog.i18nc("@action:inmenu menubar:file", "Export Selection...") + enabled: UM.Selection.hasSelection + iconName: "document-save-as" + onTriggered: UM.OutputDeviceManager.requestWriteSelectionToDevice("local_file", PrintInformation.jobName, { "filter_by_machine": false, "preferred_mimetypes": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml"}) + } + + MenuSeparator { } + + MenuItem + { + id: reloadAllMenu + action: Cura.Actions.reloadAll + } + + MenuSeparator { } + + MenuItem { action: Cura.Actions.quit } +} \ No newline at end of file diff --git a/resources/qml/Menus/SettingsMenu.qml b/resources/qml/Menus/SettingsMenu.qml new file mode 100644 index 0000000000..79f8c5b7bf --- /dev/null +++ b/resources/qml/Menus/SettingsMenu.qml @@ -0,0 +1,69 @@ +// 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 UM 1.2 as UM +import Cura 1.0 as Cura + +Menu +{ + id: base + title: catalog.i18nc("@title:menu menubar:toplevel", "&Settings") + + PrinterMenu { title: catalog.i18nc("@title:menu menubar:settings", "&Printer") } + + Instantiator + { + model: Cura.ExtrudersModel { simpleNames: true } + Menu + { + title: model.name + + NozzleMenu { title: Cura.MachineManager.activeDefinitionVariantsName; visible: Cura.MachineManager.hasVariants; extruderIndex: index } + MaterialMenu { title: catalog.i18nc("@title:menu", "&Material"); visible: Cura.MachineManager.hasMaterials; extruderIndex: index } + + MenuSeparator + { + visible: Cura.MachineManager.hasVariants || Cura.MachineManager.hasMaterials + } + + MenuItem + { + text: catalog.i18nc("@action:inmenu", "Set as Active Extruder") + onTriggered: Cura.MachineManager.setExtruderIndex(model.index) + } + + MenuItem + { + text: catalog.i18nc("@action:inmenu", "Enable Extruder") + onTriggered: Cura.MachineManager.setExtruderEnabled(model.index, true) + visible: !Cura.MachineManager.getExtruder(model.index).isEnabled + } + + MenuItem + { + 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 + } + + } + onObjectAdded: base.insertItem(index, object) + onObjectRemoved: base.removeItem(object) + } + + // TODO Only show in dev mode. Remove check when feature ready + BuildplateMenu + { + title: catalog.i18nc("@title:menu", "&Build plate") + visible: CuraSDKVersion == "dev" && Cura.MachineManager.hasVariantBuildplates + } + ProfileMenu { title: catalog.i18nc("@title:settings", "&Profile") } + + MenuSeparator { } + + MenuItem { action: Cura.Actions.configureSettingVisibility } +} \ No newline at end of file From 176c7bfc22ea4972ce194c07301b9e75ca28331f Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 18 Oct 2018 17:48:03 +0200 Subject: [PATCH 0053/1240] Small fixes in the CustomConfigurationSelector file Contributes to CURA-5772. --- resources/qml/CustomConfigurationSelector.qml | 26 +++++++++---------- resources/qml/qmldir | 4 ++- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/resources/qml/CustomConfigurationSelector.qml b/resources/qml/CustomConfigurationSelector.qml index 7dfc3737c0..9a3f91e6a3 100644 --- a/resources/qml/CustomConfigurationSelector.qml +++ b/resources/qml/CustomConfigurationSelector.qml @@ -8,8 +8,6 @@ import QtQuick.Controls.Styles 1.1 import UM 1.2 as UM import Cura 1.0 as Cura -import "Menus" // TODO: This needs to be fixed in the qmldir! - Rectangle { implicitWidth: parent.width @@ -77,16 +75,15 @@ Rectangle extruder_enabled = Cura.MachineManager.getExtruder(model.index).isEnabled if (extruder_enabled) { - forceActiveFocus(); // Changing focus applies the currently-being-typed values so it can change the displayed setting values. - Cura.ExtruderManager.setActiveExtruderIndex(index); + forceActiveFocus() // Changing focus applies the currently-being-typed values so it can change the displayed setting values. + Cura.ExtruderManager.setActiveExtruderIndex(index) } - break; + break case Qt.RightButton: extruder_enabled = Cura.MachineManager.getExtruder(model.index).isEnabled - extruderMenu.popup(); - break; + extruderMenu.popup() + break } - } } @@ -122,8 +119,9 @@ Rectangle { if(control.checked || control.pressed) { - return UM.Theme.getColor("action_button_active_border"); - } else if (control.hovered) + return UM.Theme.getColor("action_button_active_border") + } + else if (control.hovered) { return UM.Theme.getColor("action_button_hovered_border") } @@ -252,7 +250,7 @@ Rectangle anchors.right: parent.right style: UM.Theme.styles.sidebar_header_button activeFocusOnPress: true; - menu: MaterialMenu + menu: Cura.MaterialMenu { extruderIndex: Cura.ExtruderManager.activeExtruderIndex } @@ -300,7 +298,7 @@ Rectangle style: UM.Theme.styles.sidebar_header_button activeFocusOnPress: true; - menu: NozzleMenu { extruderIndex: Cura.ExtruderManager.activeExtruderIndex } + menu: Cura.NozzleMenu { extruderIndex: Cura.ExtruderManager.activeExtruderIndex } } } @@ -348,12 +346,12 @@ Rectangle { anchors.fill: parent - onClicked: { + onClicked: + { // open the material URL with web browser Qt.openUrlExternally("https://ultimaker.com/incoming-links/cura/material-compatibilty"); } } } } - } diff --git a/resources/qml/qmldir b/resources/qml/qmldir index 10e103d9ef..2681da00e1 100644 --- a/resources/qml/qmldir +++ b/resources/qml/qmldir @@ -4,4 +4,6 @@ MachineSelector 1.0 MachineSelector.qml QuickConfigurationSelector 1.0 QuickConfigurationSelector.qml CustomConfigurationSelector 1.0 CustomConfigurationSelector.qml PrintSetupSelector 1.0 PrintSetupSelector.qml -ActionButton 1.0 ActionButton.qml \ No newline at end of file +ActionButton 1.0 ActionButton.qml +MaterialMenu 1.0 MaterialMenu.qml +NozzleMenu 1.0 NozzleMenu.qml \ No newline at end of file From 66147f4172f1162a5a5a604742186f91e3025c4a Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 18 Oct 2018 17:49:56 +0200 Subject: [PATCH 0054/1240] Remove unused style button. Contributes to CURA-5772. --- resources/themes/cura-light/styles.qml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index 58e6d2cf4d..074ceb4200 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -96,14 +96,6 @@ QtObject } } - property Component action_button: Component - { - ButtonStyle - { - - } - } - property Component topheader_tab: Component { ButtonStyle From 405e4c5bbd73eb60b8b27822e3e30890ecdb97aa Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 18 Oct 2018 17:52:42 +0200 Subject: [PATCH 0055/1240] Correct year, we live now in 2018 (keep it in mind) Contributes to CURA-5772. --- resources/qml/MachineSelector.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/MachineSelector.qml b/resources/qml/MachineSelector.qml index 7a8a6b476b..49fbf16e19 100644 --- a/resources/qml/MachineSelector.qml +++ b/resources/qml/MachineSelector.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 6f12197cead700d53666f0f9007b895f830e0fd5 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 19 Oct 2018 13:41:32 +0200 Subject: [PATCH 0056/1240] Don't use an image to create a mask but a circle. Contributes to CURA-5772. --- resources/qml/Account/AvatarImage.qml | 12 ++-- .../themes/cura-light/icons/circle_mask.svg | 64 ----------------- .../cura-light/icons/circle_outline.svg | 68 ++----------------- 3 files changed, 8 insertions(+), 136 deletions(-) delete mode 100644 resources/themes/cura-light/icons/circle_mask.svg diff --git a/resources/qml/Account/AvatarImage.qml b/resources/qml/Account/AvatarImage.qml index 333bce29d7..21adbcb7c3 100644 --- a/resources/qml/Account/AvatarImage.qml +++ b/resources/qml/Account/AvatarImage.qml @@ -26,31 +26,27 @@ Item visible: false } - UM.RecolorImage + Rectangle { id: profileImageMask - source: UM.Theme.getIcon("circle_mask") - sourceSize: Qt.size(parent.width, parent.height) anchors.fill: parent - color: UM.Theme.getColor("topheader_background") - visible: false + radius: width } OpacityMask { - anchors.fill: profileImage + anchors.fill: parent source: profileImage maskSource: profileImageMask cached: true - invert: false } UM.RecolorImage { id: profileImageOutline + anchors.fill: parent source: UM.Theme.getIcon("circle_outline") sourceSize: Qt.size(parent.width, parent.height) - anchors.fill: parent color: UM.Theme.getColor("account_widget_ouline_active") } } \ No newline at end of file diff --git a/resources/themes/cura-light/icons/circle_mask.svg b/resources/themes/cura-light/icons/circle_mask.svg deleted file mode 100644 index d5bfe53a97..0000000000 --- a/resources/themes/cura-light/icons/circle_mask.svg +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/resources/themes/cura-light/icons/circle_outline.svg b/resources/themes/cura-light/icons/circle_outline.svg index 4d0b0e4eb0..3a8fb197f3 100644 --- a/resources/themes/cura-light/icons/circle_outline.svg +++ b/resources/themes/cura-light/icons/circle_outline.svg @@ -1,64 +1,4 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - + + + + \ No newline at end of file From 15b35da612e773bb78827ae7bd5940c33cd85717 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 19 Oct 2018 16:40:21 +0200 Subject: [PATCH 0057/1240] Change the TopHeader name to MainWindowHeader, since there is nothing more on the top than the header. Contributes to CURA-5784. --- resources/qml/Account/AccountWidget.qml | 4 ++-- resources/qml/Account/GeneralOperations.qml | 4 ++-- resources/qml/Account/UserOperations.qml | 4 ++-- resources/qml/Cura.qml | 8 +++---- .../{TopHeader.qml => MainWindowHeader.qml} | 22 +++++++++---------- .../ConfigurationMenu/ConfigurationItem.qml | 4 ++-- resources/themes/cura-dark/theme.json | 9 ++++---- resources/themes/cura-light/styles.qml | 14 ++++++------ resources/themes/cura-light/theme.json | 19 ++++++++-------- 9 files changed, 43 insertions(+), 45 deletions(-) rename resources/qml/MainWindow/{TopHeader.qml => MainWindowHeader.qml} (76%) diff --git a/resources/qml/Account/AccountWidget.qml b/resources/qml/Account/AccountWidget.qml index d6d4728e7e..fc99273119 100644 --- a/resources/qml/Account/AccountWidget.qml +++ b/resources/qml/Account/AccountWidget.qml @@ -13,8 +13,8 @@ Button property var profile: Cura.API.account.userProfile property var loggedIn: Cura.API.account.isLoggedIn - implicitHeight: UM.Theme.getSize("topheader").height - implicitWidth: UM.Theme.getSize("topheader").height + implicitHeight: UM.Theme.getSize("main_window_header").height + implicitWidth: UM.Theme.getSize("main_window_header").height AvatarImage { diff --git a/resources/qml/Account/GeneralOperations.qml b/resources/qml/Account/GeneralOperations.qml index 1f3cd51f26..6bc94dd830 100644 --- a/resources/qml/Account/GeneralOperations.qml +++ b/resources/qml/Account/GeneralOperations.qml @@ -18,8 +18,8 @@ Row text: catalog.i18nc("@button", "Create account") color: UM.Theme.getColor("secondary") hoverColor: UM.Theme.getColor("secondary") - textColor: UM.Theme.getColor("text_link") - textHoverColor: UM.Theme.getColor("text") + textColor: UM.Theme.getColor("main_window_header_button_text_active") + textHoverColor: UM.Theme.getColor("main_window_header_button_text_active") onClicked: Qt.openUrlExternally("https://account.ultimaker.com") } diff --git a/resources/qml/Account/UserOperations.qml b/resources/qml/Account/UserOperations.qml index ffcf518b6e..a12bfbf6d7 100644 --- a/resources/qml/Account/UserOperations.qml +++ b/resources/qml/Account/UserOperations.qml @@ -18,8 +18,8 @@ Row text: catalog.i18nc("@button", "Manage account") color: UM.Theme.getColor("secondary") hoverColor: UM.Theme.getColor("secondary") - textColor: UM.Theme.getColor("text_link") - textHoverColor: UM.Theme.getColor("text") + textColor: UM.Theme.getColor("main_window_header_button_text_active") + textHoverColor: UM.Theme.getColor("main_window_header_button_text_active") onClicked: Qt.openUrlExternally("https://account.ultimaker.com") } diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 52ff99ad9b..474f93afa4 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -86,9 +86,9 @@ UM.MainWindow window: base } - TopHeader + MainWindowHeader { - id: topHeader + id: mainWindowHeader anchors { left: parent.left @@ -103,7 +103,7 @@ UM.MainWindow anchors { - top: topHeader.bottom + top: mainWindowHeader.bottom bottom: parent.bottom left: parent.left right: parent.right @@ -152,7 +152,7 @@ UM.MainWindow } visible: stageMenu.source != "" height: Math.round(UM.Theme.getSize("stage_menu").height / 2) - color: UM.Theme.getColor("topheader_background") + color: UM.Theme.getColor("main_window_header_background") } Loader diff --git a/resources/qml/MainWindow/TopHeader.qml b/resources/qml/MainWindow/MainWindowHeader.qml similarity index 76% rename from resources/qml/MainWindow/TopHeader.qml rename to resources/qml/MainWindow/MainWindowHeader.qml index 867a6bafbb..b90968c606 100644 --- a/resources/qml/MainWindow/TopHeader.qml +++ b/resources/qml/MainWindow/MainWindowHeader.qml @@ -2,7 +2,7 @@ // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.7 -import QtQuick.Controls 1.1 +import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.1 import UM 1.4 as UM @@ -14,9 +14,9 @@ Rectangle { id: base - implicitHeight: UM.Theme.getSize("topheader").height - implicitWidth: UM.Theme.getSize("topheader").width - color: UM.Theme.getColor("topheader_background") + implicitHeight: UM.Theme.getSize("main_window_header").height + implicitWidth: UM.Theme.getSize("main_window_header").width + color: UM.Theme.getColor("main_window_header_background") Image { @@ -43,7 +43,7 @@ Rectangle leftMargin: UM.Theme.getSize("default_margin").width } - // The topheader is dynamically filled with all available stages + // The main window header is dynamically filled with all available stages Repeater { id: stagesHeader @@ -55,9 +55,9 @@ Rectangle text: model.name.toUpperCase() checkable: true checked: model.active - exclusiveGroup: topheaderMenuGroup - style: UM.Theme.styles.topheader_tab - height: UM.Theme.getSize("topheader").height + exclusiveGroup: mainWindowHeaderMenuGroup + style: UM.Theme.styles.main_window_header_tab + height: UM.Theme.getSize("main_window_header").height onClicked: UM.Controller.setActiveStage(model.id) iconSource: model.stage.iconSource @@ -66,7 +66,7 @@ Rectangle } } - ExclusiveGroup { id: topheaderMenuGroup } + ExclusiveGroup { id: mainWindowHeaderMenuGroup } } // Shortcut button to quick access the Toolbox @@ -83,14 +83,14 @@ Rectangle { background: Rectangle { - color: control.hovered ? UM.Theme.getColor("secondary") : UM.Theme.getColor("topheader_button_background_active") + color: control.hovered ? UM.Theme.getColor("secondary") : UM.Theme.getColor("main_window_header_button_background_active") radius: 2 * screenScaleFactor } label: Label { text: catalog.i18nc("@action:button", "Toolbox") - color: UM.Theme.getColor("topheader_button_text_active") + color: UM.Theme.getColor("main_window_header_button_text_active") font: UM.Theme.getFont("action_button") renderType: Text.NativeRendering } diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 517b69428e..37d851a35d 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -77,8 +77,8 @@ Rectangle UM.RecolorImage { id: buildplateIcon anchors.left: parent.left - width: UM.Theme.getSize("topheader_button_icon").width - height: UM.Theme.getSize("topheader_button_icon").height + width: UM.Theme.getSize("main_window_header_button_icon").width + height: UM.Theme.getSize("main_window_header_button_icon").height sourceSize.width: width sourceSize.height: height source: UM.Theme.getIcon("buildplate") diff --git a/resources/themes/cura-dark/theme.json b/resources/themes/cura-dark/theme.json index 99737ff8bf..897d09ccf2 100644 --- a/resources/themes/cura-dark/theme.json +++ b/resources/themes/cura-dark/theme.json @@ -15,11 +15,10 @@ "border": [127, 127, 127, 255], "secondary": [241, 242, 242, 255], - "topheader_background": [31, 36, 39, 255], - - "topheader_button_text_active": [31, 36, 39, 255], - "topheader_button_text_inactive": [128, 128, 128, 255], - "topheader_button_text_hovered": [255, 255, 255, 255], + "main_window_header_background": [31, 36, 39, 255], + "main_window_header_button_text_active": [31, 36, 39, 255], + "main_window_header_button_text_inactive": [128, 128, 128, 255], + "main_window_header_button_text_hovered": [255, 255, 255, 255], "text": [255, 255, 255, 204], "text_detail": [255, 255, 255, 172], diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index 074ceb4200..e6e5bd7fda 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -96,7 +96,7 @@ QtObject } } - property Component topheader_tab: Component + property Component main_window_header_tab: Component { ButtonStyle { @@ -114,7 +114,7 @@ QtObject anchors.verticalCenter: parent.verticalCenter implicitHeight: parent.height - 2 * UM.Theme.getSize("default_margin").width implicitWidth: UM.Theme.getSize("default_lining").width - color: UM.Theme.getColor("topheader_button_background_active") + color: UM.Theme.getColor("main_window_header_button_background_active") visible: !control.checked } Rectangle @@ -122,7 +122,7 @@ QtObject id: buttonFace implicitHeight: parent.height implicitWidth: parent.width - color: control.checked ? UM.Theme.getColor("topheader_button_background_active") : UM.Theme.getColor("topheader_button_background_inactive") + color: control.checked ? UM.Theme.getColor("main_window_header_button_background_active") : UM.Theme.getColor("main_window_header_button_background_inactive") Behavior on color { ColorAnimation { duration: 50 } } } @@ -132,7 +132,7 @@ QtObject anchors.verticalCenter: parent.verticalCenter implicitHeight: parent.height - 2 * UM.Theme.getSize("default_margin").width implicitWidth: UM.Theme.getSize("default_lining").width - color: UM.Theme.getColor("topheader_button_background_active") + color: UM.Theme.getColor("main_window_header_button_background_active") visible: !control.checked } } @@ -156,15 +156,15 @@ QtObject { if (control.checked) { - return UM.Theme.getColor("topheader_button_text_active"); + return UM.Theme.getColor("main_window_header_button_text_active"); } else { if (control.hovered) { - return UM.Theme.getColor("topheader_button_text_hovered"); + return UM.Theme.getColor("main_window_header_button_text_hovered"); } - return UM.Theme.getColor("topheader_button_text_inactive"); + return UM.Theme.getColor("main_window_header_button_text_inactive"); } } } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index d26c872b00..d72493ec89 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -79,13 +79,12 @@ "border": [127, 127, 127, 255], "secondary": [245, 245, 245, 255], - "topheader_background": [12, 169, 227, 255], - - "topheader_button_text_active": [12, 169, 227, 255], - "topheader_button_text_inactive": [255, 255, 255, 255], - "topheader_button_text_hovered": [0, 0, 0, 255], - "topheader_button_background_active": [255, 255, 255, 255], - "topheader_button_background_inactive": [255, 255, 255, 0], + "main_window_header_background": [12, 169, 227, 255], + "main_window_header_button_text_active": [12, 169, 227, 255], + "main_window_header_button_text_inactive": [255, 255, 255, 255], + "main_window_header_button_text_hovered": [0, 0, 0, 255], + "main_window_header_button_background_active": [255, 255, 255, 255], + "main_window_header_button_background_inactive": [255, 255, 255, 0], "account_widget_outline_active": [9, 140, 188, 255], "account_widget_outline_inactive": [229, 229, 229, 255], @@ -340,9 +339,9 @@ "sizes": { "window_minimum_size": [70, 50], - "topheader": [0.0, 4.5], - "topheader_button": [8, 4], - "topheader_button_icon": [1.2, 1.2], + "main_window_header": [0.0, 4.5], + "main_window_header_button": [8, 4], + "main_window_header_button_icon": [1.2, 1.2], "stage_menu": [0.0, 4.5], From 17b77c58eff28e52f9d070cbc5a80c3dd5fcc4ec Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 19 Oct 2018 17:03:48 +0200 Subject: [PATCH 0058/1240] Make the spacing between the orientation controls be dependent on the theme. --- resources/qml/ViewOrientationControls.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/qml/ViewOrientationControls.qml b/resources/qml/ViewOrientationControls.qml index bf56ef2997..acf75b1b48 100644 --- a/resources/qml/ViewOrientationControls.qml +++ b/resources/qml/ViewOrientationControls.qml @@ -12,9 +12,10 @@ Row { id: viewOrientationControl - spacing: 2 * screenScaleFactor + spacing: UM.Theme.getSize("narrow_margin").width height: childrenRect.height width: childrenRect.width + // #1 3d view Button { From 17732b116935eabf8e61accec56b44f324ea73db Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 19 Oct 2018 17:03:48 +0200 Subject: [PATCH 0059/1240] Make the spacing between the orientation controls be dependent on the theme. Contributes to CURA-5772. --- resources/qml/ViewOrientationControls.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/qml/ViewOrientationControls.qml b/resources/qml/ViewOrientationControls.qml index bf56ef2997..acf75b1b48 100644 --- a/resources/qml/ViewOrientationControls.qml +++ b/resources/qml/ViewOrientationControls.qml @@ -12,9 +12,10 @@ Row { id: viewOrientationControl - spacing: 2 * screenScaleFactor + spacing: UM.Theme.getSize("narrow_margin").width height: childrenRect.height width: childrenRect.width + // #1 3d view Button { From 69fc1e5bfb85b7fc71c1b50b24a03b8ceae54981 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 22 Oct 2018 10:30:06 +0200 Subject: [PATCH 0060/1240] Reuse ActionButton component to create the toolbox shortcut. Contributes to CURA-5784. --- resources/qml/MainWindow/MainWindowHeader.qml | 27 +++++-------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/resources/qml/MainWindow/MainWindowHeader.qml b/resources/qml/MainWindow/MainWindowHeader.qml index b90968c606..3de4dc7865 100644 --- a/resources/qml/MainWindow/MainWindowHeader.qml +++ b/resources/qml/MainWindow/MainWindowHeader.qml @@ -70,33 +70,20 @@ Rectangle } // Shortcut button to quick access the Toolbox - Button + Cura.ActionButton { - id: toolboxShortcutButton anchors { right: accountWidget.left rightMargin: UM.Theme.getSize("default_margin").width verticalCenter: parent.verticalCenter } - style: ButtonStyle - { - background: Rectangle - { - color: control.hovered ? UM.Theme.getColor("secondary") : UM.Theme.getColor("main_window_header_button_background_active") - radius: 2 * screenScaleFactor - } - - label: Label - { - text: catalog.i18nc("@action:button", "Toolbox") - color: UM.Theme.getColor("main_window_header_button_text_active") - font: UM.Theme.getFont("action_button") - renderType: Text.NativeRendering - } - - } - action: Cura.Actions.browsePackages + text: catalog.i18nc("@action:button", "Toolbox") + color: UM.Theme.getColor("main_window_header_button_background_active") + hoverColor: UM.Theme.getColor("secondary") + textColor: UM.Theme.getColor("main_window_header_button_text_active") + textHoverColor: UM.Theme.getColor("main_window_header_button_text_active") + onClicked: Cura.Actions.browsePackages.trigger() } AccountWidget From dbb62f2490c76e482320bbbfe419b91498eee147 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 22 Oct 2018 11:31:19 +0200 Subject: [PATCH 0061/1240] Add default icons for the avatar. Contributes to CURA-5784. --- resources/qml/Account/AccountDetails.qml | 2 +- resources/qml/Account/AccountWidget.qml | 2 +- resources/qml/Account/AvatarImage.qml | 2 +- .../cura-light/images/avatar_default.png | Bin 3115 -> 0 bytes .../cura-light/images/avatar_default.svg | 76 ++++++++++++++++++ .../cura-light/images/avatar_no_user.svg | 76 ++++++++++++++++++ 6 files changed, 155 insertions(+), 3 deletions(-) delete mode 100644 resources/themes/cura-light/images/avatar_default.png create mode 100644 resources/themes/cura-light/images/avatar_default.svg create mode 100644 resources/themes/cura-light/images/avatar_no_user.svg diff --git a/resources/qml/Account/AccountDetails.qml b/resources/qml/Account/AccountDetails.qml index 9cd9b91949..a88fb77956 100644 --- a/resources/qml/Account/AccountDetails.qml +++ b/resources/qml/Account/AccountDetails.qml @@ -21,7 +21,7 @@ Column width: UM.Theme.getSize("avatar_image").width height: UM.Theme.getSize("avatar_image").height anchors.horizontalCenter: parent.horizontalCenter - source: loggedIn ? profile["profile_image_url"] : UM.Theme.getImage("avatar_default") + source: loggedIn ? profile["profile_image_url"] : UM.Theme.getImage("avatar_no_user") outlineColor: loggedIn ? UM.Theme.getColor("account_widget_outline_active") : UM.Theme.getColor("account_widget_outline_inactive") } diff --git a/resources/qml/Account/AccountWidget.qml b/resources/qml/Account/AccountWidget.qml index fc99273119..f54cf366e0 100644 --- a/resources/qml/Account/AccountWidget.qml +++ b/resources/qml/Account/AccountWidget.qml @@ -25,7 +25,7 @@ Button anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter - source: loggedIn ? profile["profile_image_url"] : UM.Theme.getImage("avatar_default") + source: loggedIn ? profile["profile_image_url"] : UM.Theme.getImage("avatar_no_user") outlineColor: loggedIn ? UM.Theme.getColor("account_widget_outline_active") : UM.Theme.getColor("account_widget_outline_inactive") } diff --git a/resources/qml/Account/AvatarImage.qml b/resources/qml/Account/AvatarImage.qml index 21adbcb7c3..c730ef428f 100644 --- a/resources/qml/Account/AvatarImage.qml +++ b/resources/qml/Account/AvatarImage.qml @@ -20,8 +20,8 @@ Item Image { id: profileImage - source: UM.Theme.getImage("avatar_default") anchors.fill: parent + source: UM.Theme.getImage("avatar_default") fillMode: Image.PreserveAspectCrop visible: false } diff --git a/resources/themes/cura-light/images/avatar_default.png b/resources/themes/cura-light/images/avatar_default.png deleted file mode 100644 index 0c306680f721dba0de73fb7be90fe5c548608692..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3115 zcmbVOX*iS%8y@?FGN>;UW^hD^kmA@)wk*+Nt%xutWD5~v3CY&j$IjT-F&O*4hwKUE z94T3j?C-quPWgQQzF+4%*LPp{bv?_E`&q8%$MgEOiQZ{;0d^P+cG^H6Z4QGmp16!K zIMa#gEfyO(2~=(y-Mu-mK<{6mcg{kT38-cisu+e!2WiFqw8CCm*DTcYfleBQa=K}W z6fmrs>RSxBWCM@Vj~=ESx#R%hHPqgDdjCgy-55mZ0NwL}%npz|24!@BLFH5j!qLkL zYV#yS8iV2+LAQLsJr9VdfK8Lo(>x%t3GDqyFX*KOlmd^^jtcr{9kWn!Gnm{0woE~< z$>3wckweDO8#35B4Yf@}F=Ws_{mA|#QxCSyK;0kc*}S| z044`$nu0n`l;`O_ML=*R6<0}(sR!fg!TcT?c^vATgRr?kSQRy|hvr`fWOjm%nMY5u zfY54c?K{Yu2xN7FIbAf@93Z<3^e6zH6#!*}v=?Q-i*lfO3QC}Wr318xT51%D8eIoA zyob`;z~BmMNF~)N3uv5x^15kp4Pamy;G6}t&p;`y;Ojatq>Aca3fN^FJ;?-~mjHgn zfKM?Hc`^bCta%4zcY?WHG(tNV+W@xDLd63#$1I?77%CZ{74^~byJ^IJT2Vi(d=PTV z2HsLm8V2x9U{VX{Nd!__KrbRt{SK-gfjkO&Kz8A*gl~BKFY~dyFwK*r`ebwANjJhOPID+-Nx%tm}=cC;EG* z#j=W=110?DuK3Bl{ogFoz#k(fe^#n1(1$%vfMY>V7)@S@FQ#q&H)`@+?Kl-^bK z5*Dlcm!jhhSB~R0IFIg|H0dW=j%nAldy31hybBvnzOL2Nhpo2Lx~EFOMGUNdm2NsG zh5GY$-DN#CudgzRh^Efe_!*-p%e97Y-;K7pxgIY#RIJYs`VqZz>L!Eb3rHt5GlMlR zQ|+fCHHETsYbaK?(h^f8C9P3RP)rBLBZ4o9U@Sal3%goW8s4#y#d6j0{<)Wp7=^xA zmh6KEYa-FyaSHr6f((MUB~<}oa|_ApZ>oS?RMp?fvA6pb)@8uYR4>Sov~I;JL1qfY zZ)bDPZ0*AFPaV2)ZsLd;p^LA&^e44huViRzijg8&Riki2?u3b0&7l<`LTw4RR^mee zvpAUkg(`gwuH9@(n$uHbP5OOZokpFY)MW!5*L_4tyXKuEt)SUG5##ky9r^)JoMbpv zf|NUO5l3~A9#e+zzDE)E<@>IarxY*!Wbc!$naWc}B=jn-Bm0DFCW*d? zNA(92M74lu;TX(}eY9}=MCk`q@I1Ou1*L9jK#uQG3kY)(3=j0ma%yJ)hzU+=XVzAk zPfLYi&2@V}M_}t>BimU%5OX4ba@i&&DOR$_4@(l1gx!bt6|2s=533TC&aBK~2+svp z?rRuhaZQIx#+$F2d?Slzb(T~#rx#G}ZUq=Y1cm0ZU=VF%K$v6l#7Y`pz%`{5V^RPapBoiInb0l{pRd3o5KK;rtv`vVp?zlnlPr52w` zS>uLVnewJ0`RejxSbY>x$UTPBd>pU6;flx`=H|UPK?yA!(FYegvW@Et&~D;nND}3| ztq~?JW5b{R+MD|ZW@#Ab;~vW3b)dt#lFcipr9L7L%TVG&rFkEGnj*>R4BVS?PO-3y z4ze|P^z~c!+2)pHb%7>U2gj$89m7G4-m7$98nNIk>u_bpLH{ocJG{?_X*St}D6^H> z$%caxp)Gucn$HILak_Zsa*+(jvK>}iD_Z~S}J(c6PmtYBo}LxjmnoK3eG!M|T8dU$L@T2I>M z=d~V_7THE`bO%9R=q*yuV{1+B<$mWS1j=h=en2`(g1oyziM}Xl1?>z7eeQL>pmFTN zQ()LuP*JtGkVHQ{d_?iEmDxjHmFqm%sqRdX-PEvn@Ul9@3M4&Oafbsl;`-=Zmd+PyK-FW)M+61ViY?&|~d%7+HD z%C(8V!oP4UezbjFEk1@La&c~3W|KRD9O$sJV z<`iFDZ})Yu{v%7Z5z6O`v!)*0dh=(z|Ju7n%#wxowrLl`u(0j%4W0b*;}TVNL-BLR zZBeKxzfCU=cJy9aMG|NBD&Vl6Xm+dq5V%ULzP@Cnsrv=}fKA73t=t18I&(0E*6rHh zc)+KQX>Ca4eyi%o+biNHRBBJWm_EhKU;c=e6r1JPYdD1P|1u|6FmAiMGf3QAG10YL zOXi`gW9SEhy0*)4GE_IbwMG};&_T%#7H^&AV6@752f;Mx@s)kZLC-5TV`=lvZpi9* zIj4h!ck3zKYKg70(Y>uBL}0%HzXQZ2`q`XI{#%X-s>9lv9NRv+uKIW*$(numCzf09 z!uH;>W9=!@K5!lsuG<&wyJewqoJq`;)QVkN9zND{N)=Ix|4NmTmpixnY|ZVLc*aBo zU;6!b%cyIe$i0ClV5``j*k}kfrh!ZuL_7fxlLIYRP;~?)^X>KgE9jZXbIB;nPZ9b8 zomui?yg!g(Er5CC_boxR32;6cwb;VIt+4*!Ot@;A9#QOtvh<#U$dhwX2G-oDAKvqh z=S98$;z_d+JJJ+79UM>+` zZEH_9OHbEqx^_qRt3w3gdZzqkh!;+lN;YeWmHYh`e~|*5l$mTMb&n05Oo)P-Yx;HFQ2VR8c z;?Slc3;~iRk;A?|gGJ;GLO00@xhLA=BEx}p7m2MVX|BrkxCs3=$02g;ns!G2 + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/resources/themes/cura-light/images/avatar_no_user.svg b/resources/themes/cura-light/images/avatar_no_user.svg new file mode 100644 index 0000000000..bef9cb52db --- /dev/null +++ b/resources/themes/cura-light/images/avatar_no_user.svg @@ -0,0 +1,76 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + From bf1c23243e3c46a465121b8248483c272be61830 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 24 Oct 2018 09:55:51 +0200 Subject: [PATCH 0062/1240] Modify the styles to match the requirements. Contributes to CURA-5784. --- resources/qml/ActionButton.qml | 7 +++ resources/qml/MainWindow/MainWindowHeader.qml | 18 +++++-- resources/themes/cura-dark/theme.json | 2 - resources/themes/cura-light/images/logo.svg | 12 ++--- resources/themes/cura-light/styles.qml | 47 +++++++++---------- resources/themes/cura-light/theme.json | 18 +++++-- 6 files changed, 61 insertions(+), 43 deletions(-) diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index 44299d3734..a8ad94474e 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -20,6 +20,9 @@ Button property var textColor: UM.Theme.getColor("button_text") property var textHoverColor: UM.Theme.getColor("button_text_hover") property var textDisabledColor: textColor + property var outlineColor: color + property var outlineHoverColor: hoverColor + property var outlineDisabledColor: outlineColor contentItem: Row { @@ -34,6 +37,7 @@ Button color: button.hovered ? button.textHoverColor : button.textColor visible: source != "" anchors.verticalCenter: parent.verticalCenter + Behavior on color { ColorAnimation { duration: 50 } } } Label @@ -53,6 +57,9 @@ Button id: backgroundRect color: button.enabled ? (button.hovered ? button.hoverColor : button.color) : button.disabledColor radius: UM.Theme.getSize("action_button_radius").width + border.width: UM.Theme.getSize("default_lining").width + border.color: button.enabled ? (button.hovered ? button.outlineHoverColor : button.outlineColor) : button.outlineDisabledColor + Behavior on color { ColorAnimation { duration: 50 } } } MouseArea diff --git a/resources/qml/MainWindow/MainWindowHeader.qml b/resources/qml/MainWindow/MainWindowHeader.qml index 3de4dc7865..92d9d8ee26 100644 --- a/resources/qml/MainWindow/MainWindowHeader.qml +++ b/resources/qml/MainWindow/MainWindowHeader.qml @@ -36,10 +36,12 @@ Rectangle Row { id: stagesListContainer + spacing: Math.round(UM.Theme.getSize("default_margin").width / 2) anchors { horizontalCenter: parent.horizontalCenter + verticalCenter: parent.verticalCenter leftMargin: UM.Theme.getSize("default_margin").width } @@ -55,9 +57,10 @@ Rectangle text: model.name.toUpperCase() checkable: true checked: model.active + anchors.verticalCenter: parent.verticalCenter exclusiveGroup: mainWindowHeaderMenuGroup style: UM.Theme.styles.main_window_header_tab - height: UM.Theme.getSize("main_window_header").height + height: Math.round(0.56 * UM.Theme.getSize("main_window_header").height) onClicked: UM.Controller.setActiveStage(model.id) iconSource: model.stage.iconSource @@ -78,11 +81,16 @@ Rectangle rightMargin: UM.Theme.getSize("default_margin").width verticalCenter: parent.verticalCenter } + leftPadding: UM.Theme.getSize("default_margin").width + rightPadding: UM.Theme.getSize("default_margin").width text: catalog.i18nc("@action:button", "Toolbox") - color: UM.Theme.getColor("main_window_header_button_background_active") - hoverColor: UM.Theme.getColor("secondary") - textColor: UM.Theme.getColor("main_window_header_button_text_active") - textHoverColor: UM.Theme.getColor("main_window_header_button_text_active") + height: Math.round(0.5 * UM.Theme.getSize("main_window_header").height) + color: UM.Theme.getColor("main_window_header_secondary_button_background_active") + hoverColor: UM.Theme.getColor("main_window_header_secondary_button_background_hovered") + outlineColor: UM.Theme.getColor("main_window_header_secondary_button_outline_active") + outlineHoverColor: UM.Theme.getColor("main_window_header_secondary_button_outline_hovered") + textColor: UM.Theme.getColor("main_window_header_secondary_button_text_active") + textHoverColor: UM.Theme.getColor("main_window_header_secondary_button_text_hovered") onClicked: Cura.Actions.browsePackages.trigger() } diff --git a/resources/themes/cura-dark/theme.json b/resources/themes/cura-dark/theme.json index 897d09ccf2..1625750a19 100644 --- a/resources/themes/cura-dark/theme.json +++ b/resources/themes/cura-dark/theme.json @@ -15,8 +15,6 @@ "border": [127, 127, 127, 255], "secondary": [241, 242, 242, 255], - "main_window_header_background": [31, 36, 39, 255], - "main_window_header_button_text_active": [31, 36, 39, 255], "main_window_header_button_text_inactive": [128, 128, 128, 255], "main_window_header_button_text_hovered": [255, 255, 255, 255], diff --git a/resources/themes/cura-light/images/logo.svg b/resources/themes/cura-light/images/logo.svg index 89692f8295..55842ef530 100644 --- a/resources/themes/cura-light/images/logo.svg +++ b/resources/themes/cura-light/images/logo.svg @@ -25,11 +25,11 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="0.35" - inkscape:cx="-64.285714" - inkscape:cy="560" + inkscape:zoom="1.979899" + inkscape:cx="97.165681" + inkscape:cy="69.313647" inkscape:document-units="mm" - inkscape:current-layer="layer1" + inkscape:current-layer="g4570" showgrid="false" inkscape:window-width="1920" inkscape:window-height="1137" @@ -45,7 +45,7 @@ image/svg+xml - + @@ -61,7 +61,7 @@ id="polygon4506" points="741.8,410.8 781.7,410.8 801.9,390.6 801.9,350.7 762,350.7 741.8,370.9 " class="st0" - style="fill:#333333" /> + style="fill:#3282ff;fill-opacity:1" /> Date: Wed, 24 Oct 2018 11:28:52 +0200 Subject: [PATCH 0063/1240] Add pointing hand cursor shape when hovering the AccountWidget. Contributes to CURA-5784. --- resources/qml/Account/AccountWidget.qml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/resources/qml/Account/AccountWidget.qml b/resources/qml/Account/AccountWidget.qml index f54cf366e0..eb58d66773 100644 --- a/resources/qml/Account/AccountWidget.qml +++ b/resources/qml/Account/AccountWidget.qml @@ -29,6 +29,15 @@ Button outlineColor: loggedIn ? UM.Theme.getColor("account_widget_outline_active") : UM.Theme.getColor("account_widget_outline_inactive") } + MouseArea + { + id: mouseArea + anchors.fill: parent + onPressed: mouse.accepted = false + hoverEnabled: true + cursorShape: accountWidget.enabled ? (hovered ? Qt.PointingHandCursor : Qt.ArrowCursor) : Qt.ForbiddenCursor + } + background: Item {} onClicked: popup.opened ? popup.close() : popup.open() From 0f5dda72c4eaa8b8824efa68587d1c7452cfeb93 Mon Sep 17 00:00:00 2001 From: Gordon LaPlante Date: Wed, 24 Oct 2018 10:26:01 -0400 Subject: [PATCH 0064/1240] gMax 1.5+ Cura 3.5.1 Config gMax 1.5+ Cura 3.5.1 Config updates --- resources/definitions/gmax15plus.def.json | 15 ++++++++++----- resources/definitions/gmax15plus_dual.def.json | 10 ++++++---- .../extruders/gmax15plus_dual_extruder_0.def.json | 2 +- .../extruders/gmax15plus_dual_extruder_1.def.json | 2 +- ...cfg => gmax15plus_global_dual_normal.inst.cfg} | 3 ++- ....cfg => gmax15plus_global_dual_thick.inst.cfg} | 3 ++- ...t.cfg => gmax15plus_global_dual_thin.inst.cfg} | 3 ++- ...=> gmax15plus_global_dual_very_thick.inst.cfg} | 3 ++- ...inst.cfg => gmax15plus_global_normal.inst.cfg} | 3 +++ ....inst.cfg => gmax15plus_global_thick.inst.cfg} | 3 ++- ...n.inst.cfg => gmax15plus_global_thin.inst.cfg} | 3 ++- ....cfg => gmax15plus_global_very_thick.inst.cfg} | 3 ++- resources/variants/gmax15plus_04_e3d.inst.cfg | 2 +- resources/variants/gmax15plus_05_e3d.inst.cfg | 2 +- resources/variants/gmax15plus_06_e3d.inst.cfg | 2 +- resources/variants/gmax15plus_08_e3d.inst.cfg | 2 +- resources/variants/gmax15plus_10_jhead.inst.cfg | 2 +- ...25_e3d.inst.cfg => gmax15plus_12_e3d.inst.cfg} | 4 ++-- .../variants/gmax15plus_dual_025_e3d.inst.cfg | 13 ------------- .../variants/gmax15plus_dual_04_e3d.inst.cfg | 2 +- .../variants/gmax15plus_dual_10_jhead.inst.cfg | 2 +- 21 files changed, 44 insertions(+), 40 deletions(-) rename resources/quality/gmax15plus/{gmax15plus_pla_dual_normal.inst.cfg => gmax15plus_global_dual_normal.inst.cfg} (96%) rename resources/quality/gmax15plus/{gmax15plus_pla_dual_thick.inst.cfg => gmax15plus_global_dual_thick.inst.cfg} (96%) rename resources/quality/gmax15plus/{gmax15plus_pla_dual_thin.inst.cfg => gmax15plus_global_dual_thin.inst.cfg} (96%) rename resources/quality/gmax15plus/{gmax15plus_pla_dual_very_thick.inst.cfg => gmax15plus_global_dual_very_thick.inst.cfg} (96%) rename resources/quality/gmax15plus/{gmax15plus_pla_normal.inst.cfg => gmax15plus_global_normal.inst.cfg} (98%) rename resources/quality/gmax15plus/{gmax15plus_pla_thick.inst.cfg => gmax15plus_global_thick.inst.cfg} (95%) rename resources/quality/gmax15plus/{gmax15plus_pla_thin.inst.cfg => gmax15plus_global_thin.inst.cfg} (95%) rename resources/quality/gmax15plus/{gmax15plus_pla_very_thick.inst.cfg => gmax15plus_global_very_thick.inst.cfg} (95%) rename resources/variants/{gmax15plus_025_e3d.inst.cfg => gmax15plus_12_e3d.inst.cfg} (71%) delete mode 100644 resources/variants/gmax15plus_dual_025_e3d.inst.cfg diff --git a/resources/definitions/gmax15plus.def.json b/resources/definitions/gmax15plus.def.json index 16695714f4..3196076420 100644 --- a/resources/definitions/gmax15plus.def.json +++ b/resources/definitions/gmax15plus.def.json @@ -10,13 +10,16 @@ "category": "Other", "file_formats": "text/x-gcode", "platform": "gmax_1-5_xt-plus_s3d_full model_150707.stl", - "has_machine_quality": true, + "has_machine_quality": true, "has_variants": true, - "variants_name": "Hotend", - "preferred_variant_name": "0.5mm E3D (Default)", - "machine_extruder_trains": { + "variants_name": "Hotend", + "preferred_variant_name": "*0.5mm E3D (Default)*", + "preferred_quality_type": "gmax15plus_global_normal", + "machine_extruder_trains": { "0": "gmax15plus_extruder_0" } + + }, "overrides": { @@ -27,6 +30,8 @@ "machine_depth": { "default_value": 406 }, "machine_height": { "default_value": 533 }, "machine_center_is_zero": { "default_value": false }, + "material_diameter": { "default_value": 1.75 }, + "machine_nozzle_size": { "default_value": 0.5 }, "layer_height": { "default_value": 0.2 }, "layer_height_0": { "default_value": 0.3 }, "retraction_amount": { "default_value": 1 }, @@ -43,7 +48,7 @@ "machine_max_jerk_z": { "default_value": 0.4 }, "machine_max_jerk_e": { "default_value": 5.0 }, "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" }, - "machine_start_gcode": { "default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 ;Home X/Y/Z\nG29 ; Bed level\nM104 S{material_print_temperature} ; Preheat\nM109 S{material_print_temperature} ; Preheat\nG91 ;relative positioning\nG90 ;absolute positioning\nG1 Z25.0 F9000 ;raise nozzle 25mm\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..." }, + "machine_start_gcode": { "default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 ;Home X/Y/Z\nM104 S{material_print_temperature} ; Preheat\nM109 S{material_print_temperature} ; Preheat\nG91 ;relative positioning\nG90 ;absolute positioning\nG1 Z25.0 F9000 ;raise nozzle 25mm\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..." }, "machine_end_gcode": { "default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning" }, "material_print_temperature": { "default_value": 202 }, "wall_thickness": { "default_value": 1 }, diff --git a/resources/definitions/gmax15plus_dual.def.json b/resources/definitions/gmax15plus_dual.def.json index 5972061933..634ce2b3b8 100644 --- a/resources/definitions/gmax15plus_dual.def.json +++ b/resources/definitions/gmax15plus_dual.def.json @@ -11,13 +11,13 @@ "file_formats": "text/x-gcode", "platform": "gmax_1-5_xt-plus_s3d_full model_150707.stl", "has_variants": true, - "has_machine_quality": true, "variants_name": "Hotend", - "preferred_variant_name": "0.5mm E3D (Default)", + "preferred_variant_name": "*0.5mm E3D (Default)*", + "preferred_quality_type": "gmax15plus_global_dual_normal", "machine_extruder_trains": { "0": "gmax15plus_dual_extruder_0", "1": "gmax15plus_dual_extruder_1" - } + } }, "overrides": { @@ -28,6 +28,8 @@ "machine_depth": { "default_value": 406 }, "machine_height": { "default_value": 533 }, "machine_center_is_zero": { "default_value": false }, + "material_diameter": { "default_value": 1.75 }, + "machine_nozzle_size": { "default_value": 0.5 }, "layer_height": { "default_value": 0.2 }, "layer_height_0": { "default_value": 0.3 }, "retraction_amount": { "default_value": 1 }, @@ -44,7 +46,7 @@ "machine_max_jerk_z": { "default_value": 0.4 }, "machine_max_jerk_e": { "default_value": 5.0 }, "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" }, - "machine_start_gcode": { "default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 ;Home X/Y/Z\nG29 ; Bed level\nM104 S{material_print_temperature} T0 ; Preheat Left Extruder\nM104 S{material_print_temperature} T1 ; Preheat Right Extruder\nM109 S{material_print_temperature} T0 ; Preheat Left Extruder\nM109 S{material_print_temperature} T1 ; Preheat Right Extruder\nG91 ;relative positioning\nG90 ;absolute positioning\nM218 T1 X34.3 Y0; Set 2nd extruder offset. This can be changed later if needed\nG1 Z25.0 F9000 ;raise nozzle 25mm\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..." }, + "machine_start_gcode": { "default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 ;Home X/Y/Z\nM104 S{material_print_temperature} T0 ; Preheat Left Extruder\nM104 S{material_print_temperature} T1 ; Preheat Right Extruder\nM109 S{material_print_temperature} T0 ; Preheat Left Extruder\nM109 S{material_print_temperature} T1 ; Preheat Right Extruder\nG91 ;relative positioning\nG90 ;absolute positioning\nM218 T1 X34.3 Y0; Set 2nd extruder offset. This can be changed later if needed\nG1 Z25.0 F9000 ;raise nozzle 25mm\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..." }, "machine_end_gcode": { "default_value": "M104 S0 T0;Left extruder off\nM104 S0 T1; Right extruder off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning" }, "material_print_temperature": { "default_value": 202 }, "wall_thickness": { "default_value": 1 }, diff --git a/resources/extruders/gmax15plus_dual_extruder_0.def.json b/resources/extruders/gmax15plus_dual_extruder_0.def.json index b490f4a40e..524fa72f9f 100644 --- a/resources/extruders/gmax15plus_dual_extruder_0.def.json +++ b/resources/extruders/gmax15plus_dual_extruder_0.def.json @@ -16,7 +16,7 @@ "machine_nozzle_offset_x": { "default_value": 0.0 }, "machine_nozzle_offset_y": { "default_value": 0.0 }, "machine_nozzle_size": { "default_value": 0.5 }, - "material_diameter": { "default_value": 1.75 }, + "material_diameter": { "default_value": 1.75 }, "machine_extruder_start_pos_abs": { "default_value": true }, "machine_extruder_start_pos_x": { "value": 40 }, diff --git a/resources/extruders/gmax15plus_dual_extruder_1.def.json b/resources/extruders/gmax15plus_dual_extruder_1.def.json index ad3c628d6f..1b9f7bda5f 100644 --- a/resources/extruders/gmax15plus_dual_extruder_1.def.json +++ b/resources/extruders/gmax15plus_dual_extruder_1.def.json @@ -16,7 +16,7 @@ "machine_nozzle_offset_x": { "default_value": 0.0 }, "machine_nozzle_offset_y": { "default_value": 0.0 }, "machine_nozzle_size": { "default_value": 0.5 }, - "material_diameter": { "default_value": 1.75 }, + "material_diameter": { "default_value": 1.75 }, "machine_extruder_start_pos_abs": { "default_value": true }, "machine_extruder_start_pos_x": { "value": 40 }, diff --git a/resources/quality/gmax15plus/gmax15plus_pla_dual_normal.inst.cfg b/resources/quality/gmax15plus/gmax15plus_global_dual_normal.inst.cfg similarity index 96% rename from resources/quality/gmax15plus/gmax15plus_pla_dual_normal.inst.cfg rename to resources/quality/gmax15plus/gmax15plus_global_dual_normal.inst.cfg index 7fd2ab2296..5dea12d308 100644 --- a/resources/quality/gmax15plus/gmax15plus_pla_dual_normal.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_global_dual_normal.inst.cfg @@ -8,6 +8,7 @@ setting_version = 5 type = quality quality_type = normal weight = -1 +global_quality = True [values] layer_height = 0.2 @@ -64,7 +65,7 @@ ooze_shield_enabled = True prime_tower_enable = False prime_tower_position_x = 350 prime_tower_position_y = 350 -prime_tower_min_volume = 18 +prime_tower_wall_thickness = 2 switch_extruder_retraction_amount = 6 switch_extruder_retraction_speeds = 60 diff --git a/resources/quality/gmax15plus/gmax15plus_pla_dual_thick.inst.cfg b/resources/quality/gmax15plus/gmax15plus_global_dual_thick.inst.cfg similarity index 96% rename from resources/quality/gmax15plus/gmax15plus_pla_dual_thick.inst.cfg rename to resources/quality/gmax15plus/gmax15plus_global_dual_thick.inst.cfg index 30a99ef243..de374e3a9f 100644 --- a/resources/quality/gmax15plus/gmax15plus_pla_dual_thick.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_global_dual_thick.inst.cfg @@ -8,6 +8,7 @@ setting_version = 5 type = quality quality_type = course weight = -2 +global_quality = True [values] layer_height = 0.28 @@ -64,7 +65,7 @@ ooze_shield_enabled = True prime_tower_enable = False prime_tower_position_x = 350 prime_tower_position_y = 350 -prime_tower_min_volume = 18 +prime_tower_wall_thickness = 2 switch_extruder_retraction_amount = 6 switch_extruder_retraction_speeds = 60 diff --git a/resources/quality/gmax15plus/gmax15plus_pla_dual_thin.inst.cfg b/resources/quality/gmax15plus/gmax15plus_global_dual_thin.inst.cfg similarity index 96% rename from resources/quality/gmax15plus/gmax15plus_pla_dual_thin.inst.cfg rename to resources/quality/gmax15plus/gmax15plus_global_dual_thin.inst.cfg index decafac241..f36ed38c00 100644 --- a/resources/quality/gmax15plus/gmax15plus_pla_dual_thin.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_global_dual_thin.inst.cfg @@ -8,6 +8,7 @@ setting_version = 5 type = quality quality_type = high weight = 0 +global_quality = True [values] layer_height = 0.16 @@ -64,7 +65,7 @@ ooze_shield_enabled = True prime_tower_enable = False prime_tower_position_x = 350 prime_tower_position_y = 350 -prime_tower_min_volume = 18 +prime_tower_wall_thickness = 2 switch_extruder_retraction_amount = 6 switch_extruder_retraction_speeds = 60 diff --git a/resources/quality/gmax15plus/gmax15plus_pla_dual_very_thick.inst.cfg b/resources/quality/gmax15plus/gmax15plus_global_dual_very_thick.inst.cfg similarity index 96% rename from resources/quality/gmax15plus/gmax15plus_pla_dual_very_thick.inst.cfg rename to resources/quality/gmax15plus/gmax15plus_global_dual_very_thick.inst.cfg index a74bdfdd78..2ce55450dd 100644 --- a/resources/quality/gmax15plus/gmax15plus_pla_dual_very_thick.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_global_dual_very_thick.inst.cfg @@ -8,6 +8,7 @@ setting_version = 5 type = quality quality_type = extra_course weight = -3 +global_quality = True [values] layer_height = 0.32 @@ -63,7 +64,7 @@ ooze_shield_enabled = True prime_tower_enable = False prime_tower_position_x = 350 prime_tower_position_y = 350 -prime_tower_min_volume = 18 +prime_tower_wall_thickness = 2 switch_extruder_retraction_amount = 6 switch_extruder_retraction_speeds = 60 diff --git a/resources/quality/gmax15plus/gmax15plus_pla_normal.inst.cfg b/resources/quality/gmax15plus/gmax15plus_global_normal.inst.cfg similarity index 98% rename from resources/quality/gmax15plus/gmax15plus_pla_normal.inst.cfg rename to resources/quality/gmax15plus/gmax15plus_global_normal.inst.cfg index ddf5a4c491..1f7203a03b 100644 --- a/resources/quality/gmax15plus/gmax15plus_pla_normal.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_global_normal.inst.cfg @@ -8,6 +8,7 @@ setting_version = 5 type = quality quality_type = normal weight = -1 +global_quality = True [values] layer_height = 0.2 @@ -57,3 +58,5 @@ top_thickness = 1 bottom_layers = 2 wall_line_count = 2 z_seam_corner = z_seam_corner_none + + diff --git a/resources/quality/gmax15plus/gmax15plus_pla_thick.inst.cfg b/resources/quality/gmax15plus/gmax15plus_global_thick.inst.cfg similarity index 95% rename from resources/quality/gmax15plus/gmax15plus_pla_thick.inst.cfg rename to resources/quality/gmax15plus/gmax15plus_global_thick.inst.cfg index e6cb2b5854..8b01d1a166 100644 --- a/resources/quality/gmax15plus/gmax15plus_pla_thick.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_global_thick.inst.cfg @@ -8,6 +8,7 @@ setting_version = 5 type = quality quality_type = course weight = -2 +global_quality = True [values] layer_height = 0.28 @@ -56,4 +57,4 @@ top_layers = 3 top_thickness = 1 bottom_layers = 2 wall_line_count = 2 -z_seam_corner = z_seam_corner_none +z_seam_corner = z_seam_corner_none \ No newline at end of file diff --git a/resources/quality/gmax15plus/gmax15plus_pla_thin.inst.cfg b/resources/quality/gmax15plus/gmax15plus_global_thin.inst.cfg similarity index 95% rename from resources/quality/gmax15plus/gmax15plus_pla_thin.inst.cfg rename to resources/quality/gmax15plus/gmax15plus_global_thin.inst.cfg index d119539d32..a06fd24ea7 100644 --- a/resources/quality/gmax15plus/gmax15plus_pla_thin.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_global_thin.inst.cfg @@ -8,6 +8,7 @@ setting_version = 5 type = quality quality_type = high weight = 0 +global_quality = True [values] layer_height = 0.16 @@ -56,4 +57,4 @@ top_layers = 5 top_thickness = 1 bottom_layers = 3 wall_line_count = 2 -z_seam_corner = z_seam_corner_none +z_seam_corner = z_seam_corner_none \ No newline at end of file diff --git a/resources/quality/gmax15plus/gmax15plus_pla_very_thick.inst.cfg b/resources/quality/gmax15plus/gmax15plus_global_very_thick.inst.cfg similarity index 95% rename from resources/quality/gmax15plus/gmax15plus_pla_very_thick.inst.cfg rename to resources/quality/gmax15plus/gmax15plus_global_very_thick.inst.cfg index 884029a4ae..36f78673c2 100644 --- a/resources/quality/gmax15plus/gmax15plus_pla_very_thick.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_global_very_thick.inst.cfg @@ -8,6 +8,7 @@ setting_version = 5 type = quality quality_type = extra_course weight = -3 +global_quality = True [values] layer_height = 0.32 @@ -55,4 +56,4 @@ top_layers = 3 top_thickness = 1 bottom_layers = 2 wall_line_count = 2 -z_seam_corner = z_seam_corner_none +z_seam_corner = z_seam_corner_none \ No newline at end of file diff --git a/resources/variants/gmax15plus_04_e3d.inst.cfg b/resources/variants/gmax15plus_04_e3d.inst.cfg index a2f779f426..48fe7ada16 100644 --- a/resources/variants/gmax15plus_04_e3d.inst.cfg +++ b/resources/variants/gmax15plus_04_e3d.inst.cfg @@ -10,4 +10,4 @@ type = variant hardware_type = nozzle [values] -machine_nozzle_size = 0.4 +machine_nozzle_size = 0.4 \ No newline at end of file diff --git a/resources/variants/gmax15plus_05_e3d.inst.cfg b/resources/variants/gmax15plus_05_e3d.inst.cfg index 68ee111aa1..0bb9517da8 100644 --- a/resources/variants/gmax15plus_05_e3d.inst.cfg +++ b/resources/variants/gmax15plus_05_e3d.inst.cfg @@ -10,4 +10,4 @@ type = variant hardware_type = nozzle [values] -machine_nozzle_size = 0.5 +machine_nozzle_size = 0.5 \ No newline at end of file diff --git a/resources/variants/gmax15plus_06_e3d.inst.cfg b/resources/variants/gmax15plus_06_e3d.inst.cfg index 987e882a09..3a372b20c9 100644 --- a/resources/variants/gmax15plus_06_e3d.inst.cfg +++ b/resources/variants/gmax15plus_06_e3d.inst.cfg @@ -10,4 +10,4 @@ type = variant hardware_type = nozzle [values] -machine_nozzle_size = 0.6 +machine_nozzle_size = 0.6 \ No newline at end of file diff --git a/resources/variants/gmax15plus_08_e3d.inst.cfg b/resources/variants/gmax15plus_08_e3d.inst.cfg index bf59b47da0..39eeef748e 100644 --- a/resources/variants/gmax15plus_08_e3d.inst.cfg +++ b/resources/variants/gmax15plus_08_e3d.inst.cfg @@ -10,4 +10,4 @@ type = variant hardware_type = nozzle [values] -machine_nozzle_size = 0.8 +machine_nozzle_size = 0.8 \ No newline at end of file diff --git a/resources/variants/gmax15plus_10_jhead.inst.cfg b/resources/variants/gmax15plus_10_jhead.inst.cfg index 47355f344c..37d2546d2a 100644 --- a/resources/variants/gmax15plus_10_jhead.inst.cfg +++ b/resources/variants/gmax15plus_10_jhead.inst.cfg @@ -10,4 +10,4 @@ type = variant hardware_type = nozzle [values] -machine_nozzle_size = 0.5 +machine_nozzle_size = 0.5 \ No newline at end of file diff --git a/resources/variants/gmax15plus_025_e3d.inst.cfg b/resources/variants/gmax15plus_12_e3d.inst.cfg similarity index 71% rename from resources/variants/gmax15plus_025_e3d.inst.cfg rename to resources/variants/gmax15plus_12_e3d.inst.cfg index 8a6b37067d..57052dd0f8 100644 --- a/resources/variants/gmax15plus_025_e3d.inst.cfg +++ b/resources/variants/gmax15plus_12_e3d.inst.cfg @@ -1,5 +1,5 @@ [general] -name = 0.25mm E3D (Difficult) +name = 1.2mm E3D Volcano version = 4 definition = gmax15plus @@ -10,4 +10,4 @@ type = variant hardware_type = nozzle [values] -machine_nozzle_size = 0.25 +machine_nozzle_size = 1.2 \ No newline at end of file diff --git a/resources/variants/gmax15plus_dual_025_e3d.inst.cfg b/resources/variants/gmax15plus_dual_025_e3d.inst.cfg deleted file mode 100644 index 750a5381b3..0000000000 --- a/resources/variants/gmax15plus_dual_025_e3d.inst.cfg +++ /dev/null @@ -1,13 +0,0 @@ -[general] -name = 0.25mm E3D (Difficult) -version = 4 -definition = gmax15plus_dual - -[metadata] -author = gcreate -setting_version = 5 -type = variant -hardware_type = nozzle - -[values] -machine_nozzle_size = 0.25 diff --git a/resources/variants/gmax15plus_dual_04_e3d.inst.cfg b/resources/variants/gmax15plus_dual_04_e3d.inst.cfg index 4b5a71c53b..809227a62c 100644 --- a/resources/variants/gmax15plus_dual_04_e3d.inst.cfg +++ b/resources/variants/gmax15plus_dual_04_e3d.inst.cfg @@ -10,4 +10,4 @@ type = variant hardware_type = nozzle [values] -machine_nozzle_size = 0.4 +machine_nozzle_size = 0.4 \ No newline at end of file diff --git a/resources/variants/gmax15plus_dual_10_jhead.inst.cfg b/resources/variants/gmax15plus_dual_10_jhead.inst.cfg index cf615bb874..ee0c8fa948 100644 --- a/resources/variants/gmax15plus_dual_10_jhead.inst.cfg +++ b/resources/variants/gmax15plus_dual_10_jhead.inst.cfg @@ -10,4 +10,4 @@ type = variant hardware_type = nozzle [values] -machine_nozzle_size = 0.5 +machine_nozzle_size = 1.0 From e64698209c22b65bb556a042f935db73fcb2df0f Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 25 Oct 2018 17:41:35 +0200 Subject: [PATCH 0065/1240] Adjust the minimum size to be something around 1280x1024. Also adjust some colors color for the dark theme. Contributes to CURA-5772. --- plugins/PrepareStage/PrepareMenu.qml | 6 +++--- .../{ProgressAndSaveWidget.qml => ActionPanelWidget.qml} | 6 ++++-- resources/qml/Cura.qml | 3 ++- resources/qml/qmldir | 3 ++- resources/themes/cura-light/theme.json | 2 +- 5 files changed, 12 insertions(+), 8 deletions(-) rename resources/qml/{ProgressAndSaveWidget.qml => ActionPanelWidget.qml} (98%) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index 8d0f03ec2f..f08de9d317 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -46,7 +46,7 @@ Item Cura.MachineSelector { id: machineSelection - width: UM.Theme.getSize("sidebar").width + width: Math.round(0.8 * UM.Theme.getSize("sidebar").width) - configSelection.width height: prepareMenu.height } @@ -54,8 +54,8 @@ Item { id: configSelection visible: isNetworkPrinter && printerConnected - width: visible ? Math.round(machineSelection.width * 0.15) : 0 - panelWidth: machineSelection.width + width: visible ? Math.round(UM.Theme.getSize("sidebar").width * 0.15) : 0 + panelWidth: Math.round(0.8 * UM.Theme.getSize("sidebar").width) height: prepareMenu.height } diff --git a/resources/qml/ProgressAndSaveWidget.qml b/resources/qml/ActionPanelWidget.qml similarity index 98% rename from resources/qml/ProgressAndSaveWidget.qml rename to resources/qml/ActionPanelWidget.qml index 6e8eb59482..02ac154178 100644 --- a/resources/qml/ProgressAndSaveWidget.qml +++ b/resources/qml/ActionPanelWidget.qml @@ -19,7 +19,9 @@ Rectangle signal showTooltip(Item item, point location, string text) signal hideTooltip() - // Also add an extra margin, as we ant some breathing room around the edges. + color: UM.Theme.getColor("sidebar") + + // Also add an extra margin, as we want some breathing room around the edges. height: saveButton.height + UM.Theme.getSize("sidebar_margin").height Label { @@ -231,7 +233,7 @@ Rectangle { id: saveButton width: parent.width - height: 100 + height: 100 * screenScaleFactor anchors.bottom: parent.bottom } } \ No newline at end of file diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 474f93afa4..c5abb0f107 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -291,6 +291,7 @@ UM.MainWindow onActiveViewChanged: viewModeButton.updateItemActiveFlags() } } + Loader { id: viewPanel @@ -307,7 +308,7 @@ UM.MainWindow source: UM.ActiveView.valid ? UM.ActiveView.activeViewPanel : "" } - ProgressAndSaveWidget + Cura.ActionPanelWidget { anchors.right: parent.right anchors.bottom: parent.bottom diff --git a/resources/qml/qmldir b/resources/qml/qmldir index 2681da00e1..0e5e316409 100644 --- a/resources/qml/qmldir +++ b/resources/qml/qmldir @@ -6,4 +6,5 @@ CustomConfigurationSelector 1.0 CustomConfigurationSelector.qml PrintSetupSelector 1.0 PrintSetupSelector.qml ActionButton 1.0 ActionButton.qml MaterialMenu 1.0 MaterialMenu.qml -NozzleMenu 1.0 NozzleMenu.qml \ No newline at end of file +NozzleMenu 1.0 NozzleMenu.qml +ActionPanelWidget 1.0 ActionPanelWidget.qml \ No newline at end of file diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 68ea9b88f8..1820ead338 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -345,7 +345,7 @@ }, "sizes": { - "window_minimum_size": [70, 50], + "window_minimum_size": [106, 85], "main_window_header": [0.0, 4.5], "main_window_header_button": [8, 4], From 7eedbb6b07ca9a14c76aed521f9b304f7e0c283e Mon Sep 17 00:00:00 2001 From: Gordon LaPlante Date: Thu, 25 Oct 2018 13:48:35 -0400 Subject: [PATCH 0066/1240] Updates Per Cura Comments gMax 1.5+ Config, updates per comments --- resources/definitions/gmax15plus.def.json | 20 ++++++++--------- .../definitions/gmax15plus_dual.def.json | 22 +++++++++---------- .../gmax15plus_dual_extruder_0.def.json | 8 +++---- .../gmax15plus_dual_extruder_1.def.json | 8 +++---- .../gmax15plus_global_dual_normal.inst.cfg | 1 - .../gmax15plus_global_dual_thick.inst.cfg | 1 - .../gmax15plus_global_dual_thin.inst.cfg | 1 - ...gmax15plus_global_dual_very_thick.inst.cfg | 1 - .../variants/gmax15plus_025_e3d.inst.cfg | 13 +++++++++++ .../variants/gmax15plus_dual_025_e3d.inst.cfg | 13 +++++++++++ 10 files changed, 55 insertions(+), 33 deletions(-) create mode 100644 resources/variants/gmax15plus_025_e3d.inst.cfg create mode 100644 resources/variants/gmax15plus_dual_025_e3d.inst.cfg diff --git a/resources/definitions/gmax15plus.def.json b/resources/definitions/gmax15plus.def.json index 3196076420..069b8be999 100644 --- a/resources/definitions/gmax15plus.def.json +++ b/resources/definitions/gmax15plus.def.json @@ -10,20 +10,20 @@ "category": "Other", "file_formats": "text/x-gcode", "platform": "gmax_1-5_xt-plus_s3d_full model_150707.stl", - "has_machine_quality": true, + "has_machine_quality": true, "has_variants": true, - "variants_name": "Hotend", - "preferred_variant_name": "*0.5mm E3D (Default)*", - "preferred_quality_type": "gmax15plus_global_normal", - "machine_extruder_trains": { + "variants_name": "Hotend", + "preferred_variant_name": "0.5mm E3D (Default)", + "preferred_quality_type": "gmax15plus_global_normal", + "machine_extruder_trains": { "0": "gmax15plus_extruder_0" } - + }, "overrides": { - "machine_extruder_count": { "default_value": 1 }, + "machine_extruder_count": { "default_value": 1 }, "machine_name": { "default_value": "gMax 1.5 Plus" }, "machine_heated_bed": { "default_value": false }, "machine_width": { "default_value": 406 }, @@ -48,10 +48,10 @@ "machine_max_jerk_z": { "default_value": 0.4 }, "machine_max_jerk_e": { "default_value": 5.0 }, "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" }, - "machine_start_gcode": { "default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 ;Home X/Y/Z\nM104 S{material_print_temperature} ; Preheat\nM109 S{material_print_temperature} ; Preheat\nG91 ;relative positioning\nG90 ;absolute positioning\nG1 Z25.0 F9000 ;raise nozzle 25mm\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..." }, + "machine_start_gcode": { "default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 ;Home X/Y/Z\nM104 S{material_print_temperature} ; Preheat\nM109 S{material_print_temperature} ; Preheat\nG91 ;relative positioning\nG90 ;absolute positioning\nG1 Z25.0 F9000 ;raise nozzle 25mm\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..." }, "machine_end_gcode": { "default_value": "M104 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning" }, - "material_print_temperature": { "default_value": 202 }, - "wall_thickness": { "default_value": 1 }, + "material_print_temperature": { "default_value": 202 }, + "wall_thickness": { "default_value": 1 }, "top_bottom_thickness": { "default_value": 1 }, "bottom_thickness": { "default_value": 1 } } diff --git a/resources/definitions/gmax15plus_dual.def.json b/resources/definitions/gmax15plus_dual.def.json index 634ce2b3b8..0264ef5977 100644 --- a/resources/definitions/gmax15plus_dual.def.json +++ b/resources/definitions/gmax15plus_dual.def.json @@ -10,19 +10,19 @@ "category": "Other", "file_formats": "text/x-gcode", "platform": "gmax_1-5_xt-plus_s3d_full model_150707.stl", - "has_variants": true, - "variants_name": "Hotend", - "preferred_variant_name": "*0.5mm E3D (Default)*", - "preferred_quality_type": "gmax15plus_global_dual_normal", - "machine_extruder_trains": { - "0": "gmax15plus_dual_extruder_0", - "1": "gmax15plus_dual_extruder_1" - } + "has_variants": true, + "variants_name": "Hotend", + "preferred_variant_name": "0.5mm E3D (Default)", + "preferred_quality_type": "gmax15plus_global_dual_normal", + "machine_extruder_trains": { + "0": "gmax15plus_dual_extruder_0", + "1": "gmax15plus_dual_extruder_1" + } }, "overrides": { "machine_name": { "default_value": "gMax 1.5 Plus Dual Extruder" }, - "machine_extruder_count": { "default_value": 2 }, + "machine_extruder_count": { "default_value": 2 }, "machine_heated_bed": { "default_value": false }, "machine_width": { "default_value": 406 }, "machine_depth": { "default_value": 406 }, @@ -48,8 +48,8 @@ "machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" }, "machine_start_gcode": { "default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 ;Home X/Y/Z\nM104 S{material_print_temperature} T0 ; Preheat Left Extruder\nM104 S{material_print_temperature} T1 ; Preheat Right Extruder\nM109 S{material_print_temperature} T0 ; Preheat Left Extruder\nM109 S{material_print_temperature} T1 ; Preheat Right Extruder\nG91 ;relative positioning\nG90 ;absolute positioning\nM218 T1 X34.3 Y0; Set 2nd extruder offset. This can be changed later if needed\nG1 Z25.0 F9000 ;raise nozzle 25mm\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..." }, "machine_end_gcode": { "default_value": "M104 S0 T0;Left extruder off\nM104 S0 T1; Right extruder off\nM140 S0 ;heated bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning" }, - "material_print_temperature": { "default_value": 202 }, - "wall_thickness": { "default_value": 1 }, + "material_print_temperature": { "default_value": 202 }, + "wall_thickness": { "default_value": 1 }, "top_bottom_thickness": { "default_value": 1 }, "bottom_thickness": { "default_value": 1 } } diff --git a/resources/extruders/gmax15plus_dual_extruder_0.def.json b/resources/extruders/gmax15plus_dual_extruder_0.def.json index 524fa72f9f..d3146a0576 100644 --- a/resources/extruders/gmax15plus_dual_extruder_0.def.json +++ b/resources/extruders/gmax15plus_dual_extruder_0.def.json @@ -15,10 +15,10 @@ }, "machine_nozzle_offset_x": { "default_value": 0.0 }, "machine_nozzle_offset_y": { "default_value": 0.0 }, - "machine_nozzle_size": { "default_value": 0.5 }, - "material_diameter": { "default_value": 1.75 }, - - "machine_extruder_start_pos_abs": { "default_value": true }, + "machine_nozzle_size": { "default_value": 0.5 }, + "material_diameter": { "default_value": 1.75 }, + + "machine_extruder_start_pos_abs": { "default_value": true }, "machine_extruder_start_pos_x": { "value": 40 }, "machine_extruder_start_pos_y": { "value": 210 }, "machine_extruder_end_pos_abs": { "default_value": true }, diff --git a/resources/extruders/gmax15plus_dual_extruder_1.def.json b/resources/extruders/gmax15plus_dual_extruder_1.def.json index 1b9f7bda5f..7b7354d794 100644 --- a/resources/extruders/gmax15plus_dual_extruder_1.def.json +++ b/resources/extruders/gmax15plus_dual_extruder_1.def.json @@ -15,10 +15,10 @@ }, "machine_nozzle_offset_x": { "default_value": 0.0 }, "machine_nozzle_offset_y": { "default_value": 0.0 }, - "machine_nozzle_size": { "default_value": 0.5 }, - "material_diameter": { "default_value": 1.75 }, - - "machine_extruder_start_pos_abs": { "default_value": true }, + "machine_nozzle_size": { "default_value": 0.5 }, + "material_diameter": { "default_value": 1.75 }, + + "machine_extruder_start_pos_abs": { "default_value": true }, "machine_extruder_start_pos_x": { "value": 40 }, "machine_extruder_start_pos_y": { "value": 210 }, "machine_extruder_end_pos_abs": { "default_value": true }, diff --git a/resources/quality/gmax15plus/gmax15plus_global_dual_normal.inst.cfg b/resources/quality/gmax15plus/gmax15plus_global_dual_normal.inst.cfg index 5dea12d308..743311bb54 100644 --- a/resources/quality/gmax15plus/gmax15plus_global_dual_normal.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_global_dual_normal.inst.cfg @@ -65,7 +65,6 @@ ooze_shield_enabled = True prime_tower_enable = False prime_tower_position_x = 350 prime_tower_position_y = 350 -prime_tower_wall_thickness = 2 switch_extruder_retraction_amount = 6 switch_extruder_retraction_speeds = 60 diff --git a/resources/quality/gmax15plus/gmax15plus_global_dual_thick.inst.cfg b/resources/quality/gmax15plus/gmax15plus_global_dual_thick.inst.cfg index de374e3a9f..94a15bfa2b 100644 --- a/resources/quality/gmax15plus/gmax15plus_global_dual_thick.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_global_dual_thick.inst.cfg @@ -65,7 +65,6 @@ ooze_shield_enabled = True prime_tower_enable = False prime_tower_position_x = 350 prime_tower_position_y = 350 -prime_tower_wall_thickness = 2 switch_extruder_retraction_amount = 6 switch_extruder_retraction_speeds = 60 diff --git a/resources/quality/gmax15plus/gmax15plus_global_dual_thin.inst.cfg b/resources/quality/gmax15plus/gmax15plus_global_dual_thin.inst.cfg index f36ed38c00..534f646205 100644 --- a/resources/quality/gmax15plus/gmax15plus_global_dual_thin.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_global_dual_thin.inst.cfg @@ -65,7 +65,6 @@ ooze_shield_enabled = True prime_tower_enable = False prime_tower_position_x = 350 prime_tower_position_y = 350 -prime_tower_wall_thickness = 2 switch_extruder_retraction_amount = 6 switch_extruder_retraction_speeds = 60 diff --git a/resources/quality/gmax15plus/gmax15plus_global_dual_very_thick.inst.cfg b/resources/quality/gmax15plus/gmax15plus_global_dual_very_thick.inst.cfg index 2ce55450dd..a35e951c32 100644 --- a/resources/quality/gmax15plus/gmax15plus_global_dual_very_thick.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_global_dual_very_thick.inst.cfg @@ -64,7 +64,6 @@ ooze_shield_enabled = True prime_tower_enable = False prime_tower_position_x = 350 prime_tower_position_y = 350 -prime_tower_wall_thickness = 2 switch_extruder_retraction_amount = 6 switch_extruder_retraction_speeds = 60 diff --git a/resources/variants/gmax15plus_025_e3d.inst.cfg b/resources/variants/gmax15plus_025_e3d.inst.cfg new file mode 100644 index 0000000000..f106b13be1 --- /dev/null +++ b/resources/variants/gmax15plus_025_e3d.inst.cfg @@ -0,0 +1,13 @@ +[general] +name = 0.25mm E3D (Difficult) +version = 2 +definition = gmax15plus + +[metadata] +author = gcreate +setting_version = 5 +type = variant +hardware_type = nozzle + +[values] +machine_nozzle_size = 0.25 diff --git a/resources/variants/gmax15plus_dual_025_e3d.inst.cfg b/resources/variants/gmax15plus_dual_025_e3d.inst.cfg new file mode 100644 index 0000000000..d5f6457902 --- /dev/null +++ b/resources/variants/gmax15plus_dual_025_e3d.inst.cfg @@ -0,0 +1,13 @@ +[general] +name = 0.25mm E3D (Difficult) +version = 2 +definition = gmax15plus_dual + +[metadata] +author = gcreate +setting_version = 5 +type = variant +hardware_type = nozzle + +[values] +machine_nozzle_size = 0.25 From a8531a335dda730bc07b71620f1123d73b1c939e Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 26 Oct 2018 09:52:02 +0200 Subject: [PATCH 0067/1240] Fix the alignments for the sliders in the simulation view. Contributes to CURA-5772. --- plugins/SimulationView/LayerSlider.qml | 6 ++--- plugins/SimulationView/SimulationView.qml | 29 ++++++++++++++--------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/plugins/SimulationView/LayerSlider.qml b/plugins/SimulationView/LayerSlider.qml index 1552506969..c30ea621c4 100644 --- a/plugins/SimulationView/LayerSlider.qml +++ b/plugins/SimulationView/LayerSlider.qml @@ -167,7 +167,7 @@ Item id: rangleHandleLabel height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height - x: parent.x - width - UM.Theme.getSize("default_margin").width + x: parent.x + parent.width + UM.Theme.getSize("default_margin").width anchors.verticalCenter: parent.verticalCenter target: Qt.point(sliderRoot.width, y + height / 2) visible: sliderRoot.activeHandle == parent @@ -275,7 +275,7 @@ Item id: upperHandleLabel height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height - x: parent.x - width - UM.Theme.getSize("default_margin").width + x: parent.x + parent.width + UM.Theme.getSize("default_margin").width anchors.verticalCenter: parent.verticalCenter target: Qt.point(sliderRoot.width, y + height / 2) visible: sliderRoot.activeHandle == parent @@ -385,7 +385,7 @@ Item id: lowerHandleLabel height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height - x: parent.x - width - UM.Theme.getSize("default_margin").width + x: parent.x + parent.width + UM.Theme.getSize("default_margin").width anchors.verticalCenter: parent.verticalCenter target: Qt.point(sliderRoot.width, y + height / 2) visible: sliderRoot.activeHandle == parent diff --git a/plugins/SimulationView/SimulationView.qml b/plugins/SimulationView/SimulationView.qml index be124157fb..7a83a07ac1 100644 --- a/plugins/SimulationView/SimulationView.qml +++ b/plugins/SimulationView/SimulationView.qml @@ -588,13 +588,14 @@ Item id: slidersBox width: parent.width + height: parent.height visible: UM.SimulationView.layerActivity && CuraApplication.platformActivity anchors { - top: parent.bottom - topMargin: UM.Theme.getSize("slider_layerview_margin").height - left: parent.left + top: parent.top + leftMargin: UM.Theme.getSize("slider_layerview_margin").height + left: parent.right } PathSlider @@ -602,9 +603,13 @@ Item id: pathSlider height: UM.Theme.getSize("slider_handle").width - anchors.left: playButton.right - anchors.leftMargin: UM.Theme.getSize("default_margin").width - anchors.right: parent.right + anchors + { + verticalCenter: playButton.verticalCenter + left: playButton.right + leftMargin: UM.Theme.getSize("default_margin").width + right: parent.right + } visible: !UM.SimulationView.compatibilityMode // custom properties @@ -650,10 +655,11 @@ Item anchors { - top: !UM.SimulationView.compatibilityMode ? pathSlider.bottom : parent.top - topMargin: !UM.SimulationView.compatibilityMode ? UM.Theme.getSize("default_margin").height : 0 - right: parent.right - rightMargin: UM.Theme.getSize("slider_layerview_margin").width + bottom: !UM.SimulationView.compatibilityMode ? pathSlider.top : parent.bottom + top: parent.top + bottomMargin: !UM.SimulationView.compatibilityMode ? UM.Theme.getSize("default_margin").height : 0 + left: parent.left + leftMargin: Math.round(UM.Theme.getSize("slider_layerview_margin").width / 2) } // custom properties @@ -704,7 +710,8 @@ Item visible: !UM.SimulationView.compatibilityMode anchors { - verticalCenter: pathSlider.verticalCenter + left: parent.left + bottom: parent.bottom } property var status: 0 // indicates if it's stopped (0) or playing (1) From 54518bdc82221950ed63d1e7bde2e4dbf26b47cf Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 26 Oct 2018 11:14:39 +0200 Subject: [PATCH 0068/1240] Change the minimum window size to be 1280x800. Contributes to CURA-5784. --- resources/themes/cura-light/theme.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 1820ead338..6a83c2c566 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -345,7 +345,7 @@ }, "sizes": { - "window_minimum_size": [106, 85], + "window_minimum_size": [106, 66], "main_window_header": [0.0, 4.5], "main_window_header_button": [8, 4], From a277bd9f3b8ee3383be53e805e47058b49a7d3f9 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 26 Oct 2018 11:29:05 +0200 Subject: [PATCH 0069/1240] Don't use the hand cursor in the action buttons by default. Contributes to CURA-5784. --- resources/qml/Account/AccountWidget.qml | 21 +++++---------------- resources/qml/ActionButton.qml | 1 - 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/resources/qml/Account/AccountWidget.qml b/resources/qml/Account/AccountWidget.qml index eb58d66773..46092e4153 100644 --- a/resources/qml/Account/AccountWidget.qml +++ b/resources/qml/Account/AccountWidget.qml @@ -16,30 +16,19 @@ Button implicitHeight: UM.Theme.getSize("main_window_header").height implicitWidth: UM.Theme.getSize("main_window_header").height - AvatarImage + background: AvatarImage { id: avatar - width: Math.round(0.8 * parent.width) - height: Math.round(0.8 * parent.height) - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter + width: Math.round(0.8 * accountWidget.width) + height: Math.round(0.8 * accountWidget.height) + anchors.verticalCenter: accountWidget.verticalCenter + anchors.horizontalCenter: accountWidget.horizontalCenter source: loggedIn ? profile["profile_image_url"] : UM.Theme.getImage("avatar_no_user") outlineColor: loggedIn ? UM.Theme.getColor("account_widget_outline_active") : UM.Theme.getColor("account_widget_outline_inactive") } - MouseArea - { - id: mouseArea - anchors.fill: parent - onPressed: mouse.accepted = false - hoverEnabled: true - cursorShape: accountWidget.enabled ? (hovered ? Qt.PointingHandCursor : Qt.ArrowCursor) : Qt.ForbiddenCursor - } - - background: Item {} - onClicked: popup.opened ? popup.close() : popup.open() Popup diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index a8ad94474e..a1c03af143 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -68,6 +68,5 @@ Button anchors.fill: parent onPressed: mouse.accepted = false hoverEnabled: true - cursorShape: button.enabled ? (hovered ? Qt.PointingHandCursor : Qt.ArrowCursor) : Qt.ForbiddenCursor } } From 2cbaece3e1472639c29a5b2d7f193b62eabcadf9 Mon Sep 17 00:00:00 2001 From: pinchies <> Date: Fri, 26 Oct 2018 22:21:01 +1100 Subject: [PATCH 0070/1240] Add JGAurora Z-603S Add a preset for the JGAurora Z-603S to model list --- .../definitions/jgaurora_z_603s.def.json | 96 +++++++++++++++++++ .../jgaurora_z_603s_extruder_0.def.json | 16 ++++ 2 files changed, 112 insertions(+) create mode 100644 resources/definitions/jgaurora_z_603s.def.json create mode 100644 resources/extruders/jgaurora_z_603s_extruder_0.def.json diff --git a/resources/definitions/jgaurora_z_603s.def.json b/resources/definitions/jgaurora_z_603s.def.json new file mode 100644 index 0000000000..af7fa823db --- /dev/null +++ b/resources/definitions/jgaurora_z_603s.def.json @@ -0,0 +1,96 @@ +{ + "name": "JGAurora Z-603S", + "version": 2, + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "author": "Samuel Pinches", + "manufacturer": "JGAurora", + "file_formats": "text/x-gcode", + "preferred_quality_type": "fine", + "machine_extruder_trains": + { + "0": "jgaurora_z_603s_extruder_0" + } + }, + "overrides": { + "machine_name": { + "default_value": "JGAurora Z-603S" + }, + "machine_start_gcode": { + "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F6000 ;move Z to position 15.0 mm as fast as possible\nG92 E0 ;zero the extruded length\nG1 X0.0 Y0.0 F1000.0 ;go to edge of print area\nG1 X60.0 E9.0 F1000.0 ;intro line\nG1 X100.0 E21.5 F1000.0 ;intro line\nG92 E0 ;zero the extruded length again\n; -- end of START GCODE --" + }, + "machine_end_gcode": { + "default_value": "; -- END GCODE --\nM104 S0 ;turn off nozzle heater\nM140 S0 ;turn off bed heater\nG91 ;set to relative positioning\nG1 E-10 F300 ;retract the filament slightly\nG90 ;set to absolute positioning\nG28 X0 ;move to the X-axis origin (Home)\nG0 Y280 F600 ;bring the bed to the front for easy print removal\nM84 ;turn off stepper motors\n; -- end of END GCODE --" + }, + "machine_width": { + "default_value": 280 + }, + "machine_height": { + "default_value": 175 + }, + "machine_depth": { + "default_value": 180 + }, + "machine_heated_bed": { + "default_value": true + }, + "machine_center_is_zero": { + "default_value": false + }, + "gantry_height": { + "default_value": 10 + }, + "machine_gcode_flavor": { + "default_value": "RepRap (Marlin/Sprinter)" + }, + "material_diameter": { + "default_value": 1.75 + }, + "material_print_temperature": { + "default_value": 210 + }, + "material_bed_temperature": { + "default_value": 55 + }, + "layer_height": { + "default_value": 0.15 + }, + "layer_height_0": { + "default_value": 0.2 + }, + "wall_thickness": { + "default_value": 1.2 + }, + "speed_print": { + "default_value": 60 + }, + "speed_infill": { + "default_value": 60 + }, + "speed_wall": { + "default_value": 30 + }, + "speed_topbottom": { + "default_value": 45 + }, + "speed_travel": { + "default_value": 125 + }, + "speed_layer_0": { + "default_value": 20 + }, + "support_enable": { + "default_value": true + }, + "retraction_enable": { + "default_value": true + }, + "retraction_amount": { + "default_value": 5 + }, + "retraction_speed": { + "default_value": 50 + } + } +} \ No newline at end of file diff --git a/resources/extruders/jgaurora_z_603s_extruder_0.def.json b/resources/extruders/jgaurora_z_603s_extruder_0.def.json new file mode 100644 index 0000000000..987425b28a --- /dev/null +++ b/resources/extruders/jgaurora_z_603s_extruder_0.def.json @@ -0,0 +1,16 @@ +{ + "id": "jgaurora_z_603s_extruder_0", + "version": 2, + "name": "Extruder 1", + "inherits": "fdmextruder", + "metadata": { + "machine": "jgaurora_z_603s", + "position": "0" + }, + + "overrides": { + "extruder_nr": { "default_value": 0 }, + "machine_nozzle_size": { "default_value": 0.4 }, + "material_diameter": { "default_value": 1.75 } + } +} From bc5f92b69b096234512fa8e9ad0f3a0d63c7dd71 Mon Sep 17 00:00:00 2001 From: pinchies <> Date: Fri, 26 Oct 2018 22:24:04 +1100 Subject: [PATCH 0071/1240] Add JGAurora A1 Add a preset for the JGAurora A1 to model list --- resources/definitions/jgaurora_a1.def.json | 96 +++++++++++++++++++ .../extruders/jgaurora_a1_extruder_0.def.json | 16 ++++ 2 files changed, 112 insertions(+) create mode 100644 resources/definitions/jgaurora_a1.def.json create mode 100644 resources/extruders/jgaurora_a1_extruder_0.def.json diff --git a/resources/definitions/jgaurora_a1.def.json b/resources/definitions/jgaurora_a1.def.json new file mode 100644 index 0000000000..004fd8741d --- /dev/null +++ b/resources/definitions/jgaurora_a1.def.json @@ -0,0 +1,96 @@ +{ + "name": "JGAurora A1", + "version": 2, + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "author": "Samuel Pinches", + "manufacturer": "JGAurora", + "file_formats": "text/x-gcode", + "preferred_quality_type": "fine", + "machine_extruder_trains": + { + "0": "jgaurora_a1_extruder_0" + } + }, + "overrides": { + "machine_name": { + "default_value": "JGAurora A1" + }, + "machine_start_gcode": { + "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F6000 ;move Z to position 15.0 mm as fast as possible\nG92 E0 ;zero the extruded length\nG1 X0.0 Y0.0 F1000.0 ;go to edge of print area\nG1 X60.0 E9.0 F1000.0 ;intro line\nG1 X100.0 E21.5 F1000.0 ;intro line\nG92 E0 ;zero the extruded length again\n; -- end of START GCODE --" + }, + "machine_end_gcode": { + "default_value": "; -- END GCODE --\nM104 S0 ;turn off nozzle heater\nM140 S0 ;turn off bed heater\nG91 ;set to relative positioning\nG1 E-10 F300 ;retract the filament slightly\nG90 ;set to absolute positioning\nG28 X0 ;move to the X-axis origin (Home)\nG0 Y280 F600 ;bring the bed to the front for easy print removal\nM84 ;turn off stepper motors\n; -- end of END GCODE --" + }, + "machine_width": { + "default_value": 300 + }, + "machine_height": { + "default_value": 300 + }, + "machine_depth": { + "default_value": 300 + }, + "machine_heated_bed": { + "default_value": true + }, + "machine_center_is_zero": { + "default_value": false + }, + "gantry_height": { + "default_value": 10 + }, + "machine_gcode_flavor": { + "default_value": "RepRap (Marlin/Sprinter)" + }, + "material_diameter": { + "default_value": 1.75 + }, + "material_print_temperature": { + "default_value": 215 + }, + "material_bed_temperature": { + "default_value": 67 + }, + "layer_height": { + "default_value": 0.15 + }, + "layer_height_0": { + "default_value": 0.12 + }, + "wall_thickness": { + "default_value": 1.2 + }, + "speed_print": { + "default_value": 40 + }, + "speed_infill": { + "default_value": 40 + }, + "speed_wall": { + "default_value": 35 + }, + "speed_topbottom": { + "default_value": 35 + }, + "speed_travel": { + "default_value": 120 + }, + "speed_layer_0": { + "default_value": 12 + }, + "support_enable": { + "default_value": true + }, + "retraction_enable": { + "default_value": true + }, + "retraction_amount": { + "default_value": 6 + }, + "retraction_speed": { + "default_value": 40 + } + } +} \ No newline at end of file diff --git a/resources/extruders/jgaurora_a1_extruder_0.def.json b/resources/extruders/jgaurora_a1_extruder_0.def.json new file mode 100644 index 0000000000..71742b734a --- /dev/null +++ b/resources/extruders/jgaurora_a1_extruder_0.def.json @@ -0,0 +1,16 @@ +{ + "id": "jgaurora_a1_extruder_0", + "version": 2, + "name": "Extruder 1", + "inherits": "fdmextruder", + "metadata": { + "machine": "jgaurora_a1", + "position": "0" + }, + + "overrides": { + "extruder_nr": { "default_value": 0 }, + "machine_nozzle_size": { "default_value": 0.4 }, + "material_diameter": { "default_value": 1.75 } + } +} From 5d4aa569a9d6505736944b2f5ccfdb2548d29ba7 Mon Sep 17 00:00:00 2001 From: pinchies <> Date: Fri, 26 Oct 2018 22:34:15 +1100 Subject: [PATCH 0072/1240] Revert "Add JGAurora A1" This reverts commit bc5f92b69b096234512fa8e9ad0f3a0d63c7dd71. --- resources/definitions/jgaurora_a1.def.json | 96 ------------------- .../extruders/jgaurora_a1_extruder_0.def.json | 16 ---- 2 files changed, 112 deletions(-) delete mode 100644 resources/definitions/jgaurora_a1.def.json delete mode 100644 resources/extruders/jgaurora_a1_extruder_0.def.json diff --git a/resources/definitions/jgaurora_a1.def.json b/resources/definitions/jgaurora_a1.def.json deleted file mode 100644 index 004fd8741d..0000000000 --- a/resources/definitions/jgaurora_a1.def.json +++ /dev/null @@ -1,96 +0,0 @@ -{ - "name": "JGAurora A1", - "version": 2, - "inherits": "fdmprinter", - "metadata": { - "visible": true, - "author": "Samuel Pinches", - "manufacturer": "JGAurora", - "file_formats": "text/x-gcode", - "preferred_quality_type": "fine", - "machine_extruder_trains": - { - "0": "jgaurora_a1_extruder_0" - } - }, - "overrides": { - "machine_name": { - "default_value": "JGAurora A1" - }, - "machine_start_gcode": { - "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F6000 ;move Z to position 15.0 mm as fast as possible\nG92 E0 ;zero the extruded length\nG1 X0.0 Y0.0 F1000.0 ;go to edge of print area\nG1 X60.0 E9.0 F1000.0 ;intro line\nG1 X100.0 E21.5 F1000.0 ;intro line\nG92 E0 ;zero the extruded length again\n; -- end of START GCODE --" - }, - "machine_end_gcode": { - "default_value": "; -- END GCODE --\nM104 S0 ;turn off nozzle heater\nM140 S0 ;turn off bed heater\nG91 ;set to relative positioning\nG1 E-10 F300 ;retract the filament slightly\nG90 ;set to absolute positioning\nG28 X0 ;move to the X-axis origin (Home)\nG0 Y280 F600 ;bring the bed to the front for easy print removal\nM84 ;turn off stepper motors\n; -- end of END GCODE --" - }, - "machine_width": { - "default_value": 300 - }, - "machine_height": { - "default_value": 300 - }, - "machine_depth": { - "default_value": 300 - }, - "machine_heated_bed": { - "default_value": true - }, - "machine_center_is_zero": { - "default_value": false - }, - "gantry_height": { - "default_value": 10 - }, - "machine_gcode_flavor": { - "default_value": "RepRap (Marlin/Sprinter)" - }, - "material_diameter": { - "default_value": 1.75 - }, - "material_print_temperature": { - "default_value": 215 - }, - "material_bed_temperature": { - "default_value": 67 - }, - "layer_height": { - "default_value": 0.15 - }, - "layer_height_0": { - "default_value": 0.12 - }, - "wall_thickness": { - "default_value": 1.2 - }, - "speed_print": { - "default_value": 40 - }, - "speed_infill": { - "default_value": 40 - }, - "speed_wall": { - "default_value": 35 - }, - "speed_topbottom": { - "default_value": 35 - }, - "speed_travel": { - "default_value": 120 - }, - "speed_layer_0": { - "default_value": 12 - }, - "support_enable": { - "default_value": true - }, - "retraction_enable": { - "default_value": true - }, - "retraction_amount": { - "default_value": 6 - }, - "retraction_speed": { - "default_value": 40 - } - } -} \ No newline at end of file diff --git a/resources/extruders/jgaurora_a1_extruder_0.def.json b/resources/extruders/jgaurora_a1_extruder_0.def.json deleted file mode 100644 index 71742b734a..0000000000 --- a/resources/extruders/jgaurora_a1_extruder_0.def.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "id": "jgaurora_a1_extruder_0", - "version": 2, - "name": "Extruder 1", - "inherits": "fdmextruder", - "metadata": { - "machine": "jgaurora_a1", - "position": "0" - }, - - "overrides": { - "extruder_nr": { "default_value": 0 }, - "machine_nozzle_size": { "default_value": 0.4 }, - "material_diameter": { "default_value": 1.75 } - } -} From 1f97aa3df6f4330be335dc9529eede879636687e Mon Sep 17 00:00:00 2001 From: pinchies Date: Fri, 26 Oct 2018 23:37:31 +1100 Subject: [PATCH 0073/1240] Add files via upload --- .../extruders/jgaurora_a5_extruder_0.def.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 resources/extruders/jgaurora_a5_extruder_0.def.json diff --git a/resources/extruders/jgaurora_a5_extruder_0.def.json b/resources/extruders/jgaurora_a5_extruder_0.def.json new file mode 100644 index 0000000000..fbc6ba77e6 --- /dev/null +++ b/resources/extruders/jgaurora_a5_extruder_0.def.json @@ -0,0 +1,16 @@ +{ + "id": "jgaurora_a5_extruder_0", + "version": 2, + "name": "Extruder 1", + "inherits": "fdmextruder", + "metadata": { + "machine": "jgaurora_a5", + "position": "0" + }, + + "overrides": { + "extruder_nr": { "default_value": 0 }, + "machine_nozzle_size": { "default_value": 0.4 }, + "material_diameter": { "default_value": 1.75 } + } +} From f0277f5ef381a958e2559ffb7fbbcf33818cfb37 Mon Sep 17 00:00:00 2001 From: pinchies Date: Fri, 26 Oct 2018 23:38:30 +1100 Subject: [PATCH 0074/1240] Add files via upload --- resources/definitions/jgaurora_a5.def.json | 98 ++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 resources/definitions/jgaurora_a5.def.json diff --git a/resources/definitions/jgaurora_a5.def.json b/resources/definitions/jgaurora_a5.def.json new file mode 100644 index 0000000000..6143ef1523 --- /dev/null +++ b/resources/definitions/jgaurora_a5.def.json @@ -0,0 +1,98 @@ +{ + "name": "JGAurora A5", + "version": 2, + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "author": "Samuel Pinches", + "manufacturer": "JGAurora", + "file_formats": "text/x-gcode", + "platform": "jgaurora_a5.stl", + "platform_offset": [-242, -101, 273], + "preferred_quality_type": "fine", + "machine_extruder_trains": + { + "0": "jgaurora_a5_extruder_0" + } + }, + "overrides": { + "machine_name": { + "default_value": "JGAurora A5" + }, + "machine_start_gcode": { + "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F6000 ;move Z to position 15.0 mm as fast as possible\nG92 E0 ;zero the extruded length\nG1 X0.0 Y0.0 F1000.0 ;go to edge of print area\nG1 X60.0 E9.0 F1000.0 ;intro line\nG1 X100.0 E21.5 F1000.0 ;intro line\nG92 E0 ;zero the extruded length again\n; -- end of START GCODE --" + }, + "machine_end_gcode": { + "default_value": "; -- END GCODE --\nM104 S0 ;turn off nozzle heater\nM140 S0 ;turn off bed heater\nG91 ;set to relative positioning\nG1 E-10 F300 ;retract the filament slightly\nG90 ;set to absolute positioning\nG28 X0 ;move to the X-axis origin (Home)\nG0 Y280 F600 ;bring the bed to the front for easy print removal\nM84 ;turn off stepper motors\n; -- end of END GCODE --" + }, + "machine_width": { + "default_value": 300 + }, + "machine_height": { + "default_value": 320 + }, + "machine_depth": { + "default_value": 300 + }, + "machine_heated_bed": { + "default_value": true + }, + "machine_center_is_zero": { + "default_value": false + }, + "gantry_height": { + "default_value": 10 + }, + "machine_gcode_flavor": { + "default_value": "RepRap (Marlin/Sprinter)" + }, + "material_diameter": { + "default_value": 1.75 + }, + "material_print_temperature": { + "default_value": 215 + }, + "material_bed_temperature": { + "default_value": 67 + }, + "layer_height": { + "default_value": 0.15 + }, + "layer_height_0": { + "default_value": 0.12 + }, + "wall_thickness": { + "default_value": 1.2 + }, + "speed_print": { + "default_value": 40 + }, + "speed_infill": { + "default_value": 40 + }, + "speed_wall": { + "default_value": 35 + }, + "speed_topbottom": { + "default_value": 35 + }, + "speed_travel": { + "default_value": 120 + }, + "speed_layer_0": { + "default_value": 12 + }, + "support_enable": { + "default_value": true + }, + "retraction_enable": { + "default_value": true + }, + "retraction_amount": { + "default_value": 8 + }, + "retraction_speed": { + "default_value": 45 + } + } +} \ No newline at end of file From 494bedbe0d1f446c137d7705d5d66bec926760ee Mon Sep 17 00:00:00 2001 From: pinchies Date: Fri, 26 Oct 2018 23:40:27 +1100 Subject: [PATCH 0075/1240] Add files via upload --- resources/meshes/jgaurora_a5.stl | Bin 0 -> 1000084 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 resources/meshes/jgaurora_a5.stl diff --git a/resources/meshes/jgaurora_a5.stl b/resources/meshes/jgaurora_a5.stl new file mode 100644 index 0000000000000000000000000000000000000000..c525b036492db15e0c2c206859e54bbf20ba5301 GIT binary patch literal 1000084 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�nAJsXQuBI;Oc_GdhnxoxS7dXRN+0{t99L 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 Spr#su3KHsGOTXJo$o~KVh0fLh literal 0 HcmV?d00001 From c8269ee4c081f312c8611222fedca71952ea7ad9 Mon Sep 17 00:00:00 2001 From: pinchies Date: Fri, 26 Oct 2018 23:42:27 +1100 Subject: [PATCH 0076/1240] Add files via upload --- .../extruders/jgaurora_a1_extruder_0.def.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 resources/extruders/jgaurora_a1_extruder_0.def.json diff --git a/resources/extruders/jgaurora_a1_extruder_0.def.json b/resources/extruders/jgaurora_a1_extruder_0.def.json new file mode 100644 index 0000000000..71742b734a --- /dev/null +++ b/resources/extruders/jgaurora_a1_extruder_0.def.json @@ -0,0 +1,16 @@ +{ + "id": "jgaurora_a1_extruder_0", + "version": 2, + "name": "Extruder 1", + "inherits": "fdmextruder", + "metadata": { + "machine": "jgaurora_a1", + "position": "0" + }, + + "overrides": { + "extruder_nr": { "default_value": 0 }, + "machine_nozzle_size": { "default_value": 0.4 }, + "material_diameter": { "default_value": 1.75 } + } +} From adee0fa260b861c06f174a99ea0aba1b54b26a8b Mon Sep 17 00:00:00 2001 From: pinchies Date: Fri, 26 Oct 2018 23:42:49 +1100 Subject: [PATCH 0077/1240] Add files via upload --- resources/definitions/jgaurora_a1.def.json | 96 ++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 resources/definitions/jgaurora_a1.def.json diff --git a/resources/definitions/jgaurora_a1.def.json b/resources/definitions/jgaurora_a1.def.json new file mode 100644 index 0000000000..004fd8741d --- /dev/null +++ b/resources/definitions/jgaurora_a1.def.json @@ -0,0 +1,96 @@ +{ + "name": "JGAurora A1", + "version": 2, + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "author": "Samuel Pinches", + "manufacturer": "JGAurora", + "file_formats": "text/x-gcode", + "preferred_quality_type": "fine", + "machine_extruder_trains": + { + "0": "jgaurora_a1_extruder_0" + } + }, + "overrides": { + "machine_name": { + "default_value": "JGAurora A1" + }, + "machine_start_gcode": { + "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F6000 ;move Z to position 15.0 mm as fast as possible\nG92 E0 ;zero the extruded length\nG1 X0.0 Y0.0 F1000.0 ;go to edge of print area\nG1 X60.0 E9.0 F1000.0 ;intro line\nG1 X100.0 E21.5 F1000.0 ;intro line\nG92 E0 ;zero the extruded length again\n; -- end of START GCODE --" + }, + "machine_end_gcode": { + "default_value": "; -- END GCODE --\nM104 S0 ;turn off nozzle heater\nM140 S0 ;turn off bed heater\nG91 ;set to relative positioning\nG1 E-10 F300 ;retract the filament slightly\nG90 ;set to absolute positioning\nG28 X0 ;move to the X-axis origin (Home)\nG0 Y280 F600 ;bring the bed to the front for easy print removal\nM84 ;turn off stepper motors\n; -- end of END GCODE --" + }, + "machine_width": { + "default_value": 300 + }, + "machine_height": { + "default_value": 300 + }, + "machine_depth": { + "default_value": 300 + }, + "machine_heated_bed": { + "default_value": true + }, + "machine_center_is_zero": { + "default_value": false + }, + "gantry_height": { + "default_value": 10 + }, + "machine_gcode_flavor": { + "default_value": "RepRap (Marlin/Sprinter)" + }, + "material_diameter": { + "default_value": 1.75 + }, + "material_print_temperature": { + "default_value": 215 + }, + "material_bed_temperature": { + "default_value": 67 + }, + "layer_height": { + "default_value": 0.15 + }, + "layer_height_0": { + "default_value": 0.12 + }, + "wall_thickness": { + "default_value": 1.2 + }, + "speed_print": { + "default_value": 40 + }, + "speed_infill": { + "default_value": 40 + }, + "speed_wall": { + "default_value": 35 + }, + "speed_topbottom": { + "default_value": 35 + }, + "speed_travel": { + "default_value": 120 + }, + "speed_layer_0": { + "default_value": 12 + }, + "support_enable": { + "default_value": true + }, + "retraction_enable": { + "default_value": true + }, + "retraction_amount": { + "default_value": 6 + }, + "retraction_speed": { + "default_value": 40 + } + } +} \ No newline at end of file From 79ef3635a56ad76ac77816f98d1216b999c33d62 Mon Sep 17 00:00:00 2001 From: pinchies Date: Fri, 26 Oct 2018 23:45:32 +1100 Subject: [PATCH 0078/1240] Add files via upload --- resources/extruders/alfawise_u20.def.json | 96 +++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 resources/extruders/alfawise_u20.def.json diff --git a/resources/extruders/alfawise_u20.def.json b/resources/extruders/alfawise_u20.def.json new file mode 100644 index 0000000000..f6dccce3ee --- /dev/null +++ b/resources/extruders/alfawise_u20.def.json @@ -0,0 +1,96 @@ +{ + "name": "Alfawise U20", + "version": 2, + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "author": "Samuel Pinches", + "manufacturer": "Alfawise", + "file_formats": "text/x-gcode", + "preferred_quality_type": "fine", + "machine_extruder_trains": + { + "0": "alfawise_u20_extruder_0" + } + }, + "overrides": { + "machine_name": { + "default_value": "Alfawise U20" + }, + "machine_start_gcode": { + "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F6000 ;move Z to position 15.0 mm as fast as possible\nG92 E0 ;zero the extruded length\nG1 X0.0 Y0.0 F1000.0 ;go to edge of print area\nG1 X60.0 E9.0 F1000.0 ;intro line\nG1 X100.0 E21.5 F1000.0 ;intro line\nG92 E0 ;zero the extruded length again\n; -- end of START GCODE --" + }, + "machine_end_gcode": { + "default_value": "; -- END GCODE --\nM104 S0 ;turn off nozzle heater\nM140 S0 ;turn off bed heater\nG91 ;set to relative positioning\nG1 E-10 F300 ;retract the filament slightly\nG90 ;set to absolute positioning\nG28 X0 ;move to the X-axis origin (Home)\nG0 Y280 F600 ;bring the bed to the front for easy print removal\nM84 ;turn off stepper motors\n; -- end of END GCODE --" + }, + "machine_width": { + "default_value": 300 + }, + "machine_height": { + "default_value": 400 + }, + "machine_depth": { + "default_value": 300 + }, + "machine_heated_bed": { + "default_value": true + }, + "machine_center_is_zero": { + "default_value": false + }, + "gantry_height": { + "default_value": 10 + }, + "machine_gcode_flavor": { + "default_value": "RepRap (Marlin/Sprinter)" + }, + "material_diameter": { + "default_value": 1.75 + }, + "material_print_temperature": { + "default_value": 210 + }, + "material_bed_temperature": { + "default_value": 50 + }, + "layer_height": { + "default_value": 0.15 + }, + "layer_height_0": { + "default_value": 0.2 + }, + "wall_thickness": { + "default_value": 1.2 + }, + "speed_print": { + "default_value": 40 + }, + "speed_infill": { + "default_value": 40 + }, + "speed_wall": { + "default_value": 35 + }, + "speed_topbottom": { + "default_value": 35 + }, + "speed_travel": { + "default_value": 120 + }, + "speed_layer_0": { + "default_value": 20 + }, + "support_enable": { + "default_value": true + }, + "retraction_enable": { + "default_value": true + }, + "retraction_amount": { + "default_value": 5 + }, + "retraction_speed": { + "default_value": 45 + } + } +} \ No newline at end of file From 03532969c6c17883750f3bafb76e0e82f319cfbd Mon Sep 17 00:00:00 2001 From: pinchies Date: Fri, 26 Oct 2018 23:46:39 +1100 Subject: [PATCH 0079/1240] Add files via upload --- resources/definitions/alfawise_u20.def.json | 96 +++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 resources/definitions/alfawise_u20.def.json diff --git a/resources/definitions/alfawise_u20.def.json b/resources/definitions/alfawise_u20.def.json new file mode 100644 index 0000000000..f6dccce3ee --- /dev/null +++ b/resources/definitions/alfawise_u20.def.json @@ -0,0 +1,96 @@ +{ + "name": "Alfawise U20", + "version": 2, + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "author": "Samuel Pinches", + "manufacturer": "Alfawise", + "file_formats": "text/x-gcode", + "preferred_quality_type": "fine", + "machine_extruder_trains": + { + "0": "alfawise_u20_extruder_0" + } + }, + "overrides": { + "machine_name": { + "default_value": "Alfawise U20" + }, + "machine_start_gcode": { + "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F6000 ;move Z to position 15.0 mm as fast as possible\nG92 E0 ;zero the extruded length\nG1 X0.0 Y0.0 F1000.0 ;go to edge of print area\nG1 X60.0 E9.0 F1000.0 ;intro line\nG1 X100.0 E21.5 F1000.0 ;intro line\nG92 E0 ;zero the extruded length again\n; -- end of START GCODE --" + }, + "machine_end_gcode": { + "default_value": "; -- END GCODE --\nM104 S0 ;turn off nozzle heater\nM140 S0 ;turn off bed heater\nG91 ;set to relative positioning\nG1 E-10 F300 ;retract the filament slightly\nG90 ;set to absolute positioning\nG28 X0 ;move to the X-axis origin (Home)\nG0 Y280 F600 ;bring the bed to the front for easy print removal\nM84 ;turn off stepper motors\n; -- end of END GCODE --" + }, + "machine_width": { + "default_value": 300 + }, + "machine_height": { + "default_value": 400 + }, + "machine_depth": { + "default_value": 300 + }, + "machine_heated_bed": { + "default_value": true + }, + "machine_center_is_zero": { + "default_value": false + }, + "gantry_height": { + "default_value": 10 + }, + "machine_gcode_flavor": { + "default_value": "RepRap (Marlin/Sprinter)" + }, + "material_diameter": { + "default_value": 1.75 + }, + "material_print_temperature": { + "default_value": 210 + }, + "material_bed_temperature": { + "default_value": 50 + }, + "layer_height": { + "default_value": 0.15 + }, + "layer_height_0": { + "default_value": 0.2 + }, + "wall_thickness": { + "default_value": 1.2 + }, + "speed_print": { + "default_value": 40 + }, + "speed_infill": { + "default_value": 40 + }, + "speed_wall": { + "default_value": 35 + }, + "speed_topbottom": { + "default_value": 35 + }, + "speed_travel": { + "default_value": 120 + }, + "speed_layer_0": { + "default_value": 20 + }, + "support_enable": { + "default_value": true + }, + "retraction_enable": { + "default_value": true + }, + "retraction_amount": { + "default_value": 5 + }, + "retraction_speed": { + "default_value": 45 + } + } +} \ No newline at end of file From 135ad6d7054a84591a635ee4c4c21945dfac44be Mon Sep 17 00:00:00 2001 From: pinchies Date: Fri, 26 Oct 2018 23:53:59 +1100 Subject: [PATCH 0080/1240] Add files via upload --- .../cocoon_create_modelmaker_extruder_0.def.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 resources/extruders/cocoon_create_modelmaker_extruder_0.def.json diff --git a/resources/extruders/cocoon_create_modelmaker_extruder_0.def.json b/resources/extruders/cocoon_create_modelmaker_extruder_0.def.json new file mode 100644 index 0000000000..26d847483d --- /dev/null +++ b/resources/extruders/cocoon_create_modelmaker_extruder_0.def.json @@ -0,0 +1,16 @@ +{ + "id": "cocoon_create_modelmaker_extruder_0", + "version": 2, + "name": "Extruder 1", + "inherits": "fdmextruder", + "metadata": { + "machine": "cocoon_create_modelmaker", + "position": "0" + }, + + "overrides": { + "extruder_nr": { "default_value": 0 }, + "machine_nozzle_size": { "default_value": 0.4 }, + "material_diameter": { "default_value": 1.75 } + } +} From 49ac4233839e80423767ce13883cd3f9ba7a0cc5 Mon Sep 17 00:00:00 2001 From: pinchies Date: Fri, 26 Oct 2018 23:54:29 +1100 Subject: [PATCH 0081/1240] Add files via upload --- .../cocoon_create_modelmaker.def.json | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 resources/definitions/cocoon_create_modelmaker.def.json diff --git a/resources/definitions/cocoon_create_modelmaker.def.json b/resources/definitions/cocoon_create_modelmaker.def.json new file mode 100644 index 0000000000..f752a08fc7 --- /dev/null +++ b/resources/definitions/cocoon_create_modelmaker.def.json @@ -0,0 +1,96 @@ +{ + "name": "Cocoon Create ModelMaker", + "version": 2, + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "author": "Samuel Pinches", + "manufacturer": "Cocoon Create", + "file_formats": "text/x-gcode", + "preferred_quality_type": "fine", + "machine_extruder_trains": + { + "0": "cocoon_create_modelmaker_extruder_0" + } + }, + "overrides": { + "machine_name": { + "default_value": "Cocoon Create ModelMaker" + }, + "machine_start_gcode": { + "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F6000 ;move Z to position 15.0 mm as fast as possible\nG92 E0 ;zero the extruded length\nG1 X0.0 Y0.0 F1000.0 ;go to edge of print area\nG1 X60.0 E9.0 F1000.0 ;intro line\nG1 X100.0 E21.5 F1000.0 ;intro line\nG92 E0 ;zero the extruded length again\n; -- end of START GCODE --" + }, + "machine_end_gcode": { + "default_value": "; -- END GCODE --\nM104 S0 ;turn off nozzle heater\nG91 ;set to relative positioning\nG1 E-10 F300 ;retract the filament slightly\nG90 ;set to absolute positioning\nG28 X0 Y0 ;move to the XY-axis origin (Home)\nM84 ;turn off stepper motors\n; -- end of END GCODE --" + }, + "machine_width": { + "default_value": 120 + }, + "machine_height": { + "default_value": 100 + }, + "machine_depth": { + "default_value": 135 + }, + "machine_heated_bed": { + "default_value": false + }, + "machine_center_is_zero": { + "default_value": false + }, + "gantry_height": { + "default_value": 10 + }, + "machine_gcode_flavor": { + "default_value": "RepRap (Marlin/Sprinter)" + }, + "material_diameter": { + "default_value": 1.75 + }, + "material_print_temperature": { + "default_value": 220 + }, + "layer_height": { + "default_value": 0.15 + }, + "layer_height_0": { + "default_value": 0.2 + }, + "wall_thickness": { + "default_value": 1.2 + }, + "top_bottom_thickness": { + "default_value": 0.6 + }, + "speed_print": { + "default_value": 40 + }, + "speed_infill": { + "default_value": 40 + }, + "speed_wall": { + "default_value": 35 + }, + "speed_topbottom": { + "default_value": 35 + }, + "speed_travel": { + "default_value": 70 + }, + "speed_layer_0": { + "default_value": 20 + }, + "support_enable": { + "default_value": true + }, + "retraction_enable": { + "default_value": true + }, + "retraction_amount": { + "default_value": 7 + }, + "retraction_speed": { + "default_value": 40 + } + } +} \ No newline at end of file From 3eb71021e24b7af44626e5b4ffe1a91915f88072 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 26 Oct 2018 15:53:20 +0200 Subject: [PATCH 0082/1240] Change the way the string for the tooltip is formatted. Contributes to CURA-5772. --- resources/qml/PrintSetupSelector.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector.qml index e5a29dffad..e4cb852d89 100644 --- a/resources/qml/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector.qml @@ -220,11 +220,11 @@ Rectangle { modesListModel.append({ text: catalog.i18nc("@title:tab", "Recommended"), - tooltipText: catalog.i18nc("@tooltip", "Recommended Print Setup

Print with the recommended settings for the selected printer, material and quality.") + tooltipText: "%1

%2".arg(catalog.i18nc("@tooltip:title", "Recommended Print Setup")).arg(catalog.i18nc("@tooltip", "Print with the recommended settings for the selected printer, material and quality.")) }) modesListModel.append({ text: catalog.i18nc("@title:tab", "Custom"), - tooltipText: catalog.i18nc("@tooltip", "Custom Print Setup

Print with finegrained control over every last bit of the slicing process.") + tooltipText: "%1

%2".arg(catalog.i18nc("@tooltip:title", "Custom Print Setup")).arg(catalog.i18nc("@tooltip", "Print with finegrained control over every last bit of the slicing process.")) }) var index = Math.round(UM.Preferences.getValue("cura/active_mode")) From 5a5adb71cd159c2e7b7204aa40b42472db4d9995 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 26 Oct 2018 16:16:33 +0200 Subject: [PATCH 0083/1240] Make the height of the Print setup selector themable. Contributes to CURA-5772. --- plugins/PrepareStage/PrepareMenu.qml | 4 ++-- resources/qml/PrintSetupSelector.qml | 2 +- resources/themes/cura-light/theme.json | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index f08de9d317..27d068e909 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -61,12 +61,12 @@ Item Cura.CustomConfigurationSelector { - width: UM.Theme.getSize("sidebar").width + width: UM.Theme.getSize("configuration_selector_widget").width } Cura.PrintSetupSelector { - width: UM.Theme.getSize("sidebar").width + width: UM.Theme.getSize("print_setup_widget").width onShowTooltip: prepareMenu.showTooltip(item, location, text) onHideTooltip: prepareMenu.hideTooltip() } diff --git a/resources/qml/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector.qml index e4cb852d89..d9b2337c96 100644 --- a/resources/qml/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector.qml @@ -189,7 +189,7 @@ Rectangle anchors.topMargin: UM.Theme.getSize("sidebar_margin").height anchors.left: parent.left anchors.right: parent.right - height: 500 + height: UM.Theme.getSize("print_setup_widget").height // We load both of them at once (instead of using a loader) because the advanced sidebar can take // quite some time to load. So in this case we sacrifice memory for speed. diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 6a83c2c566..38d4f5372f 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -355,6 +355,9 @@ "account_button": [12, 3], + "print_setup_widget": [35.0, 42.0], + "configuration_selector_widget": [35.0, 0.0], + "views_selector": [0.0, 4.0], "default_lining": [0.08, 0.08], From c8bdf7321c4a888025001064f11c852097404264 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 26 Oct 2018 16:31:03 +0200 Subject: [PATCH 0084/1240] Move the visible check within the component. Contributes to CURA-5772. --- plugins/PrepareStage/PrepareMenu.qml | 4 ---- .../Menus/ConfigurationMenu/QuickConfigurationSelector.qml | 3 +++ 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index 27d068e909..c7f8a3b0f1 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -13,9 +13,6 @@ Item signal showTooltip(Item item, point location, string text) signal hideTooltip() - property bool isNetworkPrinter: Cura.MachineManager.activeMachineNetworkKey != "" - property bool printerConnected: Cura.MachineManager.printerConnected - UM.I18nCatalog { id: catalog @@ -53,7 +50,6 @@ Item Cura.QuickConfigurationSelector { id: configSelection - visible: isNetworkPrinter && printerConnected width: visible ? Math.round(UM.Theme.getSize("sidebar").width * 0.15) : 0 panelWidth: Math.round(0.8 * UM.Theme.getSize("sidebar").width) height: prepareMenu.height diff --git a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml index d7ee2c68ee..740c12d340 100644 --- a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml +++ b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml @@ -14,6 +14,9 @@ Item property var connectedDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null property var panelWidth: control.width + // Make this component only visible when it's a network printer and it is connected + visible: Cura.MachineManager.activeMachineNetworkKey != "" && Cura.MachineManager.printerConnected + function switchPopupState() { popup.visible ? popup.close() : popup.open() From 90e8a05aab63d5d16a2431571f6e9b6791ccd192 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 26 Oct 2018 17:51:28 +0200 Subject: [PATCH 0085/1240] Remove all the entries in the theme that make reference to the sidebar, because there is no sidebar anymore. Several different margins and linings were created depending on the side, not depending on where it belongs. Contributes to CURA-5784. --- .../PostProcessingPlugin.qml | 2 +- plugins/PrepareStage/PrepareMenu.qml | 6 +- plugins/Toolbox/resources/qml/Toolbox.qml | 2 +- .../qml/ToolboxCompatibilityChart.qml | 2 +- .../resources/qml/ToolboxTabButton.qml | 4 +- resources/qml/ActionPanelWidget.qml | 14 ++-- resources/qml/Cura.qml | 7 +- resources/qml/CustomConfigurationSelector.qml | 22 +++--- resources/qml/MachineSelector.qml | 12 +-- .../ConfigurationMenu/ConfigurationItem.qml | 4 +- .../Menus/ConfigurationMenu/SyncButton.qml | 6 +- resources/qml/MonitorButton.qml | 16 ++-- resources/qml/MonitorSidebar.qml | 18 ++--- resources/qml/PrintMonitor.qml | 12 +-- resources/qml/PrintSetupSelector.qml | 10 +-- resources/qml/PrinterOutput/ExtruderBox.qml | 2 +- resources/qml/PrinterOutput/HeatedBedBox.qml | 4 +- resources/qml/SaveButton.qml | 24 +++--- resources/qml/Settings/SettingCategory.qml | 4 +- resources/qml/Settings/SettingItem.qml | 6 +- resources/qml/Settings/SettingView.qml | 26 +++---- resources/qml/SidebarSimple.qml | 78 +++++++++---------- resources/themes/cura-dark/theme.json | 17 ++-- resources/themes/cura-light/styles.qml | 8 +- resources/themes/cura-light/theme.json | 51 ++++++------ 25 files changed, 174 insertions(+), 183 deletions(-) diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml index 10cc4cc082..bd4d361d35 100644 --- a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml +++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml @@ -260,7 +260,7 @@ UM.Dialog Rectangle { - color: UM.Theme.getColor("sidebar") + color: UM.Theme.getColor("main_background") anchors.left: activeScripts.right anchors.leftMargin: UM.Theme.getSize("default_margin").width anchors.right: parent.right diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index c7f8a3b0f1..cdcc1384a2 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -43,15 +43,15 @@ Item Cura.MachineSelector { id: machineSelection - width: Math.round(0.8 * UM.Theme.getSize("sidebar").width) - configSelection.width + width: UM.Theme.getSize("machine_selector_widget").width - configSelection.width height: prepareMenu.height } Cura.QuickConfigurationSelector { id: configSelection - width: visible ? Math.round(UM.Theme.getSize("sidebar").width * 0.15) : 0 - panelWidth: Math.round(0.8 * UM.Theme.getSize("sidebar").width) + width: visible ? UM.Theme.getSize("machine_selector_widget").width * 0.2 : 0 + panelWidth: UM.Theme.getSize("machine_selector_widget").width height: prepareMenu.height } diff --git a/plugins/Toolbox/resources/qml/Toolbox.qml b/plugins/Toolbox/resources/qml/Toolbox.qml index 29b026058c..a5fd2be370 100644 --- a/plugins/Toolbox/resources/qml/Toolbox.qml +++ b/plugins/Toolbox/resources/qml/Toolbox.qml @@ -20,7 +20,7 @@ Window maximumWidth: minimumWidth minimumHeight: height maximumHeight: minimumHeight - color: UM.Theme.getColor("sidebar") + color: UM.Theme.getColor("main_background") UM.I18nCatalog { id: catalog diff --git a/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml b/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml index 58fea079e9..4d27489d7a 100644 --- a/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml +++ b/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml @@ -54,7 +54,7 @@ Item model: packageData.supported_configs headerDelegate: Rectangle { - color: UM.Theme.getColor("sidebar") + color: UM.Theme.getColor("main_background") height: UM.Theme.getSize("toolbox_chart_row").height Label { diff --git a/plugins/Toolbox/resources/qml/ToolboxTabButton.qml b/plugins/Toolbox/resources/qml/ToolboxTabButton.qml index 58149e419a..b671d779f8 100644 --- a/plugins/Toolbox/resources/qml/ToolboxTabButton.qml +++ b/plugins/Toolbox/resources/qml/ToolboxTabButton.qml @@ -19,10 +19,10 @@ Button Rectangle { visible: control.active - color: UM.Theme.getColor("sidebar_header_highlight_hover") + color: UM.Theme.getColor("toolbox_header_highlight_hover") anchors.bottom: parent.bottom width: parent.width - height: UM.Theme.getSize("sidebar_header_highlight").height + height: UM.Theme.getSize("toolbox_header_highlight").height } } label: Label diff --git a/resources/qml/ActionPanelWidget.qml b/resources/qml/ActionPanelWidget.qml index 02ac154178..b5dc7f83c9 100644 --- a/resources/qml/ActionPanelWidget.qml +++ b/resources/qml/ActionPanelWidget.qml @@ -19,16 +19,16 @@ Rectangle signal showTooltip(Item item, point location, string text) signal hideTooltip() - color: UM.Theme.getColor("sidebar") + color: UM.Theme.getColor("main_background") // Also add an extra margin, as we want some breathing room around the edges. - height: saveButton.height + UM.Theme.getSize("sidebar_margin").height + height: saveButton.height + UM.Theme.getSize("thick_margin").height Label { id: timeDetails anchors.left: parent.left anchors.bottom: costSpec.top - anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width + anchors.leftMargin: UM.Theme.getSize("thick_margin").width font: UM.Theme.getFont("large") color: UM.Theme.getColor("text_subtext") @@ -62,7 +62,7 @@ Rectangle } } tooltip_html += ""; - base.showTooltip(parent, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), tooltip_html); + base.showTooltip(parent, Qt.point(-UM.Theme.getSize("thick_margin").width, 0), tooltip_html); } } onExited: @@ -160,8 +160,8 @@ Rectangle anchors.left: parent.left anchors.bottom: parent.bottom - anchors.bottomMargin: UM.Theme.getSize("sidebar_margin").height - anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width + anchors.bottomMargin: UM.Theme.getSize("thick_margin").height + anchors.leftMargin: UM.Theme.getSize("thick_margin").width font: UM.Theme.getFont("very_small") renderType: Text.NativeRendering @@ -219,7 +219,7 @@ Rectangle { var show_data = costSpec.getSpecsData() - base.showTooltip(parent, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), show_data); + base.showTooltip(parent, Qt.point(-UM.Theme.getSize("thick_margin").width, 0), show_data); } } onExited: diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index c5abb0f107..3e2515cb3e 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -312,9 +312,9 @@ UM.MainWindow { anchors.right: parent.right anchors.bottom: parent.bottom - width: UM.Theme.getSize("sidebar").width - anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width - anchors.bottomMargin: UM.Theme.getSize("sidebar_margin").height + width: UM.Theme.getSize("action_panel_widget").width + anchors.rightMargin: UM.Theme.getSize("thick_margin").width + anchors.bottomMargin: UM.Theme.getSize("thick_margin").height onShowTooltip: { base.showTooltip(item, location, text) @@ -348,7 +348,6 @@ UM.MainWindow anchors { horizontalCenter: parent.horizontalCenter - horizontalCenterOffset: -(Math.round(UM.Theme.getSize("sidebar").width / 2)) top: parent.verticalCenter bottom: parent.bottom bottomMargin: UM.Theme.getSize("default_margin").height diff --git a/resources/qml/CustomConfigurationSelector.qml b/resources/qml/CustomConfigurationSelector.qml index 9a3f91e6a3..17b652e8d8 100644 --- a/resources/qml/CustomConfigurationSelector.qml +++ b/resources/qml/CustomConfigurationSelector.qml @@ -14,7 +14,7 @@ Rectangle implicitHeight: parent.height id: base - color: UM.Theme.getColor("sidebar") + color: UM.Theme.getColor("main_background") // Height has an extra 2x margin for the top & bottom margin. height: childrenRect.height + 2 * UM.Theme.getSize("default_margin").width @@ -28,7 +28,7 @@ Rectangle visible: extrudersModel.items.length > 1 property var index: 0 - height: UM.Theme.getSize("sidebar_header_mode_tabs").height + height: UM.Theme.getSize("configuration_selector_mode_tabs").height boundsBehavior: Flickable.StopAtBounds anchors @@ -36,7 +36,7 @@ Rectangle left: parent.left right: parent.right top: parent.top - margins: UM.Theme.getSize("sidebar_margin").width + margins: UM.Theme.getSize("thick_margin").width } ExclusiveGroup { id: extruderMenuGroup } @@ -209,7 +209,7 @@ Rectangle Item { id: materialRow - height: UM.Theme.getSize("sidebar_setup").height + height: UM.Theme.getSize("print_setup_item").height visible: Cura.MachineManager.hasMaterials anchors @@ -217,7 +217,7 @@ Rectangle left: parent.left right: parent.right top: extrudersList.bottom - margins: UM.Theme.getSize("sidebar_margin").width + margins: UM.Theme.getSize("thick_margin").width } Label @@ -246,7 +246,7 @@ Rectangle enabled: !extrudersList.visible || Cura.ExtruderManager.activeExtruderIndex > -1 height: UM.Theme.getSize("setting_control").height - width: Math.round(parent.width * 0.7) + UM.Theme.getSize("sidebar_margin").width + width: Math.round(parent.width * 0.7) + UM.Theme.getSize("thick_margin").width anchors.right: parent.right style: UM.Theme.styles.sidebar_header_button activeFocusOnPress: true; @@ -263,7 +263,7 @@ Rectangle Item { id: variantRow - height: UM.Theme.getSize("sidebar_setup").height + height: UM.Theme.getSize("print_setup_item").height visible: Cura.MachineManager.hasVariants anchors @@ -271,7 +271,7 @@ Rectangle left: parent.left right: parent.right top: materialRow.bottom - margins: UM.Theme.getSize("sidebar_margin").width + margins: UM.Theme.getSize("thick_margin").width } Label @@ -293,7 +293,7 @@ Rectangle visible: Cura.MachineManager.hasVariants height: UM.Theme.getSize("setting_control").height - width: Math.round(parent.width * 0.7 + UM.Theme.getSize("sidebar_margin").width) + width: Math.round(parent.width * 0.7 + UM.Theme.getSize("thick_margin").width) anchors.right: parent.right style: UM.Theme.styles.sidebar_header_button activeFocusOnPress: true; @@ -305,11 +305,11 @@ Rectangle Item { id: materialCompatibilityLink - height: UM.Theme.getSize("sidebar_setup").height + height: UM.Theme.getSize("print_setup_item").height anchors.right: parent.right anchors.top: variantRow.bottom - anchors.margins: UM.Theme.getSize("sidebar_margin").width + anchors.margins: UM.Theme.getSize("thick_margin").width UM.RecolorImage { id: warningImage diff --git a/resources/qml/MachineSelector.qml b/resources/qml/MachineSelector.qml index 49fbf16e19..d82211af3a 100644 --- a/resources/qml/MachineSelector.qml +++ b/resources/qml/MachineSelector.qml @@ -27,13 +27,13 @@ ToolButton color: { if (control.pressed) { - return UM.Theme.getColor("sidebar_header_active"); + return UM.Theme.getColor("machine_selector_active"); } else if (control.hovered) { - return UM.Theme.getColor("sidebar_header_hover"); + return UM.Theme.getColor("machine_selector_hover"); } else { - return UM.Theme.getColor("sidebar_header_bar"); + return UM.Theme.getColor("machine_selector_bar"); } } Behavior on color { ColorAnimation { duration: 50; } } @@ -61,18 +61,18 @@ ToolButton { verticalCenter: parent.verticalCenter left: parent.left - leftMargin: UM.Theme.getSize("sidebar_margin").width + leftMargin: UM.Theme.getSize("thick_margin").width } } Label { id: sidebarComboBoxLabel - color: UM.Theme.getColor("sidebar_header_text_active") + color: UM.Theme.getColor("machine_selector_text_active") text: control.text; elide: Text.ElideRight; 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.leftMargin: printerStatusIcon.visible ? UM.Theme.getSize("narrow_margin").width : UM.Theme.getSize("thick_margin").width anchors.right: downArrow.left; anchors.rightMargin: control.rightMargin; anchors.verticalCenter: parent.verticalCenter; diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 37d851a35d..7427b5ddff 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -63,7 +63,7 @@ Rectangle visible: buildplateInformation.visible width: parent.width - 2 * parent.padding - height: visible ? Math.round(UM.Theme.getSize("sidebar_lining_thin").height / 2) : 0 + height: visible ? Math.round(UM.Theme.getSize("thick_lining").height / 2) : 0 color: textColor } @@ -110,7 +110,7 @@ Rectangle parent.border.color = UM.Theme.getColor("configuration_item_border_hover") if (configurationItem.selected == false) { - configurationItem.color = UM.Theme.getColor("sidebar_lining") + configurationItem.color = UM.Theme.getColor("wide_lining") } } onExited: diff --git a/resources/qml/Menus/ConfigurationMenu/SyncButton.qml b/resources/qml/Menus/ConfigurationMenu/SyncButton.qml index 3099d684c1..558ae1e477 100644 --- a/resources/qml/Menus/ConfigurationMenu/SyncButton.qml +++ b/resources/qml/Menus/ConfigurationMenu/SyncButton.qml @@ -42,15 +42,15 @@ Button { if(control.pressed) { - return UM.Theme.getColor("sidebar_header_active"); + return UM.Theme.getColor("machine_selector_active"); } else if(control.hovered) { - return UM.Theme.getColor("sidebar_header_hover"); + return UM.Theme.getColor("machine_selector_hover"); } else { - return UM.Theme.getColor("sidebar_header_bar"); + return UM.Theme.getColor("machine_selector_bar"); } } Behavior on color { ColorAnimation { duration: 50; } } diff --git a/resources/qml/MonitorButton.qml b/resources/qml/MonitorButton.qml index efee50a2b3..eef76bcb09 100644 --- a/resources/qml/MonitorButton.qml +++ b/resources/qml/MonitorButton.qml @@ -15,7 +15,7 @@ Item id: base; UM.I18nCatalog { id: catalog; name: "cura"} - height: childrenRect.height + UM.Theme.getSize("sidebar_margin").height + height: childrenRect.height + UM.Theme.getSize("thick_margin").height property bool printerConnected: Cura.MachineManager.printerConnected property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands @@ -162,10 +162,10 @@ Item Label { id: statusLabel - width: parent.width - 2 * UM.Theme.getSize("sidebar_margin").width + width: parent.width - 2 * UM.Theme.getSize("thick_margin").width anchors.top: parent.top anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width + anchors.leftMargin: UM.Theme.getSize("thick_margin").width color: base.statusColor font: UM.Theme.getFont("large") @@ -224,12 +224,12 @@ Item property string backgroundColor: UM.Theme.getColor("progressbar_background"); property string controlColor: base.statusColor; - width: parent.width - 2 * UM.Theme.getSize("sidebar_margin").width; + width: parent.width - 2 * UM.Theme.getSize("thick_margin").width; height: UM.Theme.getSize("progressbar").height; anchors.top: statusLabel.bottom; - anchors.topMargin: Math.round(UM.Theme.getSize("sidebar_margin").height / 4); + anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height / 4); anchors.left: parent.left; - anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width; + anchors.leftMargin: UM.Theme.getSize("thick_margin").width; } Row @@ -237,9 +237,9 @@ Item id: buttonsRow height: abortButton.height anchors.top: progressBar.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + anchors.topMargin: UM.Theme.getSize("thick_margin").height anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width + anchors.rightMargin: UM.Theme.getSize("thick_margin").width spacing: UM.Theme.getSize("default_margin").width Row diff --git a/resources/qml/MonitorSidebar.qml b/resources/qml/MonitorSidebar.qml index 179fcafb53..2282034739 100644 --- a/resources/qml/MonitorSidebar.qml +++ b/resources/qml/MonitorSidebar.qml @@ -30,7 +30,7 @@ Rectangle property variant printMaterialCosts: PrintInformation.materialCosts property variant printMaterialNames: PrintInformation.materialNames - color: UM.Theme.getColor("sidebar") + color: UM.Theme.getColor("main_background") UM.I18nCatalog { id: catalog; name: "cura"} Timer { @@ -89,7 +89,7 @@ Rectangle { id: machineSelection width: base.width - configSelection.width - separator.width - height: UM.Theme.getSize("sidebar_header").height + height: UM.Theme.getSize("stage_menu").height anchors.top: base.top anchors.left: parent.left } @@ -98,9 +98,9 @@ Rectangle { id: separator visible: configSelection.visible - width: visible ? Math.round(UM.Theme.getSize("sidebar_lining_thin").height / 2) : 0 - height: UM.Theme.getSize("sidebar_header").height - color: UM.Theme.getColor("sidebar_lining_thin") + width: visible ? Math.round(UM.Theme.getSize("thick_lining").height / 2) : 0 + height: UM.Theme.getSize("stage_menu").height + color: UM.Theme.getColor("thick_lining") anchors.left: machineSelection.right } @@ -109,7 +109,7 @@ Rectangle id: configSelection visible: isNetworkPrinter && printerConnected width: visible ? Math.round(base.width * 0.15) : 0 - height: UM.Theme.getSize("sidebar_header").height + height: UM.Theme.getSize("stage_menu").height anchors.top: base.top anchors.right: parent.right panelWidth: base.width @@ -158,10 +158,10 @@ Rectangle { id: footerSeparator width: parent.width - height: UM.Theme.getSize("sidebar_lining").height - color: UM.Theme.getColor("sidebar_lining") + height: UM.Theme.getSize("wide_lining").height + color: UM.Theme.getColor("wide_lining") anchors.bottom: monitorButton.top - anchors.bottomMargin: UM.Theme.getSize("sidebar_margin").height + anchors.bottomMargin: UM.Theme.getSize("thick_margin").height } // MonitorButton is actually the bottom footer panel. diff --git a/resources/qml/PrintMonitor.qml b/resources/qml/PrintMonitor.qml index 12e95d1e89..723279847a 100644 --- a/resources/qml/PrintMonitor.qml +++ b/resources/qml/PrintMonitor.qml @@ -31,14 +31,14 @@ Column Rectangle { - color: UM.Theme.getColor("sidebar_lining") + color: UM.Theme.getColor("wide_lining") width: parent.width height: childrenRect.height Flow { id: extrudersGrid - spacing: UM.Theme.getSize("sidebar_lining_thin").width + spacing: UM.Theme.getSize("thick_lining").width width: parent.width Repeater @@ -48,8 +48,8 @@ Column ExtruderBox { - color: UM.Theme.getColor("sidebar") - width: index == machineExtruderCount.properties.value - 1 && index % 2 == 0 ? extrudersGrid.width : Math.round(extrudersGrid.width / 2 - UM.Theme.getSize("sidebar_lining_thin").width / 2) + color: UM.Theme.getColor("main_background") + width: index == machineExtruderCount.properties.value - 1 && index % 2 == 0 ? extrudersGrid.width : Math.round(extrudersGrid.width / 2 - UM.Theme.getSize("thick_lining").width / 2) extruderModel: modelData } } @@ -58,9 +58,9 @@ Column Rectangle { - color: UM.Theme.getColor("sidebar_lining") + color: UM.Theme.getColor("wide_lining") width: parent.width - height: UM.Theme.getSize("sidebar_lining_thin").width + height: UM.Theme.getSize("thick_lining").width } HeatedBedBox diff --git a/resources/qml/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector.qml index d9b2337c96..987e3ecce9 100644 --- a/resources/qml/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector.qml @@ -25,7 +25,7 @@ Rectangle property variant printMaterialCosts: PrintInformation.materialCosts property variant printMaterialNames: PrintInformation.materialNames - color: UM.Theme.getColor("sidebar") + color: UM.Theme.getColor("main_background") UM.I18nCatalog { id: catalog; name: "cura"} // This widget doesn't show tooltips by itself. Instead it emits signals so others can do something with it. @@ -89,7 +89,7 @@ Rectangle { left: parent.left top: parent.top - margins: UM.Theme.getSize("sidebar_margin").width + margins: UM.Theme.getSize("thick_margin").width } width: Math.round(parent.width * 0.45) @@ -105,10 +105,10 @@ Rectangle id: settingsModeSelection model: modesListModel width: Math.round(parent.width * 0.55) - height: UM.Theme.getSize("sidebar_header_mode_toggle").height + height: UM.Theme.getSize("print_setup_mode_toggle").height anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width + anchors.rightMargin: UM.Theme.getSize("thick_margin").width anchors.top: settingsModeLabel.top @@ -186,7 +186,7 @@ Rectangle { id: sidebarContents anchors.top: settingsModeSelection.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + anchors.topMargin: UM.Theme.getSize("thick_margin").height anchors.left: parent.left anchors.right: parent.right height: UM.Theme.getSize("print_setup_widget").height diff --git a/resources/qml/PrinterOutput/ExtruderBox.qml b/resources/qml/PrinterOutput/ExtruderBox.qml index f0abd4cd6c..510a44f9da 100644 --- a/resources/qml/PrinterOutput/ExtruderBox.qml +++ b/resources/qml/PrinterOutput/ExtruderBox.qml @@ -13,7 +13,7 @@ Item property var extruderModel property var position: index implicitWidth: parent.width - implicitHeight: UM.Theme.getSize("sidebar_extruder_box").height + implicitHeight: UM.Theme.getSize("print_setup_extruder_box").height UM.SettingPropertyProvider { diff --git a/resources/qml/PrinterOutput/HeatedBedBox.qml b/resources/qml/PrinterOutput/HeatedBedBox.qml index 9de66ad0be..962ffa9b01 100644 --- a/resources/qml/PrinterOutput/HeatedBedBox.qml +++ b/resources/qml/PrinterOutput/HeatedBedBox.qml @@ -12,12 +12,12 @@ import Cura 1.0 as Cura Item { implicitWidth: parent.width - height: visible ? UM.Theme.getSize("sidebar_extruder_box").height : 0 + height: visible ? UM.Theme.getSize("print_setup_extruder_box").height : 0 property var printerModel Rectangle { - color: UM.Theme.getColor("sidebar") + color: UM.Theme.getColor("main_background") anchors.fill: parent Label //Build plate label. diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml index 5e41dd3007..c2d310e30c 100644 --- a/resources/qml/SaveButton.qml +++ b/resources/qml/SaveButton.qml @@ -68,10 +68,10 @@ Item Label { id: statusLabel - width: parent.width - 2 * UM.Theme.getSize("sidebar_margin").width + width: parent.width - 2 * UM.Theme.getSize("thick_margin").width anchors.top: parent.top anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width + anchors.leftMargin: UM.Theme.getSize("thick_margin").width color: UM.Theme.getColor("text") font: UM.Theme.getFont("default_bold") @@ -81,12 +81,12 @@ Item Rectangle { id: progressBar - width: parent.width - 2 * UM.Theme.getSize("sidebar_margin").width + width: parent.width - 2 * UM.Theme.getSize("thick_margin").width height: UM.Theme.getSize("progressbar").height anchors.top: statusLabel.bottom - anchors.topMargin: Math.round(UM.Theme.getSize("sidebar_margin").height / 4) + anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height / 4) anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width + anchors.leftMargin: UM.Theme.getSize("thick_margin").width radius: UM.Theme.getSize("progressbar_radius").width color: UM.Theme.getColor("progressbar_background") @@ -133,11 +133,11 @@ Item children_width += child.width + child.anchors.rightMargin; } } - return Math.min(children_width, base.width - UM.Theme.getSize("sidebar_margin").width); + return Math.min(children_width, base.width - UM.Theme.getSize("thick_margin").width); } height: saveToButton.height anchors.bottom: parent.bottom - anchors.bottomMargin: UM.Theme.getSize("sidebar_margin").height + anchors.bottomMargin: UM.Theme.getSize("thick_margin").height anchors.right: parent.right clip: true @@ -198,7 +198,7 @@ Item anchors.top: parent.top anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width + anchors.rightMargin: UM.Theme.getSize("thick_margin").width // 1 = not started, 4 = error, 5 = disabled text: [1, 4, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@label:Printjob", "Prepare") : catalog.i18nc("@label:Printjob", "Cancel") @@ -253,7 +253,7 @@ Item Behavior on color { ColorAnimation { duration: 50; } } - implicitWidth: actualLabel.contentWidth + (UM.Theme.getSize("sidebar_margin").width * 2) + implicitWidth: actualLabel.contentWidth + (UM.Theme.getSize("thick_margin").width * 2) Label { @@ -299,7 +299,7 @@ Item anchors.top: parent.top anchors.right: deviceSelectionMenu.visible ? deviceSelectionMenu.left : parent.right - anchors.rightMargin: deviceSelectionMenu.visible ? -3 * UM.Theme.getSize("default_lining").width : UM.Theme.getSize("sidebar_margin").width + anchors.rightMargin: deviceSelectionMenu.visible ? -3 * UM.Theme.getSize("default_lining").width : UM.Theme.getSize("thick_margin").width text: UM.OutputDeviceManager.activeDeviceShortDescription onClicked: @@ -355,7 +355,7 @@ Item Behavior on color { ColorAnimation { duration: 50; } } - implicitWidth: actualLabel.contentWidth + (UM.Theme.getSize("sidebar_margin").width * 2) + implicitWidth: actualLabel.contentWidth + (UM.Theme.getSize("thick_margin").width * 2) Label { @@ -377,7 +377,7 @@ Item anchors.top: parent.top anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width + anchors.rightMargin: UM.Theme.getSize("thick_margin").width width: UM.Theme.getSize("save_button_save_to_button").height height: UM.Theme.getSize("save_button_save_to_button").height diff --git a/resources/qml/Settings/SettingCategory.qml b/resources/qml/Settings/SettingCategory.qml index e3202323eb..7995619af0 100644 --- a/resources/qml/Settings/SettingCategory.qml +++ b/resources/qml/Settings/SettingCategory.qml @@ -12,8 +12,8 @@ Button id: base anchors.left: parent.left anchors.right: parent.right - anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width - anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width + anchors.leftMargin: UM.Theme.getSize("thick_margin").width + anchors.rightMargin: UM.Theme.getSize("thick_margin").width background: Rectangle { id: backgroundRectangle diff --git a/resources/qml/Settings/SettingItem.qml b/resources/qml/Settings/SettingItem.qml index 785562cff5..cad6a28bd6 100644 --- a/resources/qml/Settings/SettingItem.qml +++ b/resources/qml/Settings/SettingItem.qml @@ -128,11 +128,11 @@ Item { id: settingControls height: Math.round(parent.height / 2) - spacing: Math.round(UM.Theme.getSize("sidebar_margin").height / 2) + spacing: Math.round(UM.Theme.getSize("thick_margin").height / 2) anchors { right: controlContainer.left - rightMargin: Math.round(UM.Theme.getSize("sidebar_margin").width / 2) + rightMargin: Math.round(UM.Theme.getSize("thick_margin").width / 2) verticalCenter: parent.verticalCenter } @@ -290,7 +290,7 @@ Item { enabled: propertyProvider.isValueUsed anchors.right: parent.right; - anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width + anchors.rightMargin: UM.Theme.getSize("thick_margin").width anchors.verticalCenter: parent.verticalCenter; width: UM.Theme.getSize("setting_control").width; height: UM.Theme.getSize("setting_control").height diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 5a6811926e..cd9701aab6 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -24,15 +24,15 @@ Item Item { id: globalProfileRow - height: UM.Theme.getSize("sidebar_setup").height + height: UM.Theme.getSize("print_setup_item").height anchors { top: parent.top left: parent.left - leftMargin: Math.round(UM.Theme.getSize("sidebar_margin").width) + leftMargin: Math.round(UM.Theme.getSize("thick_margin").width) right: parent.right - rightMargin: Math.round(UM.Theme.getSize("sidebar_margin").width) + rightMargin: Math.round(UM.Theme.getSize("thick_margin").width) } Label @@ -40,7 +40,7 @@ Item id: globalProfileLabel text: catalog.i18nc("@label","Profile:") textFormat: Text.PlainText - width: Math.round(parent.width * 0.45 - UM.Theme.getSize("sidebar_margin").width - 2) + width: Math.round(parent.width * 0.45 - UM.Theme.getSize("thick_margin").width - 2) font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") verticalAlignment: Text.AlignVCenter @@ -87,7 +87,7 @@ Item anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right - anchors.rightMargin: Math.round(UM.Theme.getSize("setting_preferences_button_margin").width - UM.Theme.getSize("sidebar_margin").width) + anchors.rightMargin: Math.round(UM.Theme.getSize("setting_preferences_button_margin").width - UM.Theme.getSize("thick_margin").width) color: hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button"); iconSource: UM.Theme.getIcon("star"); @@ -100,7 +100,7 @@ Item onEntered: { var content = catalog.i18nc("@tooltip","Some setting/override values are different from the values stored in the profile.\n\nClick to open the profile manager.") - base.showTooltip(globalProfileRow, Qt.point(-UM.Theme.getSize("sidebar_margin").width, 0), content) + base.showTooltip(globalProfileRow, Qt.point(-UM.Theme.getSize("thick_margin").width, 0), content) } onExited: base.hideTooltip() } @@ -116,9 +116,9 @@ Item anchors { top: globalProfileRow.bottom - topMargin: UM.Theme.getSize("sidebar_margin").height + topMargin: UM.Theme.getSize("thick_margin").height right: parent.right - rightMargin: UM.Theme.getSize("sidebar_margin").width + rightMargin: UM.Theme.getSize("thick_margin").width } style: ButtonStyle { @@ -169,9 +169,9 @@ Item anchors { top: globalProfileRow.bottom - topMargin: UM.Theme.getSize("sidebar_margin").height + topMargin: UM.Theme.getSize("thick_margin").height left: parent.left - leftMargin: UM.Theme.getSize("sidebar_margin").width + leftMargin: UM.Theme.getSize("thick_margin").width right: settingVisibilityMenu.left rightMargin: Math.floor(UM.Theme.getSize("default_margin").width / 2) } @@ -191,7 +191,7 @@ Item height: parent.height anchors.left: parent.left anchors.right: clearFilterButton.left - anchors.rightMargin: Math.round(UM.Theme.getSize("sidebar_margin").width) + anchors.rightMargin: Math.round(UM.Theme.getSize("thick_margin").width) placeholderText: catalog.i18nc("@label:textbox", "Search...") @@ -288,7 +288,7 @@ Item anchors.bottom: parent.bottom; anchors.right: parent.right; anchors.left: parent.left; - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + anchors.topMargin: UM.Theme.getSize("thick_margin").height style: UM.Theme.styles.scrollview; flickableItem.flickableDirection: Flickable.VerticalFlick; @@ -325,7 +325,7 @@ Item { id: delegate - width: Math.round(UM.Theme.getSize("sidebar").width); + width: Math.round(UM.Theme.getSize("print_setup_widget").width); height: provider.properties.enabled == "True" ? UM.Theme.getSize("section").height : - contents.spacing Behavior on height { NumberAnimation { duration: 100 } } opacity: provider.properties.enabled == "True" ? 1 : 0 diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index ec673f2823..b5c94c3f82 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -35,7 +35,7 @@ Item { width: childrenRect.width height: childrenRect.height - color: UM.Theme.getColor("sidebar") + color: UM.Theme.getColor("main_background") // // Quality profile @@ -44,10 +44,10 @@ Item { id: qualityRow - height: UM.Theme.getSize("sidebar_margin").height - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + height: UM.Theme.getSize("thick_margin").height + anchors.topMargin: UM.Theme.getSize("thick_margin").height anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width + anchors.leftMargin: UM.Theme.getSize("thick_margin").width anchors.right: parent.right Timer @@ -207,7 +207,7 @@ Item { anchors.verticalCenter: parent.verticalCenter anchors.top: parent.top - anchors.topMargin: Math.round(UM.Theme.getSize("sidebar_margin").height / 2) + anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height / 2) color: (Cura.MachineManager.activeMachine != null && Cura.QualityProfilesDropDownMenuModel.getItem(index).available) ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") text: { @@ -262,10 +262,10 @@ Item { id: speedSlider width: Math.round(base.width * 0.55) - height: UM.Theme.getSize("sidebar_margin").height + height: UM.Theme.getSize("thick_margin").height anchors.right: parent.right anchors.top: parent.top - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + anchors.topMargin: UM.Theme.getSize("thick_margin").height // This Item is used only for tooltip, for slider area which is unavailable Item @@ -275,7 +275,7 @@ Item 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) + base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, customisedSettings.height), content) } else { @@ -380,7 +380,7 @@ Item Slider { id: qualitySlider - height: UM.Theme.getSize("sidebar_margin").height + height: UM.Theme.getSize("thick_margin").height anchors.bottom: speedSlider.bottom enabled: qualityModel.totalTicks > 0 && !Cura.SimpleModeSettingsManager.isProfileCustomized visible: qualityModel.availableTotalTicks > 0 @@ -448,7 +448,7 @@ Item onEntered: { var content = catalog.i18nc("@tooltip","A custom profile is currently active. To enable the quality slider, choose a default quality profile in Custom tab") - base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("sidebar_margin").width, customisedSettings.height), content) + base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, customisedSettings.height), content) } onExited: { @@ -467,7 +467,7 @@ Item text: catalog.i18nc("@label", "Print Speed") font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") - width: Math.round(UM.Theme.getSize("sidebar").width * 0.35) + width: Math.round(UM.Theme.getSize("print_setup_widget").width * 0.35) elide: Text.ElideRight } @@ -503,7 +503,7 @@ Item anchors.verticalCenter: speedSlider.verticalCenter anchors.right: speedSlider.left - anchors.rightMargin: Math.round(UM.Theme.getSize("sidebar_margin").width / 2) + anchors.rightMargin: Math.round(UM.Theme.getSize("thick_margin").width / 2) color: hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button"); iconSource: UM.Theme.getIcon("reset"); @@ -516,7 +516,7 @@ Item onEntered: { var content = catalog.i18nc("@tooltip","You have modified some profile settings. If you want to change these go to custom mode.") - base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("sidebar_margin").width, customisedSettings.height), content) + base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, customisedSettings.height), content) } onExited: base.hideTooltip() } @@ -530,10 +530,10 @@ Item id: infillCellLeft anchors.top: qualityRow.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height * 2 + anchors.topMargin: UM.Theme.getSize("thick_margin").height * 2 anchors.left: parent.left - width: Math.round(UM.Theme.getSize("sidebar").width * .45) - UM.Theme.getSize("sidebar_margin").width + width: Math.round(UM.Theme.getSize("print_setup_widget").width * .45) - UM.Theme.getSize("thick_margin").width Label { @@ -543,9 +543,9 @@ Item color: UM.Theme.getColor("text") anchors.top: parent.top - anchors.topMargin: Math.round(UM.Theme.getSize("sidebar_margin").height * 1.7) + anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height * 1.7) anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width + anchors.leftMargin: UM.Theme.getSize("thick_margin").width } } @@ -553,12 +553,12 @@ Item { id: infillCellRight - height: infillSlider.height + UM.Theme.getSize("sidebar_margin").height + enableGradualInfillCheckBox.visible * (enableGradualInfillCheckBox.height + UM.Theme.getSize("sidebar_margin").height) - width: Math.round(UM.Theme.getSize("sidebar").width * .55) + height: infillSlider.height + UM.Theme.getSize("thick_margin").height + enableGradualInfillCheckBox.visible * (enableGradualInfillCheckBox.height + UM.Theme.getSize("thick_margin").height) + width: Math.round(UM.Theme.getSize("print_setup_widget").width * .55) anchors.left: infillCellLeft.right anchors.top: infillCellLeft.top - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + anchors.topMargin: UM.Theme.getSize("thick_margin").height Label { id: selectedInfillRateText @@ -588,10 +588,10 @@ Item anchors.top: selectedInfillRateText.bottom anchors.left: parent.left anchors.right: infillIcon.left - anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width + anchors.rightMargin: UM.Theme.getSize("thick_margin").width - height: UM.Theme.getSize("sidebar_margin").height - width: parseInt(infillCellRight.width - UM.Theme.getSize("sidebar_margin").width - style.handleWidth) + height: UM.Theme.getSize("thick_margin").height + width: parseInt(infillCellRight.width - UM.Theme.getSize("thick_margin").width - style.handleWidth) minimumValue: 0 maximumValue: 100 @@ -679,12 +679,12 @@ Item { id: infillIcon - width: Math.round((parent.width / 5) - (UM.Theme.getSize("sidebar_margin").width)) + width: Math.round((parent.width / 5) - (UM.Theme.getSize("thick_margin").width)) height: width anchors.right: parent.right anchors.top: parent.top - anchors.topMargin: Math.round(UM.Theme.getSize("sidebar_margin").height / 2) + anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height / 2) // we loop over all density icons and only show the one that has the current density and steps Repeater @@ -737,7 +737,7 @@ Item property alias _hovered: enableGradualInfillMouseArea.containsMouse anchors.top: infillSlider.bottom - anchors.topMargin: Math.round(UM.Theme.getSize("sidebar_margin").height / 2) // closer to slider since it belongs to the same category + anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height / 2) // closer to slider since it belongs to the same category anchors.left: infillCellRight.left style: UM.Theme.styles.checkbox @@ -786,7 +786,7 @@ Item id: gradualInfillLabel height: parent.height anchors.left: enableGradualInfillCheckBox.right - anchors.leftMargin: Math.round(UM.Theme.getSize("sidebar_margin").width / 2) + anchors.leftMargin: Math.round(UM.Theme.getSize("thick_margin").width / 2) verticalAlignment: Text.AlignVCenter; text: catalog.i18nc("@label", "Enable gradual") font: UM.Theme.getFont("default") @@ -848,11 +848,11 @@ Item visible: enableSupportCheckBox.visible anchors.top: infillCellRight.bottom - anchors.topMargin: Math.round(UM.Theme.getSize("sidebar_margin").height * 1.5) + anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height * 1.5) anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width + anchors.leftMargin: UM.Theme.getSize("thick_margin").width anchors.right: infillCellLeft.right - anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width + anchors.rightMargin: UM.Theme.getSize("thick_margin").width anchors.verticalCenter: enableSupportCheckBox.verticalCenter text: catalog.i18nc("@label", "Generate Support"); @@ -916,11 +916,11 @@ Item textRole: "text" // this solves that the combobox isn't populated in the first time Cura is started anchors.top: enableSupportCheckBox.top - //anchors.topMargin: ((supportEnabled.properties.value === "True") && (machineExtruderCount.properties.value > 1)) ? UM.Theme.getSize("sidebar_margin").height : 0 + //anchors.topMargin: ((supportEnabled.properties.value === "True") && (machineExtruderCount.properties.value > 1)) ? UM.Theme.getSize("thick_margin").height : 0 anchors.left: enableSupportCheckBox.right - anchors.leftMargin: Math.round(UM.Theme.getSize("sidebar_margin").width / 2) + anchors.leftMargin: Math.round(UM.Theme.getSize("thick_margin").width / 2) - width: Math.round(UM.Theme.getSize("sidebar").width * .55) - Math.round(UM.Theme.getSize("sidebar_margin").width / 2) - enableSupportCheckBox.width + width: Math.round(UM.Theme.getSize("print_setup_widget").width * .55) - Math.round(UM.Theme.getSize("thick_margin").width / 2) - enableSupportCheckBox.width height: ((supportEnabled.properties.value == "True") && (machineExtruderCount.properties.value > 1)) ? UM.Theme.getSize("setting_control").height : 0 Behavior on height { NumberAnimation { duration: 100 } } @@ -991,9 +991,9 @@ Item anchors { left: parent.left - leftMargin: UM.Theme.getSize("sidebar_margin").width + leftMargin: UM.Theme.getSize("thick_margin").width right: infillCellLeft.right - rightMargin: UM.Theme.getSize("sidebar_margin").width + rightMargin: UM.Theme.getSize("thick_margin").width verticalCenter: adhesionCheckBox.verticalCenter } } @@ -1004,7 +1004,7 @@ Item property alias _hovered: adhesionMouseArea.containsMouse anchors.top: enableSupportCheckBox.bottom - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + anchors.topMargin: UM.Theme.getSize("thick_margin").height anchors.left: infillCellRight.left //: Setting enable printing build-plate adhesion helper checkbox @@ -1065,7 +1065,7 @@ Item { id: tipsCell anchors.top: adhesionCheckBox.visible ? adhesionCheckBox.bottom : (enableSupportCheckBox.visible ? supportExtruderCombobox.bottom : infillCellRight.bottom) - anchors.topMargin: Math.round(UM.Theme.getSize("sidebar_margin").height * 2) + anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height * 2) anchors.left: parent.left width: parent.width height: tipsText.contentHeight * tipsText.lineCount @@ -1074,9 +1074,9 @@ Item { id: tipsText anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width + anchors.leftMargin: UM.Theme.getSize("thick_margin").width anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width + anchors.rightMargin: UM.Theme.getSize("thick_margin").width anchors.top: parent.top wrapMode: Text.WordWrap text: catalog.i18nc("@label", "Need help improving your prints?
Read the
Ultimaker Troubleshooting Guides").arg("https://ultimaker.com/en/troubleshooting") diff --git a/resources/themes/cura-dark/theme.json b/resources/themes/cura-dark/theme.json index 1625750a19..571e1acbe4 100644 --- a/resources/themes/cura-dark/theme.json +++ b/resources/themes/cura-dark/theme.json @@ -5,7 +5,9 @@ }, "colors": { - "sidebar": [39, 44, 48, 255], + "main_background": [39, 44, 48, 255], + "wide_lining": [31, 36, 39, 255], + "thick_lining": [255, 255, 255, 30], "lining": [64, 69, 72, 255], "viewport_overlay": [0, 6, 9, 222], @@ -18,6 +20,9 @@ "main_window_header_button_text_inactive": [128, 128, 128, 255], "main_window_header_button_text_hovered": [255, 255, 255, 255], + "machine_selector_bar": [39, 44, 48, 255], + "machine_selector_active": [39, 44, 48, 255], + "text": [255, 255, 255, 204], "text_detail": [255, 255, 255, 172], "text_link": [255, 255, 255, 127], @@ -30,16 +35,6 @@ "text_scene_hover": [255, 255, 255, 204], "error": [212, 31, 53, 255], - "sidebar_header_bar": [39, 44, 48, 255], - "sidebar_header_active": [39, 44, 48, 255], - "sidebar_header_hover": [68, 72, 75, 255], - "sidebar_header_highlight": [68, 192, 255, 255], - "sidebar_header_highlight_hover": [68, 192, 255, 255], - "sidebar_header_text_active": [255, 255, 255, 255], - "sidebar_header_text_hover": [255, 255, 255, 255], - "sidebar_header_text_inactive": [255, 255, 255, 127], - "sidebar_lining": [31, 36, 39, 255], - "sidebar_lining_thin": [255, 255, 255, 30], "button": [39, 44, 48, 255], "button_hover": [39, 44, 48, 255], diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index 1bbb13ff26..43e8d29b57 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -491,9 +491,9 @@ QtObject { anchors.fill: parent anchors.left: parent.left - anchors.leftMargin: Theme.getSize("sidebar_margin").width + anchors.leftMargin: Theme.getSize("thick_margin").width anchors.right: parent.right - anchors.rightMargin: Theme.getSize("sidebar_margin").width + anchors.rightMargin: Theme.getSize("thick_margin").width implicitHeight: Theme.getSize("section").height color: { @@ -567,7 +567,7 @@ QtObject { anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left - anchors.leftMargin: Theme.getSize("sidebar_margin").width + anchors.leftMargin: Theme.getSize("thick_margin").width color: { if(!control.enabled) @@ -1037,7 +1037,7 @@ QtObject } Behavior on color { ColorAnimation { duration: 50 } } - implicitWidth: actualLabel.contentWidth + (UM.Theme.getSize("sidebar_margin").width * 2) + implicitWidth: actualLabel.contentWidth + (UM.Theme.getSize("thick_margin").width * 2) Label { diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 38d4f5372f..4b1252abf4 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -69,7 +69,9 @@ "colors": { - "sidebar": [255, 255, 255, 255], + "main_background": [255, 255, 255, 255], + "wide_lining": [245, 245, 245, 255], + "thick_lining": [127, 127, 127, 255], "lining": [192, 193, 194, 255], "viewport_overlay": [0, 0, 0, 192], @@ -97,6 +99,11 @@ "account_widget_outline_active": [70, 66, 126, 255], "account_widget_outline_inactive": [229, 229, 229, 255], + "machine_selector_bar": [31, 36, 39, 255], + "machine_selector_active": [68, 72, 75, 255], + "machine_selector_hover": [68, 72, 75, 255], + "machine_selector_text_active": [255, 255, 255, 255], + "text": [0, 0, 0, 255], "text_detail": [174, 174, 174, 128], "text_link": [12, 169, 227, 255], @@ -110,16 +117,6 @@ "text_scene_hover": [70, 84, 113, 255], "error": [255, 140, 0, 255], - "sidebar_header_bar": [31, 36, 39, 255], - "sidebar_header_active": [68, 72, 75, 255], - "sidebar_header_hover": [68, 72, 75, 255], - "sidebar_header_highlight": [68, 192, 255, 255], - "sidebar_header_highlight_hover": [68, 192, 255, 255], - "sidebar_header_text_inactive": [255, 255, 255, 255], - "sidebar_header_text_active": [255, 255, 255, 255], - "sidebar_header_text_hover": [255, 255, 255, 255], - "sidebar_lining": [245, 245, 245, 255], - "sidebar_lining_thin": [127, 127, 127, 255], "button": [31, 36, 39, 255], "button_hover": [68, 72, 75, 255], @@ -356,35 +353,34 @@ "account_button": [12, 3], "print_setup_widget": [35.0, 42.0], - "configuration_selector_widget": [35.0, 0.0], + "print_setup_mode_toggle": [0.0, 2.0], + "print_setup_item": [0.0, 2.0], + "print_setup_extruder_box": [0.0, 6.0], + + "configuration_selector_widget": [35.0, 4.5], + "configuration_selector_mode_tabs": [0.0, 3.0], + + "action_panel_widget": [35.0, 0.0], + "machine_selector_widget": [28.0, 4.5], "views_selector": [0.0, 4.0], + "wide_lining": [0.5, 0.5], + "thick_lining": [0.2, 0.2], "default_lining": [0.08, 0.08], + "default_arrow": [0.8, 0.8], "logo": [8, 2.4], - "default_margin": [1.0, 1.0], "wide_margin": [2.0, 2.0], + "thick_margin": [1.71, 1.43], + "default_margin": [1.0, 1.0], + "thin_margin": [0.71, 0.71], "narrow_margin": [0.5, 0.5], - "window_margin": [1.0, 1.0], "extruder_button_material_margin": [0.70, 0.9], "extruder_button_material": [0.75, 0.75], - "sidebar": [35.0, 10.0], - "sidebar_margin": [1.71, 1.43], - "sidebar_margin_thin": [0.71, 0.71], - "sidebar_header": [0.0, 4.0], - "sidebar_header_highlight": [0.25, 0.25], - "sidebar_header_mode_toggle": [0.0, 2.0], - "sidebar_header_mode_tabs": [0.0, 3.0], - "sidebar_lining": [0.5, 0.5], - "sidebar_lining_thin": [0.2, 0.2], - "sidebar_setup": [0.0, 2.0], - "sidebar_tabs": [0.0, 3.5], - "sidebar_inputfields": [0.0, 2.0], - "sidebar_extruder_box": [0.0, 6.0], "simple_mode_infill_caption": [0.0, 5.0], "simple_mode_infill_height": [0.0, 8.0], @@ -495,6 +491,7 @@ "toolbox_property_label": [1.0, 2.0], "toolbox_heading_label": [1.0, 4.0], "toolbox_header": [1.0, 4.0], + "toolbox_header_highlight": [0.25, 0.25], "toolbox_progress_bar": [8.0, 0.5], "toolbox_chart_row": [1.0, 2.0], "toolbox_action_button": [8.0, 2.5], From 5a759fa9f493bb034170b0cec566653b22b51793 Mon Sep 17 00:00:00 2001 From: pinchies Date: Sat, 27 Oct 2018 03:47:29 +1100 Subject: [PATCH 0086/1240] fix start routing --- resources/definitions/jgaurora_a5.def.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/definitions/jgaurora_a5.def.json b/resources/definitions/jgaurora_a5.def.json index 6143ef1523..0a8b93f7cf 100644 --- a/resources/definitions/jgaurora_a5.def.json +++ b/resources/definitions/jgaurora_a5.def.json @@ -20,7 +20,7 @@ "default_value": "JGAurora A5" }, "machine_start_gcode": { - "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F6000 ;move Z to position 15.0 mm as fast as possible\nG92 E0 ;zero the extruded length\nG1 X0.0 Y0.0 F1000.0 ;go to edge of print area\nG1 X60.0 E9.0 F1000.0 ;intro line\nG1 X100.0 E21.5 F1000.0 ;intro line\nG92 E0 ;zero the extruded length again\n; -- end of START GCODE --" + "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 ;home all axis\nM420 S1 ;turn on mesh bed levelling if enabled in firmware\nG92 E0 ;zero the extruded length\nG1 Z1 F1000 ;move up slightly\nG1 X60.0 Z0 E9.0 F1000.0;intro line\nG1 X100.0 E21.5 F1000.0 ;continue line\nG92 E0 ;zero the extruded length again\n; -- end of START GCODE --" }, "machine_end_gcode": { "default_value": "; -- END GCODE --\nM104 S0 ;turn off nozzle heater\nM140 S0 ;turn off bed heater\nG91 ;set to relative positioning\nG1 E-10 F300 ;retract the filament slightly\nG90 ;set to absolute positioning\nG28 X0 ;move to the X-axis origin (Home)\nG0 Y280 F600 ;bring the bed to the front for easy print removal\nM84 ;turn off stepper motors\n; -- end of END GCODE --" @@ -95,4 +95,4 @@ "default_value": 45 } } -} \ No newline at end of file +} From 6fa382a527137024d84a4cdf169cfc8a7dce67bb Mon Sep 17 00:00:00 2001 From: pinchies Date: Sat, 27 Oct 2018 03:48:24 +1100 Subject: [PATCH 0087/1240] Update jgaurora_a1.def.json --- resources/definitions/jgaurora_a1.def.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/definitions/jgaurora_a1.def.json b/resources/definitions/jgaurora_a1.def.json index 004fd8741d..4fd2eb4994 100644 --- a/resources/definitions/jgaurora_a1.def.json +++ b/resources/definitions/jgaurora_a1.def.json @@ -18,7 +18,7 @@ "default_value": "JGAurora A1" }, "machine_start_gcode": { - "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F6000 ;move Z to position 15.0 mm as fast as possible\nG92 E0 ;zero the extruded length\nG1 X0.0 Y0.0 F1000.0 ;go to edge of print area\nG1 X60.0 E9.0 F1000.0 ;intro line\nG1 X100.0 E21.5 F1000.0 ;intro line\nG92 E0 ;zero the extruded length again\n; -- end of START GCODE --" + "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 ;home all axis\nM420 S1 ;turn on mesh bed levelling if enabled in firmware\nG92 E0 ;zero the extruded length\nG1 Z1 F1000 ;move up slightly\nG1 X60.0 Z0 E9.0 F1000.0;intro line\nG1 X100.0 E21.5 F1000.0 ;continue line\nG92 E0 ;zero the extruded length again\n; -- end of START GCODE --" }, "machine_end_gcode": { "default_value": "; -- END GCODE --\nM104 S0 ;turn off nozzle heater\nM140 S0 ;turn off bed heater\nG91 ;set to relative positioning\nG1 E-10 F300 ;retract the filament slightly\nG90 ;set to absolute positioning\nG28 X0 ;move to the X-axis origin (Home)\nG0 Y280 F600 ;bring the bed to the front for easy print removal\nM84 ;turn off stepper motors\n; -- end of END GCODE --" @@ -93,4 +93,4 @@ "default_value": 40 } } -} \ No newline at end of file +} From 0487288edfaf3c10ae69112ab393e54eef4d01df Mon Sep 17 00:00:00 2001 From: pinchies Date: Sat, 27 Oct 2018 03:49:34 +1100 Subject: [PATCH 0088/1240] Update alfawise_u20.def.json --- resources/definitions/alfawise_u20.def.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/definitions/alfawise_u20.def.json b/resources/definitions/alfawise_u20.def.json index f6dccce3ee..abc206c4d2 100644 --- a/resources/definitions/alfawise_u20.def.json +++ b/resources/definitions/alfawise_u20.def.json @@ -18,7 +18,7 @@ "default_value": "Alfawise U20" }, "machine_start_gcode": { - "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F6000 ;move Z to position 15.0 mm as fast as possible\nG92 E0 ;zero the extruded length\nG1 X0.0 Y0.0 F1000.0 ;go to edge of print area\nG1 X60.0 E9.0 F1000.0 ;intro line\nG1 X100.0 E21.5 F1000.0 ;intro line\nG92 E0 ;zero the extruded length again\n; -- end of START GCODE --" + "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 ;home all axis\nG92 E0 ;zero the extruded length\nG1 Z1 F1000 ;move up slightly\nG1 X60.0 Z0 E9.0 F1000.0;intro line\nG1 X100.0 E21.5 F1000.0 ;continue line\nG92 E0 ;zero the extruded length again\n; -- end of START GCODE --" }, "machine_end_gcode": { "default_value": "; -- END GCODE --\nM104 S0 ;turn off nozzle heater\nM140 S0 ;turn off bed heater\nG91 ;set to relative positioning\nG1 E-10 F300 ;retract the filament slightly\nG90 ;set to absolute positioning\nG28 X0 ;move to the X-axis origin (Home)\nG0 Y280 F600 ;bring the bed to the front for easy print removal\nM84 ;turn off stepper motors\n; -- end of END GCODE --" @@ -93,4 +93,4 @@ "default_value": 45 } } -} \ No newline at end of file +} From 46c6ef71abfcdb6d617c724e3f72af77a5e48176 Mon Sep 17 00:00:00 2001 From: pinchies Date: Sat, 27 Oct 2018 03:50:15 +1100 Subject: [PATCH 0089/1240] Update jgaurora_z_603s.def.json --- resources/definitions/jgaurora_z_603s.def.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/definitions/jgaurora_z_603s.def.json b/resources/definitions/jgaurora_z_603s.def.json index af7fa823db..59e0ff129c 100644 --- a/resources/definitions/jgaurora_z_603s.def.json +++ b/resources/definitions/jgaurora_z_603s.def.json @@ -18,7 +18,7 @@ "default_value": "JGAurora Z-603S" }, "machine_start_gcode": { - "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F6000 ;move Z to position 15.0 mm as fast as possible\nG92 E0 ;zero the extruded length\nG1 X0.0 Y0.0 F1000.0 ;go to edge of print area\nG1 X60.0 E9.0 F1000.0 ;intro line\nG1 X100.0 E21.5 F1000.0 ;intro line\nG92 E0 ;zero the extruded length again\n; -- end of START GCODE --" + "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 ;home all axis\nM420 S1 ;turn on mesh bed levelling if enabled in firmware\nG92 E0 ;zero the extruded length\nG1 Z1 F1000 ;move up slightly\nG1 X60.0 Z0 E9.0 F1000.0;intro line\nG1 X100.0 E21.5 F1000.0 ;continue line\nG92 E0 ;zero the extruded length again\n; -- end of START GCODE --" }, "machine_end_gcode": { "default_value": "; -- END GCODE --\nM104 S0 ;turn off nozzle heater\nM140 S0 ;turn off bed heater\nG91 ;set to relative positioning\nG1 E-10 F300 ;retract the filament slightly\nG90 ;set to absolute positioning\nG28 X0 ;move to the X-axis origin (Home)\nG0 Y280 F600 ;bring the bed to the front for easy print removal\nM84 ;turn off stepper motors\n; -- end of END GCODE --" @@ -93,4 +93,4 @@ "default_value": 50 } } -} \ No newline at end of file +} From cdb5ab193051b4fb16752aeb57fe386c9a3d4e59 Mon Sep 17 00:00:00 2001 From: pinchies Date: Sat, 27 Oct 2018 03:51:05 +1100 Subject: [PATCH 0090/1240] Update cocoon_create_modelmaker.def.json --- resources/definitions/cocoon_create_modelmaker.def.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/definitions/cocoon_create_modelmaker.def.json b/resources/definitions/cocoon_create_modelmaker.def.json index f752a08fc7..7f19fb0239 100644 --- a/resources/definitions/cocoon_create_modelmaker.def.json +++ b/resources/definitions/cocoon_create_modelmaker.def.json @@ -18,7 +18,7 @@ "default_value": "Cocoon Create ModelMaker" }, "machine_start_gcode": { - "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F6000 ;move Z to position 15.0 mm as fast as possible\nG92 E0 ;zero the extruded length\nG1 X0.0 Y0.0 F1000.0 ;go to edge of print area\nG1 X60.0 E9.0 F1000.0 ;intro line\nG1 X100.0 E21.5 F1000.0 ;intro line\nG92 E0 ;zero the extruded length again\n; -- end of START GCODE --" + "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 ;home all axis\nG92 E0 ;zero the extruded length\nG1 Z1 F1000 ;move up slightly\nG1 X60.0 Z0 E9.0 F1000.0;intro line\nG1 X100.0 E21.5 F1000.0 ;continue line\nG92 E0 ;zero the extruded length again\n; -- end of START GCODE --" }, "machine_end_gcode": { "default_value": "; -- END GCODE --\nM104 S0 ;turn off nozzle heater\nG91 ;set to relative positioning\nG1 E-10 F300 ;retract the filament slightly\nG90 ;set to absolute positioning\nG28 X0 Y0 ;move to the XY-axis origin (Home)\nM84 ;turn off stepper motors\n; -- end of END GCODE --" @@ -93,4 +93,4 @@ "default_value": 40 } } -} \ No newline at end of file +} From 3248a05819fccd54f71f6765ff5846b15c22b181 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 29 Oct 2018 11:28:08 +0100 Subject: [PATCH 0091/1240] Add PreviewStagePlugin stubs Since we are going to move all the views into a seperate stage, we need to add another stage to do that. CURA-5829 --- cura/CuraApplication.py | 1 + plugins/MonitorStage/__init__.py | 4 +++- plugins/PreviewStage/PreviewStage.py | 13 +++++++++++++ plugins/PreviewStage/__init__.py | 22 ++++++++++++++++++++++ plugins/PreviewStage/plugin.json | 8 ++++++++ 5 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 plugins/PreviewStage/PreviewStage.py create mode 100644 plugins/PreviewStage/__init__.py create mode 100644 plugins/PreviewStage/plugin.json diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 5824d21b1c..8f9816e968 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -433,6 +433,7 @@ class CuraApplication(QtApplication): "XmlMaterialProfile", "Toolbox", "PrepareStage", + "PreviewStage", "MonitorStage", "LocalFileOutputDevice", "LocalContainerProvider", diff --git a/plugins/MonitorStage/__init__.py b/plugins/MonitorStage/__init__.py index bdaf53a36c..0468e6319b 100644 --- a/plugins/MonitorStage/__init__.py +++ b/plugins/MonitorStage/__init__.py @@ -7,14 +7,16 @@ from . import MonitorStage from UM.i18n import i18nCatalog i18n_catalog = i18nCatalog("cura") + def getMetaData(): return { "stage": { "name": i18n_catalog.i18nc("@item:inmenu", "Monitor"), - "weight": 1 + "weight": 2 } } + def register(app): return { "stage": MonitorStage.MonitorStage() diff --git a/plugins/PreviewStage/PreviewStage.py b/plugins/PreviewStage/PreviewStage.py new file mode 100644 index 0000000000..a51bf766b6 --- /dev/null +++ b/plugins/PreviewStage/PreviewStage.py @@ -0,0 +1,13 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from UM.Application import Application +from cura.Stages.CuraStage import CuraStage + + +class PreviewStage(CuraStage): + def __init__(self, parent = None) -> None: + super().__init__(parent) + Application.getInstance().engineCreatedSignal.connect(self._engineCreated) + + def _engineCreated(self): + return \ No newline at end of file diff --git a/plugins/PreviewStage/__init__.py b/plugins/PreviewStage/__init__.py new file mode 100644 index 0000000000..e03992fc00 --- /dev/null +++ b/plugins/PreviewStage/__init__.py @@ -0,0 +1,22 @@ +# Copyright (c) 2017 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from . import PreviewStage + +from UM.i18n import i18nCatalog +i18n_catalog = i18nCatalog("cura") + + +def getMetaData(): + return { + "stage": { + "name": i18n_catalog.i18nc("@item:inmenu", "Preview"), + "weight": 1 + } + } + + +def register(app): + return { + "stage": PreviewStage.PreviewStage() + } diff --git a/plugins/PreviewStage/plugin.json b/plugins/PreviewStage/plugin.json new file mode 100644 index 0000000000..9349da2b0e --- /dev/null +++ b/plugins/PreviewStage/plugin.json @@ -0,0 +1,8 @@ +{ + "name": "Preview Stage", + "author": "Ultimaker B.V.", + "version": "1.0.0", + "description": "Provides a preview stage in Cura.", + "api": 5, + "i18n-catalog": "cura" +} \ No newline at end of file From 9cc7a7ca23df6427f5e921ca1594dfd8c3ed176a Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 29 Oct 2018 13:27:47 +0100 Subject: [PATCH 0092/1240] Moved view selection to previewStage CURA-5829 --- plugins/PreviewStage/PreviewMenu.qml | 77 ++++++++++++++++++++ plugins/PreviewStage/PreviewStage.py | 26 ++++++- plugins/PreviewStage/__init__.py | 2 +- resources/qml/Cura.qml | 62 ---------------- resources/qml/MainWindow/ApplicationMenu.qml | 2 - 5 files changed, 100 insertions(+), 69 deletions(-) create mode 100644 plugins/PreviewStage/PreviewMenu.qml diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml new file mode 100644 index 0000000000..c364c4c3d7 --- /dev/null +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -0,0 +1,77 @@ +import QtQuick 2.7 + +import QtQuick.Controls 1.4 + +import UM 1.3 as UM +import Cura 1.1 as Cura + +Item +{ + id: previewMenu + // This widget doesn't show tooltips by itself. Instead it emits signals so others can do something with it. + signal showTooltip(Item item, point location, string text) + signal hideTooltip() + + UM.I18nCatalog + { + id: catalog + name: "cura" + } + + Row + { + anchors.horizontalCenter: parent.horizontalCenter + ComboBox + { + // This item contains the views selector, a combobox that is dynamically created from + // the list of available Views (packages that create different visualizations of the + // scene). + id: viewModeButton + + style: UM.Theme.styles.combobox + + model: UM.ViewModel { } + textRole: "name" + + // update the model's active index + function updateItemActiveFlags() + { + currentIndex = getActiveIndex() + for (var i = 0; i < model.rowCount(); i++) + { + model.getItem(i).active = (i == currentIndex) + } + } + + // get the index of the active model item on start + function getActiveIndex() + { + for (var i = 0; i < model.rowCount(); i++) + { + print(model.getItem(i).active) + if (model.getItem(i).active) + { + return i; + } + } + return 0 + } + + onCurrentIndexChanged: + { + if (model.getItem(currentIndex).id != undefined) + { + UM.Controller.setActiveView(model.getItem(currentIndex).id) + } + } + currentIndex: getActiveIndex() + } + + Cura.PrintSetupSelector + { + width: UM.Theme.getSize("print_setup_widget").width + onShowTooltip: previewMenu.showTooltip(item, location, text) + onHideTooltip: previewMenu.hideTooltip() + } + } +} \ No newline at end of file diff --git a/plugins/PreviewStage/PreviewStage.py b/plugins/PreviewStage/PreviewStage.py index a51bf766b6..4c449e55f2 100644 --- a/plugins/PreviewStage/PreviewStage.py +++ b/plugins/PreviewStage/PreviewStage.py @@ -1,13 +1,31 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +import os.path + from UM.Application import Application from cura.Stages.CuraStage import CuraStage +from typing import TYPE_CHECKING, Optional + +if TYPE_CHECKING: + from UM.View import View class PreviewStage(CuraStage): - def __init__(self, parent = None) -> None: + def __init__(self, application: Application, parent = None) -> None: super().__init__(parent) - Application.getInstance().engineCreatedSignal.connect(self._engineCreated) + self._application = application + self._application.engineCreatedSignal.connect(self._engineCreated) + self._previously_active_view = None # type: Optional[View] - def _engineCreated(self): - return \ No newline at end of file + def onStageSelected(self) -> None: + self._previously_active_view = self._application.getController().getActiveView() + + def onStageDeselected(self) -> None: + if self._previously_active_view is not None: + self._application.getController().setActiveView(self._previously_active_view.getPluginId()) + self._previously_active_view = None + + def _engineCreated(self) -> None: + menu_component_path = os.path.join(self._application.getPluginRegistry().getPluginPath(self.getPluginId()), + "PreviewMenu.qml") + self.addDisplayComponent("menu", menu_component_path) diff --git a/plugins/PreviewStage/__init__.py b/plugins/PreviewStage/__init__.py index e03992fc00..d58826934e 100644 --- a/plugins/PreviewStage/__init__.py +++ b/plugins/PreviewStage/__init__.py @@ -18,5 +18,5 @@ def getMetaData(): def register(app): return { - "stage": PreviewStage.PreviewStage() + "stage": PreviewStage.PreviewStage(app) } diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 3e2515cb3e..c3d2c98ecc 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -230,68 +230,6 @@ UM.MainWindow } } - ComboBox - { - // This item contains the views selector, a combobox that is dynamically created from - // the list of available Views (packages that create different visualizations of the - // scene). - id: viewModeButton - - anchors.left: viewOrientationControls.right - anchors.bottom: viewOrientationControls.bottom - - style: UM.Theme.styles.combobox - - model: UM.ViewModel { } - textRole: "name" - - // update the model's active index - function updateItemActiveFlags() - { - currentIndex = getActiveIndex() - for (var i = 0; i < model.rowCount(); i++) - { - model.getItem(i).active = (i == currentIndex) - } - } - - // get the index of the active model item on start - function getActiveIndex () - { - for (var i = 0; i < model.rowCount(); i++) - { - if (model.getItem(i).active) - { - return i; - } - } - return 0 - } - - // set the active index - function setActiveIndex(index) - { - UM.Controller.setActiveView(index) - // the connection to UM.ActiveView will trigger update so there is no reason to call it manually here - } - - onCurrentIndexChanged: - { - if (model.getItem(currentIndex).id != undefined) - { - viewModeButton.setActiveIndex(model.getItem(currentIndex).id) - } - } - currentIndex: getActiveIndex() - - // watch the active view proxy for changes made from the menu item - Connections - { - target: UM.ActiveView - onActiveViewChanged: viewModeButton.updateItemActiveFlags() - } - } - Loader { id: viewPanel diff --git a/resources/qml/MainWindow/ApplicationMenu.qml b/resources/qml/MainWindow/ApplicationMenu.qml index d3bc115419..884609deee 100644 --- a/resources/qml/MainWindow/ApplicationMenu.qml +++ b/resources/qml/MainWindow/ApplicationMenu.qml @@ -45,8 +45,6 @@ Item MenuItem { action: Cura.Actions.unGroupObjects } } - ViewMenu { title: catalog.i18nc("@title:menu menubar:toplevel", "&View") } - SettingsMenu { title: catalog.i18nc("@title:menu menubar:toplevel", "&Settings") } Menu From ebe533bdc831c9872d3d0c352e73134656de253e Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 29 Oct 2018 13:39:00 +0100 Subject: [PATCH 0093/1240] Move active view panel to previewStage CURA-5829 --- plugins/PreviewStage/PreviewMenu.qml | 15 +++++++++++++++ resources/qml/Cura.qml | 16 ---------------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index c364c4c3d7..0ab1c0be83 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -21,6 +21,8 @@ Item Row { anchors.horizontalCenter: parent.horizontalCenter + spacing: UM.Theme.getSize("default_margin").width + ComboBox { // This item contains the views selector, a combobox that is dynamically created from @@ -67,6 +69,19 @@ Item currentIndex: getActiveIndex() } + Loader + { + // TODO: Make this panel collapsable and ensure it has a standardised background. + id: viewPanel + + property var buttonTarget: Qt.point(viewModeButton.x + Math.round(viewModeButton.width / 2), viewModeButton.y + Math.round(viewModeButton.height / 2)) + + height: childrenRect.height + width: childrenRect.width + + source: UM.ActiveView.valid ? UM.ActiveView.activeViewPanel : "" + } + Cura.PrintSetupSelector { width: UM.Theme.getSize("print_setup_widget").width diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index c3d2c98ecc..9f53b75dd1 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -230,22 +230,6 @@ UM.MainWindow } } - Loader - { - id: viewPanel - - anchors.bottom: viewModeButton.top - anchors.topMargin: UM.Theme.getSize("default_margin").height - anchors.right: viewModeButton.right - - property var buttonTarget: Qt.point(viewModeButton.x + Math.round(viewModeButton.width / 2), viewModeButton.y + Math.round(viewModeButton.height / 2)) - - height: childrenRect.height - width: childrenRect.width - - source: UM.ActiveView.valid ? UM.ActiveView.activeViewPanel : "" - } - Cura.ActionPanelWidget { anchors.right: parent.right From fc6ad4f6233833a2b59d4086306a21e1de3427f4 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 29 Oct 2018 13:41:46 +0100 Subject: [PATCH 0094/1240] Put back the view menu, since it did more than just show the views CURA-5829 --- resources/qml/MainWindow/ApplicationMenu.qml | 2 ++ resources/qml/Menus/ViewMenu.qml | 19 ------------------- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/resources/qml/MainWindow/ApplicationMenu.qml b/resources/qml/MainWindow/ApplicationMenu.qml index 884609deee..d3bc115419 100644 --- a/resources/qml/MainWindow/ApplicationMenu.qml +++ b/resources/qml/MainWindow/ApplicationMenu.qml @@ -45,6 +45,8 @@ Item MenuItem { action: Cura.Actions.unGroupObjects } } + ViewMenu { title: catalog.i18nc("@title:menu menubar:toplevel", "&View") } + SettingsMenu { title: catalog.i18nc("@title:menu menubar:toplevel", "&Settings") } Menu diff --git a/resources/qml/Menus/ViewMenu.qml b/resources/qml/Menus/ViewMenu.qml index 593bd63bcc..33e8764cd8 100644 --- a/resources/qml/Menus/ViewMenu.qml +++ b/resources/qml/Menus/ViewMenu.qml @@ -15,25 +15,6 @@ Menu property var multiBuildPlateModel: CuraApplication.getMultiBuildPlateModel() - // main views - Instantiator - { - model: UM.ViewModel{} - MenuItem - { - text: model.name - checkable: true - checked: model.active - exclusiveGroup: group - onTriggered: UM.Controller.setActiveView(model.id) - } - onObjectAdded: base.insertItem(index, object) - onObjectRemoved: base.removeItem(object) - } - ExclusiveGroup { id: group } - - MenuSeparator {} - Menu { title: catalog.i18nc("@action:inmenu menubar:view","&Camera position"); From cfa962311bb45476fcc9e65dd24933bb9f2aa397 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 29 Oct 2018 13:54:20 +0100 Subject: [PATCH 0095/1240] Fixed typing CURA-5829 --- plugins/PreviewStage/PreviewStage.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/plugins/PreviewStage/PreviewStage.py b/plugins/PreviewStage/PreviewStage.py index 4c449e55f2..e41d4a592f 100644 --- a/plugins/PreviewStage/PreviewStage.py +++ b/plugins/PreviewStage/PreviewStage.py @@ -2,16 +2,17 @@ # Cura is released under the terms of the LGPLv3 or higher. import os.path -from UM.Application import Application +from UM.Qt.QtApplication import QtApplication from cura.Stages.CuraStage import CuraStage from typing import TYPE_CHECKING, Optional if TYPE_CHECKING: - from UM.View import View + from UM.View.View import View + class PreviewStage(CuraStage): - def __init__(self, application: Application, parent = None) -> None: + def __init__(self, application: QtApplication, parent = None) -> None: super().__init__(parent) self._application = application self._application.engineCreatedSignal.connect(self._engineCreated) @@ -26,6 +27,7 @@ class PreviewStage(CuraStage): self._previously_active_view = None def _engineCreated(self) -> None: - menu_component_path = os.path.join(self._application.getPluginRegistry().getPluginPath(self.getPluginId()), - "PreviewMenu.qml") - self.addDisplayComponent("menu", menu_component_path) + plugin_path = self._application.getPluginRegistry().getPluginPath(self.getPluginId()) + if plugin_path is not None: + menu_component_path = os.path.join(plugin_path, "PreviewMenu.qml") + self.addDisplayComponent("menu", menu_component_path) From d8dd9bf3631bcfd5cd77d11cb41688127dd31e60 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 29 Oct 2018 14:54:42 +0100 Subject: [PATCH 0096/1240] Set SolidView to invisible. Since we don't have a selector in the prepare stage anymore, solidview must be invisible CURA-5829 --- plugins/PreviewStage/PreviewMenu.qml | 1 - plugins/SolidView/__init__.py | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index 0ab1c0be83..ae875fb0a2 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -50,7 +50,6 @@ Item { for (var i = 0; i < model.rowCount(); i++) { - print(model.getItem(i).active) if (model.getItem(i).active) { return i; diff --git a/plugins/SolidView/__init__.py b/plugins/SolidView/__init__.py index db2e48f489..34148fcf05 100644 --- a/plugins/SolidView/__init__.py +++ b/plugins/SolidView/__init__.py @@ -10,7 +10,8 @@ def getMetaData(): return { "view": { "name": i18n_catalog.i18nc("@item:inmenu", "Solid view"), - "weight": 0 + "weight": 0, + "visible": False } } From bf59097320d7f5eaf4bd072cde2d8a841d27c550 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 29 Oct 2018 15:18:34 +0100 Subject: [PATCH 0097/1240] Split the action button panel in two different components that are loaded in different moments. The SliceProcessWidget is loaded once there is something to slice and it also shows the process of slicing. The OutputProcessWidget will show the print information and the actions to do to output the results, such as Print over network, Save to file, ... Contributes to CURA-5786. --- resources/qml/ActionPanelWidget.qml | 239 +++---------------------- resources/qml/Cura.qml | 17 +- resources/qml/OutputProcessWidget.qml | 72 ++++++++ resources/qml/SliceProcessWidget.qml | 62 +++++++ resources/themes/cura-light/theme.json | 4 + 5 files changed, 172 insertions(+), 222 deletions(-) create mode 100644 resources/qml/OutputProcessWidget.qml create mode 100644 resources/qml/SliceProcessWidget.qml diff --git a/resources/qml/ActionPanelWidget.qml b/resources/qml/ActionPanelWidget.qml index b5dc7f83c9..d1fe999731 100644 --- a/resources/qml/ActionPanelWidget.qml +++ b/resources/qml/ActionPanelWidget.qml @@ -2,7 +2,7 @@ // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.7 -import QtQuick.Controls 2.0 +import QtQuick.Controls 2.1 import QtQuick.Layouts 1.3 import UM 1.2 as UM @@ -10,230 +10,43 @@ import Cura 1.0 as Cura Rectangle { - id: base + id: actionPanelWidget - // We need a whole lot of print duration information. - property variant printDuration: PrintInformation.currentPrintTime - - // This widget doesn't show tooltips by itself. Instead it emits signals so others can do something with it. - signal showTooltip(Item item, point location, string text) - signal hideTooltip() + width: childrenRect.width + 2 * UM.Theme.getSize("thick_margin").width + height: childrenRect.height + 2 * UM.Theme.getSize("thick_margin").height color: UM.Theme.getColor("main_background") + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") + radius: UM.Theme.getSize("default_radius").width + visible: CuraApplication.platformActivity - // Also add an extra margin, as we want some breathing room around the edges. - height: saveButton.height + UM.Theme.getSize("thick_margin").height - Label + property bool backendStatusDone: UM.Backend.state == 3 + + Loader { - id: timeDetails - anchors.left: parent.left - anchors.bottom: costSpec.top - anchors.leftMargin: UM.Theme.getSize("thick_margin").width - - font: UM.Theme.getFont("large") - color: UM.Theme.getColor("text_subtext") - text: (!base.printDuration || !base.printDuration.valid) ? catalog.i18nc("@label Hours and minutes", "00h 00min") : base.printDuration.getDisplayString(UM.DurationFormat.Short) - renderType: Text.NativeRendering - - MouseArea + id: loader + anchors { - id: timeDetailsMouseArea - anchors.fill: parent - hoverEnabled: true - - onEntered: - { - if(base.printDuration.valid && !base.printDuration.isTotalDurationZero) - { - // All the time information for the different features is achieved - var print_time = PrintInformation.getFeaturePrintTimes(); - var total_seconds = parseInt(base.printDuration.getDisplayString(UM.DurationFormat.Seconds)) - - // A message is created and displayed when the user hover the time label - var tooltip_html = "%1
".arg(catalog.i18nc("@tooltip", "Time specification")); - for(var feature in print_time) - { - if(!print_time[feature].isTotalDurationZero) - { - tooltip_html += "" + - "".arg(print_time[feature].getDisplayString(UM.DurationFormat.ISO8601).slice(0,-3)) + - "".arg(Math.round(100 * parseInt(print_time[feature].getDisplayString(UM.DurationFormat.Seconds)) / total_seconds)) + - ""; - } - } - tooltip_html += "
" + feature + ":  %1  %1%
"; - base.showTooltip(parent, Qt.point(-UM.Theme.getSize("thick_margin").width, 0), tooltip_html); - } - } - onExited: - { - base.hideTooltip(); - } + top: parent.top + topMargin: UM.Theme.getSize("thick_margin").height + left: parent.left + leftMargin: UM.Theme.getSize("thick_margin").width } + sourceComponent: backendStatusDone ? outputProcessWidget : sliceProcessWidget } - Label + Behavior on height { NumberAnimation { duration: 100 } } + + Component { - function formatRow(items) - { - var row_html = ""; - for(var item = 0; item < items.length; item++) - { - if (item == 0) - { - row_html += "%1".arg(items[item]); - } - else - { - row_html += "  %1".arg(items[item]); - } - } - row_html += ""; - return row_html; - } - - function getSpecsData() - { - var lengths = []; - var total_length = 0; - var weights = []; - var total_weight = 0; - var costs = []; - var total_cost = 0; - var some_costs_known = false; - var names = []; - if(base.printMaterialLengths) - { - for(var index = 0; index < base.printMaterialLengths.length; index++) - { - if(base.printMaterialLengths[index] > 0) - { - names.push(base.printMaterialNames[index]); - lengths.push(base.printMaterialLengths[index].toFixed(2)); - weights.push(String(Math.round(base.printMaterialWeights[index]))); - var cost = base.printMaterialCosts[index] == undefined ? 0 : base.printMaterialCosts[index].toFixed(2); - costs.push(cost); - if(cost > 0) - { - some_costs_known = true; - } - - total_length += base.printMaterialLengths[index]; - total_weight += base.printMaterialWeights[index]; - total_cost += base.printMaterialCosts[index]; - } - } - } - if(lengths.length == 0) - { - lengths = ["0.00"]; - weights = ["0"]; - costs = ["0.00"]; - } - - var tooltip_html = "%1
".arg(catalog.i18nc("@label", "Cost specification")); - for(var index = 0; index < lengths.length; index++) - { - tooltip_html += formatRow([ - "%1:".arg(names[index]), - catalog.i18nc("@label m for meter", "%1m").arg(lengths[index]), - catalog.i18nc("@label g for grams", "%1g").arg(weights[index]), - "%1 %2".arg(UM.Preferences.getValue("cura/currency")).arg(costs[index]), - ]); - } - if(lengths.length > 1) - { - tooltip_html += formatRow([ - catalog.i18nc("@label", "Total:"), - catalog.i18nc("@label m for meter", "%1m").arg(total_length.toFixed(2)), - catalog.i18nc("@label g for grams", "%1g").arg(Math.round(total_weight)), - "%1 %2".arg(UM.Preferences.getValue("cura/currency")).arg(total_cost.toFixed(2)), - ]); - } - tooltip_html += "
"; - tooltipText = tooltip_html; - - return tooltipText - } - - id: costSpec - - anchors.left: parent.left - anchors.bottom: parent.bottom - anchors.bottomMargin: UM.Theme.getSize("thick_margin").height - anchors.leftMargin: UM.Theme.getSize("thick_margin").width - - font: UM.Theme.getFont("very_small") - renderType: Text.NativeRendering - color: UM.Theme.getColor("text_subtext") - elide: Text.ElideMiddle - width: parent.width - property string tooltipText - text: - { - var lengths = []; - var weights = []; - var costs = []; - var someCostsKnown = false; - if(base.printMaterialLengths) - { - for(var index = 0; index < base.printMaterialLengths.length; index++) - { - if(base.printMaterialLengths[index] > 0) - { - lengths.push(base.printMaterialLengths[index].toFixed(2)); - weights.push(String(Math.round(base.printMaterialWeights[index]))); - var cost = base.printMaterialCosts[index] == undefined ? 0 : base.printMaterialCosts[index].toFixed(2); - costs.push(cost); - if(cost > 0) - { - someCostsKnown = true; - } - } - } - } - if(lengths.length == 0) - { - lengths = ["0.00"]; - weights = ["0"]; - costs = ["0.00"]; - } - var result = lengths.join(" + ") + "m / ~ " + weights.join(" + ") + "g"; - if(someCostsKnown) - { - result += " / ~ " + costs.join(" + ") + " " + UM.Preferences.getValue("cura/currency"); - } - return result; - } - - MouseArea - { - id: costSpecMouseArea - anchors.fill: parent - hoverEnabled: true - - onEntered: - { - - if(base.printDuration.valid && !base.printDuration.isTotalDurationZero) - { - var show_data = costSpec.getSpecsData() - - base.showTooltip(parent, Qt.point(-UM.Theme.getSize("thick_margin").width, 0), show_data); - } - } - onExited: - { - base.hideTooltip(); - } - } + id: sliceProcessWidget + SliceProcessWidget { } } - SaveButton + Component { - id: saveButton - width: parent.width - height: 100 * screenScaleFactor - anchors.bottom: parent.bottom + id: outputProcessWidget + OutputProcessWidget { } } } \ No newline at end of file diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 3e2515cb3e..1f4d71e460 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -312,17 +312,16 @@ UM.MainWindow { anchors.right: parent.right anchors.bottom: parent.bottom - width: UM.Theme.getSize("action_panel_widget").width anchors.rightMargin: UM.Theme.getSize("thick_margin").width anchors.bottomMargin: UM.Theme.getSize("thick_margin").height - onShowTooltip: - { - base.showTooltip(item, location, text) - } - onHideTooltip: - { - base.hideTooltip() - } +// onShowTooltip: +// { +// base.showTooltip(item, location, text) +// } +// onHideTooltip: +// { +// base.hideTooltip() +// } } Loader diff --git a/resources/qml/OutputProcessWidget.qml b/resources/qml/OutputProcessWidget.qml new file mode 100644 index 0000000000..612e4f286f --- /dev/null +++ b/resources/qml/OutputProcessWidget.qml @@ -0,0 +1,72 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 2.1 +import QtQuick.Layouts 1.3 + +import UM 1.1 as UM + +Button +{ + id: button + property alias cursorShape: mouseArea.cursorShape + property alias iconSource: buttonIcon.source + property alias textFont: buttonText.font + property alias cornerRadius: backgroundRect.radius + property var color: UM.Theme.getColor("primary") + property var hoverColor: UM.Theme.getColor("primary_hover") + property var disabledColor: color + property var textColor: UM.Theme.getColor("button_text") + property var textHoverColor: UM.Theme.getColor("button_text_hover") + property var textDisabledColor: textColor + property var outlineColor: color + property var outlineHoverColor: hoverColor + property var outlineDisabledColor: outlineColor + + contentItem: Row + { + UM.RecolorImage + { + id: buttonIcon + source: "" + height: Math.round(0.6 * parent.height) + width: height + sourceSize.width: width + sourceSize.height: height + color: button.hovered ? button.textHoverColor : button.textColor + visible: source != "" + anchors.verticalCenter: parent.verticalCenter + Behavior on color { ColorAnimation { duration: 50 } } + } + + Label + { + id: buttonText + text: "Preview" + color: button.enabled ? (button.hovered ? button.textHoverColor : button.textColor): button.textDisabledColor + font: UM.Theme.getFont("action_button") + visible: text != "" + renderType: Text.NativeRendering + anchors.verticalCenter: parent.verticalCenter + } + } + + background: Rectangle + { + id: backgroundRect + color: button.enabled ? (button.hovered ? button.hoverColor : button.color) : button.disabledColor + radius: UM.Theme.getSize("action_button_radius").width + border.width: UM.Theme.getSize("default_lining").width + border.color: button.enabled ? (button.hovered ? button.outlineHoverColor : button.outlineColor) : button.outlineDisabledColor + Behavior on color { ColorAnimation { duration: 50 } } + } + + MouseArea + { + id: mouseArea + anchors.fill: parent + onPressed: mouse.accepted = false + hoverEnabled: true + } +} diff --git a/resources/qml/SliceProcessWidget.qml b/resources/qml/SliceProcessWidget.qml new file mode 100644 index 0000000000..67005cb133 --- /dev/null +++ b/resources/qml/SliceProcessWidget.qml @@ -0,0 +1,62 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 2.1 +import QtQuick.Layouts 1.3 + +import UM 1.1 as UM +import Cura 1.0 as Cura + +Column +{ + id: widget + + spacing: UM.Theme.getSize("default_margin").height + + UM.I18nCatalog + { + id: catalog + name: "cura" + } + + property real progress: UM.Backend.progress + property int backendState: UM.Backend.state + + Rectangle + { + id: progressBar + width: parent.width + height: UM.Theme.getSize("progressbar").height + visible: widget.backendState == 2 + radius: UM.Theme.getSize("progressbar_radius").width + color: UM.Theme.getColor("progressbar_background") + + Rectangle + { + width: Math.max(parent.width * base.progress) + height: parent.height + radius: UM.Theme.getSize("progressbar_radius").width + color: UM.Theme.getColor("progressbar_control") + } + } + + Cura.ActionButton + { + id: prepareButton + width: UM.Theme.getSize("action_panel_button").width + height: UM.Theme.getSize("action_panel_button").height + text: widget.backendState == 1 ? catalog.i18nc("@button", "Prepare") : catalog.i18nc("@button", "Cancel") + onClicked: + { + if ([1, 5].indexOf(widget.backendState) != -1) + { + CuraApplication.backend.forceSlice() + } + else + { + CuraApplication.backend.stopSlicing() + } + } + } +} diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 29e3774e1f..e840a08a75 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -378,10 +378,14 @@ "configuration_selector_mode_tabs": [0.0, 3.0], "action_panel_widget": [35.0, 0.0], + "action_panel_button": [15.0, 3.0], + "machine_selector_widget": [28.0, 4.5], "views_selector": [0.0, 4.0], + "default_radius": [0.25, 0.25], + "wide_lining": [0.5, 0.5], "thick_lining": [0.2, 0.2], "default_lining": [0.08, 0.08], From e486bf8b440dd3a392779821e4720f53537f66c3 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 29 Oct 2018 15:55:16 +0100 Subject: [PATCH 0098/1240] Change the primary color to a slightly different kind of blue. Contributes to CURA-5786. --- resources/themes/cura-light/theme.json | 48 +++++++++++++------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index e840a08a75..cbd5b29e87 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -75,7 +75,7 @@ "lining": [192, 193, 194, 255], "viewport_overlay": [0, 0, 0, 192], - "primary": [12, 169, 227, 255], + "primary": [50, 130, 255, 255], "primary_hover": [48, 182, 231, 255], "primary_text": [255, 255, 255, 255], "border": [127, 127, 127, 255], @@ -106,10 +106,10 @@ "text": [0, 0, 0, 255], "text_detail": [174, 174, 174, 128], - "text_link": [12, 169, 227, 255], + "text_link": [50, 130, 255, 255], "text_inactive": [174, 174, 174, 255], "text_hover": [70, 84, 113, 255], - "text_pressed": [12, 169, 227, 255], + "text_pressed": [50, 130, 255, 255], "text_subtext": [0, 0, 0, 255], "text_medium": [128, 128, 128, 255], "text_emphasis": [255, 255, 255, 255], @@ -152,16 +152,16 @@ "action_button_border": [127, 127, 127, 255], "action_button_hovered": [255, 255, 255, 255], "action_button_hovered_text": [31, 36, 39, 255], - "action_button_hovered_border": [12, 169, 227, 255], + "action_button_hovered_border": [50, 130, 255, 255], "action_button_active": [255, 255, 255, 255], "action_button_active_text": [0, 0, 0, 255], - "action_button_active_border": [12, 169, 227, 255], + "action_button_active_border": [50, 130, 255, 255], "action_button_disabled": [245, 245, 245, 255], "action_button_disabled_text": [127, 127, 127, 255], "action_button_disabled_border": [245, 245, 245, 255], - "print_button_ready": [12, 169, 227, 255], - "print_button_ready_border": [12, 169, 227, 255], + "print_button_ready": [50, 130, 255, 255], + "print_button_ready_border": [50, 130, 255, 255], "print_button_ready_text": [255, 255, 255, 255], "print_button_ready_hovered": [30, 186, 245, 243], "print_button_ready_hovered_border": [30, 186, 245, 243], @@ -193,7 +193,7 @@ "setting_control_selected": [31, 36, 39, 255], "setting_control_highlight": [255, 255, 255, 255], "setting_control_border": [127, 127, 127, 255], - "setting_control_border_highlight": [12, 169, 227, 255], + "setting_control_border_highlight": [50, 130, 255, 255], "setting_control_text": [27, 27, 27, 255], "setting_control_depth_line": [127, 127, 127, 255], "setting_control_button": [127, 127, 127, 255], @@ -231,18 +231,18 @@ "checkbox": [255, 255, 255, 255], "checkbox_hover": [255, 255, 255, 255], "checkbox_border": [64, 69, 72, 255], - "checkbox_border_hover": [12, 169, 227, 255], + "checkbox_border_hover": [50, 130, 255, 255], "checkbox_mark": [119, 122, 124, 255], "checkbox_text": [27, 27, 27, 255], "mode_switch": [255, 255, 255, 255], "mode_switch_hover": [255, 255, 255, 255], "mode_switch_border": [127, 127, 127, 255], - "mode_switch_border_hover": [12, 169, 227, 255], + "mode_switch_border_hover": [50, 130, 255, 255], "mode_switch_handle": [31, 36, 39, 255], "mode_switch_text": [31, 36, 39, 255], "mode_switch_text_hover": [31, 36, 39, 255], - "mode_switch_text_checked": [12, 169, 227, 255], + "mode_switch_text_checked": [50, 130, 255, 255], "tooltip": [68, 192, 255, 255], "tooltip_text": [255, 255, 255, 255], @@ -253,9 +253,9 @@ "message_shadow": [0, 0, 0, 120], "message_border": [127, 127, 127, 255], "message_text": [0, 0, 0, 255], - "message_button": [12, 169, 227, 255], - "message_button_hover": [12, 169, 227, 255], - "message_button_active": [12, 169, 227, 255], + "message_button": [50, 130, 255, 255], + "message_button_hover": [50, 130, 255, 255], + "message_button_active": [50, 130, 255, 255], "message_button_text": [255, 255, 255, 255], "message_button_text_hover": [255, 255, 255, 255], "message_button_text_active": [255, 255, 255, 255], @@ -266,7 +266,7 @@ "status_offline": [0, 0, 0, 255], "status_ready": [0, 205, 0, 255], - "status_busy": [12, 169, 227, 255], + "status_busy": [50, 130, 255, 255], "status_paused": [255, 140, 0, 255], "status_stopped": [236, 82, 80, 255], "status_unknown": [127, 127, 127, 255], @@ -278,7 +278,7 @@ "all_axis": [255, 255, 255, 255], "viewport_background": [245, 245, 245, 255], - "volume_outline": [12, 169, 227, 255], + "volume_outline": [50, 130, 255, 255], "buildplate": [244, 244, 244, 255], "buildplate_grid": [129, 131, 134, 255], "buildplate_grid_minor": [230, 230, 231, 255], @@ -291,7 +291,7 @@ "model_overhang": [255, 0, 0, 255], "model_unslicable": [122, 122, 122, 255], "model_unslicable_alt": [172, 172, 127, 255], - "model_selection_outline": [12, 169, 227, 255], + "model_selection_outline": [50, 130, 255, 255], "model_non_printing": [122, 122, 122, 255], "xray": [26, 26, 62, 255], @@ -317,12 +317,12 @@ "configuration_item_text_active": [0, 0, 0, 255], "configuration_item_border": [127, 127, 127, 255], "configuration_item_border_active": [12, 169, 227, 32], - "configuration_item_border_hover": [12, 169, 227, 255], + "configuration_item_border_hover": [50, 130, 255, 255], - "tab_status_connected": [12, 169, 227, 255], + "tab_status_connected": [50, 130, 255, 255], "tab_status_disconnected": [200, 200, 200, 255], - "printer_config_matched": [12, 169, 227, 255], + "printer_config_matched": [50, 130, 255, 255], "printer_config_mismatch": [127, 127, 127, 255], "toolbox_header_button_text_active": [0, 0, 0, 255], @@ -346,12 +346,12 @@ "monitor_pill_background": [245, 245, 245, 255], "monitor_placeholder_image": [230, 230, 230, 255], "monitor_printer_icon_inactive": [154, 154, 154, 255], - "monitor_printer_icon": [12, 169, 227, 255], + "monitor_printer_icon": [50, 130, 255, 255], "monitor_progress_background_text": [0,0,0,255], "monitor_progress_background": [245, 245, 245, 255], "monitor_progress_fill_inactive": [154, 154, 154, 255], "monitor_progress_fill_text": [255,255,255,255], - "monitor_progress_fill": [12, 169, 227, 255], + "monitor_progress_fill": [50, 130, 255, 255], "monitor_shadow": [0, 0, 0, 63], "monitor_skeleton_fill": [245, 245, 245, 255], "monitor_skeleton_fill_dark": [216, 216, 216, 255], @@ -440,8 +440,8 @@ "tool_button_border": [1.0, 0], - "progressbar": [26.0, 0.4], - "progressbar_radius": [0, 0], + "progressbar": [26.0, 0.75], + "progressbar_radius": [0.15, 0.15], "progressbar_control": [8.0, 0.4], "scrollbar": [0.75, 0.5], From 0a3803d6651c66cb81fb9d2b82187fe79736aac3 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 29 Oct 2018 16:21:16 +0100 Subject: [PATCH 0099/1240] Add CuraView, which does something similar to CuraStage So instead of relying on strange activeViewProxy, it's up to the CuraView to provide a set of components. These can subsequently be used by the active stage again. CURA-5829 --- cura/CuraView.py | 24 ++++++++++++++++++++++++ plugins/PreviewStage/PreviewMenu.qml | 2 +- plugins/SimulationView/SimulationView.py | 15 +++++++++++---- plugins/SimulationView/__init__.py | 3 +++ 4 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 cura/CuraView.py diff --git a/cura/CuraView.py b/cura/CuraView.py new file mode 100644 index 0000000000..978c651b43 --- /dev/null +++ b/cura/CuraView.py @@ -0,0 +1,24 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from PyQt5.QtCore import pyqtProperty, QUrl + +from UM.View.View import View + + +# Since Cura has a few pre-defined "space claims" for the locations of certain components, we've provided some structure +# to indicate this. +# MainComponent works in the same way the MainComponent of a stage. +# the stageMenuComponent returns an item that should be used somehwere in the stage menu. It's up to the active stage +# to actually do something with this. +class CuraView(View): + def __init__(self, parent = None) -> None: + super().__init__(parent) + + @pyqtProperty(QUrl, constant = True) + def mainComponent(self) -> QUrl: + return self.getDisplayComponent("main") + + @pyqtProperty(QUrl, constant = True) + def stageMenuComponent(self) -> QUrl: + return self.getDisplayComponent("menu") \ No newline at end of file diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index ae875fb0a2..5ed0e697a9 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -78,7 +78,7 @@ Item height: childrenRect.height width: childrenRect.width - source: UM.ActiveView.valid ? UM.ActiveView.activeViewPanel : "" + source: UM.Controller.activeView != null && UM.Controller.activeView.stageMenuComponent != null ? UM.Controller.activeView.stageMenuComponent : "" } Cura.PrintSetupSelector diff --git a/plugins/SimulationView/SimulationView.py b/plugins/SimulationView/SimulationView.py index 0ae8b4d9e4..0ef08b22d7 100644 --- a/plugins/SimulationView/SimulationView.py +++ b/plugins/SimulationView/SimulationView.py @@ -26,8 +26,8 @@ from UM.View.GL.OpenGL import OpenGL from UM.View.GL.OpenGLContext import OpenGLContext from UM.View.GL.ShaderProgram import ShaderProgram -from UM.View.View import View from UM.i18n import i18nCatalog +from cura.CuraView import CuraView from cura.Scene.ConvexHullNode import ConvexHullNode from cura.CuraApplication import CuraApplication @@ -48,15 +48,15 @@ catalog = i18nCatalog("cura") ## View used to display g-code paths. -class SimulationView(View): +class SimulationView(CuraView): # Must match SimulationView.qml LAYER_VIEW_TYPE_MATERIAL_TYPE = 0 LAYER_VIEW_TYPE_LINE_TYPE = 1 LAYER_VIEW_TYPE_FEEDRATE = 2 LAYER_VIEW_TYPE_THICKNESS = 3 - def __init__(self) -> None: - super().__init__() + def __init__(self, parent = None) -> None: + super().__init__(parent) self._max_layers = 0 self._current_layer_num = 0 @@ -113,6 +113,13 @@ class SimulationView(View): self._wireprint_warning_message = Message(catalog.i18nc("@info:status", "Cura does not accurately display layers when Wire Printing is enabled"), title = catalog.i18nc("@info:title", "Simulation View")) + Application.getInstance().engineCreatedSignal.connect(self._onEngineCreated) + + def _onEngineCreated(self) -> None: + menu_component_path = os.path.join(PluginRegistry.getInstance().getPluginPath("SimulationView"), + "SimulationView.qml") + self.addDisplayComponent("menu", menu_component_path) + def _evaluateCompatibilityMode(self) -> bool: return OpenGLContext.isLegacyOpenGL() or bool(Application.getInstance().getPreferences().getValue("view/force_layer_view_compatibility_mode")) diff --git a/plugins/SimulationView/__init__.py b/plugins/SimulationView/__init__.py index 360fdc1de9..3f89ee490f 100644 --- a/plugins/SimulationView/__init__.py +++ b/plugins/SimulationView/__init__.py @@ -8,6 +8,7 @@ from . import SimulationViewProxy, SimulationView catalog = i18nCatalog("cura") + def getMetaData(): return { "view": { @@ -17,9 +18,11 @@ def getMetaData(): } } + def createSimulationViewProxy(engine, script_engine): return SimulationViewProxy.SimulationViewProxy() + def register(app): simulation_view = SimulationView.SimulationView() qmlRegisterSingletonType(SimulationViewProxy.SimulationViewProxy, "UM", 1, 0, "SimulationView", simulation_view.getProxy) From aa75b64b5bf121c2908ebdf550e89969945e8fb0 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 30 Oct 2018 09:48:56 +0100 Subject: [PATCH 0100/1240] Use the lining color to the default ouline color for the account widget. Contributes to CURA-5784. --- resources/qml/Account/AccountDetails.qml | 2 +- resources/qml/Account/AccountWidget.qml | 2 +- resources/themes/cura-light/theme.json | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/resources/qml/Account/AccountDetails.qml b/resources/qml/Account/AccountDetails.qml index a88fb77956..c723ca20fb 100644 --- a/resources/qml/Account/AccountDetails.qml +++ b/resources/qml/Account/AccountDetails.qml @@ -22,7 +22,7 @@ Column height: UM.Theme.getSize("avatar_image").height anchors.horizontalCenter: parent.horizontalCenter source: loggedIn ? profile["profile_image_url"] : UM.Theme.getImage("avatar_no_user") - outlineColor: loggedIn ? UM.Theme.getColor("account_widget_outline_active") : UM.Theme.getColor("account_widget_outline_inactive") + outlineColor: loggedIn ? UM.Theme.getColor("account_widget_outline_active") : UM.Theme.getColor("lining") } Label diff --git a/resources/qml/Account/AccountWidget.qml b/resources/qml/Account/AccountWidget.qml index 46092e4153..cdae457ac8 100644 --- a/resources/qml/Account/AccountWidget.qml +++ b/resources/qml/Account/AccountWidget.qml @@ -26,7 +26,7 @@ Button anchors.horizontalCenter: accountWidget.horizontalCenter source: loggedIn ? profile["profile_image_url"] : UM.Theme.getImage("avatar_no_user") - outlineColor: loggedIn ? UM.Theme.getColor("account_widget_outline_active") : UM.Theme.getColor("account_widget_outline_inactive") + outlineColor: loggedIn ? UM.Theme.getColor("account_widget_outline_active") : UM.Theme.getColor("lining") } onClicked: popup.opened ? popup.close() : popup.open() diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 29e3774e1f..40c776ecf1 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -97,7 +97,6 @@ "main_window_header_secondary_button_outline_hovered": [255, 255, 255, 255], "account_widget_outline_active": [70, 66, 126, 255], - "account_widget_outline_inactive": [229, 229, 229, 255], "machine_selector_bar": [31, 36, 39, 255], "machine_selector_active": [68, 72, 75, 255], From b6ea0e638578daa32fcf383335b112e8efcce7e8 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 30 Oct 2018 10:38:53 +0100 Subject: [PATCH 0101/1240] Make the height of the stage buttons themable. Now the height has to be 28px as indicated by the UXers. Contributes to CURA-5784. --- resources/qml/MainWindow/MainWindowHeader.qml | 2 +- resources/themes/cura-light/theme.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/MainWindow/MainWindowHeader.qml b/resources/qml/MainWindow/MainWindowHeader.qml index 92d9d8ee26..3f088e782f 100644 --- a/resources/qml/MainWindow/MainWindowHeader.qml +++ b/resources/qml/MainWindow/MainWindowHeader.qml @@ -60,7 +60,7 @@ Rectangle anchors.verticalCenter: parent.verticalCenter exclusiveGroup: mainWindowHeaderMenuGroup style: UM.Theme.styles.main_window_header_tab - height: Math.round(0.56 * UM.Theme.getSize("main_window_header").height) + height: UM.Theme.getSize("main_window_header_button").height onClicked: UM.Controller.setActiveStage(model.id) iconSource: model.stage.iconSource diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 40c776ecf1..e8de7c98ac 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -361,7 +361,7 @@ "window_minimum_size": [106, 66], "main_window_header": [0.0, 4.5], - "main_window_header_button": [8, 4], + "main_window_header_button": [8, 2.35], "main_window_header_button_icon": [1.2, 1.2], "stage_menu": [0.0, 4.5], From 79ed15aee19a757a3c09cd8ad0c634b57d34a527 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 30 Oct 2018 11:52:08 +0100 Subject: [PATCH 0102/1240] Add the label and the icon indicating when Cura is unable to slice. Contributes to CURA-5786. --- resources/qml/ActionPanelWidget.qml | 1 + resources/qml/SliceProcessWidget.qml | 87 +++++++++++++++++++++++--- resources/themes/cura-light/theme.json | 3 +- 3 files changed, 80 insertions(+), 11 deletions(-) diff --git a/resources/qml/ActionPanelWidget.qml b/resources/qml/ActionPanelWidget.qml index d1fe999731..b4a9b4e4ec 100644 --- a/resources/qml/ActionPanelWidget.qml +++ b/resources/qml/ActionPanelWidget.qml @@ -37,6 +37,7 @@ Rectangle } Behavior on height { NumberAnimation { duration: 100 } } + Behavior on width { NumberAnimation { duration: 100 } } Component { diff --git a/resources/qml/SliceProcessWidget.qml b/resources/qml/SliceProcessWidget.qml index 67005cb133..561e38c372 100644 --- a/resources/qml/SliceProcessWidget.qml +++ b/resources/qml/SliceProcessWidget.qml @@ -12,7 +12,9 @@ Column { id: widget - spacing: UM.Theme.getSize("default_margin").height + width: UM.Theme.getSize("action_panel_button").width + + spacing: UM.Theme.getSize("thin_margin").height UM.I18nCatalog { @@ -23,30 +25,84 @@ Column property real progress: UM.Backend.progress property int backendState: UM.Backend.state - Rectangle + Item + { + id: message + width: parent.width + height: childrenRect.height + visible: widget.backendState == 4 + + UM.RecolorImage + { + id: warningImage + + anchors.left: parent.left + + source: UM.Theme.getIcon("warning") + width: UM.Theme.getSize("section_icon").width + height: UM.Theme.getSize("section_icon").height + + sourceSize.width: width + sourceSize.height: height + + color: UM.Theme.getColor("warning") + } + + Label + { + id: unableToSliceLabel + anchors.left: warningImage.right + anchors.leftMargin: UM.Theme.getSize("thin_margin").width + text: catalog.i18nc("@label:PrintjobStatus", "Unable to Slice") + color: UM.Theme.getColor("warning") + font: UM.Theme.getFont("very_small") + } + } + + // Progress bar, only visible when the backend is in the process of slice the printjob + ProgressBar { id: progressBar width: parent.width height: UM.Theme.getSize("progressbar").height + value: progress visible: widget.backendState == 2 - radius: UM.Theme.getSize("progressbar_radius").width - color: UM.Theme.getColor("progressbar_background") - Rectangle + background: Rectangle { - width: Math.max(parent.width * base.progress) - height: parent.height + anchors.fill: parent radius: UM.Theme.getSize("progressbar_radius").width - color: UM.Theme.getColor("progressbar_control") + color: UM.Theme.getColor("progressbar_background") + } + + contentItem: Item + { + anchors.fill: parent + Rectangle + { + width: progressBar.visualPosition * parent.width + height: parent.height + radius: UM.Theme.getSize("progressbar_radius").width + color: UM.Theme.getColor("progressbar_control") + } } } Cura.ActionButton { id: prepareButton - width: UM.Theme.getSize("action_panel_button").width + width: parent.width height: UM.Theme.getSize("action_panel_button").height - text: widget.backendState == 1 ? catalog.i18nc("@button", "Prepare") : catalog.i18nc("@button", "Cancel") + text: autoSlice ? catalog.i18nc("@button", "Auto slicing...") : (widget.backendState == 1 ? catalog.i18nc("@button", "Slice") : catalog.i18nc("@button", "Cancel")) + enabled: !autoSlice + + // Get the current value from the preferences + property bool autoSlice: UM.Preferences.getValue("general/auto_slice") + + disabledColor: "transparent" + textDisabledColor: UM.Theme.getColor("primary") + outlineDisabledColor: "transparent" + onClicked: { if ([1, 5].indexOf(widget.backendState) != -1) @@ -59,4 +115,15 @@ Column } } } + + // React when the user changes the preference of having the auto slice enabled + Connections + { + target: UM.Preferences + onPreferenceChanged: + { + var autoSlice = UM.Preferences.getValue("general/auto_slice") + prepareButton.autoSlice = autoSlice + } + } } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index d695ba12cf..1473a75eec 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -116,6 +116,7 @@ "text_scene_hover": [70, 84, 113, 255], "error": [255, 140, 0, 255], + "warning": [255, 190, 35, 255], "button": [31, 36, 39, 255], "button_hover": [68, 72, 75, 255], @@ -210,7 +211,7 @@ "material_compatibility_warning": [0, 0, 0, 255], "progressbar_background": [245, 245, 245, 255], - "progressbar_control": [31, 36, 39, 255], + "progressbar_control": [50, 130, 255, 255], "slider_groove": [245, 245, 245, 255], "slider_groove_border": [127, 127, 127, 255], From 45af4eec907a0113145cc15f14b11fa1b5185a51 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 30 Oct 2018 12:08:17 +0100 Subject: [PATCH 0103/1240] Add Shortcut action Ctrl+P to slice or stop slicing. Contributes to CURA-5786. --- resources/qml/SliceProcessWidget.qml | 58 +++++++++++++++++++++------- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/resources/qml/SliceProcessWidget.qml b/resources/qml/SliceProcessWidget.qml index 561e38c372..c9ff7178fd 100644 --- a/resources/qml/SliceProcessWidget.qml +++ b/resources/qml/SliceProcessWidget.qml @@ -4,6 +4,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.1 import QtQuick.Layouts 1.3 +import QtQuick.Controls 1.4 as Controls1 import UM 1.1 as UM import Cura 1.0 as Cura @@ -25,12 +26,24 @@ Column property real progress: UM.Backend.progress property int backendState: UM.Backend.state + function sliceOrStopSlicing() + { + if ([1, 5].indexOf(widget.backendState) != -1) // == BackendState.NotStarted or BackendState.Disabled + { + CuraApplication.backend.forceSlice() + } + else + { + CuraApplication.backend.stopSlicing() + } + } + Item { id: message width: parent.width height: childrenRect.height - visible: widget.backendState == 4 + visible: widget.backendState == 4 // == BackendState.Error UM.RecolorImage { @@ -66,7 +79,7 @@ Column width: parent.width height: UM.Theme.getSize("progressbar").height value: progress - visible: widget.backendState == 2 + visible: widget.backendState == 2 // == BackendState.Processing background: Rectangle { @@ -93,7 +106,21 @@ Column id: prepareButton width: parent.width height: UM.Theme.getSize("action_panel_button").height - text: autoSlice ? catalog.i18nc("@button", "Auto slicing...") : (widget.backendState == 1 ? catalog.i18nc("@button", "Slice") : catalog.i18nc("@button", "Cancel")) + text: + { + if (autoSlice) + { + return catalog.i18nc("@button", "Auto slicing...") + } + else if ([1, 5].indexOf(widget.backendState) != -1) // == BackendState.NotStarted or BackendState.Disabled + { + return catalog.i18nc("@button", "Slice") + } + else + { + return catalog.i18nc("@button", "Cancel") + } + } enabled: !autoSlice // Get the current value from the preferences @@ -103,17 +130,7 @@ Column textDisabledColor: UM.Theme.getColor("primary") outlineDisabledColor: "transparent" - onClicked: - { - if ([1, 5].indexOf(widget.backendState) != -1) - { - CuraApplication.backend.forceSlice() - } - else - { - CuraApplication.backend.stopSlicing() - } - } + onClicked: sliceOrStopSlicing() } // React when the user changes the preference of having the auto slice enabled @@ -126,4 +143,17 @@ Column prepareButton.autoSlice = autoSlice } } + + // Shortcut for "slice/stop" + Controls1.Action + { + shortcut: "Ctrl+P" + onTriggered: + { + if (prepareButton.enabled) + { + sliceOrStopSlicing() + } + } + } } From 00d75cd4d600866933147e9a0ae75e790ebf95e6 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 30 Oct 2018 12:28:51 +0100 Subject: [PATCH 0104/1240] Tweak the colors of the disabled state. Contributes to CURA-5786. --- resources/qml/SliceProcessWidget.qml | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/resources/qml/SliceProcessWidget.qml b/resources/qml/SliceProcessWidget.qml index c9ff7178fd..98f708a9b0 100644 --- a/resources/qml/SliceProcessWidget.qml +++ b/resources/qml/SliceProcessWidget.qml @@ -108,27 +108,24 @@ Column height: UM.Theme.getSize("action_panel_button").height text: { + if ([1, 4, 5].indexOf(widget.backendState) != -1) // == BackendState.NotStarted or BackendState.Error or BackendState.Disabled + { + return catalog.i18nc("@button", "Slice") + } if (autoSlice) { return catalog.i18nc("@button", "Auto slicing...") } - else if ([1, 5].indexOf(widget.backendState) != -1) // == BackendState.NotStarted or BackendState.Disabled - { - return catalog.i18nc("@button", "Slice") - } - else - { - return catalog.i18nc("@button", "Cancel") - } + return catalog.i18nc("@button", "Cancel") } - enabled: !autoSlice + enabled: !autoSlice && ([1, 2].indexOf(widget.backendState) != -1) // Get the current value from the preferences property bool autoSlice: UM.Preferences.getValue("general/auto_slice") - disabledColor: "transparent" - textDisabledColor: UM.Theme.getColor("primary") - outlineDisabledColor: "transparent" + disabledColor: ([1, 2].indexOf(widget.backendState) == -1) ? UM.Theme.getColor("action_button_disabled") : "transparent" + textDisabledColor: ([1, 2].indexOf(widget.backendState) == -1) ? UM.Theme.getColor("action_button_disabled_text") : UM.Theme.getColor("primary") + outlineDisabledColor: ([1, 2].indexOf(widget.backendState) == -1) ? UM.Theme.getColor("action_button_disabled_border") : "transparent" onClicked: sliceOrStopSlicing() } From 027bf204cd1ff6dba53b9e386e183d2251718020 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 30 Oct 2018 15:12:03 +0100 Subject: [PATCH 0105/1240] Use the BackendState enumeration in QML to not use integers but use the enumeration literals. Contributes to CURA-5786. --- resources/qml/SliceProcessWidget.qml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/resources/qml/SliceProcessWidget.qml b/resources/qml/SliceProcessWidget.qml index 98f708a9b0..b9c0dc39f9 100644 --- a/resources/qml/SliceProcessWidget.qml +++ b/resources/qml/SliceProcessWidget.qml @@ -28,7 +28,7 @@ Column function sliceOrStopSlicing() { - if ([1, 5].indexOf(widget.backendState) != -1) // == BackendState.NotStarted or BackendState.Disabled + if ([UM.Backend.NotStarted, UM.Backend.Disabled].indexOf(widget.backendState) != -1) { CuraApplication.backend.forceSlice() } @@ -43,7 +43,7 @@ Column id: message width: parent.width height: childrenRect.height - visible: widget.backendState == 4 // == BackendState.Error + visible: widget.backendState == UM.Backend.Error UM.RecolorImage { @@ -79,7 +79,7 @@ Column width: parent.width height: UM.Theme.getSize("progressbar").height value: progress - visible: widget.backendState == 2 // == BackendState.Processing + visible: widget.backendState == UM.Backend.Processing background: Rectangle { @@ -108,7 +108,7 @@ Column height: UM.Theme.getSize("action_panel_button").height text: { - if ([1, 4, 5].indexOf(widget.backendState) != -1) // == BackendState.NotStarted or BackendState.Error or BackendState.Disabled + if ([UM.Backend.NotStarted, UM.Backend.Error, UM.Backend.Disabled].indexOf(widget.backendState) != -1) { return catalog.i18nc("@button", "Slice") } @@ -118,14 +118,16 @@ Column } return catalog.i18nc("@button", "Cancel") } - enabled: !autoSlice && ([1, 2].indexOf(widget.backendState) != -1) + enabled: !autoSlice && !disabledSlice // Get the current value from the preferences property bool autoSlice: UM.Preferences.getValue("general/auto_slice") + // Disable the slice process when + property bool disabledSlice: [UM.Backend.Done, UM.Backend.Error, UM.Backend.Disabled].indexOf(widget.backendState) != -1 - disabledColor: ([1, 2].indexOf(widget.backendState) == -1) ? UM.Theme.getColor("action_button_disabled") : "transparent" - textDisabledColor: ([1, 2].indexOf(widget.backendState) == -1) ? UM.Theme.getColor("action_button_disabled_text") : UM.Theme.getColor("primary") - outlineDisabledColor: ([1, 2].indexOf(widget.backendState) == -1) ? UM.Theme.getColor("action_button_disabled_border") : "transparent" + disabledColor: disabledSlice ? UM.Theme.getColor("action_button_disabled") : "transparent" + textDisabledColor: disabledSlice ? UM.Theme.getColor("action_button_disabled_text") : UM.Theme.getColor("primary") + outlineDisabledColor: disabledSlice ? UM.Theme.getColor("action_button_disabled_border") : "transparent" onClicked: sliceOrStopSlicing() } From eabd7c6b5eac0fa4fd63b2f8bb9177dd115b0767 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 30 Oct 2018 17:07:07 +0100 Subject: [PATCH 0106/1240] Start mocking up the panel the user gets after slicing. Also the IconLabel component was created with the aim to be reused in several places. Contributes to CURA-5786. --- resources/qml/ActionPanelWidget.qml | 2 +- resources/qml/IconLabel.qml | 48 +++++++ resources/qml/OutputProcessWidget.qml | 139 ++++++++++++-------- resources/qml/SliceProcessWidget.qml | 32 +---- resources/qml/qmldir | 3 +- resources/themes/cura-light/icons/clock.svg | 53 ++++++++ resources/themes/cura-light/icons/info.svg | 48 +++++++ resources/themes/cura-light/icons/spool.svg | 7 + 8 files changed, 251 insertions(+), 81 deletions(-) create mode 100644 resources/qml/IconLabel.qml create mode 100644 resources/themes/cura-light/icons/clock.svg create mode 100644 resources/themes/cura-light/icons/info.svg create mode 100644 resources/themes/cura-light/icons/spool.svg diff --git a/resources/qml/ActionPanelWidget.qml b/resources/qml/ActionPanelWidget.qml index b4a9b4e4ec..6c76c31f1b 100644 --- a/resources/qml/ActionPanelWidget.qml +++ b/resources/qml/ActionPanelWidget.qml @@ -21,7 +21,7 @@ Rectangle radius: UM.Theme.getSize("default_radius").width visible: CuraApplication.platformActivity - property bool backendStatusDone: UM.Backend.state == 3 + property bool backendStatusDone: UM.Backend.state == UM.Backend.Done Loader { diff --git a/resources/qml/IconLabel.qml b/resources/qml/IconLabel.qml new file mode 100644 index 0000000000..2e765e2dbc --- /dev/null +++ b/resources/qml/IconLabel.qml @@ -0,0 +1,48 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 2.1 +import QtQuick.Layouts 1.3 + +import UM 1.1 as UM + +// This item will show a label with a squared icon in the left +Item +{ + id: container + + property alias text: label.text + property alias source: icon.source + property alias color: label.color + property alias font: label.font + + height: childrenRect.height + + UM.RecolorImage + { + id: icon + + anchors.left: parent.left + + source: UM.Theme.getIcon("dot") + width: UM.Theme.getSize("section_icon").width + height: UM.Theme.getSize("section_icon").height + + sourceSize.width: width + sourceSize.height: height + + color: label.color + } + + Label + { + id: label + anchors.left: icon.right + anchors.leftMargin: UM.Theme.getSize("thin_margin").width + text: "Empty label" + color: UM.Theme.getColor("text") + font: UM.Theme.getFont("very_small") + renderType: Text.NativeRendering + } +} \ No newline at end of file diff --git a/resources/qml/OutputProcessWidget.qml b/resources/qml/OutputProcessWidget.qml index 612e4f286f..244fba57ef 100644 --- a/resources/qml/OutputProcessWidget.qml +++ b/resources/qml/OutputProcessWidget.qml @@ -4,69 +4,104 @@ import QtQuick 2.7 import QtQuick.Controls 2.1 import QtQuick.Layouts 1.3 +import QtQuick.Controls 1.4 as Controls1 import UM 1.1 as UM +import Cura 1.0 as Cura -Button +Column { - id: button - property alias cursorShape: mouseArea.cursorShape - property alias iconSource: buttonIcon.source - property alias textFont: buttonText.font - property alias cornerRadius: backgroundRect.radius - property var color: UM.Theme.getColor("primary") - property var hoverColor: UM.Theme.getColor("primary_hover") - property var disabledColor: color - property var textColor: UM.Theme.getColor("button_text") - property var textHoverColor: UM.Theme.getColor("button_text_hover") - property var textDisabledColor: textColor - property var outlineColor: color - property var outlineHoverColor: hoverColor - property var outlineDisabledColor: outlineColor + id: widget - contentItem: Row + spacing: UM.Theme.getSize("thin_margin").height + + UM.I18nCatalog { + id: catalog + name: "cura" + } + + Item + { + id: information + width: parent.width + height: childrenRect.height + + Column + { + id: timeAndCostsInformation + + anchors + { + left: parent.left + right: moreInformationIcon.left + rightMargin: UM.Theme.getSize("thin_margin").height + } + + Cura.IconLabel + { + id: estimatedTime + width: parent.width + + text: "Time" + source: UM.Theme.getIcon("clock") + font: UM.Theme.getFont("small") + } + + Cura.IconLabel + { + id: estimatedCosts + width: parent.width + + text: "Material costs" + source: UM.Theme.getIcon("spool") + font: UM.Theme.getFont("very_small") + } + } + UM.RecolorImage { - id: buttonIcon - source: "" - height: Math.round(0.6 * parent.height) - width: height + id: moreInformationIcon + + anchors + { + right: parent.right + verticalCenter: timeAndCostsInformation.verticalCenter + } + + source: UM.Theme.getIcon("info") + width: UM.Theme.getSize("section_icon").width + height: UM.Theme.getSize("section_icon").height + sourceSize.width: width sourceSize.height: height - color: button.hovered ? button.textHoverColor : button.textColor - visible: source != "" - anchors.verticalCenter: parent.verticalCenter - Behavior on color { ColorAnimation { duration: 50 } } - } - Label + color: UM.Theme.getColor("text_medium") + } + } + + Row + { + id: buttonRow + spacing: UM.Theme.getSize("default_margin").width + + Cura.ActionButton { - id: buttonText - text: "Preview" - color: button.enabled ? (button.hovered ? button.textHoverColor : button.textColor): button.textDisabledColor - font: UM.Theme.getFont("action_button") - visible: text != "" - renderType: Text.NativeRendering - anchors.verticalCenter: parent.verticalCenter + height: UM.Theme.getSize("action_panel_button").height + text: catalog.i18nc("@button", "Preview") + color: UM.Theme.getColor("secondary") + hoverColor: UM.Theme.getColor("secondary") + textColor: UM.Theme.getColor("primary") + textHoverColor: UM.Theme.getColor("text") + onClicked: console.log("Clicking preview") + } + + Cura.ActionButton + { + width: UM.Theme.getSize("account_button").width + height: UM.Theme.getSize("action_panel_button").height + text: catalog.i18nc("@button", "Save to file") + onClicked: console.log("Clicking action button") } } - - background: Rectangle - { - id: backgroundRect - color: button.enabled ? (button.hovered ? button.hoverColor : button.color) : button.disabledColor - radius: UM.Theme.getSize("action_button_radius").width - border.width: UM.Theme.getSize("default_lining").width - border.color: button.enabled ? (button.hovered ? button.outlineHoverColor : button.outlineColor) : button.outlineDisabledColor - Behavior on color { ColorAnimation { duration: 50 } } - } - - MouseArea - { - id: mouseArea - anchors.fill: parent - onPressed: mouse.accepted = false - hoverEnabled: true - } -} +} \ No newline at end of file diff --git a/resources/qml/SliceProcessWidget.qml b/resources/qml/SliceProcessWidget.qml index b9c0dc39f9..38a529b04e 100644 --- a/resources/qml/SliceProcessWidget.qml +++ b/resources/qml/SliceProcessWidget.qml @@ -38,38 +38,16 @@ Column } } - Item + Cura.IconLabel { id: message width: parent.width - height: childrenRect.height visible: widget.backendState == UM.Backend.Error - UM.RecolorImage - { - id: warningImage - - anchors.left: parent.left - - source: UM.Theme.getIcon("warning") - width: UM.Theme.getSize("section_icon").width - height: UM.Theme.getSize("section_icon").height - - sourceSize.width: width - sourceSize.height: height - - color: UM.Theme.getColor("warning") - } - - Label - { - id: unableToSliceLabel - anchors.left: warningImage.right - anchors.leftMargin: UM.Theme.getSize("thin_margin").width - text: catalog.i18nc("@label:PrintjobStatus", "Unable to Slice") - color: UM.Theme.getColor("warning") - font: UM.Theme.getFont("very_small") - } + text: catalog.i18nc("@label:PrintjobStatus", "Unable to Slice") + source: UM.Theme.getIcon("warning") + color: UM.Theme.getColor("warning") + font: UM.Theme.getFont("very_small") } // Progress bar, only visible when the backend is in the process of slice the printjob diff --git a/resources/qml/qmldir b/resources/qml/qmldir index 0e5e316409..22e3ff1303 100644 --- a/resources/qml/qmldir +++ b/resources/qml/qmldir @@ -7,4 +7,5 @@ PrintSetupSelector 1.0 PrintSetupSelector.qml ActionButton 1.0 ActionButton.qml MaterialMenu 1.0 MaterialMenu.qml NozzleMenu 1.0 NozzleMenu.qml -ActionPanelWidget 1.0 ActionPanelWidget.qml \ No newline at end of file +ActionPanelWidget 1.0 ActionPanelWidget.qml +IconLabel 1.0 IconLabel.qml \ No newline at end of file diff --git a/resources/themes/cura-light/icons/clock.svg b/resources/themes/cura-light/icons/clock.svg new file mode 100644 index 0000000000..0b6cb78881 --- /dev/null +++ b/resources/themes/cura-light/icons/clock.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/themes/cura-light/icons/info.svg b/resources/themes/cura-light/icons/info.svg new file mode 100644 index 0000000000..97e4fc4f35 --- /dev/null +++ b/resources/themes/cura-light/icons/info.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/themes/cura-light/icons/spool.svg b/resources/themes/cura-light/icons/spool.svg new file mode 100644 index 0000000000..0d8ae42d9d --- /dev/null +++ b/resources/themes/cura-light/icons/spool.svg @@ -0,0 +1,7 @@ + + + + + + + From f7730302187667e74a883e061f237440a0b90521 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 31 Oct 2018 09:37:21 +0100 Subject: [PATCH 0107/1240] Add main stage to previewStage so that SimulationView can use it CURA-5829 --- plugins/PreviewStage/PreviewMain.qml | 19 ++ plugins/PreviewStage/PreviewStage.py | 2 + plugins/SimulationView/SimulationView.py | 7 +- .../SimulationViewMainComponent.qml | 212 +++++++++++++++++ ...ew.qml => SimulationViewMenuComponent.qml} | 224 ------------------ plugins/SimulationView/__init__.py | 1 - resources/qml/Cura.qml | 57 +++-- 7 files changed, 272 insertions(+), 250 deletions(-) create mode 100644 plugins/PreviewStage/PreviewMain.qml create mode 100644 plugins/SimulationView/SimulationViewMainComponent.qml rename plugins/SimulationView/{SimulationView.qml => SimulationViewMenuComponent.qml} (75%) diff --git a/plugins/PreviewStage/PreviewMain.qml b/plugins/PreviewStage/PreviewMain.qml new file mode 100644 index 0000000000..df7e09225f --- /dev/null +++ b/plugins/PreviewStage/PreviewMain.qml @@ -0,0 +1,19 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.4 +import QtQuick.Controls 1.2 +import QtQuick.Layouts 1.1 +import QtQuick.Controls.Styles 1.1 + +import UM 1.0 as UM +import Cura 1.0 as Cura + + +Loader +{ + id: previewMain + + source: UM.Controller.activeView != null && UM.Controller.activeView.mainComponent != null ? UM.Controller.activeView.mainComponent : "" + onSourceChanged: print("THE SOURCE IS", source) +} \ No newline at end of file diff --git a/plugins/PreviewStage/PreviewStage.py b/plugins/PreviewStage/PreviewStage.py index e41d4a592f..1aed95162a 100644 --- a/plugins/PreviewStage/PreviewStage.py +++ b/plugins/PreviewStage/PreviewStage.py @@ -30,4 +30,6 @@ class PreviewStage(CuraStage): plugin_path = self._application.getPluginRegistry().getPluginPath(self.getPluginId()) if plugin_path is not None: menu_component_path = os.path.join(plugin_path, "PreviewMenu.qml") + main_component_path = os.path.join(plugin_path, "PreviewMain.qml") self.addDisplayComponent("menu", menu_component_path) + self.addDisplayComponent("main", main_component_path) diff --git a/plugins/SimulationView/SimulationView.py b/plugins/SimulationView/SimulationView.py index 0ef08b22d7..f1bff5f3f7 100644 --- a/plugins/SimulationView/SimulationView.py +++ b/plugins/SimulationView/SimulationView.py @@ -117,7 +117,12 @@ class SimulationView(CuraView): def _onEngineCreated(self) -> None: menu_component_path = os.path.join(PluginRegistry.getInstance().getPluginPath("SimulationView"), - "SimulationView.qml") + "SimulationViewMenuComponent.qml") + + main_component_path = os.path.join(PluginRegistry.getInstance().getPluginPath("SimulationView"), + "SimulationViewMainComponent.qml") + + self.addDisplayComponent("main", main_component_path) self.addDisplayComponent("menu", menu_component_path) def _evaluateCompatibilityMode(self) -> bool: diff --git a/plugins/SimulationView/SimulationViewMainComponent.qml b/plugins/SimulationView/SimulationViewMainComponent.qml new file mode 100644 index 0000000000..a4ec411124 --- /dev/null +++ b/plugins/SimulationView/SimulationViewMainComponent.qml @@ -0,0 +1,212 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.4 +import QtQuick.Controls 1.2 +import QtQuick.Layouts 1.1 +import QtQuick.Controls.Styles 1.1 + +import UM 1.0 as UM +import Cura 1.0 as Cura + +Item +{ + property bool is_simulation_playing: false + visible: UM.SimulationView.layerActivity && CuraApplication.platformActivity + + PathSlider + { + id: pathSlider + height: UM.Theme.getSize("slider_handle").width + width: 250 + + anchors.bottom: parent.bottom + anchors.bottomMargin: UM.Theme.getSize("default_margin").height + + anchors.horizontalCenter: parent.horizontalCenter + + visible: !UM.SimulationView.compatibilityMode + + // Custom properties + handleValue: UM.SimulationView.currentPath + maximumValue: UM.SimulationView.numPaths + handleSize: UM.Theme.getSize("slider_handle").width + trackThickness: UM.Theme.getSize("slider_groove").width + trackColor: UM.Theme.getColor("slider_groove") + trackBorderColor: UM.Theme.getColor("slider_groove_border") + handleColor: UM.Theme.getColor("slider_handle") + handleActiveColor: UM.Theme.getColor("slider_handle_active") + rangeColor: UM.Theme.getColor("slider_groove_fill") + + // Update values when layer data changes. + Connections + { + target: UM.SimulationView + onMaxPathsChanged: pathSlider.setHandleValue(UM.SimulationView.currentPath) + onCurrentPathChanged: + { + // Only pause the simulation when the layer was changed manually, not when the simulation is running + if (pathSlider.manuallyChanged) + { + playButton.pauseSimulation() + } + pathSlider.setHandleValue(UM.SimulationView.currentPath) + } + } + + // Ensure that the slider handlers show the correct value after switching views. + Component.onCompleted: + { + pathSlider.setHandleValue(UM.SimulationView.currentPath) + } + + } + + Button + { + id: playButton + iconSource: !is_simulation_playing ? "./resources/simulation_resume.svg": "./resources/simulation_pause.svg" + style: UM.Theme.styles.small_tool_button + visible: !UM.SimulationView.compatibilityMode + anchors + { + right: pathSlider.left + verticalCenter: pathSlider.verticalCenter + } + + onClicked: + { + if(is_simulation_playing) + { + pauseSimulation() + } else + { + resumeSimulation() + } + } + + function pauseSimulation() + { + UM.SimulationView.setSimulationRunning(false) + simulationTimer.stop() + is_simulation_playing = false + layerSlider.manuallyChanged = true + pathSlider.manuallyChanged = true + } + + function resumeSimulation() + { + UM.SimulationView.setSimulationRunning(true) + simulationTimer.start() + layerSlider.manuallyChanged = false + pathSlider.manuallyChanged = false + } + } + + Timer + { + id: simulationTimer + interval: 100 + running: false + repeat: true + onTriggered: + { + var currentPath = UM.SimulationView.currentPath + var numPaths = UM.SimulationView.numPaths + var currentLayer = UM.SimulationView.currentLayer + var numLayers = UM.SimulationView.numLayers + + // When the user plays the simulation, if the path slider is at the end of this layer, we start + // the simulation at the beginning of the current layer. + if (!is_simulation_playing) + { + if (currentPath >= numPaths) + { + UM.SimulationView.setCurrentPath(0) + } + else + { + UM.SimulationView.setCurrentPath(currentPath + 1) + } + } + // If the simulation is already playing and we reach the end of a layer, then it automatically + // starts at the beginning of the next layer. + else + { + if (currentPath >= numPaths) + { + // At the end of the model, the simulation stops + if (currentLayer >= numLayers) + { + playButton.pauseSimulation() + } + else + { + UM.SimulationView.setCurrentLayer(currentLayer+1) + UM.SimulationView.setCurrentPath(0) + } + } + else + { + UM.SimulationView.setCurrentPath(currentPath+1) + } + } + // The status must be set here instead of in the resumeSimulation function otherwise it won't work + // correctly, because part of the logic is in this trigger function. + is_simulation_playing = true + } + } + + LayerSlider + { + id: layerSlider + + width: UM.Theme.getSize("slider_handle").width + height: UM.Theme.getSize("layerview_menu_size").height + + anchors + { + right: parent.right + verticalCenter: parent.verticalCenter + rightMargin: UM.Theme.getSize("default_margin").width + } + + // Custom properties + upperValue: UM.SimulationView.currentLayer + lowerValue: UM.SimulationView.minimumLayer + maximumValue: UM.SimulationView.numLayers + handleSize: UM.Theme.getSize("slider_handle").width + trackThickness: UM.Theme.getSize("slider_groove").width + trackColor: UM.Theme.getColor("slider_groove") + trackBorderColor: UM.Theme.getColor("slider_groove_border") + upperHandleColor: UM.Theme.getColor("slider_handle") + lowerHandleColor: UM.Theme.getColor("slider_handle") + rangeHandleColor: UM.Theme.getColor("slider_groove_fill") + handleActiveColor: UM.Theme.getColor("slider_handle_active") + handleLabelWidth: UM.Theme.getSize("slider_layerview_background").width + + // Update values when layer data changes + Connections + { + target: UM.SimulationView + onMaxLayersChanged: layerSlider.setUpperValue(UM.SimulationView.currentLayer) + onMinimumLayerChanged: layerSlider.setLowerValue(UM.SimulationView.minimumLayer) + onCurrentLayerChanged: + { + // Only pause the simulation when the layer was changed manually, not when the simulation is running + if (layerSlider.manuallyChanged) + { + playButton.pauseSimulation() + } + layerSlider.setUpperValue(UM.SimulationView.currentLayer) + } + } + + // Make sure the slider handlers show the correct value after switching views + Component.onCompleted: + { + layerSlider.setLowerValue(UM.SimulationView.minimumLayer) + layerSlider.setUpperValue(UM.SimulationView.currentLayer) + } + } +} \ No newline at end of file diff --git a/plugins/SimulationView/SimulationView.qml b/plugins/SimulationView/SimulationViewMenuComponent.qml similarity index 75% rename from plugins/SimulationView/SimulationView.qml rename to plugins/SimulationView/SimulationViewMenuComponent.qml index 7a83a07ac1..91ef7a2794 100644 --- a/plugins/SimulationView/SimulationView.qml +++ b/plugins/SimulationView/SimulationViewMenuComponent.qml @@ -583,230 +583,6 @@ Item } } - Item - { - id: slidersBox - - width: parent.width - height: parent.height - visible: UM.SimulationView.layerActivity && CuraApplication.platformActivity - - anchors - { - top: parent.top - leftMargin: UM.Theme.getSize("slider_layerview_margin").height - left: parent.right - } - - PathSlider - { - id: pathSlider - - height: UM.Theme.getSize("slider_handle").width - anchors - { - verticalCenter: playButton.verticalCenter - left: playButton.right - leftMargin: UM.Theme.getSize("default_margin").width - right: parent.right - } - visible: !UM.SimulationView.compatibilityMode - - // custom properties - handleValue: UM.SimulationView.currentPath - maximumValue: UM.SimulationView.numPaths - handleSize: UM.Theme.getSize("slider_handle").width - trackThickness: UM.Theme.getSize("slider_groove").width - trackColor: UM.Theme.getColor("slider_groove") - trackBorderColor: UM.Theme.getColor("slider_groove_border") - handleColor: UM.Theme.getColor("slider_handle") - handleActiveColor: UM.Theme.getColor("slider_handle_active") - rangeColor: UM.Theme.getColor("slider_groove_fill") - - // update values when layer data changes - Connections - { - target: UM.SimulationView - onMaxPathsChanged: pathSlider.setHandleValue(UM.SimulationView.currentPath) - onCurrentPathChanged: - { - // Only pause the simulation when the layer was changed manually, not when the simulation is running - if (pathSlider.manuallyChanged) - { - playButton.pauseSimulation() - } - pathSlider.setHandleValue(UM.SimulationView.currentPath) - } - } - - // make sure the slider handlers show the correct value after switching views - Component.onCompleted: - { - pathSlider.setHandleValue(UM.SimulationView.currentPath) - } - } - - LayerSlider - { - id: layerSlider - - width: UM.Theme.getSize("slider_handle").width - height: UM.Theme.getSize("layerview_menu_size").height - - anchors - { - bottom: !UM.SimulationView.compatibilityMode ? pathSlider.top : parent.bottom - top: parent.top - bottomMargin: !UM.SimulationView.compatibilityMode ? UM.Theme.getSize("default_margin").height : 0 - left: parent.left - leftMargin: Math.round(UM.Theme.getSize("slider_layerview_margin").width / 2) - } - - // custom properties - upperValue: UM.SimulationView.currentLayer - lowerValue: UM.SimulationView.minimumLayer - maximumValue: UM.SimulationView.numLayers - handleSize: UM.Theme.getSize("slider_handle").width - trackThickness: UM.Theme.getSize("slider_groove").width - trackColor: UM.Theme.getColor("slider_groove") - trackBorderColor: UM.Theme.getColor("slider_groove_border") - upperHandleColor: UM.Theme.getColor("slider_handle") - lowerHandleColor: UM.Theme.getColor("slider_handle") - rangeHandleColor: UM.Theme.getColor("slider_groove_fill") - handleActiveColor: UM.Theme.getColor("slider_handle_active") - handleLabelWidth: UM.Theme.getSize("slider_layerview_background").width - - // update values when layer data changes - Connections - { - target: UM.SimulationView - onMaxLayersChanged: layerSlider.setUpperValue(UM.SimulationView.currentLayer) - onMinimumLayerChanged: layerSlider.setLowerValue(UM.SimulationView.minimumLayer) - onCurrentLayerChanged: - { - // Only pause the simulation when the layer was changed manually, not when the simulation is running - if (layerSlider.manuallyChanged) - { - playButton.pauseSimulation() - } - layerSlider.setUpperValue(UM.SimulationView.currentLayer) - } - } - - // make sure the slider handlers show the correct value after switching views - Component.onCompleted: - { - layerSlider.setLowerValue(UM.SimulationView.minimumLayer) - layerSlider.setUpperValue(UM.SimulationView.currentLayer) - } - } - - // Play simulation button - Button - { - id: playButton - iconSource: "./resources/simulation_resume.svg" - style: UM.Theme.styles.small_tool_button - visible: !UM.SimulationView.compatibilityMode - anchors - { - left: parent.left - bottom: parent.bottom - } - - property var status: 0 // indicates if it's stopped (0) or playing (1) - - onClicked: - { - switch(status) - { - case 0: - { - resumeSimulation() - break - } - case 1: - { - pauseSimulation() - break - } - } - } - - function pauseSimulation() - { - UM.SimulationView.setSimulationRunning(false) - iconSource = "./resources/simulation_resume.svg" - simulationTimer.stop() - status = 0 - layerSlider.manuallyChanged = true - pathSlider.manuallyChanged = true - } - - function resumeSimulation() - { - UM.SimulationView.setSimulationRunning(true) - iconSource = "./resources/simulation_pause.svg" - simulationTimer.start() - layerSlider.manuallyChanged = false - pathSlider.manuallyChanged = false - } - } - - Timer - { - id: simulationTimer - interval: 100 - running: false - repeat: true - onTriggered: - { - var currentPath = UM.SimulationView.currentPath - var numPaths = UM.SimulationView.numPaths - var currentLayer = UM.SimulationView.currentLayer - var numLayers = UM.SimulationView.numLayers - // When the user plays the simulation, if the path slider is at the end of this layer, we start - // the simulation at the beginning of the current layer. - if (playButton.status == 0) - { - if (currentPath >= numPaths) - { - UM.SimulationView.setCurrentPath(0) - } - else - { - UM.SimulationView.setCurrentPath(currentPath+1) - } - } - // If the simulation is already playing and we reach the end of a layer, then it automatically - // starts at the beginning of the next layer. - else - { - if (currentPath >= numPaths) - { - // At the end of the model, the simulation stops - if (currentLayer >= numLayers) - { - playButton.pauseSimulation() - } - else - { - UM.SimulationView.setCurrentLayer(currentLayer+1) - UM.SimulationView.setCurrentPath(0) - } - } - else - { - UM.SimulationView.setCurrentPath(currentPath+1) - } - } - // The status must be set here instead of in the resumeSimulation function otherwise it won't work - // correctly, because part of the logic is in this trigger function. - playButton.status = 1 - } - } - } - FontMetrics { id: fontMetrics diff --git a/plugins/SimulationView/__init__.py b/plugins/SimulationView/__init__.py index 3f89ee490f..b12e582441 100644 --- a/plugins/SimulationView/__init__.py +++ b/plugins/SimulationView/__init__.py @@ -13,7 +13,6 @@ def getMetaData(): return { "view": { "name": catalog.i18nc("@item:inlistbox", "Layer view"), - "view_panel": "SimulationView.qml", "weight": 2 } } diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 9f53b75dd1..e49683cb8e 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -155,24 +155,6 @@ UM.MainWindow color: UM.Theme.getColor("main_window_header_background") } - Loader - { - // The stage menu is, as the name implies, a menu that is defined by the active stage. - // Note that this menu does not need to be set at all! It's perfectly acceptable to have a stage - // without this menu! - id: stageMenu - - anchors - { - left: parent.left - right: parent.right - top: parent.top - } - - height: UM.Theme.getSize("stage_menu").height - source: UM.Controller.activeStage != null ? UM.Controller.activeStage.stageMenuComponent : "" - } - Connections { target: stageMenu.item @@ -230,6 +212,22 @@ UM.MainWindow } } + Loader + { + id: viewPanel + + anchors.bottom: viewModeButton.top + anchors.topMargin: UM.Theme.getSize("default_margin").height + anchors.right: viewModeButton.right + + property var buttonTarget: Qt.point(viewModeButton.x + Math.round(viewModeButton.width / 2), viewModeButton.y + Math.round(viewModeButton.height / 2)) + + height: childrenRect.height + width: childrenRect.width + + source: UM.ActiveView.valid ? UM.ActiveView.activeViewPanel : "" + } + Cura.ActionPanelWidget { anchors.right: parent.right @@ -254,15 +252,26 @@ UM.MainWindow anchors.fill: parent - MouseArea + source: UM.Controller.activeStage != null ? UM.Controller.activeStage.mainComponent : "" + } + + + Loader + { + // The stage menu is, as the name implies, a menu that is defined by the active stage. + // Note that this menu does not need to be set at all! It's perfectly acceptable to have a stage + // without this menu! + id: stageMenu + + anchors { - visible: parent.source != "" - anchors.fill: parent - acceptedButtons: Qt.AllButtons - onWheel: wheel.accepted = true + left: parent.left + right: parent.right + top: parent.top } - source: UM.Controller.activeStage != null ? UM.Controller.activeStage.mainComponent : "" + height: UM.Theme.getSize("stage_menu").height + source: UM.Controller.activeStage != null ? UM.Controller.activeStage.stageMenuComponent : "" } UM.MessageStack From 3fc399a6443ffa0caf7fef0288c14b7b59c50cfe Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 31 Oct 2018 09:46:41 +0100 Subject: [PATCH 0108/1240] Moved default values into components CURA-5829 --- plugins/SimulationView/LayerSlider.qml | 22 +++++++++---------- plugins/SimulationView/PathSlider.qml | 14 ++++++------ .../SimulationViewMainComponent.qml | 16 -------------- .../SimulationViewMenuComponent.qml | 17 +++++++------- 4 files changed, 26 insertions(+), 43 deletions(-) diff --git a/plugins/SimulationView/LayerSlider.qml b/plugins/SimulationView/LayerSlider.qml index c30ea621c4..7a7aaa0649 100644 --- a/plugins/SimulationView/LayerSlider.qml +++ b/plugins/SimulationView/LayerSlider.qml @@ -13,22 +13,22 @@ Item { id: sliderRoot - // handle properties - property real handleSize: 10 + // Handle properties + property real handleSize: UM.Theme.getSize("slider_handle").width property real handleRadius: handleSize / 2 property real minimumRangeHandleSize: handleSize / 2 - property color upperHandleColor: "black" - property color lowerHandleColor: "black" - property color rangeHandleColor: "black" - property color handleActiveColor: "white" - property real handleLabelWidth: width + property color upperHandleColor: UM.Theme.getColor("slider_handle") + property color lowerHandleColor: UM.Theme.getColor("slider_handle") + property color rangeHandleColor: UM.Theme.getColor("slider_groove_fill") + property color handleActiveColor: UM.Theme.getColor("slider_handle_active") + property real handleLabelWidth: UM.Theme.getSize("slider_layerview_background").width property var activeHandle: upperHandle - // track properties - property real trackThickness: 4 // width of the slider track + // Track properties + property real trackThickness: UM.Theme.getSize("slider_groove").width // width of the slider track property real trackRadius: trackThickness / 2 - property color trackColor: "white" - property real trackBorderWidth: 1 // width of the slider track border + property color trackColor: UM.Theme.getColor("slider_groove") + property real trackBorderWidth: UM.Theme.getColor("slider_groove_border") property color trackBorderColor: "black" // value properties diff --git a/plugins/SimulationView/PathSlider.qml b/plugins/SimulationView/PathSlider.qml index f3c28fb5f7..701e54e398 100644 --- a/plugins/SimulationView/PathSlider.qml +++ b/plugins/SimulationView/PathSlider.qml @@ -14,19 +14,19 @@ Item id: sliderRoot // handle properties - property real handleSize: 10 + property real handleSize: UM.Theme.getSize("slider_handle").width property real handleRadius: handleSize / 2 - property color handleColor: "black" - property color handleActiveColor: "white" - property color rangeColor: "black" + property color handleColor: UM.Theme.getColor("slider_handle") + property color handleActiveColor: UM.Theme.getColor("slider_handle_active") + property color rangeColor: UM.Theme.getColor("slider_groove_fill") property real handleLabelWidth: width // track properties - property real trackThickness: 4 // width of the slider track + property real trackThickness: UM.Theme.getSize("slider_groove").width property real trackRadius: trackThickness / 2 - property color trackColor: "white" + property color trackColor: UM.Theme.getColor("slider_groove") property real trackBorderWidth: 1 // width of the slider track border - property color trackBorderColor: "black" + property color trackBorderColor: UM.Theme.getColor("slider_groove_border") // value properties property real maximumValue: 100 diff --git a/plugins/SimulationView/SimulationViewMainComponent.qml b/plugins/SimulationView/SimulationViewMainComponent.qml index a4ec411124..2484ec25ba 100644 --- a/plugins/SimulationView/SimulationViewMainComponent.qml +++ b/plugins/SimulationView/SimulationViewMainComponent.qml @@ -30,13 +30,6 @@ Item // Custom properties handleValue: UM.SimulationView.currentPath maximumValue: UM.SimulationView.numPaths - handleSize: UM.Theme.getSize("slider_handle").width - trackThickness: UM.Theme.getSize("slider_groove").width - trackColor: UM.Theme.getColor("slider_groove") - trackBorderColor: UM.Theme.getColor("slider_groove_border") - handleColor: UM.Theme.getColor("slider_handle") - handleActiveColor: UM.Theme.getColor("slider_handle_active") - rangeColor: UM.Theme.getColor("slider_groove_fill") // Update values when layer data changes. Connections @@ -175,15 +168,6 @@ Item upperValue: UM.SimulationView.currentLayer lowerValue: UM.SimulationView.minimumLayer maximumValue: UM.SimulationView.numLayers - handleSize: UM.Theme.getSize("slider_handle").width - trackThickness: UM.Theme.getSize("slider_groove").width - trackColor: UM.Theme.getColor("slider_groove") - trackBorderColor: UM.Theme.getColor("slider_groove_border") - upperHandleColor: UM.Theme.getColor("slider_handle") - lowerHandleColor: UM.Theme.getColor("slider_handle") - rangeHandleColor: UM.Theme.getColor("slider_groove_fill") - handleActiveColor: UM.Theme.getColor("slider_handle_active") - handleLabelWidth: UM.Theme.getSize("slider_layerview_background").width // Update values when layer data changes Connections diff --git a/plugins/SimulationView/SimulationViewMenuComponent.qml b/plugins/SimulationView/SimulationViewMenuComponent.qml index 91ef7a2794..a9c7305c17 100644 --- a/plugins/SimulationView/SimulationViewMenuComponent.qml +++ b/plugins/SimulationView/SimulationViewMenuComponent.qml @@ -216,15 +216,14 @@ Item onPreferenceChanged: { layerTypeCombobox.currentIndex = UM.SimulationView.compatibilityMode ? 1 : UM.Preferences.getValue("layerview/layer_view_type"); - layerTypeCombobox.updateLegends(layerTypeCombobox.currentIndex); - playButton.pauseSimulation(); - viewSettings.extruder_opacities = UM.Preferences.getValue("layerview/extruder_opacities").split("|"); - viewSettings.show_travel_moves = UM.Preferences.getValue("layerview/show_travel_moves"); - viewSettings.show_helpers = UM.Preferences.getValue("layerview/show_helpers"); - viewSettings.show_skin = UM.Preferences.getValue("layerview/show_skin"); - viewSettings.show_infill = UM.Preferences.getValue("layerview/show_infill"); - viewSettings.only_show_top_layers = UM.Preferences.getValue("view/only_show_top_layers"); - viewSettings.top_layer_count = UM.Preferences.getValue("view/top_layer_count"); + layerTypeCombobox.updateLegends(layerTypeCombobox.currentIndex) + viewSettings.extruder_opacities = UM.Preferences.getValue("layerview/extruder_opacities").split("|") + viewSettings.show_travel_moves = UM.Preferences.getValue("layerview/show_travel_moves") + viewSettings.show_helpers = UM.Preferences.getValue("layerview/show_helpers") + viewSettings.show_skin = UM.Preferences.getValue("layerview/show_skin") + viewSettings.show_infill = UM.Preferences.getValue("layerview/show_infill") + viewSettings.only_show_top_layers = UM.Preferences.getValue("view/only_show_top_layers") + viewSettings.top_layer_count = UM.Preferences.getValue("view/top_layer_count") } } From 15f586ffae119c9f2e948ce8718095156f6275e9 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 31 Oct 2018 10:30:11 +0100 Subject: [PATCH 0109/1240] Fix the location of the simulation slider label CURA-5829 --- plugins/SimulationView/LayerSlider.qml | 16 ++++++++-------- .../SimulationViewMainComponent.qml | 4 ++-- plugins/SimulationView/__init__.py | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/plugins/SimulationView/LayerSlider.qml b/plugins/SimulationView/LayerSlider.qml index 7a7aaa0649..7916cd62b3 100644 --- a/plugins/SimulationView/LayerSlider.qml +++ b/plugins/SimulationView/LayerSlider.qml @@ -28,8 +28,8 @@ Item property real trackThickness: UM.Theme.getSize("slider_groove").width // width of the slider track property real trackRadius: trackThickness / 2 property color trackColor: UM.Theme.getColor("slider_groove") - property real trackBorderWidth: UM.Theme.getColor("slider_groove_border") - property color trackBorderColor: "black" + property real trackBorderWidth: 1 + property color trackBorderColor: UM.Theme.getColor("slider_groove_border") // value properties property real maximumValue: 100 @@ -80,7 +80,7 @@ Item return Math.min(Math.max(value, sliderRoot.minimumValue), sliderRoot.maximumValue) } - // slider track + // Slider track Rectangle { id: track @@ -106,7 +106,7 @@ Item anchors.horizontalCenter: sliderRoot.horizontalCenter visible: sliderRoot.layersVisible - // set the new value when dragging + // Set the new value when dragging function onHandleDragged() { sliderRoot.manuallyChanged = true @@ -169,7 +169,7 @@ Item height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height x: parent.x + parent.width + UM.Theme.getSize("default_margin").width anchors.verticalCenter: parent.verticalCenter - target: Qt.point(sliderRoot.width, y + height / 2) + target: Qt.point(sliderRoot.width + width, y + height / 2) visible: sliderRoot.activeHandle == parent // custom properties @@ -275,7 +275,7 @@ Item id: upperHandleLabel height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height - x: parent.x + parent.width + UM.Theme.getSize("default_margin").width + x: parent.x - parent.width - width anchors.verticalCenter: parent.verticalCenter target: Qt.point(sliderRoot.width, y + height / 2) visible: sliderRoot.activeHandle == parent @@ -385,9 +385,9 @@ Item id: lowerHandleLabel height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height - x: parent.x + parent.width + UM.Theme.getSize("default_margin").width + x: parent.x - parent.width - width anchors.verticalCenter: parent.verticalCenter - target: Qt.point(sliderRoot.width, y + height / 2) + target: Qt.point(sliderRoot.width + width, y + height / 2) visible: sliderRoot.activeHandle == parent // custom properties diff --git a/plugins/SimulationView/SimulationViewMainComponent.qml b/plugins/SimulationView/SimulationViewMainComponent.qml index 2484ec25ba..5c45da6b45 100644 --- a/plugins/SimulationView/SimulationViewMainComponent.qml +++ b/plugins/SimulationView/SimulationViewMainComponent.qml @@ -135,13 +135,13 @@ Item } else { - UM.SimulationView.setCurrentLayer(currentLayer+1) + UM.SimulationView.setCurrentLayer(currentLayer + 1) UM.SimulationView.setCurrentPath(0) } } else { - UM.SimulationView.setCurrentPath(currentPath+1) + UM.SimulationView.setCurrentPath(currentPath + 1) } } // The status must be set here instead of in the resumeSimulation function otherwise it won't work diff --git a/plugins/SimulationView/__init__.py b/plugins/SimulationView/__init__.py index b12e582441..1efb7fd650 100644 --- a/plugins/SimulationView/__init__.py +++ b/plugins/SimulationView/__init__.py @@ -13,7 +13,7 @@ def getMetaData(): return { "view": { "name": catalog.i18nc("@item:inlistbox", "Layer view"), - "weight": 2 + "weight": 0 } } From 1e9aff44f635b37efc0365cce15b6f25bb980c80 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 31 Oct 2018 10:55:05 +0100 Subject: [PATCH 0110/1240] Fix QML warnings & errors CURA-5829 --- plugins/PreviewStage/PreviewMain.qml | 1 - .../SimulationViewMenuComponent.qml | 58 ++++++++----------- 2 files changed, 23 insertions(+), 36 deletions(-) diff --git a/plugins/PreviewStage/PreviewMain.qml b/plugins/PreviewStage/PreviewMain.qml index df7e09225f..04241783e9 100644 --- a/plugins/PreviewStage/PreviewMain.qml +++ b/plugins/PreviewStage/PreviewMain.qml @@ -15,5 +15,4 @@ Loader id: previewMain source: UM.Controller.activeView != null && UM.Controller.activeView.mainComponent != null ? UM.Controller.activeView.mainComponent : "" - onSourceChanged: print("THE SOURCE IS", source) } \ No newline at end of file diff --git a/plugins/SimulationView/SimulationViewMenuComponent.qml b/plugins/SimulationView/SimulationViewMenuComponent.qml index a9c7305c17..5a146910e7 100644 --- a/plugins/SimulationView/SimulationViewMenuComponent.qml +++ b/plugins/SimulationView/SimulationViewMenuComponent.qml @@ -70,7 +70,8 @@ Item border.width: UM.Theme.getSize("default_lining").width border.color: UM.Theme.getColor("lining") - Button { + Button + { id: collapseButton anchors.top: parent.top anchors.topMargin: Math.round(UM.Theme.getSize("default_margin").height + (UM.Theme.getSize("layerview_row").height - UM.Theme.getSize("default_margin").height) / 2) @@ -97,7 +98,7 @@ Item } } - ColumnLayout + Column { id: viewSettings @@ -126,11 +127,10 @@ Item Label { id: layerViewTypesLabel - anchors.left: parent.left text: catalog.i18nc("@label","Color scheme") font: UM.Theme.getFont("default"); visible: !UM.SimulationView.compatibilityMode - Layout.fillWidth: true + width: parent.width color: UM.Theme.getColor("setting_control_text") } @@ -162,13 +162,10 @@ Item ComboBox { id: layerTypeCombobox - anchors.left: parent.left - Layout.fillWidth: true - Layout.preferredWidth: UM.Theme.getSize("layerview_row").width + width: parent.width model: layerViewTypes visible: !UM.SimulationView.compatibilityMode style: UM.Theme.styles.combobox - anchors.right: parent.right onActivated: { @@ -194,14 +191,12 @@ Item Label { id: compatibilityModeLabel - anchors.left: parent.left text: catalog.i18nc("@label","Compatibility Mode") font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") visible: UM.SimulationView.compatibilityMode - Layout.fillWidth: true - Layout.preferredHeight: UM.Theme.getSize("layerview_row").height - Layout.preferredWidth: UM.Theme.getSize("layerview_row").width + height: UM.Theme.getSize("layerview_row").height + width: parent.width } Item @@ -253,9 +248,9 @@ Item border.color: UM.Theme.getColor("lining") visible: !viewSettings.show_legend & !viewSettings.show_gradient } - Layout.fillWidth: true - Layout.preferredHeight: UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("default_lining").height - Layout.preferredWidth: UM.Theme.getSize("layerview_row").width + height: UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("default_lining").height + width: parent.width + style: UM.Theme.styles.checkbox Label { @@ -264,9 +259,9 @@ Item color: UM.Theme.getColor("setting_control_text") font: UM.Theme.getFont("default") anchors.verticalCenter: parent.verticalCenter - anchors.left: extrudersModelCheckBox.left; - anchors.right: extrudersModelCheckBox.right; - anchors.leftMargin: UM.Theme.getSize("checkbox").width + Math.round(UM.Theme.getSize("default_margin").width/2) + anchors.left: extrudersModelCheckBox.left + anchors.right: extrudersModelCheckBox.right + anchors.leftMargin: UM.Theme.getSize("checkbox").width + Math.round(UM.Theme.getSize("default_margin").width / 2) anchors.rightMargin: UM.Theme.getSize("default_margin").width * 2 } } @@ -325,9 +320,8 @@ Item border.color: UM.Theme.getColor("lining") visible: viewSettings.show_legend } - Layout.fillWidth: true - Layout.preferredHeight: UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("default_lining").height - Layout.preferredWidth: UM.Theme.getSize("layerview_row").width + height: UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("default_lining").height + width: parent.width style: UM.Theme.styles.checkbox Label { @@ -336,9 +330,9 @@ Item elide: Text.ElideRight color: UM.Theme.getColor("setting_control_text") anchors.verticalCenter: parent.verticalCenter - anchors.left: legendModelCheckBox.left; - anchors.right: legendModelCheckBox.right; - anchors.leftMargin: UM.Theme.getSize("checkbox").width + Math.round(UM.Theme.getSize("default_margin").width/2) + anchors.left: legendModelCheckBox.left + anchors.right: legendModelCheckBox.right + anchors.leftMargin: UM.Theme.getSize("checkbox").width + Math.round(UM.Theme.getSize("default_margin").width / 2) anchors.rightMargin: UM.Theme.getSize("default_margin").width * 2 } } @@ -349,7 +343,7 @@ Item checked: viewSettings.only_show_top_layers onClicked: { - UM.Preferences.setValue("view/only_show_top_layers", checked ? 1.0 : 0.0); + UM.Preferences.setValue("view/only_show_top_layers", checked ? 1.0 : 0.0) } text: catalog.i18nc("@label", "Only Show Top Layers") visible: UM.SimulationView.compatibilityMode @@ -360,7 +354,7 @@ Item checked: viewSettings.top_layer_count == 5 onClicked: { - UM.Preferences.setValue("view/top_layer_count", checked ? 5 : 1); + UM.Preferences.setValue("view/top_layer_count", checked ? 5 : 1) } text: catalog.i18nc("@label", "Show 5 Detailed Layers On Top") visible: UM.SimulationView.compatibilityMode @@ -401,9 +395,8 @@ Item border.color: UM.Theme.getColor("lining") visible: viewSettings.show_legend } - Layout.fillWidth: true - Layout.preferredHeight: UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("default_lining").height - Layout.preferredWidth: UM.Theme.getSize("layerview_row").width + height: UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("default_lining").height + width: parent.width color: UM.Theme.getColor("setting_control_text") font: UM.Theme.getFont("default") } @@ -416,11 +409,6 @@ Item visible: viewSettings.show_gradient width: parent.width height: UM.Theme.getSize("layerview_row").height - anchors - { - topMargin: UM.Theme.getSize("slider_layerview_margin").height - horizontalCenter: parent.horizontalCenter - } Label { @@ -503,7 +491,7 @@ Item // Gradient colors for feedrate Rectangle - { // In QML 5.9 can be changed by LinearGradient + { // In QML 5.9 can be changed by LinearGradient // Invert values because then the bar is rotated 90 degrees id: feedrateGradient visible: viewSettings.show_feedrate_gradient From 61442545cb99dc7379ea204f0496b1464c58da27 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 31 Oct 2018 13:56:55 +0100 Subject: [PATCH 0111/1240] Fix codestyle CURA-5829 --- plugins/SimulationView/SimulationView.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/plugins/SimulationView/SimulationView.py b/plugins/SimulationView/SimulationView.py index f1bff5f3f7..6a0ffc1666 100644 --- a/plugins/SimulationView/SimulationView.py +++ b/plugins/SimulationView/SimulationView.py @@ -16,6 +16,7 @@ from UM.Mesh.MeshBuilder import MeshBuilder from UM.Message import Message from UM.Platform import Platform from UM.PluginRegistry import PluginRegistry +from UM.Qt.QtApplication import QtApplication from UM.Resources import Resources from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator @@ -113,17 +114,15 @@ class SimulationView(CuraView): self._wireprint_warning_message = Message(catalog.i18nc("@info:status", "Cura does not accurately display layers when Wire Printing is enabled"), title = catalog.i18nc("@info:title", "Simulation View")) - Application.getInstance().engineCreatedSignal.connect(self._onEngineCreated) + QtApplication.getInstance().engineCreatedSignal.connect(self._onEngineCreated) def _onEngineCreated(self) -> None: - menu_component_path = os.path.join(PluginRegistry.getInstance().getPluginPath("SimulationView"), - "SimulationViewMenuComponent.qml") - - main_component_path = os.path.join(PluginRegistry.getInstance().getPluginPath("SimulationView"), - "SimulationViewMainComponent.qml") - - self.addDisplayComponent("main", main_component_path) - self.addDisplayComponent("menu", menu_component_path) + plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId()) + if plugin_path: + self.addDisplayComponent("main", os.path.join(plugin_path, "SimulationViewMainComponent.qml")) + self.addDisplayComponent("menu", os.path.join(plugin_path, "SimulationViewMenuComponent.qml")) + else: + Logger.log("e", "Unable to find the path for %s", self.getPluginId()) def _evaluateCompatibilityMode(self) -> bool: return OpenGLContext.isLegacyOpenGL() or bool(Application.getInstance().getPreferences().getValue("view/force_layer_view_compatibility_mode")) From ec0d9f09b7a800f073422c102f679d7a24d44a48 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 31 Oct 2018 17:03:09 +0100 Subject: [PATCH 0112/1240] Create a component for selecting the output device before output the file. Contributes to CURA-5786. --- resources/qml/ActionButton.qml | 9 ++ resources/qml/ActionPanelWidget.qml | 4 +- resources/qml/OutputDevicesActionButton.qml | 98 +++++++++++++++++++++ resources/qml/OutputProcessWidget.qml | 6 +- resources/qml/SliceProcessWidget.qml | 6 +- resources/qml/qmldir | 3 +- 6 files changed, 116 insertions(+), 10 deletions(-) create mode 100644 resources/qml/OutputDevicesActionButton.qml diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index a1c03af143..05e75ac8c5 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -14,6 +14,7 @@ Button property alias iconSource: buttonIcon.source property alias textFont: buttonText.font property alias cornerRadius: backgroundRect.radius + property alias tooltip: tooltip.text property var color: UM.Theme.getColor("primary") property var hoverColor: UM.Theme.getColor("primary_hover") property var disabledColor: color @@ -62,6 +63,14 @@ Button Behavior on color { ColorAnimation { duration: 50 } } } + ToolTip + { + id: tooltip + text: "" + delay: 500 + visible: text != "" && button.hovered + } + MouseArea { id: mouseArea diff --git a/resources/qml/ActionPanelWidget.qml b/resources/qml/ActionPanelWidget.qml index 6c76c31f1b..417883af6f 100644 --- a/resources/qml/ActionPanelWidget.qml +++ b/resources/qml/ActionPanelWidget.qml @@ -21,7 +21,7 @@ Rectangle radius: UM.Theme.getSize("default_radius").width visible: CuraApplication.platformActivity - property bool backendStatusDone: UM.Backend.state == UM.Backend.Done + property bool outputAvailable: UM.Backend.state == UM.Backend.Done || UM.Backend.state == UM.Backend.Disabled Loader { @@ -33,7 +33,7 @@ Rectangle left: parent.left leftMargin: UM.Theme.getSize("thick_margin").width } - sourceComponent: backendStatusDone ? outputProcessWidget : sliceProcessWidget + sourceComponent: outputAvailable ? outputProcessWidget : sliceProcessWidget } Behavior on height { NumberAnimation { duration: 100 } } diff --git a/resources/qml/OutputDevicesActionButton.qml b/resources/qml/OutputDevicesActionButton.qml new file mode 100644 index 0000000000..b0cca0eba0 --- /dev/null +++ b/resources/qml/OutputDevicesActionButton.qml @@ -0,0 +1,98 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 2.1 +import QtQuick.Layouts 1.3 + +import UM 1.1 as UM +import Cura 1.0 as Cura + +Item +{ + id: widget + + Cura.ActionButton + { + id: saveToButton + height: parent.height + + anchors + { + top: parent.top + left: parent.left + right: deviceSelectionMenu.visible ? deviceSelectionMenu.left : parent.right + } + + tooltip: UM.OutputDeviceManager.activeDeviceDescription + + text: UM.OutputDeviceManager.activeDeviceShortDescription + + onClicked: + { + forceActiveFocus(); + UM.OutputDeviceManager.requestWriteToDevice(UM.OutputDeviceManager.activeDevice, PrintInformation.jobName, + { "filter_by_machine": true, "preferred_mimetypes": Cura.MachineManager.activeMachine.preferred_output_file_formats }); + } + } + + Cura.ActionButton + { + id: deviceSelectionMenu + height: parent.height + + anchors + { + top: parent.top + right: parent.right + } + + tooltip: catalog.i18nc("@info:tooltip", "Select the active output device") + iconSource: popup.opened ? UM.Theme.getIcon("arrow_top") : UM.Theme.getIcon("arrow_bottom") + visible: (devicesModel.deviceCount > 1) + + onClicked: popup.opened ? popup.close() : popup.open() + + Popup + { + id: popup + + y: -height + x: parent.width - width + + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent + + contentItem: Column + { + Repeater + { + model: devicesModel + + delegate: Cura.ActionButton + { + text: model.description + color: "transparent" + hoverColor: "red" + + onClicked: + { + UM.OutputDeviceManager.setActiveDevice(model.id) + popup.close() + } + } + } + } + + background: Rectangle + { + opacity: visible ? 1 : 0 + Behavior on opacity { NumberAnimation { duration: 100 } } + color: UM.Theme.getColor("primary") + border.color: UM.Theme.getColor("lining") + border.width: UM.Theme.getSize("default_lining").width + } + } + } + + UM.OutputDevicesModel { id: devicesModel } +} \ No newline at end of file diff --git a/resources/qml/OutputProcessWidget.qml b/resources/qml/OutputProcessWidget.qml index 244fba57ef..d3f306fed5 100644 --- a/resources/qml/OutputProcessWidget.qml +++ b/resources/qml/OutputProcessWidget.qml @@ -96,12 +96,10 @@ Column onClicked: console.log("Clicking preview") } - Cura.ActionButton + Cura.OutputDevicesActionButton { - width: UM.Theme.getSize("account_button").width + width: UM.Theme.getSize("action_panel_button").width height: UM.Theme.getSize("action_panel_button").height - text: catalog.i18nc("@button", "Save to file") - onClicked: console.log("Clicking action button") } } } \ No newline at end of file diff --git a/resources/qml/SliceProcessWidget.qml b/resources/qml/SliceProcessWidget.qml index 38a529b04e..3ea6e09975 100644 --- a/resources/qml/SliceProcessWidget.qml +++ b/resources/qml/SliceProcessWidget.qml @@ -28,7 +28,7 @@ Column function sliceOrStopSlicing() { - if ([UM.Backend.NotStarted, UM.Backend.Disabled].indexOf(widget.backendState) != -1) + if (widget.backendState == UM.Backend.NotStarted) { CuraApplication.backend.forceSlice() } @@ -86,7 +86,7 @@ Column height: UM.Theme.getSize("action_panel_button").height text: { - if ([UM.Backend.NotStarted, UM.Backend.Error, UM.Backend.Disabled].indexOf(widget.backendState) != -1) + if ([UM.Backend.NotStarted, UM.Backend.Error].indexOf(widget.backendState) != -1) { return catalog.i18nc("@button", "Slice") } @@ -101,7 +101,7 @@ Column // Get the current value from the preferences property bool autoSlice: UM.Preferences.getValue("general/auto_slice") // Disable the slice process when - property bool disabledSlice: [UM.Backend.Done, UM.Backend.Error, UM.Backend.Disabled].indexOf(widget.backendState) != -1 + property bool disabledSlice: [UM.Backend.Done, UM.Backend.Error].indexOf(widget.backendState) != -1 disabledColor: disabledSlice ? UM.Theme.getColor("action_button_disabled") : "transparent" textDisabledColor: disabledSlice ? UM.Theme.getColor("action_button_disabled_text") : UM.Theme.getColor("primary") diff --git a/resources/qml/qmldir b/resources/qml/qmldir index 22e3ff1303..d5e4106d33 100644 --- a/resources/qml/qmldir +++ b/resources/qml/qmldir @@ -8,4 +8,5 @@ ActionButton 1.0 ActionButton.qml MaterialMenu 1.0 MaterialMenu.qml NozzleMenu 1.0 NozzleMenu.qml ActionPanelWidget 1.0 ActionPanelWidget.qml -IconLabel 1.0 IconLabel.qml \ No newline at end of file +IconLabel 1.0 IconLabel.qml +OutputDevicesActionButton 1.0 OutputDevicesActionButton.qml \ No newline at end of file From 3a7ae58563613088006fe0cb386d20e4dad76bd2 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 31 Oct 2018 17:23:38 +0100 Subject: [PATCH 0113/1240] Small changes in the buttons to fit with the designs. Contributes to CURA-5786. --- resources/qml/OutputDevicesActionButton.qml | 9 +++++++-- resources/themes/cura-light/theme.json | 2 ++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/resources/qml/OutputDevicesActionButton.qml b/resources/qml/OutputDevicesActionButton.qml index b0cca0eba0..11be11a2b0 100644 --- a/resources/qml/OutputDevicesActionButton.qml +++ b/resources/qml/OutputDevicesActionButton.qml @@ -49,6 +49,7 @@ Item tooltip: catalog.i18nc("@info:tooltip", "Select the active output device") iconSource: popup.opened ? UM.Theme.getIcon("arrow_top") : UM.Theme.getIcon("arrow_bottom") + color: UM.Theme.getColor("action_panel_secondary") visible: (devicesModel.deviceCount > 1) onClicked: popup.opened ? popup.close() : popup.open() @@ -56,6 +57,7 @@ Item Popup { id: popup + padding: 0 y: -height x: parent.width - width @@ -72,7 +74,9 @@ Item { text: model.description color: "transparent" - hoverColor: "red" + outlineColor: "transparent" + cornerRadius: 0 + hoverColor: UM.Theme.getColor("primary") onClicked: { @@ -87,7 +91,8 @@ Item { opacity: visible ? 1 : 0 Behavior on opacity { NumberAnimation { duration: 100 } } - color: UM.Theme.getColor("primary") + radius: UM.Theme.getSize("default_radius") + color: UM.Theme.getColor("action_panel_secondary") border.color: UM.Theme.getColor("lining") border.width: UM.Theme.getSize("default_lining").width } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 1473a75eec..658fa00596 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -103,6 +103,8 @@ "machine_selector_hover": [68, 72, 75, 255], "machine_selector_text_active": [255, 255, 255, 255], + "action_panel_secondary": [27, 95, 202, 255], + "text": [0, 0, 0, 255], "text_detail": [174, 174, 174, 128], "text_link": [50, 130, 255, 255], From dcad95aab9cfb2ec215dc3cb22467fffff2918b7 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 1 Nov 2018 12:44:14 +0100 Subject: [PATCH 0114/1240] Rename Toolbox button to Marketplace. Contributes to CURA-5784. --- resources/qml/MainWindow/MainWindowHeader.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/MainWindow/MainWindowHeader.qml b/resources/qml/MainWindow/MainWindowHeader.qml index 3f088e782f..59ec542e6b 100644 --- a/resources/qml/MainWindow/MainWindowHeader.qml +++ b/resources/qml/MainWindow/MainWindowHeader.qml @@ -83,7 +83,7 @@ Rectangle } leftPadding: UM.Theme.getSize("default_margin").width rightPadding: UM.Theme.getSize("default_margin").width - text: catalog.i18nc("@action:button", "Toolbox") + text: catalog.i18nc("@action:button", "Marketplace") height: Math.round(0.5 * UM.Theme.getSize("main_window_header").height) color: UM.Theme.getColor("main_window_header_secondary_button_background_active") hoverColor: UM.Theme.getColor("main_window_header_secondary_button_background_hovered") From b03695dec66ef5bb10a07222fb11d96e92eb1155 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 2 Nov 2018 09:33:27 +0100 Subject: [PATCH 0115/1240] Add print time information and material costs to the action panel. Contributes to CURA-5786. --- resources/qml/OutputProcessWidget.qml | 33 +++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/resources/qml/OutputProcessWidget.qml b/resources/qml/OutputProcessWidget.qml index d3f306fed5..435cc6da79 100644 --- a/resources/qml/OutputProcessWidget.qml +++ b/resources/qml/OutputProcessWidget.qml @@ -30,6 +30,7 @@ Column Column { id: timeAndCostsInformation + spacing: UM.Theme.getSize("thin_margin").height anchors { @@ -43,7 +44,9 @@ Column id: estimatedTime width: parent.width - text: "Time" + property var printDuration: PrintInformation.currentPrintTime + + text: printDuration.getDisplayString(UM.DurationFormat.Long) source: UM.Theme.getIcon("clock") font: UM.Theme.getFont("small") } @@ -53,7 +56,33 @@ Column id: estimatedCosts width: parent.width - text: "Material costs" + property var printMaterialLengths: PrintInformation.materialLengths + property var printMaterialWeights: PrintInformation.materialWeights + + function getText() + { + var lengths = [] + var weights = [] + if(printMaterialLengths) + { + for(var index = 0; index < printMaterialLengths.length; index++) + { + if(printMaterialLengths[index] > 0) + { + lengths.push(printMaterialLengths[index].toFixed(2)) + weights.push(String(Math.round(printMaterialWeights[index]))) + } + } + } + if(lengths.length == 0) + { + lengths = ["0.00"] + weights = ["0"] + } + return weights.join(" + ") + "g · " + lengths.join(" + ") + "m" + } + + text: getText() source: UM.Theme.getIcon("spool") font: UM.Theme.getFont("very_small") } From 310539cb6dc84e0245a86d3e0b10aa79d413d536 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 2 Nov 2018 09:58:35 +0100 Subject: [PATCH 0116/1240] In the main action panel, only show the total amount of material weight and length (addition of all extruders). The information panel will show each cost separately. Contributes to CURA-5786. --- .../{ => ActionPanel}/ActionPanelWidget.qml | 0 .../OutputDevicesActionButton.qml | 0 .../{ => ActionPanel}/OutputProcessWidget.qml | 21 ++++++++----------- .../{ => ActionPanel}/SliceProcessWidget.qml | 0 4 files changed, 9 insertions(+), 12 deletions(-) rename resources/qml/{ => ActionPanel}/ActionPanelWidget.qml (100%) rename resources/qml/{ => ActionPanel}/OutputDevicesActionButton.qml (100%) rename resources/qml/{ => ActionPanel}/OutputProcessWidget.qml (84%) rename resources/qml/{ => ActionPanel}/SliceProcessWidget.qml (100%) diff --git a/resources/qml/ActionPanelWidget.qml b/resources/qml/ActionPanel/ActionPanelWidget.qml similarity index 100% rename from resources/qml/ActionPanelWidget.qml rename to resources/qml/ActionPanel/ActionPanelWidget.qml diff --git a/resources/qml/OutputDevicesActionButton.qml b/resources/qml/ActionPanel/OutputDevicesActionButton.qml similarity index 100% rename from resources/qml/OutputDevicesActionButton.qml rename to resources/qml/ActionPanel/OutputDevicesActionButton.qml diff --git a/resources/qml/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml similarity index 84% rename from resources/qml/OutputProcessWidget.qml rename to resources/qml/ActionPanel/OutputProcessWidget.qml index 435cc6da79..bef40fdfca 100644 --- a/resources/qml/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -61,25 +61,20 @@ Column function getText() { - var lengths = [] - var weights = [] - if(printMaterialLengths) + var totalLengths = 0 + var totalWeights = 0 + if (printMaterialLengths) { for(var index = 0; index < printMaterialLengths.length; index++) { if(printMaterialLengths[index] > 0) { - lengths.push(printMaterialLengths[index].toFixed(2)) - weights.push(String(Math.round(printMaterialWeights[index]))) + totalLengths += printMaterialLengths[index] + totalWeights += Math.round(printMaterialWeights[index]) } } } - if(lengths.length == 0) - { - lengths = ["0.00"] - weights = ["0"] - } - return weights.join(" + ") + "g · " + lengths.join(" + ") + "m" + return totalWeights + "g · " + totalLengths.toFixed(2) + "m" } text: getText() @@ -116,13 +111,15 @@ Column Cura.ActionButton { + leftPadding: UM.Theme.getSize("default_margin").width + rightPadding: UM.Theme.getSize("default_margin").width height: UM.Theme.getSize("action_panel_button").height text: catalog.i18nc("@button", "Preview") color: UM.Theme.getColor("secondary") hoverColor: UM.Theme.getColor("secondary") textColor: UM.Theme.getColor("primary") textHoverColor: UM.Theme.getColor("text") - onClicked: console.log("Clicking preview") + onClicked: UM.Controller.setActiveStage("MonitorStage") } Cura.OutputDevicesActionButton diff --git a/resources/qml/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml similarity index 100% rename from resources/qml/SliceProcessWidget.qml rename to resources/qml/ActionPanel/SliceProcessWidget.qml From c862e72514230a7ed5eee59ca3257c5c58deb968 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 2 Nov 2018 11:10:23 +0100 Subject: [PATCH 0117/1240] Add the print panel information as a popup that shows when the "i" icon is clicked. Contributes to CURA-5786. --- .../ActionPanel/OutputDevicesActionButton.qml | 2 +- .../qml/ActionPanel/OutputProcessWidget.qml | 16 +---- .../ActionPanel/PrintInformationWidget.qml | 62 +++++++++++++++++++ 3 files changed, 66 insertions(+), 14 deletions(-) create mode 100644 resources/qml/ActionPanel/PrintInformationWidget.qml diff --git a/resources/qml/ActionPanel/OutputDevicesActionButton.qml b/resources/qml/ActionPanel/OutputDevicesActionButton.qml index 11be11a2b0..32d5f8a73b 100644 --- a/resources/qml/ActionPanel/OutputDevicesActionButton.qml +++ b/resources/qml/ActionPanel/OutputDevicesActionButton.qml @@ -91,7 +91,7 @@ Item { opacity: visible ? 1 : 0 Behavior on opacity { NumberAnimation { duration: 100 } } - radius: UM.Theme.getSize("default_radius") + radius: UM.Theme.getSize("default_radius").width color: UM.Theme.getColor("action_panel_secondary") border.color: UM.Theme.getColor("lining") border.width: UM.Theme.getSize("default_lining").width diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml index bef40fdfca..a2f9f13576 100644 --- a/resources/qml/ActionPanel/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -4,7 +4,6 @@ import QtQuick 2.7 import QtQuick.Controls 2.1 import QtQuick.Layouts 1.3 -import QtQuick.Controls 1.4 as Controls1 import UM 1.1 as UM import Cura 1.0 as Cura @@ -35,7 +34,7 @@ Column anchors { left: parent.left - right: moreInformationIcon.left + right: printInformationPanel.left rightMargin: UM.Theme.getSize("thin_margin").height } @@ -83,24 +82,15 @@ Column } } - UM.RecolorImage + PrintInformationWidget { - id: moreInformationIcon + id: printInformationPanel anchors { right: parent.right verticalCenter: timeAndCostsInformation.verticalCenter } - - source: UM.Theme.getIcon("info") - width: UM.Theme.getSize("section_icon").width - height: UM.Theme.getSize("section_icon").height - - sourceSize.width: width - sourceSize.height: height - - color: UM.Theme.getColor("text_medium") } } diff --git a/resources/qml/ActionPanel/PrintInformationWidget.qml b/resources/qml/ActionPanel/PrintInformationWidget.qml new file mode 100644 index 0000000000..f8f91869aa --- /dev/null +++ b/resources/qml/ActionPanel/PrintInformationWidget.qml @@ -0,0 +1,62 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 2.1 + +import UM 1.1 as UM +import Cura 1.0 as Cura + +Button +{ + id: widget + + implicitHeight: UM.Theme.getSize("section_icon").height + implicitWidth: UM.Theme.getSize("section_icon").width + + background: UM.RecolorImage + { + id: moreInformationIcon + + source: UM.Theme.getIcon("info") + width: UM.Theme.getSize("section_icon").width + height: UM.Theme.getSize("section_icon").height + + sourceSize.width: width + sourceSize.height: height + + color: widget.hovered ? UM.Theme.getColor("primary") : UM.Theme.getColor("text_medium") + } + + onClicked: popup.opened ? popup.close() : popup.open() + + Popup + { + id: popup + + y: -(height + UM.Theme.getSize("default_arrow").height + UM.Theme.getSize("thin_margin").height) + x: parent.width - width + UM.Theme.getSize("thin_margin").width + + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent + + contentItem: Label + { + id: panel + text: "Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit, \nsed do eiusmod tempor incididunt \nut labore et dolore magna aliqua. \nUt enim ad minim veniam, ..." + } + + background: UM.PointingRectangle + { + opacity: visible ? 1 : 0 + Behavior on opacity { NumberAnimation { duration: 100 } } + color: UM.Theme.getColor("tool_panel_background") + borderColor: UM.Theme.getColor("lining") + borderWidth: UM.Theme.getSize("default_lining").width + + target: Qt.point(width - (widget.width / 2) - UM.Theme.getSize("thin_margin").width, + height + UM.Theme.getSize("default_arrow").height - UM.Theme.getSize("thin_margin").height) + + arrowSize: UM.Theme.getSize("default_arrow").width + } + } +} \ No newline at end of file From ef7ac3b089cc51712c850c3d1688ecd25cdc55d7 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 2 Nov 2018 13:29:55 +0100 Subject: [PATCH 0118/1240] Create the new component that gathers all the printjob information (times and material usage) in a popup that is shown when the "i" icon is pressed. Contributes to CURA-5786. --- .../ActionPanel/PrintInformationWidget.qml | 6 +- .../qml/ActionPanel/PrintJobInformation.qml | 166 ++++++++++++++++++ resources/themes/cura-light/theme.json | 2 +- 3 files changed, 170 insertions(+), 4 deletions(-) create mode 100644 resources/qml/ActionPanel/PrintJobInformation.qml diff --git a/resources/qml/ActionPanel/PrintInformationWidget.qml b/resources/qml/ActionPanel/PrintInformationWidget.qml index f8f91869aa..620c3a408c 100644 --- a/resources/qml/ActionPanel/PrintInformationWidget.qml +++ b/resources/qml/ActionPanel/PrintInformationWidget.qml @@ -39,10 +39,10 @@ Button closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent - contentItem: Label + contentItem: PrintJobInformation { - id: panel - text: "Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit, \nsed do eiusmod tempor incididunt \nut labore et dolore magna aliqua. \nUt enim ad minim veniam, ..." + id: printJobInformation + width: UM.Theme.getSize("action_panel_information_widget").width } background: UM.PointingRectangle diff --git a/resources/qml/ActionPanel/PrintJobInformation.qml b/resources/qml/ActionPanel/PrintJobInformation.qml new file mode 100644 index 0000000000..0eaa0ca46f --- /dev/null +++ b/resources/qml/ActionPanel/PrintJobInformation.qml @@ -0,0 +1,166 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 2.1 + +import UM 1.1 as UM +import Cura 1.0 as Cura + +Column +{ + id: base + spacing: UM.Theme.getSize("default_margin").width + + UM.I18nCatalog + { + id: catalog + name: "cura" + } + + Column + { + id: timeSpecification + spacing: UM.Theme.getSize("thin_margin").width + width: parent.width + topPadding: UM.Theme.getSize("default_margin").height + leftPadding: UM.Theme.getSize("default_margin").width + rightPadding: UM.Theme.getSize("default_margin").width + + Label + { + text: catalog.i18nc("@label", "Time specification").toUpperCase() + color: UM.Theme.getColor("primary") + font: UM.Theme.getFont("small") + renderType: Text.NativeRendering + } + + Label + { + property var printDuration: PrintInformation.currentPrintTime + + function getTimeSpecifications() + { + // All the time information for the different features is achieved + var printTime = PrintInformation.getFeaturePrintTimes() + var totalSeconds = parseInt(printDuration.getDisplayString(UM.DurationFormat.Seconds)) + + // A message is created and displayed when the user hover the time label + var text = "" + for(var feature in printTime) + { + if(!printTime[feature].isTotalDurationZero) + { + text += "" + + "".arg(printTime[feature].getDisplayString(UM.DurationFormat.ISO8601).slice(0,-3)) + + "".arg(Math.round(100 * parseInt(printTime[feature].getDisplayString(UM.DurationFormat.Seconds)) / totalSeconds)) + + "" + } + } + text += "
" + feature + ":  %1  %1%
" + print(text) + return text + } + + text: getTimeSpecifications() + width: parent.width - 2 * UM.Theme.getSize("default_margin").width + color: UM.Theme.getColor("text") + font: UM.Theme.getFont("very_small") + renderType: Text.NativeRendering + textFormat: Text.RichText + } + } + + Column + { + id: materialSpecification + spacing: UM.Theme.getSize("thin_margin").width + width: parent.width + bottomPadding: UM.Theme.getSize("default_margin").height + leftPadding: UM.Theme.getSize("default_margin").width + rightPadding: UM.Theme.getSize("default_margin").width + + Label + { + text: catalog.i18nc("@label", "Material specification").toUpperCase() + color: UM.Theme.getColor("primary") + font: UM.Theme.getFont("small") + renderType: Text.NativeRendering + } + + Label + { + property var printMaterialLengths: PrintInformation.materialLengths + property var printMaterialWeights: PrintInformation.materialWeights + property var printMaterialCosts: PrintInformation.materialCosts + property var printMaterialNames: PrintInformation.materialNames + + function formatRow(items) + { + var rowHTML = "" + for(var item = 0; item < items.length; item++) + { + if (item == 0) + { + rowHTML += "%1".arg(items[item]) + } + else + { + rowHTML += "  %1".arg(items[item]) + } + } + rowHTML += "" + return rowHTML + } + + function getMaterialSpecifications() + { + var lengths = [] + var weights = [] + var costs = [] + var names = [] + if(printMaterialLengths) + { + for(var index = 0; index < printMaterialLengths.length; index++) + { + if(printMaterialLengths[index] > 0) + { + names.push(printMaterialNames[index]) + lengths.push(printMaterialLengths[index].toFixed(2)) + weights.push(String(Math.round(printMaterialWeights[index]))) + var cost = printMaterialCosts[index] == undefined ? 0 : printMaterialCosts[index].toFixed(2) + costs.push(cost) + } + } + } + if(lengths.length == 0) + { + lengths = ["0.00"] + weights = ["0"] + costs = ["0.00"] + } + + var text = "" + for(var index = 0; index < lengths.length; index++) + { + text += formatRow([ + "%1:".arg(names[index]), + catalog.i18nc("@label m for meter", "%1m").arg(lengths[index]), + catalog.i18nc("@label g for grams", "%1g").arg(weights[index]), + "%1 %2".arg(UM.Preferences.getValue("cura/currency")).arg(costs[index]), + ]) + } + text += "
" + + return text + } + + text: getMaterialSpecifications() + width: parent.width - 2 * UM.Theme.getSize("default_margin").width + color: UM.Theme.getColor("text") + font: UM.Theme.getFont("very_small") + renderType: Text.NativeRendering + textFormat: Text.RichText + } + } +} \ No newline at end of file diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 658fa00596..f354b521ee 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -379,7 +379,7 @@ "configuration_selector_widget": [35.0, 4.5], "configuration_selector_mode_tabs": [0.0, 3.0], - "action_panel_widget": [35.0, 0.0], + "action_panel_information_widget": [20.0, 0.0], "action_panel_button": [15.0, 3.0], "machine_selector_widget": [28.0, 4.5], From 300d109cfe45cd64665536e0113ade0e12ff0260 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 2 Nov 2018 13:37:57 +0100 Subject: [PATCH 0119/1240] Set the Z of the machine selector so the tooltip doesn't get hidden CURA-5772 --- plugins/PrepareStage/PrepareMenu.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index cdcc1384a2..e8514a9ef9 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -45,6 +45,7 @@ Item id: machineSelection width: UM.Theme.getSize("machine_selector_widget").width - configSelection.width height: prepareMenu.height + z: openFileButton.z - 1 } Cura.QuickConfigurationSelector From 34b76596f19931cd7498c837e105d95f6db1d445 Mon Sep 17 00:00:00 2001 From: Shelby Merrick Date: Fri, 2 Nov 2018 09:39:12 -0400 Subject: [PATCH 0120/1240] Updated to reference speed_travel macro --- resources/definitions/wanhao_d4s.def.json | 4 ++-- resources/definitions/wanhao_d6.def.json | 4 ++-- resources/definitions/wanhao_d6_plus.def.json | 4 ++-- resources/definitions/wanhao_duplicator5S.def.json | 4 ++-- resources/definitions/wanhao_duplicator5Smini.def.json | 4 ++-- resources/definitions/wanhao_i3.def.json | 4 ++-- resources/definitions/wanhao_i3mini.def.json | 4 ++-- resources/definitions/wanhao_i3plus.def.json | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/resources/definitions/wanhao_d4s.def.json b/resources/definitions/wanhao_d4s.def.json index 8788353e92..c1807923c6 100644 --- a/resources/definitions/wanhao_d4s.def.json +++ b/resources/definitions/wanhao_d4s.def.json @@ -39,10 +39,10 @@ "default_value": "RepRap (Marlin/Sprinter)" }, "machine_start_gcode": { - "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{travel_speed} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{travel_speed} \n ;Put printing message on LCD screen\n M117 Printing..." + "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{speed_travel} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{speed_travel} \n ;Put printing message on LCD screen\n M117 Printing..." }, "machine_end_gcode": { - "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning" + "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning" } } } diff --git a/resources/definitions/wanhao_d6.def.json b/resources/definitions/wanhao_d6.def.json index 7ca3031124..c8a690d02c 100644 --- a/resources/definitions/wanhao_d6.def.json +++ b/resources/definitions/wanhao_d6.def.json @@ -42,10 +42,10 @@ "default_value": "RepRap (Marlin/Sprinter)" }, "machine_start_gcode": { - "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{travel_speed} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{travel_speed} \n ;Put printing message on LCD screen\n M117 Printing..." + "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{speed_travel} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{speed_travel} \n ;Put printing message on LCD screen\n M117 Printing..." }, "machine_end_gcode": { - "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning" + "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning" } } } diff --git a/resources/definitions/wanhao_d6_plus.def.json b/resources/definitions/wanhao_d6_plus.def.json index f17b58db85..b3b5ed9b0a 100644 --- a/resources/definitions/wanhao_d6_plus.def.json +++ b/resources/definitions/wanhao_d6_plus.def.json @@ -39,10 +39,10 @@ "default_value": "RepRap (Marlin/Sprinter)" }, "machine_start_gcode": { - "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{travel_speed} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{travel_speed} \n ;Put printing message on LCD screen\n M117 Printing..." + "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{speed_travel} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{speed_travel} \n ;Put printing message on LCD screen\n M117 Printing..." }, "machine_end_gcode": { - "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning" + "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning" } } } diff --git a/resources/definitions/wanhao_duplicator5S.def.json b/resources/definitions/wanhao_duplicator5S.def.json index 1d29b90249..b27a13fda8 100644 --- a/resources/definitions/wanhao_duplicator5S.def.json +++ b/resources/definitions/wanhao_duplicator5S.def.json @@ -42,10 +42,10 @@ "default_value": "RepRap (Marlin/Sprinter)" }, "machine_start_gcode": { - "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{travel_speed} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{travel_speed} \n ;Put printing message on LCD screen\n M117 Printing..." + "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{speed_travel} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{speed_travel} \n ;Put printing message on LCD screen\n M117 Printing..." }, "machine_end_gcode": { - "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning" + "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning" } } } diff --git a/resources/definitions/wanhao_duplicator5Smini.def.json b/resources/definitions/wanhao_duplicator5Smini.def.json index e7f9359cf1..e3ef0b92fe 100644 --- a/resources/definitions/wanhao_duplicator5Smini.def.json +++ b/resources/definitions/wanhao_duplicator5Smini.def.json @@ -39,10 +39,10 @@ "default_value": "RepRap (Marlin/Sprinter)" }, "machine_start_gcode": { - "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{travel_speed} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{travel_speed} \n ;Put printing message on LCD screen\n M117 Printing..." + "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{speed_travel} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{speed_travel} \n ;Put printing message on LCD screen\n M117 Printing..." }, "machine_end_gcode": { - "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning" + "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning" } } } diff --git a/resources/definitions/wanhao_i3.def.json b/resources/definitions/wanhao_i3.def.json index 15121f8b8b..42b19c8748 100644 --- a/resources/definitions/wanhao_i3.def.json +++ b/resources/definitions/wanhao_i3.def.json @@ -39,10 +39,10 @@ "default_value": "RepRap (Marlin/Sprinter)" }, "machine_start_gcode": { - "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{travel_speed} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{travel_speed} \n ;Put printing message on LCD screen\n M117 Printing..." + "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{speed_travel} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{speed_travel} \n ;Put printing message on LCD screen\n M117 Printing..." }, "machine_end_gcode": { - "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning" + "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning" } } } diff --git a/resources/definitions/wanhao_i3mini.def.json b/resources/definitions/wanhao_i3mini.def.json index 057fca81a6..0c70391c27 100644 --- a/resources/definitions/wanhao_i3mini.def.json +++ b/resources/definitions/wanhao_i3mini.def.json @@ -39,10 +39,10 @@ "default_value": "RepRap (Marlin/Sprinter)" }, "machine_start_gcode": { - "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{travel_speed} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{travel_speed} \n ;Put printing message on LCD screen\n M117 Printing..." + "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{speed_travel} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{speed_travel} \n ;Put printing message on LCD screen\n M117 Printing..." }, "machine_end_gcode": { - "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning" + "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning" } } } diff --git a/resources/definitions/wanhao_i3plus.def.json b/resources/definitions/wanhao_i3plus.def.json index 2b705c6ff5..e454a40ae1 100644 --- a/resources/definitions/wanhao_i3plus.def.json +++ b/resources/definitions/wanhao_i3plus.def.json @@ -39,10 +39,10 @@ "default_value": "RepRap (Marlin/Sprinter)" }, "machine_start_gcode": { - "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{travel_speed} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{travel_speed} \n ;Put printing message on LCD screen\n M117 Printing..." + "default_value": "G21 ;metric values\n G90 ;absolute positioning\n M82 ;set extruder to absolute mode\n M107 ;start with the fan off\n G28 X0 Y0 ;move X/Y to min endstops\n G28 Z0 ;move Z to min endstops\n G1 Z15.0 F{speed_travel} ;move the platform down 15mm\n G92 E0 ;zero the extruded length\n G1 F200 E6 ;extrude 6 mm of feed stock\n G92 E0 ;zero the extruded length again\n G1 F{speed_travel} \n ;Put printing message on LCD screen\n M117 Printing..." }, "machine_end_gcode": { - "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{travel_speed} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning" + "default_value": "M104 S0 ;extruder heater off \n G91 ;relative positioning\n G1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\n G1 Z+0.5 E-5 X-20 Y-20 F{speed_travel} ;move Z up a bit and retract filament even more\n G28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\n M84 ;steppers off\n G90 ;absolute positioning" } } } From 20b3b6da79aca7c42f935e3e9b6259918c72e63a Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 2 Nov 2018 15:11:49 +0100 Subject: [PATCH 0121/1240] Document PreviewStage.py Some stuff I had to ask the original implementer to explain to me. So I wrote that down so that the next guy doesn't have to do that. Contributes to issue CURA-5829. --- plugins/PreviewStage/PreviewMenu.qml | 3 +++ plugins/PreviewStage/PreviewStage.py | 16 ++++++++++++++++ plugins/PreviewStage/__init__.py | 2 +- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index 5ed0e697a9..66f7d527d9 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -1,3 +1,6 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + import QtQuick 2.7 import QtQuick.Controls 1.4 diff --git a/plugins/PreviewStage/PreviewStage.py b/plugins/PreviewStage/PreviewStage.py index 1aed95162a..1c487c8340 100644 --- a/plugins/PreviewStage/PreviewStage.py +++ b/plugins/PreviewStage/PreviewStage.py @@ -1,5 +1,6 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. + import os.path from UM.Qt.QtApplication import QtApplication @@ -11,6 +12,11 @@ if TYPE_CHECKING: from UM.View.View import View +## Displays a preview of what you're about to print. +# +# The Python component of this stage just loads PreviewMain.qml for display +# when the stage is selected, and makes sure that it reverts to the previous +# view when the previous stage is activated. class PreviewStage(CuraStage): def __init__(self, application: QtApplication, parent = None) -> None: super().__init__(parent) @@ -18,14 +24,24 @@ class PreviewStage(CuraStage): self._application.engineCreatedSignal.connect(self._engineCreated) self._previously_active_view = None # type: Optional[View] + ## When selecting the stage, remember which was the previous view so that + # we can revert to that view when we go out of the stage later. def onStageSelected(self) -> None: self._previously_active_view = self._application.getController().getActiveView() + ## Called when going to a different stage (away from the Preview Stage). + # + # When going to a different stage, the view should be reverted to what it + # was before. Normally, that just reverts it to solid view. def onStageDeselected(self) -> None: if self._previously_active_view is not None: self._application.getController().setActiveView(self._previously_active_view.getPluginId()) self._previously_active_view = None + ## Delayed load of the QML files. + # + # We need to make sure that the QML engine is running before we can load + # these. def _engineCreated(self) -> None: plugin_path = self._application.getPluginRegistry().getPluginPath(self.getPluginId()) if plugin_path is not None: diff --git a/plugins/PreviewStage/__init__.py b/plugins/PreviewStage/__init__.py index d58826934e..424f573e4a 100644 --- a/plugins/PreviewStage/__init__.py +++ b/plugins/PreviewStage/__init__.py @@ -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. from . import PreviewStage From dba31c3af435202ef93363859017927d4a043bd2 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 2 Nov 2018 16:20:04 +0100 Subject: [PATCH 0122/1240] Add documentation. Contributes to CURA-5786. --- resources/qml/ActionPanel/ActionPanelWidget.qml | 4 ++++ resources/qml/ActionPanel/OutputProcessWidget.qml | 5 +++++ resources/qml/ActionPanel/SliceProcessWidget.qml | 4 ++++ 3 files changed, 13 insertions(+) diff --git a/resources/qml/ActionPanel/ActionPanelWidget.qml b/resources/qml/ActionPanel/ActionPanelWidget.qml index 417883af6f..1fc288af6a 100644 --- a/resources/qml/ActionPanel/ActionPanelWidget.qml +++ b/resources/qml/ActionPanel/ActionPanelWidget.qml @@ -8,6 +8,10 @@ import QtQuick.Layouts 1.3 import UM 1.2 as UM import Cura 1.0 as Cura + +// This element hold all the elements needed for the user to trigger the slicing process, and later +// to get information about the printing times, material consumption and the output process (such as +// saving to a file, printing over network, ... Rectangle { id: actionPanelWidget diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml index a2f9f13576..5d5e5bb66c 100644 --- a/resources/qml/ActionPanel/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -8,6 +8,11 @@ import QtQuick.Layouts 1.3 import UM 1.1 as UM import Cura 1.0 as Cura + +// This element contains all the elements the user needs to visualize the data +// that is gather after the slicing process, such as printint time, material usage, ... +// There are also two buttons: one to previsualize the output layers, and the other to +// select what to do with it (such as print over network, save to file, ...) Column { id: widget diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index 3ea6e09975..e70ec45e43 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -9,6 +9,10 @@ import QtQuick.Controls 1.4 as Controls1 import UM 1.1 as UM import Cura 1.0 as Cura + +// This element contains all the elements the user needs to create a printjob from the +// model(s) that is(are) on the buildplate. Mainly the button to start/stop the slicing +// process and a progress bar to see the progress of the process. Column { id: widget From 059e681de59f073870d6858daac7b0cd85528be1 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 2 Nov 2018 16:44:50 +0100 Subject: [PATCH 0123/1240] Remove unused code. Contributes to CURA-5786. --- resources/qml/ActionPanel/OutputDevicesActionButton.qml | 1 - resources/qml/ActionPanel/PrintJobInformation.qml | 1 - resources/qml/Cura.qml | 8 -------- 3 files changed, 10 deletions(-) diff --git a/resources/qml/ActionPanel/OutputDevicesActionButton.qml b/resources/qml/ActionPanel/OutputDevicesActionButton.qml index 32d5f8a73b..87faa0a2bf 100644 --- a/resources/qml/ActionPanel/OutputDevicesActionButton.qml +++ b/resources/qml/ActionPanel/OutputDevicesActionButton.qml @@ -74,7 +74,6 @@ Item { text: model.description color: "transparent" - outlineColor: "transparent" cornerRadius: 0 hoverColor: UM.Theme.getColor("primary") diff --git a/resources/qml/ActionPanel/PrintJobInformation.qml b/resources/qml/ActionPanel/PrintJobInformation.qml index 0eaa0ca46f..df90e81aaf 100644 --- a/resources/qml/ActionPanel/PrintJobInformation.qml +++ b/resources/qml/ActionPanel/PrintJobInformation.qml @@ -58,7 +58,6 @@ Column } } text += "" - print(text) return text } diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 1f4d71e460..f7b4186515 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -314,14 +314,6 @@ UM.MainWindow anchors.bottom: parent.bottom anchors.rightMargin: UM.Theme.getSize("thick_margin").width anchors.bottomMargin: UM.Theme.getSize("thick_margin").height -// onShowTooltip: -// { -// base.showTooltip(item, location, text) -// } -// onHideTooltip: -// { -// base.hideTooltip() -// } } Loader From 2dfd354b25e3187f403cc1cffc028d61eb7bae2e Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 2 Nov 2018 16:49:31 +0100 Subject: [PATCH 0124/1240] Remove function and property that are only used once. Contributes to CURA-5786. --- resources/qml/ActionPanel/OutputProcessWidget.qml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml index 5d5e5bb66c..db9a23cdf3 100644 --- a/resources/qml/ActionPanel/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -48,9 +48,7 @@ Column id: estimatedTime width: parent.width - property var printDuration: PrintInformation.currentPrintTime - - text: printDuration.getDisplayString(UM.DurationFormat.Long) + text: PrintInformation.currentPrintTime.getDisplayString(UM.DurationFormat.Long) source: UM.Theme.getIcon("clock") font: UM.Theme.getFont("small") } @@ -63,7 +61,7 @@ Column property var printMaterialLengths: PrintInformation.materialLengths property var printMaterialWeights: PrintInformation.materialWeights - function getText() + text: { var totalLengths = 0 var totalWeights = 0 @@ -80,8 +78,6 @@ Column } return totalWeights + "g · " + totalLengths.toFixed(2) + "m" } - - text: getText() source: UM.Theme.getIcon("spool") font: UM.Theme.getFont("very_small") } From 6f2e8d726dac8488f6547f39556844afbd3cf4db Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 2 Nov 2018 16:52:17 +0100 Subject: [PATCH 0125/1240] Remove functions that are only used once. Contributes to CURA-5786. --- resources/qml/ActionPanel/PrintJobInformation.qml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/resources/qml/ActionPanel/PrintJobInformation.qml b/resources/qml/ActionPanel/PrintJobInformation.qml index df90e81aaf..e53a92a994 100644 --- a/resources/qml/ActionPanel/PrintJobInformation.qml +++ b/resources/qml/ActionPanel/PrintJobInformation.qml @@ -39,7 +39,7 @@ Column { property var printDuration: PrintInformation.currentPrintTime - function getTimeSpecifications() + text: { // All the time information for the different features is achieved var printTime = PrintInformation.getFeaturePrintTimes() @@ -60,8 +60,6 @@ Column text += "" return text } - - text: getTimeSpecifications() width: parent.width - 2 * UM.Theme.getSize("default_margin").width color: UM.Theme.getColor("text") font: UM.Theme.getFont("very_small") @@ -112,7 +110,7 @@ Column return rowHTML } - function getMaterialSpecifications() + text: { var lengths = [] var weights = [] @@ -153,8 +151,6 @@ Column return text } - - text: getMaterialSpecifications() width: parent.width - 2 * UM.Theme.getSize("default_margin").width color: UM.Theme.getColor("text") font: UM.Theme.getFont("very_small") From 4659b0d38717afb0b693545e19501831d5b79e06 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 5 Nov 2018 11:36:58 +0100 Subject: [PATCH 0126/1240] Fix codestyle --- plugins/SimulationView/SimulationViewMainComponent.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/SimulationView/SimulationViewMainComponent.qml b/plugins/SimulationView/SimulationViewMainComponent.qml index 5c45da6b45..69984f85cb 100644 --- a/plugins/SimulationView/SimulationViewMainComponent.qml +++ b/plugins/SimulationView/SimulationViewMainComponent.qml @@ -72,7 +72,8 @@ Item if(is_simulation_playing) { pauseSimulation() - } else + } + else { resumeSimulation() } From f0e205423f24c4e10cb2d59f4b9ec98d69c58561 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 5 Nov 2018 11:47:22 +0100 Subject: [PATCH 0127/1240] Pause simulation view when preferences have been changed. I accidentally removed this feature when working on CURA-5829, but it was spotted in the review. --- plugins/SimulationView/SimulationViewMainComponent.qml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugins/SimulationView/SimulationViewMainComponent.qml b/plugins/SimulationView/SimulationViewMainComponent.qml index 69984f85cb..f34208e1b5 100644 --- a/plugins/SimulationView/SimulationViewMainComponent.qml +++ b/plugins/SimulationView/SimulationViewMainComponent.qml @@ -61,6 +61,16 @@ Item iconSource: !is_simulation_playing ? "./resources/simulation_resume.svg": "./resources/simulation_pause.svg" style: UM.Theme.styles.small_tool_button visible: !UM.SimulationView.compatibilityMode + + Connections + { + target: UM.Preferences + onPreferenceChanged: + { + playButton.pauseSimulation() + } + } + anchors { right: pathSlider.left From 9f13de9f0d07fde160fc49c7ca9f1f0a62b77336 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 5 Nov 2018 11:48:19 +0100 Subject: [PATCH 0128/1240] No longer create two instances of the setting view. This caused a bunch of issues with the getPluginId, as one of the instances didn't got added to the registry (and as such, never got an ID) CURA-5829 --- plugins/SimulationView/SimulationViewMenuComponent.qml | 2 +- plugins/SimulationView/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/SimulationView/SimulationViewMenuComponent.qml b/plugins/SimulationView/SimulationViewMenuComponent.qml index 5a146910e7..b1613dff85 100644 --- a/plugins/SimulationView/SimulationViewMenuComponent.qml +++ b/plugins/SimulationView/SimulationViewMenuComponent.qml @@ -210,7 +210,7 @@ Item target: UM.Preferences onPreferenceChanged: { - layerTypeCombobox.currentIndex = UM.SimulationView.compatibilityMode ? 1 : UM.Preferences.getValue("layerview/layer_view_type"); + layerTypeCombobox.currentIndex = UM.SimulationView.compatibilityMode ? 1 : UM.Preferences.getValue("layerview/layer_view_type") layerTypeCombobox.updateLegends(layerTypeCombobox.currentIndex) viewSettings.extruder_opacities = UM.Preferences.getValue("layerview/extruder_opacities").split("|") viewSettings.show_travel_moves = UM.Preferences.getValue("layerview/show_travel_moves") diff --git a/plugins/SimulationView/__init__.py b/plugins/SimulationView/__init__.py index 1efb7fd650..420ee60660 100644 --- a/plugins/SimulationView/__init__.py +++ b/plugins/SimulationView/__init__.py @@ -25,4 +25,4 @@ def createSimulationViewProxy(engine, script_engine): def register(app): simulation_view = SimulationView.SimulationView() qmlRegisterSingletonType(SimulationViewProxy.SimulationViewProxy, "UM", 1, 0, "SimulationView", simulation_view.getProxy) - return { "view": SimulationView.SimulationView()} + return { "view": simulation_view} From 406dd68aa4151bdc40dde21f1b66e071038b9fbb Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 5 Nov 2018 11:54:55 +0100 Subject: [PATCH 0129/1240] Made path slider widht themable. CURA-5829 --- plugins/SimulationView/SimulationViewMainComponent.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/SimulationView/SimulationViewMainComponent.qml b/plugins/SimulationView/SimulationViewMainComponent.qml index f34208e1b5..784938e5c2 100644 --- a/plugins/SimulationView/SimulationViewMainComponent.qml +++ b/plugins/SimulationView/SimulationViewMainComponent.qml @@ -18,7 +18,7 @@ Item { id: pathSlider height: UM.Theme.getSize("slider_handle").width - width: 250 + width: UM.Theme.getSize("layerview_menu_size").width anchors.bottom: parent.bottom anchors.bottomMargin: UM.Theme.getSize("default_margin").height From 2bb7b352efca961ca3f87a38251084e3a0b83914 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 5 Nov 2018 13:25:47 +0100 Subject: [PATCH 0130/1240] Hide settings when pre-sliced g-code is loaded CURA-5772 --- resources/qml/PrintSetupSelector.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/qml/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector.qml index 987e3ecce9..8232e76a17 100644 --- a/resources/qml/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector.qml @@ -106,10 +106,10 @@ Rectangle model: modesListModel width: Math.round(parent.width * 0.55) height: UM.Theme.getSize("print_setup_mode_toggle").height + visible: !hideSettings anchors.right: parent.right anchors.rightMargin: UM.Theme.getSize("thick_margin").width - anchors.top: settingsModeLabel.top ButtonGroup @@ -191,6 +191,8 @@ Rectangle anchors.right: parent.right height: UM.Theme.getSize("print_setup_widget").height + visible: !hideSettings + // We load both of them at once (instead of using a loader) because the advanced sidebar can take // quite some time to load. So in this case we sacrifice memory for speed. SidebarAdvanced From 99bac25ab2d7a75ac5e6c9bcceebe75367babc28 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 5 Nov 2018 14:55:22 +0100 Subject: [PATCH 0131/1240] Show default avatar if user has no avatar picture set CURA-5784 --- resources/qml/Account/AccountDetails.qml | 14 +++++++++++++- resources/qml/Account/AccountWidget.qml | 14 +++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/resources/qml/Account/AccountDetails.qml b/resources/qml/Account/AccountDetails.qml index c723ca20fb..a288426e0c 100644 --- a/resources/qml/Account/AccountDetails.qml +++ b/resources/qml/Account/AccountDetails.qml @@ -11,6 +11,7 @@ Column { property var profile: null property var loggedIn: false + property var profileImage: "" padding: 2 * UM.Theme.getSize("default_margin").height spacing: 2 * UM.Theme.getSize("default_margin").height @@ -21,7 +22,18 @@ Column width: UM.Theme.getSize("avatar_image").width height: UM.Theme.getSize("avatar_image").height anchors.horizontalCenter: parent.horizontalCenter - source: loggedIn ? profile["profile_image_url"] : UM.Theme.getImage("avatar_no_user") + source: + { + if(loggedIn) + { + if(profileImage) + { + return profileImage + } + return UM.Theme.getImage("avatar_no_user") + } + return UM.Theme.getImage("avatar_no_user") + } outlineColor: loggedIn ? UM.Theme.getColor("account_widget_outline_active") : UM.Theme.getColor("lining") } diff --git a/resources/qml/Account/AccountWidget.qml b/resources/qml/Account/AccountWidget.qml index cdae457ac8..e37523296f 100644 --- a/resources/qml/Account/AccountWidget.qml +++ b/resources/qml/Account/AccountWidget.qml @@ -25,7 +25,18 @@ Button anchors.verticalCenter: accountWidget.verticalCenter anchors.horizontalCenter: accountWidget.horizontalCenter - source: loggedIn ? profile["profile_image_url"] : UM.Theme.getImage("avatar_no_user") + source: + { + if(loggedIn) + { + if(profile["profile_image_url"]) + { + return profile["profile_image_url"] + } + return UM.Theme.getImage("avatar_no_user") + } + return UM.Theme.getImage("avatar_no_user") + } outlineColor: loggedIn ? UM.Theme.getColor("account_widget_outline_active") : UM.Theme.getColor("lining") } @@ -45,6 +56,7 @@ Button id: panel profile: Cura.API.account.userProfile loggedIn: Cura.API.account.isLoggedIn + profileImage: Cura.API.account.profileImageUrl } background: UM.PointingRectangle From 8106df1ae2659da6cf460cb49a23e631be202f3b Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 5 Nov 2018 16:26:35 +0100 Subject: [PATCH 0132/1240] Bit of QML cleanup --- .../SimulationViewMenuComponent.qml | 922 +++++++++--------- resources/themes/cura-light/theme.json | 2 - 2 files changed, 454 insertions(+), 470 deletions(-) diff --git a/plugins/SimulationView/SimulationViewMenuComponent.qml b/plugins/SimulationView/SimulationViewMenuComponent.qml index b1613dff85..62f353a241 100644 --- a/plugins/SimulationView/SimulationViewMenuComponent.qml +++ b/plugins/SimulationView/SimulationViewMenuComponent.qml @@ -9,32 +9,21 @@ import QtQuick.Controls.Styles 1.1 import UM 1.0 as UM import Cura 1.0 as Cura -Item +Rectangle { id: base - width: + + color: UM.Theme.getColor("tool_panel_background") + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") + + width: UM.Theme.getSize("layerview_menu_size").width + + height: { - if (UM.SimulationView.compatibilityMode) - { - return UM.Theme.getSize("layerview_menu_size_compatibility").width; - } - else - { - return UM.Theme.getSize("layerview_menu_size").width; - } - } - height: { if (viewSettings.collapsed) { - if (UM.SimulationView.compatibilityMode) - { - return UM.Theme.getSize("layerview_menu_size_compatibility_collapsed").height; - } - return UM.Theme.getSize("layerview_menu_size_collapsed").height; - } - else if (UM.SimulationView.compatibilityMode) - { - return UM.Theme.getSize("layerview_menu_size_compatibility").height; + return UM.Theme.getSize("layerview_menu_size_collapsed").height } else if (UM.Preferences.getValue("layerview/layer_view_type") == 0) { @@ -45,6 +34,7 @@ Item return UM.Theme.getSize("layerview_menu_size").height + UM.SimulationView.extruderCount * (UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("layerview_row_spacing").height) } } + Behavior on height { NumberAnimation { duration: 100 } } property var buttonTarget: @@ -57,514 +47,510 @@ Item return Qt.point(0,0) } - Rectangle + Connections { - id: layerViewMenu - anchors.right: parent.right - anchors.top: parent.top - width: parent.width - height: parent.height - clip: true - z: layerSlider.z - 1 - color: UM.Theme.getColor("tool_panel_background") - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("lining") - - Button + target: UM.Preferences + onPreferenceChanged: { - id: collapseButton - anchors.top: parent.top - anchors.topMargin: Math.round(UM.Theme.getSize("default_margin").height + (UM.Theme.getSize("layerview_row").height - UM.Theme.getSize("default_margin").height) / 2) - anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("default_margin").width + layerTypeCombobox.currentIndex = UM.SimulationView.compatibilityMode ? 1 : UM.Preferences.getValue("layerview/layer_view_type") + layerTypeCombobox.updateLegends(layerTypeCombobox.currentIndex) + viewSettings.extruder_opacities = UM.Preferences.getValue("layerview/extruder_opacities").split("|") + viewSettings.show_travel_moves = UM.Preferences.getValue("layerview/show_travel_moves") + viewSettings.show_helpers = UM.Preferences.getValue("layerview/show_helpers") + viewSettings.show_skin = UM.Preferences.getValue("layerview/show_skin") + viewSettings.show_infill = UM.Preferences.getValue("layerview/show_infill") + viewSettings.only_show_top_layers = UM.Preferences.getValue("view/only_show_top_layers") + viewSettings.top_layer_count = UM.Preferences.getValue("view/top_layer_count") + } + } - width: UM.Theme.getSize("standard_arrow").width - height: UM.Theme.getSize("standard_arrow").height + Button + { + id: collapseButton - onClicked: viewSettings.collapsed = !viewSettings.collapsed - - style: ButtonStyle - { - background: UM.RecolorImage - { - width: control.width - height: control.height - sourceSize.width: width - sourceSize.height: width - color: UM.Theme.getColor("setting_control_text") - source: viewSettings.collapsed ? UM.Theme.getIcon("arrow_left") : UM.Theme.getIcon("arrow_bottom") - } - label: Label{ } - } + anchors + { + top: parent.top + topMargin: Math.round(UM.Theme.getSize("default_margin").height + (UM.Theme.getSize("layerview_row").height - UM.Theme.getSize("default_margin").height) / 2) + right: parent.right + rightMargin: UM.Theme.getSize("default_margin").width } - Column + width: UM.Theme.getSize("standard_arrow").width + height: UM.Theme.getSize("standard_arrow").height + + onClicked: viewSettings.collapsed = !viewSettings.collapsed + + style: ButtonStyle { - id: viewSettings - - property bool collapsed: false - property var extruder_opacities: UM.Preferences.getValue("layerview/extruder_opacities").split("|") - property bool show_travel_moves: UM.Preferences.getValue("layerview/show_travel_moves") - property bool show_helpers: UM.Preferences.getValue("layerview/show_helpers") - property bool show_skin: UM.Preferences.getValue("layerview/show_skin") - property bool show_infill: UM.Preferences.getValue("layerview/show_infill") - // if we are in compatibility mode, we only show the "line type" - property bool show_legend: UM.SimulationView.compatibilityMode ? true : UM.Preferences.getValue("layerview/layer_view_type") == 1 - property bool show_gradient: UM.SimulationView.compatibilityMode ? false : UM.Preferences.getValue("layerview/layer_view_type") == 2 || UM.Preferences.getValue("layerview/layer_view_type") == 3 - property bool show_feedrate_gradient: show_gradient && UM.Preferences.getValue("layerview/layer_view_type") == 2 - property bool show_thickness_gradient: show_gradient && UM.Preferences.getValue("layerview/layer_view_type") == 3 - property bool only_show_top_layers: UM.Preferences.getValue("view/only_show_top_layers") - property int top_layer_count: UM.Preferences.getValue("view/top_layer_count") - - anchors.top: parent.top - anchors.topMargin: UM.Theme.getSize("default_margin").height - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("default_margin").width - anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("default_margin").width - spacing: UM.Theme.getSize("layerview_row_spacing").height - - Label + background: UM.RecolorImage { - id: layerViewTypesLabel - text: catalog.i18nc("@label","Color scheme") - font: UM.Theme.getFont("default"); - visible: !UM.SimulationView.compatibilityMode - width: parent.width - color: UM.Theme.getColor("setting_control_text") + width: control.width + height: control.height + sourceSize.width: width + sourceSize.height: width + color: UM.Theme.getColor("setting_control_text") + source: viewSettings.collapsed ? UM.Theme.getIcon("arrow_left") : UM.Theme.getIcon("arrow_bottom") } + label: Label{ } + } + } - ListModel // matches SimulationView.py + Column + { + id: viewSettings + + property bool collapsed: false + property var extruder_opacities: UM.Preferences.getValue("layerview/extruder_opacities").split("|") + property bool show_travel_moves: UM.Preferences.getValue("layerview/show_travel_moves") + property bool show_helpers: UM.Preferences.getValue("layerview/show_helpers") + property bool show_skin: UM.Preferences.getValue("layerview/show_skin") + property bool show_infill: UM.Preferences.getValue("layerview/show_infill") + + // If we are in compatibility mode, we only show the "line type" + property bool show_legend: UM.SimulationView.compatibilityMode ? true : UM.Preferences.getValue("layerview/layer_view_type") == 1 + property bool show_gradient: UM.SimulationView.compatibilityMode ? false : UM.Preferences.getValue("layerview/layer_view_type") == 2 || UM.Preferences.getValue("layerview/layer_view_type") == 3 + property bool show_feedrate_gradient: show_gradient && UM.Preferences.getValue("layerview/layer_view_type") == 2 + property bool show_thickness_gradient: show_gradient && UM.Preferences.getValue("layerview/layer_view_type") == 3 + property bool only_show_top_layers: UM.Preferences.getValue("view/only_show_top_layers") + property int top_layer_count: UM.Preferences.getValue("view/top_layer_count") + + anchors + { + top: parent.top + left: parent.left + right: parent.right + margins: UM.Theme.getSize("default_margin").height + } + + spacing: UM.Theme.getSize("layerview_row_spacing").height + + Label + { + id: layerViewTypesLabel + text: catalog.i18nc("@label","Color scheme") + font: UM.Theme.getFont("default"); + visible: !UM.SimulationView.compatibilityMode + width: parent.width + color: UM.Theme.getColor("setting_control_text") + } + + ListModel // matches SimulationView.py + { + id: layerViewTypes + } + + Component.onCompleted: + { + layerViewTypes.append({ + text: catalog.i18nc("@label:listbox", "Material Color"), + type_id: 0 + }) + layerViewTypes.append({ + text: catalog.i18nc("@label:listbox", "Line Type"), + type_id: 1 + }) + layerViewTypes.append({ + text: catalog.i18nc("@label:listbox", "Feedrate"), + type_id: 2 + }) + layerViewTypes.append({ + text: catalog.i18nc("@label:listbox", "Layer thickness"), + type_id: 3 // these ids match the switching in the shader + }) + } + + ComboBox + { + id: layerTypeCombobox + width: parent.width + model: layerViewTypes + visible: !UM.SimulationView.compatibilityMode + style: UM.Theme.styles.combobox + + onActivated: { - id: layerViewTypes + UM.Preferences.setValue("layerview/layer_view_type", index); } Component.onCompleted: { - layerViewTypes.append({ - text: catalog.i18nc("@label:listbox", "Material Color"), - type_id: 0 - }) - layerViewTypes.append({ - text: catalog.i18nc("@label:listbox", "Line Type"), - type_id: 1 - }) - layerViewTypes.append({ - text: catalog.i18nc("@label:listbox", "Feedrate"), - type_id: 2 - }) - layerViewTypes.append({ - text: catalog.i18nc("@label:listbox", "Layer thickness"), - type_id: 3 // these ids match the switching in the shader - }) + currentIndex = UM.SimulationView.compatibilityMode ? 1 : UM.Preferences.getValue("layerview/layer_view_type"); + updateLegends(currentIndex); } - ComboBox + function updateLegends(type_id) { - id: layerTypeCombobox - width: parent.width - model: layerViewTypes - visible: !UM.SimulationView.compatibilityMode - style: UM.Theme.styles.combobox + // Update the visibility of the legends. + viewSettings.show_legend = UM.SimulationView.compatibilityMode || (type_id == 1); + viewSettings.show_gradient = !UM.SimulationView.compatibilityMode && (type_id == 2 || type_id == 3); + viewSettings.show_feedrate_gradient = viewSettings.show_gradient && (type_id == 2); + viewSettings.show_thickness_gradient = viewSettings.show_gradient && (type_id == 3); + } + } - onActivated: + Label + { + id: compatibilityModeLabel + text: catalog.i18nc("@label","Compatibility Mode") + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + visible: UM.SimulationView.compatibilityMode + height: UM.Theme.getSize("layerview_row").height + width: parent.width + } + + Item // Spacer + { + height: Math.round(UM.Theme.getSize("default_margin").width / 2) + width: width + } + + Repeater + { + model: Cura.ExtrudersModel{} + + CheckBox + { + id: extrudersModelCheckBox + checked: viewSettings.extruder_opacities[index] > 0.5 || viewSettings.extruder_opacities[index] == undefined || viewSettings.extruder_opacities[index] == "" + height: UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("default_lining").height + width: parent.width + visible: !UM.SimulationView.compatibilityMode + enabled: index < 4 + + onClicked: { - UM.Preferences.setValue("layerview/layer_view_type", index); + viewSettings.extruder_opacities[index] = checked ? 1.0 : 0.0 + UM.Preferences.setValue("layerview/extruder_opacities", viewSettings.extruder_opacities.join("|")); } + style: UM.Theme.styles.checkbox + + Rectangle + { + anchors.verticalCenter: parent.verticalCenter + anchors.right: extrudersModelCheckBox.right + width: UM.Theme.getSize("layerview_legend_size").width + height: UM.Theme.getSize("layerview_legend_size").height + color: model.color + radius: Math.round(width / 2) + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") + visible: !viewSettings.show_legend && !viewSettings.show_gradient + } + + Label + { + text: model.name + elide: Text.ElideRight + color: UM.Theme.getColor("setting_control_text") + font: UM.Theme.getFont("default") + anchors + { + verticalCenter: parent.verticalCenter + left: extrudersModelCheckBox.left + right: extrudersModelCheckBox.right + leftMargin: UM.Theme.getSize("checkbox").width + Math.round(UM.Theme.getSize("default_margin").width / 2) + rightMargin: UM.Theme.getSize("default_margin").width * 2 + } + + } + } + } + + Repeater + { + model: ListModel + { + id: typesLegendModel Component.onCompleted: { - currentIndex = UM.SimulationView.compatibilityMode ? 1 : UM.Preferences.getValue("layerview/layer_view_type"); - updateLegends(currentIndex); - } - - function updateLegends(type_id) - { - // update visibility of legends - viewSettings.show_legend = UM.SimulationView.compatibilityMode || (type_id == 1); - viewSettings.show_gradient = !UM.SimulationView.compatibilityMode && (type_id == 2 || type_id == 3); - viewSettings.show_feedrate_gradient = viewSettings.show_gradient && (type_id == 2); - viewSettings.show_thickness_gradient = viewSettings.show_gradient && (type_id == 3); + typesLegendModel.append({ + label: catalog.i18nc("@label", "Show Travels"), + initialValue: viewSettings.show_travel_moves, + preference: "layerview/show_travel_moves", + colorId: "layerview_move_combing" + }); + typesLegendModel.append({ + label: catalog.i18nc("@label", "Show Helpers"), + initialValue: viewSettings.show_helpers, + preference: "layerview/show_helpers", + colorId: "layerview_support" + }); + typesLegendModel.append({ + label: catalog.i18nc("@label", "Show Shell"), + initialValue: viewSettings.show_skin, + preference: "layerview/show_skin", + colorId: "layerview_inset_0" + }); + typesLegendModel.append({ + label: catalog.i18nc("@label", "Show Infill"), + initialValue: viewSettings.show_infill, + preference: "layerview/show_infill", + colorId: "layerview_infill" + }); } } - Label + CheckBox { - id: compatibilityModeLabel - text: catalog.i18nc("@label","Compatibility Mode") - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - visible: UM.SimulationView.compatibilityMode - height: UM.Theme.getSize("layerview_row").height + id: legendModelCheckBox + checked: model.initialValue + onClicked: UM.Preferences.setValue(model.preference, checked) + height: UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("default_lining").height width: parent.width - } - Item - { - height: Math.round(UM.Theme.getSize("default_margin").width / 2) - width: width - } - - Connections - { - target: UM.Preferences - onPreferenceChanged: - { - layerTypeCombobox.currentIndex = UM.SimulationView.compatibilityMode ? 1 : UM.Preferences.getValue("layerview/layer_view_type") - layerTypeCombobox.updateLegends(layerTypeCombobox.currentIndex) - viewSettings.extruder_opacities = UM.Preferences.getValue("layerview/extruder_opacities").split("|") - viewSettings.show_travel_moves = UM.Preferences.getValue("layerview/show_travel_moves") - viewSettings.show_helpers = UM.Preferences.getValue("layerview/show_helpers") - viewSettings.show_skin = UM.Preferences.getValue("layerview/show_skin") - viewSettings.show_infill = UM.Preferences.getValue("layerview/show_infill") - viewSettings.only_show_top_layers = UM.Preferences.getValue("view/only_show_top_layers") - viewSettings.top_layer_count = UM.Preferences.getValue("view/top_layer_count") - } - } - - Repeater - { - model: Cura.ExtrudersModel{} - CheckBox - { - id: extrudersModelCheckBox - checked: viewSettings.extruder_opacities[index] > 0.5 || viewSettings.extruder_opacities[index] == undefined || viewSettings.extruder_opacities[index] == "" - onClicked: - { - viewSettings.extruder_opacities[index] = checked ? 1.0 : 0.0 - UM.Preferences.setValue("layerview/extruder_opacities", viewSettings.extruder_opacities.join("|")); - } - visible: !UM.SimulationView.compatibilityMode - enabled: index + 1 <= 4 - Rectangle - { - anchors.verticalCenter: parent.verticalCenter - anchors.right: extrudersModelCheckBox.right - width: UM.Theme.getSize("layerview_legend_size").width - height: UM.Theme.getSize("layerview_legend_size").height - color: model.color - radius: Math.round(width / 2) - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("lining") - visible: !viewSettings.show_legend & !viewSettings.show_gradient - } - height: UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("default_lining").height - width: parent.width - - style: UM.Theme.styles.checkbox - Label - { - text: model.name - elide: Text.ElideRight - color: UM.Theme.getColor("setting_control_text") - font: UM.Theme.getFont("default") - anchors.verticalCenter: parent.verticalCenter - anchors.left: extrudersModelCheckBox.left - anchors.right: extrudersModelCheckBox.right - anchors.leftMargin: UM.Theme.getSize("checkbox").width + Math.round(UM.Theme.getSize("default_margin").width / 2) - anchors.rightMargin: UM.Theme.getSize("default_margin").width * 2 - } - } - } - - Repeater - { - model: ListModel - { - id: typesLegendModel - Component.onCompleted: - { - typesLegendModel.append({ - label: catalog.i18nc("@label", "Show Travels"), - initialValue: viewSettings.show_travel_moves, - preference: "layerview/show_travel_moves", - colorId: "layerview_move_combing" - }); - typesLegendModel.append({ - label: catalog.i18nc("@label", "Show Helpers"), - initialValue: viewSettings.show_helpers, - preference: "layerview/show_helpers", - colorId: "layerview_support" - }); - typesLegendModel.append({ - label: catalog.i18nc("@label", "Show Shell"), - initialValue: viewSettings.show_skin, - preference: "layerview/show_skin", - colorId: "layerview_inset_0" - }); - typesLegendModel.append({ - label: catalog.i18nc("@label", "Show Infill"), - initialValue: viewSettings.show_infill, - preference: "layerview/show_infill", - colorId: "layerview_infill" - }); - } - } - - CheckBox - { - id: legendModelCheckBox - checked: model.initialValue - onClicked: - { - UM.Preferences.setValue(model.preference, checked); - } - Rectangle - { - anchors.verticalCenter: parent.verticalCenter - anchors.right: legendModelCheckBox.right - width: UM.Theme.getSize("layerview_legend_size").width - height: UM.Theme.getSize("layerview_legend_size").height - color: UM.Theme.getColor(model.colorId) - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("lining") - visible: viewSettings.show_legend - } - height: UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("default_lining").height - width: parent.width - style: UM.Theme.styles.checkbox - Label - { - text: label - font: UM.Theme.getFont("default") - elide: Text.ElideRight - color: UM.Theme.getColor("setting_control_text") - anchors.verticalCenter: parent.verticalCenter - anchors.left: legendModelCheckBox.left - anchors.right: legendModelCheckBox.right - anchors.leftMargin: UM.Theme.getSize("checkbox").width + Math.round(UM.Theme.getSize("default_margin").width / 2) - anchors.rightMargin: UM.Theme.getSize("default_margin").width * 2 - } - } - } - - CheckBox - { - checked: viewSettings.only_show_top_layers - onClicked: - { - UM.Preferences.setValue("view/only_show_top_layers", checked ? 1.0 : 0.0) - } - text: catalog.i18nc("@label", "Only Show Top Layers") - visible: UM.SimulationView.compatibilityMode style: UM.Theme.styles.checkbox - } - CheckBox - { - checked: viewSettings.top_layer_count == 5 - onClicked: - { - UM.Preferences.setValue("view/top_layer_count", checked ? 5 : 1) - } - text: catalog.i18nc("@label", "Show 5 Detailed Layers On Top") - visible: UM.SimulationView.compatibilityMode - style: UM.Theme.styles.checkbox - } - Repeater - { - model: ListModel + Rectangle { - id: typesLegendModelNoCheck - Component.onCompleted: - { - typesLegendModelNoCheck.append({ - label: catalog.i18nc("@label", "Top / Bottom"), - colorId: "layerview_skin", - }); - typesLegendModelNoCheck.append({ - label: catalog.i18nc("@label", "Inner Wall"), - colorId: "layerview_inset_x", - }); - } + anchors.verticalCenter: parent.verticalCenter + anchors.right: legendModelCheckBox.right + width: UM.Theme.getSize("layerview_legend_size").width + height: UM.Theme.getSize("layerview_legend_size").height + color: UM.Theme.getColor(model.colorId) + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") + visible: viewSettings.show_legend } Label { text: label - visible: viewSettings.show_legend - id: typesLegendModelLabel - Rectangle - { - anchors.verticalCenter: parent.verticalCenter - anchors.right: typesLegendModelLabel.right - width: UM.Theme.getSize("layerview_legend_size").width - height: UM.Theme.getSize("layerview_legend_size").height - color: UM.Theme.getColor(model.colorId) - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("lining") - visible: viewSettings.show_legend - } - height: UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("default_lining").height - width: parent.width - color: UM.Theme.getColor("setting_control_text") font: UM.Theme.getFont("default") + elide: Text.ElideRight + color: UM.Theme.getColor("setting_control_text") + anchors.verticalCenter: parent.verticalCenter + anchors.left: legendModelCheckBox.left + anchors.right: legendModelCheckBox.right + anchors.leftMargin: UM.Theme.getSize("checkbox").width + Math.round(UM.Theme.getSize("default_margin").width / 2) + anchors.rightMargin: UM.Theme.getSize("default_margin").width * 2 + } + } + } + + CheckBox + { + checked: viewSettings.only_show_top_layers + onClicked: UM.Preferences.setValue("view/only_show_top_layers", checked ? 1.0 : 0.0) + text: catalog.i18nc("@label", "Only Show Top Layers") + visible: UM.SimulationView.compatibilityMode + style: UM.Theme.styles.checkbox + } + + CheckBox + { + checked: viewSettings.top_layer_count == 5 + onClicked: UM.Preferences.setValue("view/top_layer_count", checked ? 5 : 1) + text: catalog.i18nc("@label", "Show 5 Detailed Layers On Top") + visible: UM.SimulationView.compatibilityMode + style: UM.Theme.styles.checkbox + } + + Repeater + { + model: ListModel + { + id: typesLegendModelNoCheck + Component.onCompleted: + { + typesLegendModelNoCheck.append({ + label: catalog.i18nc("@label", "Top / Bottom"), + colorId: "layerview_skin", + }); + typesLegendModelNoCheck.append({ + label: catalog.i18nc("@label", "Inner Wall"), + colorId: "layerview_inset_x", + }); } } - // Text for the minimum, maximum and units for the feedrates and layer thickness - Item + Label { - id: gradientLegend - visible: viewSettings.show_gradient + text: label + visible: viewSettings.show_legend + id: typesLegendModelLabel + + height: UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("default_lining").height width: parent.width - height: UM.Theme.getSize("layerview_row").height + color: UM.Theme.getColor("setting_control_text") + font: UM.Theme.getFont("default") - Label + Rectangle { - text: minText() - anchors.left: parent.left - color: UM.Theme.getColor("setting_control_text") - font: UM.Theme.getFont("default") + anchors.verticalCenter: parent.verticalCenter + anchors.right: typesLegendModelLabel.right - function minText() - { - if (UM.SimulationView.layerActivity && CuraApplication.platformActivity) - { - // Feedrate selected - if (UM.Preferences.getValue("layerview/layer_view_type") == 2) - { - return parseFloat(UM.SimulationView.getMinFeedrate()).toFixed(2) - } - // Layer thickness selected - if (UM.Preferences.getValue("layerview/layer_view_type") == 3) - { - return parseFloat(UM.SimulationView.getMinThickness()).toFixed(2) - } - } - return catalog.i18nc("@label","min") - } - } + width: UM.Theme.getSize("layerview_legend_size").width + height: UM.Theme.getSize("layerview_legend_size").height - Label - { - text: unitsText() - anchors.horizontalCenter: parent.horizontalCenter - color: UM.Theme.getColor("setting_control_text") - font: UM.Theme.getFont("default") + color: UM.Theme.getColor(model.colorId) - function unitsText() - { - if (UM.SimulationView.layerActivity && CuraApplication.platformActivity) - { - // Feedrate selected - if (UM.Preferences.getValue("layerview/layer_view_type") == 2) - { - return "mm/s" - } - // Layer thickness selected - if (UM.Preferences.getValue("layerview/layer_view_type") == 3) - { - return "mm" - } - } - return "" - } - } - - Label - { - text: maxText() - anchors.right: parent.right - color: UM.Theme.getColor("setting_control_text") - font: UM.Theme.getFont("default") - - function maxText() - { - if (UM.SimulationView.layerActivity && CuraApplication.platformActivity) - { - // Feedrate selected - if (UM.Preferences.getValue("layerview/layer_view_type") == 2) - { - return parseFloat(UM.SimulationView.getMaxFeedrate()).toFixed(2) - } - // Layer thickness selected - if (UM.Preferences.getValue("layerview/layer_view_type") == 3) - { - return parseFloat(UM.SimulationView.getMaxThickness()).toFixed(2) - } - } - return catalog.i18nc("@label","max") - } + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") } } + } - // Gradient colors for feedrate - Rectangle - { // In QML 5.9 can be changed by LinearGradient - // Invert values because then the bar is rotated 90 degrees - id: feedrateGradient - visible: viewSettings.show_feedrate_gradient - anchors.left: parent.right - height: parent.width - width: Math.round(UM.Theme.getSize("layerview_row").height * 1.5) - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("lining") - transform: Rotation {origin.x: 0; origin.y: 0; angle: 90} - gradient: Gradient - { - GradientStop - { - position: 0.000 - color: Qt.rgba(1, 0.5, 0, 1) - } - GradientStop - { - position: 0.625 - color: Qt.rgba(0.375, 0.5, 0, 1) - } - GradientStop - { - position: 0.75 - color: Qt.rgba(0.25, 1, 0, 1) - } - GradientStop - { - position: 1.0 - color: Qt.rgba(0, 0, 1, 1) - } - } - } + // Text for the minimum, maximum and units for the feedrates and layer thickness + Item + { + id: gradientLegend + visible: viewSettings.show_gradient + width: parent.width + height: UM.Theme.getSize("layerview_row").height - // Gradient colors for layer thickness (similar to parula colormap) - Rectangle // In QML 5.9 can be changed by LinearGradient + Label { - // Invert values because then the bar is rotated 90 degrees - id: thicknessGradient - visible: viewSettings.show_thickness_gradient - anchors.left: parent.right - height: parent.width - width: Math.round(UM.Theme.getSize("layerview_row").height * 1.5) - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("lining") - transform: Rotation {origin.x: 0; origin.y: 0; angle: 90} - gradient: Gradient + text: { - GradientStop + if (UM.SimulationView.layerActivity && CuraApplication.platformActivity) { - position: 0.000 - color: Qt.rgba(1, 1, 0, 1) + // Feedrate selected + if (UM.Preferences.getValue("layerview/layer_view_type") == 2) + { + return parseFloat(UM.SimulationView.getMinFeedrate()).toFixed(2) + } + // Layer thickness selected + if (UM.Preferences.getValue("layerview/layer_view_type") == 3) + { + return parseFloat(UM.SimulationView.getMinThickness()).toFixed(2) + } } - GradientStop + return catalog.i18nc("@label","min") + } + anchors.left: parent.left + color: UM.Theme.getColor("setting_control_text") + font: UM.Theme.getFont("default") + } + + Label + { + text: { + if (UM.SimulationView.layerActivity && CuraApplication.platformActivity) { - position: 0.25 - color: Qt.rgba(1, 0.75, 0.25, 1) + // Feedrate selected + if (UM.Preferences.getValue("layerview/layer_view_type") == 2) + { + return "mm/s" + } + // Layer thickness selected + if (UM.Preferences.getValue("layerview/layer_view_type") == 3) + { + return "mm" + } } - GradientStop + return "" + } + + anchors.horizontalCenter: parent.horizontalCenter + color: UM.Theme.getColor("setting_control_text") + font: UM.Theme.getFont("default") + } + + Label + { + text: { + if (UM.SimulationView.layerActivity && CuraApplication.platformActivity) { - position: 0.5 - color: Qt.rgba(0, 0.75, 0.5, 1) - } - GradientStop - { - position: 0.75 - color: Qt.rgba(0, 0.375, 0.75, 1) - } - GradientStop - { - position: 1.0 - color: Qt.rgba(0, 0, 0.5, 1) + // Feedrate selected + if (UM.Preferences.getValue("layerview/layer_view_type") == 2) + { + return parseFloat(UM.SimulationView.getMaxFeedrate()).toFixed(2) + } + // Layer thickness selected + if (UM.Preferences.getValue("layerview/layer_view_type") == 3) + { + return parseFloat(UM.SimulationView.getMaxThickness()).toFixed(2) + } } + return catalog.i18nc("@label","max") + } + + anchors.right: parent.right + color: UM.Theme.getColor("setting_control_text") + font: UM.Theme.getFont("default") + } + } + + // Gradient colors for feedrate + Rectangle + { // In QML 5.9 can be changed by LinearGradient + // Invert values because then the bar is rotated 90 degrees + id: feedrateGradient + visible: viewSettings.show_feedrate_gradient + anchors.left: parent.right + height: parent.width + width: Math.round(UM.Theme.getSize("layerview_row").height * 1.5) + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") + transform: Rotation {origin.x: 0; origin.y: 0; angle: 90} + gradient: Gradient + { + GradientStop + { + position: 0.000 + color: Qt.rgba(1, 0.5, 0, 1) + } + GradientStop + { + position: 0.625 + color: Qt.rgba(0.375, 0.5, 0, 1) + } + GradientStop + { + position: 0.75 + color: Qt.rgba(0.25, 1, 0, 1) + } + GradientStop + { + position: 1.0 + color: Qt.rgba(0, 0, 1, 1) + } + } + } + + // Gradient colors for layer thickness (similar to parula colormap) + Rectangle // In QML 5.9 can be changed by LinearGradient + { + // Invert values because then the bar is rotated 90 degrees + id: thicknessGradient + visible: viewSettings.show_thickness_gradient + anchors.left: parent.right + height: parent.width + width: Math.round(UM.Theme.getSize("layerview_row").height * 1.5) + + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") + + transform: Rotation {origin.x: 0; origin.y: 0; angle: 90} + gradient: Gradient + { + GradientStop + { + position: 0.000 + color: Qt.rgba(1, 1, 0, 1) + } + GradientStop + { + position: 0.25 + color: Qt.rgba(1, 0.75, 0.25, 1) + } + GradientStop + { + position: 0.5 + color: Qt.rgba(0, 0.75, 0.5, 1) + } + GradientStop + { + position: 0.75 + color: Qt.rgba(0, 0.375, 0.75, 1) + } + GradientStop + { + position: 1.0 + color: Qt.rgba(0, 0, 0.5, 1) } } } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index e8de7c98ac..f1e0c08874 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -452,8 +452,6 @@ "layerview_menu_size": [15, 20], "layerview_menu_size_material_color_mode": [15, 16], "layerview_menu_size_collapsed": [15, 6], - "layerview_menu_size_compatibility": [22, 22.0], - "layerview_menu_size_compatibility_collapsed": [15, 3.5], "layerview_legend_size": [1.0, 1.0], "layerview_row": [11.0, 1.5], "layerview_row_spacing": [0.0, 0.5], From 319d52a2f9d6013a9261da8d0ec2aab61165a54f Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 5 Nov 2018 16:59:06 +0100 Subject: [PATCH 0133/1240] Make collapsible simulation view collapse to right height CURA-5829 --- .../SimulationViewMenuComponent.qml | 49 +++++++++---------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/plugins/SimulationView/SimulationViewMenuComponent.qml b/plugins/SimulationView/SimulationViewMenuComponent.qml index 62f353a241..cb70d1d78e 100644 --- a/plugins/SimulationView/SimulationViewMenuComponent.qml +++ b/plugins/SimulationView/SimulationViewMenuComponent.qml @@ -19,21 +19,7 @@ Rectangle width: UM.Theme.getSize("layerview_menu_size").width - height: - { - if (viewSettings.collapsed) - { - return UM.Theme.getSize("layerview_menu_size_collapsed").height - } - else if (UM.Preferences.getValue("layerview/layer_view_type") == 0) - { - return UM.Theme.getSize("layerview_menu_size_material_color_mode").height + UM.SimulationView.extruderCount * (UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("layerview_row_spacing").height) - } - else - { - return UM.Theme.getSize("layerview_menu_size").height + UM.SimulationView.extruderCount * (UM.Theme.getSize("layerview_row").height + UM.Theme.getSize("layerview_row_spacing").height) - } - } + height: viewSettings.collapsed ? layerViewTypesLabel.height + 2 * UM.Theme.getSize("default_margin").height : childrenRect.height Behavior on height { NumberAnimation { duration: 100 } } @@ -64,6 +50,23 @@ Rectangle } } + Label + { + id: layerViewTypesLabel + text: catalog.i18nc("@label","Color scheme") + font: UM.Theme.getFont("default"); + visible: !UM.SimulationView.compatibilityMode + color: UM.Theme.getColor("setting_control_text") + height: contentHeight + anchors + { + top: parent.top + margins: UM.Theme.getSize("default_margin").height + right: collapseButton.left + left: parent.left + } + } + Button { id: collapseButton @@ -71,9 +74,8 @@ Rectangle anchors { top: parent.top - topMargin: Math.round(UM.Theme.getSize("default_margin").height + (UM.Theme.getSize("layerview_row").height - UM.Theme.getSize("default_margin").height) / 2) + margins: UM.Theme.getSize("default_margin").width right: parent.right - rightMargin: UM.Theme.getSize("default_margin").width } width: UM.Theme.getSize("standard_arrow").width @@ -117,23 +119,16 @@ Rectangle anchors { - top: parent.top + top: layerViewTypesLabel.bottom left: parent.left right: parent.right margins: UM.Theme.getSize("default_margin").height + } spacing: UM.Theme.getSize("layerview_row_spacing").height - Label - { - id: layerViewTypesLabel - text: catalog.i18nc("@label","Color scheme") - font: UM.Theme.getFont("default"); - visible: !UM.SimulationView.compatibilityMode - width: parent.width - color: UM.Theme.getColor("setting_control_text") - } + visible: !collapsed ListModel // matches SimulationView.py { From 9f246910bc0e7334553490e8bacf0a7415cd487d Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 6 Nov 2018 09:18:20 +0100 Subject: [PATCH 0134/1240] Make avatar image border a bit wider This prevents a white edge from showing CURA-5772 --- resources/qml/Account/AvatarImage.qml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/resources/qml/Account/AvatarImage.qml b/resources/qml/Account/AvatarImage.qml index c730ef428f..436babe5a3 100644 --- a/resources/qml/Account/AvatarImage.qml +++ b/resources/qml/Account/AvatarImage.qml @@ -44,7 +44,10 @@ Item UM.RecolorImage { id: profileImageOutline - anchors.fill: parent + anchors.centerIn: parent + // Make it a bit bigger than it has to, otherwise it sometimes shows a white border. + width: parent.width + 2 + height: parent.height + 2 source: UM.Theme.getIcon("circle_outline") sourceSize: Qt.size(parent.width, parent.height) color: UM.Theme.getColor("account_widget_ouline_active") From 08917c37a11c8b089948c8eeffaaa2a4c780549b Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 6 Nov 2018 09:20:36 +0100 Subject: [PATCH 0135/1240] Improve rendering of scaled avatar image CURA-5772 --- resources/qml/Account/AvatarImage.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/qml/Account/AvatarImage.qml b/resources/qml/Account/AvatarImage.qml index 436babe5a3..3be81aa155 100644 --- a/resources/qml/Account/AvatarImage.qml +++ b/resources/qml/Account/AvatarImage.qml @@ -24,6 +24,7 @@ Item source: UM.Theme.getImage("avatar_default") fillMode: Image.PreserveAspectCrop visible: false + mipmap: true } Rectangle From f1f1b9f168e30ddcc15fd890362e6ed86f2e5699 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 6 Nov 2018 09:45:40 +0100 Subject: [PATCH 0136/1240] Center icon an label vertically. Contributes to CURA-5786. --- resources/qml/IconLabel.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/qml/IconLabel.qml b/resources/qml/IconLabel.qml index 2e765e2dbc..918485751e 100644 --- a/resources/qml/IconLabel.qml +++ b/resources/qml/IconLabel.qml @@ -24,6 +24,7 @@ Item id: icon anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter source: UM.Theme.getIcon("dot") width: UM.Theme.getSize("section_icon").width @@ -40,6 +41,7 @@ Item id: label anchors.left: icon.right anchors.leftMargin: UM.Theme.getSize("thin_margin").width + anchors.verticalCenter: parent.verticalCenter text: "Empty label" color: UM.Theme.getColor("text") font: UM.Theme.getFont("very_small") From 317447aa5451ac0d4220dead40bba89b61587bbf Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 6 Nov 2018 09:52:01 +0100 Subject: [PATCH 0137/1240] Fix multi-buildplate panel location CURA-5772 --- resources/qml/Account/AvatarImage.qml | 2 +- resources/qml/Cura.qml | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/resources/qml/Account/AvatarImage.qml b/resources/qml/Account/AvatarImage.qml index 3be81aa155..b76aff6990 100644 --- a/resources/qml/Account/AvatarImage.qml +++ b/resources/qml/Account/AvatarImage.qml @@ -24,7 +24,7 @@ Item source: UM.Theme.getImage("avatar_default") fillMode: Image.PreserveAspectCrop visible: false - mipmap: true + mipmap: true } Rectangle diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 3e2515cb3e..534fffb418 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -212,8 +212,9 @@ UM.MainWindow visible: UM.Preferences.getValue("cura/use_multi_build_plate") anchors { - bottom: parent.bottom - left: parent.left + bottom: viewOrientationControls.top + left: toolbar.right + margins: UM.Theme.getSize("default_margin").width } } From 11a3da3068bf71ac3d514af893db4b4870c8b3f8 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 6 Nov 2018 11:06:20 +0100 Subject: [PATCH 0138/1240] Removed color animation from stage buttons This caused laggy updates and it didn't add that much anyway CURA-5772 --- resources/qml/ActionButton.qml | 2 -- resources/themes/cura-light/styles.qml | 2 -- 2 files changed, 4 deletions(-) diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index a1c03af143..621318c4bb 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -37,7 +37,6 @@ Button color: button.hovered ? button.textHoverColor : button.textColor visible: source != "" anchors.verticalCenter: parent.verticalCenter - Behavior on color { ColorAnimation { duration: 50 } } } Label @@ -59,7 +58,6 @@ Button radius: UM.Theme.getSize("action_button_radius").width border.width: UM.Theme.getSize("default_lining").width border.color: button.enabled ? (button.hovered ? button.outlineHoverColor : button.outlineColor) : button.outlineDisabledColor - Behavior on color { ColorAnimation { duration: 50 } } } MouseArea diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index 43e8d29b57..dacff1b42b 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -129,8 +129,6 @@ QtObject return UM.Theme.getColor("main_window_header_button_background_inactive") } } - - Behavior on color { ColorAnimation { duration: 50 } } } } From f41ae128c2c14fb05dc84058b8f5833a396805bb Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 6 Nov 2018 11:24:41 +0100 Subject: [PATCH 0139/1240] Modify alignments to avoid binding loop logs. Contributes to CURA-5786. --- resources/qml/IconLabel.qml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/resources/qml/IconLabel.qml b/resources/qml/IconLabel.qml index 918485751e..7c90382892 100644 --- a/resources/qml/IconLabel.qml +++ b/resources/qml/IconLabel.qml @@ -24,7 +24,6 @@ Item id: icon anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter source: UM.Theme.getIcon("dot") width: UM.Theme.getSize("section_icon").width @@ -41,7 +40,7 @@ Item id: label anchors.left: icon.right anchors.leftMargin: UM.Theme.getSize("thin_margin").width - anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenter: icon.verticalCenter text: "Empty label" color: UM.Theme.getColor("text") font: UM.Theme.getFont("very_small") From b6b60702c054ff93c112089583c721a6ddedd44a Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 6 Nov 2018 14:32:48 +0100 Subject: [PATCH 0140/1240] Make the action panel to have a fixed width. Remove the animations when switching states. Contributes to CURA-5786. --- resources/qml/ActionPanel/ActionPanelWidget.qml | 7 +++---- resources/qml/ActionPanel/SliceProcessWidget.qml | 2 -- resources/themes/cura-light/theme.json | 1 + 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/resources/qml/ActionPanel/ActionPanelWidget.qml b/resources/qml/ActionPanel/ActionPanelWidget.qml index 1fc288af6a..0db778de5a 100644 --- a/resources/qml/ActionPanel/ActionPanelWidget.qml +++ b/resources/qml/ActionPanel/ActionPanelWidget.qml @@ -16,7 +16,7 @@ Rectangle { id: actionPanelWidget - width: childrenRect.width + 2 * UM.Theme.getSize("thick_margin").width + width: UM.Theme.getSize("action_panel_widget").width height: childrenRect.height + 2 * UM.Theme.getSize("thick_margin").height color: UM.Theme.getColor("main_background") @@ -36,13 +36,12 @@ Rectangle topMargin: UM.Theme.getSize("thick_margin").height left: parent.left leftMargin: UM.Theme.getSize("thick_margin").width + right: parent.right + rightMargin: UM.Theme.getSize("thick_margin").width } sourceComponent: outputAvailable ? outputProcessWidget : sliceProcessWidget } - Behavior on height { NumberAnimation { duration: 100 } } - Behavior on width { NumberAnimation { duration: 100 } } - Component { id: sliceProcessWidget diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index e70ec45e43..1ba5776b6b 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -17,8 +17,6 @@ Column { id: widget - width: UM.Theme.getSize("action_panel_button").width - spacing: UM.Theme.getSize("thin_margin").height UM.I18nCatalog diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index f354b521ee..27dfbaf807 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -379,6 +379,7 @@ "configuration_selector_widget": [35.0, 4.5], "configuration_selector_mode_tabs": [0.0, 3.0], + "action_panel_widget": [25.0, 0.0], "action_panel_information_widget": [20.0, 0.0], "action_panel_button": [15.0, 3.0], From 5dddcbd6664b38f091ce0565c01a5a6ece1cc35f Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 6 Nov 2018 15:14:27 +0100 Subject: [PATCH 0141/1240] Add a new property to the ActionButton to indicate whether the button has a fixed width or the width will be dependant on the content. Contributes to CURA-5786. --- resources/qml/Account/GeneralOperations.qml | 2 ++ resources/qml/Account/UserOperations.qml | 2 ++ resources/qml/ActionButton.qml | 7 +++++++ resources/qml/ActionPanel/OutputDevicesActionButton.qml | 1 + resources/qml/ActionPanel/SliceProcessWidget.qml | 1 + 5 files changed, 13 insertions(+) diff --git a/resources/qml/Account/GeneralOperations.qml b/resources/qml/Account/GeneralOperations.qml index 6bc94dd830..362e088033 100644 --- a/resources/qml/Account/GeneralOperations.qml +++ b/resources/qml/Account/GeneralOperations.qml @@ -21,6 +21,7 @@ Row textColor: UM.Theme.getColor("main_window_header_button_text_active") textHoverColor: UM.Theme.getColor("main_window_header_button_text_active") onClicked: Qt.openUrlExternally("https://account.ultimaker.com") + fixedWidthMode: true } Cura.ActionButton @@ -29,5 +30,6 @@ Row height: UM.Theme.getSize("account_button").height text: catalog.i18nc("@button", "Login") onClicked: Cura.API.account.login() + fixedWidthMode: true } } \ No newline at end of file diff --git a/resources/qml/Account/UserOperations.qml b/resources/qml/Account/UserOperations.qml index a12bfbf6d7..c167813425 100644 --- a/resources/qml/Account/UserOperations.qml +++ b/resources/qml/Account/UserOperations.qml @@ -21,6 +21,7 @@ Row textColor: UM.Theme.getColor("main_window_header_button_text_active") textHoverColor: UM.Theme.getColor("main_window_header_button_text_active") onClicked: Qt.openUrlExternally("https://account.ultimaker.com") + fixedWidthMode: true } Cura.ActionButton @@ -29,5 +30,6 @@ Row height: UM.Theme.getSize("account_button").height text: catalog.i18nc("@button", "Logout") onClicked: Cura.API.account.logout() + fixedWidthMode: true } } \ No newline at end of file diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index 05e75ac8c5..b35ad4ba87 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -24,6 +24,10 @@ Button property var outlineColor: color property var outlineHoverColor: hoverColor property var outlineDisabledColor: outlineColor + // This property is used to indicate whether the button has a fixed width or the width would depend on the contents + // Be careful when using fixedWidthMode, the translated texts can be too long that they won't fit. In any case, + // we elide the text to the right so the text will be cut off with the three dots at the end. + property var fixedWidthMode: false contentItem: Row { @@ -50,6 +54,9 @@ Button visible: text != "" renderType: Text.NativeRendering anchors.verticalCenter: parent.verticalCenter + width: fixedWidthMode ? button.width - button.leftPadding - button.rightPadding : undefined + horizontalAlignment: Text.AlignHCenter + elide: Text.ElideRight } } diff --git a/resources/qml/ActionPanel/OutputDevicesActionButton.qml b/resources/qml/ActionPanel/OutputDevicesActionButton.qml index 87faa0a2bf..be79a1893e 100644 --- a/resources/qml/ActionPanel/OutputDevicesActionButton.qml +++ b/resources/qml/ActionPanel/OutputDevicesActionButton.qml @@ -16,6 +16,7 @@ Item { id: saveToButton height: parent.height + fixedWidthMode: true anchors { diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index 1ba5776b6b..2d4a7b6b89 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -86,6 +86,7 @@ Column id: prepareButton width: parent.width height: UM.Theme.getSize("action_panel_button").height + fixedWidthMode: true text: { if ([UM.Backend.NotStarted, UM.Backend.Error].indexOf(widget.backendState) != -1) From c51995f3497dba6880014f45e077f0dd494329b1 Mon Sep 17 00:00:00 2001 From: pinchies Date: Wed, 7 Nov 2018 04:36:32 +1100 Subject: [PATCH 0142/1240] Update jgaurora_a5.def.json --- resources/definitions/jgaurora_a5.def.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/definitions/jgaurora_a5.def.json b/resources/definitions/jgaurora_a5.def.json index 0a8b93f7cf..02d9a9db4f 100644 --- a/resources/definitions/jgaurora_a5.def.json +++ b/resources/definitions/jgaurora_a5.def.json @@ -1,5 +1,5 @@ { - "name": "JGAurora A5", + "name": "JGAurora A5 & A5S", "version": 2, "inherits": "fdmprinter", "metadata": { @@ -17,7 +17,7 @@ }, "overrides": { "machine_name": { - "default_value": "JGAurora A5" + "default_value": "JGAurora A5 & A5S" }, "machine_start_gcode": { "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 ;home all axis\nM420 S1 ;turn on mesh bed levelling if enabled in firmware\nG92 E0 ;zero the extruded length\nG1 Z1 F1000 ;move up slightly\nG1 X60.0 Z0 E9.0 F1000.0;intro line\nG1 X100.0 E21.5 F1000.0 ;continue line\nG92 E0 ;zero the extruded length again\n; -- end of START GCODE --" From 70d2f407a37f186fe3712042ac84d67da1c75e12 Mon Sep 17 00:00:00 2001 From: pinchies Date: Wed, 7 Nov 2018 04:38:39 +1100 Subject: [PATCH 0143/1240] Update cocoon_create_modelmaker.def.json --- resources/definitions/cocoon_create_modelmaker.def.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/definitions/cocoon_create_modelmaker.def.json b/resources/definitions/cocoon_create_modelmaker.def.json index 7f19fb0239..204d5b9492 100644 --- a/resources/definitions/cocoon_create_modelmaker.def.json +++ b/resources/definitions/cocoon_create_modelmaker.def.json @@ -1,11 +1,11 @@ { - "name": "Cocoon Create ModelMaker", + "name": "Cocoon Create ModelMaker & Wanhao Duplicator i3 Mini", "version": 2, "inherits": "fdmprinter", "metadata": { "visible": true, "author": "Samuel Pinches", - "manufacturer": "Cocoon Create", + "manufacturer": "Cocoon Create / Wanhao", "file_formats": "text/x-gcode", "preferred_quality_type": "fine", "machine_extruder_trains": @@ -15,7 +15,7 @@ }, "overrides": { "machine_name": { - "default_value": "Cocoon Create ModelMaker" + "default_value": "Cocoon Create ModelMaker & Wanhao Duplicator i3 Mini" }, "machine_start_gcode": { "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 ;home all axis\nG92 E0 ;zero the extruded length\nG1 Z1 F1000 ;move up slightly\nG1 X60.0 Z0 E9.0 F1000.0;intro line\nG1 X100.0 E21.5 F1000.0 ;continue line\nG92 E0 ;zero the extruded length again\n; -- end of START GCODE --" From f15d03d48c84855ca30ec60017e9f00837b686f3 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 7 Nov 2018 16:30:52 +0100 Subject: [PATCH 0144/1240] Fix foldout of the layerview panel being too short or too long CURA-5829 --- .../SimulationViewMenuComponent.qml | 152 +++++++++--------- 1 file changed, 80 insertions(+), 72 deletions(-) diff --git a/plugins/SimulationView/SimulationViewMenuComponent.qml b/plugins/SimulationView/SimulationViewMenuComponent.qml index cb70d1d78e..1cbaffa65e 100644 --- a/plugins/SimulationView/SimulationViewMenuComponent.qml +++ b/plugins/SimulationView/SimulationViewMenuComponent.qml @@ -19,7 +19,7 @@ Rectangle width: UM.Theme.getSize("layerview_menu_size").width - height: viewSettings.collapsed ? layerViewTypesLabel.height + 2 * UM.Theme.getSize("default_margin").height : childrenRect.height + height: viewSettings.collapsed ? layerViewTypesLabel.height + 2 * UM.Theme.getSize("default_margin").height : childrenRect.height + 2 * UM.Theme.getSize("default_margin").height Behavior on height { NumberAnimation { duration: 100 } } @@ -469,83 +469,91 @@ Rectangle } } - // Gradient colors for feedrate - Rectangle - { // In QML 5.9 can be changed by LinearGradient - // Invert values because then the bar is rotated 90 degrees - id: feedrateGradient - visible: viewSettings.show_feedrate_gradient - anchors.left: parent.right - height: parent.width - width: Math.round(UM.Theme.getSize("layerview_row").height * 1.5) - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("lining") - transform: Rotation {origin.x: 0; origin.y: 0; angle: 90} - gradient: Gradient - { - GradientStop + Item + { + // Another hack on top of the rotation of the gradient. + // Since we set the side of the panel to use childrenRect (and that uses the un-rotated height), add this + // wrapper item with the correct width & height so it doesn't get confused. + width: parent.width + height: feedrateGradient.visible || thicknessGradient.visible ? Math.round(UM.Theme.getSize("layerview_row").height * 1.5) : 0 + // Gradient colors for feedrate + Rectangle + { // In QML 5.9 can be changed by LinearGradient + // Invert values because then the bar is rotated 90 degrees + id: feedrateGradient + visible: viewSettings.show_feedrate_gradient + anchors.left: parent.right + height: parent.width + width: Math.round(UM.Theme.getSize("layerview_row").height * 1.5) + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") + transform: Rotation {origin.x: 0; origin.y: 0; angle: 90} + gradient: Gradient { - position: 0.000 - color: Qt.rgba(1, 0.5, 0, 1) - } - GradientStop - { - position: 0.625 - color: Qt.rgba(0.375, 0.5, 0, 1) - } - GradientStop - { - position: 0.75 - color: Qt.rgba(0.25, 1, 0, 1) - } - GradientStop - { - position: 1.0 - color: Qt.rgba(0, 0, 1, 1) + GradientStop + { + position: 0.000 + color: Qt.rgba(1, 0.5, 0, 1) + } + GradientStop + { + position: 0.625 + color: Qt.rgba(0.375, 0.5, 0, 1) + } + GradientStop + { + position: 0.75 + color: Qt.rgba(0.25, 1, 0, 1) + } + GradientStop + { + position: 1.0 + color: Qt.rgba(0, 0, 1, 1) + } } } - } - // Gradient colors for layer thickness (similar to parula colormap) - Rectangle // In QML 5.9 can be changed by LinearGradient - { - // Invert values because then the bar is rotated 90 degrees - id: thicknessGradient - visible: viewSettings.show_thickness_gradient - anchors.left: parent.right - height: parent.width - width: Math.round(UM.Theme.getSize("layerview_row").height * 1.5) - - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("lining") - - transform: Rotation {origin.x: 0; origin.y: 0; angle: 90} - gradient: Gradient + // Gradient colors for layer thickness (similar to parula colormap) + Rectangle // In QML 5.9 can be changed by LinearGradient { - GradientStop + // Invert values because then the bar is rotated 90 degrees + id: thicknessGradient + visible: viewSettings.show_thickness_gradient + anchors.left: parent.right + height: parent.width + width: Math.round(UM.Theme.getSize("layerview_row").height * 1.5) + + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") + + transform: Rotation {origin.x: 0; origin.y: 0; angle: 90} + gradient: Gradient { - position: 0.000 - color: Qt.rgba(1, 1, 0, 1) - } - GradientStop - { - position: 0.25 - color: Qt.rgba(1, 0.75, 0.25, 1) - } - GradientStop - { - position: 0.5 - color: Qt.rgba(0, 0.75, 0.5, 1) - } - GradientStop - { - position: 0.75 - color: Qt.rgba(0, 0.375, 0.75, 1) - } - GradientStop - { - position: 1.0 - color: Qt.rgba(0, 0, 0.5, 1) + GradientStop + { + position: 0.000 + color: Qt.rgba(1, 1, 0, 1) + } + GradientStop + { + position: 0.25 + color: Qt.rgba(1, 0.75, 0.25, 1) + } + GradientStop + { + position: 0.5 + color: Qt.rgba(0, 0.75, 0.5, 1) + } + GradientStop + { + position: 0.75 + color: Qt.rgba(0, 0.375, 0.75, 1) + } + GradientStop + { + position: 1.0 + color: Qt.rgba(0, 0, 0.5, 1) + } } } } From 9f663cfd18b13f8504bf3d753166fe1f9a9b653c Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 7 Nov 2018 17:25:43 +0100 Subject: [PATCH 0145/1240] Change the gradient for the LinearGradient that works in the new versions of Qt. I also changed it because we had a TODO here and I thought it was the right moment to do it. Contributes to CURA-5829. --- .../SimulationViewMenuComponent.qml | 139 ++++++++++-------- 1 file changed, 80 insertions(+), 59 deletions(-) diff --git a/plugins/SimulationView/SimulationViewMenuComponent.qml b/plugins/SimulationView/SimulationViewMenuComponent.qml index 1cbaffa65e..d251244c9f 100644 --- a/plugins/SimulationView/SimulationViewMenuComponent.qml +++ b/plugins/SimulationView/SimulationViewMenuComponent.qml @@ -5,6 +5,7 @@ import QtQuick 2.4 import QtQuick.Controls 1.2 import QtQuick.Layouts 1.1 import QtQuick.Controls.Styles 1.1 +import QtGraphicalEffects 1.0 import UM 1.0 as UM import Cura 1.0 as Cura @@ -469,75 +470,95 @@ Rectangle } } - Item + // Gradient colors for feedrate + Rectangle { - // Another hack on top of the rotation of the gradient. - // Since we set the side of the panel to use childrenRect (and that uses the un-rotated height), add this - // wrapper item with the correct width & height so it doesn't get confused. - width: parent.width - height: feedrateGradient.visible || thicknessGradient.visible ? Math.round(UM.Theme.getSize("layerview_row").height * 1.5) : 0 - // Gradient colors for feedrate - Rectangle - { // In QML 5.9 can be changed by LinearGradient - // Invert values because then the bar is rotated 90 degrees - id: feedrateGradient - visible: viewSettings.show_feedrate_gradient - anchors.left: parent.right - height: parent.width - width: Math.round(UM.Theme.getSize("layerview_row").height * 1.5) - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("lining") - transform: Rotation {origin.x: 0; origin.y: 0; angle: 90} - gradient: Gradient - { - GradientStop - { - position: 0.000 - color: Qt.rgba(1, 0.5, 0, 1) - } - GradientStop - { - position: 0.625 - color: Qt.rgba(0.375, 0.5, 0, 1) - } - GradientStop - { - position: 0.75 - color: Qt.rgba(0.25, 1, 0, 1) - } - GradientStop - { - position: 1.0 - color: Qt.rgba(0, 0, 1, 1) - } - } - } + id: feedrateGradient + visible: viewSettings.show_feedrate_gradient + anchors.left: parent.left + anchors.right: parent.right + height: Math.round(UM.Theme.getSize("layerview_row").height * 1.5) + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") - // Gradient colors for layer thickness (similar to parula colormap) - Rectangle // In QML 5.9 can be changed by LinearGradient + LinearGradient { - // Invert values because then the bar is rotated 90 degrees - id: thicknessGradient - visible: viewSettings.show_thickness_gradient - anchors.left: parent.right - height: parent.width - width: Math.round(UM.Theme.getSize("layerview_row").height * 1.5) - - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("lining") - - transform: Rotation {origin.x: 0; origin.y: 0; angle: 90} + anchors + { + left: parent.left + leftMargin: UM.Theme.getSize("default_lining").width + right: parent.right + rightMargin: UM.Theme.getSize("default_lining").width + top: parent.top + topMargin: UM.Theme.getSize("default_lining").width + bottom: parent.bottom + bottomMargin: UM.Theme.getSize("default_lining").width + } + start: Qt.point(0, 0) + end: Qt.point(parent.width, 0) gradient: Gradient { GradientStop { position: 0.000 - color: Qt.rgba(1, 1, 0, 1) + color: Qt.rgba(0, 0, 1, 1) } GradientStop { position: 0.25 - color: Qt.rgba(1, 0.75, 0.25, 1) + color: Qt.rgba(0.25, 1, 0, 1) + } + GradientStop + { + position: 0.375 + color: Qt.rgba(0.375, 0.5, 0, 1) + } + GradientStop + { + position: 1.0 + color: Qt.rgba(1, 0.5, 0, 1) + } + } + } + } + + // Gradient colors for layer thickness (similar to parula colormap) + Rectangle + { + id: thicknessGradient + visible: viewSettings.show_thickness_gradient + anchors.left: parent.left + anchors.right: parent.right + height: Math.round(UM.Theme.getSize("layerview_row").height * 1.5) + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") + + LinearGradient + { + anchors + { + left: parent.left + leftMargin: UM.Theme.getSize("default_lining").width + right: parent.right + rightMargin: UM.Theme.getSize("default_lining").width + top: parent.top + topMargin: UM.Theme.getSize("default_lining").width + bottom: parent.bottom + bottomMargin: UM.Theme.getSize("default_lining").width + } + start: Qt.point(0, 0) + end: Qt.point(parent.width, 0) + gradient: Gradient + { + GradientStop + { + position: 0.000 + color: Qt.rgba(0, 0, 0.5, 1) + } + GradientStop + { + position: 0.25 + color: Qt.rgba(0, 0.375, 0.75, 1) } GradientStop { @@ -547,12 +568,12 @@ Rectangle GradientStop { position: 0.75 - color: Qt.rgba(0, 0.375, 0.75, 1) + color: Qt.rgba(1, 0.75, 0.25, 1) } GradientStop { position: 1.0 - color: Qt.rgba(0, 0, 0.5, 1) + color: Qt.rgba(1, 1, 0, 1) } } } From e66dd2be0b417646fa015dba7133a21b6d5a48e4 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 8 Nov 2018 15:03:24 +0100 Subject: [PATCH 0146/1240] Action-Panel (CURA-5786) can now know about Preview-Stage (CURA-5829). --- resources/qml/ActionPanel/OutputProcessWidget.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml index db9a23cdf3..0ea956d1dc 100644 --- a/resources/qml/ActionPanel/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -110,7 +110,7 @@ Column hoverColor: UM.Theme.getColor("secondary") textColor: UM.Theme.getColor("primary") textHoverColor: UM.Theme.getColor("text") - onClicked: UM.Controller.setActiveStage("MonitorStage") + onClicked: UM.Controller.setActiveStage("PreviewStage") } Cura.OutputDevicesActionButton From 02c97052eb54b9c2e32106f793841374fc179962 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 8 Nov 2018 17:31:30 +0100 Subject: [PATCH 0147/1240] Remove transitions when collapsing/expanding the printer category in the add printer dialog. --- resources/qml/AddMachineDialog.qml | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/resources/qml/AddMachineDialog.qml b/resources/qml/AddMachineDialog.qml index 0df8b891d9..06ef2e1fc1 100644 --- a/resources/qml/AddMachineDialog.qml +++ b/resources/qml/AddMachineDialog.qml @@ -213,28 +213,6 @@ UM.Dialog PropertyChanges { target: machineButton; opacity: 0; height: 0; } } - - transitions: - [ - Transition - { - to: "collapsed"; - SequentialAnimation - { - NumberAnimation { property: "opacity"; duration: 75; } - NumberAnimation { property: "height"; duration: 75; } - } - }, - Transition - { - from: "collapsed"; - SequentialAnimation - { - NumberAnimation { property: "height"; duration: 75; } - NumberAnimation { property: "opacity"; duration: 75; } - } - } - ] } } } From 88234f7e21620d2e045aefa6bbc9247da941e0c7 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 9 Nov 2018 10:48:22 +0100 Subject: [PATCH 0148/1240] Change ExtruderIcon to match the new design CURA-5785 --- resources/qml/CustomConfigurationSelector.qml | 4 +- resources/qml/ExtruderIcon.qml | 46 ++++++------ .../cura-light/icons/extruder_button.svg | 73 +++---------------- 3 files changed, 33 insertions(+), 90 deletions(-) diff --git a/resources/qml/CustomConfigurationSelector.qml b/resources/qml/CustomConfigurationSelector.qml index 17b652e8d8..c78ca700da 100644 --- a/resources/qml/CustomConfigurationSelector.qml +++ b/resources/qml/CustomConfigurationSelector.qml @@ -195,8 +195,8 @@ Rectangle height: width checked: control.checked - material_color: model.color - text_color: extruderStaticText.color + materialColor: model.color + textColor: extruderStaticText.color } } } diff --git a/resources/qml/ExtruderIcon.qml b/resources/qml/ExtruderIcon.qml index e4161e0058..eb4bdef0f7 100644 --- a/resources/qml/ExtruderIcon.qml +++ b/resources/qml/ExtruderIcon.qml @@ -13,46 +13,44 @@ Item implicitHeight: implicitWidth property bool checked: true - property alias material_color: materialColorCircle.color - property alias text_color: extruderNumberText.color + property alias materialColor: mainIcon.color + property alias textColor: extruderNumberText.color UM.RecolorImage { - id: mainCircle + id: mainIcon anchors.fill: parent sourceSize.width: parent.width sourceSize.height: parent.width source: UM.Theme.getIcon("extruder_button") - color: extruderNumberText.color } - Label - { - id: extruderNumberText - anchors.centerIn: parent - text: index + 1; - font: UM.Theme.getFont("default_bold") - } - - // Material colour circle - // Only draw the filling colour of the material inside the SVG border. Rectangle { - id: materialColorCircle + id: extruderNumberCircle + + width: height + height: parent.height / 2 + radius: Math.round(width / 2) + color: "white" anchors { - right: parent.right + horizontalCenter: parent.horizontalCenter + top: parent.top + // The circle needs to be slightly off center (so it sits in the middle of the square bit of the icon) + topMargin: (parent.height - height) / 2 - 0.1 * parent.height } - width: Math.round(parent.width * 0.35) - height: Math.round(parent.height * 0.35) - radius: Math.round(width / 2) - - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("extruder_button_material_border") - - opacity: !extruderIconItem.checked ? 0.6 : 1.0 + Label + { + id: extruderNumberText + anchors.centerIn: parent + text: index + 1 + font: UM.Theme.getFont("default") + width: contentWidth + height: contentHeight + } } } \ No newline at end of file diff --git a/resources/themes/cura-light/icons/extruder_button.svg b/resources/themes/cura-light/icons/extruder_button.svg index e3c01b6a0a..c79ba5c5df 100644 --- a/resources/themes/cura-light/icons/extruder_button.svg +++ b/resources/themes/cura-light/icons/extruder_button.svg @@ -1,64 +1,9 @@ - - - - - - image/svg+xml - - Artboard 3 Copy - - - - - - Artboard 3 Copy - Created with Sketch. - - - + + + + + + + + + \ No newline at end of file From 2adb79aa39a2944ddba9397512d0c51e9d048bb5 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 9 Nov 2018 13:45:51 +0100 Subject: [PATCH 0149/1240] Added re-usable expandable component CURA-5785 --- resources/qml/ExpandableComponent.qml | 77 +++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 resources/qml/ExpandableComponent.qml diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml new file mode 100644 index 0000000000..0b728ccaff --- /dev/null +++ b/resources/qml/ExpandableComponent.qml @@ -0,0 +1,77 @@ +import QtQuick 2.7 +import QtQuick.Controls 2.3 + +// The expandable component has 3 major sub components: +// * The headerItem; Always visible and should hold some info about what happens if the component is expanded +// * The popupItem; The content that needs to be shown if the component is expanded. +// * The Button; The actual button that expands the popup. +Item +{ + // The headerItem holds the QML item that is always displayed. + property alias headerItem: headerItemLoader.sourceComponent + + // The popupItem holds the QML item that is shown when the "open" button is pressed + property var popupItem + + // The background color of the popup + property color popupBackgroundColor: "white" + + // How much spacing is needed around the popupItem + property alias padding: popup.padding + + onPopupItemChanged: + { + // Since we want the size of the popup to be set by the size of the content, + // we need to do it like this. + popup.width = popupItem.width + 2 * popup.padding + popup.height = popupItem.height + 2 * popup.padding + popup.contentItem = popupItem + } + + implicitHeight: 100 + implicitWidth: 400 + + Loader + { + id: headerItemLoader + anchors + { + left: parent.left + right: collapseButton.left + top: parent.top + bottom: parent.bottom + } + } + + Button + { + id: collapseButton + anchors + { + right: parent.right + top: parent.top + bottom: parent.bottom + } + text: popup.visible ? "close" : "open" + onClicked: popup.visible ? popup.close() : popup.open() + } + + Popup + { + id: popup + + // Ensure that the popup is located directly below the headerItem + y: headerItemLoader.height + + // Make the popup right aligned with the rest. + x: -width + collapseButton.width + headerItemLoader.width + + closePolicy: Popup.CloseOnPressOutsideParent + + background: Rectangle + { + id: background + color: popupBackgroundColor + } + } +} From 1d775c984e4ebf426bc563adcc639b3d643410bf Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 9 Nov 2018 14:31:06 +0100 Subject: [PATCH 0150/1240] Added background padding to expandable component CURA-5785 --- resources/qml/ExpandableComponent.qml | 66 +++++++++++++++++---------- 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 0b728ccaff..26bdf3b3ce 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -1,6 +1,8 @@ import QtQuick 2.7 import QtQuick.Controls 2.3 +import UM 1.2 as UM + // The expandable component has 3 major sub components: // * The headerItem; Always visible and should hold some info about what happens if the component is expanded // * The popupItem; The content that needs to be shown if the component is expanded. @@ -16,8 +18,13 @@ Item // The background color of the popup property color popupBackgroundColor: "white" + property alias headerBackgroundColor: background.color + // How much spacing is needed around the popupItem - property alias padding: popup.padding + property alias popupPadding: popup.padding + + // How much padding is needed around the header & button + property alias headerPadding: background.padding onPopupItemChanged: { @@ -30,30 +37,39 @@ Item implicitHeight: 100 implicitWidth: 400 - - Loader + Rectangle { - id: headerItemLoader - anchors - { - left: parent.left - right: collapseButton.left - top: parent.top - bottom: parent.bottom - } - } + id: background + property real padding: UM.Theme.getSize("default_margin").width - Button - { - id: collapseButton - anchors + color: "white" + anchors.fill: parent + Loader { - right: parent.right - top: parent.top - bottom: parent.bottom + id: headerItemLoader + anchors + { + left: parent.left + right: collapseButton.left + top: parent.top + bottom: parent.bottom + margins: background.padding + } + } + + Button + { + id: collapseButton + anchors + { + right: parent.right + top: parent.top + bottom: parent.bottom + margins: background.padding + } + text: popup.visible ? "close" : "open" + onClicked: popup.visible ? popup.close() : popup.open() } - text: popup.visible ? "close" : "open" - onClicked: popup.visible ? popup.close() : popup.open() } Popup @@ -61,16 +77,16 @@ Item id: popup // Ensure that the popup is located directly below the headerItem - y: headerItemLoader.height + y: headerItemLoader.height + 2 * background.padding - // Make the popup right aligned with the rest. - x: -width + collapseButton.width + headerItemLoader.width + // Make the popup right aligned with the rest. The 3x padding is due to left, right and padding between + //the button & text. + x: -width + collapseButton.width + headerItemLoader.width + 3 * background.padding closePolicy: Popup.CloseOnPressOutsideParent background: Rectangle { - id: background color: popupBackgroundColor } } From 2c7bdba7d009d8dfbcebb99a7e9b0b85172432f6 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 9 Nov 2018 15:20:26 +0100 Subject: [PATCH 0151/1240] Update ConfigurationSelector header to that it looks more like the new design CURA-5785 --- cura/Settings/ExtrudersModel.py | 11 ++- plugins/PrepareStage/PrepareMenu.qml | 10 ++- .../QuickConfigurationSelector.qml | 79 ++++++++++++++++++- 3 files changed, 92 insertions(+), 8 deletions(-) diff --git a/cura/Settings/ExtrudersModel.py b/cura/Settings/ExtrudersModel.py index 52fc502bfc..9b85afa10e 100644 --- a/cura/Settings/ExtrudersModel.py +++ b/cura/Settings/ExtrudersModel.py @@ -47,6 +47,9 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): VariantRole = Qt.UserRole + 7 StackRole = Qt.UserRole + 8 + MaterialBrandRole = Qt.UserRole + 9 + ColorNameRole = Qt.UserRole + 10 + ## List of colours to display if there is no material or the material has no known # colour. defaultColors = ["#ffc924", "#86ec21", "#22eeee", "#245bff", "#9124ff", "#ff24c8"] @@ -67,7 +70,8 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): self.addRoleName(self.MaterialRole, "material") self.addRoleName(self.VariantRole, "variant") self.addRoleName(self.StackRole, "stack") - + self.addRoleName(self.MaterialBrandRole, "material_brand") + self.addRoleName(self.ColorNameRole, "color_name") self._update_extruder_timer = QTimer() self._update_extruder_timer.setInterval(100) self._update_extruder_timer.setSingleShot(True) @@ -183,7 +187,8 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): default_color = self.defaultColors[position] if 0 <= position < len(self.defaultColors) else self.defaultColors[0] color = extruder.material.getMetaDataEntry("color_code", default = default_color) if extruder.material else default_color - + material_brand = extruder.material.getMetaDataEntry("brand", default = "generic") + color_name = extruder.material.getMetaDataEntry("color_name") # construct an item with only the relevant information item = { "id": extruder.getId(), @@ -195,6 +200,8 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): "material": extruder.material.getName() if extruder.material else "", "variant": extruder.variant.getName() if extruder.variant else "", # e.g. print core "stack": extruder, + "material_brand": material_brand, + "color_name": color_name } items.append(item) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index e8514a9ef9..bcc8ec4c0a 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -50,16 +50,18 @@ Item Cura.QuickConfigurationSelector { - id: configSelection + height: prepareMenu.height + width: UM.Theme.getSize("configuration_selector_widget").width + /*id: configSelection width: visible ? UM.Theme.getSize("machine_selector_widget").width * 0.2 : 0 panelWidth: UM.Theme.getSize("machine_selector_widget").width - height: prepareMenu.height + height: prepareMenu.height*/ } - Cura.CustomConfigurationSelector + /*Cura.CustomConfigurationSelector { width: UM.Theme.getSize("configuration_selector_widget").width - } + }*/ Cura.PrintSetupSelector { diff --git a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml index 740c12d340..966133d906 100644 --- a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml +++ b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml @@ -8,7 +8,82 @@ import QtQuick.Controls.Styles 1.4 import UM 1.2 as UM import Cura 1.0 as Cura -Item + +Cura.ExpandableComponent +{ + id: base + headerItem: Item + { + Cura.ExtrudersModel + { + id: extrudersModel + } + + ListView + { + // Horizontal list that shows the extruders + id: extrudersList + + orientation: ListView.Horizontal + anchors.fill: parent + model: extrudersModel + + Connections + { + target: Cura.MachineManager + onGlobalContainerChanged: forceActiveFocus() // Changing focus applies the currently-being-typed values so it can change the displayed setting values. + } + + delegate: Item + { + height: parent.height + width: Math.round(ListView.view.width / extrudersModel.rowCount()) + + Cura.ExtruderIcon + { + id: extruderIcon + materialColor: model.color + height: parent.height + width: height + } + + Label + { + id: brandNameLabel + + text: model.material_brand + elide: Text.ElideRight + + anchors + { + left: extruderIcon.right + leftMargin: UM.Theme.getSize("default_margin").width + right: parent.right + rightMargin: UM.Theme.getSize("default_margin").width + } + } + Label + { + text: model.color_name + elide: Text.ElideRight + + anchors + { + left: extruderIcon.right + leftMargin: UM.Theme.getSize("default_margin").width + right: parent.right + rightMargin: UM.Theme.getSize("default_margin").width + top: brandNameLabel.bottom + } + } + } + } + } + + +} + +/*Item { id: configurationSelector property var connectedDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null @@ -65,4 +140,4 @@ Item onClosed: visible = false onOpened: visible = true } -} \ No newline at end of file +}*/ \ No newline at end of file From 1602bf0999974c4815faa54575ad84aabb1e3867 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 9 Nov 2018 16:59:10 +0100 Subject: [PATCH 0152/1240] Re-Add the material & printcore selection to the configuration selector CURA-5785 --- .../QuickConfigurationSelector.qml | 170 ++++++++++++++++-- 1 file changed, 157 insertions(+), 13 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml index 966133d906..e093c96ae5 100644 --- a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml +++ b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml @@ -4,6 +4,9 @@ import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.11 + +import QtQuick.Controls 1.1 as OldControls import UM 1.2 as UM import Cura 1.0 as Cura @@ -12,33 +15,28 @@ import Cura 1.0 as Cura Cura.ExpandableComponent { id: base + Cura.ExtrudersModel + { + id: extrudersModel + } + headerItem: Item { - Cura.ExtrudersModel - { - id: extrudersModel - } - + // Horizontal list that shows the extruders ListView { - // Horizontal list that shows the extruders id: extrudersList orientation: ListView.Horizontal anchors.fill: parent model: extrudersModel - Connections - { - target: Cura.MachineManager - onGlobalContainerChanged: forceActiveFocus() // Changing focus applies the currently-being-typed values so it can change the displayed setting values. - } - delegate: Item { height: parent.height width: Math.round(ListView.view.width / extrudersModel.rowCount()) + // Extruder icon. Shows extruder index and has the same color as the active material. Cura.ExtruderIcon { id: extruderIcon @@ -47,6 +45,7 @@ Cura.ExpandableComponent width: height } + // Label for the human readable material color name Label { id: brandNameLabel @@ -62,6 +61,8 @@ Cura.ExpandableComponent rightMargin: UM.Theme.getSize("default_margin").width } } + + // Label that shows the brand of the material Label { text: model.color_name @@ -80,7 +81,150 @@ Cura.ExpandableComponent } } - + popupItem: Item + { + width: base.width + height: 200 + + TabBar + { + id: tabBar + onCurrentIndexChanged: Cura.ExtruderManager.setActiveExtruderIndex(currentIndex) + width: parent.width + height: 50 + Repeater + { + model: extrudersModel + + delegate: TabButton + { + width: Math.round(ListView.view.width / extrudersModel.rowCount()) + height: parent.height + contentItem: Item + { + Cura.ExtruderIcon + { + anchors.horizontalCenter: parent.horizontalCenter + materialColor: model.color + width: parent.height + height: parent.height + } + } + } + } + } + + Item + { + id: tabControl + width: parent.width + anchors.top: tabBar.bottom + anchors.bottom: parent.bottom + property var model: extrudersModel.items[tabBar.currentIndex] + property real textWidth: Math.round(width * 0.3) + property real controlWidth: Math.round(width * 0.7) + Column + { + spacing: UM.Theme.getSize("default_margin").height + Row + { + height: UM.Theme.getSize("print_setup_item").height + + Label + { + text: "Enabled" + verticalAlignment: Text.AlignVCenter + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + height: parent.height + width: tabControl.textWidth + } + + OldControls.CheckBox + { + checked: Cura.MachineManager.getExtruder(parent.model.index).isEnabled + onClicked: Cura.MachineManager.setExtruderEnabled(parent.model.index, checked) + height: UM.Theme.getSize("setting_control").height + style: UM.Theme.styles.checkbox + } + } + + Row + { + height: UM.Theme.getSize("print_setup_item").height + Label + { + text: "Material" + verticalAlignment: Text.AlignVCenter + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + height: parent.height + width: tabControl.textWidth + } + + OldControls.ToolButton + { + id: materialSelection + + property var activeExtruder: Cura.MachineManager.activeStack + property var hasActiveExtruder: activeExtruder != null + property var currentRootMaterialName: hasActiveExtruder ? activeExtruder.material.name : "" + property var valueError: hasActiveExtruder ? Cura.ContainerManager.getContainerMetaDataEntry(activeExtruder.material.id, "compatible", "") != "True" : true + property var valueWarning: ! Cura.MachineManager.isActiveQualitySupported + + text: currentRootMaterialName + tooltip: currentRootMaterialName + visible: Cura.MachineManager.hasMaterials + + enabled: !extrudersList.visible || Cura.ExtruderManager.activeExtruderIndex > -1 + + height: UM.Theme.getSize("setting_control").height + width: tabControl.controlWidth + + style: UM.Theme.styles.sidebar_header_button + activeFocusOnPress: true + menu: Cura.MaterialMenu + { + extruderIndex: Cura.ExtruderManager.activeExtruderIndex + } + + } + } + + Row + { + height: UM.Theme.getSize("print_setup_item").height + + Label + { + text: Cura.MachineManager.activeDefinitionVariantsName + verticalAlignment: Text.AlignVCenter + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + height: parent.height + width: tabControl.textWidth + } + + OldControls.ToolButton + { + id: variantSelection + text: Cura.MachineManager.activeVariantName + tooltip: Cura.MachineManager.activeVariantName; + visible: Cura.MachineManager.hasVariants + + height: UM.Theme.getSize("setting_control").height + width: tabControl.controlWidth + style: UM.Theme.styles.sidebar_header_button + activeFocusOnPress: true; + + menu: Cura.NozzleMenu { extruderIndex: Cura.ExtruderManager.activeExtruderIndex } + } + } + } + + } + } + } /*Item From 69daa0d91a2d7a70fa9d4e3a5d90778799dbca31 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Sun, 11 Nov 2018 12:44:56 +0100 Subject: [PATCH 0153/1240] Rename Toolbox menu to Marketplace. It was remove by mistake in a merge conflict. Contributes to CURA-5784. --- resources/qml/MainWindow/ApplicationMenu.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/MainWindow/ApplicationMenu.qml b/resources/qml/MainWindow/ApplicationMenu.qml index d3bc115419..04c068cb54 100644 --- a/resources/qml/MainWindow/ApplicationMenu.qml +++ b/resources/qml/MainWindow/ApplicationMenu.qml @@ -86,7 +86,7 @@ Item Menu { id: plugin_menu - title: catalog.i18nc("@title:menu menubar:toplevel", "&Toolbox") + title: catalog.i18nc("@title:menu menubar:toplevel", "&Marketplace") MenuItem { action: Cura.Actions.browsePackages } } From 3669a3791dc04f9fa35a9ef62cd3e57b8667ce94 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Sun, 11 Nov 2018 12:56:30 +0100 Subject: [PATCH 0154/1240] Hide the Preview shortcut in the action panel when the active stage is the PreviewStage. Also adjust the size of the output button when the Preview button is not visible. Contributes to CURA-5786. --- resources/qml/ActionPanel/OutputProcessWidget.qml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml index 0ea956d1dc..f4e014b1ec 100644 --- a/resources/qml/ActionPanel/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -102,6 +102,8 @@ Column Cura.ActionButton { + id: previewStageShortcut + leftPadding: UM.Theme.getSize("default_margin").width rightPadding: UM.Theme.getSize("default_margin").width height: UM.Theme.getSize("action_panel_button").height @@ -111,11 +113,12 @@ Column textColor: UM.Theme.getColor("primary") textHoverColor: UM.Theme.getColor("text") onClicked: UM.Controller.setActiveStage("PreviewStage") + visible: UM.Controller.activeStage != null && UM.Controller.activeStage.stageId != "PreviewStage" } Cura.OutputDevicesActionButton { - width: UM.Theme.getSize("action_panel_button").width + width: previewStageShortcut.visible ? UM.Theme.getSize("action_panel_button").width : parent.width height: UM.Theme.getSize("action_panel_button").height } } From f65b1f8fbc183fb1b696baacc263c54ef9246f03 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Sun, 11 Nov 2018 13:16:38 +0100 Subject: [PATCH 0155/1240] Add soft animation to the entire popup and not only in the background. This prevents the content to show before the background. Contributes to CURA-5784. --- resources/qml/Account/AccountWidget.qml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/resources/qml/Account/AccountWidget.qml b/resources/qml/Account/AccountWidget.qml index e37523296f..d3bd6fd130 100644 --- a/resources/qml/Account/AccountWidget.qml +++ b/resources/qml/Account/AccountWidget.qml @@ -51,6 +51,9 @@ Button closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent + opacity: opened ? 1 : 0 + Behavior on opacity { NumberAnimation { duration: 100 } } + contentItem: AccountDetails { id: panel @@ -61,8 +64,6 @@ Button background: UM.PointingRectangle { - opacity: visible ? 1 : 0 - Behavior on opacity { NumberAnimation { duration: 100 } } color: UM.Theme.getColor("tool_panel_background") borderColor: UM.Theme.getColor("lining") borderWidth: UM.Theme.getSize("default_lining").width From 8a63a79896b3c22f94562ddc0aec1b939103494b Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Sun, 11 Nov 2018 13:17:59 +0100 Subject: [PATCH 0156/1240] Add soft animation to the entire popup and not only in the background. This prevents the content to show before the background. Contributes to CURA-5786. --- .../ActionPanel/PrintInformationWidget.qml | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/resources/qml/ActionPanel/PrintInformationWidget.qml b/resources/qml/ActionPanel/PrintInformationWidget.qml index 620c3a408c..82707576e0 100644 --- a/resources/qml/ActionPanel/PrintInformationWidget.qml +++ b/resources/qml/ActionPanel/PrintInformationWidget.qml @@ -7,29 +7,30 @@ import QtQuick.Controls 2.1 import UM 1.1 as UM import Cura 1.0 as Cura -Button +UM.RecolorImage { id: widget - implicitHeight: UM.Theme.getSize("section_icon").height - implicitWidth: UM.Theme.getSize("section_icon").width + //implicitHeight: UM.Theme.getSize("section_icon").height + //implicitWidth: UM.Theme.getSize("section_icon").width - background: UM.RecolorImage + source: UM.Theme.getIcon("info") + width: UM.Theme.getSize("section_icon").width + height: UM.Theme.getSize("section_icon").height + + sourceSize.width: width + sourceSize.height: height + + color: popup.opened ? UM.Theme.getColor("primary") : UM.Theme.getColor("text_medium") + + MouseArea { - id: moreInformationIcon - - source: UM.Theme.getIcon("info") - width: UM.Theme.getSize("section_icon").width - height: UM.Theme.getSize("section_icon").height - - sourceSize.width: width - sourceSize.height: height - - color: widget.hovered ? UM.Theme.getColor("primary") : UM.Theme.getColor("text_medium") + anchors.fill: parent + hoverEnabled: true + onEntered: popup.open() + onExited: popup.close() } - onClicked: popup.opened ? popup.close() : popup.open() - Popup { id: popup @@ -39,6 +40,9 @@ Button closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent + opacity: opened ? 1 : 0 + Behavior on opacity { NumberAnimation { duration: 100 } } + contentItem: PrintJobInformation { id: printJobInformation @@ -47,8 +51,6 @@ Button background: UM.PointingRectangle { - opacity: visible ? 1 : 0 - Behavior on opacity { NumberAnimation { duration: 100 } } color: UM.Theme.getColor("tool_panel_background") borderColor: UM.Theme.getColor("lining") borderWidth: UM.Theme.getSize("default_lining").width From 38a80ecae59eeac754f10cd2ef00054ed09c3ba2 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Sun, 11 Nov 2018 14:06:52 +0100 Subject: [PATCH 0157/1240] Make some tweaks in the UI for the preview stage. Clean up the default theme a bit by removing unused entries. Contributes to CURA-5829. --- plugins/SimulationView/LayerSlider.qml | 10 ++--- plugins/SimulationView/PathSlider.qml | 9 ++-- .../SimulationViewMainComponent.qml | 4 +- resources/themes/cura-dark/theme.json | 1 - resources/themes/cura-light/styles.qml | 41 ------------------- resources/themes/cura-light/theme.json | 33 ++++++--------- 6 files changed, 21 insertions(+), 77 deletions(-) diff --git a/plugins/SimulationView/LayerSlider.qml b/plugins/SimulationView/LayerSlider.qml index 7916cd62b3..42b8cf0ba0 100644 --- a/plugins/SimulationView/LayerSlider.qml +++ b/plugins/SimulationView/LayerSlider.qml @@ -21,15 +21,12 @@ Item property color lowerHandleColor: UM.Theme.getColor("slider_handle") property color rangeHandleColor: UM.Theme.getColor("slider_groove_fill") property color handleActiveColor: UM.Theme.getColor("slider_handle_active") - property real handleLabelWidth: UM.Theme.getSize("slider_layerview_background").width property var activeHandle: upperHandle // Track properties property real trackThickness: UM.Theme.getSize("slider_groove").width // width of the slider track - property real trackRadius: trackThickness / 2 + property real trackRadius: UM.Theme.getSize("slider_groove_radius").width property color trackColor: UM.Theme.getColor("slider_groove") - property real trackBorderWidth: 1 - property color trackBorderColor: UM.Theme.getColor("slider_groove_border") // value properties property real maximumValue: 100 @@ -90,8 +87,6 @@ Item radius: sliderRoot.trackRadius anchors.centerIn: sliderRoot color: sliderRoot.trackColor - border.width: sliderRoot.trackBorderWidth - border.color: sliderRoot.trackBorderColor visible: sliderRoot.layersVisible } @@ -140,9 +135,10 @@ Item Rectangle { - width: sliderRoot.trackThickness - 2 * sliderRoot.trackBorderWidth + width: sliderRoot.trackThickness height: parent.height + sliderRoot.handleSize anchors.centerIn: parent + radius: sliderRoot.trackRadius color: sliderRoot.rangeHandleColor } diff --git a/plugins/SimulationView/PathSlider.qml b/plugins/SimulationView/PathSlider.qml index 701e54e398..c7a43c6407 100644 --- a/plugins/SimulationView/PathSlider.qml +++ b/plugins/SimulationView/PathSlider.qml @@ -23,10 +23,8 @@ Item // track properties property real trackThickness: UM.Theme.getSize("slider_groove").width - property real trackRadius: trackThickness / 2 + property real trackRadius: UM.Theme.getSize("slider_groove_radius").width property color trackColor: UM.Theme.getColor("slider_groove") - property real trackBorderWidth: 1 // width of the slider track border - property color trackBorderColor: UM.Theme.getColor("slider_groove_border") // value properties property real maximumValue: 100 @@ -68,8 +66,6 @@ Item radius: sliderRoot.trackRadius anchors.centerIn: sliderRoot color: sliderRoot.trackColor - border.width: sliderRoot.trackBorderWidth - border.color: sliderRoot.trackBorderColor visible: sliderRoot.pathsVisible } @@ -86,9 +82,10 @@ Item Rectangle { - height: sliderRoot.trackThickness - 2 * sliderRoot.trackBorderWidth + height: sliderRoot.trackThickness width: parent.width + sliderRoot.handleSize anchors.centerIn: parent + radius: sliderRoot.trackRadius color: sliderRoot.rangeColor } } diff --git a/plugins/SimulationView/SimulationViewMainComponent.qml b/plugins/SimulationView/SimulationViewMainComponent.qml index 784938e5c2..4e038a1cf1 100644 --- a/plugins/SimulationView/SimulationViewMainComponent.qml +++ b/plugins/SimulationView/SimulationViewMainComponent.qml @@ -18,7 +18,7 @@ Item { id: pathSlider height: UM.Theme.getSize("slider_handle").width - width: UM.Theme.getSize("layerview_menu_size").width + width: UM.Theme.getSize("slider_layerview_size").height anchors.bottom: parent.bottom anchors.bottomMargin: UM.Theme.getSize("default_margin").height @@ -166,7 +166,7 @@ Item id: layerSlider width: UM.Theme.getSize("slider_handle").width - height: UM.Theme.getSize("layerview_menu_size").height + height: UM.Theme.getSize("slider_layerview_size").height anchors { diff --git a/resources/themes/cura-dark/theme.json b/resources/themes/cura-dark/theme.json index 344efb6fd1..62b1dbfa0f 100644 --- a/resources/themes/cura-dark/theme.json +++ b/resources/themes/cura-dark/theme.json @@ -135,7 +135,6 @@ "slider_handle": [255, 255, 255, 255], "slider_handle_hover": [77, 182, 226, 255], "slider_handle_active": [68, 192, 255, 255], - "slider_handle_border": [39, 44, 48, 255], "slider_text_background": [255, 255, 255, 255], "checkbox": [43, 48, 52, 255], diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index dacff1b42b..503a3eddca 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -915,47 +915,6 @@ QtObject } } - property Component slider: Component - { - SliderStyle - { - groove: Rectangle - { - implicitWidth: control.width - implicitHeight: Theme.getSize("slider_groove").height - - color: Theme.getColor("slider_groove") - border.width: Theme.getSize("default_lining").width - border.color: Theme.getColor("slider_groove_border") - - radius: Math.round(width / 2) - - Rectangle - { - anchors - { - left: parent.left - top: parent.top - bottom: parent.bottom - } - color: Theme.getColor("slider_groove_fill"); - width: Math.round((control.value / (control.maximumValue - control.minimumValue)) * parent.width); - radius: Math.round(width / 2); - } - } - handle: Rectangle - { - width: Theme.getSize("slider_handle").width; - height: Theme.getSize("slider_handle").height; - color: control.hovered ? Theme.getColor("slider_handle_hover") : Theme.getColor("slider_handle"); - border.width: Theme.getSize("default_lining").width - border.color: control.hovered ? Theme.getColor("slider_handle_hover_border") : Theme.getColor("slider_handle_border") - radius: Math.round(Theme.getSize("slider_handle").width / 2); //Round. - Behavior on color { ColorAnimation { duration: 50; } } - } - } - } - property Component text_field: Component { TextFieldStyle diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index f882277d65..48cf5d456d 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -131,11 +131,11 @@ "button_disabled": [31, 36, 39, 255], "button_disabled_text": [255, 255, 255, 101], - "small_button": [31, 36, 39, 0], - "small_button_hover": [68, 72, 75, 255], - "small_button_active": [68, 72, 75, 255], - "small_button_active_hover": [68, 72, 75, 255], - "small_button_text": [31, 36, 39, 197], + "small_button": [0, 0, 0, 0], + "small_button_hover": [10, 8, 80, 255], + "small_button_active": [10, 8, 80, 255], + "small_button_active_hover": [10, 8, 80, 255], + "small_button_text": [171, 171, 191, 255], "small_button_text_hover": [255, 255, 255, 255], "small_button_text_active": [255, 255, 255, 255], "small_button_text_active_hover": [255, 255, 255, 255], @@ -215,13 +215,11 @@ "progressbar_background": [245, 245, 245, 255], "progressbar_control": [50, 130, 255, 255], - "slider_groove": [245, 245, 245, 255], - "slider_groove_border": [127, 127, 127, 255], - "slider_groove_fill": [127, 127, 127, 255], - "slider_handle": [0, 0, 0, 255], + "slider_groove": [223, 223, 223, 255], + "slider_groove_fill": [10, 8, 80, 255], + "slider_handle": [10, 8, 80, 255], "slider_handle_hover": [77, 182, 226, 255], - "slider_handle_active": [68, 192, 255, 255], - "slider_handle_border": [39, 44, 48, 255], + "slider_handle_active": [50, 130, 255, 255], "slider_text_background": [255, 255, 255, 255], "quality_slider_unavailable": [179, 179, 179, 255], @@ -449,17 +447,12 @@ "scrollbar": [0.75, 0.5], - "quality_slider_bar": [1, 0.2], - - "slider_groove": [0.3, 0.3], - "slider_handle": [1.0, 1.0], - "slider_layerview_size": [1.0, 22.0], - "slider_layerview_background": [4.0, 0.0], - "slider_layerview_margin": [1.0, 1.5], + "slider_groove": [0.5, 0.5], + "slider_groove_radius": [0.15, 0.15], + "slider_handle": [1.5, 1.5], + "slider_layerview_size": [1.0, 26.0], "layerview_menu_size": [15, 20], - "layerview_menu_size_material_color_mode": [15, 16], - "layerview_menu_size_collapsed": [15, 6], "layerview_legend_size": [1.0, 1.0], "layerview_row": [11.0, 1.5], "layerview_row_spacing": [0.0, 0.5], From 9bf18031ce33f41d649e5471048ac84a3dc06bd9 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Sun, 11 Nov 2018 19:15:02 +0100 Subject: [PATCH 0158/1240] Apply restyle of the toolbar according to the new designs. --- .../qml/ActionPanel/ActionPanelWidget.qml | 1 - resources/qml/Cura.qml | 2 + resources/qml/ExtruderButton.qml | 35 +++-- resources/qml/Toolbar.qml | 130 +++++++++++------- resources/themes/cura-light/styles.qml | 75 ++++++++++ resources/themes/cura-light/theme.json | 9 +- 6 files changed, 185 insertions(+), 67 deletions(-) diff --git a/resources/qml/ActionPanel/ActionPanelWidget.qml b/resources/qml/ActionPanel/ActionPanelWidget.qml index 0db778de5a..a1cd81e9e9 100644 --- a/resources/qml/ActionPanel/ActionPanelWidget.qml +++ b/resources/qml/ActionPanel/ActionPanelWidget.qml @@ -23,7 +23,6 @@ Rectangle border.width: UM.Theme.getSize("default_lining").width border.color: UM.Theme.getColor("lining") radius: UM.Theme.getSize("default_radius").width - visible: CuraApplication.platformActivity property bool outputAvailable: UM.Backend.state == UM.Backend.Done || UM.Backend.state == UM.Backend.Disabled diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index dc64aeee38..819d986ece 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -186,6 +186,7 @@ UM.MainWindow verticalCenter: parent.verticalCenter left: parent.left } + visible: CuraApplication.platformActivity } ObjectsList @@ -235,6 +236,7 @@ UM.MainWindow anchors.bottom: parent.bottom anchors.rightMargin: UM.Theme.getSize("thick_margin").width anchors.bottomMargin: UM.Theme.getSize("thick_margin").height + visible: CuraApplication.platformActivity } Loader diff --git a/resources/qml/ExtruderButton.qml b/resources/qml/ExtruderButton.qml index 2c1b80047e..7923521658 100644 --- a/resources/qml/ExtruderButton.qml +++ b/resources/qml/ExtruderButton.qml @@ -15,37 +15,36 @@ Button text: catalog.i18ncp("@label %1 is filled in with the name of an extruder", "Print Selected Model with %1", "Print Selected Models with %1", UM.Selection.selectionCount).arg(extruder.name) - style: UM.Theme.styles.tool_button; + style: UM.Theme.styles.toolbar_button iconSource: UM.Theme.getIcon("extruder_button") checked: Cura.ExtruderManager.selectedObjectExtruders.indexOf(extruder.id) != -1 enabled: UM.Selection.hasSelection && extruder.stack.isEnabled - property color customColor: base.hovered ? UM.Theme.getColor("button_hover") : UM.Theme.getColor("button"); - - Rectangle - { - anchors.fill: parent - anchors.margins: UM.Theme.getSize("default_lining").width; - - color: "transparent" - - border.width: base.checked ? UM.Theme.getSize("default_lining").width : 0; - border.color: UM.Theme.getColor("button_text") - } - Item { anchors.centerIn: parent width: UM.Theme.getSize("default_margin").width height: UM.Theme.getSize("default_margin").height + opacity: !base.enabled ? 0.2 : 1.0 Label { - anchors.centerIn: parent; - text: index + 1; - color: parent.enabled ? UM.Theme.getColor("button_text") : UM.Theme.getColor("button_disabled_text") - font: UM.Theme.getFont("default_bold"); + anchors.centerIn: parent + text: index + 1 + color: + { + if (base.checked) + { + return UM.Theme.getColor("toolbar_button_text_active") + } + else if(base.hovered) + { + return UM.Theme.getColor("toolbar_button_text_hover") + } + return UM.Theme.getColor("toolbar_button_text") + } + font: UM.Theme.getFont("default_bold") } } diff --git a/resources/qml/Toolbar.qml b/resources/qml/Toolbar.qml index a04b3650df..9955ceeba7 100644 --- a/resources/qml/Toolbar.qml +++ b/resources/qml/Toolbar.qml @@ -11,75 +11,113 @@ import Cura 1.0 as Cura Item { - id: base; + id: base - width: buttons.width; + width: buttons.width height: buttons.height property int activeY - Column + Item { - id: buttons; + id: buttons + width: parent.visible ? toolButtons.width : 0 + height: childrenRect.height - anchors.bottom: parent.bottom - anchors.left: parent.left - spacing: UM.Theme.getSize("button_lining").width + Behavior on width { NumberAnimation { duration: 100 } } - Repeater + // Used to create a rounded rectangle behind the toolButtons + Rectangle { - id: repeat + anchors.fill: toolButtons + anchors.leftMargin: -radius + radius: UM.Theme.getSize("default_radius").width + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") + color: UM.Theme.getColor("toolbar_background") + } - model: UM.ToolModel { } - width: childrenRect.width - height: childrenRect.height - Button + Column + { + id: toolButtons + + anchors.top: parent.top + anchors.right: parent.right + spacing: UM.Theme.getSize("button_lining").width + + Repeater { - text: model.name + (model.shortcut ? (" (" + model.shortcut + ")") : "") - iconSource: (UM.Theme.getIcon(model.icon) != "") ? UM.Theme.getIcon(model.icon) : "file:///" + model.location + "/" + model.icon - checkable: true - checked: model.active - enabled: model.enabled && UM.Selection.hasSelection && UM.Controller.toolsEnabled - style: UM.Theme.styles.tool_button + id: repeat - onCheckedChanged: + model: UM.ToolModel { } + width: childrenRect.width + height: childrenRect.height + Button { - if (checked) - { - base.activeY = y; - } - } + text: model.name + (model.shortcut ? (" (" + model.shortcut + ")") : "") + iconSource: (UM.Theme.getIcon(model.icon) != "") ? UM.Theme.getIcon(model.icon) : "file:///" + model.location + "/" + model.icon + checkable: true + checked: model.active + enabled: model.enabled && UM.Selection.hasSelection && UM.Controller.toolsEnabled + style: UM.Theme.styles.toolbar_button - //Workaround since using ToolButton's onClicked would break the binding of the checked property, instead - //just catch the click so we do not trigger that behaviour. - MouseArea - { - anchors.fill: parent; - onClicked: + onCheckedChanged: { - forceActiveFocus() //First grab focus, so all the text fields are updated - if(parent.checked) + if (checked) { - UM.Controller.setActiveTool(null); + base.activeY = y; } - else + } + + //Workaround since using ToolButton's onClicked would break the binding of the checked property, instead + //just catch the click so we do not trigger that behaviour. + MouseArea + { + anchors.fill: parent; + onClicked: { - UM.Controller.setActiveTool(model.id); + forceActiveFocus() //First grab focus, so all the text fields are updated + if(parent.checked) + { + UM.Controller.setActiveTool(null); + } + else + { + UM.Controller.setActiveTool(model.id); + } } } } } } - Item { height: UM.Theme.getSize("default_margin").height; width: UM.Theme.getSize("default_lining").width; visible: extruders.count > 0 } - - Repeater + // Used to create a rounded rectangle behind the extruderButtons + Rectangle { - id: extruders - width: childrenRect.width - height: childrenRect.height - property var _model: Cura.ExtrudersModel { id: extrudersModel } - model: _model.items.length > 1 ? _model : 0 - ExtruderButton { extruder: model } + anchors.fill: extruderButtons + anchors.leftMargin: -radius + radius: UM.Theme.getSize("default_radius").width + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") + color: UM.Theme.getColor("toolbar_background") + } + + Column + { + id: extruderButtons + + anchors.topMargin: UM.Theme.getSize("default_margin").height + anchors.top: toolButtons.bottom + anchors.right: parent.right + + Repeater + { + id: extruders + width: childrenRect.width + height: childrenRect.height + property var _model: Cura.ExtrudersModel { id: extrudersModel } + model: _model.items.length > 1 ? _model : 0 + ExtruderButton { extruder: model } + } } } @@ -91,7 +129,7 @@ Item anchors.leftMargin: UM.Theme.getSize("default_margin").width; anchors.top: base.top; anchors.topMargin: base.activeY - z: buttons.z -1 + z: buttons.z - 1 target: Qt.point(parent.right, base.activeY + Math.round(UM.Theme.getSize("button").height/2)) arrowSize: UM.Theme.getSize("default_arrow").width diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index 503a3eddca..52ae3cd243 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -171,6 +171,81 @@ QtObject } } + property Component toolbar_button: Component + { + ButtonStyle + { + background: Item + { + implicitWidth: Theme.getSize("button").width; + implicitHeight: Theme.getSize("button").height; + + UM.PointingRectangle + { + id: button_tooltip + + anchors.left: parent.right + anchors.leftMargin: Theme.getSize("button_tooltip_arrow").width * 2 + anchors.verticalCenter: parent.verticalCenter + + target: Qt.point(parent.x, y + Math.round(height/2)) + arrowSize: Theme.getSize("button_tooltip_arrow").width + color: Theme.getColor("button_tooltip") + opacity: control.hovered ? 1.0 : 0.0; + visible: control.text != "" + + width: control.hovered ? button_tip.width + Theme.getSize("button_tooltip").width : 0 + height: Theme.getSize("button_tooltip").height + + Behavior on width { NumberAnimation { duration: 100; } } + Behavior on opacity { NumberAnimation { duration: 100; } } + + Label + { + id: button_tip + + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter; + + text: control.text; + font: Theme.getFont("button_tooltip"); + color: Theme.getColor("tooltip_text"); + } + } + } + + label: Item + { + UM.RecolorImage + { + anchors.centerIn: parent; + opacity: !control.enabled ? 0.2 : 1.0 + source: control.iconSource; + width: Theme.getSize("button_icon").width; + height: Theme.getSize("button_icon").height; + color: + { + if(control.checkable && control.checked && control.hovered) + { + return Theme.getColor("toolbar_button_text_active_hover"); + } + else if(control.pressed || (control.checkable && control.checked)) + { + return Theme.getColor("toolbar_button_text_active"); + } + else if(control.hovered) + { + return Theme.getColor("toolbar_button_text_hover"); + } + return Theme.getColor("toolbar_button_text"); + } + + sourceSize: Theme.getSize("button_icon") + } + } + } + } + property Component tool_button: Component { ButtonStyle diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 48cf5d456d..a33ff87042 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -105,6 +105,8 @@ "action_panel_secondary": [27, 95, 202, 255], + "toolbar_background": [255, 255, 255, 255], + "text": [0, 0, 0, 255], "text_detail": [174, 174, 174, 128], "text_link": [50, 130, 255, 255], @@ -120,6 +122,11 @@ "error": [255, 140, 0, 255], "warning": [255, 190, 35, 255], + "toolbar_button_text": [10, 8, 80, 255], + "toolbar_button_text_hover": [50, 130, 255, 255], + "toolbar_button_text_active": [50, 130, 255, 255], + "toolbar_button_text_active_hover": [50, 130, 255, 255], + "button": [31, 36, 39, 255], "button_hover": [68, 72, 75, 255], "button_active": [68, 72, 75, 255], @@ -128,8 +135,6 @@ "button_text_hover": [255, 255, 255, 255], "button_text_active": [255, 255, 255, 255], "button_text_active_hover": [255, 255, 255, 255], - "button_disabled": [31, 36, 39, 255], - "button_disabled_text": [255, 255, 255, 101], "small_button": [0, 0, 0, 0], "small_button_hover": [10, 8, 80, 255], From f534885e341bd3fd1098d59e55c7231ff29c5b76 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 12 Nov 2018 09:35:32 +0100 Subject: [PATCH 0159/1240] Modify the color behavior of the toolbar buttons. --- resources/themes/cura-light/styles.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index 52ae3cd243..6f099bf1c5 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -225,11 +225,11 @@ QtObject height: Theme.getSize("button_icon").height; color: { - if(control.checkable && control.checked && control.hovered) + if (control.checked && control.hovered) { return Theme.getColor("toolbar_button_text_active_hover"); } - else if(control.pressed || (control.checkable && control.checked)) + else if (control.checked) { return Theme.getColor("toolbar_button_text_active"); } From e64158402137953789861adfbe77f2345b5bb983 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 12 Nov 2018 09:45:55 +0100 Subject: [PATCH 0160/1240] Made strings in ConfigurationSelector translatable. CURA-5785 --- .../ConfigurationMenu/QuickConfigurationSelector.qml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml index e093c96ae5..3c354384c7 100644 --- a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml +++ b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml @@ -15,11 +15,18 @@ import Cura 1.0 as Cura Cura.ExpandableComponent { id: base + Cura.ExtrudersModel { id: extrudersModel } + UM.I18nCatalog + { + id: catalog + name: "cura" + } + headerItem: Item { // Horizontal list that shows the extruders @@ -132,7 +139,7 @@ Cura.ExpandableComponent Label { - text: "Enabled" + text: catalog.i18nc("@label", "Enabled") verticalAlignment: Text.AlignVCenter font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") @@ -154,7 +161,7 @@ Cura.ExpandableComponent height: UM.Theme.getSize("print_setup_item").height Label { - text: "Material" + text: catalog.i18nc("@label", "Material") verticalAlignment: Text.AlignVCenter font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") From b60977e435676171c3bb678c98f74c449a474cd3 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 12 Nov 2018 11:14:39 +0100 Subject: [PATCH 0161/1240] Convert PrintSetupSelector to an ExpandableComponent CURA-5785 --- plugins/PrepareStage/PrepareMenu.qml | 1 + resources/qml/PrintSetupSelector.qml | 281 ++++++++++++--------------- 2 files changed, 125 insertions(+), 157 deletions(-) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index bcc8ec4c0a..ff8cdc2a47 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -66,6 +66,7 @@ Item Cura.PrintSetupSelector { width: UM.Theme.getSize("print_setup_widget").width + height: prepareMenu.height onShowTooltip: prepareMenu.showTooltip(item, location, text) onHideTooltip: prepareMenu.hideTooltip() } diff --git a/resources/qml/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector.qml index 8232e76a17..7a415b1e2e 100644 --- a/resources/qml/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector.qml @@ -10,22 +10,14 @@ import Cura 1.0 as Cura import "Menus" import "Menus/ConfigurationMenu" -Rectangle +Cura.ExpandableComponent { id: base height: childrenRect.height - property int currentModeIndex: -1 property bool hideSettings: PrintInformation.preSliced - property variant printDuration: PrintInformation.currentPrintTime - property variant printMaterialLengths: PrintInformation.materialLengths - property variant printMaterialWeights: PrintInformation.materialWeights - property variant printMaterialCosts: PrintInformation.materialCosts - property variant printMaterialNames: PrintInformation.materialNames - - color: UM.Theme.getColor("main_background") UM.I18nCatalog { id: catalog; name: "cura"} // This widget doesn't show tooltips by itself. Instead it emits signals so others can do something with it. @@ -46,40 +38,12 @@ Rectangle } } - function strPadLeft(string, pad, length) - { - return (new Array(length + 1).join(pad) + string).slice(-length); - } - - function getPrettyTime(time) - { - var hours = Math.floor(time / 3600) - time -= hours * 3600 - var minutes = Math.floor(time / 60); - time -= minutes * 60 - var seconds = Math.floor(time); - - var finalTime = strPadLeft(hours, "0", 2) + ":" + strPadLeft(minutes, "0", 2) + ":" + strPadLeft(seconds, "0", 2); - return finalTime; - } - - MouseArea - { - anchors.fill: parent - acceptedButtons: Qt.AllButtons - - onWheel: - { - wheel.accepted = true; - } - } - onCurrentModeIndexChanged: { UM.Preferences.setValue("cura/active_mode", currentModeIndex); } - Label + headerItem: Label { id: settingsModeLabel text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox", "Print Setup disabled\nG-code files cannot be modified") @@ -98,146 +62,149 @@ Rectangle color: UM.Theme.getColor("text") } - - ListView + popupItem: Item { - // Settings mode selection toggle - id: settingsModeSelection - model: modesListModel - width: Math.round(parent.width * 0.55) - height: UM.Theme.getSize("print_setup_mode_toggle").height - visible: !hideSettings - - anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("thick_margin").width - anchors.top: settingsModeLabel.top - - ButtonGroup + height: settingsModeSelection.height + sidebarContents.height + width: UM.Theme.getSize("print_setup_widget").width + ListView { - id: modeMenuGroup + // Settings mode selection toggle + id: settingsModeSelection + model: modesListModel + width: Math.round(parent.width * 0.55) + height: UM.Theme.getSize("print_setup_mode_toggle").height + visible: !hideSettings + + anchors.right: parent.right + anchors.rightMargin: UM.Theme.getSize("thick_margin").width + + ButtonGroup + { + id: modeMenuGroup + } + + delegate: Button + { + id: control + + height: settingsModeSelection.height + width: Math.round(parent.width / 2) + + anchors.left: parent.left + anchors.leftMargin: model.index * Math.round(settingsModeSelection.width / 2) + anchors.verticalCenter: parent.verticalCenter + + ButtonGroup.group: modeMenuGroup + + checkable: true + checked: base.currentModeIndex == index + onClicked: base.currentModeIndex = index + + onHoveredChanged: + { + if (hovered) + { + tooltipDelayTimer.item = settingsModeSelection + tooltipDelayTimer.text = model.tooltipText + tooltipDelayTimer.start() + } + else + { + tooltipDelayTimer.stop() + base.hideTooltip() + } + } + + background: Rectangle + { + border.width: control.checked ? UM.Theme.getSize("default_lining").width * 2 : UM.Theme.getSize("default_lining").width + border.color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_border") : control.hovered ? UM.Theme.getColor("action_button_hovered_border") : UM.Theme.getColor("action_button_border") + + // for some reason, QtQuick decided to use the color of the background property as text color for the contentItem, so here it is + color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active") : control.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button") + } + + contentItem: Label + { + text: model.text + font: UM.Theme.getFont("default") + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + renderType: Text.NativeRendering + elide: Text.ElideRight + color: + { + if(control.pressed) + { + return UM.Theme.getColor("action_button_active_text"); + } + else if(control.hovered) + { + return UM.Theme.getColor("action_button_hovered_text"); + } + return UM.Theme.getColor("action_button_text"); + } + } + } } - delegate: Button + Item { - id: control - - height: settingsModeSelection.height - width: Math.round(parent.width / 2) - + id: sidebarContents + anchors.top: settingsModeSelection.bottom + anchors.topMargin: UM.Theme.getSize("thick_margin").height anchors.left: parent.left - anchors.leftMargin: model.index * Math.round(settingsModeSelection.width / 2) - anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + height: UM.Theme.getSize("print_setup_widget").height - ButtonGroup.group: modeMenuGroup + visible: !hideSettings - checkable: true - checked: base.currentModeIndex == index - onClicked: base.currentModeIndex = index - - onHoveredChanged: + // We load both of them at once (instead of using a loader) because the advanced sidebar can take + // quite some time to load. So in this case we sacrifice memory for speed. + SidebarAdvanced { - if (hovered) - { - tooltipDelayTimer.item = settingsModeSelection - tooltipDelayTimer.text = model.tooltipText - tooltipDelayTimer.start() - } - else - { - tooltipDelayTimer.stop() - base.hideTooltip() - } + anchors.fill: parent + visible: currentModeIndex == 1 + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() } - background: Rectangle + SidebarSimple { - border.width: control.checked ? UM.Theme.getSize("default_lining").width * 2 : UM.Theme.getSize("default_lining").width - border.color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_border") : control.hovered ? UM.Theme.getColor("action_button_hovered_border") : UM.Theme.getColor("action_button_border") - - // for some reason, QtQuick decided to use the color of the background property as text color for the contentItem, so here it is - color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active") : control.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button") - } - - contentItem: Label - { - text: model.text - font: UM.Theme.getFont("default") - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - renderType: Text.NativeRendering - elide: Text.ElideRight - color: - { - if(control.pressed) - { - return UM.Theme.getColor("action_button_active_text"); - } - else if(control.hovered) - { - return UM.Theme.getColor("action_button_hovered_text"); - } - return UM.Theme.getColor("action_button_text"); - } + anchors.fill: parent + visible: currentModeIndex != 1 + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() } } - } - Item - { - id: sidebarContents - anchors.top: settingsModeSelection.bottom - anchors.topMargin: UM.Theme.getSize("thick_margin").height - anchors.left: parent.left - anchors.right: parent.right - height: UM.Theme.getSize("print_setup_widget").height - - visible: !hideSettings - - // We load both of them at once (instead of using a loader) because the advanced sidebar can take - // quite some time to load. So in this case we sacrifice memory for speed. - SidebarAdvanced + // Setting mode: Recommended or Custom + ListModel { - anchors.fill: parent - visible: currentModeIndex == 1 - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() + id: modesListModel } - SidebarSimple + Component.onCompleted: { - anchors.fill: parent - visible: currentModeIndex != 1 - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() - } - } + modesListModel.append({ + text: catalog.i18nc("@title:tab", "Recommended"), + tooltipText: "%1

%2".arg(catalog.i18nc("@tooltip:title", "Recommended Print Setup")).arg(catalog.i18nc("@tooltip", "Print with the recommended settings for the selected printer, material and quality.")) + }) + modesListModel.append({ + text: catalog.i18nc("@title:tab", "Custom"), + tooltipText: "%1

%2".arg(catalog.i18nc("@tooltip:title", "Custom Print Setup")).arg(catalog.i18nc("@tooltip", "Print with finegrained control over every last bit of the slicing process.")) + }) - // Setting mode: Recommended or Custom - ListModel - { - id: modesListModel - } + var index = Math.round(UM.Preferences.getValue("cura/active_mode")) - Component.onCompleted: - { - modesListModel.append({ - text: catalog.i18nc("@title:tab", "Recommended"), - tooltipText: "%1

%2".arg(catalog.i18nc("@tooltip:title", "Recommended Print Setup")).arg(catalog.i18nc("@tooltip", "Print with the recommended settings for the selected printer, material and quality.")) - }) - modesListModel.append({ - text: catalog.i18nc("@title:tab", "Custom"), - tooltipText: "%1

%2".arg(catalog.i18nc("@tooltip:title", "Custom Print Setup")).arg(catalog.i18nc("@tooltip", "Print with finegrained control over every last bit of the slicing process.")) - }) - - var index = Math.round(UM.Preferences.getValue("cura/active_mode")) - - if(index != null && !isNaN(index)) - { - currentModeIndex = index; - } - else - { - currentModeIndex = 0; + if(index != null && !isNaN(index)) + { + currentModeIndex = index; + } + else + { + currentModeIndex = 0; + } } } } From 88b57518d9770ea1406cd0a97fc975132588483c Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 12 Nov 2018 11:24:11 +0100 Subject: [PATCH 0162/1240] Fix codestyle CURA-5785 --- resources/qml/SidebarSimple.qml | 187 ++++++++++++++++---------------- 1 file changed, 94 insertions(+), 93 deletions(-) diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index 59980d1f55..affd1eb3a2 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -13,16 +13,18 @@ Item { id: base - signal showTooltip(Item item, point location, string text); - signal hideTooltip(); + signal showTooltip(Item item, point location, string text) + signal hideTooltip() + + property Action configureSettings - property Action configureSettings; - property variant minimumPrintTime: PrintInformation.minimumPrintTime; - property variant maximumPrintTime: PrintInformation.maximumPrintTime; property bool settingsEnabled: Cura.ExtruderManager.activeExtruderStackId || extrudersEnabledCount.properties.value == 1 - Component.onCompleted: PrintInformation.enabled = true - Component.onDestruction: PrintInformation.enabled = false - UM.I18nCatalog { id: catalog; name: "cura" } + + UM.I18nCatalog + { + id: catalog + name: "cura" + } ScrollView { @@ -313,7 +315,7 @@ Item } } - Rectangle + Item { id: rightArea width: @@ -324,7 +326,6 @@ Item return qualityModel.qualitySliderMarginRight - 10 } height: parent.height - color: "transparent" x: { if (qualityModel.availableTotalTicks == 0) @@ -450,10 +451,7 @@ Item var content = catalog.i18nc("@tooltip","A custom profile is currently active. To enable the quality slider, choose a default quality profile in Custom tab") base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, customisedSettings.height), content) } - onExited: - { - base.hideTooltip(); - } + onExited: base.hideTooltip() } } @@ -575,7 +573,8 @@ Item } // We use a binding to make sure that after manually setting infillSlider.value it is still bound to the property provider - Binding { + Binding + { target: infillSlider property: "value" value: parseInt(infillDensity.properties.value) @@ -604,10 +603,12 @@ Item // set initial value from stack value: parseInt(infillDensity.properties.value) - onValueChanged: { + onValueChanged: + { // Don't round the value if it's already the same - if (parseInt(infillDensity.properties.value) == infillSlider.value) { + if (parseInt(infillDensity.properties.value) == infillSlider.value) + { return } @@ -631,7 +632,8 @@ Item style: SliderStyle { - groove: Rectangle { + groove: Rectangle + { id: groove implicitWidth: 200 * screenScaleFactor implicitHeight: 2 * screenScaleFactor @@ -639,8 +641,10 @@ Item radius: 1 } - handle: Item { - Rectangle { + handle: Item + { + Rectangle + { id: handleButton anchors.centerIn: parent color: control.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") @@ -650,24 +654,27 @@ Item } } - tickmarks: Repeater { + tickmarks: Repeater + { id: repeater model: control.maximumValue / control.stepSize + 1 // check if a tick should be shown based on it's index and wether the infill density is a multiple of 10 (slider step size) - function shouldShowTick (index) { - if (index % 10 == 0) { + function shouldShowTick (index) + { + if (index % 10 == 0) + { return true } return false } - Rectangle { + Rectangle + { anchors.verticalCenter: parent.verticalCenter color: control.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") width: 1 * screenScaleFactor height: 6 * screenScaleFactor - y: 0 x: Math.round(styleData.handleWidth / 2 + index * ((repeater.width - styleData.handleWidth) / (repeater.count-1))) visible: shouldShowTick(index) } @@ -693,8 +700,10 @@ Item model: infillModel anchors.fill: parent - function activeIndex () { - for (var i = 0; i < infillModel.count; i++) { + function activeIndex () + { + for (var i = 0; i < infillModel.count; i++) + { var density = Math.round(infillDensity.properties.value) var steps = Math.round(infillSteps.properties.value) var infillModelItem = infillModel.get(i) @@ -703,8 +712,8 @@ Item && density >= infillModelItem.percentageMin && density <= infillModelItem.percentageMax && steps >= infillModelItem.stepsMin - && steps <= infillModelItem.stepsMax - ){ + && steps <= infillModelItem.stepsMax) + { return i } } @@ -719,7 +728,8 @@ Item border.width: UM.Theme.getSize("default_lining").width border.color: UM.Theme.getColor("quality_slider_unavailable") - UM.RecolorImage { + UM.RecolorImage + { anchors.fill: parent anchors.margins: 2 * screenScaleFactor sourceSize.width: width @@ -732,7 +742,8 @@ Item } // Gradual Support Infill Checkbox - CheckBox { + CheckBox + { id: enableGradualInfillCheckBox property alias _hovered: enableGradualInfillMouseArea.containsMouse @@ -745,7 +756,8 @@ Item visible: infillSteps.properties.enabled == "True" checked: parseInt(infillSteps.properties.value) > 0 - MouseArea { + MouseArea + { id: enableGradualInfillMouseArea anchors.fill: parent @@ -754,35 +766,37 @@ Item property var previousInfillDensity: parseInt(infillDensity.properties.value) - onClicked: { + onClicked: + { // Set to 90% only when enabling gradual infill var newInfillDensity; - if (parseInt(infillSteps.properties.value) == 0) { + if (parseInt(infillSteps.properties.value) == 0) + { previousInfillDensity = parseInt(infillDensity.properties.value) - newInfillDensity = 90; + newInfillDensity = 90 } else { - newInfillDensity = previousInfillDensity; + newInfillDensity = previousInfillDensity } Cura.MachineManager.setSettingForAllExtruders("infill_sparse_density", "value", String(newInfillDensity)) - var infill_steps_value = 0; + var infill_steps_value = 0 if (parseInt(infillSteps.properties.value) == 0) - infill_steps_value = 5; + { + infill_steps_value = 5 + } Cura.MachineManager.setSettingForAllExtruders("gradual_infill_steps", "value", infill_steps_value) } - onEntered: { - base.showTooltip(enableGradualInfillCheckBox, Qt.point(-infillCellRight.x, 0), + onEntered: base.showTooltip(enableGradualInfillCheckBox, Qt.point(-infillCellRight.x, 0), catalog.i18nc("@label", "Gradual infill will gradually increase the amount of infill towards the top.")) - } - onExited: { - base.hideTooltip() - } + onExited: base.hideTooltip() + } - Label { + Label + { id: gradualInfillLabel height: parent.height anchors.left: enableGradualInfillCheckBox.right @@ -855,9 +869,9 @@ Item anchors.rightMargin: UM.Theme.getSize("thick_margin").width anchors.verticalCenter: enableSupportCheckBox.verticalCenter - text: catalog.i18nc("@label", "Generate Support"); - font: UM.Theme.getFont("default"); - color: UM.Theme.getColor("text"); + text: catalog.i18nc("@label", "Generate Support") + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") elide: Text.ElideRight } @@ -869,32 +883,24 @@ Item anchors.top: enableSupportLabel.top anchors.left: infillCellRight.left - style: UM.Theme.styles.checkbox; + style: UM.Theme.styles.checkbox enabled: base.settingsEnabled visible: supportEnabled.properties.enabled == "True" - checked: supportEnabled.properties.value == "True"; + checked: supportEnabled.properties.value == "True" MouseArea { id: enableSupportMouseArea anchors.fill: parent hoverEnabled: true - enabled: true - onClicked: - { - // The value is a string "True" or "False" - supportEnabled.setPropertyValue("value", supportEnabled.properties.value != "True"); - } - onEntered: - { - base.showTooltip(enableSupportCheckBox, Qt.point(-enableSupportCheckBox.x, 0), - catalog.i18nc("@label", "Generate structures to support parts of the model which have overhangs. Without these structures, such parts would collapse during printing.")); - } - onExited: - { - base.hideTooltip(); - } + onClicked: supportEnabled.setPropertyValue("value", supportEnabled.properties.value != "True") + + onEntered: base.showTooltip(enableSupportCheckBox, Qt.point(-enableSupportCheckBox.x, 0), + catalog.i18nc("@label", "Generate structures to support parts of the model which have overhangs. Without these structures, such parts would collapse during printing.")) + + onExited: base.hideTooltip() + } } @@ -916,7 +922,7 @@ Item textRole: "text" // this solves that the combobox isn't populated in the first time Cura is started anchors.top: enableSupportCheckBox.top - //anchors.topMargin: ((supportEnabled.properties.value === "True") && (machineExtruderCount.properties.value > 1)) ? UM.Theme.getSize("thick_margin").height : 0 + anchors.left: enableSupportCheckBox.right anchors.leftMargin: Math.round(UM.Theme.getSize("thick_margin").width / 2) @@ -933,24 +939,21 @@ Item { if (supportExtruderNr.properties == null) { - return Cura.MachineManager.defaultExtruderPosition; + return Cura.MachineManager.defaultExtruderPosition } else { - var extruder = parseInt(supportExtruderNr.properties.value); + var extruder = parseInt(supportExtruderNr.properties.value) if ( extruder === -1) { - return Cura.MachineManager.defaultExtruderPosition; + return Cura.MachineManager.defaultExtruderPosition } return extruder; } } - onActivated: - { - // Send the extruder nr as a string. - supportExtruderNr.setPropertyValue("value", String(index)); - } + onActivated: supportExtruderNr.setPropertyValue("value", String(index)) + MouseArea { id: supportExtruderMouseArea @@ -963,17 +966,16 @@ Item base.showTooltip(supportExtruderCombobox, Qt.point(-supportExtruderCombobox.x, 0), catalog.i18nc("@label", "Select which extruder to use for support. This will build up supporting structures below the model to prevent the model from sagging or printing in mid air.")); } - onExited: - { - base.hideTooltip(); - } + onExited: base.hideTooltip() + } function updateCurrentColor() { - var current_extruder = extruderModel.get(currentIndex); - if (current_extruder !== undefined) { - supportExtruderCombobox.color_override = current_extruder.color; + var current_extruder = extruderModel.get(currentIndex) + if (current_extruder !== undefined) + { + supportExtruderCombobox.color_override = current_extruder.color } } @@ -989,7 +991,8 @@ Item color: UM.Theme.getColor("text") elide: Text.ElideRight - anchors { + anchors + { left: parent.left leftMargin: UM.Theme.getSize("thick_margin").width right: infillCellLeft.right @@ -1008,7 +1011,7 @@ Item anchors.left: infillCellRight.left //: Setting enable printing build-plate adhesion helper checkbox - style: UM.Theme.styles.checkbox; + style: UM.Theme.styles.checkbox enabled: base.settingsEnabled visible: platformAdhesionType.properties.enabled == "True" @@ -1022,29 +1025,27 @@ Item enabled: base.settingsEnabled onClicked: { - var adhesionType = "skirt"; + var adhesionType = "skirt" if(!parent.checked) { // Remove the "user" setting to see if the rest of the stack prescribes a brim or a raft - platformAdhesionType.removeFromContainer(0); - adhesionType = platformAdhesionType.properties.value; + platformAdhesionType.removeFromContainer(0) + adhesionType = platformAdhesionType.properties.value if(adhesionType == "skirt" || adhesionType == "none") { // If the rest of the stack doesn't prescribe an adhesion-type, default to a brim - adhesionType = "brim"; + adhesionType = "brim" } } - platformAdhesionType.setPropertyValue("value", adhesionType); + platformAdhesionType.setPropertyValue("value", adhesionType) } onEntered: { base.showTooltip(adhesionCheckBox, Qt.point(-adhesionCheckBox.x, 0), catalog.i18nc("@label", "Enable printing a brim or raft. This will add a flat area around or under your object which is easy to cut off afterwards.")); } - onExited: - { - base.hideTooltip(); - } + onExited: base.hideTooltip() + } } @@ -1163,6 +1164,6 @@ Item color: extruders.getItem(extruderNumber).color }) } - supportExtruderCombobox.updateCurrentColor(); + supportExtruderCombobox.updateCurrentColor() } } From 987ff1737ff11223ec986cf74d79a0d529f2c5dd Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 12 Nov 2018 11:27:54 +0100 Subject: [PATCH 0163/1240] Remove unused background from PrintSetupSelector CURA-5785 --- resources/qml/SidebarSimple.qml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index affd1eb3a2..5e723a3d70 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -33,11 +33,10 @@ Item style: UM.Theme.styles.scrollview flickableItem.flickableDirection: Flickable.VerticalFlick - Rectangle + Item { width: childrenRect.width height: childrenRect.height - color: UM.Theme.getColor("main_background") // // Quality profile From ca637338270e12c7dd32e20e21dd005eae2066cb Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 12 Nov 2018 13:06:12 +0100 Subject: [PATCH 0164/1240] Updated header of the settingbar CURA-5785 --- resources/qml/IconWithText.qml | 60 ++++++++++++++++++ resources/qml/PrintSetupSelector.qml | 94 ++++++++++++++++++++++++---- 2 files changed, 141 insertions(+), 13 deletions(-) create mode 100644 resources/qml/IconWithText.qml diff --git a/resources/qml/IconWithText.qml b/resources/qml/IconWithText.qml new file mode 100644 index 0000000000..0b0feea821 --- /dev/null +++ b/resources/qml/IconWithText.qml @@ -0,0 +1,60 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 2.0 +import QtQuick.Layouts 1.3 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +// Reusable component that holds an (re-colorable) icon on the left with some text on the right +Item +{ + property alias iconColor: icon.color + property alias source: icon.source + property alias text: label.text + + implicitWidth: icon.width + 100 + implicitHeight: icon.height + + Component.onCompleted: print(label.contentWidth) + + UM.RecolorImage + { + id: icon + width: UM.Theme.getSize("section_icon").width + height: UM.Theme.getSize("section_icon").height + + sourceSize.width: width + sourceSize.height: height + color: "black" + + anchors + { + left: parent.left + verticalCenter: parent.verticalCenter + } + + } + + Label + { + id: label + height: contentHeight + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + renderType: Text.NativeRendering + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + anchors + { + left: icon.right + right: parent.right + top: parent.top + bottom: parent.bottom + rightMargin: 0 + margins: UM.Theme.getSize("narrow_margin").width + } + } +} \ No newline at end of file diff --git a/resources/qml/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector.qml index 7a415b1e2e..2daf8adb4b 100644 --- a/resources/qml/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector.qml @@ -18,12 +18,19 @@ Cura.ExpandableComponent property int currentModeIndex: -1 property bool hideSettings: PrintInformation.preSliced - UM.I18nCatalog { id: catalog; name: "cura"} + property string enabledText: catalog.i18nc("@label", "On") + property string disabledText: catalog.i18nc("@label", "Off") // This widget doesn't show tooltips by itself. Instead it emits signals so others can do something with it. signal showTooltip(Item item, point location, string text) signal hideTooltip() + UM.I18nCatalog + { + id: catalog + name: "cura" + } + Timer { id: tooltipDelayTimer @@ -32,18 +39,82 @@ Cura.ExpandableComponent property var item property string text - onTriggered: + onTriggered: base.showTooltip(base, {x: 0, y: item.y}, text) + } + + onCurrentModeIndexChanged: UM.Preferences.setValue("cura/active_mode", currentModeIndex) + + headerItem: Row + { + anchors.fill: parent + + IconWithText { - base.showTooltip(base, {x: 0, y: item.y}, text); + source: UM.Theme.getIcon("category_layer_height") + text: Cura.MachineManager.activeQualityOrQualityChangesName + " " + layerHeight.properties.value + "mm" + width: parent.width / 4 + height: parent.height + + UM.SettingPropertyProvider + { + id: layerHeight + containerStackId: Cura.MachineManager.activeStackId + key: "layer_height" + watchedProperties: ["value"] + } + } + + IconWithText + { + source: UM.Theme.getIcon("category_infill") + text: parseInt(infillDensity.properties.value) + "%" + width: parent.width / 4 + height: parent.height + + UM.SettingPropertyProvider + { + id: infillDensity + containerStackId: Cura.MachineManager.activeStackId + key: "infill_sparse_density" + watchedProperties: ["value"] + } + } + + IconWithText + { + source: UM.Theme.getIcon("category_support") + text: supportEnabled.properties.value == "True" ? enabledText : disabledText + width: parent.width / 4 + height: parent.height + + UM.SettingPropertyProvider + { + id: supportEnabled + containerStack: Cura.MachineManager.activeMachine + key: "support_enable" + watchedProperties: ["value"] + } + } + + IconWithText + { + source: UM.Theme.getIcon("category_adhesion") + text: platformAdhesionType.properties.value != "skirt" && platformAdhesionType.properties.value != "none" ? enabledText : disabledText + width: parent.width / 4 + height: parent.height + + UM.SettingPropertyProvider + { + id: platformAdhesionType + containerStack: Cura.MachineManager.activeMachine + key: "adhesion_type" + watchedProperties: [ "value"] + } } } - onCurrentModeIndexChanged: - { - UM.Preferences.setValue("cura/active_mode", currentModeIndex); - } - headerItem: Label + /*Label { id: settingsModeLabel text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox", "Print Setup disabled\nG-code files cannot be modified") @@ -51,16 +122,13 @@ Cura.ExpandableComponent anchors { - left: parent.left - top: parent.top - margins: UM.Theme.getSize("thick_margin").width + fill: parent } - width: Math.round(parent.width * 0.45) height: contentHeight font: UM.Theme.getFont("large") color: UM.Theme.getColor("text") - } + }*/ popupItem: Item { From 54685c983acf1e1d5d24425b67d7d5f5e7a2c3ff Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 12 Nov 2018 13:29:21 +0100 Subject: [PATCH 0165/1240] Make PrintSetupSelector use layout instead of fixed width This makes the components autoscale a bit when needed. CURA-5785 --- resources/qml/IconWithText.qml | 22 ++++++++++++++++------ resources/qml/PrintSetupSelector.qml | 15 ++++----------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/resources/qml/IconWithText.qml b/resources/qml/IconWithText.qml index 0b0feea821..d19c7e3899 100644 --- a/resources/qml/IconWithText.qml +++ b/resources/qml/IconWithText.qml @@ -8,18 +8,29 @@ import QtQuick.Layouts 1.3 import UM 1.2 as UM import Cura 1.0 as Cura -// Reusable component that holds an (re-colorable) icon on the left with some text on the right +// Reusable component that holds an (re-colorable) icon on the left with some text on the right. +// This component is also designed to be used with layouts. It will use the width of the text + icon as preferred width +// It sets the icon size + half of the content as it's minium width (in which case it will elide the text) Item { property alias iconColor: icon.color property alias source: icon.source property alias text: label.text + property real margin: UM.Theme.getSize("narrow_margin").width + + // These properties can be used in combination with layouts. + readonly property real contentWidth: icon.width + margin + label.contentWidth + readonly property real minContentWidth: icon.width + margin + 0.5 * label.contentWidth + + Layout.minimumWidth: minContentWidth + Layout.preferredWidth: contentWidth + Layout.fillHeight: true + Layout.fillWidth: true + implicitWidth: icon.width + 100 implicitHeight: icon.height - Component.onCompleted: print(label.contentWidth) - UM.RecolorImage { id: icon @@ -35,18 +46,17 @@ Item left: parent.left verticalCenter: parent.verticalCenter } - } Label { id: label - height: contentHeight font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") renderType: Text.NativeRendering elide: Text.ElideRight verticalAlignment: Text.AlignVCenter + anchors { left: icon.right @@ -54,7 +64,7 @@ Item top: parent.top bottom: parent.bottom rightMargin: 0 - margins: UM.Theme.getSize("narrow_margin").width + margins: margin } } } \ No newline at end of file diff --git a/resources/qml/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector.qml index 2daf8adb4b..4a8ebda2b3 100644 --- a/resources/qml/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector.qml @@ -18,8 +18,8 @@ Cura.ExpandableComponent property int currentModeIndex: -1 property bool hideSettings: PrintInformation.preSliced - property string enabledText: catalog.i18nc("@label", "On") - property string disabledText: catalog.i18nc("@label", "Off") + property string enabledText: catalog.i18nc("@label:Should be short", "On") + property string disabledText: catalog.i18nc("@label:Should be short", "Off") // This widget doesn't show tooltips by itself. Instead it emits signals so others can do something with it. signal showTooltip(Item item, point location, string text) @@ -44,7 +44,7 @@ Cura.ExpandableComponent onCurrentModeIndexChanged: UM.Preferences.setValue("cura/active_mode", currentModeIndex) - headerItem: Row + headerItem: RowLayout { anchors.fill: parent @@ -52,8 +52,6 @@ Cura.ExpandableComponent { source: UM.Theme.getIcon("category_layer_height") text: Cura.MachineManager.activeQualityOrQualityChangesName + " " + layerHeight.properties.value + "mm" - width: parent.width / 4 - height: parent.height UM.SettingPropertyProvider { @@ -68,8 +66,6 @@ Cura.ExpandableComponent { source: UM.Theme.getIcon("category_infill") text: parseInt(infillDensity.properties.value) + "%" - width: parent.width / 4 - height: parent.height UM.SettingPropertyProvider { @@ -84,8 +80,7 @@ Cura.ExpandableComponent { source: UM.Theme.getIcon("category_support") text: supportEnabled.properties.value == "True" ? enabledText : disabledText - width: parent.width / 4 - height: parent.height + UM.SettingPropertyProvider { @@ -100,8 +95,6 @@ Cura.ExpandableComponent { source: UM.Theme.getIcon("category_adhesion") text: platformAdhesionType.properties.value != "skirt" && platformAdhesionType.properties.value != "none" ? enabledText : disabledText - width: parent.width / 4 - height: parent.height UM.SettingPropertyProvider { From 8d49d5a1b10fe1f6e6f1459fb0e602df1ab66bec Mon Sep 17 00:00:00 2001 From: THeijmans Date: Mon, 12 Nov 2018 13:29:51 +0100 Subject: [PATCH 0166/1240] CPE and CPE+ Comb retractions In this commit I changed all CPE and CPE+ profiles to have a 50mm combing max distance without retraction. This should decrease blobs caused by combing long distances. I also reverted the CPE infill back to triangles, as we now connect this infill type as well. --- .../ultimaker_s5/um_s5_aa0.25_CPE_Normal_Quality.inst.cfg | 1 + .../quality/ultimaker_s5/um_s5_aa0.4_CPEP_Draft_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_CPEP_Fast_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.4_CPEP_High_Quality.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.4_CPEP_Normal_Quality.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_CPE_Draft_Print.inst.cfg | 3 ++- .../quality/ultimaker_s5/um_s5_aa0.4_CPE_Fast_Print.inst.cfg | 3 ++- .../quality/ultimaker_s5/um_s5_aa0.4_CPE_High_Quality.inst.cfg | 3 ++- .../ultimaker_s5/um_s5_aa0.4_CPE_Normal_Quality.inst.cfg | 3 ++- .../um_s5_aa0.4_aluminum_CPEP_Draft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Fast_Print.inst.cfg | 2 +- .../um_s5_aa0.4_aluminum_CPEP_High_Quality.inst.cfg | 2 +- .../um_s5_aa0.4_aluminum_CPEP_Normal_Quality.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Draft_Print.inst.cfg | 3 ++- .../ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Fast_Print.inst.cfg | 3 ++- .../um_s5_aa0.4_aluminum_CPE_High_Quality.inst.cfg | 3 ++- .../um_s5_aa0.4_aluminum_CPE_Normal_Quality.inst.cfg | 3 ++- .../quality/ultimaker_s5/um_s5_aa0.8_CPEP_Fast_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.8_CPEP_Superdraft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.8_CPEP_Verydraft_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.8_CPE_Draft_Print.inst.cfg | 1 + .../ultimaker_s5/um_s5_aa0.8_CPE_Superdraft_Print.inst.cfg | 1 + .../ultimaker_s5/um_s5_aa0.8_CPE_Verydraft_Print.inst.cfg | 1 + .../ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Fast_Print.inst.cfg | 2 +- .../um_s5_aa0.8_aluminum_CPEP_Superdraft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_aluminum_CPEP_Verydraft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Draft_Print.inst.cfg | 1 + .../um_s5_aa0.8_aluminum_CPE_Superdraft_Print.inst.cfg | 1 + .../um_s5_aa0.8_aluminum_CPE_Verydraft_Print.inst.cfg | 1 + 29 files changed, 37 insertions(+), 22 deletions(-) diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.25_CPE_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.25_CPE_Normal_Quality.inst.cfg index 2068ed51c0..c15311d104 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.25_CPE_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.25_CPE_Normal_Quality.inst.cfg @@ -12,6 +12,7 @@ material = generic_cpe variant = AA 0.25 [values] +retraction_combing_max_distance = 50 retraction_extrusion_window = 0.5 speed_infill = =math.ceil(speed_print * 40 / 55) speed_topbottom = =math.ceil(speed_print * 30 / 55) diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Draft_Print.inst.cfg index 421fcdf095..627302e0ab 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Draft_Print.inst.cfg @@ -29,7 +29,7 @@ material_print_temperature_layer_0 = =material_print_temperature multiple_mesh_overlap = 0 prime_tower_enable = True prime_tower_wipe_enabled = True -retraction_combing = off +retraction_combing_max_distance = 50 retraction_extrusion_window = 1 retraction_hop = 0.2 retraction_hop_enabled = False diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Fast_Print.inst.cfg index 536c6c97b8..cda8d85211 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Fast_Print.inst.cfg @@ -29,7 +29,7 @@ material_print_temperature_layer_0 = =material_print_temperature multiple_mesh_overlap = 0 prime_tower_enable = True prime_tower_wipe_enabled = True -retraction_combing = off +retraction_combing_max_distance = 50 retraction_extrusion_window = 1 retraction_hop = 0.2 retraction_hop_enabled = False diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_High_Quality.inst.cfg index 77182c21e1..3ce76bf6be 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_High_Quality.inst.cfg @@ -31,7 +31,7 @@ material_print_temperature_layer_0 = =material_print_temperature multiple_mesh_overlap = 0 prime_tower_enable = True prime_tower_wipe_enabled = True -retraction_combing = off +retraction_combing_max_distance = 50 retraction_extrusion_window = 1 retraction_hop = 0.2 retraction_hop_enabled = False diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Normal_Quality.inst.cfg index d779baf315..d402b663c6 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Normal_Quality.inst.cfg @@ -31,7 +31,7 @@ material_print_temperature_layer_0 = =material_print_temperature multiple_mesh_overlap = 0 prime_tower_enable = True prime_tower_wipe_enabled = True -retraction_combing = off +retraction_combing_max_distance = 50 retraction_extrusion_window = 1 retraction_hop = 0.2 retraction_hop_enabled = False diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Draft_Print.inst.cfg index c51e5652e1..505cd952d2 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Draft_Print.inst.cfg @@ -15,6 +15,7 @@ variant = AA 0.4 material_print_temperature = =default_material_print_temperature + 10 material_initial_print_temperature = =material_print_temperature - 5 material_final_print_temperature = =material_print_temperature - 10 +retraction_combing_max_distance = 50 skin_overlap = 20 speed_print = 60 speed_layer_0 = =math.ceil(speed_print * 20 / 60) @@ -24,5 +25,5 @@ speed_wall_0 = =math.ceil(speed_wall * 35 / 45) wall_thickness = 1 -infill_pattern = zigzag +infill_pattern = triangles speed_infill = =math.ceil(speed_print * 50 / 60) diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Fast_Print.inst.cfg index b80d3ccf22..cc5df0abb9 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Fast_Print.inst.cfg @@ -16,11 +16,12 @@ cool_min_speed = 7 material_print_temperature = =default_material_print_temperature + 5 material_initial_print_temperature = =material_print_temperature - 5 material_final_print_temperature = =material_print_temperature - 10 +retraction_combing_max_distance = 50 speed_print = 60 speed_layer_0 = =math.ceil(speed_print * 20 / 60) speed_topbottom = =math.ceil(speed_print * 30 / 60) speed_wall = =math.ceil(speed_print * 40 / 60) speed_wall_0 = =math.ceil(speed_wall * 30 / 40) -infill_pattern = zigzag +infill_pattern = triangles speed_infill = =math.ceil(speed_print * 50 / 60) \ No newline at end of file diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_High_Quality.inst.cfg index c90eedaec3..c81dc0f5a7 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_High_Quality.inst.cfg @@ -18,10 +18,11 @@ machine_nozzle_heat_up_speed = 1.5 material_print_temperature = =default_material_print_temperature - 5 material_initial_print_temperature = =material_print_temperature - 5 material_final_print_temperature = =material_print_temperature - 10 +retraction_combing_max_distance = 50 speed_print = 50 speed_layer_0 = =math.ceil(speed_print * 20 / 50) speed_topbottom = =math.ceil(speed_print * 30 / 50) speed_wall = =math.ceil(speed_print * 30 / 50) -infill_pattern = zigzag +infill_pattern = triangles speed_infill = =math.ceil(speed_print * 40 / 50) \ No newline at end of file diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Normal_Quality.inst.cfg index e098b0ffb4..7d29f8fb7c 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Normal_Quality.inst.cfg @@ -16,10 +16,11 @@ machine_nozzle_cool_down_speed = 0.85 machine_nozzle_heat_up_speed = 1.5 material_initial_print_temperature = =material_print_temperature - 5 material_final_print_temperature = =material_print_temperature - 10 +retraction_combing_max_distance = 50 speed_print = 55 speed_layer_0 = =math.ceil(speed_print * 20 / 55) speed_topbottom = =math.ceil(speed_print * 30 / 55) speed_wall = =math.ceil(speed_print * 30 / 55) -infill_pattern = zigzag +infill_pattern = triangles speed_infill = =math.ceil(speed_print * 45 / 55) \ No newline at end of file diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Draft_Print.inst.cfg index 46e3483a6a..4a55f5d24c 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Draft_Print.inst.cfg @@ -30,7 +30,7 @@ material_print_temperature_layer_0 = =material_print_temperature multiple_mesh_overlap = 0 prime_tower_enable = True prime_tower_wipe_enabled = True -retraction_combing = off +retraction_combing_max_distance = 50 retraction_extrusion_window = 1 retraction_hop = 0.2 retraction_hop_enabled = False diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Fast_Print.inst.cfg index 5c235b656a..730e058212 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Fast_Print.inst.cfg @@ -30,7 +30,7 @@ material_print_temperature_layer_0 = =material_print_temperature multiple_mesh_overlap = 0 prime_tower_enable = True prime_tower_wipe_enabled = True -retraction_combing = off +retraction_combing_max_distance = 50 retraction_extrusion_window = 1 retraction_hop = 0.2 retraction_hop_enabled = False diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_High_Quality.inst.cfg index 326a730fe4..e6921e63d8 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_High_Quality.inst.cfg @@ -32,7 +32,7 @@ material_print_temperature_layer_0 = =material_print_temperature multiple_mesh_overlap = 0 prime_tower_enable = True prime_tower_wipe_enabled = True -retraction_combing = off +retraction_combing_max_distance = 50 retraction_extrusion_window = 1 retraction_hop = 0.2 retraction_hop_enabled = False diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Normal_Quality.inst.cfg index d40b2db90e..a4eec45e38 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Normal_Quality.inst.cfg @@ -32,7 +32,7 @@ material_print_temperature_layer_0 = =material_print_temperature multiple_mesh_overlap = 0 prime_tower_enable = True prime_tower_wipe_enabled = True -retraction_combing = off +retraction_combing_max_distance = 50 retraction_extrusion_window = 1 retraction_hop = 0.2 retraction_hop_enabled = False diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Draft_Print.inst.cfg index c812066e0c..4f085f10a6 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Draft_Print.inst.cfg @@ -16,6 +16,7 @@ buildplate = Aluminum material_print_temperature = =default_material_print_temperature + 10 material_initial_print_temperature = =material_print_temperature - 5 material_final_print_temperature = =material_print_temperature - 10 +retraction_combing_max_distance = 50 skin_overlap = 20 speed_print = 60 speed_layer_0 = 20 @@ -25,7 +26,7 @@ speed_wall_0 = =math.ceil(speed_wall * 35 / 45) wall_thickness = 1 -infill_pattern = zigzag +infill_pattern = triangles speed_infill = =math.ceil(speed_print * 50 / 60) prime_tower_purge_volume = 1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Fast_Print.inst.cfg index ef634316da..2580bf952d 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Fast_Print.inst.cfg @@ -17,13 +17,14 @@ cool_min_speed = 7 material_print_temperature = =default_material_print_temperature + 5 material_initial_print_temperature = =material_print_temperature - 5 material_final_print_temperature = =material_print_temperature - 10 +retraction_combing_max_distance = 50 speed_print = 60 speed_layer_0 = 20 speed_topbottom = =math.ceil(speed_print * 30 / 60) speed_wall = =math.ceil(speed_print * 40 / 60) speed_wall_0 = =math.ceil(speed_wall * 30 / 40) -infill_pattern = zigzag +infill_pattern = triangles speed_infill = =math.ceil(speed_print * 50 / 60) prime_tower_purge_volume = 1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_High_Quality.inst.cfg index cda97e6ab3..d6f07c37a5 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_High_Quality.inst.cfg @@ -19,12 +19,13 @@ machine_nozzle_heat_up_speed = 1.5 material_print_temperature = =default_material_print_temperature - 5 material_initial_print_temperature = =material_print_temperature - 5 material_final_print_temperature = =material_print_temperature - 10 +retraction_combing_max_distance = 50 speed_print = 50 speed_layer_0 = 20 speed_topbottom = =math.ceil(speed_print * 30 / 50) speed_wall = =math.ceil(speed_print * 30 / 50) -infill_pattern = zigzag +infill_pattern = triangles speed_infill = =math.ceil(speed_print * 40 / 50) prime_tower_purge_volume = 1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Normal_Quality.inst.cfg index 5a75f3b6e3..6032ff3845 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Normal_Quality.inst.cfg @@ -17,12 +17,13 @@ machine_nozzle_cool_down_speed = 0.85 machine_nozzle_heat_up_speed = 1.5 material_initial_print_temperature = =material_print_temperature - 5 material_final_print_temperature = =material_print_temperature - 10 +retraction_combing_max_distance = 50 speed_print = 55 speed_layer_0 = 20 speed_topbottom = =math.ceil(speed_print * 30 / 55) speed_wall = =math.ceil(speed_print * 30 / 55) -infill_pattern = zigzag +infill_pattern = triangles speed_infill = =math.ceil(speed_print * 45 / 55) prime_tower_purge_volume = 1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Fast_Print.inst.cfg index e78006689b..b855eb2c8b 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Fast_Print.inst.cfg @@ -22,7 +22,7 @@ material_print_temperature = =default_material_print_temperature - 10 material_print_temperature_layer_0 = =material_print_temperature material_standby_temperature = 100 prime_tower_enable = True -retraction_combing = off +retraction_combing_max_distance = 50 retraction_hop = 0.1 retraction_hop_enabled = False skin_overlap = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Superdraft_Print.inst.cfg index c6d0962157..d74711e8cf 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Superdraft_Print.inst.cfg @@ -22,7 +22,7 @@ material_print_temperature = =default_material_print_temperature - 5 material_print_temperature_layer_0 = =material_print_temperature material_standby_temperature = 100 prime_tower_enable = True -retraction_combing = off +retraction_combing_max_distance = 50 retraction_hop = 0.1 retraction_hop_enabled = False skin_overlap = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Verydraft_Print.inst.cfg index b80f773594..b65a7ff93c 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Verydraft_Print.inst.cfg @@ -22,7 +22,7 @@ material_print_temperature = =default_material_print_temperature - 7 material_print_temperature_layer_0 = =material_print_temperature material_standby_temperature = 100 prime_tower_enable = True -retraction_combing = off +retraction_combing_max_distance = 50 retraction_hop = 0.1 retraction_hop_enabled = False skin_overlap = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Draft_Print.inst.cfg index 532aacabf7..7cb69ba3eb 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Draft_Print.inst.cfg @@ -17,6 +17,7 @@ line_width = =machine_nozzle_size * 0.875 material_print_temperature = =default_material_print_temperature + 15 material_standby_temperature = 100 prime_tower_enable = True +retraction_combing_max_distance = 50 speed_print = 40 speed_topbottom = =math.ceil(speed_print * 25 / 40) speed_wall = =math.ceil(speed_print * 30 / 40) diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Superdraft_Print.inst.cfg index 55b9ae8315..6c323fe602 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Superdraft_Print.inst.cfg @@ -17,6 +17,7 @@ line_width = =machine_nozzle_size * 0.875 material_print_temperature = =default_material_print_temperature + 20 material_standby_temperature = 100 prime_tower_enable = True +retraction_combing_max_distance = 50 speed_print = 45 speed_topbottom = =math.ceil(speed_print * 30 / 45) speed_wall = =math.ceil(speed_print * 40 / 45) diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Verydraft_Print.inst.cfg index 01761062a4..a0380ecc0e 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Verydraft_Print.inst.cfg @@ -17,6 +17,7 @@ line_width = =machine_nozzle_size * 0.875 material_print_temperature = =default_material_print_temperature + 17 material_standby_temperature = 100 prime_tower_enable = True +retraction_combing_max_distance = 50 speed_print = 40 speed_topbottom = =math.ceil(speed_print * 25 / 40) speed_wall = =math.ceil(speed_print * 30 / 40) diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Fast_Print.inst.cfg index 3e74390840..c01f9b1ff1 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Fast_Print.inst.cfg @@ -23,7 +23,7 @@ material_print_temperature = =default_material_print_temperature - 10 material_print_temperature_layer_0 = =material_print_temperature material_standby_temperature = 100 prime_tower_enable = True -retraction_combing = off +retraction_combing_max_distance = 50 retraction_hop = 0.1 retraction_hop_enabled = False skin_overlap = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Superdraft_Print.inst.cfg index 4c1b807430..b9c9ef6611 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Superdraft_Print.inst.cfg @@ -23,7 +23,7 @@ material_print_temperature = =default_material_print_temperature - 5 material_print_temperature_layer_0 = =material_print_temperature material_standby_temperature = 100 prime_tower_enable = True -retraction_combing = off +retraction_combing_max_distance = 50 retraction_hop = 0.1 retraction_hop_enabled = False skin_overlap = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Verydraft_Print.inst.cfg index 11aefc90cd..be2cc62b08 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Verydraft_Print.inst.cfg @@ -23,7 +23,7 @@ material_print_temperature = =default_material_print_temperature - 7 material_print_temperature_layer_0 = =material_print_temperature material_standby_temperature = 100 prime_tower_enable = True -retraction_combing = off +retraction_combing_max_distance = 50 retraction_hop = 0.1 retraction_hop_enabled = False skin_overlap = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Draft_Print.inst.cfg index 80c0585061..0d0ed8f8b2 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Draft_Print.inst.cfg @@ -18,6 +18,7 @@ line_width = =machine_nozzle_size * 0.875 material_print_temperature = =default_material_print_temperature + 15 material_standby_temperature = 100 prime_tower_enable = True +retraction_combing_max_distance = 50 speed_print = 40 speed_topbottom = =math.ceil(speed_print * 25 / 40) speed_wall = =math.ceil(speed_print * 30 / 40) diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Superdraft_Print.inst.cfg index 5dcc454173..a163e0c735 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Superdraft_Print.inst.cfg @@ -18,6 +18,7 @@ line_width = =machine_nozzle_size * 0.875 material_print_temperature = =default_material_print_temperature + 20 material_standby_temperature = 100 prime_tower_enable = True +retraction_combing_max_distance = 50 speed_print = 45 speed_topbottom = =math.ceil(speed_print * 30 / 45) speed_wall = =math.ceil(speed_print * 40 / 45) diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Verydraft_Print.inst.cfg index 8423e109e8..2137cf740b 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Verydraft_Print.inst.cfg @@ -18,6 +18,7 @@ line_width = =machine_nozzle_size * 0.875 material_print_temperature = =default_material_print_temperature + 17 material_standby_temperature = 100 prime_tower_enable = True +retraction_combing_max_distance = 50 speed_print = 40 speed_topbottom = =math.ceil(speed_print * 25 / 40) speed_wall = =math.ceil(speed_print * 30 / 40) From 4be33031d7553c06ed7bf5adab7d2f9c31008dae Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 12 Nov 2018 14:07:45 +0100 Subject: [PATCH 0167/1240] Added icons to ExpandableComponent This also makes the entire header react to mouse events instead of just the icon/button CURA-5785 --- resources/qml/ExpandableComponent.qml | 31 ++++++++++++++++--- .../QuickConfigurationSelector.qml | 2 ++ resources/qml/PrintSetupSelector.qml | 8 +++-- 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 26bdf3b3ce..af579340a5 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -6,7 +6,7 @@ import UM 1.2 as UM // The expandable component has 3 major sub components: // * The headerItem; Always visible and should hold some info about what happens if the component is expanded // * The popupItem; The content that needs to be shown if the component is expanded. -// * The Button; The actual button that expands the popup. +// * The Icon; An icon that is displayed on the right of the drawer. Item { // The headerItem holds the QML item that is always displayed. @@ -26,6 +26,18 @@ Item // How much padding is needed around the header & button property alias headerPadding: background.padding + // What icon should be displayed on the right. + property alias iconSource: collapseButton.source + + // What is the color of the icon? + property alias iconColor: collapseButton.color + + // The icon size (it's always drawn as a square) + property alias iconSize: collapseButton.width + + // Is the "drawer" open? + readonly property alias expanded: popup.visible + onPopupItemChanged: { // Since we want the size of the popup to be set by the size of the content, @@ -57,17 +69,26 @@ Item } } - Button + UM.RecolorImage { id: collapseButton anchors { right: parent.right - top: parent.top - bottom: parent.bottom + verticalCenter: parent.verticalCenter margins: background.padding } - text: popup.visible ? "close" : "open" + sourceSize.width: width + sourceSize.height: height + visible: source != "" + width: UM.Theme.getSize("section_icon").width + height: width + color: "black" + } + + MouseArea + { + anchors.fill: parent onClicked: popup.visible ? popup.close() : popup.open() } } diff --git a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml index 3c354384c7..75787ea033 100644 --- a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml +++ b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml @@ -27,6 +27,8 @@ Cura.ExpandableComponent name: "cura" } + iconSource: expanded ? UM.Theme.getIcon("arrow_left") : UM.Theme.getIcon("arrow_bottom") + headerItem: Item { // Horizontal list that shows the extruders diff --git a/resources/qml/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector.qml index 4a8ebda2b3..1281961ef4 100644 --- a/resources/qml/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector.qml @@ -14,7 +14,6 @@ Cura.ExpandableComponent { id: base - height: childrenRect.height property int currentModeIndex: -1 property bool hideSettings: PrintInformation.preSliced @@ -25,6 +24,11 @@ Cura.ExpandableComponent signal showTooltip(Item item, point location, string text) signal hideTooltip() + height: childrenRect.height + iconSource: UM.Theme.getIcon("pencil") + + onCurrentModeIndexChanged: UM.Preferences.setValue("cura/active_mode", currentModeIndex) + UM.I18nCatalog { id: catalog @@ -42,8 +46,6 @@ Cura.ExpandableComponent onTriggered: base.showTooltip(base, {x: 0, y: item.y}, text) } - onCurrentModeIndexChanged: UM.Preferences.setValue("cura/active_mode", currentModeIndex) - headerItem: RowLayout { anchors.fill: parent From 783fe9ab73db3c24abe5dbfcfd6e34ce4c09f911 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 12 Nov 2018 15:23:58 +0100 Subject: [PATCH 0168/1240] Updated the look of the extruder buttons CURA-5785 --- resources/qml/ExtruderButton.qml | 60 ++++---------------------------- resources/qml/Toolbar.qml | 7 +++- 2 files changed, 13 insertions(+), 54 deletions(-) diff --git a/resources/qml/ExtruderButton.qml b/resources/qml/ExtruderButton.qml index 7923521658..764e9c94cb 100644 --- a/resources/qml/ExtruderButton.qml +++ b/resources/qml/ExtruderButton.qml @@ -2,7 +2,7 @@ // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.2 -import QtQuick.Controls 1.1 +import QtQuick.Controls 2.0 import UM 1.2 as UM import Cura 1.0 as Cura @@ -11,65 +11,19 @@ Button { id: base - property var extruder; + property var extruder text: catalog.i18ncp("@label %1 is filled in with the name of an extruder", "Print Selected Model with %1", "Print Selected Models with %1", UM.Selection.selectionCount).arg(extruder.name) - style: UM.Theme.styles.toolbar_button - iconSource: UM.Theme.getIcon("extruder_button") - checked: Cura.ExtruderManager.selectedObjectExtruders.indexOf(extruder.id) != -1 enabled: UM.Selection.hasSelection && extruder.stack.isEnabled - Item + background: Item {} + contentItem: ExtruderIcon { - anchors.centerIn: parent - width: UM.Theme.getSize("default_margin").width - height: UM.Theme.getSize("default_margin").height - opacity: !base.enabled ? 0.2 : 1.0 - - Label - { - anchors.centerIn: parent - text: index + 1 - color: - { - if (base.checked) - { - return UM.Theme.getColor("toolbar_button_text_active") - } - else if(base.hovered) - { - return UM.Theme.getColor("toolbar_button_text_hover") - } - return UM.Theme.getColor("toolbar_button_text") - } - font: UM.Theme.getFont("default_bold") - } - } - - // Material colour circle - // Only draw the filling colour of the material inside the SVG border. - Rectangle - { - anchors - { - right: parent.right - top: parent.top - rightMargin: UM.Theme.getSize("extruder_button_material_margin").width - topMargin: UM.Theme.getSize("extruder_button_material_margin").height - } - - color: model.color - - width: UM.Theme.getSize("extruder_button_material").width - height: UM.Theme.getSize("extruder_button_material").height - radius: Math.round(width / 2) - - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("extruder_button_material_border") - - opacity: !base.enabled ? 0.2 : 1.0 + width: UM.Theme.getSize("button_icon").width + materialColor: model.color + property int index: extruder.index } onClicked: diff --git a/resources/qml/Toolbar.qml b/resources/qml/Toolbar.qml index 9955ceeba7..2239125fbe 100644 --- a/resources/qml/Toolbar.qml +++ b/resources/qml/Toolbar.qml @@ -116,7 +116,12 @@ Item height: childrenRect.height property var _model: Cura.ExtrudersModel { id: extrudersModel } model: _model.items.length > 1 ? _model : 0 - ExtruderButton { extruder: model } + ExtruderButton + { + extruder: model + height: UM.Theme.getSize("button").width + width: UM.Theme.getSize("button").width + } } } } From 4c1a45f904faf1b7e37bef6e18c476b113a6df58 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 12 Nov 2018 15:53:09 +0100 Subject: [PATCH 0169/1240] Added sizes to the stage menu This isn't themed, but right now it does adhere to the sizes I got from UX CURA-5785 --- plugins/PrepareStage/PrepareMenu.qml | 73 +++++++++++++++------------- resources/qml/MachineSelector.qml | 2 +- 2 files changed, 41 insertions(+), 34 deletions(-) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index ff8cdc2a47..9fd8747e28 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -1,5 +1,5 @@ import QtQuick 2.7 - +import QtQuick.Layouts 1.1 import QtQuick.Controls 1.4 import UM 1.3 as UM @@ -19,9 +19,12 @@ Item name: "cura" } - Row + // Item to ensure that all of the buttons are nicely centered. + Item { anchors.horizontalCenter: parent.horizontalCenter + width: openFileButton.width + UM.Theme.getSize("default_margin").width + itemRow.width + height: parent.height Button { @@ -33,42 +36,46 @@ Item action: Cura.Actions.open } - Item + RowLayout { - id: spacing - width: UM.Theme.getSize("default_margin").width - height: prepareMenu.height - } + id: itemRow - Cura.MachineSelector - { - id: machineSelection - width: UM.Theme.getSize("machine_selector_widget").width - configSelection.width - height: prepareMenu.height - z: openFileButton.z - 1 - } + anchors.left: openFileButton.right + anchors.leftMargin: UM.Theme.getSize("default_margin").width - Cura.QuickConfigurationSelector - { - height: prepareMenu.height - width: UM.Theme.getSize("configuration_selector_widget").width - /*id: configSelection - width: visible ? UM.Theme.getSize("machine_selector_widget").width * 0.2 : 0 - panelWidth: UM.Theme.getSize("machine_selector_widget").width - height: prepareMenu.height*/ - } + width: 0.9 * prepareMenu.width + height: parent.height - /*Cura.CustomConfigurationSelector - { - width: UM.Theme.getSize("configuration_selector_widget").width - }*/ + Cura.MachineSelector + { + id: machineSelection + z: openFileButton.z - 1 - Cura.PrintSetupSelector - { - width: UM.Theme.getSize("print_setup_widget").width - height: prepareMenu.height - onShowTooltip: prepareMenu.showTooltip(item, location, text) - onHideTooltip: prepareMenu.hideTooltip() + Layout.minimumWidth: 240 + Layout.maximumWidth: 240 + Layout.fillWidth: true + Layout.fillHeight: true + } + + Cura.QuickConfigurationSelector + { + Layout.fillHeight: true + Layout.fillWidth: true + Layout.preferredWidth: itemRow.width - machineSelection.width - printSetupSelector.width + } + + Cura.PrintSetupSelector + { + id: printSetupSelector + + onShowTooltip: prepareMenu.showTooltip(item, location, text) + onHideTooltip: prepareMenu.hideTooltip() + + Layout.minimumWidth: 460 + Layout.maximumWidth: 460 + Layout.fillWidth: true + Layout.fillHeight: true + } } } } \ No newline at end of file diff --git a/resources/qml/MachineSelector.qml b/resources/qml/MachineSelector.qml index d82211af3a..b4ca9a4899 100644 --- a/resources/qml/MachineSelector.qml +++ b/resources/qml/MachineSelector.qml @@ -19,7 +19,7 @@ ToolButton text: isNetworkPrinter ? Cura.MachineManager.activeMachineNetworkGroupName : Cura.MachineManager.activeMachineName tooltip: Cura.MachineManager.activeMachineName - + width: 240 style: ButtonStyle { background: Rectangle From fe4ed464962d76abd6a8058f1f69987f8bad205d Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 12 Nov 2018 16:55:42 +0100 Subject: [PATCH 0170/1240] Show material name instead of just color CURA-5785 --- .../Menus/ConfigurationMenu/QuickConfigurationSelector.qml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml index 75787ea033..7c70229b5d 100644 --- a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml +++ b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml @@ -54,7 +54,7 @@ Cura.ExpandableComponent width: height } - // Label for the human readable material color name + // Label for the brand of the material Label { id: brandNameLabel @@ -71,10 +71,10 @@ Cura.ExpandableComponent } } - // Label that shows the brand of the material + // Label that shows the name of the material Label { - text: model.color_name + text: model.material elide: Text.ElideRight anchors From c18b8241f55f425d5c6a91c00bb8d130560bbdbb Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 13 Nov 2018 14:00:00 +0100 Subject: [PATCH 0171/1240] Change MachineSelection so that it also uses the ExpandableComponent CURA-5785 --- resources/qml/Cura.qml | 1 - resources/qml/MachineSelector.qml | 176 ++++++++++++++++++------------ 2 files changed, 108 insertions(+), 69 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 819d986ece..cdbf1f3511 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -249,7 +249,6 @@ UM.MainWindow source: UM.Controller.activeStage != null ? UM.Controller.activeStage.mainComponent : "" } - Loader { // The stage menu is, as the name implies, a menu that is defined by the active stage. diff --git a/resources/qml/MachineSelector.qml b/resources/qml/MachineSelector.qml index b4ca9a4899..206229e837 100644 --- a/resources/qml/MachineSelector.qml +++ b/resources/qml/MachineSelector.qml @@ -2,7 +2,7 @@ // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.2 -import QtQuick.Controls 1.1 +import QtQuick.Controls 2.4 import QtQuick.Controls.Styles 1.1 import QtQuick.Layouts 1.1 @@ -10,77 +10,117 @@ import UM 1.2 as UM import Cura 1.0 as Cura import "Menus" -ToolButton + +Cura.ExpandableComponent { - id: base + id: machineSelector + property bool isNetworkPrinter: Cura.MachineManager.activeMachineNetworkKey != "" - property bool printerConnected: Cura.MachineManager.printerConnected - property var printerStatus: Cura.MachineManager.printerConnected ? "connected" : "disconnected" - text: isNetworkPrinter ? Cura.MachineManager.activeMachineNetworkGroupName : Cura.MachineManager.activeMachineName - tooltip: Cura.MachineManager.activeMachineName - width: 240 - style: ButtonStyle + iconSource: expanded ? UM.Theme.getIcon("arrow_left") : UM.Theme.getIcon("arrow_bottom") + + UM.I18nCatalog { - background: Rectangle - { - color: - { - if (control.pressed) { - return UM.Theme.getColor("machine_selector_active"); - } - else if (control.hovered) { - return UM.Theme.getColor("machine_selector_hover"); - } - else { - return UM.Theme.getColor("machine_selector_bar"); - } - } - Behavior on color { ColorAnimation { duration: 50; } } - - UM.RecolorImage - { - id: downArrow - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("default_margin").width - width: UM.Theme.getSize("standard_arrow").width - height: UM.Theme.getSize("standard_arrow").height - sourceSize.width: width - sourceSize.height: width - color: UM.Theme.getColor("text_emphasis") - source: UM.Theme.getIcon("arrow_bottom") - } - - PrinterStatusIcon - { - id: printerStatusIcon - visible: printerConnected || isNetworkPrinter - status: printerStatus - anchors - { - verticalCenter: parent.verticalCenter - left: parent.left - leftMargin: UM.Theme.getSize("thick_margin").width - } - } - - Label - { - id: sidebarComboBoxLabel - color: UM.Theme.getColor("machine_selector_text_active") - text: control.text; - elide: Text.ElideRight; - anchors.left: printerStatusIcon.visible ? printerStatusIcon.right : parent.left; - anchors.leftMargin: printerStatusIcon.visible ? UM.Theme.getSize("narrow_margin").width : UM.Theme.getSize("thick_margin").width - anchors.right: downArrow.left; - anchors.rightMargin: control.rightMargin; - anchors.verticalCenter: parent.verticalCenter; - font: UM.Theme.getFont("medium_bold") - } - } - label: Label {} + id: catalog + name: "cura" } - menu: PrinterMenu { } + headerItem: Item + { + Label + { + text: isNetworkPrinter ? Cura.MachineManager.activeMachineNetworkGroupName : Cura.MachineManager.activeMachineName + verticalAlignment: Text.AlignVCenter + height: parent.height + } + } + + popupItem: Item + { + id: popup + width: 240 + height: 200 + + ScrollView + { + anchors.fill: parent + contentHeight: column.implicitHeight + contentWidth: row.implicitWidth + clip: true + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + + Column + { + id: column + anchors.fill: parent + Label + { + text: catalog.i18nc("@label", "Networked Printers") + visible: networkedPrintersModel.items.length > 0 + height: visible ? contentHeight + 2 * UM.Theme.getSize("default_margin").height : 0 + font: UM.Theme.getFont("medium_bold") + verticalAlignment: Text.AlignVCenter + } + + Repeater + { + id: networkedPrinters + + model: UM.ContainerStacksModel + { + id: networkedPrintersModel + filter: {"type": "machine", "um_network_key": "*", "hidden": "False"} + } + + delegate: RoundButton + { + text: name + width: parent.width + + checkable: true + onClicked: Cura.MachineManager.setActiveMachine(model.id) + radius: UM.Theme.getSize("default_radius").width + + Connections + { + target: Cura.MachineManager + onActiveMachineNetworkGroupNameChanged: checked = Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["connect_group_name"] + } + } + + } + + Label + { + text: catalog.i18nc("@label", "Virtual Printers") + visible: virtualPrintersModel.items.length > 0 + height: visible ? contentHeight + 2 * UM.Theme.getSize("default_margin").height : 0 + font: UM.Theme.getFont("medium_bold") + verticalAlignment: Text.AlignVCenter + } + + Repeater + { + id: virtualPrinters + + model: UM.ContainerStacksModel + { + id: virtualPrintersModel + filter: {"type": "machine", "um_network_key": null} + } + + delegate: RoundButton + { + text: name + width: parent.width + checked: Cura.MachineManager.activeMachineId == model.id + checkable: true + onClicked: Cura.MachineManager.setActiveMachine(model.id) + radius: UM.Theme.getSize("default_radius").width + } + + } + } + } + } } From deb5fd1926763af05bf107fe7599cff1f085ec23 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 13 Nov 2018 15:35:05 +0100 Subject: [PATCH 0172/1240] Fix PrintSetupSelector for previewStage CURA-5785 --- plugins/PreviewStage/PreviewMenu.qml | 3 ++- resources/qml/PrintSetupSelector.qml | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index 66f7d527d9..db96525365 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -25,7 +25,7 @@ Item { anchors.horizontalCenter: parent.horizontalCenter spacing: UM.Theme.getSize("default_margin").width - + height: parent.height ComboBox { // This item contains the views selector, a combobox that is dynamically created from @@ -87,6 +87,7 @@ Item Cura.PrintSetupSelector { width: UM.Theme.getSize("print_setup_widget").width + height: parent.height onShowTooltip: previewMenu.showTooltip(item, location, text) onHideTooltip: previewMenu.hideTooltip() } diff --git a/resources/qml/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector.qml index 1281961ef4..65bcee8507 100644 --- a/resources/qml/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector.qml @@ -24,6 +24,7 @@ Cura.ExpandableComponent signal showTooltip(Item item, point location, string text) signal hideTooltip() + implicitWidth: 200 height: childrenRect.height iconSource: UM.Theme.getIcon("pencil") From 785c2661a2ad382d77285cbb124bfee1e5f1985e Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 13 Nov 2018 15:57:00 +0100 Subject: [PATCH 0173/1240] Make SimulationViewMenuComponent also use the ExpandableComponent CURA-5785 --- plugins/PreviewStage/PreviewMenu.qml | 2 +- .../SimulationViewMenuComponent.qml | 74 +++---------------- resources/qml/ExpandableComponent.qml | 8 ++ 3 files changed, 20 insertions(+), 64 deletions(-) diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index db96525365..aeef2bf300 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -78,7 +78,7 @@ Item property var buttonTarget: Qt.point(viewModeButton.x + Math.round(viewModeButton.width / 2), viewModeButton.y + Math.round(viewModeButton.height / 2)) - height: childrenRect.height + height: parent.height width: childrenRect.width source: UM.Controller.activeView != null && UM.Controller.activeView.stageMenuComponent != null ? UM.Controller.activeView.stageMenuComponent : "" diff --git a/plugins/SimulationView/SimulationViewMenuComponent.qml b/plugins/SimulationView/SimulationViewMenuComponent.qml index d251244c9f..fe9f4471fd 100644 --- a/plugins/SimulationView/SimulationViewMenuComponent.qml +++ b/plugins/SimulationView/SimulationViewMenuComponent.qml @@ -10,19 +10,13 @@ import QtGraphicalEffects 1.0 import UM 1.0 as UM import Cura 1.0 as Cura -Rectangle + +Cura.ExpandableComponent { id: base - color: UM.Theme.getColor("tool_panel_background") - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("lining") - width: UM.Theme.getSize("layerview_menu_size").width - - height: viewSettings.collapsed ? layerViewTypesLabel.height + 2 * UM.Theme.getSize("default_margin").height : childrenRect.height + 2 * UM.Theme.getSize("default_margin").height - - Behavior on height { NumberAnimation { duration: 100 } } + iconSource: UM.Theme.getIcon("pencil") property var buttonTarget: { @@ -51,59 +45,21 @@ Rectangle } } - Label + headerItem: Label { id: layerViewTypesLabel - text: catalog.i18nc("@label","Color scheme") - font: UM.Theme.getFont("default"); + text: catalog.i18nc("@label", "Color scheme") + font: UM.Theme.getFont("default") visible: !UM.SimulationView.compatibilityMode color: UM.Theme.getColor("setting_control_text") - height: contentHeight - anchors - { - top: parent.top - margins: UM.Theme.getSize("default_margin").height - right: collapseButton.left - left: parent.left - } + height: base.height + verticalAlignment: Text.AlignVCenter } - Button - { - id: collapseButton - - anchors - { - top: parent.top - margins: UM.Theme.getSize("default_margin").width - right: parent.right - } - - width: UM.Theme.getSize("standard_arrow").width - height: UM.Theme.getSize("standard_arrow").height - - onClicked: viewSettings.collapsed = !viewSettings.collapsed - - style: ButtonStyle - { - background: UM.RecolorImage - { - width: control.width - height: control.height - sourceSize.width: width - sourceSize.height: width - color: UM.Theme.getColor("setting_control_text") - source: viewSettings.collapsed ? UM.Theme.getIcon("arrow_left") : UM.Theme.getIcon("arrow_bottom") - } - label: Label{ } - } - } - - Column + popupItem: Column { id: viewSettings - property bool collapsed: false property var extruder_opacities: UM.Preferences.getValue("layerview/extruder_opacities").split("|") property bool show_travel_moves: UM.Preferences.getValue("layerview/show_travel_moves") property bool show_helpers: UM.Preferences.getValue("layerview/show_helpers") @@ -118,19 +74,11 @@ Rectangle property bool only_show_top_layers: UM.Preferences.getValue("view/only_show_top_layers") property int top_layer_count: UM.Preferences.getValue("view/top_layer_count") - anchors - { - top: layerViewTypesLabel.bottom - left: parent.left - right: parent.right - margins: UM.Theme.getSize("default_margin").height - - } + width: UM.Theme.getSize("layerview_menu_size").width + height: childrenRect.height spacing: UM.Theme.getSize("layerview_row_spacing").height - visible: !collapsed - ListModel // matches SimulationView.py { id: layerViewTypes diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index af579340a5..06b4146a57 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -47,6 +47,14 @@ Item popup.contentItem = popupItem } + Connections + { + // Since it could be that the popup is dynamically populated, we should also take these changes into account. + target: popupItem + onWidthChanged: popup.width = popupItem.width + 2 * popup.padding + onHeightChanged: popup.height = popupItem.height + 2 * popup.padding + } + implicitHeight: 100 implicitWidth: 400 Rectangle From c9389dd9ab824b40a09b13fd9f3939ffc531f874 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 13 Nov 2018 15:58:34 +0100 Subject: [PATCH 0174/1240] Codestyle fixes --- resources/qml/PrintSetupSelector.qml | 29 ++++++---------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/resources/qml/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector.qml index 65bcee8507..7a7d438bc3 100644 --- a/resources/qml/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector.qml @@ -109,23 +109,6 @@ Cura.ExpandableComponent } } - - /*Label - { - id: settingsModeLabel - text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox", "Print Setup disabled\nG-code files cannot be modified") - renderType: Text.NativeRendering - - anchors - { - fill: parent - } - - height: contentHeight - font: UM.Theme.getFont("large") - color: UM.Theme.getColor("text") - }*/ - popupItem: Item { height: settingsModeSelection.height + sidebarContents.height @@ -184,7 +167,7 @@ Cura.ExpandableComponent border.width: control.checked ? UM.Theme.getSize("default_lining").width * 2 : UM.Theme.getSize("default_lining").width border.color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_border") : control.hovered ? UM.Theme.getColor("action_button_hovered_border") : UM.Theme.getColor("action_button_border") - // for some reason, QtQuick decided to use the color of the background property as text color for the contentItem, so here it is + // For some reason, QtQuick decided to use the color of the background property as text color for the contentItem, so here it is color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active") : control.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button") } @@ -200,13 +183,13 @@ Cura.ExpandableComponent { if(control.pressed) { - return UM.Theme.getColor("action_button_active_text"); + return UM.Theme.getColor("action_button_active_text") } else if(control.hovered) { - return UM.Theme.getColor("action_button_hovered_text"); + return UM.Theme.getColor("action_button_hovered_text") } - return UM.Theme.getColor("action_button_text"); + return UM.Theme.getColor("action_button_text") } } } @@ -263,11 +246,11 @@ Cura.ExpandableComponent if(index != null && !isNaN(index)) { - currentModeIndex = index; + currentModeIndex = index } else { - currentModeIndex = 0; + currentModeIndex = 0 } } } From 20e2f317de0507ec889baa5dc30cf48277165bdb Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 13 Nov 2018 16:02:45 +0100 Subject: [PATCH 0175/1240] Add background to viewModeSelection CURA-5785 --- plugins/PreviewStage/PreviewMenu.qml | 71 +++++++++++++++------------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index aeef2bf300..f21c13a5bc 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -26,49 +26,56 @@ Item anchors.horizontalCenter: parent.horizontalCenter spacing: UM.Theme.getSize("default_margin").width height: parent.height - ComboBox + + Rectangle { - // This item contains the views selector, a combobox that is dynamically created from - // the list of available Views (packages that create different visualizations of the - // scene). - id: viewModeButton - - style: UM.Theme.styles.combobox - - model: UM.ViewModel { } - textRole: "name" - - // update the model's active index - function updateItemActiveFlags() + color: UM.Theme.getColor("tool_panel_background") + width: viewModeButton.width + 2 * UM.Theme.getSize("default_margin").width + height: parent.height + ComboBox { - currentIndex = getActiveIndex() - for (var i = 0; i < model.rowCount(); i++) - { - model.getItem(i).active = (i == currentIndex) - } - } + // This item contains the views selector, a combobox that is dynamically created from + // the list of available Views (packages that create different visualizations of the + // scene). + id: viewModeButton - // get the index of the active model item on start - function getActiveIndex() - { - for (var i = 0; i < model.rowCount(); i++) + style: UM.Theme.styles.combobox + anchors.centerIn: parent + model: UM.ViewModel { } + textRole: "name" + + // update the model's active index + function updateItemActiveFlags() { - if (model.getItem(i).active) + currentIndex = getActiveIndex() + for (var i = 0; i < model.rowCount(); i++) { - return i; + model.getItem(i).active = (i == currentIndex) } } - return 0 - } - onCurrentIndexChanged: - { - if (model.getItem(currentIndex).id != undefined) + // get the index of the active model item on start + function getActiveIndex() { - UM.Controller.setActiveView(model.getItem(currentIndex).id) + for (var i = 0; i < model.rowCount(); i++) + { + if (model.getItem(i).active) + { + return i; + } + } + return 0 } + + onCurrentIndexChanged: + { + if (model.getItem(currentIndex).id != undefined) + { + UM.Controller.setActiveView(model.getItem(currentIndex).id) + } + } + currentIndex: getActiveIndex() } - currentIndex: getActiveIndex() } Loader From 4b8d20cad770646c9f33c6ca5a508bedee968858 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 13 Nov 2018 16:27:19 +0100 Subject: [PATCH 0176/1240] Correct the link to create a new account Previously this button just took you to the log-in page. Fixes issue CURA-5921. --- resources/qml/Account/GeneralOperations.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Account/GeneralOperations.qml b/resources/qml/Account/GeneralOperations.qml index 362e088033..4614c4ba88 100644 --- a/resources/qml/Account/GeneralOperations.qml +++ b/resources/qml/Account/GeneralOperations.qml @@ -20,7 +20,7 @@ Row hoverColor: UM.Theme.getColor("secondary") textColor: UM.Theme.getColor("main_window_header_button_text_active") textHoverColor: UM.Theme.getColor("main_window_header_button_text_active") - onClicked: Qt.openUrlExternally("https://account.ultimaker.com") + onClicked: Qt.openUrlExternally("https://account.ultimaker.com/app/create") fixedWidthMode: true } From b83175a380e170a71e8b6cdfae0235a4777e9c44 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 13 Nov 2018 16:31:48 +0100 Subject: [PATCH 0177/1240] Add rounded corners to stage menus CURA-5785 --- plugins/PrepareStage/PrepareMenu.qml | 72 +++++++++------ plugins/PreviewStage/PreviewMenu.qml | 130 +++++++++++++++------------ 2 files changed, 117 insertions(+), 85 deletions(-) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index 9fd8747e28..7006545204 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -23,7 +23,7 @@ Item Item { anchors.horizontalCenter: parent.horizontalCenter - width: openFileButton.width + UM.Theme.getSize("default_margin").width + itemRow.width + width: openFileButton.width + itemRowBackground.width height: parent.height Button @@ -36,45 +36,59 @@ Item action: Cura.Actions.open } - RowLayout + Rectangle { - id: itemRow + id: itemRowBackground + radius: UM.Theme.getSize("default_radius").width + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") + color: UM.Theme.getColor("toolbar_background") + + width: itemRow.width + UM.Theme.getSize("default_margin").width + height: parent.height anchors.left: openFileButton.right anchors.leftMargin: UM.Theme.getSize("default_margin").width - width: 0.9 * prepareMenu.width - height: parent.height - - Cura.MachineSelector + RowLayout { - id: machineSelection - z: openFileButton.z - 1 + id: itemRow - Layout.minimumWidth: 240 - Layout.maximumWidth: 240 - Layout.fillWidth: true - Layout.fillHeight: true - } + anchors.centerIn: parent - Cura.QuickConfigurationSelector - { - Layout.fillHeight: true - Layout.fillWidth: true - Layout.preferredWidth: itemRow.width - machineSelection.width - printSetupSelector.width - } + width: 0.9 * prepareMenu.width + height: parent.height - 2 * UM.Theme.getSize("default_lining").width - Cura.PrintSetupSelector - { - id: printSetupSelector + Cura.MachineSelector + { + id: machineSelection + z: openFileButton.z - 1 - onShowTooltip: prepareMenu.showTooltip(item, location, text) - onHideTooltip: prepareMenu.hideTooltip() + Layout.minimumWidth: 240 + Layout.maximumWidth: 240 + Layout.fillWidth: true + Layout.fillHeight: true + } - Layout.minimumWidth: 460 - Layout.maximumWidth: 460 - Layout.fillWidth: true - Layout.fillHeight: true + Cura.QuickConfigurationSelector + { + Layout.fillHeight: true + Layout.fillWidth: true + Layout.preferredWidth: itemRow.width - machineSelection.width - printSetupSelector.width + } + + Cura.PrintSetupSelector + { + id: printSetupSelector + + onShowTooltip: prepareMenu.showTooltip(item, location, text) + onHideTooltip: prepareMenu.hideTooltip() + + Layout.minimumWidth: 460 + Layout.maximumWidth: 460 + Layout.fillWidth: true + Layout.fillHeight: true + } } } } diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index f21c13a5bc..1741706679 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -15,88 +15,106 @@ Item signal showTooltip(Item item, point location, string text) signal hideTooltip() + property real itemHeight: height - 2 * UM.Theme.getSize("default_lining").width + UM.I18nCatalog { id: catalog name: "cura" } - Row + Rectangle { - anchors.horizontalCenter: parent.horizontalCenter - spacing: UM.Theme.getSize("default_margin").width + anchors.fill: stageMenu + anchors.leftMargin: -radius + radius: UM.Theme.getSize("default_radius").width + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") + color: UM.Theme.getColor("toolbar_background") + } + + Item + { + id: stageMenu height: parent.height - - Rectangle + width: childrenRect.width + UM.Theme.getSize("default_margin").width + anchors.horizontalCenter: parent.horizontalCenter + Row { - color: UM.Theme.getColor("tool_panel_background") - width: viewModeButton.width + 2 * UM.Theme.getSize("default_margin").width - height: parent.height - ComboBox + anchors.centerIn: parent + spacing: UM.Theme.getSize("default_margin").width + height: parent.height - 2 * UM.Theme.getSize("default_lining").width + + Item { - // This item contains the views selector, a combobox that is dynamically created from - // the list of available Views (packages that create different visualizations of the - // scene). - id: viewModeButton - - style: UM.Theme.styles.combobox - anchors.centerIn: parent - model: UM.ViewModel { } - textRole: "name" - - // update the model's active index - function updateItemActiveFlags() + width: viewModeButton.width + 2 * UM.Theme.getSize("default_margin").width + height: parent.height + ComboBox { - currentIndex = getActiveIndex() - for (var i = 0; i < model.rowCount(); i++) - { - model.getItem(i).active = (i == currentIndex) - } - } + // This item contains the views selector, a combobox that is dynamically created from + // the list of available Views (packages that create different visualizations of the + // scene). + id: viewModeButton - // get the index of the active model item on start - function getActiveIndex() - { - for (var i = 0; i < model.rowCount(); i++) + style: UM.Theme.styles.combobox + anchors.centerIn: parent + model: UM.ViewModel { } + textRole: "name" + + // update the model's active index + function updateItemActiveFlags() { - if (model.getItem(i).active) + currentIndex = getActiveIndex() + for (var i = 0; i < model.rowCount(); i++) { - return i; + model.getItem(i).active = (i == currentIndex) } } - return 0 - } - onCurrentIndexChanged: - { - if (model.getItem(currentIndex).id != undefined) + // get the index of the active model item on start + function getActiveIndex() { - UM.Controller.setActiveView(model.getItem(currentIndex).id) + for (var i = 0; i < model.rowCount(); i++) + { + if (model.getItem(i).active) + { + return i; + } + } + return 0 } + + onCurrentIndexChanged: + { + if (model.getItem(currentIndex).id != undefined) + { + UM.Controller.setActiveView(model.getItem(currentIndex).id) + } + } + currentIndex: getActiveIndex() } - currentIndex: getActiveIndex() } - } - Loader - { - // TODO: Make this panel collapsable and ensure it has a standardised background. - id: viewPanel + Loader + { + // TODO: Make this panel collapsable and ensure it has a standardised background. + id: viewPanel - property var buttonTarget: Qt.point(viewModeButton.x + Math.round(viewModeButton.width / 2), viewModeButton.y + Math.round(viewModeButton.height / 2)) + property var buttonTarget: Qt.point(viewModeButton.x + Math.round(viewModeButton.width / 2), viewModeButton.y + Math.round(viewModeButton.height / 2)) - height: parent.height - width: childrenRect.width + height: parent.height + width: childrenRect.width - source: UM.Controller.activeView != null && UM.Controller.activeView.stageMenuComponent != null ? UM.Controller.activeView.stageMenuComponent : "" - } + source: UM.Controller.activeView != null && UM.Controller.activeView.stageMenuComponent != null ? UM.Controller.activeView.stageMenuComponent : "" + } - Cura.PrintSetupSelector - { - width: UM.Theme.getSize("print_setup_widget").width - height: parent.height - onShowTooltip: previewMenu.showTooltip(item, location, text) - onHideTooltip: previewMenu.hideTooltip() + Cura.PrintSetupSelector + { + width: UM.Theme.getSize("print_setup_widget").width + height: parent.height + onShowTooltip: previewMenu.showTooltip(item, location, text) + onHideTooltip: previewMenu.hideTooltip() + } } } } \ No newline at end of file From 551bc2fbd41984a4ca11a4d42a49be04736f5e9b Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 13 Nov 2018 16:40:51 +0100 Subject: [PATCH 0178/1240] Removed line around the headerbar I was checking an old design, the new one doesn't have a line around it CURA-5785 --- plugins/PrepareStage/PrepareMenu.qml | 5 ++--- plugins/PreviewStage/PreviewMenu.qml | 4 +--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index 7006545204..ac43fd5634 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -40,8 +40,7 @@ Item { id: itemRowBackground radius: UM.Theme.getSize("default_radius").width - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("lining") + color: UM.Theme.getColor("toolbar_background") width: itemRow.width + UM.Theme.getSize("default_margin").width @@ -57,7 +56,7 @@ Item anchors.centerIn: parent width: 0.9 * prepareMenu.width - height: parent.height - 2 * UM.Theme.getSize("default_lining").width + height: parent.height Cura.MachineSelector { diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index 1741706679..b441a78dff 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -28,8 +28,6 @@ Item anchors.fill: stageMenu anchors.leftMargin: -radius radius: UM.Theme.getSize("default_radius").width - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("lining") color: UM.Theme.getColor("toolbar_background") } @@ -43,7 +41,7 @@ Item { anchors.centerIn: parent spacing: UM.Theme.getSize("default_margin").width - height: parent.height - 2 * UM.Theme.getSize("default_lining").width + height: parent.height Item { From a9fdd455ebf39dbc66f592324ebcca08620ed14e Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 13 Nov 2018 16:47:39 +0100 Subject: [PATCH 0179/1240] Add seperator lines for the stageMenu's CURA-5785 --- plugins/PrepareStage/PrepareMenu.qml | 18 +++++++++++++++++- plugins/PreviewStage/PreviewMenu.qml | 17 ++++++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index ac43fd5634..c7164eb490 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -69,11 +69,27 @@ Item Layout.fillHeight: true } + // Separator line + Rectangle + { + height: parent.height + width: UM.Theme.getSize("default_lining").width + color: UM.Theme.getColor("lining") + } + Cura.QuickConfigurationSelector { Layout.fillHeight: true Layout.fillWidth: true - Layout.preferredWidth: itemRow.width - machineSelection.width - printSetupSelector.width + Layout.preferredWidth: itemRow.width - machineSelection.width - printSetupSelector.width - 2 * UM.Theme.getSize("default_lining").width + } + + // Separator line + Rectangle + { + height: parent.height + width: UM.Theme.getSize("default_lining").width + color: UM.Theme.getColor("lining") } Cura.PrintSetupSelector diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index b441a78dff..ccc0a98c6e 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -40,7 +40,7 @@ Item Row { anchors.centerIn: parent - spacing: UM.Theme.getSize("default_margin").width + //spacing: UM.Theme.getSize("default_margin").width height: parent.height Item @@ -93,6 +93,14 @@ Item } } + // Separator line + Rectangle + { + height: parent.height + width: UM.Theme.getSize("default_lining").width + color: UM.Theme.getColor("lining") + } + Loader { // TODO: Make this panel collapsable and ensure it has a standardised background. @@ -106,6 +114,13 @@ Item source: UM.Controller.activeView != null && UM.Controller.activeView.stageMenuComponent != null ? UM.Controller.activeView.stageMenuComponent : "" } + // Separator line + Rectangle + { + height: parent.height + width: UM.Theme.getSize("default_lining").width + color: UM.Theme.getColor("lining") + } Cura.PrintSetupSelector { width: UM.Theme.getSize("print_setup_widget").width From 12c296241436acf72fd171f00e914be74ffd3ba7 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 13 Nov 2018 16:59:02 +0100 Subject: [PATCH 0180/1240] Update the look & feel of openButton to the new design CURA-5785 --- plugins/PrepareStage/PrepareMenu.qml | 30 +++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index c7164eb490..3c7235a26f 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -23,19 +23,30 @@ Item Item { anchors.horizontalCenter: parent.horizontalCenter - width: openFileButton.width + itemRowBackground.width + width: openFileButtonBackground.width + itemRowBackground.width height: parent.height - Button + Rectangle { - id: openFileButton - text: catalog.i18nc("@action:button", "Open File") - iconSource: UM.Theme.getIcon("load") - style: UM.Theme.styles.tool_button - tooltip: "" - action: Cura.Actions.open + id: openFileButtonBackground + height: UM.Theme.getSize("stage_menu").height + width: UM.Theme.getSize("stage_menu").height + + radius: UM.Theme.getSize("default_radius").width + color: UM.Theme.getColor("toolbar_background") + Button + { + id: openFileButton + text: catalog.i18nc("@action:button", "Open File") + iconSource: UM.Theme.getIcon("load") + style: UM.Theme.styles.toolbar_button + tooltip: "" + action: Cura.Actions.open + anchors.centerIn: parent + } } + Rectangle { id: itemRowBackground @@ -46,7 +57,7 @@ Item width: itemRow.width + UM.Theme.getSize("default_margin").width height: parent.height - anchors.left: openFileButton.right + anchors.left: openFileButtonBackground.right anchors.leftMargin: UM.Theme.getSize("default_margin").width RowLayout @@ -57,6 +68,7 @@ Item width: 0.9 * prepareMenu.width height: parent.height + spacing: 0 Cura.MachineSelector { From fb72f9a0594c8ac1d3e721dbad60617c227699d0 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 14 Nov 2018 09:59:56 +0100 Subject: [PATCH 0181/1240] Fixed size of expandableItem not being the correct width CURA-5785 --- plugins/PreviewStage/PreviewMenu.qml | 1 + plugins/SimulationView/SimulationViewMenuComponent.qml | 2 +- resources/qml/ExpandableComponent.qml | 2 +- .../qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index ccc0a98c6e..5b0ae4e9c5 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -121,6 +121,7 @@ Item width: UM.Theme.getSize("default_lining").width color: UM.Theme.getColor("lining") } + Cura.PrintSetupSelector { width: UM.Theme.getSize("print_setup_widget").width diff --git a/plugins/SimulationView/SimulationViewMenuComponent.qml b/plugins/SimulationView/SimulationViewMenuComponent.qml index fe9f4471fd..92a4e1e317 100644 --- a/plugins/SimulationView/SimulationViewMenuComponent.qml +++ b/plugins/SimulationView/SimulationViewMenuComponent.qml @@ -74,7 +74,7 @@ Cura.ExpandableComponent property bool only_show_top_layers: UM.Preferences.getValue("view/only_show_top_layers") property int top_layer_count: UM.Preferences.getValue("view/top_layer_count") - width: UM.Theme.getSize("layerview_menu_size").width + width: base.width - 2 * UM.Theme.getSize("default_margin").width height: childrenRect.height spacing: UM.Theme.getSize("layerview_row_spacing").height diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 06b4146a57..c525d163fe 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -111,7 +111,7 @@ Item // Make the popup right aligned with the rest. The 3x padding is due to left, right and padding between //the button & text. x: -width + collapseButton.width + headerItemLoader.width + 3 * background.padding - + padding: UM.Theme.getSize("default_margin").width closePolicy: Popup.CloseOnPressOutsideParent background: Rectangle diff --git a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml index 7c70229b5d..721416352d 100644 --- a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml +++ b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml @@ -92,7 +92,7 @@ Cura.ExpandableComponent popupItem: Item { - width: base.width + width: base.width - 2 * UM.Theme.getSize("default_margin").width height: 200 TabBar From c35b4f1d31d295fc9d91b15167cea6ac79298bd7 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 14 Nov 2018 10:00:44 +0100 Subject: [PATCH 0182/1240] Removed old unusued code CURA-5785 --- .../QuickConfigurationSelector.qml | 62 +------------------ 1 file changed, 1 insertion(+), 61 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml index 721416352d..5565884bd8 100644 --- a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml +++ b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml @@ -233,64 +233,4 @@ Cura.ExpandableComponent } } - -} - -/*Item -{ - id: configurationSelector - property var connectedDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null - property var panelWidth: control.width - - // Make this component only visible when it's a network printer and it is connected - visible: Cura.MachineManager.activeMachineNetworkKey != "" && Cura.MachineManager.printerConnected - - function switchPopupState() - { - popup.visible ? popup.close() : popup.open() - } - - SyncButton - { - id: syncButton - onClicked: switchPopupState() - outputDevice: connectedDevice - } - - Popup - { - // 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: false - padding: UM.Theme.getSize("default_lining").width - transformOrigin: Popup.Top - contentItem: ConfigurationListView - { - id: configList - width: panelWidth - 2 * popup.padding - outputDevice: connectedDevice - } - background: Rectangle - { - color: UM.Theme.getColor("setting_control") - border.color: UM.Theme.getColor("setting_control_border") - } - exit: Transition - { - // This applies a default NumberAnimation to any changes a state change makes to x or y properties - 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: visible = false - onOpened: visible = true - } -}*/ \ No newline at end of file +} \ No newline at end of file From 878b7432327e92f2081f153d28063c42e6905c0c Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 14 Nov 2018 10:39:03 +0100 Subject: [PATCH 0183/1240] Add elide to machine selector label. CURA-5785 --- resources/qml/MachineSelector.qml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/resources/qml/MachineSelector.qml b/resources/qml/MachineSelector.qml index 206229e837..87a3154338 100644 --- a/resources/qml/MachineSelector.qml +++ b/resources/qml/MachineSelector.qml @@ -25,14 +25,12 @@ Cura.ExpandableComponent name: "cura" } - headerItem: Item + headerItem: Label { - Label - { - text: isNetworkPrinter ? Cura.MachineManager.activeMachineNetworkGroupName : Cura.MachineManager.activeMachineName - verticalAlignment: Text.AlignVCenter - height: parent.height - } + text: isNetworkPrinter ? Cura.MachineManager.activeMachineNetworkGroupName : Cura.MachineManager.activeMachineName + verticalAlignment: Text.AlignVCenter + height: parent.height + elide: Text.ElideRight } popupItem: Item From fcfe95c7d0a7217bd5793ee5e5148b835bae29c4 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 14 Nov 2018 12:57:57 +0100 Subject: [PATCH 0184/1240] Move OpenFile button to be instantiated left so it's tooltip doesn't get messed up CURA-5785 --- plugins/PrepareStage/PrepareMenu.qml | 43 ++++++++++++++-------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index 3c7235a26f..78f9ced1e7 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -26,27 +26,6 @@ Item width: openFileButtonBackground.width + itemRowBackground.width height: parent.height - Rectangle - { - id: openFileButtonBackground - height: UM.Theme.getSize("stage_menu").height - width: UM.Theme.getSize("stage_menu").height - - radius: UM.Theme.getSize("default_radius").width - color: UM.Theme.getColor("toolbar_background") - Button - { - id: openFileButton - text: catalog.i18nc("@action:button", "Open File") - iconSource: UM.Theme.getIcon("load") - style: UM.Theme.styles.toolbar_button - tooltip: "" - action: Cura.Actions.open - anchors.centerIn: parent - } - } - - Rectangle { id: itemRowBackground @@ -73,7 +52,7 @@ Item Cura.MachineSelector { id: machineSelection - z: openFileButton.z - 1 + z: openFileButtonBackground.z - 1 Layout.minimumWidth: 240 Layout.maximumWidth: 240 @@ -118,5 +97,25 @@ Item } } } + + Rectangle + { + id: openFileButtonBackground + height: UM.Theme.getSize("stage_menu").height + width: UM.Theme.getSize("stage_menu").height + + radius: UM.Theme.getSize("default_radius").width + color: UM.Theme.getColor("toolbar_background") + Button + { + id: openFileButton + text: catalog.i18nc("@action:button", "Open File") + iconSource: UM.Theme.getIcon("load") + style: UM.Theme.styles.toolbar_button + tooltip: "" + action: Cura.Actions.open + anchors.centerIn: parent + } + } } } \ No newline at end of file From ae2b3124721c9c74730073d178c0ac1927819b1c Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 14 Nov 2018 13:41:23 +0100 Subject: [PATCH 0185/1240] Add typing for all version upgrade plug-ins Hopefully we'll take this typing along when we next copy-paste the stuffs. Contributes to issue CURA-5936. --- .../VersionUpgrade21to22/MachineInstance.py | 17 ++--- .../VersionUpgrade21to22/Preferences.py | 9 +-- .../VersionUpgrade21to22/Profile.py | 11 ++- .../VersionUpgrade21to22.py | 69 ++++++++++--------- .../VersionUpgrade21to22/__init__.py | 11 ++- .../VersionUpgrade22to24/VersionUpgrade.py | 20 +++--- .../VersionUpgrade22to24/__init__.py | 11 ++- .../VersionUpgrade25to26.py | 26 +++---- .../VersionUpgrade25to26/__init__.py | 11 ++- .../VersionUpgrade26to27.py | 13 ++-- .../VersionUpgrade26to27/__init__.py | 11 ++- .../VersionUpgrade27to30.py | 21 +++--- .../VersionUpgrade27to30/__init__.py | 11 ++- .../VersionUpgrade30to31.py | 19 ++--- .../VersionUpgrade30to31/__init__.py | 11 ++- .../VersionUpgrade32to33.py | 21 +++--- .../VersionUpgrade32to33/__init__.py | 9 ++- .../VersionUpgrade33to34.py | 8 +-- .../VersionUpgrade33to34/__init__.py | 9 ++- .../VersionUpgrade34to35.py | 23 +++---- .../VersionUpgrade34to35/__init__.py | 9 ++- 21 files changed, 200 insertions(+), 150 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py index 37b6989add..a947114595 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -1,15 +1,16 @@ -# Copyright (c) 2016 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -import UM.VersionUpgrade #To indicate that a file is of incorrect format. -import UM.VersionUpgradeManager #To schedule more files to be upgraded. -from UM.Resources import Resources #To get the config storage path. - import configparser #To read config files. import io #To write config files to strings as if they were files. import os.path #To get the path to write new user profiles to. +from typing import List, Optional, Tuple import urllib #To serialise the user container file name properly. +import UM.VersionUpgrade #To indicate that a file is of incorrect format. +import UM.VersionUpgradeManager #To schedule more files to be upgraded. +from UM.Resources import Resources #To get the config storage path. + ## Creates a new machine instance instance by parsing a serialised machine # instance in version 1 of the file format. # @@ -18,7 +19,7 @@ import urllib #To serialise the user container file name properly. # extension. # \return A machine instance instance, or None if the file format is # incorrect. -def importFrom(serialised, filename): +def importFrom(serialised: str, filename: str) -> Optional["MachineInstance"]: try: return MachineInstance(serialised, filename) except (configparser.Error, UM.VersionUpgrade.FormatException, UM.VersionUpgrade.InvalidVersionException): @@ -32,7 +33,7 @@ class MachineInstance: # \param serialised A string with the contents of a machine instance file, # without extension. # \param filename The supposed file name of this machine instance. - def __init__(self, serialised, filename): + def __init__(self, serialised: str, filename: str) -> str: self._filename = filename config = configparser.ConfigParser(interpolation = None) @@ -67,7 +68,7 @@ class MachineInstance: # # \return A tuple containing the new filename and a serialised form of # this machine instance, serialised in version 2 of the file format. - def export(self): + def export(self) -> Tuple[List[str], List[str]]: config = configparser.ConfigParser(interpolation = None) # Build a config file in the form of version 2. config.add_section("general") diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py index 842499da86..51e4b617e8 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py @@ -1,8 +1,9 @@ -# Copyright (c) 2016 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import configparser #To read config files. import io #To output config files to string. +from typing import List, Optional, Tuple import UM.VersionUpgrade #To indicate that a file is of the wrong format. @@ -14,7 +15,7 @@ import UM.VersionUpgrade #To indicate that a file is of the wrong format. # extension. # \return A representation of those preferences, or None if the file format is # incorrect. -def importFrom(serialised, filename): +def importFrom(serialised: str, filename: str) -> Optional["Preferences"]: try: return Preferences(serialised, filename) except (configparser.Error, UM.VersionUpgrade.FormatException, UM.VersionUpgrade.InvalidVersionException): @@ -28,7 +29,7 @@ class Preferences: # \param serialised A serialised version 2 preferences file. # \param filename The supposed filename of the preferences file, without # extension. - def __init__(self, serialised, filename): + def __init__(self, serialised: str, filename: str) -> None: self._filename = filename self._config = configparser.ConfigParser(interpolation = None) @@ -50,7 +51,7 @@ class Preferences: # # \return A tuple containing the new filename and a serialised version of # a preferences file in version 3. - def export(self): + def export(self) -> Tuple[List[str], List[str]]: #Reset the cura/categories_expanded property since it works differently now. if self._config.has_section("cura") and self._config.has_option("cura", "categories_expanded"): self._config.remove_option("cura", "categories_expanded") diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py index 161edcb67c..af9635d384 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Profile.py @@ -1,10 +1,9 @@ -# Copyright (c) 2016 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import configparser #To read config files. import io #To write config files to strings as if they were files. -from typing import Dict -from typing import List +from typing import Dict, List, Optional, Tuple import UM.VersionUpgrade from UM.Logger import Logger @@ -15,7 +14,7 @@ from UM.Logger import Logger # \param serialised The serialised form of a profile in version 1. # \param filename The supposed filename of the profile, without extension. # \return A profile instance, or None if the file format is incorrect. -def importFrom(serialised, filename): +def importFrom(serialised: str, filename: str) -> Optional["Profile"]: try: return Profile(serialised, filename) except (configparser.Error, UM.VersionUpgrade.FormatException, UM.VersionUpgrade.InvalidVersionException): @@ -77,11 +76,11 @@ class Profile: # # \return A tuple containing the new filename and a serialised form of # this profile, serialised in version 2 of the file format. - def export(self): + def export(self) -> Optional[Tuple[List[str], List[str]]]: import VersionUpgrade21to22 # Import here to prevent circular dependencies. if self._name == "Current settings": - return None, None #Can't upgrade these, because the new current profile needs to specify the definition ID and the old file only had the machine instance, not the definition. + return None #Can't upgrade these, because the new current profile needs to specify the definition ID and the old file only had the machine instance, not the definition. config = configparser.ConfigParser(interpolation = None) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py index d8036491bf..89c847e606 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -1,7 +1,8 @@ -# Copyright (c) 2017 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import configparser #To get version numbers from config files. +from typing import Dict, Iterable, List, Optional, Set, Tuple from UM.VersionUpgrade import VersionUpgrade # Superclass of the plugin. @@ -30,7 +31,7 @@ _machines_with_machine_quality = { "materials": { "generic_abs", "generic_cpe", "generic_pla", "generic_pva", "generic_cpe_plus", "generic_nylon", "generic_pc", "generic_tpu" }, "variants": { "0.25 mm", "0.4 mm", "0.6 mm", "0.8 mm" } } -} +} # type: Dict[str, Dict[str, Set[str]]] ## How to translate material names from the old version to the new. _material_translations = { @@ -41,7 +42,7 @@ _material_translations = { "Nylon": "generic_nylon", "PC": "generic_pc", "TPU": "generic_tpu", -} +} # type: Dict[str, str] ## How to translate material names for in the profile names. _material_translations_profiles = { @@ -52,17 +53,17 @@ _material_translations_profiles = { "Nylon": "nylon", "PC": "pc", "TPU": "tpu", -} +} # type: Dict[str, str] ## How to translate printer names from the old version to the new. _printer_translations = { "ultimaker2plus": "ultimaker2_plus" -} +} # type: Dict[str, str] _printer_translations_profiles = { "ultimaker2plus": "um2p", #Does NOT get included in PLA profiles! "ultimaker2_extended_plus": "um2ep" #Has no profiles for CPE+, Nylon, PC and TPU! -} +} # type: Dict[str, str] ## How to translate profile names from the old version to the new. # @@ -116,13 +117,13 @@ _profile_translations = { "tpu_0.25_high": "um2p_tpu_0.25_high", "tpu_0.4_normal": "um2p_tpu_0.4_normal", "tpu_0.6_fast": "um2p_tpu_0.6_fast" -} +} # type: Dict[str, str] ## Settings that are no longer in the new version. _removed_settings = { "fill_perimeter_gaps", "support_area_smoothing" -} +} # type: Set[str] ## How to translate setting names from the old version to the new. _setting_name_translations = { @@ -142,7 +143,7 @@ _setting_name_translations = { "support_roof_line_distance": "support_interface_line_distance", "support_roof_line_width": "support_interface_line_width", "support_roof_pattern": "support_interface_pattern" -} +} # type: Dict[str, str] ## Custom profiles become quality_changes. This dictates which quality to base # the quality_changes profile on. @@ -190,7 +191,7 @@ _quality_fallbacks = { #No TPU. } } -} +} # type: Dict[str, Dict[str, Dict[str, str]]] ## How to translate variants of specific machines from the old version to the # new. @@ -207,7 +208,7 @@ _variant_translations = { "0.6 mm": "ultimaker2_extended_plus_0.6", "0.8 mm": "ultimaker2_extended_plus_0.8" } -} +} # type: Dict[str, Dict[str, str]] ## How to translate variant names for in the profile names. _variant_translations_profiles = { @@ -215,7 +216,7 @@ _variant_translations_profiles = { "0.4 mm": "0.4", "0.6 mm": "0.6", "0.8 mm": "0.8" -} +} # type: Dict[str, str] ## Cura 2.2's material profiles use a different naming scheme for variants. # @@ -233,7 +234,7 @@ _variant_translations_materials = { "0.6 mm": "ultimaker2_plus_0.6_mm", "0.8 mm": "ultimaker2_plus_0.8_mm" } -} +} # type: Dict[str, Dict[str, str]] ## Converts configuration from Cura 2.1's file formats to Cura 2.2's. # @@ -245,8 +246,8 @@ class VersionUpgrade21to22(VersionUpgrade): # number is stored in general/version, so get the data from that key. # # \param serialised The contents of a config file. - # \return \type{int} The version number of that config file. - def getCfgVersion(self, serialised): + # \return The version number of that config file. + def getCfgVersion(self, serialised: str) -> int: parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised. @@ -263,7 +264,7 @@ class VersionUpgrade21to22(VersionUpgrade): # \param variant The variant ID of the user's configuration in 2.2. # \param material The material ID of the user's configuration in 2.2. @staticmethod - def getQualityFallback(machine, variant, material): + def getQualityFallback(machine: str, variant: str, material: str) -> str: if machine not in _quality_fallbacks: return "normal" if variant not in _quality_fallbacks[machine]: @@ -277,14 +278,14 @@ class VersionUpgrade21to22(VersionUpgrade): # This is required to test if profiles should be converted to a quality # profile or a quality-changes profile. @staticmethod - def builtInProfiles(): + def builtInProfiles() -> Iterable[str]: return _profile_translations.keys() ## Gets a set of the machines which now have per-material quality profiles. # # \return A set of machine identifiers. @staticmethod - def machinesWithMachineQuality(): + def machinesWithMachineQuality() -> Dict[str, Dict[str, Set[str]]]: return _machines_with_machine_quality ## Converts machine instances from format version 1 to version 2. @@ -295,10 +296,10 @@ class VersionUpgrade21to22(VersionUpgrade): # \return A tuple containing the new filename and the serialised machine # instance in version 2, or None if the input was not of the correct # format. - def upgradeMachineInstance(self, serialised, filename): + def upgradeMachineInstance(self, serialised: str, filename: str) -> Optional[Tuple[List[str], List[str]]]: machine_instance = MachineInstance.importFrom(serialised, filename) if not machine_instance: #Invalid file format. - return filename, None + return None return machine_instance.export() ## Converts preferences from format version 2 to version 3. @@ -309,10 +310,10 @@ class VersionUpgrade21to22(VersionUpgrade): # \return A tuple containing the new filename and the serialised # preferences in version 3, or None if the input was not of the correct # format. - def upgradePreferences(self, serialised, filename): + def upgradePreferences(self, serialised: str, filename: str) -> Optional[Tuple[List[str], List[str]]]: preferences = Preferences.importFrom(serialised, filename) if not preferences: #Invalid file format. - return filename, None + return None return preferences.export() ## Converts profiles from format version 1 to version 2. @@ -322,10 +323,10 @@ class VersionUpgrade21to22(VersionUpgrade): # extension. # \return A tuple containing the new filename and the serialised profile # in version 2, or None if the input was not of the correct format. - def upgradeProfile(self, serialised, filename): + def upgradeProfile(self, serialised: str, filename: str) -> Optional[Tuple[List[str], List[str]]]: profile = Profile.importFrom(serialised, filename) if not profile: # Invalid file format. - return filename, None + return None return profile.export() ## Translates a material name for the change from Cura 2.1 to 2.2. @@ -333,7 +334,7 @@ class VersionUpgrade21to22(VersionUpgrade): # \param material A material name in Cura 2.1. # \return The name of the corresponding material in Cura 2.2. @staticmethod - def translateMaterial(material): + def translateMaterial(material: str) -> str: if material in _material_translations: return _material_translations[material] return material @@ -345,7 +346,7 @@ class VersionUpgrade21to22(VersionUpgrade): # \return The name of the corresponding material in the quality profiles # in Cura 2.2. @staticmethod - def translateMaterialForProfiles(material): + def translateMaterialForProfiles(material: str) -> str: if material in _material_translations_profiles: return _material_translations_profiles[material] return material @@ -356,7 +357,7 @@ class VersionUpgrade21to22(VersionUpgrade): # \param printer A printer name in Cura 2.1. # \return The name of the corresponding printer in Cura 2.2. @staticmethod - def translatePrinter(printer): + def translatePrinter(printer: str) -> str: if printer in _printer_translations: return _printer_translations[printer] return printer #Doesn't need to be translated. @@ -367,7 +368,7 @@ class VersionUpgrade21to22(VersionUpgrade): # \param printer A printer name in 2.1. # \return The name of the corresponding printer in Cura 2.2. @staticmethod - def translatePrinterForProfile(printer): + def translatePrinterForProfile(printer: str) -> str: if printer in _printer_translations_profiles: return _printer_translations_profiles[printer] return printer @@ -378,7 +379,7 @@ class VersionUpgrade21to22(VersionUpgrade): # \param profile A profile name in the old version. # \return The corresponding profile name in the new version. @staticmethod - def translateProfile(profile): + def translateProfile(profile: str) -> str: if profile in _profile_translations: return _profile_translations[profile] return profile #Doesn't need to be translated. @@ -392,7 +393,7 @@ class VersionUpgrade21to22(VersionUpgrade): # \param settings A dictionary of settings (as key-value pairs) to update. # \return The same dictionary. @staticmethod - def translateSettings(settings): + def translateSettings(settings: Dict[str, str]) -> Dict[str, str]: new_settings = {} for key, value in settings.items(): if key in _removed_settings: @@ -414,7 +415,7 @@ class VersionUpgrade21to22(VersionUpgrade): # \param setting The name of a setting in Cura 2.1. # \return The name of the corresponding setting in Cura 2.2. @staticmethod - def translateSettingName(setting): + def translateSettingName(setting: str) -> str: if setting in _setting_name_translations: return _setting_name_translations[setting] return setting #Doesn't need to be translated. @@ -426,7 +427,7 @@ class VersionUpgrade21to22(VersionUpgrade): # 2.2's naming. # \return The name of the corresponding variant in Cura 2.2. @staticmethod - def translateVariant(variant, machine): + def translateVariant(variant: str, machine: str) -> str: if machine in _variant_translations and variant in _variant_translations[machine]: return _variant_translations[machine][variant] return variant @@ -440,7 +441,7 @@ class VersionUpgrade21to22(VersionUpgrade): # \return The name of the corresponding variant for in material profiles # in Cura 2.2. @staticmethod - def translateVariantForMaterials(variant, machine): + def translateVariantForMaterials(variant: str, machine: str) -> str: if machine in _variant_translations_materials and variant in _variant_translations_materials[machine]: return _variant_translations_materials[machine][variant] return variant @@ -452,7 +453,7 @@ class VersionUpgrade21to22(VersionUpgrade): # \return The name of the corresponding variant for in quality profiles in # Cura 2.2. @staticmethod - def translateVariantForProfiles(variant): + def translateVariantForProfiles(variant: str) -> str: if variant in _variant_translations_profiles: return _variant_translations_profiles[variant] return variant \ No newline at end of file diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py b/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py index 609781ebfe..67530b9d45 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/__init__.py @@ -1,11 +1,16 @@ -# Copyright (c) 2016 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from typing import Any, Dict, TYPE_CHECKING + from . import VersionUpgrade21to22 +if TYPE_CHECKING: + from UM.Application import Application + upgrade = VersionUpgrade21to22.VersionUpgrade21to22() -def getMetaData(): +def getMetaData() -> Dict[str, Any]: return { "version_upgrade": { # From To Upgrade function @@ -33,5 +38,5 @@ def getMetaData(): } } -def register(app): +def register(app: "Application") -> Dict[str, Any]: return { "version_upgrade": upgrade } diff --git a/plugins/VersionUpgrade/VersionUpgrade22to24/VersionUpgrade.py b/plugins/VersionUpgrade/VersionUpgrade22to24/VersionUpgrade.py index a56f1f807b..48af365877 100644 --- a/plugins/VersionUpgrade/VersionUpgrade22to24/VersionUpgrade.py +++ b/plugins/VersionUpgrade/VersionUpgrade22to24/VersionUpgrade.py @@ -1,18 +1,18 @@ -# Copyright (c) 2017 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import configparser #To get version numbers from config files. +import io import os import os.path -import io +from typing import Dict, List, Optional, Tuple from UM.Resources import Resources from UM.VersionUpgrade import VersionUpgrade # Superclass of the plugin. import UM.VersionUpgrade class VersionUpgrade22to24(VersionUpgrade): - - def upgradeMachineInstance(self, serialised, filename): + def upgradeMachineInstance(self, serialised: str, filename: str) -> Optional[Tuple[List[str], List[str]]]: # All of this is needed to upgrade custom variant machines from old Cura to 2.4 where # `definition_changes` instance container has been introduced. Variant files which # look like the the handy work of the old machine settings plugin are converted directly @@ -71,7 +71,7 @@ class VersionUpgrade22to24(VersionUpgrade): config.write(output) return [filename], [output.getvalue()] - def __convertVariant(self, variant_path): + def __convertVariant(self, variant_path: str) -> str: # Copy the variant to the machine_instances/*_settings.inst.cfg variant_config = configparser.ConfigParser(interpolation = None) with open(variant_path, "r", encoding = "utf-8") as fhandle: @@ -99,7 +99,7 @@ class VersionUpgrade22to24(VersionUpgrade): return config_name - def __getUserVariants(self): + def __getUserVariants(self) -> List[Dict[str, str]]: resource_path = Resources.getDataStoragePath() variants_dir = os.path.join(resource_path, "variants") @@ -113,7 +113,7 @@ class VersionUpgrade22to24(VersionUpgrade): result.append( { "path": entry.path, "name": config.get("general", "name") } ) return result - def upgradeExtruderTrain(self, serialised, filename): + def upgradeExtruderTrain(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]: config = configparser.ConfigParser(interpolation = None) config.read_string(serialised) # Read the input string as config file. config.set("general", "version", "3") # Just bump the version number. That is all we need for now. @@ -122,7 +122,7 @@ class VersionUpgrade22to24(VersionUpgrade): config.write(output) return [filename], [output.getvalue()] - def upgradePreferences(self, serialised, filename): + def upgradePreferences(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]: config = configparser.ConfigParser(interpolation = None) config.read_string(serialised) @@ -142,7 +142,7 @@ class VersionUpgrade22to24(VersionUpgrade): config.write(output) return [filename], [output.getvalue()] - def upgradeQuality(self, serialised, filename): + def upgradeQuality(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]: config = configparser.ConfigParser(interpolation = None) config.read_string(serialised) # Read the input string as config file. config.set("metadata", "type", "quality_changes") # Update metadata/type to quality_changes @@ -152,7 +152,7 @@ class VersionUpgrade22to24(VersionUpgrade): config.write(output) return [filename], [output.getvalue()] - def getCfgVersion(self, serialised): + def getCfgVersion(self, serialised: str) -> int: parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised. diff --git a/plugins/VersionUpgrade/VersionUpgrade22to24/__init__.py b/plugins/VersionUpgrade/VersionUpgrade22to24/__init__.py index 278b660ec1..fe79333544 100644 --- a/plugins/VersionUpgrade/VersionUpgrade22to24/__init__.py +++ b/plugins/VersionUpgrade/VersionUpgrade22to24/__init__.py @@ -1,11 +1,16 @@ -# Copyright (c) 2016 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from typing import Any, Dict, TYPE_CHECKING + from . import VersionUpgrade +if TYPE_CHECKING: + from UM.Application import Application + upgrade = VersionUpgrade.VersionUpgrade22to24() -def getMetaData(): +def getMetaData() -> Dict[str, Any]: return { "version_upgrade": { # From To Upgrade function @@ -26,5 +31,5 @@ def getMetaData(): } } -def register(app): +def register(app: "Application"): return { "version_upgrade": upgrade } diff --git a/plugins/VersionUpgrade/VersionUpgrade25to26/VersionUpgrade25to26.py b/plugins/VersionUpgrade/VersionUpgrade25to26/VersionUpgrade25to26.py index 6643edb765..5c9b94cca3 100644 --- a/plugins/VersionUpgrade/VersionUpgrade25to26/VersionUpgrade25to26.py +++ b/plugins/VersionUpgrade/VersionUpgrade25to26/VersionUpgrade25to26.py @@ -4,6 +4,7 @@ import configparser #To parse the files we need to upgrade and write the new files. import io #To serialise configparser output to a string. import os +from typing import Dict, List, Set, Tuple from urllib.parse import quote_plus from UM.Resources import Resources @@ -12,19 +13,18 @@ from UM.VersionUpgrade import VersionUpgrade _removed_settings = { #Settings that were removed in 2.5. "start_layers_at_same_position", "sub_div_rad_mult" -} +} # type: Set[str] _split_settings = { #These settings should be copied to all settings it was split into. "support_interface_line_distance": {"support_roof_line_distance", "support_bottom_line_distance"} -} +} # type: Dict[str, Set[str]] ## A collection of functions that convert the configuration of the user in Cura # 2.5 to a configuration for Cura 2.6. # # All of these methods are essentially stateless. class VersionUpgrade25to26(VersionUpgrade): - - def __init__(self): + def __init__(self) -> None: super().__init__() self._current_fdm_printer_count = 2 @@ -39,7 +39,7 @@ class VersionUpgrade25to26(VersionUpgrade): # \raises ValueError The format of the version number in the file is # incorrect. # \raises KeyError The format of the file is incorrect. - def getCfgVersion(self, serialised): + def getCfgVersion(self, serialised: str) -> int: parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised. @@ -50,7 +50,7 @@ class VersionUpgrade25to26(VersionUpgrade): # # \param serialised The serialised form of a preferences file. # \param filename The name of the file to upgrade. - def upgradePreferences(self, serialised, filename): + def upgradePreferences(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]: parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) @@ -86,7 +86,7 @@ class VersionUpgrade25to26(VersionUpgrade): # # \param serialised The serialised form of a quality profile. # \param filename The name of the file to upgrade. - def upgradeInstanceContainer(self, serialised, filename): + def upgradeInstanceContainer(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]: parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) @@ -116,7 +116,7 @@ class VersionUpgrade25to26(VersionUpgrade): # # \param serialised The serialised form of a quality profile. # \param filename The name of the file to upgrade. - def upgradeMachineStack(self, serialised, filename): + def upgradeMachineStack(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]: parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) @@ -149,7 +149,7 @@ class VersionUpgrade25to26(VersionUpgrade): return [filename], [output.getvalue()] ## Acquires the next unique extruder stack index number for the Custom FDM Printer. - def _acquireNextUniqueCustomFdmPrinterExtruderStackIdIndex(self): + def _acquireNextUniqueCustomFdmPrinterExtruderStackIdIndex(self) -> int: extruder_stack_dir = os.path.join(Resources.getDataStoragePath(), "extruders") file_name_list = os.listdir(extruder_stack_dir) file_name_list = [os.path.basename(file_name) for file_name in file_name_list] @@ -169,7 +169,7 @@ class VersionUpgrade25to26(VersionUpgrade): return self._current_fdm_printer_count - def _checkCustomFdmPrinterHasExtruderStack(self, machine_id): + def _checkCustomFdmPrinterHasExtruderStack(self, machine_id: str) -> bool: # go through all extruders and make sure that this custom FDM printer has extruder stacks. extruder_stack_dir = os.path.join(Resources.getDataStoragePath(), "extruders") has_extruders = False @@ -197,7 +197,7 @@ class VersionUpgrade25to26(VersionUpgrade): return has_extruders - def _createCustomFdmPrinterExtruderStack(self, machine_id: str, position: int, quality_id: str, material_id: str): + def _createCustomFdmPrinterExtruderStack(self, machine_id: str, position: int, quality_id: str, material_id: str) -> None: stack_id = "custom_extruder_%s" % (position + 1) if self._current_fdm_printer_count > 1: stack_id += " #%s" % self._current_fdm_printer_count @@ -256,7 +256,7 @@ class VersionUpgrade25to26(VersionUpgrade): ## Creates a definition changes container which doesn't contain anything for the Custom FDM Printers. # The container ID will be automatically generated according to the given stack name. - def _getCustomFdmPrinterDefinitionChanges(self, stack_id: str): + def _getCustomFdmPrinterDefinitionChanges(self, stack_id: str) -> configparser.ConfigParser: # In 2.5, there is no definition_changes container for the Custom FDM printer, so it should be safe to use the # default name unless some one names the printer as something like "Custom FDM Printer_settings". definition_changes_id = stack_id + "_settings" @@ -277,7 +277,7 @@ class VersionUpgrade25to26(VersionUpgrade): ## Creates a user settings container which doesn't contain anything for the Custom FDM Printers. # The container ID will be automatically generated according to the given stack name. - def _getCustomFdmPrinterUserSettings(self, stack_id: str): + def _getCustomFdmPrinterUserSettings(self, stack_id: str) -> configparser.ConfigParser: # For the extruder stacks created in the upgrade, also create user_settings containers so the user changes # will be saved. user_settings_id = stack_id + "_user" diff --git a/plugins/VersionUpgrade/VersionUpgrade25to26/__init__.py b/plugins/VersionUpgrade/VersionUpgrade25to26/__init__.py index 67aa73233f..c74b3218b6 100644 --- a/plugins/VersionUpgrade/VersionUpgrade25to26/__init__.py +++ b/plugins/VersionUpgrade/VersionUpgrade25to26/__init__.py @@ -1,11 +1,16 @@ -# Copyright (c) 2017 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from typing import Any, Dict, TYPE_CHECKING + from . import VersionUpgrade25to26 +if TYPE_CHECKING: + from UM.Application import Application + upgrade = VersionUpgrade25to26.VersionUpgrade25to26() -def getMetaData(): +def getMetaData() -> Dict[str, Any]: return { "version_upgrade": { # From To Upgrade function @@ -41,5 +46,5 @@ def getMetaData(): } } -def register(app): +def register(app: "Application") -> Dict[str, Any]: return { "version_upgrade": upgrade } diff --git a/plugins/VersionUpgrade/VersionUpgrade26to27/VersionUpgrade26to27.py b/plugins/VersionUpgrade/VersionUpgrade26to27/VersionUpgrade26to27.py index dfa436e5bd..96307ca43c 100644 --- a/plugins/VersionUpgrade/VersionUpgrade26to27/VersionUpgrade26to27.py +++ b/plugins/VersionUpgrade/VersionUpgrade26to27/VersionUpgrade26to27.py @@ -3,6 +3,7 @@ import configparser #To parse the files we need to upgrade and write the new files. import io #To serialise configparser output to a string. +from typing import Dict, List, Tuple from UM.VersionUpgrade import VersionUpgrade @@ -61,7 +62,7 @@ _renamed_quality_profiles = { "um3_bb0.8_TPU_Not_Supported_Quality": "um3_bb0.8_TPU_Fast_print", "um3_bb0.8_TPU_Not_Supported_Superdraft_Quality": "um3_bb0.8_TPU_Superdraft_Print", -} +} # type: Dict[str, str] ## A collection of functions that convert the configuration of the user in Cura # 2.6 to a configuration for Cura 2.7. @@ -79,7 +80,7 @@ class VersionUpgrade26to27(VersionUpgrade): # \raises ValueError The format of the version number in the file is # incorrect. # \raises KeyError The format of the file is incorrect. - def getCfgVersion(self, serialised): + def getCfgVersion(self, serialised: str) -> int: parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised. @@ -90,7 +91,7 @@ class VersionUpgrade26to27(VersionUpgrade): # # \param serialised The serialised form of a preferences file. # \param filename The name of the file to upgrade. - def upgradePreferences(self, serialised, filename): + def upgradePreferences(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]: parser = configparser.ConfigParser(interpolation=None) parser.read_string(serialised) @@ -117,8 +118,8 @@ class VersionUpgrade26to27(VersionUpgrade): # # \param serialised The serialised form of a container file. # \param filename The name of the file to upgrade. - def upgradeOtherContainer(self, serialised, filename): - parser = configparser.ConfigParser(interpolation=None) + def upgradeOtherContainer(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]: + parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) # Update version numbers @@ -139,7 +140,7 @@ class VersionUpgrade26to27(VersionUpgrade): # # \param serialised The serialised form of a container stack. # \param filename The name of the file to upgrade. - def upgradeStack(self, serialised, filename): + def upgradeStack(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]: parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) diff --git a/plugins/VersionUpgrade/VersionUpgrade26to27/__init__.py b/plugins/VersionUpgrade/VersionUpgrade26to27/__init__.py index 0e26ca8bbf..1952c9ceff 100644 --- a/plugins/VersionUpgrade/VersionUpgrade26to27/__init__.py +++ b/plugins/VersionUpgrade/VersionUpgrade26to27/__init__.py @@ -1,11 +1,16 @@ -# Copyright (c) 2017 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from typing import Any, Dict, TYPE_CHECKING + from . import VersionUpgrade26to27 +if TYPE_CHECKING: + from UM.Application import Application + upgrade = VersionUpgrade26to27.VersionUpgrade26to27() -def getMetaData(): +def getMetaData() -> Dict[str, Any]: return { "version_upgrade": { # From To Upgrade function @@ -59,5 +64,5 @@ def getMetaData(): } } -def register(app): +def register(app: "Application") -> Dict[str, Any]: return { "version_upgrade": upgrade } diff --git a/plugins/VersionUpgrade/VersionUpgrade27to30/VersionUpgrade27to30.py b/plugins/VersionUpgrade/VersionUpgrade27to30/VersionUpgrade27to30.py index 5a141f1558..c9e9e41f7e 100644 --- a/plugins/VersionUpgrade/VersionUpgrade27to30/VersionUpgrade27to30.py +++ b/plugins/VersionUpgrade/VersionUpgrade27to30/VersionUpgrade27to30.py @@ -1,9 +1,10 @@ -# Copyright (c) 2017 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import configparser #To parse preference files. import io #To serialise the preference files afterwards. import os +from typing import Dict, List, Tuple import urllib.parse import re @@ -11,7 +12,7 @@ from UM.VersionUpgrade import VersionUpgrade #We're inheriting from this. _renamed_themes = { "cura": "cura-light" -} +} # type: Dict[str, str] _renamed_i18n = { "7s": "en_7S", "de": "de_DE", @@ -28,7 +29,7 @@ _renamed_i18n = { "ptbr": "pt_BR", "ru": "ru_RU", "tr": "tr_TR" -} +} # type: Dict[str, str] class VersionUpgrade27to30(VersionUpgrade): @@ -43,7 +44,7 @@ class VersionUpgrade27to30(VersionUpgrade): # \raises ValueError The format of the version number in the file is # incorrect. # \raises KeyError The format of the file is incorrect. - def getCfgVersion(self, serialised): + def getCfgVersion(self, serialised: str) -> int: parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised. @@ -54,8 +55,8 @@ class VersionUpgrade27to30(VersionUpgrade): # # \param serialised The serialised form of a preferences file. # \param filename The name of the file to upgrade. - def upgradePreferences(self, serialised, filename): - parser = configparser.ConfigParser(interpolation=None) + def upgradePreferences(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]: + parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) # Update version numbers @@ -100,8 +101,8 @@ class VersionUpgrade27to30(VersionUpgrade): # # \param serialised The serialised form of the container file. # \param filename The name of the file to upgrade. - def upgradeQualityChangesContainer(self, serialised, filename): - parser = configparser.ConfigParser(interpolation=None) + def upgradeQualityChangesContainer(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]: + parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) # Update the skin pre-shrink settings: @@ -156,7 +157,7 @@ class VersionUpgrade27to30(VersionUpgrade): # # \param serialised The serialised form of the container file. # \param filename The name of the file to upgrade. - def upgradeOtherContainer(self, serialised, filename): + def upgradeOtherContainer(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]: parser = configparser.ConfigParser(interpolation=None) parser.read_string(serialised) @@ -185,7 +186,7 @@ class VersionUpgrade27to30(VersionUpgrade): # # \param serialised The serialised form of a container stack. # \param filename The name of the file to upgrade. - def upgradeStack(self, serialised, filename): + def upgradeStack(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]: parser = configparser.ConfigParser(interpolation=None) parser.read_string(serialised) diff --git a/plugins/VersionUpgrade/VersionUpgrade27to30/__init__.py b/plugins/VersionUpgrade/VersionUpgrade27to30/__init__.py index 4da7257b1c..bddc71a1e0 100644 --- a/plugins/VersionUpgrade/VersionUpgrade27to30/__init__.py +++ b/plugins/VersionUpgrade/VersionUpgrade27to30/__init__.py @@ -1,11 +1,16 @@ -# Copyright (c) 2017 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from typing import Any, Dict, TYPE_CHECKING + from . import VersionUpgrade27to30 +if TYPE_CHECKING: + from UM.Application import Application + upgrade = VersionUpgrade27to30.VersionUpgrade27to30() -def getMetaData(): +def getMetaData() -> Dict[str, Any]: return { "version_upgrade": { # From To Upgrade function @@ -51,5 +56,5 @@ def getMetaData(): } } -def register(app): +def register(app: "Application") -> Dict[str, Any]: return { "version_upgrade": upgrade } diff --git a/plugins/VersionUpgrade/VersionUpgrade30to31/VersionUpgrade30to31.py b/plugins/VersionUpgrade/VersionUpgrade30to31/VersionUpgrade30to31.py index 399eb18b5d..74467d67c6 100644 --- a/plugins/VersionUpgrade/VersionUpgrade30to31/VersionUpgrade30to31.py +++ b/plugins/VersionUpgrade/VersionUpgrade30to31/VersionUpgrade30to31.py @@ -1,14 +1,15 @@ -# Copyright (c) 2017 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import configparser #To parse preference files. import io #To serialise the preference files afterwards. +from typing import Dict, List, Set, Tuple from UM.VersionUpgrade import VersionUpgrade #We're inheriting from this. # a list of all legacy "Not Supported" quality profiles -_OLD_NOT_SUPPORTED_PROFILES = [ +_OLD_NOT_SUPPORTED_PROFILES = { "um2p_pp_0.25_normal", "um2p_tpu_0.8_normal", "um3_bb0.4_ABS_Fast_Print", @@ -42,7 +43,7 @@ _OLD_NOT_SUPPORTED_PROFILES = [ "um3_bb0.8_PP_Superdraft_Print", "um3_bb0.8_TPU_Fast_print", "um3_bb0.8_TPU_Superdraft_Print", -] +} # type: Set[str] # Some containers have their specific empty containers, those need to be set correctly. @@ -51,13 +52,13 @@ _EMPTY_CONTAINER_DICT = { "2": "empty_quality", "3": "empty_material", "4": "empty_variant", -} +} # type: Dict[str, str] # Renamed definition files _RENAMED_DEFINITION_DICT = { "jellybox": "imade3d_jellybox", -} +} # type: Dict[str, str] class VersionUpgrade30to31(VersionUpgrade): @@ -72,7 +73,7 @@ class VersionUpgrade30to31(VersionUpgrade): # \raises ValueError The format of the version number in the file is # incorrect. # \raises KeyError The format of the file is incorrect. - def getCfgVersion(self, serialised): + def getCfgVersion(self, serialised: str) -> int: parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised. @@ -83,7 +84,7 @@ class VersionUpgrade30to31(VersionUpgrade): # # \param serialised The serialised form of a preferences file. # \param filename The name of the file to upgrade. - def upgradePreferences(self, serialised, filename): + def upgradePreferences(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]: parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) @@ -104,7 +105,7 @@ class VersionUpgrade30to31(VersionUpgrade): # # \param serialised The serialised form of the container file. # \param filename The name of the file to upgrade. - def upgradeInstanceContainer(self, serialised, filename): + def upgradeInstanceContainer(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]: parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) @@ -129,7 +130,7 @@ class VersionUpgrade30to31(VersionUpgrade): # # \param serialised The serialised form of a container stack. # \param filename The name of the file to upgrade. - def upgradeStack(self, serialised, filename): + def upgradeStack(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]: parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) diff --git a/plugins/VersionUpgrade/VersionUpgrade30to31/__init__.py b/plugins/VersionUpgrade/VersionUpgrade30to31/__init__.py index 7b2c213a31..c5cc851d6a 100644 --- a/plugins/VersionUpgrade/VersionUpgrade30to31/__init__.py +++ b/plugins/VersionUpgrade/VersionUpgrade30to31/__init__.py @@ -1,11 +1,16 @@ -# Copyright (c) 2017 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from typing import Any, Dict, TYPE_CHECKING + from . import VersionUpgrade30to31 +if TYPE_CHECKING: + from UM.Application import Application + upgrade = VersionUpgrade30to31.VersionUpgrade30to31() -def getMetaData(): +def getMetaData() -> Dict[str, Any]: return { "version_upgrade": { # From To Upgrade function @@ -55,5 +60,5 @@ def getMetaData(): } } -def register(app): +def register(app: "Application") -> Dict[str, Any]: return { "version_upgrade": upgrade } diff --git a/plugins/VersionUpgrade/VersionUpgrade32to33/VersionUpgrade32to33.py b/plugins/VersionUpgrade/VersionUpgrade32to33/VersionUpgrade32to33.py index 18851b82c7..5be58eab3b 100644 --- a/plugins/VersionUpgrade/VersionUpgrade32to33/VersionUpgrade32to33.py +++ b/plugins/VersionUpgrade/VersionUpgrade32to33/VersionUpgrade32to33.py @@ -3,6 +3,7 @@ import configparser #To parse preference files. import io #To serialise the preference files afterwards. +from typing import Dict, List, Tuple from UM.VersionUpgrade import VersionUpgrade #We're inheriting from this. @@ -51,22 +52,22 @@ _EXTRUDER_TO_POSITION = { "ultimaker_original_dual_2nd": 1, "vertex_k8400_dual_1st": 0, "vertex_k8400_dual_2nd": 1 -} +} # type: Dict[str, int] _RENAMED_QUALITY_PROFILES = { "low": "fast", "um2_low": "um2_fast" -} +} # type: Dict[str, str] _RENAMED_QUALITY_TYPES = { "low": "fast" -} +} # type: Dict[str, str] ## 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 @@ -78,7 +79,7 @@ class VersionUpgrade32to33(VersionUpgrade): # \raises ValueError The format of the version number in the file is # incorrect. # \raises KeyError The format of the file is incorrect. - def getCfgVersion(self, serialised): + def getCfgVersion(self, serialised: str) -> int: parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised. @@ -89,7 +90,7 @@ class VersionUpgrade32to33(VersionUpgrade): # # \param serialised The serialised form of a preferences file. # \param filename The name of the file to upgrade. - def upgradePreferences(self, serialised, filename): + def upgradePreferences(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]: parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) @@ -117,7 +118,7 @@ class VersionUpgrade32to33(VersionUpgrade): # # \param serialised The serialised form of a container stack. # \param filename The name of the file to upgrade. - def upgradeStack(self, serialized, filename): + def upgradeStack(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]: parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialized) @@ -141,7 +142,7 @@ class VersionUpgrade32to33(VersionUpgrade): ## Upgrades non-quality-changes instance containers to have the new version # number. - def upgradeInstanceContainer(self, serialized, filename): + def upgradeInstanceContainer(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]: parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialized) @@ -153,7 +154,7 @@ class VersionUpgrade32to33(VersionUpgrade): return [filename], [result.getvalue()] ## Upgrades a quality changes container to the new format. - def upgradeQualityChanges(self, serialized, filename): + def upgradeQualityChanges(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]: parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialized) @@ -182,7 +183,7 @@ class VersionUpgrade32to33(VersionUpgrade): return [filename], [result.getvalue()] ## Upgrades a variant container to the new format. - def upgradeVariants(self, serialized, filename): + def upgradeVariants(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]: parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialized) diff --git a/plugins/VersionUpgrade/VersionUpgrade32to33/__init__.py b/plugins/VersionUpgrade/VersionUpgrade32to33/__init__.py index 5073be772d..006b21bc48 100644 --- a/plugins/VersionUpgrade/VersionUpgrade32to33/__init__.py +++ b/plugins/VersionUpgrade/VersionUpgrade32to33/__init__.py @@ -1,11 +1,16 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from typing import Any, Dict, TYPE_CHECKING + from . import VersionUpgrade32to33 +if TYPE_CHECKING: + from UM.Application import Application + upgrade = VersionUpgrade32to33.VersionUpgrade32to33() -def getMetaData(): +def getMetaData() -> Dict[str, Any]: return { "version_upgrade": { # From To Upgrade function @@ -51,5 +56,5 @@ def getMetaData(): } } -def register(app): +def register(app: "Application") -> Dict[str, Any]: return { "version_upgrade": upgrade } \ No newline at end of file diff --git a/plugins/VersionUpgrade/VersionUpgrade33to34/VersionUpgrade33to34.py b/plugins/VersionUpgrade/VersionUpgrade33to34/VersionUpgrade33to34.py index e2241fd195..5077dda8ad 100644 --- a/plugins/VersionUpgrade/VersionUpgrade33to34/VersionUpgrade33to34.py +++ b/plugins/VersionUpgrade/VersionUpgrade33to34/VersionUpgrade33to34.py @@ -3,17 +3,17 @@ import configparser #To parse preference files. import io #To serialise the preference files afterwards. +from typing import Dict, List, Tuple from UM.VersionUpgrade import VersionUpgrade #We're inheriting from this. _renamed_settings = { "infill_hollow": "infill_support_enabled" -} +} # type: Dict[str, str] ## Upgrades configurations from the state they were in at version 3.3 to the # state they should be in at version 3.4. class VersionUpgrade33to34(VersionUpgrade): - ## Gets the version number from a CFG file in Uranium's 3.3 format. # # Since the format may change, this is implemented for the 3.3 format only @@ -25,7 +25,7 @@ class VersionUpgrade33to34(VersionUpgrade): # \raises ValueError The format of the version number in the file is # incorrect. # \raises KeyError The format of the file is incorrect. - def getCfgVersion(self, serialised): + def getCfgVersion(self, serialised: str) -> int: parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised. @@ -34,7 +34,7 @@ class VersionUpgrade33to34(VersionUpgrade): ## Upgrades instance containers to have the new version # number. - def upgradeInstanceContainer(self, serialized, filename): + def upgradeInstanceContainer(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]: parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialized) diff --git a/plugins/VersionUpgrade/VersionUpgrade33to34/__init__.py b/plugins/VersionUpgrade/VersionUpgrade33to34/__init__.py index 1130c1e9e2..5fd757f843 100644 --- a/plugins/VersionUpgrade/VersionUpgrade33to34/__init__.py +++ b/plugins/VersionUpgrade/VersionUpgrade33to34/__init__.py @@ -1,11 +1,16 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from typing import Any, Dict, TYPE_CHECKING + from . import VersionUpgrade33to34 +if TYPE_CHECKING: + from UM.Application import Application + upgrade = VersionUpgrade33to34.VersionUpgrade33to34() -def getMetaData(): +def getMetaData() -> Dict[str, Any]: return { "version_upgrade": { # From To Upgrade function @@ -35,5 +40,5 @@ def getMetaData(): } -def register(app): +def register(app: "Application") -> Dict[str, Any]: return { "version_upgrade": upgrade } diff --git a/plugins/VersionUpgrade/VersionUpgrade34to35/VersionUpgrade34to35.py b/plugins/VersionUpgrade/VersionUpgrade34to35/VersionUpgrade34to35.py index 9d59133036..88cc3ca420 100644 --- a/plugins/VersionUpgrade/VersionUpgrade34to35/VersionUpgrade34to35.py +++ b/plugins/VersionUpgrade/VersionUpgrade34to35/VersionUpgrade34to35.py @@ -3,13 +3,14 @@ import configparser import io +from typing import Dict, List, Set, Tuple from UM.VersionUpgrade import VersionUpgrade -deleted_settings = {"prime_tower_wall_thickness", "dual_pre_wipe", "prime_tower_purge_volume"} +deleted_settings = {"prime_tower_wall_thickness", "dual_pre_wipe", "prime_tower_purge_volume"} # type: Set[str] -changed_settings = {'retraction_combing': 'noskin'} -updated_settings = {'retraction_combing': 'infill'} +changed_settings = {"retraction_combing": "noskin"} # type: Dict[str, str] +updated_settings = {"retraction_combing": "infill"} # type: Dict[str, str] _RENAMED_MATERIAL_PROFILES = { "dsm_arnitel2045_175_cartesio_0.25_mm": "dsm_arnitel2045_175_cartesio_0.25mm_thermoplastic_extruder", @@ -57,12 +58,11 @@ _RENAMED_MATERIAL_PROFILES = { "ultimaker_pva_cartesio_0.25_mm": "ultimaker_pva_cartesio_0.25mm_thermoplastic_extruder", "ultimaker_pva_cartesio_0.4_mm": "ultimaker_pva_cartesio_0.4mm_thermoplastic_extruder", "ultimaker_pva_cartesio_0.8_mm": "ultimaker_pva_cartesio_0.8mm_thermoplastic_extruder" -} +} # type: Dict[str, str] ## Upgrades configurations from the state they were in at version 3.4 to the # state they should be in at version 3.5. class VersionUpgrade34to35(VersionUpgrade): - ## Gets the version number from a CFG file in Uranium's 3.3 format. # # Since the format may change, this is implemented for the 3.3 format only @@ -74,7 +74,7 @@ class VersionUpgrade34to35(VersionUpgrade): # \raises ValueError The format of the version number in the file is # incorrect. # \raises KeyError The format of the file is incorrect. - def getCfgVersion(self, serialised): + def getCfgVersion(self, serialised: str) -> int: parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised. @@ -82,7 +82,7 @@ class VersionUpgrade34to35(VersionUpgrade): return format_version * 1000000 + setting_version ## Upgrades Preferences to have the new version number. - def upgradePreferences(self, serialized, filename): + def upgradePreferences(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]: parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialized) @@ -103,7 +103,7 @@ class VersionUpgrade34to35(VersionUpgrade): return [filename], [result.getvalue()] ## Upgrades stacks to have the new version number. - def upgradeStack(self, serialized, filename): + def upgradeStack(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]: parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialized) @@ -121,7 +121,7 @@ class VersionUpgrade34to35(VersionUpgrade): ## Upgrades instance containers to have the new version # number. - def upgradeInstanceContainer(self, serialized, filename): + def upgradeInstanceContainer(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]: parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialized) @@ -147,7 +147,7 @@ class VersionUpgrade34to35(VersionUpgrade): parser.write(result) return [filename], [result.getvalue()] - def _resetConcentric3DInfillPattern(self, parser): + def _resetConcentric3DInfillPattern(self, parser: configparser.ConfigParser) -> None: if "values" not in parser: return @@ -161,5 +161,4 @@ class VersionUpgrade34to35(VersionUpgrade): if key not in parser["values"]: continue if parser["values"][key] == "concentric_3d": - del parser["values"][key] - + del parser["values"][key] \ No newline at end of file diff --git a/plugins/VersionUpgrade/VersionUpgrade34to35/__init__.py b/plugins/VersionUpgrade/VersionUpgrade34to35/__init__.py index 2ea74f6194..332bc827b9 100644 --- a/plugins/VersionUpgrade/VersionUpgrade34to35/__init__.py +++ b/plugins/VersionUpgrade/VersionUpgrade34to35/__init__.py @@ -1,11 +1,16 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from typing import Any, Dict, TYPE_CHECKING + from . import VersionUpgrade34to35 +if TYPE_CHECKING: + from UM.Application import Application + upgrade = VersionUpgrade34to35.VersionUpgrade34to35() -def getMetaData(): +def getMetaData() -> Dict[str, Any]: return { "version_upgrade": { # From To Upgrade function @@ -52,5 +57,5 @@ def getMetaData(): } -def register(app): +def register(app: "Application") -> Dict[str, Any]: return { "version_upgrade": upgrade } From b67d8d410343490b96357def995adb3a8f0d198f Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 14 Nov 2018 13:46:13 +0100 Subject: [PATCH 0186/1240] Fix type of fallback variable These have to be strings because the configparser getter can only return strings. Contributes to issue CURA-5936. --- .../VersionUpgrade21to22/VersionUpgrade21to22.py | 2 +- plugins/VersionUpgrade/VersionUpgrade22to24/VersionUpgrade.py | 2 +- .../VersionUpgrade25to26/VersionUpgrade25to26.py | 2 +- .../VersionUpgrade26to27/VersionUpgrade26to27.py | 4 ++-- .../VersionUpgrade27to30/VersionUpgrade27to30.py | 4 ++-- .../VersionUpgrade30to31/VersionUpgrade30to31.py | 2 +- .../VersionUpgrade32to33/VersionUpgrade32to33.py | 2 +- .../VersionUpgrade33to34/VersionUpgrade33to34.py | 2 +- .../VersionUpgrade34to35/VersionUpgrade34to35.py | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py index 89c847e606..536385b19d 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/VersionUpgrade21to22.py @@ -251,7 +251,7 @@ class VersionUpgrade21to22(VersionUpgrade): parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised. - setting_version = int(parser.get("metadata", "setting_version", fallback = 0)) + setting_version = int(parser.get("metadata", "setting_version", fallback = "0")) return format_version * 1000000 + setting_version ## Gets the fallback quality to use for a specific machine-variant-material diff --git a/plugins/VersionUpgrade/VersionUpgrade22to24/VersionUpgrade.py b/plugins/VersionUpgrade/VersionUpgrade22to24/VersionUpgrade.py index 48af365877..dac73683bb 100644 --- a/plugins/VersionUpgrade/VersionUpgrade22to24/VersionUpgrade.py +++ b/plugins/VersionUpgrade/VersionUpgrade22to24/VersionUpgrade.py @@ -156,5 +156,5 @@ class VersionUpgrade22to24(VersionUpgrade): parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised. - setting_version = int(parser.get("metadata", "setting_version", fallback = 0)) + setting_version = int(parser.get("metadata", "setting_version", fallback = "0")) return format_version * 1000000 + setting_version diff --git a/plugins/VersionUpgrade/VersionUpgrade25to26/VersionUpgrade25to26.py b/plugins/VersionUpgrade/VersionUpgrade25to26/VersionUpgrade25to26.py index 5c9b94cca3..6dbcfebc46 100644 --- a/plugins/VersionUpgrade/VersionUpgrade25to26/VersionUpgrade25to26.py +++ b/plugins/VersionUpgrade/VersionUpgrade25to26/VersionUpgrade25to26.py @@ -43,7 +43,7 @@ class VersionUpgrade25to26(VersionUpgrade): parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised. - setting_version = int(parser.get("metadata", "setting_version", fallback = 0)) + setting_version = int(parser.get("metadata", "setting_version", fallback = "0")) return format_version * 1000000 + setting_version ## Upgrades the preferences file from version 2.5 to 2.6. diff --git a/plugins/VersionUpgrade/VersionUpgrade26to27/VersionUpgrade26to27.py b/plugins/VersionUpgrade/VersionUpgrade26to27/VersionUpgrade26to27.py index 96307ca43c..39e3dea4ed 100644 --- a/plugins/VersionUpgrade/VersionUpgrade26to27/VersionUpgrade26to27.py +++ b/plugins/VersionUpgrade/VersionUpgrade26to27/VersionUpgrade26to27.py @@ -84,7 +84,7 @@ class VersionUpgrade26to27(VersionUpgrade): parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised. - setting_version = int(parser.get("metadata", "setting_version", fallback = 0)) + setting_version = int(parser.get("metadata", "setting_version", fallback = "0")) return format_version * 1000000 + setting_version ## Upgrades a preferences file from version 2.6 to 2.7. @@ -92,7 +92,7 @@ class VersionUpgrade26to27(VersionUpgrade): # \param serialised The serialised form of a preferences file. # \param filename The name of the file to upgrade. def upgradePreferences(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]: - parser = configparser.ConfigParser(interpolation=None) + parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) # Update version numbers diff --git a/plugins/VersionUpgrade/VersionUpgrade27to30/VersionUpgrade27to30.py b/plugins/VersionUpgrade/VersionUpgrade27to30/VersionUpgrade27to30.py index c9e9e41f7e..b594c3c6c4 100644 --- a/plugins/VersionUpgrade/VersionUpgrade27to30/VersionUpgrade27to30.py +++ b/plugins/VersionUpgrade/VersionUpgrade27to30/VersionUpgrade27to30.py @@ -48,7 +48,7 @@ class VersionUpgrade27to30(VersionUpgrade): parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised. - setting_version = int(parser.get("metadata", "setting_version", fallback = 0)) + setting_version = int(parser.get("metadata", "setting_version", fallback = "0")) return format_version * 1000000 + setting_version ## Upgrades a preferences file from version 2.7 to 3.0. @@ -158,7 +158,7 @@ class VersionUpgrade27to30(VersionUpgrade): # \param serialised The serialised form of the container file. # \param filename The name of the file to upgrade. def upgradeOtherContainer(self, serialised: str, filename: str) -> Tuple[List[str], List[str]]: - parser = configparser.ConfigParser(interpolation=None) + parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) # Update the skin pre-shrink settings: diff --git a/plugins/VersionUpgrade/VersionUpgrade30to31/VersionUpgrade30to31.py b/plugins/VersionUpgrade/VersionUpgrade30to31/VersionUpgrade30to31.py index 74467d67c6..f0b2e939b9 100644 --- a/plugins/VersionUpgrade/VersionUpgrade30to31/VersionUpgrade30to31.py +++ b/plugins/VersionUpgrade/VersionUpgrade30to31/VersionUpgrade30to31.py @@ -77,7 +77,7 @@ class VersionUpgrade30to31(VersionUpgrade): parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised. - setting_version = int(parser.get("metadata", "setting_version", fallback = 0)) + setting_version = int(parser.get("metadata", "setting_version", fallback = "0")) return format_version * 1000000 + setting_version ## Upgrades a preferences file from version 3.0 to 3.1. diff --git a/plugins/VersionUpgrade/VersionUpgrade32to33/VersionUpgrade32to33.py b/plugins/VersionUpgrade/VersionUpgrade32to33/VersionUpgrade32to33.py index 5be58eab3b..83cb15c864 100644 --- a/plugins/VersionUpgrade/VersionUpgrade32to33/VersionUpgrade32to33.py +++ b/plugins/VersionUpgrade/VersionUpgrade32to33/VersionUpgrade32to33.py @@ -83,7 +83,7 @@ class VersionUpgrade32to33(VersionUpgrade): parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised. - setting_version = int(parser.get("metadata", "setting_version", fallback = 0)) + setting_version = int(parser.get("metadata", "setting_version", fallback = "0")) return format_version * 1000000 + setting_version ## Upgrades a preferences file from version 3.2 to 3.3. diff --git a/plugins/VersionUpgrade/VersionUpgrade33to34/VersionUpgrade33to34.py b/plugins/VersionUpgrade/VersionUpgrade33to34/VersionUpgrade33to34.py index 5077dda8ad..704ede02d6 100644 --- a/plugins/VersionUpgrade/VersionUpgrade33to34/VersionUpgrade33to34.py +++ b/plugins/VersionUpgrade/VersionUpgrade33to34/VersionUpgrade33to34.py @@ -29,7 +29,7 @@ class VersionUpgrade33to34(VersionUpgrade): parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised. - setting_version = int(parser.get("metadata", "setting_version", fallback = 0)) + setting_version = int(parser.get("metadata", "setting_version", fallback = "0")) return format_version * 1000000 + setting_version ## Upgrades instance containers to have the new version diff --git a/plugins/VersionUpgrade/VersionUpgrade34to35/VersionUpgrade34to35.py b/plugins/VersionUpgrade/VersionUpgrade34to35/VersionUpgrade34to35.py index 88cc3ca420..d930b6e217 100644 --- a/plugins/VersionUpgrade/VersionUpgrade34to35/VersionUpgrade34to35.py +++ b/plugins/VersionUpgrade/VersionUpgrade34to35/VersionUpgrade34to35.py @@ -78,7 +78,7 @@ class VersionUpgrade34to35(VersionUpgrade): parser = configparser.ConfigParser(interpolation = None) parser.read_string(serialised) format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised. - setting_version = int(parser.get("metadata", "setting_version", fallback = 0)) + setting_version = int(parser.get("metadata", "setting_version", fallback = "0")) return format_version * 1000000 + setting_version ## Upgrades Preferences to have the new version number. From b589920f53a7192bd620e55e7d4ec87d22bbde7d Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 14 Nov 2018 13:55:39 +0100 Subject: [PATCH 0187/1240] Move a few hardcoded sizes to the theme CURA-5785 --- plugins/PrepareStage/PrepareMenu.qml | 9 ++++----- resources/themes/cura-light/theme.json | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index 78f9ced1e7..9507d786b2 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -53,9 +53,8 @@ Item { id: machineSelection z: openFileButtonBackground.z - 1 - - Layout.minimumWidth: 240 - Layout.maximumWidth: 240 + Layout.minimumWidth: UM.Theme.getSize("machine_selector_widget").width + Layout.maximumWidth: UM.Theme.getSize("machine_selector_widget").width Layout.fillWidth: true Layout.fillHeight: true } @@ -90,8 +89,8 @@ Item onShowTooltip: prepareMenu.showTooltip(item, location, text) onHideTooltip: prepareMenu.hideTooltip() - Layout.minimumWidth: 460 - Layout.maximumWidth: 460 + Layout.minimumWidth: UM.Theme.getSize("print_setup_widget").width + Layout.maximumWidth: UM.Theme.getSize("print_setup_widget").width Layout.fillWidth: true Layout.fillHeight: true } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index a33ff87042..a36940641e 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -374,7 +374,7 @@ "account_button": [12, 3], - "print_setup_widget": [35.0, 42.0], + "print_setup_widget": [30.0, 42.0], "print_setup_mode_toggle": [0.0, 2.0], "print_setup_item": [0.0, 2.0], "print_setup_extruder_box": [0.0, 6.0], @@ -386,7 +386,7 @@ "action_panel_information_widget": [20.0, 0.0], "action_panel_button": [15.0, 3.0], - "machine_selector_widget": [28.0, 4.5], + "machine_selector_widget": [16.0, 4.5], "views_selector": [0.0, 4.0], From 8ec7d6dba3b79dc88c9496185c6e49000740018a Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 14 Nov 2018 13:56:46 +0100 Subject: [PATCH 0188/1240] Fix type issues in old version upgrade plug-ins The one actual change was this: To give a KeyError when stuff can't be found in a dictionary, rather than returning None there and then getting a TypeError later. Contributes to issue CURA-5936. --- .../VersionUpgrade21to22/MachineInstance.py | 10 +++++----- .../VersionUpgrade21to22/Preferences.py | 8 ++++---- .../VersionUpgrade22to24/VersionUpgrade.py | 12 ++++++------ 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py index a947114595..478f955e49 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -4,7 +4,7 @@ import configparser #To read config files. import io #To write config files to strings as if they were files. import os.path #To get the path to write new user profiles to. -from typing import List, Optional, Tuple +from typing import Dict, List, Optional, Set, Tuple import urllib #To serialise the user container file name properly. import UM.VersionUpgrade #To indicate that a file is of incorrect format. @@ -33,7 +33,7 @@ class MachineInstance: # \param serialised A string with the contents of a machine instance file, # without extension. # \param filename The supposed file name of this machine instance. - def __init__(self, serialised: str, filename: str) -> str: + def __init__(self, serialised: str, filename: str) -> None: self._filename = filename config = configparser.ConfigParser(interpolation = None) @@ -54,11 +54,11 @@ class MachineInstance: self._type_name = config.get("general", "type") self._variant_name = config.get("general", "variant", fallback = "empty_variant") self._name = config.get("general", "name", fallback = "") - self._key = config.get("general", "key", fallback = None) + self._key = config.get("general", "key", fallback = "") self._active_profile_name = config.get("general", "active_profile", fallback = "empty_quality") self._active_material_name = config.get("general", "material", fallback = "empty_material") - self._machine_setting_overrides = {} + self._machine_setting_overrides = {} # type: Dict[str, str] for key, value in config["machine_settings"].items(): self._machine_setting_overrides[key] = value @@ -109,7 +109,7 @@ class MachineInstance: version_upgrade_manager = UM.VersionUpgradeManager.VersionUpgradeManager.getInstance() user_version_to_paths_dict = version_upgrade_manager.getStoragePaths("user") - paths_set = set() + paths_set = set() # type: Set[str] for paths in user_version_to_paths_dict.values(): paths_set |= paths diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py b/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py index 51e4b617e8..953837b863 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/Preferences.py @@ -59,11 +59,11 @@ class Preferences: #Translate the setting names in the visible settings. if self._config.has_section("machines") and self._config.has_option("machines", "setting_visibility"): visible_settings = self._config.get("machines", "setting_visibility") - visible_settings = visible_settings.split(",") + visible_settings_list = visible_settings.split(",") import VersionUpgrade21to22 #Import here to prevent a circular dependency. - visible_settings = [VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettingName(setting_name) - for setting_name in visible_settings] - visible_settings = ",".join(visible_settings) + visible_settings_list = [VersionUpgrade21to22.VersionUpgrade21to22.VersionUpgrade21to22.translateSettingName(setting_name) + for setting_name in visible_settings_list] + visible_settings = ",".join(visible_settings_list) self._config.set("machines", "setting_visibility", value = visible_settings) #Translate the active_instance key. diff --git a/plugins/VersionUpgrade/VersionUpgrade22to24/VersionUpgrade.py b/plugins/VersionUpgrade/VersionUpgrade22to24/VersionUpgrade.py index dac73683bb..ded892d137 100644 --- a/plugins/VersionUpgrade/VersionUpgrade22to24/VersionUpgrade.py +++ b/plugins/VersionUpgrade/VersionUpgrade22to24/VersionUpgrade.py @@ -22,11 +22,11 @@ class VersionUpgrade22to24(VersionUpgrade): config.read_string(serialised) # Read the input string as config file. if config.get("metadata", "type") == "definition_changes": # This is not a container stack, don't upgrade it here - return + return None config.set("general", "version", "3") - container_list = [] + container_list = [] # type: List[str] if config.has_section("containers"): for index, container_id in config.items("containers"): container_list.append(container_id) @@ -37,14 +37,14 @@ class VersionUpgrade22to24(VersionUpgrade): user_variants = self.__getUserVariants() name_path_dict = {} for variant in user_variants: - name_path_dict[variant.get("name")] = variant.get("path") + name_path_dict[variant["name"]] = variant["path"] user_variant_names = set(container_list).intersection(name_path_dict.keys()) if len(user_variant_names): # One of the user defined variants appears in the list of containers in the stack. for variant_name in user_variant_names: # really there should just be one variant to convert. - config_name = self.__convertVariant(name_path_dict.get(variant_name)) + config_name = self.__convertVariant(name_path_dict[variant_name]) # Change the name of variant and insert empty_variant into the stack. new_container_list = [] @@ -64,8 +64,8 @@ class VersionUpgrade22to24(VersionUpgrade): config.remove_option("general", "containers") - for index in range(len(container_list)): - config.set("containers", str(index), container_list[index]) + for idx in range(len(container_list)): + config.set("containers", str(idx), container_list[idx]) output = io.StringIO() config.write(output) From 7ca3490097027f9cb4342a52007ee558897d8c3d Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 14 Nov 2018 14:00:19 +0100 Subject: [PATCH 0189/1240] Make the collapse arrows consistent with the rest of the UI CURA-5785 --- resources/qml/ExpandableComponent.qml | 2 +- resources/qml/MachineSelector.qml | 2 +- .../qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index c525d163fe..bfafa9b29e 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -109,7 +109,7 @@ Item y: headerItemLoader.height + 2 * background.padding // Make the popup right aligned with the rest. The 3x padding is due to left, right and padding between - //the button & text. + // the button & text. x: -width + collapseButton.width + headerItemLoader.width + 3 * background.padding padding: UM.Theme.getSize("default_margin").width closePolicy: Popup.CloseOnPressOutsideParent diff --git a/resources/qml/MachineSelector.qml b/resources/qml/MachineSelector.qml index 87a3154338..6a33cf3a0e 100644 --- a/resources/qml/MachineSelector.qml +++ b/resources/qml/MachineSelector.qml @@ -17,7 +17,7 @@ Cura.ExpandableComponent property bool isNetworkPrinter: Cura.MachineManager.activeMachineNetworkKey != "" - iconSource: expanded ? UM.Theme.getIcon("arrow_left") : UM.Theme.getIcon("arrow_bottom") + iconSource: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") UM.I18nCatalog { diff --git a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml index 5565884bd8..b2213918dd 100644 --- a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml +++ b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml @@ -27,7 +27,7 @@ Cura.ExpandableComponent name: "cura" } - iconSource: expanded ? UM.Theme.getIcon("arrow_left") : UM.Theme.getIcon("arrow_bottom") + iconSource: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") headerItem: Item { From 429a49aa2eddc2c42c0f592181f6f77e880954c7 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 14 Nov 2018 14:06:55 +0100 Subject: [PATCH 0190/1240] Make the default size of the expandableComponent icon depend on the height of the element This is a way more sensible default. CURA-5785 --- resources/qml/ExpandableComponent.qml | 7 ++++--- .../Menus/ConfigurationMenu/QuickConfigurationSelector.qml | 1 - 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index bfafa9b29e..c098443cd7 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -9,6 +9,7 @@ import UM 1.2 as UM // * The Icon; An icon that is displayed on the right of the drawer. Item { + id: base // The headerItem holds the QML item that is always displayed. property alias headerItem: headerItemLoader.sourceComponent @@ -33,7 +34,7 @@ Item property alias iconColor: collapseButton.color // The icon size (it's always drawn as a square) - property alias iconSize: collapseButton.width + property alias iconSize: collapseButton.height // Is the "drawer" open? readonly property alias expanded: popup.visible @@ -89,8 +90,8 @@ Item sourceSize.width: width sourceSize.height: height visible: source != "" - width: UM.Theme.getSize("section_icon").width - height: width + width: height + height: 0.2 * base.height color: "black" } diff --git a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml index b2213918dd..e77f949661 100644 --- a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml +++ b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml @@ -28,7 +28,6 @@ Cura.ExpandableComponent } iconSource: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") - headerItem: Item { // Horizontal list that shows the extruders From 66c3cc9204fdc309c993ec4b40983cdd150dc00a Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Wed, 14 Nov 2018 14:20:42 +0100 Subject: [PATCH 0191/1240] Prevent an error during start up --- cura/BuildVolume.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index 547c3dae71..1589f16afc 100755 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -489,7 +489,9 @@ class BuildVolume(SceneNode): def _updateRaftThickness(self): old_raft_thickness = self._raft_thickness - self._adhesion_type = self._global_container_stack.getProperty("adhesion_type", "value") + if self._global_container_stack.extruders: + # This might be called before the extruder stacks have initialised, in which case getting the adhesion_type fails + self._adhesion_type = self._global_container_stack.getProperty("adhesion_type", "value") self._raft_thickness = 0.0 if self._adhesion_type == "raft": self._raft_thickness = ( From b671a3153a0f7f7ab9745f73c300d34c6bf7a06b Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Wed, 14 Nov 2018 14:21:39 +0100 Subject: [PATCH 0192/1240] Catch an error getting an extruder value before extruders are added to the global stack --- cura/Settings/CuraFormulaFunctions.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cura/Settings/CuraFormulaFunctions.py b/cura/Settings/CuraFormulaFunctions.py index 1db01857f8..9ef80bd3d4 100644 --- a/cura/Settings/CuraFormulaFunctions.py +++ b/cura/Settings/CuraFormulaFunctions.py @@ -5,6 +5,7 @@ from typing import Any, List, Optional, TYPE_CHECKING from UM.Settings.PropertyEvaluationContext import PropertyEvaluationContext from UM.Settings.SettingFunction import SettingFunction +from UM.Logger import Logger if TYPE_CHECKING: from cura.CuraApplication import CuraApplication @@ -38,7 +39,11 @@ class CuraFormulaFunctions: extruder_position = int(machine_manager.defaultExtruderPosition) global_stack = machine_manager.activeMachine - extruder_stack = global_stack.extruders[str(extruder_position)] + try: + extruder_stack = global_stack.extruders[str(extruder_position)] + except KeyError: + Logger.log("w", "Value for %s of extruder %s was requested, but that extruder is not available" % (property_key, extruder_position)) + return None value = extruder_stack.getRawProperty(property_key, "value", context = context) if isinstance(value, SettingFunction): From c27f51d944b3dc84946f5f17a884723afbfd1a8f Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 14 Nov 2018 16:44:36 +0100 Subject: [PATCH 0193/1240] Make the view selector also use the expandableComponent CURA-5785 --- plugins/PreviewStage/PreviewMenu.qml | 91 +++++++++++++++------------- resources/qml/MachineSelector.qml | 1 + 2 files changed, 51 insertions(+), 41 deletions(-) diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index 5b0ae4e9c5..bb8b992471 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -3,7 +3,7 @@ import QtQuick 2.7 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.4 import UM 1.3 as UM import Cura 1.1 as Cura @@ -43,53 +43,62 @@ Item //spacing: UM.Theme.getSize("default_margin").width height: parent.height - Item + Cura.ExpandableComponent { - width: viewModeButton.width + 2 * UM.Theme.getSize("default_margin").width + id: viewSelector + iconSource: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") height: parent.height - ComboBox + + property var viewModel: UM.ViewModel { } + + property var activeView: { - // This item contains the views selector, a combobox that is dynamically created from - // the list of available Views (packages that create different visualizations of the - // scene). - id: viewModeButton - - style: UM.Theme.styles.combobox - anchors.centerIn: parent - model: UM.ViewModel { } - textRole: "name" - - // update the model's active index - function updateItemActiveFlags() + for (var i = 0; i < viewModel.rowCount(); i++) { - currentIndex = getActiveIndex() - for (var i = 0; i < model.rowCount(); i++) + if(viewModel.getItem(i).active) { - model.getItem(i).active = (i == currentIndex) + return viewModel.getItem(i) + } + } + // Nothing was active, so just return the first one (the list is sorted by priority, so the most + // important one sshould be returned) + return viewModel.getItem(0) + } + + // Ensure that the controller is synced with whatever happend here. + onActiveViewChanged: UM.Controller.setActiveView(activeView.id) + + headerItem: Label + { + text: viewSelector.activeView.name + verticalAlignment: Text.AlignVCenter + height: parent.height + elide: Text.ElideRight + font: UM.Theme.getFont("default") + } + + popupItem: Column + { + id: column + width: viewSelector.width - 2 * UM.Theme.getSize("default_margin").width + + // For some reason the height of the column gets set to 0 if this is not set... + Component.onCompleted: height = implicitHeight + + Repeater + { + id: networkedPrinters + model: viewSelector.viewModel + RoundButton + { + text: name + radius: UM.Theme.getSize("default_radius").width + checkable: true + checked: active + onClicked: UM.Controller.setActiveView(id) } } - // get the index of the active model item on start - function getActiveIndex() - { - for (var i = 0; i < model.rowCount(); i++) - { - if (model.getItem(i).active) - { - return i; - } - } - return 0 - } - - onCurrentIndexChanged: - { - if (model.getItem(currentIndex).id != undefined) - { - UM.Controller.setActiveView(model.getItem(currentIndex).id) - } - } - currentIndex: getActiveIndex() } } @@ -106,7 +115,7 @@ Item // TODO: Make this panel collapsable and ensure it has a standardised background. id: viewPanel - property var buttonTarget: Qt.point(viewModeButton.x + Math.round(viewModeButton.width / 2), viewModeButton.y + Math.round(viewModeButton.height / 2)) + //property var buttonTarget: Qt.point(viewModeButton.x + Math.round(viewModeButton.width / 2), viewModeButton.y + Math.round(viewModeButton.height / 2)) height: parent.height width: childrenRect.width diff --git a/resources/qml/MachineSelector.qml b/resources/qml/MachineSelector.qml index 6a33cf3a0e..b2a34e186e 100644 --- a/resources/qml/MachineSelector.qml +++ b/resources/qml/MachineSelector.qml @@ -31,6 +31,7 @@ Cura.ExpandableComponent verticalAlignment: Text.AlignVCenter height: parent.height elide: Text.ElideRight + font: UM.Theme.getFont("default") } popupItem: Item From 9aa5b6fe3f467d50796a80a43f95770e6bb080cf Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 14 Nov 2018 16:47:53 +0100 Subject: [PATCH 0194/1240] Hide one spacer if there is no viewPanel CURA-5785 --- plugins/PreviewStage/PreviewMenu.qml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index bb8b992471..e24d0b25b1 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -106,7 +106,10 @@ Item Rectangle { height: parent.height - width: UM.Theme.getSize("default_lining").width + // If there is no viewPanel, we only need a single spacer, so hide this one. + visible: viewPanel.source != "" + width: visible ? UM.Theme.getSize("default_lining").width : 0 + color: UM.Theme.getColor("lining") } From 20b7a925a0f9525ed6e0f1ff2cb7703b739def74 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 14 Nov 2018 16:49:01 +0100 Subject: [PATCH 0195/1240] Remove unused code & outdated todo comment CURA-5785 --- plugins/PreviewStage/PreviewMenu.qml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index e24d0b25b1..df04507ba4 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -115,14 +115,9 @@ Item Loader { - // TODO: Make this panel collapsable and ensure it has a standardised background. id: viewPanel - - //property var buttonTarget: Qt.point(viewModeButton.x + Math.round(viewModeButton.width / 2), viewModeButton.y + Math.round(viewModeButton.height / 2)) - height: parent.height width: childrenRect.width - source: UM.Controller.activeView != null && UM.Controller.activeView.stageMenuComponent != null ? UM.Controller.activeView.stageMenuComponent : "" } From 21d7619a4541b122bc480630dad18e42cfa85e6d Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 14 Nov 2018 16:57:05 +0100 Subject: [PATCH 0196/1240] Added a hover effect to the expandableComponent CURA-5785 --- resources/qml/ExpandableComponent.qml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index c098443cd7..d885253173 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -19,7 +19,8 @@ Item // The background color of the popup property color popupBackgroundColor: "white" - property alias headerBackgroundColor: background.color + property color headerBackgroundColor: "white" + property color headerHoverColor: "white" // How much spacing is needed around the popupItem property alias popupPadding: popup.padding @@ -63,7 +64,7 @@ Item id: background property real padding: UM.Theme.getSize("default_margin").width - color: "white" + color: headerBackgroundColor anchors.fill: parent Loader { @@ -97,8 +98,12 @@ Item MouseArea { + id: mouseArea anchors.fill: parent onClicked: popup.visible ? popup.close() : popup.open() + hoverEnabled: true + onEntered: background.color = headerHoverColor + onExited: background.color = headerBackgroundColor } } From c08aa2205ca502580b4ba613cd9266601bfd5898 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 15 Nov 2018 09:37:54 +0100 Subject: [PATCH 0197/1240] Fix enabled checkbox for extruder selection CURA-5785 --- .../Menus/ConfigurationMenu/QuickConfigurationSelector.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml index e77f949661..6f4e58a326 100644 --- a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml +++ b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml @@ -150,8 +150,8 @@ Cura.ExpandableComponent OldControls.CheckBox { - checked: Cura.MachineManager.getExtruder(parent.model.index).isEnabled - onClicked: Cura.MachineManager.setExtruderEnabled(parent.model.index, checked) + checked: Cura.MachineManager.getExtruder(tabControl.model.index).isEnabled + onClicked: Cura.MachineManager.setExtruderEnabled(tabControl.model.index, checked) height: UM.Theme.getSize("setting_control").height style: UM.Theme.styles.checkbox } From f0b8c1e6117fc0ddb9815b09d88ea630ecc448ca Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 15 Nov 2018 09:45:46 +0100 Subject: [PATCH 0198/1240] Fix some minor display issues CURA-5785 --- resources/qml/PrintSetupSelector.qml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/resources/qml/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector.qml index 7a7d438bc3..e576dddc07 100644 --- a/resources/qml/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector.qml @@ -111,19 +111,22 @@ Cura.ExpandableComponent popupItem: Item { - height: settingsModeSelection.height + sidebarContents.height + height: settingsModeSelection.height + sidebarContents.height + 2 * UM.Theme.getSize("default_margin").height width: UM.Theme.getSize("print_setup_widget").width ListView { // Settings mode selection toggle id: settingsModeSelection model: modesListModel - width: Math.round(parent.width * 0.55) height: UM.Theme.getSize("print_setup_mode_toggle").height visible: !hideSettings - anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("thick_margin").width + anchors + { + right: parent.right + left: parent.left + margins: UM.Theme.getSize("thick_margin").width + } ButtonGroup { From eca9820fc4a91abd8bcc87562df1ed5289edcf42 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 15 Nov 2018 13:55:33 +0100 Subject: [PATCH 0199/1240] Add toggle function to expandable popup Also ensures that the machineSelector closes the popup on making a choice CURA-5785 --- resources/qml/ExpandableComponent.qml | 15 ++++++++++++++- resources/qml/MachineSelector.qml | 13 +++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index d885253173..129c7dc923 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -40,6 +40,19 @@ Item // Is the "drawer" open? readonly property alias expanded: popup.visible + function togglePopup() + { + if(popup.visible) + { + popup.close() + } + else + { + popup.open() + } + } + + onPopupItemChanged: { // Since we want the size of the popup to be set by the size of the content, @@ -100,7 +113,7 @@ Item { id: mouseArea anchors.fill: parent - onClicked: popup.visible ? popup.close() : popup.open() + onClicked: togglePopup() hoverEnabled: true onEntered: background.color = headerHoverColor onExited: background.color = headerBackgroundColor diff --git a/resources/qml/MachineSelector.qml b/resources/qml/MachineSelector.qml index b2a34e186e..b18f427980 100644 --- a/resources/qml/MachineSelector.qml +++ b/resources/qml/MachineSelector.qml @@ -77,8 +77,12 @@ Cura.ExpandableComponent width: parent.width checkable: true - onClicked: Cura.MachineManager.setActiveMachine(model.id) radius: UM.Theme.getSize("default_radius").width + onClicked: + { + togglePopup() + Cura.MachineManager.setActiveMachine(model.id) + } Connections { @@ -114,8 +118,13 @@ Cura.ExpandableComponent width: parent.width checked: Cura.MachineManager.activeMachineId == model.id checkable: true - onClicked: Cura.MachineManager.setActiveMachine(model.id) + radius: UM.Theme.getSize("default_radius").width + onClicked: + { + togglePopup() + Cura.MachineManager.setActiveMachine(model.id) + } } } From 3629e3b6046a0646c951c5d914f7797f8e968165 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 15 Nov 2018 14:04:52 +0100 Subject: [PATCH 0200/1240] Added highlight to ExpandableComponent CURA-5785 --- resources/qml/ExpandableComponent.qml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 129c7dc923..e110ed94be 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -40,6 +40,8 @@ Item // Is the "drawer" open? readonly property alias expanded: popup.visible + property alias expandedHighlightColor: expandedHightlight.color + function togglePopup() { if(popup.visible) @@ -52,7 +54,6 @@ Item } } - onPopupItemChanged: { // Since we want the size of the popup to be set by the size of the content, @@ -79,6 +80,7 @@ Item color: headerBackgroundColor anchors.fill: parent + Loader { id: headerItemLoader @@ -92,6 +94,17 @@ Item } } + // A highlight that is shown when the popup is expanded + Rectangle + { + id: expandedHightlight + width: parent.width + height: UM.Theme.getSize("thick_lining").height + color: UM.Theme.getColor("primary") + visible: expanded + anchors.bottom: parent.bottom + } + UM.RecolorImage { id: collapseButton From 6dd6f5ecc7ec6be44cd293b27925ad66771d18fa Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 15 Nov 2018 14:11:46 +0100 Subject: [PATCH 0201/1240] Made the hardcoded values of ExpandableComponent themable CURA-5785 --- resources/qml/ExpandableComponent.qml | 6 +++--- resources/themes/cura-light/theme.json | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index e110ed94be..9464a8f0ac 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -17,10 +17,10 @@ Item property var popupItem // The background color of the popup - property color popupBackgroundColor: "white" + property color popupBackgroundColor: UM.Theme.getColor("action_button") - property color headerBackgroundColor: "white" - property color headerHoverColor: "white" + property color headerBackgroundColor: UM.Theme.getColor("action_button") + property color headerHoverColor: UM.Theme.getColor("action_button_hovered") // How much spacing is needed around the popupItem property alias popupPadding: popup.padding diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index a36940641e..d28611529b 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -75,6 +75,7 @@ "lining": [192, 193, 194, 255], "viewport_overlay": [0, 0, 0, 192], + "primary": [50, 130, 255, 255], "primary_hover": [48, 182, 231, 255], "primary_text": [255, 255, 255, 255], @@ -157,7 +158,7 @@ "action_button": [255, 255, 255, 255], "action_button_text": [0, 0, 0, 255], "action_button_border": [127, 127, 127, 255], - "action_button_hovered": [255, 255, 255, 255], + "action_button_hovered": [232, 242, 252, 255], "action_button_hovered_text": [31, 36, 39, 255], "action_button_hovered_border": [50, 130, 255, 255], "action_button_active": [255, 255, 255, 255], From 240ac1f06b6458d154d84ce326a3dd8e16777aa1 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 16 Nov 2018 13:03:45 +0100 Subject: [PATCH 0202/1240] Clarify some setting descriptions --- resources/definitions/fdmextruder.def.json | 2 +- resources/definitions/fdmprinter.def.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/definitions/fdmextruder.def.json b/resources/definitions/fdmextruder.def.json index 19c9e92d18..55556f5764 100644 --- a/resources/definitions/fdmextruder.def.json +++ b/resources/definitions/fdmextruder.def.json @@ -78,7 +78,7 @@ "machine_extruder_start_code": { "label": "Extruder Start G-Code", - "description": "Start g-code to execute whenever turning the extruder on.", + "description": "Start g-code to execute whenever this extruder is switched to.", "type": "str", "default_value": "", "settable_per_mesh": false, diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 7cb6720f27..b1812ae705 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -2406,7 +2406,7 @@ "switch_extruder_retraction_amount": { "label": "Nozzle Switch Retraction Distance", - "description": "The amount of retraction: Set at 0 for no retraction at all. This should generally be the same as the length of the heat zone.", + "description": "The amount of retraction when switching extruder. Set to 0 for no retraction at all. This should generally be the same as the length of the heat zone.", "type": "float", "unit": "mm", "enabled": "retraction_enable", From 93bd5fee5387e45ea880ee1fc6125790546e465a Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 16 Nov 2018 13:44:46 +0100 Subject: [PATCH 0203/1240] Fix wrong push free shadow for one at a time. It was applying the size twice. CURA-5822 --- cura/Scene/ConvexHullDecorator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/Scene/ConvexHullDecorator.py b/cura/Scene/ConvexHullDecorator.py index 39124c5ba3..0c03ae615b 100644 --- a/cura/Scene/ConvexHullDecorator.py +++ b/cura/Scene/ConvexHullDecorator.py @@ -272,7 +272,7 @@ class ConvexHullDecorator(SceneNodeDecorator): head_and_fans = self._getHeadAndFans().intersectionConvexHulls(mirrored) # Min head hull is used for the push free - convex_hull = self._compute2DConvexHeadFull() + convex_hull = self._compute2DConvexHull() if convex_hull: return convex_hull.getMinkowskiHull(head_and_fans) return None From e3dd5f1c39cea14bbeefed4ae2191f19ffdf8d75 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 16 Nov 2018 13:59:51 +0100 Subject: [PATCH 0204/1240] Add documentation about qml z fix Co-Authored-By: nallath --- plugins/PrepareStage/PrepareMenu.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index 9507d786b2..c464727dde 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -52,7 +52,7 @@ Item Cura.MachineSelector { id: machineSelection - z: openFileButtonBackground.z - 1 + z: openFileButtonBackground.z - 1 //Ensure that the tooltip of the open file button stays above the item row. Layout.minimumWidth: UM.Theme.getSize("machine_selector_widget").width Layout.maximumWidth: UM.Theme.getSize("machine_selector_widget").width Layout.fillWidth: true @@ -117,4 +117,4 @@ Item } } } -} \ No newline at end of file +} From de020b4c758ba05dcbf374616c955b316683e883 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 16 Nov 2018 14:02:00 +0100 Subject: [PATCH 0205/1240] Remove commented out code Co-Authored-By: nallath --- plugins/PreviewStage/PreviewMenu.qml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index df04507ba4..d7b5574035 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -40,7 +40,6 @@ Item Row { anchors.centerIn: parent - //spacing: UM.Theme.getSize("default_margin").width height: parent.height Cura.ExpandableComponent @@ -138,4 +137,4 @@ Item } } } -} \ No newline at end of file +} From 81b6f3ab11b0b73d6c0db10ff441bac1de7072e4 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 16 Nov 2018 14:03:39 +0100 Subject: [PATCH 0206/1240] Update unclear qml ID's CURA-5785 Co-Authored-By: nallath --- plugins/PreviewStage/PreviewMenu.qml | 4 ++-- resources/qml/ExpandableComponent.qml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index d7b5574035..bf162be4e3 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -78,7 +78,7 @@ Item popupItem: Column { - id: column + id: viewSelectorPopup width: viewSelector.width - 2 * UM.Theme.getSize("default_margin").width // For some reason the height of the column gets set to 0 if this is not set... @@ -86,7 +86,7 @@ Item Repeater { - id: networkedPrinters + id: viewsList model: viewSelector.viewModel RoundButton { diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 9464a8f0ac..4d54ad9611 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -97,7 +97,7 @@ Item // A highlight that is shown when the popup is expanded Rectangle { - id: expandedHightlight + id: expandedHighlight width: parent.width height: UM.Theme.getSize("thick_lining").height color: UM.Theme.getColor("primary") From 514fc130457cbb488c06168eb51dc8e0437e8c9e Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 16 Nov 2018 14:04:33 +0100 Subject: [PATCH 0207/1240] Remove redundant documentation CURA-5875 Co-Authored-By: nallath --- resources/qml/ExpandableComponent.qml | 2 -- 1 file changed, 2 deletions(-) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 4d54ad9611..bbba44f743 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -16,7 +16,6 @@ Item // The popupItem holds the QML item that is shown when the "open" button is pressed property var popupItem - // The background color of the popup property color popupBackgroundColor: UM.Theme.getColor("action_button") property color headerBackgroundColor: UM.Theme.getColor("action_button") @@ -31,7 +30,6 @@ Item // What icon should be displayed on the right. property alias iconSource: collapseButton.source - // What is the color of the icon? property alias iconColor: collapseButton.color // The icon size (it's always drawn as a square) From e087db7c2e0ce3857e16b14c499d5d239abc8828 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 16 Nov 2018 13:26:03 +0100 Subject: [PATCH 0208/1240] Code style and fixes to comments Contributes to issue CURA-5785. --- plugins/PreviewStage/PreviewMenu.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index bf162be4e3..a3235d7cb8 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -54,13 +54,13 @@ Item { for (var i = 0; i < viewModel.rowCount(); i++) { - if(viewModel.getItem(i).active) + if (viewModel.getItem(i).active) { return viewModel.getItem(i) } } // Nothing was active, so just return the first one (the list is sorted by priority, so the most - // important one sshould be returned) + // important one should be returned) return viewModel.getItem(0) } From 3ef6d0544a02705eba13e5e115c3e1f40694fb95 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 16 Nov 2018 14:11:04 +0100 Subject: [PATCH 0209/1240] Fix spelling and indents Ant-fuckery, as they say in the low lands. Contributes to issue CURA-5785. --- resources/qml/IconWithText.qml | 2 +- resources/qml/MachineSelector.qml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/qml/IconWithText.qml b/resources/qml/IconWithText.qml index d19c7e3899..9ce2f412cc 100644 --- a/resources/qml/IconWithText.qml +++ b/resources/qml/IconWithText.qml @@ -10,7 +10,7 @@ import Cura 1.0 as Cura // Reusable component that holds an (re-colorable) icon on the left with some text on the right. // This component is also designed to be used with layouts. It will use the width of the text + icon as preferred width -// It sets the icon size + half of the content as it's minium width (in which case it will elide the text) +// It sets the icon size + half of the content as its minium width (in which case it will elide the text) Item { property alias iconColor: icon.color diff --git a/resources/qml/MachineSelector.qml b/resources/qml/MachineSelector.qml index b18f427980..fa680fa9a6 100644 --- a/resources/qml/MachineSelector.qml +++ b/resources/qml/MachineSelector.qml @@ -80,8 +80,8 @@ Cura.ExpandableComponent radius: UM.Theme.getSize("default_radius").width onClicked: { - togglePopup() - Cura.MachineManager.setActiveMachine(model.id) + togglePopup() + Cura.MachineManager.setActiveMachine(model.id) } Connections From e9e8c49b2d9d0c898c88dced34b8179b8bc1f0de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Fri, 16 Nov 2018 14:16:45 +0100 Subject: [PATCH 0210/1240] Added tests for SendMaterialJob and refactored SendMaterialJob for better testability. This is part of a larger project to create tests for the UM3NetworkPrinting plugin in preparation for printing from the cloud --- plugins/UM3NetworkPrinting/src/Models.py | 87 +++++ .../UM3NetworkPrinting/src/SendMaterialJob.py | 124 ++++--- .../UM3NetworkPrinting/TestSendMaterialJob.py | 327 ++++++++++++++++++ 3 files changed, 491 insertions(+), 47 deletions(-) create mode 100644 plugins/UM3NetworkPrinting/src/Models.py create mode 100644 tests/plugins/UM3NetworkPrinting/TestSendMaterialJob.py diff --git a/plugins/UM3NetworkPrinting/src/Models.py b/plugins/UM3NetworkPrinting/src/Models.py new file mode 100644 index 0000000000..6d42b39370 --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Models.py @@ -0,0 +1,87 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from typing import Optional + + +class BaseModel: + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + + def __eq__(self, other): + return self.__dict__ == other.__dict__ if type(self) == type(other) else False + + +## Represents an item in the cluster API response for installed materials. +class ClusterMaterial(BaseModel): + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.version = int(self.version) + self.density = float(self.density) + + guid: None # type: Optional[str] + + material: None # type: Optional[str] + + brand: None # type: Optional[str] + + version = None # type: Optional[int] + + color: None # type: Optional[str] + + density: None # type: Optional[float] + + +class LocalMaterialProperties(BaseModel): + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.density = float(self.density) + self.diameter = float(self.diameter) + self.weight = float(self.weight) + + density: None # type: Optional[float] + + diameter: None # type: Optional[float] + + weight: None # type: Optional[int] + + +class LocalMaterial(BaseModel): + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.properties = LocalMaterialProperties(**self.properties) + self.approximate_diameter = float(self.approximate_diameter) + self.version = int(self.version) + + GUID: None # type: Optional[str] + + id: None # type: Optional[str] + + type: None # type: Optional[str] + + status: None # type: Optional[str] + + base_file: None # type: Optional[str] + + setting_version: None # type: Optional[str] + + version = None # type: Optional[int] + + name: None # type: Optional[str] + + brand: None # type: Optional[str] + + material: None # type: Optional[str] + + color_name: None # type: Optional[str] + + description: None # type: Optional[str] + + adhesion_info: None # type: Optional[str] + + approximate_diameter: None # type: Optional[float] + + properties: None # type: LocalMaterialProperties + + definition: None # type: Optional[str] + + compatible: None # type: Optional[bool] diff --git a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py index 8491e79c29..126ed07317 100644 --- a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py @@ -1,99 +1,129 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -import json #To understand the list of materials from the printer reply. -import os #To walk over material files. -import os.path #To filter on material files. -from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest #To listen to the reply from the printer. -from typing import Any, Dict, Set, TYPE_CHECKING -import urllib.parse #For getting material IDs from their file names. +import json # To understand the list of materials from the printer reply. +import os # To walk over material files. +import os.path # To filter on material files. +import urllib.parse # For getting material IDs from their file names. +from typing import Dict, TYPE_CHECKING -from UM.Job import Job #The interface we're implementing. +from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest # To listen to the reply from the printer. + +from UM.Job import Job # The interface we're implementing. from UM.Logger import Logger -from UM.MimeTypeDatabase import MimeTypeDatabase #To strip the extensions of the material profile files. +from UM.MimeTypeDatabase import MimeTypeDatabase # To strip the extensions of the material profile files. from UM.Resources import Resources -from UM.Settings.ContainerRegistry import ContainerRegistry #To find the GUIDs of materials. - -from cura.CuraApplication import CuraApplication #For the resource types. +from UM.Settings.ContainerRegistry import ContainerRegistry # To find the GUIDs of materials. +from cura.CuraApplication import CuraApplication # For the resource types. +from plugins.UM3NetworkPrinting.src.Models import ClusterMaterial, LocalMaterial if TYPE_CHECKING: from .ClusterUM3OutputDevice import ClusterUM3OutputDevice + ## Asynchronous job to send material profiles to the printer. # # This way it won't freeze up the interface while sending those materials. class SendMaterialJob(Job): def __init__(self, device: "ClusterUM3OutputDevice") -> None: super().__init__() - self.device = device #type: ClusterUM3OutputDevice + self.device = device # type: ClusterUM3OutputDevice def run(self) -> None: - self.device.get("materials/", on_finished = self.sendMissingMaterials) + self.device.get("materials/", on_finished=self.sendMissingMaterials) def sendMissingMaterials(self, reply: QNetworkReply) -> None: - if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200: #Got an error from the HTTP request. + if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200: # Got an error from the HTTP request. Logger.log("e", "Couldn't request current material storage on printer. Not syncing materials.") return - remote_materials_list = reply.readAll().data().decode("utf-8") + # Collect materials from the printer's reply try: - remote_materials_list = json.loads(remote_materials_list) + remote_materials_by_guid = self._parseReply(reply) except json.JSONDecodeError: Logger.log("e", "Request material storage on printer: I didn't understand the printer's answer.") return - try: - remote_materials_by_guid = {material["guid"]: material for material in remote_materials_list} #Index by GUID. except KeyError: Logger.log("e", "Request material storage on printer: Printer's answer was missing GUIDs.") return - container_registry = ContainerRegistry.getInstance() - local_materials_list = filter(lambda material: ("GUID" in material and "version" in material and "id" in material), container_registry.findContainersMetadata(type = "material")) - local_materials_by_guid = {material["GUID"]: material for material in local_materials_list if material["id"] == material["base_file"]} - for material in local_materials_list: #For each GUID get the material with the highest version number. - try: - if int(material["version"]) > local_materials_by_guid[material["GUID"]]["version"]: - local_materials_by_guid[material["GUID"]] = material - except ValueError: - Logger.log("e", "Material {material_id} has invalid version number {number}.".format(material_id = material["id"], number = material["version"])) - continue + # Collect local materials + local_materials_by_guid = self._getLocalMaterials() - materials_to_send = set() #type: Set[Dict[str, Any]] - for guid, material in local_materials_by_guid.items(): - if guid not in remote_materials_by_guid: - materials_to_send.add(material["id"]) - continue - try: - if int(material["version"]) > remote_materials_by_guid[guid]["version"]: - materials_to_send.add(material["id"]) - continue - except KeyError: - Logger.log("e", "Current material storage on printer was an invalid reply (missing version).") - return + # Find out what materials are new or updated annd must be sent to the printer + materials_to_send = { + material.id + for guid, material in local_materials_by_guid.items() + if guid not in remote_materials_by_guid or + material.version > remote_materials_by_guid[guid].version + } + # Send materials to the printer + self.sendMaterialsToPrinter(materials_to_send) + + def sendMaterialsToPrinter(self, materials_to_send): for file_path in Resources.getAllResourcesOfType(CuraApplication.ResourceTypes.MaterialInstanceContainer): try: mime_type = MimeTypeDatabase.getMimeTypeForFile(file_path) except MimeTypeDatabase.MimeTypeNotFoundError: - continue #Not the sort of file we'd like to send then. + continue # Not the sort of file we'd like to send then. + _, file_name = os.path.split(file_path) material_id = urllib.parse.unquote_plus(mime_type.stripExtension(file_name)) + if material_id not in materials_to_send: continue parts = [] with open(file_path, "rb") as f: - parts.append(self.device._createFormPart("name=\"file\"; filename=\"{file_name}\"".format(file_name = file_name), f.read())) + parts.append( + self.device._createFormPart("name=\"file\"; filename=\"{file_name}\"".format(file_name=file_name), + f.read())) signature_file_path = file_path + ".sig" if os.path.exists(signature_file_path): _, signature_file_name = os.path.split(signature_file_path) with open(signature_file_path, "rb") as f: - parts.append(self.device._createFormPart("name=\"signature_file\"; filename=\"{file_name}\"".format(file_name = signature_file_name), f.read())) + parts.append(self.device._createFormPart( + "name=\"signature_file\"; filename=\"{file_name}\"".format(file_name=signature_file_name), + f.read())) - Logger.log("d", "Syncing material {material_id} with cluster.".format(material_id = material_id)) - self.device.postFormWithParts(target = "materials/", parts = parts, on_finished = self.sendingFinished) + Logger.log("d", "Syncing material {material_id} with cluster.".format(material_id=material_id)) + self.device.postFormWithParts(target="materials/", parts=parts, on_finished=self.sendingFinished) def sendingFinished(self, reply: QNetworkReply): if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200: - Logger.log("e", "Received error code from printer when syncing material: {code}".format(code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute))) - Logger.log("e", reply.readAll().data().decode("utf-8")) \ No newline at end of file + Logger.log("e", "Received error code from printer when syncing material: {code}".format( + code=reply.attribute(QNetworkRequest.HttpStatusCodeAttribute))) + Logger.log("e", reply.readAll().data().decode("utf-8")) + + ## Parse the reply from the printer + # + # Parses the reply to a "/materials" request to the printer + # + # \return a dictionary of ClustMaterial objects by GUID + # \throw json.JSONDecodeError Raised when the reply does not contain a valid json string + # \throw KeyErrror Raised when on of the materials does not include a valid guid + @classmethod + def _parseReply(cls, reply: QNetworkReply) -> Dict[str, ClusterMaterial]: + remote_materials_list = json.loads(reply.readAll().data().decode("utf-8")) + return {material["guid"]: ClusterMaterial(**material) for material in remote_materials_list} + + ## Retrieves a list of local materials + # + # Only the new newest version of the local materials is returned + # + # \return a dictionary of LocalMaterial objects by GUID + @classmethod + def _getLocalMaterials(cls): + result = {} + for material in ContainerRegistry.getInstance().findContainersMetadata(type="material"): + try: + localMaterial = LocalMaterial(**material) + + if localMaterial.GUID not in result or localMaterial.version > result.get(localMaterial.GUID).version: + result[localMaterial.GUID] = localMaterial + except (ValueError): + Logger.log("e", "Material {material_id} has invalid version number {number}.".format( + material_id=material["id"], number=material["version"])) + + return result diff --git a/tests/plugins/UM3NetworkPrinting/TestSendMaterialJob.py b/tests/plugins/UM3NetworkPrinting/TestSendMaterialJob.py new file mode 100644 index 0000000000..3cdb73af22 --- /dev/null +++ b/tests/plugins/UM3NetworkPrinting/TestSendMaterialJob.py @@ -0,0 +1,327 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +import io +import json +from typing import Any, List +from unittest import TestCase, mock +from unittest.mock import patch, call + +from PyQt5.QtCore import QByteArray + +from UM.Logger import Logger +from UM.MimeTypeDatabase import MimeType +from UM.Settings.ContainerRegistry import ContainerInterface, ContainerRegistryInterface, \ + DefinitionContainerInterface, ContainerRegistry +from plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice import ClusterUM3OutputDevice +from plugins.UM3NetworkPrinting.src.Models import ClusterMaterial +from plugins.UM3NetworkPrinting.src.SendMaterialJob import SendMaterialJob + +# All log entries written to Log.log by the class-under-test are written to this list. It is cleared before each test +# run and check afterwards +_logentries = [] + + +def new_log(*args): + _logentries.append(args) + + +class TestContainerRegistry(ContainerRegistryInterface): + def __init__(self): + self.containersMetaData = None + + def findContainers(self, *, ignore_case: bool = False, **kwargs: Any) -> List[ContainerInterface]: + raise NotImplementedError() + + def findDefinitionContainers(self, **kwargs: Any) -> List[DefinitionContainerInterface]: + raise NotImplementedError() + + @classmethod + def getApplication(cls) -> "Application": + raise NotImplementedError() + + def getEmptyInstanceContainer(self) -> "InstanceContainer": + raise NotImplementedError() + + def isReadOnly(self, container_id: str) -> bool: + raise NotImplementedError() + + def setContainersMetadata(self, value): + self.containersMetaData = value + + def findContainersMetadata(self, type): + return self.containersMetaData + + +class FakeDevice(ClusterUM3OutputDevice): + def _createFormPart(self, content_header, data, content_type=None): + return "xxx" + + +class TestSendMaterialJob(TestCase): + _LOCALMATERIAL_WHITE = {'type': 'material', 'status': 'unknown', 'id': 'generic_pla_white', + 'base_file': 'generic_pla_white', 'setting_version': 5, 'name': 'White PLA', + 'brand': 'Generic', 'material': 'PLA', 'color_name': 'White', + 'GUID': 'badb0ee7-87c8-4f3f-9398-938587b67dce', 'version': '1', 'color_code': '#ffffff', + 'description': 'Test PLA White', 'adhesion_info': 'Use glue.', 'approximate_diameter': '3', + 'properties': {'density': '1.00', 'diameter': '2.85', 'weight': '750'}, + 'definition': 'fdmprinter', 'compatible': True} + _LOCALMATERIAL_BLACK = {'type': 'material', 'status': 'unknown', 'id': 'generic_pla_black', + 'base_file': 'generic_pla_black', 'setting_version': 5, 'name': 'Yellow CPE', + 'brand': 'Ultimaker', 'material': 'CPE', 'color_name': 'Black', + 'GUID': '5fbb362a-41f9-4818-bb43-15ea6df34aa4', 'version': '1', 'color_code': '#000000', + 'description': 'Test PLA Black', 'adhesion_info': 'Use glue.', 'approximate_diameter': '3', + 'properties': {'density': '1.01', 'diameter': '2.85', 'weight': '750'}, + 'definition': 'fdmprinter', 'compatible': True} + + _REMOTEMATERIAL_WHITE = { + "guid": "badb0ee7-87c8-4f3f-9398-938587b67dce", + "material": "PLA", + "brand": "Generic", + "version": 1, + "color": "White", + "density": 1.00 + } + _REMOTEMATERIAL_BLACK = { + "guid": "5fbb362a-41f9-4818-bb43-15ea6df34aa4", + "material": "PLA", + "brand": "Generic", + "version": 2, + "color": "Black", + "density": 1.00 + } + + def setUp(self): + # Make sure the we start with clean (log) slate + _logentries.clear() + + def tearDown(self): + # If there are still log entries that were not checked something is wrong or we must add checks for them + self.assertEqual(len(_logentries), 0) + + @patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") + def test_run(self, device_mock): + with mock.patch.object(Logger, 'log', new=new_log): + job = SendMaterialJob(device_mock) + job.run() + + device_mock.get.assert_called_with("materials/", on_finished=job.sendMissingMaterials) + self.assertEqual(0, len(_logentries)) + + @patch("PyQt5.QtNetwork.QNetworkReply") + def test_sendMissingMaterials_withFailedRequest(self, reply_mock): + reply_mock.attribute.return_value = 404 + + with mock.patch.object(Logger, 'log', new=new_log): + SendMaterialJob(None).sendMissingMaterials(reply_mock) + + reply_mock.attribute.assert_called_with(0) + self.assertEqual(reply_mock.method_calls, [call.attribute(0)]) + self._assertLogEntries([('e', "Couldn't request current material storage on printer. Not syncing materials.")], + _logentries) + + @patch("PyQt5.QtNetwork.QNetworkReply") + def test_sendMissingMaterials_withBadJsonAnswer(self, reply_mock): + reply_mock.attribute.return_value = 200 + reply_mock.readAll.return_value = QByteArray(b'Six sick hicks nick six slick bricks with picks and sticks.') + + with mock.patch.object(Logger, 'log', new=new_log): + SendMaterialJob(None).sendMissingMaterials(reply_mock) + + reply_mock.attribute.assert_called_with(0) + self.assertEqual(reply_mock.method_calls, [call.attribute(0), call.readAll()]) + self._assertLogEntries( + [('e', "Request material storage on printer: I didn't understand the printer's answer.")], + _logentries) + + @patch("PyQt5.QtNetwork.QNetworkReply") + def test_sendMissingMaterials_withMissingGuid(self, reply_mock): + reply_mock.attribute.return_value = 200 + remoteMaterialWithoutGuid = self._REMOTEMATERIAL_WHITE.copy() + del remoteMaterialWithoutGuid["guid"] + reply_mock.readAll.return_value = QByteArray(json.dumps([remoteMaterialWithoutGuid]).encode("ascii")) + + with mock.patch.object(Logger, 'log', new=new_log): + SendMaterialJob(None).sendMissingMaterials(reply_mock) + + reply_mock.attribute.assert_called_with(0) + self.assertEqual(reply_mock.method_calls, [call.attribute(0), call.readAll()]) + self._assertLogEntries( + [('e', "Request material storage on printer: Printer's answer was missing GUIDs.")], + _logentries) + + @patch("UM.Resources.Resources.getAllResourcesOfType", lambda _: []) + @patch("PyQt5.QtNetwork.QNetworkReply") + def test_sendMissingMaterials_WithInvalidVersionInLocalMaterial(self, reply_mock): + reply_mock.attribute.return_value = 200 + reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTEMATERIAL_WHITE]).encode("ascii")) + + containerRegistry = TestContainerRegistry() + localMaterialWhiteWithInvalidVersion = self._LOCALMATERIAL_WHITE.copy() + localMaterialWhiteWithInvalidVersion["version"] = "one" + containerRegistry.setContainersMetadata([localMaterialWhiteWithInvalidVersion]) + + with mock.patch.object(Logger, "log", new=new_log): + with mock.patch.object(ContainerRegistry, "getInstance", lambda: containerRegistry): + SendMaterialJob(None).sendMissingMaterials(reply_mock) + + reply_mock.attribute.assert_called_with(0) + self.assertEqual(reply_mock.method_calls, [call.attribute(0), call.readAll()]) + self._assertLogEntries([('e', "Material generic_pla_white has invalid version number one.")], _logentries) + + @patch("UM.Resources.Resources.getAllResourcesOfType", lambda _: []) + @patch("PyQt5.QtNetwork.QNetworkReply") + def test_sendMissingMaterials_WithMultipleLocalVersionsLowFirst(self, reply_mock): + reply_mock.attribute.return_value = 200 + reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTEMATERIAL_WHITE]).encode("ascii")) + + containerRegistry = TestContainerRegistry() + localMaterialWhiteWithHigherVersion = self._LOCALMATERIAL_WHITE.copy() + localMaterialWhiteWithHigherVersion["version"] = "2" + containerRegistry.setContainersMetadata([self._LOCALMATERIAL_WHITE, localMaterialWhiteWithHigherVersion]) + + with mock.patch.object(Logger, "log", new=new_log): + with mock.patch.object(ContainerRegistry, "getInstance", lambda: containerRegistry): + SendMaterialJob(None).sendMissingMaterials(reply_mock) + + reply_mock.attribute.assert_called_with(0) + self.assertEqual(reply_mock.method_calls, [call.attribute(0), call.readAll()]) + self._assertLogEntries([], _logentries) + + @patch("UM.Resources.Resources.getAllResourcesOfType", lambda _: []) + @patch("PyQt5.QtNetwork.QNetworkReply") + def test_sendMissingMaterials_MaterialMissingOnPrinter(self, reply_mock): + reply_mock.attribute.return_value = 200 + reply_mock.readAll.return_value = QByteArray( + json.dumps([self._REMOTEMATERIAL_WHITE]).encode("ascii")) + + containerRegistry = TestContainerRegistry() + containerRegistry.setContainersMetadata([self._LOCALMATERIAL_WHITE, self._LOCALMATERIAL_BLACK]) + + with mock.patch.object(Logger, "log", new=new_log): + with mock.patch.object(ContainerRegistry, "getInstance", lambda: containerRegistry): + SendMaterialJob(None).sendMissingMaterials(reply_mock) + + reply_mock.attribute.assert_called_with(0) + self.assertEqual(reply_mock.method_calls, [call.attribute(0), call.readAll()]) + self._assertLogEntries([], _logentries) + + @patch("builtins.open", lambda a, b: io.StringIO("")) + @patch("UM.MimeTypeDatabase.MimeTypeDatabase.getMimeTypeForFile", + lambda _: MimeType(name="application/x-ultimaker-material-profile", comment="Ultimaker Material Profile", + suffixes=["xml.fdm_material"])) + @patch("UM.Resources.Resources.getAllResourcesOfType", lambda _: ["/materials/generic_pla_white.xml.fdm_material"]) + @patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") + def test_sendMaterialsToPrinter(self, device): + with mock.patch.object(Logger, "log", new=new_log): + SendMaterialJob(device).sendMaterialsToPrinter({'generic_pla_white'}) + + self._assertLogEntries([("d", "Syncing material generic_pla_white with cluster.")], _logentries) + + @patch("PyQt5.QtNetwork.QNetworkReply") + def xtest_sendMissingMaterials(self, reply_mock): + reply_mock.attribute.return_value = 200 + reply_mock.readAll.return_value = QByteArray( + json.dumps([self._REMOTEMATERIAL_WHITE], self._REMOTEMATERIAL_BLACK).encode("ascii")) + + containerRegistry = TestContainerRegistry() + containerRegistry.setContainersMetadata([self._LOCALMATERIAL_WHITE, self._LOCALMATERIAL_BLACK]) + + with mock.patch.object(Logger, "log", new=new_log): + with mock.patch.object(ContainerRegistry, "getInstance", lambda: containerRegistry): + SendMaterialJob(None).sendMissingMaterials(reply_mock) + + reply_mock.attribute.assert_called_with(0) + self.assertEqual(reply_mock.method_calls, [call.attribute(0), call.readAll()]) + self._assertLogEntries([], _logentries) + + @patch("PyQt5.QtNetwork.QNetworkReply") + def test_sendingFinished_success(self, reply_mock) -> None: + reply_mock.attribute.return_value = 200 + with mock.patch.object(Logger, 'log', new=new_log): + SendMaterialJob(None).sendingFinished(reply_mock) + + reply_mock.attribute.assert_called_once_with(0) + self.assertEqual(0, len(_logentries)) + + @patch("PyQt5.QtNetwork.QNetworkReply") + def test_sendingFinished_failed(self, reply_mock) -> None: + reply_mock.attribute.return_value = 404 + reply_mock.readAll.return_value = QByteArray(b'Six sick hicks nick six slick bricks with picks and sticks.') + + with mock.patch.object(Logger, 'log', new=new_log): + SendMaterialJob(None).sendingFinished(reply_mock) + + reply_mock.attribute.assert_called_with(0) + self.assertEqual(reply_mock.method_calls, [call.attribute(0), call.attribute(0), call.readAll()]) + + self._assertLogEntries([ + ("e", "Received error code from printer when syncing material: 404"), + ("e", "Six sick hicks nick six slick bricks with picks and sticks.") + ], _logentries) + + @patch("PyQt5.QtNetwork.QNetworkReply") + def test_parseReply(self, reply_mock): + reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTEMATERIAL_WHITE]).encode("ascii")) + + response = SendMaterialJob._parseReply(reply_mock) + + self.assertTrue(len(response) == 1) + self.assertEqual(next(iter(response.values())), ClusterMaterial(**self._REMOTEMATERIAL_WHITE)) + + @patch("PyQt5.QtNetwork.QNetworkReply") + def test_parseReplyWithInvalidMaterial(self, reply_mock): + remoteMaterialWithInvalidVersion = self._REMOTEMATERIAL_WHITE.copy() + remoteMaterialWithInvalidVersion["version"] = "one" + reply_mock.readAll.return_value = QByteArray(json.dumps([remoteMaterialWithInvalidVersion]).encode("ascii")) + + with self.assertRaises(ValueError): + SendMaterialJob._parseReply(reply_mock) + + def test__getLocalMaterials(self): + containerRegistry = TestContainerRegistry() + containerRegistry.setContainersMetadata([self._LOCALMATERIAL_WHITE, self._LOCALMATERIAL_BLACK]) + + with mock.patch.object(Logger, "log", new=new_log): + with mock.patch.object(ContainerRegistry, "getInstance", lambda: containerRegistry): + local_materials = SendMaterialJob(None)._getLocalMaterials() + + self.assertTrue(len(local_materials) == 2) + + def test__getLocalMaterialsWithMultipleVersions(self): + containerRegistry = TestContainerRegistry() + localMaterialWithNewerVersion = self._LOCALMATERIAL_WHITE.copy() + localMaterialWithNewerVersion["version"] = 2 + containerRegistry.setContainersMetadata([self._LOCALMATERIAL_WHITE, localMaterialWithNewerVersion]) + + with mock.patch.object(Logger, "log", new=new_log): + with mock.patch.object(ContainerRegistry, "getInstance", lambda: containerRegistry): + local_materials = SendMaterialJob(None)._getLocalMaterials() + + self.assertTrue(len(local_materials) == 1) + self.assertTrue(list(local_materials.values())[0].version == 2) + + containerRegistry.setContainersMetadata([localMaterialWithNewerVersion, self._LOCALMATERIAL_WHITE]) + + with mock.patch.object(Logger, "log", new=new_log): + with mock.patch.object(ContainerRegistry, "getInstance", lambda: containerRegistry): + local_materials = SendMaterialJob(None)._getLocalMaterials() + + self.assertTrue(len(local_materials) == 1) + self.assertTrue(list(local_materials.values())[0].version == 2) + + def _assertLogEntries(self, first, second): + """ + Inspects the two sets of log entry tuples and fails when they are not the same + :param first: The first set of tuples + :param second: The second set of tuples + """ + self.assertEqual(len(first), len(second)) + + while len(first) > 0: + e1, m1 = first[0] + e2, m2 = second[0] + self.assertEqual(e1, e2) + self.assertEqual(m1, m2) + first.pop(0) + second.pop(0) From 421af26f87892484381d398a6cbdf05e96a26165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Fri, 16 Nov 2018 14:54:49 +0100 Subject: [PATCH 0211/1240] Extra test on test_sendMaterialsToPrinter, removed unused code --- .../UM3NetworkPrinting/TestSendMaterialJob.py | 25 +++++-------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/tests/plugins/UM3NetworkPrinting/TestSendMaterialJob.py b/tests/plugins/UM3NetworkPrinting/TestSendMaterialJob.py index 3cdb73af22..172b42cb8b 100644 --- a/tests/plugins/UM3NetworkPrinting/TestSendMaterialJob.py +++ b/tests/plugins/UM3NetworkPrinting/TestSendMaterialJob.py @@ -212,28 +212,15 @@ class TestSendMaterialJob(TestCase): suffixes=["xml.fdm_material"])) @patch("UM.Resources.Resources.getAllResourcesOfType", lambda _: ["/materials/generic_pla_white.xml.fdm_material"]) @patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") - def test_sendMaterialsToPrinter(self, device): + def test_sendMaterialsToPrinter(self, device_mock): + device_mock._createFormPart.return_value = "_xXx_" with mock.patch.object(Logger, "log", new=new_log): - SendMaterialJob(device).sendMaterialsToPrinter({'generic_pla_white'}) + job = SendMaterialJob(device_mock) + job.sendMaterialsToPrinter({'generic_pla_white'}) self._assertLogEntries([("d", "Syncing material generic_pla_white with cluster.")], _logentries) - - @patch("PyQt5.QtNetwork.QNetworkReply") - def xtest_sendMissingMaterials(self, reply_mock): - reply_mock.attribute.return_value = 200 - reply_mock.readAll.return_value = QByteArray( - json.dumps([self._REMOTEMATERIAL_WHITE], self._REMOTEMATERIAL_BLACK).encode("ascii")) - - containerRegistry = TestContainerRegistry() - containerRegistry.setContainersMetadata([self._LOCALMATERIAL_WHITE, self._LOCALMATERIAL_BLACK]) - - with mock.patch.object(Logger, "log", new=new_log): - with mock.patch.object(ContainerRegistry, "getInstance", lambda: containerRegistry): - SendMaterialJob(None).sendMissingMaterials(reply_mock) - - reply_mock.attribute.assert_called_with(0) - self.assertEqual(reply_mock.method_calls, [call.attribute(0), call.readAll()]) - self._assertLogEntries([], _logentries) + self.assertEqual([call._createFormPart('name="file"; filename="generic_pla_white.xml.fdm_material"', ''), + call.postFormWithParts(on_finished=job.sendingFinished, parts = ["_xXx_"], target = "materials/")], device_mock.method_calls) @patch("PyQt5.QtNetwork.QNetworkReply") def test_sendingFinished_success(self, reply_mock) -> None: From 3fd1cc0e8cec4c77d080860bccd9deb99bb2d62c Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 16 Nov 2018 14:58:11 +0100 Subject: [PATCH 0212/1240] Ensure that the added labels are also themable CURA-5785 --- plugins/PreviewStage/PreviewMenu.qml | 2 ++ resources/qml/ExpandableComponent.qml | 2 +- resources/qml/MachineSelector.qml | 3 +++ .../Menus/ConfigurationMenu/QuickConfigurationSelector.qml | 4 ++++ 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index bf162be4e3..90c1cb7c99 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -74,6 +74,8 @@ Item height: parent.height elide: Text.ElideRight font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + } popupItem: Column diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index bbba44f743..c177dc758d 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -38,7 +38,7 @@ Item // Is the "drawer" open? readonly property alias expanded: popup.visible - property alias expandedHighlightColor: expandedHightlight.color + property alias expandedHighlightColor: expandedHighlight.color function togglePopup() { diff --git a/resources/qml/MachineSelector.qml b/resources/qml/MachineSelector.qml index b18f427980..1f18e1ab2a 100644 --- a/resources/qml/MachineSelector.qml +++ b/resources/qml/MachineSelector.qml @@ -32,6 +32,7 @@ Cura.ExpandableComponent height: parent.height elide: Text.ElideRight font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") } popupItem: Item @@ -58,6 +59,7 @@ Cura.ExpandableComponent visible: networkedPrintersModel.items.length > 0 height: visible ? contentHeight + 2 * UM.Theme.getSize("default_margin").height : 0 font: UM.Theme.getFont("medium_bold") + color: UM.Theme.getColor("text") verticalAlignment: Text.AlignVCenter } @@ -99,6 +101,7 @@ Cura.ExpandableComponent visible: virtualPrintersModel.items.length > 0 height: visible ? contentHeight + 2 * UM.Theme.getSize("default_margin").height : 0 font: UM.Theme.getFont("medium_bold") + color: UM.Theme.getColor("text") verticalAlignment: Text.AlignVCenter } diff --git a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml index 6f4e58a326..d6b9b1c06a 100644 --- a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml +++ b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml @@ -60,6 +60,8 @@ Cura.ExpandableComponent text: model.material_brand elide: Text.ElideRight + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") anchors { @@ -75,6 +77,8 @@ Cura.ExpandableComponent { text: model.material elide: Text.ElideRight + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") anchors { From 5972bfaf0342d41f987ec38d1cb7c00c56dab4cb Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 16 Nov 2018 15:06:21 +0100 Subject: [PATCH 0213/1240] Add round to minContentWidth This prevents text from getting a weird width CURA-5785 --- resources/qml/IconWithText.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/IconWithText.qml b/resources/qml/IconWithText.qml index 9ce2f412cc..dcb3ef7851 100644 --- a/resources/qml/IconWithText.qml +++ b/resources/qml/IconWithText.qml @@ -21,7 +21,7 @@ Item // These properties can be used in combination with layouts. readonly property real contentWidth: icon.width + margin + label.contentWidth - readonly property real minContentWidth: icon.width + margin + 0.5 * label.contentWidth + readonly property real minContentWidth: Math.round(icon.width + margin + 0.5 * label.contentWidth) Layout.minimumWidth: minContentWidth Layout.preferredWidth: contentWidth From fe15a0a5132716844afe25e2cd7dd7424667efe8 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 16 Nov 2018 15:09:47 +0100 Subject: [PATCH 0214/1240] Made width of machineSelector popup depend on size of the element CURA-5785 --- resources/qml/MachineSelector.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/MachineSelector.qml b/resources/qml/MachineSelector.qml index 256e6ec218..793f45ddcf 100644 --- a/resources/qml/MachineSelector.qml +++ b/resources/qml/MachineSelector.qml @@ -38,7 +38,7 @@ Cura.ExpandableComponent popupItem: Item { id: popup - width: 240 + width: machineSelector.width - 2 * UM.Theme.getSize("default_margin").width height: 200 ScrollView From 816d844258c2126c86f9be7b2be50580932cef4d Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 16 Nov 2018 15:14:08 +0100 Subject: [PATCH 0215/1240] Use textwidth instead of round CURA-5785 Co-Authored-By: nallath --- .../Menus/ConfigurationMenu/QuickConfigurationSelector.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml index d6b9b1c06a..b1058ce627 100644 --- a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml +++ b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml @@ -134,7 +134,7 @@ Cura.ExpandableComponent anchors.bottom: parent.bottom property var model: extrudersModel.items[tabBar.currentIndex] property real textWidth: Math.round(width * 0.3) - property real controlWidth: Math.round(width * 0.7) + property real controlWidth: width - textWidth Column { spacing: UM.Theme.getSize("default_margin").height @@ -236,4 +236,4 @@ Cura.ExpandableComponent } } -} \ No newline at end of file +} From b72f1bb31403baa2c1ee48aa473ee3bbd715c780 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 16 Nov 2018 15:15:44 +0100 Subject: [PATCH 0216/1240] Add missing screenScaleFactor for implicit sizes CURA-5785 --- resources/qml/ExpandableComponent.qml | 6 +++--- resources/qml/PrintSetupSelector.qml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index c177dc758d..d4eb18f4fa 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -6,7 +6,7 @@ import UM 1.2 as UM // The expandable component has 3 major sub components: // * The headerItem; Always visible and should hold some info about what happens if the component is expanded // * The popupItem; The content that needs to be shown if the component is expanded. -// * The Icon; An icon that is displayed on the right of the drawer. +// * The icon; An icon that is displayed on the right of the drawer. Item { id: base @@ -69,8 +69,8 @@ Item onHeightChanged: popup.height = popupItem.height + 2 * popup.padding } - implicitHeight: 100 - implicitWidth: 400 + implicitHeight: 100 * screenScaleFactor + implicitWidth: 400 * screenScaleFactor Rectangle { id: background diff --git a/resources/qml/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector.qml index e576dddc07..b843244147 100644 --- a/resources/qml/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector.qml @@ -24,7 +24,7 @@ Cura.ExpandableComponent signal showTooltip(Item item, point location, string text) signal hideTooltip() - implicitWidth: 200 + implicitWidth: 200 * screenScaleFactor height: childrenRect.height iconSource: UM.Theme.getIcon("pencil") From 42399ecb24492d2f38cc85447a6ab33a1ad28d55 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 16 Nov 2018 15:17:34 +0100 Subject: [PATCH 0217/1240] Let headerItem fill entire component if no icon is set CURA-5785 --- resources/qml/ExpandableComponent.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index d4eb18f4fa..8ed6dc5674 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -85,7 +85,7 @@ Item anchors { left: parent.left - right: collapseButton.left + right: collapseButton.visible ? collapseButton.left : parent.right top: parent.top bottom: parent.bottom margins: background.padding From 695d45ffbed22396035f0d32f616e610712a1923 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Fri, 16 Nov 2018 15:54:07 +0100 Subject: [PATCH 0218/1240] Initialize the models with None --- plugins/UM3NetworkPrinting/src/Models.py | 48 ++++++++++++------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Models.py b/plugins/UM3NetworkPrinting/src/Models.py index 6d42b39370..89bf665377 100644 --- a/plugins/UM3NetworkPrinting/src/Models.py +++ b/plugins/UM3NetworkPrinting/src/Models.py @@ -18,17 +18,17 @@ class ClusterMaterial(BaseModel): self.version = int(self.version) self.density = float(self.density) - guid: None # type: Optional[str] + guid = None # type: Optional[str] - material: None # type: Optional[str] + material = None # type: Optional[str] - brand: None # type: Optional[str] + brand = None # type: Optional[str] version = None # type: Optional[int] - color: None # type: Optional[str] + color = None # type: Optional[str] - density: None # type: Optional[float] + density = None # type: Optional[float] class LocalMaterialProperties(BaseModel): @@ -38,11 +38,11 @@ class LocalMaterialProperties(BaseModel): self.diameter = float(self.diameter) self.weight = float(self.weight) - density: None # type: Optional[float] + density = None # type: Optional[float] - diameter: None # type: Optional[float] + diameter = None # type: Optional[float] - weight: None # type: Optional[int] + weight = None # type: Optional[int] class LocalMaterial(BaseModel): @@ -52,36 +52,36 @@ class LocalMaterial(BaseModel): self.approximate_diameter = float(self.approximate_diameter) self.version = int(self.version) - GUID: None # type: Optional[str] + GUID = None # type: Optional[str] - id: None # type: Optional[str] + id = None # type: Optional[str] - type: None # type: Optional[str] + type = None # type: Optional[str] - status: None # type: Optional[str] + status = None # type: Optional[str] - base_file: None # type: Optional[str] + base_file = None # type: Optional[str] - setting_version: None # type: Optional[str] + setting_version = None # type: Optional[str] version = None # type: Optional[int] - name: None # type: Optional[str] + name = None # type: Optional[str] - brand: None # type: Optional[str] + brand = None # type: Optional[str] - material: None # type: Optional[str] + material = None # type: Optional[str] - color_name: None # type: Optional[str] + color_name = None # type: Optional[str] - description: None # type: Optional[str] + description = None # type: Optional[str] - adhesion_info: None # type: Optional[str] + adhesion_info = None # type: Optional[str] - approximate_diameter: None # type: Optional[float] + approximate_diameter = None # type: Optional[float] - properties: None # type: LocalMaterialProperties + properties = None # type: LocalMaterialProperties - definition: None # type: Optional[str] + definition = None # type: Optional[str] - compatible: None # type: Optional[bool] + compatible = None # type: Optional[bool] From 7f99ed1af36f5d0ef75f7087b1c1d84676e982b2 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 16 Nov 2018 16:04:11 +0100 Subject: [PATCH 0219/1240] ExtruderIcon will now display that it's disabled correctly CURA-5785 --- cura/Settings/ExtrudersModel.py | 5 +++-- resources/qml/ExtruderButton.qml | 1 + resources/qml/ExtruderIcon.qml | 22 +++++++++++++++---- .../QuickConfigurationSelector.qml | 2 ++ 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/cura/Settings/ExtrudersModel.py b/cura/Settings/ExtrudersModel.py index 9b85afa10e..14a8dadc69 100644 --- a/cura/Settings/ExtrudersModel.py +++ b/cura/Settings/ExtrudersModel.py @@ -24,8 +24,6 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): ## Human-readable name of the extruder. NameRole = Qt.UserRole + 2 - ## Is the extruder enabled? - EnabledRole = Qt.UserRole + 9 ## Colour of the material loaded in the extruder. ColorRole = Qt.UserRole + 3 @@ -50,6 +48,9 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): MaterialBrandRole = Qt.UserRole + 9 ColorNameRole = Qt.UserRole + 10 + ## Is the extruder enabled? + EnabledRole = Qt.UserRole + 11 + ## List of colours to display if there is no material or the material has no known # colour. defaultColors = ["#ffc924", "#86ec21", "#22eeee", "#245bff", "#9124ff", "#ff24c8"] diff --git a/resources/qml/ExtruderButton.qml b/resources/qml/ExtruderButton.qml index 764e9c94cb..8a1f0af44a 100644 --- a/resources/qml/ExtruderButton.qml +++ b/resources/qml/ExtruderButton.qml @@ -23,6 +23,7 @@ Button { width: UM.Theme.getSize("button_icon").width materialColor: model.color + extruderEnabled: extruder.stack.isEnabled property int index: extruder.index } diff --git a/resources/qml/ExtruderIcon.qml b/resources/qml/ExtruderIcon.qml index eb4bdef0f7..87e210e75a 100644 --- a/resources/qml/ExtruderIcon.qml +++ b/resources/qml/ExtruderIcon.qml @@ -13,9 +13,9 @@ Item implicitHeight: implicitWidth property bool checked: true - property alias materialColor: mainIcon.color + property color materialColor property alias textColor: extruderNumberText.color - + property bool extruderEnabled: true UM.RecolorImage { id: mainIcon @@ -24,6 +24,7 @@ Item sourceSize.width: parent.width sourceSize.height: parent.width source: UM.Theme.getIcon("extruder_button") + color: extruderEnabled ? materialColor: "gray" } Rectangle @@ -31,9 +32,9 @@ Item id: extruderNumberCircle width: height - height: parent.height / 2 + height: Math.round(parent.height / 2) radius: Math.round(width / 2) - color: "white" + color: UM.Theme.getColor("toolbar_background") anchors { @@ -51,6 +52,19 @@ Item font: UM.Theme.getFont("default") width: contentWidth height: contentHeight + visible: extruderEnabled + } + + UM.RecolorImage + { + id: disabledIcon + anchors.fill: parent + anchors.margins: UM.Theme.getSize("thick_lining").width + sourceSize.width: width + sourceSize.height: width + source: UM.Theme.getIcon("cross1") + visible: !extruderEnabled + color: "black" } } } \ No newline at end of file diff --git a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml index b1058ce627..c157b0b24f 100644 --- a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml +++ b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml @@ -49,6 +49,7 @@ Cura.ExpandableComponent { id: extruderIcon materialColor: model.color + extruderEnabled: model.enabled height: parent.height width: height } @@ -118,6 +119,7 @@ Cura.ExpandableComponent { anchors.horizontalCenter: parent.horizontalCenter materialColor: model.color + extruderEnabled: model.enabled width: parent.height height: parent.height } From 0062250238030932b15dd36511c96c4bc1e6af3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Fri, 16 Nov 2018 16:06:36 +0100 Subject: [PATCH 0220/1240] Moved the test to the plugin directory --- .../UM3NetworkPrinting/tests}/TestSendMaterialJob.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {tests/plugins/UM3NetworkPrinting => plugins/UM3NetworkPrinting/tests}/TestSendMaterialJob.py (100%) diff --git a/tests/plugins/UM3NetworkPrinting/TestSendMaterialJob.py b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py similarity index 100% rename from tests/plugins/UM3NetworkPrinting/TestSendMaterialJob.py rename to plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py From 1000625d6947fe701a25cdff5f13c2ff045e29f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Fri, 16 Nov 2018 16:07:17 +0100 Subject: [PATCH 0221/1240] Added spaces around all = --- .../UM3NetworkPrinting/src/SendMaterialJob.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py index 126ed07317..baea3b9a78 100644 --- a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py @@ -30,7 +30,7 @@ class SendMaterialJob(Job): self.device = device # type: ClusterUM3OutputDevice def run(self) -> None: - self.device.get("materials/", on_finished=self.sendMissingMaterials) + self.device.get("materials/", on_finished = self.sendMissingMaterials) def sendMissingMaterials(self, reply: QNetworkReply) -> None: if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200: # Got an error from the HTTP request. @@ -77,23 +77,23 @@ class SendMaterialJob(Job): parts = [] with open(file_path, "rb") as f: parts.append( - self.device._createFormPart("name=\"file\"; filename=\"{file_name}\"".format(file_name=file_name), + self.device._createFormPart("name=\"file\"; filename=\"{file_name}\"".format(file_name = file_name), f.read())) signature_file_path = file_path + ".sig" if os.path.exists(signature_file_path): _, signature_file_name = os.path.split(signature_file_path) with open(signature_file_path, "rb") as f: parts.append(self.device._createFormPart( - "name=\"signature_file\"; filename=\"{file_name}\"".format(file_name=signature_file_name), + "name=\"signature_file\"; filename=\"{file_name}\"".format(file_name = signature_file_name), f.read())) - Logger.log("d", "Syncing material {material_id} with cluster.".format(material_id=material_id)) - self.device.postFormWithParts(target="materials/", parts=parts, on_finished=self.sendingFinished) + Logger.log("d", "Syncing material {material_id} with cluster.".format(material_id = material_id)) + self.device.postFormWithParts(target = "materials/", parts = parts, on_finished = self.sendingFinished) def sendingFinished(self, reply: QNetworkReply): if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200: Logger.log("e", "Received error code from printer when syncing material: {code}".format( - code=reply.attribute(QNetworkRequest.HttpStatusCodeAttribute))) + code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute))) Logger.log("e", reply.readAll().data().decode("utf-8")) ## Parse the reply from the printer @@ -116,7 +116,7 @@ class SendMaterialJob(Job): @classmethod def _getLocalMaterials(cls): result = {} - for material in ContainerRegistry.getInstance().findContainersMetadata(type="material"): + for material in ContainerRegistry.getInstance().findContainersMetadata(type = "material"): try: localMaterial = LocalMaterial(**material) @@ -124,6 +124,6 @@ class SendMaterialJob(Job): result[localMaterial.GUID] = localMaterial except (ValueError): Logger.log("e", "Material {material_id} has invalid version number {number}.".format( - material_id=material["id"], number=material["version"])) + material_id = material["id"], number = material["version"])) return result From 52b2f4579ad1620b03b0ce3f34a44af306926008 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 16 Nov 2018 16:10:26 +0100 Subject: [PATCH 0222/1240] Fix a number of QML warnings CURA-5785 --- resources/qml/Cura.qml | 16 ---------------- resources/qml/MachineSelector.qml | 2 +- .../QuickConfigurationSelector.qml | 6 +++--- 3 files changed, 4 insertions(+), 20 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index cdbf1f3511..0312a636a6 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -214,22 +214,6 @@ UM.MainWindow } } - Loader - { - id: viewPanel - - anchors.bottom: viewModeButton.top - anchors.topMargin: UM.Theme.getSize("default_margin").height - anchors.right: viewModeButton.right - - property var buttonTarget: Qt.point(viewModeButton.x + Math.round(viewModeButton.width / 2), viewModeButton.y + Math.round(viewModeButton.height / 2)) - - height: childrenRect.height - width: childrenRect.width - - source: UM.ActiveView.valid ? UM.ActiveView.activeViewPanel : "" - } - Cura.ActionPanelWidget { anchors.right: parent.right diff --git a/resources/qml/MachineSelector.qml b/resources/qml/MachineSelector.qml index 793f45ddcf..a464949192 100644 --- a/resources/qml/MachineSelector.qml +++ b/resources/qml/MachineSelector.qml @@ -45,7 +45,7 @@ Cura.ExpandableComponent { anchors.fill: parent contentHeight: column.implicitHeight - contentWidth: row.implicitWidth + contentWidth: column.implicitWidth clip: true ScrollBar.horizontal.policy: ScrollBar.AlwaysOff diff --git a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml index c157b0b24f..33610135fe 100644 --- a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml +++ b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml @@ -111,7 +111,7 @@ Cura.ExpandableComponent delegate: TabButton { - width: Math.round(ListView.view.width / extrudersModel.rowCount()) + width: ListView.view != null ? Math.round(ListView.view.width / extrudersModel.rowCount()): 0 height: parent.height contentItem: Item { @@ -156,7 +156,7 @@ Cura.ExpandableComponent OldControls.CheckBox { - checked: Cura.MachineManager.getExtruder(tabControl.model.index).isEnabled + checked: tabControl.model != null ? Cura.MachineManager.getExtruder(tabControl.model.index).isEnabled: false onClicked: Cura.MachineManager.setExtruderEnabled(tabControl.model.index, checked) height: UM.Theme.getSize("setting_control").height style: UM.Theme.styles.checkbox @@ -190,7 +190,7 @@ Cura.ExpandableComponent tooltip: currentRootMaterialName visible: Cura.MachineManager.hasMaterials - enabled: !extrudersList.visible || Cura.ExtruderManager.activeExtruderIndex > -1 + enabled: Cura.ExtruderManager.activeExtruderIndex > -1 height: UM.Theme.getSize("setting_control").height width: tabControl.controlWidth From bc5a2ce5b0c52d36a837d52ee085a8e1cb897494 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 16 Nov 2018 16:16:15 +0100 Subject: [PATCH 0223/1240] Make ViewSelector panel close upon selecting different view CURA-5785 --- plugins/PreviewStage/PreviewMenu.qml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index 31223cfa4a..0052d341fb 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -75,7 +75,6 @@ Item elide: Text.ElideRight font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") - } popupItem: Column @@ -96,7 +95,11 @@ Item radius: UM.Theme.getSize("default_radius").width checkable: true checked: active - onClicked: UM.Controller.setActiveView(id) + onClicked: + { + viewSelector.togglePopup() + UM.Controller.setActiveView(id) + } } } From 6b55490af88e30caa1afa7419141057ebe855304 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 16 Nov 2018 16:30:47 +0100 Subject: [PATCH 0224/1240] Fix final QML warnings CURA-5785 --- plugins/PreviewStage/PreviewMenu.qml | 3 ++- plugins/SimulationView/SimulationViewMenuComponent.qml | 10 ---------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index 0052d341fb..a3cd7cb76b 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -35,10 +35,11 @@ Item { id: stageMenu height: parent.height - width: childrenRect.width + UM.Theme.getSize("default_margin").width + width: stageMenuRow.width + UM.Theme.getSize("default_margin").width anchors.horizontalCenter: parent.horizontalCenter Row { + id: stageMenuRow anchors.centerIn: parent height: parent.height diff --git a/plugins/SimulationView/SimulationViewMenuComponent.qml b/plugins/SimulationView/SimulationViewMenuComponent.qml index 92a4e1e317..c20e141734 100644 --- a/plugins/SimulationView/SimulationViewMenuComponent.qml +++ b/plugins/SimulationView/SimulationViewMenuComponent.qml @@ -18,16 +18,6 @@ Cura.ExpandableComponent width: UM.Theme.getSize("layerview_menu_size").width iconSource: UM.Theme.getIcon("pencil") - property var buttonTarget: - { - if(parent != null) - { - var force_binding = parent.y; // ensure this gets reevaluated when the panel moves - return base.mapFromItem(parent.parent, parent.buttonTarget.x, parent.buttonTarget.y) - } - return Qt.point(0,0) - } - Connections { target: UM.Preferences From 565f009e9b6ca4b447145b38c2aa6cb3ef8d4169 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Fri, 16 Nov 2018 16:34:44 +0100 Subject: [PATCH 0225/1240] Added comments --- .../UM3NetworkPrinting/src/SendMaterialJob.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py index baea3b9a78..62b98bcdbd 100644 --- a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py @@ -1,7 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -import json # To understand the list of materials from the printer reply. +import json # To decode the list of materials from the printer reply. import os # To walk over material files. import os.path # To filter on material files. import urllib.parse # For getting material IDs from their file names. @@ -29,9 +29,13 @@ class SendMaterialJob(Job): super().__init__() self.device = device # type: ClusterUM3OutputDevice + ## Send the request to the printer and register a callback def run(self) -> None: self.device.get("materials/", on_finished = self.sendMissingMaterials) + ## Process the reply from the printer and determine which materials should be updated and sent to the printer + # + # \param reply The reply from the printer, a json file def sendMissingMaterials(self, reply: QNetworkReply) -> None: if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200: # Got an error from the HTTP request. Logger.log("e", "Couldn't request current material storage on printer. Not syncing materials.") @@ -50,7 +54,7 @@ class SendMaterialJob(Job): # Collect local materials local_materials_by_guid = self._getLocalMaterials() - # Find out what materials are new or updated annd must be sent to the printer + # Find out what materials are new or updated and must be sent to the printer materials_to_send = { material.id for guid, material in local_materials_by_guid.items() @@ -61,7 +65,13 @@ class SendMaterialJob(Job): # Send materials to the printer self.sendMaterialsToPrinter(materials_to_send) - def sendMaterialsToPrinter(self, materials_to_send): + ## Send the materials to the printer + # + # The given materials will be loaded from disk en sent to to printer. The given id's will be mathed with + # filenames of the locally stored materials + # + # \param materials_to_send A set with id's of materials that must be sent + def sendMaterialsToPrinter(self, materials_to_send) -> None: for file_path in Resources.getAllResourcesOfType(CuraApplication.ResourceTypes.MaterialInstanceContainer): try: mime_type = MimeTypeDatabase.getMimeTypeForFile(file_path) @@ -90,7 +100,8 @@ class SendMaterialJob(Job): Logger.log("d", "Syncing material {material_id} with cluster.".format(material_id = material_id)) self.device.postFormWithParts(target = "materials/", parts = parts, on_finished = self.sendingFinished) - def sendingFinished(self, reply: QNetworkReply): + ## Check a reply from an upload to the printer and log an error when the call failed + def sendingFinished(self, reply: QNetworkReply) -> None: if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200: Logger.log("e", "Received error code from printer when syncing material: {code}".format( code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute))) From 22aac7b5665e6dbcda2107be8e0831b11e8d28b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Fri, 16 Nov 2018 16:35:08 +0100 Subject: [PATCH 0226/1240] Renamed TestContainerRegistry to ContainerRegistryMock --- .../UM3NetworkPrinting/tests/TestSendMaterialJob.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py index 172b42cb8b..03d2a81e89 100644 --- a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py @@ -26,7 +26,7 @@ def new_log(*args): _logentries.append(args) -class TestContainerRegistry(ContainerRegistryInterface): +class ContainerRegistryMock(ContainerRegistryInterface): def __init__(self): self.containersMetaData = None @@ -156,7 +156,7 @@ class TestSendMaterialJob(TestCase): reply_mock.attribute.return_value = 200 reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTEMATERIAL_WHITE]).encode("ascii")) - containerRegistry = TestContainerRegistry() + containerRegistry = ContainerRegistryMock() localMaterialWhiteWithInvalidVersion = self._LOCALMATERIAL_WHITE.copy() localMaterialWhiteWithInvalidVersion["version"] = "one" containerRegistry.setContainersMetadata([localMaterialWhiteWithInvalidVersion]) @@ -175,7 +175,7 @@ class TestSendMaterialJob(TestCase): reply_mock.attribute.return_value = 200 reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTEMATERIAL_WHITE]).encode("ascii")) - containerRegistry = TestContainerRegistry() + containerRegistry = ContainerRegistryMock() localMaterialWhiteWithHigherVersion = self._LOCALMATERIAL_WHITE.copy() localMaterialWhiteWithHigherVersion["version"] = "2" containerRegistry.setContainersMetadata([self._LOCALMATERIAL_WHITE, localMaterialWhiteWithHigherVersion]) @@ -195,7 +195,7 @@ class TestSendMaterialJob(TestCase): reply_mock.readAll.return_value = QByteArray( json.dumps([self._REMOTEMATERIAL_WHITE]).encode("ascii")) - containerRegistry = TestContainerRegistry() + containerRegistry = ContainerRegistryMock() containerRegistry.setContainersMetadata([self._LOCALMATERIAL_WHITE, self._LOCALMATERIAL_BLACK]) with mock.patch.object(Logger, "log", new=new_log): @@ -266,7 +266,7 @@ class TestSendMaterialJob(TestCase): SendMaterialJob._parseReply(reply_mock) def test__getLocalMaterials(self): - containerRegistry = TestContainerRegistry() + containerRegistry = ContainerRegistryMock() containerRegistry.setContainersMetadata([self._LOCALMATERIAL_WHITE, self._LOCALMATERIAL_BLACK]) with mock.patch.object(Logger, "log", new=new_log): @@ -276,7 +276,7 @@ class TestSendMaterialJob(TestCase): self.assertTrue(len(local_materials) == 2) def test__getLocalMaterialsWithMultipleVersions(self): - containerRegistry = TestContainerRegistry() + containerRegistry = ContainerRegistryMock() localMaterialWithNewerVersion = self._LOCALMATERIAL_WHITE.copy() localMaterialWithNewerVersion["version"] = 2 containerRegistry.setContainersMetadata([self._LOCALMATERIAL_WHITE, localMaterialWithNewerVersion]) From f10bd28c4ae8084f9b8f8f343b3b8471a764a2a4 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 16 Nov 2018 17:25:32 +0100 Subject: [PATCH 0227/1240] Improve grammar and do similar fix for extruder end g-code Contributes to issue CURA-5906. --- resources/definitions/fdmextruder.def.json | 4 ++-- resources/definitions/fdmprinter.def.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/definitions/fdmextruder.def.json b/resources/definitions/fdmextruder.def.json index 55556f5764..cb49b1e128 100644 --- a/resources/definitions/fdmextruder.def.json +++ b/resources/definitions/fdmextruder.def.json @@ -78,7 +78,7 @@ "machine_extruder_start_code": { "label": "Extruder Start G-Code", - "description": "Start g-code to execute whenever this extruder is switched to.", + "description": "Start g-code to execute when switching to this extruder.", "type": "str", "default_value": "", "settable_per_mesh": false, @@ -124,7 +124,7 @@ "machine_extruder_end_code": { "label": "Extruder End G-Code", - "description": "End g-code to execute whenever turning the extruder off.", + "description": "End g-code to execute when switching away from this extruder.", "type": "str", "default_value": "", "settable_per_mesh": false, diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index b1812ae705..c015ab8ccb 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -2406,7 +2406,7 @@ "switch_extruder_retraction_amount": { "label": "Nozzle Switch Retraction Distance", - "description": "The amount of retraction when switching extruder. Set to 0 for no retraction at all. This should generally be the same as the length of the heat zone.", + "description": "The amount of retraction when switching extruders. Set to 0 for no retraction at all. This should generally be the same as the length of the heat zone.", "type": "float", "unit": "mm", "enabled": "retraction_enable", From e3ebf89092333942ce6ae542c46b570d1ea22593 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 16 Nov 2018 17:26:10 +0100 Subject: [PATCH 0228/1240] Ensure that width of popup does not get set to 0 CURA-5785 --- plugins/PreviewStage/PreviewMenu.qml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index a3cd7cb76b..b73c1088ae 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -83,8 +83,12 @@ Item id: viewSelectorPopup width: viewSelector.width - 2 * UM.Theme.getSize("default_margin").width - // For some reason the height of the column gets set to 0 if this is not set... - Component.onCompleted: height = implicitHeight + // For some reason the height/width of the column gets set to 0 if this is not set... + Component.onCompleted: + { + height = implicitHeight + width = viewSelector.width - 2 * UM.Theme.getSize("default_margin").width + } Repeater { From 6fe89e3d54980d403b2b42f9ace88ed813345028 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 19 Nov 2018 10:33:19 +0100 Subject: [PATCH 0229/1240] Fix width of simulationView component not being set correctly CURA-5785 --- plugins/SimulationView/SimulationViewMenuComponent.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/SimulationView/SimulationViewMenuComponent.qml b/plugins/SimulationView/SimulationViewMenuComponent.qml index c20e141734..89615f43a4 100644 --- a/plugins/SimulationView/SimulationViewMenuComponent.qml +++ b/plugins/SimulationView/SimulationViewMenuComponent.qml @@ -64,7 +64,7 @@ Cura.ExpandableComponent property bool only_show_top_layers: UM.Preferences.getValue("view/only_show_top_layers") property int top_layer_count: UM.Preferences.getValue("view/top_layer_count") - width: base.width - 2 * UM.Theme.getSize("default_margin").width + width: UM.Theme.getSize("layerview_menu_size").width - 2 * UM.Theme.getSize("default_margin").width height: childrenRect.height spacing: UM.Theme.getSize("layerview_row_spacing").height From 23e957e1c5c8fdbb36368f319feb57218dd7ab92 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 19 Nov 2018 10:44:24 +0100 Subject: [PATCH 0230/1240] Some more refactoring, splitting up methods --- .../NetworkedPrinterOutputDevice.py | 3 + .../UM3NetworkPrinting/src/SendMaterialJob.py | 180 +++++++++++------- 2 files changed, 115 insertions(+), 68 deletions(-) diff --git a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py index 35d2ce014a..9a3be936a2 100644 --- a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py +++ b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py @@ -147,6 +147,9 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): request.setHeader(QNetworkRequest.UserAgentHeader, self._user_agent) return request + def createFormPart(self, content_header: str, data: bytes, content_type: Optional[str] = None) -> QHttpPart: + return self._createFormPart(content_header, data, content_type) + def _createFormPart(self, content_header: str, data: bytes, content_type: Optional[str] = None) -> QHttpPart: part = QHttpPart() diff --git a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py index 62b98bcdbd..6763901151 100644 --- a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py @@ -1,21 +1,20 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +import json +import os +import urllib.parse +from typing import Dict, TYPE_CHECKING, Set -import json # To decode the list of materials from the printer reply. -import os # To walk over material files. -import os.path # To filter on material files. -import urllib.parse # For getting material IDs from their file names. -from typing import Dict, TYPE_CHECKING +from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest -from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest # To listen to the reply from the printer. - -from UM.Job import Job # The interface we're implementing. +from UM.Job import Job from UM.Logger import Logger -from UM.MimeTypeDatabase import MimeTypeDatabase # To strip the extensions of the material profile files. +from UM.MimeTypeDatabase import MimeTypeDatabase from UM.Resources import Resources -from UM.Settings.ContainerRegistry import ContainerRegistry # To find the GUIDs of materials. -from cura.CuraApplication import CuraApplication # For the resource types. -from plugins.UM3NetworkPrinting.src.Models import ClusterMaterial, LocalMaterial +from cura.CuraApplication import CuraApplication + +# Absolute imports don't work in plugins +from .Models import ClusterMaterial, LocalMaterial if TYPE_CHECKING: from .ClusterUM3OutputDevice import ClusterUM3OutputDevice @@ -25,95 +24,136 @@ if TYPE_CHECKING: # # This way it won't freeze up the interface while sending those materials. class SendMaterialJob(Job): + def __init__(self, device: "ClusterUM3OutputDevice") -> None: super().__init__() self.device = device # type: ClusterUM3OutputDevice + self._application = CuraApplication.getInstance() # type: CuraApplication ## Send the request to the printer and register a callback def run(self) -> None: self.device.get("materials/", on_finished = self.sendMissingMaterials) - ## Process the reply from the printer and determine which materials should be updated and sent to the printer + ## Process the materials reply from the printer. # - # \param reply The reply from the printer, a json file - def sendMissingMaterials(self, reply: QNetworkReply) -> None: - if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200: # Got an error from the HTTP request. - Logger.log("e", "Couldn't request current material storage on printer. Not syncing materials.") + # \param reply The reply from the printer, a json file. + def _onGetRemoteMaterials(self, reply: QNetworkReply) -> None: + + # Got an error from the HTTP request. If we did not receive a 200 something happened. + if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200: + Logger.log("e", "Error fetching materials from printer: %s", reply.errorString()) return - # Collect materials from the printer's reply + # Collect materials from the printer's reply and send the missing ones if needed. try: remote_materials_by_guid = self._parseReply(reply) - except json.JSONDecodeError: - Logger.log("e", "Request material storage on printer: I didn't understand the printer's answer.") - return - except KeyError: - Logger.log("e", "Request material storage on printer: Printer's answer was missing GUIDs.") - return + self._sendMissingMaterials(remote_materials_by_guid) + except json.JSONDecodeError as e: + Logger.log("e", "Error parsing materials from printer: %s", e) + except KeyError as e: + Logger.log("e", "Error parsing materials from printer: %s", e) + + ## Determine which materials should be updated and send them to the printer. + # + # \param remote_materials_by_guid The remote materials by GUID. + def _sendMissingMaterials(self, remote_materials_by_guid: Dict[str, ClusterMaterial]) -> None: # Collect local materials local_materials_by_guid = self._getLocalMaterials() + if len(local_materials_by_guid) == 0: + Logger.log("d", "There are no local materials to synchronize with the printer.") + return # Find out what materials are new or updated and must be sent to the printer - materials_to_send = { - material.id - for guid, material in local_materials_by_guid.items() - if guid not in remote_materials_by_guid or - material.version > remote_materials_by_guid[guid].version - } + material_ids_to_send = self._determineMaterialsToSend(local_materials_by_guid, remote_materials_by_guid) + if len(material_ids_to_send) == 0: + Logger.log("d", "There are no remote materials to update.") + return # Send materials to the printer - self.sendMaterialsToPrinter(materials_to_send) + self._sendMaterials(material_ids_to_send) - ## Send the materials to the printer + ## From the local and remote materials, determine which ones should be synchronized. # - # The given materials will be loaded from disk en sent to to printer. The given id's will be mathed with - # filenames of the locally stored materials + # Makes a Set containing only the materials that are not on the printer yet or the ones that are newer in Cura. # - # \param materials_to_send A set with id's of materials that must be sent - def sendMaterialsToPrinter(self, materials_to_send) -> None: - for file_path in Resources.getAllResourcesOfType(CuraApplication.ResourceTypes.MaterialInstanceContainer): + # \param local_materials The local materials by GUID. + # \param remote_materials The remote materials by GUID. + @staticmethod + def _determineMaterialsToSend(local_materials: Dict[str, LocalMaterial], + remote_materials: Dict[str, ClusterMaterial]) -> Set[str]: + return { + material.id for guid, material in local_materials.items() + if guid not in remote_materials or material.version > remote_materials[guid].version + } + + ## Send the materials to the printer. + # + # The given materials will be loaded from disk en sent to to printer. + # The given id's will be matched with filenames of the locally stored materials. + # + # \param materials_to_send A set with id's of materials that must be sent. + def _sendMaterials(self, materials_to_send: Set[str]) -> None: + file_paths = Resources.getAllResourcesOfType(CuraApplication.ResourceTypes.MaterialInstanceContainer) + + # Find all local material files and send them if needed. + for file_path in file_paths: try: mime_type = MimeTypeDatabase.getMimeTypeForFile(file_path) except MimeTypeDatabase.MimeTypeNotFoundError: - continue # Not the sort of file we'd like to send then. - - _, file_name = os.path.split(file_path) - material_id = urllib.parse.unquote_plus(mime_type.stripExtension(file_name)) - - if material_id not in materials_to_send: continue + file_name = os.path.basename(file_path) + material_id = urllib.parse.unquote_plus(mime_type.stripExtension(file_name)) + if material_id not in materials_to_send: + # If the material does not have to be sent we skip it. + continue + + self._sendMaterialFile(file_path, file_name, material_id) + + ## Send a single material file to the printer. + # + # Also add the material signature file if that is available. + # + # \param file_path The path of the material file. + # \param file_name The name of the material file. + # \param material_id The ID of the material in the file. + def _sendMaterialFile(self, file_path: str, file_name: str, material_id: str) -> None: + parts = [] + + # Add the material file. with open(file_path, "rb") as f: - parts.append( - self.device._createFormPart("name=\"file\"; filename=\"{file_name}\"".format(file_name = file_name), - f.read())) - signature_file_path = file_path + ".sig" + parts.append(self.device.createFormPart("name=\"file\"; filename=\"{file_name}\"" + .format(file_name = file_name), f.read())) + + # Add the material signature file if needed. + signature_file_path = "{}.sig".format(file_path) if os.path.exists(signature_file_path): - _, signature_file_name = os.path.split(signature_file_path) + signature_file_name = os.path.basename(signature_file_path) with open(signature_file_path, "rb") as f: - parts.append(self.device._createFormPart( - "name=\"signature_file\"; filename=\"{file_name}\"".format(file_name = signature_file_name), - f.read())) + parts.append(self.device.createFormPart("name=\"signature_file\"; filename=\"{file_name}\"" + .format(file_name = signature_file_name), f.read())) Logger.log("d", "Syncing material {material_id} with cluster.".format(material_id = material_id)) self.device.postFormWithParts(target = "materials/", parts = parts, on_finished = self.sendingFinished) ## Check a reply from an upload to the printer and log an error when the call failed - def sendingFinished(self, reply: QNetworkReply) -> None: + @staticmethod + def sendingFinished(reply: QNetworkReply) -> None: if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200: - Logger.log("e", "Received error code from printer when syncing material: {code}".format( - code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute))) - Logger.log("e", reply.readAll().data().decode("utf-8")) + Logger.log("e", "Received error code from printer when syncing material: {code}, {text}".format( + code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute), + text = reply.errorString() + )) ## Parse the reply from the printer # # Parses the reply to a "/materials" request to the printer # - # \return a dictionary of ClustMaterial objects by GUID + # \return a dictionary of ClusterMaterial objects by GUID # \throw json.JSONDecodeError Raised when the reply does not contain a valid json string - # \throw KeyErrror Raised when on of the materials does not include a valid guid + # \throw KeyError Raised when on of the materials does not include a valid guid @classmethod def _parseReply(cls, reply: QNetworkReply) -> Dict[str, ClusterMaterial]: remote_materials_list = json.loads(reply.readAll().data().decode("utf-8")) @@ -124,17 +164,21 @@ class SendMaterialJob(Job): # Only the new newest version of the local materials is returned # # \return a dictionary of LocalMaterial objects by GUID - @classmethod - def _getLocalMaterials(cls): - result = {} - for material in ContainerRegistry.getInstance().findContainersMetadata(type = "material"): - try: - localMaterial = LocalMaterial(**material) + def _getLocalMaterials(self) -> Dict[str, LocalMaterial]: + result = {} # type: Dict[str, LocalMaterial] + container_registry = self._application.getContainerRegistry() + material_containers = container_registry.findContainersMetadata(type = "material") - if localMaterial.GUID not in result or localMaterial.version > result.get(localMaterial.GUID).version: - result[localMaterial.GUID] = localMaterial - except (ValueError): - Logger.log("e", "Material {material_id} has invalid version number {number}.".format( - material_id = material["id"], number = material["version"])) + # Find the latest version of all material containers in the registry. + for m in material_containers: + try: + material = LocalMaterial(**m) + if material.GUID not in result or material.version > result.get(material.GUID).version: + result[material.GUID] = material + except ValueError as e: + Logger.log("w", "Local material {material_id} has invalid values: {e}".format( + material_id = m["id"], + e = e + )) return result From ee9210d8d1ce895d22636c74d8bf3218f39460f2 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 19 Nov 2018 10:57:47 +0100 Subject: [PATCH 0231/1240] Rewrite tests --- .../UM3NetworkPrinting/src/SendMaterialJob.py | 2 +- .../tests/TestSendMaterialJob.py | 417 +++++++++--------- 2 files changed, 198 insertions(+), 221 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py index 6763901151..349e6929ff 100644 --- a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py @@ -32,7 +32,7 @@ class SendMaterialJob(Job): ## Send the request to the printer and register a callback def run(self) -> None: - self.device.get("materials/", on_finished = self.sendMissingMaterials) + self.device.get("materials/", on_finished = self._onGetRemoteMaterials) ## Process the materials reply from the printer. # diff --git a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py index 03d2a81e89..bc6e1def14 100644 --- a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py @@ -1,6 +1,5 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. - import io import json from typing import Any, List @@ -17,16 +16,9 @@ from plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice import ClusterUM3Outp from plugins.UM3NetworkPrinting.src.Models import ClusterMaterial from plugins.UM3NetworkPrinting.src.SendMaterialJob import SendMaterialJob -# All log entries written to Log.log by the class-under-test are written to this list. It is cleared before each test -# run and check afterwards -_logentries = [] - - -def new_log(*args): - _logentries.append(args) - class ContainerRegistryMock(ContainerRegistryInterface): + def __init__(self): self.containersMetaData = None @@ -59,14 +51,16 @@ class FakeDevice(ClusterUM3OutputDevice): class TestSendMaterialJob(TestCase): - _LOCALMATERIAL_WHITE = {'type': 'material', 'status': 'unknown', 'id': 'generic_pla_white', + + _LOCAL_MATERIAL_WHITE = {'type': 'material', 'status': 'unknown', 'id': 'generic_pla_white', 'base_file': 'generic_pla_white', 'setting_version': 5, 'name': 'White PLA', 'brand': 'Generic', 'material': 'PLA', 'color_name': 'White', 'GUID': 'badb0ee7-87c8-4f3f-9398-938587b67dce', 'version': '1', 'color_code': '#ffffff', 'description': 'Test PLA White', 'adhesion_info': 'Use glue.', 'approximate_diameter': '3', 'properties': {'density': '1.00', 'diameter': '2.85', 'weight': '750'}, 'definition': 'fdmprinter', 'compatible': True} - _LOCALMATERIAL_BLACK = {'type': 'material', 'status': 'unknown', 'id': 'generic_pla_black', + + _LOCAL_MATERIAL_BLACK = {'type': 'material', 'status': 'unknown', 'id': 'generic_pla_black', 'base_file': 'generic_pla_black', 'setting_version': 5, 'name': 'Yellow CPE', 'brand': 'Ultimaker', 'material': 'CPE', 'color_name': 'Black', 'GUID': '5fbb362a-41f9-4818-bb43-15ea6df34aa4', 'version': '1', 'color_code': '#000000', @@ -74,7 +68,7 @@ class TestSendMaterialJob(TestCase): 'properties': {'density': '1.01', 'diameter': '2.85', 'weight': '750'}, 'definition': 'fdmprinter', 'compatible': True} - _REMOTEMATERIAL_WHITE = { + _REMOTE_MATERIAL_WHITE = { "guid": "badb0ee7-87c8-4f3f-9398-938587b67dce", "material": "PLA", "brand": "Generic", @@ -82,7 +76,8 @@ class TestSendMaterialJob(TestCase): "color": "White", "density": 1.00 } - _REMOTEMATERIAL_BLACK = { + + _REMOTE_MATERIAL_BLACK = { "guid": "5fbb362a-41f9-4818-bb43-15ea6df34aa4", "material": "PLA", "brand": "Generic", @@ -91,224 +86,206 @@ class TestSendMaterialJob(TestCase): "density": 1.00 } - def setUp(self): - # Make sure the we start with clean (log) slate - _logentries.clear() - - def tearDown(self): - # If there are still log entries that were not checked something is wrong or we must add checks for them - self.assertEqual(len(_logentries), 0) - @patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") def test_run(self, device_mock): - with mock.patch.object(Logger, 'log', new=new_log): - job = SendMaterialJob(device_mock) - job.run() - - device_mock.get.assert_called_with("materials/", on_finished=job.sendMissingMaterials) - self.assertEqual(0, len(_logentries)) + job = SendMaterialJob(device_mock) + job.run() + device_mock.get.assert_called_with("materials/", on_finished=job._onGetRemoteMaterials) + @patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") @patch("PyQt5.QtNetwork.QNetworkReply") - def test_sendMissingMaterials_withFailedRequest(self, reply_mock): + def test_sendMissingMaterials_withFailedRequest(self, reply_mock, device_mock): reply_mock.attribute.return_value = 404 - - with mock.patch.object(Logger, 'log', new=new_log): - SendMaterialJob(None).sendMissingMaterials(reply_mock) - + SendMaterialJob(device_mock).run() reply_mock.attribute.assert_called_with(0) self.assertEqual(reply_mock.method_calls, [call.attribute(0)]) - self._assertLogEntries([('e', "Couldn't request current material storage on printer. Not syncing materials.")], - _logentries) + self.assertEqual(device_mock._onGetRemoteMaterials.method_calls, []) - @patch("PyQt5.QtNetwork.QNetworkReply") - def test_sendMissingMaterials_withBadJsonAnswer(self, reply_mock): - reply_mock.attribute.return_value = 200 - reply_mock.readAll.return_value = QByteArray(b'Six sick hicks nick six slick bricks with picks and sticks.') - - with mock.patch.object(Logger, 'log', new=new_log): - SendMaterialJob(None).sendMissingMaterials(reply_mock) - - reply_mock.attribute.assert_called_with(0) - self.assertEqual(reply_mock.method_calls, [call.attribute(0), call.readAll()]) - self._assertLogEntries( - [('e', "Request material storage on printer: I didn't understand the printer's answer.")], - _logentries) - - @patch("PyQt5.QtNetwork.QNetworkReply") - def test_sendMissingMaterials_withMissingGuid(self, reply_mock): - reply_mock.attribute.return_value = 200 - remoteMaterialWithoutGuid = self._REMOTEMATERIAL_WHITE.copy() - del remoteMaterialWithoutGuid["guid"] - reply_mock.readAll.return_value = QByteArray(json.dumps([remoteMaterialWithoutGuid]).encode("ascii")) - - with mock.patch.object(Logger, 'log', new=new_log): - SendMaterialJob(None).sendMissingMaterials(reply_mock) - - reply_mock.attribute.assert_called_with(0) - self.assertEqual(reply_mock.method_calls, [call.attribute(0), call.readAll()]) - self._assertLogEntries( - [('e', "Request material storage on printer: Printer's answer was missing GUIDs.")], - _logentries) - - @patch("UM.Resources.Resources.getAllResourcesOfType", lambda _: []) - @patch("PyQt5.QtNetwork.QNetworkReply") - def test_sendMissingMaterials_WithInvalidVersionInLocalMaterial(self, reply_mock): - reply_mock.attribute.return_value = 200 - reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTEMATERIAL_WHITE]).encode("ascii")) - - containerRegistry = ContainerRegistryMock() - localMaterialWhiteWithInvalidVersion = self._LOCALMATERIAL_WHITE.copy() - localMaterialWhiteWithInvalidVersion["version"] = "one" - containerRegistry.setContainersMetadata([localMaterialWhiteWithInvalidVersion]) - - with mock.patch.object(Logger, "log", new=new_log): - with mock.patch.object(ContainerRegistry, "getInstance", lambda: containerRegistry): - SendMaterialJob(None).sendMissingMaterials(reply_mock) - - reply_mock.attribute.assert_called_with(0) - self.assertEqual(reply_mock.method_calls, [call.attribute(0), call.readAll()]) - self._assertLogEntries([('e', "Material generic_pla_white has invalid version number one.")], _logentries) - - @patch("UM.Resources.Resources.getAllResourcesOfType", lambda _: []) - @patch("PyQt5.QtNetwork.QNetworkReply") - def test_sendMissingMaterials_WithMultipleLocalVersionsLowFirst(self, reply_mock): - reply_mock.attribute.return_value = 200 - reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTEMATERIAL_WHITE]).encode("ascii")) - - containerRegistry = ContainerRegistryMock() - localMaterialWhiteWithHigherVersion = self._LOCALMATERIAL_WHITE.copy() - localMaterialWhiteWithHigherVersion["version"] = "2" - containerRegistry.setContainersMetadata([self._LOCALMATERIAL_WHITE, localMaterialWhiteWithHigherVersion]) - - with mock.patch.object(Logger, "log", new=new_log): - with mock.patch.object(ContainerRegistry, "getInstance", lambda: containerRegistry): - SendMaterialJob(None).sendMissingMaterials(reply_mock) - - reply_mock.attribute.assert_called_with(0) - self.assertEqual(reply_mock.method_calls, [call.attribute(0), call.readAll()]) - self._assertLogEntries([], _logentries) - - @patch("UM.Resources.Resources.getAllResourcesOfType", lambda _: []) - @patch("PyQt5.QtNetwork.QNetworkReply") - def test_sendMissingMaterials_MaterialMissingOnPrinter(self, reply_mock): - reply_mock.attribute.return_value = 200 - reply_mock.readAll.return_value = QByteArray( - json.dumps([self._REMOTEMATERIAL_WHITE]).encode("ascii")) - - containerRegistry = ContainerRegistryMock() - containerRegistry.setContainersMetadata([self._LOCALMATERIAL_WHITE, self._LOCALMATERIAL_BLACK]) - - with mock.patch.object(Logger, "log", new=new_log): - with mock.patch.object(ContainerRegistry, "getInstance", lambda: containerRegistry): - SendMaterialJob(None).sendMissingMaterials(reply_mock) - - reply_mock.attribute.assert_called_with(0) - self.assertEqual(reply_mock.method_calls, [call.attribute(0), call.readAll()]) - self._assertLogEntries([], _logentries) - - @patch("builtins.open", lambda a, b: io.StringIO("")) - @patch("UM.MimeTypeDatabase.MimeTypeDatabase.getMimeTypeForFile", - lambda _: MimeType(name="application/x-ultimaker-material-profile", comment="Ultimaker Material Profile", - suffixes=["xml.fdm_material"])) - @patch("UM.Resources.Resources.getAllResourcesOfType", lambda _: ["/materials/generic_pla_white.xml.fdm_material"]) @patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") - def test_sendMaterialsToPrinter(self, device_mock): - device_mock._createFormPart.return_value = "_xXx_" - with mock.patch.object(Logger, "log", new=new_log): - job = SendMaterialJob(device_mock) - job.sendMaterialsToPrinter({'generic_pla_white'}) - - self._assertLogEntries([("d", "Syncing material generic_pla_white with cluster.")], _logentries) - self.assertEqual([call._createFormPart('name="file"; filename="generic_pla_white.xml.fdm_material"', ''), - call.postFormWithParts(on_finished=job.sendingFinished, parts = ["_xXx_"], target = "materials/")], device_mock.method_calls) - @patch("PyQt5.QtNetwork.QNetworkReply") - def test_sendingFinished_success(self, reply_mock) -> None: + def test_sendMissingMaterials_withBadJsonAnswer(self, reply_mock, device_mock): reply_mock.attribute.return_value = 200 - with mock.patch.object(Logger, 'log', new=new_log): - SendMaterialJob(None).sendingFinished(reply_mock) - - reply_mock.attribute.assert_called_once_with(0) - self.assertEqual(0, len(_logentries)) - - @patch("PyQt5.QtNetwork.QNetworkReply") - def test_sendingFinished_failed(self, reply_mock) -> None: - reply_mock.attribute.return_value = 404 reply_mock.readAll.return_value = QByteArray(b'Six sick hicks nick six slick bricks with picks and sticks.') - - with mock.patch.object(Logger, 'log', new=new_log): - SendMaterialJob(None).sendingFinished(reply_mock) - + SendMaterialJob(device_mock).run() reply_mock.attribute.assert_called_with(0) - self.assertEqual(reply_mock.method_calls, [call.attribute(0), call.attribute(0), call.readAll()]) + self.assertEqual(reply_mock.method_calls, [call.attribute(0), call.readAll()]) + self.assertEqual(device_mock._onGetRemoteMaterials.method_calls, []) - self._assertLogEntries([ - ("e", "Received error code from printer when syncing material: 404"), - ("e", "Six sick hicks nick six slick bricks with picks and sticks.") - ], _logentries) - - @patch("PyQt5.QtNetwork.QNetworkReply") - def test_parseReply(self, reply_mock): - reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTEMATERIAL_WHITE]).encode("ascii")) - - response = SendMaterialJob._parseReply(reply_mock) - - self.assertTrue(len(response) == 1) - self.assertEqual(next(iter(response.values())), ClusterMaterial(**self._REMOTEMATERIAL_WHITE)) - - @patch("PyQt5.QtNetwork.QNetworkReply") - def test_parseReplyWithInvalidMaterial(self, reply_mock): - remoteMaterialWithInvalidVersion = self._REMOTEMATERIAL_WHITE.copy() - remoteMaterialWithInvalidVersion["version"] = "one" - reply_mock.readAll.return_value = QByteArray(json.dumps([remoteMaterialWithInvalidVersion]).encode("ascii")) - - with self.assertRaises(ValueError): - SendMaterialJob._parseReply(reply_mock) - - def test__getLocalMaterials(self): - containerRegistry = ContainerRegistryMock() - containerRegistry.setContainersMetadata([self._LOCALMATERIAL_WHITE, self._LOCALMATERIAL_BLACK]) - - with mock.patch.object(Logger, "log", new=new_log): - with mock.patch.object(ContainerRegistry, "getInstance", lambda: containerRegistry): - local_materials = SendMaterialJob(None)._getLocalMaterials() - - self.assertTrue(len(local_materials) == 2) - - def test__getLocalMaterialsWithMultipleVersions(self): - containerRegistry = ContainerRegistryMock() - localMaterialWithNewerVersion = self._LOCALMATERIAL_WHITE.copy() - localMaterialWithNewerVersion["version"] = 2 - containerRegistry.setContainersMetadata([self._LOCALMATERIAL_WHITE, localMaterialWithNewerVersion]) - - with mock.patch.object(Logger, "log", new=new_log): - with mock.patch.object(ContainerRegistry, "getInstance", lambda: containerRegistry): - local_materials = SendMaterialJob(None)._getLocalMaterials() - - self.assertTrue(len(local_materials) == 1) - self.assertTrue(list(local_materials.values())[0].version == 2) - - containerRegistry.setContainersMetadata([localMaterialWithNewerVersion, self._LOCALMATERIAL_WHITE]) - - with mock.patch.object(Logger, "log", new=new_log): - with mock.patch.object(ContainerRegistry, "getInstance", lambda: containerRegistry): - local_materials = SendMaterialJob(None)._getLocalMaterials() - - self.assertTrue(len(local_materials) == 1) - self.assertTrue(list(local_materials.values())[0].version == 2) - - def _assertLogEntries(self, first, second): - """ - Inspects the two sets of log entry tuples and fails when they are not the same - :param first: The first set of tuples - :param second: The second set of tuples - """ - self.assertEqual(len(first), len(second)) - - while len(first) > 0: - e1, m1 = first[0] - e2, m2 = second[0] - self.assertEqual(e1, e2) - self.assertEqual(m1, m2) - first.pop(0) - second.pop(0) + # @patch("PyQt5.QtNetwork.QNetworkReply") + # def test_sendMissingMaterials_withMissingGuid(self, reply_mock): + # reply_mock.attribute.return_value = 200 + # remoteMaterialWithoutGuid = self._REMOTEMATERIAL_WHITE.copy() + # del remoteMaterialWithoutGuid["guid"] + # reply_mock.readAll.return_value = QByteArray(json.dumps([remoteMaterialWithoutGuid]).encode("ascii")) + # + # with mock.patch.object(Logger, 'log', new=new_log): + # SendMaterialJob(None).sendMissingMaterials(reply_mock) + # + # reply_mock.attribute.assert_called_with(0) + # self.assertEqual(reply_mock.method_calls, [call.attribute(0), call.readAll()]) + # self._assertLogEntries( + # [('e', "Request material storage on printer: Printer's answer was missing GUIDs.")], + # _logentries) + # + # @patch("UM.Resources.Resources.getAllResourcesOfType", lambda _: []) + # @patch("PyQt5.QtNetwork.QNetworkReply") + # def test_sendMissingMaterials_WithInvalidVersionInLocalMaterial(self, reply_mock): + # reply_mock.attribute.return_value = 200 + # reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTEMATERIAL_WHITE]).encode("ascii")) + # + # containerRegistry = ContainerRegistryMock() + # localMaterialWhiteWithInvalidVersion = self._LOCALMATERIAL_WHITE.copy() + # localMaterialWhiteWithInvalidVersion["version"] = "one" + # containerRegistry.setContainersMetadata([localMaterialWhiteWithInvalidVersion]) + # + # with mock.patch.object(Logger, "log", new=new_log): + # with mock.patch.object(ContainerRegistry, "getInstance", lambda: containerRegistry): + # SendMaterialJob(None).sendMissingMaterials(reply_mock) + # + # reply_mock.attribute.assert_called_with(0) + # self.assertEqual(reply_mock.method_calls, [call.attribute(0), call.readAll()]) + # self._assertLogEntries([('e', "Material generic_pla_white has invalid version number one.")], _logentries) + # + # @patch("UM.Resources.Resources.getAllResourcesOfType", lambda _: []) + # @patch("PyQt5.QtNetwork.QNetworkReply") + # def test_sendMissingMaterials_WithMultipleLocalVersionsLowFirst(self, reply_mock): + # reply_mock.attribute.return_value = 200 + # reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTEMATERIAL_WHITE]).encode("ascii")) + # + # containerRegistry = ContainerRegistryMock() + # localMaterialWhiteWithHigherVersion = self._LOCALMATERIAL_WHITE.copy() + # localMaterialWhiteWithHigherVersion["version"] = "2" + # containerRegistry.setContainersMetadata([self._LOCALMATERIAL_WHITE, localMaterialWhiteWithHigherVersion]) + # + # with mock.patch.object(Logger, "log", new=new_log): + # with mock.patch.object(ContainerRegistry, "getInstance", lambda: containerRegistry): + # SendMaterialJob(None).sendMissingMaterials(reply_mock) + # + # reply_mock.attribute.assert_called_with(0) + # self.assertEqual(reply_mock.method_calls, [call.attribute(0), call.readAll()]) + # self._assertLogEntries([], _logentries) + # + # @patch("UM.Resources.Resources.getAllResourcesOfType", lambda _: []) + # @patch("PyQt5.QtNetwork.QNetworkReply") + # def test_sendMissingMaterials_MaterialMissingOnPrinter(self, reply_mock): + # reply_mock.attribute.return_value = 200 + # reply_mock.readAll.return_value = QByteArray( + # json.dumps([self._REMOTEMATERIAL_WHITE]).encode("ascii")) + # + # containerRegistry = ContainerRegistryMock() + # containerRegistry.setContainersMetadata([self._LOCALMATERIAL_WHITE, self._LOCALMATERIAL_BLACK]) + # + # with mock.patch.object(Logger, "log", new=new_log): + # with mock.patch.object(ContainerRegistry, "getInstance", lambda: containerRegistry): + # SendMaterialJob(None).sendMissingMaterials(reply_mock) + # + # reply_mock.attribute.assert_called_with(0) + # self.assertEqual(reply_mock.method_calls, [call.attribute(0), call.readAll()]) + # self._assertLogEntries([], _logentries) + # + # @patch("builtins.open", lambda a, b: io.StringIO("")) + # @patch("UM.MimeTypeDatabase.MimeTypeDatabase.getMimeTypeForFile", + # lambda _: MimeType(name="application/x-ultimaker-material-profile", comment="Ultimaker Material Profile", + # suffixes=["xml.fdm_material"])) + # @patch("UM.Resources.Resources.getAllResourcesOfType", lambda _: ["/materials/generic_pla_white.xml.fdm_material"]) + # @patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") + # def test_sendMaterialsToPrinter(self, device_mock): + # device_mock._createFormPart.return_value = "_xXx_" + # with mock.patch.object(Logger, "log", new=new_log): + # job = SendMaterialJob(device_mock) + # job.sendMaterialsToPrinter({'generic_pla_white'}) + # + # self._assertLogEntries([("d", "Syncing material generic_pla_white with cluster.")], _logentries) + # self.assertEqual([call._createFormPart('name="file"; filename="generic_pla_white.xml.fdm_material"', ''), + # call.postFormWithParts(on_finished=job.sendingFinished, parts = ["_xXx_"], target = "materials/")], device_mock.method_calls) + # + # @patch("PyQt5.QtNetwork.QNetworkReply") + # def test_sendingFinished_success(self, reply_mock) -> None: + # reply_mock.attribute.return_value = 200 + # with mock.patch.object(Logger, 'log', new=new_log): + # SendMaterialJob(None).sendingFinished(reply_mock) + # + # reply_mock.attribute.assert_called_once_with(0) + # self.assertEqual(0, len(_logentries)) + # + # @patch("PyQt5.QtNetwork.QNetworkReply") + # def test_sendingFinished_failed(self, reply_mock) -> None: + # reply_mock.attribute.return_value = 404 + # reply_mock.readAll.return_value = QByteArray(b'Six sick hicks nick six slick bricks with picks and sticks.') + # + # with mock.patch.object(Logger, 'log', new=new_log): + # SendMaterialJob(None).sendingFinished(reply_mock) + # + # reply_mock.attribute.assert_called_with(0) + # self.assertEqual(reply_mock.method_calls, [call.attribute(0), call.attribute(0), call.readAll()]) + # + # self._assertLogEntries([ + # ("e", "Received error code from printer when syncing material: 404"), + # ("e", "Six sick hicks nick six slick bricks with picks and sticks.") + # ], _logentries) + # + # @patch("PyQt5.QtNetwork.QNetworkReply") + # def test_parseReply(self, reply_mock): + # reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTEMATERIAL_WHITE]).encode("ascii")) + # + # response = SendMaterialJob._parseReply(reply_mock) + # + # self.assertTrue(len(response) == 1) + # self.assertEqual(next(iter(response.values())), ClusterMaterial(**self._REMOTEMATERIAL_WHITE)) + # + # @patch("PyQt5.QtNetwork.QNetworkReply") + # def test_parseReplyWithInvalidMaterial(self, reply_mock): + # remoteMaterialWithInvalidVersion = self._REMOTEMATERIAL_WHITE.copy() + # remoteMaterialWithInvalidVersion["version"] = "one" + # reply_mock.readAll.return_value = QByteArray(json.dumps([remoteMaterialWithInvalidVersion]).encode("ascii")) + # + # with self.assertRaises(ValueError): + # SendMaterialJob._parseReply(reply_mock) + # + # def test__getLocalMaterials(self): + # containerRegistry = ContainerRegistryMock() + # containerRegistry.setContainersMetadata([self._LOCALMATERIAL_WHITE, self._LOCALMATERIAL_BLACK]) + # + # with mock.patch.object(Logger, "log", new=new_log): + # with mock.patch.object(ContainerRegistry, "getInstance", lambda: containerRegistry): + # local_materials = SendMaterialJob(None)._getLocalMaterials() + # + # self.assertTrue(len(local_materials) == 2) + # + # def test__getLocalMaterialsWithMultipleVersions(self): + # containerRegistry = ContainerRegistryMock() + # localMaterialWithNewerVersion = self._LOCALMATERIAL_WHITE.copy() + # localMaterialWithNewerVersion["version"] = 2 + # containerRegistry.setContainersMetadata([self._LOCALMATERIAL_WHITE, localMaterialWithNewerVersion]) + # + # with mock.patch.object(Logger, "log", new=new_log): + # with mock.patch.object(ContainerRegistry, "getInstance", lambda: containerRegistry): + # local_materials = SendMaterialJob(None)._getLocalMaterials() + # + # self.assertTrue(len(local_materials) == 1) + # self.assertTrue(list(local_materials.values())[0].version == 2) + # + # containerRegistry.setContainersMetadata([localMaterialWithNewerVersion, self._LOCALMATERIAL_WHITE]) + # + # with mock.patch.object(Logger, "log", new=new_log): + # with mock.patch.object(ContainerRegistry, "getInstance", lambda: containerRegistry): + # local_materials = SendMaterialJob(None)._getLocalMaterials() + # + # self.assertTrue(len(local_materials) == 1) + # self.assertTrue(list(local_materials.values())[0].version == 2) + # + # def _assertLogEntries(self, first, second): + # """ + # Inspects the two sets of log entry tuples and fails when they are not the same + # :param first: The first set of tuples + # :param second: The second set of tuples + # """ + # self.assertEqual(len(first), len(second)) + # + # while len(first) > 0: + # e1, m1 = first[0] + # e2, m2 = second[0] + # self.assertEqual(e1, e2) + # self.assertEqual(m1, m2) + # first.pop(0) + # second.pop(0) From a490e420f09327be74f0145fb055ab5021d315c7 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 19 Nov 2018 11:42:04 +0100 Subject: [PATCH 0232/1240] Prevent the setting items from being recreated every stage switch CURA-5785 --- plugins/PrepareStage/PrepareMenu.qml | 20 +++++++++----------- plugins/PreviewStage/PreviewMenu.qml | 12 +++++++----- resources/qml/Cura.qml | 11 +++++++++++ 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index c464727dde..ef01625a22 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -13,6 +13,7 @@ Item signal showTooltip(Item item, point location, string text) signal hideTooltip() + UM.I18nCatalog { id: catalog @@ -71,7 +72,7 @@ Item { Layout.fillHeight: true Layout.fillWidth: true - Layout.preferredWidth: itemRow.width - machineSelection.width - printSetupSelector.width - 2 * UM.Theme.getSize("default_lining").width + Layout.preferredWidth: itemRow.width - machineSelection.width - printSetupSelectorItem.width - 2 * UM.Theme.getSize("default_lining").width } // Separator line @@ -82,17 +83,14 @@ Item color: UM.Theme.getColor("lining") } - Cura.PrintSetupSelector + Item { - id: printSetupSelector - - onShowTooltip: prepareMenu.showTooltip(item, location, text) - onHideTooltip: prepareMenu.hideTooltip() - - Layout.minimumWidth: UM.Theme.getSize("print_setup_widget").width - Layout.maximumWidth: UM.Theme.getSize("print_setup_widget").width - Layout.fillWidth: true - Layout.fillHeight: true + id: printSetupSelectorItem + // This is a work around to prevent the printSetupSelector from having to be re-loaded every time + // a stage switch is done. + children: [printSetupSelector] + height: childrenRect.height + width: childrenRect.width } } } diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index b73c1088ae..b331ff69c5 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -138,12 +138,14 @@ Item color: UM.Theme.getColor("lining") } - Cura.PrintSetupSelector + Item { - width: UM.Theme.getSize("print_setup_widget").width - height: parent.height - onShowTooltip: previewMenu.showTooltip(item, location, text) - onHideTooltip: previewMenu.hideTooltip() + id: printSetupSelectorItem + // This is a work around to prevent the printSetupSelector from having to be re-loaded every time + // a stage switch is done. + children: [printSetupSelector] + height: childrenRect.height + width: childrenRect.width } } } diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 0312a636a6..c239dc8d6f 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -40,6 +40,7 @@ UM.MainWindow tooltip.hide(); } + Component.onCompleted: { CuraApplication.setMinimumWindowSize(UM.Theme.getSize("window_minimum_size")) @@ -249,6 +250,16 @@ UM.MainWindow height: UM.Theme.getSize("stage_menu").height source: UM.Controller.activeStage != null ? UM.Controller.activeStage.stageMenuComponent : "" + + // The printSetupSelector is defined here so that the setting list doesn't need to get re-instantiated + // Every time the stage is changed. + property var printSetupSelector: Cura.PrintSetupSelector + { + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() + width: UM.Theme.getSize("print_setup_widget").width + height: UM.Theme.getSize("stage_menu").height + } } UM.MessageStack From 2f624cc78f18baf189e3d39c928ebc605746a6c1 Mon Sep 17 00:00:00 2001 From: Dario Minnucci Date: Mon, 19 Nov 2018 12:37:57 +0100 Subject: [PATCH 0233/1240] Add BIBO2 printer (dual and single extruder settings) --- resources/definitions/bibo2_dual.def.json | 98 +++++++++++++++++++ .../bibo2_single_extruder_0.def.json | 98 +++++++++++++++++++ .../bibo2_single_extruder_1.def.json | 98 +++++++++++++++++++ .../extruders/bibo2_dual_extruder_0.def.json | 40 ++++++++ .../extruders/bibo2_dual_extruder_1.def.json | 40 ++++++++ .../bibo2_single_extruder_0_0.def.json | 40 ++++++++ .../bibo2_single_extruder_0_1.def.json | 40 ++++++++ .../bibo2_single_extruder_1_0.def.json | 40 ++++++++ .../bibo2_single_extruder_1_1.def.json | 40 ++++++++ 9 files changed, 534 insertions(+) create mode 100644 resources/definitions/bibo2_dual.def.json create mode 100644 resources/definitions/bibo2_single_extruder_0.def.json create mode 100644 resources/definitions/bibo2_single_extruder_1.def.json create mode 100644 resources/extruders/bibo2_dual_extruder_0.def.json create mode 100644 resources/extruders/bibo2_dual_extruder_1.def.json create mode 100644 resources/extruders/bibo2_single_extruder_0_0.def.json create mode 100644 resources/extruders/bibo2_single_extruder_0_1.def.json create mode 100644 resources/extruders/bibo2_single_extruder_1_0.def.json create mode 100644 resources/extruders/bibo2_single_extruder_1_1.def.json diff --git a/resources/definitions/bibo2_dual.def.json b/resources/definitions/bibo2_dual.def.json new file mode 100644 index 0000000000..e57fb64ec0 --- /dev/null +++ b/resources/definitions/bibo2_dual.def.json @@ -0,0 +1,98 @@ +{ + "id": "BIBO2 dual", + "version": 2, + "name": "BIBO2 dual", + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "author": "na", + "manufacturer": "BIBO", + "category": "Other", + "file_formats": "text/x-gcode", + "has_materials": true, + "machine_extruder_trains": { + "0": "bibo2_dual_extruder_0", + "1": "bibo2_dual_extruder_1" + }, + "first_start_actions": [ + "MachineSettingsAction" + ] + }, + "overrides": { + "machine_name": { + "default_value": "BIBO2 dual" + }, + "machine_width": { + "default_value": 214 + }, + "machine_height": { + "default_value": 160 + }, + "machine_depth": { + "default_value": 186 + }, + "machine_center_is_zero": { + "default_value": true + }, + "machine_heated_bed": { + "default_value": true + }, + "machine_nozzle_size": { + "default_value": 0.4 + }, + "machine_nozzle_heat_up_speed": { + "default_value": 2 + }, + "machine_nozzle_cool_down_speed": { + "default_value": 2 + }, + "machine_head_with_fans_polygon": { + "default_value": [ + [ + -68.18, + 64.63 + ], + [ + -68.18, + -47.38 + ], + [ + 35.18, + 64.63 + ], + [ + 35.18, + -47.38 + ] + ] + }, + "material_diameter": { + "default_value": 1.75 + }, + "gantry_height": { + "default_value": 12 + }, + "machine_use_extruder_offset_to_offset_coords": { + "default_value": true + }, + "machine_gcode_flavor": { + "default_value": "RepRap (Marlin/Sprinter)" + }, + "machine_start_gcode": { + "default_value": "G21 ;metric values\nG90 ;absolute positioning\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z2.0 F400 ;move the platform down 15mm\nT0\nG92 E0\nG28\nG1 Y0 F1200 E0\nG92 E0\nM117 BIBO Printing..." + }, + "machine_end_gcode": { + "default_value": ";End GCode\nM104 T0 S0 ;extruder heater off\nM104 T1 S0 ;extruder heater off\nM140 S0 ;heated bed heater off (if you have it)\nG91\nG1 Z1 F100 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-2 X-20 Y-20 F300 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;steppers off\nG90 ;absolute positioning" + }, + "machine_extruder_count": { + "default_value": 2 + }, + "prime_tower_position_x": { + "default_value": 50 + }, + "prime_tower_position_y": { + "default_value": 50 + } + } +} + diff --git a/resources/definitions/bibo2_single_extruder_0.def.json b/resources/definitions/bibo2_single_extruder_0.def.json new file mode 100644 index 0000000000..93c7a4e5ae --- /dev/null +++ b/resources/definitions/bibo2_single_extruder_0.def.json @@ -0,0 +1,98 @@ +{ + "id": "BIBO2 single E1", + "version": 2, + "name": "BIBO2 single E1", + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "author": "na", + "manufacturer": "BIBO", + "category": "Other", + "file_formats": "text/x-gcode", + "has_materials": true, + "machine_extruder_trains": { + "0": "bibo2_single_extruder_0_0", + "1": "bibo2_single_extruder_0_1" + }, + "first_start_actions": [ + "MachineSettingsAction" + ] + }, + "overrides": { + "machine_name": { + "default_value": "BIBO2 single Extruder 1 (right)" + }, + "machine_width": { + "default_value": 214 + }, + "machine_height": { + "default_value": 160 + }, + "machine_depth": { + "default_value": 186 + }, + "machine_center_is_zero": { + "default_value": true + }, + "machine_heated_bed": { + "default_value": true + }, + "machine_nozzle_size": { + "default_value": 0.4 + }, + "machine_nozzle_heat_up_speed": { + "default_value": 2 + }, + "machine_nozzle_cool_down_speed": { + "default_value": 2 + }, + "machine_head_with_fans_polygon": { + "default_value": [ + [ + -68.18, + 64.63 + ], + [ + -68.18, + -47.38 + ], + [ + 35.18, + 64.63 + ], + [ + 35.18, + -47.38 + ] + ] + }, + "material_diameter": { + "default_value": 1.75 + }, + "gantry_height": { + "default_value": 12 + }, + "machine_use_extruder_offset_to_offset_coords": { + "default_value": true + }, + "machine_gcode_flavor": { + "default_value": "RepRap (Marlin/Sprinter)" + }, + "machine_start_gcode": { + "default_value": ";Startcode BIBO printers\nM109 T1 S170 ;preheat the other extruder, so it will not knock or ruin the print\nG90 ; absolute mode\nG21 ; metric values\nM82 ; Extruder in absolute mode\nM107\nG28\nG1 Z2 F400\nT0\nG90\nG92 E0\nG28\nG1 Y0 F1200 E0\nG92 E0\nG1 X-15.0 Y-92.9 Z0.3 F2400.0\t\t; move to start-line position\nG1 X15.0 Y-92.9 Z0.3 F1000.0 E2\t\t; draw 1st line\nG1 X15.0 Y-92.6 Z0.3 F3000.0\t\t; move to side a little\nG1 X-15.0 Y-92.6 Z0.3 F1000.0 E4\t\t; draw 2nd line\nG1 X-15.0 Y-92.3 Z0.3 F3000.0\t\t; move to side a little\nG1 X15.0 Y-92.3 Z0.3 F1000.0 E6\t\t; draw 3rd line\nG1 X15.0 Y-92 Z0.3 F3000.0\t\t; move to side a little\nG1 X-15.0 Y-92 Z0.3 F1000.0 E8\t\t; draw 4th line\nG1 X-16.0 Y-91.7 Z0.3 F3000.0\t\t; move to side a little\nG1 X16.0 Y-91.7 Z0.3 F1000.0 E10\t\t; draw 5th line\nG1 X16.0 Y-91.4 Z0.3 F3000.0\t\t; move to side a little\nG1 X-16.0 Y-91.4 Z0.3 F1000.0 E12\t\t; draw 5th line\nG1 E11.5 F2400\t\t\t\t; retract filament 0.5mm\nG92 E0\nM117 BIBO Printing..." + }, + "machine_end_gcode": { + "default_value": ";BIBO End GCode\nM107\nG91 ; Relative positioning\nG1 Z1 F100\nM104 T0 S0\nM104 T1 S0\nG1 X-20 Y-20 F3000\nG28 X0 Y0\nG90 ; Absolute positioning\nG92 E0 ; Reset extruder position\nM140 S0 ; Disable heated bed\nM84 ; Turn steppers off\nM117 BIBO Print complete\n " + }, + "machine_extruder_count": { + "default_value": 2 + }, + "prime_tower_position_x": { + "default_value": 50 + }, + "prime_tower_position_y": { + "default_value": 50 + } + } +} + diff --git a/resources/definitions/bibo2_single_extruder_1.def.json b/resources/definitions/bibo2_single_extruder_1.def.json new file mode 100644 index 0000000000..246add09ab --- /dev/null +++ b/resources/definitions/bibo2_single_extruder_1.def.json @@ -0,0 +1,98 @@ +{ + "id": "BIBO2 single E2", + "version": 2, + "name": "BIBO2 single E2", + "inherits": "fdmprinter", + "metadata": { + "visible": true, + "author": "na", + "manufacturer": "BIBO", + "category": "Other", + "file_formats": "text/x-gcode", + "has_materials": true, + "machine_extruder_trains": { + "0": "bibo2_single_extruder_1_0", + "1": "bibo2_single_extruder_1_1" + }, + "first_start_actions": [ + "MachineSettingsAction" + ] + }, + "overrides": { + "machine_name": { + "default_value": "BIBO2 single Extruder 2 (left)" + }, + "machine_width": { + "default_value": 214 + }, + "machine_height": { + "default_value": 160 + }, + "machine_depth": { + "default_value": 186 + }, + "machine_center_is_zero": { + "default_value": true + }, + "machine_heated_bed": { + "default_value": true + }, + "machine_nozzle_size": { + "default_value": 0.4 + }, + "machine_nozzle_heat_up_speed": { + "default_value": 2 + }, + "machine_nozzle_cool_down_speed": { + "default_value": 2 + }, + "machine_head_with_fans_polygon": { + "default_value": [ + [ + -68.18, + 64.63 + ], + [ + -68.18, + -47.38 + ], + [ + 35.18, + 64.63 + ], + [ + 35.18, + -47.38 + ] + ] + }, + "material_diameter": { + "default_value": 1.75 + }, + "gantry_height": { + "default_value": 12 + }, + "machine_use_extruder_offset_to_offset_coords": { + "default_value": true + }, + "machine_gcode_flavor": { + "default_value": "RepRap (Marlin/Sprinter)" + }, + "machine_start_gcode": { + "default_value": ";Startcode BIBO printers\nM109 T0 S170 ;preheat the other extruder, so it will not knock or ruin the print\nG90 ; absolute mode\nG21 ; metric values\nM82 ; Extruder in absolute mode\nM107\nG28\nG1 Z2 F400\nT0\nG90\nG92 E0\nG28\nG1 Y0 F1200 E0\nG92 E0\nT1\nG92 E0\nG1 X-15.0 Y-92.9 Z0.3 F2400.0\t\t; move to start-line position\nG1 X15.0 Y-92.9 Z0.3 F1000.0 E2\t\t; draw 1st line\nG1 X15.0 Y-92.6 Z0.3 F3000.0\t\t; move to side a little\nG1 X-15.0 Y-92.6 Z0.3 F1000.0 E4\t\t; draw 2nd line\nG1 X-15.0 Y-92.3 Z0.3 F3000.0\t\t; move to side a little\nG1 X15.0 Y-92.3 Z0.3 F1000.0 E6\t\t; draw 3rd line\nG1 X15.0 Y-92 Z0.3 F3000.0\t\t; move to side a little\nG1 X-15.0 Y-92 Z0.3 F1000.0 E8\t\t; draw 4th line\nG1 X-16.0 Y-91.7 Z0.3 F3000.0\t\t; move to side a little\nG1 X16.0 Y-91.7 Z0.3 F1000.0 E10\t\t; draw 5th line\nG1 X16.0 Y-91.4 Z0.3 F3000.0\t\t; move to side a little\nG1 X-16.0 Y-91.4 Z0.3 F1000.0 E12\t\t; draw 5th line\nG1 E11.5 F2400\t\t\t\t; retract filament 0.5mm\nG92 E0\nM117 BIBO Printing..." + }, + "machine_end_gcode": { + "default_value": ";BIBO End GCode\nM107\nG91 ; Relative positioning\nG1 Z1 F100\nM104 T0 S0\nM104 T1 S0\nG1 X-20 Y-20 F3000\nG28 X0 Y0\nG90 ; Absolute positioning\nG92 E0 ; Reset extruder position\nM140 S0 ; Disable heated bed\nM84 ; Turn steppers off\nM117 BIBO Print complete\n " + }, + "machine_extruder_count": { + "default_value": 2 + }, + "prime_tower_position_x": { + "default_value": 50 + }, + "prime_tower_position_y": { + "default_value": 50 + } + } +} + diff --git a/resources/extruders/bibo2_dual_extruder_0.def.json b/resources/extruders/bibo2_dual_extruder_0.def.json new file mode 100644 index 0000000000..7cdc03d504 --- /dev/null +++ b/resources/extruders/bibo2_dual_extruder_0.def.json @@ -0,0 +1,40 @@ +{ + "id": "BIBO1", + "version": 2, + "name": "Extruder 1", + "inherits": "fdmextruder", + "metadata": { + "machine": "BIBO2 dual", + "position": "0" + }, + "overrides": { + "extruder_nr": { + "default_value": 0, + "maximum_value": "1" + }, + "machine_nozzle_offset_x": { + "default_value": 0.0 + }, + "machine_nozzle_offset_y": { + "default_value": 0.0 + }, + "machine_extruder_start_pos_abs": { + "default_value": true + }, + "machine_extruder_start_pos_x": { + "value": "prime_tower_position_x" + }, + "machine_extruder_start_pos_y": { + "value": "prime_tower_position_y" + }, + "machine_extruder_end_pos_abs": { + "default_value": true + }, + "machine_extruder_end_pos_x": { + "value": "prime_tower_position_x" + }, + "machine_extruder_end_pos_y": { + "value": "prime_tower_position_y" + } + } +} diff --git a/resources/extruders/bibo2_dual_extruder_1.def.json b/resources/extruders/bibo2_dual_extruder_1.def.json new file mode 100644 index 0000000000..daa1504220 --- /dev/null +++ b/resources/extruders/bibo2_dual_extruder_1.def.json @@ -0,0 +1,40 @@ +{ + "id": "BIBO2", + "version": 2, + "name": "Extruder 2", + "inherits": "fdmextruder", + "metadata": { + "machine": "BIBO2 dual", + "position": "1" + }, + "overrides": { + "extruder_nr": { + "default_value": 1, + "maximum_value": "1" + }, + "machine_nozzle_offset_x": { + "default_value": 0 + }, + "machine_nozzle_offset_y": { + "default_value": 0 + }, + "machine_extruder_start_pos_abs": { + "default_value": true + }, + "machine_extruder_start_pos_x": { + "value": "prime_tower_position_x" + }, + "machine_extruder_start_pos_y": { + "value": "prime_tower_position_y" + }, + "machine_extruder_end_pos_abs": { + "default_value": true + }, + "machine_extruder_end_pos_x": { + "value": "prime_tower_position_x" + }, + "machine_extruder_end_pos_y": { + "value": "prime_tower_position_y" + } + } +} diff --git a/resources/extruders/bibo2_single_extruder_0_0.def.json b/resources/extruders/bibo2_single_extruder_0_0.def.json new file mode 100644 index 0000000000..7d0b246131 --- /dev/null +++ b/resources/extruders/bibo2_single_extruder_0_0.def.json @@ -0,0 +1,40 @@ +{ + "id": "BIBO2 E1a", + "version": 2, + "name": "BIBO2 E1", + "inherits": "fdmextruder", + "metadata": { + "machine": "BIBO2 single E1", + "position": "0" + }, + "overrides": { + "extruder_nr": { + "default_value": 0, + "maximum_value": "1" + }, + "machine_nozzle_offset_x": { + "default_value": 0.0 + }, + "machine_nozzle_offset_y": { + "default_value": 0.0 + }, + "machine_extruder_start_pos_abs": { + "default_value": true + }, + "machine_extruder_start_pos_x": { + "value": "prime_tower_position_x" + }, + "machine_extruder_start_pos_y": { + "value": "prime_tower_position_y" + }, + "machine_extruder_end_pos_abs": { + "default_value": true + }, + "machine_extruder_end_pos_x": { + "value": "prime_tower_position_x" + }, + "machine_extruder_end_pos_y": { + "value": "prime_tower_position_y" + } + } +} diff --git a/resources/extruders/bibo2_single_extruder_0_1.def.json b/resources/extruders/bibo2_single_extruder_0_1.def.json new file mode 100644 index 0000000000..76187696fc --- /dev/null +++ b/resources/extruders/bibo2_single_extruder_0_1.def.json @@ -0,0 +1,40 @@ +{ + "id": "BIBO2 E1b", + "version": 2, + "name": "E2 not used", + "inherits": "fdmextruder", + "metadata": { + "machine": "BIBO2 single E1", + "position": "1" + }, + "overrides": { + "extruder_nr": { + "default_value": 1, + "maximum_value": "1" + }, + "machine_nozzle_offset_x": { + "default_value": 0.0 + }, + "machine_nozzle_offset_y": { + "default_value": 0.0 + }, + "machine_extruder_start_pos_abs": { + "default_value": true + }, + "machine_extruder_start_pos_x": { + "value": "prime_tower_position_x" + }, + "machine_extruder_start_pos_y": { + "value": "prime_tower_position_y" + }, + "machine_extruder_end_pos_abs": { + "default_value": true + }, + "machine_extruder_end_pos_x": { + "value": "prime_tower_position_x" + }, + "machine_extruder_end_pos_y": { + "value": "prime_tower_position_y" + } + } +} diff --git a/resources/extruders/bibo2_single_extruder_1_0.def.json b/resources/extruders/bibo2_single_extruder_1_0.def.json new file mode 100644 index 0000000000..3cf667de82 --- /dev/null +++ b/resources/extruders/bibo2_single_extruder_1_0.def.json @@ -0,0 +1,40 @@ +{ + "id": "BIBO2 E2a", + "version": 2, + "name": "E1 not used", + "inherits": "fdmextruder", + "metadata": { + "machine": "BIBO2 single E2", + "position": "0" + }, + "overrides": { + "extruder_nr": { + "default_value": 0, + "maximum_value": "1" + }, + "machine_nozzle_offset_x": { + "default_value": 0 + }, + "machine_nozzle_offset_y": { + "default_value": 0 + }, + "machine_extruder_start_pos_abs": { + "default_value": true + }, + "machine_extruder_start_pos_x": { + "value": "prime_tower_position_x" + }, + "machine_extruder_start_pos_y": { + "value": "prime_tower_position_y" + }, + "machine_extruder_end_pos_abs": { + "default_value": true + }, + "machine_extruder_end_pos_x": { + "value": "prime_tower_position_x" + }, + "machine_extruder_end_pos_y": { + "value": "prime_tower_position_y" + } + } +} diff --git a/resources/extruders/bibo2_single_extruder_1_1.def.json b/resources/extruders/bibo2_single_extruder_1_1.def.json new file mode 100644 index 0000000000..e8f3ec7054 --- /dev/null +++ b/resources/extruders/bibo2_single_extruder_1_1.def.json @@ -0,0 +1,40 @@ +{ + "id": "BIBO2 E2b", + "version": 2, + "name": "BIBO2 E2", + "inherits": "fdmextruder", + "metadata": { + "machine": "BIBO2 single E2", + "position": "1" + }, + "overrides": { + "extruder_nr": { + "default_value": 1, + "maximum_value": "1" + }, + "machine_nozzle_offset_x": { + "default_value": 0 + }, + "machine_nozzle_offset_y": { + "default_value": 0 + }, + "machine_extruder_start_pos_abs": { + "default_value": true + }, + "machine_extruder_start_pos_x": { + "value": "prime_tower_position_x" + }, + "machine_extruder_start_pos_y": { + "value": "prime_tower_position_y" + }, + "machine_extruder_end_pos_abs": { + "default_value": true + }, + "machine_extruder_end_pos_x": { + "value": "prime_tower_position_x" + }, + "machine_extruder_end_pos_y": { + "value": "prime_tower_position_y" + } + } +} From b0f3fedc94354d30a8677bc93f02e67a9ec3d0f0 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 19 Nov 2018 13:42:19 +0100 Subject: [PATCH 0234/1240] Fix import QtQuick versions to suit Qt 5.10 CURA-5785 --- plugins/PreviewStage/PreviewMenu.qml | 3 +-- resources/qml/MachineSelector.qml | 2 +- .../qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index b331ff69c5..1a744aeb65 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -2,8 +2,7 @@ // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.7 - -import QtQuick.Controls 2.4 +import QtQuick.Controls 2.3 import UM 1.3 as UM import Cura 1.1 as Cura diff --git a/resources/qml/MachineSelector.qml b/resources/qml/MachineSelector.qml index a464949192..c9756d93ba 100644 --- a/resources/qml/MachineSelector.qml +++ b/resources/qml/MachineSelector.qml @@ -2,7 +2,7 @@ // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.2 -import QtQuick.Controls 2.4 +import QtQuick.Controls 2.3 import QtQuick.Controls.Styles 1.1 import QtQuick.Layouts 1.1 diff --git a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml index 33610135fe..d428a05463 100644 --- a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml +++ b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml @@ -4,7 +4,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Controls.Styles 1.4 -import QtQuick.Layouts 1.11 +import QtQuick.Layouts 1.3 import QtQuick.Controls 1.1 as OldControls From f8f133d2ef92dfabb636eac70b09e681bd4b4d63 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 19 Nov 2018 13:42:28 +0100 Subject: [PATCH 0235/1240] Fix running tests in plugin using pytest --- .../tests/TestLegacyProfileReader.py | 9 +++++---- .../tests/TestVersionUpgrade25to26.py | 11 ++++++++--- .../tests/TestVersionUpgrade26to27.py | 9 +++++---- .../tests/TestVersionUpgrade27to30.py | 8 ++++---- .../tests/TestVersionUpgrade34to35.py | 10 ++++++---- 5 files changed, 28 insertions(+), 19 deletions(-) diff --git a/plugins/LegacyProfileReader/tests/TestLegacyProfileReader.py b/plugins/LegacyProfileReader/tests/TestLegacyProfileReader.py index 480a61f301..3c9e46b6d8 100644 --- a/plugins/LegacyProfileReader/tests/TestLegacyProfileReader.py +++ b/plugins/LegacyProfileReader/tests/TestLegacyProfileReader.py @@ -1,8 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. - import configparser # An input for some functions we're testing. -import os.path # To find the integration test .ini files. +import os # To find the integration test .ini files. import pytest # To register tests with. import unittest.mock # To mock the application, plug-in and container registry out. @@ -11,13 +10,15 @@ import UM.PluginRegistry # To mock the plug-in registry out. import UM.Settings.ContainerRegistry # To mock the container registry out. import UM.Settings.InstanceContainer # To intercept the serialised data from the read() function. -import LegacyProfileReader as LegacyProfileReaderModule # To get the directory of the module. -from LegacyProfileReader import LegacyProfileReader # The module we're testing. +import plugins.LegacyProfileReader.LegacyProfileReader as LegacyProfileReaderModule +from plugins.LegacyProfileReader.LegacyProfileReader import LegacyProfileReader + @pytest.fixture def legacy_profile_reader(): return LegacyProfileReader() + test_prepareDefaultsData = [ { "defaults": diff --git a/plugins/VersionUpgrade/VersionUpgrade25to26/tests/TestVersionUpgrade25to26.py b/plugins/VersionUpgrade/VersionUpgrade25to26/tests/TestVersionUpgrade25to26.py index 9d7c7646cc..588c0cb3db 100644 --- a/plugins/VersionUpgrade/VersionUpgrade25to26/tests/TestVersionUpgrade25to26.py +++ b/plugins/VersionUpgrade/VersionUpgrade25to26/tests/TestVersionUpgrade25to26.py @@ -1,16 +1,17 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +import configparser +import pytest -import configparser #To check whether the appropriate exceptions are raised. -import pytest #To register tests with. +from plugins.VersionUpgrade.VersionUpgrade25to26 import VersionUpgrade25to26 -import VersionUpgrade25to26 #The module we're testing. ## Creates an instance of the upgrader to test with. @pytest.fixture def upgrader(): return VersionUpgrade25to26.VersionUpgrade25to26() + test_cfg_version_good_data = [ { "test_name": "Simple", @@ -60,6 +61,7 @@ setting_version = -3 } ] + ## Tests the technique that gets the version number from CFG files. # # \param data The parametrised data to test with. It contains a test name @@ -116,6 +118,7 @@ version = 1.2 } ] + ## Tests whether getting a version number from bad CFG files gives an # exception. # @@ -155,6 +158,7 @@ foo = bar } ] + ## Tests whether the settings that should be removed are removed for the 2.6 # version of preferences. @pytest.mark.parametrize("data", test_upgrade_preferences_removed_settings_data) @@ -200,6 +204,7 @@ type = instance_container } ] + ## Tests whether the settings that should be removed are removed for the 2.6 # version of instance containers. @pytest.mark.parametrize("data", test_upgrade_instance_container_removed_settings_data) diff --git a/plugins/VersionUpgrade/VersionUpgrade26to27/tests/TestVersionUpgrade26to27.py b/plugins/VersionUpgrade/VersionUpgrade26to27/tests/TestVersionUpgrade26to27.py index eebaca23c6..45d41e7a1b 100644 --- a/plugins/VersionUpgrade/VersionUpgrade26to27/tests/TestVersionUpgrade26to27.py +++ b/plugins/VersionUpgrade/VersionUpgrade26to27/tests/TestVersionUpgrade26to27.py @@ -1,15 +1,16 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +import configparser +import pytest -import configparser #To check whether the appropriate exceptions are raised. -import pytest #To register tests with. +from plugins.VersionUpgrade.VersionUpgrade26to27.VersionUpgrade26to27 import VersionUpgrade26to27 -import VersionUpgrade26to27 #The module we're testing. ## Creates an instance of the upgrader to test with. @pytest.fixture def upgrader(): - return VersionUpgrade26to27.VersionUpgrade26to27() + return VersionUpgrade26to27() + test_cfg_version_good_data = [ { diff --git a/plugins/VersionUpgrade/VersionUpgrade27to30/tests/TestVersionUpgrade27to30.py b/plugins/VersionUpgrade/VersionUpgrade27to30/tests/TestVersionUpgrade27to30.py index cae08ebcfd..7b77b85993 100644 --- a/plugins/VersionUpgrade/VersionUpgrade27to30/tests/TestVersionUpgrade27to30.py +++ b/plugins/VersionUpgrade/VersionUpgrade27to30/tests/TestVersionUpgrade27to30.py @@ -1,15 +1,15 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +import configparser +import pytest -import configparser #To parse the resulting config files. -import pytest #To register tests with. +from plugins.VersionUpgrade.VersionUpgrade27to30.VersionUpgrade27to30 import VersionUpgrade27to30 -import VersionUpgrade27to30 #The module we're testing. ## Creates an instance of the upgrader to test with. @pytest.fixture def upgrader(): - return VersionUpgrade27to30.VersionUpgrade27to30() + return VersionUpgrade27to30() test_cfg_version_good_data = [ { diff --git a/plugins/VersionUpgrade/VersionUpgrade34to35/tests/TestVersionUpgrade34to35.py b/plugins/VersionUpgrade/VersionUpgrade34to35/tests/TestVersionUpgrade34to35.py index b74e6f35ac..4f77fcd093 100644 --- a/plugins/VersionUpgrade/VersionUpgrade34to35/tests/TestVersionUpgrade34to35.py +++ b/plugins/VersionUpgrade/VersionUpgrade34to35/tests/TestVersionUpgrade34to35.py @@ -1,15 +1,16 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +import configparser +import pytest -import configparser #To parse the resulting config files. -import pytest #To register tests with. +from plugins.VersionUpgrade.VersionUpgrade34to35.VersionUpgrade34to35 import VersionUpgrade34to35 -import VersionUpgrade34to35 #The module we're testing. ## Creates an instance of the upgrader to test with. @pytest.fixture def upgrader(): - return VersionUpgrade34to35.VersionUpgrade34to35() + return VersionUpgrade34to35() + test_upgrade_version_nr_data = [ ("Empty config file", @@ -25,6 +26,7 @@ test_upgrade_version_nr_data = [ ) ] + ## Tests whether the version numbers are updated. @pytest.mark.parametrize("test_name, file_data", test_upgrade_version_nr_data) def test_upgradeVersionNr(test_name, file_data, upgrader): From 30b20b4aa56e6bda5cc0f7f6ae0ab7a4f2a2e905 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 19 Nov 2018 13:50:32 +0100 Subject: [PATCH 0236/1240] Add borders to expandable components This adds a lining to all of the drop-downs from the stage menus. Contributes to issue CURA-5876. --- resources/qml/ExpandableComponent.qml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 8ed6dc5674..0a80e020c9 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -17,6 +17,8 @@ Item property var popupItem property color popupBackgroundColor: UM.Theme.getColor("action_button") + property int popupBorderWidth: UM.Theme.getSize("default_lining").width + property color popupBorderColor: UM.Theme.getColor("lining") property color headerBackgroundColor: UM.Theme.getColor("action_button") property color headerHoverColor: UM.Theme.getColor("action_button_hovered") @@ -147,6 +149,8 @@ Item background: Rectangle { color: popupBackgroundColor + border.width: popupBorderWidth + border.color: popupBorderColor } } } From dc17bd849968d180dc061aff6ca7eaed53bfcff1 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 19 Nov 2018 13:54:45 +0100 Subject: [PATCH 0237/1240] Fix the first tests --- .../UM3NetworkPrinting/src/SendMaterialJob.py | 1 + .../tests/TestSendMaterialJob.py | 23 ++++++++++++------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py index 349e6929ff..572558e352 100644 --- a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py @@ -157,6 +157,7 @@ class SendMaterialJob(Job): @classmethod def _parseReply(cls, reply: QNetworkReply) -> Dict[str, ClusterMaterial]: remote_materials_list = json.loads(reply.readAll().data().decode("utf-8")) + print("remote_materials_list", remote_materials_list) return {material["guid"]: ClusterMaterial(**material) for material in remote_materials_list} ## Retrieves a list of local materials diff --git a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py index bc6e1def14..b51d978ed2 100644 --- a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py @@ -90,26 +90,33 @@ class TestSendMaterialJob(TestCase): def test_run(self, device_mock): job = SendMaterialJob(device_mock) job.run() + + # We expect the materials endpoint to be called when the job runs. device_mock.get.assert_called_with("materials/", on_finished=job._onGetRemoteMaterials) @patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") @patch("PyQt5.QtNetwork.QNetworkReply") def test_sendMissingMaterials_withFailedRequest(self, reply_mock, device_mock): reply_mock.attribute.return_value = 404 - SendMaterialJob(device_mock).run() - reply_mock.attribute.assert_called_with(0) - self.assertEqual(reply_mock.method_calls, [call.attribute(0)]) - self.assertEqual(device_mock._onGetRemoteMaterials.method_calls, []) + job = SendMaterialJob(device_mock) + job._onGetRemoteMaterials(reply_mock) + + # We expect the error string to be retrieved and the device not to be called for any follow up. + self.assertEqual([call.attribute(0), call.errorString()], reply_mock.method_calls) + self.assertEqual([], device_mock.method_calls) @patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") @patch("PyQt5.QtNetwork.QNetworkReply") def test_sendMissingMaterials_withBadJsonAnswer(self, reply_mock, device_mock): reply_mock.attribute.return_value = 200 reply_mock.readAll.return_value = QByteArray(b'Six sick hicks nick six slick bricks with picks and sticks.') - SendMaterialJob(device_mock).run() - reply_mock.attribute.assert_called_with(0) - self.assertEqual(reply_mock.method_calls, [call.attribute(0), call.readAll()]) - self.assertEqual(device_mock._onGetRemoteMaterials.method_calls, []) + job = SendMaterialJob(device_mock) + job._onGetRemoteMaterials(reply_mock) + + # We expect the reply to be called once to try to get the printers from the list (readAll()). + # Given that the parsing there fails we do no expect the device to be called for any follow up. + self.assertEqual([call.attribute(0), call.readAll()], reply_mock.method_calls) + self.assertEqual([], device_mock.method_calls) # @patch("PyQt5.QtNetwork.QNetworkReply") # def test_sendMissingMaterials_withMissingGuid(self, reply_mock): From 7a210087ff0aa8c34ebe7438bc744136b3afab64 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 19 Nov 2018 14:35:34 +0100 Subject: [PATCH 0238/1240] Add header for configuration selector It currently always says 'Custom', but we will want to make that dynamic at some point. Contributes to issue CURA-5876. --- .../QuickConfigurationSelector.qml | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml index 33610135fe..d84298ee2e 100644 --- a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml +++ b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml @@ -12,6 +12,10 @@ import UM 1.2 as UM import Cura 1.0 as Cura +/** + * Menu that allows you to select the configuration of the current printer, such + * as the nozzle sizes and materials in each extruder. + */ Cura.ExpandableComponent { id: base @@ -99,10 +103,27 @@ Cura.ExpandableComponent width: base.width - 2 * UM.Theme.getSize("default_margin").width height: 200 + Label + { + id: customHeader + text: catalog.i18nc("@header", "Custom") + font: UM.Theme.getFont("large") + color: UM.Theme.getColor("text") + + anchors + { + top: parent.top + left: parent.left + right: parent.right + } + } + TabBar { id: tabBar onCurrentIndexChanged: Cura.ExtruderManager.setActiveExtruderIndex(currentIndex) + anchors.top: customHeader.bottom + anchors.topMargin: UM.Theme.getSize("default_margin").height width: parent.width height: 50 Repeater From 00a49fff26fac675207d30b6131f2d4869474197 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 19 Nov 2018 14:39:19 +0100 Subject: [PATCH 0239/1240] Rename QuickConfigurationSelector to ConfigurationMenu This is going to function as our main item for the configuration menu. It contains the part in the top bar and the glue item for the part in the popup. Contributes to issue CURA-5876. --- plugins/PrepareStage/PrepareMenu.qml | 2 +- .../{QuickConfigurationSelector.qml => ConfigurationMenu.qml} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename resources/qml/Menus/ConfigurationMenu/{QuickConfigurationSelector.qml => ConfigurationMenu.qml} (100%) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index ef01625a22..6b241e1da2 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -68,7 +68,7 @@ Item color: UM.Theme.getColor("lining") } - Cura.QuickConfigurationSelector + Cura.ConfigurationMenu { Layout.fillHeight: true Layout.fillWidth: true diff --git a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml similarity index 100% rename from resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml rename to resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml From 0b1ac87354d53457dcfba0f927e749639a7600a0 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 19 Nov 2018 15:03:43 +0100 Subject: [PATCH 0240/1240] Fix some formatting, cleanup import --- .../UM3NetworkPrinting/src/SendMaterialJob.py | 9 +++-- .../tests/TestSendMaterialJob.py | 36 ++++++++----------- 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py index 572558e352..62414763b6 100644 --- a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py @@ -48,10 +48,10 @@ class SendMaterialJob(Job): try: remote_materials_by_guid = self._parseReply(reply) self._sendMissingMaterials(remote_materials_by_guid) - except json.JSONDecodeError as e: - Logger.log("e", "Error parsing materials from printer: %s", e) - except KeyError as e: - Logger.log("e", "Error parsing materials from printer: %s", e) + except json.JSONDecodeError: + Logger.logException("w", "Error parsing materials from printer") + except KeyError: + Logger.logException("w", "Error parsing materials from printer") ## Determine which materials should be updated and send them to the printer. # @@ -157,7 +157,6 @@ class SendMaterialJob(Job): @classmethod def _parseReply(cls, reply: QNetworkReply) -> Dict[str, ClusterMaterial]: remote_materials_list = json.loads(reply.readAll().data().decode("utf-8")) - print("remote_materials_list", remote_materials_list) return {material["guid"]: ClusterMaterial(**material) for material in remote_materials_list} ## Retrieves a list of local materials diff --git a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py index b51d978ed2..0e907f58eb 100644 --- a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py @@ -1,19 +1,13 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -import io -import json from typing import Any, List -from unittest import TestCase, mock +from unittest import TestCase from unittest.mock import patch, call from PyQt5.QtCore import QByteArray -from UM.Logger import Logger -from UM.MimeTypeDatabase import MimeType -from UM.Settings.ContainerRegistry import ContainerInterface, ContainerRegistryInterface, \ - DefinitionContainerInterface, ContainerRegistry +from UM.Settings.ContainerRegistry import ContainerInterface, ContainerRegistryInterface, DefinitionContainerInterface from plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice import ClusterUM3OutputDevice -from plugins.UM3NetworkPrinting.src.Models import ClusterMaterial from plugins.UM3NetworkPrinting.src.SendMaterialJob import SendMaterialJob @@ -45,7 +39,7 @@ class ContainerRegistryMock(ContainerRegistryInterface): return self.containersMetaData -class FakeDevice(ClusterUM3OutputDevice): +class MockOutputDevice(ClusterUM3OutputDevice): def _createFormPart(self, content_header, data, content_type=None): return "xxx" @@ -53,20 +47,20 @@ class FakeDevice(ClusterUM3OutputDevice): class TestSendMaterialJob(TestCase): _LOCAL_MATERIAL_WHITE = {'type': 'material', 'status': 'unknown', 'id': 'generic_pla_white', - 'base_file': 'generic_pla_white', 'setting_version': 5, 'name': 'White PLA', - 'brand': 'Generic', 'material': 'PLA', 'color_name': 'White', - 'GUID': 'badb0ee7-87c8-4f3f-9398-938587b67dce', 'version': '1', 'color_code': '#ffffff', - 'description': 'Test PLA White', 'adhesion_info': 'Use glue.', 'approximate_diameter': '3', - 'properties': {'density': '1.00', 'diameter': '2.85', 'weight': '750'}, - 'definition': 'fdmprinter', 'compatible': True} + 'base_file': 'generic_pla_white', 'setting_version': 5, 'name': 'White PLA', + 'brand': 'Generic', 'material': 'PLA', 'color_name': 'White', + 'GUID': 'badb0ee7-87c8-4f3f-9398-938587b67dce', 'version': '1', 'color_code': '#ffffff', + 'description': 'Test PLA White', 'adhesion_info': 'Use glue.', 'approximate_diameter': '3', + 'properties': {'density': '1.00', 'diameter': '2.85', 'weight': '750'}, + 'definition': 'fdmprinter', 'compatible': True} _LOCAL_MATERIAL_BLACK = {'type': 'material', 'status': 'unknown', 'id': 'generic_pla_black', - 'base_file': 'generic_pla_black', 'setting_version': 5, 'name': 'Yellow CPE', - 'brand': 'Ultimaker', 'material': 'CPE', 'color_name': 'Black', - 'GUID': '5fbb362a-41f9-4818-bb43-15ea6df34aa4', 'version': '1', 'color_code': '#000000', - 'description': 'Test PLA Black', 'adhesion_info': 'Use glue.', 'approximate_diameter': '3', - 'properties': {'density': '1.01', 'diameter': '2.85', 'weight': '750'}, - 'definition': 'fdmprinter', 'compatible': True} + 'base_file': 'generic_pla_black', 'setting_version': 5, 'name': 'Yellow CPE', + 'brand': 'Ultimaker', 'material': 'CPE', 'color_name': 'Black', + 'GUID': '5fbb362a-41f9-4818-bb43-15ea6df34aa4', 'version': '1', 'color_code': '#000000', + 'description': 'Test PLA Black', 'adhesion_info': 'Use glue.', 'approximate_diameter': '3', + 'properties': {'density': '1.01', 'diameter': '2.85', 'weight': '750'}, + 'definition': 'fdmprinter', 'compatible': True} _REMOTE_MATERIAL_WHITE = { "guid": "badb0ee7-87c8-4f3f-9398-938587b67dce", From d65114bd5652398bf9b3fae91dc38b53fae0e35f Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 19 Nov 2018 15:08:58 +0100 Subject: [PATCH 0241/1240] use call_count to assert device was not called --- .../tests/TestSendMaterialJob.py | 20 ++----------------- 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py index 0e907f58eb..9b1e1066ba 100644 --- a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py @@ -97,7 +97,7 @@ class TestSendMaterialJob(TestCase): # We expect the error string to be retrieved and the device not to be called for any follow up. self.assertEqual([call.attribute(0), call.errorString()], reply_mock.method_calls) - self.assertEqual([], device_mock.method_calls) + self.assertEqual(0, device_mock.call_count) @patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") @patch("PyQt5.QtNetwork.QNetworkReply") @@ -110,7 +110,7 @@ class TestSendMaterialJob(TestCase): # We expect the reply to be called once to try to get the printers from the list (readAll()). # Given that the parsing there fails we do no expect the device to be called for any follow up. self.assertEqual([call.attribute(0), call.readAll()], reply_mock.method_calls) - self.assertEqual([], device_mock.method_calls) + self.assertEqual(0, device_mock.call_count) # @patch("PyQt5.QtNetwork.QNetworkReply") # def test_sendMissingMaterials_withMissingGuid(self, reply_mock): @@ -274,19 +274,3 @@ class TestSendMaterialJob(TestCase): # # self.assertTrue(len(local_materials) == 1) # self.assertTrue(list(local_materials.values())[0].version == 2) - # - # def _assertLogEntries(self, first, second): - # """ - # Inspects the two sets of log entry tuples and fails when they are not the same - # :param first: The first set of tuples - # :param second: The second set of tuples - # """ - # self.assertEqual(len(first), len(second)) - # - # while len(first) > 0: - # e1, m1 = first[0] - # e2, m2 = second[0] - # self.assertEqual(e1, e2) - # self.assertEqual(m1, m2) - # first.pop(0) - # second.pop(0) From 9d8583a3b67682442d0ab4383bdf748770105af2 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 19 Nov 2018 15:10:35 +0100 Subject: [PATCH 0242/1240] Revert "Fix running tests in plugin using pytest" This reverts commit f8f133d2ef92dfabb636eac70b09e681bd4b4d63. --- .../tests/TestLegacyProfileReader.py | 9 ++++----- .../tests/TestVersionUpgrade25to26.py | 11 +++-------- .../tests/TestVersionUpgrade26to27.py | 9 ++++----- .../tests/TestVersionUpgrade27to30.py | 8 ++++---- .../tests/TestVersionUpgrade34to35.py | 10 ++++------ 5 files changed, 19 insertions(+), 28 deletions(-) diff --git a/plugins/LegacyProfileReader/tests/TestLegacyProfileReader.py b/plugins/LegacyProfileReader/tests/TestLegacyProfileReader.py index 3c9e46b6d8..480a61f301 100644 --- a/plugins/LegacyProfileReader/tests/TestLegacyProfileReader.py +++ b/plugins/LegacyProfileReader/tests/TestLegacyProfileReader.py @@ -1,7 +1,8 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. + import configparser # An input for some functions we're testing. -import os # To find the integration test .ini files. +import os.path # To find the integration test .ini files. import pytest # To register tests with. import unittest.mock # To mock the application, plug-in and container registry out. @@ -10,15 +11,13 @@ import UM.PluginRegistry # To mock the plug-in registry out. import UM.Settings.ContainerRegistry # To mock the container registry out. import UM.Settings.InstanceContainer # To intercept the serialised data from the read() function. -import plugins.LegacyProfileReader.LegacyProfileReader as LegacyProfileReaderModule -from plugins.LegacyProfileReader.LegacyProfileReader import LegacyProfileReader - +import LegacyProfileReader as LegacyProfileReaderModule # To get the directory of the module. +from LegacyProfileReader import LegacyProfileReader # The module we're testing. @pytest.fixture def legacy_profile_reader(): return LegacyProfileReader() - test_prepareDefaultsData = [ { "defaults": diff --git a/plugins/VersionUpgrade/VersionUpgrade25to26/tests/TestVersionUpgrade25to26.py b/plugins/VersionUpgrade/VersionUpgrade25to26/tests/TestVersionUpgrade25to26.py index 588c0cb3db..9d7c7646cc 100644 --- a/plugins/VersionUpgrade/VersionUpgrade25to26/tests/TestVersionUpgrade25to26.py +++ b/plugins/VersionUpgrade/VersionUpgrade25to26/tests/TestVersionUpgrade25to26.py @@ -1,17 +1,16 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -import configparser -import pytest -from plugins.VersionUpgrade.VersionUpgrade25to26 import VersionUpgrade25to26 +import configparser #To check whether the appropriate exceptions are raised. +import pytest #To register tests with. +import VersionUpgrade25to26 #The module we're testing. ## Creates an instance of the upgrader to test with. @pytest.fixture def upgrader(): return VersionUpgrade25to26.VersionUpgrade25to26() - test_cfg_version_good_data = [ { "test_name": "Simple", @@ -61,7 +60,6 @@ setting_version = -3 } ] - ## Tests the technique that gets the version number from CFG files. # # \param data The parametrised data to test with. It contains a test name @@ -118,7 +116,6 @@ version = 1.2 } ] - ## Tests whether getting a version number from bad CFG files gives an # exception. # @@ -158,7 +155,6 @@ foo = bar } ] - ## Tests whether the settings that should be removed are removed for the 2.6 # version of preferences. @pytest.mark.parametrize("data", test_upgrade_preferences_removed_settings_data) @@ -204,7 +200,6 @@ type = instance_container } ] - ## Tests whether the settings that should be removed are removed for the 2.6 # version of instance containers. @pytest.mark.parametrize("data", test_upgrade_instance_container_removed_settings_data) diff --git a/plugins/VersionUpgrade/VersionUpgrade26to27/tests/TestVersionUpgrade26to27.py b/plugins/VersionUpgrade/VersionUpgrade26to27/tests/TestVersionUpgrade26to27.py index 45d41e7a1b..eebaca23c6 100644 --- a/plugins/VersionUpgrade/VersionUpgrade26to27/tests/TestVersionUpgrade26to27.py +++ b/plugins/VersionUpgrade/VersionUpgrade26to27/tests/TestVersionUpgrade26to27.py @@ -1,16 +1,15 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -import configparser -import pytest -from plugins.VersionUpgrade.VersionUpgrade26to27.VersionUpgrade26to27 import VersionUpgrade26to27 +import configparser #To check whether the appropriate exceptions are raised. +import pytest #To register tests with. +import VersionUpgrade26to27 #The module we're testing. ## Creates an instance of the upgrader to test with. @pytest.fixture def upgrader(): - return VersionUpgrade26to27() - + return VersionUpgrade26to27.VersionUpgrade26to27() test_cfg_version_good_data = [ { diff --git a/plugins/VersionUpgrade/VersionUpgrade27to30/tests/TestVersionUpgrade27to30.py b/plugins/VersionUpgrade/VersionUpgrade27to30/tests/TestVersionUpgrade27to30.py index 7b77b85993..cae08ebcfd 100644 --- a/plugins/VersionUpgrade/VersionUpgrade27to30/tests/TestVersionUpgrade27to30.py +++ b/plugins/VersionUpgrade/VersionUpgrade27to30/tests/TestVersionUpgrade27to30.py @@ -1,15 +1,15 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -import configparser -import pytest -from plugins.VersionUpgrade.VersionUpgrade27to30.VersionUpgrade27to30 import VersionUpgrade27to30 +import configparser #To parse the resulting config files. +import pytest #To register tests with. +import VersionUpgrade27to30 #The module we're testing. ## Creates an instance of the upgrader to test with. @pytest.fixture def upgrader(): - return VersionUpgrade27to30() + return VersionUpgrade27to30.VersionUpgrade27to30() test_cfg_version_good_data = [ { diff --git a/plugins/VersionUpgrade/VersionUpgrade34to35/tests/TestVersionUpgrade34to35.py b/plugins/VersionUpgrade/VersionUpgrade34to35/tests/TestVersionUpgrade34to35.py index 4f77fcd093..b74e6f35ac 100644 --- a/plugins/VersionUpgrade/VersionUpgrade34to35/tests/TestVersionUpgrade34to35.py +++ b/plugins/VersionUpgrade/VersionUpgrade34to35/tests/TestVersionUpgrade34to35.py @@ -1,16 +1,15 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -import configparser -import pytest -from plugins.VersionUpgrade.VersionUpgrade34to35.VersionUpgrade34to35 import VersionUpgrade34to35 +import configparser #To parse the resulting config files. +import pytest #To register tests with. +import VersionUpgrade34to35 #The module we're testing. ## Creates an instance of the upgrader to test with. @pytest.fixture def upgrader(): - return VersionUpgrade34to35() - + return VersionUpgrade34to35.VersionUpgrade34to35() test_upgrade_version_nr_data = [ ("Empty config file", @@ -26,7 +25,6 @@ test_upgrade_version_nr_data = [ ) ] - ## Tests whether the version numbers are updated. @pytest.mark.parametrize("test_name, file_data", test_upgrade_version_nr_data) def test_upgradeVersionNr(test_name, file_data, upgrader): From 66fbadf2dea2a734a4055b8f866b5397303b2069 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 19 Nov 2018 15:37:56 +0100 Subject: [PATCH 0243/1240] Convert all single quotes to double quotes --- .../tests/TestSendMaterialJob.py | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py index 9b1e1066ba..22e96f5ed0 100644 --- a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py @@ -46,21 +46,21 @@ class MockOutputDevice(ClusterUM3OutputDevice): class TestSendMaterialJob(TestCase): - _LOCAL_MATERIAL_WHITE = {'type': 'material', 'status': 'unknown', 'id': 'generic_pla_white', - 'base_file': 'generic_pla_white', 'setting_version': 5, 'name': 'White PLA', - 'brand': 'Generic', 'material': 'PLA', 'color_name': 'White', - 'GUID': 'badb0ee7-87c8-4f3f-9398-938587b67dce', 'version': '1', 'color_code': '#ffffff', - 'description': 'Test PLA White', 'adhesion_info': 'Use glue.', 'approximate_diameter': '3', - 'properties': {'density': '1.00', 'diameter': '2.85', 'weight': '750'}, - 'definition': 'fdmprinter', 'compatible': True} + _LOCAL_MATERIAL_WHITE = {"type": "material", "status": "unknown", "id": "generic_pla_white", + "base_file": "generic_pla_white", "setting_version": 5, "name": "White PLA", + "brand": "Generic", "material": "PLA", "color_name": "White", + "GUID": "badb0ee7-87c8-4f3f-9398-938587b67dce", "version": "1", "color_code": "#ffffff", + "description": "Test PLA White", "adhesion_info": "Use glue.", "approximate_diameter": "3", + "properties": {"density": "1.00", "diameter": "2.85", "weight": "750"}, + "definition": "fdmprinter", "compatible": True} - _LOCAL_MATERIAL_BLACK = {'type': 'material', 'status': 'unknown', 'id': 'generic_pla_black', - 'base_file': 'generic_pla_black', 'setting_version': 5, 'name': 'Yellow CPE', - 'brand': 'Ultimaker', 'material': 'CPE', 'color_name': 'Black', - 'GUID': '5fbb362a-41f9-4818-bb43-15ea6df34aa4', 'version': '1', 'color_code': '#000000', - 'description': 'Test PLA Black', 'adhesion_info': 'Use glue.', 'approximate_diameter': '3', - 'properties': {'density': '1.01', 'diameter': '2.85', 'weight': '750'}, - 'definition': 'fdmprinter', 'compatible': True} + _LOCAL_MATERIAL_BLACK = {"type": "material", "status": "unknown", "id": "generic_pla_black", + "base_file": "generic_pla_black", "setting_version": 5, "name": "Yellow CPE", + "brand": "Ultimaker", "material": "CPE", "color_name": "Black", + "GUID": "5fbb362a-41f9-4818-bb43-15ea6df34aa4", "version": "1", "color_code": "#000000", + "description": "Test PLA Black", "adhesion_info": "Use glue.", "approximate_diameter": "3", + "properties": {"density": "1.01", "diameter": "2.85", "weight": "750"}, + "definition": "fdmprinter", "compatible": True} _REMOTE_MATERIAL_WHITE = { "guid": "badb0ee7-87c8-4f3f-9398-938587b67dce", @@ -103,7 +103,7 @@ class TestSendMaterialJob(TestCase): @patch("PyQt5.QtNetwork.QNetworkReply") def test_sendMissingMaterials_withBadJsonAnswer(self, reply_mock, device_mock): reply_mock.attribute.return_value = 200 - reply_mock.readAll.return_value = QByteArray(b'Six sick hicks nick six slick bricks with picks and sticks.') + reply_mock.readAll.return_value = QByteArray(b"Six sick hicks nick six slick bricks with picks and sticks.") job = SendMaterialJob(device_mock) job._onGetRemoteMaterials(reply_mock) @@ -119,13 +119,13 @@ class TestSendMaterialJob(TestCase): # del remoteMaterialWithoutGuid["guid"] # reply_mock.readAll.return_value = QByteArray(json.dumps([remoteMaterialWithoutGuid]).encode("ascii")) # - # with mock.patch.object(Logger, 'log', new=new_log): + # with mock.patch.object(Logger, "log", new=new_log): # SendMaterialJob(None).sendMissingMaterials(reply_mock) # # reply_mock.attribute.assert_called_with(0) # self.assertEqual(reply_mock.method_calls, [call.attribute(0), call.readAll()]) # self._assertLogEntries( - # [('e', "Request material storage on printer: Printer's answer was missing GUIDs.")], + # [("e", "Request material storage on printer: Printer"s answer was missing GUIDs.")], # _logentries) # # @patch("UM.Resources.Resources.getAllResourcesOfType", lambda _: []) @@ -145,7 +145,7 @@ class TestSendMaterialJob(TestCase): # # reply_mock.attribute.assert_called_with(0) # self.assertEqual(reply_mock.method_calls, [call.attribute(0), call.readAll()]) - # self._assertLogEntries([('e', "Material generic_pla_white has invalid version number one.")], _logentries) + # self._assertLogEntries([("e", "Material generic_pla_white has invalid version number one.")], _logentries) # # @patch("UM.Resources.Resources.getAllResourcesOfType", lambda _: []) # @patch("PyQt5.QtNetwork.QNetworkReply") @@ -194,16 +194,16 @@ class TestSendMaterialJob(TestCase): # device_mock._createFormPart.return_value = "_xXx_" # with mock.patch.object(Logger, "log", new=new_log): # job = SendMaterialJob(device_mock) - # job.sendMaterialsToPrinter({'generic_pla_white'}) + # job.sendMaterialsToPrinter({"generic_pla_white"}) # # self._assertLogEntries([("d", "Syncing material generic_pla_white with cluster.")], _logentries) - # self.assertEqual([call._createFormPart('name="file"; filename="generic_pla_white.xml.fdm_material"', ''), + # self.assertEqual([call._createFormPart("name="file"; filename="generic_pla_white.xml.fdm_material"", ""), # call.postFormWithParts(on_finished=job.sendingFinished, parts = ["_xXx_"], target = "materials/")], device_mock.method_calls) # # @patch("PyQt5.QtNetwork.QNetworkReply") # def test_sendingFinished_success(self, reply_mock) -> None: # reply_mock.attribute.return_value = 200 - # with mock.patch.object(Logger, 'log', new=new_log): + # with mock.patch.object(Logger, "log", new=new_log): # SendMaterialJob(None).sendingFinished(reply_mock) # # reply_mock.attribute.assert_called_once_with(0) @@ -212,9 +212,9 @@ class TestSendMaterialJob(TestCase): # @patch("PyQt5.QtNetwork.QNetworkReply") # def test_sendingFinished_failed(self, reply_mock) -> None: # reply_mock.attribute.return_value = 404 - # reply_mock.readAll.return_value = QByteArray(b'Six sick hicks nick six slick bricks with picks and sticks.') + # reply_mock.readAll.return_value = QByteArray(b"Six sick hicks nick six slick bricks with picks and sticks.") # - # with mock.patch.object(Logger, 'log', new=new_log): + # with mock.patch.object(Logger, "log", new=new_log): # SendMaterialJob(None).sendingFinished(reply_mock) # # reply_mock.attribute.assert_called_with(0) From 60dd1303936a09c83a562067c9a8193c2f3d5e43 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 19 Nov 2018 15:39:12 +0100 Subject: [PATCH 0244/1240] Use logException where possible --- plugins/UM3NetworkPrinting/src/SendMaterialJob.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py index 62414763b6..6260752f3f 100644 --- a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py @@ -175,10 +175,7 @@ class SendMaterialJob(Job): material = LocalMaterial(**m) if material.GUID not in result or material.version > result.get(material.GUID).version: result[material.GUID] = material - except ValueError as e: - Logger.log("w", "Local material {material_id} has invalid values: {e}".format( - material_id = m["id"], - e = e - )) + except ValueError: + Logger.logException("w", "Local material {} has invalid values.".format(m["id"])) return result From c04ce7fce8df24298c1708a044ebfa4afee8a411 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 19 Nov 2018 15:44:07 +0100 Subject: [PATCH 0245/1240] Use call_count on specific method to be more precise --- plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py index 22e96f5ed0..a71ded75b6 100644 --- a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py @@ -97,7 +97,7 @@ class TestSendMaterialJob(TestCase): # We expect the error string to be retrieved and the device not to be called for any follow up. self.assertEqual([call.attribute(0), call.errorString()], reply_mock.method_calls) - self.assertEqual(0, device_mock.call_count) + self.assertEqual(0, device_mock.createFormPart.call_count) @patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") @patch("PyQt5.QtNetwork.QNetworkReply") @@ -110,7 +110,7 @@ class TestSendMaterialJob(TestCase): # We expect the reply to be called once to try to get the printers from the list (readAll()). # Given that the parsing there fails we do no expect the device to be called for any follow up. self.assertEqual([call.attribute(0), call.readAll()], reply_mock.method_calls) - self.assertEqual(0, device_mock.call_count) + self.assertEqual(0, device_mock.createFormPart.call_count) # @patch("PyQt5.QtNetwork.QNetworkReply") # def test_sendMissingMaterials_withMissingGuid(self, reply_mock): From 8683a1e07e5adc60e0be272eb4b6871d92f8c1ef Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 19 Nov 2018 16:16:41 +0100 Subject: [PATCH 0246/1240] Add previewStage to bundled cura package list CURA-5785 --- resources/bundled_packages/cura.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/resources/bundled_packages/cura.json b/resources/bundled_packages/cura.json index ee82b17a75..678ea0a697 100644 --- a/resources/bundled_packages/cura.json +++ b/resources/bundled_packages/cura.json @@ -356,6 +356,23 @@ } } }, + "PreviewStage": { + "package_info": { + "package_id": "PreviewStage", + "package_type": "plugin", + "display_name": "Preview Stage", + "description": "Provides a preview stage in Cura.", + "package_version": "1.0.0", + "sdk_version": 5, + "website": "https://ultimaker.com", + "author": { + "author_id": "UltimakerPackages", + "display_name": "Ultimaker B.V.", + "email": "plugins@ultimaker.com", + "website": "https://ultimaker.com" + } + } + }, "RemovableDriveOutputDevice": { "package_info": { "package_id": "RemovableDriveOutputDevice", From 2497325d606a9246bb383fa87e2dc1707f99e321 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 19 Nov 2018 16:35:19 +0100 Subject: [PATCH 0247/1240] Test with named tuples, not working yet --- plugins/UM3NetworkPrinting/src/Models.py | 111 +++++------------- .../UM3NetworkPrinting/src/SendMaterialJob.py | 14 +-- .../tests/TestSendMaterialJob.py | 35 +++--- 3 files changed, 53 insertions(+), 107 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Models.py b/plugins/UM3NetworkPrinting/src/Models.py index 89bf665377..e8efa577f6 100644 --- a/plugins/UM3NetworkPrinting/src/Models.py +++ b/plugins/UM3NetworkPrinting/src/Models.py @@ -1,87 +1,32 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import Optional +from collections import namedtuple +ClusterMaterial = namedtuple('ClusterMaterial', [ + 'guid', + 'material', + 'brand', + 'version', + 'color', + 'density' +]) -class BaseModel: - def __init__(self, **kwargs): - self.__dict__.update(kwargs) - - def __eq__(self, other): - return self.__dict__ == other.__dict__ if type(self) == type(other) else False - - -## Represents an item in the cluster API response for installed materials. -class ClusterMaterial(BaseModel): - def __init__(self, **kwargs): - super().__init__(**kwargs) - self.version = int(self.version) - self.density = float(self.density) - - guid = None # type: Optional[str] - - material = None # type: Optional[str] - - brand = None # type: Optional[str] - - version = None # type: Optional[int] - - color = None # type: Optional[str] - - density = None # type: Optional[float] - - -class LocalMaterialProperties(BaseModel): - def __init__(self, **kwargs): - super().__init__(**kwargs) - self.density = float(self.density) - self.diameter = float(self.diameter) - self.weight = float(self.weight) - - density = None # type: Optional[float] - - diameter = None # type: Optional[float] - - weight = None # type: Optional[int] - - -class LocalMaterial(BaseModel): - def __init__(self, **kwargs): - super().__init__(**kwargs) - self.properties = LocalMaterialProperties(**self.properties) - self.approximate_diameter = float(self.approximate_diameter) - self.version = int(self.version) - - GUID = None # type: Optional[str] - - id = None # type: Optional[str] - - type = None # type: Optional[str] - - status = None # type: Optional[str] - - base_file = None # type: Optional[str] - - setting_version = None # type: Optional[str] - - version = None # type: Optional[int] - - name = None # type: Optional[str] - - brand = None # type: Optional[str] - - material = None # type: Optional[str] - - color_name = None # type: Optional[str] - - description = None # type: Optional[str] - - adhesion_info = None # type: Optional[str] - - approximate_diameter = None # type: Optional[float] - - properties = None # type: LocalMaterialProperties - - definition = None # type: Optional[str] - - compatible = None # type: Optional[bool] +LocalMaterial = namedtuple('LocalMaterial', [ + 'GUID', + 'id', + 'type', + 'status', + 'base_file', + 'setting_version', + 'version', + 'name', + 'brand', + 'material', + 'color_name', + 'description', + 'adhesion_info', + 'approximate_diameter', + 'properties', + 'definition', + 'compatible' +]) diff --git a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py index 6260752f3f..cbe79aef6a 100644 --- a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py @@ -156,8 +156,8 @@ class SendMaterialJob(Job): # \throw KeyError Raised when on of the materials does not include a valid guid @classmethod def _parseReply(cls, reply: QNetworkReply) -> Dict[str, ClusterMaterial]: - remote_materials_list = json.loads(reply.readAll().data().decode("utf-8")) - return {material["guid"]: ClusterMaterial(**material) for material in remote_materials_list} + remote_materials = json.loads(reply.readAll().data().decode("utf-8")) + return {material["id"]: ClusterMaterial(**material) for material in remote_materials} ## Retrieves a list of local materials # @@ -170,12 +170,12 @@ class SendMaterialJob(Job): material_containers = container_registry.findContainersMetadata(type = "material") # Find the latest version of all material containers in the registry. - for m in material_containers: + local_materials = {} # type: Dict[str, LocalMaterial] + for material in material_containers: try: - material = LocalMaterial(**m) + material = LocalMaterial(**material) if material.GUID not in result or material.version > result.get(material.GUID).version: - result[material.GUID] = material + local_materials[material.GUID] = material except ValueError: - Logger.logException("w", "Local material {} has invalid values.".format(m["id"])) - + Logger.logException("w", "Local material {} has invalid values.".format(material["id"])) return result diff --git a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py index a71ded75b6..73bca2b0ad 100644 --- a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py @@ -1,5 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +import json + from typing import Any, List from unittest import TestCase from unittest.mock import patch, call @@ -108,26 +110,25 @@ class TestSendMaterialJob(TestCase): job._onGetRemoteMaterials(reply_mock) # We expect the reply to be called once to try to get the printers from the list (readAll()). - # Given that the parsing there fails we do no expect the device to be called for any follow up. + # Given that the parsing fails we do no expect the device to be called for any follow up. self.assertEqual([call.attribute(0), call.readAll()], reply_mock.method_calls) self.assertEqual(0, device_mock.createFormPart.call_count) - # @patch("PyQt5.QtNetwork.QNetworkReply") - # def test_sendMissingMaterials_withMissingGuid(self, reply_mock): - # reply_mock.attribute.return_value = 200 - # remoteMaterialWithoutGuid = self._REMOTEMATERIAL_WHITE.copy() - # del remoteMaterialWithoutGuid["guid"] - # reply_mock.readAll.return_value = QByteArray(json.dumps([remoteMaterialWithoutGuid]).encode("ascii")) - # - # with mock.patch.object(Logger, "log", new=new_log): - # SendMaterialJob(None).sendMissingMaterials(reply_mock) - # - # reply_mock.attribute.assert_called_with(0) - # self.assertEqual(reply_mock.method_calls, [call.attribute(0), call.readAll()]) - # self._assertLogEntries( - # [("e", "Request material storage on printer: Printer"s answer was missing GUIDs.")], - # _logentries) - # + @patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") + @patch("PyQt5.QtNetwork.QNetworkReply") + def test_sendMissingMaterials_withMissingGuid(self, reply_mock, device_mock): + reply_mock.attribute.return_value = 200 + remote_material_without_guid = self._REMOTE_MATERIAL_WHITE.copy() + del remote_material_without_guid["guid"] + reply_mock.readAll.return_value = QByteArray(json.dumps([remote_material_without_guid]).encode("ascii")) + job = SendMaterialJob(device_mock) + job._onGetRemoteMaterials(reply_mock) + + # We expect the reply to be called once to try to get the printers from the list (readAll()). + # Given that parsing fails we do not expect the device to be called for any follow up. + self.assertEqual([call.attribute(0), call.readAll()], reply_mock.method_calls) + self.assertEqual(1, device_mock.createFormPart.call_count) + # @patch("UM.Resources.Resources.getAllResourcesOfType", lambda _: []) # @patch("PyQt5.QtNetwork.QNetworkReply") # def test_sendMissingMaterials_WithInvalidVersionInLocalMaterial(self, reply_mock): From 421a64c7b0ff712ed8c5103641bda03c257df3da Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Mon, 19 Nov 2018 16:42:36 +0100 Subject: [PATCH 0248/1240] Replace queued print job cards with new design Contributes to CL-1148 --- .../resources/qml/ClusterMonitorItem.qml | 52 +++------ .../resources/qml/ExpandableCard.qml | 81 +++++++++++++ .../resources/qml/MonitorPrintJobCard.qml | 109 ++++++++++++++++++ .../resources/qml/MonitorPrintJobPreview.qml | 67 +++++++++++ 4 files changed, 272 insertions(+), 37 deletions(-) create mode 100644 plugins/UM3NetworkPrinting/resources/qml/ExpandableCard.qml create mode 100644 plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml create mode 100644 plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml diff --git a/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml b/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml index d210ab40f3..ff4bc72218 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml @@ -6,13 +6,15 @@ import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import UM 1.3 as UM import Cura 1.0 as Cura +import QtGraphicalEffects 1.0 Component { + Rectangle { id: monitorFrame; property var emphasisColor: UM.Theme.getColor("setting_control_border_highlight"); property var cornerRadius: UM.Theme.getSize("monitor_corner_radius").width; - color: UM.Theme.getColor("viewport_background"); + color: transparent height: maximumHeight; onVisibleChanged: { if (monitorFrame != null && !monitorFrame.visible) { @@ -21,6 +23,14 @@ Component { } width: maximumWidth; + LinearGradient { + anchors.fill: parent + gradient: Gradient { + GradientStop { position: 0.0; color: "#f6f6f6" } + GradientStop { position: 1.0; color: "#ffffff" } + } + } + UM.I18nCatalog { id: catalog; name: "cura"; @@ -60,39 +70,6 @@ Component { text: catalog.i18nc("@label", "Queued"); } - Column { - id: skeletonLoader; - anchors { - bottom: parent.bottom; - bottomMargin: UM.Theme.getSize("default_margin").height; - horizontalCenter: parent.horizontalCenter; - top: queuedLabel.bottom; - topMargin: UM.Theme.getSize("default_margin").height; - } - visible: !queuedPrintJobs.visible; - width: Math.min(800 * screenScaleFactor, maximumWidth); - - PrintJobInfoBlock { - anchors { - left: parent.left; - leftMargin: UM.Theme.getSize("default_margin").width; - right: parent.right; - rightMargin: UM.Theme.getSize("default_margin").width; - } - printJob: null; // Use as skeleton - } - - PrintJobInfoBlock { - anchors { - left: parent.left; - leftMargin: UM.Theme.getSize("default_margin").width; - right: parent.right; - rightMargin: UM.Theme.getSize("default_margin").width; - } - printJob: null; // Use as skeleton - } - } - ScrollView { id: queuedPrintJobs; anchors { @@ -104,12 +81,12 @@ Component { } style: UM.Theme.styles.scrollview; visible: OutputDevice.receivedPrintJobs; - width: Math.min(800 * screenScaleFactor, maximumWidth); + width: Math.min(834 * screenScaleFactor, maximumWidth); ListView { id: printJobList; anchors.fill: parent; - delegate: PrintJobInfoBlock { + delegate: MonitorPrintJobCard { anchors { left: parent.left; leftMargin: UM.Theme.getSize("default_margin").width; @@ -119,7 +96,7 @@ Component { printJob: modelData; } model: OutputDevice.queuedPrintJobs; - spacing: UM.Theme.getSize("default_margin").height - 2 * UM.Theme.getSize("monitor_shadow_radius").width; + spacing: 6; } } @@ -129,4 +106,5 @@ Component { visible: OutputDevice.activeCameraUrl != ""; } } + } diff --git a/plugins/UM3NetworkPrinting/resources/qml/ExpandableCard.qml b/plugins/UM3NetworkPrinting/resources/qml/ExpandableCard.qml new file mode 100644 index 0000000000..89d88d671a --- /dev/null +++ b/plugins/UM3NetworkPrinting/resources/qml/ExpandableCard.qml @@ -0,0 +1,81 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 2.0 +import UM 1.3 as UM +import Cura 1.0 as Cura + +// The expandable component has 3 major sub components: +// * The headerItem Always visible and should hold some info about what happens if the component is expanded +// * The popupItem The content that needs to be shown if the component is expanded. +Item +{ + id: base + + property var expanded: false + property var borderWidth: 1 + property color borderColor: "#EAEAEC" + property color headerBackgroundColor: "white" + property color headerHoverColor: "#f5f5f5" + property color drawerBackgroundColor: "white" + property alias headerItem: header.children + property alias drawerItem: drawer.children + + width: parent.width + height: childrenRect.height + + Rectangle + { + id: header + border + { + color: borderColor + width: borderWidth + } + color: headerMouseArea.containsMouse ? headerHoverColor : headerBackgroundColor + height: childrenRect.height + width: parent.width + Behavior on color + { + ColorAnimation + { + duration: 100 + } + } + } + + MouseArea + { + id: headerMouseArea + anchors.fill: header + onClicked: base.expanded = !base.expanded + hoverEnabled: true + } + + Rectangle + { + id: drawer + anchors + { + top: header.bottom + topMargin: -1 + } + border + { + color: borderColor + width: borderWidth + } + clip: true + color: headerBackgroundColor + height: base.expanded ? childrenRect.height : 0 + width: parent.width + Behavior on height + { + NumberAnimation + { + duration: 100 + } + } + } +} \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml new file mode 100644 index 0000000000..307a4f908f --- /dev/null +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml @@ -0,0 +1,109 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 2.0 +import UM 1.3 as UM +import Cura 1.0 as Cura + +// A Print Job Card is essentially just a filled-in Expandable Card item. +Item +{ + id: base + property var printJob: null + + width: parent.width + height: childrenRect.height + + ExpandableCard + { + headerItem: Row + { + height: 48 + anchors.left: parent.left + anchors.leftMargin: 24 + spacing: 18 + + MonitorPrintJobPreview + { + printJob: base.printJob + size: 32 + anchors.verticalCenter: parent.verticalCenter + } + + Label + { + text: printJob && printJob.name ? printJob.name : "" + color: "#374355" + elide: Text.ElideRight + font: UM.Theme.getFont("default_bold") + anchors.verticalCenter: parent.verticalCenter + width: 216 + height: 18 + } + + Label + { + text: printJob ? OutputDevice.formatDuration(printJob.timeTotal) : "" + color: "#374355" + elide: Text.ElideRight + font: UM.Theme.getFont("default_bold") + anchors.verticalCenter: parent.verticalCenter + width: 216 + height: 18 + } + + Label + { + color: "#374355" + height: 18 + elide: Text.ElideRight + font: UM.Theme.getFont("default_bold") + text: { + if (printJob !== null) { + if (printJob.assignedPrinter == null) + { + if (printJob.state == "error") + { + return catalog.i18nc("@label", "Waiting for: Unavailable printer") + } + return catalog.i18nc("@label", "Waiting for: First available") + } + else + { + return catalog.i18nc("@label", "Waiting for: ") + printJob.assignedPrinter.name + } + } + return "" + } + visible: printJob + anchors.verticalCenter: parent.verticalCenter + width: 216 + } + } + drawerItem: Row + { + height: 96 + anchors.left: parent.left + anchors.leftMargin: 74 + spacing: 18 + + Rectangle + { + id: printerConfiguration + width: 450 + height: 72 + color: "blue" + anchors.verticalCenter: parent.verticalCenter + } + Label { + height: 18 + text: printJob && printJob.owner ? printJob.owner : "" + color: "#374355" + elide: Text.ElideRight + font: UM.Theme.getFont("default_bold") + anchors.top: printerConfiguration.top + } + } + } +} \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml new file mode 100644 index 0000000000..7322193451 --- /dev/null +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml @@ -0,0 +1,67 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Dialogs 1.1 +import QtQuick.Controls 2.0 +import QtQuick.Controls.Styles 1.4 +import QtGraphicalEffects 1.0 +import QtQuick.Layouts 1.1 +import QtQuick.Dialogs 1.1 +import UM 1.3 as UM + +Item +{ + id: printJobPreview + + property var printJob: null + property var size: 256 + + width: size + height: size + + // Actual content + Image + { + id: previewImage + anchors.fill: parent + opacity: printJob && printJob.state == "error" ? 0.5 : 1.0 + source: printJob ? printJob.previewImageUrl : "" + visible: printJob + } + + UM.RecolorImage + { + id: ultiBotImage + + anchors.centerIn: printJobPreview + color: UM.Theme.getColor("monitor_placeholder_image") + height: printJobPreview.height + source: "../svg/ultibot.svg" + sourceSize + { + height: height + width: width + } + /* Since print jobs ALWAYS have an image url, we have to check if that image URL errors or + not in order to determine if we show the placeholder (ultibot) image instead. */ + visible: printJob && previewImage.status == Image.Error + width: printJobPreview.width + } + + UM.RecolorImage + { + id: statusImage + anchors.centerIn: printJobPreview + color: UM.Theme.getColor("monitor_image_overlay") + height: 0.5 * printJobPreview.height + source: printJob && printJob.state == "error" ? "../svg/aborted-icon.svg" : "" + sourceSize + { + height: height + width: width + } + visible: source != "" + width: 0.5 * printJobPreview.width + } +} \ No newline at end of file From 2b88e82a2a520761ede661ba0843a77b5269b1c1 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 19 Nov 2018 16:52:02 +0100 Subject: [PATCH 0249/1240] Add option test-verbose build option to CuraTests --- cmake/CuraTests.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmake/CuraTests.cmake b/cmake/CuraTests.cmake index f2ee92d65b..b6d04de036 100644 --- a/cmake/CuraTests.cmake +++ b/cmake/CuraTests.cmake @@ -6,6 +6,8 @@ include(CMakeParseArguments) find_package(PythonInterp 3.5.0 REQUIRED) +add_custom_target(test-verbose COMMAND ${CMAKE_CTEST_COMMAND} --verbose) + function(cura_add_test) set(_single_args NAME DIRECTORY PYTHONPATH) cmake_parse_arguments("" "" "${_single_args}" "" ${ARGN}) From 16deeb09510b4bbaa6bc2c7b4ca7b939aa904caf Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 19 Nov 2018 17:10:29 +0100 Subject: [PATCH 0250/1240] Remove old QuickConfigurationSelector It doesn't exist any more. Contributes to issue CURA-5876. --- resources/qml/qmldir | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/qml/qmldir b/resources/qml/qmldir index d5e4106d33..878945b922 100644 --- a/resources/qml/qmldir +++ b/resources/qml/qmldir @@ -1,7 +1,6 @@ module Cura MachineSelector 1.0 MachineSelector.qml -QuickConfigurationSelector 1.0 QuickConfigurationSelector.qml CustomConfigurationSelector 1.0 CustomConfigurationSelector.qml PrintSetupSelector 1.0 PrintSetupSelector.qml ActionButton 1.0 ActionButton.qml From a04db164e6a0679ca684dd65ff33c50b9355a242 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 19 Nov 2018 17:11:51 +0100 Subject: [PATCH 0251/1240] Make it possible to swap between auto and custom configuration We're going to need to make this disappear when the printer is not connected. But that is for later. Contributes to issue CURA-5876. --- .../ConfigurationMenu/AutoConfiguration.qml | 25 +++ .../ConfigurationMenu/ConfigurationMenu.qml | 207 ++++++------------ .../ConfigurationMenu/CustomConfiguration.qml | 168 ++++++++++++++ 3 files changed, 263 insertions(+), 137 deletions(-) create mode 100644 resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml create mode 100644 resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml diff --git a/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml new file mode 100644 index 0000000000..e1f0ed480e --- /dev/null +++ b/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml @@ -0,0 +1,25 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 2.0 + +import UM 1.3 as UM + +Item +{ + Label + { + id: header + text: catalog.i18nc("@header", "Configurations") + font: UM.Theme.getFont("large") + color: UM.Theme.getColor("text") + + anchors + { + top: parent.top + left: parent.left + right: parent.right + } + } +} \ No newline at end of file diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index d84298ee2e..10c52a7b0f 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -6,8 +6,6 @@ import QtQuick.Controls 2.0 import QtQuick.Controls.Styles 1.4 import QtQuick.Layouts 1.11 -import QtQuick.Controls 1.1 as OldControls - import UM 1.2 as UM import Cura 1.0 as Cura @@ -100,163 +98,98 @@ Cura.ExpandableComponent popupItem: Item { + id: popup width: base.width - 2 * UM.Theme.getSize("default_margin").width height: 200 - Label - { - id: customHeader - text: catalog.i18nc("@header", "Custom") - font: UM.Theme.getFont("large") - color: UM.Theme.getColor("text") + property var configuration_method: "auto" + AutoConfiguration + { + id: autoConfiguration + visible: popup.configuration_method === "auto" + anchors.top: header.bottom + height: visible ? childrenRect.height : 0 + } + + CustomConfiguration + { + id: customConfiguration + visible: popup.configuration_method === "custom" + anchors.top: header.bottom + height: visible ? childrenRect.height : 0 + } + + Rectangle + { + id: separator anchors { - top: parent.top left: parent.left right: parent.right + bottom: buttonBar.top + bottomMargin: UM.Theme.getSize("default_margin").height } + height: UM.Theme.getSize("default_lining").height + color: UM.Theme.getColor("lining") } - TabBar + Rectangle { - id: tabBar - onCurrentIndexChanged: Cura.ExtruderManager.setActiveExtruderIndex(currentIndex) - anchors.top: customHeader.bottom - anchors.topMargin: UM.Theme.getSize("default_margin").height - width: parent.width - height: 50 - Repeater + id: buttonBar + anchors { - model: extrudersModel - - delegate: TabButton - { - width: ListView.view != null ? Math.round(ListView.view.width / extrudersModel.rowCount()): 0 - height: parent.height - contentItem: Item - { - Cura.ExtruderIcon - { - anchors.horizontalCenter: parent.horizontalCenter - materialColor: model.color - extruderEnabled: model.enabled - width: parent.height - height: parent.height - } - } - } + left: parent.left + right: parent.right + bottom: parent.bottom } - } + height: childrenRect.height - Item - { - id: tabControl - width: parent.width - anchors.top: tabBar.bottom - anchors.bottom: parent.bottom - property var model: extrudersModel.items[tabBar.currentIndex] - property real textWidth: Math.round(width * 0.3) - property real controlWidth: width - textWidth - Column + Cura.ActionButton { - spacing: UM.Theme.getSize("default_margin").height - Row + id: goToCustom + visible: popup.configuration_method === "auto" + text: catalog.i18nc("@label", "Custom") + + anchors { - height: UM.Theme.getSize("print_setup_item").height - - Label - { - text: catalog.i18nc("@label", "Enabled") - verticalAlignment: Text.AlignVCenter - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - height: parent.height - width: tabControl.textWidth - } - - OldControls.CheckBox - { - checked: tabControl.model != null ? Cura.MachineManager.getExtruder(tabControl.model.index).isEnabled: false - onClicked: Cura.MachineManager.setExtruderEnabled(tabControl.model.index, checked) - height: UM.Theme.getSize("setting_control").height - style: UM.Theme.styles.checkbox - } + right: parent.right + bottom: parent.bottom } - Row - { - height: UM.Theme.getSize("print_setup_item").height - Label - { - text: catalog.i18nc("@label", "Material") - verticalAlignment: Text.AlignVCenter - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - height: parent.height - width: tabControl.textWidth - } + color: UM.Theme.getColor("secondary") + hoverColor: UM.Theme.getColor("secondary") + textColor: UM.Theme.getColor("primary") + textHoverColor: UM.Theme.getColor("text") + height: UM.Theme.getSize("action_panel_button").height + leftPadding: UM.Theme.getSize("default_margin").width + rightPadding: UM.Theme.getSize("default_margin").width - OldControls.ToolButton - { - id: materialSelection - - property var activeExtruder: Cura.MachineManager.activeStack - property var hasActiveExtruder: activeExtruder != null - property var currentRootMaterialName: hasActiveExtruder ? activeExtruder.material.name : "" - property var valueError: hasActiveExtruder ? Cura.ContainerManager.getContainerMetaDataEntry(activeExtruder.material.id, "compatible", "") != "True" : true - property var valueWarning: ! Cura.MachineManager.isActiveQualitySupported - - text: currentRootMaterialName - tooltip: currentRootMaterialName - visible: Cura.MachineManager.hasMaterials - - enabled: Cura.ExtruderManager.activeExtruderIndex > -1 - - height: UM.Theme.getSize("setting_control").height - width: tabControl.controlWidth - - style: UM.Theme.styles.sidebar_header_button - activeFocusOnPress: true - menu: Cura.MaterialMenu - { - extruderIndex: Cura.ExtruderManager.activeExtruderIndex - } - - } - } - - Row - { - height: UM.Theme.getSize("print_setup_item").height - - Label - { - text: Cura.MachineManager.activeDefinitionVariantsName - verticalAlignment: Text.AlignVCenter - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - height: parent.height - width: tabControl.textWidth - } - - OldControls.ToolButton - { - id: variantSelection - text: Cura.MachineManager.activeVariantName - tooltip: Cura.MachineManager.activeVariantName; - visible: Cura.MachineManager.hasVariants - - height: UM.Theme.getSize("setting_control").height - width: tabControl.controlWidth - style: UM.Theme.styles.sidebar_header_button - activeFocusOnPress: true; - - menu: Cura.NozzleMenu { extruderIndex: Cura.ExtruderManager.activeExtruderIndex } - } - } + onClicked: popup.configuration_method = "custom" } + Cura.ActionButton + { + id: goToAuto + visible: popup.configuration_method === "custom" + text: catalog.i18nc("@label", "Configurations") + + anchors + { + left: parent.left + bottom: parent.bottom + } + + color: UM.Theme.getColor("secondary") + hoverColor: UM.Theme.getColor("secondary") + textColor: UM.Theme.getColor("primary") + textHoverColor: UM.Theme.getColor("text") + height: UM.Theme.getSize("action_panel_button").height + leftPadding: UM.Theme.getSize("default_margin").width + rightPadding: UM.Theme.getSize("default_margin").width + + onClicked: popup.configuration_method = "auto" + } } } } diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml new file mode 100644 index 0000000000..953b6a48f0 --- /dev/null +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -0,0 +1,168 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.0 +import QtQuick.Controls 2.0 +import QtQuick.Controls 1.1 as OldControls + +import Cura 1.0 as Cura +import UM 1.3 as UM + +Item +{ + Label + { + id: header + text: catalog.i18nc("@header", "Custom") + font: UM.Theme.getFont("large") + color: UM.Theme.getColor("text") + + anchors + { + top: parent.top + left: parent.left + right: parent.right + } + } + + TabBar + { + id: tabBar + onCurrentIndexChanged: Cura.ExtruderManager.setActiveExtruderIndex(currentIndex) + anchors.top: header.bottom + anchors.topMargin: UM.Theme.getSize("default_margin").height + width: parent.width + height: 50 + Repeater + { + model: extrudersModel + + delegate: TabButton + { + width: ListView.view != null ? Math.round(ListView.view.width / extrudersModel.rowCount()): 0 + height: parent.height + contentItem: Item + { + Cura.ExtruderIcon + { + anchors.horizontalCenter: parent.horizontalCenter + materialColor: model.color + extruderEnabled: model.enabled + width: parent.height + height: parent.height + } + } + } + } + } + + Item + { + id: tabControl + width: parent.width + anchors.top: tabBar.bottom + anchors.bottom: parent.bottom + property var model: extrudersModel.items[tabBar.currentIndex] + property real textWidth: Math.round(width * 0.3) + property real controlWidth: width - textWidth + Column + { + spacing: UM.Theme.getSize("default_margin").height + Row + { + height: UM.Theme.getSize("print_setup_item").height + + Label + { + text: catalog.i18nc("@label", "Enabled") + verticalAlignment: Text.AlignVCenter + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + height: parent.height + width: tabControl.textWidth + } + + OldControls.CheckBox + { + checked: tabControl.model != null ? Cura.MachineManager.getExtruder(tabControl.model.index).isEnabled: false + onClicked: Cura.MachineManager.setExtruderEnabled(tabControl.model.index, checked) + height: UM.Theme.getSize("setting_control").height + style: UM.Theme.styles.checkbox + } + } + + Row + { + height: UM.Theme.getSize("print_setup_item").height + Label + { + text: catalog.i18nc("@label", "Material") + verticalAlignment: Text.AlignVCenter + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + height: parent.height + width: tabControl.textWidth + } + + OldControls.ToolButton + { + id: materialSelection + + property var activeExtruder: Cura.MachineManager.activeStack + property var hasActiveExtruder: activeExtruder != null + property var currentRootMaterialName: hasActiveExtruder ? activeExtruder.material.name : "" + property var valueError: hasActiveExtruder ? Cura.ContainerManager.getContainerMetaDataEntry(activeExtruder.material.id, "compatible", "") != "True" : true + property var valueWarning: ! Cura.MachineManager.isActiveQualitySupported + + text: currentRootMaterialName + tooltip: currentRootMaterialName + visible: Cura.MachineManager.hasMaterials + + enabled: Cura.ExtruderManager.activeExtruderIndex > -1 + + height: UM.Theme.getSize("setting_control").height + width: tabControl.controlWidth + + style: UM.Theme.styles.sidebar_header_button + activeFocusOnPress: true + menu: Cura.MaterialMenu + { + extruderIndex: Cura.ExtruderManager.activeExtruderIndex + } + + } + } + + Row + { + height: UM.Theme.getSize("print_setup_item").height + + Label + { + text: Cura.MachineManager.activeDefinitionVariantsName + verticalAlignment: Text.AlignVCenter + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + height: parent.height + width: tabControl.textWidth + } + + OldControls.ToolButton + { + id: variantSelection + text: Cura.MachineManager.activeVariantName + tooltip: Cura.MachineManager.activeVariantName; + visible: Cura.MachineManager.hasVariants + + height: UM.Theme.getSize("setting_control").height + width: tabControl.controlWidth + style: UM.Theme.styles.sidebar_header_button + activeFocusOnPress: true; + + menu: Cura.NozzleMenu { extruderIndex: Cura.ExtruderManager.activeExtruderIndex } + } + } + } + + } +} \ No newline at end of file From 69ca7c0f893fa5f1d9af88526ddad2c796255b17 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 19 Nov 2018 20:08:05 +0100 Subject: [PATCH 0252/1240] CloudOutputDevice scaffolding --- .../src/Cloud/CloudOutputController.py | 18 +++ .../src/Cloud/CloudOutputDevice.py | 134 ++++++++++++++++++ .../UM3NetworkPrinting/src/Cloud/__init__.py | 0 plugins/UM3NetworkPrinting/src/__init__.py | 0 plugins/__init__.py | 0 5 files changed, 152 insertions(+) create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/CloudOutputController.py create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/__init__.py create mode 100644 plugins/UM3NetworkPrinting/src/__init__.py create mode 100644 plugins/__init__.py diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputController.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputController.py new file mode 100644 index 0000000000..d31d2bf486 --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputController.py @@ -0,0 +1,18 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from cura.PrinterOutput.PrinterOutputController import PrinterOutputController + + +class CloudOutputController(PrinterOutputController): + def __init__(self, output_device): + super().__init__(output_device) + + # The cloud connection only supports fetching the printer and queue status and adding a job to the queue. + # To let the UI know this we mark all features below as False. + self.can_pause = False + self.can_abort = False + self.can_pre_heat_bed = False + self.can_pre_heat_hotends = False + self.can_send_raw_gcode = False + self.can_control_manually = False + self.can_update_firmware = False diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py new file mode 100644 index 0000000000..850eb11ed2 --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -0,0 +1,134 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +import json +from typing import List, Optional, Dict + +from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal +from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest + +from UM import i18nCatalog +from UM.FileHandler.FileHandler import FileHandler +from UM.Logger import Logger +from UM.Scene.SceneNode import SceneNode +from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState +from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel +from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputController import CloudOutputController +from plugins.UM3NetworkPrinting.src.UM3PrintJobOutputModel import UM3PrintJobOutputModel + + +## The cloud output device is a network output device that works remotely but has limited functionality. +# Currently it only supports viewing the printer and print job status and adding a new job to the queue. +# As such, those methods have been implemented here. +# Note that this device represents a single remote cluster, not a list of multiple clusters. +# +# TODO: figure our how the QML interface for the cluster networking should operate with this limited functionality. +# TODO: figure out how to pair remote clusters, local networked clusters and local cura printer presets. +class CloudOutputDevice(NetworkedPrinterOutputDevice): + + # The translation catalog for this device. + I18N_CATALOG = i18nCatalog("cura") + + # The cloud URL to use for remote clusters. + API_ROOT_PATH_FORMAT = "https://api.ultimaker.com/connect/clusters/{cluster_id}" + + # Signal triggered when the printers in the remote cluster were changed. + printersChanged = pyqtSignal() + + # Signal triggered when the print jobs in the queue were changed. + printJobsChanged = pyqtSignal() + + def __init__(self, device_id: str, address: str, properties: Dict[bytes, bytes], parent: QObject = None): + super().__init__(device_id = device_id, address = address, properties = properties, parent = parent) + self._setInterfaceElements() + + # The API prefix is automatically added when doing any HTTP call on the device. + self._api_prefix = self.API_ROOT_PATH_FORMAT.format(device_id) # TODO: verify we can use device_id here + self._authentication_state = AuthState.Authenticated # TODO: use cura.API.Account to set this? + + # Properties to populate later on with received cloud data. + self._printers = [] + self._print_jobs = [] + self._number_of_extruders = 2 # All networked printers are dual-extrusion Ultimaker machines. + + ## Set all the interface elements and texts for this output device. + def _setInterfaceElements(self): + self.setPriority(3) + self.setName(self._id) + # TODO: how to name these? + self.setShortDescription(self.I18N_CATALOG.i18nc("@action:button", "Print via Cloud")) + self.setDescription(self.I18N_CATALOG.i18nc("@properties:tooltip", "Print via Cloud")) + self.setConnectionText(self.I18N_CATALOG.i18nc("@info:status", "Connected via Cloud")) + + ## Called when Cura requests an output device to receive a (G-code) file. + def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mime_types: bool = False, + file_handler: Optional[FileHandler] = None, **kwargs: str) -> None: + self.writeStarted.emit(self) + + # TODO: actually implement this + self._addPrintJobToQueue() + + ## Get remote printers. + @pyqtProperty("QVariantList", notify = printersChanged) + def printers(self): + return self._printers + + ## Get remote print jobs. + @pyqtProperty("QVariantList", notify = printJobsChanged) + def printJobs(self) -> List[UM3PrintJobOutputModel]: + return self._print_jobs + + ## Called when the connection to the cluster changes. + def connect(self) -> None: + super().connect() + + ## Called when the network data should be updated. + def _update(self) -> None: + super()._update() + self.get("/status", on_finished = self._onStatusCallFinished) + + def _onStatusCallFinished(self, reply: QNetworkReply) -> None: + status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) + if status_code != 200: + Logger.log("w", "Got unexpected response while trying to get cloud cluster data: {}, {}" + .format(status_code, reply.getErrorString())) + return + + data = self._parseStatusResponse(reply) + if data is None: + return + + # Update all data from the cluster. + self._updatePrinters(data.get("printers", [])) + self._updatePrintJobs(data.get("print_jobs", [])) + + @staticmethod + def _parseStatusResponse(reply: QNetworkReply) -> Optional[dict]: + try: + result = json.loads(bytes(reply.readAll()).decode("utf-8")) + # TODO: use model or named tuple here. + return result + except json.decoder.JSONDecodeError: + Logger.logException("w", "Unable to decode JSON from reply.") + return None + + def _updatePrinters(self, remote_printers: List[Dict[str, any]]) -> None: + # TODO: use model or tuple for remote_printers data + for printer in remote_printers: + + # If the printer does not exist yet, create it. + if not self._getPrinterByKey(printer["uuid"]): + self._printers.append(PrinterOutputModel( + output_controller = CloudOutputController(self), + number_of_extruders = self._number_of_extruders + )) + + # TODO: properly handle removed and updated printers + self.printersChanged.emit() + + def _updatePrintJobs(self, remote_print_jobs: List[Dict[str, any]]) -> None: + # TODO: use model or tuple for remote_print_jobs data + pass + + def _addPrintJobToQueue(self): + # TODO: implement this + pass diff --git a/plugins/UM3NetworkPrinting/src/Cloud/__init__.py b/plugins/UM3NetworkPrinting/src/Cloud/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/UM3NetworkPrinting/src/__init__.py b/plugins/UM3NetworkPrinting/src/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/__init__.py b/plugins/__init__.py new file mode 100644 index 0000000000..e69de29bb2 From 115936c46bb08cec4f4c02193edea7b5f0db0f32 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 19 Nov 2018 20:27:38 +0100 Subject: [PATCH 0253/1240] Target correct cloud API version --- plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 850eb11ed2..75cef817c6 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -29,7 +29,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): I18N_CATALOG = i18nCatalog("cura") # The cloud URL to use for remote clusters. - API_ROOT_PATH_FORMAT = "https://api.ultimaker.com/connect/clusters/{cluster_id}" + API_ROOT_PATH_FORMAT = "https://api.ultimaker.com/connect/v1/clusters/{cluster_id}" # Signal triggered when the printers in the remote cluster were changed. printersChanged = pyqtSignal() From 228325eb892ee43d1173a296797e277505c3bd52 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 19 Nov 2018 21:59:57 +0100 Subject: [PATCH 0254/1240] Add CloudOutputDeviceManager, test implementation --- .../NetworkedPrinterOutputDevice.py | 1 + plugins/UM3NetworkPrinting/__init__.py | 8 +++-- .../src/Cloud/CloudOutputDevice.py | 30 +++++++++++++----- .../src/Cloud/CloudOutputDeviceManager.py | 31 +++++++++++++++++++ .../src/UM3OutputDevicePlugin.py | 25 ++++++++++----- 5 files changed, 78 insertions(+), 17 deletions(-) create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py diff --git a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py index 9a3be936a2..9a6892ce4d 100644 --- a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py +++ b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py @@ -17,6 +17,7 @@ from enum import IntEnum import os # To get the username import gzip + class AuthState(IntEnum): NotAuthenticated = 1 AuthenticationRequested = 2 diff --git a/plugins/UM3NetworkPrinting/__init__.py b/plugins/UM3NetworkPrinting/__init__.py index e2ad5a2b12..23262aed94 100644 --- a/plugins/UM3NetworkPrinting/__init__.py +++ b/plugins/UM3NetworkPrinting/__init__.py @@ -1,11 +1,15 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. - from .src import DiscoverUM3Action from .src import UM3OutputDevicePlugin + def getMetaData(): return {} + def register(app): - return { "output_device": UM3OutputDevicePlugin.UM3OutputDevicePlugin(), "machine_action": DiscoverUM3Action.DiscoverUM3Action()} \ No newline at end of file + return { + "output_device": UM3OutputDevicePlugin.UM3OutputDevicePlugin(app), + "machine_action": DiscoverUM3Action.DiscoverUM3Action() + } diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 75cef817c6..61d22052be 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -3,13 +3,14 @@ import json from typing import List, Optional, Dict -from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal +from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, QUrl from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest from UM import i18nCatalog from UM.FileHandler.FileHandler import FileHandler from UM.Logger import Logger from UM.Scene.SceneNode import SceneNode +from cura.CuraApplication import CuraApplication from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputController import CloudOutputController @@ -37,19 +38,34 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # Signal triggered when the print jobs in the queue were changed. printJobsChanged = pyqtSignal() - def __init__(self, device_id: str, address: str, properties: Dict[bytes, bytes], parent: QObject = None): - super().__init__(device_id = device_id, address = address, properties = properties, parent = parent) + def __init__(self, device_id: str, parent: QObject = None): + super().__init__(device_id = device_id, address = "", properties = {}, parent = parent) self._setInterfaceElements() - # The API prefix is automatically added when doing any HTTP call on the device. - self._api_prefix = self.API_ROOT_PATH_FORMAT.format(device_id) # TODO: verify we can use device_id here - self._authentication_state = AuthState.Authenticated # TODO: use cura.API.Account to set this? + self._device_id = device_id + self._account = CuraApplication.getInstance().getCuraAPI().account # Properties to populate later on with received cloud data. self._printers = [] self._print_jobs = [] self._number_of_extruders = 2 # All networked printers are dual-extrusion Ultimaker machines. + ## We need to override _createEmptyRequest to work for the cloud. + def _createEmptyRequest(self, path: str, content_type: Optional[str] = "application/json") -> QNetworkRequest: + url = QUrl(self.API_ROOT_PATH_FORMAT.format(cluster_id = self._device_id) + path) + request = QNetworkRequest(url) + request.setHeader(QNetworkRequest.ContentTypeHeader, content_type) + request.setHeader(QNetworkRequest.UserAgentHeader, self._user_agent) + + if not self._account.isLoggedIn: + # TODO: show message to user to sign in + self.setAuthenticationState(AuthState.NotAuthenticated) + else: + self.setAuthenticationState(AuthState.Authenticated) + request.setRawHeader(b"Authorization", "Bearer {}".format(self._account.accessToken).encode()) + + return request + ## Set all the interface elements and texts for this output device. def _setInterfaceElements(self): self.setPriority(3) @@ -90,7 +106,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) if status_code != 200: Logger.log("w", "Got unexpected response while trying to get cloud cluster data: {}, {}" - .format(status_code, reply.getErrorString())) + .format(status_code, reply.readAll())) return data = self._parseStatusResponse(reply) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py new file mode 100644 index 0000000000..1f75edc2cc --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -0,0 +1,31 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from typing import TYPE_CHECKING + +from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputDevice import CloudOutputDevice + + +if TYPE_CHECKING: + from cura.CuraApplication import CuraApplication + + +## The cloud output device manager is responsible for using the Ultimaker Cloud APIs to manage remote clusters. +# Keeping all cloud related logic in this class instead of the UM3OutputDevicePlugin results in more readable code. +class CloudOutputDeviceManager: + + def __init__(self, application: "CuraApplication"): + self._output_device_manager = application.getOutputDeviceManager() + self._account = application.getCuraAPI().account + self._getRemoteClusters() + + # For testing: + application.globalContainerStackChanged.connect(self._addCloudOutputDevice) + + def _getRemoteClusters(self): + # TODO: get list of remote clusters and create an output device for each. + pass + + def _addCloudOutputDevice(self): + device = CloudOutputDevice("xxxx-xxxx-xxxx-xxxx") + self._output_device_manager.addOutputDevice(device) + device.connect() diff --git a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py index 9c070f2de2..d7a40626b9 100644 --- a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py @@ -1,11 +1,12 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from typing import TYPE_CHECKING from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin from UM.Logger import Logger -from UM.Application import Application from UM.Signal import Signal, signalemitter from UM.Version import Version +from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputDeviceManager import CloudOutputDeviceManager from . import ClusterUM3OutputDevice, LegacyUM3OutputDevice @@ -19,6 +20,9 @@ from time import time import json +if TYPE_CHECKING: + from cura.CuraApplication import CuraApplication + ## This plugin handles the connection detection & creation of output device objects for the UM3 printer. # Zero-Conf is used to detect printers, which are saved in a dict. @@ -29,8 +33,10 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): removeDeviceSignal = Signal() discoveredDevicesChanged = Signal() - def __init__(self): + def __init__(self, application: "CuraApplication"): super().__init__() + self._application = application + self._zero_conf = None self._zero_conf_browser = None @@ -38,7 +44,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self.addDeviceSignal.connect(self._onAddDevice) self.removeDeviceSignal.connect(self._onRemoveDevice) - Application.getInstance().globalContainerStackChanged.connect(self.reCheckConnections) + application.globalContainerStackChanged.connect(self.reCheckConnections) self._discovered_devices = {} @@ -53,7 +59,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self._cluster_api_prefix = "/cluster-api/v" + self._cluster_api_version + "/" # Get list of manual instances from preferences - self._preferences = Application.getInstance().getPreferences() + self._preferences = self._application.getPreferences() self._preferences.addPreference("um3networkprinting/manual_instances", "") # A comma-separated list of ip adresses or hostnames @@ -70,6 +76,9 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self._service_changed_request_event = Event() self._service_changed_request_thread = Thread(target=self._handleOnServiceChangedRequests, daemon=True) self._service_changed_request_thread.start() + + # Create a cloud output device manager that abstract all cloud connection logic away. + self._cloud_output_device_manager = CloudOutputDeviceManager(self._application) def getDiscoveredDevices(self): return self._discovered_devices @@ -104,7 +113,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self.resetLastManualDevice() def reCheckConnections(self): - active_machine = Application.getInstance().getGlobalContainerStack() + active_machine = self._application.getGlobalContainerStack() if not active_machine: return @@ -129,7 +138,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): return if self._discovered_devices[key].isConnected(): # Sometimes the status changes after changing the global container and maybe the device doesn't belong to this machine - um_network_key = Application.getInstance().getGlobalContainerStack().getMetaDataEntry("um_network_key") + um_network_key = self._application.getGlobalContainerStack().getMetaDataEntry("um_network_key") if key == um_network_key: self.getOutputDeviceManager().addOutputDevice(self._discovered_devices[key]) else: @@ -281,7 +290,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self._discovered_devices[device.getId()] = device self.discoveredDevicesChanged.emit() - global_container_stack = Application.getInstance().getGlobalContainerStack() + global_container_stack = self._application.getGlobalContainerStack() if global_container_stack and device.getId() == global_container_stack.getMetaDataEntry("um_network_key"): device.connect() device.connectionStateChanged.connect(self._onDeviceConnectionStateChanged) @@ -299,7 +308,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self._service_changed_request_event.wait(timeout = 5.0) # Stop if the application is shutting down - if Application.getInstance().isShuttingDown(): + if self._application.isShuttingDown(): return self._service_changed_request_event.clear() From 10576d12426ae520f42167d3dcc2afa7ffd808d9 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 19 Nov 2018 22:24:35 +0100 Subject: [PATCH 0255/1240] Some scaffolding and implementation for cloud output device manager --- .../src/Cloud/CloudOutputDevice.py | 9 ++-- .../src/Cloud/CloudOutputDeviceManager.py | 42 +++++++++++++++---- .../src/UM3OutputDevicePlugin.py | 6 +-- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 61d22052be..ff83c3fd5e 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -23,14 +23,13 @@ from plugins.UM3NetworkPrinting.src.UM3PrintJobOutputModel import UM3PrintJobOut # Note that this device represents a single remote cluster, not a list of multiple clusters. # # TODO: figure our how the QML interface for the cluster networking should operate with this limited functionality. -# TODO: figure out how to pair remote clusters, local networked clusters and local cura printer presets. class CloudOutputDevice(NetworkedPrinterOutputDevice): # The translation catalog for this device. I18N_CATALOG = i18nCatalog("cura") - # The cloud URL to use for remote clusters. - API_ROOT_PATH_FORMAT = "https://api.ultimaker.com/connect/v1/clusters/{cluster_id}" + # The cloud URL to use for this remote cluster. + API_ROOT_PATH_FORMAT = "https://api-staging.ultimaker.com/connect/v1/clusters/{cluster_id}" # Signal triggered when the printers in the remote cluster were changed. printersChanged = pyqtSignal() @@ -79,8 +78,6 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mime_types: bool = False, file_handler: Optional[FileHandler] = None, **kwargs: str) -> None: self.writeStarted.emit(self) - - # TODO: actually implement this self._addPrintJobToQueue() ## Get remote printers. @@ -102,6 +99,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): super()._update() self.get("/status", on_finished = self._onStatusCallFinished) + ## Method called when HTTP request to status endpoint is finished. + # Contains both printers and print jobs statuses in a single response. def _onStatusCallFinished(self, reply: QNetworkReply) -> None: status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) if status_code != 200: diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 1f75edc2cc..53f64241e5 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -1,6 +1,6 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Dict from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputDevice import CloudOutputDevice @@ -11,21 +11,47 @@ if TYPE_CHECKING: ## The cloud output device manager is responsible for using the Ultimaker Cloud APIs to manage remote clusters. # Keeping all cloud related logic in this class instead of the UM3OutputDevicePlugin results in more readable code. +# +# TODO: figure out how to pair remote clusters, local networked clusters and local cura printer presets. class CloudOutputDeviceManager: + # The cloud URL to use for remote clusters. + API_ROOT_PATH = "https://api-staging.ultimaker.com/connect/v1" + def __init__(self, application: "CuraApplication"): + self._application = application self._output_device_manager = application.getOutputDeviceManager() self._account = application.getCuraAPI().account - self._getRemoteClusters() - # For testing: - application.globalContainerStackChanged.connect(self._addCloudOutputDevice) + # Persistent dict containing the remote clusters for the authenticated user. + self._remote_clusters = {} # type: Dict[str, CloudOutputDevice] + + # When switching machines we check if we have to activate a remote cluster. + self._application.globalContainerStackChanged.connect(self._activeMachineChanged) + + # Fetch all remote clusters for the authenticated user. + self._getRemoteClusters() def _getRemoteClusters(self): # TODO: get list of remote clusters and create an output device for each. - pass + # For testing we add a dummy device: + self._addCloudOutputDevice({"cluster_id": "LJ0tciiuZZjarrXAvFLEZ6ox4Cvx8FvtXUlQv4vIhV6w"}) - def _addCloudOutputDevice(self): - device = CloudOutputDevice("xxxx-xxxx-xxxx-xxxx") + def _addCloudOutputDevice(self, cluster_data: Dict[str, any]): + # TODO: use model or named tuple for cluster_data + device = CloudOutputDevice(cluster_data["cluster_id"]) self._output_device_manager.addOutputDevice(device) - device.connect() + self._remote_clusters[cluster_data["cluster_id"]] = device + + def _activeMachineChanged(self): + active_machine = self._application.getGlobalContainerStack() + if not active_machine: + return + + stored_cluster_id = active_machine.getMetaDataEntry("um_cloud_cluster_id") + if stored_cluster_id not in self._remote_clusters.keys(): + # Currently authenticated user does not have access to stored cluster or no user is signed in. + return + + # We found the active machine as remote cluster so let's connect to it. + self._remote_clusters.get(stored_cluster_id).connect() diff --git a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py index d7a40626b9..b441df9eb5 100644 --- a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py @@ -40,6 +40,9 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self._zero_conf = None self._zero_conf_browser = None + # Create a cloud output device manager that abstract all cloud connection logic away. + self._cloud_output_device_manager = CloudOutputDeviceManager(self._application) + # Because the model needs to be created in the same thread as the QMLEngine, we use a signal. self.addDeviceSignal.connect(self._onAddDevice) self.removeDeviceSignal.connect(self._onRemoveDevice) @@ -76,9 +79,6 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self._service_changed_request_event = Event() self._service_changed_request_thread = Thread(target=self._handleOnServiceChangedRequests, daemon=True) self._service_changed_request_thread.start() - - # Create a cloud output device manager that abstract all cloud connection logic away. - self._cloud_output_device_manager = CloudOutputDeviceManager(self._application) def getDiscoveredDevices(self): return self._discovered_devices From ca1c5fb48cacdeec7383d5bc6c8d579ee6ac44e4 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 19 Nov 2018 22:30:17 +0100 Subject: [PATCH 0256/1240] Add some documentation --- .../src/Cloud/CloudOutputDeviceManager.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 53f64241e5..d5c4abae09 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -12,7 +12,10 @@ if TYPE_CHECKING: ## The cloud output device manager is responsible for using the Ultimaker Cloud APIs to manage remote clusters. # Keeping all cloud related logic in this class instead of the UM3OutputDevicePlugin results in more readable code. # +# API spec is available on https://api.ultimaker.com/docs/connect/spec/. +# # TODO: figure out how to pair remote clusters, local networked clusters and local cura printer presets. +# TODO: for now we just have multiple output devices if the cluster is available both locally and remote. class CloudOutputDeviceManager: # The cloud URL to use for remote clusters. @@ -32,17 +35,20 @@ class CloudOutputDeviceManager: # Fetch all remote clusters for the authenticated user. self._getRemoteClusters() + ## Gets all remote clusters from the API. def _getRemoteClusters(self): # TODO: get list of remote clusters and create an output device for each. # For testing we add a dummy device: self._addCloudOutputDevice({"cluster_id": "LJ0tciiuZZjarrXAvFLEZ6ox4Cvx8FvtXUlQv4vIhV6w"}) + ## Adds a CloudOutputDevice for each entry in the remote cluster list from the API. def _addCloudOutputDevice(self, cluster_data: Dict[str, any]): # TODO: use model or named tuple for cluster_data device = CloudOutputDevice(cluster_data["cluster_id"]) self._output_device_manager.addOutputDevice(device) self._remote_clusters[cluster_data["cluster_id"]] = device + ## Callback for when the active machine was changed by the user. def _activeMachineChanged(self): active_machine = self._application.getGlobalContainerStack() if not active_machine: From 04cc6193d6a6f7098732a808cd87841235a8e29b Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 19 Nov 2018 23:25:54 +0100 Subject: [PATCH 0257/1240] More implementation for getting remote clusters, add some TODOs --- .../src/Cloud/CloudOutputDevice.py | 1 + .../src/Cloud/CloudOutputDeviceManager.py | 67 +++++++++++++++++-- 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index ff83c3fd5e..8f0bd62035 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -60,6 +60,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # TODO: show message to user to sign in self.setAuthenticationState(AuthState.NotAuthenticated) else: + # TODO: not execute call at all when not signed in? self.setAuthenticationState(AuthState.Authenticated) request.setRawHeader(b"Authorization", "Bearer {}".format(self._account.accessToken).encode()) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index d5c4abae09..e88ee4dced 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -1,7 +1,12 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import TYPE_CHECKING, Dict +import json +from typing import TYPE_CHECKING, Dict, Optional +from PyQt5.QtCore import QUrl +from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply + +from UM.Logger import Logger from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputDevice import CloudOutputDevice @@ -19,31 +24,79 @@ if TYPE_CHECKING: class CloudOutputDeviceManager: # The cloud URL to use for remote clusters. - API_ROOT_PATH = "https://api-staging.ultimaker.com/connect/v1" + API_ROOT_PATH = "https://api.ultimaker.com/connect/v1" def __init__(self, application: "CuraApplication"): self._application = application self._output_device_manager = application.getOutputDeviceManager() self._account = application.getCuraAPI().account + # Network manager for getting the cluster list. + self._network_manager = QNetworkAccessManager() + self._network_manager.finished.connect(self._onNetworkRequestFinished) + # Persistent dict containing the remote clusters for the authenticated user. self._remote_clusters = {} # type: Dict[str, CloudOutputDevice] # When switching machines we check if we have to activate a remote cluster. self._application.globalContainerStackChanged.connect(self._activeMachineChanged) - + # Fetch all remote clusters for the authenticated user. - self._getRemoteClusters() + # TODO: update remote clusters periodically + self._account.loginStateChanged.connect(self._getRemoteClusters) ## Gets all remote clusters from the API. def _getRemoteClusters(self): - # TODO: get list of remote clusters and create an output device for each. - # For testing we add a dummy device: - self._addCloudOutputDevice({"cluster_id": "LJ0tciiuZZjarrXAvFLEZ6ox4Cvx8FvtXUlQv4vIhV6w"}) + url = QUrl("{}/clusters".format(self.API_ROOT_PATH)) + request = QNetworkRequest(url) + request.setHeader(QNetworkRequest.ContentTypeHeader, "application/json") + + if not self._account.isLoggedIn: + # TODO: show message to user to sign in + Logger.log("w", "User is not signed in, cannot get remote print clusters") + return + + request.setRawHeader(b"Authorization", "Bearer {}".format(self._account.accessToken).encode()) + self._network_manager.get(request) + + ## Callback for network requests. + def _onNetworkRequestFinished(self, reply: QNetworkReply): + # TODO: right now we assume that each reply is from /clusters, we should fix this + status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) + if status_code != 200: + # TODO: add correct scopes to OAuth2 client to use remote connect API. + Logger.log("w", "Got unexpected response while trying to get cloud cluster data: {}, {}" + .format(status_code, reply.readAll())) + return + + # Parse the response (returns the "data" field from the body). + clusters_data = self._parseStatusResponse(reply) + if not clusters_data: + return + + # Add an output device for each remote cluster. + # The clusters are an array of objects in a field called "data". + for cluster in clusters_data: + self._addCloudOutputDevice(cluster) + + # # For testing we add a dummy device: + # self._addCloudOutputDevice({ "cluster_id": "LJ0tciiuZZjarrXAvFLEZ6ox4Cvx8FvtXUlQv4vIhV6w" }) + + @staticmethod + def _parseStatusResponse(reply: QNetworkReply) -> Optional[dict]: + try: + result = json.loads(bytes(reply.readAll()).decode("utf-8")) + print("result=====", result) + # TODO: use model or named tuple here. + return result.data + except json.decoder.JSONDecodeError: + Logger.logException("w", "Unable to decode JSON from reply.") + return None ## Adds a CloudOutputDevice for each entry in the remote cluster list from the API. def _addCloudOutputDevice(self, cluster_data: Dict[str, any]): # TODO: use model or named tuple for cluster_data + print("cluster_data====", cluster_data) device = CloudOutputDevice(cluster_data["cluster_id"]) self._output_device_manager.addOutputDevice(device) self._remote_clusters[cluster_data["cluster_id"]] = device From e6d9ad31ab0e432a7357dfa702332cae25aead88 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 20 Nov 2018 09:20:45 +0100 Subject: [PATCH 0258/1240] Use generated Makefiles to run tests --- Jenkinsfile | 43 ++----------------------------------------- 1 file changed, 2 insertions(+), 41 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index f9a3a9864a..a345ebbd05 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -38,20 +38,9 @@ parallel_nodes(['linux && cura', 'windows && cura']) { if (isUnix()) { - // For Linux to show everything - def branch = env.BRANCH_NAME - if(!fileExists("${env.CURA_ENVIRONMENT_PATH}/${branch}")) - { - branch = "master" - } - def uranium_dir = get_workspace_dir("Ultimaker/Uranium/${branch}") - + // For Linux try { - sh """ - cd .. - export PYTHONPATH=.:"${uranium_dir}" - ${env.CURA_ENVIRONMENT_PATH}/${branch}/bin/pytest -x --verbose --full-trace --capture=no ./tests - """ + sh 'make CTEST_OUTPUT_ON_FAILURE=TRUE test' } catch(e) { currentBuild.result = "UNSTABLE" @@ -70,34 +59,6 @@ parallel_nodes(['linux && cura', 'windows && cura']) } } } - - stage('Code Style') - { - if (isUnix()) - { - // For Linux to show everything. - // CMake also runs this test, but if it fails then the test just shows "failed" without details of what exactly failed. - def branch = env.BRANCH_NAME - if(!fileExists("${env.CURA_ENVIRONMENT_PATH}/${branch}")) - { - branch = "master" - } - def uranium_dir = get_workspace_dir("Ultimaker/Uranium/${branch}") - - try - { - sh """ - cd .. - export PYTHONPATH=.:"${uranium_dir}" - ${env.CURA_ENVIRONMENT_PATH}/${branch}/bin/python3 run_mypy.py - """ - } - catch(e) - { - currentBuild.result = "UNSTABLE" - } - } - } } } From 76f2aeb43c8ab19a638ade5016229a5f542664cb Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 20 Nov 2018 11:27:45 +0100 Subject: [PATCH 0259/1240] Fix the title's top margin size in the add machine dialog. --- resources/qml/AddMachineDialog.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/AddMachineDialog.qml b/resources/qml/AddMachineDialog.qml index 0df8b891d9..aa160acd4d 100644 --- a/resources/qml/AddMachineDialog.qml +++ b/resources/qml/AddMachineDialog.qml @@ -73,7 +73,7 @@ UM.Dialog { top: parent.top left: parent.left - topMargin: UM.Theme.getSize("default_margin") + topMargin: UM.Theme.getSize("default_margin").height } text: catalog.i18nc("@title:tab", "Add a printer to Cura") From fab0d5a4b7f1e3b5dd9bfc6ccecba5cc73c0a799 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 20 Nov 2018 13:25:15 +0100 Subject: [PATCH 0260/1240] Rename the color of the sidebar to main_background, since it was deleted but not updated in the usages. Contributes to CURA-5785. --- plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml | 2 +- plugins/UM3NetworkPrinting/resources/qml/ClusterControlItem.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml b/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml index a48cb2ee3f..62e1e3ab86 100644 --- a/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml +++ b/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml @@ -83,7 +83,7 @@ Item model: packageData.supported_configs headerDelegate: Rectangle { - color: UM.Theme.getColor("sidebar") + color: UM.Theme.getColor("main_background") height: UM.Theme.getSize("toolbox_chart_row").height Label { diff --git a/plugins/UM3NetworkPrinting/resources/qml/ClusterControlItem.qml b/plugins/UM3NetworkPrinting/resources/qml/ClusterControlItem.qml index 068c369a3f..94e75a6de0 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/ClusterControlItem.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/ClusterControlItem.qml @@ -13,7 +13,7 @@ Component { property var shadowRadius: UM.Theme.getSize("monitor_shadow_radius").width; property var cornerRadius: UM.Theme.getSize("monitor_corner_radius").width; anchors.fill: parent; - color: UM.Theme.getColor("sidebar"); + color: UM.Theme.getColor("main_background"); visible: OutputDevice != null; UM.I18nCatalog { From fb3cb67da05115779cd6964d0d24e0760180ec99 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Tue, 20 Nov 2018 13:46:41 +0100 Subject: [PATCH 0261/1240] Add printer configuration components Contributes to CL-1148 --- .../qml/MonitorBuildplateConfiguration.qml | 63 +++++++++++++++ .../qml/MonitorExtruderConfiguration.qml | 76 +++++++++++++++++++ .../resources/qml/MonitorIconExtruder.qml | 60 +++++++++++++++ .../resources/qml/MonitorPrintJobCard.qml | 74 +++++++++++------- .../resources/qml/MonitorPrintJobPreview.qml | 6 +- .../qml/MonitorPrinterConfiguration.qml | 56 ++++++++++++++ .../resources/svg/icons/buildplate.svg | 5 ++ .../resources/svg/icons/extruder.svg | 5 ++ 8 files changed, 314 insertions(+), 31 deletions(-) create mode 100644 plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml create mode 100644 plugins/UM3NetworkPrinting/resources/qml/MonitorExtruderConfiguration.qml create mode 100644 plugins/UM3NetworkPrinting/resources/qml/MonitorIconExtruder.qml create mode 100644 plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterConfiguration.qml create mode 100644 plugins/UM3NetworkPrinting/resources/svg/icons/buildplate.svg create mode 100644 plugins/UM3NetworkPrinting/resources/svg/icons/extruder.svg diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml new file mode 100644 index 0000000000..d14277a1ff --- /dev/null +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml @@ -0,0 +1,63 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 2.0 +import UM 1.3 as UM + +/** + * This component comprises a buildplate icon and the buildplate name. It is + * used by the MonitorPrinterConfiguration component along with two instances + * of MonitorExtruderConfiguration. + * + * NOTE: For most labels, a fixed height with vertical alignment is used to make + * layouts more deterministic (like the fixed-size textboxes used in original + * mock-ups). This is also a stand-in for CSS's 'line-height' property. Denoted + * with '// FIXED-LINE-HEIGHT:'. + */ +Item +{ + // The buildplate name + property alias buildplate: buildplateLabel.text + + // Height is one 18px label/icon + height: 18 * screenScaleFactor // TODO: Theme! + width: childrenRect.width + + Row + { + height: parent.height + spacing: 12 * screenScaleFactor // TODO: Theme! (Should be same as extruder spacing) + + // This wrapper ensures that the buildplate icon is located centered + // below an extruder icon. + Item + { + height: parent.height + width: 32 * screenScaleFactor // TODO: Theme! (Should be same as extruder icon width) + + UM.RecolorImage + { + id: buildplateIcon + anchors.centerIn: parent + color: "#0a0850" // TODO: Theme! (Standard purple) + elide: Text.ElideRight + height: parent.height + source: "../svg/icons/buildplate.svg" + width: height + } + } + + Label + { + id: buildplateLabel + color: "#191919" // TODO: Theme! + font: UM.Theme.getFont("very_small") // 12pt, regular + text: "" + + // FIXED-LINE-HEIGHT: + height: 18 * screenScaleFactor // TODO: Theme! + verticalAlignment: Text.AlignVCenter + } + } +} \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorExtruderConfiguration.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorExtruderConfiguration.qml new file mode 100644 index 0000000000..afbd4c3641 --- /dev/null +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorExtruderConfiguration.qml @@ -0,0 +1,76 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 2.0 +import UM 1.3 as UM + +/** + * This component comprises a colored extruder icon, the material name, and the + * print core name. It is used by the MonitorPrinterConfiguration component with + * a sibling instance as well as a MonitorBuildplateConfiguration instance. + * + * NOTE: For most labels, a fixed height with vertical alignment is used to make + * layouts more deterministic (like the fixed-size textboxes used in original + * mock-ups). This is also a stand-in for CSS's 'line-height' property. Denoted + * with '// FIXED-LINE-HEIGHT:'. + */ +Item +{ + // The material color + property alias color: extruderIcon.color + + // The extruder position; NOTE: Decent human beings count from 0 + property alias position: extruderIcon.position + + // The material name + property alias material: materialLabel.text + + // The print core name (referred to as hotendID in Python) + property alias printCore: printCoreLabel.text + + // Height is 2 x 18px labels, plus 4px spacing between them + height: 40 * screenScaleFactor // TODO: Theme! + width: childrenRect.width + + MonitorIconExtruder + { + id: extruderIcon + color: "#eeeeee" // TODO: Theme! + position: 0 + } + Label + { + id: materialLabel + anchors + { + left: extruderIcon.right + leftMargin: 12 * screenScaleFactor // TODO: Theme! + } + color: "#191919" // TODO: Theme! + elide: Text.ElideRight + font: UM.Theme.getFont("very_small") // 12pt, regular + text: "" + + // FIXED-LINE-HEIGHT: + height: 18 * screenScaleFactor // TODO: Theme! + verticalAlignment: Text.AlignVCenter + } + Label + { + id: printCoreLabel + anchors + { + left: materialLabel.left + bottom: parent.bottom + } + color: "#191919" // TODO: Theme! + elide: Text.ElideRight + font: UM.Theme.getFont("small") // 12pt, bold + text: "" + + // FIXED-LINE-HEIGHT: + height: 18 * screenScaleFactor // TODO: Theme! + verticalAlignment: Text.AlignVCenter + } +} \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorIconExtruder.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorIconExtruder.qml new file mode 100644 index 0000000000..971c6b2251 --- /dev/null +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorIconExtruder.qml @@ -0,0 +1,60 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 2.0 +import UM 1.3 as UM + +/** + * This component is a sort of "super icon" which includes a colored SVG image + * as well as the extruder position number. It is used in the the + * MonitorExtruderConfiguration component. + */ +Item +{ + // The material color + property alias color: icon.color + + // The extruder position; NOTE: Decent human beings count from 0 + property int position: 0 + + // The extruder icon size; NOTE: This shouldn't need to be changed + property int size: 32 // TODO: Theme! + + // THe extruder icon source; NOTE: This shouldn't need to be changed + property string iconSource: "../svg/icons/extruder.svg" + + height: size + width: size + + UM.RecolorImage + { + id: icon + anchors.fill: parent + source: iconSource + width: size + } + + /* + * The label uses some "fancy" math to ensure that if you change the overall + * icon size, the number scales with it. That is to say, the font properties + * are linked to the icon size, NOT the theme. And that's intentional. + */ + Label + { + id: positionLabel + font + { + pointSize: Math.round(size * 0.3125) + weight: Font.Bold + } + height: Math.round(size / 2) * screenScaleFactor + horizontalAlignment: Text.AlignHCenter + text: position + 1 + verticalAlignment: Text.AlignVCenter + width: Math.round(size / 2) * screenScaleFactor + x: Math.round(size * 0.25) * screenScaleFactor + y: Math.round(size * 0.15625) * screenScaleFactor + // TODO: Once 'size' is themed, screenScaleFactor won't be needed + } +} \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml index 307a4f908f..6d02c40776 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml @@ -4,12 +4,21 @@ import QtQuick 2.2 import QtQuick.Controls 2.0 import UM 1.3 as UM -import Cura 1.0 as Cura -// A Print Job Card is essentially just a filled-in Expandable Card item. +/** + * A Print Job Card is essentially just a filled-in Expandable Card item. All + * data within it is derived from being passed a printJob property. + * + * NOTE: For most labels, a fixed height with vertical alignment is used to make + * layouts more deterministic (like the fixed-size textboxes used in original + * mock-ups). This is also a stand-in for CSS's 'line-height' property. Denoted + * with '// FIXED-LINE-HEIGHT:'. + */ Item { id: base + + // The print job which all other data is derived from property var printJob: null width: parent.width @@ -19,15 +28,15 @@ Item { headerItem: Row { - height: 48 + height: 48 * screenScaleFactor // TODO: Theme! anchors.left: parent.left - anchors.leftMargin: 24 - spacing: 18 + anchors.leftMargin: 24 * screenScaleFactor // TODO: Theme! + spacing: 18 * screenScaleFactor // TODO: Theme! MonitorPrintJobPreview { printJob: base.printJob - size: 32 + size: 32 * screenScaleFactor // TODO: Theme! anchors.verticalCenter: parent.verticalCenter } @@ -36,10 +45,13 @@ Item text: printJob && printJob.name ? printJob.name : "" color: "#374355" elide: Text.ElideRight - font: UM.Theme.getFont("default_bold") + font: UM.Theme.getFont("medium") // 14pt, regular anchors.verticalCenter: parent.verticalCenter - width: 216 - height: 18 + width: 216 * screenScaleFactor // TODO: Theme! + + // FIXED-LINE-HEIGHT: + height: 18 * screenScaleFactor // TODO: Theme! + verticalAlignment: Text.AlignVCenter } Label @@ -47,18 +59,20 @@ Item text: printJob ? OutputDevice.formatDuration(printJob.timeTotal) : "" color: "#374355" elide: Text.ElideRight - font: UM.Theme.getFont("default_bold") + font: UM.Theme.getFont("medium") // 14pt, regular anchors.verticalCenter: parent.verticalCenter - width: 216 - height: 18 + width: 216 * screenScaleFactor // TODO: Theme! + + // FIXED-LINE-HEIGHT: + height: 18 * screenScaleFactor // TODO: Theme! + verticalAlignment: Text.AlignVCenter } Label { color: "#374355" - height: 18 elide: Text.ElideRight - font: UM.Theme.getFont("default_bold") + font: UM.Theme.getFont("medium") // 14pt, regular text: { if (printJob !== null) { if (printJob.assignedPrinter == null) @@ -78,31 +92,39 @@ Item } visible: printJob anchors.verticalCenter: parent.verticalCenter - width: 216 + width: 216 * screenScaleFactor // TODO: Theme! + + // FIXED-LINE-HEIGHT: + height: 18 * screenScaleFactor // TODO: Theme! + verticalAlignment: Text.AlignVCenter } } drawerItem: Row { - height: 96 - anchors.left: parent.left - anchors.leftMargin: 74 - spacing: 18 + anchors + { + left: parent.left + leftMargin: 74 * screenScaleFactor // TODO: Theme! + } + height: 96 * screenScaleFactor // TODO: Theme! + spacing: 18 * screenScaleFactor // TODO: Theme! - Rectangle + MonitorPrinterConfiguration { id: printerConfiguration - width: 450 - height: 72 - color: "blue" anchors.verticalCenter: parent.verticalCenter + printJob: base.printJob } Label { - height: 18 text: printJob && printJob.owner ? printJob.owner : "" - color: "#374355" + color: "#374355" // TODO: Theme! elide: Text.ElideRight - font: UM.Theme.getFont("default_bold") + font: UM.Theme.getFont("medium") // 14pt, regular anchors.top: printerConfiguration.top + + // FIXED-LINE-HEIGHT: + height: 18 * screenScaleFactor // TODO: Theme! + verticalAlignment: Text.AlignVCenter } } } diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml index 7322193451..1a69d2dc12 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml @@ -2,14 +2,10 @@ // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.2 -import QtQuick.Dialogs 1.1 import QtQuick.Controls 2.0 -import QtQuick.Controls.Styles 1.4 -import QtGraphicalEffects 1.0 -import QtQuick.Layouts 1.1 -import QtQuick.Dialogs 1.1 import UM 1.3 as UM +// TODO: Documentation! Item { id: printJobPreview diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterConfiguration.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterConfiguration.qml new file mode 100644 index 0000000000..5d4d408b8e --- /dev/null +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterConfiguration.qml @@ -0,0 +1,56 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 2.0 +import UM 1.3 as UM + +/** + * + */ +Item +{ + id: base + + property var printJob: null + property var config0: printJob ? printJob.configuration.extruderConfigurations[0] : null + property var config1: printJob ? printJob.configuration.extruderConfigurations[1] : null + + height: 72 * screenScaleFactor // TODO: Theme! + width: 450 * screenScaleFactor // TODO: Theme! + + Row + { + spacing: 18 * screenScaleFactor // TODO: Theme! + + MonitorExtruderConfiguration + { + color: config0 ? config0.activeMaterial.color : "#eeeeee" // TODO: Theme! + material: config0 ? config0.activeMaterial.name : "" + position: config0.position + printCore: config0 ? config0.hotendID : "" + visible: config0 + + // Keep things responsive! + width: Math.floor((base.width - parent.spacing) / 2) + } + + MonitorExtruderConfiguration + { + color: config1 ? config1.activeMaterial.color : "#eeeeee" // TODO: Theme! + material: config1 ? config1.activeMaterial.name : "" + position: config1.position + printCore: config1 ? config1.hotendID : "" + visible: config1 + + // Keep things responsive! + width: Math.floor((base.width - parent.spacing) / 2) + } + } + + MonitorBuildplateConfiguration + { + anchors.bottom: parent.bottom + buildplate: "Glass" + } +} \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/resources/svg/icons/buildplate.svg b/plugins/UM3NetworkPrinting/resources/svg/icons/buildplate.svg new file mode 100644 index 0000000000..bcb278a8ca --- /dev/null +++ b/plugins/UM3NetworkPrinting/resources/svg/icons/buildplate.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/resources/svg/icons/extruder.svg b/plugins/UM3NetworkPrinting/resources/svg/icons/extruder.svg new file mode 100644 index 0000000000..235cb432e9 --- /dev/null +++ b/plugins/UM3NetworkPrinting/resources/svg/icons/extruder.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file From cbd5238738b1656c986caf87b58957ff605d4fb3 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 20 Nov 2018 13:55:28 +0100 Subject: [PATCH 0262/1240] Don't show custom/auto toggle buttons when not connected In order to prevent suddenly switching to the other side when the connection is lost or restored, we only evaluate this upon opening the popup. This way you might be surprised that closing and then opening it can show something else, but it will never surprise you while working on the popup itself as a user. Contributes to issue CURA-5876. --- .../ConfigurationMenu/ConfigurationMenu.qml | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index 10c52a7b0f..fe292799a5 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -98,31 +98,37 @@ Cura.ExpandableComponent popupItem: Item { - id: popup + id: popupItem width: base.width - 2 * UM.Theme.getSize("default_margin").width height: 200 - property var configuration_method: "auto" + property var is_connected: false //If current machine is connected to a printer. Only evaluated upon making popup visible. + onVisibleChanged: + { + is_connected = Cura.MachineManager.activeMachineNetworkKey != "" && Cura.MachineManager.printerConnected //Re-evaluate. + } + + property var configuration_method: buttonBar.visible ? "auto" : "custom" //Auto if connected to a printer at start-up, or Custom if not. AutoConfiguration { id: autoConfiguration - visible: popup.configuration_method === "auto" - anchors.top: header.bottom - height: visible ? childrenRect.height : 0 + visible: popupItem.configuration_method === "auto" + anchors.top: parent.top } CustomConfiguration { id: customConfiguration - visible: popup.configuration_method === "custom" - anchors.top: header.bottom - height: visible ? childrenRect.height : 0 + visible: popupItem.configuration_method === "custom" + anchors.top: parent.top } Rectangle { id: separator + visible: buttonBar.visible + anchors { left: parent.left @@ -131,12 +137,16 @@ Cura.ExpandableComponent bottomMargin: UM.Theme.getSize("default_margin").height } height: UM.Theme.getSize("default_lining").height + color: UM.Theme.getColor("lining") } + //Allow switching between custom and auto. Rectangle { id: buttonBar + visible: popupItem.is_connected //Switching only makes sense if the "auto" part is possible. + anchors { left: parent.left @@ -148,7 +158,7 @@ Cura.ExpandableComponent Cura.ActionButton { id: goToCustom - visible: popup.configuration_method === "auto" + visible: popupItem.configuration_method === "auto" text: catalog.i18nc("@label", "Custom") anchors @@ -165,13 +175,13 @@ Cura.ExpandableComponent leftPadding: UM.Theme.getSize("default_margin").width rightPadding: UM.Theme.getSize("default_margin").width - onClicked: popup.configuration_method = "custom" + onClicked: popupItem.configuration_method = "custom" } Cura.ActionButton { id: goToAuto - visible: popup.configuration_method === "custom" + visible: popupItem.configuration_method === "custom" text: catalog.i18nc("@label", "Configurations") anchors @@ -188,7 +198,7 @@ Cura.ExpandableComponent leftPadding: UM.Theme.getSize("default_margin").width rightPadding: UM.Theme.getSize("default_margin").width - onClicked: popup.configuration_method = "auto" + onClicked: popupItem.configuration_method = "auto" } } } From 42d73836f400357e73d288d0a3be56848f7743f6 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 20 Nov 2018 14:19:55 +0100 Subject: [PATCH 0263/1240] Move default margin by default to Cura's action buttons So we don't have to repeat that every time you create a new button. Contributes to issue CURA-5876. --- resources/qml/ActionButton.qml | 3 +++ resources/qml/ActionPanel/OutputDevicesActionButton.qml | 2 ++ resources/qml/ActionPanel/OutputProcessWidget.qml | 2 -- resources/qml/MainWindow/MainWindowHeader.qml | 2 -- resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml | 4 ---- 5 files changed, 5 insertions(+), 8 deletions(-) diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index 2a8b894867..08c44fb473 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -29,6 +29,9 @@ Button // we elide the text to the right so the text will be cut off with the three dots at the end. property var fixedWidthMode: false + leftPadding: UM.Theme.getSize("default_margin").width + rightPadding: UM.Theme.getSize("default_margin").width + contentItem: Row { UM.RecolorImage diff --git a/resources/qml/ActionPanel/OutputDevicesActionButton.qml b/resources/qml/ActionPanel/OutputDevicesActionButton.qml index be79a1893e..a2a69ed526 100644 --- a/resources/qml/ActionPanel/OutputDevicesActionButton.qml +++ b/resources/qml/ActionPanel/OutputDevicesActionButton.qml @@ -48,6 +48,8 @@ Item right: parent.right } + leftPadding: UM.Theme.getSize("narrow_margin").width //Need more space than usual here for wide text. + rightPadding: UM.Theme.getSize("narrow_margin").width tooltip: catalog.i18nc("@info:tooltip", "Select the active output device") iconSource: popup.opened ? UM.Theme.getIcon("arrow_top") : UM.Theme.getIcon("arrow_bottom") color: UM.Theme.getColor("action_panel_secondary") diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml index f4e014b1ec..772c05741f 100644 --- a/resources/qml/ActionPanel/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -104,8 +104,6 @@ Column { id: previewStageShortcut - leftPadding: UM.Theme.getSize("default_margin").width - rightPadding: UM.Theme.getSize("default_margin").width height: UM.Theme.getSize("action_panel_button").height text: catalog.i18nc("@button", "Preview") color: UM.Theme.getColor("secondary") diff --git a/resources/qml/MainWindow/MainWindowHeader.qml b/resources/qml/MainWindow/MainWindowHeader.qml index 59ec542e6b..6f3358648b 100644 --- a/resources/qml/MainWindow/MainWindowHeader.qml +++ b/resources/qml/MainWindow/MainWindowHeader.qml @@ -81,8 +81,6 @@ Rectangle rightMargin: UM.Theme.getSize("default_margin").width verticalCenter: parent.verticalCenter } - leftPadding: UM.Theme.getSize("default_margin").width - rightPadding: UM.Theme.getSize("default_margin").width text: catalog.i18nc("@action:button", "Marketplace") height: Math.round(0.5 * UM.Theme.getSize("main_window_header").height) color: UM.Theme.getColor("main_window_header_secondary_button_background_active") diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index fe292799a5..cb90735250 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -172,8 +172,6 @@ Cura.ExpandableComponent textColor: UM.Theme.getColor("primary") textHoverColor: UM.Theme.getColor("text") height: UM.Theme.getSize("action_panel_button").height - leftPadding: UM.Theme.getSize("default_margin").width - rightPadding: UM.Theme.getSize("default_margin").width onClicked: popupItem.configuration_method = "custom" } @@ -195,8 +193,6 @@ Cura.ExpandableComponent textColor: UM.Theme.getColor("primary") textHoverColor: UM.Theme.getColor("text") height: UM.Theme.getSize("action_panel_button").height - leftPadding: UM.Theme.getSize("default_margin").width - rightPadding: UM.Theme.getSize("default_margin").width onClicked: popupItem.configuration_method = "auto" } From a1068a3ef27d7fa3499ae8c149a3d0da2945e2cb Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 20 Nov 2018 14:24:09 +0100 Subject: [PATCH 0264/1240] Rename action_panel_button theme entry to action_button Because it's not just used in the save panel. Contributes to issue CURA-5876. --- resources/qml/ActionPanel/OutputProcessWidget.qml | 6 +++--- resources/qml/ActionPanel/SliceProcessWidget.qml | 2 +- resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml | 4 ++-- resources/themes/cura-light/theme.json | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml index 772c05741f..61efda2f2d 100644 --- a/resources/qml/ActionPanel/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -104,7 +104,7 @@ Column { id: previewStageShortcut - height: UM.Theme.getSize("action_panel_button").height + height: UM.Theme.getSize("action_button").height text: catalog.i18nc("@button", "Preview") color: UM.Theme.getColor("secondary") hoverColor: UM.Theme.getColor("secondary") @@ -116,8 +116,8 @@ Column Cura.OutputDevicesActionButton { - width: previewStageShortcut.visible ? UM.Theme.getSize("action_panel_button").width : parent.width - height: UM.Theme.getSize("action_panel_button").height + width: previewStageShortcut.visible ? UM.Theme.getSize("action_button").width : parent.width + height: UM.Theme.getSize("action_button").height } } } \ No newline at end of file diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index 2d4a7b6b89..7de1a22edb 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -85,7 +85,7 @@ Column { id: prepareButton width: parent.width - height: UM.Theme.getSize("action_panel_button").height + height: UM.Theme.getSize("action_button").height fixedWidthMode: true text: { diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index cb90735250..1015f67a2e 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -171,7 +171,7 @@ Cura.ExpandableComponent hoverColor: UM.Theme.getColor("secondary") textColor: UM.Theme.getColor("primary") textHoverColor: UM.Theme.getColor("text") - height: UM.Theme.getSize("action_panel_button").height + height: UM.Theme.getSize("action_button").height onClicked: popupItem.configuration_method = "custom" } @@ -192,7 +192,7 @@ Cura.ExpandableComponent hoverColor: UM.Theme.getColor("secondary") textColor: UM.Theme.getColor("primary") textHoverColor: UM.Theme.getColor("text") - height: UM.Theme.getSize("action_panel_button").height + height: UM.Theme.getSize("action_button").height onClicked: popupItem.configuration_method = "auto" } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index d28611529b..8b3e606094 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -385,7 +385,6 @@ "action_panel_widget": [25.0, 0.0], "action_panel_information_widget": [20.0, 0.0], - "action_panel_button": [15.0, 3.0], "machine_selector_widget": [16.0, 4.5], @@ -432,6 +431,9 @@ "button_icon": [2.5, 2.5], "button_lining": [0, 0], + "action_button": [15.0, 3.0], + "action_button_radius": [0.15, 0.15], + "small_button": [2, 2], "small_button_icon": [1.5, 1.5], @@ -520,8 +522,6 @@ "avatar_image": [6.8, 6.8], - "action_button_radius": [0.15, 0.15], - "monitor_config_override_box": [1.0, 14.0], "monitor_extruder_circle": [2.75, 2.75], "monitor_text_line": [1.16, 1.16], From 895590c3d0a445d4e114766348cdc5de676442e5 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 20 Nov 2018 14:28:54 +0100 Subject: [PATCH 0265/1240] Change the size of the progress bar control. Also add the rounded rectangle. Contributes to the new Cura UI-Flow. --- resources/themes/cura-light/styles.qml | 1 + resources/themes/cura-light/theme.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index 6f099bf1c5..58035ae3bd 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -515,6 +515,7 @@ QtObject implicitWidth: Theme.getSize("message").width - (Theme.getSize("default_margin").width * 2) implicitHeight: Theme.getSize("progressbar").height color: control.hasOwnProperty("backgroundColor") ? control.backgroundColor : Theme.getColor("progressbar_background") + radius: Theme.getSize("progressbar_radius").width } progress: Rectangle { diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index a33ff87042..f4d50e4c79 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -448,7 +448,7 @@ "progressbar": [26.0, 0.75], "progressbar_radius": [0.15, 0.15], - "progressbar_control": [8.0, 0.4], + "progressbar_control": [8.0, 0.75], "scrollbar": [0.75, 0.5], From dca286cea51e1f336df2a5fa2aa0f7f80dc9917e Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 20 Nov 2018 14:34:06 +0100 Subject: [PATCH 0266/1240] Give action button a sane default height We don't want to give it a default width since that should just adjust to its contents by default, which is fine. Contributes to issue CURA-5876. --- resources/qml/ActionButton.qml | 1 + resources/qml/ActionPanel/OutputProcessWidget.qml | 1 - resources/qml/ActionPanel/SliceProcessWidget.qml | 1 - resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml | 2 -- 4 files changed, 1 insertion(+), 4 deletions(-) diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index 08c44fb473..0ed639db75 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -31,6 +31,7 @@ Button leftPadding: UM.Theme.getSize("default_margin").width rightPadding: UM.Theme.getSize("default_margin").width + height: UM.Theme.getSize("action_button").height contentItem: Row { diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml index 61efda2f2d..98dbe5bd2c 100644 --- a/resources/qml/ActionPanel/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -104,7 +104,6 @@ Column { id: previewStageShortcut - height: UM.Theme.getSize("action_button").height text: catalog.i18nc("@button", "Preview") color: UM.Theme.getColor("secondary") hoverColor: UM.Theme.getColor("secondary") diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index 7de1a22edb..09e0b2584a 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -85,7 +85,6 @@ Column { id: prepareButton width: parent.width - height: UM.Theme.getSize("action_button").height fixedWidthMode: true text: { diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index 1015f67a2e..aa30a1236b 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -171,7 +171,6 @@ Cura.ExpandableComponent hoverColor: UM.Theme.getColor("secondary") textColor: UM.Theme.getColor("primary") textHoverColor: UM.Theme.getColor("text") - height: UM.Theme.getSize("action_button").height onClicked: popupItem.configuration_method = "custom" } @@ -192,7 +191,6 @@ Cura.ExpandableComponent hoverColor: UM.Theme.getColor("secondary") textColor: UM.Theme.getColor("primary") textHoverColor: UM.Theme.getColor("text") - height: UM.Theme.getSize("action_button").height onClicked: popupItem.configuration_method = "auto" } From 3b4d728d6a134237c708b0093bc6925122859056 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 20 Nov 2018 14:35:13 +0100 Subject: [PATCH 0267/1240] Fix Simulation view popup not sizing down I have no idea why implictHeight does work and childrenRect.height doesn't (eg; childrenRect.height only scales up for some reason, never down) CURA-5785 --- plugins/SimulationView/SimulationViewMenuComponent.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/SimulationView/SimulationViewMenuComponent.qml b/plugins/SimulationView/SimulationViewMenuComponent.qml index 89615f43a4..caf47508e3 100644 --- a/plugins/SimulationView/SimulationViewMenuComponent.qml +++ b/plugins/SimulationView/SimulationViewMenuComponent.qml @@ -65,7 +65,7 @@ Cura.ExpandableComponent property int top_layer_count: UM.Preferences.getValue("view/top_layer_count") width: UM.Theme.getSize("layerview_menu_size").width - 2 * UM.Theme.getSize("default_margin").width - height: childrenRect.height + height: implicitHeight spacing: UM.Theme.getSize("layerview_row_spacing").height From d42ddad606111ffcf3a1e764e3bc0af6c7064d79 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 20 Nov 2018 14:47:41 +0100 Subject: [PATCH 0268/1240] Add icons on left and right side of text For this I had to implement functionality in ActionButton to be able to display the icon on either side. Contributes to issue CURA-5876. --- resources/qml/ActionButton.qml | 24 +++++++++++++++---- .../ConfigurationMenu/ConfigurationMenu.qml | 5 ++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index 0ed639db75..28bda7fd35 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -11,7 +11,8 @@ Button { id: button property alias cursorShape: mouseArea.cursorShape - property alias iconSource: buttonIcon.source + property alias iconSource: buttonIconLeft.source + property var iconOnRightSide: false property alias textFont: buttonText.font property alias cornerRadius: backgroundRect.radius property alias tooltip: tooltip.text @@ -35,16 +36,17 @@ Button contentItem: Row { + //Icon if displayed on the left side. UM.RecolorImage { - id: buttonIcon + id: buttonIconLeft source: "" height: Math.round(0.6 * parent.height) - width: height + width: visible ? height : 0 sourceSize.width: width sourceSize.height: height color: button.hovered ? button.textHoverColor : button.textColor - visible: source != "" + visible: source != "" && !button.iconOnRightSide anchors.verticalCenter: parent.verticalCenter } @@ -61,6 +63,20 @@ Button horizontalAlignment: Text.AlignHCenter elide: Text.ElideRight } + + //Icon if displayed on the right side. + UM.RecolorImage + { + id: buttonIconRight + source: buttonIconLeft.source + height: Math.round(0.6 * parent.height) + width: visible ? height : 0 + sourceSize.width: width + sourceSize.height: height + color: button.hovered ? button.textHoverColor : button.textColor + visible: source != "" && button.iconOnRightSide + anchors.verticalCenter: parent.verticalCenter + } } background: Rectangle diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index aa30a1236b..b38384ef8a 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -172,6 +172,9 @@ Cura.ExpandableComponent textColor: UM.Theme.getColor("primary") textHoverColor: UM.Theme.getColor("text") + iconSource: UM.Theme.icons.arrow_right + iconOnRightSide: true + onClicked: popupItem.configuration_method = "custom" } @@ -192,6 +195,8 @@ Cura.ExpandableComponent textColor: UM.Theme.getColor("primary") textHoverColor: UM.Theme.getColor("text") + iconSource: UM.Theme.icons.arrow_left + onClicked: popupItem.configuration_method = "auto" } } From 6de24250edbb5d5a86dbdea6eff79e15f7196326 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 20 Nov 2018 14:50:12 +0100 Subject: [PATCH 0269/1240] Make icon height equal to text height This seems to be about the same actually as what it was. But now the relation is in there. I also made the right icon inherit as much as possible from the left icon so that if we change something, we only have to change it in the left icon. Contributes to issue CURA-5876. --- resources/qml/ActionButton.qml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index 28bda7fd35..7ecaeda4b0 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -41,7 +41,7 @@ Button { id: buttonIconLeft source: "" - height: Math.round(0.6 * parent.height) + height: buttonText.height width: visible ? height : 0 sourceSize.width: width sourceSize.height: height @@ -69,13 +69,13 @@ Button { id: buttonIconRight source: buttonIconLeft.source - height: Math.round(0.6 * parent.height) + height: buttonText.height width: visible ? height : 0 sourceSize.width: width sourceSize.height: height - color: button.hovered ? button.textHoverColor : button.textColor + color: buttonIconLeft.color visible: source != "" && button.iconOnRightSide - anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenter: buttonIconLeft.verticalCenter } } From 2f84339f5cb51be69098c0a9c6e33cc535f508ad Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Tue, 20 Nov 2018 15:58:09 +0100 Subject: [PATCH 0270/1240] Finalize queue Contributes to CL-1148 --- .../resources/qml/ClusterMonitorItem.qml | 269 ++++++++++++------ .../resources/qml/ExpandableCard.qml | 1 + .../qml/MonitorBuildplateConfiguration.qml | 2 +- .../resources/qml/MonitorPrintJobCard.qml | 108 +++++-- .../qml/MonitorPrinterConfiguration.qml | 30 +- .../resources/qml/MonitorPrinterPill.qml | 34 +++ .../resources/svg/icons/external_link.svg | 8 + 7 files changed, 327 insertions(+), 125 deletions(-) create mode 100644 plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterPill.qml create mode 100644 plugins/UM3NetworkPrinting/resources/svg/icons/external_link.svg diff --git a/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml b/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml index ff4bc72218..d055071521 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml @@ -8,102 +8,203 @@ import UM 1.3 as UM import Cura 1.0 as Cura import QtGraphicalEffects 1.0 -Component { - - Rectangle { - id: monitorFrame; - property var emphasisColor: UM.Theme.getColor("setting_control_border_highlight"); - property var cornerRadius: UM.Theme.getSize("monitor_corner_radius").width; +Component +{ + Rectangle + { + id: monitorFrame + + property var emphasisColor: UM.Theme.getColor("setting_control_border_highlight") + property var cornerRadius: UM.Theme.getSize("monitor_corner_radius").width + color: transparent - height: maximumHeight; - onVisibleChanged: { - if (monitorFrame != null && !monitorFrame.visible) { - OutputDevice.setActiveCameraUrl(""); + height: maximumHeight + onVisibleChanged: + { + if (monitorFrame != null && !monitorFrame.visible) + { + OutputDevice.setActiveCameraUrl("") } } - width: maximumWidth; + width: maximumWidth + + UM.I18nCatalog + { + id: catalog + name: "cura" + } LinearGradient { anchors.fill: parent gradient: Gradient { - GradientStop { position: 0.0; color: "#f6f6f6" } - GradientStop { position: 1.0; color: "#ffffff" } - } - } - - UM.I18nCatalog { - id: catalog; - name: "cura"; - } - - Label { - id: manageQueueLabel; - anchors { - bottom: queuedLabel.bottom; - right: queuedPrintJobs.right; - rightMargin: 3 * UM.Theme.getSize("default_margin").width; - } - color: UM.Theme.getColor("primary"); - font: UM.Theme.getFont("default"); - linkColor: UM.Theme.getColor("primary"); - text: catalog.i18nc("@label link to connect manager", "Manage queue"); - } - - MouseArea { - anchors.fill: manageQueueLabel; - hoverEnabled: true; - onClicked: Cura.MachineManager.printerOutputDevices[0].openPrintJobControlPanel(); - onEntered: manageQueueLabel.font.underline = true; - onExited: manageQueueLabel.font.underline = false; - } - - Label { - id: queuedLabel; - anchors { - left: queuedPrintJobs.left; - leftMargin: 3 * UM.Theme.getSize("default_margin").width + 5 * screenScaleFactor; - top: parent.top; - topMargin: 2 * UM.Theme.getSize("default_margin").height; - } - color: UM.Theme.getColor("text"); - font: UM.Theme.getFont("large"); - text: catalog.i18nc("@label", "Queued"); - } - - ScrollView { - id: queuedPrintJobs; - anchors { - top: queuedLabel.bottom; - topMargin: UM.Theme.getSize("default_margin").height; - horizontalCenter: parent.horizontalCenter; - bottomMargin: UM.Theme.getSize("default_margin").height; - bottom: parent.bottom; - } - style: UM.Theme.styles.scrollview; - visible: OutputDevice.receivedPrintJobs; - width: Math.min(834 * screenScaleFactor, maximumWidth); - - ListView { - id: printJobList; - anchors.fill: parent; - delegate: MonitorPrintJobCard { - anchors { - left: parent.left; - leftMargin: UM.Theme.getSize("default_margin").width; - right: parent.right; - rightMargin: UM.Theme.getSize("default_margin").width; - } - printJob: modelData; + GradientStop { + position: 0.0 + color: "#f6f6f6" + } + GradientStop { + position: 1.0 + color: "#ffffff" + } + } + } + + Item + { + id: queue + + anchors.fill: parent + anchors.top: parent.top + anchors.topMargin: 400 * screenScaleFactor // TODO: Insert carousel here + + Label + { + id: queuedLabel + anchors + { + left: queuedPrintJobs.left + top: parent.top + } + color: UM.Theme.getColor("text") + font: UM.Theme.getFont("large_nonbold") + text: catalog.i18nc("@label", "Queued") + } + + Item + { + id: manageQueueLabel + anchors + { + right: queuedPrintJobs.right + verticalCenter: queuedLabel.verticalCenter + } + height: 18 * screenScaleFactor // TODO: Theme! + width: childrenRect.width + + UM.RecolorImage + { + id: externalLinkIcon + anchors.verticalCenter: externalLinkIcon.verticalCenter + color: UM.Theme.getColor("primary") + source: "../svg/icons/external_link.svg" + width: 16 * screenScaleFactor // TODO: Theme! (Y U NO USE 18 LIKE ALL OTHER ICONS?!) + height: 16 * screenScaleFactor // TODO: Theme! (Y U NO USE 18 LIKE ALL OTHER ICONS?!) + } + Label + { + anchors + { + left: externalLinkIcon.right + leftMargin: 6 * screenScaleFactor // TODO: Theme! + verticalCenter: externalLinkIcon.verticalCenter + } + color: UM.Theme.getColor("primary") + font: UM.Theme.getFont("default") + linkColor: UM.Theme.getColor("primary") + text: catalog.i18nc("@label link to connect manager", "Manage queue in Cura Connect") + } + } + + + MouseArea + { + anchors.fill: manageQueueLabel + hoverEnabled: true + onClicked: Cura.MachineManager.printerOutputDevices[0].openPrintJobControlPanel() + onEntered: manageQueueLabel.font.underline = true + onExited: manageQueueLabel.font.underline = false + } + + Row + { + id: printJobQueueHeadings + anchors + { + left: queuedPrintJobs.left + leftMargin: 6 * screenScaleFactor // TODO: Theme! + top: queuedLabel.bottom + topMargin: 24 * screenScaleFactor // TODO: Theme! + } + spacing: 18 * screenScaleFactor // TODO: Theme! + + Label + { + text: catalog.i18nc("@label", "Print jobs") + color: "#666666" + elide: Text.ElideRight + font: UM.Theme.getFont("medium") // 14pt, regular + anchors.verticalCenter: parent.verticalCenter + width: 284 * screenScaleFactor // TODO: Theme! (Should match column size) + + // FIXED-LINE-HEIGHT: + height: 18 * screenScaleFactor // TODO: Theme! + verticalAlignment: Text.AlignVCenter + } + + Label + { + text: catalog.i18nc("@label", "Total print time") + color: "#666666" + elide: Text.ElideRight + font: UM.Theme.getFont("medium") // 14pt, regular + anchors.verticalCenter: parent.verticalCenter + width: 216 * screenScaleFactor // TODO: Theme! (Should match column size) + + // FIXED-LINE-HEIGHT: + height: 18 * screenScaleFactor // TODO: Theme! + verticalAlignment: Text.AlignVCenter + } + + Label + { + text: catalog.i18nc("@label", "Waiting for") + color: "#666666" + elide: Text.ElideRight + font: UM.Theme.getFont("medium") // 14pt, regular + anchors.verticalCenter: parent.verticalCenter + width: 216 * screenScaleFactor // TODO: Theme! (Should match column size) + + // FIXED-LINE-HEIGHT: + height: 18 * screenScaleFactor // TODO: Theme! + verticalAlignment: Text.AlignVCenter + } + } + + ScrollView + { + id: queuedPrintJobs + anchors { + bottom: parent.bottom + horizontalCenter: parent.horizontalCenter + top: printJobQueueHeadings.bottom + topMargin: 12 * screenScaleFactor // TODO: Theme! + } + style: UM.Theme.styles.scrollview + visible: OutputDevice.receivedPrintJobs + width: Math.min(834 * screenScaleFactor, maximumWidth) + + ListView + { + id: printJobList + anchors.fill: parent + delegate: MonitorPrintJobCard + { + anchors + { + left: parent.left + right: parent.right + } + printJob: modelData + } + model: OutputDevice.queuedPrintJobs + spacing: 6 } - model: OutputDevice.queuedPrintJobs; - spacing: 6; } } PrinterVideoStream { - anchors.fill: parent; - cameraUrl: OutputDevice.activeCameraUrl; - visible: OutputDevice.activeCameraUrl != ""; + anchors.fill: parent + cameraUrl: OutputDevice.activeCameraUrl + visible: OutputDevice.activeCameraUrl != "" } } diff --git a/plugins/UM3NetworkPrinting/resources/qml/ExpandableCard.qml b/plugins/UM3NetworkPrinting/resources/qml/ExpandableCard.qml index 89d88d671a..4922aea853 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/ExpandableCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/ExpandableCard.qml @@ -6,6 +6,7 @@ import QtQuick.Controls 2.0 import UM 1.3 as UM import Cura 1.0 as Cura +// TODO: Theme & documentation! // The expandable component has 3 major sub components: // * The headerItem Always visible and should hold some info about what happens if the component is expanded // * The popupItem The content that needs to be shown if the component is expanded. diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml index d14277a1ff..9ffb1eabb4 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml @@ -41,7 +41,6 @@ Item id: buildplateIcon anchors.centerIn: parent color: "#0a0850" // TODO: Theme! (Standard purple) - elide: Text.ElideRight height: parent.height source: "../svg/icons/buildplate.svg" width: height @@ -52,6 +51,7 @@ Item { id: buildplateLabel color: "#191919" // TODO: Theme! + elide: Text.ElideRight font: UM.Theme.getFont("very_small") // 12pt, regular text: "" diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml index 6d02c40776..ada6f8a644 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml @@ -47,7 +47,7 @@ Item elide: Text.ElideRight font: UM.Theme.getFont("medium") // 14pt, regular anchors.verticalCenter: parent.verticalCenter - width: 216 * screenScaleFactor // TODO: Theme! + width: 216 * screenScaleFactor // TODO: Theme! (Should match column size) // FIXED-LINE-HEIGHT: height: 18 * screenScaleFactor // TODO: Theme! @@ -61,42 +61,72 @@ Item elide: Text.ElideRight font: UM.Theme.getFont("medium") // 14pt, regular anchors.verticalCenter: parent.verticalCenter - width: 216 * screenScaleFactor // TODO: Theme! + width: 216 * screenScaleFactor // TODO: Theme! (Should match column size) // FIXED-LINE-HEIGHT: height: 18 * screenScaleFactor // TODO: Theme! verticalAlignment: Text.AlignVCenter } - Label + Item { - color: "#374355" - elide: Text.ElideRight - font: UM.Theme.getFont("medium") // 14pt, regular - text: { - if (printJob !== null) { - if (printJob.assignedPrinter == null) - { - if (printJob.state == "error") - { - return catalog.i18nc("@label", "Waiting for: Unavailable printer") - } - return catalog.i18nc("@label", "Waiting for: First available") - } - else - { - return catalog.i18nc("@label", "Waiting for: ") + printJob.assignedPrinter.name - } - } - return "" - } - visible: printJob anchors.verticalCenter: parent.verticalCenter - width: 216 * screenScaleFactor // TODO: Theme! + height: childrenRect.height + width: childrenRect.width - // FIXED-LINE-HEIGHT: - height: 18 * screenScaleFactor // TODO: Theme! - verticalAlignment: Text.AlignVCenter + Label + { + id: printerAssignmentLabel + anchors.verticalCenter: parent.verticalCenter + color: "#374355" + elide: Text.ElideRight + font: UM.Theme.getFont("medium") // 14pt, regular + text: { + if (printJob !== null) { + if (printJob.assignedPrinter == null) + { + if (printJob.state == "error") + { + return catalog.i18nc("@label", "Unavailable printer") + } + return catalog.i18nc("@label", "First available") + } + else + { + return printJob.assignedPrinter.name + } + } + return "" + } + visible: printJob + + // FIXED-LINE-HEIGHT: + height: 18 * screenScaleFactor // TODO: Theme! + verticalAlignment: Text.AlignVCenter + } + + Row + { + id: printerFamilyPills + anchors + { + left: printerAssignmentLabel.right; + leftMargin: 12 // TODO: Theme! + verticalCenter: parent.verticalCenter + } + height: childrenRect.height + spacing: 6 // TODO: Theme! + + Repeater + { + id: compatiblePills + delegate: MonitorPrinterPill + { + text: modelData + } + model: printJob ? printJob.compatibleMachineFamilies : [] + } + } } } drawerItem: Row @@ -106,14 +136,17 @@ Item left: parent.left leftMargin: 74 * screenScaleFactor // TODO: Theme! } - height: 96 * screenScaleFactor // TODO: Theme! + height: 108 * screenScaleFactor // TODO: Theme! spacing: 18 * screenScaleFactor // TODO: Theme! MonitorPrinterConfiguration { id: printerConfiguration anchors.verticalCenter: parent.verticalCenter - printJob: base.printJob + buildplate: "Glass" + config0: base.printJob.configuration.extruderConfigurations[0] + config1: base.printJob.configuration.extruderConfigurations[1] + height: 72 * screenScaleFactor // TODO: Theme! } Label { text: printJob && printJob.owner ? printJob.owner : "" @@ -128,4 +161,19 @@ Item } } } + + PrintJobContextMenu + { + id: contextButton + anchors + { + right: parent.right; + rightMargin: 8 * screenScaleFactor // TODO: Theme! + top: parent.top + topMargin: 8 * screenScaleFactor // TODO: Theme! + } + printJob: base.printJob + width: 32 * screenScaleFactor // TODO: Theme! + height: 32 * screenScaleFactor // TODO: Theme! + } } \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterConfiguration.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterConfiguration.qml index 5d4d408b8e..a31c8bbd99 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterConfiguration.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterConfiguration.qml @@ -6,17 +6,26 @@ import QtQuick.Controls 2.0 import UM 1.3 as UM /** - * + * The MonitorPrinterConfiguration accepts 2 configuration objects as input and + * applies them to a MonitorBuildplateConfiguration instance and two instances + * of MonitorExtruderConfiguration. It's used in both the MonitorPrintJobCard + * component as well as the MonitorPrinterCard component. */ Item { id: base - property var printJob: null - property var config0: printJob ? printJob.configuration.extruderConfigurations[0] : null - property var config1: printJob ? printJob.configuration.extruderConfigurations[1] : null + // Extracted buildplate configuration + property alias buildplate: buildplateConfig.buildplate - height: 72 * screenScaleFactor // TODO: Theme! + // Extracted extruder configuration for position 0 + property var config0: null + + // Extracted extruder configuration for position 1 + property var config1: null + + // Default size, but should be stretched to fill parent + height: 72 * parent.height width: 450 * screenScaleFactor // TODO: Theme! Row @@ -25,8 +34,8 @@ Item MonitorExtruderConfiguration { - color: config0 ? config0.activeMaterial.color : "#eeeeee" // TODO: Theme! - material: config0 ? config0.activeMaterial.name : "" + color: config0 && config0.activeMaterial ? config0.activeMaterial.color : "#eeeeee" // TODO: Theme! + material: config0 && config0.activeMaterial ? config0.activeMaterial.name : "" position: config0.position printCore: config0 ? config0.hotendID : "" visible: config0 @@ -37,8 +46,8 @@ Item MonitorExtruderConfiguration { - color: config1 ? config1.activeMaterial.color : "#eeeeee" // TODO: Theme! - material: config1 ? config1.activeMaterial.name : "" + color: config1 && config1.activeMaterial ? config1.activeMaterial.color : "#eeeeee" // TODO: Theme! + material: config1 && config1.activeMaterial ? config1.activeMaterial.name : "" position: config1.position printCore: config1 ? config1.hotendID : "" visible: config1 @@ -50,7 +59,8 @@ Item MonitorBuildplateConfiguration { + id: buildplateConfig anchors.bottom: parent.bottom - buildplate: "Glass" + buildplate: "Glass" // 'Glass' as a default } } \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterPill.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterPill.qml new file mode 100644 index 0000000000..cd78f1b11f --- /dev/null +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterPill.qml @@ -0,0 +1,34 @@ +// 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.4 +import UM 1.2 as UM + +/** + * A MonitorPrinterPill is a blue-colored tag indicating which printers a print + * job is compatible with. It is used by the MonitorPrintJobCard component. + */ +Item +{ + // The printer name + property alias text: printerNameLabel.text; + + implicitHeight: 18 * screenScaleFactor // TODO: Theme! + implicitWidth: printerNameLabel.contentWidth + 12 // TODO: Theme! + + Rectangle { + id: background + anchors.fill: parent + color: "#e4e4f2" // TODO: Theme! + radius: 2 * screenScaleFactor // TODO: Theme! + } + + Label { + id: printerNameLabel + anchors.centerIn: parent + color: "#535369" // TODO: Theme! + text: "" + font.pointSize: 10 + } +} \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/resources/svg/icons/external_link.svg b/plugins/UM3NetworkPrinting/resources/svg/icons/external_link.svg new file mode 100644 index 0000000000..a2130fb97b --- /dev/null +++ b/plugins/UM3NetworkPrinting/resources/svg/icons/external_link.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file From 669648d3e11a969ec75b83c5d5b763cef9705f71 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 20 Nov 2018 16:28:21 +0100 Subject: [PATCH 0271/1240] Improve the toolbar style by modifying the rectangles and the behavior to get a rounded rectangle on the right-top and right-bottom. Contributes to CURA-5962. --- resources/qml/ExtruderButton.qml | 16 ++---- resources/qml/Toolbar.qml | 36 ++++++++---- resources/themes/cura-light/styles.qml | 80 ++++++++++++++++++++------ resources/themes/cura-light/theme.json | 6 +- 4 files changed, 92 insertions(+), 46 deletions(-) diff --git a/resources/qml/ExtruderButton.qml b/resources/qml/ExtruderButton.qml index 7923521658..cdd0386d30 100644 --- a/resources/qml/ExtruderButton.qml +++ b/resources/qml/ExtruderButton.qml @@ -21,6 +21,9 @@ Button checked: Cura.ExtruderManager.selectedObjectExtruders.indexOf(extruder.id) != -1 enabled: UM.Selection.hasSelection && extruder.stack.isEnabled + property bool isFirstElement: extrudersModel.getItem(0).name == model.name + property bool isLastElement: extrudersModel.getItem(extrudersModel.rowCount() - 1).name == model.name + Item { anchors.centerIn: parent @@ -32,18 +35,7 @@ Button { anchors.centerIn: parent text: index + 1 - color: - { - if (base.checked) - { - return UM.Theme.getColor("toolbar_button_text_active") - } - else if(base.hovered) - { - return UM.Theme.getColor("toolbar_button_text_hover") - } - return UM.Theme.getColor("toolbar_button_text") - } + color: UM.Theme.getColor("toolbar_button_text") font: UM.Theme.getFont("default_bold") } } diff --git a/resources/qml/Toolbar.qml b/resources/qml/Toolbar.qml index 9955ceeba7..81896f1a75 100644 --- a/resources/qml/Toolbar.qml +++ b/resources/qml/Toolbar.qml @@ -28,12 +28,16 @@ Item // Used to create a rounded rectangle behind the toolButtons Rectangle { - anchors.fill: toolButtons - anchors.leftMargin: -radius + anchors + { + fill: toolButtons + leftMargin: -radius - border.width + rightMargin: -border.width + topMargin: -border.width + bottomMargin: -border.width + } radius: UM.Theme.getSize("default_radius").width - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("lining") - color: UM.Theme.getColor("toolbar_background") + color: UM.Theme.getColor("lining") } Column @@ -42,13 +46,13 @@ Item anchors.top: parent.top anchors.right: parent.right - spacing: UM.Theme.getSize("button_lining").width + spacing: UM.Theme.getSize("default_lining").height Repeater { id: repeat - model: UM.ToolModel { } + model: UM.ToolModel { id: toolsModel } width: childrenRect.width height: childrenRect.height Button @@ -60,6 +64,9 @@ Item enabled: model.enabled && UM.Selection.hasSelection && UM.Controller.toolsEnabled style: UM.Theme.styles.toolbar_button + property bool isFirstElement: toolsModel.getItem(0).id == model.id + property bool isLastElement: toolsModel.getItem(toolsModel.rowCount() - 1).id == model.id + onCheckedChanged: { if (checked) @@ -93,12 +100,16 @@ Item // Used to create a rounded rectangle behind the extruderButtons Rectangle { - anchors.fill: extruderButtons - anchors.leftMargin: -radius + anchors + { + fill: extruderButtons + leftMargin: -radius - border.width + rightMargin: -border.width + topMargin: -border.width + bottomMargin: -border.width + } radius: UM.Theme.getSize("default_radius").width - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("lining") - color: UM.Theme.getColor("toolbar_background") + color: UM.Theme.getColor("lining") } Column @@ -108,6 +119,7 @@ Item anchors.topMargin: UM.Theme.getSize("default_margin").height anchors.top: toolButtons.bottom anchors.right: parent.right + spacing: UM.Theme.getSize("default_lining").height Repeater { diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index 58035ae3bd..723b393efa 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -175,11 +175,68 @@ QtObject { ButtonStyle { - background: Item + background: Rectangle { - implicitWidth: Theme.getSize("button").width; - implicitHeight: Theme.getSize("button").height; + implicitWidth: Theme.getSize("button").width + implicitHeight: Theme.getSize("button").height + color: + { + if (control.checked && control.hovered) + { + return Theme.getColor("toolbar_button_active_hover") + } + else if (control.checked) + { + return Theme.getColor("toolbar_button_active") + } + else if(control.hovered) + { + return Theme.getColor("toolbar_button_hover") + } + return Theme.getColor("toolbar_background") + } + radius: UM.Theme.getSize("default_radius").width + Rectangle + { + id: topSquare + anchors + { + left: parent.left + right: parent.right + top: parent.top + } + height: parent.radius + color: control.isFirstElement ? "transparent" : parent.color + } + + Rectangle + { + id: bottomSquare + anchors + { + left: parent.left + right: parent.right + bottom: parent.bottom + } + height: parent.radius + color: control.isLastElement ? "transparent" : parent.color + } + + Rectangle + { + id: leftSquare + anchors + { + left: parent.left + top: parent.top + bottom: parent.bottom + } + width: parent.radius + color: parent.color + } + + // This is the tooltip UM.PointingRectangle { id: button_tooltip @@ -223,22 +280,7 @@ QtObject source: control.iconSource; width: Theme.getSize("button_icon").width; height: Theme.getSize("button_icon").height; - color: - { - if (control.checked && control.hovered) - { - return Theme.getColor("toolbar_button_text_active_hover"); - } - else if (control.checked) - { - return Theme.getColor("toolbar_button_text_active"); - } - else if(control.hovered) - { - return Theme.getColor("toolbar_button_text_hover"); - } - return Theme.getColor("toolbar_button_text"); - } + color: Theme.getColor("toolbar_button_text"); sourceSize: Theme.getSize("button_icon") } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index f4d50e4c79..d00e998cc8 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -123,9 +123,9 @@ "warning": [255, 190, 35, 255], "toolbar_button_text": [10, 8, 80, 255], - "toolbar_button_text_hover": [50, 130, 255, 255], - "toolbar_button_text_active": [50, 130, 255, 255], - "toolbar_button_text_active_hover": [50, 130, 255, 255], + "toolbar_button_hover": [232, 242, 252, 255], + "toolbar_button_active": [232, 242, 252, 255], + "toolbar_button_active_hover": [232, 242, 252, 255], "button": [31, 36, 39, 255], "button_hover": [68, 72, 75, 255], From 481ca8cd2f2e61ba7591695c7b318346cad4364d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Tue, 20 Nov 2018 16:33:52 +0100 Subject: [PATCH 0272/1240] Fixed some bugs and added the color_code field to the named tuple --- plugins/UM3NetworkPrinting/src/Models.py | 1 + .../UM3NetworkPrinting/src/SendMaterialJob.py | 20 +++++++++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Models.py b/plugins/UM3NetworkPrinting/src/Models.py index e8efa577f6..a9210ac5b4 100644 --- a/plugins/UM3NetworkPrinting/src/Models.py +++ b/plugins/UM3NetworkPrinting/src/Models.py @@ -23,6 +23,7 @@ LocalMaterial = namedtuple('LocalMaterial', [ 'brand', 'material', 'color_name', + 'color_code', 'description', 'adhesion_info', 'approximate_diameter', diff --git a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py index cbe79aef6a..0599101379 100644 --- a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py @@ -2,6 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. import json import os +import re import urllib.parse from typing import Dict, TYPE_CHECKING, Set @@ -19,7 +20,6 @@ from .Models import ClusterMaterial, LocalMaterial if TYPE_CHECKING: from .ClusterUM3OutputDevice import ClusterUM3OutputDevice - ## Asynchronous job to send material profiles to the printer. # # This way it won't freeze up the interface while sending those materials. @@ -50,7 +50,7 @@ class SendMaterialJob(Job): self._sendMissingMaterials(remote_materials_by_guid) except json.JSONDecodeError: Logger.logException("w", "Error parsing materials from printer") - except KeyError: + except TypeError: Logger.logException("w", "Error parsing materials from printer") ## Determine which materials should be updated and send them to the printer. @@ -75,7 +75,8 @@ class SendMaterialJob(Job): ## From the local and remote materials, determine which ones should be synchronized. # - # Makes a Set containing only the materials that are not on the printer yet or the ones that are newer in Cura. + # Makes a Set of id's containing only the id's of the materials that are not on the printer yet or the ones that + # are newer in Cura. # # \param local_materials The local materials by GUID. # \param remote_materials The remote materials by GUID. @@ -157,7 +158,7 @@ class SendMaterialJob(Job): @classmethod def _parseReply(cls, reply: QNetworkReply) -> Dict[str, ClusterMaterial]: remote_materials = json.loads(reply.readAll().data().decode("utf-8")) - return {material["id"]: ClusterMaterial(**material) for material in remote_materials} + return {material["guid"]: ClusterMaterial(**material) for material in remote_materials} ## Retrieves a list of local materials # @@ -170,12 +171,19 @@ class SendMaterialJob(Job): material_containers = container_registry.findContainersMetadata(type = "material") # Find the latest version of all material containers in the registry. - local_materials = {} # type: Dict[str, LocalMaterial] for material in material_containers: try: material = LocalMaterial(**material) + + # material version must be an int + if not re.match("\d+", material.version): + Logger.logException("w", "Local material {} has invalid version '{}'." + .format(material["id"], material.version)) + continue + if material.GUID not in result or material.version > result.get(material.GUID).version: - local_materials[material.GUID] = material + result[material.GUID] = material except ValueError: Logger.logException("w", "Local material {} has invalid values.".format(material["id"])) + return result From ca6074429290b6ae5fa1f3ccfbfa74107ed1284a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Tue, 20 Nov 2018 16:34:11 +0100 Subject: [PATCH 0273/1240] Made the tests work with the named tuples Tests only use the _onGetRemoteMaterial --- .../tests/TestSendMaterialJob.py | 313 +++++++----------- 1 file changed, 123 insertions(+), 190 deletions(-) diff --git a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py index 73bca2b0ad..f4604580fe 100644 --- a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py @@ -1,53 +1,23 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +import io import json - -from typing import Any, List -from unittest import TestCase +from unittest import TestCase, mock from unittest.mock import patch, call from PyQt5.QtCore import QByteArray -from UM.Settings.ContainerRegistry import ContainerInterface, ContainerRegistryInterface, DefinitionContainerInterface -from plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice import ClusterUM3OutputDevice +from UM.MimeTypeDatabase import MimeType +from cura.CuraApplication import CuraApplication from plugins.UM3NetworkPrinting.src.SendMaterialJob import SendMaterialJob -class ContainerRegistryMock(ContainerRegistryInterface): - - def __init__(self): - self.containersMetaData = None - - def findContainers(self, *, ignore_case: bool = False, **kwargs: Any) -> List[ContainerInterface]: - raise NotImplementedError() - - def findDefinitionContainers(self, **kwargs: Any) -> List[DefinitionContainerInterface]: - raise NotImplementedError() - - @classmethod - def getApplication(cls) -> "Application": - raise NotImplementedError() - - def getEmptyInstanceContainer(self) -> "InstanceContainer": - raise NotImplementedError() - - def isReadOnly(self, container_id: str) -> bool: - raise NotImplementedError() - - def setContainersMetadata(self, value): - self.containersMetaData = value - - def findContainersMetadata(self, type): - return self.containersMetaData - - -class MockOutputDevice(ClusterUM3OutputDevice): - def _createFormPart(self, content_header, data, content_type=None): - return "xxx" - - +@patch("builtins.open", lambda _, __: io.StringIO("")) +@patch("UM.MimeTypeDatabase.MimeTypeDatabase.getMimeTypeForFile", + lambda _: MimeType(name = "application/x-ultimaker-material-profile", comment = "Ultimaker Material Profile", + suffixes = ["xml.fdm_material"])) +@patch("UM.Resources.Resources.getAllResourcesOfType", lambda _: ["/materials/generic_pla_white.xml.fdm_material"]) class TestSendMaterialJob(TestCase): - _LOCAL_MATERIAL_WHITE = {"type": "material", "status": "unknown", "id": "generic_pla_white", "base_file": "generic_pla_white", "setting_version": 5, "name": "White PLA", "brand": "Generic", "material": "PLA", "color_name": "White", @@ -88,11 +58,11 @@ class TestSendMaterialJob(TestCase): job.run() # We expect the materials endpoint to be called when the job runs. - device_mock.get.assert_called_with("materials/", on_finished=job._onGetRemoteMaterials) + device_mock.get.assert_called_with("materials/", on_finished = job._onGetRemoteMaterials) @patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") @patch("PyQt5.QtNetwork.QNetworkReply") - def test_sendMissingMaterials_withFailedRequest(self, reply_mock, device_mock): + def test__onGetRemoteMaterials_withFailedRequest(self, reply_mock, device_mock): reply_mock.attribute.return_value = 404 job = SendMaterialJob(device_mock) job._onGetRemoteMaterials(reply_mock) @@ -103,7 +73,7 @@ class TestSendMaterialJob(TestCase): @patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") @patch("PyQt5.QtNetwork.QNetworkReply") - def test_sendMissingMaterials_withBadJsonAnswer(self, reply_mock, device_mock): + def test__onGetRemoteMaterials_withBadJsonAnswer(self, reply_mock, device_mock): reply_mock.attribute.return_value = 200 reply_mock.readAll.return_value = QByteArray(b"Six sick hicks nick six slick bricks with picks and sticks.") job = SendMaterialJob(device_mock) @@ -116,7 +86,7 @@ class TestSendMaterialJob(TestCase): @patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") @patch("PyQt5.QtNetwork.QNetworkReply") - def test_sendMissingMaterials_withMissingGuid(self, reply_mock, device_mock): + def test__onGetRemoteMaterials_withMissingGuidInRemoteMaterial(self, reply_mock, device_mock): reply_mock.attribute.return_value = 200 remote_material_without_guid = self._REMOTE_MATERIAL_WHITE.copy() del remote_material_without_guid["guid"] @@ -127,151 +97,114 @@ class TestSendMaterialJob(TestCase): # We expect the reply to be called once to try to get the printers from the list (readAll()). # Given that parsing fails we do not expect the device to be called for any follow up. self.assertEqual([call.attribute(0), call.readAll()], reply_mock.method_calls) - self.assertEqual(1, device_mock.createFormPart.call_count) + self.assertEqual(0, device_mock.createFormPart.call_count) - # @patch("UM.Resources.Resources.getAllResourcesOfType", lambda _: []) - # @patch("PyQt5.QtNetwork.QNetworkReply") - # def test_sendMissingMaterials_WithInvalidVersionInLocalMaterial(self, reply_mock): - # reply_mock.attribute.return_value = 200 - # reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTEMATERIAL_WHITE]).encode("ascii")) - # - # containerRegistry = ContainerRegistryMock() - # localMaterialWhiteWithInvalidVersion = self._LOCALMATERIAL_WHITE.copy() - # localMaterialWhiteWithInvalidVersion["version"] = "one" - # containerRegistry.setContainersMetadata([localMaterialWhiteWithInvalidVersion]) - # - # with mock.patch.object(Logger, "log", new=new_log): - # with mock.patch.object(ContainerRegistry, "getInstance", lambda: containerRegistry): - # SendMaterialJob(None).sendMissingMaterials(reply_mock) - # - # reply_mock.attribute.assert_called_with(0) - # self.assertEqual(reply_mock.method_calls, [call.attribute(0), call.readAll()]) - # self._assertLogEntries([("e", "Material generic_pla_white has invalid version number one.")], _logentries) - # - # @patch("UM.Resources.Resources.getAllResourcesOfType", lambda _: []) - # @patch("PyQt5.QtNetwork.QNetworkReply") - # def test_sendMissingMaterials_WithMultipleLocalVersionsLowFirst(self, reply_mock): - # reply_mock.attribute.return_value = 200 - # reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTEMATERIAL_WHITE]).encode("ascii")) - # - # containerRegistry = ContainerRegistryMock() - # localMaterialWhiteWithHigherVersion = self._LOCALMATERIAL_WHITE.copy() - # localMaterialWhiteWithHigherVersion["version"] = "2" - # containerRegistry.setContainersMetadata([self._LOCALMATERIAL_WHITE, localMaterialWhiteWithHigherVersion]) - # - # with mock.patch.object(Logger, "log", new=new_log): - # with mock.patch.object(ContainerRegistry, "getInstance", lambda: containerRegistry): - # SendMaterialJob(None).sendMissingMaterials(reply_mock) - # - # reply_mock.attribute.assert_called_with(0) - # self.assertEqual(reply_mock.method_calls, [call.attribute(0), call.readAll()]) - # self._assertLogEntries([], _logentries) - # - # @patch("UM.Resources.Resources.getAllResourcesOfType", lambda _: []) - # @patch("PyQt5.QtNetwork.QNetworkReply") - # def test_sendMissingMaterials_MaterialMissingOnPrinter(self, reply_mock): - # reply_mock.attribute.return_value = 200 - # reply_mock.readAll.return_value = QByteArray( - # json.dumps([self._REMOTEMATERIAL_WHITE]).encode("ascii")) - # - # containerRegistry = ContainerRegistryMock() - # containerRegistry.setContainersMetadata([self._LOCALMATERIAL_WHITE, self._LOCALMATERIAL_BLACK]) - # - # with mock.patch.object(Logger, "log", new=new_log): - # with mock.patch.object(ContainerRegistry, "getInstance", lambda: containerRegistry): - # SendMaterialJob(None).sendMissingMaterials(reply_mock) - # - # reply_mock.attribute.assert_called_with(0) - # self.assertEqual(reply_mock.method_calls, [call.attribute(0), call.readAll()]) - # self._assertLogEntries([], _logentries) - # - # @patch("builtins.open", lambda a, b: io.StringIO("")) - # @patch("UM.MimeTypeDatabase.MimeTypeDatabase.getMimeTypeForFile", - # lambda _: MimeType(name="application/x-ultimaker-material-profile", comment="Ultimaker Material Profile", - # suffixes=["xml.fdm_material"])) - # @patch("UM.Resources.Resources.getAllResourcesOfType", lambda _: ["/materials/generic_pla_white.xml.fdm_material"]) - # @patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") - # def test_sendMaterialsToPrinter(self, device_mock): - # device_mock._createFormPart.return_value = "_xXx_" - # with mock.patch.object(Logger, "log", new=new_log): - # job = SendMaterialJob(device_mock) - # job.sendMaterialsToPrinter({"generic_pla_white"}) - # - # self._assertLogEntries([("d", "Syncing material generic_pla_white with cluster.")], _logentries) - # self.assertEqual([call._createFormPart("name="file"; filename="generic_pla_white.xml.fdm_material"", ""), - # call.postFormWithParts(on_finished=job.sendingFinished, parts = ["_xXx_"], target = "materials/")], device_mock.method_calls) - # - # @patch("PyQt5.QtNetwork.QNetworkReply") - # def test_sendingFinished_success(self, reply_mock) -> None: - # reply_mock.attribute.return_value = 200 - # with mock.patch.object(Logger, "log", new=new_log): - # SendMaterialJob(None).sendingFinished(reply_mock) - # - # reply_mock.attribute.assert_called_once_with(0) - # self.assertEqual(0, len(_logentries)) - # - # @patch("PyQt5.QtNetwork.QNetworkReply") - # def test_sendingFinished_failed(self, reply_mock) -> None: - # reply_mock.attribute.return_value = 404 - # reply_mock.readAll.return_value = QByteArray(b"Six sick hicks nick six slick bricks with picks and sticks.") - # - # with mock.patch.object(Logger, "log", new=new_log): - # SendMaterialJob(None).sendingFinished(reply_mock) - # - # reply_mock.attribute.assert_called_with(0) - # self.assertEqual(reply_mock.method_calls, [call.attribute(0), call.attribute(0), call.readAll()]) - # - # self._assertLogEntries([ - # ("e", "Received error code from printer when syncing material: 404"), - # ("e", "Six sick hicks nick six slick bricks with picks and sticks.") - # ], _logentries) - # - # @patch("PyQt5.QtNetwork.QNetworkReply") - # def test_parseReply(self, reply_mock): - # reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTEMATERIAL_WHITE]).encode("ascii")) - # - # response = SendMaterialJob._parseReply(reply_mock) - # - # self.assertTrue(len(response) == 1) - # self.assertEqual(next(iter(response.values())), ClusterMaterial(**self._REMOTEMATERIAL_WHITE)) - # - # @patch("PyQt5.QtNetwork.QNetworkReply") - # def test_parseReplyWithInvalidMaterial(self, reply_mock): - # remoteMaterialWithInvalidVersion = self._REMOTEMATERIAL_WHITE.copy() - # remoteMaterialWithInvalidVersion["version"] = "one" - # reply_mock.readAll.return_value = QByteArray(json.dumps([remoteMaterialWithInvalidVersion]).encode("ascii")) - # - # with self.assertRaises(ValueError): - # SendMaterialJob._parseReply(reply_mock) - # - # def test__getLocalMaterials(self): - # containerRegistry = ContainerRegistryMock() - # containerRegistry.setContainersMetadata([self._LOCALMATERIAL_WHITE, self._LOCALMATERIAL_BLACK]) - # - # with mock.patch.object(Logger, "log", new=new_log): - # with mock.patch.object(ContainerRegistry, "getInstance", lambda: containerRegistry): - # local_materials = SendMaterialJob(None)._getLocalMaterials() - # - # self.assertTrue(len(local_materials) == 2) - # - # def test__getLocalMaterialsWithMultipleVersions(self): - # containerRegistry = ContainerRegistryMock() - # localMaterialWithNewerVersion = self._LOCALMATERIAL_WHITE.copy() - # localMaterialWithNewerVersion["version"] = 2 - # containerRegistry.setContainersMetadata([self._LOCALMATERIAL_WHITE, localMaterialWithNewerVersion]) - # - # with mock.patch.object(Logger, "log", new=new_log): - # with mock.patch.object(ContainerRegistry, "getInstance", lambda: containerRegistry): - # local_materials = SendMaterialJob(None)._getLocalMaterials() - # - # self.assertTrue(len(local_materials) == 1) - # self.assertTrue(list(local_materials.values())[0].version == 2) - # - # containerRegistry.setContainersMetadata([localMaterialWithNewerVersion, self._LOCALMATERIAL_WHITE]) - # - # with mock.patch.object(Logger, "log", new=new_log): - # with mock.patch.object(ContainerRegistry, "getInstance", lambda: containerRegistry): - # local_materials = SendMaterialJob(None)._getLocalMaterials() - # - # self.assertTrue(len(local_materials) == 1) - # self.assertTrue(list(local_materials.values())[0].version == 2) + @patch("cura.Settings.CuraContainerRegistry") + @patch("cura.CuraApplication") + @patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") + @patch("PyQt5.QtNetwork.QNetworkReply") + def test__onGetRemoteMaterials_withInvalidVersionInLocalMaterial(self, reply_mock, device_mock, application_mock, + container_registry_mock): + reply_mock.attribute.return_value = 200 + reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii")) + + localMaterialWhiteWithInvalidVersion = self._LOCAL_MATERIAL_WHITE.copy() + localMaterialWhiteWithInvalidVersion["version"] = "one" + container_registry_mock.findContainersMetadata.return_value = [localMaterialWhiteWithInvalidVersion] + + application_mock.getContainerRegistry.return_value = container_registry_mock + + with mock.patch.object(CuraApplication, "getInstance", new = lambda: application_mock): + job = SendMaterialJob(device_mock) + job._onGetRemoteMaterials(reply_mock) + + self.assertEqual([call.attribute(0), call.readAll()], reply_mock.method_calls) + self.assertEqual([call.getContainerRegistry()], application_mock.method_calls) + self.assertEqual([call.findContainersMetadata(type = "material")], container_registry_mock.method_calls) + self.assertEqual(0, device_mock.createFormPart.call_count) + + @patch("cura.Settings.CuraContainerRegistry") + @patch("cura.CuraApplication") + @patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") + @patch("PyQt5.QtNetwork.QNetworkReply") + def test__onGetRemoteMaterials_withNoUpdate(self, reply_mock, device_mock, application_mock, + container_registry_mock): + application_mock.getContainerRegistry.return_value = container_registry_mock + + device_mock.createFormPart.return_value = "_xXx_" + + container_registry_mock.findContainersMetadata.return_value = [self._LOCAL_MATERIAL_WHITE] + + reply_mock.attribute.return_value = 200 + reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii")) + + with mock.patch.object(CuraApplication, "getInstance", new = lambda: application_mock): + job = SendMaterialJob(device_mock) + job._onGetRemoteMaterials(reply_mock) + + self.assertEqual([call.attribute(0), call.readAll()], reply_mock.method_calls) + self.assertEqual([call.getContainerRegistry()], application_mock.method_calls) + self.assertEqual([call.findContainersMetadata(type = "material")], container_registry_mock.method_calls) + self.assertEqual(0, device_mock.createFormPart.call_count) + self.assertEqual(0, device_mock.postFormWithParts.call_count) + + @patch("cura.Settings.CuraContainerRegistry") + @patch("cura.CuraApplication") + @patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") + @patch("PyQt5.QtNetwork.QNetworkReply") + def test__onGetRemoteMaterials_withUpdatedMaterial(self, reply_mock, device_mock, application_mock, + container_registry_mock): + application_mock.getContainerRegistry.return_value = container_registry_mock + + device_mock.createFormPart.return_value = "_xXx_" + + localMaterialWhiteWithHigherVersion = self._LOCAL_MATERIAL_WHITE.copy() + localMaterialWhiteWithHigherVersion["version"] = "2" + container_registry_mock.findContainersMetadata.return_value = [localMaterialWhiteWithHigherVersion] + + reply_mock.attribute.return_value = 200 + reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii")) + + with mock.patch.object(CuraApplication, "getInstance", new = lambda: application_mock): + job = SendMaterialJob(device_mock) + job._onGetRemoteMaterials(reply_mock) + + self.assertEqual([call.attribute(0), call.readAll()], reply_mock.method_calls) + self.assertEqual([call.getContainerRegistry()], application_mock.method_calls) + self.assertEqual([call.findContainersMetadata(type = "material")], container_registry_mock.method_calls) + self.assertEqual(1, device_mock.createFormPart.call_count) + self.assertEqual(1, device_mock.postFormWithParts.call_count) + self.assertEquals( + [call.createFormPart("name=\"file\"; filename=\"generic_pla_white.xml.fdm_material\"", ""), + call.postFormWithParts(target = "materials/", parts = ["_xXx_"], on_finished = job.sendingFinished)], + device_mock.method_calls) + + @patch("cura.Settings.CuraContainerRegistry") + @patch("cura.CuraApplication") + @patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") + @patch("PyQt5.QtNetwork.QNetworkReply") + def test__onGetRemoteMaterials_withNewMaterial(self, reply_mock, device_mock, application_mock, + container_registry_mock): + application_mock.getContainerRegistry.return_value = container_registry_mock + + device_mock.createFormPart.return_value = "_xXx_" + + container_registry_mock.findContainersMetadata.return_value = [self._LOCAL_MATERIAL_WHITE, + self._LOCAL_MATERIAL_BLACK] + + reply_mock.attribute.return_value = 200 + reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_BLACK]).encode("ascii")) + + with mock.patch.object(CuraApplication, "getInstance", new = lambda: application_mock): + job = SendMaterialJob(device_mock) + job._onGetRemoteMaterials(reply_mock) + + self.assertEqual([call.attribute(0), call.readAll()], reply_mock.method_calls) + self.assertEqual([call.getContainerRegistry()], application_mock.method_calls) + self.assertEqual([call.findContainersMetadata(type = "material")], container_registry_mock.method_calls) + self.assertEqual(1, device_mock.createFormPart.call_count) + self.assertEqual(1, device_mock.postFormWithParts.call_count) + self.assertEquals( + [call.createFormPart("name=\"file\"; filename=\"generic_pla_white.xml.fdm_material\"", ""), + call.postFormWithParts(target = "materials/", parts = ["_xXx_"], on_finished = job.sendingFinished)], + device_mock.method_calls) From f3338aa187bfe607664d0d79d5848d7cdee5fc06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Tue, 20 Nov 2018 16:53:01 +0100 Subject: [PATCH 0274/1240] Fixed the failing tests --- plugins/UM3NetworkPrinting/src/SendMaterialJob.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py index 0599101379..fe0cd98964 100644 --- a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py @@ -84,8 +84,9 @@ class SendMaterialJob(Job): def _determineMaterialsToSend(local_materials: Dict[str, LocalMaterial], remote_materials: Dict[str, ClusterMaterial]) -> Set[str]: return { - material.id for guid, material in local_materials.items() - if guid not in remote_materials or material.version > remote_materials[guid].version + material.id + for guid, material in local_materials.items() + if guid not in remote_materials or int(material.version) > remote_materials[guid].version } ## Send the materials to the printer. From 4e8979334e0d794b2cd8cae72ce9c351890577c8 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 20 Nov 2018 19:18:48 +0100 Subject: [PATCH 0275/1240] Switch Ultimaker Account OAuth2 client The new client has access to more permissions and is labeled as "Ultimaker Cura" instead of "Cura Backups Plugin". Also removes `self._cloud_api_root` as that's not used anymore now that `self._oauth_root` is used for the redirect URLs. --- cura/API/Account.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cura/API/Account.py b/cura/API/Account.py index 397e220478..64d63c7025 100644 --- a/cura/API/Account.py +++ b/cura/API/Account.py @@ -38,13 +38,12 @@ class Account(QObject): self._callback_port = 32118 self._oauth_root = "https://account.ultimaker.com" - self._cloud_api_root = "https://api.ultimaker.com" self._oauth_settings = OAuth2Settings( OAUTH_SERVER_URL= self._oauth_root, CALLBACK_PORT=self._callback_port, CALLBACK_URL="http://localhost:{}/callback".format(self._callback_port), - CLIENT_ID="um---------------ultimaker_cura_drive_plugin", + CLIENT_ID="um----------------------------ultimaker_cura", CLIENT_SCOPES="account.user.read drive.backup.read drive.backup.write packages.download packages.rating.read packages.rating.write", AUTH_DATA_PREFERENCE_KEY="general/ultimaker_auth_data", AUTH_SUCCESS_REDIRECT="{}/app/auth-success".format(self._oauth_root), From c7bb6931f48604aebcefaaa8ed8007edc4f49f9f Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 20 Nov 2018 23:44:28 +0100 Subject: [PATCH 0276/1240] Refactor networked output device All networking related stuff is moved to a separate class called NetworkClient for reusability. As example it is now also used in the WIP CloudOutputDeviceManager to clean up network calling there. --- cura/NetworkClient.py | 220 ++++++++++++++++++ .../NetworkedPrinterOutputDevice.py | 189 ++------------- .../src/Cloud/CloudOutputDeviceManager.py | 43 ++-- 3 files changed, 260 insertions(+), 192 deletions(-) create mode 100644 cura/NetworkClient.py diff --git a/cura/NetworkClient.py b/cura/NetworkClient.py new file mode 100644 index 0000000000..ce9cc50cf6 --- /dev/null +++ b/cura/NetworkClient.py @@ -0,0 +1,220 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from time import time +from typing import Optional, Dict, Callable, List + +from PyQt5.QtCore import QUrl +from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply, QHttpMultiPart, QNetworkRequest, QHttpPart, \ + QAuthenticator + +from UM.Logger import Logger +from cura.CuraApplication import CuraApplication + + +## Abstraction of QNetworkAccessManager for easier networking in Cura. +# This was originally part of NetworkedPrinterOutputDevice but was moved out for re-use in other classes. +class NetworkClient: + + def __init__(self, application: CuraApplication = None): + + # Use the given application instance or get the singleton instance. + self._application = application or CuraApplication.getInstance() + + # Network manager instance to use for this client. + self._manager = None # type: Optional[QNetworkAccessManager] + + # Timings. + self._last_manager_create_time = None # type: Optional[float] + self._last_response_time = None # type: Optional[float] + self._last_request_time = None # type: Optional[float] + + # The user agent of Cura. + self._user_agent = "%s/%s " % (self._application.getApplicationName(), self._application.getVersion()) + + # Uses to store callback methods for finished network requests. + # This allows us to register network calls with a callback directly instead of having to dissect the reply. + self._on_finished_callbacks = {} # type: Dict[str, Callable[[QNetworkReply], None]] + + # QHttpMultiPart objects need to be kept alive and not garbage collected during the + # HTTP which uses them. We hold references to these QHttpMultiPart objects here. + self._kept_alive_multiparts = {} # type: Dict[QNetworkReply, QHttpMultiPart] + + ## Creates a network manager with all the required properties and event bindings. + def _createNetworkManager(self) -> None: + if self._manager: + self._manager.finished.disconnect(self.__handleOnFinished) + self._manager.authenticationRequired.disconnect(self._onAuthenticationRequired) + self._manager = QNetworkAccessManager() + self._manager.finished.connect(self.__handleOnFinished) + self._last_manager_create_time = time() + self._manager.authenticationRequired.connect(self._onAuthenticationRequired) + + ## Create a new empty network request. + # Automatically adds the required HTTP headers. + def _createEmptyRequest(self, url: str, content_type: Optional[str] = "application/json") -> QNetworkRequest: + request = QNetworkRequest(QUrl(url)) + if content_type: + request.setHeader(QNetworkRequest.ContentTypeHeader, content_type) + request.setHeader(QNetworkRequest.UserAgentHeader, self._user_agent) + return request + + ## Executes the correct callback method when a network request finishes. + def __handleOnFinished(self, reply: QNetworkReply) -> None: + + # Due to garbage collection, we need to cache certain bits of post operations. + # As we don't want to keep them around forever, delete them if we get a reply. + if reply.operation() == QNetworkAccessManager.PostOperation: + self._clearCachedMultiPart(reply) + + # No status code means it never even reached remote. + if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) is None: + return + + # Not used by this class itself, but children might need it for better network handling. + # An example of this is the _update method in the NetworkedPrinterOutputDevice. + self._last_response_time = time() + + # Find the right callback and execute it. + # It always takes the full reply as single parameter. + callback_key = reply.url().toString() + str(reply.operation()) + if callback_key in self._on_finished_callbacks: + self._on_finished_callbacks[callback_key](reply) + + ## Removes all cached Multi-Part items. + def _clearCachedMultiPart(self, reply: QNetworkReply) -> None: + if reply in self._kept_alive_multiparts: + del self._kept_alive_multiparts[reply] + + ## Makes sure the network manager is created. + def _validateManager(self) -> None: + if self._manager is None: + self._createNetworkManager() + assert (self._manager is not None) + + ## Callback for when the network manager detects that authentication is required but was not given. + @staticmethod + def _onAuthenticationRequired(reply: QNetworkReply, authenticator: QAuthenticator) -> None: + Logger.log("w", "Request to {} required authentication but was not given".format(reply.url().toString())) + + ## Register a method to be executed when the associated network request finishes. + def _registerOnFinishedCallback(self, reply: QNetworkReply, + on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: + if on_finished is not None: + self._on_finished_callbacks[reply.url().toString() + str(reply.operation())] = on_finished + + ## Add a part to a Multi-Part form. + @staticmethod + def _createFormPart(content_header: str, data: bytes, content_type: Optional[str] = None) -> QHttpPart: + part = QHttpPart() + + if not content_header.startswith("form-data;"): + content_header = "form_data; " + content_header + + part.setHeader(QNetworkRequest.ContentDispositionHeader, content_header) + + if content_type is not None: + part.setHeader(QNetworkRequest.ContentTypeHeader, content_type) + + part.setBody(data) + return part + + ## Public version of _createFormPart. Both are needed for backward compatibility with 3rd party plugins. + def createFormPart(self, content_header: str, data: bytes, content_type: Optional[str] = None) -> QHttpPart: + return self._createFormPart(content_header, data, content_type) + + ## Does a PUT request to the given URL. + def put(self, url: str, data: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: + self._validateManager() + + request = self._createEmptyRequest(url) + self._last_request_time = time() + + if not self._manager: + Logger.log("e", "No network manager was created to execute the PUT call with.") + return + + reply = self._manager.put(request, data.encode()) + self._registerOnFinishedCallback(reply, on_finished) + + ## Does a DELETE request to the given URL. + def delete(self, url: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: + self._validateManager() + + request = self._createEmptyRequest(url) + self._last_request_time = time() + + if not self._manager: + Logger.log("e", "No network manager was created to execute the DELETE call with.") + return + + reply = self._manager.deleteResource(request) + self._registerOnFinishedCallback(reply, on_finished) + + ## Does a GET request to the given URL. + def get(self, url: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: + self._validateManager() + + request = self._createEmptyRequest(url) + self._last_request_time = time() + + if not self._manager: + Logger.log("e", "No network manager was created to execute the GET call with.") + return + + reply = self._manager.get(request) + self._registerOnFinishedCallback(reply, on_finished) + + ## Does a POST request to the given URL. + def post(self, url: str, data: str, on_finished: Optional[Callable[[QNetworkReply], None]], + on_progress: Callable = None) -> None: + self._validateManager() + + request = self._createEmptyRequest(url) + self._last_request_time = time() + + if not self._manager: + Logger.log("e", "No network manager was created to execute the GET call with.") + return + + reply = self._manager.post(request, data.encode()) + + if on_progress is not None: + reply.uploadProgress.connect(on_progress) + + self._registerOnFinishedCallback(reply, on_finished) + + ## Does a POST request with form data to the given URL. + def postForm(self, url: str, header_data: str, body_data: bytes, + on_finished: Optional[Callable[[QNetworkReply], None]], + on_progress: Callable = None) -> None: + post_part = QHttpPart() + post_part.setHeader(QNetworkRequest.ContentDispositionHeader, header_data) + post_part.setBody(body_data) + self.postFormWithParts(url, [post_part], on_finished, on_progress) + + ## Does a POST request with form parts to the given URL. + def postFormWithParts(self, target: str, parts: List[QHttpPart], + on_finished: Optional[Callable[[QNetworkReply], None]], + on_progress: Callable = None) -> None: + self._validateManager() + + request = self._createEmptyRequest(target, content_type = None) + multi_post_part = QHttpMultiPart(QHttpMultiPart.FormDataType) + + for part in parts: + multi_post_part.append(part) + + self._last_request_time = time() + + if not self._manager: + Logger.log("e", "No network manager was created to execute the POST call with.") + return + + reply = self._manager.post(request, multi_post_part) + + self._kept_alive_multiparts[reply] = multi_post_part + + if on_progress is not None: + reply.uploadProgress.connect(on_progress) + + self._registerOnFinishedCallback(reply, on_finished) diff --git a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py index 9a6892ce4d..b5bb1a5452 100644 --- a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py +++ b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py @@ -5,6 +5,7 @@ from UM.FileHandler.FileHandler import FileHandler #For typing. from UM.Logger import Logger from UM.Scene.SceneNode import SceneNode #For typing. from cura.CuraApplication import CuraApplication +from cura.NetworkClient import NetworkClient from cura.PrinterOutputDevice import PrinterOutputDevice, ConnectionState @@ -26,35 +27,29 @@ class AuthState(IntEnum): AuthenticationReceived = 5 -class NetworkedPrinterOutputDevice(PrinterOutputDevice): +class NetworkedPrinterOutputDevice(PrinterOutputDevice, NetworkClient): authenticationStateChanged = pyqtSignal() def __init__(self, device_id, address: str, properties: Dict[bytes, bytes], parent: QObject = None) -> None: - super().__init__(device_id = device_id, parent = parent) - self._manager = None # type: Optional[QNetworkAccessManager] - self._last_manager_create_time = None # type: Optional[float] - self._recreate_network_manager_time = 30 - self._timeout_time = 10 # After how many seconds of no response should a timeout occur? - - self._last_response_time = None # type: Optional[float] - self._last_request_time = None # type: Optional[float] - + PrinterOutputDevice.__init__(self, device_id = device_id, parent = parent) + NetworkClient.__init__(self) + self._api_prefix = "" self._address = address self._properties = properties - self._user_agent = "%s/%s " % (CuraApplication.getInstance().getApplicationName(), CuraApplication.getInstance().getVersion()) - - self._onFinishedCallbacks = {} # type: Dict[str, Callable[[QNetworkReply], None]] self._authentication_state = AuthState.NotAuthenticated - - # QHttpMultiPart objects need to be kept alive and not garbage collected during the - # HTTP which uses them. We hold references to these QHttpMultiPart objects here. - self._kept_alive_multiparts = {} # type: Dict[QNetworkReply, QHttpMultiPart] - self._sending_gcode = False self._compressing_gcode = False self._gcode = [] # type: List[str] + self._connection_state_before_timeout = None # type: Optional[ConnectionState] + self._timeout_time = 10 # After how many seconds of no response should a timeout occur? + self._recreate_network_manager_time = 30 + + ## Override creating empty request to compile the full URL. + # Needed to keep NetworkedPrinterOutputDevice backwards compatible after refactoring NetworkClient out of it. + def _createEmptyRequest(self, target: str, content_type: Optional[str] = "application/json") -> QNetworkRequest: + return super()._createEmptyRequest("http://" + self._address + self._api_prefix + target, content_type) def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False, file_handler: Optional[FileHandler] = None, **kwargs: str) -> None: raise NotImplementedError("requestWrite needs to be implemented") @@ -140,30 +135,6 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): self.setConnectionState(self._connection_state_before_timeout) self._connection_state_before_timeout = None - def _createEmptyRequest(self, target: str, content_type: Optional[str] = "application/json") -> QNetworkRequest: - url = QUrl("http://" + self._address + self._api_prefix + target) - request = QNetworkRequest(url) - if content_type is not None: - request.setHeader(QNetworkRequest.ContentTypeHeader, "application/json") - request.setHeader(QNetworkRequest.UserAgentHeader, self._user_agent) - return request - - def createFormPart(self, content_header: str, data: bytes, content_type: Optional[str] = None) -> QHttpPart: - return self._createFormPart(content_header, data, content_type) - - def _createFormPart(self, content_header: str, data: bytes, content_type: Optional[str] = None) -> QHttpPart: - part = QHttpPart() - - if not content_header.startswith("form-data;"): - content_header = "form_data; " + content_header - part.setHeader(QNetworkRequest.ContentDispositionHeader, content_header) - - if content_type is not None: - part.setHeader(QNetworkRequest.ContentTypeHeader, content_type) - - part.setBody(data) - return part - ## Convenience function to get the username from the OS. # The code was copied from the getpass module, as we try to use as little dependencies as possible. def _getUserName(self) -> str: @@ -173,130 +144,7 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): return user return "Unknown User" # Couldn't find out username. - def _clearCachedMultiPart(self, reply: QNetworkReply) -> None: - if reply in self._kept_alive_multiparts: - del self._kept_alive_multiparts[reply] - - def _validateManager(self) -> None: - if self._manager is None: - self._createNetworkManager() - assert (self._manager is not None) - - def put(self, target: str, data: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: - self._validateManager() - request = self._createEmptyRequest(target) - self._last_request_time = time() - if self._manager is not None: - reply = self._manager.put(request, data.encode()) - self._registerOnFinishedCallback(reply, on_finished) - else: - Logger.log("e", "Could not find manager.") - - def delete(self, target: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: - self._validateManager() - request = self._createEmptyRequest(target) - self._last_request_time = time() - if self._manager is not None: - reply = self._manager.deleteResource(request) - self._registerOnFinishedCallback(reply, on_finished) - else: - Logger.log("e", "Could not find manager.") - - def get(self, target: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: - self._validateManager() - request = self._createEmptyRequest(target) - self._last_request_time = time() - if self._manager is not None: - reply = self._manager.get(request) - self._registerOnFinishedCallback(reply, on_finished) - else: - Logger.log("e", "Could not find manager.") - - def post(self, target: str, data: str, on_finished: Optional[Callable[[QNetworkReply], None]], on_progress: Callable = None) -> None: - self._validateManager() - request = self._createEmptyRequest(target) - self._last_request_time = time() - if self._manager is not None: - reply = self._manager.post(request, data.encode()) - if on_progress is not None: - reply.uploadProgress.connect(on_progress) - self._registerOnFinishedCallback(reply, on_finished) - else: - Logger.log("e", "Could not find manager.") - - def postFormWithParts(self, target: str, parts: List[QHttpPart], on_finished: Optional[Callable[[QNetworkReply], None]], on_progress: Callable = None) -> QNetworkReply: - self._validateManager() - request = self._createEmptyRequest(target, content_type=None) - multi_post_part = QHttpMultiPart(QHttpMultiPart.FormDataType) - for part in parts: - multi_post_part.append(part) - - self._last_request_time = time() - - if self._manager is not None: - reply = self._manager.post(request, multi_post_part) - - self._kept_alive_multiparts[reply] = multi_post_part - - if on_progress is not None: - reply.uploadProgress.connect(on_progress) - self._registerOnFinishedCallback(reply, on_finished) - - return reply - else: - Logger.log("e", "Could not find manager.") - - def postForm(self, target: str, header_data: str, body_data: bytes, on_finished: Optional[Callable[[QNetworkReply], None]], on_progress: Callable = None) -> None: - post_part = QHttpPart() - post_part.setHeader(QNetworkRequest.ContentDispositionHeader, header_data) - post_part.setBody(body_data) - - self.postFormWithParts(target, [post_part], on_finished, on_progress) - - def _onAuthenticationRequired(self, reply: QNetworkReply, authenticator: QAuthenticator) -> None: - Logger.log("w", "Request to {url} required authentication, which was not implemented".format(url = reply.url().toString())) - - def _createNetworkManager(self) -> None: - Logger.log("d", "Creating network manager") - if self._manager: - self._manager.finished.disconnect(self.__handleOnFinished) - self._manager.authenticationRequired.disconnect(self._onAuthenticationRequired) - - self._manager = QNetworkAccessManager() - self._manager.finished.connect(self.__handleOnFinished) - self._last_manager_create_time = time() - self._manager.authenticationRequired.connect(self._onAuthenticationRequired) - - if self._properties.get(b"temporary", b"false") != b"true": - CuraApplication.getInstance().getMachineManager().checkCorrectGroupName(self.getId(), self.name) - - def _registerOnFinishedCallback(self, reply: QNetworkReply, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: - if on_finished is not None: - self._onFinishedCallbacks[reply.url().toString() + str(reply.operation())] = on_finished - - def __handleOnFinished(self, reply: QNetworkReply) -> None: - # Due to garbage collection, we need to cache certain bits of post operations. - # As we don't want to keep them around forever, delete them if we get a reply. - if reply.operation() == QNetworkAccessManager.PostOperation: - self._clearCachedMultiPart(reply) - - if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) is None: - # No status code means it never even reached remote. - return - - self._last_response_time = time() - - if self._connection_state == ConnectionState.connecting: - self.setConnectionState(ConnectionState.connected) - - callback_key = reply.url().toString() + str(reply.operation()) - try: - if callback_key in self._onFinishedCallbacks: - self._onFinishedCallbacks[callback_key](reply) - except Exception: - Logger.logException("w", "something went wrong with callback") - - @pyqtSlot(str, result=str) + @pyqtSlot(str, result = str) def getProperty(self, key: str) -> str: bytes_key = key.encode("utf-8") if bytes_key in self._properties: @@ -332,7 +180,14 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): def printerType(self) -> str: return self._properties.get(b"printer_type", b"Unknown").decode("utf-8") - ## IP adress of this printer + ## IP address of this printer @pyqtProperty(str, constant = True) def ipAddress(self) -> str: return self._address + + def __handleOnFinished(self, reply: QNetworkReply) -> None: + super().__handleOnFinished(reply) + + # Since we got a reply from the network manager we can now be sure we are actually connected. + if self._connection_state == ConnectionState.connecting: + self.setConnectionState(ConnectionState.connected) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index e88ee4dced..08e43152ae 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -3,10 +3,10 @@ import json from typing import TYPE_CHECKING, Dict, Optional -from PyQt5.QtCore import QUrl -from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply +from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply from UM.Logger import Logger +from cura.NetworkClient import NetworkClient from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputDevice import CloudOutputDevice @@ -21,20 +21,17 @@ if TYPE_CHECKING: # # TODO: figure out how to pair remote clusters, local networked clusters and local cura printer presets. # TODO: for now we just have multiple output devices if the cluster is available both locally and remote. -class CloudOutputDeviceManager: +class CloudOutputDeviceManager(NetworkClient): # The cloud URL to use for remote clusters. API_ROOT_PATH = "https://api.ultimaker.com/connect/v1" def __init__(self, application: "CuraApplication"): - self._application = application + super().__init__(application) + self._output_device_manager = application.getOutputDeviceManager() self._account = application.getCuraAPI().account - # Network manager for getting the cluster list. - self._network_manager = QNetworkAccessManager() - self._network_manager.finished.connect(self._onNetworkRequestFinished) - # Persistent dict containing the remote clusters for the authenticated user. self._remote_clusters = {} # type: Dict[str, CloudOutputDevice] @@ -44,27 +41,24 @@ class CloudOutputDeviceManager: # Fetch all remote clusters for the authenticated user. # TODO: update remote clusters periodically self._account.loginStateChanged.connect(self._getRemoteClusters) + + ## Override _createEmptyRequest to add the needed authentication header for talking to the Ultimaker Cloud API. + def _createEmptyRequest(self, path: str, content_type: Optional[str] = "application/json") -> QNetworkRequest: + request = super()._createEmptyRequest(self.API_ROOT_PATH + path, content_type = content_type) + if self._account.isLoggedIn: + # TODO: add correct scopes to OAuth2 client to use remote connect API. + # TODO: don't create the client when not signed in? + request.setRawHeader(b"Authorization", "Bearer {}".format(self._account.accessToken).encode()) + return request ## Gets all remote clusters from the API. - def _getRemoteClusters(self): - url = QUrl("{}/clusters".format(self.API_ROOT_PATH)) - request = QNetworkRequest(url) - request.setHeader(QNetworkRequest.ContentTypeHeader, "application/json") - - if not self._account.isLoggedIn: - # TODO: show message to user to sign in - Logger.log("w", "User is not signed in, cannot get remote print clusters") - return - - request.setRawHeader(b"Authorization", "Bearer {}".format(self._account.accessToken).encode()) - self._network_manager.get(request) + def _getRemoteClusters(self) -> None: + self.get("/clusters", on_finished = self._onGetRemoteClustersFinished) - ## Callback for network requests. - def _onNetworkRequestFinished(self, reply: QNetworkReply): - # TODO: right now we assume that each reply is from /clusters, we should fix this + ## Callback for when the request for getting the clusters. is finished. + def _onGetRemoteClustersFinished(self, reply: QNetworkReply) -> None: status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) if status_code != 200: - # TODO: add correct scopes to OAuth2 client to use remote connect API. Logger.log("w", "Got unexpected response while trying to get cloud cluster data: {}, {}" .format(status_code, reply.readAll())) return @@ -86,7 +80,6 @@ class CloudOutputDeviceManager: def _parseStatusResponse(reply: QNetworkReply) -> Optional[dict]: try: result = json.loads(bytes(reply.readAll()).decode("utf-8")) - print("result=====", result) # TODO: use model or named tuple here. return result.data except json.decoder.JSONDecodeError: From 4bccd2b7b5b0faac7ddd6140c5bebe81c43659cf Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Tue, 20 Nov 2018 23:46:03 +0100 Subject: [PATCH 0277/1240] Restyle printer settings dropdown CURA-5941 --- plugins/PreviewStage/PreviewMenu.qml | 2 +- resources/qml/ActionButton.qml | 18 +- resources/qml/MachineSelector.qml | 2 +- .../QuickConfigurationSelector.qml | 2 +- resources/qml/PrintSetupSelector.qml | 343 ++++++++++++------ resources/themes/cura-light/theme.json | 7 +- 6 files changed, 251 insertions(+), 123 deletions(-) diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index b331ff69c5..7b3a3af4e3 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -3,7 +3,7 @@ import QtQuick 2.7 -import QtQuick.Controls 2.4 +import QtQuick.Controls 2.3 import UM 1.3 as UM import Cura 1.1 as Cura diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index 2a8b894867..08f4d9d679 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -12,6 +12,7 @@ Button id: button property alias cursorShape: mouseArea.cursorShape property alias iconSource: buttonIcon.source + property alias iconSourceRight: buttonIconRight.source property alias textFont: buttonText.font property alias cornerRadius: backgroundRect.radius property alias tooltip: tooltip.text @@ -29,7 +30,8 @@ Button // we elide the text to the right so the text will be cut off with the three dots at the end. property var fixedWidthMode: false - contentItem: Row + width: buttonIcon.width + buttonText.width + buttonIconRight.width + contentItem: Item { UM.RecolorImage { @@ -57,6 +59,20 @@ Button horizontalAlignment: Text.AlignHCenter elide: Text.ElideRight } + + UM.RecolorImage + { + id: buttonIconRight + source: "" + height: Math.round(0.6 * parent.height) + width: height + sourceSize.width: width + sourceSize.height: height + color: button.hovered ? button.textHoverColor : button.textColor + visible: source != "" + anchors.verticalCenter: parent.verticalCenter + anchors.right: source != "" ? parent.right : undefined + } } background: Rectangle diff --git a/resources/qml/MachineSelector.qml b/resources/qml/MachineSelector.qml index a464949192..c9756d93ba 100644 --- a/resources/qml/MachineSelector.qml +++ b/resources/qml/MachineSelector.qml @@ -2,7 +2,7 @@ // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.2 -import QtQuick.Controls 2.4 +import QtQuick.Controls 2.3 import QtQuick.Controls.Styles 1.1 import QtQuick.Layouts 1.1 diff --git a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml index 33610135fe..d428a05463 100644 --- a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml +++ b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml @@ -4,7 +4,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Controls.Styles 1.4 -import QtQuick.Layouts 1.11 +import QtQuick.Layouts 1.3 import QtQuick.Controls 1.1 as OldControls diff --git a/resources/qml/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector.qml index b843244147..ed09238b51 100644 --- a/resources/qml/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector.qml @@ -28,8 +28,15 @@ Cura.ExpandableComponent height: childrenRect.height iconSource: UM.Theme.getIcon("pencil") + popupPadding : 0 + onCurrentModeIndexChanged: UM.Preferences.setValue("cura/active_mode", currentModeIndex) + Component.onCompleted: + { + popupItemWrapper.width = base.width + } + UM.I18nCatalog { id: catalog @@ -109,142 +116,242 @@ Cura.ExpandableComponent } } - popupItem: Item + + Cura.ExtrudersModel { - height: settingsModeSelection.height + sidebarContents.height + 2 * UM.Theme.getSize("default_margin").height - width: UM.Theme.getSize("print_setup_widget").width - ListView - { - // Settings mode selection toggle - id: settingsModeSelection - model: modesListModel - height: UM.Theme.getSize("print_setup_mode_toggle").height - visible: !hideSettings + id: extrudersModel + } - anchors - { - right: parent.right - left: parent.left - margins: UM.Theme.getSize("thick_margin").width - } + popupItem: Rectangle + { + property var total_height: popupItemHeader.height + popupItemContent.height + 10 + separator_footer.height + id: popupItemWrapper + height: total_height - ButtonGroup - { - id: modeMenuGroup - } - - delegate: Button - { - id: control - - height: settingsModeSelection.height - width: Math.round(parent.width / 2) - - anchors.left: parent.left - anchors.leftMargin: model.index * Math.round(settingsModeSelection.width / 2) - anchors.verticalCenter: parent.verticalCenter - - ButtonGroup.group: modeMenuGroup - - checkable: true - checked: base.currentModeIndex == index - onClicked: base.currentModeIndex = index - - onHoveredChanged: - { - if (hovered) - { - tooltipDelayTimer.item = settingsModeSelection - tooltipDelayTimer.text = model.tooltipText - tooltipDelayTimer.start() - } - else - { - tooltipDelayTimer.stop() - base.hideTooltip() - } - } - - background: Rectangle - { - border.width: control.checked ? UM.Theme.getSize("default_lining").width * 2 : UM.Theme.getSize("default_lining").width - border.color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active_border") : control.hovered ? UM.Theme.getColor("action_button_hovered_border") : UM.Theme.getColor("action_button_border") - - // For some reason, QtQuick decided to use the color of the background property as text color for the contentItem, so here it is - color: (control.checked || control.pressed) ? UM.Theme.getColor("action_button_active") : control.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button") - } - - contentItem: Label - { - text: model.text - font: UM.Theme.getFont("default") - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - renderType: Text.NativeRendering - elide: Text.ElideRight - color: - { - if(control.pressed) - { - return UM.Theme.getColor("action_button_active_text") - } - else if(control.hovered) - { - return UM.Theme.getColor("action_button_hovered_text") - } - return UM.Theme.getColor("action_button_text") - } - } - } - } + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") Item { - id: sidebarContents - anchors.top: settingsModeSelection.bottom - anchors.topMargin: UM.Theme.getSize("thick_margin").height - anchors.left: parent.left - anchors.right: parent.right - height: UM.Theme.getSize("print_setup_widget").height + id: popupItemHeader + height: 36 - visible: !hideSettings - - // We load both of them at once (instead of using a loader) because the advanced sidebar can take - // quite some time to load. So in this case we sacrifice memory for speed. - SidebarAdvanced + anchors { - anchors.fill: parent - visible: currentModeIndex == 1 - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() + top: parent.top + right: parent.right + left: parent.left } - SidebarSimple + Label { - anchors.fill: parent - visible: currentModeIndex != 1 - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() + id: popupItemHeaderText + text: catalog.i18nc("@label", "Print settings"); + font: UM.Theme.getFont("default") + verticalAlignment: Text.AlignVCenter + color: UM.Theme.getColor("text") + height: parent.height + anchors.topMargin: UM.Theme.getSize("sidebar_margin").height + anchors.bottomMargin: UM.Theme.getSize("sidebar_margin").height + anchors.left: parent.left + anchors.leftMargin: UM.Theme.getSize("print_setup_selector_margin").height + } + + Rectangle + { + width: parent.width + height: UM.Theme.getSize("default_lining").height + anchors.top: popupItemHeaderText.bottom + color: UM.Theme.getColor("action_button_border") + + } } - // Setting mode: Recommended or Custom - ListModel + Rectangle { - id: modesListModel + id: popupItemContent + width: parent.width + height: tabBar.height + sidebarContents.height + anchors + { + top: popupItemHeader.bottom + topMargin: 10 + right: parent.right + left: parent.left + leftMargin: 5 + rightMargin: 5 + } + + + TabBar + { + id: tabBar + onCurrentIndexChanged: Cura.ExtruderManager.setActiveExtruderIndex(currentIndex) + width: parent.width + height: UM.Theme.getSize("print_setup_tap_bar").width + z: 1 + Repeater + { + id: extruder_model_repeater + model: extrudersModel + + delegate: TabButton + { + z: 2 + width: ListView.view != null ? Math.round(ListView.view.width / extrudersModel.rowCount()): 0 + implicitHeight: parent.height + + contentItem: Rectangle + { + z: 2 + Cura.ExtruderIcon + { + anchors.horizontalCenter: parent.horizontalCenter + materialColor: model.color + extruderEnabled: model.enabled + width: parent.height + height: parent.height + } + + } + + background: Rectangle + { + + width: parent.width + z: 1 + border.width: UM.Theme.getSize("default_lining").width * 2 + border.color: UM.Theme.getColor("action_button_border") + + visible: + { + return index == tabBar.currentIndex + } + + // overlap bottom border + Rectangle + { + width: parent.width - 4 + height: 4 + anchors.bottom: parent.bottom + anchors.bottomMargin: -2 + anchors.leftMargin: 2 + anchors.left: parent.left + + } + } + } + } + } + + Rectangle + { + id: sidebarContents + anchors.top: tabBar.bottom + anchors.left: parent.left + anchors.right: parent.right + height: UM.Theme.getSize("print_setup_widget").height + + + border.width: UM.Theme.getSize("default_lining").width * 2 + border.color: UM.Theme.getColor("action_button_border") + + SidebarSimple + { + anchors.topMargin: UM.Theme.getSize("print_setup_content_top_margin").height + id: sidebar_simple_id + anchors.fill: parent + visible: currentModeIndex != 1 + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() + } + + SidebarAdvanced + { + anchors.topMargin: UM.Theme.getSize("print_setup_content_top_margin").height + anchors.bottomMargin: 2 //does not overlap bottom border + anchors.fill: parent + visible: currentModeIndex == 1 + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() + } + + } } + + Item + { + id: separator_footer + anchors.top: popupItemContent.bottom + anchors.topMargin: 10 + width: parent.width + height: settingControlButton.height + 4 + + Rectangle + { + width: parent.width + height: UM.Theme.getSize("default_lining").height + color: UM.Theme.getColor("action_button_border") + } + + Cura.ActionButton + { + id: settingControlButton + + leftPadding: UM.Theme.getSize("default_margin").width + rightPadding: UM.Theme.getSize("default_margin").width + height: UM.Theme.getSize("action_panel_button").height + text: catalog.i18nc("@button", "Custom") + color: UM.Theme.getColor("secondary") + hoverColor: UM.Theme.getColor("secondary") + textColor: UM.Theme.getColor("primary") + textHoverColor: UM.Theme.getColor("text") + iconSourceRight: UM.Theme.getIcon("arrow_right") + width: UM.Theme.getSize("print_setup_action_button").width + fixedWidthMode: true + visible: currentModeIndex == 0 + anchors + { + top: parent.top + topMargin: 10 + bottomMargin: 10 + right: parent.right + rightMargin: 5 + } + + onClicked: currentModeIndex = 1 + } + + Cura.ActionButton + { + height: UM.Theme.getSize("action_panel_button").height + text: catalog.i18nc("@button", "Recommended") + color: UM.Theme.getColor("secondary") + hoverColor: UM.Theme.getColor("secondary") + textColor: UM.Theme.getColor("primary") + textHoverColor: UM.Theme.getColor("text") + iconSource: UM.Theme.getIcon("arrow_left") + width: UM.Theme.getSize("print_setup_action_button").width + + + fixedWidthMode: true + visible: currentModeIndex == 1 + anchors + { + top: parent.top + topMargin: UM.Theme.getSize("print_setup_selector_margin").height * 2 + bottomMargin: UM.Theme.getSize("print_setup_selector_margin").height * 2 + left: parent.left + leftMargin: UM.Theme.getSize("print_setup_selector_margin").height + } + + onClicked: currentModeIndex = 0 + } + } Component.onCompleted: { - modesListModel.append({ - text: catalog.i18nc("@title:tab", "Recommended"), - tooltipText: "%1

%2".arg(catalog.i18nc("@tooltip:title", "Recommended Print Setup")).arg(catalog.i18nc("@tooltip", "Print with the recommended settings for the selected printer, material and quality.")) - }) - modesListModel.append({ - text: catalog.i18nc("@title:tab", "Custom"), - tooltipText: "%1

%2".arg(catalog.i18nc("@tooltip:title", "Custom Print Setup")).arg(catalog.i18nc("@tooltip", "Print with finegrained control over every last bit of the slicing process.")) - }) - var index = Math.round(UM.Preferences.getValue("cura/active_mode")) if(index != null && !isNaN(index)) @@ -257,4 +364,4 @@ Cura.ExpandableComponent } } } -} +} \ No newline at end of file diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index d28611529b..fc62edc480 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -528,6 +528,11 @@ "monitor_thick_lining": [0.16, 0.16], "monitor_corner_radius": [0.3, 0.3], "monitor_shadow_radius": [0.4, 0.4], - "monitor_shadow_offset": [0.15, 0.15] + "monitor_shadow_offset": [0.15, 0.15], + + "print_setup_selector_margin": [0.5, 0.5], + "print_setup_action_button": [13, 5], + "print_setup_tap_bar": [4, 4], + "print_setup_content_top_margin": [3, 3] } } From 8b085aa877194c45a3122ccbafa5460d57f9e28d Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 20 Nov 2018 23:47:25 +0100 Subject: [PATCH 0278/1240] Revert "Switch Ultimaker Account OAuth2 client" This reverts commit 4e8979334e0d794b2cd8cae72ce9c351890577c8. --- cura/API/Account.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cura/API/Account.py b/cura/API/Account.py index 64d63c7025..397e220478 100644 --- a/cura/API/Account.py +++ b/cura/API/Account.py @@ -38,12 +38,13 @@ class Account(QObject): self._callback_port = 32118 self._oauth_root = "https://account.ultimaker.com" + self._cloud_api_root = "https://api.ultimaker.com" self._oauth_settings = OAuth2Settings( OAUTH_SERVER_URL= self._oauth_root, CALLBACK_PORT=self._callback_port, CALLBACK_URL="http://localhost:{}/callback".format(self._callback_port), - CLIENT_ID="um----------------------------ultimaker_cura", + CLIENT_ID="um---------------ultimaker_cura_drive_plugin", CLIENT_SCOPES="account.user.read drive.backup.read drive.backup.write packages.download packages.rating.read packages.rating.write", AUTH_DATA_PREFERENCE_KEY="general/ultimaker_auth_data", AUTH_SUCCESS_REDIRECT="{}/app/auth-success".format(self._oauth_root), From 791929d6897942e971a112a26839c0fa6f94f847 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 20 Nov 2018 23:47:46 +0100 Subject: [PATCH 0279/1240] Revert "Merge branch 'fix-oauth2-client' into CURA-5785-Restyle_stage_menu" This reverts commit 4caf770a62f07b71a646b7f09a08623485b89582, reversing changes made to 0cdcd61ff2dfe2677e636cc2f1ee993cbd9d641d. --- resources/qml/Dialogs/AddMachineDialog.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Dialogs/AddMachineDialog.qml b/resources/qml/Dialogs/AddMachineDialog.qml index aa160acd4d..0df8b891d9 100644 --- a/resources/qml/Dialogs/AddMachineDialog.qml +++ b/resources/qml/Dialogs/AddMachineDialog.qml @@ -73,7 +73,7 @@ UM.Dialog { top: parent.top left: parent.left - topMargin: UM.Theme.getSize("default_margin").height + topMargin: UM.Theme.getSize("default_margin") } text: catalog.i18nc("@title:tab", "Add a printer to Cura") From 60ed0ddc2493f357417922529f25ce242d8daba7 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 20 Nov 2018 23:49:33 +0100 Subject: [PATCH 0280/1240] Revert "Revert "Merge branch 'fix-oauth2-client' into CURA-5785-Restyle_stage_menu"" This reverts commit 791929d6897942e971a112a26839c0fa6f94f847. --- resources/qml/Dialogs/AddMachineDialog.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Dialogs/AddMachineDialog.qml b/resources/qml/Dialogs/AddMachineDialog.qml index 0df8b891d9..aa160acd4d 100644 --- a/resources/qml/Dialogs/AddMachineDialog.qml +++ b/resources/qml/Dialogs/AddMachineDialog.qml @@ -73,7 +73,7 @@ UM.Dialog { top: parent.top left: parent.left - topMargin: UM.Theme.getSize("default_margin") + topMargin: UM.Theme.getSize("default_margin").height } text: catalog.i18nc("@title:tab", "Add a printer to Cura") From 8ad8489af0afeb7278d325b1db9eb26e31473086 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 21 Nov 2018 00:03:36 +0100 Subject: [PATCH 0281/1240] Fix returning reply on postFormWithParts --- cura/NetworkClient.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cura/NetworkClient.py b/cura/NetworkClient.py index ce9cc50cf6..da27456ac8 100644 --- a/cura/NetworkClient.py +++ b/cura/NetworkClient.py @@ -195,7 +195,7 @@ class NetworkClient: ## Does a POST request with form parts to the given URL. def postFormWithParts(self, target: str, parts: List[QHttpPart], on_finished: Optional[Callable[[QNetworkReply], None]], - on_progress: Callable = None) -> None: + on_progress: Callable = None) -> QNetworkReply: self._validateManager() request = self._createEmptyRequest(target, content_type = None) @@ -218,3 +218,4 @@ class NetworkClient: reply.uploadProgress.connect(on_progress) self._registerOnFinishedCallback(reply, on_finished) + return reply From 8453cd693e98bf7d0e5a632b0a6ef8380826c931 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 21 Nov 2018 00:04:34 +0100 Subject: [PATCH 0282/1240] Make QNetworkReply optional --- cura/NetworkClient.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/NetworkClient.py b/cura/NetworkClient.py index da27456ac8..20c026f4e6 100644 --- a/cura/NetworkClient.py +++ b/cura/NetworkClient.py @@ -195,7 +195,7 @@ class NetworkClient: ## Does a POST request with form parts to the given URL. def postFormWithParts(self, target: str, parts: List[QHttpPart], on_finished: Optional[Callable[[QNetworkReply], None]], - on_progress: Callable = None) -> QNetworkReply: + on_progress: Callable = None) -> Optional[QNetworkReply]: self._validateManager() request = self._createEmptyRequest(target, content_type = None) From 23744e42d1f4480fdc7f3c56f3591074b3ae8723 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 21 Nov 2018 09:15:18 +0100 Subject: [PATCH 0283/1240] Remove SaveButton file, since it's not used anymore. Contributes to CURA-5942 --- resources/qml/SaveButton.qml | 478 ----------------------------------- 1 file changed, 478 deletions(-) delete mode 100644 resources/qml/SaveButton.qml diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml deleted file mode 100644 index c2d310e30c..0000000000 --- a/resources/qml/SaveButton.qml +++ /dev/null @@ -1,478 +0,0 @@ -// Copyright (c) 2018 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.7 -import QtQuick.Controls 1.1 -import QtQuick.Controls.Styles 1.1 -import QtQuick.Layouts 1.1 - -import UM 1.1 as UM -import Cura 1.0 as Cura - -// This widget does so much more than "just" being a save button, so it should be refactored at some point in time. -Item -{ - id: base; - UM.I18nCatalog { id: catalog; name: "cura"} - - property real progress: UM.Backend.progress - property int backendState: UM.Backend.state - property bool activity: CuraApplication.platformActivity - - property alias buttonRowWidth: saveRow.width - - property string fileBaseName - property string statusText: - { - if(!activity) - { - return catalog.i18nc("@label:PrintjobStatus", "Please load a 3D model"); - } - - switch(base.backendState) - { - case 1: - return catalog.i18nc("@label:PrintjobStatus", "Ready to slice"); - case 2: - return catalog.i18nc("@label:PrintjobStatus", "Slicing..."); - case 3: - return catalog.i18nc("@label:PrintjobStatus %1 is target operation", "Ready to %1").arg(UM.OutputDeviceManager.activeDeviceShortDescription); - case 4: - return catalog.i18nc("@label:PrintjobStatus", "Unable to Slice"); - case 5: - return catalog.i18nc("@label:PrintjobStatus", "Slicing unavailable"); - default: - return ""; - } - } - - function sliceOrStopSlicing() - { - try - { - if ([1, 5].indexOf(base.backendState) != -1) - { - CuraApplication.backend.forceSlice(); - } - else - { - CuraApplication.backend.stopSlicing(); - } - } - catch (e) - { - console.log("Could not start or stop slicing.", e) - } - } - - Label - { - id: statusLabel - width: parent.width - 2 * UM.Theme.getSize("thick_margin").width - anchors.top: parent.top - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("thick_margin").width - - color: UM.Theme.getColor("text") - font: UM.Theme.getFont("default_bold") - text: statusText; - } - - Rectangle - { - id: progressBar - width: parent.width - 2 * UM.Theme.getSize("thick_margin").width - height: UM.Theme.getSize("progressbar").height - anchors.top: statusLabel.bottom - anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height / 4) - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("thick_margin").width - radius: UM.Theme.getSize("progressbar_radius").width - color: UM.Theme.getColor("progressbar_background") - - Rectangle - { - width: Math.max(parent.width * base.progress) - height: parent.height - color: UM.Theme.getColor("progressbar_control") - radius: UM.Theme.getSize("progressbar_radius").width - visible: base.backendState == 2 - } - } - - // Shortcut for "save as/print/..." - Action - { - shortcut: "Ctrl+P" - onTriggered: - { - // only work when the button is enabled - if (saveToButton.enabled) - { - saveToButton.clicked(); - } - // prepare button - if (prepareButton.enabled) - { - sliceOrStopSlicing(); - } - } - } - - Item - { - id: saveRow - width: { - // using childrenRect.width directly causes a binding loop, because setting the width affects the childrenRect - var children_width = UM.Theme.getSize("default_margin").width; - for (var index in children) - { - var child = children[index]; - if(child.visible) - { - children_width += child.width + child.anchors.rightMargin; - } - } - return Math.min(children_width, base.width - UM.Theme.getSize("thick_margin").width); - } - height: saveToButton.height - anchors.bottom: parent.bottom - anchors.bottomMargin: UM.Theme.getSize("thick_margin").height - anchors.right: parent.right - clip: true - - Row - { - id: additionalComponentsRow - anchors.top: parent.top - anchors.right: saveToButton.visible ? saveToButton.left : (prepareButton.visible ? prepareButton.left : parent.right) - anchors.rightMargin: UM.Theme.getSize("default_margin").width - - spacing: UM.Theme.getSize("default_margin").width - } - - Component.onCompleted: - { - saveRow.addAdditionalComponents("saveButton") - } - - Connections - { - target: CuraApplication - onAdditionalComponentsChanged: saveRow.addAdditionalComponents("saveButton") - } - - function addAdditionalComponents (areaId) - { - if(areaId == "saveButton") - { - for (var component in CuraApplication.additionalComponents["saveButton"]) - { - CuraApplication.additionalComponents["saveButton"][component].parent = additionalComponentsRow - } - } - } - - Connections - { - target: UM.Preferences - onPreferenceChanged: - { - var autoSlice = UM.Preferences.getValue("general/auto_slice"); - prepareButton.autoSlice = autoSlice; - saveToButton.autoSlice = autoSlice; - } - } - - // Prepare button, only shows if auto_slice is off - Button - { - id: prepareButton - - tooltip: [1, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@info:tooltip","Slice current printjob") : catalog.i18nc("@info:tooltip","Cancel slicing process") - // 1 = not started, 2 = Processing - enabled: ([1, 2].indexOf(base.backendState) != -1) && base.activity - visible: !autoSlice && ([1, 2, 4].indexOf(base.backendState) != -1) && base.activity - property bool autoSlice - height: UM.Theme.getSize("save_button_save_to_button").height - - anchors.top: parent.top - anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("thick_margin").width - - // 1 = not started, 4 = error, 5 = disabled - text: [1, 4, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@label:Printjob", "Prepare") : catalog.i18nc("@label:Printjob", "Cancel") - onClicked: - { - sliceOrStopSlicing(); - } - - style: ButtonStyle - { - background: Rectangle - { - border.width: UM.Theme.getSize("default_lining").width - border.color: - { - if(!control.enabled) - { - return UM.Theme.getColor("action_button_disabled_border"); - } - else if(control.pressed) - { - return UM.Theme.getColor("action_button_active_border"); - } - else if(control.hovered) - { - return UM.Theme.getColor("action_button_hovered_border"); - } - else - { - return UM.Theme.getColor("action_button_border"); - } - } - color: - { - if(!control.enabled) - { - return UM.Theme.getColor("action_button_disabled"); - } - else if(control.pressed) - { - return UM.Theme.getColor("action_button_active"); - } - else if(control.hovered) - { - return UM.Theme.getColor("action_button_hovered"); - } - else - { - return UM.Theme.getColor("action_button"); - } - } - - Behavior on color { ColorAnimation { duration: 50; } } - - implicitWidth: actualLabel.contentWidth + (UM.Theme.getSize("thick_margin").width * 2) - - Label - { - id: actualLabel - anchors.centerIn: parent - color: - { - if(!control.enabled) - { - return UM.Theme.getColor("action_button_disabled_text"); - } - else if(control.pressed) - { - return UM.Theme.getColor("action_button_active_text"); - } - else if(control.hovered) - { - return UM.Theme.getColor("action_button_hovered_text"); - } - else - { - return UM.Theme.getColor("action_button_text"); - } - } - font: UM.Theme.getFont("action_button") - text: control.text; - } - } - label: Item {} - } - } - - Button - { - id: saveToButton - - tooltip: UM.OutputDeviceManager.activeDeviceDescription; - // 3 = done, 5 = disabled - enabled: base.backendState != "undefined" && (base.backendState == 3 || base.backendState == 5) && base.activity == true - visible: base.backendState != "undefined" && autoSlice || ((base.backendState == 3 || base.backendState == 5) && base.activity == true) - property bool autoSlice - height: UM.Theme.getSize("save_button_save_to_button").height - - anchors.top: parent.top - anchors.right: deviceSelectionMenu.visible ? deviceSelectionMenu.left : parent.right - anchors.rightMargin: deviceSelectionMenu.visible ? -3 * UM.Theme.getSize("default_lining").width : UM.Theme.getSize("thick_margin").width - - text: UM.OutputDeviceManager.activeDeviceShortDescription - onClicked: - { - forceActiveFocus(); - UM.OutputDeviceManager.requestWriteToDevice(UM.OutputDeviceManager.activeDevice, PrintInformation.jobName, - { "filter_by_machine": true, "preferred_mimetypes": Cura.MachineManager.activeMachine.preferred_output_file_formats }); - } - - style: ButtonStyle - { - background: Rectangle - { - border.width: UM.Theme.getSize("default_lining").width - border.color: - { - if(!control.enabled) - { - return UM.Theme.getColor("action_button_disabled_border"); - } - else if(control.pressed) - { - return UM.Theme.getColor("print_button_ready_pressed_border"); - } - else if(control.hovered) - { - return UM.Theme.getColor("print_button_ready_hovered_border"); - } - else - { - return UM.Theme.getColor("print_button_ready_border"); - } - } - color: - { - if(!control.enabled) - { - return UM.Theme.getColor("action_button_disabled"); - } - else if(control.pressed) - { - return UM.Theme.getColor("print_button_ready_pressed"); - } - else if(control.hovered) - { - return UM.Theme.getColor("print_button_ready_hovered"); - } - else - { - return UM.Theme.getColor("print_button_ready"); - } - } - - Behavior on color { ColorAnimation { duration: 50; } } - - implicitWidth: actualLabel.contentWidth + (UM.Theme.getSize("thick_margin").width * 2) - - Label - { - id: actualLabel - anchors.centerIn: parent - color: control.enabled ? UM.Theme.getColor("print_button_ready_text") : UM.Theme.getColor("action_button_disabled_text") - font: UM.Theme.getFont("action_button") - text: control.text - } - } - label: Item { } - } - } - - Button - { - id: deviceSelectionMenu - tooltip: catalog.i18nc("@info:tooltip","Select the active output device"); - anchors.top: parent.top - anchors.right: parent.right - - anchors.rightMargin: UM.Theme.getSize("thick_margin").width - width: UM.Theme.getSize("save_button_save_to_button").height - height: UM.Theme.getSize("save_button_save_to_button").height - - // 3 = Done, 5 = Disabled - enabled: (base.backendState == 3 || base.backendState == 5) && base.activity == true - visible: (devicesModel.deviceCount > 1) && (base.backendState == 3 || base.backendState == 5) && base.activity == true - - - style: ButtonStyle - { - background: Rectangle - { - id: deviceSelectionIcon - border.width: UM.Theme.getSize("default_lining").width - border.color: - { - if(!control.enabled) - { - return UM.Theme.getColor("action_button_disabled_border") - } - else if(control.pressed) - { - return UM.Theme.getColor("print_button_ready_pressed_border") - } - else if(control.hovered) - { - return UM.Theme.getColor("print_button_ready_hovered_border") - } - else - { - return UM.Theme.getColor("print_button_ready_border") - } - } - color: - { - if(!control.enabled) - { - return UM.Theme.getColor("action_button_disabled") - } - else if(control.pressed) - { - return UM.Theme.getColor("print_button_ready_pressed") - } - else if(control.hovered) - { - return UM.Theme.getColor("print_button_ready_hovered") - } - else - { - return UM.Theme.getColor("print_button_ready") - } - } - Behavior on color { ColorAnimation { duration: 50; } } - anchors.left: parent.left - anchors.leftMargin: Math.round(UM.Theme.getSize("save_button_text_margin").width / 2); - width: parent.height - height: parent.height - - 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: height - color: control.enabled ? UM.Theme.getColor("print_button_ready_text") : UM.Theme.getColor("action_button_disabled_text") - source: UM.Theme.getIcon("arrow_bottom") - } - } - } - - menu: Menu - { - id: devicesMenu; - Instantiator - { - model: devicesModel; - MenuItem - { - text: model.description - checkable: true; - checked: model.id == UM.OutputDeviceManager.activeDevice - exclusiveGroup: devicesMenuGroup - onTriggered: - { - UM.OutputDeviceManager.setActiveDevice(model.id); - } - } - onObjectAdded: devicesMenu.insertItem(index, object) - onObjectRemoved: devicesMenu.removeItem(object) - } - ExclusiveGroup { id: devicesMenuGroup } - } - } - UM.OutputDevicesModel { id: devicesModel } - } -} From d74393498b18805de9453e05bded2e9c09a3d928 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 21 Nov 2018 09:35:35 +0100 Subject: [PATCH 0284/1240] Fixed view selector header not updating correctly CURA-5785 --- plugins/PreviewStage/PreviewMenu.qml | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index 1a744aeb65..fa1b6aae0f 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -54,22 +54,27 @@ Item { for (var i = 0; i < viewModel.rowCount(); i++) { - if (viewModel.getItem(i).active) + if (viewModel.items[i].active) { - return viewModel.getItem(i) + return viewModel.items[i] } } - // Nothing was active, so just return the first one (the list is sorted by priority, so the most - // important one should be returned) - return viewModel.getItem(0) + return null } - // Ensure that the controller is synced with whatever happend here. - onActiveViewChanged: UM.Controller.setActiveView(activeView.id) + Component.onCompleted: + { + // Nothing was active, so just return the first one (the list is sorted by priority, so the most + // important one should be returned) + if(activeView == null) + { + UM.Controller.setActiveView(viewModel.getItem(0).id) + } + } headerItem: Label { - text: viewSelector.activeView.name + text: viewSelector.activeView ? viewSelector.activeView.name : "" verticalAlignment: Text.AlignVCenter height: parent.height elide: Text.ElideRight @@ -98,7 +103,7 @@ Item text: name radius: UM.Theme.getSize("default_radius").width checkable: true - checked: active + checked: viewSelector.activeView != null ? viewSelector.activeView.id == id : false onClicked: { viewSelector.togglePopup() From 9e8be286af8736a97bf05db3b4bb5b0063be944b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Wed, 21 Nov 2018 10:12:53 +0100 Subject: [PATCH 0285/1240] Used NamedTuple from typing iso namedtuple from collections so we can at least give type hints --- plugins/UM3NetworkPrinting/src/Models.py | 54 +++++++++---------- .../UM3NetworkPrinting/src/SendMaterialJob.py | 51 +++++++++--------- .../tests/TestSendMaterialJob.py | 4 +- 3 files changed, 55 insertions(+), 54 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Models.py b/plugins/UM3NetworkPrinting/src/Models.py index a9210ac5b4..e84a39db5a 100644 --- a/plugins/UM3NetworkPrinting/src/Models.py +++ b/plugins/UM3NetworkPrinting/src/Models.py @@ -1,33 +1,33 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from collections import namedtuple +from typing import NamedTuple -ClusterMaterial = namedtuple('ClusterMaterial', [ - 'guid', - 'material', - 'brand', - 'version', - 'color', - 'density' +ClusterMaterial = NamedTuple("ClusterMaterial", [ + ("guid", str), + ("material", str), + ("brand", str), + ("version", int), + ("color", str), + ("density", str), ]) -LocalMaterial = namedtuple('LocalMaterial', [ - 'GUID', - 'id', - 'type', - 'status', - 'base_file', - 'setting_version', - 'version', - 'name', - 'brand', - 'material', - 'color_name', - 'color_code', - 'description', - 'adhesion_info', - 'approximate_diameter', - 'properties', - 'definition', - 'compatible' +LocalMaterial = NamedTuple("LocalMaterial", [ + ("GUID", str), + ("id", str), + ("type", str), + ("status", str), + ("base_file", str), + ("setting_version", int), + ("version", int), + ("name", str), + ("brand", str), + ("material", str), + ("color_name", str), + ("color_code", str), + ("description", str), + ("adhesion_info", str), + ("approximate_diameter", str), + ("properties", str), + ("definition", str), + ("compatible", str), ]) diff --git a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py index fe0cd98964..ee8dd8042d 100644 --- a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py @@ -2,7 +2,6 @@ # Cura is released under the terms of the LGPLv3 or higher. import json import os -import re import urllib.parse from typing import Dict, TYPE_CHECKING, Set @@ -13,13 +12,13 @@ from UM.Logger import Logger from UM.MimeTypeDatabase import MimeTypeDatabase from UM.Resources import Resources from cura.CuraApplication import CuraApplication - # Absolute imports don't work in plugins from .Models import ClusterMaterial, LocalMaterial if TYPE_CHECKING: from .ClusterUM3OutputDevice import ClusterUM3OutputDevice + ## Asynchronous job to send material profiles to the printer. # # This way it won't freeze up the interface while sending those materials. @@ -86,7 +85,7 @@ class SendMaterialJob(Job): return { material.id for guid, material in local_materials.items() - if guid not in remote_materials or int(material.version) > remote_materials[guid].version + if guid not in remote_materials or material.version > remote_materials[guid].version } ## Send the materials to the printer. @@ -122,23 +121,23 @@ class SendMaterialJob(Job): # \param material_id The ID of the material in the file. def _sendMaterialFile(self, file_path: str, file_name: str, material_id: str) -> None: - parts = [] + parts = [] - # Add the material file. - with open(file_path, "rb") as f: - parts.append(self.device.createFormPart("name=\"file\"; filename=\"{file_name}\"" - .format(file_name = file_name), f.read())) + # Add the material file. + with open(file_path, "rb") as f: + parts.append(self.device.createFormPart("name=\"file\"; filename=\"{file_name}\"" + .format(file_name = file_name), f.read())) - # Add the material signature file if needed. - signature_file_path = "{}.sig".format(file_path) - if os.path.exists(signature_file_path): - signature_file_name = os.path.basename(signature_file_path) - with open(signature_file_path, "rb") as f: - parts.append(self.device.createFormPart("name=\"signature_file\"; filename=\"{file_name}\"" - .format(file_name = signature_file_name), f.read())) + # Add the material signature file if needed. + signature_file_path = "{}.sig".format(file_path) + if os.path.exists(signature_file_path): + signature_file_name = os.path.basename(signature_file_path) + with open(signature_file_path, "rb") as f: + parts.append(self.device.createFormPart("name=\"signature_file\"; filename=\"{file_name}\"" + .format(file_name = signature_file_name), f.read())) - Logger.log("d", "Syncing material {material_id} with cluster.".format(material_id = material_id)) - self.device.postFormWithParts(target = "materials/", parts = parts, on_finished = self.sendingFinished) + Logger.log("d", "Syncing material {material_id} with cluster.".format(material_id = material_id)) + self.device.postFormWithParts(target = "materials/", parts = parts, on_finished = self.sendingFinished) ## Check a reply from an upload to the printer and log an error when the call failed @staticmethod @@ -174,16 +173,18 @@ class SendMaterialJob(Job): # Find the latest version of all material containers in the registry. for material in material_containers: try: - material = LocalMaterial(**material) - # material version must be an int - if not re.match("\d+", material.version): - Logger.logException("w", "Local material {} has invalid version '{}'." - .format(material["id"], material.version)) - continue + material["version"] = int(material["version"]) - if material.GUID not in result or material.version > result.get(material.GUID).version: - result[material.GUID] = material + # Create a new local material + local_material = LocalMaterial(**material) + + if local_material.GUID not in result or \ + local_material.version > result.get(local_material.GUID).version: + result[local_material.GUID] = local_material + + except KeyError: + Logger.logException("w", "Local material {} has missing values.".format(material["id"])) except ValueError: Logger.logException("w", "Local material {} has invalid values.".format(material["id"])) diff --git a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py index f4604580fe..ff896683e1 100644 --- a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py @@ -19,7 +19,7 @@ from plugins.UM3NetworkPrinting.src.SendMaterialJob import SendMaterialJob @patch("UM.Resources.Resources.getAllResourcesOfType", lambda _: ["/materials/generic_pla_white.xml.fdm_material"]) class TestSendMaterialJob(TestCase): _LOCAL_MATERIAL_WHITE = {"type": "material", "status": "unknown", "id": "generic_pla_white", - "base_file": "generic_pla_white", "setting_version": 5, "name": "White PLA", + "base_file": "generic_pla_white", "setting_version": "5", "name": "White PLA", "brand": "Generic", "material": "PLA", "color_name": "White", "GUID": "badb0ee7-87c8-4f3f-9398-938587b67dce", "version": "1", "color_code": "#ffffff", "description": "Test PLA White", "adhesion_info": "Use glue.", "approximate_diameter": "3", @@ -27,7 +27,7 @@ class TestSendMaterialJob(TestCase): "definition": "fdmprinter", "compatible": True} _LOCAL_MATERIAL_BLACK = {"type": "material", "status": "unknown", "id": "generic_pla_black", - "base_file": "generic_pla_black", "setting_version": 5, "name": "Yellow CPE", + "base_file": "generic_pla_black", "setting_version": "5", "name": "Yellow CPE", "brand": "Ultimaker", "material": "CPE", "color_name": "Black", "GUID": "5fbb362a-41f9-4818-bb43-15ea6df34aa4", "version": "1", "color_code": "#000000", "description": "Test PLA Black", "adhesion_info": "Use glue.", "approximate_diameter": "3", From 7b0f8882a2715d4beb9378646bc915e77c8f4963 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Wed, 21 Nov 2018 11:01:26 +0100 Subject: [PATCH 0286/1240] Reverted models to namedtuples from collections because NamedTuple is a Python3.6 feature --- plugins/UM3NetworkPrinting/src/Models.py | 54 ++++++++++++------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Models.py b/plugins/UM3NetworkPrinting/src/Models.py index e84a39db5a..d5e1007555 100644 --- a/plugins/UM3NetworkPrinting/src/Models.py +++ b/plugins/UM3NetworkPrinting/src/Models.py @@ -1,33 +1,33 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import NamedTuple +from collections import namedtuple -ClusterMaterial = NamedTuple("ClusterMaterial", [ - ("guid", str), - ("material", str), - ("brand", str), - ("version", int), - ("color", str), - ("density", str), +ClusterMaterial = namedtuple('ClusterMaterial', [ + 'guid', # Type: str + 'material', # Type: str + 'brand', # Type: str + 'version', # Type: int + 'color', # Type: str + 'density' # Type: str ]) -LocalMaterial = NamedTuple("LocalMaterial", [ - ("GUID", str), - ("id", str), - ("type", str), - ("status", str), - ("base_file", str), - ("setting_version", int), - ("version", int), - ("name", str), - ("brand", str), - ("material", str), - ("color_name", str), - ("color_code", str), - ("description", str), - ("adhesion_info", str), - ("approximate_diameter", str), - ("properties", str), - ("definition", str), - ("compatible", str), +LocalMaterial = namedtuple('LocalMaterial', [ + 'GUID', # Type: str + 'id', # Type: str + 'type', # Type: str + 'status', # Type: str + 'base_file', # Type: str + 'setting_version', # Type: int + 'version', # Type: int + 'name', # Type: str + 'brand', # Type: str + 'material', # Type: str + 'color_name', # Type: str + 'color_code', # Type: str + 'description', # Type: str + 'adhesion_info', # Type: str + 'approximate_diameter', # Type: str + 'properties', # Type: str + 'definition', # Type: str + 'compatible' # Type: str ]) From 8e47d0475632296030f9e2d1f49c6d8797b93982 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 21 Nov 2018 11:12:59 +0100 Subject: [PATCH 0287/1240] Adjust sizes and alignments to the print selector panel. Contributes to CURA-5942. --- plugins/PrepareStage/PrepareMenu.qml | 2 +- resources/qml/ExpandableComponent.qml | 19 +++++++++++++++--- resources/qml/MachineSelector.qml | 27 +++++++++++++------------- resources/themes/cura-light/theme.json | 9 +++++---- 4 files changed, 35 insertions(+), 22 deletions(-) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index ef01625a22..121928e19f 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -46,7 +46,7 @@ Item anchors.centerIn: parent - width: 0.9 * prepareMenu.width + width: Math.round(0.9 * prepareMenu.width) height: parent.height spacing: 0 diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 8ed6dc5674..c1e3bc7985 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -10,6 +10,13 @@ import UM 1.2 as UM Item { id: base + + // Enumeration with the different possible alignments of the popup with respect of the headerItem + enum PopupAlignment { + AlignLeft, + AlignRight + } + // The headerItem holds the QML item that is always displayed. property alias headerItem: headerItemLoader.sourceComponent @@ -21,6 +28,9 @@ Item property color headerBackgroundColor: UM.Theme.getColor("action_button") property color headerHoverColor: UM.Theme.getColor("action_button_hovered") + // Defines the alignment of the popup with respect of the headerItem, by default to the right + property int popupAlignment: ExpandableComponent.PopupAlignment.AlignRight + // How much spacing is needed around the popupItem property alias popupPadding: popup.padding @@ -42,6 +52,7 @@ Item function togglePopup() { +// print(popupAlignment, popupAlignment == PopupAlignment.AlignRight) if(popup.visible) { popup.close() @@ -116,8 +127,8 @@ Item sourceSize.height: height visible: source != "" width: height - height: 0.2 * base.height - color: "black" + height: Math.round(0.2 * base.height) + color: UM.Theme.getColor("text") } MouseArea @@ -140,13 +151,15 @@ Item // Make the popup right aligned with the rest. The 3x padding is due to left, right and padding between // the button & text. - x: -width + collapseButton.width + headerItemLoader.width + 3 * background.padding + x: popupAlignment == ExpandableComponent.PopupAlignment.AlignRight ? -width + collapseButton.width + headerItemLoader.width + 3 * background.padding : 0 padding: UM.Theme.getSize("default_margin").width closePolicy: Popup.CloseOnPressOutsideParent background: Rectangle { color: popupBackgroundColor + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") } } } diff --git a/resources/qml/MachineSelector.qml b/resources/qml/MachineSelector.qml index c9756d93ba..f029914884 100644 --- a/resources/qml/MachineSelector.qml +++ b/resources/qml/MachineSelector.qml @@ -17,6 +17,8 @@ Cura.ExpandableComponent property bool isNetworkPrinter: Cura.MachineManager.activeMachineNetworkKey != "" + popupPadding: 0 + popupAlignment: ExpandableComponent.PopupAlignment.AlignLeft iconSource: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") UM.I18nCatalog @@ -31,6 +33,7 @@ Cura.ExpandableComponent verticalAlignment: Text.AlignVCenter height: parent.height elide: Text.ElideRight + renderType: Text.NativeRendering font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") } @@ -38,26 +41,25 @@ Cura.ExpandableComponent popupItem: Item { id: popup - width: machineSelector.width - 2 * UM.Theme.getSize("default_margin").width - height: 200 + width: UM.Theme.getSize("machine_selector_widget_content").width + height: UM.Theme.getSize("machine_selector_widget_content").height ScrollView { anchors.fill: parent - contentHeight: column.implicitHeight - contentWidth: column.implicitWidth clip: true - ScrollBar.horizontal.policy: ScrollBar.AlwaysOff Column { id: column anchors.fill: parent + Label { - text: catalog.i18nc("@label", "Networked Printers") + text: catalog.i18nc("@label", "Network connected printers") visible: networkedPrintersModel.items.length > 0 height: visible ? contentHeight + 2 * UM.Theme.getSize("default_margin").height : 0 + renderType: Text.NativeRendering font: UM.Theme.getFont("medium_bold") color: UM.Theme.getColor("text") verticalAlignment: Text.AlignVCenter @@ -73,13 +75,12 @@ Cura.ExpandableComponent filter: {"type": "machine", "um_network_key": "*", "hidden": "False"} } - delegate: RoundButton + delegate: Button { text: name width: parent.width - checkable: true - radius: UM.Theme.getSize("default_radius").width + onClicked: { togglePopup() @@ -92,14 +93,14 @@ Cura.ExpandableComponent onActiveMachineNetworkGroupNameChanged: checked = Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["connect_group_name"] } } - } Label { - text: catalog.i18nc("@label", "Virtual Printers") + text: catalog.i18nc("@label", "Preset printers") visible: virtualPrintersModel.items.length > 0 height: visible ? contentHeight + 2 * UM.Theme.getSize("default_margin").height : 0 + renderType: Text.NativeRendering font: UM.Theme.getFont("medium_bold") color: UM.Theme.getColor("text") verticalAlignment: Text.AlignVCenter @@ -115,21 +116,19 @@ Cura.ExpandableComponent filter: {"type": "machine", "um_network_key": null} } - delegate: RoundButton + delegate: Button { text: name width: parent.width checked: Cura.MachineManager.activeMachineId == model.id checkable: true - radius: UM.Theme.getSize("default_radius").width onClicked: { togglePopup() Cura.MachineManager.setActiveMachine(model.id) } } - } } } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index d28611529b..5f52adff14 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -367,11 +367,11 @@ "sizes": { "window_minimum_size": [106, 66], - "main_window_header": [0.0, 4.5], + "main_window_header": [0.0, 4.0], "main_window_header_button": [8, 2.35], "main_window_header_button_icon": [1.2, 1.2], - "stage_menu": [0.0, 4.5], + "stage_menu": [0.0, 4.0], "account_button": [12, 3], @@ -380,14 +380,15 @@ "print_setup_item": [0.0, 2.0], "print_setup_extruder_box": [0.0, 6.0], - "configuration_selector_widget": [35.0, 4.5], + "configuration_selector_widget": [35.0, 4.0], "configuration_selector_mode_tabs": [0.0, 3.0], "action_panel_widget": [25.0, 0.0], "action_panel_information_widget": [20.0, 0.0], "action_panel_button": [15.0, 3.0], - "machine_selector_widget": [16.0, 4.5], + "machine_selector_widget": [20.0, 4.0], + "machine_selector_widget_content": [25.0, 32.0], "views_selector": [0.0, 4.0], From b826a420265051cefd6483eb228e3716182b1380 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 21 Nov 2018 11:37:58 +0100 Subject: [PATCH 0288/1240] Add the RoundedRectangle as background to the ExpandableComponent This way the expandable components can have rounded corners only on one side, thus preventing the need to do add backgrounds to the rows that they are in. CURA-5785 --- plugins/PrepareStage/PrepareMenu.qml | 99 ++++++------- plugins/PreviewStage/PreviewMenu.qml | 191 ++++++++++++-------------- resources/qml/Cura.qml | 1 + resources/qml/ExpandableComponent.qml | 9 +- resources/qml/RoundedRectangle.qml | 39 ++++++ 5 files changed, 181 insertions(+), 158 deletions(-) create mode 100644 resources/qml/RoundedRectangle.qml diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index ef01625a22..22f93aa5f2 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -24,77 +24,66 @@ Item Item { anchors.horizontalCenter: parent.horizontalCenter - width: openFileButtonBackground.width + itemRowBackground.width + width: openFileButtonBackground.width + itemRow.width + UM.Theme.getSize("default_margin").width height: parent.height - Rectangle + RowLayout { - id: itemRowBackground - radius: UM.Theme.getSize("default_radius").width - - color: UM.Theme.getColor("toolbar_background") - - width: itemRow.width + UM.Theme.getSize("default_margin").width - height: parent.height + id: itemRow anchors.left: openFileButtonBackground.right anchors.leftMargin: UM.Theme.getSize("default_margin").width - RowLayout + width: 0.9 * prepareMenu.width + height: parent.height + spacing: 0 + + Cura.MachineSelector { - id: itemRow + id: machineSelection + z: openFileButtonBackground.z - 1 //Ensure that the tooltip of the open file button stays above the item row. + headerCornerSide: 2 // Show corners on the left. + Layout.minimumWidth: UM.Theme.getSize("machine_selector_widget").width + Layout.maximumWidth: UM.Theme.getSize("machine_selector_widget").width + Layout.fillWidth: true + Layout.fillHeight: true + } - anchors.centerIn: parent - - width: 0.9 * prepareMenu.width + // Separator line + Rectangle + { height: parent.height - spacing: 0 + width: UM.Theme.getSize("default_lining").width + color: UM.Theme.getColor("lining") + } - Cura.MachineSelector - { - id: machineSelection - z: openFileButtonBackground.z - 1 //Ensure that the tooltip of the open file button stays above the item row. - Layout.minimumWidth: UM.Theme.getSize("machine_selector_widget").width - Layout.maximumWidth: UM.Theme.getSize("machine_selector_widget").width - Layout.fillWidth: true - Layout.fillHeight: true - } + Cura.QuickConfigurationSelector + { + Layout.fillHeight: true + Layout.fillWidth: true + Layout.preferredWidth: itemRow.width - machineSelection.width - printSetupSelectorItem.width - 2 * UM.Theme.getSize("default_lining").width + } - // Separator line - Rectangle - { - height: parent.height - width: UM.Theme.getSize("default_lining").width - color: UM.Theme.getColor("lining") - } + // Separator line + Rectangle + { + height: parent.height + width: UM.Theme.getSize("default_lining").width + color: UM.Theme.getColor("lining") + } - Cura.QuickConfigurationSelector - { - Layout.fillHeight: true - Layout.fillWidth: true - Layout.preferredWidth: itemRow.width - machineSelection.width - printSetupSelectorItem.width - 2 * UM.Theme.getSize("default_lining").width - } - - // Separator line - Rectangle - { - height: parent.height - width: UM.Theme.getSize("default_lining").width - color: UM.Theme.getColor("lining") - } - - Item - { - id: printSetupSelectorItem - // This is a work around to prevent the printSetupSelector from having to be re-loaded every time - // a stage switch is done. - children: [printSetupSelector] - height: childrenRect.height - width: childrenRect.width - } + Item + { + id: printSetupSelectorItem + // This is a work around to prevent the printSetupSelector from having to be re-loaded every time + // a stage switch is done. + children: [printSetupSelector] + height: childrenRect.height + width: childrenRect.width } } + Rectangle { id: openFileButtonBackground diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index fa1b6aae0f..d6033d6272 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -22,135 +22,122 @@ Item name: "cura" } - Rectangle - { - anchors.fill: stageMenu - anchors.leftMargin: -radius - radius: UM.Theme.getSize("default_radius").width - color: UM.Theme.getColor("toolbar_background") - } - Item + Row { - id: stageMenu + id: stageMenuRow + anchors.centerIn: parent height: parent.height - width: stageMenuRow.width + UM.Theme.getSize("default_margin").width - anchors.horizontalCenter: parent.horizontalCenter - Row + + Cura.ExpandableComponent { - id: stageMenuRow - anchors.centerIn: parent + id: viewSelector + iconSource: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") height: parent.height + headerCornerSide: 2 // Show corners on the left side - Cura.ExpandableComponent + property var viewModel: UM.ViewModel { } + + property var activeView: { - id: viewSelector - iconSource: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") - height: parent.height - - property var viewModel: UM.ViewModel { } - - property var activeView: + for (var i = 0; i < viewModel.rowCount(); i++) { - for (var i = 0; i < viewModel.rowCount(); i++) + if (viewModel.items[i].active) { - if (viewModel.items[i].active) - { - return viewModel.items[i] - } + return viewModel.items[i] } - return null } + return null + } + Component.onCompleted: + { + // Nothing was active, so just return the first one (the list is sorted by priority, so the most + // important one should be returned) + if(activeView == null) + { + UM.Controller.setActiveView(viewModel.getItem(0).id) + } + } + + headerItem: Label + { + text: viewSelector.activeView ? viewSelector.activeView.name : "" + verticalAlignment: Text.AlignVCenter + height: parent.height + elide: Text.ElideRight + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + } + + popupItem: Column + { + id: viewSelectorPopup + width: viewSelector.width - 2 * UM.Theme.getSize("default_margin").width + + // For some reason the height/width of the column gets set to 0 if this is not set... Component.onCompleted: { - // Nothing was active, so just return the first one (the list is sorted by priority, so the most - // important one should be returned) - if(activeView == null) - { - UM.Controller.setActiveView(viewModel.getItem(0).id) - } + height = implicitHeight + width = viewSelector.width - 2 * UM.Theme.getSize("default_margin").width } - headerItem: Label + Repeater { - text: viewSelector.activeView ? viewSelector.activeView.name : "" - verticalAlignment: Text.AlignVCenter - height: parent.height - elide: Text.ElideRight - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - } - - popupItem: Column - { - id: viewSelectorPopup - width: viewSelector.width - 2 * UM.Theme.getSize("default_margin").width - - // For some reason the height/width of the column gets set to 0 if this is not set... - Component.onCompleted: + id: viewsList + model: viewSelector.viewModel + RoundButton { - height = implicitHeight - width = viewSelector.width - 2 * UM.Theme.getSize("default_margin").width - } - - Repeater - { - id: viewsList - model: viewSelector.viewModel - RoundButton + text: name + radius: UM.Theme.getSize("default_radius").width + checkable: true + checked: viewSelector.activeView != null ? viewSelector.activeView.id == id : false + onClicked: { - text: name - radius: UM.Theme.getSize("default_radius").width - checkable: true - checked: viewSelector.activeView != null ? viewSelector.activeView.id == id : false - onClicked: - { - viewSelector.togglePopup() - UM.Controller.setActiveView(id) - } + viewSelector.togglePopup() + UM.Controller.setActiveView(id) } } - } - } - // Separator line - Rectangle - { - height: parent.height - // If there is no viewPanel, we only need a single spacer, so hide this one. - visible: viewPanel.source != "" - width: visible ? UM.Theme.getSize("default_lining").width : 0 - - color: UM.Theme.getColor("lining") } + } - Loader - { - id: viewPanel - height: parent.height - width: childrenRect.width - source: UM.Controller.activeView != null && UM.Controller.activeView.stageMenuComponent != null ? UM.Controller.activeView.stageMenuComponent : "" - } + // Separator line + Rectangle + { + height: parent.height + // If there is no viewPanel, we only need a single spacer, so hide this one. + visible: viewPanel.source != "" + width: visible ? UM.Theme.getSize("default_lining").width : 0 - // Separator line - Rectangle - { - height: parent.height - width: UM.Theme.getSize("default_lining").width - color: UM.Theme.getColor("lining") - } + color: UM.Theme.getColor("lining") + } - Item - { - id: printSetupSelectorItem - // This is a work around to prevent the printSetupSelector from having to be re-loaded every time - // a stage switch is done. - children: [printSetupSelector] - height: childrenRect.height - width: childrenRect.width - } + Loader + { + id: viewPanel + height: parent.height + width: childrenRect.width + source: UM.Controller.activeView != null && UM.Controller.activeView.stageMenuComponent != null ? UM.Controller.activeView.stageMenuComponent : "" + } + + // Separator line + Rectangle + { + height: parent.height + width: UM.Theme.getSize("default_lining").width + color: UM.Theme.getColor("lining") + } + + Item + { + id: printSetupSelectorItem + // This is a work around to prevent the printSetupSelector from having to be re-loaded every time + // a stage switch is done. + children: [printSetupSelector] + height: childrenRect.height + width: childrenRect.width } } } diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index c239dc8d6f..711c05c64c 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -259,6 +259,7 @@ UM.MainWindow onHideTooltip: base.hideTooltip() width: UM.Theme.getSize("print_setup_widget").width height: UM.Theme.getSize("stage_menu").height + headerCornerSide: 4 // Show corners on the right side } } diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 8ed6dc5674..262c6bfd3f 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -40,6 +40,12 @@ Item property alias expandedHighlightColor: expandedHighlight.color + // What should the radius of the header be. This is also influenced by the headerCornerSide + property alias headerRadius: background.radius + + // On what side should the header corners be shown? 1 is down, 2 is left, 3 is up and 4 is right. + property alias headerCornerSide: background.cornerSide + function togglePopup() { if(popup.visible) @@ -71,7 +77,8 @@ Item implicitHeight: 100 * screenScaleFactor implicitWidth: 400 * screenScaleFactor - Rectangle + + RoundedRectangle { id: background property real padding: UM.Theme.getSize("default_margin").width diff --git a/resources/qml/RoundedRectangle.qml b/resources/qml/RoundedRectangle.qml new file mode 100644 index 0000000000..d7ba7d6d13 --- /dev/null +++ b/resources/qml/RoundedRectangle.qml @@ -0,0 +1,39 @@ +import QtQuick 2.0 + +import UM 1.2 as UM + +// The rounded rectangle works mostly like a regular rectangle, but provides the option to have rounded corners on only one side of the rectangle. +Item +{ + // As per the regular rectangle + property color color: "transparent" + + // As per regular rectangle + property int radius: UM.Theme.getSize("default_radius").width + + // On what side should the corners be shown 0 can be used if no radius is needed. + // 1 is down, 2 is left, 3 is up and 4 is right. + property int cornerSide: 0 + + Rectangle + { + id: background + anchors.fill: parent + radius: cornerSide != 0 ? parent.radius : 0 + color: parent.color + } + + // The item that covers 2 of the corners to make them not rounded. + Rectangle + { + visible: cornerSide != 0 + height: cornerSide % 2 ? parent.radius: parent.height + width: cornerSide % 2 ? parent.width : parent.radius + color: parent.color + anchors + { + right: cornerSide == 2 ? parent.right: undefined + bottom: cornerSide == 3 ? parent.bottom: undefined + } + } +} From de650300e20abf2a54baa953076bb71cf2943e1b Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 21 Nov 2018 11:43:20 +0100 Subject: [PATCH 0289/1240] Add the buttons to add printer and manage printers in the bottom of the print selector panel. Contributes to CURA-5942. --- resources/qml/ExpandableComponent.qml | 6 ++-- resources/qml/MachineSelector.qml | 52 +++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index c1e3bc7985..aa9e1467fa 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -52,7 +52,6 @@ Item function togglePopup() { -// print(popupAlignment, popupAlignment == PopupAlignment.AlignRight) if(popup.visible) { popup.close() @@ -149,8 +148,8 @@ Item // Ensure that the popup is located directly below the headerItem y: headerItemLoader.height + 2 * background.padding - // Make the popup right aligned with the rest. The 3x padding is due to left, right and padding between - // the button & text. + // Make the popup aligned with the rest, using the property popupAlignment to decide whether is right or left. + // In case of right alignment, the 3x padding is due to left, right and padding between the button & text. x: popupAlignment == ExpandableComponent.PopupAlignment.AlignRight ? -width + collapseButton.width + headerItemLoader.width + 3 * background.padding : 0 padding: UM.Theme.getSize("default_margin").width closePolicy: Popup.CloseOnPressOutsideParent @@ -160,6 +159,7 @@ Item color: popupBackgroundColor border.width: UM.Theme.getSize("default_lining").width border.color: UM.Theme.getColor("lining") + radius: UM.Theme.getSize("default_radius").width } } } diff --git a/resources/qml/MachineSelector.qml b/resources/qml/MachineSelector.qml index f029914884..3e70fda299 100644 --- a/resources/qml/MachineSelector.qml +++ b/resources/qml/MachineSelector.qml @@ -1,7 +1,7 @@ // Copyright (c) 2018 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.2 +import QtQuick 2.7 import QtQuick.Controls 2.3 import QtQuick.Controls.Styles 1.1 import QtQuick.Layouts 1.1 @@ -46,7 +46,9 @@ Cura.ExpandableComponent ScrollView { - anchors.fill: parent + width: parent.width + anchors.top: parent.top + anchors.bottom: separator.top clip: true Column @@ -80,7 +82,7 @@ Cura.ExpandableComponent text: name width: parent.width checkable: true - + onClicked: { togglePopup() @@ -132,5 +134,49 @@ Cura.ExpandableComponent } } } + + Rectangle + { + id: separator + + anchors.bottom: buttonRow.top + width: parent.width + height: UM.Theme.getSize("default_lining").height + color: UM.Theme.getColor("lining") + } + + Row + { + id: buttonRow + + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + padding: UM.Theme.getSize("default_margin").width + spacing: UM.Theme.getSize("default_margin").width + + Cura.ActionButton + { + leftPadding: UM.Theme.getSize("default_margin").width + rightPadding: UM.Theme.getSize("default_margin").width + text: catalog.i18nc("@button", "Add printer") + color: UM.Theme.getColor("secondary") + hoverColor: UM.Theme.getColor("secondary") + textColor: UM.Theme.getColor("primary") + textHoverColor: UM.Theme.getColor("text") + onClicked: Cura.Actions.addMachine.trigger() + } + + Cura.ActionButton + { + leftPadding: UM.Theme.getSize("default_margin").width + rightPadding: UM.Theme.getSize("default_margin").width + text: catalog.i18nc("@button", "Manage printers") + color: UM.Theme.getColor("secondary") + hoverColor: UM.Theme.getColor("secondary") + textColor: UM.Theme.getColor("primary") + textHoverColor: UM.Theme.getColor("text") + onClicked: Cura.Actions.configureMachines.trigger() + } + } } } From 64bbab9d4024df8d1be956040d81a69551922308 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 21 Nov 2018 11:50:00 +0100 Subject: [PATCH 0290/1240] Use the group name to show in the printer list if it's a network connected printer. Contributes to CURA-5942. --- resources/qml/MachineSelector.qml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/resources/qml/MachineSelector.qml b/resources/qml/MachineSelector.qml index 3e70fda299..417c5722b4 100644 --- a/resources/qml/MachineSelector.qml +++ b/resources/qml/MachineSelector.qml @@ -79,8 +79,9 @@ Cura.ExpandableComponent delegate: Button { - text: name + text: model.metadata["connect_group_name"] width: parent.width + checked: Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["connect_group_name"] checkable: true onClicked: @@ -120,7 +121,7 @@ Cura.ExpandableComponent delegate: Button { - text: name + text: model.name width: parent.width checked: Cura.MachineManager.activeMachineId == model.id checkable: true From eef6ad662d816ac8266faaa391342accab36f07d Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 21 Nov 2018 11:50:39 +0100 Subject: [PATCH 0291/1240] Added enum for the roundedCorner property This makes it a whole lot easier to read what is being set. CURA-5785 --- plugins/PrepareStage/PrepareMenu.qml | 2 +- plugins/PreviewStage/PreviewMenu.qml | 2 +- resources/qml/Cura.qml | 2 +- resources/qml/RoundedRectangle.qml | 24 +++++++++++++++++------- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index 22f93aa5f2..bf2a0e1283 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -42,7 +42,7 @@ Item { id: machineSelection z: openFileButtonBackground.z - 1 //Ensure that the tooltip of the open file button stays above the item row. - headerCornerSide: 2 // Show corners on the left. + headerCornerSide: Cura.RoundedRectangle.Direction.Left Layout.minimumWidth: UM.Theme.getSize("machine_selector_widget").width Layout.maximumWidth: UM.Theme.getSize("machine_selector_widget").width Layout.fillWidth: true diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index d6033d6272..29632e5f00 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -34,7 +34,7 @@ Item id: viewSelector iconSource: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") height: parent.height - headerCornerSide: 2 // Show corners on the left side + headerCornerSide: Cura.RoundedRectangle.Direction.Left property var viewModel: UM.ViewModel { } diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 711c05c64c..2814bb9eb2 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -259,7 +259,7 @@ UM.MainWindow onHideTooltip: base.hideTooltip() width: UM.Theme.getSize("print_setup_widget").width height: UM.Theme.getSize("stage_menu").height - headerCornerSide: 4 // Show corners on the right side + headerCornerSide: RoundedRectangle.Direction.Right } } diff --git a/resources/qml/RoundedRectangle.qml b/resources/qml/RoundedRectangle.qml index d7ba7d6d13..9ad2230be5 100644 --- a/resources/qml/RoundedRectangle.qml +++ b/resources/qml/RoundedRectangle.qml @@ -1,4 +1,4 @@ -import QtQuick 2.0 +import QtQuick 2.7 import UM 1.2 as UM @@ -11,29 +11,39 @@ Item // As per regular rectangle property int radius: UM.Theme.getSize("default_radius").width - // On what side should the corners be shown 0 can be used if no radius is needed. + // On what side should the corners be shown 5 can be used if no radius is needed. // 1 is down, 2 is left, 3 is up and 4 is right. - property int cornerSide: 0 + property int cornerSide: RoundedRectangle.Direction.None + + enum Direction + { + None = 0, + Down = 1, + Left = 2, + Up = 3, + Right = 4, + All = 5 + } Rectangle { id: background anchors.fill: parent - radius: cornerSide != 0 ? parent.radius : 0 + radius: cornerSide != RoundedRectangle.Direction.None ? parent.radius : 0 color: parent.color } // The item that covers 2 of the corners to make them not rounded. Rectangle { - visible: cornerSide != 0 + visible: cornerSide != RoundedRectangle.Direction.None && cornerSide != RoundedRectangle.Direction.All height: cornerSide % 2 ? parent.radius: parent.height width: cornerSide % 2 ? parent.width : parent.radius color: parent.color anchors { - right: cornerSide == 2 ? parent.right: undefined - bottom: cornerSide == 3 ? parent.bottom: undefined + right: cornerSide == RoundedRectangle.Direction.Left ? parent.right: undefined + bottom: cornerSide == RoundedRectangle.Direction.Up ? parent.bottom: undefined } } } From bfd8e666570d636293bae9dbda0c2134de992065 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Wed, 21 Nov 2018 12:50:18 +0100 Subject: [PATCH 0292/1240] Removed margins and unused arches CURA-5941 --- resources/qml/PrintSetupSelector.qml | 46 +++++++++++++--------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/resources/qml/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector.qml index ed09238b51..61cc06b426 100644 --- a/resources/qml/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector.qml @@ -116,7 +116,6 @@ Cura.ExpandableComponent } } - Cura.ExtrudersModel { id: extrudersModel @@ -124,7 +123,7 @@ Cura.ExpandableComponent popupItem: Rectangle { - property var total_height: popupItemHeader.height + popupItemContent.height + 10 + separator_footer.height + property var total_height: popupItemHeader.height + popupItemContent.height + footerControll.height + UM.Theme.getSize("print_setup_selector_margin").height * 2 id: popupItemWrapper height: total_height @@ -176,7 +175,7 @@ Cura.ExpandableComponent anchors { top: popupItemHeader.bottom - topMargin: 10 + topMargin: UM.Theme.getSize("print_setup_selector_margin").height right: parent.right left: parent.left leftMargin: 5 @@ -213,7 +212,6 @@ Cura.ExpandableComponent width: parent.height height: parent.height } - } background: Rectangle @@ -232,11 +230,11 @@ Cura.ExpandableComponent // overlap bottom border Rectangle { - width: parent.width - 4 - height: 4 + width: parent.width - UM.Theme.getSize("default_lining").width * 4 + height: UM.Theme.getSize("default_lining").width * 4 anchors.bottom: parent.bottom - anchors.bottomMargin: -2 - anchors.leftMargin: 2 + anchors.bottomMargin: - (UM.Theme.getSize("default_lining").width * 2) + anchors.leftMargin: UM.Theme.getSize("default_lining").width * 2 anchors.left: parent.left } @@ -249,18 +247,17 @@ Cura.ExpandableComponent { id: sidebarContents anchors.top: tabBar.bottom + anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: parent.right height: UM.Theme.getSize("print_setup_widget").height - border.width: UM.Theme.getSize("default_lining").width * 2 border.color: UM.Theme.getColor("action_button_border") SidebarSimple { anchors.topMargin: UM.Theme.getSize("print_setup_content_top_margin").height - id: sidebar_simple_id anchors.fill: parent visible: currentModeIndex != 1 onShowTooltip: base.showTooltip(item, location, text) @@ -270,25 +267,22 @@ Cura.ExpandableComponent SidebarAdvanced { anchors.topMargin: UM.Theme.getSize("print_setup_content_top_margin").height - anchors.bottomMargin: 2 //does not overlap bottom border + anchors.bottomMargin: 2 //don't overlap bottom border anchors.fill: parent visible: currentModeIndex == 1 onShowTooltip: base.showTooltip(item, location, text) onHideTooltip: base.hideTooltip() } - } } - Item { - id: separator_footer + id: footerControll anchors.top: popupItemContent.bottom - anchors.topMargin: 10 + anchors.topMargin: UM.Theme.getSize("print_setup_selector_margin").height * 2 width: parent.width - height: settingControlButton.height + 4 - + height: settingControlButton.height + UM.Theme.getSize("default_lining").height * 4 Rectangle { width: parent.width @@ -299,7 +293,6 @@ Cura.ExpandableComponent Cura.ActionButton { id: settingControlButton - leftPadding: UM.Theme.getSize("default_margin").width rightPadding: UM.Theme.getSize("default_margin").width height: UM.Theme.getSize("action_panel_button").height @@ -315,10 +308,10 @@ Cura.ExpandableComponent anchors { top: parent.top - topMargin: 10 - bottomMargin: 10 + topMargin: UM.Theme.getSize("print_setup_selector_margin").height * 2 + bottomMargin: UM.Theme.getSize("print_setup_selector_margin").height * 2 right: parent.right - rightMargin: 5 + rightMargin: UM.Theme.getSize("print_setup_selector_margin").height } onClicked: currentModeIndex = 1 @@ -334,8 +327,6 @@ Cura.ExpandableComponent textHoverColor: UM.Theme.getColor("text") iconSource: UM.Theme.getIcon("arrow_left") width: UM.Theme.getSize("print_setup_action_button").width - - fixedWidthMode: true visible: currentModeIndex == 1 anchors @@ -347,7 +338,14 @@ Cura.ExpandableComponent leftMargin: UM.Theme.getSize("print_setup_selector_margin").height } - onClicked: currentModeIndex = 0 +// onClicked: currentModeIndex = 0 + MouseArea { + anchors.fill: parent + onClicked: { + currentModeIndex = 0 + + } + } } } Component.onCompleted: From bd636e61a0577b8f5f7eac160fa0af05757240c6 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 21 Nov 2018 13:17:36 +0100 Subject: [PATCH 0293/1240] Minor changes suggested in review CURA-5785 --- plugins/PrepareStage/PrepareMenu.qml | 2 +- plugins/PreviewStage/PreviewMenu.qml | 3 ++- plugins/SimulationView/SimulationViewMenuComponent.qml | 7 +++++-- resources/qml/ExtruderIcon.qml | 1 + resources/qml/MachineSelector.qml | 3 +++ resources/qml/PrintSetupSelector.qml | 1 + 6 files changed, 13 insertions(+), 4 deletions(-) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index bf2a0e1283..963908cea6 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -34,7 +34,7 @@ Item anchors.left: openFileButtonBackground.right anchors.leftMargin: UM.Theme.getSize("default_margin").width - width: 0.9 * prepareMenu.width + width: Math.round(0.9 * prepareMenu.width) height: parent.height spacing: 0 diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index 29632e5f00..656cf185d5 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -54,7 +54,7 @@ Item { // Nothing was active, so just return the first one (the list is sorted by priority, so the most // important one should be returned) - if(activeView == null) + if (activeView == null) { UM.Controller.setActiveView(viewModel.getItem(0).id) } @@ -68,6 +68,7 @@ Item elide: Text.ElideRight font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") + renderType: Text.NativeRendering } popupItem: Column diff --git a/plugins/SimulationView/SimulationViewMenuComponent.qml b/plugins/SimulationView/SimulationViewMenuComponent.qml index caf47508e3..110cd1c109 100644 --- a/plugins/SimulationView/SimulationViewMenuComponent.qml +++ b/plugins/SimulationView/SimulationViewMenuComponent.qml @@ -132,6 +132,7 @@ Cura.ExpandableComponent visible: UM.SimulationView.compatibilityMode height: UM.Theme.getSize("layerview_row").height width: parent.width + renderType: Text.NativeRendering } Item // Spacer @@ -188,7 +189,7 @@ Cura.ExpandableComponent leftMargin: UM.Theme.getSize("checkbox").width + Math.round(UM.Theme.getSize("default_margin").width / 2) rightMargin: UM.Theme.getSize("default_margin").width * 2 } - + renderType: Text.NativeRendering } } } @@ -254,6 +255,7 @@ Cura.ExpandableComponent text: label font: UM.Theme.getFont("default") elide: Text.ElideRight + renderType: Text.NativeRendering color: UM.Theme.getColor("setting_control_text") anchors.verticalCenter: parent.verticalCenter anchors.left: legendModelCheckBox.left @@ -310,7 +312,7 @@ Cura.ExpandableComponent width: parent.width color: UM.Theme.getColor("setting_control_text") font: UM.Theme.getFont("default") - + renderType: Text.NativeRendering Rectangle { anchors.verticalCenter: parent.verticalCenter @@ -357,6 +359,7 @@ Cura.ExpandableComponent anchors.left: parent.left color: UM.Theme.getColor("setting_control_text") font: UM.Theme.getFont("default") + renderType: Text.NativeRendering } Label diff --git a/resources/qml/ExtruderIcon.qml b/resources/qml/ExtruderIcon.qml index 87e210e75a..79106bdd3a 100644 --- a/resources/qml/ExtruderIcon.qml +++ b/resources/qml/ExtruderIcon.qml @@ -53,6 +53,7 @@ Item width: contentWidth height: contentHeight visible: extruderEnabled + renderType: Text.NativeRendering } UM.RecolorImage diff --git a/resources/qml/MachineSelector.qml b/resources/qml/MachineSelector.qml index c9756d93ba..750ac7f620 100644 --- a/resources/qml/MachineSelector.qml +++ b/resources/qml/MachineSelector.qml @@ -33,6 +33,7 @@ Cura.ExpandableComponent elide: Text.ElideRight font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") + renderType: Text.NativeRendering } popupItem: Item @@ -60,6 +61,7 @@ Cura.ExpandableComponent height: visible ? contentHeight + 2 * UM.Theme.getSize("default_margin").height : 0 font: UM.Theme.getFont("medium_bold") color: UM.Theme.getColor("text") + renderType: Text.NativeRendering verticalAlignment: Text.AlignVCenter } @@ -103,6 +105,7 @@ Cura.ExpandableComponent font: UM.Theme.getFont("medium_bold") color: UM.Theme.getColor("text") verticalAlignment: Text.AlignVCenter + renderType: Text.NativeRendering } Repeater diff --git a/resources/qml/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector.qml index b843244147..2ecdc9e546 100644 --- a/resources/qml/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector.qml @@ -182,6 +182,7 @@ Cura.ExpandableComponent verticalAlignment: Text.AlignVCenter renderType: Text.NativeRendering elide: Text.ElideRight + color: { if(control.pressed) From c7dbaa3a001fbca621e94134e15ce0d0c5ae4fae Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 21 Nov 2018 13:22:03 +0100 Subject: [PATCH 0294/1240] Add rendertype for labels in quickConfigurationSelector CURA-5785 --- .../qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml index d428a05463..ef7a425a87 100644 --- a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml +++ b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml @@ -152,6 +152,7 @@ Cura.ExpandableComponent color: UM.Theme.getColor("text") height: parent.height width: tabControl.textWidth + renderType: Text.NativeRendering } OldControls.CheckBox @@ -174,6 +175,7 @@ Cura.ExpandableComponent color: UM.Theme.getColor("text") height: parent.height width: tabControl.textWidth + renderType: Text.NativeRendering } OldControls.ToolButton @@ -217,6 +219,7 @@ Cura.ExpandableComponent color: UM.Theme.getColor("text") height: parent.height width: tabControl.textWidth + renderType: Text.NativeRendering } OldControls.ToolButton From 406fac9e605578422417cdbb7a666343a4260129 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 21 Nov 2018 13:38:08 +0100 Subject: [PATCH 0295/1240] Remove duplicate entries of renderType. Contributes to CURA-5942. --- resources/qml/MachineSelector.qml | 3 --- 1 file changed, 3 deletions(-) diff --git a/resources/qml/MachineSelector.qml b/resources/qml/MachineSelector.qml index 1796df5678..417c5722b4 100644 --- a/resources/qml/MachineSelector.qml +++ b/resources/qml/MachineSelector.qml @@ -36,7 +36,6 @@ Cura.ExpandableComponent renderType: Text.NativeRendering font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") - renderType: Text.NativeRendering } popupItem: Item @@ -65,7 +64,6 @@ Cura.ExpandableComponent renderType: Text.NativeRendering font: UM.Theme.getFont("medium_bold") color: UM.Theme.getColor("text") - renderType: Text.NativeRendering verticalAlignment: Text.AlignVCenter } @@ -109,7 +107,6 @@ Cura.ExpandableComponent font: UM.Theme.getFont("medium_bold") color: UM.Theme.getColor("text") verticalAlignment: Text.AlignVCenter - renderType: Text.NativeRendering } Repeater From a9672458fd31ffb7c64273b88a779a9f262b19d6 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 21 Nov 2018 13:46:50 +0100 Subject: [PATCH 0296/1240] Update extruderIcon to be more in line with the design CURA-5785 --- resources/qml/ExtruderIcon.qml | 10 ++++++---- .../ConfigurationMenu/QuickConfigurationSelector.qml | 3 +-- resources/themes/cura-light/theme.json | 9 +++++++-- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/resources/qml/ExtruderIcon.qml b/resources/qml/ExtruderIcon.qml index 79106bdd3a..1e51835d60 100644 --- a/resources/qml/ExtruderIcon.qml +++ b/resources/qml/ExtruderIcon.qml @@ -9,8 +9,8 @@ Item { id: extruderIconItem - implicitWidth: UM.Theme.getSize("button").width - implicitHeight: implicitWidth + implicitWidth: UM.Theme.getSize("extruder_icon").width + implicitHeight: UM.Theme.getSize("extruder_icon").height property bool checked: true property color materialColor @@ -22,7 +22,7 @@ Item anchors.fill: parent sourceSize.width: parent.width - sourceSize.height: parent.width + sourceSize.height: parent.height source: UM.Theme.getIcon("extruder_button") color: extruderEnabled ? materialColor: "gray" } @@ -49,11 +49,13 @@ Item id: extruderNumberText anchors.centerIn: parent text: index + 1 - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("extruder_icon") width: contentWidth height: contentHeight visible: extruderEnabled renderType: Text.NativeRendering + horizontalAlignment: Text.alignHCenter + verticalAlignment: Text.alignVCenter } UM.RecolorImage diff --git a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml index ef7a425a87..eb6800cb36 100644 --- a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml +++ b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml @@ -50,8 +50,7 @@ Cura.ExpandableComponent id: extruderIcon materialColor: model.color extruderEnabled: model.enabled - height: parent.height - width: height + anchors.verticalCenter: parent.verticalCenter } // Label for the brand of the material diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index d28611529b..b09370ccdb 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -64,6 +64,12 @@ "size": 1.15, "weight": 50, "family": "Noto Sans" + }, + "extruder_icon": + { + "size": 0.7, + "weight": 50, + "family": "Noto Sans" } }, @@ -406,8 +412,7 @@ "thin_margin": [0.71, 0.71], "narrow_margin": [0.5, 0.5], - "extruder_button_material_margin": [0.70, 0.9], - "extruder_button_material": [0.75, 0.75], + "extruder_icon": [1.8, 1.8], "simple_mode_infill_caption": [0.0, 5.0], "simple_mode_infill_height": [0.0, 8.0], From a1f3ebc25b97f555362a1dead86d7cc6081f718a Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 21 Nov 2018 13:58:21 +0100 Subject: [PATCH 0297/1240] Fix typo. Contributes to CURA-5785. --- resources/qml/ExtruderIcon.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/ExtruderIcon.qml b/resources/qml/ExtruderIcon.qml index 1e51835d60..c103ee245c 100644 --- a/resources/qml/ExtruderIcon.qml +++ b/resources/qml/ExtruderIcon.qml @@ -54,8 +54,8 @@ Item height: contentHeight visible: extruderEnabled renderType: Text.NativeRendering - horizontalAlignment: Text.alignHCenter - verticalAlignment: Text.alignVCenter + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter } UM.RecolorImage From eb056ee17bbc7d843a83ce108e95d51bc6f499ba Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 21 Nov 2018 16:06:26 +0100 Subject: [PATCH 0298/1240] Fix display of custom configuration Since it had no width, everything was resized to 0. Contributes to issue CURA-5876. --- resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index 953b6a48f0..aa4cf3624b 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -10,6 +10,8 @@ import UM 1.3 as UM Item { + width: parent.width + Label { id: header From a052b8ec69e014c0ebce92ffb4fcb0067d85831c Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 21 Nov 2018 16:08:29 +0100 Subject: [PATCH 0299/1240] Use Theme.getIcon instead of Theme.icons This is better for performance, according to the deprecation warning I got. Contributes to issue CURA-5876. --- resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index b38384ef8a..111f42483e 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -172,7 +172,7 @@ Cura.ExpandableComponent textColor: UM.Theme.getColor("primary") textHoverColor: UM.Theme.getColor("text") - iconSource: UM.Theme.icons.arrow_right + iconSource: UM.Theme.getIcon("arrow_right") iconOnRightSide: true onClicked: popupItem.configuration_method = "custom" @@ -195,7 +195,7 @@ Cura.ExpandableComponent textColor: UM.Theme.getColor("primary") textHoverColor: UM.Theme.getColor("text") - iconSource: UM.Theme.icons.arrow_left + iconSource: UM.Theme.getIcon("arrow_left") onClicked: popupItem.configuration_method = "auto" } From a826dfb156c46acfbe26533eab594aa27305040d Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 21 Nov 2018 16:12:00 +0100 Subject: [PATCH 0300/1240] Fix binding loop in height of view selection bar Since these were part of the childrenRect of the bar, and attached to the bottom of the bar, the childrenRect depended on the position of the children and that depends on the childrenRect again. So just attach it to the top, it's logical. Contributes to issue CURA-5876. --- resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index 111f42483e..8f31d80810 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -164,7 +164,7 @@ Cura.ExpandableComponent anchors { right: parent.right - bottom: parent.bottom + top: parent.top } color: UM.Theme.getColor("secondary") @@ -187,7 +187,7 @@ Cura.ExpandableComponent anchors { left: parent.left - bottom: parent.bottom + top: parent.top } color: UM.Theme.getColor("secondary") From fe7d1825d4f124788e9fc0ec19db6528fae388c3 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 21 Nov 2018 16:54:57 +0100 Subject: [PATCH 0301/1240] Add styling to the buttons in the printer list. Contributes to CURA-5942. --- plugins/PrepareStage/PrepareMenu.qml | 2 +- resources/qml/MachineSelector.qml | 34 +++++++++++++++---- .../cura-light/images/header_pattern.svg | 1 + resources/themes/cura-light/theme.json | 1 + 4 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 resources/themes/cura-light/images/header_pattern.svg diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index fd296144c7..eed1da0ad8 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -91,7 +91,7 @@ Item radius: UM.Theme.getSize("default_radius").width color: UM.Theme.getColor("toolbar_background") - + Button { id: openFileButton diff --git a/resources/qml/MachineSelector.qml b/resources/qml/MachineSelector.qml index 417c5722b4..a6339e2621 100644 --- a/resources/qml/MachineSelector.qml +++ b/resources/qml/MachineSelector.qml @@ -46,6 +46,7 @@ Cura.ExpandableComponent ScrollView { + id: scroll width: parent.width anchors.top: parent.top anchors.bottom: separator.top @@ -54,16 +55,20 @@ Cura.ExpandableComponent Column { id: column - anchors.fill: parent + + // Can't use parent.width since the parent is the flickable component and not the ScrollView + width: scroll.width - 2 * UM.Theme.getSize("default_lining").width + x: UM.Theme.getSize("default_lining").width Label { text: catalog.i18nc("@label", "Network connected printers") visible: networkedPrintersModel.items.length > 0 + leftPadding: UM.Theme.getSize("default_margin").width height: visible ? contentHeight + 2 * UM.Theme.getSize("default_margin").height : 0 renderType: Text.NativeRendering - font: UM.Theme.getFont("medium_bold") - color: UM.Theme.getColor("text") + font: UM.Theme.getFont("medium") + color: UM.Theme.getColor("text_medium") verticalAlignment: Text.AlignVCenter } @@ -77,13 +82,20 @@ Cura.ExpandableComponent filter: {"type": "machine", "um_network_key": "*", "hidden": "False"} } - delegate: Button + delegate: Cura.ActionButton { text: model.metadata["connect_group_name"] width: parent.width + height: UM.Theme.getSize("action_button").height checked: Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["connect_group_name"] checkable: true + color: "transparent" + hoverColor: UM.Theme.getColor("action_button_hovered") + textColor: UM.Theme.getColor("text") + textHoverColor: UM.Theme.getColor("text") + outlineColor: checked ? UM.Theme.getColor("primary") : "transparent" + onClicked: { togglePopup() @@ -102,10 +114,11 @@ Cura.ExpandableComponent { text: catalog.i18nc("@label", "Preset printers") visible: virtualPrintersModel.items.length > 0 + leftPadding: UM.Theme.getSize("default_margin").width height: visible ? contentHeight + 2 * UM.Theme.getSize("default_margin").height : 0 renderType: Text.NativeRendering - font: UM.Theme.getFont("medium_bold") - color: UM.Theme.getColor("text") + font: UM.Theme.getFont("medium") + color: UM.Theme.getColor("text_medium") verticalAlignment: Text.AlignVCenter } @@ -119,13 +132,20 @@ Cura.ExpandableComponent filter: {"type": "machine", "um_network_key": null} } - delegate: Button + delegate: Cura.ActionButton { text: model.name width: parent.width + height: UM.Theme.getSize("action_button").height checked: Cura.MachineManager.activeMachineId == model.id checkable: true + color: "transparent" + hoverColor: UM.Theme.getColor("action_button_hovered") + textColor: UM.Theme.getColor("text") + textHoverColor: UM.Theme.getColor("text") + outlineColor: checked ? UM.Theme.getColor("primary") : "transparent" + onClicked: { togglePopup() diff --git a/resources/themes/cura-light/images/header_pattern.svg b/resources/themes/cura-light/images/header_pattern.svg new file mode 100644 index 0000000000..2a9de2f3e9 --- /dev/null +++ b/resources/themes/cura-light/images/header_pattern.svg @@ -0,0 +1 @@ +Pattern \ No newline at end of file diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 5f52adff14..4e6fe0776a 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -521,6 +521,7 @@ "avatar_image": [6.8, 6.8], + "action_button": [15.0, 3.0], "action_button_radius": [0.15, 0.15], "monitor_config_override_box": [1.0, 14.0], From f866390c7bb0cc9ac4af65d78a30c6b7995f6201 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 21 Nov 2018 16:56:25 +0100 Subject: [PATCH 0302/1240] Fix an issue in the action panel. When the panel is created while the user is in the preview stage, the row has no width. Contributes to CURA-5786. --- resources/qml/ActionPanel/OutputProcessWidget.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml index f4e014b1ec..3c4386f079 100644 --- a/resources/qml/ActionPanel/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -99,6 +99,7 @@ Column { id: buttonRow spacing: UM.Theme.getSize("default_margin").width + width: parent.width Cura.ActionButton { From a9f0402f636b652585c23c5ea7299f0256ba195d Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 22 Nov 2018 09:37:36 +0100 Subject: [PATCH 0303/1240] Made size of viewselector themable CURA-5785 --- plugins/PreviewStage/PreviewMenu.qml | 1 + resources/themes/cura-light/theme.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index 656cf185d5..d660db549b 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -34,6 +34,7 @@ Item id: viewSelector iconSource: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") height: parent.height + width: UM.Theme.getSize("views_selector").width headerCornerSide: Cura.RoundedRectangle.Direction.Left property var viewModel: UM.ViewModel { } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index b09370ccdb..0df1c8eb31 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -395,7 +395,7 @@ "machine_selector_widget": [16.0, 4.5], - "views_selector": [0.0, 4.0], + "views_selector": [16.0, 4.5], "default_radius": [0.25, 0.25], From 7e3f86f0913b7432c46b20507e33be57b7a1d514 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Thu, 22 Nov 2018 09:37:47 +0100 Subject: [PATCH 0304/1240] Moved some of the mocks to class level because they are used in every test method --- .../tests/TestSendMaterialJob.py | 35 ++++++------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py index ff896683e1..548704fd33 100644 --- a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py @@ -17,6 +17,8 @@ from plugins.UM3NetworkPrinting.src.SendMaterialJob import SendMaterialJob lambda _: MimeType(name = "application/x-ultimaker-material-profile", comment = "Ultimaker Material Profile", suffixes = ["xml.fdm_material"])) @patch("UM.Resources.Resources.getAllResourcesOfType", lambda _: ["/materials/generic_pla_white.xml.fdm_material"]) +@patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") +@patch("PyQt5.QtNetwork.QNetworkReply") class TestSendMaterialJob(TestCase): _LOCAL_MATERIAL_WHITE = {"type": "material", "status": "unknown", "id": "generic_pla_white", "base_file": "generic_pla_white", "setting_version": "5", "name": "White PLA", @@ -52,16 +54,13 @@ class TestSendMaterialJob(TestCase): "density": 1.00 } - @patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") - def test_run(self, device_mock): + def test_run(self, device_mock, reply_mock): job = SendMaterialJob(device_mock) job.run() # We expect the materials endpoint to be called when the job runs. device_mock.get.assert_called_with("materials/", on_finished = job._onGetRemoteMaterials) - @patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") - @patch("PyQt5.QtNetwork.QNetworkReply") def test__onGetRemoteMaterials_withFailedRequest(self, reply_mock, device_mock): reply_mock.attribute.return_value = 404 job = SendMaterialJob(device_mock) @@ -71,8 +70,6 @@ class TestSendMaterialJob(TestCase): self.assertEqual([call.attribute(0), call.errorString()], reply_mock.method_calls) self.assertEqual(0, device_mock.createFormPart.call_count) - @patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") - @patch("PyQt5.QtNetwork.QNetworkReply") def test__onGetRemoteMaterials_withBadJsonAnswer(self, reply_mock, device_mock): reply_mock.attribute.return_value = 200 reply_mock.readAll.return_value = QByteArray(b"Six sick hicks nick six slick bricks with picks and sticks.") @@ -84,8 +81,6 @@ class TestSendMaterialJob(TestCase): self.assertEqual([call.attribute(0), call.readAll()], reply_mock.method_calls) self.assertEqual(0, device_mock.createFormPart.call_count) - @patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") - @patch("PyQt5.QtNetwork.QNetworkReply") def test__onGetRemoteMaterials_withMissingGuidInRemoteMaterial(self, reply_mock, device_mock): reply_mock.attribute.return_value = 200 remote_material_without_guid = self._REMOTE_MATERIAL_WHITE.copy() @@ -101,10 +96,8 @@ class TestSendMaterialJob(TestCase): @patch("cura.Settings.CuraContainerRegistry") @patch("cura.CuraApplication") - @patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") - @patch("PyQt5.QtNetwork.QNetworkReply") - def test__onGetRemoteMaterials_withInvalidVersionInLocalMaterial(self, reply_mock, device_mock, application_mock, - container_registry_mock): + def test__onGetRemoteMaterials_withInvalidVersionInLocalMaterial(self, application_mock, container_registry_mock, + reply_mock, device_mock): reply_mock.attribute.return_value = 200 reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii")) @@ -125,10 +118,8 @@ class TestSendMaterialJob(TestCase): @patch("cura.Settings.CuraContainerRegistry") @patch("cura.CuraApplication") - @patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") - @patch("PyQt5.QtNetwork.QNetworkReply") - def test__onGetRemoteMaterials_withNoUpdate(self, reply_mock, device_mock, application_mock, - container_registry_mock): + def test__onGetRemoteMaterials_withNoUpdate(self, application_mock, container_registry_mock, reply_mock, + device_mock): application_mock.getContainerRegistry.return_value = container_registry_mock device_mock.createFormPart.return_value = "_xXx_" @@ -150,10 +141,8 @@ class TestSendMaterialJob(TestCase): @patch("cura.Settings.CuraContainerRegistry") @patch("cura.CuraApplication") - @patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") - @patch("PyQt5.QtNetwork.QNetworkReply") - def test__onGetRemoteMaterials_withUpdatedMaterial(self, reply_mock, device_mock, application_mock, - container_registry_mock): + def test__onGetRemoteMaterials_withUpdatedMaterial(self, application_mock, container_registry_mock, reply_mock, + device_mock): application_mock.getContainerRegistry.return_value = container_registry_mock device_mock.createFormPart.return_value = "_xXx_" @@ -181,10 +170,8 @@ class TestSendMaterialJob(TestCase): @patch("cura.Settings.CuraContainerRegistry") @patch("cura.CuraApplication") - @patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") - @patch("PyQt5.QtNetwork.QNetworkReply") - def test__onGetRemoteMaterials_withNewMaterial(self, reply_mock, device_mock, application_mock, - container_registry_mock): + def test__onGetRemoteMaterials_withNewMaterial(self, application_mock, container_registry_mock, reply_mock, + device_mock): application_mock.getContainerRegistry.return_value = container_registry_mock device_mock.createFormPart.return_value = "_xXx_" From 352427e4601204851daba848599153ebab3f8c41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Thu, 22 Nov 2018 10:01:15 +0100 Subject: [PATCH 0305/1240] Moved exception handling closer to the cause of error --- plugins/UM3NetworkPrinting/src/SendMaterialJob.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py index ee8dd8042d..72269040e7 100644 --- a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py @@ -46,9 +46,8 @@ class SendMaterialJob(Job): # Collect materials from the printer's reply and send the missing ones if needed. try: remote_materials_by_guid = self._parseReply(reply) - self._sendMissingMaterials(remote_materials_by_guid) - except json.JSONDecodeError: - Logger.logException("w", "Error parsing materials from printer") + if remote_materials_by_guid: + self._sendMissingMaterials(remote_materials_by_guid) except TypeError: Logger.logException("w", "Error parsing materials from printer") @@ -153,12 +152,14 @@ class SendMaterialJob(Job): # Parses the reply to a "/materials" request to the printer # # \return a dictionary of ClusterMaterial objects by GUID - # \throw json.JSONDecodeError Raised when the reply does not contain a valid json string # \throw KeyError Raised when on of the materials does not include a valid guid @classmethod def _parseReply(cls, reply: QNetworkReply) -> Dict[str, ClusterMaterial]: - remote_materials = json.loads(reply.readAll().data().decode("utf-8")) - return {material["guid"]: ClusterMaterial(**material) for material in remote_materials} + try: + remote_materials = json.loads(reply.readAll().data().decode("utf-8")) + return {material["guid"]: ClusterMaterial(**material) for material in remote_materials} + except json.JSONDecodeError: + Logger.logException("w", "Error parsing materials from printer") ## Retrieves a list of local materials # From b890e40e81f62da2eade839ad48d7168cdcde7ff Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 22 Nov 2018 10:54:25 +0100 Subject: [PATCH 0306/1240] Close the popup panel when the user clicks in some of the buttons in the printer selector. Contributes to CURA-5942. --- resources/qml/ExtruderIcon.qml | 1 + resources/qml/MachineSelector.qml | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/resources/qml/ExtruderIcon.qml b/resources/qml/ExtruderIcon.qml index c103ee245c..8f312adb85 100644 --- a/resources/qml/ExtruderIcon.qml +++ b/resources/qml/ExtruderIcon.qml @@ -16,6 +16,7 @@ Item property color materialColor property alias textColor: extruderNumberText.color property bool extruderEnabled: true + UM.RecolorImage { id: mainIcon diff --git a/resources/qml/MachineSelector.qml b/resources/qml/MachineSelector.qml index a6339e2621..14e1ebb48e 100644 --- a/resources/qml/MachineSelector.qml +++ b/resources/qml/MachineSelector.qml @@ -184,7 +184,11 @@ Cura.ExpandableComponent hoverColor: UM.Theme.getColor("secondary") textColor: UM.Theme.getColor("primary") textHoverColor: UM.Theme.getColor("text") - onClicked: Cura.Actions.addMachine.trigger() + onClicked: + { + togglePopup() + Cura.Actions.addMachine.trigger() + } } Cura.ActionButton @@ -196,7 +200,11 @@ Cura.ExpandableComponent hoverColor: UM.Theme.getColor("secondary") textColor: UM.Theme.getColor("primary") textHoverColor: UM.Theme.getColor("text") - onClicked: Cura.Actions.configureMachines.trigger() + onClicked: + { + togglePopup() + Cura.Actions.configureMachines.trigger() + } } } } From d4e4e507411dbc6e63c26737c0ae6931847ceba3 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 22 Nov 2018 10:55:53 +0100 Subject: [PATCH 0307/1240] Fix typo --- resources/definitions/fdmextruder.def.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/definitions/fdmextruder.def.json b/resources/definitions/fdmextruder.def.json index cb49b1e128..0af1e68075 100644 --- a/resources/definitions/fdmextruder.def.json +++ b/resources/definitions/fdmextruder.def.json @@ -189,7 +189,7 @@ "settable_per_mesh": false, "settable_per_extruder": true, "settable_per_meshgroup": false, - "setttable_globally": false + "settable_globally": false } } }, From bf8a04fa4049b4a7bf99c4903b437109ec415b73 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 22 Nov 2018 11:08:46 +0100 Subject: [PATCH 0308/1240] Fix some minor display issues for simulation view CURA-5785 --- plugins/SimulationView/SimulationViewMenuComponent.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/SimulationView/SimulationViewMenuComponent.qml b/plugins/SimulationView/SimulationViewMenuComponent.qml index 110cd1c109..53b64afb47 100644 --- a/plugins/SimulationView/SimulationViewMenuComponent.qml +++ b/plugins/SimulationView/SimulationViewMenuComponent.qml @@ -40,7 +40,6 @@ Cura.ExpandableComponent id: layerViewTypesLabel text: catalog.i18nc("@label", "Color scheme") font: UM.Theme.getFont("default") - visible: !UM.SimulationView.compatibilityMode color: UM.Theme.getColor("setting_control_text") height: base.height verticalAlignment: Text.AlignVCenter @@ -273,6 +272,7 @@ Cura.ExpandableComponent text: catalog.i18nc("@label", "Only Show Top Layers") visible: UM.SimulationView.compatibilityMode style: UM.Theme.styles.checkbox + width: parent.width } CheckBox @@ -280,6 +280,7 @@ Cura.ExpandableComponent checked: viewSettings.top_layer_count == 5 onClicked: UM.Preferences.setValue("view/top_layer_count", checked ? 5 : 1) text: catalog.i18nc("@label", "Show 5 Detailed Layers On Top") + width: parent.width visible: UM.SimulationView.compatibilityMode style: UM.Theme.styles.checkbox } From ba7863c9d9640ce30f01cf47fff8d1ec9564d06f Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Thu, 22 Nov 2018 11:09:35 +0100 Subject: [PATCH 0309/1240] Fix type error for hovering manage queue link Contributes to CL-1148 --- .../resources/qml/ClusterMonitorItem.qml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml b/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml index d055071521..328f82dec5 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml @@ -91,6 +91,7 @@ Component } Label { + id: manageQueueText anchors { left: externalLinkIcon.right @@ -110,8 +111,14 @@ Component anchors.fill: manageQueueLabel hoverEnabled: true onClicked: Cura.MachineManager.printerOutputDevices[0].openPrintJobControlPanel() - onEntered: manageQueueLabel.font.underline = true - onExited: manageQueueLabel.font.underline = false + onEntered: + { + manageQueueText.font.underline = true + } + onExited: + { + manageQueueText.font.underline = false + } } Row From 088b2f6f2808d3aca074e68e47e05d0d0b46808c Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Thu, 22 Nov 2018 11:36:32 +0100 Subject: [PATCH 0310/1240] Added an extra import module, did not pass coding style test CURA-5936 --- plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py index 478f955e49..ff5c33517d 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/MachineInstance.py @@ -6,6 +6,7 @@ import io #To write config files to strings as if they were files. import os.path #To get the path to write new user profiles to. from typing import Dict, List, Optional, Set, Tuple import urllib #To serialise the user container file name properly. +import urllib.parse import UM.VersionUpgrade #To indicate that a file is of incorrect format. import UM.VersionUpgradeManager #To schedule more files to be upgraded. From 3c3343a4073f7cf574ee3aa45eaeb17a0d25f166 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Thu, 22 Nov 2018 13:11:59 +0100 Subject: [PATCH 0311/1240] Use bool for expanded or collapsed state Contributes to CL-1148 --- plugins/UM3NetworkPrinting/resources/qml/ExpandableCard.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/ExpandableCard.qml b/plugins/UM3NetworkPrinting/resources/qml/ExpandableCard.qml index 4922aea853..0877a15f00 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/ExpandableCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/ExpandableCard.qml @@ -14,7 +14,7 @@ Item { id: base - property var expanded: false + property bool expanded: false property var borderWidth: 1 property color borderColor: "#EAEAEC" property color headerBackgroundColor: "white" From 1de21c1d94a5be627c125694118edf39ea5a2f9b Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Thu, 22 Nov 2018 13:13:06 +0100 Subject: [PATCH 0312/1240] Remove unnecessary "else" Contributes to CL-1148 --- .../UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml index ada6f8a644..913e684827 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml @@ -91,10 +91,7 @@ Item } return catalog.i18nc("@label", "First available") } - else - { - return printJob.assignedPrinter.name - } + return printJob.assignedPrinter.name } return "" } From a1613c7f816c0d9c10659972f32bbed3a1995ea1 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 22 Nov 2018 13:53:27 +0100 Subject: [PATCH 0313/1240] Set the variable types of the Action button to the right kind. Because of boyscouting; these must be colors (and not generic vars) --- resources/qml/ActionButton.qml | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index 2a8b894867..69d65e1c3f 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -15,15 +15,17 @@ Button property alias textFont: buttonText.font property alias cornerRadius: backgroundRect.radius property alias tooltip: tooltip.text - property var color: UM.Theme.getColor("primary") - property var hoverColor: UM.Theme.getColor("primary_hover") - property var disabledColor: color - property var textColor: UM.Theme.getColor("button_text") - property var textHoverColor: UM.Theme.getColor("button_text_hover") - property var textDisabledColor: textColor - property var outlineColor: color - property var outlineHoverColor: hoverColor - property var outlineDisabledColor: outlineColor + + property color color: UM.Theme.getColor("primary") + property color hoverColor: UM.Theme.getColor("primary_hover") + property color disabledColor: color + property color textColor: UM.Theme.getColor("button_text") + property color textHoverColor: UM.Theme.getColor("button_text_hover") + property color textDisabledColor: textColor + property color outlineColor: color + property color outlineHoverColor: hoverColor + property color outlineDisabledColor: outlineColor + // This property is used to indicate whether the button has a fixed width or the width would depend on the contents // Be careful when using fixedWidthMode, the translated texts can be too long that they won't fit. In any case, // we elide the text to the right so the text will be cut off with the three dots at the end. @@ -80,6 +82,7 @@ Button { id: mouseArea anchors.fill: parent + // Ensure that the button will still accept the clicks on it's own. onPressed: mouse.accepted = false hoverEnabled: true } From 9720512f50d87de2b6149f1d2475704309f3c07d Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 22 Nov 2018 13:54:10 +0100 Subject: [PATCH 0314/1240] Add a new printer selector button that is connected to the output devices and shows labels containing the type of printers that are in the same group. Contributes to CURA-5942. --- cura/PrinterOutputDevice.py | 5 + .../ConfigurationListView.qml | 2 +- .../{ => PrinterSelector}/MachineSelector.qml | 38 +----- .../PrinterSelector/MachineSelectorButton.qml | 110 ++++++++++++++++++ resources/qml/qmldir | 3 +- 5 files changed, 121 insertions(+), 37 deletions(-) rename resources/qml/{ => PrinterSelector}/MachineSelector.qml (79%) create mode 100644 resources/qml/PrinterSelector/MachineSelectorButton.qml diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 969aa3c460..f8a663f0e4 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -211,6 +211,11 @@ class PrinterOutputDevice(QObject, OutputDevice): self._unique_configurations.sort(key = lambda k: k.printerType) self.uniqueConfigurationsChanged.emit() + # Returns the unique configurations of the printers within this output device + @pyqtProperty("QVariantList", notify = uniqueConfigurationsChanged) + def uniquePrinterTypes(self) -> List[str]: + return list(set([configuration.printerType for configuration in self._unique_configurations])) + def _onPrintersChanged(self) -> None: for printer in self._printers: printer.configurationChanged.connect(self._updateUniqueConfigurations) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml index 7aaf87b4df..210ff6057f 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml @@ -21,7 +21,7 @@ Column { // FIXME For now the model should be removed and then created again, otherwise changes in the printer don't automatically update the UI configurationList.model = [] - if(outputDevice) + if (outputDevice) { configurationList.model = outputDevice.uniqueConfigurations } diff --git a/resources/qml/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml similarity index 79% rename from resources/qml/MachineSelector.qml rename to resources/qml/PrinterSelector/MachineSelector.qml index 14e1ebb48e..9280c45cf4 100644 --- a/resources/qml/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -8,8 +8,6 @@ import QtQuick.Layouts 1.1 import UM 1.2 as UM import Cura 1.0 as Cura -import "Menus" - Cura.ExpandableComponent { @@ -18,7 +16,7 @@ Cura.ExpandableComponent property bool isNetworkPrinter: Cura.MachineManager.activeMachineNetworkKey != "" popupPadding: 0 - popupAlignment: ExpandableComponent.PopupAlignment.AlignLeft + popupAlignment: Cura.ExpandableComponent.PopupAlignment.AlignLeft iconSource: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") UM.I18nCatalog @@ -82,25 +80,10 @@ Cura.ExpandableComponent filter: {"type": "machine", "um_network_key": "*", "hidden": "False"} } - delegate: Cura.ActionButton + delegate: MachineSelectorButton { text: model.metadata["connect_group_name"] - width: parent.width - height: UM.Theme.getSize("action_button").height checked: Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["connect_group_name"] - checkable: true - - color: "transparent" - hoverColor: UM.Theme.getColor("action_button_hovered") - textColor: UM.Theme.getColor("text") - textHoverColor: UM.Theme.getColor("text") - outlineColor: checked ? UM.Theme.getColor("primary") : "transparent" - - onClicked: - { - togglePopup() - Cura.MachineManager.setActiveMachine(model.id) - } Connections { @@ -132,25 +115,10 @@ Cura.ExpandableComponent filter: {"type": "machine", "um_network_key": null} } - delegate: Cura.ActionButton + delegate: MachineSelectorButton { text: model.name - width: parent.width - height: UM.Theme.getSize("action_button").height checked: Cura.MachineManager.activeMachineId == model.id - checkable: true - - color: "transparent" - hoverColor: UM.Theme.getColor("action_button_hovered") - textColor: UM.Theme.getColor("text") - textHoverColor: UM.Theme.getColor("text") - outlineColor: checked ? UM.Theme.getColor("primary") : "transparent" - - onClicked: - { - togglePopup() - Cura.MachineManager.setActiveMachine(model.id) - } } } } diff --git a/resources/qml/PrinterSelector/MachineSelectorButton.qml b/resources/qml/PrinterSelector/MachineSelectorButton.qml new file mode 100644 index 0000000000..5ba229c31c --- /dev/null +++ b/resources/qml/PrinterSelector/MachineSelectorButton.qml @@ -0,0 +1,110 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 2.1 +import QtQuick.Layouts 1.3 + +import UM 1.1 as UM +import Cura 1.0 as Cura + +Button +{ + id: machineSelectorButton + + width: parent.width + height: UM.Theme.getSize("action_button").height + leftPadding: Math.round(1.5 * UM.Theme.getSize("default_margin").width) + checkable: true + + property var outputDevice: Cura.MachineManager.printerOutputDevices[0] + property var printerTypesList: [] + + function setPrinterTypesList() + { + printerTypesList = (checked && (outputDevice != null)) ? outputDevice.uniquePrinterTypes : [] + } + + contentItem: Item + { + width: machineSelectorButton.width - machineSelectorButton.leftPadding + height: UM.Theme.getSize("action_button").height + + Label + { + id: buttonText + anchors + { + left: parent.left + right: printerTypes.left + verticalCenter: parent.verticalCenter + } + text: machineSelectorButton.text + color: UM.Theme.getColor("text") + font: UM.Theme.getFont("action_button") + visible: text != "" + renderType: Text.NativeRendering + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + Row + { + id: printerTypes + width: childrenRect.width + + anchors + { + right: parent.right + verticalCenter: parent.verticalCenter + } + spacing: UM.Theme.getSize("narrow_margin").width + + Repeater + { + model: printerTypesList + delegate: Label + { + text: modelData + } + } + } + } + + background: Rectangle + { + id: backgroundRect + color: machineSelectorButton.hovered ? UM.Theme.getColor("action_button_hovered") : "transparent" + radius: UM.Theme.getSize("action_button_radius").width + border.width: UM.Theme.getSize("default_lining").width + border.color: machineSelectorButton.checked ? UM.Theme.getColor("primary") : "transparent" + } + + onClicked: + { + togglePopup() + Cura.MachineManager.setActiveMachine(model.id) + } + + MouseArea + { + id: mouseArea + anchors.fill: parent + onPressed: mouse.accepted = false + hoverEnabled: true + } + + Connections + { + target: outputDevice + onUniqueConfigurationsChanged: setPrinterTypesList() + } + + Connections + { + target: Cura.MachineManager + onOutputDevicesChanged: setPrinterTypesList() + } + + Component.onCompleted: setPrinterTypesList() +} diff --git a/resources/qml/qmldir b/resources/qml/qmldir index d5e4106d33..458338c78a 100644 --- a/resources/qml/qmldir +++ b/resources/qml/qmldir @@ -9,4 +9,5 @@ MaterialMenu 1.0 MaterialMenu.qml NozzleMenu 1.0 NozzleMenu.qml ActionPanelWidget 1.0 ActionPanelWidget.qml IconLabel 1.0 IconLabel.qml -OutputDevicesActionButton 1.0 OutputDevicesActionButton.qml \ No newline at end of file +OutputDevicesActionButton 1.0 OutputDevicesActionButton.qml +ExpandableComponent 1.0 ExpandableComponent.qml \ No newline at end of file From da834d6a1fdf4ef7123e737015acdc10b555bece Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Thu, 22 Nov 2018 13:55:22 +0100 Subject: [PATCH 0315/1240] Silence binding loop Contributes to CL-1148 --- .../UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml index 913e684827..0f2ae67442 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml @@ -71,7 +71,7 @@ Item Item { anchors.verticalCenter: parent.verticalCenter - height: childrenRect.height + height: 18 * screenScaleFactor // TODO: This should be childrenRect.height but QML throws warnings width: childrenRect.width Label From 55554c62a9a4d8995ae6358b9d29cb2a4b6861d3 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Thu, 22 Nov 2018 13:55:43 +0100 Subject: [PATCH 0316/1240] Use array for extruder configurations Contributes to CL-1148 --- .../resources/qml/MonitorPrintJobCard.qml | 7 +++- .../qml/MonitorPrinterConfiguration.qml | 38 ++++++++----------- 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml index 0f2ae67442..8231870c21 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml @@ -141,8 +141,11 @@ Item id: printerConfiguration anchors.verticalCenter: parent.verticalCenter buildplate: "Glass" - config0: base.printJob.configuration.extruderConfigurations[0] - config1: base.printJob.configuration.extruderConfigurations[1] + configurations: + [ + base.printJob.configuration.extruderConfigurations[0], + base.printJob.configuration.extruderConfigurations[1] + ] height: 72 * screenScaleFactor // TODO: Theme! } Label { diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterConfiguration.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterConfiguration.qml index a31c8bbd99..6aa11528de 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterConfiguration.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterConfiguration.qml @@ -18,11 +18,8 @@ Item // Extracted buildplate configuration property alias buildplate: buildplateConfig.buildplate - // Extracted extruder configuration for position 0 - property var config0: null - - // Extracted extruder configuration for position 1 - property var config1: null + // Array of extracted extruder configurations + property var configurations: null // Default size, but should be stretched to fill parent height: 72 * parent.height @@ -30,30 +27,25 @@ Item Row { + id: extruderConfigurationRow spacing: 18 * screenScaleFactor // TODO: Theme! - MonitorExtruderConfiguration + Repeater { - color: config0 && config0.activeMaterial ? config0.activeMaterial.color : "#eeeeee" // TODO: Theme! - material: config0 && config0.activeMaterial ? config0.activeMaterial.name : "" - position: config0.position - printCore: config0 ? config0.hotendID : "" - visible: config0 + id: extruderConfigurationRepeater + model: configurations - // Keep things responsive! - width: Math.floor((base.width - parent.spacing) / 2) - } + MonitorExtruderConfiguration + { + color: modelData.activeMaterial ? modelData.activeMaterial.color : "#eeeeee" // TODO: Theme! + material: modelData.activeMaterial ? modelData.activeMaterial.name : "" + position: modelData.position + printCore: modelData.hotendID - MonitorExtruderConfiguration - { - color: config1 && config1.activeMaterial ? config1.activeMaterial.color : "#eeeeee" // TODO: Theme! - material: config1 && config1.activeMaterial ? config1.activeMaterial.name : "" - position: config1.position - printCore: config1 ? config1.hotendID : "" - visible: config1 + // Keep things responsive! + width: Math.floor((base.width - (configurations.length - 1) * extruderConfigurationRow.spacing) / configurations.length) + } - // Keep things responsive! - width: Math.floor((base.width - parent.spacing) / 2) } } From 963b8aa97487bc839ec051473eb79f1839767316 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Thu, 22 Nov 2018 13:56:01 +0100 Subject: [PATCH 0317/1240] Fix QML warnings Contributes to CL-1148 --- .../UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml b/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml index 328f82dec5..19a152e6eb 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml @@ -17,7 +17,7 @@ Component property var emphasisColor: UM.Theme.getColor("setting_control_border_highlight") property var cornerRadius: UM.Theme.getSize("monitor_corner_radius").width - color: transparent + color: "transparent" height: maximumHeight onVisibleChanged: { @@ -83,7 +83,7 @@ Component UM.RecolorImage { id: externalLinkIcon - anchors.verticalCenter: externalLinkIcon.verticalCenter + anchors.verticalCenter: manageQueueLabel.verticalCenter color: UM.Theme.getColor("primary") source: "../svg/icons/external_link.svg" width: 16 * screenScaleFactor // TODO: Theme! (Y U NO USE 18 LIKE ALL OTHER ICONS?!) From 698409803d6c3717dea8312741e0f188aee9a7b5 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 22 Nov 2018 09:07:22 +0100 Subject: [PATCH 0318/1240] Fix null warning in QML CURA-5943 --- .../qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml index 97b5bee745..73fc342d66 100644 --- a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml @@ -70,7 +70,7 @@ Column Label { id: materialLabel - text: printCoreConfiguration.material.name + text: printCoreConfiguration.material == null ? "" : printCoreConfiguration.material.name renderType: Text.NativeRendering elide: Text.ElideRight width: parent.width From 45d4f2ad3d4ba05d42757a4b0aef7433b22589b7 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 22 Nov 2018 13:48:50 +0100 Subject: [PATCH 0319/1240] Update MonitorSection CURA-5943 --- resources/qml/PrinterOutput/MonitorSection.qml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/resources/qml/PrinterOutput/MonitorSection.qml b/resources/qml/PrinterOutput/MonitorSection.qml index 6ed762362d..7ef89dabf7 100644 --- a/resources/qml/PrinterOutput/MonitorSection.qml +++ b/resources/qml/PrinterOutput/MonitorSection.qml @@ -1,10 +1,10 @@ // 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 QtQuick.Controls.Styles 1.1 -import QtQuick.Layouts 1.1 +import QtQuick 2.10 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.3 import UM 1.2 as UM import Cura 1.0 as Cura @@ -13,7 +13,8 @@ Item { id: base property string label - height: childrenRect.height; + height: childrenRect.height + Rectangle { color: UM.Theme.getColor("setting_category") @@ -30,4 +31,4 @@ Item color: UM.Theme.getColor("setting_category_text") } } -} \ No newline at end of file +} From cf0994037c962a35adbf26ace560fc0e749e239d Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 22 Nov 2018 13:56:03 +0100 Subject: [PATCH 0320/1240] Move MonitorButtonSytle to a separate QML file CURA-5943 --- .../PrinterOutput/ManualPrinterControl.qml | 84 ++---------------- .../qml/PrinterOutput/MonitorButtonStyle.qml | 88 +++++++++++++++++++ 2 files changed, 93 insertions(+), 79 deletions(-) create mode 100644 resources/qml/PrinterOutput/MonitorButtonStyle.qml diff --git a/resources/qml/PrinterOutput/ManualPrinterControl.qml b/resources/qml/PrinterOutput/ManualPrinterControl.qml index 70961a2eb2..3219dc5792 100644 --- a/resources/qml/PrinterOutput/ManualPrinterControl.qml +++ b/resources/qml/PrinterOutput/ManualPrinterControl.qml @@ -9,6 +9,9 @@ import QtQuick.Layouts 1.1 import UM 1.2 as UM import Cura 1.0 as Cura +import "." + + Item { property var printerModel @@ -16,86 +19,9 @@ Item implicitWidth: parent.width implicitHeight: childrenRect.height - Component + MonitorButtonStyle { id: monitorButtonStyle - - ButtonStyle - { - background: Rectangle - { - border.width: UM.Theme.getSize("default_lining").width - border.color: - { - if(!control.enabled) - { - return UM.Theme.getColor("action_button_disabled_border"); - } - else if(control.pressed) - { - return UM.Theme.getColor("action_button_active_border"); - } - else if(control.hovered) - { - return UM.Theme.getColor("action_button_hovered_border"); - } - return UM.Theme.getColor("action_button_border"); - } - color: - { - if(!control.enabled) - { - return UM.Theme.getColor("action_button_disabled"); - } - else if(control.pressed) - { - return UM.Theme.getColor("action_button_active"); - } - else if(control.hovered) - { - return UM.Theme.getColor("action_button_hovered"); - } - return UM.Theme.getColor("action_button"); - } - Behavior on color - { - ColorAnimation - { - duration: 50 - } - } - } - - label: Item - { - UM.RecolorImage - { - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter - width: Math.floor(control.width / 2) - height: Math.floor(control.height / 2) - sourceSize.width: width - sourceSize.height: width - color: - { - if(!control.enabled) - { - return UM.Theme.getColor("action_button_disabled_text"); - } - else if(control.pressed) - { - return UM.Theme.getColor("action_button_active_text"); - } - else if(control.hovered) - { - return UM.Theme.getColor("action_button_hovered_text"); - } - return UM.Theme.getColor("action_button_text"); - } - source: control.iconSource - } - } - } } Column @@ -551,4 +477,4 @@ Item } ExclusiveGroup { id: distanceGroup } } -} \ No newline at end of file +} diff --git a/resources/qml/PrinterOutput/MonitorButtonStyle.qml b/resources/qml/PrinterOutput/MonitorButtonStyle.qml new file mode 100644 index 0000000000..7bb1b91e55 --- /dev/null +++ b/resources/qml/PrinterOutput/MonitorButtonStyle.qml @@ -0,0 +1,88 @@ +import QtQuick 2.10 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.3 + +import UM 1.2 as UM +import Cura 1.0 as Cura + + +Component +{ + ButtonStyle + { + background: Rectangle + { + border.width: UM.Theme.getSize("default_lining").width + border.color: + { + if(!control.enabled) + { + return UM.Theme.getColor("action_button_disabled_border"); + } + else if(control.pressed) + { + return UM.Theme.getColor("action_button_active_border"); + } + else if(control.hovered) + { + return UM.Theme.getColor("action_button_hovered_border"); + } + return UM.Theme.getColor("action_button_border"); + } + color: + { + if(!control.enabled) + { + return UM.Theme.getColor("action_button_disabled"); + } + else if(control.pressed) + { + return UM.Theme.getColor("action_button_active"); + } + else if(control.hovered) + { + return UM.Theme.getColor("action_button_hovered"); + } + return UM.Theme.getColor("action_button"); + } + Behavior on color + { + ColorAnimation + { + duration: 50 + } + } + } + + label: Item + { + UM.RecolorImage + { + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + width: Math.floor(control.width / 2) + height: Math.floor(control.height / 2) + sourceSize.width: width + sourceSize.height: width + color: + { + if(!control.enabled) + { + return UM.Theme.getColor("action_button_disabled_text"); + } + else if(control.pressed) + { + return UM.Theme.getColor("action_button_active_text"); + } + else if(control.hovered) + { + return UM.Theme.getColor("action_button_hovered_text"); + } + return UM.Theme.getColor("action_button_text"); + } + source: control.iconSource + } + } + } +} From 5a1691839169ba0ee3f4ca6be15514d5714f062a Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 22 Nov 2018 13:58:18 +0100 Subject: [PATCH 0321/1240] Update ManualPrinterControl CURA-5943 --- .../qml/PrinterOutput/ManualPrinterControl.qml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/resources/qml/PrinterOutput/ManualPrinterControl.qml b/resources/qml/PrinterOutput/ManualPrinterControl.qml index 3219dc5792..1a719c379e 100644 --- a/resources/qml/PrinterOutput/ManualPrinterControl.qml +++ b/resources/qml/PrinterOutput/ManualPrinterControl.qml @@ -1,12 +1,12 @@ // 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 QtQuick.Controls.Styles 1.1 -import QtQuick.Layouts 1.1 +import QtQuick 2.10 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.3 -import UM 1.2 as UM +import UM 1.3 as UM import Cura 1.0 as Cura import "." @@ -14,8 +14,10 @@ import "." Item { - property var printerModel + property var printerModel: null property var activePrintJob: printerModel != null ? printerModel.activePrintJob : null + property var connectedPrinter: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null + implicitWidth: parent.width implicitHeight: childrenRect.height @@ -388,7 +390,7 @@ Item if (printerModel == null) { return false // Can't send custom commands if not connected. } - if (!connectedPrinter.acceptsCommands) { + if (connectedPrinter == null || !connectedPrinter.acceptsCommands) { return false // Not allowed to do anything } if (connectedPrinter.jobState == "printing" || connectedPrinter.jobState == "pre_print" || connectedPrinter.jobState == "resuming" || connectedPrinter.jobState == "pausing" || connectedPrinter.jobState == "paused" || connectedPrinter.jobState == "error" || connectedPrinter.jobState == "offline") { From e65f2d66a631d43dfa0c3da716ac332db7767a9f Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 22 Nov 2018 14:02:17 +0100 Subject: [PATCH 0322/1240] Add connectedPrinter to MonitorItem CURA-5943 --- resources/qml/PrinterOutput/MonitorItem.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/qml/PrinterOutput/MonitorItem.qml b/resources/qml/PrinterOutput/MonitorItem.qml index cad8d2f7f3..a26ec20f64 100644 --- a/resources/qml/PrinterOutput/MonitorItem.qml +++ b/resources/qml/PrinterOutput/MonitorItem.qml @@ -15,6 +15,8 @@ Item property string value: "" height: childrenRect.height; + property var connectedPrinter: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null + Row { height: UM.Theme.getSize("setting_control").height From 75661d9bba0079df9dfe3288c38282ca8e857c35 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 22 Nov 2018 14:04:08 +0100 Subject: [PATCH 0323/1240] Fix MonitorSidebar due to widget renaming CURA-5943 --- resources/qml/MonitorSidebar.qml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/resources/qml/MonitorSidebar.qml b/resources/qml/MonitorSidebar.qml index 2282034739..50416e34ab 100644 --- a/resources/qml/MonitorSidebar.qml +++ b/resources/qml/MonitorSidebar.qml @@ -1,15 +1,17 @@ // Copyright (c) 2018 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.7 +import QtQuick 2.10 import QtQuick.Controls 2.0 import QtQuick.Layouts 1.3 import UM 1.2 as UM import Cura 1.0 as Cura + import "Menus" import "Menus/ConfigurationMenu" + Rectangle { id: base @@ -85,7 +87,7 @@ Rectangle } } - MachineSelection + MachineSelector { id: machineSelection width: base.width - configSelection.width - separator.width @@ -104,7 +106,7 @@ Rectangle anchors.left: machineSelection.right } - ConfigurationSelection + CustomConfigurationSelector { id: configSelection visible: isNetworkPrinter && printerConnected @@ -112,7 +114,6 @@ Rectangle height: UM.Theme.getSize("stage_menu").height anchors.top: base.top anchors.right: parent.right - panelWidth: base.width } Loader From 8eb0f66df384a50f5f9afb12d8747737c20c4deb Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 22 Nov 2018 14:05:23 +0100 Subject: [PATCH 0324/1240] Add connectedPrinter to ExtruderBox CURA-5943 --- resources/qml/PrinterOutput/ExtruderBox.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/qml/PrinterOutput/ExtruderBox.qml b/resources/qml/PrinterOutput/ExtruderBox.qml index 510a44f9da..f5a1bd75c4 100644 --- a/resources/qml/PrinterOutput/ExtruderBox.qml +++ b/resources/qml/PrinterOutput/ExtruderBox.qml @@ -12,6 +12,8 @@ Item property alias color: background.color property var extruderModel property var position: index + property var connectedPrinter: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null + implicitWidth: parent.width implicitHeight: UM.Theme.getSize("print_setup_extruder_box").height From d8c3078d78fc176fc764ee54bd12264850076b3e Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 22 Nov 2018 14:10:04 +0100 Subject: [PATCH 0325/1240] Add connectedPrinter to HeatedBedBox CURA-5943 --- resources/qml/PrinterOutput/HeatedBedBox.qml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/resources/qml/PrinterOutput/HeatedBedBox.qml b/resources/qml/PrinterOutput/HeatedBedBox.qml index 962ffa9b01..8c99814e02 100644 --- a/resources/qml/PrinterOutput/HeatedBedBox.qml +++ b/resources/qml/PrinterOutput/HeatedBedBox.qml @@ -1,10 +1,10 @@ // 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 QtQuick.Controls.Styles 1.1 -import QtQuick.Layouts 1.1 +import QtQuick 2.10 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.3 import UM 1.2 as UM import Cura 1.0 as Cura @@ -14,6 +14,7 @@ Item implicitWidth: parent.width height: visible ? UM.Theme.getSize("print_setup_extruder_box").height : 0 property var printerModel + property var connectedPrinter: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null Rectangle { @@ -114,7 +115,7 @@ Item { return false; //Can't preheat if not connected. } - if (!connectedPrinter.acceptsCommands) + if (connectedPrinter == null || !connectedPrinter.acceptsCommands) { return false; //Not allowed to do anything. } From 0211122b1209d55db109cb00d8a442d30b4c970b Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 22 Nov 2018 14:46:13 +0100 Subject: [PATCH 0326/1240] Add a proper component with a label and a rectangle in the background that shows the type of printer. Contributes to CURA-5942. --- .../qml/PrinterSelector/MachineSelector.qml | 1 + .../PrinterSelector/MachineSelectorButton.qml | 24 ++++++++++++++++--- resources/qml/ViewOrientationControls.qml | 2 +- resources/themes/cura-light/theme.json | 5 +++- 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index 9280c45cf4..66ed4d4a2c 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -84,6 +84,7 @@ Cura.ExpandableComponent { text: model.metadata["connect_group_name"] checked: Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["connect_group_name"] + outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null Connections { diff --git a/resources/qml/PrinterSelector/MachineSelectorButton.qml b/resources/qml/PrinterSelector/MachineSelectorButton.qml index 5ba229c31c..44b162d00d 100644 --- a/resources/qml/PrinterSelector/MachineSelectorButton.qml +++ b/resources/qml/PrinterSelector/MachineSelectorButton.qml @@ -17,7 +17,7 @@ Button leftPadding: Math.round(1.5 * UM.Theme.getSize("default_margin").width) checkable: true - property var outputDevice: Cura.MachineManager.printerOutputDevices[0] + property var outputDevice: null property var printerTypesList: [] function setPrinterTypesList() @@ -63,9 +63,27 @@ Button Repeater { model: printerTypesList - delegate: Label + delegate: Item { - text: modelData + width: UM.Theme.getSize("printer_type_label").width + height: UM.Theme.getSize("printer_type_label").height + + Rectangle + { + anchors.fill: parent + color: UM.Theme.getColor("printer_type_label_background") + } + + Label + { + id: printerTypeLabel + text: modelData + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + renderType: Text.NativeRendering + font: UM.Theme.getFont("very_small") + color: UM.Theme.getColor("text") + } } } } diff --git a/resources/qml/ViewOrientationControls.qml b/resources/qml/ViewOrientationControls.qml index acf75b1b48..fa5a51181d 100644 --- a/resources/qml/ViewOrientationControls.qml +++ b/resources/qml/ViewOrientationControls.qml @@ -21,7 +21,7 @@ Row { iconSource: UM.Theme.getIcon("view_3d") style: UM.Theme.styles.small_tool_button - onClicked:UM.Controller.rotateView("3d", 0) + onClicked: UM.Controller.rotateView("3d", 0) } // #2 Front view diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 2f86829f11..94a89342e7 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -81,7 +81,6 @@ "lining": [192, 193, 194, 255], "viewport_overlay": [0, 0, 0, 192], - "primary": [50, 130, 255, 255], "primary_hover": [48, 182, 231, 255], "primary_text": [255, 255, 255, 255], @@ -114,6 +113,8 @@ "toolbar_background": [255, 255, 255, 255], + "printer_type_label_background": [171, 171, 191, 255], + "text": [0, 0, 0, 255], "text_detail": [174, 174, 174, 128], "text_link": [50, 130, 255, 255], @@ -398,6 +399,8 @@ "views_selector": [0.0, 4.0], + "printer_type_label": [3.5, 1.5], + "default_radius": [0.25, 0.25], "wide_lining": [0.5, 0.5], From 7f11142d5086b40164b490d90617a31bf41f63d8 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 22 Nov 2018 14:57:55 +0100 Subject: [PATCH 0327/1240] Fix height and vertical layout of popup Many things were made simpler. This took some time to fix... Contributes to issue CURA-5876. --- .../ConfigurationMenu/AutoConfiguration.qml | 4 ++ .../ConfigurationMenu/ConfigurationMenu.qml | 49 ++++++++----------- .../ConfigurationMenu/CustomConfiguration.qml | 6 ++- 3 files changed, 29 insertions(+), 30 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml index e1f0ed480e..cde18ab488 100644 --- a/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml @@ -8,12 +8,16 @@ import UM 1.3 as UM Item { + width: parent.width + height: visible ? childrenRect.height : 0 + Label { id: header text: catalog.i18nc("@header", "Configurations") font: UM.Theme.getFont("large") color: UM.Theme.getColor("text") + height: contentHeight anchors { diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index 8f31d80810..4aa8d66caa 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -96,32 +96,36 @@ Cura.ExpandableComponent } } - popupItem: Item + popupItem: Column { id: popupItem width: base.width - 2 * UM.Theme.getSize("default_margin").width - height: 200 + height: implicitHeight //Required because ExpandableComponent will try to use this to determine the size of the background of the pop-up. + spacing: UM.Theme.getSize("default_margin").height property var is_connected: false //If current machine is connected to a printer. Only evaluated upon making popup visible. onVisibleChanged: { - is_connected = Cura.MachineManager.activeMachineNetworkKey != "" && Cura.MachineManager.printerConnected //Re-evaluate. + is_connected = Cura.MachineManager.activeMachineNetworkKey !== "" && Cura.MachineManager.printerConnected //Re-evaluate. } - property var configuration_method: buttonBar.visible ? "auto" : "custom" //Auto if connected to a printer at start-up, or Custom if not. + property var configuration_method: is_connected ? "auto" : "custom" //Auto if connected to a printer at start-up, or Custom if not. - AutoConfiguration + Item { - id: autoConfiguration - visible: popupItem.configuration_method === "auto" - anchors.top: parent.top - } + width: parent.width + height: childrenRect.height + AutoConfiguration + { + id: autoConfiguration + visible: popupItem.configuration_method === "auto" + } - CustomConfiguration - { - id: customConfiguration - visible: popupItem.configuration_method === "custom" - anchors.top: parent.top + CustomConfiguration + { + id: customConfiguration + visible: popupItem.configuration_method === "custom" + } } Rectangle @@ -129,30 +133,19 @@ Cura.ExpandableComponent id: separator visible: buttonBar.visible - anchors - { - left: parent.left - right: parent.right - bottom: buttonBar.top - bottomMargin: UM.Theme.getSize("default_margin").height - } + width: parent.width height: UM.Theme.getSize("default_lining").height color: UM.Theme.getColor("lining") } //Allow switching between custom and auto. - Rectangle + Item { id: buttonBar visible: popupItem.is_connected //Switching only makes sense if the "auto" part is possible. - anchors - { - left: parent.left - right: parent.right - bottom: parent.bottom - } + width: parent.width height: childrenRect.height Cura.ActionButton diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index aa4cf3624b..d424f35ebe 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -11,6 +11,7 @@ import UM 1.3 as UM Item { width: parent.width + height: visible ? childrenRect.height : 0 Label { @@ -18,6 +19,7 @@ Item text: catalog.i18nc("@header", "Custom") font: UM.Theme.getFont("large") color: UM.Theme.getColor("text") + height: contentHeight anchors { @@ -62,14 +64,15 @@ Item { id: tabControl width: parent.width + height: childrenRect.height anchors.top: tabBar.bottom - anchors.bottom: parent.bottom property var model: extrudersModel.items[tabBar.currentIndex] property real textWidth: Math.round(width * 0.3) property real controlWidth: width - textWidth Column { spacing: UM.Theme.getSize("default_margin").height + Row { height: UM.Theme.getSize("print_setup_item").height @@ -165,6 +168,5 @@ Item } } } - } } \ No newline at end of file From 5c30df2a688d858fb5b23e6ccc4250acae1dc9b6 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 22 Nov 2018 15:00:35 +0100 Subject: [PATCH 0328/1240] Create a reusable component for the printer type label. Other parts of the UI can just reuse it. Contributes to CURA-5942. --- resources/qml/ActionButton.qml | 1 - .../PrinterSelector/MachineSelectorButton.qml | 24 ++----------- .../qml/PrinterSelector/PrinterTypeLabel.qml | 34 +++++++++++++++++++ resources/qml/qmldir | 3 +- 4 files changed, 39 insertions(+), 23 deletions(-) create mode 100644 resources/qml/PrinterSelector/PrinterTypeLabel.qml diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index 2a8b894867..ff8ee4b149 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -3,7 +3,6 @@ import QtQuick 2.7 import QtQuick.Controls 2.1 -import QtQuick.Layouts 1.3 import UM 1.1 as UM diff --git a/resources/qml/PrinterSelector/MachineSelectorButton.qml b/resources/qml/PrinterSelector/MachineSelectorButton.qml index 44b162d00d..e98036dbc8 100644 --- a/resources/qml/PrinterSelector/MachineSelectorButton.qml +++ b/resources/qml/PrinterSelector/MachineSelectorButton.qml @@ -3,7 +3,6 @@ import QtQuick 2.7 import QtQuick.Controls 2.1 -import QtQuick.Layouts 1.3 import UM 1.1 as UM import Cura 1.0 as Cura @@ -15,6 +14,7 @@ Button width: parent.width height: UM.Theme.getSize("action_button").height leftPadding: Math.round(1.5 * UM.Theme.getSize("default_margin").width) + rightPadding: Math.round(1.5 * UM.Theme.getSize("default_margin").width) checkable: true property var outputDevice: null @@ -63,27 +63,9 @@ Button Repeater { model: printerTypesList - delegate: Item + delegate: Cura.PrinterTypeLabel { - width: UM.Theme.getSize("printer_type_label").width - height: UM.Theme.getSize("printer_type_label").height - - Rectangle - { - anchors.fill: parent - color: UM.Theme.getColor("printer_type_label_background") - } - - Label - { - id: printerTypeLabel - text: modelData - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter - renderType: Text.NativeRendering - font: UM.Theme.getFont("very_small") - color: UM.Theme.getColor("text") - } + text: modelData } } } diff --git a/resources/qml/PrinterSelector/PrinterTypeLabel.qml b/resources/qml/PrinterSelector/PrinterTypeLabel.qml new file mode 100644 index 0000000000..cd9f3b9743 --- /dev/null +++ b/resources/qml/PrinterSelector/PrinterTypeLabel.qml @@ -0,0 +1,34 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 2.1 + +import UM 1.1 as UM + +// This component creates a label with the abbreviated name of a printer, with a rectangle surrounding the label. +// It is created in a separated place in order to be reused whenever needed. +Item +{ + property alias text: printerTypeLabel.text + + width: UM.Theme.getSize("printer_type_label").width + height: UM.Theme.getSize("printer_type_label").height + + Rectangle + { + anchors.fill: parent + color: UM.Theme.getColor("printer_type_label_background") + } + + Label + { + id: printerTypeLabel + text: "CFFFP" // As an abbreviated name of the Custom FFF Printer + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + renderType: Text.NativeRendering + font: UM.Theme.getFont("very_small") + color: UM.Theme.getColor("text") + } +} \ No newline at end of file diff --git a/resources/qml/qmldir b/resources/qml/qmldir index 458338c78a..67388100ca 100644 --- a/resources/qml/qmldir +++ b/resources/qml/qmldir @@ -10,4 +10,5 @@ NozzleMenu 1.0 NozzleMenu.qml ActionPanelWidget 1.0 ActionPanelWidget.qml IconLabel 1.0 IconLabel.qml OutputDevicesActionButton 1.0 OutputDevicesActionButton.qml -ExpandableComponent 1.0 ExpandableComponent.qml \ No newline at end of file +ExpandableComponent 1.0 ExpandableComponent.qml +PrinterTypeLabel 1.0 PrinterTypeLabel.qml \ No newline at end of file From 2d2f24251dcaac15bed5bba3e0ec782161743181 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 22 Nov 2018 15:07:49 +0100 Subject: [PATCH 0329/1240] Fix MonitorMainView for USB printing CURA-5943 --- plugins/MonitorStage/MonitorMainView.qml | 106 +++++++++++++---------- 1 file changed, 61 insertions(+), 45 deletions(-) diff --git a/plugins/MonitorStage/MonitorMainView.qml b/plugins/MonitorStage/MonitorMainView.qml index c48f6d0aab..57dd033792 100644 --- a/plugins/MonitorStage/MonitorMainView.qml +++ b/plugins/MonitorStage/MonitorMainView.qml @@ -1,45 +1,61 @@ -// Copyright (c) 2017 Ultimaker B.V. - -import QtQuick 2.2 -import QtQuick.Controls 1.1 - -import UM 1.3 as UM -import Cura 1.0 as Cura - -Item -{ - // parent could be undefined as this component is not visible at all times - width: parent ? parent.width : 0 - height: parent ? parent.height : 0 - - // We show a nice overlay on the 3D viewer when the current output device has no monitor view - Rectangle - { - id: viewportOverlay - - color: UM.Theme.getColor("viewport_overlay") - width: parent.width - height: parent.height - - MouseArea - { - anchors.fill: parent - acceptedButtons: Qt.AllButtons - onWheel: wheel.accepted = true - } - } - - Loader - { - id: monitorViewComponent - - width: parent.width - height: parent.height - - property real maximumWidth: parent.width - property real maximumHeight: parent.height - - sourceComponent: Cura.MachineManager.printerOutputDevices.length > 0 ? Cura.MachineManager.printerOutputDevices[0].monitorItem: null - visible: sourceComponent != null - } -} +// Copyright (c) 2017 Ultimaker B.V. + +import QtQuick 2.10 +import QtQuick.Controls 1.4 + +import UM 1.3 as UM +import Cura 1.0 as Cura + + +Item +{ + // parent could be undefined as this component is not visible at all times + width: parent ? parent.width : 0 + height: parent ? parent.height : 0 + + // We show a nice overlay on the 3D viewer when the current output device has no monitor view + Rectangle + { + id: viewportOverlay + + color: UM.Theme.getColor("viewport_overlay") + width: parent.width + height: parent.height + + MouseArea + { + anchors.fill: parent + acceptedButtons: Qt.AllButtons + onWheel: wheel.accepted = true + } + } + + Loader + { + id: monitorViewComponent + + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left + + width: parent.width * 0.7 + height: parent.height + + property real maximumWidth: parent.width + property real maximumHeight: parent.height + + sourceComponent: Cura.MachineManager.printerOutputDevices.length > 0 ? Cura.MachineManager.printerOutputDevices[0].monitorItem: null + } + + Loader + { + id: monitorSidebarComponent + + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: monitorViewComponent.right + anchors.right: parent.right + + source: UM.Controller.activeStage.sidebarComponent != null ? UM.Controller.activeStage.sidebarComponent : "" + } +} From 3f4d379908add0ec7054eb65e87ae91587ffaebd Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 22 Nov 2018 15:35:40 +0100 Subject: [PATCH 0330/1240] Added shadow to slice button CURA-5959 --- resources/qml/ActionButton.qml | 18 +++++++++++ .../qml/ActionPanel/SliceProcessWidget.qml | 31 +++++++++---------- resources/themes/cura-light/theme.json | 2 ++ 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index 69d65e1c3f..8cd53b5d7e 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -5,6 +5,8 @@ import QtQuick 2.7 import QtQuick.Controls 2.1 import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.0 // For the dropshadow + import UM 1.1 as UM Button @@ -26,6 +28,9 @@ Button property color outlineHoverColor: hoverColor property color outlineDisabledColor: outlineColor + property alias shadowColor: shadow.color + property alias shadowEnabled: shadow.visible + // This property is used to indicate whether the button has a fixed width or the width would depend on the contents // Be careful when using fixedWidthMode, the translated texts can be too long that they won't fit. In any case, // we elide the text to the right so the text will be cut off with the three dots at the end. @@ -70,6 +75,19 @@ Button border.color: button.enabled ? (button.hovered ? button.outlineHoverColor : button.outlineColor) : button.outlineDisabledColor } + DropShadow + { + id: shadow + // Don't blur the shadow + radius: 0 + anchors.fill: backgroundRect + source: backgroundRect + verticalOffset: 2 + visible: false + // Should always be drawn behind the background. + z: backgroundRect.z - 1 + } + ToolTip { id: tooltip diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index 2d4a7b6b89..4f10e6879b 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -87,28 +87,27 @@ Column width: parent.width height: UM.Theme.getSize("action_panel_button").height fixedWidthMode: true - text: - { - if ([UM.Backend.NotStarted, UM.Backend.Error].indexOf(widget.backendState) != -1) - { - return catalog.i18nc("@button", "Slice") - } - if (autoSlice) - { - return catalog.i18nc("@button", "Auto slicing...") - } - return catalog.i18nc("@button", "Cancel") - } - enabled: !autoSlice && !disabledSlice // Get the current value from the preferences property bool autoSlice: UM.Preferences.getValue("general/auto_slice") // Disable the slice process when property bool disabledSlice: [UM.Backend.Done, UM.Backend.Error].indexOf(widget.backendState) != -1 - disabledColor: disabledSlice ? UM.Theme.getColor("action_button_disabled") : "transparent" - textDisabledColor: disabledSlice ? UM.Theme.getColor("action_button_disabled_text") : UM.Theme.getColor("primary") - outlineDisabledColor: disabledSlice ? UM.Theme.getColor("action_button_disabled_border") : "transparent" + text: + { + if ([UM.Backend.NotStarted, UM.Backend.Error].indexOf(widget.backendState) != -1) + { + return catalog.i18nc("@button", "Slice") + } + return catalog.i18nc("@button", "Cancel") + } + enabled: !autoSlice && !disabledSlice + visible: !autoSlice + + disabledColor: UM.Theme.getColor("action_button_disabled") + textDisabledColor: UM.Theme.getColor("action_button_disabled_text") + shadowEnabled: true + shadowColor: enabled ? UM.Theme.getColor("action_button_shadow"): UM.Theme.getColor("action_button_disabled_shadow") onClicked: sliceOrStopSlicing() } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index fefc4adc14..748a4e2643 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -173,6 +173,8 @@ "action_button_disabled": [245, 245, 245, 255], "action_button_disabled_text": [127, 127, 127, 255], "action_button_disabled_border": [245, 245, 245, 255], + "action_button_shadow": [64, 47, 205, 255], + "action_button_disabled_shadow": [228, 228, 228, 255], "print_button_ready": [50, 130, 255, 255], "print_button_ready_border": [50, 130, 255, 255], From 692868a0b4f25ba4aa8f2a1fedfe05fe36c9f0c9 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 22 Nov 2018 15:45:38 +0100 Subject: [PATCH 0331/1240] Create a function that given a printer type name, it will return and abbreviated name. Contributes to CURA-5942. --- cura/PrintInformation.py | 16 +------------- cura/Settings/MachineManager.py | 21 +++++++++++++++++++ .../PrinterSelector/MachineSelectorButton.qml | 2 +- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py index e11f70a54c..f1d8e81b3a 100644 --- a/cura/PrintInformation.py +++ b/cura/PrintInformation.py @@ -395,21 +395,7 @@ class PrintInformation(QObject): return active_machine_type_name = global_container_stack.definition.getName() - abbr_machine = "" - for word in re.findall(r"[\w']+", active_machine_type_name): - if word.lower() == "ultimaker": - abbr_machine += "UM" - elif word.isdigit(): - abbr_machine += word - else: - stripped_word = self._stripAccents(word.upper()) - # - use only the first character if the word is too long (> 3 characters) - # - use the whole word if it's not too long (<= 3 characters) - if len(stripped_word) > 3: - stripped_word = stripped_word[0] - abbr_machine += stripped_word - - self._abbr_machine = abbr_machine + self._abbr_machine = self._application.getMachineManager().getAbbreviatedMachineName(active_machine_type_name) ## Utility method that strips accents from characters (eg: â -> a) def _stripAccents(self, to_strip: str) -> str: diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index f321ce94a6..a65d2ed302 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -3,6 +3,8 @@ import collections import time +import re +import unicodedata from typing import Any, Callable, List, Dict, TYPE_CHECKING, Optional, cast from UM.ConfigurationErrorMessage import ConfigurationErrorMessage @@ -1537,3 +1539,22 @@ class MachineManager(QObject): with postponeSignals(*self._getContainerChangedSignals(), compress = CompressTechnique.CompressPerParameterValue): self.updateMaterialWithVariant(None) self._updateQualityWithMaterial() + + ## This function will translate any printer type name to an abbreviated printer type name + @pyqtSlot(str, result = str) + def getAbbreviatedMachineName(self, machine_type_name: str) -> str: + abbr_machine = "" + for word in re.findall(r"[\w']+", machine_type_name): + if word.lower() == "ultimaker": + abbr_machine += "UM" + elif word.isdigit(): + abbr_machine += word + else: + stripped_word = ''.join(char for char in unicodedata.normalize('NFD', word.upper()) if unicodedata.category(char) != 'Mn') + # - use only the first character if the word is too long (> 3 characters) + # - use the whole word if it's not too long (<= 3 characters) + if len(stripped_word) > 3: + stripped_word = stripped_word[0] + abbr_machine += stripped_word + + return abbr_machine diff --git a/resources/qml/PrinterSelector/MachineSelectorButton.qml b/resources/qml/PrinterSelector/MachineSelectorButton.qml index e98036dbc8..e7b44a4447 100644 --- a/resources/qml/PrinterSelector/MachineSelectorButton.qml +++ b/resources/qml/PrinterSelector/MachineSelectorButton.qml @@ -65,7 +65,7 @@ Button model: printerTypesList delegate: Cura.PrinterTypeLabel { - text: modelData + text: Cura.MachineManager.getAbbreviatedMachineName(modelData) } } } From 06cb628699bf23e04c5ab6e06b462582053f64c3 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 22 Nov 2018 15:46:56 +0100 Subject: [PATCH 0332/1240] Update desktop and mimeinfo to add gcode mime type CURA-5878 --- cura.desktop.in | 2 +- cura.sharedmimeinfo | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/cura.desktop.in b/cura.desktop.in index fbe8b30fed..b0195015a5 100644 --- a/cura.desktop.in +++ b/cura.desktop.in @@ -13,6 +13,6 @@ TryExec=@CMAKE_INSTALL_FULL_BINDIR@/cura Icon=cura-icon Terminal=false Type=Application -MimeType=model/stl;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;image/bmp;image/gif;image/jpeg;image/png;model/x3d+xml; +MimeType=model/stl;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;image/bmp;image/gif;image/jpeg;image/png;model/x3d+xml;text/x-gcode; Categories=Graphics; Keywords=3D;Printing;Slicer; diff --git a/cura.sharedmimeinfo b/cura.sharedmimeinfo index 23d38795eb..ed9099d425 100644 --- a/cura.sharedmimeinfo +++ b/cura.sharedmimeinfo @@ -19,4 +19,12 @@ + + + Gcode file + + + + + \ No newline at end of file From 4c26262054877c502e9244d06df9c2dce138c365 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 22 Nov 2018 17:06:56 +0100 Subject: [PATCH 0333/1240] Use re-usable TabRow component Contributes to issue CURA-5876. --- .../Menus/ConfigurationMenu/CustomConfiguration.qml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index d424f35ebe..50ff108431 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -29,22 +29,19 @@ Item } } - TabBar + UM.TabRow { id: tabBar - onCurrentIndexChanged: Cura.ExtruderManager.setActiveExtruderIndex(currentIndex) anchors.top: header.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height - width: parent.width - height: 50 + + onCurrentIndexChanged: Cura.ExtruderManager.setActiveExtruderIndex(currentIndex) + Repeater { model: extrudersModel - - delegate: TabButton + delegate: UM.TabRowButton { - width: ListView.view != null ? Math.round(ListView.view.width / extrudersModel.rowCount()): 0 - height: parent.height contentItem: Item { Cura.ExtruderIcon From 1d84bd735660cc8c8b98157854fc15b46d5cd55e Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 22 Nov 2018 17:37:10 +0100 Subject: [PATCH 0334/1240] Improve the machine selector header to show an icon in case it is a network printer. Contributes to CURA-5942. --- .../qml/ActionPanel/OutputProcessWidget.qml | 2 ++ .../qml/ActionPanel/SliceProcessWidget.qml | 1 + resources/qml/IconLabel.qml | 13 ++++++----- .../qml/PrinterSelector/MachineSelector.qml | 22 ++++++++++++++----- ...us_connected.svg => printer_connected.svg} | 0 .../themes/cura-light/icons/printer_group.svg | 22 +++++++++++++------ .../cura-light/icons/printer_single.svg | 15 ++++++------- .../cura-light/icons/tab_status_busy.svg | 13 ----------- .../cura-light/icons/tab_status_finished.svg | 13 ----------- .../cura-light/icons/tab_status_paused.svg | 13 ----------- .../cura-light/icons/tab_status_stopped.svg | 13 ----------- .../cura-light/icons/tab_status_unknown.svg | 13 ----------- resources/themes/cura-light/theme.json | 1 + 13 files changed, 50 insertions(+), 91 deletions(-) rename resources/themes/cura-light/icons/{tab_status_connected.svg => printer_connected.svg} (100%) delete mode 100644 resources/themes/cura-light/icons/tab_status_busy.svg delete mode 100644 resources/themes/cura-light/icons/tab_status_finished.svg delete mode 100644 resources/themes/cura-light/icons/tab_status_paused.svg delete mode 100644 resources/themes/cura-light/icons/tab_status_stopped.svg delete mode 100644 resources/themes/cura-light/icons/tab_status_unknown.svg diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml index 3c4386f079..87f9d2015d 100644 --- a/resources/qml/ActionPanel/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -47,6 +47,7 @@ Column { id: estimatedTime width: parent.width + height: childrenRect.height text: PrintInformation.currentPrintTime.getDisplayString(UM.DurationFormat.Long) source: UM.Theme.getIcon("clock") @@ -57,6 +58,7 @@ Column { id: estimatedCosts width: parent.width + height: childrenRect.height property var printMaterialLengths: PrintInformation.materialLengths property var printMaterialWeights: PrintInformation.materialWeights diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index 2d4a7b6b89..9c46539220 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -44,6 +44,7 @@ Column { id: message width: parent.width + height: childrenRect.height visible: widget.backendState == UM.Backend.Error text: catalog.i18nc("@label:PrintjobStatus", "Unable to Slice") diff --git a/resources/qml/IconLabel.qml b/resources/qml/IconLabel.qml index 7c90382892..90930e91c7 100644 --- a/resources/qml/IconLabel.qml +++ b/resources/qml/IconLabel.qml @@ -16,32 +16,35 @@ Item property alias source: icon.source property alias color: label.color property alias font: label.font - - height: childrenRect.height + property alias iconSize: icon.width UM.RecolorImage { id: icon anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter - source: UM.Theme.getIcon("dot") + source: "" width: UM.Theme.getSize("section_icon").width - height: UM.Theme.getSize("section_icon").height + height: width sourceSize.width: width sourceSize.height: height color: label.color + visible: source != "" } Label { id: label - anchors.left: icon.right + anchors.left: icon.visible ? icon.right : parent.left + anchors.right: parent.right anchors.leftMargin: UM.Theme.getSize("thin_margin").width anchors.verticalCenter: icon.verticalCenter text: "Empty label" + elide: Text.ElideRight color: UM.Theme.getColor("text") font: UM.Theme.getFont("very_small") renderType: Text.NativeRendering diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index 66ed4d4a2c..d10478227a 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -14,6 +14,7 @@ Cura.ExpandableComponent id: machineSelector property bool isNetworkPrinter: Cura.MachineManager.activeMachineNetworkKey != "" + property var outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null popupPadding: 0 popupAlignment: Cura.ExpandableComponent.PopupAlignment.AlignLeft @@ -25,15 +26,24 @@ Cura.ExpandableComponent name: "cura" } - headerItem: Label + headerItem: Cura.IconLabel { text: isNetworkPrinter ? Cura.MachineManager.activeMachineNetworkGroupName : Cura.MachineManager.activeMachineName - verticalAlignment: Text.AlignVCenter - height: parent.height - elide: Text.ElideRight - renderType: Text.NativeRendering - font: UM.Theme.getFont("default") + source: + { + if (isNetworkPrinter && machineSelector.outputDevice != null) + { + if (machineSelector.outputDevice.clusterSize > 1) + { + return UM.Theme.getIcon("printer_group") + } + return UM.Theme.getIcon("printer_single") + } + return "" + } + font: UM.Theme.getFont("medium") color: UM.Theme.getColor("text") + iconSize: UM.Theme.getSize("machine_selector_icon").width } popupItem: Item diff --git a/resources/themes/cura-light/icons/tab_status_connected.svg b/resources/themes/cura-light/icons/printer_connected.svg similarity index 100% rename from resources/themes/cura-light/icons/tab_status_connected.svg rename to resources/themes/cura-light/icons/printer_connected.svg diff --git a/resources/themes/cura-light/icons/printer_group.svg b/resources/themes/cura-light/icons/printer_group.svg index 614bea90b8..5e439faca4 100644 --- a/resources/themes/cura-light/icons/printer_group.svg +++ b/resources/themes/cura-light/icons/printer_group.svg @@ -1,12 +1,20 @@ - - - icn_groupPrinters + + + Icon/ group printer/ disconnected Created with Sketch. - - - - + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/themes/cura-light/icons/printer_single.svg b/resources/themes/cura-light/icons/printer_single.svg index f7dc83987d..69c4e212bc 100644 --- a/resources/themes/cura-light/icons/printer_single.svg +++ b/resources/themes/cura-light/icons/printer_single.svg @@ -1,13 +1,12 @@ - - - icn_singlePrinter + + + Icon/ single printer/ disconnected Created with Sketch. - - - - - + + + + diff --git a/resources/themes/cura-light/icons/tab_status_busy.svg b/resources/themes/cura-light/icons/tab_status_busy.svg deleted file mode 100644 index debe4f6360..0000000000 --- a/resources/themes/cura-light/icons/tab_status_busy.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - Busy - Created with Sketch. - - - - - - - - \ No newline at end of file diff --git a/resources/themes/cura-light/icons/tab_status_finished.svg b/resources/themes/cura-light/icons/tab_status_finished.svg deleted file mode 100644 index 2519f2f862..0000000000 --- a/resources/themes/cura-light/icons/tab_status_finished.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - Wait cleanup - Created with Sketch. - - - - - - - - \ No newline at end of file diff --git a/resources/themes/cura-light/icons/tab_status_paused.svg b/resources/themes/cura-light/icons/tab_status_paused.svg deleted file mode 100644 index bab6c9ca6b..0000000000 --- a/resources/themes/cura-light/icons/tab_status_paused.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - paused - Created with Sketch. - - - - - - - - \ No newline at end of file diff --git a/resources/themes/cura-light/icons/tab_status_stopped.svg b/resources/themes/cura-light/icons/tab_status_stopped.svg deleted file mode 100644 index c9b150db3a..0000000000 --- a/resources/themes/cura-light/icons/tab_status_stopped.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - Aborted - Created with Sketch. - - - - - - - - \ No newline at end of file diff --git a/resources/themes/cura-light/icons/tab_status_unknown.svg b/resources/themes/cura-light/icons/tab_status_unknown.svg deleted file mode 100644 index 9f413baffc..0000000000 --- a/resources/themes/cura-light/icons/tab_status_unknown.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - Unknown - Created with Sketch. - - - - - - - - \ No newline at end of file diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 61b7600384..8f110db65d 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -396,6 +396,7 @@ "machine_selector_widget": [20.0, 4.0], "machine_selector_widget_content": [25.0, 32.0], + "machine_selector_icon": [2.66, 2.66], "views_selector": [16.0, 4.5], From 5be8b2810dbcde6274513f71abaadf9e792b5fa6 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Thu, 22 Nov 2018 22:10:54 +0100 Subject: [PATCH 0335/1240] Fix: if load a model and scale it up to 0.1mm and then load another model then Cura will crash. It happens because the model 1 does not have any points for arranging it on the build plate CURA-5867 --- cura/Arranging/Arrange.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cura/Arranging/Arrange.py b/cura/Arranging/Arrange.py index 5657ee991a..32796005c8 100644 --- a/cura/Arranging/Arrange.py +++ b/cura/Arranging/Arrange.py @@ -66,6 +66,11 @@ class Arrange: continue vertices = vertices.getMinkowskiHull(Polygon.approximatedCircle(min_offset)) points = copy.deepcopy(vertices._points) + + # After scaling (like up to 0.1 mm) the node might not have points + if len(points) == 0: + continue + shape_arr = ShapeArray.fromPolygon(points, scale = scale) arranger.place(0, 0, shape_arr) From fa59a6a7dba646b18ab543dbf58c4e349e6eda4e Mon Sep 17 00:00:00 2001 From: THeijmans Date: Fri, 23 Nov 2018 09:17:32 +0100 Subject: [PATCH 0336/1240] UM3 ABS first layer temperature Fixed the first layer temperature for ABS Fine and Extra Fine profiles on the UM3 (ST-2281). --- resources/quality/ultimaker3/um3_aa0.4_ABS_High_Quality.inst.cfg | 1 + .../quality/ultimaker3/um3_aa0.4_ABS_Normal_Quality.inst.cfg | 1 + 2 files changed, 2 insertions(+) diff --git a/resources/quality/ultimaker3/um3_aa0.4_ABS_High_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_ABS_High_Quality.inst.cfg index 9ceab110e9..4e79728945 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_ABS_High_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_ABS_High_Quality.inst.cfg @@ -17,6 +17,7 @@ machine_nozzle_cool_down_speed = 0.8 machine_nozzle_heat_up_speed = 1.5 material_standby_temperature = 100 material_print_temperature = =default_material_print_temperature - 5 +material_print_temperature_layer_0 = =material_print_temperature + 15 material_initial_print_temperature = =material_print_temperature - 5 material_final_print_temperature = =material_print_temperature - 10 prime_tower_enable = False diff --git a/resources/quality/ultimaker3/um3_aa0.4_ABS_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_ABS_Normal_Quality.inst.cfg index e5b699c35f..3bded3b97c 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_ABS_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_ABS_Normal_Quality.inst.cfg @@ -14,6 +14,7 @@ variant = AA 0.4 [values] machine_nozzle_cool_down_speed = 0.85 machine_nozzle_heat_up_speed = 1.5 +material_print_temperature_layer_0 = =material_print_temperature + 10 material_initial_print_temperature = =material_print_temperature - 5 material_final_print_temperature = =material_print_temperature - 10 material_standby_temperature = 100 From bb5c0326de589ee09b2398b74a542f838d6ea8e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Fri, 23 Nov 2018 09:20:19 +0100 Subject: [PATCH 0337/1240] Used duoble quotes iso single quotes --- plugins/UM3NetworkPrinting/src/Models.py | 52 ++++++++++++------------ 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Models.py b/plugins/UM3NetworkPrinting/src/Models.py index d5e1007555..bcdeb8299c 100644 --- a/plugins/UM3NetworkPrinting/src/Models.py +++ b/plugins/UM3NetworkPrinting/src/Models.py @@ -2,32 +2,32 @@ # Cura is released under the terms of the LGPLv3 or higher. from collections import namedtuple -ClusterMaterial = namedtuple('ClusterMaterial', [ - 'guid', # Type: str - 'material', # Type: str - 'brand', # Type: str - 'version', # Type: int - 'color', # Type: str - 'density' # Type: str +ClusterMaterial = namedtuple("ClusterMaterial", [ + "guid", # Type: str + "material", # Type: str + "brand", # Type: str + "version", # Type: int + "color", # Type: str + "density" # Type: str ]) -LocalMaterial = namedtuple('LocalMaterial', [ - 'GUID', # Type: str - 'id', # Type: str - 'type', # Type: str - 'status', # Type: str - 'base_file', # Type: str - 'setting_version', # Type: int - 'version', # Type: int - 'name', # Type: str - 'brand', # Type: str - 'material', # Type: str - 'color_name', # Type: str - 'color_code', # Type: str - 'description', # Type: str - 'adhesion_info', # Type: str - 'approximate_diameter', # Type: str - 'properties', # Type: str - 'definition', # Type: str - 'compatible' # Type: str +LocalMaterial = namedtuple("LocalMaterial", [ + "GUID", # Type: str + "id", # Type: str + "type", # Type: str + "status", # Type: str + "base_file", # Type: str + "setting_version", # Type: int + "version", # Type: int + "name", # Type: str + "brand", # Type: str + "material", # Type: str + "color_name", # Type: str + "color_code", # Type: str + "description", # Type: str + "adhesion_info", # Type: str + "approximate_diameter", # Type: str + "properties", # Type: str + "definition", # Type: str + "compatible" # Type: str ]) From 294527f7febf96d81f4bcdf62b142a552d58fbbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Fri, 23 Nov 2018 09:21:09 +0100 Subject: [PATCH 0338/1240] Review changes --- .../UM3NetworkPrinting/src/SendMaterialJob.py | 19 ++++++------ .../tests/TestSendMaterialJob.py | 29 +++++++++++++------ 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py index 72269040e7..48760af28e 100644 --- a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py @@ -7,6 +7,7 @@ from typing import Dict, TYPE_CHECKING, Set from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest +from UM.Application import Application from UM.Job import Job from UM.Logger import Logger from UM.MimeTypeDatabase import MimeTypeDatabase @@ -27,7 +28,6 @@ class SendMaterialJob(Job): def __init__(self, device: "ClusterUM3OutputDevice") -> None: super().__init__() self.device = device # type: ClusterUM3OutputDevice - self._application = CuraApplication.getInstance() # type: CuraApplication ## Send the request to the printer and register a callback def run(self) -> None: @@ -44,12 +44,9 @@ class SendMaterialJob(Job): return # Collect materials from the printer's reply and send the missing ones if needed. - try: - remote_materials_by_guid = self._parseReply(reply) - if remote_materials_by_guid: - self._sendMissingMaterials(remote_materials_by_guid) - except TypeError: - Logger.logException("w", "Error parsing materials from printer") + remote_materials_by_guid = self._parseReply(reply) + if remote_materials_by_guid: + self._sendMissingMaterials(remote_materials_by_guid) ## Determine which materials should be updated and send them to the printer. # @@ -158,8 +155,12 @@ class SendMaterialJob(Job): try: remote_materials = json.loads(reply.readAll().data().decode("utf-8")) return {material["guid"]: ClusterMaterial(**material) for material in remote_materials} + except UnicodeDecodeError: + Logger.log("e", "Request material storage on printer: I didn't understand the printer's answer.") except json.JSONDecodeError: - Logger.logException("w", "Error parsing materials from printer") + Logger.log("e", "Request material storage on printer: I didn't understand the printer's answer.") + except TypeError: + Logger.log("e", "Request material storage on printer: Printer's answer was missing GUIDs.") ## Retrieves a list of local materials # @@ -168,7 +169,7 @@ class SendMaterialJob(Job): # \return a dictionary of LocalMaterial objects by GUID def _getLocalMaterials(self) -> Dict[str, LocalMaterial]: result = {} # type: Dict[str, LocalMaterial] - container_registry = self._application.getContainerRegistry() + container_registry = Application.getInstance().getContainerRegistry() material_containers = container_registry.findContainersMetadata(type = "material") # Find the latest version of all material containers in the registry. diff --git a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py index 548704fd33..f5a475b3ab 100644 --- a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py @@ -8,7 +8,7 @@ from unittest.mock import patch, call from PyQt5.QtCore import QByteArray from UM.MimeTypeDatabase import MimeType -from cura.CuraApplication import CuraApplication +from UM.Application import Application from plugins.UM3NetworkPrinting.src.SendMaterialJob import SendMaterialJob @@ -70,6 +70,17 @@ class TestSendMaterialJob(TestCase): self.assertEqual([call.attribute(0), call.errorString()], reply_mock.method_calls) self.assertEqual(0, device_mock.createFormPart.call_count) + def test__onGetRemoteMaterials_withWrongEncoding(self, reply_mock, device_mock): + reply_mock.attribute.return_value = 200 + reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("cp500")) + job = SendMaterialJob(device_mock) + job._onGetRemoteMaterials(reply_mock) + + # We expect the reply to be called once to try to get the printers from the list (readAll()). + # Given that the parsing fails we do no expect the device to be called for any follow up. + self.assertEqual([call.attribute(0), call.readAll()], reply_mock.method_calls) + self.assertEqual(0, device_mock.createFormPart.call_count) + def test__onGetRemoteMaterials_withBadJsonAnswer(self, reply_mock, device_mock): reply_mock.attribute.return_value = 200 reply_mock.readAll.return_value = QByteArray(b"Six sick hicks nick six slick bricks with picks and sticks.") @@ -95,7 +106,7 @@ class TestSendMaterialJob(TestCase): self.assertEqual(0, device_mock.createFormPart.call_count) @patch("cura.Settings.CuraContainerRegistry") - @patch("cura.CuraApplication") + @patch("UM.Application") def test__onGetRemoteMaterials_withInvalidVersionInLocalMaterial(self, application_mock, container_registry_mock, reply_mock, device_mock): reply_mock.attribute.return_value = 200 @@ -107,7 +118,7 @@ class TestSendMaterialJob(TestCase): application_mock.getContainerRegistry.return_value = container_registry_mock - with mock.patch.object(CuraApplication, "getInstance", new = lambda: application_mock): + with mock.patch.object(Application, "getInstance", new = lambda: application_mock): job = SendMaterialJob(device_mock) job._onGetRemoteMaterials(reply_mock) @@ -117,7 +128,7 @@ class TestSendMaterialJob(TestCase): self.assertEqual(0, device_mock.createFormPart.call_count) @patch("cura.Settings.CuraContainerRegistry") - @patch("cura.CuraApplication") + @patch("UM.Application") def test__onGetRemoteMaterials_withNoUpdate(self, application_mock, container_registry_mock, reply_mock, device_mock): application_mock.getContainerRegistry.return_value = container_registry_mock @@ -129,7 +140,7 @@ class TestSendMaterialJob(TestCase): reply_mock.attribute.return_value = 200 reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii")) - with mock.patch.object(CuraApplication, "getInstance", new = lambda: application_mock): + with mock.patch.object(Application, "getInstance", new = lambda: application_mock): job = SendMaterialJob(device_mock) job._onGetRemoteMaterials(reply_mock) @@ -140,7 +151,7 @@ class TestSendMaterialJob(TestCase): self.assertEqual(0, device_mock.postFormWithParts.call_count) @patch("cura.Settings.CuraContainerRegistry") - @patch("cura.CuraApplication") + @patch("UM.Application") def test__onGetRemoteMaterials_withUpdatedMaterial(self, application_mock, container_registry_mock, reply_mock, device_mock): application_mock.getContainerRegistry.return_value = container_registry_mock @@ -154,7 +165,7 @@ class TestSendMaterialJob(TestCase): reply_mock.attribute.return_value = 200 reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii")) - with mock.patch.object(CuraApplication, "getInstance", new = lambda: application_mock): + with mock.patch.object(Application, "getInstance", new = lambda: application_mock): job = SendMaterialJob(device_mock) job._onGetRemoteMaterials(reply_mock) @@ -169,7 +180,7 @@ class TestSendMaterialJob(TestCase): device_mock.method_calls) @patch("cura.Settings.CuraContainerRegistry") - @patch("cura.CuraApplication") + @patch("UM.Application") def test__onGetRemoteMaterials_withNewMaterial(self, application_mock, container_registry_mock, reply_mock, device_mock): application_mock.getContainerRegistry.return_value = container_registry_mock @@ -182,7 +193,7 @@ class TestSendMaterialJob(TestCase): reply_mock.attribute.return_value = 200 reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_BLACK]).encode("ascii")) - with mock.patch.object(CuraApplication, "getInstance", new = lambda: application_mock): + with mock.patch.object(Application, "getInstance", new = lambda: application_mock): job = SendMaterialJob(device_mock) job._onGetRemoteMaterials(reply_mock) From 9f1ce72b9e428cab43a35b84cbc7f3ae76698069 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 23 Nov 2018 10:03:08 +0100 Subject: [PATCH 0339/1240] Add Cura 4.0 printer cards Contributes to CL-1150 --- .../resources/qml/ClusterMonitorItem.qml | 40 ++- .../qml/MonitorPrintJobProgressBar.qml | 112 +++++++++ .../resources/qml/MonitorPrinterCard.qml | 228 ++++++++++++++++++ 3 files changed, 375 insertions(+), 5 deletions(-) create mode 100644 plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml create mode 100644 plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml diff --git a/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml b/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml index 19a152e6eb..6bbc338c17 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml @@ -10,14 +10,13 @@ import QtGraphicalEffects 1.0 Component { - Rectangle + Item { id: monitorFrame property var emphasisColor: UM.Theme.getColor("setting_control_border_highlight") property var cornerRadius: UM.Theme.getSize("monitor_corner_radius").width - color: "transparent" height: maximumHeight onVisibleChanged: { @@ -48,13 +47,44 @@ Component } } + ScrollView + { + id: printers + anchors + { + left: parent.left + right: parent.right + top: parent.top + topMargin: 48 * screenScaleFactor // TODO: Theme! + } + height: 264 * screenScaleFactor // TODO: Theme! + Row + { + spacing: 60 * screenScaleFactor // TODO: Theme! + + Repeater + { + model: OutputDevice.printers + + MonitorPrinterCard + { + printer: modelData + } + } + } + } + Item { id: queue - anchors.fill: parent - anchors.top: parent.top - anchors.topMargin: 400 * screenScaleFactor // TODO: Insert carousel here + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + top: printers.bottom + topMargin: 48 + } Label { diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml new file mode 100644 index 0000000000..e7f9799310 --- /dev/null +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml @@ -0,0 +1,112 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.3 +import QtQuick.Controls.Styles 1.3 +import QtQuick.Controls 1.4 +import UM 1.3 as UM + +ProgressBar +{ + property var printJob: null + property var progress: { + if (!printJob) { + return 0; + } + var result = printJob.timeElapsed / printJob.timeTotal; + if (result > 1.0) { + result = 1.0; + } + return result; + } + width: 180 * screenScaleFactor // TODO: Theme! + value: progress; + style: ProgressBarStyle { + property var remainingTime: { + if (!printJob) { + return 0; + } + /* Sometimes total minus elapsed is less than 0. Use Math.max() to prevent remaining + time from ever being less than 0. Negative durations cause strange behavior such + as displaying "-1h -1m". */ + return Math.max(printer.activePrintJob.timeTotal - printer.activePrintJob.timeElapsed, 0); + } + property var progressText: { + if (!printJob) { + return ""; + } + switch (printJob.state) { + case "wait_cleanup": + if (printJob.timeTotal > printJob.timeElapsed) { + return catalog.i18nc("@label:status", "Aborted"); + } + return catalog.i18nc("@label:status", "Finished"); + case "pre_print": + case "sent_to_printer": + return catalog.i18nc("@label:status", "Preparing"); + case "aborted": + return catalog.i18nc("@label:status", "Aborted"); + case "wait_user_action": + return catalog.i18nc("@label:status", "Aborted"); + case "pausing": + return catalog.i18nc("@label:status", "Pausing"); + case "paused": + return OutputDevice.formatDuration( remainingTime ); + case "resuming": + return catalog.i18nc("@label:status", "Resuming"); + case "queued": + return catalog.i18nc("@label:status", "Action required"); + default: + return OutputDevice.formatDuration( remainingTime ); + } + } + background: Rectangle { + color: "#e4e4f2" // TODO: Theme! + implicitHeight: visible ? 8 : 0; + implicitWidth: 180; + radius: 4 + } + progress: Rectangle { + id: progressItem; + color: { + if (!printJob) { + return "black"; + } + var state = printJob.state + var inactiveStates = [ + "pausing", + "paused", + "resuming", + "wait_cleanup" + ]; + if (inactiveStates.indexOf(state) > -1 && remainingTime > 0) { + return UM.Theme.getColor("monitor_progress_fill_inactive"); + } else { + return "#0a0850" // TODO: Theme! + } + } + radius: 4 + + Label { + id: progressLabel; + anchors { + left: parent.left; + leftMargin: getTextOffset(); + } + text: progressText; + anchors.verticalCenter: parent.verticalCenter; + color: progressItem.width + progressLabel.width < control.width ? UM.Theme.getColor("text") : UM.Theme.getColor("monitor_progress_fill_text"); + width: contentWidth; + font: UM.Theme.getFont("default"); + } + + function getTextOffset() { + if (progressItem.width + progressLabel.width + 16 < control.width) { + return progressItem.width + UM.Theme.getSize("default_margin").width; + } else { + return progressItem.width - progressLabel.width - UM.Theme.getSize("default_margin").width; + } + } + } + } +} \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml new file mode 100644 index 0000000000..2fd3f2ead2 --- /dev/null +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml @@ -0,0 +1,228 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.2 +import QtQuick.Controls 2.0 +import UM 1.3 as UM + +/** + * A Printer Card is has two main components: the printer portion and the print + * job portion, the latter being paired in the UI when a print job is paired + * a printer in-cluster. + * + * NOTE: For most labels, a fixed height with vertical alignment is used to make + * layouts more deterministic (like the fixed-size textboxes used in original + * mock-ups). This is also a stand-in for CSS's 'line-height' property. Denoted + * with '// FIXED-LINE-HEIGHT:'. + */ +Item +{ + id: base + + // The printer which all printer data is derived from + property var printer: null + + property var borderSize: 1 * screenScaleFactor // TODO: Theme, and remove from here + + width: 834 * screenScaleFactor // TODO: Theme! + height: 216 * screenScaleFactor // TODO: Theme! + + // Printer portion + Rectangle + { + id: printerInfo + border + { + color: "#EAEAEC" // TODO: Theme! + width: borderSize // TODO: Remove once themed + } + color: "white" // TODO: Theme! + width: parent.width + height: 144 * screenScaleFactor // TODO: Theme! + + Row + { + anchors + { + left: parent.left + leftMargin: 36 * screenScaleFactor // TODO: Theme! + verticalCenter: parent.verticalCenter + } + spacing: 18 * screenScaleFactor // TODO: Theme! + + Rectangle + { + id: printerImage + color: "#eeeeee" + width: 108 + height: 108 + } + + Item + { + anchors + { + verticalCenter: parent.verticalCenter + } + width: 216 * screenScaleFactor // TODO: Theme! + height: printerNameLabel.height + printerFamilyPill.height + 6 * screenScaleFactor // TODO: Theme! + + Label + { + id: printerNameLabel + text: printer && printer.name ? printer.name : "" + color: "#414054" // TODO: Theme! + elide: Text.ElideRight + font: UM.Theme.getFont("large") // 16pt, bold + width: parent.width + + // FIXED-LINE-HEIGHT: + height: 18 * screenScaleFactor // TODO: Theme! + verticalAlignment: Text.AlignVCenter + } + + MonitorPrinterPill + { + id: printerFamilyPill + anchors + { + top: printerNameLabel.bottom + topMargin: 6 * screenScaleFactor // TODO: Theme! + left: printerNameLabel.left + } + text: printer.type + } + } + + MonitorPrinterConfiguration + { + id: printerConfiguration + anchors.verticalCenter: parent.verticalCenter + buildplate: "Glass" + configurations: + [ + base.printer.printerConfiguration.extruderConfigurations[0], + base.printer.printerConfiguration.extruderConfigurations[1] + ] + height: 72 * screenScaleFactor // TODO: Theme! + } + } + + PrintJobContextMenu + { + id: contextButton + // anchors + // { + // right: parent.right + // rightMargin: 8 * screenScaleFactor // TODO: Theme! + // top: parent.top + // topMargin: 8 * screenScaleFactor // TODO: Theme! + // } + printJob: base.printJob + width: 32 * screenScaleFactor // TODO: Theme! + height: 32 * screenScaleFactor // TODO: Theme! + } + } + + + // Print job portion + Rectangle + { + id: printJobInfo + anchors + { + top: printerInfo.bottom + topMargin: -borderSize * screenScaleFactor // TODO: Theme! + } + border + { + color: "#EAEAEC" // TODO: Theme! + width: borderSize // TODO: Remove once themed + } + color: "white" // TODO: Theme! + height: 84 * screenScaleFactor + borderSize // TODO: Remove once themed + width: parent.width + + Row + { + anchors + { + fill: parent + topMargin: 12 * screenScaleFactor + borderSize // TODO: Theme! + bottomMargin: 12 * screenScaleFactor // TODO: Theme! + leftMargin: 36 * screenScaleFactor // TODO: Theme! + } + height: childrenRect.height + spacing: 18 * screenScaleFactor // TODO: Theme! + + Item + { + anchors + { + verticalCenter: parent.verticalCenter + } + width: printerImage.width + height: childrenRect.height + MonitorPrintJobPreview + { + anchors.centerIn: parent + printJob: base.printer.activePrintJob + size: 60 * screenScaleFactor // TODO: Theme! + } + } + + Item + { + anchors + { + verticalCenter: parent.verticalCenter + } + width: 216 * screenScaleFactor // TODO: Theme! + height: printerNameLabel.height + printerFamilyPill.height + 6 * screenScaleFactor // TODO: Theme! + + Label + { + id: printerJobNameLabel + text: base.printer.activePrintJob ? base.printer.activePrintJob.name : "Untitled" // TODO: I18N + color: "#414054" // TODO: Theme! + elide: Text.ElideRight + font: UM.Theme.getFont("large") // 16pt, bold + width: parent.width + + // FIXED-LINE-HEIGHT: + height: 18 * screenScaleFactor // TODO: Theme! + verticalAlignment: Text.AlignVCenter + } + + Label + { + id: printerJobOwnerLabel + anchors + { + top: printerJobNameLabel.bottom + topMargin: 6 * screenScaleFactor // TODO: Theme! + left: printerJobNameLabel.left + } + text: printer.activePrintJob ? printer.activePrintJob.owner : "Anonymous" // TODO: I18N + color: "#53657d" // TODO: Theme! + elide: Text.ElideRight + font: UM.Theme.getFont("very_small") // 12pt, regular + width: parent.width + + // FIXED-LINE-HEIGHT: + height: 18 * screenScaleFactor // TODO: Theme! + verticalAlignment: Text.AlignVCenter + } + } + + MonitorPrintJobProgressBar + { + anchors + { + verticalCenter: parent.verticalCenter + } + printJob: printer.activePrintJob + } + } + } +} \ No newline at end of file From 76542a82d5a19b3dd9ea2a3f2c785d8fd956fc57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Fri, 23 Nov 2018 10:33:57 +0100 Subject: [PATCH 0340/1240] Removed the asserts on internals --- .../tests/TestSendMaterialJob.py | 22 ++----------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py index f5a475b3ab..b669eb192a 100644 --- a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py @@ -1,4 +1,5 @@ # Copyright (c) 2018 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import io import json @@ -66,8 +67,7 @@ class TestSendMaterialJob(TestCase): job = SendMaterialJob(device_mock) job._onGetRemoteMaterials(reply_mock) - # We expect the error string to be retrieved and the device not to be called for any follow up. - self.assertEqual([call.attribute(0), call.errorString()], reply_mock.method_calls) + # We expect the device not to be called for any follow up. self.assertEqual(0, device_mock.createFormPart.call_count) def test__onGetRemoteMaterials_withWrongEncoding(self, reply_mock, device_mock): @@ -76,9 +76,7 @@ class TestSendMaterialJob(TestCase): job = SendMaterialJob(device_mock) job._onGetRemoteMaterials(reply_mock) - # We expect the reply to be called once to try to get the printers from the list (readAll()). # Given that the parsing fails we do no expect the device to be called for any follow up. - self.assertEqual([call.attribute(0), call.readAll()], reply_mock.method_calls) self.assertEqual(0, device_mock.createFormPart.call_count) def test__onGetRemoteMaterials_withBadJsonAnswer(self, reply_mock, device_mock): @@ -87,9 +85,7 @@ class TestSendMaterialJob(TestCase): job = SendMaterialJob(device_mock) job._onGetRemoteMaterials(reply_mock) - # We expect the reply to be called once to try to get the printers from the list (readAll()). # Given that the parsing fails we do no expect the device to be called for any follow up. - self.assertEqual([call.attribute(0), call.readAll()], reply_mock.method_calls) self.assertEqual(0, device_mock.createFormPart.call_count) def test__onGetRemoteMaterials_withMissingGuidInRemoteMaterial(self, reply_mock, device_mock): @@ -100,9 +96,7 @@ class TestSendMaterialJob(TestCase): job = SendMaterialJob(device_mock) job._onGetRemoteMaterials(reply_mock) - # We expect the reply to be called once to try to get the printers from the list (readAll()). # Given that parsing fails we do not expect the device to be called for any follow up. - self.assertEqual([call.attribute(0), call.readAll()], reply_mock.method_calls) self.assertEqual(0, device_mock.createFormPart.call_count) @patch("cura.Settings.CuraContainerRegistry") @@ -122,9 +116,6 @@ class TestSendMaterialJob(TestCase): job = SendMaterialJob(device_mock) job._onGetRemoteMaterials(reply_mock) - self.assertEqual([call.attribute(0), call.readAll()], reply_mock.method_calls) - self.assertEqual([call.getContainerRegistry()], application_mock.method_calls) - self.assertEqual([call.findContainersMetadata(type = "material")], container_registry_mock.method_calls) self.assertEqual(0, device_mock.createFormPart.call_count) @patch("cura.Settings.CuraContainerRegistry") @@ -144,9 +135,6 @@ class TestSendMaterialJob(TestCase): job = SendMaterialJob(device_mock) job._onGetRemoteMaterials(reply_mock) - self.assertEqual([call.attribute(0), call.readAll()], reply_mock.method_calls) - self.assertEqual([call.getContainerRegistry()], application_mock.method_calls) - self.assertEqual([call.findContainersMetadata(type = "material")], container_registry_mock.method_calls) self.assertEqual(0, device_mock.createFormPart.call_count) self.assertEqual(0, device_mock.postFormWithParts.call_count) @@ -169,9 +157,6 @@ class TestSendMaterialJob(TestCase): job = SendMaterialJob(device_mock) job._onGetRemoteMaterials(reply_mock) - self.assertEqual([call.attribute(0), call.readAll()], reply_mock.method_calls) - self.assertEqual([call.getContainerRegistry()], application_mock.method_calls) - self.assertEqual([call.findContainersMetadata(type = "material")], container_registry_mock.method_calls) self.assertEqual(1, device_mock.createFormPart.call_count) self.assertEqual(1, device_mock.postFormWithParts.call_count) self.assertEquals( @@ -197,9 +182,6 @@ class TestSendMaterialJob(TestCase): job = SendMaterialJob(device_mock) job._onGetRemoteMaterials(reply_mock) - self.assertEqual([call.attribute(0), call.readAll()], reply_mock.method_calls) - self.assertEqual([call.getContainerRegistry()], application_mock.method_calls) - self.assertEqual([call.findContainersMetadata(type = "material")], container_registry_mock.method_calls) self.assertEqual(1, device_mock.createFormPart.call_count) self.assertEqual(1, device_mock.postFormWithParts.call_count) self.assertEquals( From 67dc415b58eec45b497b3cd29eedee538514ec0a Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 23 Nov 2018 10:46:55 +0100 Subject: [PATCH 0341/1240] Add the connected icon to the printer selector that shows a blue icon when the printer is connected. If not connected then there is no icon. Contributes to CURA-5942. --- .../qml/PrinterSelector/MachineSelector.qml | 36 +++++++++++++++++-- .../themes/cura-light/icons/connected.svg | 5 --- .../themes/cura-light/icons/disconnected.svg | 6 ---- resources/themes/cura-light/theme.json | 2 +- 4 files changed, 35 insertions(+), 14 deletions(-) delete mode 100644 resources/themes/cura-light/icons/connected.svg delete mode 100644 resources/themes/cura-light/icons/disconnected.svg diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index d10478227a..a33c54ed88 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -14,6 +14,7 @@ Cura.ExpandableComponent id: machineSelector property bool isNetworkPrinter: Cura.MachineManager.activeMachineNetworkKey != "" + property bool isPrinterConnected: Cura.MachineManager.printerConnected property var outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null popupPadding: 0 @@ -31,9 +32,9 @@ Cura.ExpandableComponent text: isNetworkPrinter ? Cura.MachineManager.activeMachineNetworkGroupName : Cura.MachineManager.activeMachineName source: { - if (isNetworkPrinter && machineSelector.outputDevice != null) + if (isNetworkPrinter) { - if (machineSelector.outputDevice.clusterSize > 1) + if (machineSelector.outputDevice != null && machineSelector.outputDevice.clusterSize > 1) { return UM.Theme.getIcon("printer_group") } @@ -44,6 +45,37 @@ Cura.ExpandableComponent font: UM.Theme.getFont("medium") color: UM.Theme.getColor("text") iconSize: UM.Theme.getSize("machine_selector_icon").width + + UM.RecolorImage + { + id: icon + + anchors.bottom: parent.bottom + x: UM.Theme.getSize("thick_margin").width + + source: UM.Theme.getIcon("printer_connected") + width: UM.Theme.getSize("printer_status_icon").width + height: UM.Theme.getSize("printer_status_icon").height + + sourceSize.width: width + sourceSize.height: height + + color: UM.Theme.getColor("primary") + visible: isNetworkPrinter && isPrinterConnected + + // Make a themable circle in the background so we can change it in other themes + Rectangle + { + id: iconBackground + anchors.centerIn: parent + // Make it a bit bigger so there is an outline + width: parent.width + 2 + height: parent.height + 2 + radius: Math.round(width / 2) + color: UM.Theme.getColor("main_background") + z: parent.z - 1 + } + } } popupItem: Item diff --git a/resources/themes/cura-light/icons/connected.svg b/resources/themes/cura-light/icons/connected.svg deleted file mode 100644 index 18423bb6c4..0000000000 --- a/resources/themes/cura-light/icons/connected.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/resources/themes/cura-light/icons/disconnected.svg b/resources/themes/cura-light/icons/disconnected.svg deleted file mode 100644 index 019dff117e..0000000000 --- a/resources/themes/cura-light/icons/disconnected.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 8f110db65d..23b1ffcada 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -449,7 +449,7 @@ "favorites_button": [2, 2], "favorites_button_icon": [1.2, 1.2], - "printer_status_icon": [1.8, 1.8], + "printer_status_icon": [1.0, 1.0], "printer_sync_icon": [1.2, 1.2], "button_tooltip": [1.0, 1.3], From 48c24b103484ddf40a1e3da13ac480a983a4f493 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 23 Nov 2018 10:49:50 +0100 Subject: [PATCH 0342/1240] Remove log entry for bonjour services added For most networks that would not be a problematic log entry. But for our own debugging on Ultimaker's network this is a very spammy log entry and doesn't add much value anyway. --- plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py index 9c070f2de2..daea696cd1 100644 --- a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py @@ -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. from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin @@ -325,13 +325,12 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): ## Handler for zeroConf detection. # Return True or False indicating if the process succeeded. - # Note that this function can take over 3 seconds to complete. Be carefull calling it from the main thread. + # Note that this function can take over 3 seconds to complete. Be careful + # calling it from the main thread. def _onServiceChanged(self, zero_conf, service_type, name, state_change): if state_change == ServiceStateChange.Added: - Logger.log("d", "Bonjour service added: %s" % name) - # First try getting info from zero-conf cache - info = ServiceInfo(service_type, name, properties={}) + info = ServiceInfo(service_type, name, properties = {}) for record in zero_conf.cache.entries_with_name(name.lower()): info.update_record(zero_conf, time(), record) From 3ba4b9fd81ac3b38aa393d71697f9e1514ecb6d7 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 23 Nov 2018 11:20:53 +0100 Subject: [PATCH 0343/1240] Add shadows to various used actionbuttons CURA-5959 --- .../ActionPanel/OutputDevicesActionButton.qml | 6 +++++- .../qml/ActionPanel/OutputProcessWidget.qml | 3 +++ .../qml/ActionPanel/SliceProcessWidget.qml | 17 ++++++++--------- resources/themes/cura-light/theme.json | 2 ++ 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/resources/qml/ActionPanel/OutputDevicesActionButton.qml b/resources/qml/ActionPanel/OutputDevicesActionButton.qml index be79a1893e..9682dddf14 100644 --- a/resources/qml/ActionPanel/OutputDevicesActionButton.qml +++ b/resources/qml/ActionPanel/OutputDevicesActionButton.qml @@ -17,7 +17,8 @@ Item id: saveToButton height: parent.height fixedWidthMode: true - + shadowEnabled: true + shadowColor: UM.Theme.getColor("primary_shadow") anchors { top: parent.top @@ -42,6 +43,9 @@ Item id: deviceSelectionMenu height: parent.height + shadowEnabled: true + shadowColor: UM.Theme.getColor("primary_shadow") + anchors { top: parent.top diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml index 3c4386f079..79b9898e49 100644 --- a/resources/qml/ActionPanel/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -115,6 +115,9 @@ Column textHoverColor: UM.Theme.getColor("text") onClicked: UM.Controller.setActiveStage("PreviewStage") visible: UM.Controller.activeStage != null && UM.Controller.activeStage.stageId != "PreviewStage" + + shadowEnabled: true + shadowColor: UM.Theme.getColor("action_button_disabled_shadow") } Cura.OutputDevicesActionButton diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index 4f10e6879b..7cce323905 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -93,21 +93,20 @@ Column // Disable the slice process when property bool disabledSlice: [UM.Backend.Done, UM.Backend.Error].indexOf(widget.backendState) != -1 - text: - { - if ([UM.Backend.NotStarted, UM.Backend.Error].indexOf(widget.backendState) != -1) - { - return catalog.i18nc("@button", "Slice") - } - return catalog.i18nc("@button", "Cancel") - } + property bool isSlicing: [UM.Backend.NotStarted, UM.Backend.Error].indexOf(widget.backendState) == -1 + + text: isSlicing ? catalog.i18nc("@button", "Cancel") : catalog.i18nc("@button", "Slice") + enabled: !autoSlice && !disabledSlice visible: !autoSlice + color: isSlicing ? UM.Theme.getColor("secondary"): UM.Theme.getColor("primary") + textColor: isSlicing ? UM.Theme.getColor("primary"): UM.Theme.getColor("button_text") + disabledColor: UM.Theme.getColor("action_button_disabled") textDisabledColor: UM.Theme.getColor("action_button_disabled_text") shadowEnabled: true - shadowColor: enabled ? UM.Theme.getColor("action_button_shadow"): UM.Theme.getColor("action_button_disabled_shadow") + shadowColor: isSlicing ? UM.Theme.getColor("secondary_shadow") : enabled ? UM.Theme.getColor("action_button_shadow"): UM.Theme.getColor("action_button_disabled_shadow") onClicked: sliceOrStopSlicing() } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 748a4e2643..c5802b6a7e 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -83,10 +83,12 @@ "primary": [50, 130, 255, 255], + "primary_shadow": [64, 47, 205, 255], "primary_hover": [48, 182, 231, 255], "primary_text": [255, 255, 255, 255], "border": [127, 127, 127, 255], "secondary": [245, 245, 245, 255], + "secondary_shadow": [228, 228, 228, 255], "main_window_header_background": [10, 8, 80, 255], "main_window_header_button_text_active": [10, 8, 80, 255], From 666ed595e5ed91cf4140b164abb745a021da03e7 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Fri, 23 Nov 2018 11:36:31 +0100 Subject: [PATCH 0344/1240] Added space --- resources/qml/PrintSetupSelector.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/qml/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector.qml index 61cc06b426..7cb47002a7 100644 --- a/resources/qml/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector.qml @@ -348,6 +348,7 @@ Cura.ExpandableComponent } } } + Component.onCompleted: { var index = Math.round(UM.Preferences.getValue("cura/active_mode")) From 1a8df9e10ecff985d8ae70ff960b4337e294f721 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 23 Nov 2018 11:46:38 +0100 Subject: [PATCH 0345/1240] Removed blue border if slicing is not possible CURA-5959 --- resources/qml/ActionPanel/SliceProcessWidget.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index 7cce323905..9a9f40ffac 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -102,7 +102,7 @@ Column color: isSlicing ? UM.Theme.getColor("secondary"): UM.Theme.getColor("primary") textColor: isSlicing ? UM.Theme.getColor("primary"): UM.Theme.getColor("button_text") - + outlineColor: "transparent" disabledColor: UM.Theme.getColor("action_button_disabled") textDisabledColor: UM.Theme.getColor("action_button_disabled_text") shadowEnabled: true From c4d0207cc13abfb75da88a8c50c3a9c19147b964 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Fri, 23 Nov 2018 13:10:48 +0100 Subject: [PATCH 0346/1240] Added close button in right top corner CURA-5941 --- resources/qml/PrintSetupSelector.qml | 33 +++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/resources/qml/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector.qml index 7cb47002a7..8271484e6a 100644 --- a/resources/qml/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector.qml @@ -151,7 +151,7 @@ Cura.ExpandableComponent color: UM.Theme.getColor("text") height: parent.height anchors.topMargin: UM.Theme.getSize("sidebar_margin").height - anchors.bottomMargin: UM.Theme.getSize("sidebar_margin").height +// anchors.bottomMargin: UM.Theme.getSize("sidebar_margin").height anchors.left: parent.left anchors.leftMargin: UM.Theme.getSize("print_setup_selector_margin").height } @@ -165,6 +165,37 @@ Cura.ExpandableComponent } + + Button + { + id: closeButton; + width: UM.Theme.getSize("message_close").width; + height: UM.Theme.getSize("message_close").height; + + anchors + { + right: parent.right; + rightMargin: UM.Theme.getSize("default_margin").width; + top: parent.top; + topMargin: 10 + } + + UM.RecolorImage + { + anchors.fill: parent; + sourceSize.width: width + sourceSize.height: width + color: UM.Theme.getColor("message_text") + source: UM.Theme.getIcon("cross1") + } + + onClicked: base.model.hideMessage(model.id) + + background: Rectangle + { + color: UM.Theme.getColor("message_background") + } + } } Rectangle From 2fc5061c41ae93d664320d30413c766eeaecdd5c Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Fri, 23 Nov 2018 14:11:43 +0100 Subject: [PATCH 0347/1240] Fix some PR comments, cleanup imports --- cura/NetworkClient.py | 6 ++--- .../NetworkedPrinterOutputDevice.py | 24 ++++++++----------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/cura/NetworkClient.py b/cura/NetworkClient.py index 20c026f4e6..eeedfeaa79 100644 --- a/cura/NetworkClient.py +++ b/cura/NetworkClient.py @@ -15,10 +15,10 @@ from cura.CuraApplication import CuraApplication # This was originally part of NetworkedPrinterOutputDevice but was moved out for re-use in other classes. class NetworkClient: - def __init__(self, application: CuraApplication = None): + def __init__(self) -> None: # Use the given application instance or get the singleton instance. - self._application = application or CuraApplication.getInstance() + self._application = CuraApplication.getInstance() # Network manager instance to use for this client. self._manager = None # type: Optional[QNetworkAccessManager] @@ -89,7 +89,7 @@ class NetworkClient: def _validateManager(self) -> None: if self._manager is None: self._createNetworkManager() - assert (self._manager is not None) + assert self._manager is not None ## Callback for when the network manager detects that authentication is required but was not given. @staticmethod diff --git a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py index b5bb1a5452..12769208f4 100644 --- a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py +++ b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py @@ -1,22 +1,18 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. - -from UM.FileHandler.FileHandler import FileHandler #For typing. -from UM.Logger import Logger -from UM.Scene.SceneNode import SceneNode #For typing. -from cura.CuraApplication import CuraApplication -from cura.NetworkClient import NetworkClient - -from cura.PrinterOutputDevice import PrinterOutputDevice, ConnectionState - -from PyQt5.QtNetwork import QHttpMultiPart, QHttpPart, QNetworkRequest, QNetworkAccessManager, QNetworkReply, QAuthenticator -from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QUrl, QCoreApplication +import os +import gzip from time import time -from typing import Any, Callable, Dict, List, Optional +from typing import Dict, List, Optional from enum import IntEnum -import os # To get the username -import gzip +from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply +from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QCoreApplication + +from UM.FileHandler.FileHandler import FileHandler +from UM.Scene.SceneNode import SceneNode +from cura.NetworkClient import NetworkClient +from cura.PrinterOutputDevice import PrinterOutputDevice, ConnectionState class AuthState(IntEnum): From 3e100775df7c295931cfc0613309205f8d8c0ea5 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Fri, 23 Nov 2018 14:14:35 +0100 Subject: [PATCH 0348/1240] Fix instantiating CloudOutputDeviceManager --- .../src/Cloud/CloudOutputDeviceManager.py | 8 ++++---- plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 08e43152ae..0ec07df923 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -26,11 +26,11 @@ class CloudOutputDeviceManager(NetworkClient): # The cloud URL to use for remote clusters. API_ROOT_PATH = "https://api.ultimaker.com/connect/v1" - def __init__(self, application: "CuraApplication"): - super().__init__(application) + def __init__(self): + super().__init__() - self._output_device_manager = application.getOutputDeviceManager() - self._account = application.getCuraAPI().account + self._output_device_manager = self._application.getOutputDeviceManager() + self._account = self._application.getCuraAPI().account # Persistent dict containing the remote clusters for the authenticated user. self._remote_clusters = {} # type: Dict[str, CloudOutputDevice] diff --git a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py index b441df9eb5..f3e2b66d50 100644 --- a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py @@ -41,7 +41,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self._zero_conf_browser = None # Create a cloud output device manager that abstract all cloud connection logic away. - self._cloud_output_device_manager = CloudOutputDeviceManager(self._application) + self._cloud_output_device_manager = CloudOutputDeviceManager() # Because the model needs to be created in the same thread as the QMLEngine, we use a signal. self.addDeviceSignal.connect(self._onAddDevice) From ef5ca6f5a9f8b2eb5ee3f5ffd84998003404ac5b Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Fri, 23 Nov 2018 14:15:21 +0100 Subject: [PATCH 0349/1240] Remove unused typing import --- .../UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 0ec07df923..75d2efd4f7 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -8,10 +8,6 @@ from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply from UM.Logger import Logger from cura.NetworkClient import NetworkClient from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputDevice import CloudOutputDevice - - -if TYPE_CHECKING: - from cura.CuraApplication import CuraApplication ## The cloud output device manager is responsible for using the Ultimaker Cloud APIs to manage remote clusters. From cf27a211df10e592c0d509321017c0f620159786 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Fri, 23 Nov 2018 14:15:40 +0100 Subject: [PATCH 0350/1240] Remove unused TYPE_CHECKING import --- .../UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 75d2efd4f7..8459527d39 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -1,7 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import json -from typing import TYPE_CHECKING, Dict, Optional +from typing import Dict, Optional from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply From a3bcdaf3b625087204a04867e8a9a374c9bfa857 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 23 Nov 2018 16:58:57 +0100 Subject: [PATCH 0351/1240] Make the popup in the printer selector resizable depending on the contents. Also there is a maximum height that will fit 9 printers. Contributes to CURA-5942. --- plugins/PrepareStage/PrepareMenu.qml | 3 + .../qml/PrinterSelector/MachineSelector.qml | 78 ++----------------- .../PrinterSelector/MachineSelectorList.qml | 78 +++++++++++++++++++ 3 files changed, 89 insertions(+), 70 deletions(-) create mode 100644 resources/qml/PrinterSelector/MachineSelectorList.qml diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index eed1da0ad8..a99426acd8 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -1,3 +1,6 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + import QtQuick 2.7 import QtQuick.Layouts 1.1 import QtQuick.Controls 1.4 diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index a33c54ed88..c73836ad6a 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -82,87 +82,24 @@ Cura.ExpandableComponent { id: popup width: UM.Theme.getSize("machine_selector_widget_content").width - height: UM.Theme.getSize("machine_selector_widget_content").height ScrollView { id: scroll width: parent.width - anchors.top: parent.top - anchors.bottom: separator.top clip: true - Column + MachineSelectorList { - id: column - // Can't use parent.width since the parent is the flickable component and not the ScrollView width: scroll.width - 2 * UM.Theme.getSize("default_lining").width x: UM.Theme.getSize("default_lining").width + property real maximumHeight: UM.Theme.getSize("machine_selector_widget_content").height - buttonRow.height - Label + onHeightChanged: { - text: catalog.i18nc("@label", "Network connected printers") - visible: networkedPrintersModel.items.length > 0 - leftPadding: UM.Theme.getSize("default_margin").width - height: visible ? contentHeight + 2 * UM.Theme.getSize("default_margin").height : 0 - renderType: Text.NativeRendering - font: UM.Theme.getFont("medium") - color: UM.Theme.getColor("text_medium") - verticalAlignment: Text.AlignVCenter - } - - Repeater - { - id: networkedPrinters - - model: UM.ContainerStacksModel - { - id: networkedPrintersModel - filter: {"type": "machine", "um_network_key": "*", "hidden": "False"} - } - - delegate: MachineSelectorButton - { - text: model.metadata["connect_group_name"] - checked: Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["connect_group_name"] - outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null - - Connections - { - target: Cura.MachineManager - onActiveMachineNetworkGroupNameChanged: checked = Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["connect_group_name"] - } - } - } - - Label - { - text: catalog.i18nc("@label", "Preset printers") - visible: virtualPrintersModel.items.length > 0 - leftPadding: UM.Theme.getSize("default_margin").width - height: visible ? contentHeight + 2 * UM.Theme.getSize("default_margin").height : 0 - renderType: Text.NativeRendering - font: UM.Theme.getFont("medium") - color: UM.Theme.getColor("text_medium") - verticalAlignment: Text.AlignVCenter - } - - Repeater - { - id: virtualPrinters - - model: UM.ContainerStacksModel - { - id: virtualPrintersModel - filter: {"type": "machine", "um_network_key": null} - } - - delegate: MachineSelectorButton - { - text: model.name - checked: Cura.MachineManager.activeMachineId == model.id - } + scroll.height = Math.min(height, maximumHeight) + popup.height = scroll.height + buttonRow.height } } } @@ -171,7 +108,7 @@ Cura.ExpandableComponent { id: separator - anchors.bottom: buttonRow.top + anchors.top: scroll.bottom width: parent.width height: UM.Theme.getSize("default_lining").height color: UM.Theme.getColor("lining") @@ -181,7 +118,8 @@ Cura.ExpandableComponent { id: buttonRow - anchors.bottom: parent.bottom + // The separator is inside the buttonRow. This is to avoid some weird behaviours with the scroll bar. + anchors.top: separator.top anchors.horizontalCenter: parent.horizontalCenter padding: UM.Theme.getSize("default_margin").width spacing: UM.Theme.getSize("default_margin").width diff --git a/resources/qml/PrinterSelector/MachineSelectorList.qml b/resources/qml/PrinterSelector/MachineSelectorList.qml new file mode 100644 index 0000000000..11a61194b7 --- /dev/null +++ b/resources/qml/PrinterSelector/MachineSelectorList.qml @@ -0,0 +1,78 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 2.3 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +Column +{ + id: machineSelectorList + + Label + { + text: catalog.i18nc("@label", "Network connected printers") + visible: networkedPrintersModel.items.length > 0 + leftPadding: UM.Theme.getSize("default_margin").width + height: visible ? contentHeight + 2 * UM.Theme.getSize("default_margin").height : 0 + renderType: Text.NativeRendering + font: UM.Theme.getFont("medium") + color: UM.Theme.getColor("text_medium") + verticalAlignment: Text.AlignVCenter + } + + Repeater + { + id: networkedPrinters + + model: UM.ContainerStacksModel + { + id: networkedPrintersModel + filter: {"type": "machine", "um_network_key": "*", "hidden": "False"} + } + + delegate: MachineSelectorButton + { + text: model.metadata["connect_group_name"] + checked: Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["connect_group_name"] + outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null + + Connections + { + target: Cura.MachineManager + onActiveMachineNetworkGroupNameChanged: checked = Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["connect_group_name"] + } + } + } + + Label + { + text: catalog.i18nc("@label", "Preset printers") + visible: virtualPrintersModel.items.length > 0 + leftPadding: UM.Theme.getSize("default_margin").width + height: visible ? contentHeight + 2 * UM.Theme.getSize("default_margin").height : 0 + renderType: Text.NativeRendering + font: UM.Theme.getFont("medium") + color: UM.Theme.getColor("text_medium") + verticalAlignment: Text.AlignVCenter + } + + Repeater + { + id: virtualPrinters + + model: UM.ContainerStacksModel + { + id: virtualPrintersModel + filter: {"type": "machine", "um_network_key": null} + } + + delegate: MachineSelectorButton + { + text: model.name + checked: Cura.MachineManager.activeMachineId == model.id + } + } +} \ No newline at end of file From e1f3e07f049376240293cc2956c0afce4fd18432 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 23 Nov 2018 17:11:20 +0100 Subject: [PATCH 0352/1240] Changed Marketplace button to no longer use the action button The actionButton is weirdly named and overly used. That being said, the marketplace button is unique, so there is little sense in re-using it --- resources/qml/MainWindow/MainWindowHeader.qml | 35 ++++++++++++------- resources/themes/cura-light/theme.json | 7 ---- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/resources/qml/MainWindow/MainWindowHeader.qml b/resources/qml/MainWindow/MainWindowHeader.qml index 59ec542e6b..ceb27dd726 100644 --- a/resources/qml/MainWindow/MainWindowHeader.qml +++ b/resources/qml/MainWindow/MainWindowHeader.qml @@ -2,6 +2,7 @@ // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.7 +import QtQuick.Controls 2.0 as Controls2 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.1 @@ -73,25 +74,35 @@ Rectangle } // Shortcut button to quick access the Toolbox - Cura.ActionButton + Controls2.Button { + id: marketplaceButton + text: catalog.i18nc("@action:button", "Marketplace") + height: Math.round(0.5 * UM.Theme.getSize("main_window_header").height) + onClicked: Cura.Actions.browsePackages.trigger() + + background: Rectangle + { + radius: UM.Theme.getSize("action_button_radius").width + color: "transparent" + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("primary_text") + } + + contentItem: Label + { + id: label + text: marketplaceButton.text + color: UM.Theme.getColor("primary_text") + width: contentWidth + } + anchors { right: accountWidget.left rightMargin: UM.Theme.getSize("default_margin").width verticalCenter: parent.verticalCenter } - leftPadding: UM.Theme.getSize("default_margin").width - rightPadding: UM.Theme.getSize("default_margin").width - text: catalog.i18nc("@action:button", "Marketplace") - height: Math.round(0.5 * UM.Theme.getSize("main_window_header").height) - color: UM.Theme.getColor("main_window_header_secondary_button_background_active") - hoverColor: UM.Theme.getColor("main_window_header_secondary_button_background_hovered") - outlineColor: UM.Theme.getColor("main_window_header_secondary_button_outline_active") - outlineHoverColor: UM.Theme.getColor("main_window_header_secondary_button_outline_hovered") - textColor: UM.Theme.getColor("main_window_header_secondary_button_text_active") - textHoverColor: UM.Theme.getColor("main_window_header_secondary_button_text_hovered") - onClicked: Cura.Actions.browsePackages.trigger() } AccountWidget diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index c5802b6a7e..888bc9bfa6 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -98,13 +98,6 @@ "main_window_header_button_background_inactive": [255, 255, 255, 0], "main_window_header_button_background_hovered": [255, 255, 255, 102], - "main_window_header_secondary_button_text_active": [255, 255, 255, 255], - "main_window_header_secondary_button_text_hovered": [10, 8, 80, 255], - "main_window_header_secondary_button_background_active": [255, 255, 255, 0], - "main_window_header_secondary_button_background_hovered": [255, 255, 255, 255], - "main_window_header_secondary_button_outline_active": [255, 255, 255, 255], - "main_window_header_secondary_button_outline_hovered": [255, 255, 255, 255], - "account_widget_outline_active": [70, 66, 126, 255], "machine_selector_bar": [31, 36, 39, 255], From 2e262dd9d22add74f7ee5b638a64c1b9bc63ff27 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Fri, 23 Nov 2018 17:35:55 +0100 Subject: [PATCH 0353/1240] Added Icons to SidebarSimple --- resources/qml/SidebarSimple.qml | 58 ++++++++++++++++----------------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index 5e723a3d70..52fc9320f2 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -187,12 +187,11 @@ Item } } - Label + IconWithText { id: qualityRowTitle + source: UM.Theme.getIcon("category_layer_height") text: catalog.i18nc("@label", "Layer Height") - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") } // Show titles for the each quality slider ticks @@ -532,17 +531,19 @@ Item width: Math.round(UM.Theme.getSize("print_setup_widget").width * .45) - UM.Theme.getSize("thick_margin").width - Label + IconWithText { id: infillLabel - text: catalog.i18nc("@label", "Infill") - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") + source: UM.Theme.getIcon("category_infill") + text: catalog.i18nc("@label", "Infill") + " (%)" - anchors.top: parent.top - anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height * 1.7) - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("thick_margin").width + anchors + { + top: parent.top + topMargin: Math.round(UM.Theme.getSize("thick_margin").height * 1.7) + left: parent.left + leftMargin: UM.Theme.getSize("thick_margin").width + } } } @@ -855,23 +856,23 @@ Item // // Enable support // - Label + IconWithText { id: enableSupportLabel visible: enableSupportCheckBox.visible + source: UM.Theme.getIcon("category_support") + text: catalog.i18nc("@label", "Support") - anchors.top: infillCellRight.bottom - anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height * 1.5) - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("thick_margin").width - anchors.right: infillCellLeft.right - anchors.rightMargin: UM.Theme.getSize("thick_margin").width - anchors.verticalCenter: enableSupportCheckBox.verticalCenter - - text: catalog.i18nc("@label", "Generate Support") - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - elide: Text.ElideRight + anchors + { + top: infillCellRight.bottom + topMargin: Math.round(UM.Theme.getSize("thick_margin").height * 1.5) + left: parent.left + leftMargin: UM.Theme.getSize("thick_margin").width + right: infillCellLeft.right + rightMargin: UM.Theme.getSize("thick_margin").width + verticalCenter: enableSupportCheckBox.verticalCenter + } } CheckBox @@ -980,15 +981,12 @@ Item } - Label + IconWithText { id: adhesionHelperLabel visible: adhesionCheckBox.visible - - text: catalog.i18nc("@label", "Build Plate Adhesion") - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - elide: Text.ElideRight + source: UM.Theme.getIcon("category_adhesion") + text: catalog.i18nc("@label", "Adhesion") anchors { From af1ee535788b4d64945ef578bde3764551297ab4 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 23 Nov 2018 17:41:58 +0100 Subject: [PATCH 0354/1240] Fix the hover effect of action button CURA-5959 --- resources/qml/ActionButton.qml | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index 8cd53b5d7e..6dd5839bb9 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -12,7 +12,6 @@ import UM 1.1 as UM Button { id: button - property alias cursorShape: mouseArea.cursorShape property alias iconSource: buttonIcon.source property alias textFont: buttonText.font property alias cornerRadius: backgroundRect.radius @@ -22,12 +21,14 @@ Button property color hoverColor: UM.Theme.getColor("primary_hover") property color disabledColor: color property color textColor: UM.Theme.getColor("button_text") - property color textHoverColor: UM.Theme.getColor("button_text_hover") + property color textHoverColor: textColor property color textDisabledColor: textColor property color outlineColor: color property color outlineHoverColor: hoverColor property color outlineDisabledColor: outlineColor + hoverEnabled: true + property alias shadowColor: shadow.color property alias shadowEnabled: shadow.visible @@ -95,13 +96,4 @@ Button delay: 500 visible: text != "" && button.hovered } - - MouseArea - { - id: mouseArea - anchors.fill: parent - // Ensure that the button will still accept the clicks on it's own. - onPressed: mouse.accepted = false - hoverEnabled: true - } } From b82ea58bc815b28815cd6991230d5a3b4d8ca78d Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 23 Nov 2018 18:07:50 +0100 Subject: [PATCH 0355/1240] Changed all the action buttons to either use primary or secondary button CURA-5959 --- resources/qml/Account/GeneralOperations.qml | 8 +-- resources/qml/Account/UserOperations.qml | 8 +-- .../ActionPanel/OutputDevicesActionButton.qml | 5 +- .../qml/ActionPanel/OutputProcessWidget.qml | 9 +--- .../qml/ActionPanel/SliceProcessWidget.qml | 49 ++++++++++--------- resources/qml/PrimaryButton.qml | 18 +++++++ resources/qml/SecondaryButton.qml | 18 +++++++ resources/themes/cura-light/theme.json | 10 ++++ 8 files changed, 81 insertions(+), 44 deletions(-) create mode 100644 resources/qml/PrimaryButton.qml create mode 100644 resources/qml/SecondaryButton.qml diff --git a/resources/qml/Account/GeneralOperations.qml b/resources/qml/Account/GeneralOperations.qml index 4614c4ba88..b9f1025d5e 100644 --- a/resources/qml/Account/GeneralOperations.qml +++ b/resources/qml/Account/GeneralOperations.qml @@ -11,20 +11,16 @@ Row { spacing: UM.Theme.getSize("default_margin").width - Cura.ActionButton + Cura.SecondaryButton { width: UM.Theme.getSize("account_button").width height: UM.Theme.getSize("account_button").height text: catalog.i18nc("@button", "Create account") - color: UM.Theme.getColor("secondary") - hoverColor: UM.Theme.getColor("secondary") - textColor: UM.Theme.getColor("main_window_header_button_text_active") - textHoverColor: UM.Theme.getColor("main_window_header_button_text_active") onClicked: Qt.openUrlExternally("https://account.ultimaker.com/app/create") fixedWidthMode: true } - Cura.ActionButton + Cura.PrimaryButton { width: UM.Theme.getSize("account_button").width height: UM.Theme.getSize("account_button").height diff --git a/resources/qml/Account/UserOperations.qml b/resources/qml/Account/UserOperations.qml index c167813425..b9ffa395d6 100644 --- a/resources/qml/Account/UserOperations.qml +++ b/resources/qml/Account/UserOperations.qml @@ -11,20 +11,16 @@ Row { spacing: UM.Theme.getSize("default_margin").width - Cura.ActionButton + Cura.SecondaryButton { width: UM.Theme.getSize("account_button").width height: UM.Theme.getSize("account_button").height text: catalog.i18nc("@button", "Manage account") - color: UM.Theme.getColor("secondary") - hoverColor: UM.Theme.getColor("secondary") - textColor: UM.Theme.getColor("main_window_header_button_text_active") - textHoverColor: UM.Theme.getColor("main_window_header_button_text_active") onClicked: Qt.openUrlExternally("https://account.ultimaker.com") fixedWidthMode: true } - Cura.ActionButton + Cura.PrimaryButton { width: UM.Theme.getSize("account_button").width height: UM.Theme.getSize("account_button").height diff --git a/resources/qml/ActionPanel/OutputDevicesActionButton.qml b/resources/qml/ActionPanel/OutputDevicesActionButton.qml index 9682dddf14..d24d440241 100644 --- a/resources/qml/ActionPanel/OutputDevicesActionButton.qml +++ b/resources/qml/ActionPanel/OutputDevicesActionButton.qml @@ -12,13 +12,12 @@ Item { id: widget - Cura.ActionButton + Cura.PrimaryButton { id: saveToButton height: parent.height fixedWidthMode: true - shadowEnabled: true - shadowColor: UM.Theme.getColor("primary_shadow") + anchors { top: parent.top diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml index 79b9898e49..ddbe709a84 100644 --- a/resources/qml/ActionPanel/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -101,18 +101,13 @@ Column spacing: UM.Theme.getSize("default_margin").width width: parent.width - Cura.ActionButton + Cura.SecondaryButton { id: previewStageShortcut - leftPadding: UM.Theme.getSize("default_margin").width - rightPadding: UM.Theme.getSize("default_margin").width height: UM.Theme.getSize("action_panel_button").height text: catalog.i18nc("@button", "Preview") - color: UM.Theme.getColor("secondary") - hoverColor: UM.Theme.getColor("secondary") - textColor: UM.Theme.getColor("primary") - textHoverColor: UM.Theme.getColor("text") + onClicked: UM.Controller.setActiveStage("PreviewStage") visible: UM.Controller.activeStage != null && UM.Controller.activeStage.stageId != "PreviewStage" diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index 9a9f40ffac..199b94ab33 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -81,36 +81,41 @@ Column } } - Cura.ActionButton - { - id: prepareButton - width: parent.width - height: UM.Theme.getSize("action_panel_button").height - fixedWidthMode: true + Item + { + id: prepareButtons // Get the current value from the preferences property bool autoSlice: UM.Preferences.getValue("general/auto_slice") // Disable the slice process when - property bool disabledSlice: [UM.Backend.Done, UM.Backend.Error].indexOf(widget.backendState) != -1 - property bool isSlicing: [UM.Backend.NotStarted, UM.Backend.Error].indexOf(widget.backendState) == -1 - - text: isSlicing ? catalog.i18nc("@button", "Cancel") : catalog.i18nc("@button", "Slice") - - enabled: !autoSlice && !disabledSlice + width: parent.width + height: UM.Theme.getSize("action_panel_button").height visible: !autoSlice + Cura.PrimaryButton + { + id: sliceButton + fixedWidthMode: true + anchors.fill: parent + text: catalog.i18nc("@button", "Slice") + enabled: !autoSlice && widget.backendState != UM.Backend.Error + visible: widget.backendState == UM.Backend.NotStarted || widget.backendState == UM.Backend.Error + onClicked: sliceOrStopSlicing() + } - color: isSlicing ? UM.Theme.getColor("secondary"): UM.Theme.getColor("primary") - textColor: isSlicing ? UM.Theme.getColor("primary"): UM.Theme.getColor("button_text") - outlineColor: "transparent" - disabledColor: UM.Theme.getColor("action_button_disabled") - textDisabledColor: UM.Theme.getColor("action_button_disabled_text") - shadowEnabled: true - shadowColor: isSlicing ? UM.Theme.getColor("secondary_shadow") : enabled ? UM.Theme.getColor("action_button_shadow"): UM.Theme.getColor("action_button_disabled_shadow") - - onClicked: sliceOrStopSlicing() + Cura.SecondaryButton + { + id: cancelButton + fixedWidthMode: true + anchors.fill: parent + text: catalog.i18nc("@button", "Cancel") + enabled: sliceButton.enabled + visible: !sliceButton.visible + onClicked: sliceOrStopSlicing() + } } + // React when the user changes the preference of having the auto slice enabled Connections { @@ -118,7 +123,7 @@ Column onPreferenceChanged: { var autoSlice = UM.Preferences.getValue("general/auto_slice") - prepareButton.autoSlice = autoSlice + prepareButtons.autoSlice = autoSlice } } diff --git a/resources/qml/PrimaryButton.qml b/resources/qml/PrimaryButton.qml new file mode 100644 index 0000000000..8450e524e2 --- /dev/null +++ b/resources/qml/PrimaryButton.qml @@ -0,0 +1,18 @@ +import QtQuick 2.2 +import QtQuick.Controls 1.1 + +import UM 1.4 as UM +import Cura 1.1 as Cura + + +Cura.ActionButton +{ + shadowEnabled: true + shadowColor: enabled ? UM.Theme.getColor("primary_button_shadow"): UM.Theme.getColor("action_button_disabled_shadow") + color: UM.Theme.getColor("primary_button") + textColor: UM.Theme.getColor("primary_button_text") + outlineColor: "transparent" + disabledColor: UM.Theme.getColor("action_button_disabled") + textDisabledColor: UM.Theme.getColor("action_button_disabled_text") + hoverColor: UM.Theme.getColor("primary_button_hover") +} \ No newline at end of file diff --git a/resources/qml/SecondaryButton.qml b/resources/qml/SecondaryButton.qml new file mode 100644 index 0000000000..0e6b79b3a7 --- /dev/null +++ b/resources/qml/SecondaryButton.qml @@ -0,0 +1,18 @@ +import QtQuick 2.2 +import QtQuick.Controls 1.1 + +import UM 1.4 as UM +import Cura 1.1 as Cura + + +Cura.ActionButton +{ + shadowEnabled: true + shadowColor: enabled ? UM.Theme.getColor("secondary_button_shadow"): UM.Theme.getColor("action_button_disabled_shadow") + color: UM.Theme.getColor("secondary_button") + textColor: UM.Theme.getColor("secondary_button_text") + outlineColor: "transparent" + disabledColor: UM.Theme.getColor("action_button_disabled") + textDisabledColor: UM.Theme.getColor("action_button_disabled_text") + hoverColor: UM.Theme.getColor("secondary_button_hover") +} \ No newline at end of file diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 888bc9bfa6..cbdc37caa1 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -90,6 +90,16 @@ "secondary": [245, 245, 245, 255], "secondary_shadow": [228, 228, 228, 255], + "primary_button": [38,113,231,255], + "primary_button_shadow": [27,95,202, 255], + "primary_button_hover": [81,145,247, 255], + "primary_button_text": [255, 255, 255, 255], + + "secondary_button": [240,240,240, 255], + "secondary_button_shadow": [228, 228, 228, 255], + "secondary_button_hover": [228,228,228, 255], + "secondary_button_text": [30,102,215, 255], + "main_window_header_background": [10, 8, 80, 255], "main_window_header_button_text_active": [10, 8, 80, 255], "main_window_header_button_text_inactive": [255, 255, 255, 255], From 9e92542eeec397440f2f383d68650c291e30c8d7 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Sun, 25 Nov 2018 18:14:01 +0100 Subject: [PATCH 0356/1240] Fix the height of the IconLabel to avoid getting binding loops. Contributes to CURA-5942. --- resources/qml/ActionPanel/OutputProcessWidget.qml | 2 -- resources/qml/ActionPanel/SliceProcessWidget.qml | 1 - resources/qml/IconLabel.qml | 2 ++ 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml index 87f9d2015d..3c4386f079 100644 --- a/resources/qml/ActionPanel/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -47,7 +47,6 @@ Column { id: estimatedTime width: parent.width - height: childrenRect.height text: PrintInformation.currentPrintTime.getDisplayString(UM.DurationFormat.Long) source: UM.Theme.getIcon("clock") @@ -58,7 +57,6 @@ Column { id: estimatedCosts width: parent.width - height: childrenRect.height property var printMaterialLengths: PrintInformation.materialLengths property var printMaterialWeights: PrintInformation.materialWeights diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index 9c46539220..2d4a7b6b89 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -44,7 +44,6 @@ Column { id: message width: parent.width - height: childrenRect.height visible: widget.backendState == UM.Backend.Error text: catalog.i18nc("@label:PrintjobStatus", "Unable to Slice") diff --git a/resources/qml/IconLabel.qml b/resources/qml/IconLabel.qml index 90930e91c7..0941254e7b 100644 --- a/resources/qml/IconLabel.qml +++ b/resources/qml/IconLabel.qml @@ -18,6 +18,8 @@ Item property alias font: label.font property alias iconSize: icon.width + implicitHeight: icon.height + UM.RecolorImage { id: icon From f3bf20ca1b8f17a9b63aa14f57df56a2e17b95f4 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Sun, 25 Nov 2018 18:24:21 +0100 Subject: [PATCH 0357/1240] Separate the view selector into a different file. --- plugins/PreviewStage/PreviewMenu.qml | 72 +--------------- .../qml/PrinterSelector/MachineSelector.qml | 2 - resources/qml/ViewsSelector.qml | 82 +++++++++++++++++++ resources/qml/qmldir | 3 +- 4 files changed, 86 insertions(+), 73 deletions(-) create mode 100644 resources/qml/ViewsSelector.qml diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index d660db549b..a1f59cd4ca 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -29,80 +29,12 @@ Item anchors.centerIn: parent height: parent.height - Cura.ExpandableComponent + Cura.ViewsSelector { - id: viewSelector - iconSource: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") + id: viewsSelector height: parent.height width: UM.Theme.getSize("views_selector").width headerCornerSide: Cura.RoundedRectangle.Direction.Left - - property var viewModel: UM.ViewModel { } - - property var activeView: - { - for (var i = 0; i < viewModel.rowCount(); i++) - { - if (viewModel.items[i].active) - { - return viewModel.items[i] - } - } - return null - } - - Component.onCompleted: - { - // Nothing was active, so just return the first one (the list is sorted by priority, so the most - // important one should be returned) - if (activeView == null) - { - UM.Controller.setActiveView(viewModel.getItem(0).id) - } - } - - headerItem: Label - { - text: viewSelector.activeView ? viewSelector.activeView.name : "" - verticalAlignment: Text.AlignVCenter - height: parent.height - elide: Text.ElideRight - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - renderType: Text.NativeRendering - } - - popupItem: Column - { - id: viewSelectorPopup - width: viewSelector.width - 2 * UM.Theme.getSize("default_margin").width - - // For some reason the height/width of the column gets set to 0 if this is not set... - Component.onCompleted: - { - height = implicitHeight - width = viewSelector.width - 2 * UM.Theme.getSize("default_margin").width - } - - Repeater - { - id: viewsList - model: viewSelector.viewModel - RoundButton - { - text: name - radius: UM.Theme.getSize("default_radius").width - checkable: true - checked: viewSelector.activeView != null ? viewSelector.activeView.id == id : false - onClicked: - { - viewSelector.togglePopup() - UM.Controller.setActiveView(id) - } - } - } - - } } // Separator line diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index c73836ad6a..b14734dfb1 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -3,8 +3,6 @@ import QtQuick 2.7 import QtQuick.Controls 2.3 -import QtQuick.Controls.Styles 1.1 -import QtQuick.Layouts 1.1 import UM 1.2 as UM import Cura 1.0 as Cura diff --git a/resources/qml/ViewsSelector.qml b/resources/qml/ViewsSelector.qml new file mode 100644 index 0000000000..0e11cccc5a --- /dev/null +++ b/resources/qml/ViewsSelector.qml @@ -0,0 +1,82 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 2.3 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +Cura.ExpandableComponent +{ + id: viewSelector + + iconSource: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") + + property var viewModel: UM.ViewModel { } + + property var activeView: + { + for (var i = 0; i < viewModel.rowCount(); i++) + { + if (viewModel.items[i].active) + { + return viewModel.items[i] + } + } + return null + } + + Component.onCompleted: + { + // Nothing was active, so just return the first one (the list is sorted by priority, so the most + // important one should be returned) + if (activeView == null) + { + UM.Controller.setActiveView(viewModel.getItem(0).id) + } + } + + headerItem: Label + { + text: viewSelector.activeView ? viewSelector.activeView.name : "" + verticalAlignment: Text.AlignVCenter + height: parent.height + elide: Text.ElideRight + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + renderType: Text.NativeRendering + } + + popupItem: Column + { + id: viewSelectorPopup + width: viewSelector.width - 2 * UM.Theme.getSize("default_margin").width + + // For some reason the height/width of the column gets set to 0 if this is not set... + Component.onCompleted: + { + height = implicitHeight + width = viewSelector.width - 2 * UM.Theme.getSize("default_margin").width + } + + Repeater + { + id: viewsList + model: viewSelector.viewModel + RoundButton + { + text: name + radius: UM.Theme.getSize("default_radius").width + checkable: true + checked: viewSelector.activeView != null ? viewSelector.activeView.id == id : false + onClicked: + { + viewSelector.togglePopup() + UM.Controller.setActiveView(id) + } + } + } + + } +} \ No newline at end of file diff --git a/resources/qml/qmldir b/resources/qml/qmldir index 67388100ca..43ae4411af 100644 --- a/resources/qml/qmldir +++ b/resources/qml/qmldir @@ -11,4 +11,5 @@ ActionPanelWidget 1.0 ActionPanelWidget.qml IconLabel 1.0 IconLabel.qml OutputDevicesActionButton 1.0 OutputDevicesActionButton.qml ExpandableComponent 1.0 ExpandableComponent.qml -PrinterTypeLabel 1.0 PrinterTypeLabel.qml \ No newline at end of file +PrinterTypeLabel 1.0 PrinterTypeLabel.qml +ViewsSelector 1.0 ViewsSelector.qml \ No newline at end of file From 68c96a25775c6be13bdcdba64544e68837cc41c2 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Sun, 25 Nov 2018 19:42:00 +0100 Subject: [PATCH 0358/1240] Modify the header item of the view selector. --- resources/qml/ViewsSelector.qml | 36 +++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/resources/qml/ViewsSelector.qml b/resources/qml/ViewsSelector.qml index 0e11cccc5a..b9048803a2 100644 --- a/resources/qml/ViewsSelector.qml +++ b/resources/qml/ViewsSelector.qml @@ -37,15 +37,35 @@ Cura.ExpandableComponent } } - headerItem: Label + headerItem: Item { - text: viewSelector.activeView ? viewSelector.activeView.name : "" - verticalAlignment: Text.AlignVCenter - height: parent.height - elide: Text.ElideRight - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - renderType: Text.NativeRendering + Label + { + id: title + text: catalog.i18nc("@button", "View types") + verticalAlignment: Text.AlignVCenter + height: parent.height + elide: Text.ElideRight + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text_medium") + renderType: Text.NativeRendering + } + + Label + { + text: viewSelector.activeView ? viewSelector.activeView.name : "" + verticalAlignment: Text.AlignVCenter + anchors + { + left: title.right + leftMargin: UM.Theme.getSize("default_margin").width + } + height: parent.height + elide: Text.ElideRight + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + renderType: Text.NativeRendering + } } popupItem: Column From c5d0ed26513e12045b2fb84d6408a79f729a81df Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Sun, 25 Nov 2018 20:13:56 +0100 Subject: [PATCH 0359/1240] Define the look and feel of the view selector button. Adjust the sizes. --- .../qml/PrinterSelector/MachineSelector.qml | 5 +-- resources/qml/ViewsSelector.qml | 39 ++++++++++++++++--- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index b14734dfb1..b6657f112e 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -15,7 +15,7 @@ Cura.ExpandableComponent property bool isPrinterConnected: Cura.MachineManager.printerConnected property var outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null - popupPadding: 0 + popupPadding: UM.Theme.getSize("default_lining").width popupAlignment: Cura.ExpandableComponent.PopupAlignment.AlignLeft iconSource: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") @@ -90,8 +90,7 @@ Cura.ExpandableComponent MachineSelectorList { // Can't use parent.width since the parent is the flickable component and not the ScrollView - width: scroll.width - 2 * UM.Theme.getSize("default_lining").width - x: UM.Theme.getSize("default_lining").width + width: scroll.width property real maximumHeight: UM.Theme.getSize("machine_selector_widget_content").height - buttonRow.height onHeightChanged: diff --git a/resources/qml/ViewsSelector.qml b/resources/qml/ViewsSelector.qml index b9048803a2..e9fdd57177 100644 --- a/resources/qml/ViewsSelector.qml +++ b/resources/qml/ViewsSelector.qml @@ -11,6 +11,8 @@ Cura.ExpandableComponent { id: viewSelector + popupPadding: UM.Theme.getSize("default_lining").width + popupAlignment: Cura.ExpandableComponent.PopupAlignment.AlignLeft iconSource: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") property var viewModel: UM.ViewModel { } @@ -71,25 +73,51 @@ Cura.ExpandableComponent popupItem: Column { id: viewSelectorPopup - width: viewSelector.width - 2 * UM.Theme.getSize("default_margin").width + width: viewSelector.width - 2 * viewSelector.popupPadding // For some reason the height/width of the column gets set to 0 if this is not set... Component.onCompleted: { height = implicitHeight - width = viewSelector.width - 2 * UM.Theme.getSize("default_margin").width + width = viewSelector.width - 2 * viewSelector.popupPadding } Repeater { id: viewsList model: viewSelector.viewModel - RoundButton + + delegate: Button { - text: name - radius: UM.Theme.getSize("default_radius").width + id: viewsSelectorButton + text: model.name + width: parent.width + height: UM.Theme.getSize("action_button").height + leftPadding: UM.Theme.getSize("default_margin").width + rightPadding: UM.Theme.getSize("default_margin").width checkable: true checked: viewSelector.activeView != null ? viewSelector.activeView.id == id : false + + contentItem: Label + { + id: buttonText + text: viewsSelectorButton.text + color: UM.Theme.getColor("text") + font: UM.Theme.getFont("action_button") + renderType: Text.NativeRendering + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + background: Rectangle + { + id: backgroundRect + color: viewsSelectorButton.hovered ? UM.Theme.getColor("action_button_hovered") : "transparent" + radius: UM.Theme.getSize("action_button_radius").width + border.width: UM.Theme.getSize("default_lining").width + border.color: viewsSelectorButton.checked ? UM.Theme.getColor("primary") : "transparent" + } + onClicked: { viewSelector.togglePopup() @@ -97,6 +125,5 @@ Cura.ExpandableComponent } } } - } } \ No newline at end of file From 8f9d6bee6243f32c9d966202e1f9a4ea5086b611 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 26 Nov 2018 08:30:24 +0100 Subject: [PATCH 0360/1240] Document dependency on fdm_materials Without it, Cura simply won't start if you had a printer loaded with material profiles. Fixes #4460. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 70466e9c22..93abcc0c61 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,9 @@ Dependencies ------------ * [Uranium](https://github.com/Ultimaker/Uranium) Cura is built on top of the Uranium framework. * [CuraEngine](https://github.com/Ultimaker/CuraEngine) This will be needed at runtime to perform the actual slicing. +* [fdm_materials](https://github.com/Ultimaker/fdm_materials) Required to load a printer that has swappable material profiles. * [PySerial](https://github.com/pyserial/pyserial) Only required for USB printing support. -* [python-zeroconf](https://github.com/jstasiak/python-zeroconf) Only required to detect mDNS-enabled printers +* [python-zeroconf](https://github.com/jstasiak/python-zeroconf) Only required to detect mDNS-enabled printers. Build scripts ------------- From c9eb57ceade38f567fc49f5e5ad0f6f1c8913d79 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 26 Nov 2018 09:32:59 +0100 Subject: [PATCH 0361/1240] Don't copy preference file to the same location This crashes Cura on start-up for some people. --- cura/Backups/Backup.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cura/Backups/Backup.py b/cura/Backups/Backup.py index 897d5fa979..80681acb4f 100644 --- a/cura/Backups/Backup.py +++ b/cura/Backups/Backup.py @@ -46,12 +46,13 @@ class Backup: # We copy the preferences file to the user data directory in Linux as it's in a different location there. # When restoring a backup on Linux, we move it back. - if Platform.isLinux(): + if Platform.isLinux(): #TODO: This should check for the config directory not being the same as the data directory, rather than hard-coding that to Linux systems. preferences_file_name = self._application.getApplicationName() preferences_file = Resources.getPath(Resources.Preferences, "{}.cfg".format(preferences_file_name)) backup_preferences_file = os.path.join(version_data_dir, "{}.cfg".format(preferences_file_name)) - Logger.log("d", "Copying preferences file from %s to %s", preferences_file, backup_preferences_file) - shutil.copyfile(preferences_file, backup_preferences_file) + if not os.path.samefile(preferences_file, backup_preferences_file): + Logger.log("d", "Copying preferences file from %s to %s", preferences_file, backup_preferences_file) + shutil.copyfile(preferences_file, backup_preferences_file) # Create an empty buffer and write the archive to it. buffer = io.BytesIO() From 09af7a9435178bc9a6e811c7ade4889880a745f9 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 26 Nov 2018 09:34:27 +0100 Subject: [PATCH 0362/1240] Slice button will now be disabled on error CURA-5959 --- resources/qml/ActionPanel/SliceProcessWidget.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index 199b94ab33..05a9345585 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -42,7 +42,7 @@ Column Cura.IconLabel { - id: message + id: unableToSliceMessage width: parent.width visible: widget.backendState == UM.Backend.Error @@ -98,7 +98,7 @@ Column fixedWidthMode: true anchors.fill: parent text: catalog.i18nc("@button", "Slice") - enabled: !autoSlice && widget.backendState != UM.Backend.Error + enabled: widget.backendState != UM.Backend.Error visible: widget.backendState == UM.Backend.NotStarted || widget.backendState == UM.Backend.Error onClicked: sliceOrStopSlicing() } From 5469613c17c0caa34b58750a1aeeb664333886ca Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 26 Nov 2018 09:40:14 +0100 Subject: [PATCH 0363/1240] Don't fail the samefile check if second file didn't exist If the backup file didn't exist but the original did, then apparently they are not the same file so the copy should be allowed. --- cura/Backups/Backup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/Backups/Backup.py b/cura/Backups/Backup.py index 80681acb4f..714d6527fe 100644 --- a/cura/Backups/Backup.py +++ b/cura/Backups/Backup.py @@ -50,7 +50,7 @@ class Backup: preferences_file_name = self._application.getApplicationName() preferences_file = Resources.getPath(Resources.Preferences, "{}.cfg".format(preferences_file_name)) backup_preferences_file = os.path.join(version_data_dir, "{}.cfg".format(preferences_file_name)) - if not os.path.samefile(preferences_file, backup_preferences_file): + if os.path.exists(preferences_file) and (not os.path.exists(backup_preferences_file) or not os.path.samefile(preferences_file, backup_preferences_file)): Logger.log("d", "Copying preferences file from %s to %s", preferences_file, backup_preferences_file) shutil.copyfile(preferences_file, backup_preferences_file) From c8e065d7a78f3d62b26cded3cd0063e5a05fead4 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 26 Nov 2018 10:21:15 +0100 Subject: [PATCH 0364/1240] Fix code-style Contributes to CURA-5942. Co-Authored-By: diegopradogesto --- resources/qml/ExpandableComponent.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 80c65f12a7..ccfb9c6da2 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -12,7 +12,8 @@ Item id: base // Enumeration with the different possible alignments of the popup with respect of the headerItem - enum PopupAlignment { + enum PopupAlignment + { AlignLeft, AlignRight } From 5b940b524204ca56b5fafe05bad28d5e477b8ea7 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 26 Nov 2018 10:28:36 +0100 Subject: [PATCH 0365/1240] Use thick margin and change some anchors. Contributes to CURA-5942. --- resources/qml/PrinterSelector/MachineSelector.qml | 8 ++++++-- resources/qml/PrinterSelector/MachineSelectorButton.qml | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index b6657f112e..d721ed5b83 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -48,8 +48,12 @@ Cura.ExpandableComponent { id: icon - anchors.bottom: parent.bottom - x: UM.Theme.getSize("thick_margin").width + anchors + { + bottom: parent.bottom + left: parent.left + leftMargin: UM.Theme.getSize("thick_margin").width + } source: UM.Theme.getIcon("printer_connected") width: UM.Theme.getSize("printer_status_icon").width diff --git a/resources/qml/PrinterSelector/MachineSelectorButton.qml b/resources/qml/PrinterSelector/MachineSelectorButton.qml index e7b44a4447..fef6867e35 100644 --- a/resources/qml/PrinterSelector/MachineSelectorButton.qml +++ b/resources/qml/PrinterSelector/MachineSelectorButton.qml @@ -13,8 +13,8 @@ Button width: parent.width height: UM.Theme.getSize("action_button").height - leftPadding: Math.round(1.5 * UM.Theme.getSize("default_margin").width) - rightPadding: Math.round(1.5 * UM.Theme.getSize("default_margin").width) + leftPadding: UM.Theme.getSize("thick_margin").width + rightPadding: UM.Theme.getSize("thick_margin").width checkable: true property var outputDevice: null From 908628e2aadcff72826e099577049be0a6287a8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Mon, 26 Nov 2018 10:47:53 +0100 Subject: [PATCH 0366/1240] Added a model to represent a cluster --- .../src/Cloud/CloudOutputDeviceManager.py | 32 +++++++++++-------- .../UM3NetworkPrinting/src/Cloud/Models.py | 11 +++++++ 2 files changed, 29 insertions(+), 14 deletions(-) create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Models.py diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 8459527d39..e93393d736 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -8,7 +8,8 @@ from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply from UM.Logger import Logger from cura.NetworkClient import NetworkClient from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputDevice import CloudOutputDevice - +from .Models import Cluster + ## The cloud output device manager is responsible for using the Ultimaker Cloud APIs to manage remote clusters. # Keeping all cloud related logic in this class instead of the UM3OutputDevicePlugin results in more readable code. @@ -60,35 +61,38 @@ class CloudOutputDeviceManager(NetworkClient): return # Parse the response (returns the "data" field from the body). - clusters_data = self._parseStatusResponse(reply) - if not clusters_data: + clusters = self._parseStatusResponse(reply) + if not clusters: return # Add an output device for each remote cluster. # The clusters are an array of objects in a field called "data". - for cluster in clusters_data: + for cluster in clusters: self._addCloudOutputDevice(cluster) # # For testing we add a dummy device: # self._addCloudOutputDevice({ "cluster_id": "LJ0tciiuZZjarrXAvFLEZ6ox4Cvx8FvtXUlQv4vIhV6w" }) @staticmethod - def _parseStatusResponse(reply: QNetworkReply) -> Optional[dict]: + def _parseStatusResponse(reply: QNetworkReply) -> Optional[Cluster]: try: - result = json.loads(bytes(reply.readAll()).decode("utf-8")) - # TODO: use model or named tuple here. - return result.data + return [Cluster(**c) for c in json.loads(reply.readAll().data().decode("utf-8"))] except json.decoder.JSONDecodeError: Logger.logException("w", "Unable to decode JSON from reply.") return None - + except UnicodeDecodeError: + Logger.log("e", "Unable to read server response") + except json.JSONDecodeError: + Logger.logException("w", "Unable to decode JSON from reply.") + + return None + ## Adds a CloudOutputDevice for each entry in the remote cluster list from the API. - def _addCloudOutputDevice(self, cluster_data: Dict[str, any]): - # TODO: use model or named tuple for cluster_data - print("cluster_data====", cluster_data) - device = CloudOutputDevice(cluster_data["cluster_id"]) + def _addCloudOutputDevice(self, cluster: Cluster): + print("cluster_data====", cluster) + device = CloudOutputDevice(cluster["cluster_id"]) self._output_device_manager.addOutputDevice(device) - self._remote_clusters[cluster_data["cluster_id"]] = device + self._remote_clusters[cluster["cluster_id"]] = device ## Callback for when the active machine was changed by the user. def _activeMachineChanged(self): diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models.py b/plugins/UM3NetworkPrinting/src/Cloud/Models.py new file mode 100644 index 0000000000..7d9bba32f7 --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models.py @@ -0,0 +1,11 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from collections import namedtuple + +Cluster = namedtuple("Cluster", [ + "cluster_id", # Type: str + "host_guid", # Type: str + "host_name", # Type: str + "host_version", # Type: str + "status", # Type: str +]) \ No newline at end of file From 63fab9f038e8615432a8c592138e915351131386 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 26 Nov 2018 10:49:43 +0100 Subject: [PATCH 0367/1240] Use theme sizes. Contributes to CURA-5942. --- resources/qml/PrinterSelector/MachineSelector.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index d721ed5b83..120ce02edd 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -71,8 +71,8 @@ Cura.ExpandableComponent id: iconBackground anchors.centerIn: parent // Make it a bit bigger so there is an outline - width: parent.width + 2 - height: parent.height + 2 + width: parent.width + 2 * UM.Theme.getSize("default_lining").width + height: parent.height + 2 * UM.Theme.getSize("default_lining").height radius: Math.round(width / 2) color: UM.Theme.getColor("main_background") z: parent.z - 1 From fa1ef5c45c70ebdebaef3d9d19b026e6586523b2 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 26 Nov 2018 10:51:08 +0100 Subject: [PATCH 0368/1240] Rename function name to be more clear to what it does. Contributes to CURA-5942. --- resources/qml/PrinterSelector/MachineSelectorButton.qml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/qml/PrinterSelector/MachineSelectorButton.qml b/resources/qml/PrinterSelector/MachineSelectorButton.qml index fef6867e35..992ea55b1d 100644 --- a/resources/qml/PrinterSelector/MachineSelectorButton.qml +++ b/resources/qml/PrinterSelector/MachineSelectorButton.qml @@ -20,7 +20,7 @@ Button property var outputDevice: null property var printerTypesList: [] - function setPrinterTypesList() + function updatePrinterTypesList() { printerTypesList = (checked && (outputDevice != null)) ? outputDevice.uniquePrinterTypes : [] } @@ -97,14 +97,14 @@ Button Connections { target: outputDevice - onUniqueConfigurationsChanged: setPrinterTypesList() + onUniqueConfigurationsChanged: updatePrinterTypesList() } Connections { target: Cura.MachineManager - onOutputDevicesChanged: setPrinterTypesList() + onOutputDevicesChanged: updatePrinterTypesList() } - Component.onCompleted: setPrinterTypesList() + Component.onCompleted: updatePrinterTypesList() } From 4990f205662aeba91bce43c57fe41a94abd1d545 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 26 Nov 2018 10:55:32 +0100 Subject: [PATCH 0369/1240] Use QStringList instead of QVariantList since the return value is a list of strings. Co-Authored-By: diegopradogesto --- cura/PrinterOutputDevice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index f8a663f0e4..99c48189cc 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -212,7 +212,7 @@ class PrinterOutputDevice(QObject, OutputDevice): self.uniqueConfigurationsChanged.emit() # Returns the unique configurations of the printers within this output device - @pyqtProperty("QVariantList", notify = uniqueConfigurationsChanged) + @pyqtProperty("QStringList", notify = uniqueConfigurationsChanged) def uniquePrinterTypes(self) -> List[str]: return list(set([configuration.printerType for configuration in self._unique_configurations])) @@ -243,4 +243,4 @@ class PrinterOutputDevice(QObject, OutputDevice): if not self._firmware_updater: return - self._firmware_updater.updateFirmware(firmware_file) \ No newline at end of file + self._firmware_updater.updateFirmware(firmware_file) From 21bfa6e4c20819e09b8cb2f27f62427dc71c8476 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 26 Nov 2018 10:58:38 +0100 Subject: [PATCH 0370/1240] Fix code style. Add brackets in new line. Contributes to CURA-5942. --- resources/qml/PrinterSelector/MachineSelectorList.qml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/resources/qml/PrinterSelector/MachineSelectorList.qml b/resources/qml/PrinterSelector/MachineSelectorList.qml index 11a61194b7..5ef04b7351 100644 --- a/resources/qml/PrinterSelector/MachineSelectorList.qml +++ b/resources/qml/PrinterSelector/MachineSelectorList.qml @@ -30,7 +30,10 @@ Column model: UM.ContainerStacksModel { id: networkedPrintersModel - filter: {"type": "machine", "um_network_key": "*", "hidden": "False"} + filter: + { + "type": "machine", "um_network_key": "*", "hidden": "False" + } } delegate: MachineSelectorButton @@ -66,7 +69,10 @@ Column model: UM.ContainerStacksModel { id: virtualPrintersModel - filter: {"type": "machine", "um_network_key": null} + filter: + { + "type": "machine", "um_network_key": null + } } delegate: MachineSelectorButton From 72d972c8b428e8ad8d6bf0b831b67d6301058049 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 26 Nov 2018 11:03:58 +0100 Subject: [PATCH 0371/1240] Remove PrinterStatusIcon that is not used anymore. Contributes to CURA-5942. --- resources/qml/Menus/PrinterStatusIcon.qml | 27 ----------------------- 1 file changed, 27 deletions(-) delete mode 100644 resources/qml/Menus/PrinterStatusIcon.qml diff --git a/resources/qml/Menus/PrinterStatusIcon.qml b/resources/qml/Menus/PrinterStatusIcon.qml deleted file mode 100644 index 6ff6b07af8..0000000000 --- a/resources/qml/Menus/PrinterStatusIcon.qml +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2017 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.2 - -import UM 1.2 as UM -import Cura 1.0 as Cura - -Item -{ - property var status: "disconnected" - width: childrenRect.width - height: childrenRect.height - UM.RecolorImage - { - id: statusIcon - width: UM.Theme.getSize("printer_status_icon").width - height: UM.Theme.getSize("printer_status_icon").height - sourceSize.width: width - sourceSize.height: width - color: UM.Theme.getColor("tab_status_" + parent.status) - source: UM.Theme.getIcon(parent.status) - } -} - - - From 5397cda2b4d3077a76ca5a3d9444e09cf386a318 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 26 Nov 2018 12:00:06 +0100 Subject: [PATCH 0372/1240] Add a label to the action panel when auto slice is ON, indicating that the process runs automatically. Contributes to CURA-5942. --- resources/qml/ActionPanel/SliceProcessWidget.qml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index 05a9345585..3329ac4b23 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -40,6 +40,18 @@ Column } } + Label + { + id: autoSlicingLabel + width: parent.width + visible: prepareButtons.autoSlice && widget.backendState == UM.Backend.Processing + + text: catalog.i18nc("@label:PrintjobStatus", "Auto slicing...") + color: UM.Theme.getColor("text") + font: UM.Theme.getFont("very_small") + renderType: Text.NativeRendering + } + Cura.IconLabel { id: unableToSliceMessage From 845b3209819f8e7b1449c15118c6c995b2c548c3 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 26 Nov 2018 13:08:30 +0100 Subject: [PATCH 0373/1240] Fix adjustable monitor component width CURA-5943 - Add rounding - If there's no sidebar, use full width --- plugins/MonitorStage/MonitorMainView.qml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/MonitorStage/MonitorMainView.qml b/plugins/MonitorStage/MonitorMainView.qml index 57dd033792..f5696bbf54 100644 --- a/plugins/MonitorStage/MonitorMainView.qml +++ b/plugins/MonitorStage/MonitorMainView.qml @@ -38,7 +38,10 @@ Item anchors.bottom: parent.bottom anchors.left: parent.left - width: parent.width * 0.7 + // If the sidebar is not set, the view should take the complete space. + property var widthFactor: monitorSidebarComponent.source == "" ? 1.0 : 0.7 + + width: Math.round(parent.width * widthFactor) height: parent.height property real maximumWidth: parent.width From 6bb010e74a09a95c68bc9010cfcf8ea9dfffb454 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 26 Nov 2018 13:14:24 +0100 Subject: [PATCH 0374/1240] Move MonitorButtonStyle to styles.qml CURA-5943 --- .../PrinterOutput/ManualPrinterControl.qml | 21 ++--- .../qml/PrinterOutput/MonitorButtonStyle.qml | 88 ------------------- resources/themes/cura-light/styles.qml | 80 +++++++++++++++++ 3 files changed, 88 insertions(+), 101 deletions(-) delete mode 100644 resources/qml/PrinterOutput/MonitorButtonStyle.qml diff --git a/resources/qml/PrinterOutput/ManualPrinterControl.qml b/resources/qml/PrinterOutput/ManualPrinterControl.qml index 1a719c379e..5d07bd5fc8 100644 --- a/resources/qml/PrinterOutput/ManualPrinterControl.qml +++ b/resources/qml/PrinterOutput/ManualPrinterControl.qml @@ -21,11 +21,6 @@ Item implicitWidth: parent.width implicitHeight: childrenRect.height - MonitorButtonStyle - { - id: monitorButtonStyle - } - Column { enabled: @@ -108,7 +103,7 @@ Item Layout.preferredWidth: width Layout.preferredHeight: height iconSource: UM.Theme.getIcon("arrow_top"); - style: monitorButtonStyle + style: UM.Theme.styles.monitor_button_style width: height height: UM.Theme.getSize("setting_control").height @@ -125,7 +120,7 @@ Item Layout.preferredWidth: width Layout.preferredHeight: height iconSource: UM.Theme.getIcon("arrow_left"); - style: monitorButtonStyle + style: UM.Theme.styles.monitor_button_style width: height height: UM.Theme.getSize("setting_control").height @@ -142,7 +137,7 @@ Item Layout.preferredWidth: width Layout.preferredHeight: height iconSource: UM.Theme.getIcon("arrow_right"); - style: monitorButtonStyle + style: UM.Theme.styles.monitor_button_style width: height height: UM.Theme.getSize("setting_control").height @@ -159,7 +154,7 @@ Item Layout.preferredWidth: width Layout.preferredHeight: height iconSource: UM.Theme.getIcon("arrow_bottom"); - style: monitorButtonStyle + style: UM.Theme.styles.monitor_button_style width: height height: UM.Theme.getSize("setting_control").height @@ -176,7 +171,7 @@ Item Layout.preferredWidth: width Layout.preferredHeight: height iconSource: UM.Theme.getIcon("home"); - style: monitorButtonStyle + style: UM.Theme.styles.monitor_button_style width: height height: UM.Theme.getSize("setting_control").height @@ -206,7 +201,7 @@ Item Button { iconSource: UM.Theme.getIcon("arrow_top"); - style: monitorButtonStyle + style: UM.Theme.styles.monitor_button_style width: height height: UM.Theme.getSize("setting_control").height @@ -219,7 +214,7 @@ Item Button { iconSource: UM.Theme.getIcon("home"); - style: monitorButtonStyle + style: UM.Theme.styles.monitor_button_style width: height height: UM.Theme.getSize("setting_control").height @@ -232,7 +227,7 @@ Item Button { iconSource: UM.Theme.getIcon("arrow_bottom"); - style: monitorButtonStyle + style: UM.Theme.styles.monitor_button_style width: height height: UM.Theme.getSize("setting_control").height diff --git a/resources/qml/PrinterOutput/MonitorButtonStyle.qml b/resources/qml/PrinterOutput/MonitorButtonStyle.qml deleted file mode 100644 index 7bb1b91e55..0000000000 --- a/resources/qml/PrinterOutput/MonitorButtonStyle.qml +++ /dev/null @@ -1,88 +0,0 @@ -import QtQuick 2.10 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Layouts 1.3 - -import UM 1.2 as UM -import Cura 1.0 as Cura - - -Component -{ - ButtonStyle - { - background: Rectangle - { - border.width: UM.Theme.getSize("default_lining").width - border.color: - { - if(!control.enabled) - { - return UM.Theme.getColor("action_button_disabled_border"); - } - else if(control.pressed) - { - return UM.Theme.getColor("action_button_active_border"); - } - else if(control.hovered) - { - return UM.Theme.getColor("action_button_hovered_border"); - } - return UM.Theme.getColor("action_button_border"); - } - color: - { - if(!control.enabled) - { - return UM.Theme.getColor("action_button_disabled"); - } - else if(control.pressed) - { - return UM.Theme.getColor("action_button_active"); - } - else if(control.hovered) - { - return UM.Theme.getColor("action_button_hovered"); - } - return UM.Theme.getColor("action_button"); - } - Behavior on color - { - ColorAnimation - { - duration: 50 - } - } - } - - label: Item - { - UM.RecolorImage - { - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter - width: Math.floor(control.width / 2) - height: Math.floor(control.height / 2) - sourceSize.width: width - sourceSize.height: width - color: - { - if(!control.enabled) - { - return UM.Theme.getColor("action_button_disabled_text"); - } - else if(control.pressed) - { - return UM.Theme.getColor("action_button_active_text"); - } - else if(control.hovered) - { - return UM.Theme.getColor("action_button_hovered_text"); - } - return UM.Theme.getColor("action_button_text"); - } - source: control.iconSource - } - } - } -} diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index 723b393efa..ee9c530d9b 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -1200,4 +1200,84 @@ QtObject } } } + + property Component monitor_button_style: Component + { + ButtonStyle + { + background: Rectangle + { + border.width: UM.Theme.getSize("default_lining").width + border.color: + { + if(!control.enabled) + { + return UM.Theme.getColor("action_button_disabled_border"); + } + else if(control.pressed) + { + return UM.Theme.getColor("action_button_active_border"); + } + else if(control.hovered) + { + return UM.Theme.getColor("action_button_hovered_border"); + } + return UM.Theme.getColor("action_button_border"); + } + color: + { + if(!control.enabled) + { + return UM.Theme.getColor("action_button_disabled"); + } + else if(control.pressed) + { + return UM.Theme.getColor("action_button_active"); + } + else if(control.hovered) + { + return UM.Theme.getColor("action_button_hovered"); + } + return UM.Theme.getColor("action_button"); + } + Behavior on color + { + ColorAnimation + { + duration: 50 + } + } + } + + label: Item + { + UM.RecolorImage + { + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + width: Math.floor(control.width / 2) + height: Math.floor(control.height / 2) + sourceSize.width: width + sourceSize.height: width + color: + { + if(!control.enabled) + { + return UM.Theme.getColor("action_button_disabled_text"); + } + else if(control.pressed) + { + return UM.Theme.getColor("action_button_active_text"); + } + else if(control.hovered) + { + return UM.Theme.getColor("action_button_hovered_text"); + } + return UM.Theme.getColor("action_button_text"); + } + source: control.iconSource + } + } + } + } } From d6614941dc1b63aca417e0b27d9cc45c2e9477fe Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 26 Nov 2018 13:15:23 +0100 Subject: [PATCH 0375/1240] Update QtQuick import versions in styles.qml CURA-5943 --- resources/themes/cura-light/styles.qml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index ee9c530d9b..010e5564f1 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -1,9 +1,9 @@ // Copyright (c) 2018 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.1 -import QtQuick.Controls 1.1 -import QtQuick.Controls.Styles 1.1 +import QtQuick 2.10 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 import UM 1.1 as UM From 6bed1c1390b33720621ae781668a93135f1a7fba Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 26 Nov 2018 13:21:52 +0100 Subject: [PATCH 0376/1240] Move monitor_checkable_button_style out to styles.qml CURA-5943 --- .../PrinterOutput/ManualPrinterControl.qml | 67 +----------------- resources/themes/cura-light/styles.qml | 70 +++++++++++++++++++ 2 files changed, 71 insertions(+), 66 deletions(-) diff --git a/resources/qml/PrinterOutput/ManualPrinterControl.qml b/resources/qml/PrinterOutput/ManualPrinterControl.qml index 5d07bd5fc8..106ae7db03 100644 --- a/resources/qml/PrinterOutput/ManualPrinterControl.qml +++ b/resources/qml/PrinterOutput/ManualPrinterControl.qml @@ -279,72 +279,7 @@ Item checked: distancesRow.currentDistance == model.value onClicked: distancesRow.currentDistance = model.value - style: ButtonStyle { - background: Rectangle { - border.width: control.checked ? UM.Theme.getSize("default_lining").width * 2 : UM.Theme.getSize("default_lining").width - border.color: - { - if(!control.enabled) - { - return UM.Theme.getColor("action_button_disabled_border"); - } - else if (control.checked || control.pressed) - { - return UM.Theme.getColor("action_button_active_border"); - } - else if(control.hovered) - { - return UM.Theme.getColor("action_button_hovered_border"); - } - return UM.Theme.getColor("action_button_border"); - } - color: - { - if(!control.enabled) - { - return UM.Theme.getColor("action_button_disabled"); - } - else if (control.checked || control.pressed) - { - return UM.Theme.getColor("action_button_active"); - } - else if (control.hovered) - { - return UM.Theme.getColor("action_button_hovered"); - } - return UM.Theme.getColor("action_button"); - } - Behavior on color { ColorAnimation { duration: 50; } } - Label { - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: UM.Theme.getSize("default_lining").width * 2 - anchors.rightMargin: UM.Theme.getSize("default_lining").width * 2 - color: - { - if(!control.enabled) - { - return UM.Theme.getColor("action_button_disabled_text"); - } - else if (control.checked || control.pressed) - { - return UM.Theme.getColor("action_button_active_text"); - } - else if (control.hovered) - { - return UM.Theme.getColor("action_button_hovered_text"); - } - return UM.Theme.getColor("action_button_text"); - } - font: UM.Theme.getFont("default") - text: control.text - horizontalAlignment: Text.AlignHCenter - elide: Text.ElideMiddle - } - } - label: Item { } - } + style: UM.Theme.styles.monitor_checkable_button_style } } } diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index 010e5564f1..f982b1eac8 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -1280,4 +1280,74 @@ QtObject } } } + + property Component monitor_checkable_button_style: Component + { + ButtonStyle { + background: Rectangle { + border.width: control.checked ? UM.Theme.getSize("default_lining").width * 2 : UM.Theme.getSize("default_lining").width + border.color: + { + if(!control.enabled) + { + return UM.Theme.getColor("action_button_disabled_border"); + } + else if (control.checked || control.pressed) + { + return UM.Theme.getColor("action_button_active_border"); + } + else if(control.hovered) + { + return UM.Theme.getColor("action_button_hovered_border"); + } + return UM.Theme.getColor("action_button_border"); + } + color: + { + if(!control.enabled) + { + return UM.Theme.getColor("action_button_disabled"); + } + else if (control.checked || control.pressed) + { + return UM.Theme.getColor("action_button_active"); + } + else if (control.hovered) + { + return UM.Theme.getColor("action_button_hovered"); + } + return UM.Theme.getColor("action_button"); + } + Behavior on color { ColorAnimation { duration: 50; } } + Label { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: UM.Theme.getSize("default_lining").width * 2 + anchors.rightMargin: UM.Theme.getSize("default_lining").width * 2 + color: + { + if(!control.enabled) + { + return UM.Theme.getColor("action_button_disabled_text"); + } + else if (control.checked || control.pressed) + { + return UM.Theme.getColor("action_button_active_text"); + } + else if (control.hovered) + { + return UM.Theme.getColor("action_button_hovered_text"); + } + return UM.Theme.getColor("action_button_text"); + } + font: UM.Theme.getFont("default") + text: control.text + horizontalAlignment: Text.AlignHCenter + elide: Text.ElideMiddle + } + } + label: Item { } + } + } } From eea6490e75cea6cd30555edc674bd58969fcd72b Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 26 Nov 2018 13:27:26 +0100 Subject: [PATCH 0377/1240] Hover of output device now spans the popup CURA-5959 --- resources/qml/ActionPanel/OutputDevicesActionButton.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/ActionPanel/OutputDevicesActionButton.qml b/resources/qml/ActionPanel/OutputDevicesActionButton.qml index d24d440241..e4b4884794 100644 --- a/resources/qml/ActionPanel/OutputDevicesActionButton.qml +++ b/resources/qml/ActionPanel/OutputDevicesActionButton.qml @@ -68,7 +68,7 @@ Item closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent - contentItem: Column + contentItem: ColumnLayout { Repeater { @@ -80,7 +80,7 @@ Item color: "transparent" cornerRadius: 0 hoverColor: UM.Theme.getColor("primary") - + Layout.fillWidth: true onClicked: { UM.OutputDeviceManager.setActiveDevice(model.id) From 21c81603b491ab7378752b5afd2e16dd0b648899 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 26 Nov 2018 13:33:14 +0100 Subject: [PATCH 0378/1240] Use application singleton instead of locally cached application --- cura/NetworkClient.py | 8 +++----- .../src/Cloud/CloudOutputDeviceManager.py | 14 ++++++++------ plugins/UM3NetworkPrinting/src/Cloud/Models.py | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cura/NetworkClient.py b/cura/NetworkClient.py index eeedfeaa79..b150f59011 100644 --- a/cura/NetworkClient.py +++ b/cura/NetworkClient.py @@ -7,8 +7,8 @@ from PyQt5.QtCore import QUrl from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply, QHttpMultiPart, QNetworkRequest, QHttpPart, \ QAuthenticator +from UM.Application import Application from UM.Logger import Logger -from cura.CuraApplication import CuraApplication ## Abstraction of QNetworkAccessManager for easier networking in Cura. @@ -17,9 +17,6 @@ class NetworkClient: def __init__(self) -> None: - # Use the given application instance or get the singleton instance. - self._application = CuraApplication.getInstance() - # Network manager instance to use for this client. self._manager = None # type: Optional[QNetworkAccessManager] @@ -29,7 +26,8 @@ class NetworkClient: self._last_request_time = None # type: Optional[float] # The user agent of Cura. - self._user_agent = "%s/%s " % (self._application.getApplicationName(), self._application.getVersion()) + application = Application.getInstance() + self._user_agent = "%s/%s " % (application.getApplicationName(), application.getVersion()) # Uses to store callback methods for finished network requests. # This allows us to register network calls with a callback directly instead of having to dissect the reply. diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index e93393d736..17e82417ef 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -6,6 +6,7 @@ from typing import Dict, Optional from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply from UM.Logger import Logger +from cura.CuraApplication import CuraApplication from cura.NetworkClient import NetworkClient from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputDevice import CloudOutputDevice from .Models import Cluster @@ -25,15 +26,16 @@ class CloudOutputDeviceManager(NetworkClient): def __init__(self): super().__init__() - - self._output_device_manager = self._application.getOutputDeviceManager() - self._account = self._application.getCuraAPI().account - + # Persistent dict containing the remote clusters for the authenticated user. self._remote_clusters = {} # type: Dict[str, CloudOutputDevice] + + application = CuraApplication.getInstance() + self._output_device_manager = application.getOutputDeviceManager() + self._account = application.getCuraAPI().account # When switching machines we check if we have to activate a remote cluster. - self._application.globalContainerStackChanged.connect(self._activeMachineChanged) + application.globalContainerStackChanged.connect(self._activeMachineChanged) # Fetch all remote clusters for the authenticated user. # TODO: update remote clusters periodically @@ -96,7 +98,7 @@ class CloudOutputDeviceManager(NetworkClient): ## Callback for when the active machine was changed by the user. def _activeMachineChanged(self): - active_machine = self._application.getGlobalContainerStack() + active_machine = CuraApplication.getInstance().getGlobalContainerStack() if not active_machine: return diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models.py b/plugins/UM3NetworkPrinting/src/Cloud/Models.py index 7d9bba32f7..b118f3e61c 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models.py @@ -8,4 +8,4 @@ Cluster = namedtuple("Cluster", [ "host_name", # Type: str "host_version", # Type: str "status", # Type: str -]) \ No newline at end of file +]) From 098714254d8a5d366f22d1894083fba4fee4aaad Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 26 Nov 2018 13:35:02 +0100 Subject: [PATCH 0379/1240] Remove unused code CURA-5959 --- resources/qml/SaveButton.qml | 478 ----------------------------------- 1 file changed, 478 deletions(-) delete mode 100644 resources/qml/SaveButton.qml diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml deleted file mode 100644 index c2d310e30c..0000000000 --- a/resources/qml/SaveButton.qml +++ /dev/null @@ -1,478 +0,0 @@ -// Copyright (c) 2018 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.7 -import QtQuick.Controls 1.1 -import QtQuick.Controls.Styles 1.1 -import QtQuick.Layouts 1.1 - -import UM 1.1 as UM -import Cura 1.0 as Cura - -// This widget does so much more than "just" being a save button, so it should be refactored at some point in time. -Item -{ - id: base; - UM.I18nCatalog { id: catalog; name: "cura"} - - property real progress: UM.Backend.progress - property int backendState: UM.Backend.state - property bool activity: CuraApplication.platformActivity - - property alias buttonRowWidth: saveRow.width - - property string fileBaseName - property string statusText: - { - if(!activity) - { - return catalog.i18nc("@label:PrintjobStatus", "Please load a 3D model"); - } - - switch(base.backendState) - { - case 1: - return catalog.i18nc("@label:PrintjobStatus", "Ready to slice"); - case 2: - return catalog.i18nc("@label:PrintjobStatus", "Slicing..."); - case 3: - return catalog.i18nc("@label:PrintjobStatus %1 is target operation", "Ready to %1").arg(UM.OutputDeviceManager.activeDeviceShortDescription); - case 4: - return catalog.i18nc("@label:PrintjobStatus", "Unable to Slice"); - case 5: - return catalog.i18nc("@label:PrintjobStatus", "Slicing unavailable"); - default: - return ""; - } - } - - function sliceOrStopSlicing() - { - try - { - if ([1, 5].indexOf(base.backendState) != -1) - { - CuraApplication.backend.forceSlice(); - } - else - { - CuraApplication.backend.stopSlicing(); - } - } - catch (e) - { - console.log("Could not start or stop slicing.", e) - } - } - - Label - { - id: statusLabel - width: parent.width - 2 * UM.Theme.getSize("thick_margin").width - anchors.top: parent.top - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("thick_margin").width - - color: UM.Theme.getColor("text") - font: UM.Theme.getFont("default_bold") - text: statusText; - } - - Rectangle - { - id: progressBar - width: parent.width - 2 * UM.Theme.getSize("thick_margin").width - height: UM.Theme.getSize("progressbar").height - anchors.top: statusLabel.bottom - anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height / 4) - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("thick_margin").width - radius: UM.Theme.getSize("progressbar_radius").width - color: UM.Theme.getColor("progressbar_background") - - Rectangle - { - width: Math.max(parent.width * base.progress) - height: parent.height - color: UM.Theme.getColor("progressbar_control") - radius: UM.Theme.getSize("progressbar_radius").width - visible: base.backendState == 2 - } - } - - // Shortcut for "save as/print/..." - Action - { - shortcut: "Ctrl+P" - onTriggered: - { - // only work when the button is enabled - if (saveToButton.enabled) - { - saveToButton.clicked(); - } - // prepare button - if (prepareButton.enabled) - { - sliceOrStopSlicing(); - } - } - } - - Item - { - id: saveRow - width: { - // using childrenRect.width directly causes a binding loop, because setting the width affects the childrenRect - var children_width = UM.Theme.getSize("default_margin").width; - for (var index in children) - { - var child = children[index]; - if(child.visible) - { - children_width += child.width + child.anchors.rightMargin; - } - } - return Math.min(children_width, base.width - UM.Theme.getSize("thick_margin").width); - } - height: saveToButton.height - anchors.bottom: parent.bottom - anchors.bottomMargin: UM.Theme.getSize("thick_margin").height - anchors.right: parent.right - clip: true - - Row - { - id: additionalComponentsRow - anchors.top: parent.top - anchors.right: saveToButton.visible ? saveToButton.left : (prepareButton.visible ? prepareButton.left : parent.right) - anchors.rightMargin: UM.Theme.getSize("default_margin").width - - spacing: UM.Theme.getSize("default_margin").width - } - - Component.onCompleted: - { - saveRow.addAdditionalComponents("saveButton") - } - - Connections - { - target: CuraApplication - onAdditionalComponentsChanged: saveRow.addAdditionalComponents("saveButton") - } - - function addAdditionalComponents (areaId) - { - if(areaId == "saveButton") - { - for (var component in CuraApplication.additionalComponents["saveButton"]) - { - CuraApplication.additionalComponents["saveButton"][component].parent = additionalComponentsRow - } - } - } - - Connections - { - target: UM.Preferences - onPreferenceChanged: - { - var autoSlice = UM.Preferences.getValue("general/auto_slice"); - prepareButton.autoSlice = autoSlice; - saveToButton.autoSlice = autoSlice; - } - } - - // Prepare button, only shows if auto_slice is off - Button - { - id: prepareButton - - tooltip: [1, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@info:tooltip","Slice current printjob") : catalog.i18nc("@info:tooltip","Cancel slicing process") - // 1 = not started, 2 = Processing - enabled: ([1, 2].indexOf(base.backendState) != -1) && base.activity - visible: !autoSlice && ([1, 2, 4].indexOf(base.backendState) != -1) && base.activity - property bool autoSlice - height: UM.Theme.getSize("save_button_save_to_button").height - - anchors.top: parent.top - anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("thick_margin").width - - // 1 = not started, 4 = error, 5 = disabled - text: [1, 4, 5].indexOf(base.backendState) != -1 ? catalog.i18nc("@label:Printjob", "Prepare") : catalog.i18nc("@label:Printjob", "Cancel") - onClicked: - { - sliceOrStopSlicing(); - } - - style: ButtonStyle - { - background: Rectangle - { - border.width: UM.Theme.getSize("default_lining").width - border.color: - { - if(!control.enabled) - { - return UM.Theme.getColor("action_button_disabled_border"); - } - else if(control.pressed) - { - return UM.Theme.getColor("action_button_active_border"); - } - else if(control.hovered) - { - return UM.Theme.getColor("action_button_hovered_border"); - } - else - { - return UM.Theme.getColor("action_button_border"); - } - } - color: - { - if(!control.enabled) - { - return UM.Theme.getColor("action_button_disabled"); - } - else if(control.pressed) - { - return UM.Theme.getColor("action_button_active"); - } - else if(control.hovered) - { - return UM.Theme.getColor("action_button_hovered"); - } - else - { - return UM.Theme.getColor("action_button"); - } - } - - Behavior on color { ColorAnimation { duration: 50; } } - - implicitWidth: actualLabel.contentWidth + (UM.Theme.getSize("thick_margin").width * 2) - - Label - { - id: actualLabel - anchors.centerIn: parent - color: - { - if(!control.enabled) - { - return UM.Theme.getColor("action_button_disabled_text"); - } - else if(control.pressed) - { - return UM.Theme.getColor("action_button_active_text"); - } - else if(control.hovered) - { - return UM.Theme.getColor("action_button_hovered_text"); - } - else - { - return UM.Theme.getColor("action_button_text"); - } - } - font: UM.Theme.getFont("action_button") - text: control.text; - } - } - label: Item {} - } - } - - Button - { - id: saveToButton - - tooltip: UM.OutputDeviceManager.activeDeviceDescription; - // 3 = done, 5 = disabled - enabled: base.backendState != "undefined" && (base.backendState == 3 || base.backendState == 5) && base.activity == true - visible: base.backendState != "undefined" && autoSlice || ((base.backendState == 3 || base.backendState == 5) && base.activity == true) - property bool autoSlice - height: UM.Theme.getSize("save_button_save_to_button").height - - anchors.top: parent.top - anchors.right: deviceSelectionMenu.visible ? deviceSelectionMenu.left : parent.right - anchors.rightMargin: deviceSelectionMenu.visible ? -3 * UM.Theme.getSize("default_lining").width : UM.Theme.getSize("thick_margin").width - - text: UM.OutputDeviceManager.activeDeviceShortDescription - onClicked: - { - forceActiveFocus(); - UM.OutputDeviceManager.requestWriteToDevice(UM.OutputDeviceManager.activeDevice, PrintInformation.jobName, - { "filter_by_machine": true, "preferred_mimetypes": Cura.MachineManager.activeMachine.preferred_output_file_formats }); - } - - style: ButtonStyle - { - background: Rectangle - { - border.width: UM.Theme.getSize("default_lining").width - border.color: - { - if(!control.enabled) - { - return UM.Theme.getColor("action_button_disabled_border"); - } - else if(control.pressed) - { - return UM.Theme.getColor("print_button_ready_pressed_border"); - } - else if(control.hovered) - { - return UM.Theme.getColor("print_button_ready_hovered_border"); - } - else - { - return UM.Theme.getColor("print_button_ready_border"); - } - } - color: - { - if(!control.enabled) - { - return UM.Theme.getColor("action_button_disabled"); - } - else if(control.pressed) - { - return UM.Theme.getColor("print_button_ready_pressed"); - } - else if(control.hovered) - { - return UM.Theme.getColor("print_button_ready_hovered"); - } - else - { - return UM.Theme.getColor("print_button_ready"); - } - } - - Behavior on color { ColorAnimation { duration: 50; } } - - implicitWidth: actualLabel.contentWidth + (UM.Theme.getSize("thick_margin").width * 2) - - Label - { - id: actualLabel - anchors.centerIn: parent - color: control.enabled ? UM.Theme.getColor("print_button_ready_text") : UM.Theme.getColor("action_button_disabled_text") - font: UM.Theme.getFont("action_button") - text: control.text - } - } - label: Item { } - } - } - - Button - { - id: deviceSelectionMenu - tooltip: catalog.i18nc("@info:tooltip","Select the active output device"); - anchors.top: parent.top - anchors.right: parent.right - - anchors.rightMargin: UM.Theme.getSize("thick_margin").width - width: UM.Theme.getSize("save_button_save_to_button").height - height: UM.Theme.getSize("save_button_save_to_button").height - - // 3 = Done, 5 = Disabled - enabled: (base.backendState == 3 || base.backendState == 5) && base.activity == true - visible: (devicesModel.deviceCount > 1) && (base.backendState == 3 || base.backendState == 5) && base.activity == true - - - style: ButtonStyle - { - background: Rectangle - { - id: deviceSelectionIcon - border.width: UM.Theme.getSize("default_lining").width - border.color: - { - if(!control.enabled) - { - return UM.Theme.getColor("action_button_disabled_border") - } - else if(control.pressed) - { - return UM.Theme.getColor("print_button_ready_pressed_border") - } - else if(control.hovered) - { - return UM.Theme.getColor("print_button_ready_hovered_border") - } - else - { - return UM.Theme.getColor("print_button_ready_border") - } - } - color: - { - if(!control.enabled) - { - return UM.Theme.getColor("action_button_disabled") - } - else if(control.pressed) - { - return UM.Theme.getColor("print_button_ready_pressed") - } - else if(control.hovered) - { - return UM.Theme.getColor("print_button_ready_hovered") - } - else - { - return UM.Theme.getColor("print_button_ready") - } - } - Behavior on color { ColorAnimation { duration: 50; } } - anchors.left: parent.left - anchors.leftMargin: Math.round(UM.Theme.getSize("save_button_text_margin").width / 2); - width: parent.height - height: parent.height - - 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: height - color: control.enabled ? UM.Theme.getColor("print_button_ready_text") : UM.Theme.getColor("action_button_disabled_text") - source: UM.Theme.getIcon("arrow_bottom") - } - } - } - - menu: Menu - { - id: devicesMenu; - Instantiator - { - model: devicesModel; - MenuItem - { - text: model.description - checkable: true; - checked: model.id == UM.OutputDeviceManager.activeDevice - exclusiveGroup: devicesMenuGroup - onTriggered: - { - UM.OutputDeviceManager.setActiveDevice(model.id); - } - } - onObjectAdded: devicesMenu.insertItem(index, object) - onObjectRemoved: devicesMenu.removeItem(object) - } - ExclusiveGroup { id: devicesMenuGroup } - } - } - UM.OutputDevicesModel { id: devicesModel } - } -} From 1fe65013584b6bb1130a8fc4a7bac3e3ddff618c Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 26 Nov 2018 13:35:41 +0100 Subject: [PATCH 0380/1240] Removed unused entries from theme --- resources/themes/cura-light/theme.json | 8 -------- 1 file changed, 8 deletions(-) diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index cbdc37caa1..59927da663 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -181,14 +181,6 @@ "action_button_shadow": [64, 47, 205, 255], "action_button_disabled_shadow": [228, 228, 228, 255], - "print_button_ready": [50, 130, 255, 255], - "print_button_ready_border": [50, 130, 255, 255], - "print_button_ready_text": [255, 255, 255, 255], - "print_button_ready_hovered": [30, 186, 245, 243], - "print_button_ready_hovered_border": [30, 186, 245, 243], - "print_button_ready_pressed": [30, 186, 245, 243], - "print_button_ready_pressed_border": [30, 186, 245, 243], - "scrollbar_background": [255, 255, 255, 255], "scrollbar_handle": [31, 36, 39, 255], "scrollbar_handle_hover": [12, 159, 227, 255], From 3c86c0ae6c691c4eb1be17f0fe15b1ff1cc5919f Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 26 Nov 2018 13:36:45 +0100 Subject: [PATCH 0381/1240] Fix spacing CURA-5959 --- resources/themes/cura-light/theme.json | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 59927da663..001818c2f8 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -80,8 +80,7 @@ "thick_lining": [127, 127, 127, 255], "lining": [192, 193, 194, 255], "viewport_overlay": [0, 0, 0, 192], - - + "primary": [50, 130, 255, 255], "primary_shadow": [64, 47, 205, 255], "primary_hover": [48, 182, 231, 255], @@ -90,15 +89,15 @@ "secondary": [245, 245, 245, 255], "secondary_shadow": [228, 228, 228, 255], - "primary_button": [38,113,231,255], - "primary_button_shadow": [27,95,202, 255], - "primary_button_hover": [81,145,247, 255], + "primary_button": [38, 113, 231, 255], + "primary_button_shadow": [27, 95, 202, 255], + "primary_button_hover": [81, 145, 247, 255], "primary_button_text": [255, 255, 255, 255], - "secondary_button": [240,240,240, 255], - "secondary_button_shadow": [228, 228, 228, 255], - "secondary_button_hover": [228,228,228, 255], - "secondary_button_text": [30,102,215, 255], + "secondary_button": [240, 240, 240, 255], + "secondary_button_shadow": [228, 228, 228, 255], + "secondary_button_hover": [228, 228, 228, 255], + "secondary_button_text": [30, 102, 215, 255], "main_window_header_background": [10, 8, 80, 255], "main_window_header_button_text_active": [10, 8, 80, 255], From ebae4347a841e90f257d3296b24ca3e8531e6852 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 26 Nov 2018 13:44:32 +0100 Subject: [PATCH 0382/1240] Fix unit tests that were failing after adding the getAbbreviatedMachineName to the machine manager. Contributes to CURA-5942. --- tests/TestPrintInformation.py | 52 ++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/tests/TestPrintInformation.py b/tests/TestPrintInformation.py index a226a437c6..baa36fb338 100644 --- a/tests/TestPrintInformation.py +++ b/tests/TestPrintInformation.py @@ -1,5 +1,7 @@ +import functools from cura import PrintInformation +from cura.Settings.MachineManager import MachineManager from unittest.mock import MagicMock, patch from UM.Application import Application @@ -11,14 +13,20 @@ def getPrintInformation(printer_name) -> PrintInformation: mock_application = MagicMock() global_container_stack = MagicMock() - global_container_stack.definition.getName = MagicMock(return_value=printer_name) - mock_application.getGlobalContainerStack = MagicMock(return_value=global_container_stack) + global_container_stack.definition.getName = MagicMock(return_value = printer_name) + mock_application.getGlobalContainerStack = MagicMock(return_value = global_container_stack) - multiBuildPlateModel = MagicMock() - multiBuildPlateModel.maxBuildPlate = 0 - mock_application.getMultiBuildPlateModel = MagicMock(return_value=multiBuildPlateModel) + multi_build_plate_model = MagicMock() + multi_build_plate_model.maxBuildPlate = 0 + mock_application.getMultiBuildPlateModel = MagicMock(return_value = multi_build_plate_model) - Application.getInstance = MagicMock(return_type=mock_application) + # Mock-up the entire machine manager except the function that needs to be tested: getAbbreviatedMachineName + original_get_abbreviated_name = MachineManager.getAbbreviatedMachineName + mock_machine_manager = MagicMock() + mock_machine_manager.getAbbreviatedMachineName = functools.partial(original_get_abbreviated_name, mock_machine_manager) + mock_application.getMachineManager = MagicMock(return_value = mock_machine_manager) + + Application.getInstance = MagicMock(return_type = mock_application) with patch("json.loads", lambda x: {}): print_information = PrintInformation.PrintInformation(mock_application) @@ -28,17 +36,17 @@ def getPrintInformation(printer_name) -> PrintInformation: def setup_module(): MimeTypeDatabase.addMimeType( MimeType( - name="application/vnd.ms-package.3dmanufacturing-3dmodel+xml", - comment="3MF", - suffixes=["3mf"] + name = "application/vnd.ms-package.3dmanufacturing-3dmodel+xml", + comment = "3MF", + suffixes = ["3mf"] ) ) MimeTypeDatabase.addMimeType( MimeType( - name="application/x-cura-gcode-file", - comment="Cura GCode File", - suffixes=["gcode"] + name = "application/x-cura-gcode-file", + comment = "Cura GCode File", + suffixes = ["gcode"] ) ) @@ -49,42 +57,42 @@ def test_setProjectName(): print_information = getPrintInformation("ultimaker") # Test simple name - project_name = ["HelloWorld",".3mf"] + project_name = ["HelloWorld", ".3mf"] print_information.setProjectName(project_name[0] + project_name[1]) assert "UM_" + project_name[0] == print_information._job_name # Test the name with one dot - project_name = ["Hello.World",".3mf"] + project_name = ["Hello.World", ".3mf"] print_information.setProjectName(project_name[0] + project_name[1]) assert "UM_" + project_name[0] == print_information._job_name # Test the name with two dot - project_name = ["Hello.World.World",".3mf"] + project_name = ["Hello.World.World", ".3mf"] print_information.setProjectName(project_name[0] + project_name[1]) assert "UM_" + project_name[0] == print_information._job_name # Test the name with dot at the beginning - project_name = [".Hello.World",".3mf"] + project_name = [".Hello.World", ".3mf"] print_information.setProjectName(project_name[0] + project_name[1]) assert "UM_" + project_name[0] == print_information._job_name # Test the name with underline - project_name = ["Hello_World",".3mf"] + project_name = ["Hello_World", ".3mf"] print_information.setProjectName(project_name[0] + project_name[1]) assert "UM_" + project_name[0] == print_information._job_name # Test gcode extension - project_name = ["Hello_World",".gcode"] + project_name = ["Hello_World", ".gcode"] print_information.setProjectName(project_name[0] + project_name[1]) assert "UM_" + project_name[0] == print_information._job_name # Test empty project name - project_name = ["",""] + project_name = ["", ""] print_information.setProjectName(project_name[0] + project_name[1]) assert print_information.UNTITLED_JOB_NAME == print_information._job_name # Test wrong file extension - project_name = ["Hello_World",".test"] + project_name = ["Hello_World", ".test"] print_information.setProjectName(project_name[0] + project_name[1]) assert "UM_" + project_name[0] != print_information._job_name @@ -93,7 +101,7 @@ def test_setJobName(): print_information = getPrintInformation("ultimaker") print_information._abbr_machine = "UM" - print_information.setJobName("UM_HelloWorld", is_user_specified_job_name=False) + print_information.setJobName("UM_HelloWorld", is_user_specified_job_name = False) def test_defineAbbreviatedMachineName(): @@ -102,6 +110,6 @@ def test_defineAbbreviatedMachineName(): print_information = getPrintInformation(printer_name) # Test not ultimaker printer, name suffix should have first letter from the printer name - project_name = ["HelloWorld",".3mf"] + project_name = ["HelloWorld", ".3mf"] print_information.setProjectName(project_name[0] + project_name[1]) assert printer_name[0] + "_" + project_name[0] == print_information._job_name \ No newline at end of file From 014b1d6e4ee4e3e9193bed7ba8b188865416ed4d Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 26 Nov 2018 13:45:37 +0100 Subject: [PATCH 0383/1240] test --- cura/PrinterOutput/NetworkedPrinterOutputDevice.py | 10 ++++++---- .../UM3NetworkPrinting/src/UM3OutputDevicePlugin.py | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py index 12769208f4..a44b42a8ba 100644 --- a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py +++ b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py @@ -36,7 +36,7 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice, NetworkClient): self._authentication_state = AuthState.NotAuthenticated self._sending_gcode = False self._compressing_gcode = False - self._gcode = [] # type: List[str] + self._gcode = [] # type: List[str] self._connection_state_before_timeout = None # type: Optional[ConnectionState] self._timeout_time = 10 # After how many seconds of no response should a timeout occur? @@ -182,8 +182,10 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice, NetworkClient): return self._address def __handleOnFinished(self, reply: QNetworkReply) -> None: - super().__handleOnFinished(reply) - # Since we got a reply from the network manager we can now be sure we are actually connected. - if self._connection_state == ConnectionState.connecting: + # Since we got a 200 reply from the network manager we can now be sure we are actually connected. + if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) == 200 and \ + self._connection_state == ConnectionState.connecting: self.setConnectionState(ConnectionState.connected) + + super().__handleOnFinished(reply) diff --git a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py index 09aecb2187..47c3482aa5 100644 --- a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py @@ -41,7 +41,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self._zero_conf_browser = None # Create a cloud output device manager that abstract all cloud connection logic away. - self._cloud_output_device_manager = CloudOutputDeviceManager() + # self._cloud_output_device_manager = CloudOutputDeviceManager() # Because the model needs to be created in the same thread as the QMLEngine, we use a signal. self.addDeviceSignal.connect(self._onAddDevice) From a6544997525f0a066429223da1bd432eb3653ec5 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 26 Nov 2018 13:47:22 +0100 Subject: [PATCH 0384/1240] Re-enable hover for marketplace button CURA-5959 --- plugins/Toolbox/resources/qml/ToolboxDetailPage.qml | 2 +- resources/qml/MainWindow/MainWindowHeader.qml | 6 ++++-- resources/themes/cura-light/theme.json | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml index 437a2ef351..0c04dc2bab 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml @@ -37,7 +37,7 @@ Item leftMargin: UM.Theme.getSize("wide_margin").width topMargin: UM.Theme.getSize("wide_margin").height } - color: white //Always a white background for image (regardless of theme). + color: "white" //Always a white background for image (regardless of theme). Image { anchors.fill: parent diff --git a/resources/qml/MainWindow/MainWindowHeader.qml b/resources/qml/MainWindow/MainWindowHeader.qml index ceb27dd726..a24af7ee45 100644 --- a/resources/qml/MainWindow/MainWindowHeader.qml +++ b/resources/qml/MainWindow/MainWindowHeader.qml @@ -81,10 +81,12 @@ Rectangle height: Math.round(0.5 * UM.Theme.getSize("main_window_header").height) onClicked: Cura.Actions.browsePackages.trigger() + hoverEnabled: true + background: Rectangle { radius: UM.Theme.getSize("action_button_radius").width - color: "transparent" + color: marketplaceButton.hovered ? UM.Theme.getColor("primary_text") : UM.Theme.getColor("main_window_header_background") border.width: UM.Theme.getSize("default_lining").width border.color: UM.Theme.getColor("primary_text") } @@ -93,7 +95,7 @@ Rectangle { id: label text: marketplaceButton.text - color: UM.Theme.getColor("primary_text") + color: marketplaceButton.hovered ? UM.Theme.getColor("main_window_header_background") : UM.Theme.getColor("primary_text") width: contentWidth } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 001818c2f8..2343cd3f2a 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -80,7 +80,7 @@ "thick_lining": [127, 127, 127, 255], "lining": [192, 193, 194, 255], "viewport_overlay": [0, 0, 0, 192], - + "primary": [50, 130, 255, 255], "primary_shadow": [64, 47, 205, 255], "primary_hover": [48, 182, 231, 255], From b63c4f7a74a06f6b167f6c48d3538b9566fc2e9f Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 26 Nov 2018 14:06:34 +0100 Subject: [PATCH 0385/1240] Add header in new files. Remove unused imports. Contributes to CURA-5959. --- resources/qml/ActionButton.qml | 1 - resources/qml/PrimaryButton.qml | 4 +++- resources/qml/SecondaryButton.qml | 4 +++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index 6dd5839bb9..b9a04f3b46 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -3,7 +3,6 @@ import QtQuick 2.7 import QtQuick.Controls 2.1 -import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.0 // For the dropshadow diff --git a/resources/qml/PrimaryButton.qml b/resources/qml/PrimaryButton.qml index 8450e524e2..fca63d2cdb 100644 --- a/resources/qml/PrimaryButton.qml +++ b/resources/qml/PrimaryButton.qml @@ -1,5 +1,7 @@ +// 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 UM 1.4 as UM import Cura 1.1 as Cura diff --git a/resources/qml/SecondaryButton.qml b/resources/qml/SecondaryButton.qml index 0e6b79b3a7..f03d8acdfa 100644 --- a/resources/qml/SecondaryButton.qml +++ b/resources/qml/SecondaryButton.qml @@ -1,5 +1,7 @@ +// 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 UM 1.4 as UM import Cura 1.1 as Cura From 68a90ec510b15d41ba585f013c7d9c5fe52fb94f Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 26 Nov 2018 14:08:21 +0100 Subject: [PATCH 0386/1240] Use simple models instead of namedtuples Named tuples would throw a TypeError if an unknown attribute was set, but we just want to ignore those --- plugins/UM3NetworkPrinting/src/Models.py | 67 ++++++++++++++---------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Models.py b/plugins/UM3NetworkPrinting/src/Models.py index bcdeb8299c..e2ad411e90 100644 --- a/plugins/UM3NetworkPrinting/src/Models.py +++ b/plugins/UM3NetworkPrinting/src/Models.py @@ -1,33 +1,42 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from collections import namedtuple -ClusterMaterial = namedtuple("ClusterMaterial", [ - "guid", # Type: str - "material", # Type: str - "brand", # Type: str - "version", # Type: int - "color", # Type: str - "density" # Type: str -]) -LocalMaterial = namedtuple("LocalMaterial", [ - "GUID", # Type: str - "id", # Type: str - "type", # Type: str - "status", # Type: str - "base_file", # Type: str - "setting_version", # Type: int - "version", # Type: int - "name", # Type: str - "brand", # Type: str - "material", # Type: str - "color_name", # Type: str - "color_code", # Type: str - "description", # Type: str - "adhesion_info", # Type: str - "approximate_diameter", # Type: str - "properties", # Type: str - "definition", # Type: str - "compatible" # Type: str -]) +## Base model that maps kwargs to instance attributes. +class BaseModel: + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + + +## Class representing a material that was fetched from the cluster API. +class ClusterMaterial(BaseModel): + def __init__(self, **kwargs): + self.guid = None # type: str + self.material = None # type: str + self.brand = None # type: str + self.version = None # type: int + self.color = None # type: str + self.density = None # type: str + super().__init__(**kwargs) + + +## Class representing a local material that was fetched from the container registry. +class LocalMaterial(BaseModel): + def __init__(self, **kwargs): + self.GUID = None # type: str + self.id = None # type: str + self.type = None # type: str + self.status = None # type: str + self.base_file = None # type: str + self.setting_version = None # type: int + self.version = None # type: int + self.brand = None # type: str + self.material = None # type: str + self.color_name = None # type: str + self.color_code = None # type: str + self.description = None # type: str + self.adhesion_info = None # type: str + self.approximate_diameter = None # type: str + self.definition = None # type: str + self.compatible = None # type: bool + super().__init__(**kwargs) From 89e88a73bbf2e998e846fc11a803107fefafc170 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 26 Nov 2018 14:11:20 +0100 Subject: [PATCH 0387/1240] Temporary patch for when printer has one or more materials not installed --- plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index 8314b0f089..54b888d2f0 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -592,7 +592,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): def _createMaterialOutputModel(self, material_data: Dict[str, Any]) -> "MaterialOutputModel": material_manager = CuraApplication.getInstance().getMaterialManager() - material_group_list = material_manager.getMaterialGroupListByGUID(material_data["guid"]) + material_group_list = material_manager.getMaterialGroupListByGUID(material_data["guid"]) or [] # Sort the material groups by "is_read_only = True" first, and then the name alphabetically. read_only_material_group_list = list(filter(lambda x: x.is_read_only, material_group_list)) From 90a73f351da7bfa695ab5fcb6e9e9361e6200a44 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 26 Nov 2018 14:12:34 +0100 Subject: [PATCH 0388/1240] Fix positioning of tabs in custom menu The label's bottom side is at its top, so we must add its height so that it gets positioned correctly. Contributes to issue CURA-5876. --- .../qml/Menus/ConfigurationMenu/CustomConfiguration.qml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index 50ff108431..25111a9365 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -33,7 +33,7 @@ Item { id: tabBar anchors.top: header.bottom - anchors.topMargin: UM.Theme.getSize("default_margin").height + anchors.topMargin: UM.Theme.getSize("default_margin").height + header.height onCurrentIndexChanged: Cura.ExtruderManager.setActiveExtruderIndex(currentIndex) @@ -114,7 +114,7 @@ Item property var hasActiveExtruder: activeExtruder != null property var currentRootMaterialName: hasActiveExtruder ? activeExtruder.material.name : "" property var valueError: hasActiveExtruder ? Cura.ContainerManager.getContainerMetaDataEntry(activeExtruder.material.id, "compatible", "") != "True" : true - property var valueWarning: ! Cura.MachineManager.isActiveQualitySupported + property var valueWarning: !Cura.MachineManager.isActiveQualitySupported text: currentRootMaterialName tooltip: currentRootMaterialName @@ -131,7 +131,6 @@ Item { extruderIndex: Cura.ExtruderManager.activeExtruderIndex } - } } From 12f78fa21ad022d9f1f278fb5f3d56ced7e65f60 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 26 Nov 2018 14:29:42 +0100 Subject: [PATCH 0389/1240] Remove border of the popup selector for the output devices. Contributes to CURA-5959. --- resources/qml/ActionPanel/OutputDevicesActionButton.qml | 3 --- 1 file changed, 3 deletions(-) diff --git a/resources/qml/ActionPanel/OutputDevicesActionButton.qml b/resources/qml/ActionPanel/OutputDevicesActionButton.qml index e4b4884794..2111038cfc 100644 --- a/resources/qml/ActionPanel/OutputDevicesActionButton.qml +++ b/resources/qml/ActionPanel/OutputDevicesActionButton.qml @@ -94,10 +94,7 @@ Item { opacity: visible ? 1 : 0 Behavior on opacity { NumberAnimation { duration: 100 } } - radius: UM.Theme.getSize("default_radius").width color: UM.Theme.getColor("action_panel_secondary") - border.color: UM.Theme.getColor("lining") - border.width: UM.Theme.getSize("default_lining").width } } } From a382b77eaa81998d5a413a019701137117b45eb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Mon, 26 Nov 2018 14:30:17 +0100 Subject: [PATCH 0390/1240] Added validation to the models --- plugins/UM3NetworkPrinting/src/Models.py | 14 ++++++++++++++ plugins/UM3NetworkPrinting/src/SendMaterialJob.py | 4 ++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Models.py b/plugins/UM3NetworkPrinting/src/Models.py index e2ad411e90..d0708c8127 100644 --- a/plugins/UM3NetworkPrinting/src/Models.py +++ b/plugins/UM3NetworkPrinting/src/Models.py @@ -6,6 +6,10 @@ class BaseModel: def __init__(self, **kwargs): self.__dict__.update(kwargs) + self.validate() + + def validate(self): + pass ## Class representing a material that was fetched from the cluster API. @@ -19,6 +23,10 @@ class ClusterMaterial(BaseModel): self.density = None # type: str super().__init__(**kwargs) + def validate(self): + if not self.guid: + raise ValueError("guid is required on ClusterMaterial") + ## Class representing a local material that was fetched from the container registry. class LocalMaterial(BaseModel): @@ -40,3 +48,9 @@ class LocalMaterial(BaseModel): self.definition = None # type: str self.compatible = None # type: bool super().__init__(**kwargs) + + def validate(self): + if not self.GUID: + raise ValueError("guid is required on LocalMaterial") + if not self.id: + raise ValueError("id is required on LocalMaterial") diff --git a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py index 48760af28e..6f33e75ee1 100644 --- a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py @@ -159,8 +159,8 @@ class SendMaterialJob(Job): Logger.log("e", "Request material storage on printer: I didn't understand the printer's answer.") except json.JSONDecodeError: Logger.log("e", "Request material storage on printer: I didn't understand the printer's answer.") - except TypeError: - Logger.log("e", "Request material storage on printer: Printer's answer was missing GUIDs.") + except ValueError: + Logger.log("e", "Request material storage on printer: Printer's answer was missing a value.") ## Retrieves a list of local materials # From 0a1c0e18d1bca499168b062f1e0538665768cd85 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 26 Nov 2018 14:38:43 +0100 Subject: [PATCH 0391/1240] Reuse the component SecondaryButton in the printer selector. Contributes to CURA-5942. --- resources/qml/PrinterSelector/MachineSelector.qml | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index 120ce02edd..93e5103aa8 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -125,15 +125,11 @@ Cura.ExpandableComponent padding: UM.Theme.getSize("default_margin").width spacing: UM.Theme.getSize("default_margin").width - Cura.ActionButton + Cura.SecondaryButton { leftPadding: UM.Theme.getSize("default_margin").width rightPadding: UM.Theme.getSize("default_margin").width text: catalog.i18nc("@button", "Add printer") - color: UM.Theme.getColor("secondary") - hoverColor: UM.Theme.getColor("secondary") - textColor: UM.Theme.getColor("primary") - textHoverColor: UM.Theme.getColor("text") onClicked: { togglePopup() @@ -141,15 +137,11 @@ Cura.ExpandableComponent } } - Cura.ActionButton + Cura.SecondaryButton { leftPadding: UM.Theme.getSize("default_margin").width rightPadding: UM.Theme.getSize("default_margin").width text: catalog.i18nc("@button", "Manage printers") - color: UM.Theme.getColor("secondary") - hoverColor: UM.Theme.getColor("secondary") - textColor: UM.Theme.getColor("primary") - textHoverColor: UM.Theme.getColor("text") onClicked: { togglePopup() From 579522857a99505aedaf8c6bf7cfcd1ab0aca521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Mon, 26 Nov 2018 14:42:50 +0100 Subject: [PATCH 0392/1240] Fix the code-style test --- cura/NetworkClient.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/NetworkClient.py b/cura/NetworkClient.py index b150f59011..fbe0c63c36 100644 --- a/cura/NetworkClient.py +++ b/cura/NetworkClient.py @@ -206,7 +206,7 @@ class NetworkClient: if not self._manager: Logger.log("e", "No network manager was created to execute the POST call with.") - return + return None reply = self._manager.post(request, multi_post_part) From 6a43d10982e41e175c54338383435ca91d666199 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 26 Nov 2018 14:51:24 +0100 Subject: [PATCH 0393/1240] Use BaseModel for CloudCluster, some fixes --- plugins/UM3NetworkPrinting/__init__.py | 2 +- .../src/Cloud/CloudOutputDeviceManager.py | 21 ++++++++--------- .../UM3NetworkPrinting/src/Cloud/Models.py | 23 ++++++++++++------- .../src/UM3OutputDevicePlugin.py | 23 ++++++++----------- 4 files changed, 34 insertions(+), 35 deletions(-) diff --git a/plugins/UM3NetworkPrinting/__init__.py b/plugins/UM3NetworkPrinting/__init__.py index 23262aed94..3da7795589 100644 --- a/plugins/UM3NetworkPrinting/__init__.py +++ b/plugins/UM3NetworkPrinting/__init__.py @@ -10,6 +10,6 @@ def getMetaData(): def register(app): return { - "output_device": UM3OutputDevicePlugin.UM3OutputDevicePlugin(app), + "output_device": UM3OutputDevicePlugin.UM3OutputDevicePlugin(), "machine_action": DiscoverUM3Action.DiscoverUM3Action() } diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 17e82417ef..e48c06dbe9 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -9,7 +9,7 @@ from UM.Logger import Logger from cura.CuraApplication import CuraApplication from cura.NetworkClient import NetworkClient from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputDevice import CloudOutputDevice -from .Models import Cluster +from .Models import CloudCluster ## The cloud output device manager is responsible for using the Ultimaker Cloud APIs to manage remote clusters. @@ -78,23 +78,20 @@ class CloudOutputDeviceManager(NetworkClient): @staticmethod def _parseStatusResponse(reply: QNetworkReply) -> Optional[Cluster]: try: - return [Cluster(**c) for c in json.loads(reply.readAll().data().decode("utf-8"))] + return [CloudCluster(**c) for c in json.loads(reply.readAll().data().decode("utf-8"))] + except UnicodeDecodeError: + Logger.log("w", "Unable to read server response") except json.decoder.JSONDecodeError: Logger.logException("w", "Unable to decode JSON from reply.") - return None - except UnicodeDecodeError: - Logger.log("e", "Unable to read server response") - except json.JSONDecodeError: - Logger.logException("w", "Unable to decode JSON from reply.") - + except ValueError: + Logger.logException("w", "Response was missing values.") return None ## Adds a CloudOutputDevice for each entry in the remote cluster list from the API. - def _addCloudOutputDevice(self, cluster: Cluster): - print("cluster_data====", cluster) - device = CloudOutputDevice(cluster["cluster_id"]) + def _addCloudOutputDevice(self, cluster: CloudCluster): + device = CloudOutputDevice(cluster.cluster_id) self._output_device_manager.addOutputDevice(device) - self._remote_clusters[cluster["cluster_id"]] = device + self._remote_clusters[cluster.cluster_id] = device ## Callback for when the active machine was changed by the user. def _activeMachineChanged(self): diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models.py b/plugins/UM3NetworkPrinting/src/Cloud/Models.py index b118f3e61c..3cbfecadfb 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models.py @@ -1,11 +1,18 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from collections import namedtuple +from plugins.UM3NetworkPrinting.src.Models import BaseModel -Cluster = namedtuple("Cluster", [ - "cluster_id", # Type: str - "host_guid", # Type: str - "host_name", # Type: str - "host_version", # Type: str - "status", # Type: str -]) + +## Class representing a cloud connected cluster. +class CloudCluster(BaseModel): + def __init__(self, **kwargs): + self.cluster_id = None # type: str + self.host_guid = None # type: str + self.host_name = None # type: str + self.host_version = None # type: str + self.status = None # type: str + super().__init__(**kwargs) + + def validate(self): + if not self.cluster_id: + raise ValueError("cluster_id is required on CloudCluster") diff --git a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py index 47c3482aa5..086bca03e2 100644 --- a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py @@ -1,7 +1,6 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import TYPE_CHECKING - +from UM.Application import Application from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin from UM.Logger import Logger from UM.Signal import Signal, signalemitter @@ -20,9 +19,6 @@ from time import time import json -if TYPE_CHECKING: - from cura.CuraApplication import CuraApplication - ## This plugin handles the connection detection & creation of output device objects for the UM3 printer. # Zero-Conf is used to detect printers, which are saved in a dict. @@ -33,21 +29,20 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): removeDeviceSignal = Signal() discoveredDevicesChanged = Signal() - def __init__(self, application: "CuraApplication"): + def __init__(self): super().__init__() - self._application = application self._zero_conf = None self._zero_conf_browser = None # Create a cloud output device manager that abstract all cloud connection logic away. - # self._cloud_output_device_manager = CloudOutputDeviceManager() + self._cloud_output_device_manager = CloudOutputDeviceManager() # Because the model needs to be created in the same thread as the QMLEngine, we use a signal. self.addDeviceSignal.connect(self._onAddDevice) self.removeDeviceSignal.connect(self._onRemoveDevice) - application.globalContainerStackChanged.connect(self.reCheckConnections) + Application.getInstance().globalContainerStackChanged.connect(self.reCheckConnections) self._discovered_devices = {} @@ -62,7 +57,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self._cluster_api_prefix = "/cluster-api/v" + self._cluster_api_version + "/" # Get list of manual instances from preferences - self._preferences = self._application.getPreferences() + self._preferences = Application.getInstance().getPreferences() self._preferences.addPreference("um3networkprinting/manual_instances", "") # A comma-separated list of ip adresses or hostnames @@ -113,7 +108,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self.resetLastManualDevice() def reCheckConnections(self): - active_machine = self._application.getGlobalContainerStack() + active_machine = Application.getInstance().getGlobalContainerStack() if not active_machine: return @@ -138,7 +133,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): return if self._discovered_devices[key].isConnected(): # Sometimes the status changes after changing the global container and maybe the device doesn't belong to this machine - um_network_key = self._application.getGlobalContainerStack().getMetaDataEntry("um_network_key") + um_network_key = Application.getInstance().getGlobalContainerStack().getMetaDataEntry("um_network_key") if key == um_network_key: self.getOutputDeviceManager().addOutputDevice(self._discovered_devices[key]) else: @@ -290,7 +285,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self._discovered_devices[device.getId()] = device self.discoveredDevicesChanged.emit() - global_container_stack = self._application.getGlobalContainerStack() + global_container_stack = Application.getInstance().getGlobalContainerStack() if global_container_stack and device.getId() == global_container_stack.getMetaDataEntry("um_network_key"): device.connect() device.connectionStateChanged.connect(self._onDeviceConnectionStateChanged) @@ -308,7 +303,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self._service_changed_request_event.wait(timeout = 5.0) # Stop if the application is shutting down - if self._application.isShuttingDown(): + if Application.getInstance().isShuttingDown(): return self._service_changed_request_event.clear() From 856276d8b782b0bacaca0047a47ab2afbae867da Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 26 Nov 2018 14:53:08 +0100 Subject: [PATCH 0394/1240] Cleanup plugin imports --- .../src/UM3OutputDevicePlugin.py | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py index 086bca03e2..47720f3ef8 100644 --- a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py @@ -1,23 +1,22 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +import json +from queue import Queue +from threading import Event, Thread +from time import time + +from zeroconf import Zeroconf, ServiceBrowser, ServiceStateChange, ServiceInfo +from PyQt5.QtNetwork import QNetworkRequest, QNetworkAccessManager +from PyQt5.QtCore import QUrl + from UM.Application import Application from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin from UM.Logger import Logger from UM.Signal import Signal, signalemitter from UM.Version import Version -from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputDeviceManager import CloudOutputDeviceManager from . import ClusterUM3OutputDevice, LegacyUM3OutputDevice - -from PyQt5.QtNetwork import QNetworkRequest, QNetworkAccessManager -from PyQt5.QtCore import QUrl - -from zeroconf import Zeroconf, ServiceBrowser, ServiceStateChange, ServiceInfo -from queue import Queue -from threading import Event, Thread -from time import time - -import json +from .Cloud.CloudOutputDeviceManager import CloudOutputDeviceManager ## This plugin handles the connection detection & creation of output device objects for the UM3 printer. From 269d596f5da45dc1d61a6002901092e00c0546a1 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 26 Nov 2018 14:54:24 +0100 Subject: [PATCH 0395/1240] Fix typing for cluster list --- .../src/Cloud/CloudOutputDeviceManager.py | 6 +++--- plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index e48c06dbe9..9aba01d164 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -1,7 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import json -from typing import Dict, Optional +from typing import Dict, Optional, List from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply @@ -76,7 +76,7 @@ class CloudOutputDeviceManager(NetworkClient): # self._addCloudOutputDevice({ "cluster_id": "LJ0tciiuZZjarrXAvFLEZ6ox4Cvx8FvtXUlQv4vIhV6w" }) @staticmethod - def _parseStatusResponse(reply: QNetworkReply) -> Optional[Cluster]: + def _parseStatusResponse(reply: QNetworkReply) -> List[CloudCluster]: try: return [CloudCluster(**c) for c in json.loads(reply.readAll().data().decode("utf-8"))] except UnicodeDecodeError: @@ -85,7 +85,7 @@ class CloudOutputDeviceManager(NetworkClient): Logger.logException("w", "Unable to decode JSON from reply.") except ValueError: Logger.logException("w", "Response was missing values.") - return None + return [] ## Adds a CloudOutputDevice for each entry in the remote cluster list from the API. def _addCloudOutputDevice(self, cluster: CloudCluster): diff --git a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py index 47720f3ef8..e4b4c2bb0a 100644 --- a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py @@ -34,7 +34,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): self._zero_conf = None self._zero_conf_browser = None - # Create a cloud output device manager that abstract all cloud connection logic away. + # Create a cloud output device manager that abstracts all cloud connection logic away. self._cloud_output_device_manager = CloudOutputDeviceManager() # Because the model needs to be created in the same thread as the QMLEngine, we use a signal. From 39f92ced90194a3f818c87c653ccb1604a2d87d6 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 26 Nov 2018 15:02:52 +0100 Subject: [PATCH 0396/1240] Comment out test --- .../UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 9aba01d164..8efce87094 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -72,8 +72,8 @@ class CloudOutputDeviceManager(NetworkClient): for cluster in clusters: self._addCloudOutputDevice(cluster) - # # For testing we add a dummy device: - # self._addCloudOutputDevice({ "cluster_id": "LJ0tciiuZZjarrXAvFLEZ6ox4Cvx8FvtXUlQv4vIhV6w" }) + # For testing we add a dummy device: + # self._addCloudOutputDevice(CloudCluster(cluster_id = "LJ0tciiuZZjarrXAvFLEZ6ox4Cvx8FvtXUlQv4vIhV6w")) @staticmethod def _parseStatusResponse(reply: QNetworkReply) -> List[CloudCluster]: From a9fedb4f66a8717c1d35e9ecca51197b06ad082e Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 26 Nov 2018 15:05:53 +0100 Subject: [PATCH 0397/1240] Restore NetworkedPrinterOutputDevice for now --- .../NetworkedPrinterOutputDevice.py | 211 +++++++++++++++--- 1 file changed, 177 insertions(+), 34 deletions(-) diff --git a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py index a44b42a8ba..35d2ce014a 100644 --- a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py +++ b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py @@ -1,19 +1,21 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -import os -import gzip -from time import time -from typing import Dict, List, Optional -from enum import IntEnum -from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply -from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QCoreApplication +from UM.FileHandler.FileHandler import FileHandler #For typing. +from UM.Logger import Logger +from UM.Scene.SceneNode import SceneNode #For typing. +from cura.CuraApplication import CuraApplication -from UM.FileHandler.FileHandler import FileHandler -from UM.Scene.SceneNode import SceneNode -from cura.NetworkClient import NetworkClient from cura.PrinterOutputDevice import PrinterOutputDevice, ConnectionState +from PyQt5.QtNetwork import QHttpMultiPart, QHttpPart, QNetworkRequest, QNetworkAccessManager, QNetworkReply, QAuthenticator +from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QUrl, QCoreApplication +from time import time +from typing import Any, Callable, Dict, List, Optional +from enum import IntEnum + +import os # To get the username +import gzip class AuthState(IntEnum): NotAuthenticated = 1 @@ -23,29 +25,35 @@ class AuthState(IntEnum): AuthenticationReceived = 5 -class NetworkedPrinterOutputDevice(PrinterOutputDevice, NetworkClient): +class NetworkedPrinterOutputDevice(PrinterOutputDevice): authenticationStateChanged = pyqtSignal() def __init__(self, device_id, address: str, properties: Dict[bytes, bytes], parent: QObject = None) -> None: - PrinterOutputDevice.__init__(self, device_id = device_id, parent = parent) - NetworkClient.__init__(self) - + super().__init__(device_id = device_id, parent = parent) + self._manager = None # type: Optional[QNetworkAccessManager] + self._last_manager_create_time = None # type: Optional[float] + self._recreate_network_manager_time = 30 + self._timeout_time = 10 # After how many seconds of no response should a timeout occur? + + self._last_response_time = None # type: Optional[float] + self._last_request_time = None # type: Optional[float] + self._api_prefix = "" self._address = address self._properties = properties + self._user_agent = "%s/%s " % (CuraApplication.getInstance().getApplicationName(), CuraApplication.getInstance().getVersion()) + + self._onFinishedCallbacks = {} # type: Dict[str, Callable[[QNetworkReply], None]] self._authentication_state = AuthState.NotAuthenticated + + # QHttpMultiPart objects need to be kept alive and not garbage collected during the + # HTTP which uses them. We hold references to these QHttpMultiPart objects here. + self._kept_alive_multiparts = {} # type: Dict[QNetworkReply, QHttpMultiPart] + self._sending_gcode = False self._compressing_gcode = False - self._gcode = [] # type: List[str] - + self._gcode = [] # type: List[str] self._connection_state_before_timeout = None # type: Optional[ConnectionState] - self._timeout_time = 10 # After how many seconds of no response should a timeout occur? - self._recreate_network_manager_time = 30 - - ## Override creating empty request to compile the full URL. - # Needed to keep NetworkedPrinterOutputDevice backwards compatible after refactoring NetworkClient out of it. - def _createEmptyRequest(self, target: str, content_type: Optional[str] = "application/json") -> QNetworkRequest: - return super()._createEmptyRequest("http://" + self._address + self._api_prefix + target, content_type) def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False, file_handler: Optional[FileHandler] = None, **kwargs: str) -> None: raise NotImplementedError("requestWrite needs to be implemented") @@ -131,6 +139,27 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice, NetworkClient): self.setConnectionState(self._connection_state_before_timeout) self._connection_state_before_timeout = None + def _createEmptyRequest(self, target: str, content_type: Optional[str] = "application/json") -> QNetworkRequest: + url = QUrl("http://" + self._address + self._api_prefix + target) + request = QNetworkRequest(url) + if content_type is not None: + request.setHeader(QNetworkRequest.ContentTypeHeader, "application/json") + request.setHeader(QNetworkRequest.UserAgentHeader, self._user_agent) + return request + + def _createFormPart(self, content_header: str, data: bytes, content_type: Optional[str] = None) -> QHttpPart: + part = QHttpPart() + + if not content_header.startswith("form-data;"): + content_header = "form_data; " + content_header + part.setHeader(QNetworkRequest.ContentDispositionHeader, content_header) + + if content_type is not None: + part.setHeader(QNetworkRequest.ContentTypeHeader, content_type) + + part.setBody(data) + return part + ## Convenience function to get the username from the OS. # The code was copied from the getpass module, as we try to use as little dependencies as possible. def _getUserName(self) -> str: @@ -140,7 +169,130 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice, NetworkClient): return user return "Unknown User" # Couldn't find out username. - @pyqtSlot(str, result = str) + def _clearCachedMultiPart(self, reply: QNetworkReply) -> None: + if reply in self._kept_alive_multiparts: + del self._kept_alive_multiparts[reply] + + def _validateManager(self) -> None: + if self._manager is None: + self._createNetworkManager() + assert (self._manager is not None) + + def put(self, target: str, data: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: + self._validateManager() + request = self._createEmptyRequest(target) + self._last_request_time = time() + if self._manager is not None: + reply = self._manager.put(request, data.encode()) + self._registerOnFinishedCallback(reply, on_finished) + else: + Logger.log("e", "Could not find manager.") + + def delete(self, target: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: + self._validateManager() + request = self._createEmptyRequest(target) + self._last_request_time = time() + if self._manager is not None: + reply = self._manager.deleteResource(request) + self._registerOnFinishedCallback(reply, on_finished) + else: + Logger.log("e", "Could not find manager.") + + def get(self, target: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: + self._validateManager() + request = self._createEmptyRequest(target) + self._last_request_time = time() + if self._manager is not None: + reply = self._manager.get(request) + self._registerOnFinishedCallback(reply, on_finished) + else: + Logger.log("e", "Could not find manager.") + + def post(self, target: str, data: str, on_finished: Optional[Callable[[QNetworkReply], None]], on_progress: Callable = None) -> None: + self._validateManager() + request = self._createEmptyRequest(target) + self._last_request_time = time() + if self._manager is not None: + reply = self._manager.post(request, data.encode()) + if on_progress is not None: + reply.uploadProgress.connect(on_progress) + self._registerOnFinishedCallback(reply, on_finished) + else: + Logger.log("e", "Could not find manager.") + + def postFormWithParts(self, target: str, parts: List[QHttpPart], on_finished: Optional[Callable[[QNetworkReply], None]], on_progress: Callable = None) -> QNetworkReply: + self._validateManager() + request = self._createEmptyRequest(target, content_type=None) + multi_post_part = QHttpMultiPart(QHttpMultiPart.FormDataType) + for part in parts: + multi_post_part.append(part) + + self._last_request_time = time() + + if self._manager is not None: + reply = self._manager.post(request, multi_post_part) + + self._kept_alive_multiparts[reply] = multi_post_part + + if on_progress is not None: + reply.uploadProgress.connect(on_progress) + self._registerOnFinishedCallback(reply, on_finished) + + return reply + else: + Logger.log("e", "Could not find manager.") + + def postForm(self, target: str, header_data: str, body_data: bytes, on_finished: Optional[Callable[[QNetworkReply], None]], on_progress: Callable = None) -> None: + post_part = QHttpPart() + post_part.setHeader(QNetworkRequest.ContentDispositionHeader, header_data) + post_part.setBody(body_data) + + self.postFormWithParts(target, [post_part], on_finished, on_progress) + + def _onAuthenticationRequired(self, reply: QNetworkReply, authenticator: QAuthenticator) -> None: + Logger.log("w", "Request to {url} required authentication, which was not implemented".format(url = reply.url().toString())) + + def _createNetworkManager(self) -> None: + Logger.log("d", "Creating network manager") + if self._manager: + self._manager.finished.disconnect(self.__handleOnFinished) + self._manager.authenticationRequired.disconnect(self._onAuthenticationRequired) + + self._manager = QNetworkAccessManager() + self._manager.finished.connect(self.__handleOnFinished) + self._last_manager_create_time = time() + self._manager.authenticationRequired.connect(self._onAuthenticationRequired) + + if self._properties.get(b"temporary", b"false") != b"true": + CuraApplication.getInstance().getMachineManager().checkCorrectGroupName(self.getId(), self.name) + + def _registerOnFinishedCallback(self, reply: QNetworkReply, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: + if on_finished is not None: + self._onFinishedCallbacks[reply.url().toString() + str(reply.operation())] = on_finished + + def __handleOnFinished(self, reply: QNetworkReply) -> None: + # Due to garbage collection, we need to cache certain bits of post operations. + # As we don't want to keep them around forever, delete them if we get a reply. + if reply.operation() == QNetworkAccessManager.PostOperation: + self._clearCachedMultiPart(reply) + + if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) is None: + # No status code means it never even reached remote. + return + + self._last_response_time = time() + + if self._connection_state == ConnectionState.connecting: + self.setConnectionState(ConnectionState.connected) + + callback_key = reply.url().toString() + str(reply.operation()) + try: + if callback_key in self._onFinishedCallbacks: + self._onFinishedCallbacks[callback_key](reply) + except Exception: + Logger.logException("w", "something went wrong with callback") + + @pyqtSlot(str, result=str) def getProperty(self, key: str) -> str: bytes_key = key.encode("utf-8") if bytes_key in self._properties: @@ -176,16 +328,7 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice, NetworkClient): def printerType(self) -> str: return self._properties.get(b"printer_type", b"Unknown").decode("utf-8") - ## IP address of this printer + ## IP adress of this printer @pyqtProperty(str, constant = True) def ipAddress(self) -> str: return self._address - - def __handleOnFinished(self, reply: QNetworkReply) -> None: - - # Since we got a 200 reply from the network manager we can now be sure we are actually connected. - if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) == 200 and \ - self._connection_state == ConnectionState.connecting: - self.setConnectionState(ConnectionState.connected) - - super().__handleOnFinished(reply) From 84f263f1111a58d46817c2d9d6572e7676a8dcf3 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 26 Nov 2018 15:08:33 +0100 Subject: [PATCH 0398/1240] Fix style for the open file button in the prepare menu. Contributes to CURA-5942. --- plugins/PrepareStage/PrepareMenu.qml | 41 ++++++++++++++++------------ 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index a99426acd8..a953c2b5d1 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -3,7 +3,7 @@ import QtQuick 2.7 import QtQuick.Layouts 1.1 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.3 import UM 1.3 as UM import Cura 1.1 as Cura @@ -27,14 +27,14 @@ Item Item { anchors.horizontalCenter: parent.horizontalCenter - width: openFileButtonBackground.width + itemRow.width + UM.Theme.getSize("default_margin").width + width: openFileButton.width + itemRow.width + UM.Theme.getSize("default_margin").width height: parent.height RowLayout { id: itemRow - anchors.left: openFileButtonBackground.right + anchors.left: openFileButton.right anchors.leftMargin: UM.Theme.getSize("default_margin").width width: Math.round(0.9 * prepareMenu.width) @@ -44,7 +44,7 @@ Item Cura.MachineSelector { id: machineSelection - z: openFileButtonBackground.z - 1 //Ensure that the tooltip of the open file button stays above the item row. + z: openFileButton.z - 1 //Ensure that the tooltip of the open file button stays above the item row. headerCornerSide: Cura.RoundedRectangle.Direction.Left Layout.minimumWidth: UM.Theme.getSize("machine_selector_widget").width Layout.maximumWidth: UM.Theme.getSize("machine_selector_widget").width @@ -86,24 +86,31 @@ Item } } - Rectangle + Button { - id: openFileButtonBackground + id: openFileButton height: UM.Theme.getSize("stage_menu").height width: UM.Theme.getSize("stage_menu").height + onClicked: Cura.Actions.open.trigger() - radius: UM.Theme.getSize("default_radius").width - color: UM.Theme.getColor("toolbar_background") - - Button + contentItem: UM.RecolorImage { - id: openFileButton - text: catalog.i18nc("@action:button", "Open File") - iconSource: UM.Theme.getIcon("load") - style: UM.Theme.styles.toolbar_button - tooltip: "" - action: Cura.Actions.open - anchors.centerIn: parent + id: buttonIcon + source: UM.Theme.getIcon("load") + width: UM.Theme.getSize("button_icon").width + height: UM.Theme.getSize("button_icon").height + color: UM.Theme.getColor("toolbar_button_text") + + sourceSize: UM.Theme.getSize("button_icon") + } + + background: Rectangle + { + height: UM.Theme.getSize("stage_menu").height + width: UM.Theme.getSize("stage_menu").height + + radius: UM.Theme.getSize("default_radius").width + color: openFileButton.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button") } } } From 42ccabc7b6426307fe370abbf5a7312d267353ae Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 26 Nov 2018 15:09:56 +0100 Subject: [PATCH 0399/1240] Fix relative imports for plugin --- plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py | 4 ++-- .../UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 8f0bd62035..93e97a7c71 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -13,8 +13,8 @@ from UM.Scene.SceneNode import SceneNode from cura.CuraApplication import CuraApplication from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel -from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputController import CloudOutputController -from plugins.UM3NetworkPrinting.src.UM3PrintJobOutputModel import UM3PrintJobOutputModel +from .CloudOutputController import CloudOutputController +from ..UM3PrintJobOutputModel import UM3PrintJobOutputModel ## The cloud output device is a network output device that works remotely but has limited functionality. diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 8efce87094..a252f9e4d3 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -8,7 +8,8 @@ from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply from UM.Logger import Logger from cura.CuraApplication import CuraApplication from cura.NetworkClient import NetworkClient -from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputDevice import CloudOutputDevice + +from .CloudOutputDevice import CloudOutputDevice from .Models import CloudCluster From aaf0f69820ee06f8c2a8646328c68b6e3b7e6f3e Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 26 Nov 2018 15:10:36 +0100 Subject: [PATCH 0400/1240] Fix some more relative imports --- plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py | 1 + plugins/UM3NetworkPrinting/src/Cloud/Models.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 93e97a7c71..accc8429b1 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -13,6 +13,7 @@ from UM.Scene.SceneNode import SceneNode from cura.CuraApplication import CuraApplication from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel + from .CloudOutputController import CloudOutputController from ..UM3PrintJobOutputModel import UM3PrintJobOutputModel diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models.py b/plugins/UM3NetworkPrinting/src/Cloud/Models.py index 3cbfecadfb..e98d848d51 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models.py @@ -1,6 +1,6 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from plugins.UM3NetworkPrinting.src.Models import BaseModel +from ..Models import BaseModel ## Class representing a cloud connected cluster. From 9732099250f9085137883907293f5dbd1080691a Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 26 Nov 2018 15:14:32 +0100 Subject: [PATCH 0401/1240] Add border to rounded rectangle. It's designed so that it works in exactly the same way as rectangle. --- resources/qml/BorderGroup.qml | 7 +++++++ resources/qml/RoundedRectangle.qml | 23 +++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 resources/qml/BorderGroup.qml diff --git a/resources/qml/BorderGroup.qml b/resources/qml/BorderGroup.qml new file mode 100644 index 0000000000..94d0d68594 --- /dev/null +++ b/resources/qml/BorderGroup.qml @@ -0,0 +1,7 @@ +import QtQuick 2.0 + +QtObject +{ + property real width: 0 + property color color: "black" +} diff --git a/resources/qml/RoundedRectangle.qml b/resources/qml/RoundedRectangle.qml index 9ad2230be5..3ca05e2125 100644 --- a/resources/qml/RoundedRectangle.qml +++ b/resources/qml/RoundedRectangle.qml @@ -5,6 +5,7 @@ import UM 1.2 as UM // The rounded rectangle works mostly like a regular rectangle, but provides the option to have rounded corners on only one side of the rectangle. Item { + id: roundedRectangle // As per the regular rectangle property color color: "transparent" @@ -15,6 +16,9 @@ Item // 1 is down, 2 is left, 3 is up and 4 is right. property int cornerSide: RoundedRectangle.Direction.None + // Simple object to ensure that border.width and border.color work + property BorderGroup border: BorderGroup {} + enum Direction { None = 0, @@ -31,6 +35,8 @@ Item anchors.fill: parent radius: cornerSide != RoundedRectangle.Direction.None ? parent.radius : 0 color: parent.color + border.width: parent.border.width + border.color: parent.border.color } // The item that covers 2 of the corners to make them not rounded. @@ -45,5 +51,22 @@ Item right: cornerSide == RoundedRectangle.Direction.Left ? parent.right: undefined bottom: cornerSide == RoundedRectangle.Direction.Up ? parent.bottom: undefined } + + border.width: parent.border.width + border.color: parent.border.color + + Rectangle + { + color: roundedRectangle.color + height: cornerSide % 2 ? roundedRectangle.border.width: roundedRectangle.height - 2 * roundedRectangle.border.width + width: cornerSide % 2 ? roundedRectangle.width - 2 * roundedRectangle.border.width: roundedRectangle.border.width + anchors + { + right: cornerSide == RoundedRectangle.Direction.Right ? parent.right : undefined + bottom: cornerSide == RoundedRectangle.Direction.Down ? parent.bottom: undefined + horizontalCenter: cornerSide % 2 ? parent.horizontalCenter: undefined + verticalCenter: cornerSide % 2 ? undefined: parent.verticalCenter + } + } } } From da5683c876682019fe2360cf56fd27afe9f39844 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 26 Nov 2018 15:30:30 +0100 Subject: [PATCH 0402/1240] add typing to models --- plugins/UM3NetworkPrinting/src/Models.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Models.py b/plugins/UM3NetworkPrinting/src/Models.py index d0708c8127..2a34e41f86 100644 --- a/plugins/UM3NetworkPrinting/src/Models.py +++ b/plugins/UM3NetworkPrinting/src/Models.py @@ -4,17 +4,17 @@ ## Base model that maps kwargs to instance attributes. class BaseModel: - def __init__(self, **kwargs): + def __init__(self, **kwargs) -> None: self.__dict__.update(kwargs) self.validate() - def validate(self): + def validate(self) -> None: pass ## Class representing a material that was fetched from the cluster API. class ClusterMaterial(BaseModel): - def __init__(self, **kwargs): + def __init__(self, **kwargs) -> None: self.guid = None # type: str self.material = None # type: str self.brand = None # type: str @@ -23,14 +23,14 @@ class ClusterMaterial(BaseModel): self.density = None # type: str super().__init__(**kwargs) - def validate(self): + def validate(self) -> None: if not self.guid: raise ValueError("guid is required on ClusterMaterial") ## Class representing a local material that was fetched from the container registry. class LocalMaterial(BaseModel): - def __init__(self, **kwargs): + def __init__(self, **kwargs) -> None: self.GUID = None # type: str self.id = None # type: str self.type = None # type: str @@ -49,7 +49,7 @@ class LocalMaterial(BaseModel): self.compatible = None # type: bool super().__init__(**kwargs) - def validate(self): + def validate(self) -> None: if not self.GUID: raise ValueError("guid is required on LocalMaterial") if not self.id: From 9afc5748a8ec1eb7bbbdb8ba7395f7b3d2554a49 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 26 Nov 2018 15:39:34 +0100 Subject: [PATCH 0403/1240] Add copyright header. --- resources/qml/BorderGroup.qml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/resources/qml/BorderGroup.qml b/resources/qml/BorderGroup.qml index 94d0d68594..38ad9fadff 100644 --- a/resources/qml/BorderGroup.qml +++ b/resources/qml/BorderGroup.qml @@ -1,3 +1,6 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + import QtQuick 2.0 QtObject From ac5f79ba2ced74cb13fe1fc00fe328f0b7d5a8a0 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 26 Nov 2018 15:47:32 +0100 Subject: [PATCH 0404/1240] Add tab style to tabbed content It now gets a proper background colour, lining and radius. Contributes to issue CURA-5876. --- .../ConfigurationMenu/CustomConfiguration.qml | 35 +++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index 25111a9365..1f7673cd0a 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -1,7 +1,7 @@ // Copyright (c) 2018 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.0 +import QtQuick 2.6 import QtQuick.Controls 2.0 import QtQuick.Controls 1.1 as OldControls @@ -33,7 +33,7 @@ Item { id: tabBar anchors.top: header.bottom - anchors.topMargin: UM.Theme.getSize("default_margin").height + header.height + anchors.topMargin: UM.Theme.getSize("default_margin").height onCurrentIndexChanged: Cura.ExtruderManager.setActiveExtruderIndex(currentIndex) @@ -57,17 +57,46 @@ Item } } - Item + Rectangle { id: tabControl width: parent.width height: childrenRect.height anchors.top: tabBar.bottom + + radius: UM.Theme.getSize("default_radius").width + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") + color: UM.Theme.getColor("secondary") + + //Remove rounding and lining at the top. + Rectangle + { + width: parent.width + height: parent.radius + anchors.top: parent.top + color: UM.Theme.getColor("lining") + Rectangle + { + anchors + { + left: parent.left + leftMargin: parent.parent.border.width + right: parent.right + rightMargin: parent.parent.border.width + top: parent.top + } + height: parent.parent.radius + color: parent.parent.color + } + } + property var model: extrudersModel.items[tabBar.currentIndex] property real textWidth: Math.round(width * 0.3) property real controlWidth: width - textWidth Column { + padding: UM.Theme.getSize("default_margin").width spacing: UM.Theme.getSize("default_margin").height Row From 3d1157522a4d9c318a2c2d3f068c3326e3e3b81c Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 26 Nov 2018 15:51:36 +0100 Subject: [PATCH 0405/1240] Reuse the RoundedRectangle component and indicate that only the bottom part of the popup should be rounded. Contributes to CURA-5942. --- resources/qml/ExpandableComponent.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index ccfb9c6da2..9b2826daed 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -2,6 +2,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.3 import UM 1.2 as UM +import Cura 1.0 as Cura // The expandable component has 3 major sub components: // * The headerItem; Always visible and should hold some info about what happens if the component is expanded @@ -162,8 +163,9 @@ Item padding: UM.Theme.getSize("default_margin").width closePolicy: Popup.CloseOnPressOutsideParent - background: Rectangle + background: Cura.RoundedRectangle { + cornerSide: Cura.RoundedRectangle.Direction.Down color: popupBackgroundColor border.width: UM.Theme.getSize("default_lining").width border.color: UM.Theme.getColor("lining") From e4d8fb36abccd5e1a5f7f68bef086ae82ec3b9a4 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 26 Nov 2018 15:53:04 +0100 Subject: [PATCH 0406/1240] Add more typing as per request from @sedwards2009 --- plugins/UM3NetworkPrinting/src/Models.py | 35 ++++++++---------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Models.py b/plugins/UM3NetworkPrinting/src/Models.py index 2a34e41f86..5ef44bc006 100644 --- a/plugins/UM3NetworkPrinting/src/Models.py +++ b/plugins/UM3NetworkPrinting/src/Models.py @@ -14,43 +14,30 @@ class BaseModel: ## Class representing a material that was fetched from the cluster API. class ClusterMaterial(BaseModel): - def __init__(self, **kwargs) -> None: - self.guid = None # type: str - self.material = None # type: str - self.brand = None # type: str - self.version = None # type: int - self.color = None # type: str - self.density = None # type: str + def __init__(self, guid = str, version = str, **kwargs) -> None: + self.guid = guid # type: str + self.version = version # type: int super().__init__(**kwargs) def validate(self) -> None: if not self.guid: raise ValueError("guid is required on ClusterMaterial") + if not self.version: + raise ValueError("version is required on ClusterMaterial") ## Class representing a local material that was fetched from the container registry. class LocalMaterial(BaseModel): - def __init__(self, **kwargs) -> None: - self.GUID = None # type: str - self.id = None # type: str - self.type = None # type: str - self.status = None # type: str - self.base_file = None # type: str - self.setting_version = None # type: int - self.version = None # type: int - self.brand = None # type: str - self.material = None # type: str - self.color_name = None # type: str - self.color_code = None # type: str - self.description = None # type: str - self.adhesion_info = None # type: str - self.approximate_diameter = None # type: str - self.definition = None # type: str - self.compatible = None # type: bool + def __init__(self, GUID = str, id = str, version = str, **kwargs) -> None: + self.GUID = GUID # type: str + self.id = id # type: str + self.version = version # type: int super().__init__(**kwargs) def validate(self) -> None: if not self.GUID: raise ValueError("guid is required on LocalMaterial") + if not self.version: + raise ValueError("version is required on LocalMaterial") if not self.id: raise ValueError("id is required on LocalMaterial") From 4b897ffd67a5572f305a981cba79ace625d086d9 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 26 Nov 2018 16:03:40 +0100 Subject: [PATCH 0407/1240] Fix width of content in tabbed content Contributes to issue CURA-5876. --- .../ConfigurationMenu/CustomConfiguration.qml | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index 1f7673cd0a..78448d5be5 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -59,7 +59,6 @@ Item Rectangle { - id: tabControl width: parent.width height: childrenRect.height anchors.top: tabBar.bottom @@ -91,14 +90,18 @@ Item } } - property var model: extrudersModel.items[tabBar.currentIndex] - property real textWidth: Math.round(width * 0.3) - property real controlWidth: width - textWidth Column { + id: selectors padding: UM.Theme.getSize("default_margin").width spacing: UM.Theme.getSize("default_margin").height + property var model: extrudersModel.items[tabBar.currentIndex] + + readonly property real paddedWidth: parent.width - padding * 2 + property real textWidth: Math.round(paddedWidth * 0.3) + property real controlWidth: paddedWidth - textWidth + Row { height: UM.Theme.getSize("print_setup_item").height @@ -110,13 +113,13 @@ Item font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") height: parent.height - width: tabControl.textWidth + width: selectors.textWidth } OldControls.CheckBox { - checked: tabControl.model != null ? Cura.MachineManager.getExtruder(tabControl.model.index).isEnabled: false - onClicked: Cura.MachineManager.setExtruderEnabled(tabControl.model.index, checked) + checked: selectors.model != null ? Cura.MachineManager.getExtruder(selectors.model.index).isEnabled: false + onClicked: Cura.MachineManager.setExtruderEnabled(selectors.model.index, checked) height: UM.Theme.getSize("setting_control").height style: UM.Theme.styles.checkbox } @@ -132,7 +135,7 @@ Item font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") height: parent.height - width: tabControl.textWidth + width: selectors.textWidth } OldControls.ToolButton @@ -152,7 +155,7 @@ Item enabled: Cura.ExtruderManager.activeExtruderIndex > -1 height: UM.Theme.getSize("setting_control").height - width: tabControl.controlWidth + width: selectors.controlWidth style: UM.Theme.styles.sidebar_header_button activeFocusOnPress: true @@ -174,7 +177,7 @@ Item font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") height: parent.height - width: tabControl.textWidth + width: selectors.textWidth } OldControls.ToolButton @@ -185,7 +188,7 @@ Item visible: Cura.MachineManager.hasVariants height: UM.Theme.getSize("setting_control").height - width: tabControl.controlWidth + width: selectors.controlWidth style: UM.Theme.styles.sidebar_header_button activeFocusOnPress: true; From 6506596eceeb241decdc8456f8d2d3d21a9b140e Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 26 Nov 2018 16:19:01 +0100 Subject: [PATCH 0408/1240] Fix typing syntax --- plugins/UM3NetworkPrinting/src/Models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Models.py b/plugins/UM3NetworkPrinting/src/Models.py index 5ef44bc006..2bcac70766 100644 --- a/plugins/UM3NetworkPrinting/src/Models.py +++ b/plugins/UM3NetworkPrinting/src/Models.py @@ -14,7 +14,7 @@ class BaseModel: ## Class representing a material that was fetched from the cluster API. class ClusterMaterial(BaseModel): - def __init__(self, guid = str, version = str, **kwargs) -> None: + def __init__(self, guid: str, version: int, **kwargs) -> None: self.guid = guid # type: str self.version = version # type: int super().__init__(**kwargs) @@ -28,7 +28,7 @@ class ClusterMaterial(BaseModel): ## Class representing a local material that was fetched from the container registry. class LocalMaterial(BaseModel): - def __init__(self, GUID = str, id = str, version = str, **kwargs) -> None: + def __init__(self, GUID: str, id: str, version: int, **kwargs) -> None: self.GUID = GUID # type: str self.id = id # type: str self.version = version # type: int From efd5f3799bdb1c156722046bc2056b961d217f4d Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 26 Nov 2018 16:31:32 +0100 Subject: [PATCH 0409/1240] Also catch TypeError now that we have explicit arguments --- plugins/UM3NetworkPrinting/src/SendMaterialJob.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py index 6f33e75ee1..f536fad49a 100644 --- a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py @@ -160,7 +160,9 @@ class SendMaterialJob(Job): except json.JSONDecodeError: Logger.log("e", "Request material storage on printer: I didn't understand the printer's answer.") except ValueError: - Logger.log("e", "Request material storage on printer: Printer's answer was missing a value.") + Logger.log("e", "Request material storage on printer: Printer's answer had an incorrect value.") + except TypeError: + Logger.log("e", "Request material storage on printer: Printer's answer was missing a required value.") ## Retrieves a list of local materials # @@ -189,5 +191,7 @@ class SendMaterialJob(Job): Logger.logException("w", "Local material {} has missing values.".format(material["id"])) except ValueError: Logger.logException("w", "Local material {} has invalid values.".format(material["id"])) + except TypeError: + Logger.logException("w", "Local material {} has invalid values.".format(material["id"])) return result From d85aee1c53cb27983f73d153685a1681efa78b58 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 26 Nov 2018 16:40:29 +0100 Subject: [PATCH 0410/1240] Ensure that no weird data is set in the printSetupSelector on first start CURA-5961 --- resources/qml/PrintSetupSelector.qml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/qml/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector.qml index 2ecdc9e546..9b90d8589f 100644 --- a/resources/qml/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector.qml @@ -54,12 +54,12 @@ Cura.ExpandableComponent IconWithText { source: UM.Theme.getIcon("category_layer_height") - text: Cura.MachineManager.activeQualityOrQualityChangesName + " " + layerHeight.properties.value + "mm" + text: Cura.MachineManager.activeStack ? Cura.MachineManager.activeQualityOrQualityChangesName + " " + layerHeight.properties.value + "mm" : "" UM.SettingPropertyProvider { id: layerHeight - containerStackId: Cura.MachineManager.activeStackId + containerStack: Cura.MachineManager.activeStack key: "layer_height" watchedProperties: ["value"] } @@ -68,12 +68,12 @@ Cura.ExpandableComponent IconWithText { source: UM.Theme.getIcon("category_infill") - text: parseInt(infillDensity.properties.value) + "%" + text: Cura.MachineManager.activeStack ? parseInt(infillDensity.properties.value) + "%" : "0%" UM.SettingPropertyProvider { id: infillDensity - containerStackId: Cura.MachineManager.activeStackId + containerStack: Cura.MachineManager.activeStack key: "infill_sparse_density" watchedProperties: ["value"] } From 87a0dc65e17742760fe7b551e2b8b5c4db0035a2 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 26 Nov 2018 17:09:36 +0100 Subject: [PATCH 0411/1240] Add list of available configurations to AutoConfiguration This is the main item it needs to display. Contributes to issue CURA-5876. --- .../qml/Menus/ConfigurationMenu/AutoConfiguration.qml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml index cde18ab488..8e86549e17 100644 --- a/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml @@ -5,6 +5,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.0 import UM 1.3 as UM +import Cura 1.0 as Cura Item { @@ -26,4 +27,13 @@ Item right: parent.right } } + + ConfigurationListView + { + anchors.top: header.bottom + anchors.topMargin: UM.Theme.getSize("default_margin").width + width: parent.width + + outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null + } } \ No newline at end of file From a825daea9565cdb5cfe405a1bb58750a76cdf988 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 26 Nov 2018 17:10:48 +0100 Subject: [PATCH 0412/1240] Cleanup the Theme.json There were a lot of sizes that weren't used --- resources/qml/Dialogs/AddMachineDialog.qml | 1 - resources/themes/cura-dark/theme.json | 4 - resources/themes/cura-light/styles.qml | 248 --------------------- resources/themes/cura-light/theme.json | 20 -- 4 files changed, 273 deletions(-) diff --git a/resources/qml/Dialogs/AddMachineDialog.qml b/resources/qml/Dialogs/AddMachineDialog.qml index aa160acd4d..8b2b9d1868 100644 --- a/resources/qml/Dialogs/AddMachineDialog.qml +++ b/resources/qml/Dialogs/AddMachineDialog.qml @@ -298,7 +298,6 @@ UM.Dialog id: machineName text: getMachineName() width: Math.floor(parent.width * 0.75) - implicitWidth: UM.Theme.getSize("standard_list_input").width maximumLength: 40 //validator: Cura.MachineNameValidator { } //TODO: Gives a segfault in PyQt5.6. For now, we must use a signal on text changed. validator: RegExpValidator diff --git a/resources/themes/cura-dark/theme.json b/resources/themes/cura-dark/theme.json index 62b1dbfa0f..34b944b25b 100644 --- a/resources/themes/cura-dark/theme.json +++ b/resources/themes/cura-dark/theme.json @@ -133,7 +133,6 @@ "slider_groove_border": [127, 127, 127, 255], "slider_groove_fill": [245, 245, 245, 255], "slider_handle": [255, 255, 255, 255], - "slider_handle_hover": [77, 182, 226, 255], "slider_handle_active": [68, 192, 255, 255], "slider_text_background": [255, 255, 255, 255], @@ -209,9 +208,6 @@ "quality_slider_unavailable": [179, 179, 179, 255], "quality_slider_available": [255, 255, 255, 255], - "quality_slider_handle": [255, 255, 255, 255], - "quality_slider_handle_hover": [127, 127, 127, 255], - "quality_slider_text": [255, 255, 255, 255], "toolbox_header_button_text_active": [255, 255, 255, 255], "toolbox_header_button_text_inactive": [128, 128, 128, 255], diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index 723b393efa..f4aeb95bb3 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -599,198 +599,6 @@ QtObject } } - property Component sidebar_category: Component - { - ButtonStyle - { - background: Rectangle - { - anchors.fill: parent - anchors.left: parent.left - anchors.leftMargin: Theme.getSize("thick_margin").width - anchors.right: parent.right - anchors.rightMargin: Theme.getSize("thick_margin").width - implicitHeight: Theme.getSize("section").height - color: - { - if(control.color) - { - return control.color; - } - else if(!control.enabled) - { - return Theme.getColor("setting_category_disabled"); - } - else if(control.hovered && control.checkable && control.checked) - { - return Theme.getColor("setting_category_active_hover"); - } - else if(control.pressed || (control.checkable && control.checked)) - { - return Theme.getColor("setting_category_active"); - } - else if(control.hovered) - { - return Theme.getColor("setting_category_hover"); - } - else - { - return Theme.getColor("setting_category"); - } - } - Behavior on color { ColorAnimation { duration: 50; } } - Rectangle - { - height: Theme.getSize("default_lining").height - width: parent.width - anchors.bottom: parent.bottom - color: - { - if(!control.enabled) - { - return Theme.getColor("setting_category_disabled_border"); - } - else if((control.hovered || control.activeFocus) && control.checkable && control.checked) - { - return Theme.getColor("setting_category_active_hover_border"); - } - else if(control.pressed || (control.checkable && control.checked)) - { - return Theme.getColor("setting_category_active_border"); - } - else if(control.hovered || control.activeFocus) - { - return Theme.getColor("setting_category_hover_border"); - } - else - { - return Theme.getColor("setting_category_border"); - } - } - } - } - label: Item - { - anchors.fill: parent - anchors.left: parent.left - Item - { - id: icon - anchors.left: parent.left - height: parent.height - width: Theme.getSize("section_icon_column").width - UM.RecolorImage - { - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: Theme.getSize("thick_margin").width - color: - { - if(!control.enabled) - { - return Theme.getColor("setting_category_disabled_text"); - } - else if((control.hovered || control.activeFocus) && control.checkable && control.checked) - { - return Theme.getColor("setting_category_active_hover_text"); - } - else if(control.pressed || (control.checkable && control.checked)) - { - return Theme.getColor("setting_category_active_text"); - } - else if(control.hovered || control.activeFocus) - { - return Theme.getColor("setting_category_hover_text"); - } - else - { - return Theme.getColor("setting_category_text"); - } - } - source: control.iconSource; - width: Theme.getSize("section_icon").width; - height: Theme.getSize("section_icon").height; - sourceSize.width: width + 15 * screenScaleFactor - sourceSize.height: width + 15 * screenScaleFactor - } - } - - Label - { - anchors - { - left: icon.right - leftMargin: Theme.getSize("default_margin").width - right: parent.right - verticalCenter: parent.verticalCenter - } - text: control.text - font: Theme.getFont("setting_category") - color: - { - if(!control.enabled) - { - return Theme.getColor("setting_category_disabled_text"); - } - else if((control.hovered || control.activeFocus) && control.checkable && control.checked) - { - return Theme.getColor("setting_category_active_hover_text"); - } - else if(control.pressed || (control.checkable && control.checked)) - { - return Theme.getColor("setting_category_active_text"); - } - else if(control.hovered || control.activeFocus) - { - return Theme.getColor("setting_category_hover_text"); - } - else - { - return Theme.getColor("setting_category_text"); - } - } - fontSizeMode: Text.HorizontalFit - minimumPointSize: 8 - } - UM.RecolorImage - { - id: category_arrow - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: Theme.getSize("default_margin").width * 3 - Math.round(width / 2) - width: Theme.getSize("standard_arrow").width - height: Theme.getSize("standard_arrow").height - sourceSize.width: width - sourceSize.height: width - color: - { - if(!control.enabled) - { - return Theme.getColor("setting_category_disabled_text"); - } - else if((control.hovered || control.activeFocus) && control.checkable && control.checked) - { - return Theme.getColor("setting_category_active_hover_text"); - } - else if(control.pressed || (control.checkable && control.checked)) - { - return Theme.getColor("setting_category_active_text"); - } - else if(control.hovered || control.activeFocus) - { - return Theme.getColor("setting_category_hover_text"); - } - else - { - return Theme.getColor("setting_category_text"); - } - } - source: control.checked ? Theme.getIcon("arrow_bottom") : Theme.getIcon("arrow_left") - } - } - } - } - property Component scrollview: Component { ScrollViewStyle @@ -1144,60 +952,4 @@ QtObject label: Item { } } } - - property Component toolbox_action_button: Component - { - ButtonStyle - { - background: Rectangle - { - implicitWidth: UM.Theme.getSize("toolbox_action_button").width - implicitHeight: UM.Theme.getSize("toolbox_action_button").height - color: - { - if (control.installed) - { - return UM.Theme.getColor("action_button_disabled"); - } - else - { - if (control.hovered) - { - return UM.Theme.getColor("primary_hover"); - } - else - { - return UM.Theme.getColor("primary"); - } - } - - } - } - label: Label - { - text: control.text - color: - { - if (control.installed) - { - return UM.Theme.getColor("action_button_disabled_text"); - } - else - { - if (control.hovered) - { - return UM.Theme.getColor("button_text_hover"); - } - else - { - return UM.Theme.getColor("button_text"); - } - } - } - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - font: UM.Theme.getFont("default_bold") - } - } - } } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 2343cd3f2a..67e0c701c2 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -122,7 +122,6 @@ "text_detail": [174, 174, 174, 128], "text_link": [50, 130, 255, 255], "text_inactive": [174, 174, 174, 255], - "text_hover": [70, 84, 113, 255], "text_pressed": [50, 130, 255, 255], "text_subtext": [0, 0, 0, 255], "text_medium": [128, 128, 128, 255], @@ -228,15 +227,11 @@ "slider_groove": [223, 223, 223, 255], "slider_groove_fill": [10, 8, 80, 255], "slider_handle": [10, 8, 80, 255], - "slider_handle_hover": [77, 182, 226, 255], "slider_handle_active": [50, 130, 255, 255], "slider_text_background": [255, 255, 255, 255], "quality_slider_unavailable": [179, 179, 179, 255], "quality_slider_available": [0, 0, 0, 255], - "quality_slider_handle": [0, 0, 0, 255], - "quality_slider_handle_hover": [127, 127, 127, 255], - "quality_slider_text": [0, 0, 0, 255], "checkbox": [255, 255, 255, 255], "checkbox_hover": [255, 255, 255, 255], @@ -245,15 +240,6 @@ "checkbox_mark": [119, 122, 124, 255], "checkbox_text": [27, 27, 27, 255], - "mode_switch": [255, 255, 255, 255], - "mode_switch_hover": [255, 255, 255, 255], - "mode_switch_border": [127, 127, 127, 255], - "mode_switch_border_hover": [50, 130, 255, 255], - "mode_switch_handle": [31, 36, 39, 255], - "mode_switch_text": [31, 36, 39, 255], - "mode_switch_text_hover": [31, 36, 39, 255], - "mode_switch_text_checked": [50, 130, 255, 255], - "tooltip": [68, 192, 255, 255], "tooltip_text": [255, 255, 255, 255], @@ -384,7 +370,6 @@ "print_setup_item": [0.0, 2.0], "print_setup_extruder_box": [0.0, 6.0], - "configuration_selector_widget": [35.0, 4.5], "configuration_selector_mode_tabs": [0.0, 3.0], "action_panel_widget": [25.0, 0.0], @@ -412,9 +397,6 @@ "extruder_icon": [1.8, 1.8], - "simple_mode_infill_caption": [0.0, 5.0], - "simple_mode_infill_height": [0.0, 8.0], - "section": [0.0, 2.2], "section_icon": [1.6, 1.6], "section_icon_column": [2.8, 0.0], @@ -428,7 +410,6 @@ "setting_text_maxwidth": [40.0, 0.0], "standard_list_lineheight": [1.5, 1.5], - "standard_list_input": [20.0, 25.0], "standard_arrow": [0.8, 0.8], "button": [4, 4], @@ -482,7 +463,6 @@ "modal_window_minimum": [60.0, 45], "license_window_minimum": [45, 45], - "wizard_progress": [10.0, 0.0], "message": [30.0, 5.0], "message_close": [1, 1], From a268c95559fcfda0e90048c666a1c9e3ed62f002 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 26 Nov 2018 17:10:56 +0100 Subject: [PATCH 0413/1240] Remove double header This header is no longer necessary since the AutoConfiguration item provides its own header that doesn't scroll along. Contributes to issue CURA-5876. --- .../Menus/ConfigurationMenu/ConfigurationListView.qml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml index 7aaf87b4df..ef967dfa35 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml @@ -12,7 +12,6 @@ Column { id: base property var outputDevice: null - property var computedHeight: container.height + configurationListHeading.height + 3 * padding height: childrenRect.height + 2 * padding padding: UM.Theme.getSize("default_margin").width spacing: Math.round(UM.Theme.getSize("default_margin").height / 2) @@ -27,15 +26,6 @@ Column } } - Label - { - id: configurationListHeading - text: catalog.i18nc("@label:header configurations", "Available configurations") - font: UM.Theme.getFont("large") - width: parent.width - 2 * parent.padding - color: UM.Theme.getColor("configuration_item_text") - } - Component { id: sectionHeading From 3b219e3ac331e3404867433e1f968d2a29be4fa6 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 26 Nov 2018 17:13:25 +0100 Subject: [PATCH 0414/1240] Remove padding The padding is already done by the enveloping Column and its parent popupItem. Contributes to issue CURA-5876. --- resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml index ef967dfa35..be9aad6d04 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml @@ -13,7 +13,6 @@ Column id: base property var outputDevice: null height: childrenRect.height + 2 * padding - padding: UM.Theme.getSize("default_margin").width spacing: Math.round(UM.Theme.getSize("default_margin").height / 2) function forceModelUpdate() From a03e1be6011362cb044cc589373640e60aab1e5a Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 26 Nov 2018 17:19:32 +0100 Subject: [PATCH 0415/1240] Simplify sectionHeading element No need to define that component in a higher location, just define it where you need it. Also, no need to use a Rectangle for this if it has no colour, just use an Item, but the Item itself is also not necessary if you just need the padding. Contributes to issue CURA-5876. --- .../ConfigurationListView.qml | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml index be9aad6d04..25dc3fac11 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml @@ -25,21 +25,6 @@ Column } } - Component - { - id: sectionHeading - Rectangle - { - height: childrenRect.height + UM.Theme.getSize("default_margin").height - Label - { - text: section - font: UM.Theme.getFont("default_bold") - color: UM.Theme.getColor("configuration_item_text") - } - } - } - ScrollView { id: container @@ -58,7 +43,13 @@ Column section.property: "modelData.printerType" section.criteria: ViewSection.FullString - section.delegate: sectionHeading + section.delegate: Label + { + text: section + font: UM.Theme.getFont("default_bold") + color: UM.Theme.getColor("configuration_item_text") + bottomPadding: UM.Theme.getSize("default_margin").height + } model: (outputDevice != null) ? outputDevice.uniqueConfigurations : [] delegate: ConfigurationItem From a3fe9839baafe17c2c221a50c16f590d5b1acd39 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 26 Nov 2018 17:33:37 +0100 Subject: [PATCH 0416/1240] Change all uses of 'small' and 'very small' fonts to default These two fonts are exactly the same, but I want to use the 'small' font to be something a bit smaller. I'm 'removing' the superfluous font but will be re-using it immediately to look a bit smaller. Contributes to issue CURA-5876. --- .../Toolbox/resources/qml/ToolboxAuthorPage.qml | 8 ++++---- .../resources/qml/ToolboxCompatibilityChart.qml | 2 +- .../Toolbox/resources/qml/ToolboxDetailPage.qml | 16 ++++++++-------- .../resources/qml/ToolboxDownloadsGridTile.qml | 2 +- .../qml/MonitorBuildplateConfiguration.qml | 2 +- .../qml/MonitorExtruderConfiguration.qml | 4 ++-- .../qml/ActionPanel/OutputProcessWidget.qml | 4 ++-- .../qml/ActionPanel/PrintJobInformation.qml | 8 ++++---- resources/qml/ActionPanel/SliceProcessWidget.qml | 2 +- resources/qml/IconLabel.qml | 2 +- resources/qml/JobSpecs.qml | 2 +- .../ConfigurationMenu/ConfigurationListView.qml | 2 +- resources/qml/PrinterOutput/ExtruderBox.qml | 2 +- resources/qml/PrinterOutput/HeatedBedBox.qml | 2 +- .../qml/PrinterOutput/OutputDeviceHeader.qml | 4 ++-- 15 files changed, 31 insertions(+), 31 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml b/plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml index 4aaea20813..9c1df0c49e 100644 --- a/plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml @@ -86,13 +86,13 @@ Item Label { text: catalog.i18nc("@label", "Website") + ":" - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text_medium") } Label { text: catalog.i18nc("@label", "Email") + ":" - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text_medium") } } @@ -118,7 +118,7 @@ Item } return "" } - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") linkColor: UM.Theme.getColor("text_link") onLinkActivated: Qt.openUrlExternally(link) @@ -134,7 +134,7 @@ Item } return "" } - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") linkColor: UM.Theme.getColor("text_link") onLinkActivated: Qt.openUrlExternally(link) diff --git a/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml b/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml index 62e1e3ab86..6a01d7ff2f 100644 --- a/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml +++ b/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml @@ -210,7 +210,7 @@ Item } return result } - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") linkColor: UM.Theme.getColor("text_link") onLinkActivated: Qt.openUrlExternally(link) diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml index 437a2ef351..90f372cf87 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml @@ -82,25 +82,25 @@ Item Label { text: catalog.i18nc("@label", "Version") + ":" - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text_medium") } Label { text: catalog.i18nc("@label", "Last updated") + ":" - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text_medium") } Label { text: catalog.i18nc("@label", "Author") + ":" - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text_medium") } Label { text: catalog.i18nc("@label", "Downloads") + ":" - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text_medium") } } @@ -119,7 +119,7 @@ Item Label { text: details === null ? "" : (details.version || catalog.i18nc("@label", "Unknown")) - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") } Label @@ -133,7 +133,7 @@ Item var date = new Date(details.last_updated) return date.toLocaleString(UM.Preferences.getValue("general/language")) } - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") } Label @@ -153,7 +153,7 @@ Item return "" + details.author_name + "" } } - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") linkColor: UM.Theme.getColor("text_link") onLinkActivated: Qt.openUrlExternally(link) @@ -161,7 +161,7 @@ Item Label { text: details === null ? "" : (details.download_count || catalog.i18nc("@label", "Unknown")) - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") } } diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml index 887140bbfa..be44c0f374 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml @@ -81,7 +81,7 @@ Item width: parent.width wrapMode: Text.WordWrap color: UM.Theme.getColor("text_medium") - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") } } } diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml index 9ffb1eabb4..7edeb81a96 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml @@ -52,7 +52,7 @@ Item id: buildplateLabel color: "#191919" // TODO: Theme! elide: Text.ElideRight - font: UM.Theme.getFont("very_small") // 12pt, regular + font: UM.Theme.getFont("default") // 12pt, regular text: "" // FIXED-LINE-HEIGHT: diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorExtruderConfiguration.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorExtruderConfiguration.qml index afbd4c3641..1e53191d8c 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorExtruderConfiguration.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorExtruderConfiguration.qml @@ -49,7 +49,7 @@ Item } color: "#191919" // TODO: Theme! elide: Text.ElideRight - font: UM.Theme.getFont("very_small") // 12pt, regular + font: UM.Theme.getFont("default") // 12pt, regular text: "" // FIXED-LINE-HEIGHT: @@ -66,7 +66,7 @@ Item } color: "#191919" // TODO: Theme! elide: Text.ElideRight - font: UM.Theme.getFont("small") // 12pt, bold + font: UM.Theme.getFont("default_bold") // 12pt, bold text: "" // FIXED-LINE-HEIGHT: diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml index 6f9e6a02c3..296ee2fc16 100644 --- a/resources/qml/ActionPanel/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -50,7 +50,7 @@ Column text: PrintInformation.currentPrintTime.getDisplayString(UM.DurationFormat.Long) source: UM.Theme.getIcon("clock") - font: UM.Theme.getFont("small") + font: UM.Theme.getFont("default_bold") } Cura.IconLabel @@ -79,7 +79,7 @@ Column return totalWeights + "g · " + totalLengths.toFixed(2) + "m" } source: UM.Theme.getIcon("spool") - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") } } diff --git a/resources/qml/ActionPanel/PrintJobInformation.qml b/resources/qml/ActionPanel/PrintJobInformation.qml index e53a92a994..b1172a91e0 100644 --- a/resources/qml/ActionPanel/PrintJobInformation.qml +++ b/resources/qml/ActionPanel/PrintJobInformation.qml @@ -31,7 +31,7 @@ Column { text: catalog.i18nc("@label", "Time specification").toUpperCase() color: UM.Theme.getColor("primary") - font: UM.Theme.getFont("small") + font: UM.Theme.getFont("default_bold") renderType: Text.NativeRendering } @@ -62,7 +62,7 @@ Column } width: parent.width - 2 * UM.Theme.getSize("default_margin").width color: UM.Theme.getColor("text") - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") renderType: Text.NativeRendering textFormat: Text.RichText } @@ -81,7 +81,7 @@ Column { text: catalog.i18nc("@label", "Material specification").toUpperCase() color: UM.Theme.getColor("primary") - font: UM.Theme.getFont("small") + font: UM.Theme.getFont("default_bold") renderType: Text.NativeRendering } @@ -153,7 +153,7 @@ Column } width: parent.width - 2 * UM.Theme.getSize("default_margin").width color: UM.Theme.getColor("text") - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") renderType: Text.NativeRendering textFormat: Text.RichText } diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index 09e0b2584a..9ce86463f8 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -49,7 +49,7 @@ Column text: catalog.i18nc("@label:PrintjobStatus", "Unable to Slice") source: UM.Theme.getIcon("warning") color: UM.Theme.getColor("warning") - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") } // Progress bar, only visible when the backend is in the process of slice the printjob diff --git a/resources/qml/IconLabel.qml b/resources/qml/IconLabel.qml index 7c90382892..1f15e0823f 100644 --- a/resources/qml/IconLabel.qml +++ b/resources/qml/IconLabel.qml @@ -43,7 +43,7 @@ Item anchors.verticalCenter: icon.verticalCenter text: "Empty label" color: UM.Theme.getColor("text") - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") renderType: Text.NativeRendering } } \ No newline at end of file diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index 45111992c1..717d6e925b 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -124,7 +124,7 @@ Item { } height: UM.Theme.getSize("jobspecs_line").height verticalAlignment: Text.AlignVCenter - font: UM.Theme.getFont("small") + font: UM.Theme.getFont("default_bold") color: UM.Theme.getColor("text_scene") text: CuraApplication.getSceneBoundingBoxString } diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml index 25dc3fac11..8a7094497e 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml @@ -46,7 +46,7 @@ Column section.delegate: Label { text: section - font: UM.Theme.getFont("default_bold") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("configuration_item_text") bottomPadding: UM.Theme.getSize("default_margin").height } diff --git a/resources/qml/PrinterOutput/ExtruderBox.qml b/resources/qml/PrinterOutput/ExtruderBox.qml index 510a44f9da..d72f9ba0b2 100644 --- a/resources/qml/PrinterOutput/ExtruderBox.qml +++ b/resources/qml/PrinterOutput/ExtruderBox.qml @@ -45,7 +45,7 @@ Item { id: extruderTargetTemperature text: Math.round(extruderModel.targetHotendTemperature) + "°C" - font: UM.Theme.getFont("small") + font: UM.Theme.getFont("default_bold") color: UM.Theme.getColor("text_inactive") anchors.right: parent.right anchors.rightMargin: UM.Theme.getSize("default_margin").width diff --git a/resources/qml/PrinterOutput/HeatedBedBox.qml b/resources/qml/PrinterOutput/HeatedBedBox.qml index 962ffa9b01..59f7fa7bc6 100644 --- a/resources/qml/PrinterOutput/HeatedBedBox.qml +++ b/resources/qml/PrinterOutput/HeatedBedBox.qml @@ -34,7 +34,7 @@ Item { id: bedTargetTemperature text: printerModel != null ? printerModel.targetBedTemperature + "°C" : "" - font: UM.Theme.getFont("small") + font: UM.Theme.getFont("default_bold") color: UM.Theme.getColor("text_inactive") anchors.right: parent.right anchors.rightMargin: UM.Theme.getSize("default_margin").width diff --git a/resources/qml/PrinterOutput/OutputDeviceHeader.qml b/resources/qml/PrinterOutput/OutputDeviceHeader.qml index b5ed1b7b4e..4e81d852bb 100644 --- a/resources/qml/PrinterOutput/OutputDeviceHeader.qml +++ b/resources/qml/PrinterOutput/OutputDeviceHeader.qml @@ -43,7 +43,7 @@ Item { id: outputDeviceAddressLabel text: (outputDevice != null && outputDevice.address != null) ? outputDevice.address : "" - font: UM.Theme.getFont("small") + font: UM.Theme.getFont("default_bold") color: UM.Theme.getColor("text_inactive") anchors.top: parent.top anchors.right: parent.right @@ -54,7 +54,7 @@ Item { text: outputDevice != null ? "" : catalog.i18nc("@info:status", "The printer is not connected.") color: outputDevice != null && outputDevice.acceptsCommands ? UM.Theme.getColor("setting_control_text") : UM.Theme.getColor("setting_control_disabled_text") - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") wrapMode: Text.WordWrap anchors.left: parent.left anchors.leftMargin: UM.Theme.getSize("default_margin").width From a3b45ff2039c22981643ff501780ee016f9a2bef Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 26 Nov 2018 17:35:23 +0100 Subject: [PATCH 0417/1240] Simplify the viewOrientation controls --- resources/qml/ViewOrientationButton.qml | 16 +++++++++++++++ resources/qml/ViewOrientationControls.qml | 24 +++++++---------------- 2 files changed, 23 insertions(+), 17 deletions(-) create mode 100644 resources/qml/ViewOrientationButton.qml diff --git a/resources/qml/ViewOrientationButton.qml b/resources/qml/ViewOrientationButton.qml new file mode 100644 index 0000000000..682fd71b4e --- /dev/null +++ b/resources/qml/ViewOrientationButton.qml @@ -0,0 +1,16 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.2 + +import UM 1.4 as UM + +UM.SimpleButton +{ + width: UM.Theme.getSize("small_button").width + height: UM.Theme.getSize("small_button").height + hoverBackgroundColor: UM.Theme.getColor("small_button_hover") + hoverColor: UM.Theme.getColor("small_button_text_hover") + color: UM.Theme.getColor("small_button_text") + iconMargin: 0.5 * UM.Theme.getSize("wide_lining").width +} \ No newline at end of file diff --git a/resources/qml/ViewOrientationControls.qml b/resources/qml/ViewOrientationControls.qml index acf75b1b48..51ed6e3dcb 100644 --- a/resources/qml/ViewOrientationControls.qml +++ b/resources/qml/ViewOrientationControls.qml @@ -7,7 +7,7 @@ import QtQuick.Controls.Styles 1.1 import UM 1.4 as UM -// View orientation Item +// A row of buttons that control the view direction Row { id: viewOrientationControl @@ -16,43 +16,33 @@ Row height: childrenRect.height width: childrenRect.width - // #1 3d view - Button + ViewOrientationButton { iconSource: UM.Theme.getIcon("view_3d") - style: UM.Theme.styles.small_tool_button - onClicked:UM.Controller.rotateView("3d", 0) + onClicked: UM.Controller.rotateView("3d", 0) } - // #2 Front view - Button + ViewOrientationButton { iconSource: UM.Theme.getIcon("view_front") - style: UM.Theme.styles.small_tool_button onClicked: UM.Controller.rotateView("home", 0) } - // #3 Top view - Button + ViewOrientationButton { iconSource: UM.Theme.getIcon("view_top") - style: UM.Theme.styles.small_tool_button onClicked: UM.Controller.rotateView("y", 90) } - // #4 Left view - Button + ViewOrientationButton { iconSource: UM.Theme.getIcon("view_left") - style: UM.Theme.styles.small_tool_button onClicked: UM.Controller.rotateView("x", 90) } - // #5 Right view - Button + ViewOrientationButton { iconSource: UM.Theme.getIcon("view_right") - style: UM.Theme.styles.small_tool_button onClicked: UM.Controller.rotateView("x", -90) } } From 7f2f2090da66caa3686de27c3f5d6474aeeab8fe Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 26 Nov 2018 17:35:58 +0100 Subject: [PATCH 0418/1240] Make small and very_small fonts actually smaller Rather than the same as default_bold and default. This also makes the extruder_icon font obsolete. Turns out it's actually just a very small font. Contributes to issue CURA-5876. --- resources/qml/ExtruderIcon.qml | 2 +- resources/themes/cura-light/theme.json | 12 +++--------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/resources/qml/ExtruderIcon.qml b/resources/qml/ExtruderIcon.qml index c103ee245c..7151719fb9 100644 --- a/resources/qml/ExtruderIcon.qml +++ b/resources/qml/ExtruderIcon.qml @@ -49,7 +49,7 @@ Item id: extruderNumberText anchors.centerIn: parent text: index + 1 - font: UM.Theme.getFont("extruder_icon") + font: UM.Theme.getFont("very_small") width: contentWidth height: contentHeight visible: extruderEnabled diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 57007827a8..a51b397262 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -41,12 +41,12 @@ "family": "Noto Sans" }, "small": { - "size": 1.0, - "weight": 63, + "size": 0.85, + "weight": 50, "family": "Noto Sans" }, "very_small": { - "size": 1.0, + "size": 0.7, "weight": 50, "family": "Noto Sans" }, @@ -64,12 +64,6 @@ "size": 1.15, "weight": 50, "family": "Noto Sans" - }, - "extruder_icon": - { - "size": 0.7, - "weight": 50, - "family": "Noto Sans" } }, From 5ddb3ff90994bd5e5579fc6a8f8ecaeac81a6859 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 26 Nov 2018 17:40:34 +0100 Subject: [PATCH 0419/1240] Factor out superfluous use of small_tool_button style The style was overly complicated for what it should do --- .../SimulationViewMainComponent.qml | 11 +- resources/themes/cura-light/styles.qml | 112 ------------------ 2 files changed, 8 insertions(+), 115 deletions(-) diff --git a/plugins/SimulationView/SimulationViewMainComponent.qml b/plugins/SimulationView/SimulationViewMainComponent.qml index 4e038a1cf1..16b9aeaae6 100644 --- a/plugins/SimulationView/SimulationViewMainComponent.qml +++ b/plugins/SimulationView/SimulationViewMainComponent.qml @@ -6,7 +6,7 @@ import QtQuick.Controls 1.2 import QtQuick.Layouts 1.1 import QtQuick.Controls.Styles 1.1 -import UM 1.0 as UM +import UM 1.4 as UM import Cura 1.0 as Cura Item @@ -55,11 +55,16 @@ Item } - Button + UM.SimpleButton { id: playButton iconSource: !is_simulation_playing ? "./resources/simulation_resume.svg": "./resources/simulation_pause.svg" - style: UM.Theme.styles.small_tool_button + width: UM.Theme.getSize("small_button").width + height: UM.Theme.getSize("small_button").height + hoverBackgroundColor: UM.Theme.getColor("small_button_hover") + hoverColor: UM.Theme.getColor("small_button_text_hover") + color: UM.Theme.getColor("small_button_text") + iconMargin: 0.5 * UM.Theme.getSize("wide_lining").width visible: !UM.SimulationView.compatibilityMode Connections diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index f4aeb95bb3..f00aab44c0 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -436,118 +436,6 @@ QtObject } } - property Component small_tool_button: Component - { - ButtonStyle - { - background: Item - { - implicitWidth: Theme.getSize("small_button").width; - implicitHeight: Theme.getSize("small_button").height; - - Rectangle - { - id: smallButtonFace; - - anchors.fill: parent; - property bool down: control.pressed || (control.checkable && control.checked); - - color: - { - if(control.customColor !== undefined && control.customColor !== null) - { - return control.customColor - } - else if(control.checkable && control.checked && control.hovered) - { - return Theme.getColor("small_button_active_hover"); - } - else if(control.pressed || (control.checkable && control.checked)) - { - return Theme.getColor("small_button_active"); - } - else if(control.hovered) - { - return Theme.getColor("small_button_hover"); - } - else - { - return Theme.getColor("small_button"); - } - } - Behavior on color { ColorAnimation { duration: 50; } } - - border.width: (control.hasOwnProperty("needBorder") && control.needBorder) ? 2 * screenScaleFactor : 0 - border.color: Theme.getColor("tool_button_border") - - UM.RecolorImage - { - id: smallToolButtonArrow - - width: 5 - height: 5 - sourceSize.width: 5 - sourceSize.height: 5 - visible: control.menu != null; - color: - { - if(control.checkable && control.checked && control.hovered) - { - return Theme.getColor("small_button_text_active_hover"); - } - else if(control.pressed || (control.checkable && control.checked)) - { - return Theme.getColor("small_button_text_active"); - } - else if(control.hovered) - { - return Theme.getColor("small_button_text_hover"); - } - else - { - return Theme.getColor("small_button_text"); - } - } - source: Theme.getIcon("arrow_bottom") - } - } - } - - label: Item - { - UM.RecolorImage - { - anchors.centerIn: parent - opacity: !control.enabled ? 0.2 : 1.0 - source: control.iconSource; - width: Theme.getSize("small_button_icon").width - height: Theme.getSize("small_button_icon").height - color: - { - if(control.checkable && control.checked && control.hovered) - { - return Theme.getColor("small_button_text_active_hover"); - } - else if(control.pressed || (control.checkable && control.checked)) - { - return Theme.getColor("small_button_text_active"); - } - else if(control.hovered) - { - return Theme.getColor("small_button_text_hover"); - } - else - { - return Theme.getColor("small_button_text"); - } - } - - sourceSize: Theme.getSize("small_button_icon") - } - } - } - } - property Component progressbar: Component { ProgressBarStyle From ad3fa9548a2c78f484fef42336670be822335e8f Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 26 Nov 2018 17:53:38 +0100 Subject: [PATCH 0420/1240] Add sourceSize to the open file button. Contributes to CURA-5942. --- plugins/PrepareStage/PrepareMenu.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index a953c2b5d1..81799206a0 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -101,7 +101,8 @@ Item height: UM.Theme.getSize("button_icon").height color: UM.Theme.getColor("toolbar_button_text") - sourceSize: UM.Theme.getSize("button_icon") + sourceSize.width: width + sourceSize.height: height } background: Rectangle From fcc6af68af76db114875479066a63c8660785670 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 26 Nov 2018 17:55:25 +0100 Subject: [PATCH 0421/1240] Don't show the current checked version of the firmware if the version number we gather is ZERO. That means that there was a problem getting the right value. Contributes to CURA-5980. --- plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py b/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py index 4c60b95824..9efd3e956a 100644 --- a/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py +++ b/plugins/FirmwareUpdateChecker/FirmwareUpdateCheckerJob.py @@ -93,6 +93,11 @@ class FirmwareUpdateCheckerJob(Job): current_version = self.getCurrentVersion() + # This case indicates that was an error checking the version. + # It happens for instance when not connected to internet. + if current_version == self.ZERO_VERSION: + return + # If it is the first time the version is checked, the checked_version is "" setting_key_str = getSettingsKeyForMachine(machine_id) checked_version = Version(Application.getInstance().getPreferences().getValue(setting_key_str)) From 519e2a3399b69e41af048212c45eda6492d7d773 Mon Sep 17 00:00:00 2001 From: pinchies Date: Tue, 27 Nov 2018 15:06:54 +1100 Subject: [PATCH 0422/1240] uploaded wrong file... oops --- .../extruders/alfawise_u20_extruder_0.def.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 resources/extruders/alfawise_u20_extruder_0.def.json diff --git a/resources/extruders/alfawise_u20_extruder_0.def.json b/resources/extruders/alfawise_u20_extruder_0.def.json new file mode 100644 index 0000000000..2fbe3f1772 --- /dev/null +++ b/resources/extruders/alfawise_u20_extruder_0.def.json @@ -0,0 +1,16 @@ +{ + "id": "alfawise_u20_extruder_0", + "version": 2, + "name": "Extruder 1", + "inherits": "fdmextruder", + "metadata": { + "machine": "alfawise_u20", + "position": "0" + }, + + "overrides": { + "extruder_nr": { "default_value": 0 }, + "machine_nozzle_size": { "default_value": 0.4 }, + "material_diameter": { "default_value": 1.75 } + } +} \ No newline at end of file From 47ed9e3f5db99114f30c884cea413dc4949544ec Mon Sep 17 00:00:00 2001 From: pinchies Date: Tue, 27 Nov 2018 15:09:36 +1100 Subject: [PATCH 0423/1240] deleting the old wrong file.... --- resources/extruders/alfawise_u20.def.json | 96 ----------------------- 1 file changed, 96 deletions(-) delete mode 100644 resources/extruders/alfawise_u20.def.json diff --git a/resources/extruders/alfawise_u20.def.json b/resources/extruders/alfawise_u20.def.json deleted file mode 100644 index f6dccce3ee..0000000000 --- a/resources/extruders/alfawise_u20.def.json +++ /dev/null @@ -1,96 +0,0 @@ -{ - "name": "Alfawise U20", - "version": 2, - "inherits": "fdmprinter", - "metadata": { - "visible": true, - "author": "Samuel Pinches", - "manufacturer": "Alfawise", - "file_formats": "text/x-gcode", - "preferred_quality_type": "fine", - "machine_extruder_trains": - { - "0": "alfawise_u20_extruder_0" - } - }, - "overrides": { - "machine_name": { - "default_value": "Alfawise U20" - }, - "machine_start_gcode": { - "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 X0 Y0 ;move to the X/Y origin (Home)\nG28 Z0 ;move to the Z origin (Home)\nG1 Z15.0 F6000 ;move Z to position 15.0 mm as fast as possible\nG92 E0 ;zero the extruded length\nG1 X0.0 Y0.0 F1000.0 ;go to edge of print area\nG1 X60.0 E9.0 F1000.0 ;intro line\nG1 X100.0 E21.5 F1000.0 ;intro line\nG92 E0 ;zero the extruded length again\n; -- end of START GCODE --" - }, - "machine_end_gcode": { - "default_value": "; -- END GCODE --\nM104 S0 ;turn off nozzle heater\nM140 S0 ;turn off bed heater\nG91 ;set to relative positioning\nG1 E-10 F300 ;retract the filament slightly\nG90 ;set to absolute positioning\nG28 X0 ;move to the X-axis origin (Home)\nG0 Y280 F600 ;bring the bed to the front for easy print removal\nM84 ;turn off stepper motors\n; -- end of END GCODE --" - }, - "machine_width": { - "default_value": 300 - }, - "machine_height": { - "default_value": 400 - }, - "machine_depth": { - "default_value": 300 - }, - "machine_heated_bed": { - "default_value": true - }, - "machine_center_is_zero": { - "default_value": false - }, - "gantry_height": { - "default_value": 10 - }, - "machine_gcode_flavor": { - "default_value": "RepRap (Marlin/Sprinter)" - }, - "material_diameter": { - "default_value": 1.75 - }, - "material_print_temperature": { - "default_value": 210 - }, - "material_bed_temperature": { - "default_value": 50 - }, - "layer_height": { - "default_value": 0.15 - }, - "layer_height_0": { - "default_value": 0.2 - }, - "wall_thickness": { - "default_value": 1.2 - }, - "speed_print": { - "default_value": 40 - }, - "speed_infill": { - "default_value": 40 - }, - "speed_wall": { - "default_value": 35 - }, - "speed_topbottom": { - "default_value": 35 - }, - "speed_travel": { - "default_value": 120 - }, - "speed_layer_0": { - "default_value": 20 - }, - "support_enable": { - "default_value": true - }, - "retraction_enable": { - "default_value": true - }, - "retraction_amount": { - "default_value": 5 - }, - "retraction_speed": { - "default_value": 45 - } - } -} \ No newline at end of file From 2e81b97623d542c23f7f0243871269e238e8fae9 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 27 Nov 2018 08:45:42 +0100 Subject: [PATCH 0424/1240] Use global_stack.extruders to find extruders CURA-5978 --- cura/Settings/MachineManager.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index f321ce94a6..f7ad108ad4 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -909,20 +909,14 @@ class MachineManager(QObject): # After CURA-4482 this should not be the case anymore, but we still want to support older project files. global_user_container = self._global_container_stack.userChanges - # Make sure extruder_stacks exists - extruder_stacks = [] #type: List[ExtruderStack] - - if previous_extruder_count == 1: - extruder_stacks = ExtruderManager.getInstance().getActiveExtruderStacks() - global_user_container = self._global_container_stack.userChanges - for setting_instance in global_user_container.findInstances(): setting_key = setting_instance.definition.key settable_per_extruder = self._global_container_stack.getProperty(setting_key, "settable_per_extruder") if settable_per_extruder: limit_to_extruder = int(self._global_container_stack.getProperty(setting_key, "limit_to_extruder")) - extruder_stack = extruder_stacks[max(0, limit_to_extruder)] + extruder_position = str(max(0, limit_to_extruder)) + extruder_stack = self._global_container_stack.extruders[extruder_position] extruder_stack.userChanges.setProperty(setting_key, "value", global_user_container.getProperty(setting_key, "value")) global_user_container.removeInstance(setting_key) From ec63e6c13066d307238a7e176f04adee8df1dbf3 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Tue, 27 Nov 2018 09:27:25 +0100 Subject: [PATCH 0425/1240] Added icons CURA-5941 --- resources/qml/SidebarSimple.qml | 53 ++++++--------------------------- 1 file changed, 9 insertions(+), 44 deletions(-) diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index 52fc9320f2..2e0f0d6c4c 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -192,13 +192,15 @@ Item id: qualityRowTitle source: UM.Theme.getIcon("category_layer_height") text: catalog.i18nc("@label", "Layer Height") + anchors.bottom: speedSlider.bottom + } // Show titles for the each quality slider ticks Item { - y: -5; anchors.left: speedSlider.left + anchors.top: speedSlider.bottom Repeater { model: qualityModel @@ -206,8 +208,7 @@ Item Label { anchors.verticalCenter: parent.verticalCenter - anchors.top: parent.top - anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height / 2) + anchors.top: parent.bottom color: (Cura.MachineManager.activeMachine != null && Cura.QualityProfilesDropDownMenuModel.getItem(index).available) ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") text: { @@ -258,7 +259,7 @@ Item } //Print speed slider - Item + Rectangle { id: speedSlider width: Math.round(base.width * 0.55) @@ -367,12 +368,12 @@ Item Rectangle { - anchors.verticalCenter: parent.verticalCenter color: Cura.QualityProfilesDropDownMenuModel.getItem(index).available ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") - width: 1 * screenScaleFactor - height: 6 * screenScaleFactor - y: 0 + implicitWidth: 5 * screenScaleFactor + implicitHeight: implicitWidth + anchors.verticalCenter: qualitySlider.verticalCenter x: Math.round(qualityModel.qualitySliderStepWidth * index) + radius: Math.round(implicitWidth / 2) } } @@ -453,42 +454,6 @@ Item } } - Label - { - id: speedLabel - anchors.top: speedSlider.bottom - - anchors.left: parent.left - - text: catalog.i18nc("@label", "Print Speed") - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - width: Math.round(UM.Theme.getSize("print_setup_widget").width * 0.35) - elide: Text.ElideRight - } - - Label - { - anchors.bottom: speedLabel.bottom - anchors.left: speedSlider.left - - text: catalog.i18nc("@label", "Slower") - font: UM.Theme.getFont("default") - color: (qualityModel.availableTotalTicks > 1) ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") - horizontalAlignment: Text.AlignLeft - } - - Label - { - anchors.bottom: speedLabel.bottom - anchors.right: speedSlider.right - - text: catalog.i18nc("@label", "Faster") - font: UM.Theme.getFont("default") - color: (qualityModel.availableTotalTicks > 1) ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") - horizontalAlignment: Text.AlignRight - } - UM.SimpleButton { id: customisedSettings From 4be8af7cb278fc8f1a7118d1cfbc9ba02d47cd80 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 27 Nov 2018 09:38:32 +0100 Subject: [PATCH 0426/1240] Restyle printer type headers It's now a grey box with the printer type name inside and some padding and such. Contributes to issue CURA-5876. --- .../ConfigurationListView.qml | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml index 8a7094497e..2f5bf0b1a2 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml @@ -43,12 +43,24 @@ Column section.property: "modelData.printerType" section.criteria: ViewSection.FullString - section.delegate: Label + section.delegate: Item { - text: section - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("configuration_item_text") - bottomPadding: UM.Theme.getSize("default_margin").height + height: printerTypeLabelBox.height + UM.Theme.getSize("default_margin").height + Rectangle + { + id: printerTypeLabelBox + color: UM.Theme.getColor("text_detail") + width: childrenRect.width + height: childrenRect.height + + Label + { + text: section + font: UM.Theme.getFont("small") + color: UM.Theme.getColor("configuration_item_text") + padding: UM.Theme.getSize("narrow_margin").width + } + } } model: (outputDevice != null) ? outputDevice.uniqueConfigurations : [] From 8e7e8354e7196b1e449f43f89c7aa521b9d43931 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 27 Nov 2018 09:41:45 +0100 Subject: [PATCH 0427/1240] Set colors to the correct ones --- resources/themes/cura-light/theme.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 67e0c701c2..fdc5b2a2de 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -86,8 +86,8 @@ "primary_hover": [48, 182, 231, 255], "primary_text": [255, 255, 255, 255], "border": [127, 127, 127, 255], - "secondary": [245, 245, 245, 255], - "secondary_shadow": [228, 228, 228, 255], + "secondary": [240, 240, 240, 255], + "secondary_shadow": [216, 216, 216, 255], "primary_button": [38, 113, 231, 255], "primary_button_shadow": [27, 95, 202, 255], @@ -95,7 +95,7 @@ "primary_button_text": [255, 255, 255, 255], "secondary_button": [240, 240, 240, 255], - "secondary_button_shadow": [228, 228, 228, 255], + "secondary_button_shadow": [216, 216, 216, 255], "secondary_button_hover": [228, 228, 228, 255], "secondary_button_text": [30, 102, 215, 255], From bd42136712e63abc8043a41ce1004275f4f94d5f Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 27 Nov 2018 09:49:35 +0100 Subject: [PATCH 0428/1240] Fix GcodeStartEndFormatter to use fallback values CURA-5901 Use values from the global stack (if exist) as fallback values. --- plugins/CuraEngineBackend/StartSliceJob.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index 79b1e5249c..273dc0b6f6 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -66,11 +66,19 @@ class GcodeStartEndFormatter(Formatter): return "{" + key + "}" key = key_fragments[0] - try: - return kwargs[str(extruder_nr)][key] - except KeyError: + + default_value_str = "{" + key + "}" + value = default_value_str + # "-1" is global stack, and if the setting value exists in the global stack, use it as the fallback value. + if key in kwargs["-1"]: + value = kwargs["-1"] + if key in kwargs[str(extruder_nr)]: + value = kwargs[str(extruder_nr)][key] + + if value == default_value_str: Logger.log("w", "Unable to replace '%s' placeholder in start/end g-code", key) - return "{" + key + "}" + + return value ## Job class that builds up the message of scene data to send to CuraEngine. From 39e1cfd6bc080cb193191a1d8472a30b3fb21274 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 27 Nov 2018 09:50:01 +0100 Subject: [PATCH 0429/1240] Fix indentation. Contributes to CURA-5902. --- resources/definitions/alfawise_u20.def.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/definitions/alfawise_u20.def.json b/resources/definitions/alfawise_u20.def.json index abc206c4d2..87726fec3d 100644 --- a/resources/definitions/alfawise_u20.def.json +++ b/resources/definitions/alfawise_u20.def.json @@ -83,7 +83,7 @@ "support_enable": { "default_value": true }, - "retraction_enable": { + "retraction_enable": { "default_value": true }, "retraction_amount": { From edf8460619d9228a00154632abb76a88c2793ab3 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 27 Nov 2018 10:35:19 +0100 Subject: [PATCH 0430/1240] Return empty material model for empty material CURA-5982 --- plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index 8314b0f089..f8726a5441 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -593,6 +593,15 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): def _createMaterialOutputModel(self, material_data: Dict[str, Any]) -> "MaterialOutputModel": material_manager = CuraApplication.getInstance().getMaterialManager() material_group_list = material_manager.getMaterialGroupListByGUID(material_data["guid"]) + # This can happen if the connected machine has no material in one or more extruders, so we should return an + # "empty" material model. + if material_group_list is None: + return MaterialOutputModel(guid = material_data["guid"], + type = material_data.get("type", ""), + color = material_data.get("color", ""), + brand = material_data.get("brand", ""), + name = material_data.get("name", "Empty") + ) # Sort the material groups by "is_read_only = True" first, and then the name alphabetically. read_only_material_group_list = list(filter(lambda x: x.is_read_only, material_group_list)) From 0cd9c8e0870e1ace2bb6add479b5fb5f83863d37 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Tue, 27 Nov 2018 10:52:49 +0100 Subject: [PATCH 0431/1240] Add machine images Contributes to CL-1150 --- .../resources/png/ultimaker_3.png | Bin 0 -> 792765 bytes .../resources/png/ultimaker_3_ext.png | Bin 0 -> 1038742 bytes .../resources/png/ultimaker_s5.png | Bin 0 -> 1476464 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 plugins/UM3NetworkPrinting/resources/png/ultimaker_3.png create mode 100644 plugins/UM3NetworkPrinting/resources/png/ultimaker_3_ext.png create mode 100644 plugins/UM3NetworkPrinting/resources/png/ultimaker_s5.png diff --git a/plugins/UM3NetworkPrinting/resources/png/ultimaker_3.png b/plugins/UM3NetworkPrinting/resources/png/ultimaker_3.png new file mode 100644 index 0000000000000000000000000000000000000000..4639cb3fde2c32574ce6743fd3515a7c38aa1290 GIT binary patch literal 792765 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 z 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

CR>N#?IyF`X67lk6IzNjTH0*R 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%0DMp8deP+WM}2{3AU5;g7JDLtc347PoHQ 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

eS+UQq<;XpOwFGW10rl=Xs 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�D;Sj6wQ<8NnKz;?yZyt9m{SH!lOu!x$lBm= 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

  • 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%0APM<8$|%uEW& z^bth9Ka~siFI_~bvOiQBlx3C>yHYJo&}d8lL zeB~=&$5T&y13AB1s=E|+J>E)xnq}7vF9jN^SdEX{I#y6_imdI4{4XgzRw)%)fQ#qO zBa?KEm;OUy+2xclm!df|?QA#DrLsjU@idPkH2KnaBzay#6U;IO`8tEqlIn>dNyF}q zY~db(-FLu1mc|r~<^_#3N)ZG*Rlq@8R5rZ7N&q0sz7*S(iOwq*&Y}HdfRmN>nMmoh z`1`#KxlNb>%Ww1bssK14=K&~3AeDvFG^0X7pUSkx&bW(p+Qm3zlmh8jMJ!$pWE527 z)c~G~hKyJaNL1h%OTN>4@+@5yNCy_ZVo7q=Dl6Il==pk0<=xQ5#@5bQ*#DXf} ze77HOq%HHTLgn-Fx$XQI-5D=M67P+0(xiK?#S=BgD)f-D*`dFOv?irh#G(Pl^T0qUwS*Tf zEfB{GczkyuPxL2$@+X&>nZ;$Vt~pHtVGbZ8hXX1wKDfYivMwvxr_a@5yEVLl01HD9 zqP95}q)MpCmmIvLAqu$;Ito;)v?G7E^JRy&jTsKAn>AL2GaHbB1M*{*3#1_}fYF@I zRYS#sHK!}i>FKO%)p}?R*OF}}=oho|+2Aq552^<{&!#HA|CdBYFcG{JH#Yh23J+LtoJU7C@ zD2%__Tv{>-k%XG;?2P!$-}yuQ&;Q~dVTUK|9~|KHiIceZ4L^btSKWdOmuI~A_Lt%R z^h@ z3A5zoRjX49YoI1!P%t2<&&(-dni!WZKBpGIjEaJkrEKDAHKLY+GS2`;5GB+y!{fl2 zv}9)h=_bESc>&-(61WdLFn-X0CW)gVLF)NKdAo+v87T9l(pr3vMA%nY;;EmB@V;cc zBz))f`XjAVQ_nd37eR9ea!eUTkWbv`%YdeYn{hi@)W&2WTtZWo#}FZ@f?I7&X5vSc zLKl@V&jqutaZ13Dq`w7_Fbo+y6|9dY@aeJ1Fc8L+fh;{kcv2qWpp+V&QF1Tsm87-J ziMf?0704jxE-QGG{nC!$99KERqAY~ZisvPvswYD|97)UxrAR}zJY?s0g&9Zd**L$%qa|hu`(uSSbB;ytky!$z-XalN@G>!p)|#-3y|L4hUfWwCklmA zz+eC0{q-B?N;^fZ09Bbtt=w`X$gFu9ve3z8YDyky4+}BJX<5y-pkk#_D-Z|Igl(n) z8v{B_{YG?~qMC33(V0RBlUh=&-ucD>ZTk*8FqMn`p3sA#82%IXN9SZNOcw;)+;J66 zKzNRa9sDr;sNvejm2%REGZIiEYn41XO0cF`wQTC_0>!l6wpKlO>;~eiLLTqe+;!;g zXM%L;b$HnJ!rloN+7+?+oyh_z;mrDKVFZ*2nSrT53$XzMnbN4>SzYP`N!sINF`6pu zEd`!bHVP3l_V&aDpp|V932zs9wl^E>}K`XBr%r_>eV1? zk|pEbX7Ox$){w=kLH+$wQZQ*qV9{^^IV(p~#m?z#@$y%FAC8`T3g^#1 zg}G|BNRG^2k!q2e&wJ2BRu%U&*9ngmvF<4x8s&u?~rhg z5_mC5XotmU+J2156Bn(5sh4lf}>m%%pD}jWyH8acO01)ADeTbSW@-T?GlpwhhNS*^W%@fKz zVSRLnoihrOAUr-9U)6OtFOwzt#?tk|r!Bc!0G9J|Zf9ueMVDWVx3%uqfO>u^94RSe%Vc@eXjOGmse` zv1)>LI0<3B7W3C}`*~wl&uyz7*2Z8o8Zn;@+?1SVuy<&r?2>rtt0RE%D_BZ~kr=GnqUVX#WSg(r{ z0Kn~V*-aFAg_AYHaCAM=k<*BU!hdD)WgZ)3V3A*Nzmna>x%)l7} z`#ViwQyaLx+UoL~m0Lo)BsoqxltRPvq9RTj!Yqmh1*&&p0*yg7fyz}Aw{ke-!jmMA zLu;kGFee2T!WDD{)%qoCIA8!(##|&mJ|Q8a0+o=`paMBznkTHM1Du*?tPc-S!N@t| zXqvDZcD3JxfGcb4XLU%~&S}+9g=PfPb=7?|VI!kN%G4@EIA_}~24IVyFPSql z16n$nL+Qatn67X+c(ST_GUB-u(9r>Y=EvWL7hQ8be*IVfN4)vESK+Pi_;z5p4Z@}8*j%w_r4idUo+tB*-IWXT)BjB z*akf1te6ZSpk@L2EE{7bx~V2DuWa8Xue>r011>)I1kRm(3abIw%@tEQz@_u&QFnK6 z_0=bE%S&E_VboZBt+FB{W4ziLHzq&7#RKGnfgq)j$pPm<=250(N{4zZDYfKoj^2B7 zske{@KL@PcaY+>pQm~)^h8MKvduwM%$HRKwL?JUiXIA=f-%?icf@T8mcSVJR(!%ht zN#pIwv6We1Wz}+J%mk!bQ8f%L4=Yg47_u}pGY{PUAuyM|LmVB|cXe1H0%tG?fuzn!75%2MB0=NcS3oiNr%n zkh8Aff((S{I`$q4U1h^J-1A0%wvBS0XvU2mDI-6 z9g?_w)Rer=XiQCpd6PUV?hzChj|6a>IPu9FILn(!{XxS{rtoK z{vY4_=`VcgT|fBJm-r@^QXLc#iDoIv9yQ`LrWu&9kIv9rI8<%CGBgRhux3ZVKa5%u>yJgyB7tpJS4XB z4p`VqJVVQmu_WofY|7ydL^%&UN>_!MG0zhc7s)H5jQ#!R@a;z*MNY5ry#xr>0g*U~ z&SxMlw{WP`0X+0;-htpn7y=06FyOhT&f(La{C!+{_UkyuSK)X5)&GQjx&}Ag^jcin zzl5{TJb`h15$?YCr}4wL?Beu^0na@91itc>ui{%@dl>cmUV|ZJ%(XbZw^HE(n3D|D z_uNByKQj1}+U>YJ{^OJ965 zhMaJ)mN;p`tb34N0ipyaQR;~u3sRdnteOu|pf^1e%!D#m%t^|^=L($K zJBi=_&>vp)U;H2bkN@TS-uJ%$1g%1Z?dq|gZDvJU{2L!c*I#}0U!JB5xc1sJSRYl) zbH!W>=2_KKP61r?Y|S3T241Rke3e5ig0#g~^aoAWXW=5OjAfkk(nCNb9+4Ir(PELU zqJz68(HKG}j7^cKEUn<33H4)Jg7V&NtQIR>%1D?}>U2AkSUxj+;Q*qt0ch4lH(_L@ zR=?k?AF*1`?OJMXO&fC$MB;x@Icjd+1{AeExgPFf7OIYv)OBb36z4|dV6Ax%!gwmR zJq}Eqw;X=gcSu&-b4rwwz4{Z>qUeuEh+#R68L%@-tJ2|{=kL?Td2N;Snx0+=43dC% z08*jCnYUzr0L&PNj5K6SwIb!gldURG7!oiH8JQAt8svKw`jvp166Ol5*Xy;#1ivE(c%DX5r> zCd``vwECKQYb{mIE*J#``$0yV44apoIk*GXU~HuVI0-I#?rIdiho2v9|08r@fJvt6kN`?M(8Ndy! zX}BPPCB9u1c2Grzc$rcjuv+b)&NJ$yC1hDWB#E%I5+FOx6}1)|9qyyfn#*SMVn!(i zLmH5f!BkP|tcHQe?+t|Yq2aMVMKAMhG2}n*hoj0A2e1n09(jS8MUM+#C|f&~$F+#( zB`6e;tQgC9 z#(h;;ZD6_Gxb5$pHmNttu$0g+0ioq5I=Ghq9FZtbiQ!Mn`LNQo+^)1?<6{WD!t#ae z)k^zOVYHjN4&L2NK$e?pO1Di4vQZJA_0P`j9&9Q?A}&>_ZH!nIuz~`{Tnfs}D21?| zfW1>EurrQ$_L=8?>hJx%zjqrtA!%t=JoZ z{e9_PDORY2S|=rb0p9JQ%1~I#J}R?LDM@WAESQZYV$t`2+KUY7~qvj0Nm9 zy6r5oaT}%G+J3ezQGwqak78Z5@RIJ#z<7YXo18Aun1`W25+UQ&uShJ8^E~Ti%>ruc zRT$Xa+r>+7e-SP{^9|&bu$u;~_jd5qV~^qD#Th40uP{%OhO4yD3`#NqW&*19=V^+> z(lY55MH8YNj6S-CM{CB;nH%t`d;S9E3s2$F{yDt-Rrlbp{%?K`dnd2QbLYN_Q>RYg z;`&Lv{{x@Izxl2A;h`@+hzkd6Tzlh7@$$Rx#js15%G~&|ontaPq+0hYkdhQpsH7f4 zkgu~wm-Lyl!vUdYSE7VgV_oqxSWA!uiB^Y<1?>L+I7-s3E>_RdB_98r_czvcLep~AdP%4q>^ zd5SPTn38x`Q$k9FoFzHA%+ld{%NE%`ZFelU^ z_{m;m=hm9?vcAtkWS3*!P#~3rwt@ZLxT*VUDst1&Wq(X*HZ&b9M@vI0?5GIoHXdV! znG`NGETtF?9HUqk=rqdBRwcu8IP5)k))*3A=GT*Xruz9V4PH?pGlktTNL|XHuz=hS zcJp;C#;zX=fs&}5X6$d6!+4^cS2s`qAhPngsqyMY4*Dp=;BCKUU#(FvBOndGtyD&? zjDm!Dt}6HB{9bqURX8X!{^DQ!i$DCg|MuVh$3EybQ3HYYF9TQ3p`gXj^om!%;kK(z z?Y;OVw_S%)fvJk1taFS5m%1ffNjmXr%()Y4C+tm9OWITn#b^sh3iu2-P&;&Jac7b7 zEtzC1Gbw~gF;>ndNXGsU@r>pYI!znUM?M@0V9S3c@C>?ibvc> zV|;Qgc|{B^+OUrjhLmuK8MChWpu%Lx*^@LSADuvHl$z^_u)Dj1#Ei^9ri=j@0}%$T z##ES5K+-4-%@a4fqQd|sS1PRe2_xybY4;e)GWOPJliE7ymL-oDU0TFVh!B9{>jN|) zZ2P#h92uRM#W=Z_ksDwowV0wk$KJiErf66l$aA||QaZOGs}O8w-OhakS}H6HJ1yp% zF-k|T=3MneZ1w(cjDx!0i~?{@jw$0 zOxl^)yl)0-I$V&eyYUXZ?4}ptn(J=C8{YI5oH&v2+%t#J zq$HTR(Cq7^g1)MfXnS1TlO*TdI1N<}c_wB=5JObXgzIm)3l9$Wz-;mQE&+ z3qeWpRN6}_TP=sg5dQ4%p^>APQkq+Y~3Y##t_j(Io+zf7S4g zY=K5+knwo(XZExKUo*!=sXbpV3Cz^SRm1oeN{~I)avfaWz`{Ds7$@{yYYD{w3?mvX zFV-Q9-wj}4KBgpk-kChq`B00Xu(0fY-0``ro0CkQ(zd;^;w3HgS}rvHVB8ShoVr{U zD+FQ~GSHQAPCJ9PA9Z4_l12bkZ~>?=hB4#t@DQiYoVnGuA?)-vm`4Ek`En>I{D-~M zyL)FZT;BWm{g2{3cSxMH@FxCL02d2f`8c#Dzh*{a(-$zL-tCW?LTC8yxse^-U`jdNO5E>aN zkRNHl3InD(fl~!jLegA`aU3v`B@et_n&55YtE#0Fi+5%mN6ZuBnWrz{ z#NIB(Az_{maB%Sw=2CF|bywpXk9-UJ^+8OB&*6!0K7`MH`eQi9 zr*P)#mtn5JlTSW@@x*O-*SmiKCsqfz`>vPa@h858kA3PseEL%#!|Pss2gcQic`Ev@ z28;{OFfUowb!BGH^@{vUsaGN~8jEsyTT8(>RJ`eJZ^Qon5gz;c!>IF-h6;`tS0e~B z&R;ITcoMtoGq`oss$WlVeC~V2X!%uUU9>XlwpQEtXxr;1Cq)sVpiFF@we7|#8Cf*ob(iP_4Ct^{ zdb04ys=NgiC}H2+PFgG0(;B69I@Gz#LrVpwNy0UA8UZHEJYg6~!{w@!{+$IQfQ~Si zSvR3**b^!1mov^-%CRsVUxNnPDD3KLcLknVo=c6t8ExD3tTFnjnqgGsjg^wtIkM&U zsfR|kOL9B2?J>}gXTdv<s?3!#Z-iZgWm2z|U68^|4A2*MU`5dzz{knuOl#Rn^Keyzu zDi3x=>o}!ho3eAIIfPN69U^7QRvKb)ohfQ54M@C}#9?Nn0pOwu%N8?b0^7$N1+^-y ztkyLb3POvuR%A%>sEx3}gnr(QDYVcCQkrP?0zD@hzddM9L?Wq(k_MSuffPZQdB!Sd zOjE)B(OTT>Df!wIP9Pporx~a-a@IIt9iH|u((6eQ9Z5nJCE_AA#f=Rh!|BvY&c*pU z4N!kp8pG}rJ=@wW*K6~$js7?3N}(Pb=v0MJ-(ev(!k26>7eH0{7aJ%F%zHUTR|J{0 z^sZR9iIRtm<(x3??BK%1L!3K*0Yr@HXu`A4p2vkt7jbg$X`DWJ5RwaJ0)I% z1l)f6EjWD&xOnLz1k}~;y}~DeRuzfnv&7cGT5ubAkebi)9r0gb_A$cNL&M=A`7XV z$qg#$>BN3qh8cI)1Fj9ZvXTHRxyHPtJxu`BQpduh6NWrsNK$!jo+hBq@>s2kIOIX{ zi!`jdRLv7C^4_EA0PE=hwM?jUMJWY2tpt|y4A5?n^OCe`F=Rc6^pQHoPz()F!jK8; z`B3ui)Jk2sO0Gza{DC}nzjE~u94V3^Yi5e76tS%C@v023j%D6pFQ|e|PrdC6#h7gc z5X=m()sHCO)QHwlJame_XCA%5{WVLkhwvWbDZMBbC7N6Hr$k-VSjTVji=QFmN!#9M z>rlg73_DsM2{SPHIIQH9dclvAFI5%1&V!+Iw|Njzm*e5r#5rwK_DmM(9$Of2Lwhs*8l?jkmuuVO$M3I-2n8v(Mp~XPjT`^b|MS25@_+uH|JR@8>8>A4iNUjWcC9w~xVYgV`iq17U~XcA zq=|r=BVl6OCKdqmvKmU8dIKs;BHi}!GNsrI&xpT9 zMqt8+p{_c8D;vn+%?&v2v+4TWwCWyj(ZbfhHLwo|qum~QRhV6q*EWaGJ3er;Ol}a_ z)+B}OZXpm-J zIVTB$$`W!ijsr+7g9755eFTs}x+KEf6Pv{l3XtIf0!eAsdrb8Ew^XR35bDnsU}xOD z9O0g&C6*T70Tz}PJw&|~&n(Xl2w`qtu_)U0&5{%;j=amb#lstz@@bpJKO`2D5{D&@ zR+0)rV-;m^+#7Mr?N{MuCJ+j)yY?i`oY@0`F^*Ek_vmQC{-vX^u4r>2<*ZD>P2WcD zjq4WsqExKsqRrRoJP+7kSKRR8JMg+U{s<1wK92Qu`*`i$ug6dS_|IWHc>})v*rzcJ zgl|0|3EO}C;a|qXU->F7A06SvFMSQ}z4t9x?MU3Y4LVUmAr+>a-Gf$@b68O$mw(C( zNtOPhe<#v-WEJ!v!dxnd*SO}Y5m#SxEeKi9-=0(!s=!=;X=Yr$bQzZqq^~ZRF%Ahk zI|EWyc@@FFGGY6dl_8UT2U9#2*`nbd-7u$9IE|VaAy|sT`@CGX@i(-6?s%~E2Jo{Y z3)Rk>WSlkS9~%DCfJCQ^yQaWC=5_`H=p+x~LP`nKWR2X48v{v|cB1OxBNi`TtAv`N zR?OuHrAlHoSk7b8f)ZS25Q%ptRRYiODt0-qkhA2zrD4Pf##9a^hsJW-u3_Sgn7++&2{TeKDC?i|8-ey1iTV2Yr{(7Nle@k$8+NL34;y zF(BIDbM2+epM^=ey^Y$x06Z_iXvKh;^x(1K2L_iQ73C;`djMG}rJ8C$utuTMDnMyH z>DpSjxoglybY_rj^i2MJCt9Y1*a z!bR+yJdIK_#LXfaxg{A9nRcZiP;mMIf<-4OwNGjpNFI<7}0zC^fpPD3U_~v$}`}f-6AUcfbW?VvW<(XCz5*wX(Ak`V$YMD!PS{2N`pz zW_>q{aR8NO18|Tem3k6{(T*vh0$9&8iW>t8m442usBNqW$`O<7pd>GJ$V z028I*Hd=_!iC1r<*Z6$l6HS1nAgEYcuWl$F@f~ulu|0iOM&D!-Mn5Ugs3-{VXNyvo zi*;IODO(H%rHaagRZieC7gd?rS5$8+6x~`jn5kStut2r1xTdrc5 zBOA_W(F=~CtcY9vd-m7Pz|_faT@wp0rqE}B;lf|pT^Hg`q>6xh573o#Wr9~LiyjN= zWQ0wHjF@B}lVTIa0ZG3nT8@3YA=H(A6@H0Am}t1hf*G8-hIQYNACc{HGcXgYk*90l z*{`)3xP8ymAYO%tG^(N?cDipfOt5?{|0~;BV#4~yI9dk%R9maPb3ymD$L^00IIkc8Ko9b&RY+t zFcr(Z*S{E8U5u#{d;qK|O1IMfem%mGq04~uohCf0&ZpO7k5eY%cgOyX;~=*Ky$!`n z|5gcVsXqRbsA=+9#~$Y%pD$a$UNSKXHM{P(DjaRy;RkmUSQ;265RzC=SOK)g`W_t} z0W@M~XLP2*AsH8J@zU(chEkoF6&yi}yoqO^6zdFbdunb?z;b_pW?2QCFZ!CSwQlf0 zNv!5-lBI!=3Q5H@E!8UQg;1w8Kn1lX(69rjkhZWwP9)(o1(?@b$x6>t$?p|r9QD#ZG2hby_)3Sq+W{w1YoKKnF}&kASL8H%AOD~m_f13 z7&5Rkjwti20F$*6oAT1RVy=^y#ZHMZm4fwrgp~p;SrbXrWe0@pOVb+&+B(WMR-G5O zH2~Cm5SEKt$V>{#y&G&;+LXqOnosy(7PWp=I`mq+puu4K*S=7L5pb9kEcPgu8MI8&+xP*KI_EQbj5tw=R zNva}JBFvM@ai)#AB0N7F0Qood@~r!gTB5c^VK91y$gT|>0H$yf;01y7PhIP<+pzR43Ru+v5c_5@g`o81@44H8D(q-Iv*IoGKU-_s16^{RG zBR>9)6$$|OrN8wz|FeJgFMsWQmHGCRL=&rSQ8z$Nsvxi@0y;w`JxHZsHg^OOB(XX& za{w9xDy7tFI9z~LNa%l$jcaT%n^hR?I2Tsz+*@hW0TP> z=VS_5cfR~p?0cIMdFeCj&`Or@Rx3nVGpm(Zmx0nqQW=0UxMW^10F{wz0_SF#5l|Fp zT!lC*fwZa}9NuSiC`_qBKwaUAY`GIjnEYHIWo=O~mo9?NjW&o|g2%9AdGd`aXjyX*fmi z)r`WK2x8PK1qlfy0fRPM8wdF>XOk^`=s~0>S1^JCZl+;qSoOV(5m^zvp!fO(- zgqLa`pqvO)3#Mtt?k_?)*7SdpBTbHG-Kj&oihLtcDSJlst=hJ)xA@Lt;uP zC=$Sm#3pFPq~f_c+|~hD@*dI?Nv-U*^#L+9&YaJz*OHY3w>?kJ>rGoMWd;V#pJTK} zxfI4E?qIYKY8?X>G-_wp5}nf~e`SQywfY|64~-J9WLyLYlyy$-rLb+4XOM;Ww$z=D zhKP)=s+QIEc(e_y05vf$5g`h%yu>r{*~$V?|~XK zbjS@)x~IOaiN9Axrzt-T!n9}z&Io&q?y4&xza?K6B|!|P&O!{kDnTX1$tCcHq@hkj zmQd*-OP=qE-SL@^eeS;h;_v?5zx&N&2XV}2n?k``e(wJEfe(D(+}FPR#eeg%*WCLz z3uTlfF8!1fXii|BK~Renky=23=Ba2~mQs==yHZgUV1Z-&rmv#MzLRQv=D2T+xw(zD zPRe;3fwb;?;U?sl0>zXNVH9;iXgW8mi|i!338?*fsf7`l(9c8GZl?hhAdpiS%XTZ} zc0j)LZrCS!0OA-opS_INT#7Rjc8xLd;t*^Fl=dWB2cqt-QmZx`YhyB@1%x=w#(CwM z3g3ss!o1{Lu@#Ui8aXF#swm8!8(0S5A@jz-mAT8oHFJiHqX_}IyJ6VijaV}H1<*|u zSfvbWVl&N$NW&h6Jb=I`HMv!`lo?ZzbG90Ha5yE*WsS-cxXehkh!Uu!{^nFsKp2Ls z7Iq7f5M?Dn=4q8i#LAR4##zF*YPAMmfSEtM=#9^*g>@N&T?(e2akq1A<)9<5Syn0H z@Z6=u+D2pcci-c{csoxbHTU|lvCxYU9jaz25ASwx!T=S3GR$(2QbH{Slz?HBc}cW z895DDt#)C(KG(Hk1|jDGoX8VfQN?4MX+TN^l_m_7Fr8=%}5A&kcd6+%$M*#Y2H zu|Wi?P3e>VMeK?;$LAZ~TkKWi+s#0^{JyPKIOhb%p=K=PO0XOWYuxL(L@mHs3SuN+ z$nJp0x4-?3-}q~P?SK6#Y!0y-&X%9I=1|xLOJ$B`!3`**{G)opNE>I3eg2lME5CH(eGXOupoz~wqGwT_w|ICd@ zmdk5yKC6O*C2?*%vp6_Wj;0zbD+ALwIBxd>&o5k-?}4qS+`Mt!w(qwKAc)`xEi44f z^|Co{nkbI5Ah)Mnc}^btt?SQWN(N3D?ADWHb$z&jeyh&FEFnrOT7f_)#z2{YYKd9) zOaYQq0472qNb)pG8OxkAl%T_0l&-aO8}q_55u~o_JOZ>TqiANT^imfqT-GG()wm;} z0Yn%_olBVkRHRf~_|~G99RT2pQj2y^SLR%+S}H8hM0P9|osfG0Q@%|}TzlEop5~0yH|UUhgGhW1WAl3Z zTZ@lwy!JRll3obF3~C~Cd)_FYr4eZfruyHPuaIXNl}FiJH7~0(2neI`^i%0xuOVxY z9F)Qiy2zoNZ%?zgwRnC@wB6O1^}!C+*iF z^rL+gb?pgE)K^C8scy-Py5GN(kSC*92EaMzoG2q}g5RVeN|N9zjp&TvB~i;30BS3! zZpCX>xTevWP!JHVHP`&`$$`#+0eZoLFyJL4?F0`9ti=i=knv5ke;Kajd$n}xEmvd{ zL%->pMA{!xV#c&yYc2nF=(Zi|V};7r_Z$PV?C`-cW2Zz(tsux>#QUg;k!FB!a$Ht=X>t-r~)2BA`y- zIb+Nj0|8|&n6>#_8brh3ApcLqM%mOamqj9a*WcV2DDMzyPt9 zffk~)upLu^D5bE?&7~c|*wRzjofnS>CRiAn#NjqiM?7O4z{Hi$Li}`AoG^~?i^s1= zYBG8f1<)iM9QaC=m)e0^_m*0@-9VR#5>l#QVhkD5`+^8rlkF(ky$FK7#aKrL3sBi^ zr7bmSB32nO?+gQ~<2U~)>p96oYLYyx*EsIrt6zN*AO4eH!};gFh6nF|1|R;nzl}Pa z#BI0Ti!>1)ed<}<{IY}}`OAL=J1OJ3Yfj?wvrpm!AN(V{P+yA}SASVk`aGB;x zj?h2Q%zGLVq`#VsCK3QrnE@?cVThyFstRuZiR(o~Uav=%T*5f5BtD$4H6v4$2hk7^ zf%lS@mb}qvSAN+Tqh7QjE0}AyMsYtkZ(q=~%Z@Dy%Yyf8n?w88G|Zk)LQ(t%fjG@+esjr>JrU}7K*8Hp?EJfqGNYFUHpjH4r9y`Hcd2JEg@ zsJpuuazT=MjhiLy+ar5##zNA_3RCQrz|~7bB+5uO8dRP{3ceE&Qe`Rgty^q6-{%`%Ir1R# z!uIomFDKecR~9;KWk|`e7|6lUV}#AqF5Sn3OsaC3K!G4}#k{pv3)}Meu zY1B$;Hl?EFqJ)Xa*|o({R=iwmGl&54US@!eB=vnHC02aiwN?yc#@^n54}R#+@IU{` zKgXGoad`OvH(h@nUjO$00Eculc=t2-kstoc_=R8m8@T1nj8A>^_wd0#c|X4R#Ru`^ zGy8b(i(kNn^NiKbE{>*)$Xda!lS(#TE4fQ@HWTc3z)m2H1|c!`61vD*CEwkmDWcwrq&;>XS{ZKaJXd`#wq{aB8Vy34oh-{QNaQn{RS{C!hN*e;j0 zoz#>S)TB>|10b=K^v+3gm2#~ZND@U;PFk|H zAR+77N|@&viDnFG1QRe08T0xO7zU&)-LzSaBAJwrF;rnwAfEuM=ClOXzCK|)y^Az! zMMp2vAx12M#GjioAecx%u<&`ftN>S3odcEj!O0c=wu

    O(?^|d9>Hhj!TjE=&niL)kHD~}J>A6sh zT$3~wR7O^`v=~v+#61HJ`lu9?w5Y~V1uVI{GAOr0+ZssOczfn8$5aMLssKTm4wx_( zhNMvsV3snNQxyg}YVHRCsPhc21)u_;NK&;nM3W9mS&|c3N|Rz3Jeie^1XMs2NHwqQ zzGikyh2&8rA7`SLz{+4q$<$WTbV-88(d8y!GMW;_lzb@td6a!gzSqZDZMMDVg(&@z z2{W4|F8nOts2~*YrkyFH+whO`3@++X4M~&9cn?<h&93TvFO6Z zd0Wl$&e}=jMF5yZMd9_V52m>_7b|&Ru!}Z+^@7<2~>E-(r8C zaq;{Yv9|&q`TUpgN00m&9=Pu}@TG^og3A+d*PZv^?pM7YX<$rchBcTVD9EfmXm?Pr z3g$)#ii$_l^Dad>QZrcu#i`w=YtGHf#4J5Fqzkcz66kl7lG+Sh)OKTVCDzuFwtFj% z7xo*3)@ZSAS7Me4s8{CiQb{gasGCAnyD`Uc+H>QY*}8?%z>IH1B-M#@{SpQVL#kq+ z@zcRTs*Ks!(m;Gx+S9NU5}tOT&0fcGlqzT>;TWqy@WeQ-u)Dj5!&)#;6KIts#HTVS zH&`;zfO%d^lIbvbB6cA$7|E&8bpcxO4aWyzA8jIxhAs3xnZyerf|*@b+4bEvvBF=i zyp;&LhEN4WZflh*b1R;q!pIc2w!Tef0zJ@;USZ#3x6U`KezT10{RB0Z*&N#Q<@(@B z2-qt5INSQzGcy6^!6A}$0~$~D{3VcIOKi_+o(=%5s*|m05;>6d-I;)rk|<(m6%Bnn z;mj_TiMC#QcE$c&S7z@W21ueN3SU(z4sjZCVKf>abJGQi}n$L27r- zq;AF(8IuB%ijWvCzGv*~#<2ZOTgQd@dgN1w*!k!U|OFPN6ZD zwJ>0bR79d+P(`6Z!da4dH4-yUo!rBTofVjyYrSeX0wn^Ks>*~a7941zR-lp!!>VVA zHT+9fLQ3m4z{PBI0bj>$*5mEKJ~C_;!Ej7$aJ%k!;Preo7q-3uSnjU_%sn40T`W?9 zk^lj&t*iJFkeOj6X}2w^;)*6tB9JAXuPIH`?q$Ztd>A5VWJ4FBP~6r%{1wj9i8M@9 z=ISAy5K+&Saj<_G?|JvVxMuG(-v1l#!_VCF{dnn}uf}xYYxwHdAH(M#{1kRqJ2<+u z#^*ot$2foaY21A0-FVrZug1yo3|@cFy*Pb3;q2M9dx@(dryz72o!EJkgjc`>S`Nj7R*TFzwf-SiVZ&!f#^hlbVrw~8K(Ncqf>lUY zf`xZ8G~7C@jbV(<^%lh;1F(!{q6Ds4X)&RqO384R)@K4jh2~~63dJXGQ4%){nh^d} z9jM#6As<8B6b7V+QRM|6QsCbWfqPoe?_F7dT~CW=1j7)kQ7g&wESCzp&M}-lnr=W% zUfLVRoPcqKwUw<6m|i&#oqXN6H|3R zGtjajvEX5CJuP&!=TQs-Yg5HGCu$ywnC7tjcS-0BG=(Cbsw*iK{$EOB)6J~0=c+uI zM?PtCmRV&Th*0>QLQ|8JBsNoh^OTy<0aCX}+YVCuu6mV_hTnBcjlbyi&3mwEH6=ZW zvJ~<+b3gTqbtR&+dwW8G%6Do)mg0Uj;o`t^F}4@{Q9vAtx(R><6WXExYzk)8^qn~x zQ?{}9Ay3CC6&s8~gR@L1{*@-Si?S6JmF%%o~sB1r`d%iYK%=hQcdXzEjEN(yBW=#y^;0}*&ys!N&c76sdfrbLnJ40ttfLA-a z`0|6_#E1XjSMb#158=McU&n9!`ai{qt6z+}U;AUY>f|l>(j%Y4RX5#*cl`KI;JQ<% zan0!+JoUu4@W7`Yz{3xJ2CupI-F4Z{|2v-xmQ%QY*w3I`-QB$lUm^k5U`L^ z%Wd1DShk)NTb~w4H7^C_}vE`)!p4w;5vmyJNyr+fw!8ia##_i-nsmdH8WW zD0)B40MHcC5at>{832eQ!8V8y;sUg}X!UbcA!1yLjV#v^fQ5_d*iy=O^3_Wo= z#$JYZN?8)4={##fE5RzXY7ZA(bMC>Cgy)ba7E{jRDQ9X`10myVAu4vaJSpkgB`f@( zG5WFV`L>T6vP`tFDHi}XjFo^NJjZPT|2STX3QP_3&VEmBhoe22d>H=cWM%-;uqQ0f z0fk3YR7j)&m^~afUY=BYnbwC|rK+b}Ta)F_(cl+(5<73@4cY+u^JBc#Lnjw{2YNE z;v4kV=j}tWl%@rugfRQa%)F`ulsv7$jRDpMJbT$q>E6fMwOZ!Ndg$Q5UpB$A-SuEO zu`k@X0HEErYmtgjY=a=PXfHKG`7B+`fD+k4Xg39fl&O-=dkMPxPt;U9RfJjUaKn^} z{+$$0q}~z*VfPLLIY3uI!RAUt&7!Wr+gVw-&;Zu)a|pt|`&$@EH{at0C(!a%oc_5n zSQm^+59TyAWpp`CR|+7C$dI!%JmS!3PG`;lOhU+3%Qr-f%H#>jRb7tFMJ~dUD~n)D zr0=sszNt8MU{`?@Yto=3o^eh z_h%qgfGUPz6eWrPLIx2qYnfKC0D%%LwiXL&>mmIUB(|B!Xpg}T@ldT!oB z5=*X(o05cJ5W)3HDR_6B(CLMtoDWS_4nmC8=iT$iW~Hkx+*ra?l_v|1WPqe5`R?wB zKmFJz@h^Y#SFoFa{mU0|`>nU(&NsaSb?;hSbM4jm{ulowe(rDlXSfDW;(!n zKf;3#K8WWo?&HNTdmWArfpImW)^#f#-XwLmmtZX+tKdS|lkQ`J^fO3Vd4N?1vi;>& zf-?YwMkBp&kNdkZ4llR?aIhrTcHt&{9izY=$M23)be|6(`hEfeO8xfZETZl6d}E1t zpi^2jG$(^RbWL8b(E~-R;F-`TJri17Ap+6(PRk1b5yo-E-rg=sDHzj)LO?G`E?+3)16C`0c?7cRUhses>CzwDnIitA~6+jXh0DX^c)7E{ob)Rf@g<(!nwiLwA^twV?Ri?;4F-4DihX6N7y@YQZ2Ks(U1va zJIp3Il_ER%W;Fv_h>zqx zWGz!#Rj9ZS(bLa9d=aQg7lTN=Z<)yBQ7cH3Tjg2u2vdN2;9(xTQi@ug$Y~tR&S;5& zoDxQoQi(H*U>tRr(nP!9=yhmHW)gCsyS>_t)} zMM|Qq#UtbB*!1kj6-dX$_a*KD82}V4ROW(L)BlG`8uVuRUR>ETPM5 znT2b4f3(G3Xp%O@7Vi&{Y;D>*Zq%<+pJ)04)aq({6v;BKmU25d&n#g^2YXgQyvcrM z4D}6q2WAUgohs(JNs8>Ndz4aeI3B?Ob`_|liZaoT=u?+8-?rcKnJ*By7PQu}uR95e zB9U%gX|Q#2+9Q>DLP7@Q#w+%&gmjXnB-K3^Pc6LM3^}lEh^RAA)Nrb24try87b7W` z;1QiAB(W>tE2gk?BWq1kpk9tGI*I}4=eqaJAi7MPVv3&|)Dn#tooQ)<%pPGtn3+Lb z&`IQv8A}YAWqD5kW_`O#2ghPd1k`(At)_G1;FIC|qp?;quNX4+t!2sjGUX!!Z|Bj3 z!2`-1A)?ObZSS$}g+iX1yXZMBAG^Y@r6M2a1a;gCT8xlT<^5gP$uI`U!{e;Y993a^ z*JS(>LkgYIVhLP;*r&Ysck+6!L)pCe+heMxsTQ}2yE0HbGlM-zMPrZ^=s=YPR;C09 zfa1BUTuF?`7EKQ`8tApz7m(LcxFljPIKOgrE!>TfEI`AOk>=VJfC}o&>`_$Fq(Ruo zG@P1iBOa3r1PMSXky@GzWW-ZTkgdP?3d<%{5Ujs@78(a6i`6vdXPd?)wE1wz$NF3w z^7J(_2oFO;5OHiG+c2Xw%f-AYAwYPXKxwW6IF_Pm47qE7rmRUVMN|5TVrgJQ7(_4; zCOzy2LNUt?!ZIb5VVfAa6t`L31-An&m|7^lNOVL#OPT>0lcpR5S!}Jm^VnyA#p=ey zl0caVmxoJ<9Yyi{_-$@D+fS0& zm)G%YzjPnY+JA|E^Dq8e{Kl`o6R*DKjrjW4K8G)V^N0BKLw}A!1=oJ?EI$9}pW`q$ z-2dkH;I*%P0MrZazxy8CdJFKAClBzXtrpKr1V(p-+}_Q0j!5T|)G%O4Hq5Ff=Z)C2 zRIm7x=fFP=vaAM@@L{t!BGDOx$U+y}x{ZxBvCrI?k=EV5doIOeP;3;K!(@&lRbV`0 zH@;`xR=Q)M2*qc>7O!U&!bDJWxy(N$I%ex?XE4voFHOdpZ0d0kwwwGUO__c~Z%0g1 z(en}omE?TdRDrYoIT{1gZUXQ3@GADDf>?{|F=1CK`aH|I4K>(!YHN1ildrkq1~(2u z4VD;1y3M;NMS2zr7KUrUEYXe@U<(-!kLD|dFwdqeGCI%lv&q3)JKrGlO&(BYQrSDF zA=ehokY$Cc%QbQU zk69{hUc+m`(f;y#p9xX=9iO8HC|gB6WJ2H}RlNQ=$X1{qA^? z(?1J&@?kIj9T+mXau7@E%0dNHfy+xtd&z38 zFt0?!3zJR;L;agJd>1eS*kfQ>8&@=yIk0W2#KFBBjXoXmcpm}MDslPi<8pTz{1H@~>j{3O)1+F%Po zx^>U!a+Q1CQmKb8LmRgZ?Y<&dI@oc|D8;nK8o32TnY1iHv6TQ(5f6m6Hv$xy z%57Eb@R(VRIMG0iZ3}RS^`SzP9uFtbwVA;vWhVk-ZkQ|3iyYi-b3>`c^X?xHGo~`( z%GniAnQ++~DpmAjN1Kn>U%3fW-QmbFx0WdjER}V;(F)it9HiDS$$DiVSu8}A5Lv2v z9Hxtz39rVM^DfWDk~T(bdsg<#voe=m)EQFBSUHN@e?c zWS*lpC1wb&{JI2Mc|ZWhX?`L?&O-69BxK#1}HrP$fp=*z5l z#E#X%RXf?G4Vh1vjJMCSXBQ%iJ5`|j5iPkl>h);m>EjAla>Us9}sCYbxo|dmde*ckfhFmj-Los+ZXMW3|;zR=B0H3TcN#6{HZHjs(3Mg-a00okFMjh zRDQlm6C;a3nU$ZMnT3Z()-#9t0M^e@;1J-k5gS!f@nicyInNHNF(#kD9x3y5NCO5 z=SEo1ELmi%2x@VEyCri_s!oBL_&sRB1b3#XOp6rzf3bJP0?Epd$GyGW(-~TBU0XEm z0?3w*x~3N`4j>Zy8I+3r``SX7mHO>aF_}+C6Dqu8&t^r&Xpa3p0aymm<$ev#U|=FS zGxG<3^dbD)Km1R)c~|k(Z+r*0T)l!f-Sb);_KaJuUd3Pj&HobrhyVNk1Gm-3@n^sP z&+x%N{}X)aD__CW*BZY4y&vG>Lc(C?nVo-kweYg{(e_MJMS?t>#+aeab5o_*DiD{V z;F3>!r?%I!B!FT+ElkJ^8d-K0rSTiJ88kPpS#DbaX@`HdyfR&uqIGguMs>={YW>_Y zVC2sw)_Rmv%jZt=auv8ldd`c;r^l2Xn^V%%qk@G9y$I(hW)M`*{@gp}KBG_tprJ}v z_EHCjJ4&r+3~-mQP3~$agn-jh39SM9B5O0xBH!vXVJdR4yS0`wDM??MIWj=4=0E1s z=0oaO6mTgx^e*aXNukQd53^yQLyar!GX#_6t|Z8LI6E6OGh6&sJ45s2< zn@(mlFq~ta59=c9I1*Or!6N}iEzq^rzIyn4C{MPBg8$o^CoE-ijQDQ37Zzy1YX=$6 zM{akZE=vE;siHUJA-XEa*=G}msljW)0IU*xrUl48VrJA@P^n_S-#b<$6qKo8zpJQK zJ=g|R0F;{YF)zFK9M?}%DLZpYJ;oG(sSsgb8Feqrz%|m&D3!6Rg#CVx>(6$esZbzs ze>Z|8#9!P>xvV5T3U{3)EWHH=`YlDPS$URPtGA`C-^hYASW_2;zht=GGvEXQ zQo!Kgvx{IS63s%#CUpVg%7v|RYEGM{;j%wj)}@l&6Ne3bj0s>=X5ZKbLIzJs-kb>Q zb6@mfOGurXzx>Sj_c-UJkSm8xtpV;0u=cu*(Uek&`LybgCO%xCcMDTe_0kF>LdUIP zQVh#L1_x!V!c)A-1hYZt8xjMEu||K5Be15#fCFB$0+k{{7-Y<+%+t#qs<5O=_vC-Qb>(TFh|?7hNSguBN~~dPpyu66RRK-{7SCCfyOmvo0C-S zH}BO`UIh_MqcI+kof3A`P|F1F9W&`1K~?z)73_CX1(~^HKTT+*N*i9&r9g^*9#Ju&kbu(NZl}p5BK*x|VDe7Le8+hL z;|xlepqupCg zbORub*Z`T=)b@%P2^oRkLe?aTE(NSPq(%Tv2|JE+HOOT=H-_0lGz7$XHpPTAJZo!Y za;y6o1CNf_Hr{Rh>G5}#KWxY&t0^jbIxtOp)Tv<4j8gWBxkX0QD%IYn5^~iI8siX` z%Eb+pil0WISiB}#69G^s?VM<&ngj(@36&UiA{^$1d2ZOB?a<~1i>)tQFt=HRJ&8%L zI1%eKZD^O4(O?uProrls7Vo2i+m?gVX0XfB>#rH>D8QCV%qr}O*}ntBh2RJ#l<-T0 zb6Ky0!1AZ-E85aK6d5|iLTnHV=!{AyK;ZKH4ZZ@bTHp*+sB^;|rW>F_CLe#YeQP;CH-iWN8ZYbCOjbB@soIX>if+3be^MtU) zd8Y&K1C7|LNSLam*Ax2a$`K8Wv|cbb##EYC#a2tK0+qVk>6}$5Le^%??TAvfaxf8& z1e~jfg8}sU2yQc=m1aSVZeAOrbfAm$5Uk8zH-PKIFrs8|>nKwaK^&Fo_0H9G%8F`? zK4{I@*~nZY6w1ESzH9)zrJ-Q?d2Sa*4+|Q;kunkEvGC7QzYze^THVDFp#lU>|&iP>4I1bC3HU( zW`Z-#CF_3l`3sMMtN};yJ{toGe=Qp7VO}-B&O=~W|K-}P3`_~QnRg9{q#$O|%yM&! zzpVjWrkA{)?4KFGj(g(%=yQZW$h)_*f80>n-K2YH+gS8tO=n?X6R7|u2`ka8c-=Wh z$O@rF;j>MjE_#-d726Ab)zCxr^i~b!?0p8KW2zH&yGd$J)r$T3RqUpU*0jVM`SCc^ zvcoh@;3ny)5ag43PhzCyHGKjvllq0CdM1#D3SP%njf!3m1fO|g`e+}yi( zH_D{5+r3{@%*bFxK`X48x};~aD4K2l#sD{^cOVWTSR1bND%P>@7D{ji zune{&S=w%O5KP=-TlNs1z&X+PGD>D|R6?@;XhqZ3mnk?Ce@F%i! zp;*e;v6s{!C63fI-c|G4s+1uZl08=C@;HqP+`9(hA zVmYpfu;tu?X}%I~DQJ#Ka{Uv;4bQ$SWCEf(?;4OU`G0)Bb~k$)zM;m?x9(-;!J1*9 zWQeE7vR}|S(nmIdiK`1tdB+`9?9+1L#2`^nI(eEOD72cM8_@?2M?fV zZuQpXVBb`Cm|K%9A=8;;Iw8VtH({O|4#yeb4%P~yPH)^@xhQG{LRHXF$ZD=Z(sWwv z4uPs2^2R{txe~G)1UB}O%~Mt&@-1emw>ivJUFErskP#z@Tq<;@Ed8-ALMI*yu~0a+ z0O?9`k#7@l`{}MwvJGU9z;_82JY}p*iWVU3aOLR6$M5~S$MBP%9Qqgh#*eb-eEW58!YA&IfSQ%}4Oz zDSYj#U&14wdl-*A@_M}EJ#WJ_O`hs=1RuFi2~?AfKR$L4n$GJ`0($)u<%WT#>V4`My^jYq9W;;hZ*=Hq!Bp;YrnUULaOlMI zrTx}BFctKYumynX?4^0X<>Ap zWXi&YM0w89_4%Y!kjivUCS-0B>B2xmm!miw(G&nM5rN!V3a7WlbtA!f^1J8bp|Dxc z4UscAzzI*lmD=nr8i1`h8?fK+aF})=l6<;U0Ah<%)hf~4)8K_68{o@;Ggr3`pnX~Z ztTyEWIE?pXA(NjAA@4cQvrN3fJIxwuo7!Rl#rif*w1*cD+V0SF>W4B*ott}jMg#< zlPIb}Goe!Pkf(V*0z}xIouf>9G)VXW6~eApW$dCnw8nmbTr7$U4^fZI6c*3Q%-EGl z=1{Zp5V|NbHi=unOUGTyY42XXmdtkxkaJp(%mbe5dM-sKtgzaC>PJ?PPVtE?tKiy$ zahmUpg2nh;4u9aqeM{x0tJ0so2gfl4W6^~~hqiWL>(nt7W&es6&h|T?RQ$nz{xClL z(U0NQ^Bo@k!VmD8SG^8*z4a})*f+fNrFY`~w|@YC<9B`sx0fgIr@#0A!KXj$kr_mFoo(gLlA7vB0id# zJyoeqtj&l*2489O1Vj=ATR-E#d7c+MeD(X9hR45`9^6s!v8*hBt#1@hEo!7Q`L}F; zn}067(s2dPG|{)>bP|a|k_r|+=H)D#8k>z}q$~h)T8c`5Bbx)frU&z@Rso%l? zRR#SWZJsez0EO|)wd;81@&Z?`LOmG`AYixKVV)Z%tJft zY_m7ARKwvh^G0Kw?+09NlG3vu>^GUVn1>!=gq~~$CY~t5rIdC;Sup_XP}X_~FuN=R z>>OF9@t`?L=S#!3<760=bkRbyvSg|xD&UT{H&t>RMY%j0_Fso*iStSEcP)6eBaX!L zWAaW)As+TbnonBpP325Ud5zBH)4~KRV40r-kqA_e=D~Du5>xwros{#xukc zOQ=&=F-(sc0H)HJWd z<<~=TS4~I}XPqumiZwn0o_hTI-9;1EwlaX4>tr-|vvOf$!|RS9n~Lt{#nUQo`VefjtJe zHRa1lyx!=;_ARHS40)Up5(F&)Sj@ivq(bNrQ9iP=&96)#6;dTDl=Yz0l77#MMw;Vr z7wH_6zuV6umjX+nOJK*~MRBrGW>(5fAAT{{3sxu`2vKM@M2YbzMcW#RS6yvJW?3o) z{djbu*FCS74LP~O!9Gdyd-Y9iF0b`Mhu}D6)$u!BfTw`NVa$CHL}FFwPGF2}_q#vu zyOVvMT5C~=G&5}b+c;NdfJzi>eKP3mw^O*P5rw;CtV!y2b)>N#%GPBwaGZgmF}>%q zGTXIGr)Wr)m1YRxdS1q^%2h&WsS;pWh*t0Cm>#zPFiT246bHkEDju<}pj8hGn$Wer z^v=#0b-H2s$2)aYRN0hN&{#sKN?G=7oF8Z#cBn9swJchOeiW;rQgChnrA_ia~~`gGm;9fR@99Fohuu!0d@g+%t%?(Y+Pcw%PEU( z{)KjJ{cW$(CZONiuVFO>#4=^T+mwhk4P>PtTlbfiq8nXCn_EimwgQ30@8fd@tBhwX zi>+ChTv&_&i|OC;=nU;U9~lKGAbf^pBO|Q%_P7EXB7`84wvVl0$4C67U$_s?{O}1} z`~D;Ntq=Scxb;=9#6y4jhj{prui;al{A0As1HSW>uj7dye+%<`6W;aS58(CpJ%DT1 z7_WQX>u~eUgvTGBbpeB_8aF+l8+4#HVU6)k@Q-=`psuokyCz}Szy%3F+Ef`2E9(6o zGd8hXPUCRAHiQunjF$i9n#)_4N??9ZmL&lYhTaYXv9JcK0T6O^TV7dzMq03U2Pl^3 zMTSZR=JB#N3WqW#d*8ekq~`;&XGnf8>e8ND6S=I4a);6vEA1CKH^5!_GPE`@HJ}~k zjE462csPR5z}&E(3Q7UyE`~^u7*?)KXSlpP0xU&5Yq>xrQ4F|LJs2MB*5<>;8y*6< z5VVlWm>Bs{HRMTW989b!aUmeGu~fpg7g>@8-G9Az94-#n-+V=Dbs6meSl|k1n_@ID zd6e2V&%+|w11w}b^*dy+eHeR)3ZxcB(q;x)&eg_~&cV8mo%RirCFR2kBQCFecCb4j z&ikf#U%RdiVf57b!dN_;7K_bTz{YMJ@@}TJU&{xReNff1jfTfXwzOrMW-DlS>S8ds zH|70VQFo$QJQ0*@2B?AO5MbeFS(gP%<}-`CO^8jJVArc@JDFY}GsX?YC6M+-c()uO$ zZ3&Wsyy?9itEdPAV+qVStT5WQpm?zuwbn3qc8%U%&>bug#>tgQwdg?dgN+A>$Wb4~p}FAlga z-fIe)SD2N}lQSQLQWi0)62e6KHF=D!2H&|-C&1b~X$%wr==02&rX2g6=RqmK2}akT zTaS}+L@JD!aMUbelHsbMa6xZPvY_ntr~qco#oo~Y&96T9jzc?Qf`(Qxp&#uqVeC(* z>731ES+fDM_qZD!nT10I%m7er{u9svQu}MLbJ_}Q^Jn`yn5i!xfUPqW`Q1hX*}@-f zpQfYy{gx7P%42Y{4FpJ7Up6*g^mE;I{2h-PUm1a|;t3;068-={&ji5*P}lT)X1QW! z?4})dyK{W_j~~Pz{O*5;pZxIK_^1EV7x2R;AIE4Ys-uuq?;qaeXSy_ zfh^-=fv|l>a}mQ~J(N)GEM5Q!K;oE_(ZSzs>l2purLZ3r2m3qLgLS~g&LH_YiR^*c zqQ{bGJ3R7Bk^fxAog5Md#PhjocOLUF9#)HY4>{@zsNK{#Jck|N-sKQ;xBV$p+LgYF z8We>;NI&Lz2AD7(JFZ`U2E+}!v%U0#DmqB&Gt^1?P7w1vfMc5hVqDozQmLE8z|m%v zCt3?*seA(%=D@RlLDR%)cnCwn5DkMjVja9syI91139%@hG4*gR`Bl zHKQO%&u$V&8dv}h3w1(Vi3SgbuGk~d=jF9DW@V#67(s0WCUErqyeKQV5fZypFqTG$ zC4X9a{gxO4FdECeBJ&-S?ICk|fgH%!^DV@b=1KRt6{|&XIjN_ynzEnjdJ!Q{0@8|Z zI4c()G&Ud(0Zpv##8S{H(xzE^-0dD=ymoNJ7<^`aw(#<^>87R#H8A3)HF4)F1B9Xs zYefiy>bHI_kC-e^HQ<>VY-7rK5)lxc^n5}U0PE)wb3g4KY!Ko&!%B_94+;aYkS4{_ z)(y@L4C7e{eCEN+u!u86c3^9Nz-&nI#($K?>V2s~5q?7Uu~poTw1gnijx3 zujK$n74S-otG=7XvR|l3>RABj08K@T2Q?LbP3KAfnJ}G;FcQh;+MV9O7!HfdZ>%6v zy;V?)g-$LjS5y)0;hg5$W+IeHVsse=eC*ioc52aMxAsiC2`E58!EtWrEP_=EF#)wy za0li#>wHDpWzsndFO&!p@_g;#`GN*7&|(i+-GIi73U-BhNuyR&&(gr&SP@-<|1IFO zjn4hOi6bx1Kb*Rj7OE}J04>_gJm`i3Vq=`?FIcS&yYoiMPVhhgi@1C<=}4vZqUWEw zuO+!Mk@+n^DBv!I7D2#NB(vC`eEbvm@FzZr+pk=~Q$P7J?s(bD@YeU5e+rL$;fpxTzz-h(Aug^1yM4v+5MdFnU?YU)(qLl- zn3(radYOS*M=um3$DH5zid#CuFl8-dz}?d`1PobLqTfpcNKWlYHvgRWgfW-!9t7J; zYny;fcot&KZ0ym;8++jlgpHtUOVL#x+X-we2c5Q3^bAS@%7P9BG@ADcjCtl%sjwa!ZHN4q zXHH?{pXPc|Ax~r*t4IalP&AEs$$h01x5Y3dhSJ>7vre)hJ<*<^2ekmea`7AEkG8{t zDU&e(X1%OZ>?vcAY0IMRu9XOqZaGmu7f6wLG3#^?J|Kc4fElO1#SV0;{kF%Jvt06;O z7=f&bj;+YJgCnIm14lJcd#|3!uyqxsNaBnS^8r#VxN9m|DfD%DxQ@~quI$fcx_X!F z4Z@A(aO70M0BV`A*Jmx@&E}NZZP)+^6A_jNEv43lCO7or0wm*1j3|Sfp~^W2c{0LN zx#g*_yNHZw4y(Lk+=Ie9(B+ywmfxLLmY4lmf4*hy+zNGE0_JRph<%-e@}e)XRw3-n zKgor_O#l)t+E2|oNV1Pgirw`YXDJXhw&mH4i085 zzTPr_MU4&>ta=*NfVj^U7%r1hl^{Bc|PLe@&dK&Jaw!XE=kU%aluFdGSE_hF|5qy$YdHj1(1c*$V`c$YRMj0 z@QvaBPQn8voKy?-T4<~yN~0HshKjL4S7;bFsoauZ^TFw$NsBPJ5p5*in&(2_XGWqW z9~M5M;C;1!)PQFn#}Nj7d_-jCi3r1i&Ar_d*_nKA8LngL$I{?xz0?&&?<8J&hMXh7 z=#9#kg*@l+tmiDfbNRi!f0*|?uHt=jgB|Nxk41X)>@_uP8wHv(e@Wg|`;Ix)ZD!k{QRSw>C(#_)BLls7WlVNRfz2u-rdsp`(Os;F^h zV%P?$eL><5(y&1oqV=qwwcl0qWiP5ChfgiYTwh)Y2fwa#@P1{~QhQ_h>KMQ=X z5(*)5NgOB*s@StcqZ%$6>FRnoTh)=|sOH}{e@dS@?El;kO-2#+Wfavn|yr2ycW(Dx$jN-3C+3dTyz9XAGd z!ftKNI1n(ltRFy%`0>90~tbXx>{eS8 zcAfQ1hTW>LBWMYZZJ>MAC`XF7>0U1dyE=)C(i$tq8|swIItnIhsS>`z(LebmQkF6c z{@F_2Vr}`e3z;m_RJ`zMKJ05=o1k%BepQM|Ep-CZNR6%$X5$aIVE`ax9vX9RblH_h z#fH{rj0baS*T$rBn?R^qT+p&9+NP82U=gy%F%bdGv!1fpOUwPWoZp=Kmnh4bY|hoc zy}i*`W^zs1u=;8;vJp##{PFcCH6Jr6ZZ3k7LKOJO^Z4@1CHdMm4gECNi&H4DnEYY@ z4*FSIe}cN)%mOUgY(%g@wc7%2|7-5&=B3RG6hI z3c);g^e$--#Y#u0g~0B)>l#V`?59cEZ5`Y?njYphP^lspM1%_RBtQc>Dg%~#%}~HD z-C_@J(7p-<8~d<6rv1|L{^b>5ZHtND2s~y*VOg){`8;>(T}#iz^8v8W|MHVd!+RY= zx*}E3xe1UNTx%=OTJ6`!)xb@EK*v(Jk(+_?U-~QG&@$hFqhWEVt z?bz*ixVXHGJ%oa&JZS}-OlFus)gGBuN^21@Gsv<=xI!ulNLIp5CBs;I7nZziRQ#l! z0xOWk9Nw}~v6P@iXf2WxEe1L)_Q{ zMov9ypDP)-Q4+*{&osPEKvZAeIZOJn2$%y$`?sP(WY8aO9cTq9xgD?2ON(x zY6GUJqL_D?A?GiMP-?}F3G+d!ZlhE=yl-+HtdTmTUS;9=03}03_9ijnJf@COm6EP& zQJ7$PUWf8lHqUy9v@1Dt7qDG-Rci~$I(p;bfmw!^)IP~y`1I+z4jA*ahY{^7Z$ z98Y*40BoWymtPsr>E{a%wHfwA)~tD^(2mK~peIklfd$UV*d_fO#>L7D!00@yqNtsa z2v868%HE%`F@%^}C>ASaF5SGfZ}a{j-$ObY4=pC|J4$$Lvmzo_itAYz#E1RLK?9Ps zVnM%d+qtp)`&nEsPN7JPpXA=^Ya>LDZzBeJ%P1Fx2{Q=2bOk-(nm2~%)*p*)_0SSH z@dr^FDAe!(<9ms?hb%*|{X2eNK!Y17-0&C(B&?9;kuiX6v2PuTL@JQM%Pq@ zdgm?$>?|&lE#J4Sv}HG9$iZgwDgo@7HN=fg?rLz5Pzo%`qFF>J0TUw0m;xXU$&;Kt z@Jvq3dcaTf!!syUFimGtHNFGKxdWvEvmE5E z`#s98>N}xljC71Tr!q7ZfD|g4>?Hwu7Qtl#B?MdI#Y^Cx#vCZarP3xnj0S;-OV9MU z1o@|~8v(E9^USICPbnU!4!bVLwP?*+IK4N7(A_xE)nm@eaLxLtvR~y1r87RdN+k<( zk!Hk`HrBA4CiG7D@FzckPkiRHxaIsDPe1hpUUT>B@t!yRA}*=o*4yvI8{YC>{Pu7E zEqw2LpTQ?T{V(zGLm$T%zWhyKcQdYCKcwQbMfteHZ5cyTtA7E^UFLD%&;%SZ_?Syn znPYTRn$rA+(zLWBZt=h+PZLlV(n|z)gdrLInKBV;Xi7tarHT!uVr}Hv>f3W&;^j<= z$_9{GUZyNA=|0P*+(y3K0@?}ONzw@?zRSuhhR|HAMBEwF0OEq~%4`jPi|>2!>=FZ9 z3xFB(e8llM;cz%eNZVA=XW(!+pl}0W7O(5Efu}CC(x8TthIu}Kn`E@3TBTqtH_Z-4 z;?dm*zadi=0L}VmyuJl+)_D;Fix$S?G%X1vcRl+-C8)y^pb*J9isP|izqfR+?6hIZ zN6lno-P5keUWk5~ksx@$xUR;Rmb`PI3(j2pG*1oM5v1;WVgvDK!zw521CtT@Zl>73 zaT(5A_hBVt*y@+Z-c>5r#6BEapst0@FDW{VF+_-4bZDHL2=QL?^wqpk818adq~kQ0 zLR57P%~)>At>i0OnHI5|yC7~644Av_|(?)1!6*NjK z((44YB2BEcWry*?@?rVq19YhC7vhfdAOP(^Uh`q|k6m9c$PEDf@a(_?cmNrjUV~ z>x@>!s#i;_H?k0-M4zloFjb5<2AbEjD;TzQ%5!sCc-i~oWd<7IGdf<+rDWX5Lc9d= zOZq%Tx3&i)r~GI=KHaZ81{3x*#muI8;KZBslB_`mmPA+^cfsS~d+S%hvdfu;lbQXz z&pbDjGUG3Q;J-lU9Ugh`LwL*0_v6?8%HP0Se(|^Pz3+YnU;O;X@XQn6!Y3d3eLVW$ zf52xx^$~pQ@h5T9t#{(?d*6n)z5N%lI|H76O6s7%(lhHE*cgo#!}jg)O@J(HE)5<) zQXbbSz<~oHTFu*X-j_Tm)|`fR;_s%vN4=P!*_hZ!Wh{V!sHb~@v43IMT3S5W_EMHd zH{jVVs|5v?`Qi_1k7*>3mRw#gw|#-pK0C@qZ^Ya;_)haAe+5; zid6fKzP0c9&^z+JSpd*99$$SMM~W&LXiKr#DdVJ{=de8yf2Kxw{G3KUY+I>#YImid zzV6Y$gN?Lxerl0KFL@x&S4`F}Mrol~Y%-jA$bQHiMPF^EZ2vF!=2G4f(P~ad{!d{P z8t$~;qdbEBzQlssQ9*Dvx4i>YC7jOR3zwZi6EMqw_vc2~^7&rRN1;edmmaLsF8^%| zpnx&!V@rN0nQ~aeMmnq}b_cxDZ)y5Ul6WvFcvF<%f++R$Y7oni)g?3I1sG2w$?ndT zwrOrS${WbMAy#2D6(=*jnAaOPGK3f-Qcpw@XaNDT0KnL%6B_Z2fRc^VnOd=+o=_rq z>IZRngkVht)Az*@?OirYKt%A zdv4V4<^IVGJcNhv4!MUGJYsxT2A0-dYYn}rwUolcrl(F|N}$oZTQUJnDQqD{096go z;DUK(>~}}}!rSl0x4-^+{Ny{I#&5j$-MIP6B|h};|KIrXSHFkHpZEz5*DmnGAO8nD z^^xI6%0#xzZ$e0EMAx`tf^E}wlC$HR3nfQz|7>I@Mk5+h2jQrbe; zU5BOeV-}-{$z7L)n%zp)*}N8j?H*>=$pO+(6>Fm+lomuGGy`(BR+i}*oZyg>kL(#MqZcYai*t?4WMIMc{g09u6d&=HJmf}kx|{~ZM85xs03%d z(d1-wR>o$<{{};jf;R*BJ@u=AMUI=IF%Dwr-|!WY?;U{<7K{(4lf`cF{gV5(lrPzc z7~#b}$$?|NapNVjmoiNRe(1FYLq-fT5gh6NnAmnKj#$rc$-dghX(Q@!Y6r-qswfe)qBGjgp1e z7Kk#T8e{e+PXMS>*0mdfz2~}Ldac`o^iXQE&1Tt91Z11?Bd%}ujYX9(O;b`7d|Cju zRI1vJ9>|9a1Jc+HS!~(&64fkBaAc5~897T>tf}RFt|&m4+7d3(OOb=ny@RIe2@LEV zrR>n>S-Lgt0Tj$Uqd*c0bPX45{ip{Miz@Qcm&&@+h|u&PCNWTs4Ru%5gJV|O#JE+s zbxZ4OBeea_3;J-6&B)qL?ZHtQ&+{eyjr+El`>D284Cnh5(^t0V}}emZFAVNLV1S?UGMYqu<-=z!k~rQX=s zv0OKA#d&wEiM)5@XGeY`&%##O2FTuQ{Seu%x2c}|vru4*ot z%~n)zQ-L*WffB2=U=zjN4q}Vf6At`jFR|00`h15uV{ipf?0ibW<*|Xvgj#3x-Z4$; zEuDeZIu31?S`?5}v6pibTJNMsPYI2I^Yg2?esMt68b0W(nIvUhEsb)FF@Zf>A7+@B z$@VIDp;w8k=s=ZK@~XlPLe1O9qA(Q#&kFQFstH(1Zw7h-^B#^-FpF5gUX#}}gWwcv zX7rSKAK7@8{P%<`elBBV%+tc7XM3WU3>+SEeze+WiXkF}`SMJF&ZXb(aVefif9sq{F0VGOG1=d*s{Q$nU3gwj8;}XWO1TclR1oJ2$RvPn)30r)v{T(-l#Yu=^t-RR#qPk{pT6uJ_u1m%s?QMrr z#68>BDS#B;$AQqLWj%5uXF}d)MoX;j7a#wR`1FGx!?(Zl1Jtvdae3^xJZjPz)kdNr@kJ#-daf8dBgIuXO>A5PV=QOyx8yCypt1u+`dLD<7qzZw(>=v zp35S70uH8c#{Su}Mq6@7Ia%frVVulr$=3`%TnJ1BY&E2mR=dhG*0kSd5bWID(Bkvm zfw|8P)Gv=S=6OcFA~e!klW71XI{ZHbEg{tWx+9LM<*)Mj*+YQP0b z&!u4Q!Ry_!%hosH#Up$hF@y0w_7J?{1}BCZ7CXDOoB&ZK0JNjb=~SxhQLER*9pI|x z@62iN%~{&k%79b1qqlo*~76`u|F65++VR!DNZOu2Gp}cwY;{B zN}T(=xK8sFIg?eC)|fa701k3b6-OW@T7cXf}6f zY)<)^*CHLzv>dsMegTA1#k$fpY%>UT2{Op%YS4E!gw57mqz6tA%oYe5$DBdQy4kgM zzWjc8+XmQMz9oMKA_q<8pOXXg+8+wHYHJ!Z%N5{RHzr|rfRIMP@N{=fkeVe#Cl6=V(0+rTe*nn6;g2 zU1S)xXNBguF7i+cg2mgf`?B8iq;wyzh<9~@S1kuHROq$%V^b-NK23LI^U6Ty zTjzE@9sxSwfA_!to4Ebzop|umAH>V<*yH{0|83lV|F7b^-~Tcm`|8K>GL1K=N|qPp1pp=&9~i&dtUc*c+*?ojr}=r?P&pdrLZa$l5MSPkx4)mj07acLQQ57 zO_IX}wUCE}pSUHSLk59NHOV}B%L`?PspX+lf`0vZ^eficyc*UgVKDOdnofrPn81$) zPV)CWCiuAHPwfm$R^@G&+w8r2a)>OGft0qgr7)FLWYy%OoJ)vTl6v3(0Ybt9*g%2Iir7B_W)m%-i`AYe-)~v6-U)f2}u6 zRa24o?qTe#>m@01B|8?WibGT+{F}2EuL$JfqW)zjTw3Zy0(9;N2GK{QJO%(W7W{eM!BsUTwdrCQ41=~m!q(Z zYdSQ%x5J`WVx9xruJK}i?V7i2@f$A|oW5Tfz=ik_9(I+JTwsNAbNJg9wz4P#y1_j^ z?e#NIC`O@b#MoV!+4nXX07?bs*_oE`Ev-JRo(E#B7R+5g%Yark^iufdJ_P`${IPGy zEvq8wMq%GX817kWy36lw1nQ08WK+wIXuX3b%QB)JSb0B2=JtH9lS1)^Pc7Cl5wHBd zuDP+i*M8rs4X}0Z^6CmEmlim)@)^^$b|{4;3GdT>_M_Ib2w~l!VMLZLQ`4fBss)E2 z49TqWW*M4^RpX@yYm(l8p$DKKj7hucag9Qk!i0q9mw`!miaSwPOJh zjqmsO@tSV(o>|Jkl^g@cJqAG1a;q_oY;!X-hYDED5R~RpoB`q$mb#p}pG_&Iu-Y>b zItr#~f+=&{3A<^JX_C;O-5w~@4)ZLT5G#rCjk;tcI~=aza51~GaqXF>0M-=V3T%gS z3|=5r!nRy#Q>-Ds8!w4-AL!87bZ2lnPen^$rTK1ZQ8u(raPUdcgVn6Er!Z?(u)CQ1=)VT^4PF7iTp5rHt_7& z55kj+?**$z68zkIn0UFpVKgd1*U)NcgeQ!GRu^fwt=+*3VtZl0h`~9+lLNh1L;Ti{ zczATIN4iY5ZYUGOO(2~kjijI==iPKVhWkz4u&uAnyJQoanVoI~Rwhq;erH-UTgw-7 z{Y*R*c4ZPM%HuO_I5S7Vu`Af3S!sk2Rd_Yl5SH52h@%c~$&B$-wb~{4KLv`t9c3;N z9%Y~^uIFRL^55-ss{fDz)7Cp`5m%xs6gODSQ(l!*V><0pJKeSkpEdCEzlKiU&~mo% zx2@AD>+iDf6vz+**wlO2I+!*-87*}S1qnB|$7BO~ARB&Y(7Y5d;X?*9CA>sj{W3|V zWJ-^TvW!#_D=C0U_?v_RSeDx<&$(JYnx>X3x^X%KXdRR0XD=jmC|Z;3PWv+{`n0PB z+%*+~dMOUagX|wmr>HT}=z1_wnNKG<@LpNGI79?ApiVV`@{HBU$1BUi;?YS9u-7cNVs;S`|r^-Gnfx9LAp z4QA`Fk@qPcW%M5)Q!St?rG_`|UH4V?UZf)Q88e!k_L%A(|MS28clc-j^uNKbUB{Om z{x~KgTzToexN`G7K)ngO-BrBr-M@)<{M=y{p+8_7ax8IkA3Y6_~N6V!LPjk zU6>|9JBkIZ_dY0H1`dc!Xh-a{6@C){#$#WV7la{-9JFbx@-ti)!}zU@(c(A0|EIko zQN>o{7FAV|t!K(#T3ele&{^5c53iL1RhpeRV&fQpc5ZpiVnn&K-Q1ak&1hu+h2U zczKD5jwnpn?I%zr)M?TzPaRX;NdZOnE`X^Dk4$5ct6^OHWZflbN>Vj;$&@Naz;GWk z`cabUPkRU#jH7kOms!ExX?~+C0d1+F~Q@0i52`S(RN6PP-TF+Fw(C5XiXSrYd~G>AOfMA{%PrF0ay)y z5hy6APv?=$3W8#lG8#Y~jR0`L5yAq`&u_xz#dTa>9&k2I*wu>pD5oEGy9vAf-ZNYs zTLVK5Qa4CtWP4X(0#})E~zUC0gUtfhOt3ccgbJ!+Hde_pC?}{Y+hTmF}7=b+Cp%*+ps%hv}SivYa zps26&(2i=+EtrZJ80R*lYnCZu#;#TzjtA^P*jK>@yZt$K=$M-n#w&eB-S2Sy;u4K? zUucAbl~7!+ApstmWMf1cuZ)=e{9EU)^TJNO&dzmwPLco!c6I`Qo@Xl{2fa?+ijl%_ z68(%l;GZJz1v7w>Y5|^^5}B{*en^bf4QEQfq~{?ztLH&IETlqJ_zoet1eM3AfQ0Qy zz@h?1tQ9nAb0qYiX_^7_#KrBq<=n`cS-N6|VoD-E`-ilrRz@wS2JeW^xBnc3P|Ux1 zcTK6Y9{By4y_l@9vIWxkhKv~H0nyS}6mww)F9M_Y@NQW*jiy;~tFO21TbN>ObP`2; ze^%EE&+_zq?^|Ff`<*O)i_|49h!LdeU^QqPa*ziQL{{8tRZ{!bBU!{m4U0t<%`P8I z8kG<0U{}E%8Y~n{jf%sTMe5{9ey^O_ecVI}dK2&OZ21kcK>b->>o&KPF!`KJzCJYd6?0ev7>^iPN=1#RA8#Wu2vaaD=1}>HPw0m1tgvt=wWHw ze;});hEnpHW{|AoFaR%u^#3pUatAbK+ZgdkD_bBbKsmg{pc|w&Y%$`cLK6((l%ksj z;lQh0sm(O3LU8Cu8gQ6aQvXB1#Ct~|x%InyCAg2%rtfyTPe0t6X@I9v+RO4?u~f)l zDI`V{{?>o>cW~$Juf|{e>F?pyulxo4rC)(zyz4=|J6L4{<#lboN+i8R-ie_la>Kb&(IJQn|I{Jyk4j-Vm6tNd##Jd~Z_ZOmbn9=%gb zz_y$-IM^KC%@O{yd^eZ|k7NL7C}`20-36F*@0WrdQn{gkROGun+?l~e!c6D6qoZIq zona~!bD#An0^@L)aVeG)J)m6Bs9=Gv*#zTSfEAB&0C#7YXU&;Xd2HlHV-esbd8soUrTJz=64q z5=kd5($1!Ao-gKl!3sry?Z|u}U5m>GX<#FWRy9?EYvPiXlYDdMYK*Lm$(b4Zl88%5 zH~kXu4jT4`pcp+E@(zzFC$T1U?Q_ls5n~&b5`RBb6I>`B60=-DMKcNYj%im>cCrl3 zkXhj3?KO4jc4Eyy!kS5me3RPD6b3Byya1@! z*mn38X>6k8rp4(UTqI9*7Oq(zT4Iv6U-H3EKLl+dDT}pTTl)13M>^2G zwtOM|oq!kzpIW|Sd2Itoz%xdZ@&^SHqi|SX;Q@fn&Pu6XD^Kgb#GVpkN!PHBCg@HC z0o%F=h~px>RWkG>Peo^-WRHLVLknpTb~)qKFTILe&yINY%WuUmzw_<*@i!mD7a#f< zo_P9zPki*>qcz6kKfZ=}ej4|@@m+ZD%U+FJZn=W*{qRZDso=^Ec)HEnXA%HLR@^Lr zmj^K>C1Q&+j9wuzI|wLLX`;L;Qsi&*c$;M7DYklRpuWwomOOT?U~XSarN%*eTJM#i z#pFktl{JmZu14Z91HBZA^8(fv8#7|AM}t^#zTl|T_PjnUbFVbx8S!LJZJ!GMQK}-S zHmCL&}PQ5HS8w=>HAtR_lC-ZvuToZ z2e1&UrhG{)#cBLL<}GDU1{w0cYuzb^oVU=huOL=ZB?15oBI@0&`eG!two9%0UioLu z_?aiiE+Y54h3_fo8Tbi!s69biH^ahVDCbjMU@?#w8agvdB~+~gQz&p+DdN?(b0mc* zk_V7wdxD?qAocJstCGuFXav9@t4&cz^a=?v2FyU`L)SI(6#^K3=5kgtA3d9ky_c1~ zv~(*gy^U*2HfbAKt|w3!%{r#UqM6;vP@OvyS1Le6FiIjgxEiS4Z%e#jgAV#%v= zUlGohe^>~=pV!e_u#Kv^K2e4?0mzcj#wot&O? zf<|Exu5`+E~F}`1408*1}fJOV9Nt>BPAl=yR<6CvwDcViBoeZuA_=tQwwNl^6Gk*U^^Eg zz72_7n@>AGLIPL;nGzvs9&zzv7(I5~+A2VmRtkJ^b#^Sw#O_&GGgT=k0#GWrXjSVn ziS?uayR)5I-6YmsQaFn16STwm{v21&_qe>MIL-&jeo^E=cLPa`tFFC;ff0*Ut6Q!` zXx~XQq;y~cfv5&hp@DCz0uuCr6$QuotGsnUl>yC`$6y5vX|de~+RKWL9hAnZovmxU zM}CTDW(>!X{X4#I?6c0G(Nd~>ZOYAZ|BZWN;eAMB3?Lx~UrEz$3ZHfL*|50-0}HFk ze2o!lAoG}~T5)!E1^@H!{vQ7EKly)R*RSJ|PktEG8(#Z{_hEPIeJJ~v;Fha5;azWg zKi>7ux8mx}M||G&=y#L+UO%o2sIVp7jsDOIbj0sZ7w4`~= z>k@Mm#u>Bl4%^m7@9Vjo-<8M0q5AVTf{gbWk6a(_UbeU8p;*#({w%-u)V_$%LqYQ2 zQM~Bdwh}(h5BKgKbQB43G&4~j-YxhP;0?uJ7At?oK?a!0c|D?wUYN%Dwz8vT5q7v*wsCDWiLC0?ED-1 z#)v<C#`vV4EfT#XQ^^hRTxK6QlsVvxbb?)LK9ce9v{T`ZGERlNf!7;xIlN-!c_Z z-rdchr^X{IUgmvHhk(iCkn(BC?yBR5cv0-|wPk`6uO$?uk~IYL?iUeg1AzBSw{n@> zGynjo)h2?J2Bp%tOikLSMG}#O(Ny^)iQEazm!t|dlqW-E6Ib#x9TI&fVRJ;1#AKP8 zHj9hFu#xz+C4MkmhQ+=o2B?-1OL&_`F0o%tLC8Gb1{P#D;@CBxiG416IWVxz??}s8 zW{hJV+f3h(lg*2|UbsRrF!CFN#{99AHfJGpX4j-jA#fu!GVo#GT?yUiV6=$ahK)rF zYiOS2ED*Mtbj~E8BXbH7OS8*CeMY>2`Ji!f4{yxHC^&>5T5I6B>AvY;R;ERviEb$q zTW#J5w6DLTtu79iSoN#_-dRGBmV$4%e<2)K{XlHI?S8?Lf-oRgw}$t``-A!Fx5}IX z0JCu^2Z%y9jZ+wowqu}Gqcc%Jo;jqF9muoIY~2#2hoMYO`_~wLfZK#rH!w{E*LPVJ zAeM6;eIhXFfvzs+KbYd#I#sEpO@z)3b7vfT2Z*sNgjPgY9s4YYx>0~qQjtH_>^tJw zu-MZIl9Y~wAv8b^savSdV!c&ag8>N2D}@-VgATK1rF$~!PRuu-N%DJ^={HT%k*lgqPiZ zhIhRAZTRu`zJkB_@Soxn4}JpQ`p)-pe)V=-UN#(#qRe*8uqkkXu7JmtQ!azjAzMq+ zj_Tg(U5esqaf_k*!M}k3)EJbISc`bZK0EdO8i=)-XO8~#+NJ>hm<#(Z0jgWc51LG@s8eS5KZ1!OaCr6sIp8}MzN#y zj^o@wP5LqSBaZEe-F}Dj9dPyP4%`{%`#owQv@Y*00MI+C0(u~fk%ko{b6>#$3IbVq z=pw|7Yz~yx0zbsLC@zL4`aqi|dm06w@Q^%R7)CODcA&QoROno1x-kG;lm~9e#&i(c zJjjB16YUYJi-NTPAtary5sHC)gh#;6a7e{z^JFYB2Rnw9&x-Rs8wXT=WaEr{9I2_9 zpP&{Yn}T1wy$w2w-%Cel$$!LCj?TrvPjK+;<%6M4OTDi_K?zGu0A z-2j%()B5KFV6-J+T^ApiUg7QcN4$;Ea52h9B)MeSv3SU1wPk>FjIzK+V#G}RLE%yC zffs(g2oHr(7ujFK#jAU53Zhs}L6s`KL>`lAg<^=rx?uA%wk9=T4a6kUPXur06-FY- z{R^;@7bG^PX|Y=jpLJ*Sl)WS~oMnG}9snt_rUw+uR6sQjhRKX}wUopAe@YaWf~F-sfhwG0g!`dy&bXNO*kAUIeZDe2@Gux zS*>r#2c!!$iM)|ox{N`Gz;{JIDFN|05Y}h~Oq#7l-e>zEo#P3}hu|xV5JUTYp-hd! zMV@cX1UNb90E!kr%(HFsIjnF4FED|U=7k72&NIs45`X(|{#SVIov*^jKlYz++s$vo zul(w7;k9?Y4UhluNBGL4e})IY_DvKJ9{uzmu1o&?2uJ`F zmh`9!uwi{s9+N}I*6Gg8yKAx)D0M=mf?cUN+nu3O!R6%vGl1ja68qf_bL*HG;-M(O zZeM{8TwWg0+N>Uys>Yug7l#W|^$}Tv2#h|i3yWdXMr+;)T?zTL0NiYylDzZ%Bp*qr zp$1!-ET|P7jEMj%09vP=L^1T1+7M6iS}~Q+7_4b{fil_Qe+N4853Cupa=4Asq>T^o zL>f^ei%~Qr3?){dpb;0^FFm55?WVqxgfkU3!(5hY^8QGSDD4)q5U8~w)YlH8ls2TZ zrKC{wXu?cxi7MhW$)#* z&1mUr44zy z8`^8i%u?$O1Y`$83fTd0a1c($0KhW*>I`YTMn-+gdhY-b@CuqsV$1h0Q=K}KX01A# z2+|n5+$S+{FmOh>gRCFY@n`;68* zx>n8>I#l{pW?7VRG-6MIXa#!|7W&k2hnz4=W0gjV%jFJ^{Q5d{S9B92AQaR>pc?CM z6@W8Ji}HS=igu!@Me8yyHf7g9jM7SpeG=_0-p#^pi*t^g0#(pOz;8*8i`pIUJgmop)s~_ng!t2Yk5bP zEIUL}t+-a0y{fYuY<>CdH{oo07PnkI;GWmM9AEtGf5IpJ;y>g1WyM3E`Y^7~zXkgn!`f98=031$+EpouZ8|U?vp(e=Vlei z=$%HN#)ErL8D7%61oRa!suUhnZQm@HUI|*)#OToD`$AKPcJac2m`} za3nks1;^tN*Dip=QBGzb+6+3*n7f4Kl`7dDFE5Wc_AUiM3FsFy=ED(YAeog0GYHMx zDv=jtUsadgtgdSAU5c8SqNqlzt{xU*ph61ILF*v^sP{M1j|5@@!7V$4VKqJ@ueRR|K5b7*`<6n;<$-QLfLn zcZDW2_lbA46zQvq9G~l&_A`|3mX{^$DaP6e@S_BzW`h!Pr4XU+CLE^{-dl=f8A}{8 zzAO}kcz+mq241ZDjhx@7=n;-{6%>GEafNKMIrO&Etc3TQg$A7um~70Un@~O)^I`AL zd=?zXFmu06=GVk(Zm^A?^Lo)|C^Bi|*3 zZrXwPh*Bmwj4dW@9FH^d68U;KJ&Q6zF`8Hsgj*MJ(K~ zKWuJBT3B8y;{Y4YcLh6D>D?G{rcL{4Ubp-{gSxgCMz9$7wBd7!*OfL&0kQ~$;%)a~ z%nSZu-w}aRmNJ4_mIGW`$eYnSYwmY)(B54kWa9&7#q!BjMZ@>DdRiFRO%u+}ui$_B zSHFvY{*V7(DDzM7@sBp#c77Evef8VGSMNgKUB#U*xf%Do{oQ!O8}7v`&o1!o#~#IJ zKKl?p|D~_tzBjxDzx@99VZYnq^6~%&C+6u4B3LO-rUY!QGi)mjJUHJo(n>Z0Ve#B4 zLsPzQOR+m;nA-a6)Meq6b}bWkEZfbWFAZN|P$8WQ_)q)FJi_Lc>`9)Ie=i*~iDUdX zF6)bWTy0;mqB;eRz2!9TBfa}UQON6#+%ZiRt<5+b4#2HvIL=4R+%cV<;o3D|J~Yh7 zqnx|Z2j^o$n`i8bghE|Rd$jq8!jgfh?#?mocIaK=>`{t@RLLmZeBqU%2R@kgCFue4 zPGVRhC`eV~ClR<56xOugMMJzA`&RS4N}?>Gcl0)+R#C4zg5~@kf>OLqBcK$MAu;mrCZWf-4jlC`BDI*C{$R6w1wjp9`bfes{ zz8TKd=oK&4;s0}7DXe@nIvbX0Hd8kOPeX5VPhQ;hq7(`Pe5c7`(McmJG9GAdMg^Np zXy$-nPA(d=TuLM2Qj>HB#t$qb24O7-{Po(Pxw*u>H!_o@(vXE68}mq?j}^gV!G*ZS zcsco8RP9>iiR1BrtD)z{og0GcMuaNvH%gYj_y>Y}vWoVt%k64C4`#GfR<`ES*1_9H z)}k%EMm9Fk5aCL82V^s-kQ=P~W=$AExa6~(h$ZGHS)80X6o`dbEgakdnnWSewfC$e zutV9#(!c)-*zvD@E_saEW?675uNE&IZ1YsH3|(+D$As?vc8 z7-R-3^xIg@c!comai6UPjP^!>lCx;p+LANP08-C~IkTQU83lr&)=-L~ndSn~=qKnm zGy3Iayrw1kH%|t9?}$fItmmBw;92T;_(t3oR-P=Ui(!SqhLRS3iqC{_LOPGoSe+zV+Q7;ntVD0=-vU&g!{E0%m~s!}_=dJo7pR z?|~L9o3;i0@^2&|x!_;<`LNut>xgW532w|2x8+BEcFTLQ#c#J1p=iVZ^3jfTpVqUG zW(%^ALzNK)uro)Ld7vOhxlciEcf*41=V;GDoUG5aR`rUQQmymAfddF?lp^4GJfig( zyWND|7`@K`5q5QlT7XIgtOd+kn=#K?7YGx2JAk+g-|2*QxCBkCHFY|u2+*95at6&V zN&hN*EixKm#c!C32xP-R)pl5&*Cb0F8+UfUcW>V+K@c$(g1T%p0TS zCm8T)V%ic6ndw9fW`1@QUTdouynwHrr3fIreZ_nr#%%MM`XCI>KkiL2u7X3$g&lfR!o_N?aN>c^+=|@gy|$05j*B5{&fNXX zj4z5#5*w3hGJkXL02=x%1s^rJjfAIx9T+egN(Jg}Lc0Lkd~{*LoiSC*g&j`qn8W0u zBbj5dTFRVtRU9nctJ60(FvRjHv9MkAk5X--;0!S>s{@Pdd)Ir&6xzj}%k#Tr^u`;; zHzdaEbv5GcUSZW|BMWe$$njlSK`y>8?$OX8VvgD)0q?Wy*{`2`>{+t2k;kuv~x z_Dc6`ScKEq&8yMH7-6yQwNy`&K9vG+LzyaSt(Xr-09x~*R7__(Ow)u~07l1g?$}KQ zhjxLk+0H<~JkL1JM_5$2oyEy}VhJ8k&GQJ%CD}nXa20Kj5#hp}yVOw_TL*g$43Y!z z%JWOfhK}_`iZ8q)Da6QV-9ouMIzew0n4~-*eaKEf(w6=yoI}^KYxo)`7S7CQvlu%7 zz>dC(@~XU1LmE2=uMwR~*C68{eQZibX?p0Sj^nH{ljVl#AkL=LdgmfYTL?vW-$ zNyc$`de4YO7w?Dg=`}}s{?`jHY)2AJmlt3q>}>*#z)1j1VPIAZv5i^^hIKu#8V4yy z#%w}6yck*N7_43?HVX#Qjm0oh0h(=Bbtaar_O|%J)-!o5w!{`IC(6?J?CKc<1P+#< z8v%h!m7!#~RJ47k9)d74-MP&NFanvdFq6+K4`Wyod4$1a2JLl9yjWT&Kb^DClTQtb zQ(FJIg|NyBjjJ``mG>VN%A#nWZm6;T7Nls$!{X9cY^Q0~YdBD*7y8N~KhwcU)Y*4l0D{ z>wq%9NfF_7kq^uTnc<0s{ zo$kxFJh;(kG6UPn$Q|W!0tuczpL_GW7n; z$>c|ZHK;)3xy~K;zc_8F5qzf6`yF;!VMLkNF5q3=`@KsAPVN%cQ)>aY?uXOtxvnXW ztniXCv|{vPLNsf0+Mf)NmBl*Ug2T#BI>+7SjMdWBpn0x4stQ5cP3mosJu^2sRDQhP z(T;M~VyZh(lSAfpx0AkT1`Of=n?7?kmlk6WDe`ALx1vNrI#_IK2)n_Hgo5l71sJnc)xedF%NX@|E%$cj3mbRRa|kRR zLFTf+8cY}xsVmz>t@2W{j_TYd>F?eNz1PF}iU?Do@8V&byR$vG?C{_HkN+6|>Yx4t z?D!I&`ileZyyYdh=dJHUy;^|XRowlCH{<7i`PcEr*WZa-@e_RGYmef=PktO<{>nG; z)vtdMhr_R7x7*?Ja0v_bb59u2P|-H8W7Lz#y#5OcfM{Ak*Kw|-1 zd9@l^$S}TCr)N$2(3)i@A{cPYUO0vv>W+RU`e68eXM1ZFhX(8|J#Dcx{fQoJ?TjY; zK&Vn%ljqrKzB}k>y{W;{HkXElvd9boXia`M!$lfP6yiq3=S zO`?cKLJs#xfi-f4ot{S-61>~zz!plc&Lc`S{I(^RwhYgsqMFyy_LZGUX~Xd4*|OP| z?JY}i3CjFJ`&6ov!$M~m`kg;Jg_eJs>qTaHf)8fN6gSoevLKo!ORdNN-1;UOr)d4A zd|2iWZ5)Hj~f^pY@IPT`*_w8~Fwaf!9%aO~f`& z#o`l{!M3LG{3MWs17xsP|AYxGmZYm>{8rxI`|R0KOgZQ#K(tNshna%H1t?r4Z7eg2 zr=T@PV`~xsk5vdUbzdJ)QDp*}3tc)mw*#goH7{E4D21>i#`#_{nK0zsjm)de=d`OR zg(S4;=nxWfYnb~{vXSVbvT8=Y4giR|Rty(qs_Mb!xphoCVWL*wz`+QD)?J`#<#0c+ z9g~p&pTzPK>mfek&3)wSNk&2AzCLX%;57}dKwfj9nk_^y8cBP=exBwn*$A=jO;<%N z&zFza_yTY9s<5&pK|v9U28hw1s}Q#xO%TJ(R5e6YWVqZ{Lx?Z{WSAA*q`E&&pm?g z{pbnY@{(7A$_|&!XuwhzN<4p4OCnLxEKuvq>B;9i!sdi-Bys(m&XH`n<~pKlql_2sYSx0xdA z6#T{b7`q|Rct)TOmwl;OUuS3n7xN+;uOyvRt0|-HIUaF3V?CQe&ik#}HrtCxcwX;} zV>8U4yny4K)~8+wNaM+&Zh{wKBl59r8H$O9!45PLUczmeljYHK;X%8?7~3@bU5!8H z@fvXIhM~daMk3_=78*QG@S6r~x;dH`cfIH=PfOG|8|#y2E&bVsmbm`` zw{|utAi8FnIP~6uytX^9>mo@iv`UCsJ{(vj0;I%td~HXc3Fq(fs}`Lns|kTq-XI%C zC_NqtKg>A|{jt2^q<|v;naoL@Dy*70Q2|<4a7&uLP+QeG&|FX{%aMN0Jr4UhaOU_G zaWIzH(%;42i0~T*N=p3`_*gY$GBI~y>LIkbC_jqJK8#)$oy^FpMz>|C{`1{22i}5~~ zHCcl$G&_@!5Gl`1ZK|Rwh8oRWrj@1{1y&%rRI&CSE-$d#cl=-f?%%^l?tTM4`scrk zUwF?O@#eR@6Sv)dAHMmmAK~L4|1*5+%OAsQU-Kq>`3oPyqmO(V7c=nM``&`ryy||u z;r_RxPQc}5(*=k_^8#k{%!GC_dlFpOr?t#U1PE}NibAAW&~jS8%r6aztk>3_-BK>{ z_j&D>^p@9lT>fn@TfTQm5B`j=n6FGiX{hVe_5+7Ls}Jq41@ zI$&pYJlv%ew_Zb)l^7$0pRk4uX`f^*q2QY(e!c;nXLK|%icE60;y43s?wIEp&pz`s z&ZdS^fMb_Kzi4tuAEjstZN`y1j`Ixe1_B${-)5Uz$79=71s%hTm~gqs44{igLAD!# zSgHjPGiuMCG3b6zv!pqc2&#}lBH0+%Q^qE6WJ)| zv;1XXI0L~d=fm(g(yD2YZk8}W&bpqE`5+xDw9uRWDzwoRLm)gMLZivp#?X|*67f)E zs>JYD<-Kz%(-F0=LK(6>QET`mz~*tYY+Q!Eka*5`J*Po<$-i~%quH0!yTX9GwZ~WQ zLiR*ZYIlQKv7uwZlAYKyd9uBa0gz@mQ`yE5{S@QUerLtSmixtAFHWJ@kfGM|mVl-S z!Ql`Ri=FAHdF_u@Em~})4!ANXfNF6?2`jYE(}LT(x>(R9v$qY+>D~--m%?HVkk_GK z{v3&;Qy7+6afKNA(C)YiZGOpyd4r1~?!!SbeU4{@Q3UwPUAch{WvYvVv3z+588$ZK zLTE2tZrhhy>j@rPKFo0;Ewp4~39EV6cv&Hpi1j64NEKVW0;bTN#gqyjBCUs~Ohn^Q zUC~+4NuCcoq%I}t7;d|Bpa&yv)Jbcruz0MP$+LrWW*m-3NmXmp&{+;&GvhdS5v;wV z;D|Eq!L@*U$F!S7aZj19LjH+x=_Grxl7P~=vswamA9l&R9_K~uEK~Xu&_jd)5c5v} zPBSGj0jn^M#a?ru=y!5t*SGoZW0R)@ zkS1@(LQF`(u!R#apqa5oKE}`@a9Dt0EjVb%sN45O!zu!cp3PwfP~?2WRCl=byyMxY zzJM$BS-j%5JMhpa|1;)`k6?FxH$MB|r*QG?)A-R7Pl8It&;RmY$17faC$8)ZzWKH9 zq93p0d%Zk8#!;vPamH>-QP|?)cF!qm zW4?Ff1q)gj?#+N{q($j9t5S%Jei4kr_{RoKr9O{}bDVnuG=r1395e!dr5g8&c_jiA zN(hSFTeJ`-fzVkp?3F_3QjrYI6|DS2__3rn=H5}bfl9&Ln&_Ubp|=^+1WeO}!|~Dq zqSa99eFn1>v@290C51ZX-a(BdxkKXhpvV&Bil-GP^EFt@D?0~LP~|=a+aOZ?X{4>< zb4V)l<^I-zJ>={gvrV35GUAjmO8QC)v_TfyqGv86+9<9pvm3nFsIO&MX zF2Pj-+@_?YjyOcp`748ZunsLY9B1FFrnuSX=X<^Qkf#$Loo2f#I7?Woz>HcnlMD!! z>m7*AYJ$}ywBUqb40bO7k3qbVHPaS*IP_*4Z>1arGnZS}jQf;zN-|X?&(G+Z(O!VlJ|<%X7VDNm~o- z01|1C8w1dh2n=ILMVX^PF46MMf>)9(SJj|PmYiJYB46gt zwQgohM(-?w5KMU`4-OeijOV_h{1h_)dDRsoRC9%TS$`;utbd-<}Z zU0Lus`pH8d^6%DOANHipd*+i%5V=-L^7msOx2@MX&NTzS)ZFvo(YjwjW5~~{vOGla zGxO$m>87FICVZq_OKJhl6ejC;c4Y^m9sc1z`A7Kw{@#C&>GBev_{g8*_B&pRdmnfo zE)Lf)UA+x=-}Pqv;;+6RZ+_EVpv&uc=)sTR3!nQmzWUW~!5$qvX8P9OB?fNb=A*okLTKH+69g&##xNexTb$9*J0hn5 zLDNfz;|i8Y~Q3(-mCBDJyQU69Yj z>H?TB+P1uhn=N`K?Z}`R62cRw#uSVWmbfiOp?yIf@(7HK7Q0OqG+#g`6zW6YY;!UT zK{owtxr*XG9L0?ge^^VGvBchY6?z$%Syqv-{MxuJm%nYOB=C!-Vr2lG(T!0qBriik zfm)j(=OY-zC2gAMxA2%yA{gBjC1V$ISeZgmG4#c1)nOIrE9(Y>^)Lz(quMiYX@&qu z7ALQ90KLnZAtKGYUu6xM!8w++6kHx9J>pc0*I?;jP$_aYWo|gOF0Nxh6b5&FvZmCv zVn!BfuB}JrUkjdpgzJYN<284_2DiN8HK_aB@K;{-H*vl{!>u>( z@!01-jc+{q8GQU>e}V5m@ifkFdj)zu!(ldHDd8IENEzODL;%kyuwH=wDO`t4*O38W!pFw5?^ccPUSVD#IGyB8Bcu9+0QvrXq); zsRQUmJpcvQkC&L$xOO-~LYJswns%UCyK2PS7ylP&UZs zCAAqj1#~`g)P#3vF+P&M)drx;c}NuFee7*Y(Z20#$y+061Q|>Jk76cQ991K0YRwdpS%3 zx|U6_B5l^UmEhv!QE;LfQ`kzAnm@kSp*a!g+%Y^0nA=s^!m!zRU__>*l^*lm6@-b6 zo%HaMjL{eu+YGSbM`Z|zFYJ8VDPb+^kz;6!pP?xU=m9}4mifZg#M+F-BLJcpNUXgs zwu3FXZ*cp;#wYiAdOT*yFr|mvx*vwuadx;1J2L!{9;BuSwJNN&H1s(#3m3VvQi|Ka z+%jfs8dYg+jHWEh#LRO?n~%8Z${uD7Ivfs4D@BAmgqhCVQR{?`B58GraGYm=W>l85 z7{qdDkXu(JR;-JmOekm}TMt>c-Ww(&tyKVki-e77b|Bk#!$r3CCKoKeHsXo{=Nb*= z!|X}WLqn}p5kV5%B?;^0m1$~ifs4n&>Yar#R?ptZYCQr+z}b1?MKD3m@t!4sT`Dl6 zkdJp+4FKk1>aDu8#WI@5>h0wBAwtGvGg~qNeFq2^mzOBh4*$2``9I@LuX_VN`X~Pi zuYUcj@cK8s5m)L>_|CV!i_bjxQSfoX>+X6pzWBwD;?XaD60KLf>88G0Vs}93=;oT$;?^=#O|2|mwYz}p=1Xu#l#X>1OH1qcLoRac$TiGmpGi~N; zYmL0GdMI>BD=X#MLu4%NYMrpJ0QV+go6`=Y zi6Z2GI~3aC>Nzl9T!Lp{KkYF011d}Az(^-7#y~S5xpBy;C-#d4ng9u;vB_MaS>r>t zE;5yBJfk9EK1+FLYmYcbBH6dplPk+1ds^4IDzP-p(6kKQ$=4SDFqxS7jNQ9bCUQHj zd?fzopp_7^5Q5VNXrtJT?0kgD5SFOeB768$6A-fkRP9p)osfXFyYvus`o3mqnaq~ZnA9`d#HzKeIu zdrAS|2zK5xFla1J%p#@VXxOi-?!TbxMJW`U-ihS|W9(!pVihQ*Xa)$v+~CYY1ZMZM zUO=*Hu{zReARb`+qzSX39H@=4ls&_l?hFqYrp3u6qKdCY4+4p5gV5!sThlQH8; z;I3gWEV-DM%EYEcb3p*nFSyQQ1Z6i^sy#VJ5CAt?38L&2!YfxgU0V!|*)lA1Q=VUm zuO(GNY$HZHh1M1lK$J}j(Q+?{u0kh0zLhq6HL3@OB8f=x~zYbcTl z;3OajCcahhDXrP~Esd-7s&`P4_+YalI!HA^N}jEIjN|p+!#c)btq5CCrh?guHld)+ z=D7n}YuNA4!0mwRmj|7tiq;w`bsVYT&>F4~p&c%8JY4EM(ZAMO0$D}}a!@jikxIuZ zAk32#7SjcVo6v#^Vj_%b_JTp$8prSC-v?>xb3J+IPJn!)%dP#h^kxN!2!UanAm|yZ z=ePDkwiqt|UbDs#IG^^4jJ%4GXo|YinO1&MA&jv|90g>`?=#RG-!m+Xg_&{F`5CT1 z^CW)!gHPk;TN-Y^I^zqU_#nRb-KRnI6?piOM}f|G=Gp}=4;}CR#lMON-uMQzAT~iKUioFQItmxmW=opAvER*A&+32esX%k-R&u_-Z zUD366f?vBrQHq{J6GgS4pkbDAnu0|H$&w*^t3$Gq5vz9r@_cJEjz?gsdz{q^wAL`| z`HS6Da6DXKhl2ecI6OT|+H8_gtZ6Ffv({Xx6^DMrH$K`XM@EI9jv8m>fPp#ure!A;Lo}u88V30QA@^e5ljflmd zqhnU@gjGScO9A{CfhA)Cg1*nF$DauyfDV)LX8A?5-fdu$3}>K2sdE?Rw54!oI=W_Kk6k^+!P z=EX>D2s9uiN2~Q3qWySmgWl?^`8$fkvD1Er0h8DunX%Z-KesrI%M z3sPPbk3>jU-p59ZBVW=)0*iy-cCWUq2?_)_b_F6^%6TDz=la#TGp~TuCb4Mkn4j~r zb8xTtNB`_!;`jgcKf)OmeCUs!!R@!+hWmf+{pjbn{_&+KTh(Ra8EKgXrz^!$UzHNn;WPldDDL_=*7Jn~`tQwCsy3W)U^v01=I)R^H z?g0?&9G7kt-N%b==l)yLhu#5-Gi1hxmZ`lhJj2fI7?6|-)lt~9GfQ?o-}oaR1_RU> zTo}Nl^>i5At5(78Vz}UrV48})N8Vqjf_4zAZfn;8bR6a;@`m6= zOGz<zDDmA<~wi8LeX)A zhsX9lOT}=KBB~5kb|}2Go3io5rEfjWqtj;t-$=^quy&9>$X3kYbbM#N%z(jc2|Z%h zxze2EwL2!V*w;B84Nd8LhRazw*JEcM)1FU-G;cT`1X;7;JLBu!$(nU8@_pGrA4d!E6J49`(ysB}nY2ashy(oo((ngHyxjHP2 zsW3^U%YJZB$y$$sO)X$Zx=d8Kdrg^VK~alWPmEAhTnWvHh3;EG)U{*7E@KE3S(YCd z@Jl1igxN#40YYelXr(B{n2OFrm&^~E!qG9h0|a<~hZ#xJ_H;kldO`#YAPm?9Z|mJN9a#r#6Wa6QG+@_k2JIf|Wl#kuU&`A; zaO(yl4MDIa=pS`bQ)-l3$lOJ#QzwzUQ`(|1~0e+SQ983atIsD%dwh??(_7Ih(fK~J_M57rl5Jq8w;D#>Yt4Twc2hIRGu zjRZK2R6@*PnEWNs-@b}JVC%F5qswQP3e2gW0=(khrDAUVWfhu{VTPSgSbD{LErp;9 zP>N^02x!{>WPLIKujfg)GYAc+B!AnzMMCVZ%{%OGeE_)j6QC0Ae&FZvhPS^Td{*(= z*SrRI-1btO-?YP5zwmK9{1<sR-T3}u zzi6~UC5cvOt%G*e({>jPpX$9yrYMlmst&glS=$c6;dqI|d(aw$R3Lqd!e;=e zI>;pz{)|{pn_`1$tY})iYK92R@{Kq%0v-+*+jE|*f-O1&+E|Kwa1XG&u_{JzUbwOB z&*#cYui^Ks=;nK}$Ig3_8IfTTrPEDdZpu$zw=3XZ@CIw7fcHfYaa$%W8!I8v<>vUL z9j*ZN0x@oe0n7WNGMCccTeJi>aw)kw-*Jzig8fAn!(pB`E=C)?I2orISmVs(o`XLb z1==4L+O+b*K!%eMYmj{fYgpGKkBoVA1n{idzH+PaFn+BP5nRFu-u=h+F~!gEaP zZ`@;ZnPrqLR$V4o1Z|6gXb2bI@(|LT$!8{yzT-8{@KGkP8~ zM$kRHq!dpfnC}H72e*Y!4ZvZ!QCt~Rr6-4exG)jxk12XG-|8Yvp%PF!VG@B=Sk_5| zhPc+Lk_&vZCGx-32CDI2M53I@&y|%bCE(jxrS-xdFX-3RxzI*W!3U zZ%1%v94-#9G=#Oup+r)D!?Cx4L7L!?uG%DaIb#BX)hsAfL^($TnudNAV@foD*|;c8 z!8(+{=2z|v3yYskl7&%zF+r~>;(gik6QP$8szM4<45$iiM7L9)p&9RB2OStQT4u&2bm|s-a#5e z5j-jY>EK<`Z=AO-)<5?z|Nn-Ut#2N1`pw%@zRQ4zgAfI;Gf*l`X3#Lw3z)s_Eg>yu z$p~OMqAJFLif-W?sx0}?e0>;1T>-CJeLW)~wDK~Xo~-&aQLHba_}K_E?lA+c&FEdS zHHoof0#)&rPSY7~z8N^bas}K8$GPM3AjLcj%o89yvF@&-v?J!m*pbdKGx zjF`7^_T7V9yIw*d4AxAC_8+=d8r}~J@;WLU&&{)BzjK$$j~Jgo}lt$=urb z?M{opNq}-=M~kwmeNyllIFUeFe6b;FP8mhkm{pU6gJEki^DpRn(S_}PnkxisM<=yD z8^Ex|Wr#a|G0NiJ0}~W#(Q^Sj3gImwz5TaYVfyNO3+))jz=`Ocap|8%aLMYE30LlE zsj-5&b+trW8y3?o=R}xW(y~uqOVAR(`^x|%?LD4!U>RR60Z^=6T?BT9b}i%d;&3YD zmcVH7=5)wS1-pN4&5qD1az|tMXLBv<)={-smjG~$vo!78HX3U{Sh4hoCS3J6BvlEp zBV#o3urMK6mq^RIwtTdtTsk1+$lxU6R*Y|RZyu6HQjQyq35*FHTHp=@>Jkc2racaa zr_s2f>~{bWYN@Ey-RgKY?QmtKqoW(k%+Up+R==FXQg6 zIA<&UD2~I6nZd7pZ;`@lmv#z;={W?22T%wDFIgz%buiGz7Rs{b5u3_tx<)Td;wwrU zLJKtX+1gs}e;1(XyKF7YfKlj1o+~BiRtKtr!M^;C;BDlTkEV=x>qR`e1!@`8kc9KI zJ?5vM!S}xU5!}4LhCQCbfByIX2v0w~L*2arkA3ZNTpYiGpFI6EZoTtveBgnf!|U(8 z2bb5M#@D{~bzDC4BTR(IfTQP?08z@(Y|oFvEWrC3$jg}1ybNpL3#SVi=V3ClHlvE{ z)3oN9ewGLU&gNtdSQWHEfi<2TeGc&u;cWs02o|$!+m6NbJsgnFLO=83#fam5Aw2wv zrL>5ceN$l=7FXqcH<*(EjmYLyVSyw}*Axk$0UGiH{ftNrFG6VEYu4oufG^%%*~|sI zvmK^s0#WrPq*_3e^!DP~0T&m*st{WRJj6jaP;PJ@IZSt~P^ zfK-7ts*pm0yjs|m(z{{2pi(3(&NA5~+Of7%i%vuKi;D=Bvoe_!xo?2rYKBEM-WgF5 zG~~@bQYDMo4Q9T#Gsa5&9cWwOAlU+EJmBw5#-|0Zv8xp03p~gRlh=mBM$RLDnfzN` zrohPudFH!t97?btUXv;R1tT%c8JC<0__|k%Z02)1E(I7AGdeI8S zQei&TAtA~jb}AJp9knx>2_NMG=Kit?En27~LP@MuttuQ`VC;)2D<`fcuvh{B1OLOO zP4q5!ab(ExdyroS3>H?!DxBodf6=g)5O_n}&7!|s*GV@$Mj@TYxxQ~~Kjz26M8Z0? z)lSbLPK)oi`ZvB~^nT?0%)t=GXPwdMedcnO6q{iMW`?Fl)c5GqQ~++`as|N;|J$UI z!|pB~7gkra9{SGnTTGZ_8rSixtoWG$uvow5-tg?<0=G;By*C_d!G5YBOyJ!)Koy+} z4oxb^V%meJ2~-H@wPRlx``sQBGkUFP{fO%qPvhG4XMiW4L~AojIRmo@rlOu9umN;q zg6zPf^^U6My;5TxV$JpN*RYNZImDU0Mv0XrY2>bOZ#WPDCk8rLlovF&w63g-2dS+s z58o*RM7~zSTEqHs+WGJ*VDs7M^S;O^vgTc@Jz7W)>gRXIGYlR=giHxH3$7Sqx`Z(5 zGiSR!I#>Mn|HD7Qzxfyc05|RS_~ZG2Tkp6P_uTgmoSi?5E4SZ)*S_QdyyqS7!|U#O z6?pzWzVW3;@bOQ45lmY4mgJUZ-i z8t7(NYIVOCPDaSP)aOaJVqfk9Som3bkG)=?6NtJaU5q-R5tE= zA=it`@&wxs8^AE%$I)RU7twZuX!4>-ygN$_CYh#YlWPoN8}JjUd)$cO1c*2VQizOc zY;34BW&sow@uNhmerz#zfm@_Ud>GV4^A&fuc|Hx*YyOnX^`CiNCB4&Gi_;CG> zEGFZg6?n688ppnY5_7|Mvbx$6U^6CeS8E+yiWyGi{kr+=?+Un;phGQU#+bWg5-Y4$ zcimv7G)juRhN^Y0Z5|kq5GV?T2OtN7`GB7yI20mGq{W&v=2i|Lr_J0U8BZA4*9pcK zN-3C1#csF5RQG5zaNF%KN5c-c-F64=yyMmQvp@SFT4!9j=@uNGzK-k9UVy=P^64LA zcjY#et1l7eTFn}x8C?`8*y=egXu4PfwlRLSzD;XOdiMaB3r9N2f6(|r#E*t$Y0IOq{Au6$I~EBfmg%RTi`Iw!CaYoeWxdyyd?Xtw zBD4Up=hWQfpRrM|2>sqI7G2*9;d}G(i2W^h;-#;72g-aM*RTHwZ+zgLc>P=6jpJFz zEARRZT&*{w)&suy$cON)uYLg!e&#_u@$|EJ`K#^&?Qg+s-8VY~Ljh|_1DO>Ew@yMr zNqoHm96;qCk<>e3?tZ{~nDapiQ%{Md3{2;n@=apJB|jvcL4l;c%|rxehh$^4FGO6$ znzDg&1s*Uf;wT&RWyiLL2g#~L%1fmm3_0V_8{^sQ7r3~*lrv%=w0TD3j^nZ8at6+?+=BC~ zw_|^PD{i{!Rva#YHWPN|w}AR}P`QqYDjGM;=wi@nQXH>?LA9oHN)Tg_?@gAtdys^H zQA9gLT2fil&XyoXc(fG_Wu*~fU1Q^6+|_{9)c~b?7VP(l=cDNUZK$=z9@f`qrTdf; zkgpNLB)*DaTdc0(oab=kGQ>)w6*%Y$a8lQdZE_!h;(U&U8!Vs(5hCn&OCic#G19i$ zHA%^}wg7uqV7OC?R=x%!VD1a8M99W7n@vVob3(SE+>E?jJ~Q4FMmF!dF-_NmBXlne zMebjew%O{REH08hMO%4}=;U~w@Adz;kSDT54_P3e12DoY%xEy+pAkX^JEV+Dm|H1T z4r_G}O-uMSDG9VtQdDTx(X~TH)9UZN$-I1T@y9}ZsS0- zG@OM&jB5>gmScFn*Nt4I%dte*S6%!aZFNrW$lS)pdqO(~po(G$tt3EMYe~3bHNLm^ z@LGaH(SJk!AdN9gdT=n^0J$XWZIj4gj;&QRgBn(9L)V!or;uj0mMuFvzUwjOF}-LPEz!VA7f{u_l(t z;Ba}s*_8?Zm*4qYxc9ZM$DjStzrx*j{W9Kg|C@0^SMcS>zJ!NA_95JQ%gb@s-S^_* zhaSdPzx;VzVvjey^=){~YwyQf-|{w`p8-!jHG?%9NvTBuW!GpH1I{Km{Sb1j*2*{o z5=(8hXF5zVGTy(jif$=1;Xb7L%;8a|}XH=OM&dOPC$Y!4>jI7_ys!{q^UI{>wT zx`s-Z9cBi~Zih+*$Kx@tGF-EcjQzy5&DA!K>VAT-jm5-inRSxWqRU$sMe-T;{O@T9aGLdE95fV5Z| z$&QUGe`Po~DHMRwD6pk; z$C{>ahtyZ|9)REqsdb$V;Q3mtfgx)fE_`we@@U?Gz5|2Oe1or!v;Qb%? zZM^!fSL1N)X?*<~-@>(Lf20CT0G#!Uh$I%h&uWZG`~+H$Lc?y##+sWqyZ4R>;{jW; zL$4b=-GPfKzy^v~yk`So*V_$nl4>}Vt4XM_j1;y}PMcdv!1>L0NQne{9eDKKmZ zbB5WWyMw-}p+@ZAt8zfL)3QM<);;4EvL!|E+)UnEYn-pahzC_LF!IR!{FWlIcM&Pe%G%R<13i$*l?c}po~IbtlN2F^=ZO+*UNrmP;F&P``Was@SfMDT zOh)}fMN2S(u^&b~}3Eb_RqCxRllT--q?kod>_IakT`ME0~pZ5+49fh8dCi4i>WeU9!e|RTHN{!37HZPTrCp_>JcEqVLsye z;egJ-ZaTx&D_8LBGtb~~c^%**u3diyz0J7!$~lg6!}W`6=%~1PH-Wi(Av?2Bb!N?8 zmBQYLRG|twF3P}iRbOLpgo3m+H8g~R!5MvV>^q+?tZ7uh+Gy1kp%X$rTdd_F#7|z( z`-<7078+Xu$TB76^*d$RIw7}U#*t8>mb`*NHH0gtK5A|<54&MN3t&2z^l_t@^hjUY z*i2m@(lR&_QYrrr;Cp^{j^2QO_&@w({QkfB=eT*=iBT^ufiel71vRwJn0Dv5XxG3^ z>XTU7+i6FrQvn^!<7{5A62KIC?#|_vt#eOR$BX#c)L2zO=-dD>3U`f>H}J1Pc-%$Z zAW3)+v`{T{PAmx#lrM4bm}v5-An{g|kZ-m!+Q%j6kB~LEa^4dSyFo`6gMi^Xr_Kc< z8@wFJyRoUfXOK!p6LBctxF6?t!+5^d^083O^c^W)%r&OO+bD_6hO4?l>9KKnUb96RoR(|hom*F1m@5$x=s$dC>o zAy*2Le15k9N+=Ji^p-Ki!})r;f9o38%bv`kAM5s(pzT!K@ebo3=3JuR>`Z4eiMnvrZ za4VxKBjFhfIitP?xhEBDkUqn?KasYPiV7Azvsj7H^c>I9=p*91tNbEt&1HUOZTJk6 zOFk5D_xSH1@lOp=O9WFyfz0P@gzyOy-Z6&AcRP5lMi_gYJ#Wgnt)K1;00pf>6Ah;k zGdxt-XnTEv_QhQp=-1n48h8rNg_P zOR#M6>@1*0U<(BEl%1*|dZVqdPOaUDtG?MxkT91r71Uj|d{tO?eFQQ~;(xAjXFwFh znk~)S*Elchxwh{;gK0qpdkm7XLKrdXwau8ffG2@?9B6)YCb>QNLJQXxNIAzOh! zT5O5g1O;$7%;nOa05(k8#*BFo!{mS$z zmbw;BU6Ek`id5<4uxc95wKFqfX@)D_hvSSZ=X?Cu|Hr?9yKcV|pZfUkv-!M z-+~!u`1nH);xm8oL7Yuj@yb^|fQKLaFuwJzui=iD-;LMZ`)0iIRjxkyc+Zdsq$EfgJleiIN7fTWZn-m{*7337*>0 z`ap^13X!A*ZP5A>ViYWt>rGhS3B9p~5ee7@%fabVE2tLCjZtgCq{y47qSgu11nhPa zp>RAj^qFxy0Q1b4+Y#r}4!sfD{4~IXsZO}Oyaq-`)x*xFFj(y;WwhGAIA|?%rTt9> z@9ghp$zOU$gVs7G^IhBgE8jPHq>I9$O*md+kQ<|HRf!1I!mD}2B_gX9a~yvgX_*FdbaGxmZ#&nt`MHv-D{ei2f&s|LsN<9@I{$G$08k{91&vpOA67sVAz`3$5SxmGhRidhfLqkwV&^E-exq({>S*-R@-`8+7kX4=XcpG5F_Gj#WJTVLaITb_Zomg zQP7aI4()Jc8zToc-+k;G_`)N91U~)<-~YxVc=!{4f{S*J*6+Z#zV%~VzxHKZUKZT_ z!29vOU;XQN=^Z!W`t=KZ^(&9z+mC$(54_=(v1he6J!MtnJV4KE65|{)rRjH#*p&%u zFGBSKm}Cp{nm3L{nHwsb!}Gy&#?=ho)O%Os3a`hq;JstJLeu%oM84O5OMTA$4Bo}T z4|T)JIhLkUMN&MEdoG!QOv%zlP04XMLZ;No3%Ua%L9Uj{*EJjOV|b+PSpuO<6OOIP zIfBko0Io|&>3+AzRDj+Z<}UngDpgnk5h_)1FF3XtWh&TV!sX=!X4o2Zr*Y;e=rt<< zCZvAq<{2_5>NvWbghnY7jvVK&nHr=>pjnY^`q)E8EoG?A0DL<2;4lH|{na)*TdiKf zI0W||0~5nj4Fgy_ts%P| z=4&uwEQHUyO<|&4FP;^15cGNa8gL>K{u|c9 z=2T6^ofM;vAW;l>`T4&1K59D2yvd#wKCa#gG6l)Go!np4SONC4ctjlGh$MFp{Emem zcg=2RYx^Sgi5IR=7@77Hy~vuGgDBmoyCP{*P1CQXNMSE#f5Jp{C@iL-nk8q8@niUG z`FHjp*k@acfR8Z=gQXB!wx7eCPJw*+F4CDv2!m*f1`5Xc0)UA5wI-iWU$?#=|GsfQ zZA@nMUkWt4hTNdyl@6wo z9Ngj#iNV$WxW%5~8BfG%FI8n_>6PqLLWSsN=W9`qrZaa?r zK?guZ+Wi74NgV*v}_FMlUesbGV zTg%;2%Ppy@T5^?CGgV}9p2;FOPaumEK!O17z27-IGyD+oi;Udoe3vA^T?O9rowJAB znUN8{m^b@t+RGU0b}ODZ80!3YjBh^q_`7s)?#PbF-?m{?CoPLftPcgrDtcw_N8Z;= z=v*O)gjmh@@?q2B9x^R_0j^J^^SeT-jr;6uhZ`3S|NZ~)ukmMp^!r$};i;#8f;(Pt z7an}wyKviW&&Qq5e<>b*=da?w{9pV%+;`8*@!iKhia-C;-@zCD>Wlcv&5k#}?biVX zmRhl{E$F+5CCjqXR0vd6904Yu_q@jR#vIKnn8Z&W;^7fnAY?N*)(r$@mjep}(Y8jp zbM6r@-K;b3*PY+x@e;?D81hU9yxUgBWN+6gS!Bzs`EGtMug2pbTc-^QuoBpGsA7s& zN%H|$LLmiR8idM)&u&wNcyAN>E+cFO0E+SBbzQM5#XJq&dgRnn-M~^%D~!iV#j@;h zzB|YH8Bl76NJYVdiellbSaE181jU=d6g)BABou2Z?rnh*4|Nf#jEV8H>K>phQ5|g;|l=qImqXIrbTI%Uuwo`9VUUw@-7ciDjb+S-7h7Y_2cMjyKaAPh z_thWM6%MRfZlAKw4H!QK>+raY7f_b-oMD(4^J71D9LXgh&1*?}lld+_2eQRKb5+ZX zi;-Qbe{^%gc`K#?sb6Hs7Xm<`AKu z<^j1(sk+rBI`!Kz_$impO&*v3m~`$T)bqSI93LB7aR5)2lv5T!pPpxQpw8gfQJ9R( z=%{0D&0Mur5|A-aRTtZU+U&dHV7iMv(=1G4{8A9is<&?Ow9c?8xH8~5?nsVA!v^}S z&5Da%QDA|W5ccxO=n!2|cNHiF9hPqdk52{^OJzkYtC%7HRsh=FO|OWMEjr`1H=vd% zqfFc^$kw5;7OI|8TU43fLp1L)4fbDpOVngAYL<=7m;FQ2rp*oU8 zZJtXESjFq$+Z}B_y7d5fK@VZC-OO)I1z_l0Nj=Rnl z9IiiszkKhX(0cvmkU z8IzQGe+JuwMi{!5D>ucvOXf=HiN+GF>@hNc83#KYXPGmW$ilEl!*e6LW2GR!t1Z#s zWm~?QtA>r|=>*d93E>Gbvd znX!Pbc0a-x=0b)w)+#m@_h_6g3oeR>we*ht;b45uGnQ*vAgx1N$HjVqwE^ovvGxOY z#rrpPg`#;_ka?s{G4YBxP8q{!R8WdKosCeN<^=<|mca+Ss+CQv{|g7uT_e1VN)iIRBrhWv0u?OHl!|K}v$+l@@4Nkzm&#xOU}SNk}mc z*F#7fAl4xhH{-)FqIK(L&Yn!j{8?iRq1;PSQv|40e5l!2N6*vg3Gd&;S>CBPUfnTL5N z5V;`SX{_rqj_zST$Cb2x`f)2y#r7ReUt{E$-|y52GDw7ohj3*fl&p-Kd)eQEA!V-y zx>(G$uQLTU86G!Z76Q{dXm$&ijMl(pY%f5FLd6ZHv7>tUjZ!cxs_Y=@!Wk)njLSz_ z*b04}yX6i`L62m&>TcOX>o`{fcMwa|Z|ec;+OT%TZUL6M;BYwL>Xj>~6>iBA9M(OS zY6_0UIu{&zgBBCCrW7=2%te}OTj}^*y(yIV%UQmoM2++vaY7sqFHxXpYz@o(Qkq1WK)?XlduOp%9!7t##9FTpEfscn4zrAdik{r)3@Ka6lEDk;Yb!D!q$1ohVNG+8^9 zUVsz71EH9RB)}CQej|5896)Zvs~miiZqj`QEofT9{CPfF;M@?~qk0-}MfYz{LHIF9*b=jdV z1^s4&YRB4v{ml!A!a_7FP#2&bEQ_HDfJJb23XMV@q*e1UB;!vnR zUbnvHw6>lFs69J!1l%HBWt2iKKs2Vy;P4cPA~;MVK<1oOCJp?~gCC89)Yoyl*~c0d z0tp#S`BW{QvwOuHVlPnzJ-BdToY*o{R{%RmVQ-WWJII!KCa>iVyW;O}c8aubqpTtY zOO1C}>JDpL%_Gt+yr4DU=^HnpQqbD!-jyEt73>h-RIIU$Wm}`e62PPY73TU^O>yWj zW^Yxv07wHn#4@2N|Jn3&%5G3JTF`!c?ROTHiXN6?g} z-%oi!PAM5Z#x(#OBIOc8x!Ncb+vglB-N(tcHdIrpnV_y65I2mCMrj6$xL@r(b)-vR z2n~@@eichfdaM;^XFELlnJ?kb|M>rd_RM$jov-il>5sl2yQ?q5^?nx~|KU$?ID8Kc zSMcfwehu&Xm8*E!i|)pgKYj|I`@$FTy>EXV`pBzMmIZw^{@J;Z3X3r&2oqNDGq6bu zoTrI!(6I*@2fVGKXM1Zl#EkjPLY?brpQ9-re@m=pC;`I@KJ#-y#|%&Xa{PWE3^UC5j>wga1Qd_ZHPsO7lf-S<^f{wBNCjEJr`IaG>M@{ zrZ6N6ckGj)tk1mBWxk9&5LqM$y`;|o&!JN7_woG@`p&qsS2iTBlDM|RWOVmZz!VT| z?WhopqA%t#g9GdykU{Bo47!G#HrSrx(^iNCK}&HD9LtLNaF15&_PaGXg3ap>(A4&UQQWe$!mIfL9c;XBH^X8!lF$${Ehi&T-?qprPRA z#U8yMz+wgmUD42?eRYQIV8QcFx;8IYnrR0hA(v7B3T4= z#Gh|s<86Fj^Qy4hT#eT7;cd(_=u<(i7+A%Ot9Me zR(LGC!mI9aiDfJlE2m9#B~BZOpXX}@2*WUE}<<@)33+`L; z{7o6!nbzmO$xFJpEZ8)(+@4FUHkbV$+fKep-cRef&Z^6eqvq9uXPFCV!z);I*kuY3RDzOI2~t#F!I{`34}&g0G;Z zB{y3ei2(jW>gn zOdwO-y3Z^WxsQ(npBca_CaRt8nH#**V+}ZUMaZCPQ3E=x+ zIU?yE19Y+DDCew{f^}UDd<`ibOdl8qP;NRtMa0_>6@hdQCn_CH1ki%&^|ZB{Qc$(~ z8gu^KD(FO9eN8wcF<(@8Vaeel1!v2G{c5YC+Oa=$EW5><&s8I^4hwTCR;}sK_Gs=& zFk!y>+KXMIESamQQn7YP?>gqyXsU!DwXmAu;p^H_+|e^LD6Rv6*r0y^$?}unTL#!^ zziARaF|ME(WrQ=J8eW17;*2hhZccdswx1`SKv0_+#Hp_{$C7<&=24>eMSNJp>K>L$ zk*GaCAG1m}8ERxTZ!pb`i#?dVSR*RYERDtU6Nc1aS$~fM&;bPt6 zrLTAhZ~vvgkEfsb4%VBG<6ZCiHN5Knx8dem@cfrNA6J%wdhyKa><`ekVp$fH zS}mEnE+}P>-LgOhxY&1;Qn6~o&GiDt#u}t37Ol7`W`(V_qW1$1y<_c>i3pJes$Li% zAt!=u@p7ytCl%VcZ+q~O0!A_jR2HZnP`W7&MVlqGDuNKrBcrkAq88&XrJx<$i>#xT zpczFwtckBz3G`6tf@3dNtcxpcEN~J$HJ=?|GkM!`E_RMJg6%T_yqRft8PExAk~d-) z#}s34Zh>oTA&k(&;>0L?O^ziYTC?4`;h?yP)`1Tc-rGqGOD?sb*NO%p%K3`I;O+2E z9pS{7TS?=X`A3##!K1m?HrK@3OmcBHOoK)rKZg#xEu*M10iOrA_x?dZFT;((Dy!SYz516wnhirMhg z=43+*6O&IHERT*}4npfw%From=TFaH5D1fVQ&ONRy=xML!G<{M2|7{)wbK1WJ$!zT z%f@)>?<_30f0M4F0unbG=O+oD*tGltKu!?1P%U~T;DBlf@i=cY4qEV!nGeAf{5%Zi zuLwnj2}E}KHZ0BV+q+von30HV1)h@)%}NnLbp9MmdeUMUZ^nE~G1T6mWwG+OMF6Ku z^>+{*b>JDZIRNkgO`~z5#vWv2;M(bV6R*$t9qVi} z9P8!uy#;_-xusRc6dMT|n`nmDC7>N^yB$ls=Xll()`qaoG`T(IU_BXN1*3Fd>?uCW zD=*~&OT2!RP95V6%XH)oWkFz!k>EAF55hU}P&F42Z1^Oo4wS5vXjf0nceCZi#Q|5Y zEcmbg$v?m=UiKn<=zYI~x4ro{@T!;Ji=X_c;fvpR44?kMA7iOk@ST_5gU@{WPw=BB zp1{jr{V?u*<*RY~oiD~q?s=KZ>0umcPSX&xHkuV!CnF4v1>8p=D7eO5raJq8#piQE znb03#`ve~!JUp+T(U|FxYJCGT-NyZfV(pW>Sw@MTN|HhY8^-414!&Kd38g{z$63c&8qZJ zdqX(!u|&wLz@P6%4FDE-#n3lRG`qoIKEJG3XTzEKDQR$o9#6NxXdLb#UZtR|J&cc* zlc1z%=Ygd89Kwj{#-^+cT4A^mjcrQ4<@7Ks4)gj<7)Xf!ypdnXM2x+~WtkKZq-V@K z-WmlaUZ+lLPIZ{pK&E@QcV`5o9BZd*1)cXcg!2lnkpeei+Cedi&fQ9(3-if#hK}t z2>@zUBf6NvNFic#J+B27h5}eATE9=kr#oOWpveDh?5wUL^dEOvPm0Bucs}&**weGOM6q4OiQN+quT9W3lhI6I{Fi#M~K~?V^wbrrT&Hx8} zk2T51V5;#HL>XD1AJSKmM7gB5jbgkNOi-Y=AuxvJ9*|_6;ke;~_7t!i$`vl%=Ubj} z`M%&-H`m0s8R+UrL{8;jC0!f(h89~pwLN#>ko)X$MC_aq4kgu)5$R1KfL07|3Iddz z?RNOmqhG`a-uFAW{^WP?>5n{tKmP9T;;xt6hvlx9;j!<153=v5<#xR39lwQ_zUUh6 zxON3k{^Tiq?ytUxM?d#TJov!N3{+cc(+c9A!!Cqqg?kPI|r1= z8!Vt&0*UH3HY11Az@&lcu%uH&1pnvX8Wo=;8J(!OT8|hBz{rGGR%JqQBZyMAOOlNO z)dtam<@^j+&d;Nop54Eq$_}cQ5WXz1(5JrQwkub$Q$=5azADfgv?zAV8QR57TpVsf zAy~WzQdetE8VSN_KG_q3d7T5P7?R0!p<19+1OPK8L7-)T?_fB-=1gw4^O!k`Q4GPu zM$J3Y{641q1O`|euv;A0j`3w2z>@QPF)x8&3Ok&~dwyO3&Imae$|tjOgZ4Tg>>Q?Q zVPe8L>+YTLqucO$Q*;$8PwpPw%BVVxTiFwRO$z7a814jZ8cCC0hrIDjodL*|p0}JE zArNUnd!wk|0Q4BQjrk4!8J@fm<|{%fn(<$VkEAGUL*{`JJb;py@0XQ|h$9I(TjKr* z`4zi9ln!+yZFJy}Kd6tHbFB&Z=690m&n>W`Wb}>g0={#T_%&oy;q4KQ zD1}pD0Nbd<;Ln{DC-vO9cnO4(!gCGC3VQdX_l|J?(XzwNxZPuaa|MuDjIZb-t|+-+ zD5<-4)ZliG$!8~^l;^O9qLCD3O&+?|tQwP;)lZ6|>Vma3W27QjJ0MlCEDKt-_~o{) zXzOZ#t2M99u4qupr4UnC%zk6xSzWs+8Ir>D09ewZb+0w+)Bs>+)fvfSW512&RVNj0 z%=?9(1PCSw8NfC?T}d$`XzLJn$-_Wc3fdLG%vS_T&M3RiN^FEJg83!0)t1Mt+`p%c z4f*?9U*k~5LsC=;;*QUyJe3L$CN?ho?8DSY%pe~Qn4^{cpkQSr=^*Byix^Nhg4R(e}Ok0=yJbi!Ejh-pv!S}?+y zZ0%eDsPx3>2y8khv9;u!o<{-pgkX%JDlv9O#zl7w%kX7O10Q@mpoi* zzCjf1aDGF}{kk3?DmcG-1!Y-0RE_|+K@SaG6jCcL4%Tm{04Q~bS`N^@dhL8cDGSbb zJ5!9bMYr~m_~x`wh$+mw8lsHKp#oAQlEHhg!KW?2-@>X`mX~dLtGZX8H}O0R@;75g zEQm16hsl_Df_Aw$TV*(uT}OvS+2GSG{3<#b`f>pi5dpdxW(?)qc-opm^e_yhsspDL zaoOtQ7WbxepD_=kxbj0hz^$d{rvXzW>9M?(?5{@9mK}L1sKG9>u@D%d-HdLxNc>IJoO#F=Y0rNvNjolD;{Y76JbL>Em^P~gEQwG z;?^G}63&8xjy3za@{+U`AwRsS$xw*k#Ff`>fH@n>aD~r1os7n=9~Lczq8+0J!(P<%v=aAuxPE@ImIn*@JeGT*8zzJw6hwB21B+DPbM#5 z2^jJ6Z%)2o4ejWHO`w|kGH}YPNVpg$OJp~~=qz(vJNDhI(Fa{|d##>BQESVDm*9bK*KbysU7sgUp)Ys|r*>F%|wYj+@_;ynunkRNW_K_9A& zlbz|;);0llXZRE_46D-kMy~R;3{VNYgXw=dw~=ZqJal{2o(o_9kN9usLu!C zH0Yhvi-X3CcEEiPz8SytYyTVk@Y`QR*8|@8&UfI!*FKDPbB8-#a3`)^UGT%l9>b@< z{yBW*i=V-#AN?ZsUGSE-{s!Lo@LMtJr2G7MH&$AWGj9z(9xQI=J=-Kv^ohb1!y!4ZD@Q)$!h8@RROW z@cli{4j6V$u7l?!bRfcaTI*P?HuggX|6Ds;#>m}r z;NHDd{ScSxMhgF~YhVSEOmFIQh@rfIN{hXmNbztdQ{*~K>8#%UoAlw;MNF9sk{9RG z9^yn^2;P2R8P^4E*eGaSu`BQptU^H?nvZYV=*B z>=g=@Hrp)J%-l4H+$=W^2#X~>RS256rvkv*Q-D-T%QcwNruQ ze;2QQ=}YjT_x@YF@lE&R+6!KYzy9kVv zR>pc+&H*V{DlCLaIx0HOc4s)-0lT8OSTCU6dr^3I(*5DWLPk*_vOuJwlof|{ggV;u zy)TEFoE!ZW$tOFYmM~q5?L+3avqZCaIjHJJrXp$;twf{0l&Y=i&intcxnt>v5&! zEY%Won|2i0jrA@CId0{Y2W>2SWmFZS3MlFNcoGJK_21mtLtTfBWJ__r z1VxLO8-yoI8tL6{-KJGP+vYLiKA{p}!&SG&+d!CuWl;deH(OUMOToGZ;^c(E zLFeF)82jZiZ#sV)a%d#_Z;#RVcg*GN?d}`T#yHoWK^{`lZweYrhm0L?ie4`vPIKk7 z5F<(Po*`+RS=a*8Z`1deyywq)JbQ&gT(Y0Q*ui8W5u!q9&Zaj+2)2=^r2=fm1X!s` z6}rXJbA2Hkn)>97?D@+GCNtDrt?Bp$D46WGYz5ff_vB{`fK9+)n1tsRw}RRuYJiyR zmX{51O0FtZ$FK>!aOz;h;}M0Rw}3tLl=l*N*-$~D<{bG&JVgI^J(5Jzd6yi31p(S}Ft`@)xGid-v(_yF_SeM!EWw%4Q5L(fy zOaE>I))CM=8G0ZiDIP#wDt7e@pZUaR@rQr#FR*|52l&WeeiJ`@{7KyP!uxRTMfc$8 z8x24F{+Dnlx8W_n_V@6bS3DoPs`$=#zJpJH<`cN@GY{bPufNy#r;pzqCMI4H^WH(A zJrhh;(qfIZL$JhtMu$nSkd!(U2d?(}!)iV@nC}-9C3IT_Mm0~|XFTURgARC2@?OnwcA5$(AY(8f%>V!+V=DgXA zl;mS2$~fCxT0(QkD43F=#n-!tfzXf-W4(Q%yn+vnu{i396|0`sCyX1cO6TuLc_r}M zM~tcS$Q;_^??@%f=p)aULX~x=$M-ZiRhDD3?FxAKZ}IsZU>RiF&UdYgyR)bmUsqt& z4((2N#kgf#hv#K&w)YNgwc-x}!jbj>LXqejGyE3AU zPz@R;cPpjf007zmg(bdBM#l~qr>t-rqLKn94Y1q z`ZH^F9|@i?3sNCK^bOqrbTrSMSZt#gf%fJWHUd;%7tkvvfX7eLf@I{i@c*2vo!b8@XP_&H$iLNO5>`cwL|1e6#qXe`$YG8n4M6qKhi(}dd2Ln1%N%ZK-aRX`3Yr9+aJ41ocTfe!wW(i69DQ7GVN3z-5tbqK zZmWKoxzvqvOrRa_TlW|n@4@jgfhj?R?Mw>67|MooZ*P`3RN_HY+@rc(5pPsDTh3=b zJ9{D2-**1 z@4pXs-~B>-?}ty}m9KgTkG%EGc;~y`hP%(t@%fMa8Q%XFe~vGG<(oLHJ7}v|JD^3- zR{_Mj%BwdjWm!=%4Re{?HB(RovJE`?Wy}usgpR zV_3Uw*lSnMakJjQzBfRM^%(NK1b=6UK&AWspm)LdOvpkHv59FL^wI$j*>U+?3f6u= z0Cb-lmdUjdaIS1#S_8_?E&I(a`hxHV8JDHe&*cYPF1fMbB!E4lKVLJx&tM?)c_OnH z_XI6!$S<1@?wo?u;L&HYH}VxT>d-LUwL=2T+A4z!r{@KGezn{1~yi9?}b2$tJqeS6&n2n3=rGavc0L0N~^SaCRB&zVFs(G!T)CL)4!^E!RRx)q@6xxwy!ltHdjJm!fTNoGD6&ZDA0+xp3n zSb`q^HsAVK7-wz!^f@Qi&IOnO7A1lN_l3V@G4Fg#&vOV)0@+o zHLUdlEIUg!bYP}r@RF2FLV#vKuT&SZz89ImX&}5pM9_jw(xe1c^l2X= z{Q{6~^V+&(b?|$Nk40r4f?x_lpRW)E*^)Lim<>iWOQ9{Y&pQ;yy=AfhhT~d|^$PmW zs4hJt#w5I4Vl1wc%;(4F0eE@S0!zg2!g8Pp3+YfuoR?0(2+(eG*1OHGg{Cb&WQKlu z9drO~zenpGzw#@82X|e4F&_KoU*TeX25)%hTkz($ydF>dNbtoke-(Q16u$nckKoV0 z@JW2`uRe)KzxFk#T*G_b^IzeO55Lutf;(;6Ll`@kG{Y|=C4F~WU(R>+au-yk|Jd0_vakv???5}9%C8v z>;JlppaM94H$1?vVh_c0IN3fQk#E7@aJ2R`kN}9H3da8|r^I-lKqxPe9zk4r8Lx^h za4YP3>*E*NhhhkK^{mO^qEQ#%0=n^S^1BCDiZTJGWArv4IOhf$3qG{D<1wAGR^{LL zT~-oGnHO>E3cHneVi27k{XT2K{NB2inv1qY@)kE`cz1G**Q0lwa7WU2ld#gU@lPED z;p0Avb?iJ)kn&j?*BL|QXJ7_m+s{-!#+sdGhTHjyXZav$)u)Sa{ATA;psE9rS_*14 zMkSp<1V~Zs#(-eu{fam{V=`W(=vKFSD-_1yU7ZcOoR~L3r(3HOq#+$+mKs976c33g z_T1JRmc^9jS{JJ{r8bgX9eG3|mgK%$7VPSRsz9j~CU(kJT4mIS zC%smvS9r3$(?Zb2x;ul>)*!V84+9XD!r9SQSshZWn$=Lo#+pm!LGi5QDah-a1%5uC z3Q#Z@iIdn|^E98EfvXT4^0#zm{8aS+T2mPq$QdiK~3iOxg0b#F}|#ADe> zz8W$1n-h%hkG=~c;t8<`ATxlS&$H8M@oReT76;!gX}WiyNX4=gp!X*d~_jl`%T8#(vj_Wl zN&~!SS`JDC;Gtxw2?Quc_|S%S(;V@eF_$=!C+4ZqcxWh0NlpxfU*a91<9ZvCExH80 z)bt;fi=hDJP$&DlPrPKvu;GQv=UE8%Fhm5`VHjJQf$h=Dag@Q&dpsM3VvI>o4^9C@ zsW#Bni2#JPc=<~hh+3OKoar?2#4->05ZP`Z**@ur5aQQ~*$(EP&!r%;a}=2opSpKg z(URpq18&)zJ3p7q+J0xa3>!M-O=K~ZUQ_4<$d1n)-qglQx@BbGQ*2CT`!;1kq>pfAhTh2mBDYA=k zim>8Dve5eYt&FaLrUi=Cuc;~+LAV4T1MUR7OtfGjE_Qo*H=?$;5Y$Q#m-?a$GH@I8 z#$&6T#`ka#9h8tP;~_c7i|iVP;1tZB$qXt-ev(dtCLdcs%6-Ay1sYq11u|Q^nIAjK z4)aCDfnfI#Iw=Ai9B2+=$58SqMw~Vatjbe2&|vvW#X(K;va*6@x5HA;@!t1-48Qm9 z{-3~&AK_2F|7AS=)ODQQ`AYQV`M7zp$5T6-2R62JDh|4(?)9RfLA;D=BA5MTJh z$MLFrUy1wf`@5)1wNl2g#8c^mZ|PVD01UCPyaovKkVdFlvA7doETcDDb8j}86fmEF zScj;|?(#e*?ZJ2A%p1hQVi8bMv9w^Yb|w;KX>j z_5}jVamq({J2>|??HF(`$*`@K;UgmNb?Icvgc~cQh=;+t)fpqq&ZbGCr04*0mghqb z-1`bC!0zlEJE>u0*{>HU)#WB-0~AYLAhMvMptlRGT~X_TE4y=?@6OWP$WZE>HsJ3c zO1`r&cgFhs^Sb7x-a8;Y61qh!$IT4%*8c*96#`9;t!SvaU=cBq16RPh8+O$4;i@v! zR-8U&_LBK3*K@nRT&FGW*c$C}&_PIHO@KM^x^d3@oG1_MC22=!w1O4GSlUVBmpmwc46HBe=&I4gC-c#!tH z6iC;jIh?mxnSPriFHQyHk~b&>i;a&;P3i{RT2Jbk&5_A=U*|G&9xmB7NATx8o|A{- z(r3#hKdJ`tRai)dRRa+VJ5h~_gW?P?wu%ybX@Ca0qxt*MFivc$W0#gOV%7@I3tmd` zn%+2ZjXy&HcFm`lQByoQ}l1s?hYAVSVG z9tvY35@Of@uLB(4uPikfvr53Jygoz`28bUU&Z2+DeDS)@YCyh8MQ>3ivlLKoNfMuDvirgz6x7yGwZ-a_l5v-!DJ&}}2Lu%}&Md6`o?@FFN(pjRZ znB(-hB|vq$@(eE@WsC=H8tt*q2al~WVY93Nj>Dhgv%RA8oO zrfY%FONZc0bC-c1>7P9;Ja)yci^E$=#YnpgJ_@@em@SpY6ZWCFa<;=WH!tuz|MP#q zfBuUP;jSxZ*zcdh1NXlUZ@B*zc;acni|&08uYbeq@$O&#o4E5F_{QhnhYx=EFYv{0 zehb(41=oN1;NA%@@Zc{Y1t_X$jbCJ?$!N-Ars=BZygFPiHvd+v&FR6bx7mC30I#P6 zJ?|GFWdpj#?2aSWAcHa;3Mw3koibMSL_Z=P#+WJ&PR1s?IOH3ZiQuW}{xb|n3X_tT zIH)S~nE0$v(p|fX{Tje*j3GjOIbX38%b3TILta`{%(}0>Ppp}tjk#fkNR8@y*cCK% zwBE6LHM;$Jz`E{D`RyI&%MR_Z_fp_C?$Q-yXO;O5hdruPtYV##Eo|z5t!JLHWGWLp z1beJLy%XGVQUqOWjDmt*6lfh1Idp!FD&XIdP84-i{Bz$8pjg-D9mbsx7Rz_5lJh8h z{ybSddmgrYEc2*qOp!cnaGPlsta<@my7YK$D3v%?f#jY+`o-gDKJ;@8a1=yrZ!?7)-T@r zcymh#XC^?i>QYe&@WKXi+7v4q7J>IHaAs$AVJm-!4#>`XNqECJSbhIet+$4V3xx!x zZF~g29ph!pN@vQKqEw5?-M0(u&Wh87;L7<`R8a^T1O;mYT3b=;f{SiV=z3Gsxc;wYY5DFT{SxfcG9mdeAf7Z<&8;7S_%V7QjiG4!;-k}j% zXbILmfK*r!GoYsB<-|cI%+GcWJwQW%VM8Cs*1dF}P3-u#*0WX7ySBreiL_H zdnx|vvwsOG2fX!{-ilv(*CY7ucRK#_2d)E$3w-y{&)~yf{}ev=xew#ZU-=rAt9Rky zN8W=6Uh{e=;MVIB_3uMK0#SB4*~cw>!SiJz$vnUTfA<0hK5G`D*kSE_E1)fuugrhB zR&rwf{YPYj)v4Y^1opQA5^<`p8P|c&=kCb8S2$J$M&%!BVSWykpRlxMrKl9^*#v^n zNx}ixD8`hH&X|HdPsawhCLjV>K9BKgFAb`K-o=~WbqmpHMqe86SVtvC-paket$D84 zs+I@Qt&!&<)~BW`aM-WtDqR&X57EZz+X_KDaag7nAa`5 zkhueJhj;1LSU()i1!Wue0eQM*$!~L1w)1rA^YL-oIAI&1r}>!2qpo0tyn;9;@TBfC zuufhB&Y5`m^T4G6h%8*NMnXKtFL7eNhAidC)B7xrU#LO>48l}L5iwx@T|^ATf&x4# z3l6Y53``PyDs*V`v0%AN_R#EjL|Bpn!@@0K*{(@9NlY#P0JUv0ZZcJu(qqJua{Yit@pqJ z73*9KUD0LafV5_cm$q(CC=G{xz_Rm@02gSbRIlH?+B|lPH||(aYPEM%SI!b<;QL!O zr)qJ|ZV6}eBJSyY@Nb)iK5pnlr3$aM0^;u^Rp*V{*} z8g=3`_#8rn%5NP)+C!dLxe5a8_j}xa+wJ)Kf9IXJZE2AH4g8IFzZKY^91&KIiY zIql$JQ>X-2O~$$n{%b!LDi(`_4|pXrR@M;@+1!Atv^4RY?Xd*}3{{E-rKWiX9R$r} z9?f?E%#bCcd}{L!(9`?Mpp6O4!OMJOC)7vTI%H%EEp6hrhikh6- zJize2!FZGkJapELN=ch!$S3mqIYHYs_MCxg5cCEPK{0r@0MYg&&Ev#NVoz{Bvy3>N z+wm+j+?<-LvENGidti|*9<{$2h?Mhg$G)X^fq6{ZHHk4sK?SmJ2@kE^&+;&b@$&Y3sD16*0iRVg5E5VstC-g&@o&u6q^42Y;_Oh zMa zd~Jb#!bAC>t`s1xp~PI)I@qODyH>hlDV5S3%UQ+#V#UQlv3e4>04QYv;k|hi zY{JfV3wpn4%BotZ!T!(!46VLqMbr{o8~ZXy(2t0q!#dU$sn#jAS{0co5j|#TUPTd{ zq~GwEbp^wLVs3q{MPvZDOeZI~4OxWIzWDy{0(0kLB8%f|Ls*VIU`?-PYasVp&Y8+% zAxtN~a=6y}xl{k=hd~ab?p~4uId^|)CMbTS@m51)JKD@s&7ibb-{v0>j zR1pJcbwR~~KmNn_;otmE|1C;;2EYIPFXP7b6=%1<9=kj4!S1%(@z`HKhUJcz;GKW# zAK;ZQz7rQu{|Mjw#y9YxkNg>4@yh$~x;MNUyIQcW4)29yfQNvjy&?1ik^xfifeg`4 zqkpxyEHPU6@sf*uc1J2cI67n&UmjnGdk?|*dK1L z23+W^MLv|x8$#CQH6)a`IU+hOP^^$DkUqk=%rk9tx{A}UR{}%2n=rnscL0mWxY`Vq zD6g$#xhN1+Ma+?^0%c$e@{uE2+Pq6_p7hpC)Ib`FWBJS;m znpY0-AbHKSu$8rUULK%fbXGPX^IA*sF@###j*kSHP6a2G47QhsXikLMMl@(MV?EIn09c>bBK8%SRV15w6lrXg2 zfZOi~P}2SF^GLqwlF5|BF~$rMH8+q~+X1*yNkob*!RLA2{%uvGj<1=$97)&#BmUJ5 z)tv%-b4>syz}@S!l{!ChtL)gn+%UI(7mjT{PBBl8=J;J{RbyBMty%DLkgF6n8BW4|VSdY{N6*{{B{PUHx4au2)!H##&u&@p1vd`S6d?^_R30^KKrzlE6^j=k)GSHS4|}k%yA# z>d4y%TAvZZJ)fJEiTvI1I8IP(aQtMM<#nBck6+o9#$x@gxHIw_>;eQKR)t_&*R!|!J$2kw?F)5yx@f|!uN0N@uF9} z3U7bMyYSX`{yNUiZ{k~@ejh&h!T00SkA4a3@;p3qvtfULDZCz|rJaEv%bOYzR;5mtdPfag zzjxmsqF#*D!xXcOgpd!Fv7Z2^3wwX&FDoP}_nfrMAMFvFrBR%Oygp#J^^2<27}2=e z!k<`b*A-MId1}lzo1!%(fs`L~wQvEW2|Y_8q%YalW%=cA{1V zs)#A8XJ_Zw-@L$~cQjQT*24f=mIcx~R=^7j zXj0R=EAO@eCBCDfCOseSNuwe~+2D$eXKE!dOZl03=_ya-h7M1hZ!*#=0;T-UW0^f@ zJ+9|?QvI&uXlzpK4_^Kn+0rrYjZ zV(v~InH5l8XeQ3*aQwm)ifm~dlZJtv?MF}{=^KV?llby1YFqaJ|;r9TdI#c?ALCKRgKQ=wUSpN81%wo}dLs(J34Ix0V6+I%@ z`F-ZG#eMRW&x0KbpP@NRLX*oV$+YI|3a1P0)UgGU({o+04Qua)iZf$zKpu(2M*z>S z7myoFV1(nOZItnZ!US-r#a@S&IGA?`A{TUIC|2b|%(^dW&N-bvCSw8By(`6;x0QLd z%2Zp>ib&{}2w|%ZB3GcS*cGdKvlO4hT5-6sqC>3YBEhD(bz9BibhbN3tp-dC5Gh7d zD=@03fEXXzx?KZ>!iy&4c|QTH#3#X~B;_8ELBm78R8gdQ2-FNHxtS*b6+h_!CSc{C zc>n^q^>lc-GKk3Z17H{nJn>g??1{*wn;GiC@sM6ZEC>*tav`pm4grYnp(8m|@03uA zaZ_lIi2Wtr{jGYW8a;}h{sWksWTs4>4JXPqgUWP|I7b1uHE)h zeC_ieLR~I!|HH4x8{Y5$p7?Rcr#}5O+Ij6nbgPs+)a*_i0&H*}tFve%;*4(jZ0qrn~5&o6)--4d}U~a4k_+bRS_1KI_76=LIC2jjK;$> z&f|9)dw#26@%c1)i}TrO4ig>QAX_B%$9~*mPV!&Q*TenEXsY^SlVuiM+iZiOncL5;zG@7VAam4f zCkUUx+4SVJ&|8b*5xBUF*}6T!7)o|WVY)6#)ai(mrq)SjF+G#x%^p(leeC_HXap!x zTsr2<=3tByQ^#1R`UJwa*Eui&ZTA`j${_DcT z7ncBUJGZJB?qLO5v+7XYLsY6*dD8GveJ z;XwrodQ4_Y%mOq7a$f&3_UW*yY0Ud6T8zEOXMkihzPI%wLr#Ef5}|fpi96(P3GS1} z4Dk4B8FRcD8`8LjFaua|ta}BTB7$0rC$Jig+AS!&xhDX2=*r&OVev-1)5YO{tLNwV z&EI$f^y$Ze8;{~055Epr3cml%FXAg-{V_iF;Sb<>cfSBXeEtvc^{;*bPu;9|(>s41 zkG%e^I9#kaUmDITaD!dy2_{ehG*8M5T#cUQO@L9pTGdo)LG&h&%sW{9Nz!N11o=Gy zM9=_kd1P=9Ymt`r=*Kv+P@k0&B_G_34-UA(V#|8GE+>&X*>ADT zaoCoOds4u__%3PJ3~wF=rDCC!oEsh1ZuRfj7%)Iin)HzqIU1C8xFy-^v(&nScE!5d zURkB%=Gt(vHV^ACKxGD^g8lx0v)vhj^wlV#bwJpe4sOlfnuuMFW)OfzB|P#;isurtLZFPmWu=$^*Mrka1<)DxHjfdG z3pywhsG5*(CXaxcES{6KPs~FSI$I-c#%=a{Dou;yLqUTUrO2u0lFt-~)7sYH4b-Ic zsm?rT zRT2l#sX!W{2C)KczBH^)rwIMn+7Ng1?->r1ewdkBI@jcxTa>cmGM)3YDKrF^;qsnm z9u!`{90FkTf7&IxJ$kCr!n9kWF*QcuX{y z^$30s`>Y(JG6cg!r$+>X3N)DnD>#J0>7hDJ1&dTP?N|?cECr}6*6JO-AsvVH!eXrZ zYV@Pl-IsaRMXX$G(++J7y_-NU#li+hMKbS)A_~=pk~}C7K`+)#yg);kmx;FEz#}Wr zW3QCN2Smga3@;VT(4?sO@6WuI(p-!PhWKb65>kfHmNH^`ImiqMPtj5alf2Zxflk6J zSAH5`F%0gBXSmQ|yehB z3w2cA16IA4mT%*^r4FxDyi@x7l))$;C;8V_j>P_!_)XGzml?H!Cdf3vFs39kqvG1k z!>t6iea9qSk#s+G5F!QW0#uiTtSmSv9K}#H=3J4;A49B)$Hf`%w5e>L)fx~4wxBQ9zT`I@OtQ|nLcVx zJ0o;O!T7UP;p+Zw@X93wqS+!K>AB`Ew}dZkqbXQx4NF~M7B}@;`x0*L!5fBfw)ce7 zRZl;&1hINQ3b&Y5gS6JT)xCCHvr;VHx-}EB=qNkuS+O1(bf>7*dz`Eabswn-J}O>j z)ij!>okvD^fco@_T3O3F-iaQxvGfv(a_+q{DFxo$R7wv)wM`z}UNZ?d#Eyt{0xY>W z#zF}OATnDMC>R zJ(k^z@cn1@c=ZEs#2a4wM!f94`>~W~@WU^D93TJC`|;^7eF?YSbuZ4&?(($|%X5&S zG}}6*(8+Fql%4HMdLMdI2JoZANcKN_xd7%T`M>|$H-As(q&XR~#st)m^h^tKx`qzcMXjW*SaPVANiXS*{T zRs&6?EV$U;L_h3}DnwC5(Zu%FQoZnmB4`LYy!~AX4jqFyI#(uJJNlvhOgv9I(wP#r97JIS8nrcwP;2plV^ZEm) zyi!@N9rI@n!KVblSOK7H_67*4$9D)t1&sbYdX8Xv}4z9!s)SHpCJJS zgU*T$QV7YGWfAV9%)=ho7m?fEV!6m{$n+b*a#Acb4@s}M+()g z-shBH*?MEP=MTabRBzN4P3HJyzsD&sxV>LG{p2AII^c7|ro1;RUJ=)}L5 zWKp9PrP{jm0@VXh*K0E+;y#B5Ygbo9$x>!<>hy7+nkrR=PdlLA-H&QNd>K)u!y^Rt{)6m+bU z!X<&X1Dte)R2RNYQF}mKqul_fGa%<&6B2LV51!<0<&_N>2y1j_`rOb?&7ci4&kH&n z%O4?YkP)vef>CxF1apJ=84&DAkkYC)zy*(70mbNcSjIK+iFgN3>a#h3CDA28IPQr6 z))Tl3Ajpb7J%G;GHznquBjtGPdok;tsO4P1z?}g*<`H$Q>k26g{`TMg-{E<8+=E9y z`##)$_YRLd@>aa+o>$}XXI4D=*^lArpL`!b_-4U3zVcyw$8U{D zEa%z9tKq@vI4V4;^Ogig!;Ne2(hGoQ-UM6T5egdb$4iP=Asbm2fEJuBJ6tI{D4Owd zSaZ+bI<%P;f8DQm`st@}esO``fVJBgce2Be_Ior{Ts=F-Gf)2nq7~=6bCkNcXT8OG zNTLnTIz4|_{rOzdbH*$)KFsrS%4j?0aF%a& zO$*GD&z%p>CwUnaiJW{b7(v&^&n0iLF!UZdy0AQXvX+8bhe{9qGJ>xnohy0vwD0NPngJsl!+QV}?DO(+FPh@ZNK ze`ik$Yx~=0WyyLB5IyD~rbH}Kj-83;cOHbF-{tfeemaqH&a-f4g5Xc{M2yWyrG#U% z`(AcEo4f(Ja>f{UcF_)NYZ=OeE2Vi3I-6y=zzfT!>PX>1MQff*AO&4kXXyo11*-t; zHKBeEnTbjgr)@3j-HhwdiY@1*`tT?Mo>t2FEEz1H(#oB?ku69ik z^u9v7V!y6f^?)-~W4c9Amx8V<8oGCNg;k77o(NS3DR!G$Zx{fAC4aRH$6Vbu#byxlic&)T<3`n9rSycA-&;DzK1#&k#Qd(9c8|g zRTT&Kw1DT~P5ZQK-g8au7UK(e#H=rz#BG~Wuxb3zx{O$L=5f@K?7t+3h2VZ+1uHU?YuYT!M z_`wej_~=JJh`a8%122E^y?E?fkKq};9q;~Ie;=>C|8G&tvMqk|_f&?{VGG3Odl_?!R{ zRy7o9AGFTT6lDdZao-N3>AI^Z%Ys#bi`B!cIy`<}O*v5Wc-95DIIK9d6+7qa9YDL- zLu7}h9s8Smtj+ToSiwwTik~;KnB+G1qB9B3qw0`*zEN$aT`63VtiQgF5&EPZf{N-q zpx8=(@8V2mWix}JS-7fwmq@|lh5SenLbn%Y)}_@t-)6` z<|aa3=?Ngl+u{o+G&AZY(k2*J3=gv3tE92mH3Yn4pTxct-k&lG&^$R2PGque?EQJ4 z#C;tjUS?>Wm5P|X>3+a4vRZiYIBzzt=7z^!)lJ1bYskjAfirE>dU!G{cP?ZKG!#+e zJ{mKShr8u}-J>Oik2^&`KgaP4EgQ?dkgJ|#WiVTwOt70}xHWShcZ`H)zzzO{f#kJQ zQRHK-8{lT)eI%VffcGhlJ%2vwiAWl2KH_&#u;Gk&B9Hm9V*uac9iU~p=G48n&)5p6EbvYXb;keoNaEF`)P!i2k=)Je!*Wvu zrPwCveRMAOGi{SOgDMXGntQuS?`{z(QLPD5ZG#IeQMwm^^RENrg2J*G_jkIer(Y#cQ$-t} zyGYiKJu`&II2!;%5Y#*yOoBbN9~o*;AmQ<}kP_!jS+Jb|S|Qjc8XXPDVpO!M@t`Ng z`BScFL?zT=1|X{|$yV8^JI}POsnOvYiYYY3=3rzrmI7D2)jTAwgbyXq-eFmGsItTV z_uu>;e)o6&Id*-IFMRATae&}~*S`bTZhIw`dX67Ga|5rwb{qcw|Ms8Y_PYx9H-3!I ze&*A7^z)D6{?|Q(x4q+G>k{0IW}Ck_bzROtRyqw7Z06VBjfSY+JC?=y3ip)%hVI-x zl22%HALyJk6{|(1!FluWY7B3Wq-(YY73LoV67lB@eu?8l2Ds$^@cl_?5%vLg11BAN zS?d%mAE7depq(r?PI+G-BUf$gADzI|v}cMq!lzvBsE{y(l#mw;@za=Zll3eL5KoHI zxCe`gfm!4i^s=C9NAEyeJ8G$T{@pLcSv|x4!tUQ%LtiSEW zY#RJuVf4HSax3oP| z$XAS~P)jGhx_dc!Sissy8t&K^VrI zA_1MYXg=mSI#v$yIj8ni#*}_`;%hz!C)RFW2j@RUAy`ggs8Ec>LZBUK*t!5=lqT*L z*g6t^Qv%QUa;!bCon+OsIG&3_F+F)$a5fhE^hXFa0eZ1gnw)TYDp>D-Vz_YSBc517 z7!qI}Ss+#TkY*emnjhWJ~q+;V7BLv33IhUH)}`XuzipDPVqk5apdQ0 z-!qSuxdx{crtPz0O)pWnZl%c&rgA(F`MhZYU~}H$U59X-$K@eQ351Pz^>a2Q*LrWL zwM5j!)|)ZLAVbkbcVWTx^ZDoIreeWe+($kF_rmH{uSkA$t2{eyhh@N-Hao6LEoh=> zdVr#PW4VS}0qq?Z*Pq74#SJV=wcHH>pr%Yj^&fZ_aE1zMk8+h!B&GoLB=POYl}rez zcm1uN6fTi~yp+N4Y#u3wj_Hf5V#-;z6y@+8J#x=e!H`f8GPP{>J81#}m0`UP>y96j z*F)gplR5-wQiOs5XQeP-pN~z^1M9A?Akz;4!|_TCA)sUy#)iYZy3;naFeja-xtJ`) z>2DEOvY`679I&u#rfSFe*%dtglbiUX_kS3Ve(^ha{E(d~Fyo_i;dr~-5=2w>Kk z9(>If+*An>nJOIhE>R@J_H*j!?Q$pTcL0%Fk!AK|40W2>8a(nk=5-G~L;R4+wFaAO zqhxK**E+bBLOMv}-i`|h?*b>dx(7wP7>PA99+~gLg_y=HD0S416|<`NbDS@5{k#*o z?URi498Zc|vi1_sVlKnwhEsmOWhFX2EBzAJ%nH}cPx!l}>+SwomQXHRXdWX~NY@Mv zHW|7)ui$s{`_A4L4A~@juKL8YS15|e0?ZSFG%y}~Wd))V3xXlL0^*HGdbsQ)wuwck zTXGW1O=iuldxM>cn3WsCd$8(hMtKUj&x%HhXN4U7PX2K`2eYOy{!I4~lMVz5Q6*wC zo-ODe@8aNB0C*_WWqQbVvu|tdA9;6v%HOAcJ02(3b-QuAl7-Co7(_l?lUc*(MpNzX zeQ=ld+`lHC#pe$t&Ew3Q#db#s7u*ePPl_7%tsvt5&5{idG3t>AkgIczTKlH_3LENi zJ~&66&1xJ>dznS_j#>oLEk?Dgbpb9SXid?J#m)PX#A(_{iyyNy#xh&rhC-Y9 z#%yJ_8$>`#um|y^Y}3zN*!=U>yI=iHbw;s(~`Hq2T8f;-cw@qs9^<4^}6>4$`z_3j8alQ zz-%d$P%h|Q5+LY4Xwhj?H-D272__kWkmX^PfmBrZTdbFj5|ETH!MSpJCLipOka6_n z2D0M#93^N;Go)XknNa7$>+F-koYDBeTNc0_H6NRzog~%SuH1FOyiMa|^9}`7`e~7=5TxoZ zC$;tyfH{Nogi!qYK8TF5p*XshEOr|!4MO5+I9v7 zOSCPF*M?43Gr-2ZrI9VYsLqWIm2)K$B@cOBUip|eYye(QF})x?>R_On??CXh-8Y%e z5?3XND$F}^#VZ=q5fs|FQ7s0D9}>(Q%$)yCdo6*Q$O|h07%QtGX*W-U$D0elbc-lt zW$*86tUW-HJ!j*&(}wD$nGfq8x8Hs{-t+cX<0p@O5f@Lr6py^|HP~OQ`0ywG2w(l$ z5Ao5Dd;oXe@jTq~;#c9vKRTdXdnx|LBfo)rUwjX4t}CuytJs|vJbl9|;#dQ!hVIG4 z^e&(jpwW`91H{41MvIJ2z9_>>QEIVdZwhGNM*;<;`Ft@@RwvLjaVZ%yF$3q2xCyY2 zzZsel_w!nI8G(<8^Syew@ZyEQsJwiEYL^KLpXvmV{-UoWI z$Xr8l2I-rx%=dPlaJJxLO?2u^F?a}h4+a+4DiB*aHBD1e4DxqFzB$%QOSv_s&T?q` z44=H1QqB#h!tW&*lRPHFLb6!}5{SmHv0}js5OXm94Re0rxgF2G%99Rq-yXTJNdP95 zM3@NmU`|LLmaO@_5dVPoO@p@lyFZbhkPWe%0y{i%^_BtcGXV%PLk_Di1cf0u$Tj7M z#XSS-i0k%}rEF5h{b{+E$K$iVtvOW8jjDa8;~h^aO}y8wl!-0y1BTTt08+YNT!)zo z?DniJI^r3(1e{ArBA&u^q?ck|*OTK-bT;nko7+M9sPHquUMa4I z*JA721zI|U33Z)~lB)F$D`esX7Cti$j0~t3x6pFNXi)`LZCEY&154eZqNBO>`K*}B zm!SwT#$k z^ZfW4B@5NqUqEz--Gne5FC>CtDr6W@60@AH)zKHiF3<0Shb#^Jlg*hoFc>Ayl%ES z9`j)(2p+3y^G%Sz^AZ5eTukE`Mvo{sdMIf;5V3q@NEdR3N8aOrH zL8fUB9vfYI)A-Z7m|+2cl$Zk%Q?v_}2N~F0eNLiGFmn}zMmTkg{|YIB-LgX|1qUi6 z0+gk=hs6|@)`7K|LF#q;*c|&kkjzE@Lr)=*3|kUc1JktgO?D@tS0$OW^nDpj?yjH*ASqne)X+ z;4=&;N&@p|_2;`9EE zh-0d)81Q!~uB@qRx7=@AJh+unm(Dq#EvNaztq#upD4dZEq~san{m3IgTJjgpAxz`G zKL1|vj`G_sNrpR&H969AegVgGO3EgIOj_Gz=V45k?O}ld&x3_^I5XAI@EDTfmTZ4c ziSk-Q={Q=@6FBr3oSzV`G0xFAZ~ImQF!{#t^GxV(k;(Sx6Eg>Z{o#PK13fl~MBU36 ziR%2XP+_~paq8VK+YGe2b-+p%A~4_S`zk@E-N*VsOyYKP4cD*vU8}#(?47WKo~NU` z#HW}a9UDzVJbv5k^_4=fY~1)A5H$;@F>tq9_&Br|gZzOkd7t84rD+8aUweKA%W`yAa$8>#zE!<3Ar6qH(U{o*EiH?VJ^Q0}z? zD<$PQuz%?(&HIC1JDWT+;6Yqb5V6XC)r~u?M`&y0eLBCl2B6HLg6}b>;VRaUvlKJb zMDA7bVjk)Z9Bs~y41weof+(I5A(81k?h&KM6vYAY#Bl;lp07iu6ITU)b^u?<$7IMZ zI`5-uA0ix7T=}9b25gUx)ROTYxrTX{VdS+np$Eo_o~V~77Uz{wc0Isy4iyvkpaH7w z7K#Eu1Mp?%8{fBsA6bWS^64Cq)7TXA%)EqFzb(vNE zl#6RS_~S0v$IO{A(`4q}li7dgUhXjwN`gp$Y0RTrET~yr`pkJuU3&p-2o!#cYGy_tVdz!>#vLbh9j9a>O1ZgDRMEe_MT#j2Mq zZe8dQ4+*h^LzYA>5Yug-reJiSso>(!(Dpzn3u;}_-HKPGq93xbL|DD-B^2u1oRyqx zlPMzHPrF&=d=sjUvc87yE^)=j!H&jqblz5}WsX(E@tzW})M`t8ba zso{O3atQ@;lPfrwWr$eUkyM%Y&PZ4tLW8tw|I)jduJe6F5j4CXgTDR50CmhegLqfp zPYw0RHR0Tcch;9C!tb2slK_5;x zneNE{7}^;M12A$fHoxT;A}tR=@3dt|-8@ZcT~WmKPgfEI9?}KRv-3JiI;`Ztt!3H( zJVt-^od!7ZEq`>x^eQ|YLY+UEg*%#-@m`s~kiNZCE(7?ZVt8VllV6J1>oOc3L1_ z<9>J5hZ-nLrKx~NiPJQ(q9peCoQL+b(Y;oZ%Kj!^EA7eut z?=${Q41_R5%;yeHaqI~l`!M!tnaAiR6`Sh_f%Iz}I|mNE6obVy&qF_b$`y2|) zFB9MU1QutY7Wb*1&c2&;&H_8WypC& zPy!g({1kzOJUNnZ&s)%$*xb%VhPcieF6OZ}HsG1`vZ2pRk-fL1Jf@-Qu;doiBjr0E zWwQ^sm&kweQ$yhU9*I5`aNa_N0EpZ!($S2@WZWZx5Lyv)!a8sokg^l6#T7gKsfl9t zRIx$?Lx2IfD>)~Na~+y;%sq!%yc$n&@UpHeZa=?*H$V6yeC0Dc+<4$cc<0+6!V`}_ zhClqz{{z1EgCF71zxoWGfAx0U{P9gZ_Py`o?5+~BVXI18qVOk-^ZU4@Z> zGD97R%Am~>3r>{<7@nX(4pboEMM2CcCf=mZ0TmEELJGb1+;wi)?oq`Az9(5~RN(P! zU{0OLahG5YmyqWOFQ7q@Zg!D{@AU?=YOU)`ziX7$RX0eK z%{}w#pWJ`xZ+gUdg(WDn3?cWNx^m?2km7a~8uRZri0=u-=7W2|!JS(v86q;yiCr|^ z_xN7!&B$-9mVnoF#D~*TteVR1Zn$!v@+=0=tLpDA!1?(ZdISE=zy7!Qy?^)5q3s4f z@zM9;X6tzHb#KMh)U_K#)wkL`;CV%bVQYOqEHm~ZuInywpQcm z^ZN4Hd5!sb08@^cbK~RCp)$|_G-RjKZ~@S~--34q)@~jK%00DP*!7+3?0E%HC=5t1 zQlM)??+299pk1&m#9x(AR^7(iP|Brj4O0Aay~lJ4kQt&61E+aGn+6 zyTW&zE!D$Ys6aPb>F7}AC?(lrVc8vM0C9JBuUU2QOQQ{QMXFdes)CB5K=b)qO7+yc zj-sZFm@)(pnX=}kTg*aTQvd|v7p}KV`~B$fdxud37Dg0YBN&CPZ>cPGZeN!I6z3T{ zwK~sX<-<^TxCk=iV`f>N8lqkASh3pLDFCekujU)MrnN{I^rV>V9{E6yszfnDj1c?P z)@Lp(a%wE|k&KI*V<-gaUOM(9jowCo)?jotY!4K8>ab4q$#~=#Ir48HRxm&uXL8;y zz2h(b_=PpLXOfMQf* zu8?JHRP74w^nj0b zqqo=^UDBiG3S31b!fT?N;EQd1*VRjv2QF}$o8;&Sty3jm^ZPNJu)q|%?fy!i$r4D? zw(sn$%P<$er8MgP2Se+KD@#RqTaNER`klW&ZD`(Ho3PMOK2t8r5k{G#F}7YM0LhCu z<4~4uCf~G6Sv@U3BcJEGHuE!<~{ zMS#8fCNSQg?sa1EGEUeq&QorzE$Gf};mf=XM^=lU;kZm$8I~RnSDa}(!xd}7qu5$| z=P_k^zf}1#)!r>_ZaIT^1tJ14KQ$s#BsM!YDjVt8d0UTySw`7?H*3s2Z>AWZLe;E} zrLc75tjQ0{nGIx=fWLe`5o0cqBVp$W#N%rfbwt#L44*d00e2yK^&>u!hOlVP74px)T~ax zW0X$=RirUuGoGzQd05br;N+Y|_gG0Zp2wDjnUxdF*6FOwBv^!t{RQs0c8=fvKmONv z#VcNiPkitX@cb8E#jpR;FXL4&dMzIP#y9ZS-~TGU_NC9@=8u1ZFMs~M_{7IQfFC@$ z$E)vqJ$~ue{tn*w@LSMSaaa$Ku3o&rLTXqE#$INGbBPRq&8VD=(PQsNUIj*VF>!^E z5Kmx0SK*d_W(ot4vY8tV{svf%3FkSKg;*2-2$M;K;{xIxX-9`-RriKSk51aWkN9;4 zbegR#@8uynl6bNDW*xMxW8UDrECcpS4uYZX*N|=t=)D|rO5G4sTvqiy3^x&CR*RKw z?c8JT1u7M-Tl+T*zHVy zx7KiFF^}!d!x~EQ4Q*JpM?*dCjR5^=K1Ratm_MH=-y0(il=PN6f&(T?*qf-sycI%e zq=JgsDz0jDQ$(l^(tnp*}>k$GG&g}b{Hkc9-d4(J#Cy0pv>O1nI0?zk@72a zU;m9=bUKC{Xaes=5_f_%D>olhcdfyohz9TCUgEy)pzO%M=0jLNgO8b@pJ5yXOG7$z80qAj=XG^@- ziGa;e`KVK)zVuywcF243cy3jmhMYY8J4nnD|2wJ#cq}Chh(Vjn@Up3lF#xNbP!kc( z$CgO8F0P@mU%s$e;m9sg-e>z;{yd&@ym7%H3t$Of_C)}X0!S~nB=m`>WAcZPE^Y}i zrNZ6RG5|x~U=&6@rXz3HIkYOj%b3^Ev?n&~rhM(N4&}TD6N)z8e9w4GJ}pmJyzZ&B zbxmMY$#Y8qW35%y1XHP?C9oDjmx9(;geL<4y4SLo>IvHF^f`oyy4AOO+1Z$5A>eO- zTI0Q_6y$(}mTPiv6W}%{#dADFfqh@y9j;i{6;0Pr2ura}*oXB1bc;EzJ1b9kXbn5l z4zQF3(1JtrGO`50>Pk+5D@{FsgeB2=MW<3+CaD)maEllm{H#K#oS&c5H!J+0(`OdO(5EFi)KNGVE6)X&XeftS7IvXR8ml_c z2H}v9`wdufsFms1ff8X{L*lBYb^CBF6&7x|wrm%1>HNM#+zM1qQHaC9X zQj9N%Yb4?d22D|0OL|Nm_lJ*LNuLRT$9_k`GCW^F%ZYpS+@lO;{&IG$w~QVLMjo!vMqc%-bo}G!f0*Hc8e^GV8=% zAC(i2fBSikXRlC90`~MFsjIO1#LyxN5j59mqZF8vsw9%lk0?ltII5U5Z>lmTg3MxD z4vu%>me{tlKczfmYyI^4mnaQ`5KmZB3n$J%Tvzp&cC#|Hbo#OP!G$H?&pyPA1-njx zikv~O%Dp)^;3c{U0;*{|Zj#)`iiH8`r79ZKn)?Khq$!GnisL^6dCvp zAgZzP(n_ScZ|L36FCp16a#$2--C{A_TGyP8&24Mvn-@b|*mel2D`%c?C(dwtht!T< zs9@N5jOiudKq!%fnW_#8k^`>ZJEV1BvHJb!#vla1S*>V?hW*|!Z|MzdU(tKR;pQIe zp`rDLdS=RodWDoC1|AtlZZbyzaZprB#IIWzjm5c(3R-WF-45)rP%`iVyU-6+fNPQS zXtv(8Q{aVj&A8b7b17xA@`hC&4A)6a+JSc%EQI?p6WPqXC(Ga5A)*BRa?5`B>+Z|7oj^0e*Up>Eq zr*8oN_J90m_~YOIAJO%I5B}w!LR-Tt-|$X6|AlYD)#Xk+({A7muXzCf@a_KuFMH`V zeE%Dt#{1s)UVQZ%-^QCBc?aI`#s{&h3l43?b`aB)3Btl2O_d^%TZZfq411hQ5f0t4Ze9 zHW|!hFa^~1R#zrXyO)Uo!}BR5jdk$cEl`sHivj`&N{9hk1L#1}98J<#QvE%+-`Czl z0UCR2SpviQo`4o}x-(0hY7b_bjfb+yKJyHaswS+E|~ z2pNTnbz&|BOD)#lBJC^j_ih_mAM=vdji5V-)${50YY!fwy$xBXY|+YIGF}F~C|2K~ zbueD6ZAGchN1Y%U(j+jCMeD_SGo;+ z_Yy!Gpcwg2UE#{T?+Ia<>)1Ac2znC$%|iWPQU2=qP1SIRC1qnIa_hKWU3qV5Uo?Lw zmO62f?v+4?hY;Xa7-O_a-sU>#5l-CxPX4`ZPT zo=8IeEvK$XN!ebfkF6Xo(C}Q|%4_q}j^`HglmTZ5_4&8V*EDv3QD1ig@4a~$xvY&F z8=xySKrk%?G0C$6FdjdH;h{YAc}$lm2K-wmf;hpX6DY{{JGCgpV`fnxC?HrtR4&Qm z4+4cl6^{w9&l`r~Lb1f7lSq?vjSoZt6t}8%8RHuRw(E?y`g|Lbi;ZI9MZKJc z)Exb`NFzzHyP2d)i2lG1Qmk%y!2+n*Jcx}mZ%%d+9p(7^F#u|{?|ZjuPY~-Fv3e}- zQWr}UEWlX-+Pb23MXfskR>NJJTbJsB-UKQghqhvE4FW5{C{iI9Rn+()geYmPNc1)+ z)BY>Q&=WGFGGc@?I-qi4ZneKMu--N?%z`tb^K^u<1t=qA`4riL92P*O;XMh znd_<;LLsWl3$s@!xVHSpCR1E!nF8edB8Znt_jFObZ$74ZOv5Y?FKFTSm1EwsRb019 z9bjO!YsVthtcPM1X`Xrd0^j_>Pw>oo8*V)FBOI<@;B9YtJMO*rK0I~fX}sq358!3@ zK7hAA{6>@;kK_G+^n3Wz_x>S%_`@IL1uwo22kQ+FfhX@eNQ*GAw4WWX`gk1B@%@1S z_6eg5eoN2e1d46nWFiPpg3~!9_XnD2timoR=$$Yn&!Anco)6SCGL>Bw88jU7-W0!53RbmBN4i?_uPJ8@+JUuM2D_sJtbVf{QA8Qur^z$4P1w}vFm8O#=b>V zoD@)3_<{~;9Du?CpN1!Ysny>5utF7B)zfJUL35cxE+Lr4N0||LX!@)JONjCCo@kio z<)Rx@YLs%IjCCTfh0{_8fQC?>##2WjjppaN(+*W@^whD46MLjJ61S5wDF;c}IW>7;*in zN?QEB9S^_bJh>cK5(4+J(GpF2lQ$p0G>Q{gR!^PyERN^ghawMpI-DWTv0k<-SyHc* z>eXue*9o9UHd+8ox>rNFC(})!JAg+5B=ZEGp0#P?I3b=&Bt7YQIH71~JQ5X4G{!yt zE@^bsNvPnB$|nMgtvz&Fg59;QClm=ktj0YzyKg|Tgfmbn)DFlQ*l4GyS%FO0R&-q* z3_4>Lu~^X%4&CA}ivZnXe8a`eb5v7KL)*}#F%OZ@I6}zNQ&5UGJgPg(Yv^i?oJzsk z3=UKQqzaa^bCipMrh>XG-tbPa9##}otA+w#kp+uaM#M2RD%eg<2-C#Hs4a zcN>aOKGesJf=Gr=lLElN)s2grc;0PS@sEG|pW>A-zaO9c*nh-JUT_WXc;~O;_B&sU zZ-4VKeBk@v!sFlm2EKNFhA(~o&+zF_eiTnU-SLW-zY#Bg<%78A-dA}GmDRx{ZHWS) zpb8C1_D+_Iun{I)dp`{VZ(LO(tS5A;V36|*4MpB(oDD;_Go0iL1Wq?j1YIn#R(!7$ zu))m*DiC%qR}AGqn75HvoA6ozG{1*^6P`45HidVihIIh$ieAxYm=krZZ0 zdIxgiHZTl0*>FahGN32uMFFj$;MiPxaf@2fnkgRkS_-KS230KqNF+co=bLnKUKd9kQV*=90 zp!>Xs39QtWbz6cS zO|DoEEuca`!=-GV+0^##>IOGfUh8UeOXbU&h~|W6dK`N!IK6^f9(fuk&PVoET!sZ_ zIiTUuIoez3(_F^Jjj`wFno*q#DX2igWWl>2fhoIM)kr2~%XGif@8`ENUU!Rge%kTe zJQUh(a}8d$osMAC03r}Mg(<^IE0!pY7GG~%)QtNi`myzBGjRwp+VO;(61H2waw`Ja zekUh>KXv|m&mr_!_OZK!BH08;ig}>xY9fol7%@uM0kljF8OwVdkBe9PxjIOi>p9M| z-^Vm-2obJhw%TT9RA54e_2*e(h~rWRwk5dks7}Ph!=_ADykz6+pggOvl4FUTsW?MB zG$DtzB`^fh$+8;mU4@AMGz7rJ|!jswodeJG5Bh;`x;Ybyv|__qbH~>XU6E#@2yKN@6uhc$j$W1mtm|3|ijsjma(Uw5iUM zC|tPrO!-MgBJ9=yf+Fso;73Pzk3G}=6Lx$G*?HOmB@SW;J`AK%!8PEvJ1cnk3vWm5 zkK-kG@9>rfUyW}(`Vl;S{R}tu3m*O4XK~y4op|y`*YWs|p2kaG{V@L48{dj53%>UC zZ{YSF%dJY{7vg(y2CkHgTTqppd z%PhRPVdUNvckgy+FHVx~T@g0|k%ryMUaJ6Vps9PF5nrr$PZy&VyKh$sW!Ki#OF=Rm zR{IETa)6i!-)I~ffO60ZX>52KxgV%hB@T}PYRW|Oahm#*)ZWL8FUCtPhpW3+)%FwD zHb5>S(8d`Y)hR_S7Q(5$yP{xuUhBb0mEBdWhnuLi zV5tk%y?J5*phb;Q-z%gMPT%H;?}#0;OcMaB8?<1#ap4Jr)2gzflN-I1ge}^vE9F4* z{m5k)GGQYBBRZI6&1JG@``z|)abakc-26r6Cv6lnPox%xbjL&7t0K-1WULR|m?47~ zz*G&dFjhjT&ST#8W}5IX1S9A178CVTj^~_|?NG}8o_?dym)IcoJqgV^QHrWn34nKC zbbwKyfCCoZd~EIN#9cRwovnwPvEVcz@ZSmS+~Nr6$`8)8PUTtfIr(Y@0YM3{#s+=k zE$mHVtm#*u*0^7VM3tN%82_)_Y6tm~gq8spszq&sn~=yAm&Tr)E$``W0dx{*pkhI8 zjbV|r%|g;hK-KALMMpK84Vq}gjvsr32IFMHD%{PyunAJQCMoa}ZK6R?;q75Ukg5YC z3&$$n(cM8X>^G#lFbsmqfq>PO<}f?XW_KU(;{Nn?Jz(*wPQAfnfe%It3nd(|-|ula z>`|mfH**Sj6f#?5R;yczi*Vk|Ol^P?-X+T7x<9H3s+4Ix^pvA`yUK9BfIlO2qeGmQ;O1TStxdYcL@Zx)3h=1@~e;-$G z>-g5UK825d^dtDvSH6xfeBtwW(_7wPVIAE776qSr*cfBGsW8R5+e;hI7b>s_5VYm> zyg0&-93W{Z?!I3g9LKz-b%P?3hYJNetOLDmqe9dI<$jKR4}i)BFsiH&um!aZBLjRd z*}Sl;E{zce3~k+=UwP6l0<8ONB{UZ+=zp`qlF=82a+1#8e%^Z7Lhg3Y_hSqSNYl9j z4a~D0c`Mv_W);$iU-6j`1J?WX5QTf6caHP3#ma*U&<+<6X;{~e zx>S@}ak%Kf+EACpWLdF!4Mj&!ysPt)po1_R*vhdvsCem4H!pb4J69UeEbL9pf8iNu zJ~zy+fXlJfy@?gF*t03Ko0!+PdrnZ1p)5?%6S1FV9>0wB3D0SiLLZz}koa&IVF0a; zg|vuW$=A)MxqK`ZsaXWvp&2fqj$303c=cL?a#USGr z4>Qj^2ZgPFrA&u=W{Y?p=hZO{?ZZEUYd&2}IOm ziO0sj+`Hgsg{yGJDnAcNXu38)j6cN7iN|^4eTEfn_Vir-{)lM!anF9@9aU*Xw3z+g zvw++rRdvMY)>?YjaAo&yT|D8yj?r|qw3y=RkU?GGb3%K;3d?g~b(6v1#9i_Vc1Z2M zeg)#nS)Z&0LD!y)1-M(+|1MHoDKZPPpnGqKC6uP?H*aEXE3RHS1JH23?6A}w*4A;T zibWO+dFl>_0bgyc~FQRFrc!-<#yRb$@#Y-GEOA_>urtV}X+M}Z@7oL3@gfz}4 zr8TZ#_`tBH_Hp90C$mr$+x&4J*ZRov0s))JIoS+AkHKse{@lcf?evXDgn1Cyc;OU7 zkvdX`;_4xkz`eHhY9t}XIdNf6B`yF1%RJ_Lrq0>(y#{~V3Kaflh8x>N%Tn;<(-(N` z2R9&RFU8r}8E)MC5$=EE8*%rmUWlijYjU=JEILyiI8 z#%?!AIrz9*Mqtw*nP~`DNNY1g!d=9SXRBN)=)b z9o2d@F?&xY*Q*;m*o}NSJ3~EN(0}YX2%Z}X*mtHtq95M?F821T(yQjVjvhe(wP*#&MiKXtDVnc`hef&phSvNX z6*G8`k9ULXI6tQ%BDRL;8QbxE-lb3Xn>`bG9e7@y@8^SLSe_}A#XV0MSB~LJG6pI} z^gulGNaoBd%y)dY$4^rzel}csVxr>j=`OaKy?0O4^+cp%gu#U#_{8eobHPbu{0+$9 z$R^@cl8lbo4y;UTR3?~HLOy?w}xa$@th)Xi_ot25%x|Pq$<7br?zKsd~cwaJ?fqgBW zGl2#Zawil&XVd`|WdN2HUhCVRXVTg>WD%<%p;95}aF>ArYYLZ5;#lrtE}{WL77S)j zk=`8$GG0G3k5Q>cyxeM!dg`hjwJyF#%t}{Ev1&~UEV~^b1?zefOZB?ut)Um7ECs7t z!>3xT>uhU>FdWh<#2lNh7A^&NP4WPnv~rQ)ET^P<4vLC_w@6aGDz5g z6_7n@q?r+`t(?o`A|qoYU2eO?$8&n_Pz^+dtFZ2-eNONiq@=k}8p+G)*MFEok-9&_ z?i=;4RD<60z4PappU40*&={-Hn2oVChLvcXtEwJW(HS#2zK{1G^Fx4=y%ys1eDlV2 z+5Fg2Fa6qY;3fCmgGWF5IsEZ^{|t|P`6doeybz!J z#DBsUAN?$zd|GhdYahV_uX_ad-S-;XcWVP7bnl!a8TnRuUc6WWkCMh2NutGyIE2@# z_tAxyzyM4-rtxQ*eSeOJL#flJ^zcNQz&2)Ty)$et(vxZy=$h7Eko}zFNeG${z~bIj zGpUZbZ$Ma8Ls;4m#z*S#e#;RTKPhAK=NWW#fK`Y#GK<#DFp_lu-Q-8y0K?|43P`8Y z>g&OI=yn5!=b4Nd%7^=aBc!scLaIO(^J-PK651#W&{tesTu^9R?~3Id+ASelS9`9i zqOHyIEt;>PfWgG#@31xQP4Df%dg3w87nOTm1nNp{{O2pKs8(XTh{vy6cx|bCuJ>

    wtIQ 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

    S^ZE_;q_Nu;qA9BGhZy|W;0f+HQ)dK-?G1d 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 xi_seE(G%m;54WXd9Oso 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�-H8DkEOevpK@-KcPnKJ71P+HI=F3Y=^MWtk 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 literal 0 HcmV?d00001 diff --git a/plugins/UM3NetworkPrinting/resources/png/ultimaker_s5.png b/plugins/UM3NetworkPrinting/resources/png/ultimaker_s5.png new file mode 100644 index 0000000000000000000000000000000000000000..29ba428e38a90b74072823c941e10a173ae7171f GIT binary patch literal 1476464 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 N_a*4HE(SsGEo$F;M*APO*f5|nw( zM)b-x*6e7m6?sVMbKfXGPE^m8pkSjgBzReJo=mE1!#;{WJsGIZvfm6ID~_m+yLfE+ z=H_tDz?qV5I(1!M!?W=h#wf$`j0CD2MNZR1Ff*=<7CpbxD*5wcjmDcbdBD!d&H!K} zj3C+B#hJaPaxs=6I+SK@rk{LhO}oabS8cTEQPI#GugOcTkmnisXWS(2o1l6TDdm8T z5l2}fA;;cGFE4OI&YEDEzSWQ-;sDtWm241dS}M7r-xnWVjC1{e8KM2WE8RT)oV;Blyk9XM z$u$iwOJ}!cV{yBMW6_d|B;t_2ZnC8JI_wJVc^C?M7Zu_QB9|oGculDKH<$b-=wiCaQ69UpX23=7xg}K0|>hI{834z z*Ws7G>Ut)Yua`3B%l^*$_IF>G)sheA^mxrtr=TjIOZ4eAqvpM_m_?_=HL@|QusCHJ!OrK!YsJ-hiT$`Osp?>1)l)Jcoj zJPtsaK3FH1nF6H7Gs~SOoY^mP;j5gwrRB{M{+N3Z03BV*tbwz-afX?0XK`yN% zPn}V#!A@rATd%Oy3^CdfYT(|>M1Zut_f>C7`&@Kw;kt>ZjWw6|Qe0*`(fsPO&EzXC z>Jg4Hm*Z#?YjDhQFJ{@UBDi42T+)W*Sv!+sU3l7=GyJ!*=Cd&vgJxZsD3Q^jaazvvoLqk9 zv#PZN$RLe#*bSw3J?l7x6~GrbO$TSm$ktEu8SG&du^DKL#O*i`ao0W{lF{l1xC6&J zIGV@rH1_|ie7@Qgq}$Z*m8&CEoxQzUl+Hy&!96hzodQGh}C2B?tm+#9#>w2xN- zL5(8CRQQQJ2Qrv-ug;QF+Frj@gTw)&+Q|9$_c- zIzP_KGVUFxW5eGm~i^x*M-ph44 zq!Ceb6SMJg^md*z>Rog$(F!m7*DM*T=|7|wt9JtUl@WqgIP2i%?Akk@X;xa| zmB7a82OSdwv~$RSb}CyyHS(3EIBoz?>z5p8g*O{t0+4KC24C?u{OY`?M+MT*y+)?% z4JJ)K7`bHg<~fTK1Lh07sGr-}`s#h1rB;!_w5*(2Q>b)bo(XCC&=YH-k0Kf8B$?<2 zp?mSWw*+pv82OT>I>s3x7AF?fKcli+zWCxZy!+0(`0jUq0q?#4HQetf?oU*PrRHZ6 zMQxP1J38Q1=PPjLCbM?9*IB%(&mIlaxu4!^Zoejf44FK$4(1+vRafKm>NI=&Ok*6t zWlS?4Bj8$7c#I6VhSmYvo7@`VJD)KYsc34;t|70X(czhj^3G{by-#xwyDw3WYjswS zipA_?S(+_3Gl2Vf;(ov5jW^yXBloeF2mTZt=kuUU*DU8ekOJnQ?hZS1RQeu)eYY{i-=E7m*zbT*-rP>w(yIcm zRIdyG>@6o+WhhFm38QQ^4GTxrM<(oJL)Qy`WOVaUXWA^^yNz)ht+c9n2JC}ujGMyB zQS`=b3LrEt9tZN2?pofnA?wV)8<(;*XW(0AU$6rL9$Jx0&6Jfz#EUH-hl9dI5UIUQ z>2DN1^}f(CeZ>~Nch>($8Q+7hy2-j4$KIkOTD?aeox{=Pv>J9i}p?L z6n>>pcMa`jCQCT6uCnY(7Jk3(C+Eho>A=N<{K#~MX^vY=XNL5Ub*F<=xh{R@64xNh zqYNM|o`Y6erWudged&z%q7@oBLoe740DwE`B#%0%meCsxK!#0ukzono)Rd?KTgsq{ z>W5PJ!L%S}K~}#f^jW(_Mmb=G$)dkYen{A;Eb=TINxs^E%IF5q$*I>ot?v!m?I4O} z75RqFCFt3xevQ@GJo@=}XUUA1UcvBzIe~IJqo#C@0U^`w!NeY0v^JWIlWiK0*&bqf z7q4++<@0PBF`jl#x1E5op@1kP+a7S{&+z%mW2jBqKkE0Ff5Xvt31UUjlX*}Se*t)O zn+MJosHEhDjSqy=6r*x+=vJRfK1xZHqSn0*5xtTydEG$k5itO6He(>cuFeZK(4F$V zmC3@49hA(Cx$YGRchQ8#)@3TCY1g{_ahm6)Su9=$SR}k8?DqhF?VS&6DE~`D-{%-M zxI4Y0zSDxr0*onr90AOfTET2oIwqnNt{EEk&{KL=+15cR#ue|b!}TpZz>rx{DX)4* zT(aH@YO3n~gtiY}TSEyzPm zK981JG-@4K3!h4DjCV#m3L2A0AsLtyU1*;~6WfA!M0Im7;F%w3XOZ~TiUcuAIieM= zaMp!P&0F4YIfSzIED+Ik=Kji~jA?x~c}3oOZ)p8Bi>3|wQW-0bays*Z+v4-=J*99R zSq(auN^=?1x!H-LR~5VZs}!7iFjTMaW?3PpfM_sG$5sHD$^tXEb!Gq+bpe&Mqbnqv z_Mm_MekylPvf5=lgpWp_LyEb&?vJQc(T(18VX2bJ;8sS6*7oeU)nR(7)*);9%jaL> z8y|cP-~Bzm7wfp;`STZ|kKw>fAXTPTbYaI~ng|4izlO=r>SYuu}=Vh{DwsPiu)_QB9iWUbLtBvhD@Rc^B>=A&jS!I+M_io;I1xpoT1V?Z~W>gQg+H+3`62@hQ zM-Ynz;4Ibc`$SYJx%KCA7JE?l#R{y=>4!GhAI-m)VVPNN23+#wpyfKYBH$Ey z%Z{HJXd)3%MkA>6;7O{|ts!H4ze<s5g~t_*i%TT4zyHgKB@3HE#J; z0oKX8D$m~l58cj21$^481lwL1_pj#GIv--NtaIr7g);T3;9E7Lw`1kn$t^$|ZItunw zl*9)LrHl#ytYirqT98FWS0pIa(2b6vO)srRl~vRZQdqSXM{~?xo#N!uJdbRp0}!Q{ z({y71BmvdlatlxIyGHu%K*p)GLhg!9G5H4U-c{ka&6g51|18r)m-ne(;^54oYH4yqw={19jnh-&pqq8I!_D4@Yw@y z;OCwm9%~MzBf`cYtPlh&0V#GMJ^9Nan3)MwG%0#s61e5)q(`3pIrNHBXzuG$@*-OP zJUU8h54mFgaEJDzy#S7+rS=E`-0zHnx&-X9SLieLPDd5C`x)L3?{5hy@w#MDq{=)E zwkSi`4`3molu9(HD$tW1A#X{-k1{N_GPzp6igypoe)sC4 zgYe~xFYxuRe}G^3#a~7uaKE2@-(i&0hLwEoh%wZhJ7a5PX?0@B4R?btRJGwr7KdMQ1gvOaZF_OIL(Nh+MW%%^g|YF)l{2_w@@ z>NM^rUcP*ZH{X5}Yu#|3Ctkj|&Wt}@AbMhm9hHpK7GA- z459&j_IcvvPJH`2-$r2Lul?tL6`6r&Z@hsgm%S&?9RVC?OJc(v#Kx)qC=N1MM^;m3 zMD;q^JF8FD-cZ>>+MZH5%+T?J_5*=95|JmidQeoi^%K0Zh}5LN(38?7|V{iTr!g&s-o-%*TrW-nLD zvC4%rGiAU!DC&E0%||jodIr$JC*X$Q+{bXq67K`!K1|+&Av*OhB!8VvWCwsOvq`${ zw*!@o6pw6YiYM2wHjs_!*>}dHnrYz9`?6F%3&U*nW?9n1>|10M80u>?R2?^udlvhZ zb=Uf*{J9GUuce^qsWKbLp;ijGkhi~kD6{Ak#=I@MKgt2Of}?;_RSj_};rs@~lMX>P zShm4m&+z2ByratgHnEGdq+EhW9u)R>>V@N+x{Yha*u7Bgg3PL)p)Rqm^PiW zdMR+|gpGgqnKUiSIqtwC)VU;~3_BE!8+B3sbk?#$Ky_1MA>Yq(W)HFQ466x>^+%10!bGRYWtc89TKSiPnIt}BVwLi$6}yx1T%Ry zWbf{t*j997*%6l0Q5n3n1KM%Q2J6I3m4L8Frz=qtOA{%vNL;@=0{=68zLEn5(B_zk z?O4oxNf9jN3k!oRK$%JKvCqvqJ_OF4I989_vcVG)rm+r`w>Mg-K*puPhNF}vq_Z6- z6&YC~&j8{!Kp7-h9T30(0~v+=Qw+a?lRCEDMG06thqH8`^uQ5`N*%m=jM(|YDcllh zO6BTmAcJkKt+b)wvKWW-Tz$s@V5BW~gxDht{Vv`Q8Y_;HvWaUwa~iB_(B}I#hsNbc zCDhdb$;zLGSk#(eBPalXJ!f#*U@{O`tuV`>l_lUPgmyVaiblLHr}e5lWr8`=9ZOKC zY-cu+uc;Ss#ERUly|*yjXrx6WBY<+!Oas4!k@jkC)}JBzYbDyBYP_nRJ?>S++V!%!QO${iQByaKD>)Pd z%g)`l!kR#q+t5?s%8-YYx4`FR$iv`e^Ch#BwAW~U?~*jjq$vK!MaG!!a#F-FOm%H# zKsZnF$_p!^oNYWmzJDO3%bMRFod_?UKgZX;{sF%8i@$_3vm(E3hgml8jKWGc$OBu? zTN>w_P}I2I>T2v-h+5f~JO6LuI%VXIeZ>JuXAWU-s4BD zg>P0rMZfzU_b1@n-}x@?Pfz$Sf9tQ|xZOBK%fzkd{^7w7npBj4`q6Q1)r*xsAcl%v z&286GZz3u2INI@%V|@*)_}q1L_VrS4J!dFxz68Ipd;wggPDg~^FM|Oh*wLvV*_N&u!H_5q zmNJd>4(um~(z^gOasZ9(w9FI600V1A6h;p>)rWLJ^a*)tB_}nuB3=M-GW>qLmKei$ zL;6KQ)~RPp2m6c>H6b$1n}gmNOm5t2dtm8gw=zZ!9Q9ltQ8u(@WE2 zj`L8j(bJB>3^^tzmTXK5;o69!z6)Ut_e60+XOX_%I|O~bo|fOn(iOx<%P0l{Z{)5e zhn68kn2&c88{(n&m}s~U4YnKk+$`+fw2Z>^eQwECeHB3Ge?0F~i3e+xm5Jt~2W1=y zP8afTgTdKyXvrz`s1s^@bUjaNtmFa8logH8viOC(#;r);;8|s&%B*hSd+lHvN81fi z@9BL)s^1g}hBUqn0LoC?g`F*5Ewq02&m$uJsXkV5!duPgfZcXQD1|+B(X)dYEc+y5 zPDB|i*>Zk7!79kT-}i7dS+MxSECfzMqRoED*hrmpmQv~rlUD@~wl&!Gv$N~w5|F2j zp68UJ=!ov|%6&U}!`;eEtu+Tj8Nlj$(a#~ndZNt(kTLHgF$J#o#XRlO3725-9wPLL z4$Q{1QA*K5#HalTrlzoA#swJlStRTLm2c|B>YaU$in{PQTRk~;uoHSwzctryTa#P6%mAn$Ck*a#pSl#EF1_td8%j z??egEbtKjyWwcIg+zG6Lk&C^fK-cx0d>-C0gOiQKiM80sErOfrY@imd{mnWL?Mc1k z_GmqOW^E*=y1;8EtXS+2meRcJSxj1p@j#y!@3(@0)1^6<6NBaTowztQE_^?Ms^?J6i*Zzvop4+QY~tSxi4m_L#5m{SAZ29M)56VzD0 zXI{_XL0Td~AL35D_0HRP_Usv+UOvZpzQldM#7_2qaid+|bL!o>=a0Jobnuu=m}{lYalih zq$3T}!Q42FeCpmA(E8CjvYyPzq5c;Vt!H6vJicEgGTe)`LOma>bVB>1Sb8=tsC_ir z%!AD;lMP=f2HVizD*fO)?!m!wOt^Pn>5+Az>AF|J%Fb zltQ+yYJOeB2z%m|+Eu?rgR<;MdCC}Pi3|8#muo7;!SdU!7o7D{S@4L~Q&1`CqNBR> zV~w1CjT{*m#5A%Zu;M^Dn1F}IarUoeNZyK-a!@SD21gFy$o(1Z? zEjR87+%rmlupdpLYHcgS7GurXf2xlMpTDXI>D=9SvofvxzVKc~J*Qt^Hp>#jhGm@B zZG)dQd<42dt@eBe*X0SKE`KJUlO{*lYVf+`g`F7btbM*4_A}UKs$giWy0`5N|L=lr z2Hr_$=Jp4d$@i~PrXk* z&*bQE;S$D8+rIWYSI|@EeqF;LCxm!A`^EiC;|6ys9*6heL<4@+DM%$i?Wvx^ZGv+7 zlfdaCVuMV2$amHlzTBu%jUB+VWA%Zk(!nG2E^E;sl^SWxh`3X$TXkBwSb(wgNb3$VY}K$?*?4ayaExgo zt7Fnq8P4d)))la2HG6eA0RkG2%V%DVJEh3PspmvdTMJaZXD+wZ?4Mz0 zLUqN5Cj%&ZDC~OkerT&qVVTuy0^wTVF{PrqRPCWmO)Ct!WJ^#q2=QB*5wos0)VVZR z(@|e0cZ31pfFeE*WuuhQUeOIZsiKzMyJ;t!=tB`UcJRJemc%Za8b<+V30d*h-zrNb z{IDHLJDtS9Y=U-=YDHJVF8bApHytd1jLiL@Q-LFni{>{5&9dP#k?WO52fm<#Hc}-J zQrVR$I%ywpqNs%_>Aj~eOyM5^;Bcgk^bu8WE+N0V*HO_T8bGB0T%_sDX~~6IM9+z3 z91&{Tm;?5dqO`JwdW_i6Re*39x=R4GM8zgxY^S$(K<4Bz_$~00tH+ea1v9S& zf3a24=zzlXbQpt#bVxO*PGyMJJ7LK5tiR87y#mmuAl6w7Y6iK|C5phYRQfj0V%)9t z;ka)KR6PQ;og24f;dZ-WpC`^2PuRI}PpEIkH9+&zIx1dg$bXg3>z?`O{CVBIA8F&q zIW~S;$H%L>xDWeBt??(_t908>v0txS<6}0=kK%=&bic>FeEi)*t(hPJmDC4mtx7XYyr5J?wKNLhD|Ja*Mq#UL&SSWdFeCraU?4E zS2}}uY@Z=Hu9Dj@Ed@>_jtm?)n6+_UF)B*0?%Ma-QsK!OmpnxA(z~9*h}8qKvTZ`U za$a;2lVz2S5!YbQc1hZiui?4NKjENDVI*&s&nW;;4~<-U%`M&4{To@5D$Eue*uFMt zTAagJ4gfeom(9}n=G}NB6ImI_^MFOmo7q-1{H3s6)H*`tKxLGha$YBs0$lgr9ZXPD zW~bGfqKh^$F@-QnKGHL~7i8GM#P{S^h5dIha0KHHzyDdh0-ZIJ0NUZ1)l0Ey&Lg3{ z@;!pMI$6GFh-XZX6-exUSH({oa48r|(!3iB$eYU7eIxGSh4xYtnS4qKoDubJ+e97w zEorL;zqRrzr?RjM?dypfmm}J0{eUW$I&>bHiMTbr$=s;5mem-c9;AHcn!x*K{CuTv zGCT8;*dC*0D5YRd$=JA`@_?)XutZ=sq9CUWjaV+zzTP&H0#0~3hex-WISb^UClxMJ zk#-SidFvLY!%Go`?38Q?8qYgV%~+?$8O3~Ys0`_(v2=kDTOMzZjs;Y=8_2bQ740og zAWt7I;2JMRrVUe2E5s`wYuT8ehdeDKEXEo+ohLQO%vb&Jg4K z{IgPbqxjPbzB6Y0!>zNnN<(MDT#N%co%oD)aBW2*EZ_~TTo+uUDg!`1IyQv|wrMMe zra;qR$**3s`PT-3>E5JQ(F8kpE)V6oag2_nDq z-S6V9H{Vgu7sF7}j2$nJy+l`?lbpAfe>M`gy!0fAt=Zj+oau6n@n6e$>5w z(lyt$#c!0{ytp>zprhy+7;yFV4E3aE9l(@$A_f_`SdH_fh6OJ>gj_ z96>34TK6oLn8zs^H@`zV&1>D1$9q%H*2A=yEHQr5dHX#N$BhsD`YLc<-GR z5Xl`s;#mjSNL}=VJD(w8!liEP3UWy&OU6bVEGfKw`iI2z#wFBm9TvJ~rurJ{ zVPh>2+gc;%!S5+cV(&n@&m>xBrZ7`hkDb@ZAWv#Z`8OoPPh~@`qn%X_N_EMTecVrRH+zb*Pg3v4xd&1$E7fwCw(0& zIn`KYH|{eyoqJF}rGjH@SP0va2fJ^RhXQbav>L_`*3AoWTZnBOL^>9FtMe%*g$BLU zw6r-+WUjQ7{ocdm9T?Y;Xjz~FhG4AKgTQ1Tzbe`jlp)k_f@_WX z^H}sn(&@;*9}z_ibZ!KaGe63x#_%Zf?(I)6V(U8 z`Cs#pf#OXWi!uj`@Kd-yv7GbzaA;c&ixszGVk>JlE81naBC^Pj9#)@c3)tMN9A(5J zeSM#Qlp%v`p+<$tb!ELH$$n;rqzP^a3Ch=vD#y|}@LU&mdbIgnN=yznTQUI}^VL*K zJ0vGr~zo}Bok--%+7@R&Z>*_ZWMMKlP0F(2YJCtx9%sv z6y%LS6b+HSLk+T20wIN#IK&^Xi9k!KEP<&8`6mJ!vn1w_BlG~l(B6*h$blyw0k^#i zlH~N*V=wn6079IVA|9{;@mTZ8&!+$|X_$b_4g@ODbpgze-SGRQ!-tUzxsQt>ZJJ+l zf`ksFl+F16-0l3SR8mA?iVN-JLSl__OgXM{;{fHXGd3a?RwmB-OMK%SU&ni2`#Nj2 zVO=)UY!@Dm6h$ocSHZLauA0~${#iOo{fB5(^?SU|Kz%f-wMOV&>XG5dTXHX^Mwjxh z=D>cQ_J8i@Me^AxX7GxZE!BHP*U?z(5>m@hj{#q3r_YbfaM_cdyr-{jW$k0;_+4Ky z4Fpsfo@j49ynkaiM?r9du=~$7E3va@cQq}tdxmw zUia_Uefmru9h5&CX!~i__IUl{bAHq_U-u6F`|Bok{j~dA&d`T-PN(fx`ON44xc7XW zqs*VDrzd>l8$XYq`_6ap{KXe=`Spsxu}Gt)*kcKYD*D=3v*lyq3!sucnZagjIPYi^!hwk#|MLOG61vj(hs zcVX+bMhR?1SUO{%u_%ahsvIS;-M>b1EM(-Tjlk9ST8kFM(t0P|UO$a-$ zw4?SyIVQbLUN>*O03|>>zvGb4qV-!F`m+ju%UR$70M0ZD1jlrb>}a8GRE9=vK_DY= zKb_$#tcBJW+#Asm0<0D|&1Dz)XS=89G9f-Q#?>vH`JHPx$CND|UGnHe0)YCx24oBj zXZ*mba8Hfwfb-g*^CWZZ>BXelrpduiwiV_>eC{=3eV zFU;D}R3hF48wZ1Ud>8Iu_KVNZ?+b-^%2tZ*pF=`uIbPFdl_Kah0*zT-8-!5yd1k6N z)vIRMPx^edG1PvCXu$hgFz3vWVAg|)jC&hJCeZ!pdQd>yfk4VYw1GMH>K0)#*)=PVuR0t`ABDG_TJsq6;^oGv({p)6}7?#K&7&Hg%S*kP*oxZG3{g% z8;76>V@gape_j9`A1F||5+&cX4HSVr7Z$EayNZG%Ty!h~5ay*wrwE9l_7RT`!g(`T z5T-pUGUud-Q!I*0$b=4iS3oQuXkbxf1VLFfie~~8 zWO-TvUB>p24?sb1W;_IHWn8T&lm&5>bdNv-uH>7>8bk7T9njmb=X{L*R16^n5m6@s z(s69nMT`uuD43&CC4f&w?rab^0YnnZQG`+46hg`z2W(138m<|Y_okQRfcjvB=wgjZ zD&?kY#;#JTbf(7?ymEMN#Bf!)jLOA4(+Z_BtRYI345y$_g?g!_s(BQ)z8qHp4u+_n z93zJ)itx-zg>4twNDHkguF|-?(G{8Cd_n6jlI>MAM$37KV4iF-70z1hp%9kXNAo4f z3yrYgC?lb&oRH$0QRNDSDqhW($|$BBRmO8%^qqKCOZq!hKNF1$Hzu=fV5MpuWp>!- z6}cI2UDUy;2#e8RNV8F6c6KflxvPT4@&3__*rZ#6W{yT^8G-Q?LvCDlkL~F4=ojx4 z-w6s{5$&|i-;bYnr~v$dFJ#Q|Hygi)%$W+05Z2gHbcU*eFhL}hO1F4LHdC)8NZULg55}3-DgyVCqL<|v zqb$LDPj2(E(rphG92l2JTNqhC#C1-@+&Jz&TU`Iae&b<`m;4q>M1;sJbgFgJ}y z{usb}AQmkxf?uHvNDN-NU{6K%0N#`id@cyfgm#5rY zG3W%5V-i4mExNL(sS<1{xwk6xOsr+2Vu!zCna9s$T<_8&49`Gija@Xnt0_Z@qESY0 zR13Z2;<|{A^~wj6G_*hVrTjZi!fp~fg`6ka92_@aQHo2MhG-{B9gM=5(3J~8NU2h# zc``Qd#b1hhi~+5Vk}*-};Aq1@>@XASvxrTi3TK;OH|x;BFPbP-nJ*vnZ0=|qeE|8t zmGo^DdBI_qR3@o(^n-ql`=kIz%4dvcz{l<}ao>i$T69DUj}Tj!m#bGc2aupq*uy5; zXu9wzE7A^CElCl(=Z5)>R;}o8$@nM=N5^$hs_;eP-Yhy1F|IF4Ur?zBR{V~~0^ag3pWct`;=3fZq=(TbIca!MRpwD*NP zm9>_6HA-p=`QpZL&V9L`im^(a9I;9Vc5RcIN)t|(Qo0b*!f?xDvHfO8(36AI$1 zFm^>u>+;dzmX!`dROUP0tIWkL1#@%|ytZ=SReUEzCtL&wUGdz70#XzLKm`=L|5;xlo%1#Mt$LhA{~s2 zBEn1+b@%0eV~ibD=D8|RKIYnn?^%z{V??637^MPt#dDXDCD~h2EnY@$l*214b(gGs z#yKShn~>a#buiu_sc0UQlT<_FiETiIo$8AsjZsACuvvXDgq&4)8wwVC*(`G2Vz;UC z%g&A)f?=j*&yLOLkp*?Q2gWPq8yqk_H;BtN*P!z6e><>WG_cv`4*0U$xfko*0Hll zdGG4D3w@)Yh@8dq7ZoJsx}7K*yw%GW0!j=$Zw0u{bBDE&vL9i`alN}0L!!K82jdXZ*~9b{T|HopXgb)y58}xW|d%rpxlm%OqRzE%!eXbwd^#CN4d(p zdkTG5u2y7s#4-8`h$ZsW$0+jH@5n^u?JpbUrN8KK6f+%yHq-_;P zqssIM9Nt(rFvw_`=aL~`?u%d_7j*p60r2D%zrY4t#us*9Ii~V#Yef+tkLTJKXCdeb z9H_oazWlzSvqQd+Y z1$v#>JLNs8%pHY|uaq!@bhKBC_0~8mXImR3EwEK&wZ*f*raAJ(zj28RZRyK^qKGru z3o)h0R<>r?X63vW&s|xIf&uPOxFYZ++gF~Y3TDLbsd8%-nGiU{(De9a&7^`oUA z5fz;;gmJlWVN;KfT?ShzR^y`3Mg_)4RA{0gWf2_~F|>+yRg}>UIV=wEq9CdYY7v&v zIV|kLxcPOZC0}qL1Q?Xf=_1PMR3*nXK4ZlxsW{XMgk~3%yAB{wtg8~0ZLD8HG$_NN zS3!Xn2a8B$D>O94K?YIyI%M_xXf&3Z(0Pv(Ukr|}3VFs}>V+$x6q`TNC_y4GVl>Np zAvS~eR#L;1r;M&BVtpI$OJy7tGfqkIxj@U0sX4{*^dXcz6vJ9y+BkiB_$sPu|2ADGQOCf8V_D{^1LVH9P7X~azqEJhz>;s9Q83ibX=$& zVOI*xxJ26FbvU!WG*Dup)o?Njq+E)#I-&>Vyd|at`S@T^K96t<#o>2~SS(HER29P2 zySRR;Fz6(YSQT9hq7mU-IEz|`^n&o*fxQ)(RkFVzR{oF5&ieomy4apMMwhJk)J`G- z9x-~TI{33WjWN+9>+6c^#{zBMJ23{VOyd~(ui~@#upb2orm}l|xE^sGJjM5i!;EIT zO?PT0Q0hx?v~;jauv94pPU>65?hxLG(-3qQKe!R1ESJFt8zVAhpGcu`I@cj zkEn}E(5iG=!uv*-eqS7iQb^Y^d9LCf&ZAra3Q^^Lhz5F?C%J}?J67gM%N&TDi-n8h z_`(Ahfdr*hg7L?n0~JK-Z7;0%9QHggC(#a~7{%{kdwCju~7h0Ao(}xy|6f zW+!Cx^KEs#&7a>2Xx#k0lK~%Fjqzk)($+7(>EH41oEWS*@f?g_y9y|l0mkAx!M3ZW z(`^MfmE#G~W2DAN1AFBlXO_Rtj>tSS5F#5K_9;=6?c35+3mX0KxXol3f=6W)ST7F&Q9 zGRkNN6rXd|eHk`L*rPF>O0HKbF%?pAl_}GvZM5IKmJQ|t!mdi2EAHP)6|RyYUEy9t z;3;aTVy(RdY30DVV$XP|LoGN}FlAIxvqaXp3cPFukUB@G`KXJaUL38gb7@;vV+Li) zS!FBhA|xrPTnb`}T}&^e_-?7gqG*ZNb+$~A^~x39y^8y&l35mO5{VE9>m2HAs@UiR zf&)8l_eH&1EtnjGnl5y7jTo25W35Lzo<=*YER0)%M>3aEMnUG+`Z91TCVXU18OmU5 zg^B1z#;FSE1iFwPF(tnWf)ji{f=v+Z!?>CF zAju?`lq$l&;;+@S5nJ3*q(aC;Tc07lx#j(s&S8uxK0^?|3W_4sr2{TPKt5iH(ndfi zQslz-!kd*z)a83BIy`Qi#-}RjOg_Xli(EhvIiVsM5QGV3T2#SDsVFC-Blx5P=%XlV zYJ?e%V1|lyPE>FT;;nq7rmIvNti1O?>~ z(P0Wls967EeWU3w|BnKz%x&}#l?hVmTAnx1%8;nFN0oP=v8^M;c+mRYGV2ZgZj0{krz@5v>VpcvXnYb6IAll&?vFKFXMkt{! z#umMVwTOo&9&YDD>*hGYg~BYaEx?&7oaZ7svWYes$0yMv7YrI*R@e`#rtkv1g!L&> z?ZRq$TO#S@g%#dgiX7AU08xpBJ>W4&@xh3qS0W$9Dg@;ee=E+koH(t^x*9SrTbySR z7BV&&y9#Sp8X3hxcu+nhWJRz<0p7;#8BS9o`&`+Qg6@LBQ5b{1cm{de;09Kem{RiW zI(GaL>TqE<7V964F2vD~n=-7CfIG4CP$;tMz0}b1pdzs3O#sB=ToL9cD0}6Lv6W7F z+()YXeEb?g=7+zjVBiXhIW>vanmjL~m&H~OKgX!7 zi_DU$^QB4yvCu08%g`6=Ty7w>>H4J+?}H+U%jH$|%TYFX#0oJL6R|R;Fn96#5uhmc zW;BxJ24lm6Xn2%SxKgK=fc<7ixo}8|-AASPx~r^b6{J4iSdjhii}P40#)?ia=U~$_ z7q4*6z2EX4<=jVkx9JrMHx^&@j6Gju{nctUv{GcbrIywt%BOUhPR#M++iGt;7-q*- zAkYJSX6%v1?2_Xhkgcw9GJt3+aAU0V-U)z||1Vz0UoYNAA2+`b46tlHx5w}8_qINZ^0!UrHD+9xu3 zMNwR$T<}5OD^zd~#1@hJN139+XChHHF0-pRU&@J0i5+u%?5Y`_Ek@6a^xK#!pcKki z7S*XBk2ml0xF5pA;3x}++OY`<-zGY(UV(MGm`5FK9g(r2)poR3gOOSI9${{)GhHxh zb-Dk`tsjGdJ_=D;&$wTMy(uxL$Z*kR&{fHPiXxu=W2*KXyk3=i3+~<*%;)>@E-HM2SG9kzrYz7p6G<*ZN@$0^Q5^)QqhAu4Uc<`D;9hl4~# zxR(YM(P0Xk(iXxB@SZ#;P}E`}u0*cwMc6B?@ix{d35Tvi&GV{^jNSKb!SE7!3oU9t*_mU~brQXBS#QmO=zTqr7~ z<2t)=DnkbVDrsM#2aAH0y;_1| zVyw90jHu{ocwe%-!az%dr!U8vLH#N-g678Of09(*y*st}I~ zC>FSqy&G9%6noOSYN0?I((wp;&4<&bu^|_yU{g3LR;6S86C3w|3hgh!dn@dPv2zfQ zR-BJeb1Ccvybpp`=)WQ$1IuQtu2H!kRoq*J@Qes~1hizs#Da(3`vN#A0Lx8++S&*{ zlxI5rP$&Ww*hJyEy)bD7Sft{82Dz>ZMpUfet_lO#g|i-Il`qDr%e_!we5x{qK19k& z#TVIsMu%>3KFEw{%oHi2T7dFU6Cr{I1vo613wINMPf{sj#7){xHyRDvoetL8F-GHY zj#K5=!jL46C5*pftQ2}kMFUmlO~C33b2929RgtMGZnLNdqHK)dcnBmEtgdka>1O7D z46W1$r5uUBmjzTLv_h&@5G5cLuTeF=;u$SL3sYYEB(LH-(Z;DcTjvg?T@B8ShglC~xtC#}ltw*`grBBPgA5WiBWB09&WiwcD zB5>rSY>wiPpZIyVdOZ+8aI)_`&^@Zpb)x$`&^=EE6g`;tPjuY}dTn;h!re}M|0vL^ zhWHlG9QDY_Kp~|R^Yim8E*vAb8AWOa!Hx_zOx1?7L4H#u|7_?I%ohg9q!EP&LcN7sm$or}c3R1WP&Vybmr;D=MYp{(e z<$&@bELfaxkb=FzmJPO4p*!P6y)QtON?4HCg)u-8g$#@>Wy^q|*ezmr#^7vtB3&{| zJd&m%b__&EION%@$Zl1(QWU zEa_G??xR$h6Qzw7&V>%H9u;82MJwJHwp#>?@ZlX5V~vr45oAOqXd(wS+F4;nRarz| z{ExVvrJWl{p#q5ob}|l(7n?b*c-4-NXE+iSln!ZdDzf0C?v;CMqslode9}rCBo)^x zu&d(p=-9<8_%pb&>ug}P>F8&`_{HL{Zy|QxMS#A96E=dTVs(;8YxZS+_DJA&TB|!c7D`v1$yE-SD)R6Pq?YRbff$~SLfnV-iIhBRbF`|3y%Y^~b(~Nt z9IoQlK@nGs{b*Qu!&p5OF`85{9x3DnNkXYmRa88_aIR5G>fx3vU5+7kT_S*x-o<1L zybFrS2*9Ov1eFB+H=$s4VK?azeF(x#YYmAw_RdT3Dr1CxX`K~(LPg_J<~>QGKo4^e zL@z2TOK}8kSl1$&tph_P0EWUGi}X)RnwSFc5MAr>!O4&5OR?^OivUto*umLXp_Wrr zbe>XfM3xR-3Zv=@pc}Kw6~&j)h%>0Yl2*x36#P~y(#m}>fP@DRoKU|WvHO3NUnYflt?s7 zj2g5rsgyyHTIWMO9WiXmNJ}&dWz61LQR7MlIqE|?U`P|f6ANPrAZLhar_Om=?G`3U zuz3X(+4QdpGMjjx@SG~h-%^LF(r$}Ks;Xc&$@Jpbsb~{a<;?gv!nUu-2I)pn0OZGA z)NlHJu_?m23=ui>n4?@ec9k)^ssRiU5PGoAZ8e|~^`d+=%INXy6pmo{ZuMOOCu4A; z9IFb?BEOl+9G2JL?D(qUHlC8IUcFniW1FoG+6;C* z5Fk_x^@sR;b)HWKY;CT?QypiXTRNQ%^?FL4SxllNpb!FJ;sD0KNj(wg#J02{=%ho+ zyY)(BR2-KM=|~nr?g>u0Xzdnhjk$Lu(E)N}2?l5bnJwjD91l2&a>w(m+}Jsb(P*tg zI&yUQViEA5kArFxo1372gQ+Vgv2>^jc8w3Z3KiJ8NT)j=<~9$EPvupx-feiVJfnqv z0gB*)uj=bc`uL``1BJC#u8Dl+*7DR)nO}#JV$}oIoT5@Jea0-by-5SON-JoT6*B70iikAK!hmO(gg zM4v(SRCRfM%z%iSBe4B+kUw69U|j_aD(wJ8!LX_-+D-bk?40C-U#*J!iPaW&k-TJg z3cpI`Njez2EF;X+*elAO7~E%UI1f&*ZGQcTDM%HChc5;(9%T~YZZsjKM+GETB)3)x zqkM!?U=o9MULsmL9*}Td<1k8xbOBu$h|$T6##@2$7n=zp)lZtS}R$4oY$1FA(Cqw^rZkP2xqB9O(Aa!Mh^s=%WI?JX>)3QlQ}7pX|J zp;k{wTTPN$9c>h;PAG({^W^yuXDvyRl3Pb^E%h|Tn1oTDG3*V=204$@S>n|w6F z7)8C=q>-j1Nh(L!dxpaSS(Y;z4)H*}UY9jAS_&U}If&NfH0yP|&1g2-G}~=y;pTji zeql^PHX4zQGC__BEss&SJVzOgNhDPz%cYQ6s=(SD>zt%D5FB-*3&oI!>lrQXUaZ@p zLUC6YBBmw@LN0b)Q85ioHjc}4l2nA1b2b!-4niTMfr+tM$;AvTk;v%qMCU)45Z)16 zyT;i?`Xato1uZX)K-N(9LC^>U7|3)a{AlFmj13m(6zFaFM zV2RkF(J+-liaH=O5=D@C5@5{-%QV#Ev0-m08D~-N%DaM=?NMSdcwaK(;*vOzNfTlO z&^d>-7Ng7VaRM-ElGKFOwh*IBL~|k(jE)6lqoEWPn%2TVjHEF|P(;x-42nDoI_IV8 zyjCG^KJG~?#&nX@D7zQ=^7+HB&WVFzouihfI3OF1Xf<0=Vs8nM6h?Wa5L+R8Rs6w( zy9UG6mj-|jQM=fBFhW@bFhZ$dnin>KkAnTUafneIo+m`Xl?otoJgY$vRiRxK4IZQ} zP>c%3CMGHtJ}oX#eh++!Yit_WW{17l|CP46@fD4Q5=08$Q*kaT07M}xN?lehZY+RR zG;GAc^Ili3U9|zKV;+}7@~U=1D&LpiDbG*knhH7BgUm8=Dcg6QN_%oL+SKenu%FdI z9BRW2iA)B$Y=vmA5`7kA&MCXxM4Q$ajYbzb zJkFD3VTJRfYm$ynWDJ$*TTz>Od}y9o%5)XhC%zv)k1vBlk@Y}vov?y2kQ*80E}jct ztZNJeD#@cM^`cw=d<@o8+E_)3W7%yi)>-tvEOKm2EbTy;FA6_S>}X%CvG+0f9sJb9 z2z%Q4(lAn*L`yJQ?DvR*d7}RE-lBC<_;x@so|sB62^6ZBBc}puA_j+p9jgFqEoc*1 zdqgu8=WVc=W07$Aj@BZ7R5-H|I4{!`3nMV@b??#M2+*P;uoSN4<2Wn&F6>3A`4~%T zggQS(cUFfB;A&AKz{ljxQdgE{Vz7gX@(CqZ&4+YWTY2kB#bez^Ax@S z#f0^&ibgfnV2nlpL61g-h8J;&Lh>kW&v$e$K0Rhs7cOgXUR@B55yH{oUe37!)h$>Wg@7;VT)r5rE1xO~YOe%g)S(@YRS=y^ zt+3gMB(0H7OtHK^;?|q*;@&%NXSlYEb)MeF8s6pX*nK*upLrhp4%|=OBq)`W2SuG=g??( zSv$HU6|9|Md1aYqvq_#?wr!hYW9=x*t9_h?JFovf!}T8ZG$pOK=-Uj9qup%LnVKP~ zB^V`}IRXtPNzq1Vy4FfpqIxYQ%jAHkX#zG@EH|Jfr9msr5M z25pQ8tKg_;1+`k8-e8ErQ>)eR-jZcms7+xpi6Jp5k?kVsH>xnGl+pr3>7Za;4sq9p z)Tunr!3#K|6BV4;Q~)O(m9mx=ZPpe_*#yC5tu2Z~MZp^xL;~2z=M@%hlAx3fDqS50qCXsm{>;n~fc2!nR$qaMWV@&8Q5G0fGPFPoAFN-Z@i?QNmzLO*g zfLXYI5gF9jAa}qs91cm7I({@@vfZH7KBp)`S;*ojYd1N21xQel-6hiE3wa+^lUGq| zRON4AgB1qhxYG2FBK7Tf_DuMUDmx zd&Rtj7*3@IM~v12C8pzx|CQs~(!ni(I-!K?ZK_cUHtSJ82%Z2fs$Uuokbh(^R zrdb^a40y=%5lNErgeO0p9ox6jY&D61+QQN@jhe>hwwQ^dvy9!VMi2rW!Wnd=0lL+2L- zI4?oK;BXtGL+}i;JQsUS8?2Wi#A%YCyv5pJ%Odu19J7pD;eAe?=NO%Y;F&L;FYZ?f z2t+oHtfyCA0=-HG81+6{lNfoP;ra&FdD3Q`;b6dU7(jvGGiWuS-E5E~8f_A4X$s1V zjgq8dw_3~E`Wm^*Dm6hwub7B&d4f|f0FTnLN0gWSILvZlU@Ol|jHJ2_S}j9?pXR1TnZl0A*a^&seGcD*8r89Z(iY z6_CJ?;jrEYZ%C1(0^E5U02Dk%d3?wg$b;?ge6ZcZvnb`sY?*eFXz}U12W^tjo5Im( zG-)ymPGg2Q-^4l!pGWkIi$ev!qF#T9RO$qt{<4 z08MKH(?uDq_o38=qTQ-vw842tQmbQ7WZ95WmJ2~X)bcYrA!(#i<7|}4yebdcV3NAn zbKX-+>sXgF?Dwf9wO}JS(poCEa$sIZRu1Gy^AK6EnC|3bDmsB-5^UcfNEl8O{kPs z5phzYc#J49>m1gFB3a%8)pVC|kP%0zViL$^(!QWpL)9qpltsiM((en0Oqb-Dv6oD= zAKO&%dgAnI%AKVuhQA1WRQ#?Ag~Q4yY#2dV7%&zi)(|anQR)%>4ppgr%=?o$?G5{w z)(nT1``r57Z*%44Kj5yr?qXwg1!WYf-eoi#Fj!w?dS)AEUGNC@-hB(FpEggs+hTEf z1?3fuBwm^R{;FMF(rQO}m{)78iT3jU0N2q$r&KwBBNIag8HuP#b3K+_9aJZ*$#kx3Tl=i}~cgzn5=*;-9D|itfZT zoyjRS`hAqox!|D}v+MM;u?Q+2k!Yn8bV#StIzgL+BngoWost+~Q{ctPR4PHD2r`%T zdL4%skOj~&$Qn{xq>3CE6CxGfQ%e)UZUaDK5<{K`VMSClqC{OD_G&o1(ePIabef=j zU@3&>iTgVmQrM6#6sxtzYlMC1yck}IDJgDoJ4zOlM0g|uBT7zbg*Ha$h|UF2D#SBf zRDLCeGDhyTQn=iSGDQ?xnrQ0vHedhR*SPVT?@>=1)YBUEG-3Ppc_!z!(P*`4w3>9g zEgtoVM{&*>yU2hP=nXWV``njkHtGzroRwoM96q#{R=Y#)a{7ZFk9gE$*tuhlUcWC! zS#gFH+C*R^AlktgD$@JBDhx%Gq=PvRCMXHCu*6(2taUJ0oG+EM4*Ew^ag(A=_(sYxhd+kI>XWNo%^-|D5~#Oe~wYmaaXt! zB@N@=>`ce^qP^`2XSHq~*&JzDanD>^6`Adx{rtSO-@>L!fGy8s|6afF~c zmXfU(Rc$f#Ri`>We+4WkBHeLc9p{u*jr6eZw|LG?PhSqAs{4;~)=MyC{O@H{G5+0! znl8sZlLDttuk* z1A!gY@5lF!XPs=Go2`L}aC;Hyz_co7d z>vMd9mx9z7R@XLI9f(uv{SbnKQGg!K2WLi@8D)B;NC6klcVw&rMPywE+s`@&3^Cdi z$0*9|Xgf%%rO0)lC=49OBeldFi7{igtA{XlS2)6{F*p^w>&d*udrKNmnHDQ)IZhmz z5Q(K zJmTVunVX&A`fIPJGqIg#KjW$3AxR`ycioM*bMu{dvbMU&#@Ya9b8yTD&1Gu9kf}d(dlGMnaO}VWG!hjw26iAx^sXo;;6s ziRb|VzC0R>w@8TsrO+M$*^DO}jnKM=4P({_D_42QvRv$4A6Upr^jyK3ir}JxQ8p4= zSu3(U!&}c_&}Vhw2)Tn@r(Zz7w+P-f`%_erC7rqBF6L zVK$&%H)Ml#2K@o`dY##s8PX(SILc`?8=Nvf4I~VP1GI+T$g*e8eJHOm^@PF30;9oz zW~+lz&}gP)qY;C_5U&%~R+dRkLU(G4Y&68>IaxMhy`M8PGlg;kR+iUkG+VS<4YW~o zC#TuIeHK3)uz2(kNs=%%HI3E@&Pt#+`liw8kcJv^27`qnX(C~%J5OaV=B*Nwexo%o)>;n^uMGrgQ4t5t19&_LArMZKU+&- zXH=*L8}_?W4uxbOjJ3}WV^tMjz6icVAxmjnID67I)F)IrKNb_NBqhxZy2t2g4ztx6z^Tu57bbv;p=mAZ(?f?xW)rqf+=-WH}sqBrc*XtwA!+tk_}NSmm74W)A?r|Q(Yb(+m4 zr=K!M+HTRFoM12*&`vzQ89t#T#bf>17oS0&5Wr<;bmG0yujm9La zE6Z4yF*7|yVhqbGtAJ;EY8qt{R+d-LMl(GDf7y(kw46pzush&EUObd2x{>O_-XUqu=kdzOq82S!ZH$nnAzM+Ug3;W`oY; zH0x{Y^fp#$x7&2O)2yzo(Ce+!?M~2YPq4DQ!l=K&#MA_}T7%`KRXmo7i3zkaEG`{G z8%=j|ibTOMhgzdWcVdQSy+NAPXf_h2x6RX@n!*E()+CdYGj!VBaMtCOrU|)kQtwRA z(2CK}k}frAG#i+-j$iV0It^y#=BW1v40;h(H}Mw9+%z-Tz6k=AIm+6+g1vMeJ_Ycv`yhQmJ2dFu5V zCP^9e26$y@)Y>Rx81(xj+S6>eu?~8@9;w!}T5U4xM41BZ&IC3eF&K<6X+o>nVAvm$ zVRf2*Z`o>Y+-Z9XgEY`O}){;TFYoOKw+pg>R9W@ zM;U2iNNXv>Y)Eb`MkO>F4Ti%Z-aC>+SWbif0PC7GTkYeVvgmAW<%n*M#slL8n&s1! zdM-FBRRBr_6<-+Y@jY=;V!82n?RejD#>4SWZ?#s|n3^pjPgNKo8mdrToNT_vmfr~*LXN`t**g)4QMqWbk4^^gY&DL}&$1Qhbh5v1ex2xfw-~_# zj?p@XvQK#@gCNt&n9KGc7s@MQ|(w1YH&6jCVIhpZJ9Y9mR_Lz6^6RZ?1|Mwk@;*Gi)j zAM9xf5_mj13AMve666b|0hAN>*{Sjj3nRgyk`O_5mK-hJT2dpd6vQld+q_RdGeEgf>Tb}&iAjrno~~O#_2n! z#`e#V)zi80_B%Lobd|&V571v*VRdx{trSO&9%XH9gZZ61c*(BQ**QHyZ)G*qO^Jb5 zk0QmO3MRBP@loJ4edSQYk?02HaZck$d69nR{iu{FPW&nX4P60zsTgPqTE+!G&N&}g z(niv%BPbKq_nFv5NBfJj94FN z1W2kJKI+Oi?l318C zwR)F4hs&?Np26B0)AQT0-qEe69Ju!`?zn$}cB{i+?HGgph_qH`VycT(p6=un4}16} z{Mb`|oHNdS1dIFbWZ(Y1%+772-E8Ar7QmMv36%>G{!sY73X*6~C8SgYs!GI!(w`0P zakO?+BqaFqa}df21a^7sq#K)eC@OM1rf5ZUkA2Bivs z&Lb$A%(X8T)rERADrh`KY?v&YAwEd4FHsN>W$<_x>%j=RjwetdOG93T_SI70Gbnlm zX)R@KZH-)MhD`JEPkfFqfA-_pVULN)S<-fcTzLjIW3aZ2W*M(N`EW#9>(FX;Pzfxp zuad8=uzhY9NA};(s8Q!z-}yGrpV`4Fr=7~u;vy?6YYc}YrluyCo1MgtGDd@S@;qZ? zaR+(NH6V0NlY=3r@gouk7ZgZ?@XzxZMr?OERYuYb+1Q+9)C@}YnD6H-QOKkGc^ zPd$w^t+BGO$mwUC$JpE5)MFT$4!7TV3tg3Q$;A)l z@P_7wn{Q!T2OjmXhq16W;>KHUV%t=QOCI)6mew}7^_Cksb@whVc<99}E-rHSowsoQ zx#w{9xsT-jy$3jO&+R@H%)H3 z{cf7ZanS{5GRPcv-g7?_t(1p8>^%Ct5qI6UhuO&q&U*NTEUs*D;J$k}b?0`@I`3i@ z7FO7M?_Hd|dpGTK&*ad;5=Zv!;hfV>A?=>d;iVN8jvnUhQ+A-t4vwz$SYKb_jI(xP zZO*Y{tBi&loORaeWO>H1qsP$3aMn2&(4LxMDINWbtggB*;qPCk|s<{PouqOG#WB9JISeM?4;hRq5Ozl+jinzjPsv9a-MS*tGYnLXY&fFbs*AytG|7eo8jU%!Y)Cz=lUs{clZ-}= zMyH7z4N1}&dP7UIRY&C+iH1S0F{wd?yxGip(liOVz!SJ&Xx2=E%X3H)oOh(!&>s$I zv|4!Us3)*7%xTmb*gU6}c>0;eq$w_Q?3$aTpGn66=RDhHI%K)W)KZkQXu6CXpb}E8 z*f!fmX~W1Tq{c(m!D|D`P*kt)kx1us?|=syO*o zc9&eQ{0vd+1m`8z!-=&QquP#O>fT@rJ!t1Qg|2!Ie> ziO(Jk1MSOsDfC|Wf{$GV!m~vOIy%IjqJE6rCyvUO^&Y4?z+!xte}}Smk?6iSukjY; zv&aIL^_oIq{IM=g6&rx#L76xaTY?M0@F`eBk!@O?>*HRP??zifg>?&)wH1i56~Omk z7!zB8Fk1mDCu6&8b+65zQQ?_3zqY#f=KF2^`3m}?jt@MY=-DEGCAQNEfSr@PwgSB3 z-+x#@tNJ{$Kf)uO5X_1O;mIxt^+uh^$qDF$AMHNUM=DGrNt|l{FSu zR%o@`%uLU)wzkC5(khcv(@aiIvb1oB^^J9=XSUI7wOKrRgyE>i^z1zKdW&O+5955! z%-lA#G8{d61dn6;+&s!T7LFZ78JL}&!(};(%S)KF!M2$>Hu|fquB=dRG?<&7V|8_v z)%6Wp-40Vz6Ra#RG1%zQo$S(TcR03ijL~qw)buR%T8)K;1xC4Les+e$Bpg0+80S4Z zw{OSVoW-RDJcil1d2BXhabbxhO_`dVq2JqJePx|iBV}rOnx&OxHr7{ZbvjH=&a$w) zK)=^#cDhTm)n;K~iT-HF%*-T>TFT<$0$FC6nw=)mnx&;htcU5DNt9Bot%l%Gcakj2 zSzBL6X_)GE=#K{UdqWbH(3YI%;X1Mx(~Y#s;G-r`D{|s3r6^df2pq0@9?$ zU^pPRIqi0n#3b}XAfnUgV7+HB9HN~eO%s}}I?YytMynwKy{SoT=BYL6D5I!1>L^{> z_rVKbZG8i8YP36DTCFAqk4_T2(G2ntsj4wCIZv(CqEXlQP8|gdGmBPGPg5M0Vdkl+ zlv+I@&vU%bQ7XY0L!MjmY)CClNYgsKQHIYg+DNLD%`=hRCLu8igW(Y46=|(bqAi2b z2=5h*v?giIHph7_K%Li&aw#&MqzT?y@?4U$O_Gq?q3BC(&_*#D*}xhS&>Qc_GGUG- zi6P6u=0M_5N^Gc_R(K_;{?1AeS|tMLIGf>-x^m7*oxvp4q((E!ErcSi+8ahgi&q(F zjnN6tDe^ocaUNwfS?3yUi@%o=1-wLHNnK>44rn9^^G+aSC&{fynr#9w5CwLPr`^+x5>)VF)%6H zx1EX87N=oxb%T{dM@j1q>Q2!+w17%9`|m%Mc98TN5i~H`p zkx_fdw%J+oJR|oGh15jVL0%TYo)}aLziZ_wa_)=3O!$n2qW+tUK7MFa5dvTi(8_@~ zI6}S4$O;T~P{m)5Y#4=C!lEkfgOn~2`y<$QQTD59ph{Q7djx1A6V6xseQ8x)+MeP5 z7y&zt!INV{ecIxBl&o=}Aom1!A6?D=`ypP{sj}+eJI}(3NHCjTuCUON^%n;;G%W$p797eUOcuh!IxtA z@xdgkECwawN2HBw%!w50UrEOD;hw6T(!yY%*hGqQyaX60B5n}tji^B3GsU>1+H}mL zjBT+zwOXCwV8~*Bz|x?}2R`s0eB+CspqbWacP6A8o|nyRJ>)h=c|q>cntp%C(L?(f z^;c*#yQo@M=x@q!eC!Pe3a!Q2f6UvvpMCA$MWI-_%Pr7=GWP|V;kN13;5u_y^m|Ydl_e*eF4hM@a}j0 z6E|M>10H(G<5)bp!Mor2x9qv~MlN~uk8|to_wld)_%|%>zn>>O`GtJ%vhVT1cl`sS zjWr(k#24|Y|NIpH_2KtWPYe%x#IyOpdp^JyKK}2_Y@g@!bDzpP|K{)b>gPVrIps2jOD;HhMR&HtBcuKXUSopBo1|KKXV`04*( zqSIpguH9UJ^;O(_-S;{7g0p$@PduOZ{o_0N^vC~$AAk1qIQxP}@+WWmKYZ(}U*^Rx ze?84@=kY(^`X+Ar!FPG}>wl9KJHwlQ{a08%a4-Mk4Zp!{dxyO7O|K^%4fvJU{1TU6 zzn3@v=9}2PW1d&M=tX?tn?K+We*gEl@Z!hv{AWFlkA3pX{Ovpbm>+xQ3wYvVFX3KgeedM?Km97se8@Tc#XH``w?6j~UitFZ(>nE3e&>Jv8Mj~kZGQE2zf9hk=huJp zcUU;Qhu6RCWgJ_BUwhM=(6u_Re$n%|{hs~&&)<40^E=Ptr=R&0zH#}r{QmF0g^M5g zXrA?i$Mf%>{ub~2^WWv!&wehCd-y|n$9q1-f4uwu@$#3xgbU9*kGH+!J^c4)KFlw@ z>gPG_)YEy(@BSHAU-^Ar|Jqm6YEAOi-~4Uvy7Ml6<&`gEZIJVpH~tz4_fBr{2?MYAMDUW$1pZe?<`R8~39Y6JgpW(c7&ttT)%0#yW_4(r+tg`Un7${Q_>9NpdMcW?tD()Eo4P7d}O-|+bmiEA&it47lQF%FM$F{=B0Fmn7PQ*B={=OBXz7&qQA*{Z^1O$0=zkHUgVFZXA8@pFgL>7qM zI*Q;NrBl~amho@#SZ&i z`t3{EHoudHU33n;wIwdU{5za`&IO!z{@EPZdk?qWbUhbcbRMUkb|!oF?BTvU@8A&^ zpTq37?c8$59jq)J<o#n)A;&lWsHN#+z@X z-Rg4QIcHE$JU8EX6WivubH-_>;K{i4#_Kub)E%61$}ZN{H@JV#ot%I68O&{)<=Da! z`}f_$d1s%=DLZD_f8+?OE6bd7_F3%MHp$-m?_)U1IsNognVs&ickg{P>kW3Fwu5df z<>=u9blXjK?A}hJ(_sJJJV;bS>fFzWwZ)-^Pv|^EjngIChv_JLlOsH%V?S zs~a2a+P#C>$u?O&!dl1tjyYx~+vNEWu4R+zgZL zI_+l4)XW6alUPe-QbV)4vO?x8gJGXkrwn>)^!gi2&hFrmkGYsrPut1F#01@L z7o#&~XS>X8pQF`kli=8~W0IMf8QQHTiPmi2KF#d(B%M}`dacIX_HE40&M?(VNYaE| zyLU1>H$}6ak~VAX+_8)4sV4qKna<$*B&f?AlIuViIi5wrw-)JY}BAZi`eqX12|OwY}briM-}bx zCa3M*$>c964E(Ze*F+j!vvmsqJJwWBayU99~#rdUl>}yUo(l0^8>1L0M*}x+KQXALKM^DbwA0A)6eqzA>QH zo#gxrE@ZOXB}ojujdhk67wPviPzm*V6O(9Kt(4)$Dp~HC-8N4)?9pn=^^Ywr)1Kai z9}Unr5|fZ5(g`Coc@KFMk9hRsSzA8L>hcn;&O|gEg6tD2 zor<3Rae_6-NVh4SI_e_rLu!)(Zwf_KbjB;q2l0nB!QYM)K3}{ai&Sql4j);D!5)ch zyhyrG0Z56ydSs}?%CYzuj8U=SEvRaMBeXNuf-z)yj#h@r$!R40&V_)88`E`BcR26Z zbMO6M_{?WNeGgKY$cCk_{ul2&_WIEPm|ywT0Kgn?1jNXj7^m`{)`r1gC^Y{h7UmEd zXC59bqF%mO_$ugfyiuUzkd-RBjKo?S;}KCAjtk+qvFpdrs0#Hu7zNP)-fW0eUvc4m zB|7Kh293B50V#~>T4RQ4+%O>)X$&^cS?l+h*zpkF^RD;vnUB4T?Q=Uw+Y>k=#W%Cj zNb0lr99#~0PCgv6(cfTod69*Ecd>KlX{==l2kyFsp-mZD!?3r()bt#Siwj(P{WW-H z*tugT(^FFzW5{wT>e|~_XOw4TSw@~^?JvbxMo*WJvq1AD1y#gQY2 zxZ&Ds*|+B|nrXt~k;B|}<1H*6KFs7~hx_;3&$ZwE4k{aR`Wa_%+ikb;gKvHr*I%Vk zZ*tv@w{X*y-=*y`>a`j--EkXt-*O{0Zy976*Is)A2kyOxiAICv#Raap;`{91b1zfv z77L3@+>o~M`9}9~M+hkZ>U)^Bup(EUQ)lFRc{co``9C7d7!(4aO4P15kHyLj9xck2STzlnBT=Rp= zSl?LVo;&a32UlFrjn`exsMlxD-FI^J4{qSj+izugWrf>sx{>{R?-N!Z9MvX_nJWrk~ujIsfCG+?-~!RqP? zsgXSCyKXlvvIH(s-!|NQue z*|+C5a++NB?W_6xXFkb&d+s8m#aF)cb-wzQuW?}S0qPUm`RspvnlF9n^XxmehE8Yr z=!ZYbW#9TTM|-d`Xz-!;y^kBNx{RJpIIsfmd+!Ii5sD_g!?F6Wo30eSF}(|Hj?-?x8!qiz~0VijRHxqujs$2#x6-eEZVx^538NBu9=d z)7^14pZery`TS=;NpHPRb806a``D+s^lM+jx}2ml%fG$n{e1uO%Q3YU)}{RGyZ(hM zuepvSZIJdY(^EBOw#}30ZcM2Oa2*p!$6r3kZ&e7@sHMh%jMBl3qV{-j=0r~4iJ1dY zPlVsY|5PwGOJ2g~KJ^KH^UZH&V%NF6=%=2}hyL|F{NdaFFQ-5B(LDXhkKynC z=AHcgU%i7TJ@Y5I=#qKR@>+e(|+$X0|iIYhU+DF1_kzUi+)R%5ZhW>t6Fx?%a2bUwZAU zxcB}ge&tm!!6XyB`lT=8OW(boH@yDUoOkZIy!ctq;6Fa|HQxG`H}d2sJ&k8Q=~2A% z-S6d({`Bqq)C*tAk6nBLZ-2+z`P+YeA1{6N8+hb7r|^5f_3M1>GhgDBzxoz-ZJ*~i z-|&lE{)4Od)n9)T8~qM%eBI0Gtu6B_uX-hS>|fyxzw$aJyIo%M(iijn>u%!BZ~0Bm zIp=(S?x&u`m%j2Pe*X{ult(@OnY{SvkLN!>`a%Bc@Bf)6{=`dp+LIo^```7C{KrQ> z%1^!YbzJz63;DaZ{~6!^&bRovm%oanHN&6&=^wG@t~+_Mzx`{P6Q}ab z=RTinuDycyzxQ7`?X(Mc@{^y%SHJp2KK{{<^6*O@$73J;SU&xqALi3v_!3Wk%1?3j z>1XoM55Jr5U3mjfe#VQK>bCgEhu+I=ckSWn&-+1oep&t13i$xnQoxox|6{9~TLH9zyvJCo`UT*~2lO~$U?gX>jwsGTi*KqAsSF&TrPWt^7?!Nn8rYE;?;NSrUxx*+$ zmJhLBy7qVzYEfxTyVYW9Vv=J=4)V3H{WtxA=c!M94#VL(iwj5Sv^&8$l@SU=5#(l} zcS{-TLVEUQR&t?3f*s)tyC>8Ej76`J!nx584x&0_mqeND1>_0ApK;&4qDNe?^@77 z!vipC)6biMmn8g`hW~0n{n~49_^avZiRW*Qq7@3w1O)HG#SSz2LbX@Rt6@Xld# zSzPNZHdIuX{8EM23KQEvX+=`xPlqljN`rF86=*w|k>i~#vNnWPRkBfp1r@r4__Ekq zl@?zVcw#*5NvTlFhL|@O4A@Zg)mP?RA#^)*s8Qn#WkE#A&#Tv)96WN6q3UqyA6_};f6hG^)^PqV3?8TBiij2 zXv3)2W0Z|(wc6kvgZ=>T6}4Io=bhB?@D^hdjM5DH>ljnRBuxgRKBK`JjaHjlt&Xye zUT>Xxt3}P!$VMYBup%VUOv#?I^8TT3jN_hHTrh4doTbjvmEY$L!oJC|Epp1hiptW{#{s zWM%O%jaG~9#611pI!h~u=yWIPOw6*ezRt?RVJ4?$XtkzUT3MjCw#M|#G__jF(#i_G z-hlaSv*10ei^s^VV`^p_8dzLBMt?YX|HUta0Do`6P+m*jvZ!Yd5Il6c2R3IIks>ZpXJPL-^M7n96WRs@{FB3 z=K;mxBS%T_Y}>wrq0KpX@BqzPjqTfa(c4&Md1Z}8v(D7aH0v8ZR+g8fe!#>OOKU4^ ztgg}Rv}kpvSYBCRxUoUE+ojQ-VqxJh*|5j_+%}9(Il8!j%X4OCWGgBR=CuUe!Tx4Toz|71PoyjTo@41W9 zw{PRKpZz%JU2q}2-g-ngf;=r*7n?!%2n-aglZw9G%vy*5O#Dz47>UY#EX89B2QXYY z5?o^}CHz&Ck&e+Gph!KcI+#V5zxdMSHA=xsEHf=*99QsFW5nap@jjc3B-ot(bK-QG z^7%tM(8{Z13q&}mq}Z1!NufSWF)0Naw&_0OfRgf?Kofnu6FxQ;KmMHYRj=2jrrZf$ zS(a^4BWN77dN8k(fi#<)_XqRclco7=zIFuVs%GVh=ITVOod@xR#nm^Dck4iCHJQC#9~9xTn&nX}*2gf0OGDKmN4mu(5QQ ztG@qrRBM7~Jm*Cm*|(poF8dNY&peMO{n*pF=E^I$>-HNs`-1a$@)MuVrQiHI_uP9Y zk9hRsxa6V>_|kuWk$s03dBRhk!ox2(lh1zYlN?-H=b6ubKBv!5^2JYof+K?w&-tmJ zXQq+x*-w5D*X;0fzwlZP?%l%|KK(Ivopw4ef5k6y?X_3)qFMHW5`0^J% z%h$hsDNlIPkMYybdp@83FF6>`}((X#~nBD$^ZO!&OG&O z9{>0s_TA$8pgG7jyFsS8(9SAtolKm~2h5aO^Nc+vjDk`W^P&eJ%g~k$++L z8RzrnxBL;``ub=1+$Y}8Igff8Z~C?0;xnK4H@@-Z&+zc4zlhhq@|XC~yZ?$Szxz#| z{*qti#m|2p|NQrF{t_q*ukF5dE+f5`rOZ{`Efa4`9u8oXa0+GANfRn`Axsa7e4%tT>7;y@ucUxgcrQzWqjzJf5uJM z-^g=b{7N44#HaFa|NN)y-*Z1NedRB6+L`C_ukZXb*4F#H@^!yWU2X8;_rH@&)p^xx ze~ZD9yZG40KS+Ca2d{YD8`*RH_xRp-E~PuO3#Fmap5WmAJ#?pM*m?SS+<(VS9J%iv z9{22@;{`ANdH(sYf0uo?-^9gFc>%9{)ob|tN8ZV|zWxm!^PHFQ!sq=I@BP=m=In<* zowxtVACtH>Hu^($%}jCH_I4qVJc^axh+|7VywR+$_gG!u;K_`c!e=hzaUnDv+N(;P zIVZN8Hz*y`kpi{AmtBk^;vgQkA~sM_#J1s~v53}16M>>RlP|j_X%%a{*!jC<3LO?i$59wbKMPpH8VZ={OVVu(HI9AktVGS7;(1NRu(ad##zX6 zY0c-Y#bLqP&;m+QgJSBDM+{37+NTKuCMiW;`7&)tMU-tk`Z8vG`Uu>pP)OH9h)xPK zCbaow+?b7KO4aBpjeIJbt-2AlfHGABBHSOOfR+?0wndfa{$N1gCfu@rnUBBkgZSPt z4lEvHV(t_=%{tafPP|r{VQ-z~rA3gKP*Ug^8wIw>N@%3Q2fO3uGDOfeGGi zKt24u5_cUpE-UwmV~anBV~na?qsYMZ6d;iYypw`Ya!|m!^5P~5;~GgVN3A875#(np z4Fia&gVI1C2!ZecX9f5&i6+l8>245O;$?+wJ8=03 ziw}j$EU8KHz&-ce#nHq2q!3kfKuv=4p5dSmD6&DHsWf47dOLUCdnc>Cb-LXy&O6$j zN!pDji^~hFuPtFril;`s-r&shFJxwV25(2CX^m#1MN&&?H5&!crZH%pkh@XQ5ussH zatoPvq=~c^&z7e!7E9ra~{04qI9$&F$q~dB1bw4WTTA!D93uJ zg<@p6x1>pe#^AgrcOy|6&f&?6_L)hN;Jss%4MJ-@p)gA8%vAD`vjL;w0Ap&>x;h_9 z0lg&THxC62Z8lnHr5KEcBI|;3%dN%bIa+IyM2D7>8n3jpXV0zt9f`K&qalq%isAJ} zBW#}2t~beSCau*&7l9-(vd3*M-4C?F=DBo3P$FaV+<|hWN`q3xUNVWnW;r$+fz~LU zO7T;h%U*XLpGkqkG_9kIVL0fK>YO*c`e!-owB1qwxGH3{C`Eh()~hv0JSDLqmWMmp zYaGN7aHDkEV|TMyNWOITf;{pR(V(gWT-7H!D!Bj*c~@n>Yz}usy;!+HEF`C-U8Z-D zCFugNQx*n}x=LitxKrvvC#wipD5b^<=ar7=cp=!$h4Z)OA#Z-kg*Q9t#m^BGqpTrQ z0!gXQ;F!^CXj0gMK6M-*rJSPQ;Dt680 z?;iKrAMy3W04)I<*LwY^-^(dH$ufYi}^* zf4}X|xb$0B(3dA zA_tBgT_Q1-xtUqKa~wH(lr%L=OinT!4q01WB{7CZs|{AMvA#sD(V*L&W;j}Baq%d% zTAj33$22CGXm>e!Xb;`)G+8#J-&?0vZ_ucB80I-`bU zT1va!W;pEA-{@0s)MzwY4Eh7|{)l>-kapYj2P3kL4eHI5MyExuHz4m1=yY4CBw@YZ zM_Fjs3|<*l`$M#LbQ?`Pn#JW6k~E>yX@OI$FD;AxHQ8a9I|i$3)Dlf|qDy}?B=2Q3 z6GOe-rZ*fi9F1tyQqp>ZUcV2{)2h{|)$6RUuH#WOnhmt_Y^<%IYjx`N4mlo|^-<1| zHrixL;re})&uDZyp0CnLw@QZj>7 zvJ_ISIeKi7GavRa{_P+Ck#1A7vbxGtr^&AA#+dY38(8kycZ8wy)Qn+mt;fN`$5>ik z0|T8-hqd($4jw+l-S-{gmK$!M%@D5?0}^(edN$J?!)4$2Ji}gwLTd4?uP>v$quc7x zXtYSu6tu^f6s-+uQm4PZz&-cg%P+tA4|vlXU(LdOH!{`jlIJySDe^mPGNs8H7> z+8Y#pYxD<{k7YVGv0{V$A%(GHo{B5wFGx376lnQ-^z3t|#2*i7P0}d2sz=1PR^9}k zIix@-FU@N_fyELX!U)0;d~zj)$%B35@kq;ggZE&JuuikgQEztHxoaN0Wi-n0-iL0Z z;vaY=`z5NmZ++|1Uwh4Kes}=pN9uwEj7_@yS6;VJa7u;tZV{1*iqN37!{wHO3awNq z5L5+HDu%ZAg+n4sZvzUZP^xJBd%`G{Fmos)jasVSR}E|h7!~WW#0@2@S^x&V+(_P2 z*0d?W8eKRH5w#mLA{4>!_QX^v@1fCbvb46&Qs1)gz+uim{~>f*b?!ZUkY=li?e|FQ zU25$Pi%0hXnwS3AQ+enkAI>8ma~4y;txNFD%Wve+y?Yt;*U<`EQ}gIrg7zNo97!!D zt<_N;5~E4e8par$wd6Ku*dH<)4oFf_KFS!3(V^mVKt)4W81EhRdPAJh+~S<0KkCs) z6J}=SX*62m)PMXjW5v(<$~>79IDBOL$$R3ht{d&sRr*A(8HBG<5yG>Cpc1b@62 z5GS+XyhU|dD0Be!LJ_gJ7h>J0ip>~ZAY%joD$&32`7mVX1t?RLQ=n8)N{Py`3f2E9 zie42+R`@x7C%2v?Q9S7>52H5{uqw+v`DjEoaxAZ{(%aY&fXn5qEia->ozpIP1m2`f zbz3Os$a70tGw8%%y<#vJF&Ydp+L0PVs-^I4ticMU`AOT zo;OOcM3<8#Kre*=4q_%K)&SUr0{!00*Q)1!vn%JyOBd5$y29mWs!Qi9->H@kkK>Ki zpT?x`rfXLJ7MDs_{uZ}EECwF$Uj-)PIN~$K-gpUwz5-?C_3GHG|5mRZ?;W2l{u_Cg z)n~4{)P9TaD_~&3Ew3;d3?+Ip_Td0{1VAE!BOnS?Aw}H0Qz25ad6b)HQ)sB=&*M&P zG#KRXZqxV6)u{p-$AJ!1g3eZPYt`cduR2@efz*AW*rF~HFKvCbUvSWO?gQQzT zxl2{$vnuE${P7NLaj5_xDi!tM$sOeQiaN7;o$|7qQp5_zi!cB0WxVopC6M7m!QCq0 zbb>KF&};K|Hv=p2TGftCC;IMI*WS!1dN8k(0WK%Kx8Euu@A$F&u;V$|>tth8@z7Mp zwwZ;z)f{aFsBOhq-ux^-91G`oAgfAEFKx8(@y5SdJ0i0gumAPm;x)gKO7KNV=S_#h zV{z7(EquL~&uHoX6P2!}WK36IMggkqoDsV#X(bV20mmiSC|#DqxSdA@yTQB2tCQz4 zQIQ3wpybh02p{P>;wjCgu>V6(Pyu~oOJ@_p%pPMzep>GeSE|ZHgaD8SIqpKxD9RJ% zB?Z#~UMrb58~Us0&=5m=iB=bK#TSxXDJ6O)wz>lj%837 z@;pN;gGmy!hfy}bYmYIB1i3Oxn$}P{0c}Z)rqyUrOHHV!;iL|U2AhdLSF6?VND*Bf z0$vVJo@JOM#TW$^@_d9&QwgFw&tNblt<^|sQbac&<<#mmGy>TQ$c!Pm-i) zEv?I)N9=tCqfsWQJ+&Ijd$QpWrBl3-*6SuP9`YQVb);#iFB*={XlO~aL21t@&&h^E zYKBdy1yjiOv| z*EcZ-m8_Fef%y_TO&+grp=gQ|zDHpfNI*}ilCd-Z%Cv%sfn;v3ZsRpR;$%$HrhCCur8;+vChWI5*sVa z3^#gs+s7zJEv?a>nk8+t@OCIgdV3p;dOgXRo83uweh0gD?`He#B3dV+ypxbd798Rv z6v)w)mgB}qyF9cad{oxQ$-L0a26lrmT=>u0o<0~s6%0X0=o56BpKR;5ZRvN+_r zNNod2ZHoo_G{T071`1l31u9eokC9$&1U1DAJ4I?e1iaTf>WK;06~;kfA9*pZB#J97 z7PS&XMx#PUkzkx@jq{KMN}l*;;S3Q_=3xB9Vxh6?R_>*yk5W3I{?g`8g>#zO4CMs! zwBAWIdJk89_q*J2^9|&q9=H*WdY$(4EVWjLR;x?9J3*t~AT`jKoW>fM>9n}{nnT=n z^Oc-(`URjgi--2pTV0~wYNP9Qrnk*=@xvZTr_)6#P3oX$bCx$Y07a6fp^aP+iwXg+ zLYP7+0S)8(ac&B5Bv7ApbgnDw>wN&M!kJg`NFog@a6>M#ig}O z*Bz*|^wSIZ>oM@64UXOzM_0jf-r6hR23HA;;_mmZlXM>L2^M=k=g5j1i^j~gXSU133v!g~2&t5ZbF zor5g1B*tKj!P>0oSmLcKqSnqkoD-&8uQz0EBV(95M%f7M6~q2IgW(3=I~I>E;7!6A zr=G?1M3s4E1^ktR>F}^hb`P$BuAtf15_FMwU5rqM4oF zO}o{So-qkQHV)7xH1)t?^bnII5Wp($M<@fvBMfZq$#MY$4IVuZHfgRTBw%tY_G@rD zH}YNx0)`cFaXrg`r&5rGmetxjT<9#L^E|M)74@2-*B_E5Qa4lV&{CeOiap^%Kpii8 z(-gp^Edq%+32r+b3g)kEl)Ei<))NI+1f#qrv|SGu@eXBz4HgUtxrPL4igTi1lv~z^ z8`vzvjRquXlUAe2V7P`!n=~3-2EBE2k%*4J?)|l2I(wZL{;N zFD`ND$O8NByoxhB(@e~s$-VdN=iD<+MQ1}{dl(~(<{$&48?Y<=)gZf~e)mp+b3`I> zw28*_b|HeIL@Zg%g(V~r+*0QU*%3b?HiQ)Qjj~sV!oJ>t*TUGA4Hsp5$+C&|aP;3| zU|hvbfX68?=wg&H{V2iQ(x8n_nkq&q%0}2g;WNsIzT=)|qsHR$DjU5a-b1&Q zFf}txmJJx>mWwVqkL{cVPkS7fef^8< zoY>CJ-H+n#!#9v>VI=I@Ie~6;8TD&q9!P2!Es+zi4N4o5Bq2>3U{cb0o%vICqg2W$ z8_{1~Vtx4tS#JeD%0#GPBwA5}=p%+f%lb*Q%7QmI9rE_H5`H5h4wC_9R)$)VuZ{ash8kv;lLCbmR5v}(G*2ZO_(Q_TP#*mj*I|IStb-l zuQj@}qg4o~70&Wb^+wE=Iz zD{RmOvINmX-Bj8~-u2=zY12AmH@pC}dU zf|H;V8I=-Nlyi>MXkoKiiR>6{0)Q=syH>(IEeq+BC-NMuRu}fui1xcExjxLN9g5IHpwPW*Ml|n2FTk!XSx&#|fPELjNgM8Imop z<$ZCls>2%fU=>){($PhM^f%mOD^U447+(&Y5|&$Ow~aFhst*2Wl+$dU+;rItAe`vE z>igAa+6*W>5QAkau;+Njkvi`0TfU0HE@ zPxNe?!H+o3a*R;r(VYmeIobG5_W1_{&w>X!$Q2uzp`HrRAAu(4#O5sJf(pk^&WQ82 zur-~tMFHig@1s65K^77nh{8q|=Qu#tIAukTc+rI_IH7otNn&6s06#jgSA0B+;UEP^ zBKOf=D0Py69MWkNMUbVT^JoA+#BLRECDCPIQkW&KaCRW3hiXg`u4lk{9a6+S+DAsH z2?QIDRuT@7@4C{-i%zssSUlc33M)+;ob_05(WJo&5(i&fP)6ZW#mFnX4N^P!9!hJm z@01Z8X*Gj9!`2!!rW!Q;G+90(O={eD{k2?k#pR^la(LgpBx#-9FMTzg_B>a7?`m$o z{(9zjok2ezk!Lv@gAuLvBxj#<9=){{mJaV{V`YKGV++_UXEfMgV||0}j#GI4FTRxv z&%1!p#sWG?Bru|-5UAD~<$dL6E8d|uC=vf;WI&>W_e!hBdz6} zD-^l)WnfYHB8}HWQFlWZ!JokTjAuCni@XKvJh{toUcf)8Ln-IdD1);jnyn6p4;<#d z-uKrmEgj{;$3Ks4JLc)H9bve(!T$U2q1K&acIW8~HkR0a)&*SjxSycrM=TuKgV8lS zo}-8EV{Ks(HyV;ABcAk>XYsI$9zt(@g*?lcojHpg^G$kv2`Hs?#Z7nK$shjt+i6eE zvu)R@c;&If5lOQ_quC%^UE|Qc`$(EyIt{~c;Q&VuA7W{3K&{r`ob%47J2ykGx5i-L z=rr53n{9TSvXcugJf905@dRG{^FPVjqG#XUy=UcoCx3)qc|fU=^<#rHZp(z-{3kiWsa8-_^ci%6X|przAkFydo(wYaCz%u(9K|V^wcR za99|1@@J2=?g5ng)~_Eq1t|`u=!?DCs0q$RsffTjaSnB;BrNEgII_w`;Vy`})(IG0 zF$O6PV_fKB+Eg$+E9fUN|Gb#IQc=hNgMI5k^2$_YqIGd?q5~b2Rh;nP+l?=bQUyBb zeF?lMtpZ)ogBRz$=EKhsV zMLhm#Pv%RP@8hncBTjwD;~4f8DLHF{CDxX27u!JBvALtSuoqjKq~GgFHD?qWfdrM2 zJPwm2Xl=-B=+mx2n;NxRU04Ywq1~M!&vOR-bvZ`bF&GV~Y497K<=b!OK6f)3PwoV@ zY}A@$`4F_GUK1Llvl4aDNm3|$oS^(BO~5M#Sq2ztX@W@-a+jl&uuYUnS-OX zrC!J=8<6M(ZPKtaTqxMAf`S9ICow73E6|2S2@TZQ45M|RFPh+ph;o2jYJr#(uRSi$ zsi#seBg@2r)!Lz`fllx?Ln%wWRu@_yo-B8y^+u6m;Jw8tMVi*YS!@tOwWN-95}DCj zk=AOVPEy#i5w!AF1Y$sKW&RAqkY|k7WLYjo69Tdb>%)iCEf{7tIExmXCd)I1qlAHO zVA7PynHduLSZ8T>J2V;%YV{`dZkM#)q}^>YH$O?Lp`Iz)3g%`fn3~&xX|$-f`gEse zan921w6IFylZ5*Z9AvUllYO7os3kRwNoX|d6751->Y3os%X$Oe6_L3JQfQ@7&STO# z*5w#I2&}GBA!r>POaZ`*R#*$#SOKdr*uZ=dxs(g5DHp~F2-suuoWw}|Dy3Z5VIHFe zyf9u2>KO3>74lS*g2@uuNAg{THZ^g~y_0LGgps#_LF2(_iIOQXQk?fv$jC{WoOL-S z)aKDO12%$tvY$+1gjwY!{Vg*7glHC6K0z2tDy@_HFaiAZKnO;79+6vrRb-|>#5k2%~C)C{bl%{oW~ zv8;*`d0b_O6f(K+EW-*a6$}pwAYja03u{;~U*ezTC<^dL1wW$VPgfnm*tIXX7ow{O zN=b}VZ!6EFRGDiBK~_O59E0-K-h zL^Wl$y4C}MAzLvqHVyKmF!>~R$ttBL}6 zWevi5aA=-{3XjG~R(jv}WNJ}c*JXOdBW58LF#qrsqA7E_^UO1E%ognocyaW!y9yb9*l1nSJ z*W`AD^(oDIlTn`ItYbJDVYH^z>Cl;)qS@*&*=;dcU&iZ{&g>Mr4oR)SB|r9Te*MjF zVRl-IW9ADQW)X<&V&g9%YvokZSr#94HlYo>F zk5*FBl%&a|N|C!9ucfYCWNAx)Sk@xd3Et;eJgKgs6jE0c32r-&*fL&%Ple|c_MwY= zz`3HNf{RWqE&yIoZ-!uv3#oZ309xJ$Ag`>i;+!pIeK^-qv&Y~-3>xH-LFNO4RttN^ zdxR(vbfqw9rFLTUsH2TA7!Ad)LxNKtP3|QKWwc^6%IHqbV3Hb%8{n1?Vf=vIXP!%M zFiTS>>^$QvZn^1ddK(LLC#SjVmMb`PXfNkqcpySPka=wdgUwm(zm`xj-+XIXJ+wAvv_DP!`=q7r=Lf= zrr9`jI|uH)iK7P&(a$~2xn0zn4U`*UjH21}IJ?f$(i%sP?d6u6u4Vp|(|OLbp2H&^ zdJ#tsAKVD4kZv8RGE-1 z>mhbsESMXj>ctww6CTgEQiNEN{EcW&`Do)*WOV$ws?!KsT?d+j^A!D>6yOt3xyYjc zg7^@@Q7YKU-UORZKs)(4vT@4$5buRBzMTk13cx7LM_~sP`!3o_k`|d|SNFKi*^OQ5v>HZbRjwrCL|tO=iFGqy{gf!WAs`6LSeUs-(wDa zX}HH2s}FgrVW)Tkn(7J_;E}@{o!l{cRkT=qVA*V8=t=y4C^Y)D$~h+y^(uM#i@mt|5PBcR?YNy%I$ z&Z2doYh=Sdjh5tcqHEYZ3n^48p#Kr1@sew7YUIPAbT#mz1hZ^NViLU9I3)#~Q&ga- zT2zwYvH{Iz6YD*W1Z#5=6C(P`$nRQFtJg&!C`C3Jkk(RRNukMYhE5Z-siU07+0TUCkk)GG#E@s101ZhR_L~Sptu@+Ol6s3#mWk3% zQxS5`lNcq%t+kBbdoc#v{)-@}lkDY>(#e1ytIr1dt< zMw{Nofb{_kvmVf5G*oz9CpDh6!GJu=Q9;?7T7%V|SDr&^h zR$hy|P?0(;hQD*R5NratNe-=x$}T!I6$xss7^NsHV|376!MRsn&ahI{L+DBu82{hamT%MfWyx|Ms>u&^hGct z1~aPj74Lt15!`Ygs$v<7x^2O@uFh%o_kuO!OMswo#-sBTI;v$^j~i~dndkoGGlVU- zX$&WF!cRsM9tT~@+BGKvV>a)(&7Y~>??m9smO-nERp|Wj%-hZ5t^R)e`fdW3!gY(^ zqVX(;`lP@OJ^+&D#B+GE*H!?^_|L}!To1%-IT^tCV8&5xWp0#WnCC2P^vHdR8)i}) zFAU2$sehrgFeVj>%!`wShu953C5GI2Qj)3n6(1xCn_a(If*f3d5+R`G8+nfdvPL^7H3_! z6|yIC7ues*VN@!$T9m;?;KN&R!cLCNwSO%h)wQtj1Ot^3P!=M5nD*3b*N+LLVCaU0qBb6y2i#jGd>RhgJEQI z2D!&(BV3-7ThCyaGa8K0IGmLZtp+JjzqGo>#?aENr!-QS4jDa-6xuaKzdxj%Bq*b$ z7^+evMqxvcQ+dT`(5F8fFdRCukDDnjcjQVsj~XrZiPs4^gcJ>h*26RP^qb>lNG|@O`$%R@gb|DC(Apo9nta1;Bb)GEGgg+PT z0*4JvI6RpbJ7hE(vA%i~lW4R_x#7S6jghuI@#)WJuy%|c=bcY$+dMaZ_bUw62Ap%= zMI7FHC*S|#_3S+FV$Qwfv9ud)*4I|B*@(2B(wLqn_mV26gMD^nrN?Q52~wqS%42g+ ztEPC}%bw3^r|jnH>u+LhqsRK7$AJU;vBM#ai79Mu9S)R&NCVDn2~O z9uB{|pz~whU2meSv*rE6UXZOOHi`tbwRB}7uv3t=h&eIdgCAogSM<4eMR^GyZt02l zh{bkGwr|wkC(^SI^5xxxv|2AQR9BwtqC86T9r)Lg06QqV>evoT|(C{1celOzal zk8&18g7q4&a_X&=v0_VF*3M`pv{)@2er!IXUY`UdU0l2hsJBrll}JIc;QXSYVQM0< zjm0}pv)cvo(78&H))Fy5JkssPMc@IFU_>O18eRpKgm=`#{?SQ1SUKJcL&l^kbks56 zEZWq{k8cCAkbBpl~ zXGd6X@R>sSjKtI!*$sS>phDWqAlnFq+9WpZ@*oI|!R8r>Nx(X6zlYL_BuU8nE*L~! z=$AU|C$tzv0f=eT44G9}43>l}M>yR2|sild`iAjTirbr7^n7{->iV7!n z@Kz9Z=kX*3sA58Dj?%&&NfQB%k~Af^wvb20q=7Xeg{QMDLnji^siiftEF(z-_>;($ z2dyPt&{|YT!EDqLh9ha^Wiv;brlFIOJXeyWc;(4P0#bSJNz;VkU`Sd|$nrcm2stK^ z{UABDM)qf-=?^m+%_gH^UqH%Sy3g1kr=-i1LE#vUhBTUO@?7$n^DL)UOUXuk>1^fX zoa-dP4mKDy5*E+f#TlpUWYn`nmJD8)T2;qr%quOFXCeLz9Z{9LDxwBn5v87h!~qN` zB)1O*b}OKOkL3GktXF*22r8)K!IyDRL;hwru_lyKMT%oATvl(r{ee@|0?VH1jSn!aGdO5ocOpNQP-Eukmzg$o;!s38wUW!?_Xpt ztIAVEo1!|e1*j&#PP5ry|G`77u8o+TiXA6PdtvjMoap!J-^#g>IX!+)Z4Ep;nEP#& zCbZQxt2RVoT|5ZzZ3T>N zbpCL)fheNS1Jm|n0bAbVn=efw%(U4k_;IU|v zVn-I8gd(Xbq*!`MOSFLz>)c4dd>aaxVuKEecC3}OMxBHjZ<_xfcYhvj-XG6XtnNw2J$3Ctbl9ORcK)1Fk&oF90c$>X30B@6tk(! zwkuvcEm^v4q}i&m%%^b1egCS@^E}Orqs{v?TiVKM98iR*>={yaU?x$j1+C{YGjA`T zD%*V_jUz99`lH6?E+8qB^GGd~*WY-+iq&Z8HZHD1tay2Rb{%FK2*F|JmO^lh=xw%jBJCMj)}>o&%LUZfuRq%x=F$ z!~%BnJR2}4iD}+b=Dp2v?Xsej!aSP|d31cl<(r>p+FHPKc;d^rb@LAAuYHyW4<3+S ze~nRucfRx8y!h!)@akuOiO1gkWsFC+c;%Bn&x6-rmCAg_+BJLbh1`)O*ns(MgwxxB1Z5d?g?Gg`Z_RDVuRc@w7)5i7s62 zZ1$2X3;hMO8asi)xSi0ty-MisYx-F*zqz=KklvXp?oype8(#U+Hi?oBk+$l@K4bog z`QFz4I?*~Q^#gsXbT}@-n&L>`$vRM;jD{Cv($(Ee#dT_NohnYjN_U*D^EQ3 zUS9gdKjh0k@FXAj+Hd4zpL&@WKJ!_A^Y8qZ`ESq8xcJnMAfpu*{up`7%ker zyWk4Ay4WG2jAEe1UM-p-ciSXnn8FL)JmUtG9Zg?wwvmAnpzrP5>s zqfh3Xn3I7f^K2;QdNnjkq^eOKC-Z!nY9)(WEf-ZYT*N{Qr3wt0EM8a%yr5|qXok!W z+0$;+^Mtc0eejeFwKl~Rix?PV#=0D#7D=%T5KBQx0y}fZ$U`ugdJEf}2TCzPzuK&+ z^Pa2AGjHE(IkX~>^8m?e*|av_l96KVinn^f9OF#M(_O4>w2Boh&MqWEpcNnu8L8en zePO63ua{9%NJZO~>Yv+Q2D`xa<&Mm>8j!TI0%Zz#{SQ@3agxyrr|x%GaN`c^^$lu) zxhk(sAbFO+!m@`%zM2#$iwUahxi%jM??2@gmt(_}?>S>!%`^pR3 zo}bUYmowAU_cpFTYcUFYc(f@E)bmOng#FGqFc(gYNuC!!=7B6$3n=AGN{Rb-FI&F0 zR@m<^TA@R&uydzf#JUuy*ZiopvfWw4cb-cFR)%p!M)>SYFZ1zFJO}QL*E|0J2(7rX>(wT+o1T&YJwkX$1KhYaj)>f9yN>}nJ*DOmuN3FAIF=zq zN093*h&Yc6lY$`Px5B>*eNx*!7fj3+-D%;?Lq}U8fKk2XQf<0>Y1AxP7QrxbrS02Y z`{&gdBBmd&0Za~lH{6DHi$}gX#N>ezuNTmbv;~^wbYmBs>`S$}1=n87x&dD(D)s2- zRc96%OQ0S2gZ=nc0Kykbvsvzg)aos~6{ujk@2x)-ie*Z)GG%<_BJ*X{b}W6yu$Q!nwkmtJAmtT{fm_R_=Z zi0#!StM!IyzxP^I7E~DWhW&oadb8&0>VoyrG1KmfljBp)&d%5zA5rI-G#H4WRT#%L zS65qB>ka$;j+5hK&M(e6+N{}6TgLIonFgjG=6PbhIcB##XT936pY|LdopOG0#?jG< zf$FOjbD0q>tcHPkF32#l-(PZie9Fb;CF{)*``sl+N5^cpdseFryJ_peB|){YT8~^^ zUb5M2nD#qX>k}@oE;!m8b9s5m>S#?Z#XB!m$~?0^K4p7(&T6&cYIn);$qlyK3&y-+ zx8JfJH_Vequ$}Fm^U8v9nt5;tfeA@1s!tE?Dmtb!!%nhkGf|V)H(}Q&}olIWjj|^>tik- z+_Tz#!-|>2D6rdZ*_Xoc@d=xC;&N9wU0FkjigN4tnANyu+Fmg4cTC$Wx8Ja$wvyQI z3-{lAgV$briSxT}Fs_`w$V{_;31@oef&Ek%hJjiqQeHDpJBE~*_FIN=)#@Ei z^Gwc}A=|h>ja8oqOKr~s`@N;A&t)QIT#ja{(XSW=?;<76%*6nd_4>G_t5{tjual(8 zezzr!D~2>uN}=dPl1k1iYANP3mBKKt+0Qe`VBLoXq3*YkEfu`ZGkI83F%WUyZ&|I5 ztb>vW^R)Atei$e???@ORSEemP_NEuLQp%nrgM&73HYDbYd+c^w$cbO~+kY?bdjI=) z?fH)~&mMLuJD6uqk50MRUGU(g&+znnKFDKFJ_db2xw6Oj6uBIx> z;@_>p_R2CP3UuE)N~>T^Lv|C~ee*6q@nb*2kAL{5`K{mf8+ht8u^KFhD4JOTLtZmb zSZ$8DbL%EAy!0BZM<+#e)*8ueq~=pZ;K>K!va=|jy3|7ai$IZcuGIkR#s=3OR577! z|Af}6u7{hs5+4dc#OY2VVzj6BLGO&*3jKCH=*{NZxGn8}!FqOEwzP&N9gMlIOS*oE z>+!Z;Z;t`9xY?tWT3}cLt6WcYbJz}gt-(}nou<+ zO9&@&=IHn)CnrZdxVU7lchHo*yMwYiUNf#YY_|rkroke9Ijha}=7?dvAs1MWYmyXW zr>KD!2~UGSti`vw<0ZzhHb5>z%FbMRq!J~qb9f#&PAf zt03oWkuNP474`Z%A<@-k!g^&9;@xhCCT+!}i`SFcWCtb~AP;=5D_B8Ispu2#}+LCCdV~ zO*1S42%$$LN0g@DR~?u!%-JHJ5~)4}uoQq$2Pp(&HywVd=F@|D{-myYMc zkP2g{)sb5`wQc-f=n>Dwca){ASg<6;t-{8PRR8|~$2y~-Rit+&YA`}W&o2Gsph7EZ zbz&AN&Iha}jiAe;o$~9z-XS|A_DM&LV&8*0wI^* z?XQUFbb#rLF>CsF98C2oj^ogOczFDWc3rnvXf-(=ea}Y%sBiK2B-n_?;{N#tZxY)6&M_fF3;C9)Y=JcFlwYQQNNmsKGP`Eg=dLtJX z=b*~@gL^1&d2!!n-wNx(7E>IQnaKK`%6zruYIk7)8xogtMpXmh zGgfTBBrAq5ci#k2E<7;hmc5@3@ttNtg{@o|OWO+>N7IhWb35-AtWNBw$ewDYY__S!GyfVOIN|r3cNH(yo_boPFLK!f0t=@#&+v0Obpb^dFZt;tw4^2G*f1GRZuF! zYR$agTQ|`P^L|GWYl)sDQTA|E3o7bq_sV1k*Ti&H=Bs)(^GU%_WLVdd;XVr@&g~@>wnw-fLCAm3~zk?bA05(|BP2Z{~FJI z#RvI{4}3YpaAI|bfYs)RSMJ{9&9gf^ahfdEEYSeM{anyFSRUQZD=wd-B!qgfs~8Q7 zxh{f5Ve5Fky(GeE&OGtXJ3RB<-^KTR{}1xX&%D69zv11~$@Qa{z8+F$kP)!9-3jyA zQs^bu+f5C6FhZ;8Xw$czmXBH!0SB==3)cPXC{!zoNNIi(ZYx>iP4(NjZIhtt+-z)B zMQidh4%+MDHQ=OKOdv`Y+vgQ$y}LiJYQRszn|U1Q3Gn7#3kjw_BK?P-jwX zHb<*IZerc zhZHEFrEle&NkgRbWK)KUrP)NqbO6cDQo_6W2;-2vYgOv|pwbG>*;;zRG`07eB7|_5#XnBW|Wc3n)t-WKE>I6^5jwY=!(%N=yZY)xfmhadh(zU-#?3ljHS$2uJ!QUW7La1UqFhC4ZLFUd%LaB zK5uQ+rL8UPve)$%?aSf@lmS)?4Y{~St&u9bbb@WxB?h+{wQH|Eg+o=73=DPD&TxINRO0w{}_`2v4aeT%>8GRV{SN^A147{hX3 z$l@WFp9==jAqd35Ge11eVC6iV-gbEJZdArTcmxje1?J#k8?SF2`GWT5TY)2w`rLJp zCtiF z6?4oju#lV$XM3pDNbUFNzw1ydE^Ky$U_gG1HaP&BlIgPDZiiABl2E2ft(6<=JEqI% z0fbVEbwW}oUJxBsZ|z+fS1WY300UUd^ zmf|I#Ue`|~TkV(PuKHlXK}pIqZ@u$kMvA43kJ$nztM!`AX2WjETwYzWJ3S#K;lWmU z_5L{}DdWj0vOZxeQ;#c?(z#H)9(Z(GS623 zWtr$;BJT&Z?_?4L-Y)D&!9XS>D+e^}F4O8Jqe&8SSYkvK=@zkB0OlS9z zl$guRzD%IAl@J(4c9-|~>`(n5mmm0Qp8JYl!#m#nUS4|sGu(aUC0=^|Q=FYY;KLvO z34Ygid>8L~=TqFh_d53;Jm4oj@+rRa*M5L;Ozif>d`F}!0j*swt1})&N!f9`>h`BM zmRMydMVUN<0&)Awi`5sMI6vQW>-G`vd(U%x@>8GT8^7}1+};RfhU#?=tCm&|FZ`;I zN|kQ=DB`|^+W;=xlHa`Pd1;2Ht2qw{)c`f{HP`;^ZnA_76aQJUOoraiI3y*sucsdP ziEBnP2K+olG5_$zW0Gr=x{$+0^IHfbU2GD6eV9xX+ zx$dyk{vf8O`$WqEV2Mp|;0P#I_ePCfp)3%77p!&`dm&hJdc6HyP8=Pb@bc$B%;oML zU-qlMj`Q;iF5h^8Z~yM!$47qQ7bu_oL4L=-^&jw0Km7B2^dJ1+8FY^fE0h6>)el*p z-e9#FNflNyz{(1}u96k1N(h63U@c>7nO$I8eUdbezVQbtRu4qy8C9k1+~Ie@q9{|s zfEu;d;sLx>g?Y}NuUtH0ThZz$X3l7_LTNU6aSW`cjf&O6m{c3!Ad|HcQxUJ2ZEL4$ zZF3ci)QGRAq|Cfkh9r$uG7KXWu?UD1idOaxb`06?zpGY>q+)G-omr!$lCX|QR=~=> zXDQ1Tm6?gL2~b-hyZ*BINeC*@w$+6|vJ$7Zm;3%nNvN|$Nk+sXHdPryYiXEx(RvUs zBB%(3I#))ld(&K_7C^F`-LwK3QLG?zP6oi#Qk`8>jf#D8%xWCi?JTNuUg2`8OqZ8X zD&yuD9^CU{Z;47~bMqEAZ=P^8Sa+w@m|2gO-#U&~y+04xjS(}blGpAr!_yRvq72C! zQ|8SX1 z3!{J+UZ2I}UG~Ct0Y;$e#$Ph947Ei|iRc1z=*-yK)&i0$ty7X0JTv;R@0KNyAcC#hO(scEy}rF-{z>du~% z^}syME<@uC>q82!uNgiLaowuTyNu{e_=vq3A#I#A2WSZN=OxpmfAHG;FW!qYZO^WafwBBbH%IyC5q0MUS(^yk4&Kt5LWTz^vYl=ThbjDG2R+tM##^d!-c*F0Z(E zmU!y+F(>Q9GfzIl3$MP;IIPG+>cHHv0=J(@ncI)QgZI4qy_}ytFvff9G6hMB2R#R$ z|6rdRa|*;;a9dhS%(>P=(XcJdo)~hnxt{}26HiO3&4HcE3@JNnw~)OCU2@xdu9J7< z5_FzCeLi^&KAXqtXUT$*LMqB!m6}FG3X};Es}VG0EB?QqCQHi!=E>}{VK4xs6bAxp zC1aps!R*>P&m^^H-_J8z3w7R^voFHF%oH!gOm%<>WtwKz$7_n(JSGaeyRUIRDepYJ zL!KryuUMBtUfDfYwJ^;yM|Ec2U9x@UWnR&r<;IiG@buG9@$?gqb8_P*=NIQZxZ3iO zPk)9xw{9SLB&UJTyznwVe00LMe9beAtIV{o27-v!c=9t{#cY7&x)$*cXX;?m(ssb= z09CYS-UI;QKI!G9^2|G);`z_L#H)8NdHkKn%?=W}&k0ed&WQn`cl@LvJ%t*=wvvTx ziYo?uvjjb~pXcu6$AIaeKuAJRZ$2S4gs&Iwwj92G%_c6EGQZ3I#!wFS3og4{e%0EC zgWKJ$i{=7U>0s2udam2N(&q2&yxtZ7Q?DtzM=9L)(Ecz+iLZ@jgjlufsu4x)hD64q z+Tvi0cntzWzi1QL?%yWC1cj*(yO)MyY;|8;07<3>;LoYRL?1)~7ev zZ0?gtjH{8HjJ;B2pcGhTAq^I8$tl6Cq#?V(Vv$Ho!!U+Nqkv;Az$2}}9yO!2FfoC6 zYD%qL@z-ROwK9PUS+b$=YH$$l1UnJT*3DT4R09-BEr54#LagOsw1o61>QszbqOKUI zQ0m?cmhc_Ca9pi0XeD{;)DR9v5OEe2iHa#Xih50jWQFHyA`M=Q3eeevnK1+s6e|#y z-C-!D96B$uFkG9%hZX511aRrfc2+OY)@vP9bp>1foVmp`PqWV>>H;vZ z4I20h#(J_e3}$UVOQdlCg45R}k_|$zxq?*#vQkqxr5o0f8X1yJjIm-VVM4@smq3gm z)Pt{0?}89AvNKfN5fPg~-5H3u-NTEoo%5CNdW_<2OGB;1{27KO?fs}nML3lr4b1PL zSoMEne{>H%^io8Q12|h=Q~WGNE7VTbm*GuxcV_JPHiu6_R@2iWH` zf)DZPY>eu|X}lsgVy=M*+ULi_P64v-T|4*P$TH?$MEP}LTg3$ei0gB%&0uRlS*%H0 z!XCr`-_jxT6{q3{qs{hVRbyOkHXByswZi;(-LZLYhV1pZ`$CM2{#n;&EEza%KOERT zOJG#n-sg> zkz*vaG*0L;0@Y?363$5!{niD@s%5YL;y>%j%gOLIx9ZV8tayh^d1^79{^q`i zF?}QG(9gTM6XM{6*$LH+gX-zb1}2R*=cTyW;VE3D>$n;^#jg>mc!GMDB(r~u8l_zk z1C9hlt%N{T3xY|tK&SZ|1F4F!$x_%%4&bL`44>j1eyeL2-vVW>Xf5o^WSJ7qKvuOb zuV7^iv=+3&%_kn`V?X^PeE0{ypJ7<>E#LD8c+ao;5Osga`sN*uHb;!Z$SeankF3^f z8^1R~*zYbmdvKr0Q?KUTmN#E}h5hcFRUY{HfA#}>`saU~@A|F3hxfeeIab32Zp;4M6KomEYmQCWD@8thg^#Z+3?RR};ddO{X(f%QS z=A_Oj4_hw2j|HZp&ZgPibF)1lYBVmpFH4hyh^JFk-;e&BsSUgOp7yK?pW@I-MgWm6 z?;goEwSZ@@>-yGu>kGZ!7623aSPtI1rpU3+WT#D+s?KEcXssV^$ley+E)Q!dAZ` z6aIvq9|dw}utz?yc}5T^)goxJ;>wgz$c{{gRH35D@}vcG{0fj%{8)KygQSkKs~`Tb zhHRa)2;zDE;^1LQ)#}xl)NlJSBvi1qBr;9fyoJ$>?@784bcJdW=5QqO^U#fL_0L2O zsfac<62)_16{6dfWI>8i4@D}GjiS{fX>DB$Y#RMKD%oUEstkj0vRPYXrB=#RtxgM- z(*&ENBML&%3F^f0=9ul(8IpxTthJ^TGicRwLk&c-Jmq<+&VcC-LrkYi7;1FmO_hMPQeTxVE}A5)U5NkBER+X9`I|Ein$CC^yhj99W@&7AnbV27vFCj5t7KSf=kK zTz+CuQ$%7UTA62Q#+*-;KaT6rW80k7JfHUbAZ%RI#?A8X= z9kteSxkLnpX0&Rc9}7LxBQ-Ymg4Y$o87IqesV)?a>%Txm{8b`Kp%w9TTMLUduwCx@Se--FTeLFR!KA1`xl9W_k}>i!)MXOXRIz;Fk;@W z0sF<~eEG6Dw)3jMxEguk)pI`c+5=c0F-?1nHJaUDP=YBK`%@y|hB7rZ$ zHa4F`AXWq|?qH`LX%8=?*6Kiy2S+?bV^(WT9eOeJysFEeYOM3QOzvRM?B|I}Wj}4L z3!@CorLakfvY*&&tmW`-cS)JI)@u3ah_lN*pZomh7*_+eB+4|C$>ecBoYgW8M`%h+ zwXmONc9)k{lQ3nblt^iCc9`3l2{&XrtIWDCCMLQw9dndizAxO3Yxa;eXDGF{y+!J} zB*2{r`iTP?;jnAdL4EBIdMd4J%Lp(=%WBkW!3oAZk8xGSiupRIR*Tn#@pKY#5GRa4 zapg5@G3Qs+n~xMr5tn4wklF3wMAm8`Q-xG!j#lQEFdXsT4}LwTr)yq){zW#&x6xr| zyLS)<5k^hm46pU(h|PNB=8fA_EnHn)vc0-ub9BsZd(8gol5}*FyKlbC_x;`PJBI4;DJ(UDL7T}R5?G7r3btd2Hu4^mBOc% zUW+b33+q4&!|I5W%?aC`rMcxHk*YcTbTBfv617BaERj0g@@2lmYGh=7`%?@IW6BGp^!7#I{O$_+3Hzz87xK5UgmH6c8(z_MZ2CC3l{953j!V zI_u*b{Nl%cj<0#&d-+4Z_iymx^B>`fnt0~1Z{|n-!Jpz{JM(iN`84b=pj?vl7?>GS zvP$#2EAp5PS)SF3W7UYPIgQNcvwGwpY7C?qN41#piv05GY$k#Z?5RrosZdjH)KD9^&B10hFn9+o3gmI%ayPdvU%_2~s&`q+X0*BIZAER1?rYhO?5yYR>fJO%mnv;_K znLC3ZfD{7-a$|%^jnqaPON*4!Wsib_k(kE>_ zJX*c@J?7=$(HfYMT+ZOMV*6}F@=fN(#_dY4v9H{?$@FI^#SJdiR%|%r(ZRg>`NDNv z>|^!uEuH*e8@1(Xj+(SLO}^K-93ZS#8y=jU@%$@q^5mU&a$$Zb(*#%d&bWDcWYKti zIC6EMi~Zh}%HgEgY*=g8!##6-eHT|}xj&ZQiJyNtERVN(9WW^#`rE_fxehpeq3p@<2+x#*B1g-uCL=4n$L%?VLEfQ8o7V5=c6yaNy#h5 zoGFu?(d4o~TCIiT^|V|M*+Ha5U7OwC^_8VK6_>$w=2EbBMO!`b0RIB^zzf!fyy-Oz zJFB|e9X_A6F3y(AYY96l_5gl1le6cRYor0W&}51%>NtcCh7d*3MV4%B%o)+##uvAE z*e=!uupD-y64<6Ro5uB*sxgI}312+mWGTJYNWg~Nn--L0ZMicFDLs7l#GP0Nv2q%q zn*Ap!wQ*>5uB+r$FYm_54eEYoKVNWpb&krO&1TIYJ5~{qCqY^vxp?;*OBcyPvr*^S zn-65$Yrz~+b%0A8%wFifXlPNYQ6QFeFd`^tW5?wG;=IK(&82jG8BT8vP*R(oF%a8i zTG4KM8ELl0CMSaAVdVzUqhPEgXnvMr>FjNefvSaSl~_@fS)Ik8)>gfX<+zX88F7KC zN|~z%i3(_9s&S$sHP-;AX}9Cz{DPblXXh7u|NrAJ@GalP#i8VZ+wi!QZ$wvR=dfB2#7{xlN>@ckK+; zYF$Xf-qE^5;R!ohBKDV+M+UB>}Sz$^Me^RW?bKi67 z^6!&T|5@H6QF59dhmyeb52$Jd$5@mR4Pd<#j-N9Ov{$VJ_A4FZtqz3k{8}} zvV%>%*)vwtG{&3ID%}rU*n!L78(}m0**d5y@g=<89s`CnK1vB5yeIvA@CEWBwrw@l zCM{{eFFrM~@ZpfVvlQ121-QI-D2N`h;b6z;!oX?~E1adEv3Z)6*Bq4oTr?*zsDev_ zxXq0$jmU=$wuok$@*Y4uQj&yHto^UmD@okEal&U`Kj+W<=|9i6fA??ZiT8dZpZosr z=V$-%-{GZ~|A+kKKmNP$#tZzFPrb;;fAI&&!^kiF$lpii0(nK1fz>!Nq=eSlSp>EY zgST5246#nx>YJ!|%e1Dbx}&y4Ljx@r!g;ns87N{zLJQ##dJP{*4s6x#Tq-U46S>)K zFLYM~#M&7~RNQ@e!ud3)#R}SHu7kc3c-+*Z|#-r5z51`~!wq{w%f z^mUiTe`*g9X1J|$uq<4mhLgNJA9lc9VTdW~<(cwb%R(+%fv^x{08t}pAt-GxnsHW( zdaka4NxcZDxyk!^CQC*{S*;RtEhH62-vh&t7_+ti8^(e4IJA|?*-U{l*`kOPtMlQu zR>FXWBjYv>KPzqVT?wQ|f!Gnv>hB96saIu>)JSQI zE(d_m(l2SoeaKdqp^+MB?N!66@br(^f6|pupVNLOLIlSC6JOXGpn8)O_I{+6s#eG= z9_$Jqeg0K`^?TpJ>G6u~R30j#Dxt+=11(b@}`y7j9IdHb! zA(Q_3+7!Lt%Lk~H#l8u4%wi7X+5tp~uXD_&Iy($)#RivY8?w;y%@|muRk+LLOjBva zl3V_CJahY`@2^LXpyH%1KMNXiy6=N-dM{rN07*Yr@mhk6=0Nlzg@IGO zkYDz-v~3{3%=0h5&dsOa!JBX1XPPFqXZLyf=D_23P7N4v$^0lkUVP2^5;h5bFuq5< z|IoGG0x0-WfR;mBqyPJbt`S@5ivcUw@3E9~*X_7?p6laz)OA(0)wOyQlji#Mez{DQ zw|f2ay2ff4*%kQ6XJ6;dt*|;;VfFW2|9JhekPS^=D^xjb6(83G9B5hCyD3@vgr}#f zboPd@NZk;jCyiQ|yA_pJbzs=tZLJUvZG7J;o!aDyjjQPbt#-D>k9dn9E!QbdwxyMt zJy8}i(bI%5nb^|u3Au0bAll?g08Ku1p;H2xNj?XbdSa>8T~`JOV&C^m9gMdW#k%%n zcif%%f`QK_qiw9QMp2U|X9F>c)@HkG=bf<-<`R)APeJu=D0OBSSB%4od7iks+>?fp zX}aQt7e3F;8^_!_KIQZGUZ?J7kjz{wrOb?Jpw4>=QwO!2ISEnQ1yVM7UP>ZEEAY465xi%ywIU^km*P_Y_@vo|6^ z_q_b^pW@B0`}I8guCHKsb&qkqBBgG9{V4y@*fq@0a$4WuFS z>gPYp<%2i5dFLG*-?+s*?MP`PJ6JH}!TivcE@O2e_p`NK*9w=Hd!Bg5(|q!CcgeMI zaekl8X5{5pU+2Bgy$e68s7CAMLwhd7L;#^lmMBAKRSBLN>Fn(;7uv?~znj8}h8S(j z1`Q_LRSm33W-+>5W+W_&zr}{{8yKRE zt}^*1x8hkxr#E@+l@Ify-~V6pJAT)w!^I3UAJn${=-sTM|2bP${4Qcb7~K3~<5b*WSeLmo(OeHeYP&{}y_tOHdjSdS1z z3W)l;*@BrgwUjd&olFdB$XSh!e)fKQ{l{+ph#-q2_jz2-`X)JJknIGK2sjEQ~so#ag)>9 z>Mt1?#tkBM#jahD+Y^U(+u1_6(m-RaV_H&+7+B|s;-94%k+aWf4p1U?VhM)SVI;ph zX%QVYdTc9)*BpB2IiuB&h$|Q|13W$>?hL!MGbQNb>xwe2wRAKcRP%FY%FbXmw2vo? zMfg+7o^$=M;)yd@t2S^f*en4kEsb39`E_%PD45(gDNt^%1o^fMJ2*F9flCm(+s;50J*JlYXE_L`)f5+Lia~@qzh8B58VH^{;Zr|X= zS6<~u|ID9bzr8@KaA%YFSAO5`Bo8B{P74c==FBe7Yacm;;n~ktkMKj6JrR(&eoU72 zMIIG|xo)pqe;ov992J2DlWX&S;P7DU+5mp-XQ``{aE|=5bO)(N{?bEZ zz78TCK3^a2A-MA}F!WY)v^0XRKU0YL9@uC%MYOUWN3II|+-F|lrL)3nvq7zzxm);_ z-q2D36_j{J+xKnOo3j}E!6NZF4+b2Byo)*{cyP@g!p7%Rm`D$A0BK_bhrMC;k$^KM zXtq_XU1vCYkzX$WuL0a@1kBo20TvzAQscbY|AuiC3eddzg?U@!B)bh{!HuxDt!$l8 zEVuZ(0Y9XYtI6rAN_Km$fERS_K~yUqtF69N z6xf}sv6G7bt$OW{cuFmm{j~4VQwq148#LQEfF}4w zhEK?t$T@SoI^u~ZoaAiJ&vm0K=XpN#76tuOC;Uh9=VOfAHaWk>%FyD7oxx7f~N)$ZfIVMHF33?j&u} zYGHSPR`ZEnD$tO{CMyqh`awN0jr)G-ueX)brP#UJ@U z{8zm2*-!I*|Kb0XcfaE~{^-B?=lQdL;XmZXANp&2=v#jWAN|P3(KmmQLS;A295Em| zupW|C0M7$a%v5sns0gHCM94<#q*@jIFgQdYdC{RLLYD@Rs0Cq*7Am-ufjsK z2RB`6ABtY|1Vf5o*_e>aX!}eU!bWck6x5Ap$>gPz<@EqeI7?G-p`;=MTe2Ur6tzD> zKy|t#sXD+PMyBh*EbTK|qZqFXj4%?gRI@OSmfdAy|1|4SXsB=lC_EJ`q?eWoS==d4 z;L0}UH^2wPjDb*?wTed%>=~xHxI=Cx_B2Yq1hmWZ6|(zuigZ3>rl zm^8Xc*>fUsCWA3#hMPg3xKUyMTh!-Y9kf!9012Ky8j+*8r-IH+vDxQcPWgI-lM}3) zFc=8n1!x7nG>i}%M~$f2f(;SR;(E!Bs;INAx_nkQU~uQwE5S6!%J#&eT($4x=Z=1# z09~8o>Owm}D8CkJ=T4gVw(e^p!8Wt}eO(>%+SLQ*Prb;mde@UY{rF?-rkS}+Hjl~A zfaRpxiAnt|2l!*ygAm4izj2pG;oOH|5QeKQpPxr(tc9`;Ks3JZckcTvoTG4H9XtqQ zG{k@J)dohAKXf)_@q>|lx{=W$gMPCvDdRPG-7~e+%P>CtP>82Y5c(*`vt_T%l^Wt3 zQCHQ@#%Umom@JC?>6TsIm?ee9JT``d%WGrAE$$ogcVXXz zW4{D-;yhjl4&rZ1d+0g<_*U;f>RQ(Ujf0m{_oWq7(`GZhB}>O>80{R#nO}aV8{9r0 zN}I1ojrBT9W(j6SO3hm_Y!1fu7C_8$Y!3le3t%Yh%Q(5K^_t6V;paZ{DlgvKBI_e) z4xZ`c>TH4PX%?1+;Z?Q#`T|HjJX?`k(%4*WPVH=1n`9ws9khU~%bc(UOh-7tLUQt2 zb)P60$o}jmz)bzS{tqWQWO{U{2|z>XYb>575C)3PmDm20(oI{3~E=S+}Uv z<(49p5-gD!$sTM`MYMnxQX1HAtr+j|@iAi_7%@AbR9LMBwzIIEty@oVy_EClx^s5B zC3$_V(D^f!%rq72>L_9&IBUaK%+azG(Cl*^XCp=Zme}^{&Z8|-v)dI5Y;yg4C+YRi z;()tO&o0sx4}>TV7DCiEOp*-DuV!Nf7y@qd)Gp_v{dtJgR>MzHE8Lq9PdiZpeA9M4 z%qCY1=o8zkA%}}j)On(knCjj-WiP|$N%Bq;=${$@#J$KoqwiCY_Ny zkY!*Tya2XZsepOjae4nf<7(veXvKVf(d2nchU&8BcW-f&Rau~rlP^3*+Njn5aX>`*wWwb6^+?kMX zg#<}dNmeuzF@;Cu^z~cV4`DPVcMxpyf<~{Zt+-!&A?RvupNUrZ5gui z-luNx+ke~l@W=m)@8|x}2F44%<-7kdwN!3D{TK|V=>Ci`SH|_RD}hQLM#f=dW0>6_ z_j$>AAZIHuCOH|^Ed-z>r$9PWA`SKgX>lS%I`aW*y@;3-YUv-oOEjW82HZq}HhU$H z>J&c|3H73n$^LB79PxLYAuxz33`?tWM=L-X|F4P|n*()b!$QG{ESB(51~P7xg;98Y zo5x)i%&8F4(sLLc<)H~0wRVZhg6dzYTL9*Cn~mjrDHL6)4fF|sqeVQ`A|=Vc*HUG55`_j?wzQ_ohTRTMRwE}P^F7jvYdUZk#NfHxy0fNEQDV>6hHtnK+@Peb9bhDLIQW}mCBj5~wpI%6OV zJ@LqG2zoUKG4+}?S_P@Xczlcdddkmy`ZYfL;!6x!*sPE6O0j-#rH{hd9t}EMz9d8u zA>@}%ZgRQQ=0v3JbxGOIY7z%Ic~p5XH<<0eFm^jQ6Gnlw|M2Tn!YR9Ujku5h-sLat ze8(9#qeP?Yd~-U`)S{;eGZO5GtEKQI+q>j&b~=b~-NBEa)pdRKdj_*b)f_2F(e0}5 zeFK_Je=8Qbwp2SiLFT}DTEKGNLNvDh<7*lF&d+*E!g{^p>F2(T4}R4*^Hm@EX70S> zz1Hc?>B45*nZYf1`_XW892k)#UW8Dt{TJKD^^G$UOt;1FT`uJ?rrXwtAb}jb7ymAQ zzRn_nhnXcyx!B)79J8e^cN-@Dz66I3#^awSiFLV=Y&!K3<2JAi1ND+R_$}=|ZJizg zzAVSy<=^5grgR|x+B5lv@a&m`YdrKUZu~b;&##+OVjME7)r!04TYmDBFZ2A}9eI6( z`%?b#E@Q(%_fppQDIs*S*-W*i`YF-g+2wxqnl6j;Wfo?z2*dVs85}l-jhd>iJ8IOP zbF4FtqS5gvhGzCsRX^u`=Gxf`IVb*cwJtN-4BAN1Zsxl?=%yoT=pji>Hz~F! zBABIqpMHVc&X2-Gen|hpE;@3YLHaSE>8iVV8!UQSYS(p(%91>&s?VY z{bT>CryG@NHpZ~mn;eIM_4&t^4p9Ar7f$M|jcK5M5Cbk;c%TPoy6KJho6rJlvAN=GYcQcg&sHT>I#?5$ zUZB+oOcC%}YFA?!&GX&@<#cA1h^==Sl-+*MW*pgH-sSK7mH!hjzwkL8fA)PG9UZY= zZ%9LCSPdMXo-mGUa>|V3NX{z@2v+5QLymS%D`b_&rA2(>_VbT2K% z*-^?kjC|&kAK~YI^dB)@Jz%w7vAej>gEwC$i88D$-Dw<0l2{S(akWAuF{aEY$(xG^ zMU+}*#$m;Nd&O?Mbs$Ju;agnVHSf#yB-hSklk0M@*uJlPZB)E=PbSjr9|{)4J`~Sz ziFA5(az-`Kq*k$X&g!gsXzb%APaM!}`^|&;eLsdzM%!!9|D2?^;tf;FAGY2dD`BEMU2X48sLP(ne zq0LkBIuGiFF)iXBU-UW`wfLM^3QCREh3y7*E&E>bl#FW;;W*%K+Y3vf;FZ9AZFBtH z_h_(eV*gbt!KMXY=Uf{>Dgo^CgK#(oV@_A>7?1|*`+w+CZ$&W-gz5#&04M2j2c}t_Ci_6 z7bBj8p%(Rr`rhe=JZ}6r>%`~Q055NwSi?vSCWExSBFN(VzUBcu#K^^rpD?sL6T}S0 zw4_qTwun8iJ)O;TSun?fhG5SWaZP&KX+Ve%NrjUP8yfNxIHI{|b z+QT^N(ZgUl7JAXi-dQg8iOGW6&UYm`yxDn(g2RC*bXnv3TPs=wHaEE15B&U#cX{pp z1z+~;Q#^M27E@KGd0KQ1l0`wtrM&I0c(;8n-qDb`PHS#DWvOVpJd0?~!b>{1Z!f`90oE+)WrV@ivHbqhTuF0`7?end67 z=F~S?8L2pOZSEG1PW%p}JjUZ4oY7W*)%VYJfFjZqY(C9FAIH&5f;5uGHBUVIE^gd7 zWxZN+=X8ZiV!Pckj059p{m`@cp=$C0q~(X3zZ_@DdpSjkpuYYoJZTtJut~$8(QlDOb(xonKduM6HUK?Sh*Kg{T<`*APS|}wpCvEv2 zImRXE8_c4(pU~&lzBkY|EVPuBwocdP=I|a}z8!!6|nBGE=KE z&$HPEIlBz*K?+4Isj6aiL57jdD6EG>jnm|`x4 zA!p{QXpv^~iU?&^QZ`mWZ40pUCj!WFh~B?zkMHxAjBR;6P;Y^r&fIVAiS#pS>s6Z! z6V_ zbvhA-VTGlWiPyN(}gwOIyVq^U)-$IHQUs2evz*gl6TOTXOL+e|^?U?5T-HV~E(j7G<92AAbn zr36x7PY6eCQOS6qoCiOzQJJ_~m$v>s3xuLfhz^#v5*DDCGBcB^_SWD&QNB19#!SW&6~XW;F7=k zH~up|@TuIo7vs^NT<6BV1i= z$u}QkzlVEoyvFT!eTbY#s+BNb80s03WN9F(XmVg8RHUkX#&=pGgUs#}4wkB;kP+sF zj&`HL*2{h_Uc}8M5gnX}-%ZJ;h0;&rFh|0HgGggCR=^-GAd(+stm~1)IzcN^6^2ey zZJFLZZwwNvt*AZm#4V%=2Vxq(T;9Tn|Vsrr`7F)%yiS4@=$+|ed-T0#Y+muHJio`A zzxWy6{p3xad*%sFPEL`ODYZ0x5tq5{5Q9bC>LN#29~z2zK=1W<45a$Ub2 z-q!)zg>$HjzsG0dnU`Zk&6vz7FXkp>R{z$aa~WXmGT7j9LrjK+ff@#GJY0OQZ*bo~ zeMQ<6#5_ye_x*2S{v15-g2`5jty7O?E_9cdsV7J{PGNQn% zufD+RFYlPA$_L)}F1Gs}AN}|zn6_Isn+=i@tJT2C$q7lUVB;_hUNfPBdhjDbnfCw; z!^#{EEeKxhP^#zYJ9sPx-fFGnVQ5SN5itgpKjU(*JGANZGXTX5W_lsa2qwg8M(*{OYNw;eHdhxa_AIrF zt9Va~*K~8x!42*(5jv3V>*NkO+EYYIEeEoZYC!IOns{(_!R5YiF$?#$a5f1#9x<*) zYMt#Kp>HueFKVy>sDyp8n4<;DPU#G;rCbdPJlX>{;w-)DtU_4^{Nl`pO+*xi_j~wR zUMP7_x6y0kfy(Un60}rjz*?=ZB~Z7_OtgI@EhT!vyS4kr{nCJFX=kPv=Pn0MQLH=7 zCn)aBr*?0RXmec#1-jgg8Z8HrXhSccF^ahD=tvylTD(ZMDMKjI=7Lcz{uzq1m*^f$q{U zWa2dKB47ct)Nj-m!dA}o*tVr}YG7O{F*Qunzb${5bs;pNlj*pJ0b>{TuO|CA^D0L>+ z0nudTC1O7i^49!}E)$hQRwr%Dab`Q<=H;60u&$CH0NC#5%P7&f9vu%{J7F z(Glm=7O*bfby>}r7HGd%;ma&A`GWL=mYmKW?{&|l7r*lYX~}B`ph%9+I$-o>ap7am zr47wd|s$2kx8<__k0j^o6W?-1$U2aShvQ`Z{ z5GhrVl)1dT;$2Ta#lQN8{>L1xHk@zIxtRt&^v&PJQ(yK$F1F`zanAAS<9zzXm-y_D zd>?0Z#k3mGde;(D6eN3{nE((lfl(2%WO5ozvDU)qdArq9v~t1^Tx{kZaW_vZQANDI zKosS4C*Z)*S{VHPdqmh5^0yUZLQQ}UV#1(!x=2v>A}F1t18Cuh(!`|S$N$&TlX}Fs zF>9{X5vjhRhOUK&{K+%@?pp$xo>QADRbQfoGY0~^G? z?#7;~GS@|y2o0mq4Y=?>{d!hV&1t0xr$8SH?c!{h+){y{fsPXLLHn5}dBHtpFzXXZ z*;zB)Ndl)^;}Nn2*cfR0avbe_3IU|zwRY?}(j2TVa14kG1`Py|WreLx5z=bbqyUMa z)ia6vhCDU&)Bh$4-mblYylv=0AWerL8GQ~NEUKR4Ki+zrtL>VPynL5Wzw!pp+&Sj) z8>gI{p0e2-nemabDNlFYMG`~K3^_6F_Dy#rqrA0fC+f|v3x3W8p*Dj(99mF{79s^D ziZ_~#rqG;U+h?`ZwqL7y!RcTXEcPHkDRZ9(H$Z9|pUH;m26(B}T6ivu2L<-~sR7-! zRA>45q`F63J+&aJ{M(-P@u1AbgA&2wiZh3G$g8!G$ALP}mNx8X4q(W_G|kMVFb>&T zmR3t+tferQYOIx7m?mexWz*GjnV}Z)IFj=~nI@((Tg@P}If=0+=jw$WB{L;uwH}$b zJ5nmlQ{}<=C9mJR&vbRp{RbE9_Y-rqB9_OS!PhV|4kLs!DGtGV9VT-YTwaLPK@)ez z1K5hQQ{CvzgLQdaW%OSZ>?g%K%PFi$p~!S`P{3%YEg07Uy<(>2x8 zOU;;1VtSxDV_Do8nI{kAWLfA{d!8W=txj3g$O@TawImEk+fUoJE|#up-juTZXe|aKua~*(3lu*bJiNR*TOMh*ig|WP~FaL zY!U(}jX-4RLLYrp4msI_#xd7*El@*X0qZ zbE}hs1C4dAR+~r+J~QCiYRQNshPq(QYRFl8`e5*+lt|)5a+A0(V*XALWJ2G`fvwuX z_%I3Qa&0HY+KFTHl~Za!U!F5sO7lbarEok99IrCEyJXs5k%p03Ay0d2O5!rox+`I< z?^?V@X>BZ-#xU|RClb*lB+ij2mJ(RI?=c6vQ|#}X<6|D&dx^jG7yc7I_H#eZRA+AA zc^sj1RqnQoi(y2BVO&9h!2E}lndX_*(T3G(q|BAoX2ohXa=G1G>YL#5)r6W(m3-vq zDlN``0Zq$pXG`BD8{0*CVftE3r!C6vv}f!#li3f=->H6vQxE&ZevSRn{-$1{gs`pM zPjp#ci*vyIT!((PR7DQ-NDqhwIOA(EL~4?BW}q%@R)yk0`;g=ZHujhHdiwy3T>DvZ zBNm&q+TL=(QE6>4H41QrurNcv`lP5&V0K}hb9Ut16i~Z;7B38?j&4On#i?&B4hdaR z28%`9x@(AXn?x5{Qrf`^0RXsh>;{!Az9oVOW!iyQ&X31}pfMLxi+uGIg6^>FwpTp% z*e%}sRbR_+e1m#9v6~8da)+DGyqjP3p0D8RzVWMh_PuNV@?ZQ(e(8t)K63Lh%EcAz zXY!33+7;lc^WewHXOU5j5o)`z2XPb~g?}qWIovzH=xX zP=b!D{(S+WTTDlD8<>;q5Lc~aF~dS7wfzy%>@eYj(bJG;tsbf9gyIH3X!VqlC=Bat z6;ElhYVbbSeip2yT|}j9O)>_?NJ`A5kgAYsW^&^%Va%1ZUQ<(MH8^_%(43j-%*L5A zbIshnR~Ry!91VxDWG@TTF&_Q$+OhOs*pu^Utwf}PO}W21Pg8cJ>o zEao3iNt4LBGKYnJmXg^1Exzu{qTw?%`ez;zDHvMK*tG(@iD13B0i&T-pr^TsM z3nQv_5D+k@TtW_+Gf@MCMTw>q8g(X8R{)PnLs+s&TBn`ZbIm&tQ|5Fw-$UQmo@UYB zJ4l)6*Xz~hp`GtVI-mObrB=+EeqXg`kD5_E+d+c)Z(!G48P}(**EiT+-shvQy+MBQ zv#ez1Xc)+1&SyAj-EbB5`#m`cDUalw+^IB2r_{nc&23$kh>S)`nS+QL7-OQ8;>8}#7%66Pd)&f6mbq4sVo7s&q7zW5wVi*HYZ*=E z$R>BXYEwRL47OSgrR|O6&PefnvKj1}avkk;WWqRvA$ z`85n9w@y#U!(eN@tbMHo_IKTq60JENiu z!-gS`)KST^6&oy6$jWZsb6FElJo7$=+dJ;vzeh?dYOR#AibIuuf3KG)K*23F%6RpzSmsmWNECT*e|t#juV|7<*nwcZ-nM(+UZN#WoI}$ zy41rEAW!xBBi7Y#acⅆyW=&FjU1IDC-=Pv&(m{Uy@xHRq)jr`1cU{ckSk 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�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

    lDe^P-*N94 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�MU=&1+&9XO05` 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

    ZE zNuh&k+z2%1dx{PO^JZ4s&xjs{^E0{ZL!D#sd8c!5ac24N>P88Ys~+02aizhNo_Fy! zlt!J>wz^eG$iMjRupl^(R?0I0CgZp4zhl%n8!m@j(Su^nVg24GHUT;&THB!0Qk8n| zIO{-bJ05I6*F9t<=D^&~=GX)PH(D^#Ky(7Sd%Y%wYD4$b&0QyY>wbU*;#5tWOJCQC zMLA3dhQ@r*@yE5QYgnJDYtdXqiWmr|muTB9GllB}FQ7p-_7COY9anW!TzrR>y{tw_ z?iBJRv1q3 zbhf}0IyFvTEQ<4wuvXS^vX7>8l1k#3k z=Pg9ZPG!lqTh zmLfjt5)!9S1W=RfQJ~jqGZ*dM*yRb z{Rh;1=*#}y^%<|4-*{L5W0A|rCCLcyZu`YA&aotr^L1^N0oo!Mu&v{kOAm$#ey^P} zQ>3uS6d-2tje0Hl>{36$Gw3IUl&m!0MEtmFGoZCrJf@`OzM;2M)@IU)zD;Z=^WL0# zhoIs9y?eN~b)3Ae!WaW%-!b+bR_w9kcDKDg#vZ5n0Dhu|o6qwYd8;*}NI`g2FsfPB zg%L*w{$pdcy*Sk7VS4zlbLm)r0kFmK*BYnXkT$RFoxE0SyY-V@Ov8*_m#HYZ_BiZ~ zkVyF*={5L571GCc4S*&s);&TV-+1)qYQ&v9{6gRiNee7<};Ub>q=QX z&t0}WWyX9pkM@%Cpsd|Nh;XT(D9rvZbE0_>SQ=llrdZvhHi=#6;X|q0l<%#7E2!w1 zB65_CdS%YS2o_^s`K8@sKc|PvB`>Xpc7o-?U)1=AwA?!i3wwudsVKONk_IyjM2!}U zo{%;mGIBkAj<{b;*~n@nlU*iYA=WpYO{`>F6 z{rflAPN$I9&HLDTgLq|h)`B~N;))5@eZ7Ka5y`cdB#9K^xmSG3LSc#a-15u0kLSEU zxi2N=H|DMR8kgw7Lk|`{wu(Q3?yQxxe53Ap){OkSNR#2*z~$V5y7S%Qz1Mh{v=Hz0 z`?#0tDH}j`P+*GRY&vHGucGlndB^>m#sNiS)#Xzqk{k3Q=>DP=d} z)YnLPuJo>XRCugWS<$*~_0W#IVO@99bk-VN>7REoE5Y8;;>5(NGXy=dzhbA|PXC?n z4<5mP`ygJ65H0!qv2Bj3PJCUL zqn>xI?~6evr_uOy@TYuJzQ^Tt!MvW;Y4auS?2`_Kos^_gTu(@SjdH&lyzL;7tQX@gkNMJ~GaL6>k#AFeYI zTKO1~MIoBrNak1rW|Sf#%I_T@#HX1N8A>sdI4-TL-i`w9gEg>wxc?ecS(r{)RTVYE zfC^e4o*HAou?wpM9Ok54SB3YHxm?+lceZy|8^`v_&DX|!>{n&Xm)~o^U%^7Q0l+Nu z&I~CLSBt`6{bPC;5N(xEMNhGrsvc=%Ofb=uU944L^<{bh2~|4@fM7_}l*e;&ShQha z!=jg*v3fLuNW<$g!6_ZL=Ybbqcoy6C0Qa^QN3?eUr_E0c7`A8I*5jyT zhlLK3J*QoO=>|%2Ia7|@_*Ce-?p<*J&?uBS!LfBy_%A@Y2q^JC(_84kj)KsYHrwBo zlg|XkM?qfS&75ki_e3*ViBxOXu3uV}!UE=&hpy3h-FNjnzBvUc=QW?b?2y*yW32XB zM&58QT^d@GXVp2gzV%v6fq=KXc}@jd3s1HM5O9rhZ_>*iGv&pZ?Udc;?|VXaaofXZ|IA z>>qz0-te|};8VZ)Squoes5=h||5t!g3(BNKM&x(|EvyEN(ZZ!XSY#E6{3lL6A zK6jb6?en&b$0IyFwSGCV1ZK?Yu1D~oM5Zutk5QJ=Hm3ldR*$B|vbfKvIi z7D>Czk1wj%fXO-FFtQ&^_aE-fZ_2cWX=xD_ij`PN6$%#h2X1wu>F_zQwCo!gkH0;V|F-bUay#zZq%B$5|h+^H^@LciDW*>3E%GuTokcqBSUU zCXG^wV=jz=sE;K-sS#;OyDSD=*3xCSh(#$t=sd@A@CoYD*wUH4peBi;@ONz3ax4O5T)8J-Gz47GkVq_Xk{`G#zK`dcwTC-$Q zma2-?kZY;)2X^;o;d5K5AY@iT9Aq|$f6i1ZVkvQ_0BD$6X{72NSl*SgLDVE-+=m}=ncrFGoS0i(i(F%=Kb z17iYhYk2;Jhj`}ULp<8gXk*9L8(O!seuk$B2@A1EL-!E>V2IOBTr&D^;E4o)MTN;^ zl$nEc$$3b{5>eLyDsI~qK${h^wCqMS*s-Qv9blqHLugEK1>^;qfS%8wA2RCW{wYjn z$}0Shly^;t(mN1>3pws}FBE7Z`8?ZWYLESS+$V>32M}NyTeh7ekb3LIRohHHlarC@ zjpiW>vgf#CT%|wItNT?#fI8n02R`1xA*Qf2NEj*ko-vd6D}0T_BVWWox_V(m5}55- z0zB*9CM}9_Qh*UOr=%!?nsmsNhS+zPvNS&+fACX4kct-RTGf;h9#0P_BouQ1QD`>D z0rC~NG!yk`m+Ab*3t1EBasYtIPs(TH33ijpj85pZ`YLlyD?BL%F!z1OoWuE=rPXq@ zT7D-ThzOp0_8GkMt#8L4{^NfJZ+P36Vd}&yo&vu4oBt@Dd+r(h$oKwTeD8Pv75u$_ z^!G6UoS%OlH%~o{4}9or@zr1R+c9nj_Wg{hJLVXe)1qs~u9&-eABNY5u+zp`x0J}H zB~_+?ub^2)Q5>x{iXs*Eg>rpgB~LHWM&j$I^m1FPs~kwnW#o(fu(r!7QB^TfVqE6~ z*ED93#zMgtGggkF z*yqHUirYtzAedGg=Db6s;o*75J|?7fOf-yPd`(n=v13dt@;f}*A0n7zM;Lt_vnVu9 zkfbrI?gpFhlo`MpJt887u5FByGSt7jcBS&1|0q{=_!>F?;67{WS(^&0kEPRUdl`xX z&!=jEO+2dmz&D#?wN}O|Krw3CDVzHQhFaYm)pQ7E3f3%hC1|q6p5VI{L&V|lI;Yok zplkQVTzRLpwJ>xN%ZnnwIq_H4Cgl54{jO_q-4MIZUu>X*@~=Qfm-3TzUYjS1=lcDjGeMkoQ1B<`2uSCG7xJIn z+va#O+`-6sjw{=t{zY!m8UZJ39+_UF&i}1V`#htG*iTLrh z?xD9-0te7Xp-EY2U_I}2=0brXyv7wFrO2F=?z5hXDR^Zg_?;sN%srgy z9xTr*w+(7@GKL9((+!WveBb4}F2^CxC-|<|^Gmu+8t>DPY0tKc zK9s+mA4>U~|H60@!;n&FF@>Ajc7?(U9}dPZXqtc(1Y3u-azI$4aJmCTMzqv96EQlp zLYUWiSk5JuJ_r>ykD%bXH4m#dk&p&1jXmxilL?3)={S2R?BdVbLEA^;kJUi2wpbe8 zz?u|!n^VjbO80)ji}7(zK%nV8)YbU1&X7x4P0pT@WTp+AT(`-%_Z{=EnI z!WRerzyFW_6aMOVemg$<+0Ub$zlc|yOnG?ITi${9fA!bmo$q}wzVz*HgXpa(FzU%_ z&SzWAQ!C8P!0GTZT>>i_sL}U}JsauJ{D(+guFA2(jUSPM;Y6#HA5QJ+DO^p9f-+Nv z#=aKfNjJ_ptzcC!*wd+IKsR15fkmhfn~B{GHOom`J|_Y@Frb;idW3JQfUY-j|?s1!pvYNmIhYmUHgx?u-<;>vF?~S|M^YSJ(&Fg=4DI zQhA`sP5R@8|5Idtlm5JbGs=foF46Vt!~*tU$O@izSY#Ou(yc;Z%Ccl#^_k2rGtw4jg{Ik$AJ;Yycj3(jLwFp9hmO0uu1oqrhP#@O-hvbr1^#y zz>UzhPn04WstwH0;hJ5W^ZHrErv#6xdjG%rE8q3o*!8^O#jlrj^M^nDjo}kKg&RezaOjvbY7JBcffmMmUoqgw!J`i#>t|dJG}58q z-fPI=2YA4PeLs3w>}A?Y?aZ(_4}Mnlrld&PGI*Nb`fz8#d}Wkq6sBwJC%4llmz z(KvIOL69^FhzTySFw!|E_ED_M3!O|fuSnke3Hz>?BA9Kn?hDVuGQrHEoI2e5J!y4@ z72ZYev4T;JX%i4$9OGG)Q!MmMAyQG}gn^#*yz&`5pcIrw$Rc`*iJP5tp*k3JCxCj z@kF%G@hyA|*XHKhdmVTMKs}5_jqiv8CuvbUm=O#e79Caj&Jg?8C(t5IG_R2F0E#YJ zU%7mshu^O$d;Hw!c)iD_)T2*)l$N5%DdZZqWlyBYEc@a}hcM5TMU62E&-u868!Zt= zfZf7+Ev>bv@6uZHJ=-8#w`ii)(3{tP>27GK$E=G1Geb1pfRX#FA3XIWKKW~(!FPSf zU&O!q$PZxX#O>bjxnKDNUiHKc-thJ>$2WiLzlVSOkN$BC1zz#0r}4$lehUBcBR_ZmgdS}$=H(HaKDj)4nP6HftxKUOk7BY@ zA!z8s;-NNwhWK3AI!MugSDHyNLNHmj>*2Eblr|w9pfc#H8EQda>h$YAyyBVFG$@jW z;w$oF=iTI=O!c56LlDH7J#~U*WR=2!v*U+_=Ym{M16Q=R_9I2=^zSc2>3P+E(|80*oBISTJ|`cz;Ji`2h489vxV(0jwK5_rFL!ISsz0P4EPrQAO#+nf#&!$=mMfYh4~c<>4as6T16IPP^8X&p*K zY&+m3o7e6Rf&oUX$%55Kb)Ox_ zf4q0u3&m2V?&QxyUr55$7_;sre!tH3L4P`q0m(9*y=EJl$0FD0q7_&xD(_$v7PRaa zQ78tq%^U-KJ-MdjjpXBiF%6j}HPsA2+Bi@~w1^p0VEak51$k#3IZ5Dbp%|b6hnT~R zDxBBCcR`Gv?mH#+4|X>^WUA< zF#<*oKWjsn`*`a7=n))0K?Jeb8P0R5%=--yGNhQ61=)bCP42=CmLF)J39R6&sWDR- zg2s2cz>8`3l#j}&sr;!%2<|L>TZC^BK1sNRF#&$OmbIlNGNp>MP=2^sy&Vd1?jU>r zEFqn%M=|7v3neKizcSawqFLtruC=(vVC&UH%!CqyaYXb-k3cVZKF8~J<=P%?InO$M@QM%F0>fkq5x!i zWpE0Uw!D}%0WO)Tlc@t1qlGkRsHWE+s-gJUrZz(i^T9>n1+M`)89Wr-0i9-OiPM`J zTJPvxaoRQm3v z$Olkzxt1Aip;8oQDO0&<$4DjQ@ZIC+W`xS-7zz!=wt;hg!!sM;ZZ#ApsPspj_cbrZt+1nu8`Mv55->)X z%D3UX^jMMZ%{VMj?NAZiJKf;9hcDmdFsb9d203Z9ApT^JstDnT@KmR#=>}NlQ^S)a|pG+&j=XFnJj9b0_isaZu97NjD zqS{Pd)P#t%5vGh+RU0d=6j3KNxv?co%i4=VDM1IWF+*oZq^J5^+P&2< zkPZSZgsUnR#TW}{_;}9T3zZ+NfPFPIPc11AY_TW{%RVUpn;z8UhDhM8>9t(uD7H)P zVedXbZ0Cl90M5jg4a-QxEA2&b*%d^@o33P_JsX`KUeOgwtDLx&j=#~9va!JKY1oees>={GQ17>hnN zV;*Novp~!N*TRkA>qcDmQJs%UQ~d0B3aUjkDLNWXlIeY1Tk?JfpbL!zggtJH*jwDy zCY#9jLN>v8bU5QBs2gF}UT}f>{|*1NLN|dS%b2y$-eTN)%>2~*A&6Q$HSI9hhO=08 zqLon|`OHy|Hjp$V0>GGwmpJa}`=#zDuGiEV_>yx@ITQLr^qnlZ%vli-iIeO2 zhp!JJ(}cXmf?Eb0@Ixra%A6QY4X615G&|HI*^WZs)vZOr(+ulz-Rj|%mP^B|0LK`S zc3q_kh~bH(2HIIWb}2u@4)}c@9lh^3?N-v zTCDZJQp9I^9SGO9e5L%1c%?uRt|;g?j`SS|p>`I%M2{#mI^0v`{}!TASmDnoU~7H1 z+40=PLPVo(l)72{UG{@v$cTuPJi*D|B0Nfy^=eGfo<5$|4BV-(ka6>xH?mB6US1f? z=dyZX{>K!KD zweRr`%3*POC1V6nTySMVfO;ZHygBIC=5nHWAw!$TK0P-U7CvX7fdp?LOW1D*90dMW zK|h6z(Z6~TI0ryt-r7A9Jei|SR_=ZF&+J$^L6^;pEfIoiT&r&#H(l`bgB#2-G0wZ! zey}+ns^$gn7LvQGO(5qBEl)>*DyA3Xwsmpl*#N7y2Z)323D&9bbOQI#KW_bc3-)UC zFf%%(GFa-X1d%q?mjdzl4*SLD(Ep&40Wb_x;vNvf9_crhaAn8jJIR)v&X!@P@W>8v z4;<k}C|FZUnz{_p1Uoc(_!^T~bvBG01~3F*!FckY zSRKLP#16q27R}jUg+6C^W2e=m7{Clet{!S~xPzi06?73i`|LA#>zm$)KmH&5CwRlV zKM38=cam8NAWLy;J?Lx_jmsq{_gkxfJGXNf!DtAEqL{pya{i9+qlY2H=DOX^;CS4b>S?8p~`@O(fEU zbG*LKJT0O*0oc3OIpXPN<{*;;ALBeWKXKCKrb7}@Q)mbAO530qFn5wgIZRcY_ko+f zMLL*mxyd(JBu8^B+&5SD#)3=j*6GJndz};S+i*rvPtYTMRik*!EKmBZ5xOw?V|4R{ zL9guf3q{jug#yC(jjhof@H$bJzHaNHZm==)qPVrF%F}CChoVb5l?t@@kxH(!KtaIn8hPGwwa{)Ej+tN0BCnpV`hGYkhoJ0AN4wn)zIMm7o_n zSO7~Dr*-Ak>WP#HktvYjAxrS@CBsRVqYd2{(_tIfiwUg9Wj7fwV|-fGFBan>omhMdN|<3-30 zRSYp^+jJD1ZUdPBq#LL*I`_yJ1-dAjD^lni&LhH6>~(YE;q%W!J&5{r+JLsOzA2-DC0S(=sztT4IQS_)4u02ba*y|TIAgaqfdJ^x=Z>#xR) z$|I7lnq6r@l2;p6= zu~SO=l<@ZY1eQLp{Zzh8^R_9#Pzy~TY@*PL9NGX75m+998wwqyhUSaQMh7kgJxCA` zkt>z7rx|Yh2Cvh#$FxU!3OfNo6#JfX*gw(n0CfggG!&WP^U=w7I6YjKIc=OuV+(>` z)-{a%2#v@)`*51yL>0U5i+8>8X}tc8Z^X^b13dHW#CLqh{}ccCfB1WN^za3|>LhsE zo8E$30B?BbyYYs%ybZ5?&1>+1_kRGV#?u%&8m93S&li^vzTLrU_Sa68YY7}P;*&BCXigX>4lLmb>6Jlo{N^N)M&?XW3qShKA|Pl{fgs)Fxl^ zAUm(2rEsH~uct75PZKaYPbcIuL#4?u^3Rl;)^B1h8fxfwYl4V4s~D&kcGH|MTK$bQ zSPC+g0f#+jhW9+MTb=Z2oMBrm94dwQt3t+cm`8pF!)#L{s$_DnEbMDF*yUQ4;*&nV z+!M#^h;0@Rx%YFlBCiS!)b$NVPHEF{fzRXAGy;{BvP(K#@lZIW)r>g1um+5y-ilYa z2Y^V0wl&eH*%NGvvNXEwk#iOzvd%-~eRKX(FpH-04ONV>TeQk~hU}ohW5$~lC^aXC z4s4q((!LMq7|@}(=_j1F8x~+dmxi&QF^3z~nm~tQYYk|?*cmT@z{itkR5xPkmWBqv zJuL2MQX0ToMC<2jU%8@QOWqT*XpwbAhEuk3?tLHgZPOJ~h#G#YC;RySyn+>E!Niw6 zh0>2Nd8mvpG#qPlqDEy`W3TwXGE6tK(dz>kgym5*4I}cZPh9uKF)2OYd~p z9scGiN!N6<%ikUI5&!rfju<>sgN~>&77>hnVBaThAKkiMsfC_QKdsML+Z0+e)<@vR zLbX~ghG=IxMA<>uX~%RtFq@llGY-~qLkxujzmMs4XM!r_n*Ve#H7n(KfPnb>DL2!R z7{fK)4b3nWm#$#SBSsB*5PHA8d5n22qe#l{^``=M4E5E$TON)F&*xq4-#aB^=3cHu zL(5u#bIoRiM^0Hkn<`W!3K9w!4(0i)Vw2h>J;_7>Y@b6GSJ2>!14ZUkE(C1x*e7K; zF&PLhH?t!aj-rMO*-$fli&lMK(E$9=w3yb)%0_V3GDF3Hd{-GN2OKqPj)YJNwgNv%3T-xeRYlkoEvSBUWF%1 z%;|ZsR&OBofLdxD!LzFK4RzX?6WS2Mt8QiP3ln<&S+l-GgoRbo%n()0aA5PuHk>CF z!w$s&3m@uq!>0~Fj3qQ?r#JItG!I#yYDY!4*6{H55uSPec|7sd)7Uog6VBW_I|oE} zG;L69Xx+S|^zg91;;9Y;@LXxLU^k*Vn`{%jMFFTDTAw7|ib{L>2~2~JPO+<>5hG) zIpsz}0X~W%c)gsw4bfp^Lm-w)m1kpP$8>U_6A zqDDc7M1g7nPfoEf73+KUhB#ft#Cnl|hll~IWtQ@T;^{c+0$Yn3*#jGHN@~s_!AawO zH88?xIlz@Cr3^HmEnljhmT2@U;dOGxv#ve42m4QeU6z~?J55}{U=#$RScmgV4H{_O z4X)a;Gd(S!gYys0q4UXCJoPkw>C>OWU;A&q6QBC{&tpQcKLTzZeGYGW!#nZDx4s7- zdjH$-+kVII0sx$E&-kUE|7qOYI{wAK_z1q?8@>Ve@7>r*NYm3GJzZ-e*EbG%a@;Vn zS(PYm0o4fGCRm1ury{$PNy#I;&YCY|Mzp}@TVsmKzL)qgu)%c0dsE1ipoQkjY()MA zK=QrV+@uiwDbX(RI{U?Heyz0HAZ9cUg{XK5{Vm*93UkVEUk$%^Nwwot@SU(!K#g~5 zLB-j>+Qby@-}Ryl<~su23xfW+gtO&|Xe_D=$8N-pbP$x-f@SQBqfFM5J?gwJJXa}$`u3hjzuyXiR39nU=b9A0?-A)bEn#v*>j>R_GK z?vptoV_=hxF5Pq>-@~%djkzu(P1kE;iSyyQ783NmuB(vel70ib=l7g5``eGVr!FQX za>Vz+EPaF&I+OZ#UHVe>C;OryV@K`&x~Fo#rXF}08HT#imr;G1Y4e<$IuDgzj`;1G z1L)d!*YqL4WtAA;G^6Y_{I+5l_UCc>+T*S&chU$@d5pb!8S|mt>Ba()hTa8mW7g(r zdsBw<1wxb$IwTel6Q?v$F2WIda+Ksp(gY2DeEf8tTIPJ=J(#AXDBO=F(DYE&=5hFa zxwgB$I9vii_Bk;2J;K?J%1I1|E)Fh}gOQ;V(p>!$V3!SJSjqwoH&fM8Ttqz-NwGCW zQ>#R-FrsfAcEnr=o$#}|x0w~7eKY$)sJPPF9emmV0El~eJU345n5_W4NaC~az#Gv8H0g@A)!o$M7Y^k*m_YOHTieyUM$vBRm-E~z3@)~2g)|M%*Aub)9 z`5E_)#0%*!z$FN1nUm|+HG{{CT`$)67i;&W^efsgBecJKW4a~3NyGF4n9#YV_lPqc ziZ6lJ5U42+9xg|esql!cRPMves_7wD06M*B3LH2zHRFe>3kk8*9Zw(1SL6|(G8JuW z*iJoxk&m-CL2EaFY}n_py*LzG10LMJkDGo*7sXBAO0h(RU`O{8q2`$Akqcx>iV1k1 z6`{chJ`M^CU%yt3t3dtDK*YRQ>t$`VA&PQqf>SeA(U|K7A}#B!3=4(}3kQl;h1(QM z4?}GP7A-v`1WX#8FHr!#zgf%eSev9?DFoc0Y?EW7FUo#+Qplo}!ld$$BXo=5Ia`+i zp_Tvf`E{O)Rgm*Vej38Bz9gDlUVPz^a-d)DwPCW*Z}kG&-1b=jC+AlH@pMxyb3dSQ zjIMHCd=4An^$ce*u4wKRpAYFPjm6yfM{`cE(U4wt>dG^^d7RH8)hM?Z(dnSbXvp_J zWq^BzZCY4hnw~O+wv>4yu;U^%Pxt|O3%J%&Xf~($LZNTOM-J%RPytV|h<07>h)qna zd(5kvF~g2I=5h5_Q>(RxU;X^Ec;?yX0THxQ$M5?Ce*|xR+uIg40V!YnEDtVbKhR1r;0!(ZHm6Mg`g$ zNeZzaRA3Gq1Rn{IH#MR=j$StlqR359_hrhgLJNNHjFdE4=S>j2GHDZFVgFe3zV0Qy zlQN0)ug+UUQ~9#}!FQ!0ZlQgj0|I|4$e}Fwkh#Cu_n}5Z9J07nVyYli%@!DYBHyZ6H%{0MOyA1?lmz79(Aao$)JgfT&siJBf5~zCUc0>c1r$^{e*%XfUdu^WD|>LKG)!!?}-_g5-9iv=*oC zHsUCujOHvGE~PU;#G}}m-ZV!TLl}C-6aHEwzsNpNx2Jo>qN7Iiml3*jWM z_QK?}k>+p*p+Ygjfy246arB@u+bhj+i#w7buBQx$Gz^*PbUVy{&Z8TLT;FJ|LC3`H zh;UOm{JVAu0KH~_IX)Z>*pb`23#YZ+I8w@U9M{hB_}usub4pm_Ebokkf#^B3IwQR` z_rxF=mjoE1!d1Lxfae@LpmKu?*{q`<6E!4ewMA~u1m{2ztQ1w#RaGOR$T6IzR1P(* z)1r2$emL8RSUwv&H=G;LoZ%rtKPh$8p9!WlHOko>t-W_yfA2CzycFX^FlKQ^6nWzz zS7I)$rcDY4it@XR8+Tcmm$%>1w->V@AFafX8|&kk@#XV2CmE~i!q46c%hmU>yHaAg z@g64Zc+zX6dv3CU2P;f#MlA&lEQ1Oo`@u1&lW{7%Tokk7n$d+y-*6z?;B?BkVNnPg zrVKw#rTKUUfSIA*o~l3tXm=rg@7_H;xVh(sr0z^npkl^SX@a2x`yANMXPnfO2nCh2 zfXe7mWq%sbl`_R1<{x3_5tNbXfl)uf|8tez#XL_Q@#~LpFA5e3`H7S{Jp@jg zQPJK{BX$;HJiI36qs%!Fbj$mvz-uMsb2#t_Q;5n}LLhJ)|Mfjxl?#VPW~%UgGh*EQ z)Z_q7pBmboBoNaKnF)RI5H@Fl;W(6L5f=W*CY$*eP4+TxrGX;sOeX6zwG zy;$EojZg|u01I$Z7Uw4PWxcx8g1D{s4a4hkp-l1bF`88QN{%um*_312e>I0A`CQp^g@( zONdyG$Lu7_7N@|WtaMd~41UaznWo$rzqN5XCbC5IH0CnSRwgWZHVtah;CcG6bTd@a z5u{&CLe`K;*0fyOm+!6QgKIq*4H^XZ15Dm_wS8SALR>8$bp?WH@3wWOFoMG#Wq?uhHLS{wSwWH~ zGum$Tq_6ynGqOQ&+D=RNXl%Afq7@IRs-FlbsM9JUk;*fJk7-603H;Cs!4dzkE)A9N zNJU0c^>Eth)1#IEKxgpBr9HJd+@XGw4Um56RXMf#H})nW5cb7Q-rnNgm7b^sXW?u` z^2nTbqt6YdYYe3UdZ4-CBlIdCoYAlu;`+8>?zd=AoVI%yQ*o~=wyk519gm)S4yWye z`%gcK7oPn*9zJ@6-cFbvF=O>40G^OEn6eWJ-UPIR$|ls6*5)bTt*rGTQ^bwKo6Gna za)@%%7q+Z9<@d_NWA=D*-pu=&v`~ndgv7lMuLJ34YnDAJkT#&&Fx-IM;L5wC3_+27 z0!B$8B0HzJ=n4EV7t9+G+E{hJqY>q9EWiGJ^=fiRDfBf82#|QnoSsrC6g7w8F1&Kh z`BI-rM+cVY17k47UyWvXj|fV18^;4Q^;3q&js`n6-NvI(vGgH9Gx^a*gyOpm!*+4A z(_ZOdQ}0iMJ5&qcHN5S+U&K=at+6n6^ZWG81{N3RgUXa5z&@j`qnS*vY4* z0Z%&xZ4q!i1PpQjD2e2-=xJ~{$g^0JhVy>L`F8T->kE42IvQZ^dawb&{re|r8@+HE zK!f2!0HZlp;z|(MtypIRR4md()WcUj#I|l6o^)nPqD@(koNNc1p4gu}$KP+hXj6iH zRkbX--Q$z$%qBplPa!v!@uyY_?Y117zta>M2VTr7p3SX)!xbzCP0nQKA?wnBo+m6o z$dJfX=|Pk1W0~W-et*1i#qO&{nj;wqt&vQx(deEtAjEtLT>299g;C%- zq!YSfwbW|h45jcl0T8Elr~&aXF!*~kn~(Xm2ss3eR>k?6(rol#JZkyg`Qa!x3J?a& zG^j-HVFtbgAT%o2?-W#gXQAVuF9nf97GSG0l=Kuv8jUw&dSpW7K_Lg?O$g9`q|-Fo zS3bb^*#mxd1w?@125(MtpOWKPMwjmrA>TNF6mOq+&edQ8mt^?_5?lwaKgUq!qybi2 zX?lulRm#{O|L>6xE$MB@XDhF%SACulhw8=yc3zf_F4OF@BrHjV8f_8A@0k$<&|DiT zgm$l9fKdZ&f;nv8*{M2epw8!cs?YUQ4Dxj?;}l6x;&Oa%$|opry7vJ3!WVG+#b3q! zn-d<6fxrCM{}w*|i$8@ozwR}7<*QzeSHJaLcmcp0-|}|6;?=Lk6Hh+CE1tXuoo558 zj2`1c`+Hk+53&qTgLK{pUHaKw?NlmChxMiLCBXRPJ?Yt=YK9G`{Ny?g5SP90Ou>v% z3PZpRStBM5)nNo!P6N=SkLdS7ISGK8v=vbXy2?R%5#9ZDkckpNoUG2XjcKA!`Y z+DZkP0DU&2J@ZbsQ8G2XkdTo}F(tjwD`IuxxBd?zC*j9yJiXl;Y) z1bE6&U@8H0c)GSYj-MD_EVaRFu82F1nR>u~0-X096N>HBEscB*XzMsVcnUfd&p&*K z7jADc)SPSE$&MoC=~QFhV)uTT6H6n?URug`+t0=?3?CSMceryHhL`B5hSb8!b#Z;Z z7F|cGsE<7xM%gQd2(CX%B&Z8N1!V!9r-t_|8+J9P3W2b|1rPx8AhA_-j^nft8!#~R zfe>PdDz=Z})SoNSBlUVII>C|lrj`itz!6N(th_e42FDkevC8){|OYbjc<`3-AQ;aH3dll;O5><+fLn-&8Qr{hUQ%BD5F}EAv(Ak z0rOy9n+Y8wm=rzKT{_Y&k{~XK6?Xh36<-WtI7@sV{M~<{!Fag0uKT-UA)$yf#v9i! z#06*o3I-EO<#d1|QJjvzV~zUoP(4pwu-{XdD#NU}$0gVMXmF^T_1bkU5{CC*vka@b z^<#4i|0fJ*E&q|ahKKck5r%iEY_QG6ZhQ5)T+7F7kZ4yf-gT`X$2KkvS-x&@bgQ4F zQvf+&v^A#Ue2QpB{E?UT|m%-7!qbV+j2ig5EaogAHcicg(5S_kr8Fn?P?3y?31FBM1b&pRo0Y zeGJ^V*Tjwp$OnF-kVBaS3Lk4T4C( z6g{C=J*m*fOt4F((*O4L9B@y$E29$t(>*HCVMza7%EggqSI=*q!}wgyI5{<3wU_hC zA-9P*NiArF@6!kYV@ey8V5k=v=xIPS+#TdkgLp)p-QIhPdJLR5NcV`j0f>YVruB|- zCScC4XU;aFmcc7t`AYoM&;K0$v;Xw}h|{BoP_Z=T*Sz8>y#M!n3*P&Vx8oh}dON=C zH~&ry0M9-1>-g6{|5G^0E&lmOK7?=imT$r7reickjr6t?vq=@i@CVyTKVAy2-O5YLrx%BSvSM(7f zLo6C6BhB)O0^lL#7~_>;a0knz3gE^H;vdc-iV+Bo(L7R9%KVX@Bx+e8Fx2kv?7WZs zjHwavd>SEg5fH}JdKn8%;|hND6H8ogGY&DVtCBkn=2gDIen2iCLC zj(w|!nM*l-xgOWXMmccNb5Bbv&r%Dm~Gyr2J-|8H%r4!8|R_CtBUQUzAc;5p) z8YmWev~lD-(;>9rU%39=8@3E{uE4HHrGqfWOET z0yI|^$4u|s5~(2>O`@}zJ}vIGMt84><)9luq+!PD;&6R(m15<{Af|e|$uKUMYWe-T za%4d5TI5S8jBzAE3&bKmF^nc+^cO>jhr*-PFWJ&v3fOTR?Z6BlYgB=SxU*>OHKSC0 z$4jw(=R8~rtiui72ZI^{gaHr%By3;IN#XC_HA&DL$7=EjHEB*WX1#WN>ghyeF7$%Y zGwQ-Nh^G6oio-4SaHS{bO`wpjkYONSH7)(EZ=i2VboddBaF307ksHLz@mE0%SWA|e~=+LUw)fV{vz@Q`Unx7dCy zGvN9*-}&^F&xKx&EV!&Xp){y3OX&A% zDvI^QHV!@&zFCeIr$XTh!(r_7nDh7GGfq#cR>y2ppQXxsy`4#(=I!fzyrG=?+I~;j z6W?{x1E9lmqL+11Fm)x>DaoRY1RE}Y5Q=wtvDO)A5V%7`59{v>$TUj(F8jmIH$SmM z-K%9$cNP(9DGv!?2y3ehCLM3+c=Oxdg%>{eX^bIgQ}BnrpZOx5e)TKy;OQqV1;CX7OQUgmnGIAXBzTJnvNVEo4#kXs z$h~khxR~)t!nm8@-KZ(CjmEyKhU8WDnWx{TlnPR|cT~2``0d1}fK*BQfGBK18um&quf&-G(P3@&;|#O8u9s>-!|gbuNyCGC4>0E~L^q5vAuI?y2U=@Zb7tQxO+y0$ z6n3e>i=dQasTe{5bWay_XOnF$uAq0f>~q`BY}fBn{>c4&jPT$UwO~A&XLqdTz`@e2 zU_3ZJo$`cM#e6aA20DVSLjHD`1rE_@tCH8>BWp1i!sV+fw~Wu zA+TGv;z((wSL%n=k$V>|I^rv%UtPW{olL^H(oPuQ;(3675{r%~BF|`#J#Z!<$OEcu z)Z+Um99&#Wj!T?3yJJCJZ)~hPH{D>V)6-o|#$h_P5KEL#*yeO1BWNbx9W*X>imS`* z$k6aLQZFCUJh$nnBN`6yz6)&>%WN(*Q6sD(2tb_|XhU!F1QPd4yk3q020#AKf9#)q zD+5)*kT%U%=}0Lx!nbc7Eirs^Ft;kZBC%KN#TXI z#6kh(+PcO26Cga^qs-ecg&Ef*80*F7B+OpE1GoVP2z2b(u%If#Q)hJdNQ0FD-et*s zNyd^w-pzM_5*Qpe*rDBP@4m}0bY#}?y8zn7H?Dogia#8@0>wM2Wero-cUY*gfj}dM z=^($bkYWs@p;L07EK0K%uhfab`XJ3BacH2ikUcLPJS=@eSeVwOOOuY)ficdQ`;P72 z3H$DI-4M)`VX52ciY+JY}(FGHNg(k>UK1wxKJ@bBPg8i@scsMYPQW|@~Wut|;f(pLz1NHiE8pcso%H@`+CB9F- z`1vp3{qOox{LlVZ{|Ef_zxrRGHAUYVe);1+hadjF@5U$o^{4O?ANf&y^SAv8Yye*M z>My|uzu^zytAG3N#8-Uqt8jjFi}Rzir(n9gp9A6rxh>L#uXIJ-H~p#6P$>~&tyRv+ zDtpM5$u}$_mcZEl*5JjY4**Ruz0hZr_M))xb*v#Cxh@W*6{(C$1Jj3fNMJ00`%!Ey zoAY4p(Z0BsQHzp6Q=IKNr^V8paI|qOnh^)2ed5F7vro<=Aqh-I$c!-Y<#=iZP7sV{ z=}TSB3#yu@o*nxG0JBLAptkQg4xi$ol*_RNjkNhJ1!OF9!4l*r)9q>;k7~$+=uH$o zMMPXB-B@&;89}T=+F;mH^F5cw@N&iSrjpS}lXJPnR)&S`mG3A`agD{DN6s&b4a2!J zQelNSE~bNlygZfA$@wGS_D-3Hob71xUQtB!UK^kz)rMC+_{lfLJ2iZJ(J(D_TeB=*y_FRw?(q`@1OF6%;mmadbdO9L7@ zS`+Nbgj+_#O3EoTz|(l**}#Sp`UwrBv6n*zTHB-}b5^m2t3drULPnTU=d^YN;=vRw z<6!mxVhl+`eVq@ry0Ov?SBvB5;KI%~wE3YKoEQOgP})iCvunC#Vy%wbec;~B32mMq zy^JLWaIp8h*!5@r%zyCX`+ob^TW1Nb9>4;ngGYtHissU~n_IDP3Yp$1hFU;z3OzC(nwCsQ_qBW3)mzdE|S`pvy zUMbHVnch`~srmDwU;O7U2u1b|Bg(b;x$EA$zFWrR8(pJvn~ggP=}NwrUd-0~NOkyH zOET64z={Ggz=1b3i=@(qv`impS+dW>)e-FxbySM566?bZkB>_~P5K)9jt;@i>4a_T z&@s`v6^d&eKm+zUF$ZvSa}PH+CyevNdAEoFX^Otxp!E~xZUFzL1K4-V^E{lO#{Fsn zyrwgjHFsr>dpaUVIHwMDMaY*7FbuJ5#Od=AIqIcYS~b)SuuhI$(+hT2;aO>PYe3Z{#Clx zaE~mH#V-khukfexw-)|E&&Kk-UOZA}A@{#HAP(aU*WK5grym6!`8(+io}vL5vzu0% zf~O=k-#=?qgT`;iv1*;$g_qCe9l0LauUwAx^IBhxFJT(1P-=xTEBQG`<`L?*ONus)AMvE7_- zyWip`KJo+j^e_GlZcZJ~oOk?2)m- zglF_58S%g=sZl|IMATF(pCbBLXSV(%#PGuoAVvA7GTGdC;Co**lsoclOOrgK=>VEw z-$(iGgnKtP;RM|GVTKrK=E#|9=||qB-h0%4YE0Dx`Lwuf$uU|_HTV#aJbE$yIn4xO z&}K?!%yr>EYy-Kk>daktfadQLz&?RF{TPXYKeD~#tLd~Xa-RIwW$~n_!R5cS`mOz_ zS5>j@bo!KW-{kF&{b9oLgY+syE2fg{iXy>&2@P$Ws z@)fW6*}wFczVmOrge6(7mlXj6KtJ&Q_xztf_OYM;CIy}_g7+_gWkpoI(3CWzPhY$S z%Wb69be{wCT&_ij;MKxM3>IaBHAfL1>*58(vdC`SV}E$mhn-Xtdn`a0VgoJVW?o+- zJ;^r2iClPkog@g^BO5&YiDAn&KaN~I1e~5p0)Pl*UHKLWYdF0}Vz^kO^8JN}mE#B{ zORhYdiy2BywHkx@gAVj~ucM=YhNbC=3^}~>iav6V4qIG`0674(YfS7o#s$pjYW2Qr zJ{KDC_cHaWMmaF0ro4gt0E}LVh#@5KAfSwQ34qD5@mamF+8@``0P8vjO?6hKSFX%!#J%P3*nU&WOGW(nBs9 z1cq0<>ecw?AN?r4)(ZUf7zEo0DSys ze-a=0M}He{c=J2(6F>3ec;EZpkJIUdeLn-u>%n=2|0z^f9Lr1%KInhCGk8MLux9m_ zc|(y`zU~GRJ(_fqsy%{tk>?KfYt&?{LAeTR;M#Fh+Y=1pH)F8TykQh;FFpR&mY2Pj zQ9QOTdk=JB^d(Mq;&h!dUey$I_9ZD@!F1W#fO({S5O+xch_J_@Dm)tai5PA6rJ*G{ z5#kYy8oe-a>`??f9gfjSyp@U}*K!6PabB!6{s>e}MP{80MhO{wslqB(2uNJQj4{q% z@KzA*l|r`8gf5($QrcHyX8V&EnQyMkxzHbZ1(D`i)%fdBl(sPPf=kKt_{~LWqH)K@;-=aoPZZS&}q0~x`Wd@ zCC>AD7xbwX96VEIcdz5J?*n5h#u$*v^HlsKOc~h6ZqBS`aq+#kNO!9G1l0I++0AUCJmqXp8g^N+lcA;Mo9iYRZIqMKOBCf{P~Upmnd0X{jT9+dRb`kY>Dc zp)EPQvtT6;Y!3RLYgZD}Ep$#oE3{i^lAj0WE%ja=XOM|kUeSM!-&Lc`@*U}J0Y&S8 zs@z<-yX26r>1dSkN7;Pgoo`9Jo@zU|-t;UE5?&*5cW#|W6aC^fdgyWjop8$(S; zg}&U017R!*i-MjtRe}!7--~zOr(N#h%PY$hE+gj2J(!Fy`2nP!;Ok!=s@R={q`0QZ_j{qG`&TeJ8sYCpxuF6 z&|{;j5q{3p6Z$0w_r4|=yqNF9-meQ|p~P3d)B1PW`{f=2Zh&6P%S#VyL=QdG4`s@6c)P5HM=nMao;map{^Hv?*>@XT-#u#Rm^i; zb3UX-VJM9z3D}#gfP{q*2x9E=x#;$DL&M3H4s(hLw${yn>%7clx}{&sxfbDzaO`rF@$zx_A zeCpE}XT=-d_IAAaEpJEfo7bwcaVqD8#uxur*FoSiIaC)wrh~ztgGww2KBo={M2#-e zsO4#47!XmH=?7kPW$PsL&HMQJG*Bw$Ft4H%GZDw6&R-XWjd3cW8G1o@4?)l8u|DsA zPsz^#8a0&E5DJ%yDU$^evGehX@q7PsXh2}{otSF(mZQ8kW=2>3x8lIOH@1j>B76@} z46^AoQy>mVFhh$sC>GhUwdN5a#4b6;3tUz(?uCL#Z0Qcm8s%|~qh^F_u4|lvRb|Np zSdhDpS|FH>b;)e|Q})IY(UnKeiKY{?F}l=dw>h5`o1mDd32U407#{sA5MWN%2eLK} z_YZkw?ni;v$ULv3664O1#yr=k4_{#awUtxf-0|r=RD`A=!I)?0xW%?@FzoXQcYHc@QTBO+2!6912x(Z=uA22W@Syp z_m%?KdLKXcb3gYmChw&ZC$6U;>6L%6im!X!>oyS)Q7vK3f-nv&8;2I>VbiM(yDttP z5%P;T5d}egmTx>mF`TmqVUUItI!7{BX{Z&_YoNTz`(V*f&AI|`CJA9UwLn1SG{-`7 zKdtd0{Nj!(82ml#*Yp!0$kbf81-Pypr)9sb9;I0!mzll7SgsjQt}z;Z{y4+KwauO1 zc^l<;vQA-J{wV~z&slech@f#v_3--{itN_7i*eZ0$kj0eCckQ0&x;vWt{Sc6@}47)EHnsQ(lkBh zaa!BOz3l1b04AgG{M~h&oHOH-DIigPV_gA8$yhp2a_DyGYYIlAyY&2)Jy^PU^w?eU zuyv#IeN;#pO&F(pD9{GI|2EprIXlj?gF*%3800#7g}P! zI?u{!y#$Cfnp9)RB9lrOw}EN*5MnwQ(U4he-)>&}EQPPpD1*9&ZfIa?$ee&!ZVQG^ ztA8+u6@X{ThVCn1l%D{TkWJgji0TN z|COjW%=LM1!_rcuU>8AzKUUe}AUTXXY&Z7c%AfL4B{?Nl6W@cL2D2%`gFmUe!G^@L9u;GYk#zbSriK5kzmDL3jC6BTQ)Usyx2J3d-??PYNuuq8%}u6M8Y& zG+&>t896A^{%HwsZt6x-@36LYEeX(j3`?Wt9f}M#Q=uDz3XQQ5(mCa+9pK?c1~+5V1vWa@bDUfrm)D zS7Y}wB5C2}po65!REm+5;n4^2$S(&v(=5+`mRg4}S{4zUJ(>`)mn)wV#8MV4*Q&!n zh}?kYUbCYi&Ujnu8+rj)bOm9>Rqj9pOb)scg8FK2&9je2JfJA)KH4DKoR4`O7Qd0f1qv&+I~fKJ zL339|o_dBj0oV*I5;$bq^aWF|j!&xxW0CnS_}%n2x|Iuogl?L$4ly47P?&DTLnkc~ zt@+xS&Ekl+=p><`+U|jT-~%7HUzXx%ddOEEu4@LEyDn=$IN?Xnsd)oxM*)Oo8H8{m zXkC^%% zv8Ea;qCd5M2iFxI6itxf-`emmqj{)AH3IzF-}278#&~cjUX10qk(SLh3uAD+R0Z2q z4XmmnmNQwD5Qh<9J^&3a5mg?_Q8IOUSILbQ(hPBGWunwOw#XP2S0I^tI)CcojhQSs>|I|^<;sYmm~tQ$RI*GmYkzErNA9SG zZ7qa%ZT>E)%C{3dA^~G7ESYpqtezCG(lGaZ1>Q$le7T>GvcQ!Wp+`3(9P-%|hyd$W z+2qjhM;c82z(@?At}FZpV85N`*19I-xvkbcLbT1jGbht5*8Myj%mwWTB7Zp)&N68^ z{jQX8Pf=)N1pv_>UHfv)P|Oh?*Dv~PU&P;WDgfpM@VjPM58g%kmE!$zkleb-U!4-i z8FOyzu5Lg@h8r~3wYR; z)ZM#(!WWT#uHE7$FKat&){Ew@)H1$p7pn+;mI5!6OJc}7H>e$j4+Gb)eNOiRX%2oqp<#0 zB?SbWoZm!*RTfo+XKB0DXa@xE59OO}Fb^NPLVvk z>r(r~i%Qsn-|BoTqf+eOVB6*U;Lry)zhE+j*Y>F$;SY{KW78}ByKW!KVSUzKn~pH{ zgEDz4oqP)DWepkPfuXG?K_O0dYR&4M*fCAesW_hpRDo09F!mk$en#&bFek?C*`EU- ziVc8hGrzmHsbPS#D39*>t31>|x*LOp(M3itgEF?v4}V{rQb1rV!&$_kSa=w3mcULR z5hf4f{FZ$oK;Uu-Oo{-^G~NWAEW8P^ILCej@^0#pI%bR_uN0}~|78i6D|H0Y5bNyp zg(mO1QWn>JRrX_Pq8fbp%))+Kth^Fx{w$n!W;!l*_uFM?SvN1~XP9JC?hNf7hQE*LHZI zUiIO+)ik~=f{*@$n9?9f6LrTNXK`oE_2PaboJw*Ln*247ogHp$^>mszegNcF3QwltBOb2s1pGJppo2hjfo4L z&XqJuMb5=16EOwmkrr4)Ezg`BPXE|pti*u0DH#KpmNqbC;(R_s#Ew)n<#WfFXKb!G zHb=uvI`(mE9*TJj%rnM5aML@sZl0Mr6+>Yl1l;59lMEo3xyXPmj6c#$I>P5&7v;kKh)dJ^o83Q~XLPp^yuCV~H0)D2X zt(`B^jIjq<)R#J6NPZcoY#iu)M#wDZD|xNW0qHb>PQKCG*5tdD*DzAzxJUi;XynNO zm1fgO!!a1o$vtxkPWbuyp1^mSW2^HOfZpenV~K~I#_IOt+7JK3ZQ>Wa!k-SF9`8MX zrRew_yjdWJHOqco18P)40?Za-j>wF{UU{M&14LCXBc@10QNaG#9UzWyoMjyY^azcR zJpIe~0A+BVFbHQ+YKGW3G4_2~3-NVIXN>iyiQ@i)C-BRk{T%-0fBlznIzI;mu!-U= zU-5N#$D3YXv zL8ZVd$GO3-A?Q^IDR}By%rAM8gfWta>@Z`BjWf8KSKNKPOf@;*{I`B*E2_xI;@su7_jyElombv4 ziNBZqv0}u2MNWin5Z^FTykMhx>AF9RpAeJDpgxeY)AlzO)(8 zbW}#&n(1|Cmikax=SR!jU(7R~Smy2^S77+39_PInETd5#7EWVe+-!TL&}3Z?{!AQK z*7ypU3S|LtS-}9;$XbzD=1OZIW->l@S<`MA?;XaCixz1JA%>_6?ZbWD$)N#D^gt zxe~0b#q}kouXF8V6>FQ3zhUqevl~oU6n?ca=$w|iM& z7`v|&B(6_LS?j|*FZy4@ob(DJI&p_YSYCf;1dLw!8S>1CuB{WJcC6a;qPpzl9Ci$4 zYl7Z7;2?tDc2$@+th_B2!{5`IE%-h(&YrgrNE3nt)9N7#Bq{;ocq?m5ktrgpz!&LW z!Q8w^maFZX79RHKeKUsL;$cAx<4e-p1(5oQVwv0U zUN~cUF$2o+?;5uMIL5^HFTfvI;Y{7!9yUn}Bu@q4w>Bl7^I!Uvj7`b0mi{E5x_;HR z?lQO>nQd$&f^J1P`s_GKAG-q%6~)GN4W=s?b9(5UqfQqX#Gp*k;P|?lkPmQ2pissm z9^GZp9K|5xN^l+bQ3#or*&=l&%-+()EZ(9GsvT2Dgz~C(v~K$*b2+&TW>w}1CRT`U zqHUWOqXM41XT^7I>^5+n1M@t9b}}#CyoKt-)=q$mr*L>2&=u(_B4z#xtKwd9%XWAJ zncgDYrz8piDt%R2Af2+?POGzJe-xU=vkH0?5!JU4rXt1@%j8E!@?;*wbE&wea?M88 zORwz-j_Clv9)rPiOeotd9!S2c&FFvky%1mslHN5S_>OuzoWpu$idepr&t1}P%CA-V zsC)a;8`S^*r2Sd2tyyv&hJCr#zt6q5#-8V)y3r$uMgzpmK`=m&1Pp>ODT|^!Q&!jv zDXh?&gAaaigzX1WVLLp89F{~|5JQ$ik`0iwU>g!ek){k#27$)JZZyznG$->O!!Nd=gJC< zcpumB!svqBlWU;#nuk8_VZ=hvL_SN)y?!L&O}b+13}jaR>1&}H&jJ$AtkFB^InB=~ zIoK_ESb!@VOK&|DQ#Y_SH^{00(!E=vV+QL4ViAN%MhNmhK+lDVnJ|xg1`z$|AWs@|Iz$FqXsk0%Hjo&u`Zd!xtZue~`Y^RRCb)3&<+}yuGzjq%GAD{8~e2Yhq zZ$sZ`(;Ue|C%P!+J}kYVb?Z1L;(V1(R7aUCZW}m5&;@87ElB!`xgD@W>^WACJ#h{V z&!O~rDO`@%bn>v~ekPyt-+i3OrT5)R+1(|T_q#~H zp0{e$X zIyOG?EdiY=8Y#j2bWkQ+@PiKVw(LjT>^vF(rpDRMkOcU@6T+77{s!9@fPon#lIa}~ zjR1&+*=G$v^Hiyp>DGx{@;N5njjo!m5n^nUZR-WVvkT^&eqHWGjl#afMb10;_o?R> zUU;4hStR4~^VQGGbwN@b9@tu+Nyrp39H51ix1zjQE*Q+nB6$!+Ixu&r6Xqts?Oqry zfUP%R4h!LHwoqX-@Q8%w!h+ozY@K-BIu$FL>)*@F(A7FcR}xsChAJR8U`)-M1n-OWg{E1daFOtHMr);gdQbDS}C;-+or-I!V5PLSTPpLd+k zXLRdn1+#`P_^_-&-BWd>v z+*RgNn=;UX7p|Z;y96X1(Yr=tcJg(O&*oB(lF>CMTh=u|Ndc4b{#5>^dytzXxaZm; z%x*qrAZq3`sDoLn*>5wXVVh^h*{{B35CpiYw3c{O54_3O#`$Z|v?e$YO90flqZvV$A*rpD02#3*Ol?dLd+E}{ z5Jr*Db^feVHZY;X&Qfc`(|$~uo(kwNNAW(KrdxNN!Jc=z*#K!6b7Fsdi*dfigVToR zU%bVm+sC-M_W)2+I=61#`vGz1i+JrES0Gyt!-5E%p)RjnzBfs{V0@8|ycM^`hcZU< zciY7yzgIucCGsDcnh{2f7Sw6`<1#S}Eq-r{G)>!=0AR;)1)Z8aamrZgL6R0k#A>Pd zf#?t`;tUDpZ1B8^M>4zL#?nWvL*daNcTJhjVP5i*_*~INQ07$cb(RASoR$=yXvPfE zY*#-UENU>`LH?2agzq@&d35&FvmNVw7;e=jD^9E^j_QVEMN-iS*r-77fZ zAg&X`T?UqyGs37UYR>2arU{T2VeZUX_#YWXC6SekDi5&S){Ka;!gn?{dI#g(*XDx+ zDD+faoAWgWb(|M}UcN8ubo3Lpsp9G55Y<47<0t&6JlppqfU~h894|=*j2o8jOMS>K*pa>`*r% zNgJ^)IxK8HB27@D*OoOikiG>;tLI#60u9M}MD`@YX5g@0fU4Du5k%dD5XyjoY>!Zg z4kAHoc9n=&=bkH!z&t3IQEvQnSoax*bsqTz=f4Q*R>_>F)j%sZqC&H{uSj?+^}$Wc zLTkrQ^-xW7t%PAL*NPzf3gB^`9za~OyT$~dyOyJx;G0KtqMVf(E*#*IzitEBxh}hd8}(SrESRD=kF`<39(lVEFJXe-n+GPg4Xk z-2>#I4C41H82R9akJeU(%5X6B3mLBt+Q$RZRF zt~b}#4aA<>`@~aE;&Wg6ef-ql`Va8s&wm;>r+fI)S03ZnKK8SC?X%C|tAw>3sX)&nSfK2j-iFB(iSoKzHo_ed$s z?DBd^gWyV)Rda>4rMZ^EG%?6QX`ZZ~BgIRtR-l{_k!D=;dMy^B8Ec3u?@!NS?x#gT z0fClWAs$iiN>k|OEUV7#&q?`IzVbSgy8&eU4(Pw$a67{D*pA+53-X)DMOA&J68X3V%z?*My z&8?nE=lDqn!KYmLHjZd~1uMOA4+K0j_6((|cnY>g9&R{dheA#_Xx)r~<2*6vj%^bR z9l-t~bUwyYPd|ZYpLxppnd?73-{yuV?L5QcdAgqV)zXk}4v7{H1B=#?Anc}f|BACg zHJU+C#st1Fx_}?`IaQU%kI_)~{VI3*k zJ7QNYY{=eDgO)jL(@0_2>5}sR=ZZL#myX%hczAJ6e601FyNor|TP47u(U`GPiGKlj z!wX+aVa|X;HRa@SEQ|m-I-29VkZ)wZh<_&|JVl`M>>UU*a6uaTQz}f)HA9ek^gmZZ zU9UAbYD6CM=G#?_@QbroJ<3Hf0mmoJX^!>DrWgIT<8OL5YraG4%#Fw)4@_KtmUVE33#;z-#t5`Zyo`%1sU*4XCBpHEF9&QbfWWl1o1(#!Ij=AWn z(A+ilwX8fA{aIDnxD#_olYv0!k(y^$hLdoz{`G^8D$$(FH9iQogxEAC@+ zaFMH21hVf*1=Qwo1v7Pwxu|*_0gduJN+BT~O*SaZE8Q>wH6PrV(~9u+hFt+@!$8C1 z+lh8ky!gT+oFAXjwc-e&kE_P;DUFL1i0(YSzDp(k0(o|ni6@~f#x z9}~~ODp;@5A+CS~<+JJ>_+I;cU7vhsJ#I&%B|S~y>n~va_*oR7<=Qe%|9eLe=EXHO z=8V=6XN;s{?kyHRZUe|J5i|5;6*Iy_riGuy={%PnT1sS*h5@zCNdos!Aq}XUeHl8l zJhGOE@1tRXSjVe->Gx0$X^DE|by}`7+RfB2DL;u%SpA>~?=6uWaUFRNV1UJcZmqd7 zb^15}54;Z0!m$la1LtfFG6zJu846&9@|B$z#1n^Apb?Gb`!}tYOmAu6Q7G=;zmHFU z@ptiaKlk%^-8P}~7JXBE+duVV_{fL86;C{SgXh2YWxR0v5CY({zx4_H#;^V&?%&(+ z@n8EGKJcT*p){6GkUFerPG5Hzh_Ozo!$^naaW=c}7Vi#H%USszH0jgD-7h z$u+qaNrj7R*Lx1nV9*|U@}y_nz2aid2KKyY1~+UE3ZGY~r@vX3GK=&fjID;&L19`==@e7 znNbjFz`ivXe8~9>RwH8b;_h9Vr3xv6mML?tz=Nx_CNm2fy-KOg553s>4#YrTeg;8OfrmIa8^?z^6U_}j&b zO-;vUhz~i;a>A^GfXH+?kx}5(ZyD{U3d0G3KX~5Y_{FE7W>{ht1B1QO`0aFKt}&P{ zuTgVKb$Ya<$!ifGD&;i!aTraLPNZON_3o=&J3I4YSE*p@Ugyfs673P(1Ug$%|)Ddaqy+vQ^jWH-^- zv6ciV24870mS6&yYM=oKgAHZ&;yD1^s}Bdl0{S=>c~=+s7+ zg_mQn{7khpn$`+faVeGz39uIAwqH{mNT?dK6Iz>%!5D+8AqhtR>P0Rmy*y>Vj_j0R zdCuHRorfiq8=2t$KIbaCIs8L}6CW{Y=5inVU{ zM0%?;*cEu&3rDlHje@cDJYOR+vKV6~&X*ALXhrC!GUr05&=m_tn^2%YcF#Zgg5>&W zy}yhD5HQ5LzSEu|snFo^OytilJR8Tlo|DAUE03sPozEgIuM2hwsR#~_bB${#K=0sh zHZIAtBC+TI+{5Z$bvM!sNPx59fK~+#eob^ILrW!Kp@A}%*=>xQ(^>E}n2?{oc8jt2 zxDGtW(J6!Pbi`2zEU3XF9n70V8j}FnfO~|c0T>SCEOb5|9ed_L73SPn*V&*3w_ z{)^c6$9ThQp2DB~;UB{<{(t@+e)B*5Ieh$6zlHDqfgi)4`H?@4^TS7Y>X~Qp;ORHu zJ>T>}+`DOb_=2Y=4#N|GN3Qz&TcNk+wX95rTkF~0CANa;GO-R04Hyx35e{C#yMxkR zYRC~7*R#Yl?ny3A-EPDw|&>a3`|gT@l> zHTmqFa<;66ojp2qJU&Rdw#FcvWT#3xY8q%5QRg9d;~?ATi6OvN6d%r`-b3RHeDs@R zK6S8~c$dm=;$PM`@+tZ?X<)2FJMI}D(*SouraIc8Vc5M~N`4t9u`ctn55XRbGFhLK zV^Tlwm2Y3-pISUUuUT;;d@lt6YefJV@sMf$DbrI9+ILNwM?^UW;b_$MUtDH2M#4mz zrK^x+U_M3#0PkWor?qQu0%#wYI?bTaI<~&yM%8(yMZ9cVhqMjn-LTMudnasL$2bG~ zKCo>zwn@6$HfR%U+h%Fmy`i5>uaVwiQ4Z$VvMf+L=S!rA_<{A60n&h?YDwbadAxi^ z?6W)XqR8ojgI9V0JUK?28H@_OG!akBE6$ynG)d!~=_bEVw1`LQ)QZ ztkDP7g333B{3yy8^qyrcnirIxusT2BdKgVL;u0QKh#4q&R)uNW{6CK~;V@ih zH6v=FA9egfT2UtRno)2Lc%4{89W&@UcmKl2;n6xSOL%RQ+#A*lW{DyQvlKj{$PEA^ zj6{AhkpC_;fanB8_>d!A{=N25;N@$;)Q3y}*!Q!-%D}B)Jvzm#iYvpVS_!q2rho&B z$;0m(!Qxe0iKOXnWC;r#*tTsrqfHNU9({p$3W&lOEjLY|Ddb61WNdI@M{5iT*y-6K z4D7H75Wsm(w+MO{fsCFsFq*zr!g8wKv!pA`0O4y%2@9PL;9wFfcILu!RX#6gSgAk1 zw9&?tTr((;_%zP?6vdcs5eaOPL0?iyTX#2;X$Cil+du$!%v^(KpddI?0 zQ`56Sp#y>Ff_p<42R$>bF1fzkn5@63rLF?K6x z2w(AJrU{_AS@s?c&j~H#HmB}qEEF50kZK-EoJxVGb!mx;^G_HRTU*W|FPSJU!QsNM ze5kCX>6JHCfg@j6GNlIQ8iA z`8y4O{+hOoxvW3|T_A6F0_&y`Vu*UtG>!fB5d&b_m>JMStQ(R7UT8B7?zZhz8k1iz zx`kf(=>{D2ztOP3^#p+KIBYD6q07}0?pt{knO{WA6&{-2B@LcS>%de-9&~trja&g& zG8M2w+&aC0p(`hvJbjT9f|6F8-be)?YKB~X_MtE@u17s$H$dn$)#xIV?)0g~gap9t z`4(?_?HlnYzUzdPkl+I)R$1r`RoEsmM?+i2XA# z7rt{T?|toSs)y9GXAcYTEy=S{#~YvIvmw*VpHp4&@%r;}y~6 zFcmQzo|QjWlbkH?it%Xvosusd00&Hobf(Sdlm_FQC^clc@g z8N5zjC{p-lp0S}x+xE}Q^Hq4Gs-jJW^|Z1vOpmr?)U}{E(UC@U%7kNWqvO<6;Kx1X zF%OIYZbamISA$UfxZK0~oTEWMUVGqUp#x0vR9=&-#B_2xeYmD{gcC8d9%mEEg654t zd`&zeO_kSGjOfpG3}k;0=gqcIW;|~Y-E1w?o!Nmgd~f(PLDb2zxm3dypDAFyNYxhi zkw2<;9)k!hGNzR?BX%}C_1VOjSZdwAhf%c})I%Pl2(bL<;oqyDS7Lx!9ws6xp%HMg zjG;C$21h_GKp0?7+lJN}9-jwV>*(4rrxm_qq>NyC_%RtH35W;5R@88`0|*$~id@Mu zT17)s#S}7s0vK3MtpEsSoo2QW;4b-M@|;t*U#8}}8tXeaF-G!zp%Zan%-BhxQV_zt zEVyG*Qc>m?R|`2}jq<*g!Bs2iH3LHhpLa3Dqk$#`vmlEZIkRaf$^?~w;$h^0IVf{! zxwTK$ktknY0D`;ca&694@w|)o-(|##EiFSDlTl6%8kHRd6s#+dz6-a&IwNhc&{u+# zcy1tyv{-|H|FB1kH$&l!Oyl|NIKk*&1r@Dw2GrI?{QV7HtK0#=oC6a*qS#2ny$PB& z+}@tiZ*K9#{f5)chJ8%z`wn1O2Q>JehcLAlH8hMiu%R0ucLpwgjuYf&#dFh1{Y)w& zUVM++42qjmpy;Trr=bY%Y;;eUEeno8JXOP$63y+1PCB)?xG?x2Bo8QaL%>~}mxB?M z%0pq*%CXez>}KB}f7YZ|D41OcuZ2i){amkOLeQE0E$<6qc%ZvFpO|-{Px5_$;m<2@ zsD`4uJ`9c~7(DX1D6&}=%4`?vMf(tRhi1_a@qQrcPLbDYtfjAWR!_PQkR>T+gXRg8Lq@h=1f~3-Hv_&)`$P^9B6-|CfIQ`}1Fg z$_clRzJbSo@CCf?wQs^3-u6!X*njn#@PmK$FJc4m;E5;j@cFOcw?6SP{NgYCJihlk zz7t#9Fx-e@^g0btYJz30=`rGC=!S2qXEZ{>mc>DgH^4U}k#HgPU`bO0Z_{YTu;0AWTPI+F72VH1nMb1fVuLP2$2xXd3sbYeD(nrTi`OizlGvEl8ssYGeYuj9eu*(I-8cPg)0yswAP z@vO3dtCVv-LourSQx_7s;El2x`|^FQyq897iNCQkYIV>o^Sy#8u6=4m)XL}6c$@F7 ze7-)TgXwIE4wL86`8mCKBIOZTviV?zs=l39xEj*TsW-+z>&;W2lZS;T-6LPj0oyQ} z*O43m)btmIv5E5{%wgS++6`LUpg3EEtyl%a-a8)dXFIQ_4VV*~bcg__Znf~v`{S~j zMvueKx-~YieduUF%pU*-T21+E#3mYxt9Edat?J9lYTbu*#Zzvt_}@fH!2w$2HCI#> z(wbvRGAA_|ixP!WXI(JGEBz4jxsTB6rc@bu`JPL@?7T$ zY`ba%K@khV3qd3gh&db55m+2D2mPdufySTX=TcwHb>;iaP+ry&h`EaOowk5f0k-Bw zQl@1!MbqZArjVY-5YyBJ;`oCjFwKJ&V5rR{q*^PZ>M5(6PrtU4kdQ1wGZh_wQ*zaP)08Wx;ChF+8Own4f>bm&5 z`Z*e4?t*)D0zxp#qEEK?Hn84%7z=vu!GvcV7-zMU-ZsD46)#$)kHR7h5{8(tz9pL zhCr|Joh?%Vsl~W**QYYgWvu>R#OTye`a9O~>c@vT)$);pc?Laan7`^Y!Qt2JzV}(l z9v~w&Lo-1U$7P}90Fb5PS0NFLrAbG|u$;+zmn4X#^E5K8x%=NGEWC73_k3h9Viq0S|VgYTe%*_OX60aO7Y$91Wn{9TKZv7Vni zhZW@1wYbis79$Q1TWW;CLTRvDZ$_@4@=TTJ1Uam!b0jWeQ((BAe$%`*#14mG;_G6y z0x0mN!-r{S^fnp$Opp}s!b&pDAX)-NF)uWUC@aenfg3&TCro8z{SC@Lc-W zb0$Jjbz8(LBd*E{LCE$E0&N}VeYd@l7Ad7P$WRJq47`D>Q{V8y<45?7PyaTa?q=v7 z+VSn*_e1!|hrR{(Ui&n@^y!b`Q@{BMeDFIzgfD#lbNIrie*@3H?seGrGj8tP;L*bk zz}e+;dgg&t@}s7Utp&nL)pbLVM9o>YPYn8y_X z+Cf07T8zjjXRUxn-doP?@f?bS*cyCS#Bt2^+J491pi)jY{K&0vbGn-ljBEFNCw>FRrc`S75iNK)cICf5wes@6FjC^@NJP+KQ zI!--P?@xU*2l8gA);bjT?{Bzw|Hci>1JE67`>G#a2RFVSO`rgaek^NK$0@Ow(~%a75Jn_5?JeiynAA3NO2ZVh~fNh^RC`7opqCFF(}^s0ydQn+3+)8o&TaV#54 z03g>Hz+Ft5{s=}?bePi2E=P7vHIxY;6D*7*Ie>L8Sj5I6i0=dphxuIlS({hbe;Fn7 zV*W*-1<-3$`iC(M&32^ZPQ$x@2_(V-p@J!<#4PaJ6_rVgtcNKE-c~H<0`{ozriYc? zSA&r^wK#R;SzyNV)Yru#0qnjh3LOKzZ)k0UxZs*lE6h0sDh-eK9b@cxaPL0mu&zkb zIz&3=WMMBa+&MCWfDo`vPYFO8C@0^e&Xm0y5ARW^DMO9!eTgMmz}{sKX9wWU{0Vqm z7^|`fWd0nq;cpQNKT>pXOGf-9ogZne?lYfBi++61_gn$k@%JRJptkA%)fyxTv}@fD zE`P#Czio9KOHgU8@pW?r|gfJdBNN%C%yl)G4rH zkB9Umuypn4oP*<~ZhZqphVelt zRJ$2a$}Ln%#RsHAOsQ%L^9B#IdZ1f3twmOAV7J5u_52h%sb{tfGSCz&C6-Tv@zdaG z(|JEh-1jDp;l`^wDwj<<6wQ<-qqV}DD!-I+E?q0(xnl$kAZ32lpw9W?`bYDh3h2E@ z3s#I-*Dh)P_#y7gYfIYS#dBg+4tU0ul6HjqSKb%z;_rv=0W&~i#Z47E8rEK&vwWs% z*+0Q@Klh5W2nUS6IMA0S7GNa%^u4smwOop!9`(>1qdP53FA6S5p2bNwnN~;~g5EmL z=N)729v#3)`hmy$881FMgXMo&Z$e>oI>#-b6EY_-X6Q#y#n^Xy7oRB2+GMMi`hjL3 zN}QMR9l1{)xyTNA!P^))K$_F#++NcQLjA`I=xy~A^5K^!WsEJt0Um2W6SpgeE3uV#{J-b~24LTcim>z~cJoP*y<`)56 z`C{mC(`BudKC$-Y&5Lk5FE~U&B{Y-xeI_kvyy)_Cb;DmS1!*}i@ukwjL;~gnH7>hU zf+sKsI<3?)Fh|(K%=^S5tG*De!E-mWP);l&7X)by=5U917HN&j7J@lsx^QI%>vnOt zMvLy1LIJIjW<#OiH)#s{rTVjI7@xWr6BsT4ns!svi((f%qim&8{E#USv|V8a*F7XF zqpb(`_}yI$Y5C}?+&3UCsXmtr9T;m(7yFJL>W1bFwsu7_c({%`pXXx-mbIzZ#XQxc z4Qlbjiz!`tjUwM1>c%vQFn$|=NyoM!W&#(0TeprL)bk}ZE{Lk9Ac08I7>{;9y)Y`7_KefL>5R<`C+s)@&2y|OKu^DLi^vvrdb7DUUvsNd zW=1Qa>>#aZ4SgTr^kfo#Ad`xehmdoq|o}T|; zsZG9)4n7R*AHa}py|toNb>%MijeDwaZ!HjAzSgr3;6N+3ui{Z(6AeAP*1*rM|7IYo z0|nGkVQft@?ZIV8W9rRxz~gP~BpyfF&CW&P-vHbrrO-j(b=|$SXJuq^0B7$g zVeDlVxy3a%xn{cFjgh zWUECdF1{@^l)TP(+HO2uP1?wfVzGh@ul_FgHo%hrn$mwgt{qi%g05XDp zI~)fKuB7nfRO=@~3=eZFlK~*!y3h?~2pWNK>C(8L!H8#lI>Gn)D?E8on-) zo{a zv3bWif~L1)Iin&O^1J+vzqjB=^%KC7bLaQI-nwFv6ndKUL(srg>CTg`ovB&QQFDW0 zj?%q>We&?&kA|lhZ3U8tv+SE0{92I4T=2VcJ|t&C!&n&3;Cy#k_k2}Gm8A>=#9Eg+ z-xkGU@9`*HGU;wG za03CP+nSD%GAs9su90QqLKf8&PRfKaIA;KGx$fX`fJR{ZjsRLT$Ism)bRyt+&9kq^ zKl$Wu;(!0w|HpXa>)(Lw)G*IyeEko8AFqGMhw*{;e-pm-BOk^0|L~8)-aPTiU-?J) z+^0T41;Cwsabb2p)RTy&xbf3r9LY=`=+akOd1@V zyE=fvxKbizC=)BsB95Rjl)Fy+rIP8HO~m;sLJ{R0Iwabb4oOGNVz8~`3-8hbi(rcy zQqBWa5b3Us2(z#Uwq)mLQPTWUnF$Lu@Nt^XD@F<&btte|ec;I|9Cs{h&5`Aqz0OzT zanPbgRxClfKYIH4oD9Er!`a0(>x+`DR>$GWPZ%W`?U6Otckt?v%W;>#q5+~9u9%sI zuNXVe!McXf)ES0Opf~6o&^a-NVh-cQ@Yjtgm}5fca6eeXc?_WKAf_MtEjAfwuuhe8 zPV~OvyzjU@@0bdVA=rn>%YArcodSU2!om8#LOJ96gce2%u2JxWf~BZ4LDsX#wTJ)N zE@@5!RW6Iwz#+eNoJf?bem%Rfv2Gg3?{N@>D9@5|7KMf{6dq+eVlCg@W)^gQKn3I_ z1>78Q4(m{|`yLe-q<}{fIu{I?LtYUkx_^Q48 zRE-u58v%%J_%3zXMA;hd27{Zg)jP=iDq3H_*#zP9cGaM>8x2e*lb3w z)!~_72)01yzVAmysE2>AeqL!8q~#$r23TROe2?n5Br+!==f7TW!tyA`G$v)mf0EEB za43#gsCFU`k0L=nz31wM>za_6x`mzAvZ&c&_q8BC_}^y%%if}@Tpwg23`GR2%6Ti$ zK81*)0?Y=T-3X+_Ra#ve##tO08OOC6nAX4LdROB{-K+GJTv2Swb6(0CeDOa1jonWC zaMsrkQONWz4=oj;<-Ptif~@*I58DNhB2a0r-YEGVMZ(Q7jAwBeXP(%i$NVcOv}Op!Cj2CvS|VW|tOYUrxWvA`x1Xe!uXQ66(T z6^nrmGt4w#-_3}n<}n{0g4?{jxpXtgm|@3_uN^quyN3qBlQ;L#wi`Tr{K%qr=DEnO3%(lOl|zfZO`Xj!rIjTmc#2Nws&l!!;*#ZEvUy^DM7hv~1`^@}0QV z?^0EW7qA8<(1t%&0<1L_NGAYeno$hVqtPn!ya3ZY39`@{K}-eItuZRrdPs4Nl-_$@ z2SB@BJBIh0qNlJZOEi|McAE9DL1-S6~sc#@%|3F3;Qgx4I_fVbFD`!Hb|8?+<)d zm|?}~{Jmm}BQL!^MxQ60cQZ$v?@BVzw{2HFy2t<Md_>4tb@}orWcUhN-nv*Ab$El^ zNYXqxdzRH?r;@4gbRA6($8koYr65nSo>qqAP|0o_i0(arUEH|13;}eej|bF4 z`PD&dbRX~6A?X@SjEZWkg=#H85qBwRM2zW8WExF$qEaB5 zYAtsoj2)_WFX>Dt(6^hYe81nG@znhXc<{sn%rPODkZ#~(j)C(SXicmWfP0U14xGpC zV<9v5$gimYrt^3kL-(_&3Ph!U-y4bC{b5~Tmi;*x4M;On5%yT+`uofko(oI9;B|C_ zvNfZ4NE=!u0pC8?!n(0v{;$Fe0kdJDe~v|A_jibOgp=HJg58BoUwVL4-B1u9 zA-N~-jWFTb)(sX(FbLYU>U^W8gHD!3_cq>D|wFnjE*^!-zXpFG-NOsWd-!?~5%)j+8a`mB0dm{ssgM)ESHJodeE0(&z+d^Re+}>X;J0Bv4?OkM zQ@FQH{L0_|+xVrQ{ZH`!`stskq^%2 z_;0L{r-aPr-D0LW256+_9x!5qSfJuPRn`*=lEwg?08$JK_535x2?j9fUFJZo^AladmVLyeo{vg-uvB66(LJB>_XttERNX~QB0eWz!pN!}mh^vEE6y<@HZ}8vUqBdoRh^4=z znsA93ShnG%3>o$Lz@^S}?VdWWsK<6VM(V;i74Y&I8a0JdCflCcJ5heVu4mObRik8- zc_`=ou8-2V?B^k}Pk`97$|G8{8b{RQL={aYdgEy=Q6I`VkY0@1_>QLC$L{=VPFRY! z_?U>~*7a}Q0x)du{%jRQK4Q?f$u41ZlN345{hJEWl(p=($AZHgy%gSWeiEN=p?~i3 zabK1*?2gi7de|T4^Quyi0Juf3ZV(yt5jeL{iR?bBlC8~{sb^N$CIHacLTGn6gjxE8 zo7cUd1&fNT)r~P=F^V8#Xy4T8CC@O~CZo|GOS)R|A$s29YshBD*1#|i>s073(s7lm}hu9Td`rzemUw31#65z;Wz zd>R@QXC1)+Ri@Ppn6@u$d1{=}TG6#8X#Hg8M0aaXsOr&W10sqsCeCNeDIMb$4HK>1 zKq1)2?#d}}yAPaNH>L<%vwfZ|DylVfbkK9$l%BP=IOC0vPlT_BTQtQw32ffQOuT-8 z=Z`Ab4aR8$9+3sNek<3|w}|`W@3uN4UVOEH&1FBU!U6I{L0Z?V6<#VCp-tn7Q&v!Y zd4{nHXa6@MZa`1`Q5d*V<4Qt{b5u-=f@D07W{V`} zQAf}%Lc-$Q5#5Z$#{i|V$`vo|s6ak>3EhGzlY$1S%om>9y6>7DD?%PyDri&n!rlp3 z-Nk5WuEa@olO;hh+pGsjF6S7o>}Wia4mR>$J5OOKBX2Vn9(~lx+)G6vPa~hqC2Q|4I8x8j*)37O|2-oMFx6ZDz(xIvk5lx zC1bJ}B4*T`i2Zle?9U5ob_WJitk?6*F;TC(X2`gh&y~+Y_tzBX!`djpgUjsY(!v(Q z@v*#LUC*Jesv#q<&FwfQ>u`0q5f?x>E*i`+W_Ay?dubr$vNu4_>RNyY8OQ{*dpVK% z?%=L=e}mDT2MX9uNo*7OB5?}OmWUZe$OCLnPNU+rUeVndq;YoLGLmzi9GAfAxcPVl zVK0ylh)kzP1J_nV_8?0^GBjPFqu2;Mv&ZCl;z}B(sV6ZFlg7|j`+3y{n4HBmM(x*# z`;kS=ODzNf0?a)N-mvu`un>U}c7v$*oY#TgyMVxCL%%}AR6Za^unQEb zOybJ+R0B;tyhmYrb!&@4lly!5cPb>l2%z#xk4jxTQD8Lps49{v@CxNTS0*``mBUr^ zeUh|@fga2|7vsvBz1%ef2@Y+|2;=ouCP*xUB?7!~lif*=K}Jlt;1o3wt>)n{>$Z9; zL~&ZB=b95?-mazZEY{Xovr1F3ETIsNS@w%591cWGSsH$icpGTN);IGisbcH*0G+tq zJrb<}{j}j54{W4?&|kBi6FN z_Jzczm?zkt)fzp>a!J?fhF9?WQiqDyYVQ4VK8m3?MqCA90iLUOYqj%Vcwsm%7n%wy z?0L*GJ$|Y+{sKJsIjm=J@z&^NxT827i~_t843x3*JD+c~AgXD@qTU!EuXxYPNRS(op@zD(G`KKy| zYsfh)#>8ds>KFm+)7Q=wN%L-58cwu(_Wdxv%>uB#FUmHi9?(1oo_^+8eCBhX$G`hG z{?~Z+=?6gns~G2j?a9~TxsQAl&%OQ4_~G~eQGEBG_*1wA@aXXi`0bDX0#0~@Uwqd) z@qOR>$8fWquT{;3v-|uNf$E}W&<&E=`%Vj(b+yfO)i5V940)pB?>ja|$ zNSp3yFQlSJqg&EOYb=DD=Zw#^)cY1=sLV$eIm~(U*KRT=Qa=5Ae>Nk3R-?1?y4?K= zN({rf>=}e$@N+*jYS>x52gDg<=evsQ$Zw2m&scmK_gEt)SOJOwTC6rz?40w8t~5dU za|Px(_6~`g&vU>dW#BULuqTUA-RXTNpMjL4?>~#+urbtDHCB^#K{h?a_5(_im7A=2X&&-qRU0#z1ergZ+e|4f^O7nAWMNNk@~8 z)9D^=P8;StgU+)#23VfJrZzEm!$u)kMC~1Y1GzwFKB((B(P>QpmUy61%2L=V^Tgzz z&+jOEDn7o%!K_`iI)MCg!&qXeS?0Vmg>{d#Z=Yv$26cJb+<-PV<1u0_9BgbOqjJ@v zYV>6h5ZCRJE*FRNE5P|zjW5WxiFM%X4#v+E@5_nCy6}t)wS;%AnDdS?EPpFbxjMgP zoOX5KJ<~Zi=c7!mMby|4=SL!z{(Bxfux)N!E=FLc>A~({lNh5Po_i!d7C|@yl;ss> z_!vCN%;bJE;jTg_F(PMQ7{#VZTc)d&J)S1FKs`ZylwK}C1LxS-TO-d#$Wj1qe@GA| z!mfC7HcH&zLb5paKMLuud)@W~qXR!k}6)X(3b(owkk4P;Cl=Oj$^OpnE2Y z#0?#i?<)YDeCW!;mi6UxDhOHkMC0k};XW#sFID{Mc>T5Ck7J4PEK1dC{ILiI8zJ{# z8N5$(VPC$44Ee8ner-H=G1Y7HxQj_wuvvf37JBL7GJ2z@)Fz@p2}dN=iboUp6^De~ zRE)$I#!_ljv7`wEwek%01fQXp#@O-=vk1kyz~bC1yh#L0eY4sODqge29@x6XSsp{N zb-@XW$1lEM!nbYcuo_IQH=J5}fXBWA(}47JAdx(QEgGjYG=_+m!q5er+C2R4%y9DW zF89^H6H9iAOWV3$ab5kc0+BrONkhtYUsLqB0mn124y&;DqG<3LF9#6#sV(32j%&&* z0a%&@`Av3S=vauuuaEPq$%SI%0m)n_>%2-p ztkn_|i-zg`GyOag|M^c^402jh5>(B&GSfR>&zwVX2gTwFoO~};)^cxDVTJ(PpVqpAAq712c;W_qJ7H@l+&)sg_K64h7yjzMj6e6!{`c_2 zQ?J1<{r#WB&;H$?LKEP(KlMp`>5IRIufO;Z`<&?gWOZnyqxXj1#n-W;$p)r-ru_DW z6YI=pxF(G$?AA!UtpJMUVL3N-Uj={wdO(H0dbgt7x|LtY`A~onv$cI{f_SbTq98h8 zUi2K4fCg$n0^fwgqRQt=uN=!lXT(L~fV^TEpN!^n0&JJ5wgb;?w(?fQ8{(-vh3EJg zB^Zuko#a5V2fE;&1@FX~`j*Bmd@kj@CNJ~38{SB{Gs}8qy(53hk^k4dKmIPcm&fn# z;`^5d@@mj_LPXL}kXAC0G9J15E-OxaIUdSzIno0amCB3C-)Bj688h1{4_CEUTn8AMfuhLG+~3p9jYXi3Tj)4rj1hQm%pGU% z?)JiqFXHz6*l#bsIWJh|BO zIIXRr_if=H02VB3e0OF6d$1dkfh_V88;_?6%7jI2N?_YaSEZvsjQhK{&hf{^3LOr#qG2U0 zOb$2-2acDDPzwVEqV&Zts&Et2KXbdT1v~!*cTY-M|lk5R7qR1ElYfxXvQO7a#mI@{Y!gHx) ziX5lgmeo#>L>d#UKG8Iz!%)kywsm%pAqj-k4hdTKsE@H@YYIrW-0gkGcCv8SajR&3 z!>M=N$PJ|5gUUJRmtoO!4pVMhckl8HTAowUx&zn%1HZMF?Uf|9iH@N z1L9pt{I_Or;#u_9E&Fa`YS#W28b-ot#1u8C$XB#!)U67h#3(6AesY_V+7^O zG9sYzO>r5)zgJ^SSr7w}jAY_?d;kLzXoAgw(r}A!bNb^MQ+UTEVRw$pk2qaT5j1a| z8xxg!zS9B0v2r=rg2O(3|DDCJC6vNp9%1gIV%Z-vowQIcxEB_R%XiiBH!QL|@yGW@b`ZQfA??w1YY>cm+`gV`z^fTjc>s{0H^y;!`U%8uW_YGkq+#>%h>xl zJtKBf++AYu0~oq$;FQ$?R`JRayVPe~`#ufQ{%%Q=Fb99WhL7~{JK}fF?KnP> z;~ABUaq<~8m_d>aLZPJu$I7iHSh(Bvrk5L6zChjBesA$?0q7hFW@xg~VV3WwdWi%Z zoQgGK5x9{ts+p5zO2{VRa(lWjWk~LmpH(kYpspX;J{YEK+L_<&b!cXZqCwy#mk6*Z zIE_?><2$Rtj661tFPA*)m404D1k6zZJQB8I>3jjfdZ_IGN}mddl>kpcJ!a+s!vI3f zH>O}S1^@&F0>N-ulVmd25^%tppd>A0-GSIZ^E_!)wN`X0OZ}JbIhVjN;U?8fD5zm$ z36xTZaU)GR6bO2@o#h-4JjSl?@Sb!rZtDh~1XII1uYIZo{4T=^|5mN>T(B0!|LmT@ z;%{CYkk4Bf1`QT|5OfNrHq1=!LD-YE{0G1eQt|Tpb7@$qUM?0375=YG>)Kqe8DlOO zB@!t0a(_a!WI)`!=GI+)G6*`om@GQD)Ho@xHkz_9JB?_Vq^00cGiinxiA&C;NmY1F zmlo%Mg^Ep-*wCeU4)`!e+PY%Q-ISC$Q|vT>v<*<;yzhAUXaMa7)B7o&hjljV?H=~A z1G-}qVC$QO_X6HEY!&hf1W2-R&&+M+H4&jo*kl&)h!Sc5WcWeIB4Tz##OlQWo&&xz z*ZA4r5lVRBElVVk??D4>ef;1~u{8=~P5G=${4NmS0`Fj8q^x6m@#8aN4t32`3bZo% z5g&IL>0D^teI7Dmf!9l24@rL0q@hWRT1eS-Xq|5iRsD~3KK^?Xwv}J!nPXYhmWWWx zLXOTSeZHz0t-|v{U3LJW5Hz+4o;fCiDgv1nU1Oe9AGNQyx)Rd7c1(86g6GX^{7yMH zk#%UU8Mmk-%VZ6RN)t}w!Jnw^=3k{btxkHw>|BT3a=vVNo--62i{2FHKd`!Nl;_H{ zT|_Erm22yN9-3Zct!mbSp;1XA&63pWeACyWw~l>0!Y4ld%lO7OzJh0-?fCrf|33cx zzxi+CcRu+mX!oALGw=LXe9L$LAQZsc-~J9f`_z-TIW?RgzlgElI_R>wF%@{~p8R!M zod;X*>Gdrn>CGKL+=r-70rG?n3;9N-7IBS378ZFLKielGEN%CU?J$I#UgpG5;`tov z;h~n!3Rpfrz%ZycWRCID#*jt zCQyM7CG^V(x z8!_gi0?X@{-{IbS9YI%3ZI5=H=5TQZ*1EAe?T8g8o%_JpfvF8v8?T{lCqM-I?Jf56 z87Bd5PB-Y=JzI}4(6`N;s;W?F=vy;qmrjUmnChtx(;Y_zQHtZ3DJJ-{;&~C>IXxKV=i&W5vNz@ z)aAJIn!9wF`rHe=biB44ACwqE!l&=T4-CVER9 zuJW1cl#IW*WZkyy+L3$N&#Q=lF`{d6_*aH}ae$Ftc839AD-x?c;Hf?(PfJ}v+`QgP zyjD^;D}c?VX(-;VO$yi!n@V{P=3~YqfZ{=-fStc)PDpEL$ij%1;@v*IDisjGi{vfu zK5SZa1R@9_fUXMH0fZQp9S-(OZ1-K(RX*!3<*$BssT2ybeKx`nVr|8X!gJ(5N@J0P zk`m%kt4BqGX}{FxE^Bt>o5l9;%;-9Eez{MqVSPX6#Bm6seJ>e^_<^Q(F08Jg%IIAH z$j8e0pd1CzMx8|{s{qQ-`)MWc0yIm*)=y}% znP*it^wWmh9k?9>y`3SV*xCtCJn=LddemVNH3o*(tX>mIUCpC30G!~4W?x%v&7!HA zwWvNxj-L#&Ny`x4Zpu#Vw#Lepmenq>l|SV9KhoGD@NFebIacnYUq?0}G`Ut|pudOU zr@Z3YnYwNiq{q9Q33bryso@03TBwQ7;kEqQs^r8K08F7l16m1P%la!dj9!D$UgHx$ zvLt#pRn-mO_PWMr$u*dj@8xGI-|e0YAb5(ij#xVzQ_gEq9_@=@n+mmXcn5*z9a41A zD48!{)OY1$EYcn_g0DU7F`8sOGs!5LsS_KTwSouM)TVO(u@JhpDDA%RZ20PkN3cO@ z&@4q31d%31?n)pQ->8qEIq&x3eQe=KAVzYSjydTZiG6bwA{FdvI5UM)3oWS+WHKVs zTc2A8IbA@Xs^Y;DPvCcc_w)Ga|M>6Voo{%36g_|XZSTXkz5gxv_V4^|^rzm254`Wg zI0N|l*Z%;Y`S{21nrH6g=imNr{J;xCeOT^J)Ew0?RMkUfi89dk5ygA^!2H112Npu>AE&e9z(L>~qJ|8!pNmE!DxYP5(^CZ5n z=Va|BX5$R#pqwOWg9cYSU&$xvEbx6qp5!e2=!Pw|C>DAqxsEN~565Bfs6+&Shk60W zpfi(y1me7(Q8B?AK|^{@HP50Z_6nYc?505=UJ!=OktRtEdA>YX5d|IBjjmY5M^Jbw z53G=16d6K(qv{=?_+8mPVwLz}A{tGI)30JeTl21+^aeXt2%eYc8HFCWP7#?jCoHJ2 z@ccLzikxb4YNo6TJC$&6r$#Z_Y^yENaq=ipF8DupKJkF425plw3s zY|d3rryBeTriEXXu?HNMhRH&|TtB`KBUU&tYB_^d z|GM_M=4`ok@A2QOd*dM2_PI!{=F#LaFxdGobT1L0VEe~-Bf^g@!s_~NUE#V?g26oAV^>c7c!kFW+H$)je!N8vdTaAX=1py5&G z7zsFSUKBE{X<_$q^P<(J6yL36t6h4*RLe_9G{yTL?_Mui2&5j?_)oPIIV-z#g#jL| z;{Y&cb8njh7*`atWaQTvcs{f2P6%s`JO6&|Ltv6X-max*ay=g_Gk+=T^tEf~sVxJG z>ynWO=9pfC!n2zTfUcdwjQ1sjht}uuV_~Lq4rIGf0op2zsv_o3F{V`#`Rc!Eu+ac> zP)0%+y^noIYnB!`r$Bl~>w>n~cqSTJ>)2pjW^Tuheh&a{aXt_4M%2)I!`N@Vwnn$- zDQge)*h6{|C%ntp)B)KQvTCfM%IUwnY zuXC%sPSw&cj%k@8$f@AeX&pf3l?5yOL_=w=drulMBZ}V!wXQEJ7z(I1Sg}LnDRcTb zT*etcsyVRv20%T+QC6DrxmG1|K%?#XWLjWRjw&cNK<&ntDS)=cm|-b61N(l92TweW z_kQprc=|POz>6;eZ+PZu{FVRyzlcBm!+#zR|KM}@Pk!oe;%ENue~b;lSHJuf>=3;8 z@DU!(iPQaiIKjMU(sPbk+s&LeY#Gh9wnz)op@XwUIQt4YKT9xo*i#o%mF9y_7mN|- zFs@%|C=9ZS$em|hx>ccX^t{VQA7dj9*%Ix5`!KwMobzSOX1i)5tSXr02 zjy&EkSnBv`Ionmn7uhVx=oB0bxbFzgA#sjZ z<;brVD~3D$=OfPmm$ZzSE4A>va(!zJcgGNLsigdC(V3!dBo}1s=fcy8Po0C5eOVP| z8eZzSoAV-vE3sBu#=@e!6+2FsF_qrxyjxeVb0mI->RBlU+rvVN|%`gRZfbi)069nf3jchaB(IQ0$Nwz;u*S|bEl zBsG1l;!(|ilZXWH9pGVYP`s^FM*8&X=tb8 z%XFL6?NVu>f=$EhYLveAsq0Y3;w;wap*Y#bZX@xnK9?qQ2l^SS+$ne8D2O5p?TJm1i07nv|5mVx3%@*JUy1<5=EB ztV<_o!%0cqxe z9+*MMWQ2xSXFT2bkOU)3BMg~St|J90*$RhSu}>WQ%-p$PV%Llw{I@c``unx(`K(LE zK*BcElBh-u3Jsah)USZB0(_Kp6%e)H80wn&0 zPWQNpM@vLa6W-z8C7rNirVzE7C_Cs|<$l3ObL$*Ip@mBzfTKHb@KDdx(bQdg4 z+&VHP6Q8CTAr)6UH`}&BRWZhnsT2E{R!DaaK!KAO`OVoq&-oV5KmS#{{xz@1gZuZa zj*)CQZTIk^-eT*M#h z=Yh0N1y`JRsUf9u;Na_NW1Adi9j8bX)q`XQ4QUM7*V1a8<0SBsVUW62W>G3ShVKGW^(&kG;+iMi-Boavl@(C=>A1Otc9z`g&wR z(`YIH;a>aw&B-+nWbz~gDPtCRN1BTx*G%a%nrR?Z5Ze?_GhI^xN&8gvSf_)uXaCl7 z+d?5{he^AKG*Be?0)pVFkkx2m3*mk2idgm93XSK?ea$ZGt5P2%e+|cgn7~t$dFHvM z!H{VzPB0%jEcc4C0C@tbxBCK$nTg|$(G-XQ7abG&rV^75!dCo2Y^St zXTt1cy1lRGoD<@aw$*5*;>KNF*PfjhMa+5B(3os!=bPy?&TpnGcRJM!)(~VJN2||x zY8|@(+9uBD9s8JQz2Wix2-^m87zw@Y-H@2_5DI`DTQ{9!`Z|mAM>LJSVH&KeViOi? zFvCOgXzq~|dd{^M#{i6GN-DnlQ}vXUcDQjiw&`+4)w3ALsD$rUmsM1z;q?X+tB}{T zjtMgZw7mB~$u3@Jj43bUx~yrPN8G_V9_LwJfi$Ssh2g)$)93r)5BBphqW8kcdPNsG zes{d~nt|*RLppDm68u7^@zZ4uJhagCP)p4-Lz)Lc5P>xMf5#+PD`Z*fWR8>x=)5{&NtDjd<0|uZdptUc)wb9ukX!+JJ8Dj^l`cJjR z?b0KRH6bNz!MN2*Acz3SX0>xGycgT7Oe$H<%E;l@zzHUea?JE^0PC|j5VQ-Lhm$kX zNML=$*;}OpzWG*Y7zv_4rs>QyhBAPbp><`cY@Fqn76nF;M0!OD;pZ;P_VM?lHM}x~ zOGfAg#ylr4fok&zrNM{$x+O3$qn4tJStUhGpz1V_te{=YXUe=@mETtZFu_gGlCR6> zT(d;q^|{M9L%KBK;QuG=EAI+KYiK@~d=&}N7?w86M~`O;3W=J*r4GRkxW9cCYcv1u z*GBXY0C;#L!K~q`JY5uahM5{zmnXuknF7$f{?QI=AKig26LajCvYA&8f_tZXxOeX! z8g>BY={b+#o<4hDcMxN_m0oAUff-yFs}p}`cMlf=32L2@M?og|93=N#9V}=vYgdKzqwtRg&{zN``s}wOl!u*Sr~wx>IB~3f2dR zi@i{hyta`R6*In-^%EdFq&Y8-x-`z?sVvV1SBC6s-p6`OB38wtyGG1Nj48nAEWYbd z*ax~nqQ*rm8Wlq4NpvO*$#ti2FYNIG93MTh_T`K&@_vFAtNAqi z{l){r;OJ+_-akzlo{h)S&z;AS1(JcYJ9^petJ#{_OTDH>^Yr2|cOPSis#?f@mnaS4d=&?@zp>063(}ep|FCPI=m$|tfPsRT2Z9(s4oIUy()pYVx)uvURTTS zY;NRrEMX~5nlyq)J#nj0dg*W_Z@xVL(R(A(@Hr<}JqD`-GfMkGdyf~QU4^Wnw5 zaU(SqiWNhox0w+(q}y`5fP!iGSSmvk?0REk?tZQMI@gFX)@X7&fPr?k6dRdpMorD= zmBkPP8a)x`FPJ!Dn_?l=={!<6oa`Y$+b7zzRAP>usf9Kti}IrEVU$-RKoCR}n7|E3 z&AiTRSfz8i!d_l1&hz5Xwsh=L=ERJ>wn~7K^`k*2$!I2J1tVA7%GCTC%By&Vm}1p^ zZMmFt&T#02@ujkMU;d`K7i;=DqV`IDNuKWVS2gKf0nv_Xe5!_85({~D7$}0;TgtL# z2C*<+@O^W-KG6zBN@M1}+v*Kigm9CFo0}V}&w~!x?zx42TmduAwn)HEPUKZ|Pc8ZsHUEe*N7> zSGBi-e$69eVd|f@-(thSOuK&T$l;gXo*IH|yp<&7ZFrk!>K6lekCNlb8Eh3gW z3Gy_UzCJ9{%#A(bOx9XuYgri(41!Vb5y=Ab7)$F5TwGeXFD}(9`g24Np{Z7H#UJ8x zR|L$RyT0$cs<#J-2ov3HP;qOb()8R5T5r%@F)=KJaxc}fC53j(L=`$GTJO*RlxZD~ z_tmK@Eu~TvEHWGGz`!cwp|pA=LvC8i?n(F(0$o@O7XTw<;5%wW8?D(~$(TMDOd20p z+}EL!3%NQE$`C4{DDy`7H?P0jJABvo$NR56uX^c4YPIol*nPd&+7YayBiXV{#6n z$s7bRHMWY5N*l+{F?_yxF`S+YU{~uL^*pT=L|p|X9BU6m{|51~xC`%D>1(^IP{D=ky zzHJ8Jznp|Eol_KnmWX%zQJgdj9!K{iWqgqRK7_?`8A<79Adhfav%^}%A{O41zZS)W zG{SY~2sD0=J#S@ey<`p6%}+duGbeB;sAFz!8t z3E+#r`#F647k>^-Z}B^y{eFDlz3;Y2lU;I8#0ZKVK7`#52!ef*G8MgCl|FYuwyCrc^(du zI`b6gUAbR=uRoU#0J1o?ppUmXdxO&s3$)j09v&)UHi)IzcB#CceBSHVfNYhDhBDMI z;=Rd1nzVu|lw9VM!|m&eA|m}oR78U$!YD{OrmaWQgu zcjfP5=Yz$oYcy8umC!KlgYk5NSmJfUk@&RQ4j`!IU#E#yXH z34z8`oCcN6&bR;~8@9e-Yl;^iKE|9I#ss$3t^SO#an$?UBGv+g$ zClPRpD0&CRT;FL}bAYiW0NhA8!O{pyj0ObbSV$_y>&3uT9XQa@W@o?vo70bYpi3*^ zxFTU>wR3?I-A_g(9=*Ud^dWdO&6te3LX4$RUyXSm#_yk12Q7U2Ret`^DM*hVK2lZe z;a%Y-BJ=$yB2*EAjQQ46tOEK_Yefl$u!Yc`7@F5x+edGq%m389X&3z?3ON3{eMk;mzqErvSL!ov+8Hcs$*hG8MW{OND72+jb9=1|`qEn5yWl0#9FKX|Cb9&ja14RXO-l4x&gLygCE zq8FzX2@tm4U-Xt$vAAFbe%6Sjm+z0?VfEfP6KVt{K1PwGbSgHD>$^O!?(V_ZjO3w$3wS3#!4!a;D!^OI(hkCYiZAZ+owDd%pqnlJ}Kk)H|#0P|Zw zPrW-rAu4Fv?fWqdFuB&iq0o2}pl4cPP_EAH?PEOqn%Cf4zwd`|Uw1ry2)zE;2lz8T z`WNul`%mCEfBpZBzxB6&3h)2+@5c}Rz_;V;U-=U5J^5O^?<3!Xx4z?@E_EB&sV@xz zhZ+G9dZ7U|oB@N%p-{jb*PKR^6)K;DmbE7=;?<${e&WBXibDjKE78%+#u^OdkYH1Qi zBxP|(m>)u6k%@J=#d$UNl7AA&VsH)VCw!H1`&^!u^H}8$l~ys2|72U!gVI;o5DhsI zoyZrHmOOQc)UWjcN@*g&rVa1p86%b0{4a4!o@`%xVhnEsTMLqk@68ZJjjmq!jAlAL zoebQwAsSIIQllqwISLNHBBlTv7vKdrot~$ndL?2!&KDKH?x`UuPpi|x&vR>riB##! ziZjxrxq*frdMe9?=|v~DX5C7gc%p_V_Ve!8eFAC)ugBid+J@FPr}Z;{LHCv^x6WHw zGepr~>7tdlD#W7_(-27C>vN^jYnk62 z=j0`ye{F6>HeAzPu3<%f=K>Wk!9K89PZnGGU!Q zyNE~Vv8HLF7=;!IHN3x+|nvQcwFj)sfy7#&5Fd$8EC2J5yHdX-DPi}xqBEV;BIFV(2K$o7{=BRdhxFGc-2+#ztQT=<;@>2 zp7C;qrE7CM8mg|%+5QOE%o}!P%XGdINbRCw3^z9TC>aG*6;o(h+ynF~-);0cbs%y~ zoepYUmmDX1tqaD!BorT5%D3$LjT`dMtAo32`?%2M+ zPR{@q{#c*OJ1>Q{mi6HKS;4+=F&EhMRRQMt9m-XtK{&nKW<&y*2Eh&_kP5DB&n!Z# z@-O!gny`ql5T<)5Ji?$b$Xyg0B-G7FL1=E|BZw^u!LmA`g{6Ecf5h=a1rC6Dfs4Rq zJqs$^@r=S7S^)*A@cHAAXW_Vyt+{u$U-^aq60-r1A3w&={NH{8pZer4;nCOs0Kfmmuj0es@k4k7;JI)5Fuv!9ejMNQ z&EJOS-ti9TFaR0Fca$FUB1e^n*Ip_u4?WknRj-<-Eb!bYJiMC#&kfC(itgTZMy>^l zanCUCUpjE80-7~s#EtgiYh<1BOwQ219DCI$U)*67-9siwc+qY>c!crA==@fAfeo%f zWu$i<4s^u~vph#t9l9oeWdhoLy@&@Cm@+ZM%BEAqvolVh8YM39aV_VBGL!UK&bGf> zgRiUU_K3jcNE;QHQrO_>!pxTvL=6i@wgNOVA>>dUpLHoqd86=%YU04UJ}^fK=Sg!u zQ-TBgiLD1gROQ=?1BoLk{B16Ba$z(PLK4-2$M5#zIvNzvv=kJ6eZ$S^9w4{Y)kwX5i~?=$=uNQi9t|kd>P<%H7oKwycfA490k-qC zKZxm?L1$d?q&M!Za7_Vd!IFaoh5Bt!_&yJM63K~Zm@_VNN-qvbPO&!K> zp5#BS(6@r8n5jTgPUl9RiK5?M;Gb*w>6&A?MvPo@THN*FIjk5Vb$yBG+r@cv_?OtK zJn`;4XPfY1HdDfOvO+~2_~;2*)O!t2hp1gvsj9hg-Xoi66moi*^_4La`hIdaBw82J569_qYgh;!rfJ7z|o^Ra>xk zA$f`*dbqd%rLl&LMP!2BE%k6K5x3l-=*4lkW5q*w$9D6eUu+Cn`}NP!DqIhE^|oF! zVyv$+LRS^H)O${|Hv2tJe7PRijUqXQ)%q)%_W@L}l51Bc2w$H}yQ%M8=Y3?_N23cr z18ZSWAO$AiAXgfC7mN}9CH~O`Sx0LMnV!QPpqu+RA>E9PnE;RY*XYyZ{3$lcUt7yH zo0(pufP2@xE|GePYB6DGkMCgl=V0ApO2lc~jM?Z!lg+{sRXk-77}HYr#=hh6<43sJ z?&HS8n^nYeJ*8ociG3dy88$VVtPmnupCUUCElzm?G8=D#r~tl#>|Ee!g#ggHjV(k; zD}q@Kz@fn%YXMRH95tBHYKGB-xzh&hxvwP@_dr(xFcoeDOR#y3zJT<2RKZ$#T4oyY z{L~laLTM9MSftAOq;sT_(KJ0->e7v`wAO%WTscmz$35yeQL3&JMhKlOoR;gCo`&SM z1o<85v0{Ay8rYyr-O*G&d_=>I1oU)vDu^D+I^7s#=@Kriix3qS#bnJ64~a$b=<+@T zUX)>0n_~L&tnLZDA~v`o0J|}bW_x5FLod|G6)tkI(n-ovF2A>ARfH$>+9wLrxr%A+ z7!_F~kg}|WM;$2Pg@x&8P;rD*;i}=a^;QxsGZNq~nfahO$gkS#u{zE+8C*J+u593e%;Cu1oKlY=T z0G@r#>+#j!{SEx1|LhBR?wxPKxBroEMT0#{J;ejitkdRen}*JYL5v~_hG0isI4S!j zNvC3rDI7Cy?FKN*o*;H!n)7uUl#9?8u1_!kuh$h!v=~x_%xWU4MSCX-;*+WzSceKP zy6nk7(%8~Gm9RJ?NX3|7x2Z%&Fk1_TZ|jnV$&ac=fDspO2tjRPyeiLW6i}Z_lY%3= z@jH6IrxR`;Kel(ev$;ttYwAj`TFPec+#;puv}@UXfRrv~kWnw}B$_6twxko5 zGeyIX9pEI`LXxp=e0lTc5-wwD%2&1M10$*F=QlB`?F%D|SR-b|Q-UH-3QkWvp7`b3 z-}(FT@5fK-U%ZEOA2TLtC(DH(1SVBhg@vs_`ezUAS%YFdI;?%3XW5VQqB=X@?*WCD&xxd$~ry>p^}gqssBRdi^!4ZM4nf>m|e0O_|FzyX)DD zVWhr~;;9H~rREC_CbK+)0EkK{2;#~+DzLn=4vQ5VelP_NQ+|2%ZxDcvb-hBkEr4eF--E#~*CL+lE3^QS0uuZ^Rlvqi zie)d#V>!A6h6KmDf*$yMG-%k2Ayn>&297)>lwj^X(*QgbFQZVbMoSmL=KJbR=F3D} zDKyQ~j6tdy_UrGmHnhM}h*Dii6+{u?Yy7$ql|;_F1{-N58isqG_t$*ZR21_gkVKmo z9}`g{gptHvI86y66^6Bdm|mzl!<%o0xJ;8A-f2WVq{=wh-d-&tu^T)v?n*Yj={5an7_V-}uhNt>BFdx5w4Zx><^VjezKlfkYTfgVK z@r|#2&C=I=4^_E~3LS3t^>Z|3WF%6Rr6OVMw*P2|$@N$yvCA9_xb7a>ZR>CJFJ#_0 z@{Ig>7W^*>2*g33w!kppGXZ2LMu9hMa-*HNUrdA6SSWZo|2%u7691c#bQ+PGG*1;t z10Ct(NKuQz;a5m3K`J+C9ZlaMDe** z2@`RdrD3mSssQ`iMfxy`$2Z`M@2CiNF+)~&V?d-yIWHI<3DNz4O-b5F9!>hN zbqb6UI3bOK#4a<6Mu3b9T&yT+ta0j{uVf9X=)Tkpe>9j5GCW*k=s?>xY+J*w<_7E{ zcyMYsb-`IDZf|eV+6jOe+R?3JTIip`XwBrKwI5Gq8JhLj zk{%gBQ4FErewASpuT)v1YmwmN^>=+1EH5h^*}eJtRS%I!Uk9BM#3P6EL4L;DEqGwX zU&k>W$M9a9#ZE)d)j9(|C%H0m(O1Eoz-lpHm@SoIlzIHR6I8UvO>X(-SVI z1#}PzMa^SfDiZfu}!eeP9bpFLG znAmr?anpGbB%O~TFZ`2Ooi$-y9?rRL-XG#~bnw4|4<`n<4zGk3)@1SK%0cDo1tJL; zEGL|~#>N1wE6$h#9d@5+Ax1v{97a`Kj=U$l?UOTkM-CGpbbq#?8 zG_fa#>#td#>vJRs7)uN8YwyZu-_3;PGACKKWtkU}G4Yb=-e$=9e8tM>>7_X;KtUP4 zpQLBQa9M{WQ-Qc=-c1W^_g1$ykc%H;GtzXV0vqc$fQ)>xkssGT*U)p3qZ1WF>%4Vp za6liVl$1Q*m*L~Jwv=WZ9CLca%f!|b{8$YaQ-vCUF5QepYR1>rt@U<;712hsXU>4G zwg=NeG8sOJU8?tCCIF886@@7y7N#tXdsr3_UZ_${QEAOPm}vnEL7UTn0YUHufbfB} zx*OL%mxA}=4DsJ~MUf+OS=bK+y+umZBf%}rG36Os&!l8TPXQvA2A~u`42ND7v~@Y^ zd5#(kvhsnrSE+2}v)J_J*h11|;VPgxmWA&l&lDB9Id!os$i)wJ{UrWGexsU^birdY z`!B(ag!s#KUb-rn+%N-+M$QHIUCa8J0r#-++*py|rSz^z0P*$x`Gt5*Cxjxt@;CJ; zn&dmVr<|#e;$S?Eq(y=jg&V@kJ!(o!?3HG;SFJ6;`DG%FzoF3Xiw|GGyWaUu{E0vN zm+@5EpDXZ}05(KCMO=l-AgkN&-X2S5GOf7=wj^Tg}k{!Mt_NB#U&eowh}2)#B)sb>0TSD+ka$W0lU*#k)7ZsvJ+f7Nv?g!J4iny14fqd zO{T0}@`>dc^*hk>Z@-%)NoA3cKjZ?R~J(X+R!#9>WcShH=K+ApjoSzYkU5_Pm28yUjbl zjj7mmV$6Z9b!^R39DsDJ1@<+ai88L0DXJ>O3xM0Fq$wR(Dd!n@w(LO#E*Gagl#TV7 z#J%GBlavQh1wP{IDmRm!($IP|K(0i&o;Ubf9re89xx4mVj76^KhW_qrxJ;oDtxary zq-VR3t(>zvRcq_m>lo_zkC+g5jQ_6Zl+`bY8p-#fGt_}VVn1C!3T`MaIxK1=$CA!X z|J7v_f?owDD!<#>DOC47V?{r-)WW1Az;I*mTUA74C}6c-n1asra_$tpON$YZjzp^= z+dP$u#*dr69$sEW~C^^3o#79ph!* zX3InBSck=J=E=Mno(#dZSr?=UTRhfpvBK`{DnZ+0*nPn*_#%|ZFiSK1-3D;dD}Ep(rh!DPm7&pY=GD=h+6wWi$f9V*vPpqbLc_~ydduyD@}3HYm*=&%fTI|T(0eh<^C}Q4%pN?48Q~Pb#SbFqZ((TM-2?ccTrOj& zAPOMJ%TJ_z*wf;4hsyBEbELOL0+odb7AZ=MU<_j- zm7#}X6bnRejnRrFIwyE)msR4~!8pU-## z8+vb$J{_DiH@YZJ_ivzWqC>GY!GrtvaXxKO47ASFHg+4+*blCJgV%)#U_WxsLNB0g za*MoZ`!{J1UVBbMsf>CUlFAUD*~Rx$QV1CE@ep2BlQdG8bPj3}Zt0VAuu+s<(skqZ zNn{D6QtEWyMAq1Se`Y&&C2$hR`!CzL+fX&|jg+d>Rj*;g z)C}iC!ILC-{*+uP@-5=o3PfknRMMT>LagY#mNW*Bs$pj)%9WKrCm<2>0@AW*7+$c< zn`c8}yaB#^Tm<#q7s%Zh zkc|-CY7&96^=RM^o>F*Zat0%E8bl0`!ZB6ezdqA0X<|_usvZ(Q>>MGKHBSMY)&^c+ zDI^gkTFxo~0z2Au7q4cx6+fSfz!-jur5mdM?Bgq%ukxG)epro2x#xN2>zF(^Khja5 zD~&8lq4S#LbIF?q5K&pYy3!8UhP;F3Iz7Nf!V~!EzQJ1u>^C7+QOb{p12qDNbqe#3ry%l}y z0yQs}tzql7!(P|WQfS13;l;bdcpHtrVMxdM@flr!wr!BU0hpNQ-4J5~tN@kOi<;+& z^VxHs#N?kvi#MQC-rd+*dQBDWl#{lzi7N;2++$8jEwLdSV0&z0jVk%XR(<gH8_T2EhUqyZJBKFd11<`$<G3_wD`H;^H$aCrDE3Sl9!{(LlxbnxUXRhOpRhwwpXrj`p5=wfBw2wtL*#x@QzdYBA5VlRpPcDV z2M!~l@L?{N8{7_a%>}f=Pb@O$+{SiVa~}pKdR*gqzB19!js88XT%ZAF(%_`-bZLH` zd>mfd+zKALVoG`|ieP2HrXX-*oZjcObO$1@iKy8#qQi!*&F#JiW9(jiW44azT{gN{ zak<_HZtmSj-%c2K5gV8<3Sd8DdaZ%6pK<@bujx?qO>hRo3l+@&Zv=={coTSr!f9u7 zEgGsWDAI6Jje|k-kr(P;g+i2oPF)&F$+sqOr2;^~!NPU1hpJXH&xk7p$3bZhl0iA= z@KA5Wwq?=jD<5$>quJ*!?+=j65ao3J`PuF^?kIO_@aVKKb87XBM@^0ch)cD$lB5lnP+J$PFW6`+Ed`VYu<dg7|J^N)mWBg<{T(JA)1b4I_P*E#_igJmUyW6c3(w z3ZMPL7x16|ou9xn&%6fGf!l{K;tQYt9X$QUcj7H?c@w_-PresF{?Gjj=E>GueD(J~ zi_d=g6ZquY-h}r)_ijsp9OjMoidYD*e9#9D0JS;)G?`5>wiM!)xG^+}l;4R~W}!Ld z2rhs>WlMLXFlo%_!f+GMuvv{}>@5vvj7l~>FTPom9hW+Jk-t)Cc3frN1%B|+ux8g# zwa!U`P`azbgtKX?Xj8GXz^9=+`+t=*ayk?WevPFSX%zhAnHrAESYsv9 zY``@~jFAR$(L>q}=S_nELB0Y(~E!cfP_%@ik@-e;w`vpuS$IW{re z1nE}tKo#1%r-mtjGtT?Kc5@#O?%xA+8Xbxt0w>fe-3^a=XZvIp^tO(W?qu-7q^iL1 zLaDiD0K$7OpbG37knBHaH3LvEx{b$f~%-@FskAWe%{tvTz4V0$qY z+SE3f5p__*uG1xbGfE~uwQFkW>#~mrh4NE7~sNaMXU6z6;`%7s|T zF`NRcJNKA@kk{FMuN}vZb5xGi$6;M{H34FMstXu8hL&hQWdOi{i0Kr$$3R9r+Rvf_ zBJtFFTb!X8PEX>@)xRcx@Z!f{o=;w7XtxPGGZdS-{16K>oMJ6DTt0Vn1C0u6!CV{W5?71?2pkMSco*7ZwGGAJ6bcKh%r3^z8LAe zAYIUHYpyh=up>xrtw#+FRcDZm`#1yKF%n6T>1);v!~lcsqxy9%*0A8-03p+r7i(US z5f2om^a%AJ+BAtEXf6eA@T?*jgk>(c&`9xdsNAQi?o5vk-hyYPhbf zqmbv=&vh$B>?pXM4klTvZ`{!zWkw^X&l2)x(Xt9Ugf2Z|Sd}*(XD7fgD0BZ}9c0=6 ze4cF*m2IZWVDi`L{7W&htEUkNpp!*1 zT?t}KTbGf>|0cu>0M0R?Z9uwUyMGT~{@p*oFa63d;G-Y;DCPuiHsD8o^vCd#Z~v3{ z`tx7I_VD*0&wn06--*wB<}>*CFZ?6C?hS9i7ryu+NaF`EvcT>LpbXTAHn z=){!G`J5YwDb7(?FZZFrbLVShpCXI^TrnC;4m5D#sa4;{lK$mEQ2~^d;E`_&P|qUd z2*$=JEh&3mKU8+*XpyKix*xn9+Q?dS5Fv!*Hox2p zoA=UzO-m$x7`uH%A`ciLGvZn58=`p+fF*5~yx0m5Cl)0ia}?;f%@t!e$W^RvLbu4C z19q@4%T6B*AoaSVv64U6zg?W$V^ag(qiRNhWw8!&lExQK-Z5L8*LY2U1E>o6Ry)Wh zUELKCbDSH>oWjv`TpvxEKMJE6*t{3KDfJSQW4)2&Ai@BOR*d<8ss{wktCWbo{ z{B)@l{X~PIm}A<4G#JX(ysKc4OW1aQJE&E1bP3$JW=V3^u&5iT(e6#@k&0gDgx$CV^9zz;$OlK5%JJJvjSU7 zyJm-d__YlYa%&$CkAyNh}x4zd6$kBxF+Y`l$;G4SgDDiANwX$*+$I=x~H z<+9Q3_E#s?2*CykIR!7Rhw*+gI!ZjZ8nP`V39oJ<3)vcDUK!K69TM#v>otmuX1UlQ zk*K}1Jn9;ND*qlqy@jXp<>l`bR0V+gP$82B8Z$|1{rjXq@c0&nIJ)2v_BO0*S?5*& z1wl>qGFxSq1OPQcfUzYPUP&=FZ7NH0F%j{QIX4dI41vW;+qFQmQ4%7jczBpUDv(W7 zAHQljQ(=5@QnBR-j=bD!76dY)6r=+1SS3RN!g(Pu+OTdf69XfG1a@x?(lH@8?-LV( z?cM{NwtGO^z@pN_(+Q;uq;)_Vi|_+F(X<#|blNo_ATFT9zfA`}d4->y0WeIGLLpn) z7y1qb0iYp<+*^FHX$T_~tNNO7$qx}J0RvswHEG2tli=GPUuXxmc`Jtmlf@GF^##O>je533cyO7=p#m0Pk^tBZv+I>{yZGEHp?yT8|Wb|D29$y{_u0 zC@^v%H*>s*w|&%-@-RKk(BA*>8{fcl?|3VI{J-@t;nCN=j7JZFH^1@C`1Wu6HvHBn zejdN^=}+M6U;Hin?iW6f@BE?f!FKNnyyh+M!831oC*J+s`*Cw~gGY~Fv>Gq1Lq;lW zOw+3AMXd?C8=d{QI5=}hg0BYxu=#_y>GUMm^XCMtAWzZdoO8VcJcb6%K}g^XuCH}_ z9Kh`Mds>2>YEs5jZ)09k@tTisnmjrxB#Y6q;G+v#t;&T@p6QllG5y+AK2{}@s9A_r zS~MH>GjFS@VXR4+0T_i4VgSBe(b4v+cb*miB#jJ}OpQ58We9G@^DXH|rdMk~T;a z74Z}uHl=2c8Moy^aOHlY%-bJF2XHGqTLcjG;;8PpX)tW)H5OaYTQI{5c|8{%TI6#X zSEbD(wz%tz^4gOJ5fRQAb?f*Nng$5+tH7&1vu0XE%0~q|6*ku)?~atn&$Tmu zoR43){H`3o4~xd;sf-8#&U0Yu=AGf3P9!lzlZLSm%%Rx#-O}p1%YG4;OEV&=1l9#> z4u#R38F!j=G7WdkS#%5h49c9|gtg`p8eLd6fD!YI?>+TMGz=^Ca5JdEWVEjb>YDS+ zsKXBRj+1mq8O|{gB>q5a)-e@mn>}r+M-71n+1wY_hvIj+hcCP3cYa=d4H&RvL4HKC z6w+^{=(iodgOsNR9X`Mlw!IJg_g4w2PtY+D4nspecSh4M=wYjH& zb;>NDH`4S7Seu6@Yd|MAEFRC3mQ=~h@YjHu2gS`Oq8Jk$JcJF8|01A|jddn34ba*= zLSs%uYw{2%A@gl7p=sPep6NP9@@{L*IrBMOHcc8%I$qdRTr1pU2y?P3?U>a^sB zJ_0qL`TM$Gj-&8n2@j&ob_eFBbzGtc}j3^EHjNG-d4`R`Cbh22D z7hr>>b`A%3+qSv9rxN8r4d!$)!}YXOhSoz)-k#5R-5c)VjjwwPe*3Fm!IMt_pZLU= z@o)WG|6AtbASC!)6nY1)QqD(QQ+~dnSLfj+bF`!^+P$+G zeYW91nXh$u-63Hx6ThIa=lDzz2+CpxNTx1K3emH<`=noUfJ z8&S{R%NaDT8ElSo zjOU0%#5AdFCGqaERH4%}iH0=HhyGw!!%~wJX58)zzkH?RUdDOl%w9UV%6^n*5T~BD z9PkecQ0B83ClJCx@6Je9AP3x0=CGWx$a%M5jkV0nqL;FuM>50LD9{ zGeI&fkDKjL2I|Jj4wp}ATywy&Ck-xaEFv_dAmpC7QP5JV#UrU}2U&NzvvJBac_+>5 z!cm_M$5`%@h+z<{5LYEXCA; zC_LAd7T%oaEFCT49RzQM$*0GFf-kRU$girktz@P9NS_0+J7?*ULT4|U!BE{3l*q%s zQ&g_bT0>mdWj*0cg$k#pCtu<1T^)F;id&`aJM#<)wgxYVnaq)fhSn&MCv*(3O{1dY zv^DIvX56r|p%9#L(>G&8=QDZ(vMk=NyIp45uNG;Bj8R}`ENoCjsamLvh`}`MKU1)E5t8i1;k{4ZEs_bT<0S5JedLt@`U;^+c8oi7dB5wbKoo!RQW_pxYgwi(00c9>+;ViG}`yna!sH+CQVNEBf^8xjrbA58ZxHiXk%2l4s zPX}vV_*~0&sQOr#!NPaMMNS8c0-Jkm*V%cJ0Q^3Gg@+T{feMT-X@H&twJt9=G^^8~ za2oUYo(d?P?^@JS?mIh(P}V;4?6dgzZ+r@W^Z)$6!AHO4qqw&LFFt$`U;Nq&c-LFs zi}$_fJ$UyAzXjj_r~e{!0?&Wr>-fFj{y2X39dE?1|Hj+!M?Ua=^xmGLKlvpBmZI1Q?EI=10uPx( z1&|Iq@@B==y;gXXuq6MPEL81tpkpg1ir}Sy{cZR=TX#&PrI@?;jxt(m<`M`z^+YH? z;b+p%OLN}hylS!*6^9~*;i-HBumY3s@T^5x||Rd&ZYChe8y zW>lwBt-?}L#4sxaklumG{<+TY)v+kB1`m{fUkOjK^LOPsxUIejdk)nwycA5xBTqv&yQj)`)7^Cks~6=OJFcHF9wCMqCY=Z+OTr>M*?PWtP=|GQ^;}&vOMLQF z;^G{289Wb0Q(|K3M6c#^8N>dvAJ%y|S`Vw0onTWO$a>8WXoMTH0|GH)w2pJ-$%w}n zSPdkbXFT2r^)!Y#ho!A}>ud9z383A_GN3YmX(3qF_S-?M@3@GA02Q!hmB0#hsd$GN z5Ud!p78#fpK#7JE01s)@0(^W$41A8TR6++=3S&3M9xF^#7*l2O-aJ_5L2PkQNX)Sh zz8B^Vk{m?^C482TB;SAOrLByFgt^``m%rN&vP@)A7@TA;%ttDS!pM{0(WW;2la6shx#IUYe6aQJ5ZR8L?iF!0b&s9V?J3QCI(8T5OtUO%(G2Cyw& zMaqYTd0WdIFx7HZ=$w{$wo2%s{k<@Ge`Igu#%zgR=|C(RNUX!xoTjRucUKS`Ja{CUcn(Aalzx}4~injjFj@d%*c;CcNgG>pI~lxQ(BxkDM|xp z;32?iVg{1WS~}wd5P69Q5Y}45B@O#C%9-y9uYI#d>4Y+Lm>Wr}V7Uaw@pDGgbams0 zXw(z{5aG`ko-6=|DTflx0~7BTZ$*pjyujA9xHNG&SPWS}Mv>vLmT3sbLK}7+3PM%z2o%2@XrL2U znBjldq8zINbC{RO9Qq~`$4Dd0oE`#QVKO}ua1sMFy!c3=qB!-5hqu6Mp1Q$5 z{ZIcT{15-dzlOi?7ylBT?vL=9fAU{q0{Hze{w}`umEXtLzVr)e@ny-*Ql zN$j^$E_m}@J8V!bZI-o|80RW8q!iR&X^2=tLvgwbKP-lYg(2q}GRl!_8Lv3--Y`y- zci6nt+AV9I6W-HRqMb8;4v`@0F$Bhc2BWeL`)Bgu@(gJigF=#j6-H$;)P8MK(9{gF zwQJ3V6VvD3T#>dKftmWm_gDjRxVIV}(U&-HiWxyItY0u3TXX}u8AVz6hD;57AwZ;k ziTg;KP=?;^G^lI1!HO*JVoo!4A(8hNz9zOu0+017%Bi?Q81hvBHYdn)l5&J&=;+wI z-lV!^p~?ZK9kwPwo57>a=%eVpqu<8-q9M{VQ zppA8TjodkgyP|RKnv)~Ki!soRr*~e%x}Tc(p6{JDe>wv>6+y#!ADF5bb9#dp0I-Nl z6;0=rc$A!^0avHV96^H$>!sP#TVpUqS@TAet=IQTcoy0MolSO{cg&*t#m9fdLhdk< zoI7hC60f9T$j*elW5l=(pS5^*{g=9y*$7G@{!*{<|DVsRFu=f>$Ow6Q1m^s1MK`c0 z6$rKsfO!3ebk*j46zhwkUpziI;vHM?F?ScT0#^GFD#s8LJ|SQfQnt~sXHhg_EH(kA zjafuTtc2gpFf>07CcR>VR6Sfw_+APPWAqZnshNXWKJ@DPnowXO1^8W&D~qBE1F>%L ziu4*9p_cou{7iurvoG(x>Om?8v9S_93An8LwR>VMr?vm9?G6bpxhK(S;5a5b*$sC|F@jth8r7Ky|}*b06EyJ)B3w?b*(9 z?*<&kP(V(;C&nv=YOd4dInrE5Kf5U?X{6&hyAH> z&VbKq1`Qr)FOT_~v8)&@t^y2uQAU5!Gp_M-naVn(D!9vhQh}=@7i_bks~KhoTa-FY zcV8m=PvZVa5p-q4x1dgbosC#W4;AqaEGV?2Nkg0q*ZpFhk@W0qLu2gm>bv;)JX}eZ zAT75d&JF`o4!k@K^v4m_?+bH@GXOxd_$8-WpH0dYk@Uu2;k(F7eW?=M(6J+4pJuAy z>tFvGKKQxzHh;^uYVif@s4+)^@h1yT93Cnu(sb~YV3ktG(ldU6PqFw&_yo!0@QTH ztHw#m^;`aS?d+(7PNi`iU_$_b9XfMLZ~=4A`v-4b31yD!|2nUL)^J@ea1CsC1aAN1zd8&_v7%8~4i{CI9 zh5X5Dj=VPvSiYvs&y1~-7K5#i-m#V2_Ac>+nB3%Z?oukFYY|x=|Kan+YOD-irYWP; zzo6_Gl<7#>lxNhRU1dm3eeuHNF-ksHvuoM4&o#sR(a6KUE3DWuogQs24}z+v6NrI4 zqn{S_8RA)#)dC!KA<`DHUj%T5V6Z1lzN^T1+gE|Gt+4QUcCWMziVl%xB z-q66(GCj&j^L$wJiz{{EthL%OLzzkrfT=zT&_J$^bz(g|GMc| zXm!#C@w~)4uh0Fi>zBXZF~7uuiGM1Bs`@H`EdO8|laV!k!JGrzwxMqwHxKSZx|bku zxhGOb6U@_Oof}xxJx%Fa=DEpxNw*?BWg5%@D+QOtdQH14X~-ht8R2io)&3Us<=`E- zY-UM=kv0#6{tNZY8<-BvbEGy-g<_5oI8N$+?98kwL#P*;K{uv+MX0~@=T+5!Apu;o z@NsZqSP?ig%}^ic4huVKE@+L2<}jDkSg_s#)+s0t+W(^@OxMl9v(iZuXQg2TcF7BT{&woKH`*#UUIq1vHo)^j-+ zZS1B~gw@r6rC|8?6U@ttO3rD8i^RmAe$u1kWpj4Wd<3 zfYn!sper{M1_ad&HSpGNRCY8|0tOGIY7{#i4st3TLN&WOsO@_SEu#^2MU#T$_z8NJ zxXCEQX@xWtf~Xm7%~_eZoq_theV6Rj6S;%ih<_(g_XclpEg--E4QEX{bPV^>bIizE z1R35E7H=y~PC#H4y{9Tpt)sVYx!WS>9i|*f!`8cnoKK*&4UZq6ar@{Io_P9cJbrja zQ^6cN#x6MBdw~5LU&YopJoC)6v6hZoEwW=W6i|a%Rv|<12^CnR>zaSB&49N7Joh{y zF@^eJGG1ApUBSQKpbj!yV~w4p(K%;&E`5C`(+d2$;RU7B7?SB!)w>jQ1z!VjuM1uH z|8)m`>T~Ha&QeS|z}Ar$`wGr!g(#g#WJr6`HvtukT>@s*ps|c+5L(~og9@IBFo=fn zME%@GEOcB6g+xRcd@X80?)Ccmjnlmr0ZE040y_k>(JsSX>-i2t46N1{d2Y;$Z*C5| zeckLj_4^Pht55}Ngm)uw1kf+DVak%gWF(`ULiQY#mzVks@2JQ6WXVTa%L)h-XkrXuEZzL>%eicNS3WAB!p2QPxcsD-$(eJ~30B?TV zJJA4)$G3RfTi=P!;=a;=&T}(-{1)|MOEuBO3ucO82G(p+G$3-nU0D^%)|4?5jA&Fu z=r%#ytTad-l;+sxzF-qX<0$*tVmd zJr^JmHB%fxN?OsV;gmeWn>w%dxiJp=e!wwvlSozK*cNA-<0o}yU;j2-ws%Ix9b{arg-^(6 zr^tbbh990<-0ckcp$dm0rD!qQnG!mK-Z(Z9v5t(&?o)%uS^!1E(b3o zA|ac#j9;M;y*;1rCV-dtyvicHHnmG@W=5yPcQGsyoo|9#D$^V=MvLiPqOCi_P%Glp zy#=I>tU^zKXU{jQoPqoXb`)yfs>%QZJrr6ZF>+kap8-Fhe0o@{MIlkEriEZs2iW2d zm>|Gj{MT_162RJ8FdJZ!XjSH;unSnqYJljmkdUl--7nbM)N%$Z^26&bJ@TKfc|u zj~!#~xM>{?ikq8z0J_zC(H-Kg=;j#MJcNK{Js}yL)P?)2YpS6uhVYztDiPU&&JCdu z&--vIy`~|wdAbz~akoZ8FvIR0XfRR@21JKY5NAtMws04}2E!iRczr41_=`9R$PvGQ)GMJ(CcL=TniufXcZZ&M1o|roI^} zgDaRbo`b{lQD(?!2VM~1KY({CYFvw^<~bRSXI(SR)oZ($E^KEuc&I;Xh_@3*^~Ppo zV$(ECbG6k&1tNW@j1Ms7WC~oN$%PzVij1vy2bg+*0Cq-S9He`Od|zJ9@DlsubBLd% z@X}C1j&isHKqQE&fTt8G| z;HzKxGJf;pzl8huPx$pu{u;jLyS~$SwGPW&_i@?WXXJLX9@227!?v}B457kgkc$g$ zdp13tDLQJo!WCE{$X$#_fnZc}eZ*_Yg%b)u;gv=_zQ>9Z)~N?f0}u;TFRoEZOUA>$ zhhh`R6H#-93{Ft4DZE0}4QaYOD-1zue1jIj;qPdqTau^v+4OVV{9HrC&bGBg%;);z|<}uhl{MK(^GJ5 z?}#l?loyG$F+-fgi&WCxvbp)XsJ}}c&(X-}vkpU$suwSIzSlJQ%kJlIEBvG?pQZxF+W3aI~3q-dr-td$&lU8$7%@VGc$27S#Kg znEQyjl+%qc2<~2Scd?ehS#Xe!7(N2)fu=*cV~^>IhJcq=z7g@U5@u3pCe*8KWiv+J&Dh_ z_!?)gwppgURs?LpoL&lJ4Dfs?tiEaYqRy7-;Xwq_-MHDp;Yf!b9cgep##&WV;$(t) za|9$1cfj$33_z(%aeiJurl~u$eW+eO#AJ_^6Ct-}y(kE*1`<}CDQQ6^=w zW-I{HPxy?33`gRYG4*_VdwWU5ui*144KMUrFdc+XoP)WWf^R37m0C3p;MTZjT=tN%XV)>56c+VqX)6RdX8iC%rlg(Yxd5Www#Icf*Z9r>Fcny9t%gFUD9@#E%{Fad zW%H?4UaARHGwMuLtf1nU7V@uTVxAf&4Pz);8|DcD3m_XPxY;)J(>?6E<8}_5$3WjY zq&JK)v7H)jPA5P6Hq)K6^LhB$svlEhYzp_vX#aj7dQRLy9b;#2+V+_3~9W@PU zT|eX98ZXm}G0<3&WaVd85j8hFlCqqxB6JH0DEuBZMPP!(TML8#|8e%G!M1JJc^LMM zIoCet-uvDI9`JyNh7Uaf0w6%J6Dg6BEZL$dDT*U0j2)?xiYt{;949F`iJf0g;#A7z zvXeNn(nz+;N-7d zfVY7A&N;hTYc^w!@r@oS|ASZYuJrtcz_-^sB)sqp*BAf0E3(wXtYCprMMcW&giTq7 z!rRgsNIh(er39q^WL4X9u!&0XGdF>3U0YUcGy=&4ma$8*1}QK}&$Xx@=VtU_ir|_RHFP1>PUW`ZU`h-PnEi&$mg`_=-UmZBM zvZ-=G?zL)I=_y~y9I-ubwnZV(QQJe?u_brl-q*9amu(S`k(g;G|4im=&wqZ*>4kDm z{k*m`98}{=8dZ5T19$*VXTbxb%Y{b}IS=bTJsK8_w-J)?JB24Gw@pHKs%yFl$B#%Y zH^fM&n{joQ{ah>Z6ke?w{!;2L8-v`1zOYj_GmQDJKB1zvAT&< z_S7hD@GXjF;6X;JYRJ@(2g-B6dE2=Sic>dGsQS6h`_rk+H94Jz)jsjD_2qzdT8j6f z2^t1k8+hUN#^|*#cES{1YH zd6hy7Y7wute(ugwn$3qfFq)X1)A?;mDGj{Ow&#NF8Uc*9c8ywE@VM2(aHsEUpm*iUBfnprzLiKB0->3n`N9ckaZKIUWNq|v_Dv!1m!rKxCWLlr` znMYQfgH}OKK+{{sdZc~8L}zor%+E9S+0smgGrFQQgc4?dTN}(G*fg!+o5kmIf;4Ol zRKQVw3xWZ_l#*oj33><)c;2Z`Oc!{;D70*YOSJ%1K^Q(i0BmW>uf;v~%tG>jZkS-@ z%w>PR7E|4LDs~2!`ZEk$1m74gT)cHB94H7Z$xGCtVVdIPpqgH54pKlc2N#|Njg{q7 zJZugUYZ&(fLw?2I7XYh)_+T19Y1ZiJHI?ae&UWuy3ww&uLLH}gxF|L`@iB$iTYfJ+ zB&w$TF!Z%~^u=Jlrvo^)o+bc6C=F2dY-5&7#&Bd{9KX`Sln^O5=E&{?}{G#$( z+>l`2q1Ng2z?pJ*nLk$mQ4MI$3zP~riae=Ek8_v2og^0}k0M~q5jdc4>?A;gji(r} zUW_pI!PbNUBF{eeEZ+T&ci}JoYyVq(<~Kfqn_eT}bv_EUKO2Yvxx`QjJw=}&wN z{oWI}0r2=MUybkjzJDIy_lN!%-u{-ic;sI?Xj0OaeGq~9!!t1f=wVnP9uP%(-Q0;n zu;^^ZUL(o5_QUgFsi`GY8$cX|nGR_ZE~LiGxiPh#OPcUCAR` zN^*pNfiM|~sc;_Y(Q=x^d5Y$d`Ijv{em^^c)_{6PkgZ1dCJ&=J@((b^!eb2YR+vo@3V-$9GGF|;pc_>!!x&A zE8VVTp3UTS4;$OXx$Wk@|DN3!LDxP!ATVYO($UvvKFTqK=9>}Vpp}-z_X$booD8ZgDj3psz+zh%0pu8jofcKsILpC% zRLeYl&svK`vGw-onV12B@9+0;quZ7}d?{D^+8=q?m4uV~-ms%JBn)$40RxSeLg0tn z8TjPEQ96_AhNaidl*^Le33Z2K7JrN|Bi>V55U*Ot@l&3NpNaSx=D5BLlxJ3F) z*3{Bn$=fY=taVl!@F7Ym(M|HwQW6XEF;Y}R&fH770DoO!k+ z=$0NHXUD#GWWWkE2a00WXc3@^w@y?zh*|9!-TK|nbzn~G3U)fJSmz0t9l8$md9rgk z1?a&}d7DgFw}d{;Ht&rQqT?3x*s}INbXk?ftq@Bv(>#3v;C8A7{J3vs>m81AOE|zl^7! z{t{mH81R|j{TzPm@BbZq`Zqs`TfM@2zU%w({onToFco>p#>u@M&`Ny}XK%z2~(|nB@_DwUTQp%T| zw`=o5(m{iag>p;>pNg%F)nEpjv&jw)V>7&zlZEtTGmbKUAZjkzX-vg^f7VQQ*hgQq zNd18%-&)Yny*!UWB{FzRMG`?t2`kJ_9P2nXYd}OdBpyhPB1(Bc{%FRxoD&j=`!!YJ zT76yN^86fEG!2-5%xp)GGUVx4wCo7 z!7O&aFrIMzghuMEfrnXYq*;w9MgNFc)Mbr+;diH&=au=#+G^fJY0WM(6!A`t<@{_F zNC~=A5b_u%X&!YO(W0286o5@?IV@K9P6Wqs#O>(@Q%?{txVw%9MZ?vdYn;|2##{lJ zR$pU1VT^&Lbu4|s0zl{RRPpIvcaO%WG6>?hpn}}LkgKdiMa-nZDjcK$|-!roHI>$Lq25GN8@7 z0M5i*3eA$Su^MK0&$N9le1V?gZ8RK)yvGEow7^#?8(7=#D z5y1M*^ALFGuY(RN5GC;Zj1c$$pfVk#QXQ{LECFKqSp&f?I2STJjg7@juOq}GWTatG zFf06T157mLh=|R>l|Bs1r-O&g&ei9FO!FwCbcT|k%OZK)izpWHz_>)mH2FOdKP;uS zWHqjE{tjNaMgb^@xjkR{Va)SuU6+_`CG0G;W}|6ndDv$Q#=_7t5vwFW#)NiXObiF_ zRxnm|dJPnXU?5DP<CF@mZ(}=hL{6ivnbW%0 zQjo|4x98|&7o+O^FNa;q+9MTc-$rLhGxl%_O+D)83XtYNeVQe^JMAW(o0uvscSB&)pKyX}-7~_OT?mZe_vXf4%^MvK<8q3nK9?iSfx&q?HPCs8$fz=CP_14hC z>QpZWeqmLRD0GF*TPk}9%Yzx>(&*|YPh5wZ_AS}020 z7;!PLK8`&Iv8D0fT_kP*6+uHP)BJmXiC2Z)7;1x@HOSsMaPXq;Z%vPco32#1>1V>n zCMe)la-NHWLq^9jzxga{LXLU=l=1J;%)DGFW&vG_uvgrJqeM9xhhoFP`)Em zlVH*5$>^X67@7G@)q+5GD3j##IywQGnM=8oGA8==X0KAW7>UW!(Nznwz>SQkSEb#oiqzq zdhFxfKczl@78D&_4R?_){63iyvo$DCMG2K;E#n@eZbq zQJL;48|MM{Cg$NhuuHEA}xIa#fsiQxupYw}R==K!R!%mKjYr#Y-X#bLR|1h9_PoxE2#t-xGwa9od= zNM+gX+o27(Sx=abaoltcg=8%X$`97?Q8P|%q8eN$g4Qmi3#kr`#g~CFLNaPRKoJo1 zi|CY-=OYa~iBBdFH407FqDDL;jbno_RHH6kq=Kky)|u;b>DuT5#TanOshgjp)8E!k zbi_FW>8J@XxkijS^s`(og*@|uNCni3dI>q_e1Qfw>dqb|Gj+w%9bb7=dGiP~ zg}O7{_kqJUqIKN|5h-VNVXNSA%rot51|Ntt@f9>C3F$#>Y=Y_ZTh^MwUxuAW>+%5| zY7PoXRdFAMH6zL_HsbN4`F2B25tr67k~JcDkbO*Q#c|t`WKH2=d+MGjX_zV) z)AAoN>6LAiCPpyOh(f7HP}%@J&tq6F9t&;)Np|_S##px(8I;2b&M5???@iE%#sP$T z{bj0${3DrcejK!(3-bAUps-=yyX(a)gd4^*>#SWZR{jH)W)RI3co#m6;3C&|qtx+V z9q)b&NURmqtQyX)$X73SC_LZVmuaqf%s?_Kpsvl-X@WqEMp{!abz36@4`C#zYi^jU zL@(?I4}=FN^ok9WCZk4n>=6ycwE$8;t-o5pCp=0(%&XCw0S9S|TR;cabzqJgJbL%q zi{09}6##Q~Tf1=tltr-=inP#oRY1+suDt~iwA3RP!Wm%ZH7OiyiZjAEyV(4lf?}e1 zVav_F`xgc#2Bx*`7RBtWpHe`#AlF>Zpog9OCIRBWHm6<~kJZ+Lf~pZnDR^?v&WO~< zo1o;v2SOG+?7Ye5z2Gg%!nO`iq~yc~udwgSZVpB>9uKsU2j^o*9$|FC2t*)x`lS(5 zA&ATZKa+HRD9j)e21*B?srcEa8UdE?O;=N5L@mvsA@|rmKOyDpb^nexFSao_-zibb zp8Qx;s=4aRWm3=K` z!G?zm9SUZgj%ZN;s-A+V!;QfX`sOqTivz$p6>|`vn|!XI8@?C4cdYBg;rb5V@XhbV zBQJXb&piOX?HzB$|KeZ&H}P%X{Rgl<|3&=7Km1$xk$?E3XaJu6((mDPJ8=EzD{=kE zBN(TNb*#32)6y_$$5Ko;CK(MgWmAEuvtYHd;EHeoT(Q^^G{ne z6wy_**2cajTbr=dm#8BUuUQdm8YdXFzzjrD37(MOGy`bU5-C~si2RM*q18=3;}iiR%-6-VbzpuCLZes0a_Zl zX0zhE0HDo^=L!%;nvEe=iNu^Y?yw&fsqXwRCscMw9Fc48mhtQD*dscFyhp987aSu+ zFL<$YGu`PGU9W~j8Ot!>ruk);i_*03yB(XN|OEORUv zW3l&iVnVPwre3@N?CE%mRR@;k5NQ||5HLM*-<_|*I#>uS(3D1!?Sj#8i?X(LZ%81@ zdpVtxMuC)%5!F)eZ8D4oH{*LE#KIm)Zkro=!A8R=JSn3s&&OGnTRyw~tnZ&ko)Aws z-Yu-zzJS2w!Xn*D!z%(y&^b@~-|H|OL&ab9b1$afY;+^O=Jf4iVT+bFXN+ft2APyK zyx$%(k+%uF7G&TO-_zD`JRWg6Db~~K8mqz>leq4X-RzPcr|Z5jeG!Zpv3_$vmJ0x2 zDWWhutpG%LCV4-{HYj@sPa%TW6CyUyl#9}26Qw9fT2e0-ElsUewz$Df0Ko!gAsFv) zj5|B#K(wMKKdG|iQq&fYuo;23H2jTR-*^#TRj~mi1F)BqNb{o5hgVriz|4tOYC zri3sjPl6G%s8|r17gY>{h|?p(2l-(;U`C1*A_8N;kQO1v^u~z-(c&4TS#iebck#B? zVGX20G8HU3lxtvv<3WdahrmR5>$hIu1jW>slMk>)j7PkvLS&Ya#%jk)d&Qj}+v(~t z-WHZbDW6jZM(!!%7x%)mfaZt2_Ao1WWoTOltKku_Irw;J)UbRtuvN7gGX`{;0xZqq zt6AqqDovbo>5YI3*5}OGJPHF%tR}$p*zEw7%_{0kSy_nwIITxC>8Z#J1?E6+4b9u#k!jkQMb^Ghi%_7ao$C-ZLeHkcloC;oR!Kn* z8LkX-G3TCK&#wYBB?O!U57Le3h0h^DhZY{=X$*V~Ky?2AG`$Wa#Rdm7D2g*TE$81_ z+n(t{Zx<)^(3#WgZtr)rG#~_`%6q7Tf6hD4Zq_H5UehD!f5Qv6!(u3Vwq&j+=h7?) zLBw;B8EuxpQ7I7pTO#6x0t2g;oS2fNe zSEH^Kc{OPl;g$5A_SaV^)=@mqalRROsM)2YvC!?rbI(14Z-3{v;xGJ9|2O#Z?|l{% zz|H9vKl6`%1Rws@pT(U8_}p*(Hoo-s@5T|p%U=0Pyz4u^7w`Ly@4|cE^B$azM?e+5 z3+7<*frC^7AfW&UFBC~{pcngWy^%O<^~!#QS4|3rP)73P9_J@>Yqgw1oNFqvx}9I;6I8b2b`Mb) z&1jRnKB0h?Nn!K()N1m~Z9M2MhpY0$HRPAsk%-%5>yu|Py~-v4ww=^+Em9_++{?S6 zNy}oxA;kf9wF_KRR&lG0X7tTPuc^c`SmZxbJmDZSIU+3SVZpKpjwj=z-6yen(QkXEcNF3r zr$@4gb!zLa``k>f3Y{Vsv9`_f%z!6bDU;pa{PltfA=^Ef%C4~%z06L2d9KYOnwVXJ zm(|npwe*7OxZUdnn=O~~dqG?o=lcaS#Ye&*Td*5!e&=+SSn`swsI`VM#t!pbzUqAH zQuAjuSnZ9mXS!0z9EAoB4L3o3YV3!}U1AEL8qLY4Aiz2m-RBQg=t$IKYhp&o=Gh50 zl(~@w0HYC`I|^WUR?50-@>;ulY5aiX(*pG~3NN+F_l@QZ)45zJyoHF!ZmB`n%rcm> zbTsn+3d8~@76xJ@{Gy?*%D3&FCThKeTfg4x8;*dfB=xX^6rbe*;rGN2X9A76nOwGc zbnxk6v~X`xZ*6-BJ-o{Jnr)^+GEG+$ssju`Ds*Q#ZBa#?o+!H*$}NC-v_;&(Xt~!@ zfed|gi+W=SxUGRzfp@EOtBXyKpIKI;K(F#pI?f;}17E0uC^_egS&Z#xjFFbaXSke& zBmh9&Z>~TBj*Zev=Bb70mh;JwMvu?(v8ji^x zXVW&AIi}8hnMUSb=1ank_p~SW+;>j_V~90rhU>%KaI|4(nY@Lx+)^PJ?wQ>9h+=bj zKD_X417>M2Yy2d=+tnI>ey(Kq&p<`Kmp?ba2Wb?^<#F(A%NSj3@@$+xFO;wskw_vQ zy;5G&lB$|Zdp@_OMlTGB7r9%@M#6(j9;3~8>H-M^?y`7Yh1UFg#++Hx%`$pD z+yqu9>TrFHFF*5DeB=W^jnjjtaeWu~)Tcj%pZ?MR0yhsP=Jk{K*7tq~{<%N=2hadK z@v0|rb@vYDJmTpu{T}+E<|pYy67hiF*3jDlXjg{m=PlOp zJdURqa9U58Zls^%b` zYT4VeK9Z}$6Oz2v_ZouJI`H83*6%%IJbOb>_D|U0Lcx=vz!?IxU7ISR!K~_?SF^7Z)i8`y=pzU6&RzgI3;qjR= zo4Wun-0QL`XjptNDBAYMIXjKw@|DwJtyh8=nFR2anj0s!RNvL0v?7bBh0SS$$n-Xbk%ey{YD#zn>=3wJtd-9>qbaP|53%Qh%eF*h z!A&8_#T7%V?BCHaBl?C47v%*7{k~nQLTC-*3*>>Qwli9!D-YNkLV_`Qnv*ex;d>}? zzSEY-@}2fvk{zv@kzygVd~Yp;U&}(wi=T_{3Jk)ZF*HT>uy4Na2eiX0`on~_S-I1@ z)ngrv*$&QA5uSAx6XjxC)CT;3I^%84TMD`W6-Ap95{+t!9@R&cQ82*tcD*gE*)))7 z0TVXNt-z#3wn4I$q9BYncDq#QDX0~Qh>Qtfcmb^GT@xA=E7*9abgSPYf-VCyJh9fw zm>$X2rqsXcauN4nJN9l3q-l|SLT27oM$k1ag zy)d)T#VQk-7Y?ASzbCM;fA#9^d+&iCUX`c`Vgjttn_3qMh?qAmG*N_Hv=moA)Ya~x z-qN(~_euTctiK`qNu^+6gfx+brpcaVl~uDelaoQQG!W3_0mFT*~ZqqzA2ylxvmIBk!v?-5_bgcOY_iSHIFXb7~}N~ zx)ZaCU|rV$pmyfGxL(}T&oO|<9(@cy|KX3}Z~pLqj6eAUKY)(^A*5g9HE(?v-tn&Q zz}+WahgaVlc=J=QLj&;n&wd6U{<;4KcMbzjz2>!e^3{I`5R2sFfE8v;Ck=2cIwxB1 zMt@MqOnJz?6fxepglEN!U#w%JZn&Xr7pEAbW9J)^9V4waS#!W#&S|SeSv~d5nT2~3 zixDRGTEGyoj545srYZ%#_Q8+p=Y1!<+ni1_f(Cm+@)u{H)NjH-!t{nvVtl>a;$ZSX z%rF4=w{=(zCAa4XpBcNdiV9HXm}{U+@kkPCaRW_B*!_S>_m;BEfHL zEm(&{V}#*MCNJ{GO_w>Ie46Q=R8nCCL8JLT(qT_0D7+tbJfc2FHV$CgUXSwwe`ZE- zvhLypTT5u&GqC&U0K0D4S$8j(8o%lS%%M1~1F93J^%h+oLlbxUh&9e=eZf54w*tGk z!a4?IO>a9CSarprH|Xj_CJo&qw$}+9+5&07n1;s>&Bp^bjQK|Ypq!sh&g7;6_tqGjeP(jg0Nnf3>14K!lK!RawN6OyNTIwV=+RK@AIqIK$Broiu=ICbpXIVk8D+Z&iJOw`uC=tLQP zldS6O8M<}AU3&hUql|_7is&@VNUznGp`+onp0EygaM~)xb69gT5oU?^bG;wZFlB@+ zG@hfD)S~vHVQn9aGq8$91=fJXu)cx@RjsLQN_moQE+sQ^o-08E2`a7j9V1vx2QXT9 zP)?jC&I4UvXdPkvrJR*!^;x<8dm2Qn)l0ixk^!d7VncXmEyE~qVOzPDTspv`O1K#6 zt!YcLtWH=*g-*a^IpL{qxKM2ks?$T4jc77FiQCkm-YZykU@8uG`JGlo1mIb=V%F@podEtmIWXUF|hGVhD_Qpa(#`UbaP106)OY-so=U^Trd75(#!qw zr%gF6u-s2Z!Gz3|1e*L}WnSA5zsqEL$#CPdK`k1*FY?EIOEIFwRz=25F`|CI1eyTQ zecu?0YOJWvIgKN5K`k8^p|BW}Q8C3uuw|z9T7zf@4G-~n0!Dt;77TODh%(ylrGp14 zjKG;{YYju)pD`VL!5FM*3mRw4l2xyi(3cR>SBE>8 z=on*#o(2}_=zTFynnw&BM6h^qOYT%}2?#{YlRvws!(bv#Uy_4!WcBS82a5E{yXU?d zCPz63Ucg%1^IQP}m68DS4k#JDS5_uZl+%~(`HbJIHwgelEkYxC3y_^>0_GN~tCz1A z?@7ZF-_`OtKDH{{;Dr1wzAn>^G-8S;`78}R!M`-c`*uBz*3Ho2Ve^%yCBfaE?e2l4@f`re zWK=b}?4QdTUEKHPAergKQ0tDCK*`3zv@PaYlcqP`#&Wu<2T!4RaC3vFUh`Uf&ma94 z@W?Bl#O*Ecx>rAmKk?uFAK>=+XYsLL{{TMvyPv}S=by#*{jooQ7oNWlv};UUJ5L2Vd~0M#+n&Z-xeH)y2L8yhZ4MqofJ~Vzdm=}6I zy9b?xn#30PMMEB{(#Zpswt{$VLn|6Q&EGmox6`z^8)BRjtFGv+d)K4|f`N4&fw4jq zxIL{F4b=}g92Vmfb6{CIFuhw*GyF$`i8VEFr={;LotjM=Fa@Il*6k%}Jmq?xIY!EW za#S3P8W9>c^!=(hJ7j9?FO0{qtDR%WDHgG;4OoxX-j^jI#xTPan&Hx^ibVnGDc1u7 z$##z~=`I&_1fK__@TeW{s0Sj_jr?m6Pq$TbiO?v+Tel`STrHvdY#zOT&TfpM<6Zd9 znZsm;RNsA85jEH@pg&a>j=hYtw#K432!s!gv1(+GVQ}I=b2to)0jE=olq4QChHUs^ z1`lxA0*_veMKbiFLYX4L11$rkj2l4KO=&@c*0mL0=$_9rUOBa-JvqM%%}?O`x?}19 z>zU!Cd#iw8LgzeP+@P=H`i2cK0#o>e+Mh?5 zga?hp;DW3Plcp4ej#;%nGm6FlnyXx%o4XP4v07ZEEwHZ&Uw!>yR`31iaxRxId#a7i z{g+U7K&sE0;)UjPyDpp2lLb~9zAzsDzQWq>O$YTJ2L>Pkw;6@q1k9(j3G4EtXp2I1 z7ttfrm=7+kb0V)lB zX_%+!kv9!Z8oD&BQ*m5Z+`9`XZZS@`fHW*u3+_Mv0CTe4ycvSn3Z9DFhAv`95C|jE z5%L{hPL}VogO&!W9!iewZFUHOE7#JHmT~O%87_QyMk30~-XE@@W??)Hfn&o?AX<%? zl~*N@K+sAxC>X%3XS9G9j@juqJTEGtpz*Wm?at>`9vw17_cU*BOh}|iilmp6^ND>E z(mRy}8Z60ED#Kt2EtT2$cOuPT|4YRP-2I!1^W12&g zG$soRE!wxBid|}`#`A2Sa=aW7BRv#Ig)|D)ArzIIBLYYVnD#HTn6OkOV}JR6(-^6G z&MV8K9O!S~54ur8m6RPWH~^(nV9SG8YzLf%rNC#>cHjT`_%ey8{#t#Ph4;@oQ+ z&kN5#gSWrwE%@*JxBh$hHrcfJ$r?E}Z@Ha2>J?JVW|R5cWDb&s5Zu?A*E@q4WiU;;8`MqW7m z<+<|p@mgMaK8RzGfT03u6z9ff|LCb@dJ>n?@r)O&w`e7fgS8x(=9L|P9(JW6If;}z zGaJBSxIj6ZK>14utry;PSdte17>O99UJ!ZLny+sTLU%h1m;lu3M53Gv@|h8w2H-p2+~zH3-JD z#a)aFtZaJYP~|gOA5a2O=b9^7h4@~S3; z5M7z<-m>|+IZV}Mr(?k;-h{$1oQi~jb}k*`7U&+Kay$(zhlV?M4p`SC#+AP=0Ei`h!Hpf8!E5PU%f7)F z!Gr0jRmZ%gF4bO%wDU-3gO8zLo}h2a815N8=fSVYE6o6HyvQBZJn!WUKkVXr#5Ma9 zoOF-mp&;Ht7z{)#Wom-CY+JfExh!a%vTA!Sz8?K!KbQUQ8~yCkeN?fXpaR&{bPVIf z`FJipJI-1~OYuwMDq;>s^EX*G`gza?jPC4v5_D+C&?7``m_Cjc=hMvSi+|G{rBDxy z5OX}hBifu7iD-oSZJdMw5Rv#-8O68V55xR!a4UdIb)weA3M>md13?IK&a*CEMe+a{ z@BR&6FFARMOgpKFMgjrAnj#4SJ0aS13wKmoW>g9y-_yKLMz?0HDV{RT%~3CgYKoGz zch(6frnPM1bf(Rr671w;81mP&7BUT6AQ7gGRSF?{%0%=O7kc7te$4e#6?kD7z|%xR z0hnxS+~QGudYn^rPtrpUA-{nb`y~P@47VU5$t zpjd=!-I5uWY#%R~lnNz~FXG0?q$`1S z8Djxku6c7=N)L3(F$y4S=_t*Q62X!pBDXf5uRC4&aYU5Sx!`8%>2Bjf7gecMRge12 zQ$gMmvO}!Si!?FsS`+l8Lz@4S5@E`8=Atm#SvHyc*rSi*6TkT>{N2C)mvOrP1zaD1 zXTSV;{N_hLfK~3|6|Z>;fAUZL5dNhf`WJB;z;bnk=fC(nc=k)5!Dm1FJ6H}KONT9% zgR9<~6;+4Tmg}u~^o!vaD&YvzS~BV=q3RQ`c{>lqh^7KI$C6sDj&VG-bWT{Fy$rCZ zc+$8At<)`ybW`WoOb^R;<=b;i18QxZsj85fbwOsqF8QE_)T5@Q;!+kl&$d_sPurOJ zeTB;$=ff4z@B^7VQTRZxD5tqyi`OY;a2?Pve#LivOnW6oRlRPLfy}5CBNAy(>;%MG zP;uHa)EeX`flJD`&OH3~NBLV~rqOQ##v6xtyLzRUIp@!G@d=Y7>f@*3R;Hpex>dGw zue@1(j{{%$9`bV=*;Ver4p}lYU6T8x09c&5?CcinZFV;XhHw&Q$V$?u&yDhCqr19i z+nQL|`6=p0Dq|X4E2D~BDI|X)kXOJJKbk!#cgYV;QSYRSX-1mX7w8-~4a1SAlXZGq z2dplJn8Rrtk5<4`tOi~aU}-iFYehX{Y9bzWk_k)*-Xr2^HdK-~aI@f5iT2jvc2u=g zq#b==syW{L-ho^EJ8YW`Fm(x0yHBJ;)N?k3^oHPvd+W&_GgMmH^Q=pTkBxyOR%YXt zvpwTJd8biEF=oN$d>%2_!;BeUOMkiK^r+nKqCUjW5SM)oUFae|+kK}RT8Z~V;E-0z zP{%qjG}lfWH_pl>Xn7A2*pN;*i&UAILj%84y0B(Y7zi=X__*N%``p9|=y34l8kPb; z3#^&z-L`Zk?)>t@(8N=Ud0d=k)p-raA6;SH!fl-!#cIO|Fvr-Y&0bh<0W8f*0mTJ^ z{W`C2I0A-DNFqiw87Evh(O_(d#UvvLtN1jf--=c-oRK}sJ35#q0Zi?vGzAZNBWM%N zaB*JZgfpi`#15hSbS6s69MD0pxfP!>crg9&+Ws2IG2=@z$gs!>1ITMl4SEVqxtC^b z?(8wWoWvf2k-$!jlNp_a!G?CkB#uiA?UJQ*>*3xi^_gS1A^@mI4~S+DBnw$f0t5&) zcHWoryp55djBW2luk+ZB_wqFi%^L9ZHER6*5-?Jq7Ku_^VtaeKIcFRwpm{MT1xj&N zDG4&u*G+Z8V?(TUI+9rhyW#w2bDpN|DV#*g-r>Ma)0fHCZKtNmipM5=&F$2uemZk|pnPAff!io+uA?L43#u1twg z;Lw*)V4K$%VAIn%JzmJ`9w^U@=jqneIA6dxy9K5D7~&n!;+^MCqgXN(Rf&RIJ;g?$ zwTM=bjB4QhnG-DzgfX`?)`PaVPY5O2FqqLH>(JOOvY?~_B_+qs+AJZV0f2o>QM^@c zgsO?4n&#*Dn<`stVs~jA2_=A14Yh$$4P?TXPNpE0o93xGL?;jRb!P?}(|bDSO$9Kz;1B)bKZ<)Ve*&+1&6D`vZ~JyU@rnaffzST-Z{d?4 z{RKSn+E?J4-}zoV^}5#qFpsFfc)b-5p3rr8zPJ;p%i=kQ@%s|_$~4&4eY`A;9A`@M zF!1zghBGIEotAdw;1IKUYEd==w7ruIKBx|5?qnuj+o9+~nkzIk&r}0PWsgRpt zB|Dlq%8pjuL4zEcYSs!;a$`Q}{JJE|N6gaCOJOk`tRXf1{HLj}c~I%_`$_;N@lGvy zMI8yJL2>+)bz1C+6EsNLLNkTKQhVG0>jp>Lc3X*X*%VyE-@9W3VqQjvcy#^txJg+F zV&0$?pT8z26O;%WraZ>hY_TBM-;{fR5g^0{?DS0A;~BrAND(P1G471wp81&fE)(%R zA>oj=?0#7oAHL^FNBMbG$4jbl#v}$6AA}#!&G&4g-n9}wQAjq?U_6gV8o%I*n;RaN zWwClyBDgvjZ&**(gkb8x&GCf3bS$k|$4BR5~YdvT{F(KNc zn7O4QxmO3WzBuz!7z;rH^3=ZcRpUqay&)!{?D0q>A`A@1N|MxRUXT{`cV-tz^a zCV19JONxL=D>7i<-4!`%C+Z$O z7|>Fuz$Rjg=H=QO&xKDoJuxU2I%5LXHNliP69R6a!3@hV%BVQb^valmJVgL8gN_wh zqpE6iqOMM3hjSMLKCT2bH&Ae_P2l-(Ef|kD01trTVN|`@lhun^spqVTAo7J{9XT25 z&c(}7d(+o>^0hkuBpk{q-Mp7fZVBbLdDPGp2NF|Fz{Eokg_HMwjy6*0}fXm*VlJ2#X2I1M}CcATyD$}T^2ezL??Q< zT zM10Oxt@(4OPEW~m`YZcW`5Kppw{TqS5nKLE-A1;sz@OGTa~w?|y`za4Y`R(`ON^VJ z6S(1(vH_U_rcQmk)HlwTMn3*NUD#Le`K5K-07(%qfXP%|%blKDgnQXKA#Jc&DtS@x zL*viuEb}O1xCChD1V+@SFlC6{zt~E04`l+|!*=K-ndc)iB1AqL_;P%-s`DCjl`9ePgDoU*?M{8gf=rwv#v>{X6_un*HzIb`5bWx zG0bKaKpt1eM7d>U3zURY_+v33>NMw}Ym<2%cy6qcmrc*`JlAv%_pCPs)ErT#F|0te zxNg&c>%(G-EM|0Q?M*P(6LcMr-i^mj!;cdaed*aqmCIXYDUcbtXpgd|DLSc-=ke=dzD z*TTk8mWVmJqn;7x@N-Z47I-A3Kq9+@ z4nK2RahX_SP*mLl&G%aHy>u>l9I9kZ8wD~FcQ(R2Pgk-KWn$Erx9?f=#vGAlqSR~8 z`Cqf3DwH&*Ash%LAlBBGjHpeuR%fk6QNIxGAOlk8 zttLX6SJSW7>+MgEDo~hu;6;9#-#61v0AQktr&Z{H$cUS%s(D78w}+T)s zaohVpjA`tRM|^G1Y%Vd}I@ehwkiA|~kS)D8Y<~g6J#=4TGiV(LN@nPr!|B% z*cph!*^uw?9Y$h|LeC`vK|Nnl%J~Sa>rqp6od`H8xVFROYvx8l7$6!@$e3u;0c91) zGV`+(41O-=m{^u>t?JZLc+e)sIN|yLJoeZfD_}Z1ZchWZ#}lv~(WWU;%)?&QyfD%m z#+;byRp%Y7O)V|Fl`OdDJT^c-HX-T5^F1xiNK;vHK_<}^A{FPnVWEq%zr1*+q{KFtQ&sg)V@CZrU!lO6 z02pb3JV|Ym#Ffoi-~=CNIC`)qSw zLnBS{#iV!2p^M;5$ZJiO&bdO|xWI}^=0Na!?!bq5F&l-=8wl~W0nXd8snd^rm^s)4 zsfsP&CIz=JG}#1?HxKZ-S3Qp3|J~n>!{H9@Tmzr}>}T*F{lES_{Km(A1v-!T=x==v zUpn0d0KD<7Z^ct@_-4HJ^>4zv-~B#})6ta&g*Nr*U5+(*=9tEhIy~iqO63j|^AM%% zG9KPKJnfP5-kzJX8=JNA20eA-`LX(UJj2Gqz+Yx}q+QqPPUHsfiomTmJ#VY%& z8XjdTFtr%X%jbmqn30<_QiqYGe7<7%-4pG3#hA6Zs8bYK%z00!719N$rz`PJBd~C=O1{#?3@R2a zEQ!lNxnqWz=Km8a&?>=K0S{g%w0y>Q(ktmH&9+V!;K0HrIaZ@l$I8_AWTsOIiQ^PB zWQzxd08n=vl~N8HWAtW!+eO~{ja=Wb0fvvt@O-n%#vvp}PpMUi&hit7S^+9d6Jm3& zXvo5cMkh=gn(mBr5YdJc$jFg+4mrClByoEPqZ?n$qcfl$nof^5V9W&mM3IK(ea}VG zItB;oifN(A=&+WYo?3#D;{>clnD`=-2{DY>^V2N%>;Q4gI2SnqZDrCl9BqliZojn@ z+w;FZ!_1Zmx^bZ;2saO4kBagXmMRTs-E;h29N5&?Ud*cd;#TPGed~Pp;D7&p)%2uS za0(=SMqyXLqh;*fz_$S&B#VwR>I1B{&YLR0%H1tNn?iv${An3AilnoSScV;Vwl%cQKbB@VR`c>S zugwxx=iSBUY>X+6&Z>f3!S)_J*KP|IHG}-SnX`pFOvDQi`*X;N1;C?^y#l}XiBI7F z_HX|i_}$g$(9k`#Zs@eu z^HkdFc?iax<6dDAwjApgllXU#>v(pN?x2JaGEBS`*IyTbJ-LzhwCH5&$-ivTFSRY-nxzvdtke)suB6becp5`bYAi>ye&a2 zTiqdz6yjDhe@>$U4lFTN?(@TOeQXlWBh~iUeXp0Co&QUhQrB?=qUqd_&3E0@u)EPE zz2RZH%l`f3?PMQ`SE~M&N1LQToNMwR99*u0i_pkzT-k2<5$xMPbelV?K1ilE)Rf$#WR>RS;azF1EJp z!5>sLvN3FYtoxYGg)q)bjV7-)ZXEQ!cF5yotcrK zra-57?z(k+F$O3OB3uwmuWRE4wx)+xro6X8#GwSpfJ#ZX{}u>o@GFg z()xVf_QLkL3RvN$0CAE)PSwG88b9h9q2B$75cDp)u8nplT111H!se|s0~&bXW`$lB}jJzDnkq#+R~vbptHO6-r7iy zsW_gd-2;kc0j{sE{45`U!?oAW891Fzn5Ppkr`5NBb&iUPC%!jr0*i=wrCSRxSt>ny z+7U|+5t#CV5|8T!nbrZ7N|h-T_C^T+1~3%p(xP?>Jq9KOe5{ZR3D+{NLc44Y%9f+z z_Z01e1bV?T|3(8BH{aG#ZMr0I_hoU z=d?T9bV`JxMB!zc185L7JXhqwgd13WU#Gtx12(x9-`}x&`%Z(_$Ob;DC@hdIA8(Bz z$@bw6;2>7;wlyovOYG2#GB=$uneh+7bDIdRuI}KAUwRt9^SLi!tOGX_c3H*Hzl^HFJw$Z%5^XoX16;mLY$iLZkb3&p0=0U$0>xukwa#$#cUyZ?sL2pGoKzwG-R$F*jAnPzG#~ z3~Zc*&vMVJamEZ&0_|p)TC`cJSFED*0~%}8`Jewo8Iu4Jbry7^2tJa}6=dE_3uSxy zKdVm&0A62Gfj$$1r?PC3x*`(k*!E0`sDWkaklxHWDigP-TMQ_g94yrng4+QcS77Oj zecs1(b9=(+G$76DxtJaxp*vuzVIJuS_@Fj!cr-l6*xb5F9jdh=MY%uI&PoBY_A(uR z&OTHYE*m3YjUy%`@|-z#AL{hd8gQK2ymrc%3OYDx$T7#28%9{< zK`Z}5u84X?O|^f3y6stw_8BRoG$Iwcp6~k_<2r89|^_g+o zMhYvIx%aR!$_4iM#$sy@DxI^7uw3di>t1+vwE))i8q<>{-eCeH#z|5opt4Ou%%3bi zYrq8Ai%IwCgb5^{r1wbYC^caalo}gf1#Vg2y)wBsd|dKo>_cHM?ysJEp9IYj{fu?X zc9^Lc?ycC>7_w(_(`y6l8OtTBZ}gvk2qUsD`m=YS=8}ij=43d0D4BxMY^F%;dy6sF@Hm5^ zgMLO@bfth7{&kP5Iv|d9;by6|%Lc&iT-L3H-e2$8;2=%VgYky9x6nfTj^rQJ&_=*5 z9toC356Rbx@GGJ35#H_RHO>R}^veCnw1DY~RD*cljq}=~coQ-W(u{)-GwhgDZLOI? zLplx;Xe3K+M}QhCB=nk~tP#%vBY4;1n`gibCKk;9pH(;#5o4)nC@O^*uPvNa^ zemidNKk)iE2JRs4Q!5dorkqeFx8QeM=$O$G!ZYDO*6I&7=Wn()D!#ssjJXFIJc^`8 z0BST8cE7r}Ko;dy?h8D$-6CaZoQIFSo}K`GtiE5& z)Bn+^MEd44_SlZU2lo<*m#w2Prs zSrV{E%lWWX5%50VghNfiwDjs>t=n~C9TUspfCk0T6WXC+Vql#LX$uZl*O;prZqR`_ z2Mz*sOdNWL>WrFtmeOsqESxE7JSS5xecu8PvuJ#!EaxQxNSBDX-C$>s&$|o|1pk-> z0Ss;MB`1DiAy??MdYnaXQtlzwKNMfhp}Avht9&OtJ`bxUgz5ws&>C|k3xb) zt~!#JO_PVMQKlfyRq*ENjRMfY5)J}lf#xea5x85)U+w*vvuB@>wkeC6XQx<{EWplw zhK+dU)y}^(kMgsK^vptY`t<1g=pE;!30QMBVHBTX&x`JZ!r+b6xA*(Ixg?@6<^0XB z@A`(jAaO9Y$~BpyF+AIO%d@w|vDURcm2jU-fHCKbq7y9Y)4MAK1kLb+17{N9&2tNG zXIP(xasd)91r!v+xqe2I=3%4<%$T)xxGAZk4vu`$wR#SiOJ5ZcG7fP9>Im2(#vD`y zO$Z3#Cm;nd<%cOhUdg{TZv|JMgA0F?Ar{7X;q!d$lDA_cY|Ao`soQ0HX5N}D_}MZS z$?DjW-w)8Y8yCkz@X!6&8;2^hQr*sF|NDOKW(4v*lS=}j2>Hz_W+-Nwy0?)#y#K~% zC_MCvx}f$QIs0B3PyGHoC^0r{l(l@Gn}ywmDJXl<1iI3AI)YPA|0#A&aNr zt>*df-3uKka63=9xw(NX*T#&-urqLISgx-jH@BE7=&hS_<~?$`PwbX6RjnGiGJQiW z5X7# zCyu)U0YH&kOb@X|2CRkNa=w)UjmM$@5?clhzOlKlpV@8w4L^1+a0&V&I)K7Ew846Q zb1hhiwrdcpkL5+=QU&x}Z7KzDkM=a6IQaN37-A7Bm;x5Qh9C@%QxT!@fGHRR`s|>> zfj|((DFv4($1CWl{?GdCK)kBaG83 z3-v<`x04~6;suX-82`uXSa_#?nS`M|H^Fa5j!dp!5_S1{!smX|$=H@x{< z003`)*Sqnq0INV$zxVwebt-aRIR$)QF-0Lb|tZ$!i^gK>Z?-Omq zck87B^eNEBT2dP4gXdRV=8@+Z-H;bKeGp%WME4X9dy4S@5#Z#;#Riz-ATXLCW}>X9 zWAvYl9+_%}pIU=%GbesSylYYXDVv{Zs45QlQbO9Y&IegVMahhSsegr=p8z-IIl6| zu8p1D46RdL?fu2M8=sX}e}n;73z{X33F?02+PN@;Dy)G>1LjcZ@VE)}nt!u-V3I`v zMR!7wSIK5S%cCB8DZj@#p*j0-97SHj8%Dawel@Y7l}q|Vy{L2S^`g4A4;yc>Tl>0F z|0B;oKeOk0$#%S)2Icd)yd{KjiRDJVEZXfngqU^~><2m;+J*+yF%b>*xta=0b{y7t zW;hZzcnE0C(w7wQvLf!Fkm=9&SZTGR5RC=Z?nzl8)6P;dTuX|{7#tg44#*5S&ZvYP=Q;#1Zvo)Z>70tE^ui&}DFqv8FI z^e776U=nrXixTaEE&-h@52}E=VZy@5;r1D-JJV0BBkJo_MIR@P5Nma+4A*lUmRpgIT_w! zUHyz%7GA?TG>S-3VCVQJs2G@WQn0#}yEpe_wvI7ZCRoCMgy?##gG za|y$QLTFs|WiYm)8^eu-qZU3zKCwXy_9M~?oEd7X1}|3z{j&@Sjy8^3T6)G=OMoa4 zPXWt4TqV!-ZrQ5o0|1P>Fai=*FKYEQY$Y-YzD_}RXJF5jo-i1fg$OSfWmpobVg3$j zY6W-&WnHVZwY`S+0CJV*8?Uh%P-pxemEH^wh|`wN8EZ{BCKH8R^P8na8-CH z&tA_@&dup4&JtpS5|L{7j-cB?ux@c*te%!Jkk|XTSsd2_OYIvict_%g{hIAP+%I-% zoqGA`bYpr$bE9G%C}lx@O>^mUC0&-W{t69)v{CHX^^0D6Lj(iJ`CJtNTICa4K1wBI zs73uWwX{r~R_K^Dx6TYbjx8a+f4=^dccYQdwJ6AME=Qz1bNI(-OV605t-$13BR4cG z3`YG0pRTw@;>=Z?O*f1>J__un&Uc}(_PN&jmfb!{|4lq~dMZwH;tqf|C$5$ShedF@ zy}|K#H0;zAx=yRLg+kk|#Exmkn6aL$?fhIVt-M`R6|i;mH6|tsDQ4`Z)UgsUs+Huz zNGV$zu8YZhg*h~;lSRBpU$u)8&x&tS?Z|1bJMWUhpPXKZMOhbr=d7{ zlAh^Er5vj8$L8t|Pw|^qW!obRt=s2i^z|@<$|YT)*6{jTdPV3i_4ev>7u^`hFY+H^ z+rr1-I8fOYZPIW@%%q$9(Y(fJ@+iV0jOewcTF1aTfpwh`buWw_CI(R-^Jc0pTs*SG zqQ>228mV#9Ft(@8QV(H-jKa$@2zG_A$rt<}I4`q?DeCgk^oRJ4e=i&e&Qlp(%IIIp zSQ_@J57=7v;6Kq21()f8O>3GAbZLR8<-F zPA}0Y_v1hnTH1JuhcG6vanynB<;$)=Rt?hkZWV$>sCTEzQ+T7Bn~cAfNr1HlDw&Iv{64pMzbTdY8A2e(6rJ*3`oEtC{AQ@kk~j? zwLD5^LsSr&c$q)xnDA~O!V2ML3_VR?oCX>Ma}JCF9Imc#xVkdIt^`-@?C7P?xp(dT7wJ&2igQFcf`SN@fyGmu-#7_9A;}#MW*QNxp@^f{(=i*`;T}8 z0j>N5P83868aP~WY01g}>>Sj)^Fk$&?}l`C|reoqG~^qSK^6|}Nht-PkXZ_xon zL^GUy($^r?l-^ChzY=U;`X;|a@24WdXH^0eSLZ;0U^lSQC_;KT<8dzE$p1nviUBKn z(<2lZ;*A#eGd-Wpcd+@fEKeGskDm~8S=q~qE9E57)Z=K%qpUx9D? zmT$(t@)!RNJpYxi;`aH^hy` zwmV}trB_j`znDsq;ck>zK^d6FVDfKqL&9)8x)8?}xe)kIDMzbAhp*>8DOmp|c^W@E zIR;nsn|kDr2o@ooM;VCu1V+|fERX^>G}}QzUCdxAUi@Q zRHW1AqN8V#PRVgJ6~z7d*+6%U4y4e32+*#>5Wj_xpR$mcHFu1oRt=!bra^)JRA;K zx)~P5i}9<@peyn8Ascw%k7B%7XxOMS8#IzIwRA$1k*&o@BHoxpy^^dbFcs5NkK>VT z2EUsg8EJpleGreTab6`rq!!ZAJQdB$5rn=)oCjE>aMb%NigekZGXu15bd!d$j#!14 za=pX|7>J04y(*ci@(nV_s_3L(TE~iIS-hTu1IED$*7iy35?Ll(C|ATn>$-PQu#ak1 zsCUIPbBx_<(cEfP0H%0UjBTzl|FoYA7&6FuCt&0Y=&550l>?a80rff@GHoUT5UJFN z2m-X$jL|-L@Bqi#BLu+V$~@?w{LEMJ>Cb-&pZwIv@#)Wg9#6mU93H)U*Fc8=bFApC zdqZ3hbyz#_m1nUD?Iw>BY5H!kK^^PeF{&$>Q#8+i90wRf#+X1VO>Y;)g_DU zC8ksfz!>w`fFI-Q67VSjJ%WiF1NJa*L4-E~y0NrBjPcdK>$uL9#mi@6qx@-oPBO-e z%r*=fygvd+sWbdWt3M>@7l5@WM6KljTO$hv3YraLONhQ1;1={AQl@ba_j9Yr%L0vnh-;XNQ1v>E!hPPWA$iu0x2|17ouYhtn-NLt7|-R?@=>M$biZ~ zZwq=mcuLVk?^LE7SWk=Y5-}m%)s$!g)!f6GOUd&O6Yf^Tlk0Y_?1&c$n?oXmf3pd` z1W?)ReLWLtnfK=xUPOb*BsdbcvKIxo!GJEKDFg{L=FGy`^;rQ5=5{>*_X?UEgO@UK z{BJbJPgl44f(G>5@DLF@S6m{bkxgKg%CxM( zZu;lSE5y$yqJs(NNzdseI*ibBHUr>l3oNx&>D8GCWnB$hh$}5t7}5*A@ke{^H zFa~Ac_FOjcaeFvFi;4>}$Go|$w)5IHe03eei%jN*dS*IQIZyLUzwrFCc+a=I3;&aU zg;&4f&G?pge<$AeZQqXL z{Rf!q2ryXN|EhabTR3%$U(;jUsOA*lMct6JN{$^zD5#&ZZFyLsCu*>xM(C^pRi$I(@AM!ay3vpxx{KDWe-yB|t zh=y)cLX3AZ1tmLW^1z(SYL1y4i)Ww41^+;P&hOD7$wyFlUEmCRgZCNa@luQ+F+LRj zkVebk)%*-{u3VVy zLz|vnC_tlvPrMgnay|~p6piMJxATq}lX1hKmm!#wsrt4~Y!ypI%_4>!Pd7=UPGGq$ zzB55EF;@ko``!+}A^E1w*=!a9=?fHwbq>qo5y}n6b>Qaq28Y8P%=H$_;eZLCw*!XW zM5KCa6FO`R%d!BlV&u2ugzJ6{ael-)Wo%{=9s882B$lR~IHiNp%s~U?__w%U!H1+p zz}7gr#**(N*@#GTWLLPm^4nQZ*!G%{@fiWEp<8L8%{;3*2ed7Lr|LNj%qv@&!Sq)F zLSAxnZJzz?96ro=T;F?`4#YE2&s3~+!BjM0M-vwAg%pR4<~Tz>Hl9V>w7k9G`sx~2 zcRCISgq}TsDC;Kz+#G?Mn_HYtio5sjV3`vSZtmlS=U%|)e(z~Ofjd`MUZ2s2p}_6! zvCQA}VD2^XMWZ}IVza>G(}fu$cL6{fS}hIPJ%>Cqm%?MviCKj!3O0o z1Rhq#EyqR}yPhFlrRx+n8^%%(vVW$z z!WPR)tEsX;PXpV6roezR`eX?4EHQ$DYBDw@Fv#)qPz5l{NxDMdN)`qn4ad`po6{{i z%)|E!ANT}*_X}Ud;nA1jW54qy{QNKe3cmcgPvhk;e-w8fdBi$#oKB|5UR~kp?ju+Z zhY0g)(x7ui@4#dC9>Hs0{VKfc9q+_o#G619FXff(!okqVBkpRF_;|2ugEto?heJ7X& z)RZ>`nk-ny>if}LA%M4|v(NT!aTHC&YH;X^MHes9YYOt!LD1cBgV}&-t>tyDV5cmR z2A!t}7Relsdc0i@X=dFqyWs%QI5sc?x6Om|;NMUN)}ghUxmO zJP-9;V577Vm~(nuC#5F3dr~zt@Jn%i6)DWh9M$J1u)4(44_I?F!EQ%wAkM;|_nRQ8 z0x2MXdVL+J=@7pBJJ3}x(wiAxZG5HS*~2ei=c&_ENY9PPyzh%XUkt31e`w@O84%gj zTp%U;*d`R>-7HK7F@-tnqi{TSK8>%WT;t_5-^7Z@huk&8owaMVbzOBvidFR-W+V<$ z@rJm^wt1A{gpMN~f9wvfAb8{v;HUnZU&3Ge%l{`F9(@@OcVCWIKKVwx`>k(*0(iq) zz8T;2rnljVCti+w*H@4=k>sxcFy{!1aS50;qwWgoqa&+IWC*6p-+PKXfX^caNTZ^R{l^vCL}eNZQRo#B{?6=$Lr^g$KB~eE~Qf zF>j7I+`WcZ!Lz;c0_vn=nae`vx4P0(NIPm zk1@S=bEaqRm33-#V`q2yz`9DB(F!&8U&`5!;tgJ~Lm?RJz@Zc0j-B4ayx|3%@Hy=9%#xV;$T%&&Q02j-fx=R(QubVGInAxVsz>v-d#8-P0|@b5v<&CPqC#l zvZA)iFNjYK>!clkMlwLtN#((PEs9?Q?8d}-pBdZ=?ZD|U_wl7&->?DZg%|Ft&N)IqiabF;xL559WMc`P@ zM;MGIt+qC9x78<>gpJ>FMtP8?6DteM!HqQxFLlPGRDPUDp;2y2Qwwlw`GTvfE35$i z$?(6A@=hs_&^i#i$U;fBP@niq!C-B~Pe+&MZ@Be;$+q>U_n+FS?d;T_yg_h0S zD+cv7UMwu@fBA82!K5qj4})830wBR}Y5-k$&VC#Z8{gOZ&coi@GvhOZjbmS?XprG3 zC`>#LJREh#1tPVRYCI{)Qn!3l`e&Xm4+M*r2vwVF+7vT zGl)zPe2Z&M25)se%z(k@3vph-&rO0{h2Nw4ffD40Vj}rEcAt}cx~EMb-6JtJ!oOL* zdw$Q27ZjvE0u#@Nk9=D)O$J~a!)#?ia=#oD?m!49y@10lQ4~mQZMrgn9k)UKNQoPq za!#6$**qt{EBw0ebtnxUeFZ;jp(y2=R|8Eoh-|%j6=-oSc9(;g0J**=vkX?^^~=gF zja&Qt={zY3Mk(6V7a>3r6o93HT1f8L@w{-tg30@xJf+P5{7HzVcOk;#Yqjk3DjQkACzW_^$VTH~MkG#0t#jXR-w??01ZN z$pX&VjU;JcO41NPBY^~sO5teG#4j6T3LL~YMeF3d3KsH?&sQ2}l<1~25a~BzY0imQ zWW&=XDY9n}Je1JXXf! z2+qIfcidF(5m?b+__?b_miSJMjN;mq{p~)_)$6KsPv-`eR>77xNG|f3iCq!3yXv9@ zKZ2A!_AiLn&XSVct(zZbD-rh(0|3fdkGD#srSET%E3ETUk_f|8+nuCn7)NR8I-pq8 z@WrljE52_6>Ja~ZP@rXP3?qR5<7-4#G+GcHbyJIPat}X}G zeerZ}JN%ex9Vi7U+iX4xmF8UsUAD96k8POgQ|b;Y@~Jt|OZZ(icn3Quv_N!jjxvkr zHr-XD12}6Ik-p41q;XEF9&yQ$DiCD7%_65!j7hNo$twv3f@sSf=dk_k{k$NteOx!c zUoy(@Sv1D%`5xtSoPvr$kmtnK1Dmy;^*Q1xZqw=jJ^JWfJn?wP3j_GXZ+!+I{NRW2 z;SYZVpa0TV@Z9|eSm%W3@LP5361a5e9A0N};MafsLy&eb`6|Habc_3+{~~_!XMPq> zJ^47^{MyIRVfv^!T@l?!ZNGqQ@eapIEEi$4OCuO%6!jHb3r|QZ7@Cn$cHZ34+~|O< z7~hC&Ao;m36U3vM6o{q)Y`Jddx0U3Ambk&MF@|Ty`23Qyhhy4O#ateT?5nfmlGxu- z3rvW|&gIY7d%e^eFp`R7PRKcM`JmTy2!LYTUyGCC!yyEGWi5VKpS6&;YE?H0dp5h^+rFBOj=Y!=pSJS_1~}Da_X_ z{*l%!o=h7KZiSwDz^{Dr3;5)3eG0F9;uRPJm^VkPrxWJ7LPT*|Pmtq+!(s}w>crg+ zEMozd15T@Aj-yxA0hV?E4vKztjjKl=$Ft8pgCGAVKaF4g;0N%BfB)~tAOFEWf!DwK z9=`OI6OO0#;x3+T|88HlznidZt%I#iC98{phl_wB3P9nUG42f^mwvzW-hP}9^Xl!D zolEyjp#m3>1-$WJ0m+6H1gkKh)(S({P-+p+o||64n8q|0ba2ql&_>lfXTcu>c+oEa zK%3`Fx+g@ZcjmK52^}s_MLm>JJnCR#U1xw?=?he?+PHUC1av?mbh2Yk%fD>>3NPH; z;`TJGrbAOKPS0Zk+B(KMak%#C;%c5ILZzB+VQ0V$vV=?$p~A8uHdC6L^K$nzNqXCC z|LFOr*6!r#D<_hB9uWl2s8eN4Q-%m5Pz+Eiu<_7tj3~Z8QM&*D(=4OKA@_Pidk$Q} z;KCbjFM6i7K*WW90sNbYmAZC*Ut^_7&-T`I!(BD5OXPgpd<|qo4Igq~;T!@tnzz65bnu;sox~YxB@dTY!KEg$G-bPBMx08D=($MC#5ZmBSve`~O-@2fAWx6c+PLPO& z{R-f2yfYGsjuIh#(Dfj4qE^kIErRQBpmPI5h>Qmy${XGX{2(q*l;UE#F;q zLMFV;rTuJ^bWdWig1y&w?qPZK6}WA8@ca$%o$q=V{?-5WzlGoW^($@Fc(;Ose`(bzvuyIFq6w(}zh#NVmFBs`h`zM*6JGJ4oPT<;kkyv`1 zG^_YPA~x(rdYR*M3#z) zVNPk*(@^d;3T3%*%pFrYr!~0{a}Fgf7GvpDfKkM@sIh)r#^ zbXtmV-v)#u*6~RV2H>Sm>%sd4Sl5BOS66uLlUI252Ke!x{$>2k&;L{W+Q&bIXP&>0 z>#J+rx%UX}z5EHZ$E_D7oL3%3l=QPJ^b=VK8Y8e ze=WY_n_mZM4aaqQ(Q7w^QPv34G!5^t@U?TEz=jrOviR#6C#9gV($~yWoTtkt5wRLf zj!Cov7q`psab2tf7$dbzWLxbxIISp}MaPPwX@@pNbUx##1Gjj-Vu&02)EJHzsrxuh zRp(^vW5Im4k4vx9~NnE9&l@URP&4o_EUP zQ&Srh-D*^bVoq1WkW7i*PGe9;6cM86mc&iu4ta|L>_ig4(l;!jMvG^J#a%+|dpI4U zwVgPyx1yrnsioyu(ST(+0IlPfKKNmb&&`gjuXZhIG$D}fBqvshF|&M2l40s`+pAK`yKDZ zGtYE9xVa5QcHdSSz!?Ez= zE8A8VkX*<35JSzwKF?jJ;8y3nt=Zuaewq?Qu_})b(byat+|cx(fma_u2qy7z$i*LSXQ=pF0n6rs+>8*IFNX_#x-N3|%J@HF%u;~*d+;#M*_?{*e+RnBig zr6qOHnLc5?=Q7ub0w9$n<2808vT?2V^$p~^^nlWP+-o zXqZtad2Ec+h31~|3(wv;^~RM3r@T*|W3Gu@x_#1=f0qSq!`QRAzpt6EUlv4;1us0+ zp`;_5;q`jDKWqAZnj+HZP?%9+rauJ5a;p;ktXGmRxcK$7CL>nCC25{l> zRlp>_)>@0YH^b?ia!^DtJ%uQq9?CTGCS^_exhP=nDKfmQ#jE%{q(a>?DHH6z1Oyn{c!ZUa3=#&yeGR4GoNfto5AyU>dt#dUrDG&}_l z=QPFd>Cq9x?;~iYs6#jFPQwg)0+6a{)Qwinoc9iM*0pJ^nIW4+{*joHOxt6LV=2=p zC>SbUrzH&(jP@O4WEV@F&@ufn3z|*{PKa6;k*?U`9T(82Mf{)O_(i+9peYbmajA-eN3v@;y$F+1DhvCo#A`{Xx z?Lxr!>v^)@FV!x$3KaFeFs;MJc?Jo=5<(FhtAa^ zONO<3V9FiTq6?QMIGt`Vhu|~^WULUEanONv44^gi!-8M<)sNuSPdtG~?%V}C2p@X0 z=qQL6_oc&^L>`BIaHx0XWy%-Vb$G2*+c*tmA0G=nDAh?h@b_` zDH8kXLPx>CjE1SF4ULJv2SpUzCblu7nPuA0+1VN~?S6}Guyf80Jv~HS{D!U<3v_t+ zrDwkY*b}J4PmzW(2Y9Beb+@I+C^QavfW>l)RM zk0`VQm_g1XFI<2!lPv4XN{ z>uE@(!q<$p7{K%M5duFxuM%gF!p;Pqd6onn>uFjz;!ZfTzK#%?`g*Wl2@#J#(5D$u zRsq_SGddlrR>OyL0oT}RPZ9N529=B*pdMn`x1;b7B2sL>##`2^ArlL~c>B|KFq zXE}ix;y95D-$6YuM;O&A=60H^H1^bmC@i^Ap} zQB$hCN`$Xz9*E!(9`)viD-n2Jx+yWKfb&c+u8X{qBc8FRstJb9cq)wm(-*3&OBk1? z{XW&AsRCHd>D>|oz9EdEZg>rl?Q`kN0na@96@2`|KZU+NkC)v8KKS8Z!H@r)zmD0i zAXl%zYhU>!zW06a!JNSBpL!c!{;JpD-rWVK`_BV9fR=L;f%D6@uQ z;D%RDar*4lo z=uL2Ydy8ND_@^+`y6aNsu6yYd40=i3%4Zln8(@2RIxL7g6xLuvArVzf*;p1N1iZ1x z87W&8*NZkxcuQ+FiIC1zK(Io!yK~v}USc)wK=4-}%1G2Hi&)Ttx@-(vLAz`k*&6Or z1YZ2`*Kxh12pEa#&0AuximeS621 z4@GMZCe@fV8HO>>S{#^*Wfr(?zONskRx0*OK8Nl2#I-cmf}y^W3}NS`214*gRD))^ z1g5pC>#axV;+%@pTn&iM1#=FZPS)nliU*62b9iKhDHb{fV@xa_C17Fw&7v))Vx7a4 zOocR`+h}5DYhJl{2yvcl}kc=lFp&ZpjM|C#;g;$AT8Ernl% zPZHj0(qUsIdytv=`gjiGq`^QLEs=B=k7V)c;f3G$UWxAD^RwoNpA`UF*9Zb;yv;%8 zsixpgb=u})yJ3`yq0`%XT5)^2#?X$AfiYGzG~B(u!kwk#bo&6JD^xnxX)QF{;yIbB zIJ5&WZ!yLM4h{rDX=JYvc#{qn`bglZZk-Rq0d!+9*mFPy+xpo%z4vHg8HFCp-w;Mf zQ1S5vcOcRtI!{7K0rtI(qC=eF)_S%}X6sU!-a)_xX4Yf3u~LX}M}tp=l9Phv0><2& z)BTy-JaDKn;-S&_>*hez}^GsYnKGlxf7!xX&i@mJuV{?aetNB`lE;9vZqKX21K zybMpi5X{n+ux1`;49C31)uo9KgHwsfHyq#dVKG9{sHuxqeoVd z$G4mp43n_#MMJZiS>ow)F}@urdTb-{zz4MC>tc`D!q*0fpSTzZLD2h5-m#)OAmf{lu=lErW8^nmPGetv77)u-r1~RhqHQ@%83CGV$Yo=jkcN$v6 zo*TW&K5THj^CGlzAXG7RpfyjEo9Zd_D2jJ6bj048@+?4WX;LY!7fkdbYo5G=pZtXn z;;;Ob{}5k%`T?GJ{Zm*D3y#xkp~-?(6>SdF6U5ezSq=@;>Mps!A@3MY;Okg1S2HS% z;|f4A))iORSNNUJeGX4N{wM@3(#5Cmug%!HC#o0a6`7b!d3MZfDL>X?+P>uS)45jE z4OG&nFyzk3Ee(wJ8f9_=wSejCXu<#XT5B{Wx-Fmy*zqWwl0FueRnUmEQHyfmneqK= z7V78w*W3`wOc{VW|C}@G-R>{rzp~GkeNjk%PxSYBc=7bNw$bpndKS9%Oee~EeU9qsUr3$DMFI- zopCT-iH1OBqS4(-zfGgnULf%LAFiN_Z}vv1hFQ%|$0Lr%BW`YQaC1E3bUNYY<_5>r zYQ3<~l$iF2sbYoU8gN{v*XU53=ENAV&`LF{`sxQ*6&R~RrV;5lo$$!xPoUj-IsUW1 z{kQQCe(Wdl=wo7*Sl+{W(>V6j$O$8FyP>$LX1QTHUi zUJW%)g%?Qv(t)wVG~@lfL1|xmC>4YB_-9&>8RHh|(Ba^;NrX!iT*SSm(=ZUJZVFiL z;x^jSIpd7ib<*3F-qz}!ZLT1RD%vyy%WwePW;9%PsKE|GR7SXt;>De94Teq+@DePC z10aHR4h$WT-f)_VF;1ALBgSc%ke&lqhXoKTEIo#XWtvRVrordZV3m7N;j#*iiOy?g z1zRq>yPrR8Xu>ls06I(&Sv{Y%AuR7yZiGro76lYkAWA-heBTWP^yHCVnwW=GTo_K~ z5h)_KUz>fdxq`%KKy|NYQ;FRFphHKw(+zmFb!!q=`CUvTN;$K8!=s(l1hj0lwM7|Q zj}nXfK`s4?-edxOhWHbJO+oNsnp%b{MNHF5mXOK;`M}vfoEsuuHI0-YBf-pz7?QpnlZnLdc6Y&ldS+{?wJ4H#KPNKYte0s zq4d(k3gyFidTR}>FKAvYk+Qmxq-}}4=(j!Z_VyM}yz9F$3KSFfG>RJQy9k^jCKciAAJSTmt2EsxMVtzn^Q68 zi1o3q2;!hrFq!`bDf2axgVi2F08BXTi?C|KM1=5(?+qj3MgwLurKY9gr3y<;B{WmR zEzyD(#E30EpL|Twx?@eKV~@r8qPO;Sbu?@{$Wz#YcR8(2#5tsO~WjlVvq@74a zxS$Zl_x#L80d=P#Mpg{T_y<+vAFOc^=uCJX%B>kfQmv&2u+E9Be!N?Nw zwunG2pJ5#+$`-CumxeA@uzh1RcM7qn9P&$dGT0WxeCx&XO-_+MPcs&&8gCPt{l#F@_d+Wg>an5Eu{A#MQm76YTyOS!5Wwt9f>1Dlk3jQF%6qx0BbVam!LR zy8vE%rffpa*NBhKE;lT*D^n15iAv!3oo8pqN)6s)kXmqcePwl6L?GHM1xW|iaq`jv zjz5_b-hg#X9FG=}E^|cvFh+mcx$h9^Sk=<5L>p?vcx(3EuIBTXmU2?ukf9)UF^M^hINnot==m(s{@niu9nqhBWN)ff3!JJ|0yFJ4A zA!uaaI*%!M>d8C!hyVCz@xS}G{*QQOT;X+Zej5(Gq3M83#cbV-jMKS;PU~3eWf!6? zySQGb>d$Lfr$w4d2qb}eqm9#P#Z~WE*AssELm$Kow>Q52R&IdCrOFrKH6U3C-_&TI zJO9^AAE!fK<2{9Vq^%kK=LUOF&!Q1hv}Zb`ChfuN=CGEmkoh{0QW#D#M>l!mgmo)% zIqgjZtec`*4H~cUsMBOk?^*LvqzGe<8-ScCm<88Pn5Aor!oRNTr4`{VjI$DYRmN#K zs=dyqTCEqjw-Gwf8?ZVcYK_s?4LUTG61#tTWx})LUVa6l*v>wxA(zUp=?t_Pq4GOp znv&NVVnb~*-u{hoy9$_v22pE5J#~ZyR(W_$-}JGFfbH2>*8yU+Y{pcaP7}w|z+ntP zE%IQ6vzTG6g_kB?#d=uBBUP;9gno4eOSK>wnMb6%t7E8|P*Y1KnG=xih2dtyaRu%? z@;L5~iNE=`ei*NO#VhfB-}644jvZreJ7v^$J-0M_9S_vc<##=ZAg@xB1Dg58stluU z)=`ITY0G{0w0343lI^}8r2x-c5{&k|(2 zP-fxgK$%zGdTi42i6kdGZ_sH2oY?}dJM(#LR;23s*)qp?0Tllng zShNS9I08udCxZu}Sv`X}wOHst<`!U;K#I{U1nR1*0~8H<9qvQ`5b41#PGj6P!boM} zh3B8fJKp{_{Lr8I^LXy*F9HHQ|H8BQ>Hqq_z-K=GA+*C4e&_SQhc`ZL?X+L{#H;Yu zcYi0|`<;IX?|$cdaQonSoW`2O#q_Ib-g=8_Y9SMh7X18q_%j5KXIQsK@^+icqh*7` zh$|oxzmsYjjWO{$(g9F;zN#BT7>S~QPEUgsX8dktmRPi-ld9jI4g?!@b6yi1nau)7 zOfAQ4Y(h8ktU4#Ef@k+Ck)M>IJ6;BqC=(H{nsR@%IMV7zY%# zAH(lk4OCS)vt|m{mHf_hO&@ne-l!F}ofDAZn4ZR08f*Z#JSJY{&)z#)TQH}`XU~bl z(oIQIKvk?h2NdIU1DuW+#}jn+QjoSA7Faq?H#Zgqc(5iOCPTav8x(U2mL^tHsds2M z`ft;mNnA3xY>;OB+k7Dn%hJ$WFJ~%webQmehzWm>T&kiV@qMZWosIJ~au<8Zort)T z<~pA5hXF5cobyoe^FgC*@*bw)+CP_>ytU*{MoU|&`NNI7mz>4>>l{aN?cl0j;_D4< zhSW#-iL;1ovoAHAZjN~EYahe?Rq>zxjUUEx_X#}ls#ihRqn#Oea?Q!{DrM)gU?{K- z#nL*O0}dU4&f)ey;%W<2m{YMV9mgq{L$Iy`>$>9hc!PF0;0vGo0v^A<0sx%GYJ5T1 z65NO_B2QGn0=L1Kq~P9yZ??R|XvGYF#?1DzfX`LbC^~VzWw?K$p^3LNpH6=vzxa=Q ziQ~gIsvoR1spcdPFy%Txf+U+yC~sviH+-VdPSEAt65BU$y~GF@F5-%?ws{(k)vHKg znS$EC>Y+$9&dg~!V-`NESZ6X=w7gGv#HU;RV9I0wK8PQ1UiG*1{s}VFzJlL~tZ@$k z85qe5Kv>&NZfG!sIbE@6aF>JE;0R#td8Yo_ez)w9cif&%c<|r>#=2rXtvDU6_^!^? z*V};MPyoa8Q6U(oTU@&vZRo^76bB5q_-TYALbLnLItPyHYB_{s#qBua`eiT2{nG(I z{I~yeeEPG`;E~6!E;6#cA%hVm)gVxn7#dTi&Zv<=f;O#ex*|pEF+v#$KpD{HJd~>j&$!v;8 zEx>pk4^R|r8gVI?S07f>&3$}tui zMH%A-U_vktR|rzrY(d2(&(Zlg@$aWh!=)pn`Dst*@=ipqOrg+o3UtNd;UXC^!yBb1 zFNOh9RF0MRWuxN_KTubul!{)NKFZpQ12I>6LSfW2Duis=tykSY=1i9%pRMcE33hY> zX!9rlPrbFNX4($viS*AE=a_<@z@d?Qmglf8f^89&!P#vdzn`?V=cnp`XR1fb1uwP8 z5AiV%WE9Wzs1TjCFn+Rap4kBY0Pfy<1Yi2n@8Rd){||Azc@~e~1wQtnU%@Z_?E8U7 zUybG7TPf940UD)72Dyaf+#Z}Hhre*$0p{HJj^ENE>>rPNnnz|_Q6=9GKh zV4l2Me}d#{oTZd8TM!CY*yh|Bbn~Y1Yn9UvyF?_4dItxp5#T#HeXeLxqB-euUtniv zm5DYiij3XH@*6IDkkN1ZYyT`7p!i;WmuI0Go5DDSe0|Gh;RWmMFX6}vZxa!yM@T9( zA{OeI41*eZy02XpNsnxvn0Kt6z|(MOHslmT*g=6!O@wM-N?8cBX$6Wc+EZe^t(dlK zWa9`=wnnjafM<{Er9o%ucy(t8+gok+&A(jM1`+XOYdF9dAS&7#&l3U0nATjt@7Md4 zGeMSQUvYJHg~Q=s_*w;R0KHiTzzTDC9%M26wRE(F-CEfRl1?^37ln>ngN@P*H;Bvn z7$?>AE&aEaEw!hP+|arl%FZ_2`$CUilZ8=Vv797C<&wHVTDSL}-b8Lhcg$iCLBVI$ zi*N=+|Bfl`@EiIXbE0Fpp$zZ_WSFb6=^^9 z!;B<^Nz>)2X5_3*)C%~XPAA+RkGMUp-s}cA-X3ioHOm(ab^HylvE%v&#f190ZGFy$=HBhofd^`zAI#iX#Gmxuy`;{?sxa`r{Qf;+nDEThUWKS5@t zF1%bZ#tL;^P3N?B^tyF!$1{9}ah|4^Q+bKkOEbXufoZK5YZSmDx<;T3EAud1M*!lJ z4*@M5tsN}LPSbN_dAs#Wg@8E9ERGd`@}pVJIB>WN0H&^y`EHe*$w48Kj_IW;%*VdV z@vvN+Z*F`h@0%oC#-RWUr}8=-k2sxKYoEQXDI$#By6n7tg0>Aw`pTmFk6aN*o1Bc~M=D{gkQ0!vf zF9NtW@P|hXV0u|eCU8Y^dd(l(6Oo?u!RPr;Dla@4GSA1cHxBLRVY{UJFXgWr5VQT9 zxY31<;-IBHlP^Hl5Qzo!nsqy?iyPSTuuD%DsPjl+;H%{!kga*xHc_eO+)U9Rkmu@K z{xMX;<2Y0x&iF*d-k0WeFcgpuRcocE3LLHvXa^777xU6cv%=QW8dfzUoC#GmLmE?O z1N7Ompz7ZF9kchbU81jbT`|V+6q&k49fAccSc-IA|G4PcJC1D8%Yf4S+>r3?P*7N{^mBl^G9QH~*x-y!V?B{@CXP<}_gN z9KyZ8DCgqEuFW3!zO|V~a06i5bi`aU;%-|{rd*|QZ9BifZToX7d9P672>6jnY?Agu%{=-XqAyAim1y)ek)9Q@kk|t*jZ=EMYVx8dZj82kexxwvdk%Nm(7FW!(Pmy?gl3M?Z#t{L?>$+v5=m;PIC~hIhW>-FWNUz7?;3(>w7k z@BVhY;$Fid!0&zbGx&`U{StotBOk9dm`hHt^wu$q zUtIWKWscyrRVg!9f1YoW0Yl$3J2wc}(o<9QR-977OY zC&islhkgKh!yFTH9ayoVUvb|X(5^97wGNMB61a;wq1UlOhuv?u0jD|tV0R)bWbZ}E z#cDi?GDX6p9WjHy(pUkc?xQ@jaQUlV@5xz=G*7pvN@C#)fq^xZ(r_e(B8;fFmR~0R zXcmb<-lchO%hKwok;Qw*XWXpZJ9!z=z;xq7D(@nfb&Wm7udkmwE_dHoUssl(*uM3m zfA24o30Cv$Rj<6lfAYisC4THD-;b9+`5H_;Vy-8n53aqyc^9L_{VYr)>w$sc7+Pg! z3TEq=0EVv6xuU6JoK8?Zxq)!tbiBbBtCh33y~Wj)MZhd=u}*fN5oGJEl%`(d!e5V6 zR5v6N^LUz}r4dbtm*1FfG?`lXhqsNc?kII^FD2y}UK7TG8_M~wd+IOFxCa2P8z?iK zo>aV4h)2m0#i@p~+32cW)Dv^8S$k4|(5YVP^^y!Q#a53{nrnd%7_g~}sR}Q~XTP_e zz$L8nGQC)#eYJyavK)901?vNZGUUt*J7b1Dht@eM025GUJsEMsg@sGaX#@kwg1edV zN^7A&%9uok3+kGa9r(y{>8k)!I}|2I2%G7YSWGixC7dH0v98CKd9vs}U^LQ*8}_`fQsxyN|JUgeZ_D*buc|==tV- zq-d8viY96!cxZq z1?C`Vb(up<6G}ZF$p{%LI0^J~jTPc?MU<}%0=DMO`8TgArI;P=ZS8<3>h+~Z;rjsA zW{&fnI?mmL2UjW|w?Y|Fy_RswVEXU{Yx!?E*e)RAE4gB=` ze*_)C=fChd+Euc3`^uVOQ>0f1DhL-Dk5F;evP;a;bCNEnhc9P~2c5o{5(jF}60Ct!%NOT3hzU0-gZW zoi#dsDv;)qrdQMnyyE=18a@pBqZ3mzDZB#W~B*vM6)KCJ!^X?6C{4i@FOa7wxrK zzhCaJq8E)cF>e?R9#1P?{n~r@rH_3E|G&TY_wnTG--y0kyIv-k+Jo=SX*EcF1&Ta` z)m@xGYX>M6qYs^if1ArLbfB{V#{qyD12HDnam2cwFjlJvb39rE3}|pfsX5lB>+V|_ zYz|CCZ`Rez=wqTuIuB$0+>xqoI1~R#{BOZ=6KGbmOd7e0-Bagqnpdyys)1R!w#_4K zn%7vh-(&eJV%oaV+L#C&T3!76$fh$|lFb3zkQg5JPPtxM1WXtb zXgTBa{AUoZA0`ToU?Sp$@8BJUvQj=Rr$Z-J9U-jjAsWm>-Dr~JvsEC(2}Gg-mBxE^ z__u%CiLx13DD{ka`jnkH(lc+$+Vm6)XAQyF43O@@NFXh+0}TanYgS$Scsk+c_6FuS}f)r=&)cZ_vlod$x|(*r0h@?{KKF#*fZ&Ge@+TzEEb`>JNtn5xi0 zp)hd$$fNl3)6e4{zyF`${z-AQ9Dw4z%{9)su@&aSnOzD+Z2qdmo#{;F)%bK2NTMLH zo138vS$Ns*e*wJ3Shfs0pP#^)A+yyZ9rJe3F~{7TvW~0^F~o#>cxU_!SkcFlZ(hz) zfZQS&>)5I=O`xm61e9x`RxgOcd}E=33pPy!Im3&+IJ8<=u=E~vZ@MZLQEOobptX)U zPo7sjaeI4%F^rxR24;F+&|AaOfTcJ0mKh!QX1@yo_k>yXqd1qdOLMQJf&qlrR@93I zFTQ84G@7^84KdST+fweQu+AX;k9_?|1g}z04()1ADF@|?TQZpoq!dkPcIU}gVLU42Q0B?Ka zQ~3Tr`2Dzd?=G$mz;A!%xAC`r_&>z2eef4>|M_R}`Db6iS5F6=0KDxT@5Jx>BR`05 ze&@UJy4OF2Y8i}k|OY8e-Gh18m-DV@>z}N(fHB~!)IYw$26RwI#H5drHpa=+#QG}kHm)jVbEM+ zGZh*E8nbZXPz(!*FWQ5Bmm)bWFeb*yR~+M1BeAx4Yujjo>m*Xn`(=J!pH(Sgqep0V za^!odh`Ip~JVN%ZDXBy$Ts|Y$!0HW=e{cD%s#=la!h4#pFliTY7w&Az3(x9{`e?kS z7sSnZBnKr9-b7Cli_I14*iO{zK1qkFU|qe}Ula6Z&esP=cY=wHxxs3|EaufUr|49S zVc1c{r={=+LEp=<7XC6}PBC}R1fHvBi^nc8WNfOy%QZ}-WXb%#O;io)J^emKa7dwj zfd*}x0h;#!7B9lScf#6jYNS9qtS?>E@9LN~a&>oazF+xo{p>{@;KKX$9{Xo)|CX~c zp4}e}S6A0KPT)WNn?H;P$ARm+cX2x1VjUCf1da-vrt6U2QhE;4#dJ(F+`*kU!_s`k ztQA%x)uYpC#FN&%qJxc5M6e!Dc<{mvj>i+moH!njIGwDd#X5&$LF1CDZX5xlQHt@) zWwZq^EP;`x(@5H{y$lJ{JbXhdzcJlC4bZK@^a0fZtCDWh(8$}_;YmZ4*9My=xGxc}e*j>lV^ zjux)QXbaJa?ok2jJO!wo;{+Hx-~3M`UA9MDW}eQkti)NUP;1ej+0nycb)=TUs< zqaVd@edaSb+-V`~t7pg7PP&*eVmf z#ANfzaJ(4xPC${a)#C0FlsJN!s_r2rla|e(4d|NW4?2VcYwU!@CZ$cuA>w!CoSe!t z*H?q-MM{h+Sj15k5q6uAV@B&br=^9kGC#c2Q&V70=sM7Qb0x}*q^Hvfz1w-cx^oZG z4uCYs(ygVsPHT_nQ7Jugg?%~av@l&k&}{5S0Z^5aLa7a<#C-=X!GU#Os@vvkK~N_X z8%2nFExBix1h_pJp3dk7DJcdmm{%2=o*Snxu1`f zbkAq-dwa0B{~dx%ISK`kVAXh(MOxtOtZfJHYcu(pSX3(4#OG_{6OSm=zzb*lIoPNa zjwcxPW}oXGK7Z%lJ^aRReFA^?Z~Rq!^-EvIE3bfG`_M=5fscM1{S~jl-Nzom@Bh9Z z#P|P^KMhsj)vtOrzWTY}#4rBrPvEzH^Vf0j-XlKT3A6#lY8^NIJXC{crAN|N1!MfI zN-70sV7;ticz$^(xDHJ1sv?n=_g3m35XWjQ&a4}UM>XIBB@HZg&ED(1m6o>KE5@!H zvEu{V@&n2jes9ko_Of+z@0tnmkg?bbx(Z&DFBeyUs&$m>68vD&xGQzH{BG(VQ=f~C zDaRIyYm7DJ?4|R-5cEh>Q$!b_c*aAfmiJ>kV!6}}I;|e>f^4;X#qlvQ0N`m%nwaPk zHcNUz&K$Dzm4tHGil2@Fj1_p~kwzYM z7^`=gv5k|~tVSMFpr-GIw}~EJAU6rqaa!!zJUe5mH7Wqxy?)64+pV3D+!(~PX3|I9 z$B=lg)K*?8aQzS1)%PeH8A|+SXyx~F*~RxSVuSr$%J%}>?C|{=ez_#)cfX%KWAnL( zk3m(@Tf@2zJo5N8e&JU?h7WxBV|dl;UWe1VV$Oj9U{#IC%V>@viqo)~Fhes!7fp;O zjA5N{xo%Uv*r4DvP8g@v_1K9qPo^_>!OiUnH#bMz+&;klo9A)uMR=K>$8@$JKaafB zGS@IBPhgH3-`t#59TTZUnqgo#$?vn~40(^0HArK7#ZzJE156JudchRM^e7tZ5aw_kq&YvADG;>|XDl$Uc@$s{TZyxCer1*mX+i{Oy}8i%#;=!{f&@?jEXQU?zyip0 z51847Cp;ZT4L~f?^B@S^fRSNQ%*_rWafEMPdWk%Adz^D;SIR>;%nXyGa=MX^Dgb(< zUl{N8?i_rib3z3}-!32&A)pWwG@@bXP!+RmX0KC(!D%M&JRXm@y}7~BBVcY%Cmc^l ztm}%YUhH?CFvk%B#e#{`X;_NKoS4TOVBA9H#Clq>-rfRgtu!qjl!U;d?E#86=IS}^sh`@J{JY*t4ENovX^Dxk3(R##d>Ip2Ue z61^)c4+U`BXTO*=I}9Q~DoYgDF6>R=^83;2Zk)o;Kyay`2L+;TgS9{dl(C(rz6YXG z5?bUa@>%e-8S!+JoIe3+o;zN&^mu?;|b&L7jjB9P(ym}f-cz${|u0C?1BO9g2I zUV$Aw`>Zz#GXW3E{%_~H2pv<11f%+6rOHf+ihJ4pzI)|JOvkCQS3t@Ul**p4wbTb&Z2c2iHJ$`)q6K zxIpqQT%w5jEvstlQ<7zpp;?3rU?I$f7G*!LgRV!{dw817bGKk_hTy~4E$>n z)T>^(EHJ#N`G7d*=W7;*G=9{_%T!SscpRca8+q#a&1gKhd=(tUd+FS)vx}RJ@;{Xr z#iJ=DQ#glzp7mfj?gm5+LdpTkvRuWKD`&jkp}>C4ZBX>kmnnHF+m^@D`D!$6b4DSO+k!a;rt2wJ%?!!I4bJPjdia?HZJegba2hz3 zHHXw(1uB-p1y`)OjSJ{VpsPNeOPr(8c-uCIxKINzU8Rz= zG4$jm8#+p3Nc}1GOguy$?lJqGHeSq7vcdB^{%Rr1UGmkdJuPC6cV>bHDY^Uwfd z-F<)VXW#EdT~pUv$B5L`xvtPTA|g~8yiS?z$7wxbb)$>va(>XpY7Xw3goZ*jW0#W)Sz+};APMj0|^c1%^KWk7%V$(^(bJXZnvv--! zY=d>3!D;}h-+wCC(Df=c9ew)!sSBs0NJ59+M9}3<`q=GYF=JQ+(B=`g7Dwb`@O|^l zkVZpH1W>q9^MYIlb`gf2y>kZKzk%x;H^4ZcsKp5pJjON8QC-VtCoX0*NBKDiVsZIJcQWH}+j2t+Bvk1GEwJG9S_na)Kyw1p@Yz(tvvP@BkBO zsM@bRZ8y`?&@5QcQ0M?bc1};!;V_>GK)qd{fmSHtsc)ft!Uly#)mdc zZ8$8(bl1}nSAEH&sU12yR-K-Tl`E_YtVm_u(ifK^$a9U1EWDU!Fmr3#zV*cWIae-@ zfqI7KZv-z&ryMSl-Fnj{WxwWxCaY!;y2~H12&A8ME9h51VivY5B0bj1N9E6A zFdS_8|HX4BRwP%ZMt3KQ(~Y;Ne65%JQHiO~h>>%RD40mP9EWhMimW}LasKNwImZ-~ zHul*H&HJA6yv;_Sk2rg(ih&vV)XiI^!fWmT>G88MYZyGZ8v=xPyD5ugO8E}J4jaKk z2sXvQslb5tD3NYFTW9+uRzJaxyhLoKIhvE?qRV?0HG*Okd!l3z>Ck4KosOsN*~fpY-BLclR+e&2a7Q)tiB z0_W2WS?_Iur5s0{F&9ECjBC*?iP?y@JKBaX`jG&MomfRgHjayB+PPr_jg*w8_#(H3 z+eIahd*u;(bdqk@5J6L|b^QNX`}1Ghw(L9%`^KE>oO|DU^dLKae0uehCo3aS#Lu5F0RHArTT;4s0t%?5K$no02Gs zy+}1A#imG=SQM+ss@L3m&)I9vksrqQjXBoZ`@Uj5WEXC|bIxAPY{nep8@>6&#GrAb z%DPa>#1q6*TaRv?V@%xLzY4Pr?v8yYReIpTun2)&Z4^YYwwix*IkWo>f z)FV}d{JfbWyyCe{-H4J73m^1}(H32~gHGIJK5R`~F~O3_F4`bL=$VU_#o?Gx zh+XZlA~sZG&fQp4e#!2$L1S$%k%vH%##{MN#U#4##xq~`{aLo2pOv`3UW4n;nco=! zTdsR~-qDB?M!L`bvp4aJpZ+vnedSfs>BMEqxjE2j8o&rVJjTS>X^h!-U<|bB*e?TP z-!aDp7z2-&M?5_4*yoPvd!*G40Ehy(V?1IX1BQnE@_@(711@9W;W2c}G1$<9olX4! ze%PhdKwUb%8>(H^fry*%BOQPld2ihq5D%|?U`071Z*6^1opeQu#PA|H~OGPS!D-Z^bXj{s0QOKvlSh`K9;r$;CPO86wBB(4 z&U4QS9dfv_(gN$LfCu=KpmQK|-W?JQN19EVM~o@j00)lFj-@PZ_o6z-6uv{pfH|9{%Zg{>cl9OIH#U6Wkt56M6yC6wv48SP2*bY zw*+HQ2=3Y!H)L_n$qoRAX_seIdhzj;Cmu9Gi~daqSEUZW#qv^;@V-K!9OUxuhNZ{}(0#JSgT%4W?AI9$?lP(Vz zeE35j!uNjP_XCe_;ZYe8=~E zA3pL8AH`RH)mLGEe4f#t4FPr?KXl<+`y!>lX*3fdjkx|mB!?(uexi(eaHTUENbP*v8Ng9ua5I;z6%Gh%_ zi)H+r*iGa6dY;fC_cnPi^88D=Pc&JcUu(ftzJ2R$IE}O2yli2WWw2SsObv^YCF_g% z^emuV$0&&?Wk-f2+<5o&wU(aa;Aeq~96`W7$=B25u0EyEt7ID;xUI-iLZjRRrGNVb z23AUfqUjCcWDVc$fE&~GVc6Oo!qD>wvw?k|c>3fC(6&hTw-L^z&G7gzu=O5|JbaXu zYp}M#_la)NeQx?>8N&hh$wK7D+Kx&ql#hk7pg5e92UL{QDP*aRt*6oiEF(|0 zfC@jhNC|OdC%fG8om*&d&`~NDe>!&C{6JKxMNi_M!ML4_|cHazq5ox>mWqvcN7Qo*p_EO>rD7 zBYt$_m!qsUg7P#bcxfvXxVJFw1+bSV+#myGTKNspP8t!aPqc#Gdvq^o4Ntav^cjVJ zdk4n8X92i5XQYI5L+>&QG}eI%RWhQ`qHtZCf?MJ;C%T~`CFRZ}93Zd8O2t~&=-kZ% zNtvPSsj9Q{UZ|4>va3cRhJ&ImrJ;GW2n||5(r*PmqQNutsD_e=>=43QYjE(cT0KQa zetQ5J<@YFzi;O5cgO%{qwe*&q&rBU_zK_$o;+9r;Z~{pR)^r$Hjw7!0d``?&e6kpJ zG**dPFCJI`az=<3hhn=tUV8pvKMfi7UnZGz(ZAa9#<^GUb;c^Sx+ClL90mCG8eV<) zp6Ypb)bx*+1J@i^KI%AzG|V9Yo?CP$NFC-duH$jn#%vx#i(Ip z!VFFYBz2FMu>vbnB9<<^a1q)Z)Un>*R-~qBPk?GwZz}XYDKAP*lsqRSxFaWU=&LKm zRuM)XXY014Tu-XiS^O>d5Mgzg|eG5+BX)A>vq~N4@1k$I%blhmfLmU z)54X`vs7h)_UA;8FIywW;pUYxNq7?XYMMUF zl|rDgh!5NtL&YqI@t8xUY82@uId-UbNFk7X<|N-lvnjLeg-EqJ40d*J}leVt6z0W}^YAxKVN$tOF{b*)Cd?v_F3+WBBtPq6w+ zandFJtK{v66)Z^S{5|L9fNc$L{?>0|f83*)g;6)7A>JEPv4cntc(GxkSf$| zk3D5$43+YT0|^vdb{^IWi;p&d*(MXPaOTSW zS~u2bV+n{j*OR(=v&IM;hgxSxdi&xZ=lwlnIep#@kGtWusWsfO!7APGUWVZw68jsaE-A3l~Kb%jE4(N z_wXMkEzuJdKrNwOnIx@ddZG+Jhs9g%6YDmbHCW&9?D-=uj~9&ng2%@fczk@0dAXpE z2NJu%*lyZXn2bt#$Vv}G%HVefj2YbJLebs)0=Q$J(Xl34I7(PJE)2ci;FxZ>jEOIN z;kR->%Am`S3m7e{;B^-ReGY;Zi}8t$>bjh6Jp6pq3-~1fV;#dSW6XIThi7WcWIP)h z8rwsARVEVfOd#8>M_7MriYy4ajj*^KD&=ps#vUM?;#q($$N<8MAxM^wBl9hdAhRf- zYGaCoh6*WWO&v1K)+22|28@sm2J?okcjzqib{Cacu^5QUh?E*Q{Z>! zGG(YLqJWG{xsg|jUW#Y7?|M)J^)B$>*_)}f`cLNNW;vpVW(nO9C2gm&i`noQ_4p5OyshKN!% z)b7q{h$#1lJnu|#Xn5<{GyH?U_cQqX=f8l910VmEZ^FO!=l*Sc>U;iqeBIan4m>>n zEdKE?{6jPVKliggiC_80{}3-;JjdsM^Ec79hTfu!Q0stoi%7f1LfviEnu{GGlT8Ot zE!JJf=WA2q@+HHR?vQh$oNfy>9PrUx{0BBr)3H{~D(!PDRv4lgc{dj?OOIgv8~_JU z6|k3Kww&inzWZ%`PQYB=%v&Fw!MLx4s2`NY<- zy2Yn$J^nDi8eRUSFop3P#P&pIpP5l#R5yxZddmP6NbK0PEsO?CQG<}{GA6HuF4#qK zABQbC(1i>aR<6~f97yM&uz|)kNRqi9#Qv?dL&Q*3kO4awz+@`?mC}ifX�rPoBcO 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%0D7pp! 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�U%TvjZe|T5 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�? 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�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%0AC&4LrD+4A0q>Q?55JQ)hvf^~r zdtVqJM)bF(KJpf=IGf7g&TX6vfbl zd5tJI+(~HKrl)+WA#-B=Lu%7 zec*R|rFL2N7N_yqKGfwS^P14LOz+VBkj5e5LIwhf9Ri60VO~b*D(K1pw2v`YGt+y( zQhh5KmZH!AIe^$Bkj%4@bQ%&lu;zC1Cf+F`hfJB!(*7aCMvojHZaJW=Vfm3yK{I%C zGIq^pVy@IlnP!G3hfY^Lz-eAAPPvWLw43_JEFd$O2MI*6ldcOGC3pnHRK3rFn+(PT zr6gxWr8%4!4!&v7cP;KczK6DL0RpPJMi(3|F3-_4jkHL@GrV)C>k6c&lVx9pAF|9l z6WBRX%^4tPGR~u}Cj$Cn)bC^zpM3f$9zA{tS2bw6h>FB8?0uD8nG;<{)Q)g;c!bra z!*tW4A&1q)5=ZkR@&1URP|S)`dgt-s$M^8zr}t1dGxVDdzx~4>adP!KzWL6(Scj1E z?~rq-Au@^)0apMcEuYCyFAq>EnGRy9@C3p}W3*EOVoN;8KstRi%S1~04BPs0s$Paj zs9dwWQy@M42B9Jk%i5CUHjK}Zx*luN_VosT_~&2Z^ZTD+I;%lW^4`P{&`c*1g4(sn zKwSA#qD2YWb0kLA%o~PsK|G{PX6L>{l@rPJp9q#ZKG8qSkSCr6LBs2uwZoc}9$6MA z9?f#_UB~NtL4H?%cD;uc z>x{jG{**E8!oh=g4L0R)o-#%TsxsUj`A9CepAY&&;hVhPb&4&OW^G;o zAY-&aUja2pj_(ddHC(E^M`T2&J3RKP zF9=^Hvb_vA??HV`^Xtd~LFTimJ-U^2Ks>j_5M#z(kHz|<>&}?-2nX>ZIFB`UFdQ3_!vZluJ3Vrd4Z~~apQQ7Ro7#&JjdnvDabcCIy%8@ zUZLNtFt30XQsTS~J*uA2R1NCsG0x6cxPI*zo6RMD^U*zg>+9dd$+7oLyYv`1l0gIjq|bZtCE@N8beu zy_00k?LgYrtRxIhC5_EUZ*s=;EFjlKw!;ZsL#7e!a`K){{IbA_tWAL!+;=c}xl4~M zX|B|%cilUt<(FaP^1fYsVr*b3`%zxIzJJ;MyNoZp-Z!){>=|dlw_rTxUvYgk4;WUL zD@PJfuB$7Aqskox5x^{#b4L!|SBQNpK!<3MHF<2=ve2V)5=!qA`>edijIf;a@xqp$ zj}v$8YJ1o9GQ-Qu2xR!ZB>(9{uYGnP7gL#5Ly%Ve1DZR8M+;w;8D#aqNbcVRXevL` z!;)f3im+JSL&&Mpr$9{hKIbI0oJr0M4A$t@I1@Uyp!u@8k%osT@GeizWL>1c1x(WK zOaRIKb{`j;p#)C=GWY;rI#&R}gnhiSM(n?SFh6Ieq^ zj9&mmtS9YG|Leb=}EFc zM%h0sJS8~s2k~HdPB2WCH3F1EFf=bpWLRih)_crrW4N!=yR=`jqmplmxJBz8CN0PQq1Ogt}78vw@IK`>YT4FS3RgaX>(?&#Cv8l_yl0 z>saQ*o_4CFS9OK9i}NlPoXGU$s|sMklNV=L z^#CRg92{1Q4riB(WV{BOhl+_ZVJHM>m{#72RjKCGy>_~1q4?4RB zyz}U7>@LER`N;{In0`G`a^0_l8A@oY8V888*BeMpf+~LtJucyOQcJoAp=U9kI1-s; zdA^`V5l@6ZfdX3#QW+NEN#tBaNCwqp49(0-WUNz@s^+LjMgf-1BL{QJZB;Rp`PNod z@w41cX%TH%ZB4C7!!HTHnNkUO#U5Ev9Rllwp{ze_a;}`z;VEav6{#24b(K7Mtd#6K zM9gwU*I=ah>@$AkDIzhVGqg&Z<(Z(JJ^fO}1w-7tJ7&*oaJy3TuGIboDMM}YOWbag7YG=Bwx2g1Enr}rPAyL}7Su3SOo zYk)l3uE)i4g|6#y{p2d@s={Km!fLg~;e3uie)Jh0Jh{Zl^=mkL@eIxM8e$*eT#YsW zXQxkbd3lQ4*Wbcwc>!R8Z@qgL_a48%{f7^5^ZE^3zxpax%LOLBLYPf(vADp|!3jgASl_St>9hO&38IF0gD5fh2`*bcU%NjXF{Y!cI}lK4`(PuNrsX=>*%o7A|hcIUlH z&tkZb?e}qhm+@!g2$SJ_Y`>?>kySpk@3~$4bh!6rB1#tiGNVwo0gygdPUMj9W@EST z3@IY8yWKza`pOV&cSBmTd^idB?G3m4q5S&b}4aw za@HqP0=LJmI9cCWR1!#-TQ@)1>8w0t7Feq?1hP%yGSIeDd{c;RPRNwma*iB`ikvaU z>9`3$;VUYDhqI~e4YTaC0?WwIJS31)Spqo7{e9@LS}kyRcmjab^-@mwvx~19Y@Aa! zO-AM9q_z)wB1mG@CS3yoJ`%UDrYGGJoD|vNI@r0M7(%vASCXP@jGNLNiEeBsltC3A zS0vJtXT4^|Ps^HCSG-ghqFgZL@FQVN>L+5r3da1|puBb>YSkF=SO{QvH7o@EoS+FU zRoJi-P~e%>de5qW#zfml9uc|$Q*axElos*P|P2MT@=IrfhG`jAlIH!Fnn^XXj z)Zq~tQsa;%(2fv~DQFD0H{eM!vjeWivM0s8U%WN!w5Bvd*(@`Qf>(hxd`AqFHp+Yg z$cg?Ek3)jV8ir!{h81uY-cOt-B`{=7G!d!I5lrc0(!5npfum3PqYUtu$qNZiy%hqS ztFh@ie01+VPERj!d~}GY3AfuA1*paredy7JfQyR@eE-dF;pFHDeYYz1sO>{e6xFva zPL2+6^VUuL_IJO<(aBX@E>;MAi#P7xky6(w9Ma+0**QMB{{`xLo@DFA>Kv=}1~;zX zMCBb?V$HQ9hSW{Fz}3SB|M{`ca~pmj?BCbxxAdptYa;L+0;m>nDonwrL?ry%JUZb}={f*N-kCPW2-f7vq-sS-2Ib82HGy`*eJTs>w}KaD zML|?+!Ad}RsFUc7(-aMtd*o6UsZTz?Idpq#S2J<0)ER7(;@lrB2 zI2~`$-B&8WVNcGoao=ojVzf{9Kg%#|H1KdtS7C(b0;_;iB{3ug)MV9gKct6>wT`LS z$M&IW>SHFE!CVS1<7B`!&)ZZyg7aMDXSGjelm}z9v}wLd439KM48K!+8C5?jq$3Q+ zrja{>g{tL`S@Bq=6NX?9GqB3<0HXSCvdftXVxF3)ec>RX;nGlq9ULW2#02NP(yDng zs^?3>iLv0f+OG(~eGF8daBz5lXHQnR|L7?I!tLwV;X)5MLf1Pi+ZN%)a~#bj1a>x= zpsIlfUwnaf)4?xSXeNgck5xvySs*fDa&#T{A3nfqw{Ky7aDdN0zlU{q12?Xnpz!CKmYdExcmCsm`+4azWvslc=+fkLLYH@evaj8iC1pj!qaD`s4Iu2Zt&#E zb4;dFfCzoxH?KOhOTW∾lkZE)zY(1oY;2!! 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␢RJF8qeZkNI8f|_Z((|r&l%){P7gyDsYRnC zVC{-0bPk<#)8I?cDJ4RKW&&f0ldq4)J~!n9LG#@hlB<$T!zEpjjtH*f3`|r*Kw1?j zTe4O^$ytdRcaglSmN?)ScL4OTA02ZyI$>IkM_s!DM6-mYNwVwC?GRw6hoF%bL95;d zm4@!<&N2f|yd!CYZLi}#+7b}@bq4R}?*&YAP}D!8=UY_dxpqa=(`bz@X?*9o<-M)N ztJ4%Dh9E4eF9_MCkme!p*S#CJLcqFMHWuSMHB}^J3<{K_VDP*ZjBf@S0Z^*OegTN| zC`Lt?Ofn;DMJF@tAjcU_$?5{O^>0IkQxDTYcn$G~x_krqkur&_#moe*NqMp_#uqfs z1r=^piAcl@LDZ${YTR@!3Z{c4RVjaxo_#Es@_~>?xa~2vudsfi(Qpz|xZABW^6V zy=XGsA`4SQIGYaA66sPmwqAK_e;bx)`|oE)yrz-B>Z1Aj)&x_m+3pz$!4*0=TeTo^ zdt(>oFX>3dM$9@pRuJFE?&mxs0F;!i03a}07-DQ+2ZT(zQ_K-F;)x3_U0NVk3Pa`v4U>Su-ROr$`b2?L*q;17JGa9 z5XpG<`~~u2z`^Pm-~R9d%OT_7;27UOdycbdx;mXU*d3oAHV&>*ZB04 zpWyL>_wf4k6bHxmadWeUK(Jmcu^n%4a&(Nh-@S&`jC&`CI6FVbYIT71dWBLpUfjO# zW-GDA0koV~vS1(O6lD})h4H+Onr5`6s%|juqDWkxCJ_vSSKIXcHD?5nhis7^3d13@ zR?zb^)Bl{mb{S^*{k|6JgwO1_&-}A@J?}1K@4Q9!{C@rSZ`%VBR7cS8;%wE=kzsc~ zSuoBafKDS&f6VJojexPi8ghm)qzv)Aw(vGg<&j5aw*tC$5It~r%c|D@{;qrTUU5wq^M4ls%)J~vA><(=XS0Q+$+)QqXm|M9b9&;E zuucNTWEptfd+NLIKz6T7FwyQ1^1yct7Jzxk6)rd%d4ddwfK4$8;qnDAMU5oW11}f! zVUYw2@v6_OQ$Aq%d_3zk7O=AvBUi+QU&_`t`r`5u`+En-S)dr9T2KmuNO}uxMRb!A zcC2zx?!vpOd&QHbP7Lm0vdOR*8fig;`Ds-PBkjRd_9IzMkty_4N*CQC3S}-bs&!Ct zkR~ui&{DBnpgPF3KNV69XtOAzz#>v|g&P1|SrGTCB?@SQYV{}@14UV_^N8Ku>KaHj zP@zH(xb4HJi24Ku8f<+t6k5dqu!E`+7AS2k6;&|;ru4z;$y5*bFK*jVxSk(h|JV!IqIHO=LJzkZ9tI6~zVB4h2|%5% zj{bYn4xEPMhliH?_+3`ggGAcwTLVMYqDbfg6U6&-wLqa51+cc;;`ib{vsOq#g{5Q_ zg%@Y7o^r_{uwXfW$tuZul4CpHQ|i@;T$ ze}l(WLam84wKSPxXVisqQU>b7c~&z}*qptljM{qjyI&=>2nqnbQCh-U5fB&^#vfan z$`rgeOp`FGSlW^wwZZqI3g>S)z*vD}Xobl1DHiFNhzFdf)#|+7 z1s&In7>;W$-C~jJ=G3}B9UUBCv0UJbufM|YzW5TKfB6;u?(;w3?CKi80>A(KON>Ra zf3Sx)r*E*g9`N}75wvV^vn{yX6dWENW0f+bT;uBE90$iIIM`bQ5Zqi}(VxA@s7PjGp4g*T^HSRdWPoAXNyS#ff701?6Q(E+~s{%gEB zJHv8eMwU-L{S@nZ7)z7e0P<46cMgR+M zLddtCrq%G6diTfBMUkKt7P;#3w^YN5P(>NHD5YW?TbHJ}&f4q6yUtSo+NJN#b=_Ux z-^Dxn$e4+4E8>xd+RtBGJs>n z9z3==vS6%=HjkV(;S;x~f{~iH#c$%z^*roEM(sc@tS|jsy2Il@R=qIbwBj`9*IQ=| z*kwOZCcK-HXwYN8+2uJ(0`~Uyu-%SEZvqo!YVj+zQ=uvD|a9fA)*7?3g-oGGfRfr?PsrA}I)uWZAE2N3W z8Ji`FGOa{F4`J)dB$Io!8Yk81#F>&qxY22YOh#(87O1ec!MP$lyHr9sR})G!{<$GT z%mdX-f}Fn@@)C<0cSb$)etOi8uN?uV`MmnICeSgSHag0;bgvo}3gv50FSPPyzy_ z5kx$w!;CQMAhihV5SgKZ%D0WdR6O2Py?{V-k%PDU|%4k`TZ3yxwRbFeHJ3rieUqJ;<)6VH6GAGBntV$x0Zp zlhchiqG<*ju7{b96Gz%D(VOi~9|(nYrU`lO@*h9>}Urj#=TzktzJ9?c@H={=lg zwOgaMa_HE$@y)Ew_{M9hrypjKNVQA8_4IBR0w8pIrqk|3JunK~K)RG58F-ydJM+F) zw*aI6bpjOi7V31u zW9s#DPH1Zl%4o+XId=k^w49=@i3m_N#rpNiuJU*SV9KMw(_>62$0}2!r>(=7j6*M3OfUaOO?g@Vv(ybT0_t~q{jR+k!>>T5?>_%i^(*p5`N~fpM zpFw__JZg_bL;5uXMxzCFM}Tw%+KuCS)G>_yi5mgZv9GPvsF#y!OdoySh6cUbez35) zox3h2P@Ez(IsN&)r5PRtmVRx9Xs`K2yazR`l5HK$*r5Omz|PJ?^Tuc6iM0Y^#$g^5 z;R3={%+9veXhlIiBg%mbqbod;N;J}wj4!nH=2)f*7|*f9){uW!3z|mma9Q=S4j%o- z=&~4S{AmW%IAK17el88ya~xfS06gL+c&kEFR~5_mJW`@e(mQVQ!t9(YkB&49tBj6u z%%}nk-hq?!q@jm-gnts~2-Ip3z1bDt<#ILIhgty1l(h=fx`Q6be@&LD|D6Rm+B?MQ za0Zoxy}cFI2M5@03jX_F{~F(Y{|p~|^a-}(24`pIc>jZskOwPdesQtEA`iHCe2CSM z@a*L&(y+$i!9HHUc!8gM_EU`85pUkU#^Xm1aB}}1zW(khZZ;dNmJ9s$4__iLR`|)2 zPx1Wa8>|)!9PADF%^!Y?#}A(%A)^%F@uMd=zc|Ouc7xNiE3Ef!@ZjVGFJC=JMZ&@1 z0nRTku)khm$jR1GdK^KT?9Yif^pPMZ1=*cl-?%luE|W9T6Ch={_*oZNI$3pHFTrus zFd#3Ntv1r^)$5Ub&6~)~o&S?zcKBp|ZU5TUMY#V?|4daHJ^f79+tD_6>Adsv-(NQk zOn~ciGWqU&Tv4Mb_U-l>{wc2?6#>(pDRE$fsf2W#di06{SJzjtNCvmqCRZ?KmTRC* zy3aeyImhPAH;cc|jV|+7zwmci#1xG&@x5Kw0k^Gh^Jm@lz5aJ`;n6xmY0QhYNJ8P} z*gJ#GoEZD(_O)|(A0G6()}{JC8W~!1Pzg|X_Zpf)M04q8#3Rb$jbOIsnRMjaTb~?QDI;@W#ulbz;$)JU~+ggHFR6BeOA!dtRV`CJP4_cu_%H`+=YU?nPnY$=aea6#iW8 z7uWW;g?dZt{s1(uWf%u+y^|z!+Jq1*5eb#FB5Kt5I#J1CXO83{^Y+hqlCTa3Z3*fi zRw40wQ$7;CJJNf+0dX&X)Er1{ZdDDC*!6w}lC42yp0^9xifY|;+|o&zK{`*;D|Y5l zCJpxI49fHo*Ef_(R0m+)T38?v=TCvaa(F;GlFMZ4IC77v`B@V3sK0cW;us-6x2 zEArgBt~3xmp%BVPkn8(;x$@XY>@YfyJRLsQ7Jb%?-SHgCECAvWqUdN{oG00Srx`uD z&nk(Z|0z%l?Tm2%7%54sgcZvc zuP?;sG#TaECu2%rhI<0qYBkHWMY<3}Qs#4MnL;;+L^@_kjdyTQYz=bTOR++!rC1DQ zcHUKCa&c6JS?nst0NFj?obJAK3N%wHf#hCkfW}Mi0Razdfz8jbbSYJ4FkvknyFZP& z)iG6&5P6=OOiCWvI0sm*28ClH1*D=yFjX490)&{HbEDWGEEZei*1MFH{%14rbq3#9 zK~Bw8a;GB&=`R3Z+gGN(5y4bUX@q=uhg@j8*9_r3_6u_D2iLuFf~Q8cC=XTMkKdKx z<@T8dj^f<=6;RbqpCNbkI7`P7ZM9mIdl)F)Ddm+yyA|$_p53!}6>ov38TY%O+Wqk8 zJjdfmQ~gY&Imv_Nkp|KDEayiC=akX-f>dZJSS}WL`0ydl&(HAfH(%r1Z@xVgE)$-PH7+}p#eH?Oco#`_9fxu5Us&n(K1AoDRG6 z-$pi5V+()IjUN5`cNu4P>BiweNVmV+U)$ROg6?Mbxof=qz1E6aM*q7jaUo64O?38m ztjpi`_2VO8c<|7G&;++@6E*=MC8KJ^kj%1Qm{&OiM!Y9Ojj~(b?=pz=*0cat@3?0E znp?%1_p5>UZo2RMZa;>*UK65FdcT_zsk#jUm{{!r)G}SC$r!tNzcVt(4Fy^eIfN-o zMA{_x0FXy0;Q8k6b?D|9V+koZT_^&DejRVE0Zees>6yx?eNQ^T6L*ct3Tch8lFaCdD0FcQm1@*OYkIC(?sFSRAF6~;NI^HR2^e4lPhct zb&k}p+I0+3AbWiuq~sn&g!fRqs((regYO#w3E@yqf?QkI7x?(0f`!k^&IHMb9c=_i z39Ne&hLxTNpn2|T6wJxOz%{X!fVI2xpnC8Ci5PPp1rRT2oVpNq=9Gnks|45*-v{U! zwy#9$0M9KL$s+-B*&0#Ql}2T&XFCVJLQ&tu_RnabxCcFX{V#H#slws>-4$c)Jow3@ zqg1u)%EAK0o_?j$oR{1OU+*wL`ipe*iRTgsMd0s*ohX$i%WX?+|A2hQi%8Kozmpxo%o*oj0}nxq<(W8?QuVjUfwZu(dAKu2Nr{!@JORfG46TV!JfXNp*%zpiZNh zv&@ibmCAr3=$rfD4??3zUoBOOf`LRNfV!eu!|2e?DWe(G*Tf0S=(92sC5UTklyjLw zK5RuY?aU8;kCNQ;>%|-68XI9-qtxxUG^%QJVWS4=(Z?gG<~4UY7{DFPvBBq6KZ-2e zTKFvD7^%(|Jc`HVK5M`z6eE?nkZU|UG#GMe?3_m-XI~|R{9!UD>!)#VxwXC?lDt(s zVr^4u25b*^f>%BO>oi$Zu~Ag=bynwR!jM#tY7>`-19LFZl>fhIU`q0EIqjWnw4NLQlqUf;bIb%`&e3@`CTYX>a zQVNnRAW2b6%RGqngzHq|1;@$5L2_#Iv!~VIogu4L96Kvcj*jue{yzTsfBm=63LM|N zj}M+a!NJ}FFMs$RV@Y`O-p5$xC0@LIffp}d;{EqO#{GLII6uEYO8fXnzj%UsCu=Mu zp=!m|^$p&hpJBbX!mDR5aDBDKgNG+LI{rC+^Z6fe@7{g9ckcxM@?ZUHeDcYsxOZ}b z*Jr1A@BTw9bH>x>KVZ3B;`<-I!OuVYbKJl82&ZSKI5|1Q>G>7T&M&ZBt}t%5*gIHZ zZ+{<~?TCZ*%IivUjU8_Y>+|(RZUHLK6fHVMqmEmNI|)X`i|QKPTN)kpaCZiDcqj}8 zYR7mx(!JHm5#3SuiF^1*blQI3=eqZ<`^{-&*EMseNXW21^4(pp{<_;nA^*P82=03z z35@cKqS&fn(QVoBoImyTqYW^|FfA)GJlFwvdF0&-QLQ!V;lTm6n=8};)H=H5cWS(t zTdMk&)c__&g`I|g?lTh8yNoaW^%LUJUN+q(s`>lxvg++(O!2q>tHmM;{7b%;c0d!u z`zFtGzo}SZ+UkgE*H|SEBtaPB_o&*fE8;SJkhu;{ z57m{1G*MjV>kUa59F%b#`a)?*nsJk%vqlfqjRJ1`L#H@P6B3ebA5*7-B&Q#7&>uC4 z9At}F>PDgB!#s8Z^CUoJ9g4;rlahjbMZGmQ0a&ATwa&3>XM*W%IjF!D1fNHOD~VN+ zJbI^=;xy(4NJFR|ZDw3;$ZmY#v(-~790peq7cyGG(ylB7l$qcOgx{I>Rz;~m$_$q` zz>|YtSm-z5RT9>0Y5P2>PedgF7J0!4gaCs=&>5&G6rUk`?VM^k0vRyf#tpEwR`UjO zXs%39X`x<18fHWoyXh42E~n|7tlTB}3BlqZ8KYWF8QXI)qb2}RdJQ$sD7~`~YlEIs z0!?I1$uoInXS#;t(6b3`4(t|q{f#f0hITd}@At_85P(aW!0NWpSP`6>>zVT;3a_P5z!fJknb*mAQMH<|urtMOLpcE@d(|nCFkUpP z1-uZj^B$LZ5q3}6u<`0cLE6o%XtgjkPDCe z^Zj5KC`OwkKbuu7DkXW15fNM4QWOku%VPY}Jf!e64>Mm>wVGhb4ariai)8f{s!p>^29xz>m7$TH$U%y1T z6N*eiX2gsu{BUOlqTsXbVFud$x@ZbB^imPBvyovK6|CK6%i!y%F0;kkY&*6{jOJq9 zBkww-Xx?Wh9vWAo^C0O%+HCEy)uVDvzNeI(-kiUyrNYplJxFr^RE>iS8q}MT$GImr zMkPr0=qKX0N`=CVRdJ4#SlZXn`BkZu_dskk9Y-vn>$J`DKlF~$L~KZB0MATwWMlwo z$esxiEY%y4g22MU;!*t}9^9?;CGy*vlMBX5daV zc#}W*88=<1GjuzpQ=ZcR%Tz%cJLfOSF+ooy5|<@nZROGSy%i)zTO%P#Ax@2{8#Z^t zfzhGqZP{tCIZ6`P^~l|II~8FC!T}}7=D>5FOazpY8bK+f&>>c?1~}zWnw}y#L-4?5_`Saejftdynv!|M;)*{P}ap*%>acF7WpCOZ?>1 z&v3YZgzZ?czkd&J&rY$ow}(7rKr7aJD?I)Fdptb3kM(MaSFc~;-tm2N=sMk)5liFz z+xtd`;RGedMDnw!ZO`KD8n2dGT$ghv6nPa51k&^@)dDTGJ^$a#Ul&Q$zD zzH8`2^T3E7`}%PP80EpUKWtv984zHfrx30{Yp3Om)B#sZyO}QHJd&f|=;=bst%biG zU2&T*?t1kW^ZvWL3@f|7cWbHUOld~60wCuF(y%}&WM{@mt1^uQruY|h>uLYf5ad+G zZjYyVog83l1E8vUE5|Qnp&*>?5jAsb&aQAcLB=yd<^UhGdZ;snorY3psWc3a<=VT? zyQ8&Q)l&yRoFBJBH?mf{#+9+ENO{2d#nQIW zCn(uLsu^|DU=(M%3yn3VsH-;P67NHvT3O81Ry(|n)r+m_q$5xDdL|ZOqA-AKYLm^K zMwW%w!zpUtO<_>9bhyOI9lyaN`sW`RQ~o@ab@M0JB`_NW|F zsw@{iBIG2Q0a2*Uxydr+kY^k_l|d6c8KqP=)=4|>)s(QLG|KTSsoy{(MwN&lX93FS zkzi!mHg~3HnzqRZsf3hjM4*&fy>^w&7b%0rEK8SkW2qZID$^@F>;Z8eWhc|31c?y< z40-U#6^l%=Gu?Q^ATUE-sZP@pIKgI+g5icyT^pnP9t57s)r>g_O#k$==54Vpy|vvWrIubr&}jCV&N1RYl?&G4 zYf&>E8tH!nvhBb)0Tmqe-<70~`{a8Fqj+8P%)27=2^txh&R7H0@FGMjO|GyHm_ z2%8BoE{ettc0!6mphDw(FPB{a!CGKftz@6p1y3DQtD@F|zGyB$$BpBk&Y4}^0GHkQ zWIT27Lf09hR)8I#vk9oBqKsZ|B$1XH-X+$ktR1F+8Td%^qeXJG(Y8jEzp3z#l!MGXSVZl8pxPNkjFTVZ;VSiAH4Sn-+uobDXox` z;ES)mz~jg7Lr`(BUg6V^KgRQyFK{z%P)fm@({tQA+Q(|ShConyuW(0OG(1z8?=d6g zlQ><|fdZEsn#6Ed(QtCMNBF$Gdz#V!50_bf_G>99UbvaHOyQpgpQ5D|EC=hYkM9QWkqM<*Y0`#h6!PfQ14L-pvgTJ?8K38jg7!xleZi6f?}7~ z*SNaA!NdC}lR67FUI~F`!c-usq81;g1D}8dM9dDv)IokqJPg?hcqJ~>3DD@6|N{zA)+y_+lr>Rgo2=xeGP9iOo5Hsw8m9C1r3LU6-JtZ+~f%~t{L8B_;QO}vIwf$6|$6w2qGOO82GDzMb1=2 z1XQE9#=mM5$wcE|?HC}ssEDl<=@~G? zNFoRe(9JFyjZXyq)oPu!7-l{o=#wK5F&LxLtgk<(1j+RerNNuVpRu+`7#91@jA_z}lm=`)eR;_CS>qoWL-y#ZTCpAy*2{%O z@;Fbkw9X-l6-F`Ru4OfRO-;EfLE}*GC{Y#2Iq-0Gxg`VlDn(ehP!>2-Hacs@)KUvl zUc_1$zwwA&Un8SOb^wwH&?v=Zg^-W<%2sVS3BESdVvr4h+9NDoEiuB&cHH@Z85cCW z5mZpctXEi$*|INwQ;3KL_|A)c>Lzi%`AS}LFMd7In3GmXx%5X_AR+O7a> zM@{}>Msll>v8@GKD;C275aUrPiPIyoLIR|^b3?qjq+|SOi6%a;L~vm5i5Yq0Y$^ar zLCdDhx&t71kN_ROqJe{kY@nKPIAshX7|UpmxDE>|;By%w&5X1hhHX?xPS9e{9nuiI zRKYZ5;|JN#FhE~{&48GUbEsO8vp|ZaM^oM@)YJRyn>JpbX--j-v*{S>ysi2=I2LEr zT1Y-y4>r~)#W2AlC9GHGp#13ZW7N7qE!A)a6nVWysshz5#_KC6tV^Fchb*qxoK!a7 zR7Wf%;pF55H#gTfKR?G$fBG|=onGL}FTcW*Pd>r<(w5Xt0m4Z z&QWW@{(3!$VCsVb;ygNyJNEWF(tuR;J;h+%e+@Oqg`101(}hPo`EN6^xI&`hsV5FNr4bV& zQw(=8bePJFfWSeut*4PfQ$R%l84^w}&aoJlSTEKsssRRKd?5?~6@aa?AtRtH{^;{V zQ-~mdRz=PhWfOwb*U4cPDjS_@fvBZ#APE+du*}Kl?8#q;->-x~WVaj(!5)vhb<(WWs;RU@Rj`IR-iPYUrOu&$V(U9YthTp0mqMEp+l zmh!&B{o!fN=(Y>%x7{F$pu02bA5t|oGHK9~9#z3gpd*sQS##QaU{-)|+b(9TN zVT?5+_?f+eHhip=ohqu;yWs3R&%q2Kj8qc<3*YPZo;XlYrB_WRkEVgnlr-*j0R$LF zr)g>7(^1z&3>e$$duT{^?t9=+-7sdJ3j=V;6^!6#KAzTMxG84l<IQ`lAU3|CjuF~5x^U$ljfQw`f|qnW6#Om+M+W3)A_=@EPA7g{X4^hH z)0EudIeShiwqJ>O&&SWDqx_awXYv3)uh5_+OKWi}4%7`{jzXNig>qla10QX;)p1eM zZjUWupc#8pOt7!B0+CSkEFDr}9;`vevzVk}7!o9z;SgG|t{FKE$TDEmisewOaDRoT zk9Yg&>(6vfL@H%FQ}fPN-HrfgDb}Zpb}300%*ra2M{1k_zEMvd>u-6)&%M)y3Q9IyYQr`R-_CphXG>&w#CzA+<{?s z6t8*Gi@EOjwv=Fn4y~3^(3eLb26kg@p8zxH_M=6OGeQg6qK#^?XwKyKtcI+x5Hl{Q zD(WIfDHe{U?QL#nADzoct;?rsMOp}V-bN%Edt?ry#y{-n%H=&7QuvQ!3Y9{;e_maT!Qxz@1?3ZI6T0!XV38F%>w)D z6|T1%Bu_i9qb2pSb>857OR_Pp(T!43{9er@v0N-sp*TA`#qr@jKKuA1ynXW;KfHK} z_a8jOVzt6I-~WKay#uV*d$`(M;`sO;-ke|I{QMFR?j7O9%jY;ee1Lkcc=75Sqi%6I zzQKo&A7OcXj5lYeI5^k`#sP0nFOl;Ct07r*AooZcrU;(goqe9gC}q%g*Q2kx6XSau zGKWN}E_+lXG83_<#^XwH=Yiwn`D?BV&kZVbLq>m1|9+3n`VPT!ef}<8dY9hZ|2;R_ z-0qxo`#bj2*08UWYKG4iQQP>N8Cz_dDktlKAGg=;PksI92pDS!$QWty!Fi5kRWKm0 zLmM8_xakgF=MA4N{4Q(Q+&V|)KgjY9;&|6*^NreNkm;}46(RG-?sXS2D+Lt+cnzo$ z;Iw+Nw1W0IfiS_G9V0~xN$Red5qCd7$|nx**7^c{eaPv0CPYi+$^CkqfyP7_1rqs+ zoKJ+}n%B#g*G5Jo5s;krd60RS%k6b`6ypWofi&Rk>I!42czALz)-{v>7IAeCivhS2 zYa*#y9R#$RCz8wyq>!e-c4v{hm{g$I0MC%Kna6xj2o_WU7FVrabHg{j1A?3~8LY2S zGKEWg{PxunV8PBM0)!I!2E*50I5hfStz>vVd*qjXwy0fjmgP zs|f3=1R;x`Z3U8h`#Nw_3owXS4m(52g?y8c+_M-2RwJrAR~EUTjn{O_crftjWA6Od zBTeiYThEHNrQ+u!lv*BDpA8bJI{%TA#a;axr1(tl2}N+DvNG5mfPc?EXnuZj4?e#inB6_ zQ0wKC%iaLR!3pB~($U%AL=dvgG}{n@jl8=1D%Xxi{uqGEMVIEq&u@pH!lB>_HWy0U zlY$;6U^5KG&7c>xKz#r6(2WNgMnoEgV~>4psQeBdSz${-dT=^-{zN0Jpx+My?*vIU z=LL=XfJOCH;(*4pjTSO03etkcNdcojhE-heXJaf{hiS>*oG+oBjW#bDVB5HQ1Eyg= z@u;B2uFV`0VP8h$&UD9ZF~f?dKN=R2keM2L?FPC&b;q}bb^d5jQvk&c;yL$`&5TA1 zleNodL|S?9pYD8<$K7l;V9u@xBj7Y>%k0jcDy|8cj^7|3ZzMo|LH@5Efx~)#lt$Gf zV_`M$(g3duTCiR$aIk-Xx94YAtyjn?gIU)C^$wkO-Ycw5QW3ZlTK#-FCab`5wL*P+ zidS#XaCCf#FTeU4AAC^p)6af_%d1O#{`=qKFaPqdadB~uU;W*0@Mk~&1=i~&uCCAV z)1UqXUw-)&p1pXD>+2i5e0`2z{PZ)NEe2d(U*PcY2rphe$8uO;xmcoXx7b@Bpw>%l z$AZ-&!y;SA|GS)GRH*g_7^ihcN~i4g|NJxy%%Cz1R=Pmx;CAenDpFEFGh|G-y1oXW zSgjJaH(q;3YM8fcY3Z=&3?Y2&(wG03;iWgU@b6tlin%dn{=E6UCyur5v)&*y;n%Zk zZqEY%n!#mqy`A*QS@acwhvfNlGgQZLwEo!Fk2b)V1+=ql3P5XqEP*QxNmvnHCY#9x zo8-!2>H+AZVER;~{^xvwdm{orzsm^H|IPR2hKv5Si%|N{{yz7a{O#9_+Yx)~{xgZ< zfr^5h6Nk0rXnA$9! z7UquHEABDuNltxADT*ovsJ5?qDu4CX&~{d`uYXmw)^#v&ZP_POwq3zhTDu$sK_T!w z(AY;BS9~rWMi;cw(P9szO($@V4FQkKiNyAZ((X>>-QG6Z+tAFQo&CrKH>sjU!8jec zMlQWFQL@HFOzHP#sP@4N)b!HfuKy%q}0<8Y{Mq)>bvJr(JC}%1z2(Lrnk=y zT=<0`6hw4QQ;-PWJ-53e7AX0A2oR0zQy3VkUvFod_d>T01?<=XQH&jpjsYx5tI2a) z16>-WiYxWv_6)T(vs5!ro#|>iut0Dv4jhw#rdZF8)*u4$niL8II25i?zs>*%>Zt(q z9XAXUrZz~xWX<%bMV$cNH!`S5n?6KI31hK0Oi>{ZX-bF)EGny`JAejZo~O2!&UyVe zMT?K{aBrB`PasiPGs1(Fq4Ef$2Lf`(48i~tYX{8I;F`CewHP#Lp^ewh?-cO!iOo3I zLddDKsXGcJXtDSV1>;yyRWS@6>7`btyYU!>YH7v~JJ?P<>k23TX zp3yi*Q))#i1v%2~NP9K*hcp+FO6tbp*jj$C_t4pgwNl|^S3C_q9-s~lbL^D;sw8jX zUwgK8n87`l{ddUoni1A(R^x({Cun> z)`4dssFe{j3J9!IN|ZF>_#4sL{<||zVTN+4W?YfDr}0NWHUNMT5_9Njn`w2T><(x9 z)MTYSlLd$=RV|gQw)0=N+%@}#s5^~J*<BGjsgX~4M{m(`AE_GV0R#J~Ch>@Ui zPY8|GzE&b;bRca;%WsZ6^u{>G*OHa)MW2G%b_JezY*oh%!;F;xM_l*x^W8G^A| zZJ4BrqSh>6NZtU%`Fl>O(U>%2sF1%m!GnhO47&iZ^W(Dc>Ljs zJiztM2DOejIXT33v%xr)h(;xmI1Bgp8YYXTDrlQtJv|g^^<+Ny@DUD{g3V10o>0ev z&344u)dn{=6^o(b`esC_f>&?OP_NH%e7MGXeSp)84aU+6_qX$QTR)x~H<}!j$?v@` z-5XZsI(Yv+f1iKG*RK2Tx_*KsFqOG~9|O{Kd|gQt?07kwZqF&*2S5JxqYW@dNF0?* zg(#E-05$CWEUHGZ4z$Tn!Yy@b$?A*#+-if?{RFVn`qe*&uXk-~|J_{%jQ$>XUH>kF z%REHNK12ZMu<-dIZ9uLAX;ixG#^|T;F3V*9`P@)MR5S6A?|4n_$vvOU)5kvfTjN@S zZWB7vF68_|X|Nmh>oy+;l?_zN(AsLdz}t-Gj5lW&sI}tgU=LM`fu1A~HS2Wqd`vq9 z*^6l>(-xX+i7N0;D*{MPV5I*-tlN(PJj+|ApdM=h4Uyt=E5!>Kq7`5(!Zm7!6*Ec@ z6qDHi5tBh-j@Eg`zUL7w@Xjbbvymngwoac`P;Xv^zbSC)3CO(%K4{1Hl_9VmC~sq6 z9<@*b5TwLbl@jAJP^btWpTNiE`y8_@F$M~?)~cK`h6LH}RkeVrKLkHbC~ADyJQ30W zZwRU$$f@rULJ4UKYHgo!eJa<|=BFTrb)y=UJpr_q;ImW!SYDL)poZ(x1~j*(Q65XjMgT+)HW}`jGes##{J^TX(-v8N1F@jYGO!~_Ds@plEz1P zUK^}&SqYc2Xf2%=Z1nfmmS#g_h@+FJpvQBj9{^6{tjQWQq90ytF^}-tH}|{iSVLJ zGhnoHI|GK&_;c^5X!4H^B}B?Z97)xn>^Nht;ITH*+=q@Qc6jRgCjdy#^tHg?Qeoqe}V=@1xNYG&8bRsRX zWE}ln)-mYz#LA#lPw_Lw@MauCnPg$vsv7dg2|U=k@hW$Q`IxF?lrjPkEW&D3!tj)O z-lG6J#8Ix5*c)k)*&*u`tXoDoBb6MhGfqg5gc$}yJ^>J~c~a=8VRVa~c-Cf1!4nYs zrvS!ICK}(Vjph`f!8G_9crK)i2IW3f-%qZm$rNX^cGgGYqFyREO^5(moGb82h%=%P z=tzE^prr(#1gyRhM1Uk%Cdws~{mC0iv<3l|TGaDM7Cui|LXTrpf{LD(wvIqH{DReD zg}wa)oL^q!@ZiWI8GWC7hZ(=r>cFvSiQ^1O*p4?iJlMy>2M_S{*$;T~!AIC%F7d11 z{0f&>TYUQQ#~AAdzx(2M_~he1!{zZcPS4JfmwULn9C7b(gHJwsh|mA#fVqq^j)BkOyf9nDWnznr>3Ecy z469JQ_ud2i;n)8f|Jncb|AO)I8r!6}+6or?_i+5tXL$dkPjUajBh#WaRHDi3?RxH$I8c^=z)1-#)eFqoP9!zNe5NC6bE#IUEALTJgNXYA0$N%iMkk&s$45 z;|vLSoPq+VNUTG5h8S8-@Q0R)B;A}f{^Ga<=)o^X%XnRpD4rNbF47(MTsj)+6h@is z6G3&O5E_sO7`Rq;9iu*&a@PZnb}w2Tr7INJ;H&gx0;U=Cgu&1HmvrB)-Ck;MGzO1C zJ9G9M`B4XowEf=ll-+9_Yt?{d1CYieRNQl0ZEw6@qJ`md4lEq%{GygPC9Y5rw9!kM z8bk3KE;Z;Gah|G=GxkT@ny_1wbp>#T0w2Y_l-@|_N@PSc_?h$cfTHS(j-NwFtg#tn z?Ox5;YHOP~XJvc{L4-Z0FrpC6tE2XS*zHm~19bc9genq&Z6{wt1_P*LbtS?~t+iG+ zm|0CS)h1h$zzsGv3TKM6_UAPG_VcLK(#W`9zTCDaFas|;2t(BhHdaJ|;C4Rjde+jW zqFuFlog2*{l*UlFYV)eM#kA-VboAD0skBIsxZIr_eerqjd@YoV?V~wXs52$4->3G{ zzz(Dpa!}S=Rp+loot~3S=#7aE)5}lL+@ye%?hQB?Th}xSRs_H2ajrQ-2!PwWuRo z0}Z*`%d8lSn&G65V|%|#M`>%*?J6T~Chu7RR`@fTWb^xLA!wM&lwPMTrkiMK2ddC!HgH4A92yuK^q8`Y&Ex{qVW_|rQ8;{K^(&Yq^ zCT9_+#G8)6#Epc27t^m83sVi6oUcR%fumfmbuzM6^K$2ugFlQOscc2&P2ROAIzXj* z&5gE7>XBlt4i}X-H;nXI0lxpqb3;ofxkxD}5oC$56?APxLtQ>2kF0kz`f;1JKQuW^2P zj^l$pY)1`8rPaO5rVpvDgJzd!VlpAC8A*`@wQh0$_y}*$FYxB|OMLL?G5+G0e}k{S z{tD;ce2FJdp5Uu*zQ@&Oz+e3NXSjEIhOfW-77rdh#J4{@#d6rg!Tuq}a)tNbe;-xf z;PsnR9PBS}asC$f?|*>3{XJ|q8$dEHuQpJvI62ryO>N%6+qF5>ck(QtvChKO3UY8e|1%=Yds{rE0DdY40C*FERPkzIQK{Qfl5?7Bz)th@B| z`5floZF)M3qBofhv{PfJ(*U8#e%c-`|%Zd#1c35c$+uPGOZ?VW32m1$6%+z5x*a|rm zOzD7$BT7ll!@83$V4arO2_?48=bF8)3_*GF_N%Ao=UA@>?620*cG>`b1#&AuN)K+z z9@byI%MDsyaiZ7D$IR;a6zTjexmU3&>>gt4Z6L207 zfGEt@fD_PrWr5_%8I1?_{B{nVUW6N6UFHu>EOlPP<~0#8H{G6>!&_6kvlhw%6sieu zKo{OYRZu|>Z;E?RgU%_`VTvN={KG+hoGCXXC=_gY+O|c^D9J*?09pY`8^2Z>Rmd+m zRe`tx#y}LhNSFqW1B_SgXqyA&P&)p`r#Tq5L&;X@&68#%U~6U_YI5woh#f-KALs#E z;Wf^%8rUX)?Z##VHW_!V3K?|DLpYB~y@kFQBYB$bd*kN>K$y^1mJAjwjY6GHV>HO| zIhG#Fvj;>0J@P0>oUPtVXWp*_79WnBflzJ*%&SKguzjhLMf>{8O8E7V^s5vReR8 zRbGZ6Cv}h^xwp~g>7H3>dj;*zkn-N{DW2hqUZD)R<+&78W^8YLPiVO9=+V}?r7J-h zxxq+Ymv>;nWJc3;DVTO6qd`@V2x$DB2I&Ba+VF=0lVZ#wO|J0%83OIkt^%wt0&(NA z|G_m3x&qTOd=1=))0{F*>9T1MCpKey6vd7a+x>Im`zk^gu(BjYgT)%E@)u-FSiRHR)6wyKFWaOF?5^mybJ()r%_tGVvnQl{>z`7Y*DB zu95v!oGyr&jpkb_ld5G(EEa;j^&01w*VtdJgU?hLG#ms@V+kV!xj3%%d)f+Cpk`>T z*jp}exVMIi;@f9W@&5Zy@Mk~!8UD?G`~So1H|O~5r+)?!!EgTV*ZAoD53pJ;akJfE z|L`7OJ$r@YlZ3@0IMa`@bLaUEQXBJ(=!|#?qhRvg*T^X zcyMwO>);F1VQ9*FI)QCp<1D-PXSG`4+poUD)9+s5gP;C0JozX83;f6b`~L%e`OA+X zP@F7AJlIP(+)KDwtnl{o2BlV1k1k{M-OXl$89xzQk0;Zd}>wUhoBlLG|a)14<_nVR7 zZeqC0AhYYa{kZ(g02mEW14NiSZ1Eu(mg`%(Fy*4VH~&s+VB-EzWHjZ-N9fPL>%P;u zPYqHE7ES9gXMj6s+p`#sAq@0hy4e#urC@FZ5zh-Yp>1>Vapa8a%?9UJS9oymA#zG6 zb@b=i{iJ3f~ z_p!fP`x>_5q%3^ORMv2w_z%(q4GmG8ne+E$Ds>ti#yN(tt_{?;`Wk|5DJZpo5e+8m z6!79-q>h|Zc(KL3Hr2!4$5O((T8eqT*s7c0h2;gJa}-*aB+`Qbu|CE)5(K5CDmX}0 zAUvc7cnvhsdulwT0X|+=wR--s0&3y4P_G?hqjB8ZHi5>joSBa1Z7)G3ggMw9d-nOk zX~DwnO`)B3{IPiz|0Zi$glCp%Cy51U{RC7*o;+B411&;~hnMvLsIU~z31;hJ0=PH> zO2x%#9%3zYU6E8M>bRYp&*cyOardOgE%6Jf~? zjJX~;A=L)(Z#nvfMggCHMYk4qof!_8vfxFk-O$})Pwd(Ah{m}Qfa|KTx)-%3SC;)e zDUF~K%M|jZQy~2i25&XlXUfGeNUUY`2%VfV^02^WEbuaD!Gn!2RBSNdYO^sAm5eVj z8be}@B@98?4P0h$mXKBb+;b1!Kso^;l``f9upVA@+7%IGu&YAjqiz0NlIB3M7QLUqUl_YZrk9r>ihd`@XeDQU+=8RDMzX%(070_eqBkY@XrJ;k`ShP9atp-OmIg zGI+L~yT>>ntao5erYOcN!l|@wF64da3|?^-NK0+XzEW{+1&q+}dCk{I5k{n-`yof9 z7MGVi&}y1xS8k|Y`u_#C4>f~l8Y0N-SjWh5?hMyehj5g*_HARQWC4$^Evo@JGqR_^s$>RMm>n+vY{Q6I;B~~v4+gd@hC3se zRuY)Ww?XZ>N{+>L=u!XAoNg&4EWBf+g6V6HL(q)LlG&ZBdseh-uRhUcT+hxsj5ns1 zuGmfiXh@HgX{SFZphe6YEQ*CY&JtvT!&JT9cW#aX)@dTBNFF1DqTk;@R^TIJ>^W@!kR8?Z=T^_bduzb-jis z(3yqv#i|M^1;>X6IQ!k_kSpul`1;Mi#V>yPQ~c9^@;CVEoA2<$v+wa&fALrN@{8Z& z<(muq;%7g>=4OL~^%9SM@iG4GZ-0yJ_8xwC_5-fAicdfN2rr+%!FIdFAQ>-y_yPAH z+{a?Ez_=NaWx!^8jhpQTd&{Mzdqqir-Y{V6r*7~J13&9uRX|I{W-M4d{u%!A=l>)8 zhyT%kjQ{n&_Yu+vynKC*i_3Go+N_Y%Jrv!dmMyXvk4`YYP~nB40W5}u!BfL%0GK#C zIvzOxz5lt(=yuoVyLCp~`Q7<<=AZ2v-(3clyX*jbj|NR~vT14P2&%IGcYNp__xw{{ zKQ09+8M6%!g##rLPhY&R2iJ?m0^=+NN!7!4CpmF42~oCdY7p(M@yr%s7a`2AofFo4 zH|g4!ySOr*FnaE8O7yJ4L*i}yQZ`SJIC!e=!AR^u=>*ZZ_Ck4hYX>N~i)Xh6NTmW2^;rELMyyC2UK@wv1Th zgymx42GHN-%)X@Xh%`Q&Yls{28-4>oH=P(}d z4(*h1FFTM$kSx6z5KPyd}V$&Z}w^VT>{n{dZ%JG~|y0lL!)2Z>yW! za}1*$D7=GKjid@sR)}YqN3vT#RUqY1mJ}@h*$mDWUFZAC`Kz^z6&U|$3V}zmvCdnh z4Z_b(Ga_xa8*H{)Xd3Y1^%*YSzOkssELbj=$az4@1K!>gZ0iPJeDeiT8n9aLS%jgA zrI?60c4!E$UNzgE-%|Br-X3`*=nVE^>KgYGXe`CZnQf67A%QTki68DTVnhMbAO_E5 zaoTfP8skwa!!j8sjS4}_Xfo3XCv_y@il!IT4rQ`a`c1}ljup)C5f#9W)*^Dmc}$#g zzfa!=nDi95%W0wp{Wl|#bV>^y6Fi?84N$P?JLd&ZW^%n)K)TywaaXSTjE3XB>0}P>!*V9r?J+K)_oe z``#qy^&SD3q@`bS%Y)DN{D_V^u~a~#UHiKzJF3|Ju$x|txzf1fxA5~^wIVb6r*o=I zBIKOmRfapndf0u2yfFOX4u^~07f5&N1E~s8<|L8 zG&mlsTD-Y{T1TvkI5)(PDdmnX8$LJ3U|Tn0<-rX&RjFf{iP^BZtsf)IT0JbsI^h%& zV7sxgbEYC-8|-EjR@`j1IKRHe`PDT{1u^^;HHv)$#A$XE+jmh3EQaokSmZHjx{LU5 z{zs!>2omlGql;mw?W;w`{{B9$Z*Fk7-bbw_<1@sfX_H{^wJiRA;3f5lcqmc<7CGVJ z{RjB%mtW)KkDp+a3}$II&rJb(2D5AL7f=K2czdn?>K zIYB8Fg~A2~z20P7KqHztkdlZQUBudvn?a!9YE$s<{rmqB{+Iu&e;*$mS6sch#%i&E zmV!lIqiDru9I;vsSPY}#Yx2Rwv!~dWEymG5=Z?*ELEDSQd){Saxa&K+?lBKQXwFKx zb-(`lU7x+{y}R`O`TFr)Gg70(VL%7X~_|C6a2%f0LU=l z>iQa2HyfOI+v!rq@EUbG=45|VS=XT%E!=YAV{G8G-+Ki;{scP->i2<%t%q?*VBxp0 z&#DS^R4`?V@~A>Av{@c4VEN(-jbfPrjm54lV+})5 zDZp5Ox91y-+by;_0CfupFboT*2o|MawM@9a*S z78O;uSdU8_9h;}p!h~xG%WWOQ@FIo4OvCd=#Zsh?*3iy-Z8YJ;=%CWx7ZfD#N!u5$ ztdZkA1&^XFM3$Z@Dg*?3iJy5`cJ*k$&1lgfNfM-5_%^aY3ZsKGKnV92s)jRZtOL=D zIFR)`a)=A&1SuZzLIsM&2YqkVQ@wzS?1qGGSJXF{bpU<=mBv_KRV$!g)mvL9jM|&p zOH*KKiT1w$qPtCw+8s@BnX*s=7?W2BteX>4R8TDxU6b*^%(O8t`cY`;N{nV8r)Xj0 zE%nDE3qnTOxsC`mj|K~1C=Sr8gK*I(2N9{5K!kaVJ0YrH#F&%TX+=WO3B5$kW0{iI z+o%pw6I1GHC<#`7qISwmOJ6mTA-HYy4^`xBpOHDc(&e(vUfI)tVLet7l*4<9~;6yWsY5;<8Ng4PKv2W`fg zcIB#e5p$7J7O8=*q^UOJ2oO7MU$NG`RQ3WFMGYB*45fk=Ks#tcA_I^w6ICsx$*@y% z3UCxW+hd_&xJA(>j~*}@9z)*p%(G(&YiaZjq@0@prHR63gtF@Z79g*jtB;k%imSGQ z_LC@PMPP%$Q?Y3X7jb7oZpIipKQ8OkI@obfq9awD2XW<`W90;qo}WmuX9AFf{r$$A4}~t z8Z5PVw98h{a>}^bZt>>L8%sU% z$fCutfOsvKaU4C}T9Af};@#hJ&QTw1+-^-ab^5_Lj##huaPQSs8T)UiM-03 zhT(j1S!__$ji-KAYh_(EB^B%4_jf@O~Pdr}cHEw&NBbeefQ>eEKb3yn2p*^ozfQmV*E8H^0V5AAN*h{^HN@ z;>9z(tRsH@+0XH-U%kcY`3-c~!}C|K@xkKH*m|E*-fXcv~d9O4I(uFZ27R!Xclm<%d#dd2sa>|21Qo=9{6G1)C z<(eB>_K!MDr^C0@aW!&&P1T(;N%qIKWz2bhtFgF7m{djo)36@l}LbGY= zX>CRrSNxO;2t6;!My2gJBJBy)6^!%CD=b$_?61s13^d?@P@)ND>7nCb{eU64P|vZ< z1S!)AnYdthCK*%J8buD()u)umh)qxyK^S04ws}+8frxvl+xiqo8Cr{bwVakq#ikgL zFMfv9l|IwM1-c!PX+#4RCWC$BcC_f@3~YoRbsx^wP6s#!L3BX&VSWOG38~1Q0Pb;zdGX{R`Iu>~@vEI$_*A%SD z`RLA6iR)~P6!=;dm7Z0nE#3A?3p{+?YX?d5N{gpdsaDGy=YAV1kMX2Lu+tS66ifSJ zA#WCmP05WWqXF?y6nH;B7rh~qJVe{nF{(uVxpkUx2-wwnFngdQVR0{%cTo~QUr89f zXL}WCPQKLDjV_4*k7ojAz~UL?xlWR-P&qY8(5S1yY(!HlzS?`Lr%+T#fLnKvuPQL-`)-{b$LR5q(o6HDDeqe@#0*UM; z8ex({a0|#v7=vz%j%pk>SF~(%z%Ft+;rmR}Bi$4;+9#7s7U`Oy%C1?I{VrSleiDj5 z&A;OF@bAdf0F}UcWff_WupKLIHdefJe}4}rClB%Bn*AswwD`EuVAJsLz&ij&|FTni)}=2K;Pq+~5I zfQ4I;AMotC0gAPwDmwlHfhGk!JtE~sW9AX|wXJ4^G36B=0l;XrO2`+o9ezGjU)=$bi99{>XE=)^t<=8b!u+w0kIkaa3Sa0I8@tf(AaVKpqBM zUT^Tj>r<0wuolvL>wQ25lnUHzZcxU8oPgzWjqNx>QLXC@D#q;=R7wjCnyR?Ey2jCA z!rmdHlZ+3f43yE*Ly){vP-%sgQ}Rd#=Jh+xS%!cjX|+h202oebG9c;{h2A9I?VFx{ zuZ1W)f8GVEmrY9S>ncZuN1fREve3DBWR!;PQ71K|8kpdjp##|+@$fpB>PB&=pOgiQ z)f#74m)PH50mvAgHV3Cx2r#IPwYWi5)Fl}swzdVUVZaBE-^0r{r+EA36^;%M@fW}R zC;07ee}}WPGdz6o5MTWM3#`_M_{)F%OMLt74|wzX4Gxd?@WofZ#lw5=VYxiO&CNAF z{P+o;{_qMfUOdO%Y6+m?!}mYHnJ6xW$$FKDkf z6|2<}k4~0Iy21V9LtI|pV6)xBau{%Zvq8#6yVsi$i(x>{L-1+Dlguz*{oYzDTEXqx zhJ<$+M*5$3>EiQicj?-<>WJ?6{#?JG8-4og!gzhhcXt_>n$8R3iF{wjkU{>A)2@q{ z+4}yN*N<#N-2k0)cbhELWd&&A6!$z4&|$U!rV!a&no@SV?KHpB8)D|hg9}N z2s8=xB;oG-Xiz{+_T{X5oKr5*9se7?qpqlYrG2O`udif1J z8Bj2QmQadf7#29Yy1~uO77rgh45h~rR;*Q{A1ZWV_e!szWYm_Z>)r*KLf87nfk@N~TnEFwcCqv{rY8^;FzGPV&8 zpSlosYDi*WL_9ndim?`K#x3^O3*>BjofoYeLaO4eh;7c>af_?#Eyk)?r2*MflvD(o z5;SED%ROky;VDr`o7-o#;hsQ51}|u_IaIyY_r)PaUmy`(%bG zHzc8@TB$2V_B<<8XLHct1AWVNc9TeH2%u)qXBQhp8U_4UtFO>H*s<1 zji?{siYLKy+zg@1K*Z~RD4MB7y&z;)=cOYoPgiTLl?zPCf~4Tp0oq_5V5mg;N&$lQ zs}s=;WyVZqX_Xb4v!w;{YUpOZmv;^qNV|&UfW8o)>V%)lA7|GvYmlajUOkbOgO!S5zyt9 z6)*P!ke&Q)4x{Te_AYtEuDxwua@@p8B+FUe@&5VAscIk>F zt=8XId3KT;uv_Gb83s%#PHxmUc2WGC+lAX=^v5|F?`4fZkMMBCLT7SNyufiY@{%XI zVG%@P;@_e1zjn$>OUVhtmNecv*L^xeV0)IPwP-st9^#S_>mhD1_8!Wad~x(G%PI@yo?xiz%W?4@!PXAWLZIU z#JT`Q1hs4}{XQj(rQrJdI{3#HqNzQI`4JDY9^B%wu-G$Suuo_RE8azkN- zIoVju8KTHpq6okFwP}zw+9}vN21w9xY*8uVbj|ej0xk8Ih5JW`I6FVbShslj>KQ(M@^k#;FMf;t{SiO^#V>Js zc80UJFY)1rpI~!+4S`_2zK<7gPjPZ|jLXYw3<>z;$@}>ByKix|*`k&aFW)(;EsKbnpZtvMV!amuU!ELyNn&X zbmqJC@?8d%`S*4ie|p`&H@fuK_Xd~wcjuoakCLO~Or+SarH#$*(T`)UqyFiyAK8Z5 ze}drJGaf=V_Z!87HzhJosFV^5W1)wxAau&p+<3wFfVnV6yw_j%t_|#e-(~FCWs!TA zp=Vc4+byGuGu31g7Uo?A=tUx<1Pmk9YLOroDdP&CE&g;q1UzU!Nu2Sq{Tx4^GJ$UH zb714jRJ%u;Q-1)vRRBPRMmti_1`@*5LPZVnY?G;E)os1VsM;Lp74(BJSXKOst1GPc z_pn?J6J|#MJKbw~l-jrhH1ZJy)lSy}14O(B70;AdU%^&Nk{6kVhENElPt4vwNGkx_ zQYGAzVq7_;PjN5|E=-zV8$g;q8yG2=4PqXpJksh>hiEKMP z5`oIFtt$hjS!`8RCdEw|-W;B37Ad3xkSvPATCJvzc0K(;70@0WO!-9cmEZxUKq;Cx zr)dfiL(=Xc29P~QEtomtd4le7U@9RM@&HIdi+#aP(hu)J;okGgDQ{{oQ|71uwJt2i zw~Y;W)JyQG8qW%T;4`M;$2FWh$+3kN<$}1=x<7vz%q=HN>b(#XZ46964)oKd6__n{)1b4!~c zUYMRbryW=N{D0lE+CAe>)woYXtqNrXOk*65E1m8ggUqoA=h&}T9l6#VQfiTYz~rom ze?RUK=gRj0FvCnJNxsfX*zI_RMhQV&V;_6kq#M5#YH8nU)Pooc&t;8yP!_~F?s%yB zp|Yc%V*e}Cq~;T|Q3)Q^?H!32q26J9@w7oGF_L)572GlIjw#R(uP(P}2!YEJkI1FS2BLtlh+!u| z>ntkjDXq5XcAxN1Ns|ZCP7>uRcq&MDRk0j04iArTaej)U{rxCvU8QqT@%N4~_SnxX zsRa*?kMM7Q^J|X`OZdYdKF53SJ;ML&-~FHBzy90*2323-m%scA{8#_qe}S{hf{#9Y zjO(jwoE+cBVL8Cl@4vwD(L;Rw{ZoAUso*D{{RH2A_X2s?!#E~<_0?B+|GoEcw72h3 zCW6b$Yn&Vx45`(p>P}Gml`=}*!RtI^5W#ofeT)6&0v|j)!f=%^q>9aUW1kNRi^0zR zxZR?zR-R_)^xHj)oEy#ydc0;sXotH$d%JXZ%)U3*ndiooU3&Ptj6S=bvrFfk8-6CM ztGA6o{qKENjir6YQUyLYNlGH_Wfj7(|6^Zs?fFl5sc)~HB~zGqoE($|>Lpk~wDg-B zP-wj9+eG(Pv7L&LzvkEUzxNwA0YAHD)ZO?VYYfn;7ZnS| z53MGN<*_zzdoY&@u-d&=LCWyz@(HL$EF#GRKx+K!6^1r%A9EE49wuomiliz2l58D& z!ZvTnG@R^~wUKEbR7ZsWq}5*FxIloLy0vZw1HeIvCMYa~G7SSxFU~QRisPdLK#Px) z=|--wlHTAT)SnX`Vxt-&&944Gis)237-WvB?1c>wMuA6x;~ zqTyXO(8Z_&BZ5K@%_{S*hYKySQfy7DV03|M2A$d&?|`JeM@j+TLq=)P%UD$dpsOT6 zJRiGgC__VDU>GcNtwJy?R@h%IG0-co)e2*ZH`IefkkSBpx=4dLC!}mC8MOjwFc8q>wS~J{ccE-`ab(UO?b%c$ z30%zpP=X%p-)$>=t&JB14NFI#QDBLd_%=4%qc~i?UwnQx#Sh(@h@T>1)RuaL3D=CY z&vVNt7$71}w4jOT=Bcvd(4>&@ful6HS{-BKJ-&vvv(IPRsi z`OF;vJn!fPJ@>7xgLdo90-@R8CQl>nF}kpZr+m7OG`1N1g^@CNaaR{jJ!(LSI3z;9 z8=0C%yvaG|Z`D$mOc7xsl6UkIn9`EG?PIkyH2Sjppt*~4SNU0zV12$XW*{ipB zb#{tp&!6GN^JlocxPWBiYbS?C_~8AAST7gY-fSVrrmSi!TrOT0CJFqXObSjqM@AIE z>||!jA)vw3*ywO(T1Q9cyqyGH$G@%)ME{w+qfUeD&|;Ay4Hrdj(TrYt#Y5t=<}_(W z3L1BX9#?gvUE=-P8H1k@bpwMq-)V+hi=eO@m5y%vmk=HLxKc}5t2A;*OW9My2DXj~ ziM&_gzqfJou3WRmV`)r?1IUdGS>G^QGn7FSDw=^mvmpd#`xHh6M-b|CBi?b2XII&5 z!epYQ|5^lCkeu`NoQ6=8$e;T?4KFN+Z*0iXq0twGd)_)XR)lgJ=N|#atbQO3kqXM$ zXp%9bC0lEDcH-`8@Z;E(ppVAO#CcuA<&9_Ay({Q|pgbX#vnQpf!NxTVc>u#MtXoox z2EEq;po(E;R7key51v!vp0SMBZmc42Re0oKLa7NCSC`ln!x5X!h?}jo#a0!JMNzzq z({@ykLQtrVjt_0^qrn2K?q2n|RPr2EjJ29$t5#3TLpzt1WYu&A@@irutroFqE?$66 zCW|mFQL~ZQGfs!pwl}`6DQBddo8AJA+K1wTARI6Ph%{Pb>U|SIo<1cK9D(WRL7zb2 zQ=s7JXdeZ@+4&ikt5r~2$6(=bJ1|V|02ayEj$0h<@8kaQJ-mMT7JGXKkTl@w58vVZ z;x#_}@G%y{5_R0-um9;k!_8RnyWjm5H#ax<=G!l@$V)uB{{&SP4<0?n<<$+I|L_b4 z>ormW_V?G=D)7UL*BI-F%kxtl>>uF4gNIg(w~Nf0>zeVNDo_l+ms&BFieb6HtLNY1 z_g{UDVbA8cSPYO_aJAVW4TD7|Y4xJJI$wN;Dm&SS`DU$!I z-r$yYQbes#vF~>!koP+#g?ZR~KL-(@3Pu_$R6}u4nRn`H0-HvfoHCZffW5sXR;z_A zMW>uX2dJ7cLlwwLu*_a?xSH2Ub4Fe+kg|na=OKF+FV?ki>wXfOn*i0Lg{)3)J0}(y zk&P*)>QP0VQ6n+(h#eeEA>ilq%rQI{RghD{kcWsavg>LC&%*OWNY59Q23L_g?>T_* z8uv!Rp1s=pY)dMny5QmMd9J+A(p#uZ36d)Z&T}~;ait9nq?!`lX(&kOYfv=fLf3K2 zfmgrQoO@1;D55B07&w)#wx-6Pn(;!NClMgELg5{N0$??HXgpOAkhL}N6RQTm?EVAb z^;uF*p+u(Sh0Xo^vTb_K9LL~>HaAX5>WpyBD;Gv7XzXK?9c^Ey``@ZPxJF|X5oZvw z*6p%h_VsenO#z9+u&BKkL3)(4bQ3j%=h2D?8dvCuBArWK&$1|#>{8L*t=R)&0yrK=Fv}_GnGCT z*yRd#2oyvYyq~HdEfU}byP4S&q8|;whjAvgGny04*A8dg(E^#_7UBsJsE4VCLBrQv z01V0Hq*PSYX0Voa&YWon58!xAF0&A{MY#rk4g*I+gRD8kh`&&L#T^&^y|(^-&J*Qd zql-k;k{BUs*K!Y2YQ~e+3SJzyBuol%cf2QPEnaQRIjJntEDwXzq@@p~4hK*UB#75d^LjT`X_-MCiNCwZQfai)^etFw zT|{rSz9hZ!>(&eZiVquA%qb?t=_(m+Sf=cFjnVl{Hjqw}kmr%NQfb#1k2WS5 zdri5|v&zEV>-8R9yn2ILJq4>jnK3W&78);`A-NVaZ#{VM2vrqtPT%6>}8*{}>1RhbUvg(eVMEe*YA2 z&R%1GZ;6|m3p{@BeXREP@%+_Glv?rT>(1?&!*Kv9slTKfS848s;&#*+j zxx&Bsm;VCSBd}bpP*iZW+2ZQ@+Ji8&XH+OQu&*rN&|1la$H0;K?s6ehzsc{bT z^wEFv>rXMj#7Jr37chL&&X9CwBkjHr%DPg?PPZ1lH=@j=C(z$Vri^<2oqqBA%?^cZ z_RL+@ySBN~JP48^l0geIzA&3QmW=+e`nR54j20%n-WDdX+= z1tbF}M|(h}wV4W(hi^;shBF*jt3`a&%DMV_E4z0Xa8QiC?j{gSFxdCXI9j@nYFFS& zy~ve?5~>9ixN>3!ndIvYD9UJn)`W1f{Zi?S0_~h3QjQRNV`t2cb9RMB)9!uorEfGB z`)YF@w_Dt7x3T8%oL>k~VMXUG%E~&CNCs}|h>aH0O zOk<@NLkLi9(NWFlMa9&OZIfpSlD3d%Rr4q|{xbFO1I-I>d(b@crm)-dhMZf}inK_E zmXg$z!$u#y&}F0yobH@Sb%v;>=ps@9+SodUhB9%^`#W$+u+!5M72UXIA}?~>*V2hn zcTJONmm}}{DnSBQ8e)~+lypTnJJrsJ4iyR zYX4M_s_}-by@B1s?O}y*D}d{pYg}z_AZdY{s~e0rZ}Gu{J$&-PdwBBEM|k}BeLQ~f z9uD@_7|RxCmshynCTy$Ci7A{;o+d|h8ciU{0GD-0aRtqdGf;N|dRf|lA>}|vJN@aAW;kdOVzyoh?fX-V!Sha9CqE$YY_!{sgYz4}1l1&R zRJwaLzGP7^^VG)ndy8)9$H{T+ivFW~wv>SuSq-me)tpW#&H?At)f)7ljgI3s!FLi6 zN(dY{_+)g2ukB;pGR`0OSAi(RO{@}Q2_9rO$k8PS zp|p_)#eUBCxwx^9v=Pw@(qSF30`exQjIL8s;To(4&kz=Lo zZs#y((t3va&lEK8<7KodQYad#3X)2#qo*U2XDc9-U&IgXxjl`PQcw#cwTZcWtf?7L ztHNq^phg6Et)oXqnt`Z{1!XJ~dB<)_MVpI}5nn(-b_18mcaJ!$#y?Zi;PovXNiaF@ zzD`!J%y^ygD-yLEyxf^;^@+x4r=DD9(m0ew+!*g^SAjJYV;MmsAo$OAT;E38)sc>LbS_|5M=$M3)R63hJ~eEr4OxH>z--~9DoW5@$?UP2Z4-EV(| zQVWJ*1dA-5a%?*}|1%}0dQb%mJ+llcq zmOdpgMsbl^y}eu5uhVIF1ujg_{q6U6>C|@_Q0DsjyI#BW?YTkc?y>GR2(^2l*$0GK z48(2reE^*ti0{68(0}ChSYvVJ&w7;GJTe4L z&(q1Ad&5ktDRFz=x3AYfI*(Ydz-1I{iU9dS6`O0@)}D;{mesX8Co}-DIAIS_1;8?A zTx~Zvy|}{3@rem;Y(r2suxCP1CwcJ*R5l036kMv-zAY#71Tu^`!MgE40D*x7yFWc) z_Egq+HDiF)aWNnxE{qwLZ-T7flhiLKOg{<$P%DtKrxw`-C9ovNCAa8PUCA+H3PDP; zjuJ_rqqnDqAm@eA78>2>+=@bSFv-2zn{kVS^$O5}GLE1^t)(DRd&IqhN}yoiFJ%LD z<{eaDVFy0GCxCy{fo4ew@bqa?Y&UXvALEeN9%}^+ASv}NUVA`J10=n`4fOOh zb6Wt=0NCiv_L31KZ8#*u%<$eSF~{Bjz%$hhXBPQGkW1jwKy}C0)Rg_gBz|qjmgAUo znVTpM+yQoO;%5YYEe&D%YCJt9dOH1YuR$S`dkI7q@i*5R0EU4zowV^lj!obNHRr(I zhB^%G+}ipXh%}rmV01u$bI`*mU>5}z3MX@l%tYUZHuc6gNvf28_Pl09B!97eY>^dH zStnCE@j0eMT5kqH(m=bfKabaTsas4#F)-YKY6%{3`_;O+c!Z)GCt{pvUdxo-!{>Ym ziU5THhGQ4i03{NJEKuZAB!t;#;YZHAytL(*{Y$# z*%5CG)q4_s;rW5%peda<<7e;SXo}$UGa74Y81_3!yC3h}XsJ&FY1gqXN24!B$7nn+ z3Z-}IK~t28Pb@m9l!{U;l1X)JQ5@Y`s5ahK9V3t@2bOfYV3Ae}s;vmQE$)o)QC0OW zC;hsGytAmuTNtogJrVQ1ix1G++~3BFl+8UZn{YX83Ul078G&Qto#4uDZDKYd&opqK zBNhle<&AR=LmZ%*p(tIQCq5wtGsR0HVRTY;B%}(~Xc6Hd?NZv%3dh^nQdg^JTA;Fe z6s)~kp<#aBa6b7O)AX7lhWxV4guHq{iwtjk-_{V_9(2Y^HH_9}NkNI*0Eh}K6WJ8g zoU<9X5|F%JkK_f?V9(AeAuk5+B%{#0umZuTMzav4A)#zD3JM^K#d3k-_>oaSCaRM0 zA;(Atd2A1oM+hb;AOp-XL*D68A%*F+CV!*L9_^DUnRi|~d88=}wr$N?H#Go|+-iZP zoT0^1$88P7PlSIzwN8_5eN7e<2ctVN;xyc3ZFK`#)3qAbX*@DXMoN|?u--qwcQ0Py z;e!X(CD&7GYT#Z8tkLKU!4o%|YrOaH9v+<>3mFSO z{n?-4yBE*#^5yq<`0z1azRg%J_aWHB^XD({@Zlr8eDw@T5#q}1;lSN?5S`}m2hF)xEE=8nj?e1%i-Nq&6IQB06c&=05 z_5NIs?(eh9@T1yNpXblp_38~f^KtI_&Mt$`^x1OjdKx%Y6;kQsG*ecZf8kGj{V5SJ zvzMU|JH%7Ztbghtln2&Q2n@8|Hh}C(MY_wX)i3&8mbJU?b=Q6DXPeuu&Ae-a=Z2uW z?=L&YY3)6kH*NnYr0r0S%mhJ$to}^6qNr10c>AYLp1%|0~ zy0#}hfdK9)GD7Z+E(*Ig357J3jixnH;nSS(=Iji47_h%uqin~C0@=ElSgL{v8_z%O z{MFB=%o0MDR2^CcB=VYh{H8n^pZu9`dG?OU-EWQTunmz%}sQfvo z7MXZ?!e9gXW$BHDEBl;h$5i7(4%42UbH;)SuNz33f-QhrIOS%u!OeDyo6QEI6^jI* zT19xtHm@;Kv*5K8{0uqYN;$hiBvD@k4hALJb4zLG0@}|d$6Bbs^B#N!xE2I|;5s(? zA+=ek=QMoVHm&&IEp>;ldBHfYad(C9C+pe>toBHDK)}=u0y8soRlrgt2$(1LZuHYw z4c7uliKzqq8nv{kD0VB_)mHHJn)6#Uu*d5pd-@KjthIl_(CP%RevU<;SUsKUW47ol zQ?_j^DV~U~Mu(+O8A#)^$W2<|Z2zhB+)40ITLTA~5s~AHb`)ps(^?g(V;hTmR~_g3 z_!_^re^WVT`_g1U?^(Ick6WZ;GsyJcr=iG0(X|u_PSPbUfJ2*JNy?YRRyf)ft|>dV%l0e~M?{e}|i^OANz+<&c1K?vlrh z!&NNeVUZFBNywf8B?(A_MZ_`6)JU>|t)aunQ{UT9(#IUP@a!|4&{E6PquS^(cMd`_uzOb4n4%2J zMyEAJvCT;uIL8mfxo4Cb$Cne3BQlIBNX9SVYm!qkTvWRj&tg}#`Ae)Jztf7FN;G^h z+}f)kn8;10idZeDCbuSh(N{U=rsPPBpu0w7_Rk|e#6Qc@Qd#}G)#<0Yyf%JDyjiVH zIw<4Yxo%>eGxN2g@lgbW8yvDQJ%@A%B*#c$Ao4}FUt5G@&+k$OS~oeaGi8F&R+D+{ zeHtcODkmfUoaVaFdddvtIGHLXXvWc;fy5dyjsG$-j|Mg4JvHj5k=GWF>WcPcc8oEO zz_vC+0cyo^NLVd~7#o#oGvHdQ?3`0zG)a~Uo5KKZ_yW0=a|oL-V7Yya%{Jn#k$(-GZTWAB>r5b=#!gzw_6BiKkD$!ui=7y#L-KeD(BueDlL|tXB(MT%Y2> z<40Jp5AfZ0-=M1E`|p3ic6*J_KKT%393gp)O-cCf*;AyP%}69hN@r(RmO7qT9J!6r zjNTbQc1rh%ttdVQtd@KDPyf^Z8-D%t3WrB4ECDPR1Gc4L9F70xVZdhGVq1zwbGE1r z)QZ9NshhF|IV;z!8ju2Yj$OeH{cG;rjq};P_xI7^`|HS;cfEFfzn5iyEa+jE(WuE@ z@@tO{>-0UO9xHmyJ(#g;xm+&r4|x431B`(%hL$3jv1o>%TB}Dt^u|dHv@k@_l&TW< z+qKcXaYp2Jf!=;BeD78yMfYC+dob(BcRr)7+jM=W2WCFjTf;ZN7x~(9=iwxXq#|dAxEl9t!cL&6 zDI;XG5%*vOHp$P2dSna)mp4~9zq-M_qhk->?ALI&5DQj(IXS~Yq)z8Vz9|5jd?6}cZlL@MroTd1k#-=ce*ea6NC3)Q%Z35F- z0I2+{^F9a#qe3uryr|8^=xS<=0?409!=A@3MkC~1HX^6LSXYfTx9xN9r|+*llXAw- za-_QSYur42)3(<9*U74Y1~90p{u3Q-bp=uT{nm+WXvRD{W26Dlj1%XfU#FHP)qx%= z#*(~!v!81HAK+)7eT;{X z9%KLDK8j|1|Lg_MudgvIR(SjN6rcb83!I)`SmYE}GgT`FAA4K90PPfP_GgBwa6~q$ zDGCZjof%MUIr}ITuYm+A5CZ0%(m+Y0Y`fl#mnnlOezuQ|hllOFs{v#+IJ$zU!u9R3 z6ze^SpW6W~3ZeZiv0}z2?A)+^nr&QFYjbQy1V#s%Qip8vtFH}0`l4}wN><&ki#&+Asd~kr@*sbGuvl-jB`eyW~E#@(OR1X3Rtzmq8WJ<5pS!E}Vq2pr!deR|PxafhC2w%t*t*bu?Txh!{NAS*hy@Ttp@JIuwLlP#+ zS>3d;TB^;E7hoiV|yhkyR5$H+WXsA0bhHE%YFP)hVLF%=Jz{%C47nc$R#O zakS^>e}Mb<@8Rv+w^*)@uv+co z+i$+Y^Y6dM&;RTnVX<7G-fZ#9U;YiwF0b*`H(z1gjClF-DNgPk&N%i*pnmaeaM>Xn2!B)Uilw98J&F+m2 zbE5(yYv%9GUu`3S01{zbniAQr&An@b=Mf{j2$!pwvlTdX78pYD}KMJnM#oZQb#`RJ5*S5?Fu!5!YN$YYUZpwIQ#H3AgR%1qjwFD7ge6p zDT&ll%!wcXQsg(cuy;V`Y1B_}@_@^g-+7&70 zRr_6Q_hX^lP#OYUIi2Z%RwSFsTmw~cF4Rl-ieTb(=l$G3tvtj2J(_Krp|~j{cFxU% zV|(icv8W}%6&JI&wY4 zHljy5+60XrJ{R)_w%Qb`rK_RU_Ngya+W0>WC!Fup`@{WVr#LhGwi*r7?kK(do94c^ zI8=Hx838~=lF>Kl){(S4HG=iDl%yGlsDQEKiqQBV5{dx0_CfO&nUmGqyata}WN)h( zshtX>G~nXu60cr8N2&$)kM{7<`w#HhPkw@rKm81Qd;1v629h&Q?mxz2wKOA3tr$E7 z()f>o(&^f{@l)IUCf+z{%7f8X^FBAy(COvi*>`2fz0EYh_iNeHL@%#;BV?p?IJGfC zr7G#Zok^Zi-nMlPYsSEaM=*I-qt)u?*yKRW4Ypo60$1$pB=-=h(vi^8Ywf=ljTf_Z z!U`HeFRvQqst;5!#ZfLIK_%2h3{08rIvP@ub~>EkfjU+Eqjh>J8=cp=Vws|31(#8W$2gSGu%+ChtWlph)3wAG?h8suFh$( zkJjOu5wpkpzJ5ICfTes+i_%XqNl$AuLQ)0gi;j?pDB@YQk+ld)pwnWgXxEmWm!{6Y zfTt;;3j4VBx{E2hf&mc!G*SQ+Lo%Z3u8Je6ki8+%u(YVy)l$m+5uZ5z=7A?~D=;#nPT&8$AZ1}0-t^QF*Y|h*lsqsckez{t2KWA<(Ig+IK!idC;0uBpX1H*XZVX> z`~t_v_fR%lTwQMP_Vg7VJ$iuc<{IO;!NLALeD}jM)LL^=ny#S*i^UQ{xyG;l_1~hvit#4N*lr4L#to1Z zi(vsMu)n{;;r_}Dykaqun{k9DSRvkR-yO2U4zX5un-*^AdfjWM;Y0OS1bYA6`91%b zQAXr;)>tsr&DY2~8C;R`$>swa^q z-26H7XR<9=%1rs_t_N_ize39J@Mta;+;B1-dnjmK6o@z@H|E>}uPF%Q`RKtI!Kl4V zNsDeQF5ntNZS&f>21>=TpT8-CmP(^BXWp-NVcziw2Q-VtfQ!p3Tx~|2936Wt8D4E6 z+0%XGJxxI{FOU}ot?-=4f?Bs;{I@f34~a6>SqhKt`ht0UbM9L}9dKDN<=jkR;gF z5kR$cplT5VwIt*;V6|F+t)pu)prL@Zd25zajR)n_l+NVG!>{#|;2bmKOqz}J;w z-5gIsc=14(vm>fSg0>t5fWFT@yz)aW%uDn}pb)M$jX*DDyeWeW{*J>Q z1V6lZjqjelz^gZJaDIM@?ac)q-8;hJ!5+3Z*Ld&Y1N`i>PjRr`!*(3QOG__!qPN!# zcu8ouWW?N5uD4oWZ3j7agsg+fvI&lel@5F|1zatnjqB1@G^&-vnh-Ejai0Rn<}hkx z!Sm6IJvYTq?&m2W8 z`rU9R`DAp2oQa~$G`bDY1$(3+PNh!Esj6fg(toPV2D&8y9ZO+ zQam@o9CKrn;iX`EvjIXW6G&Rb-oVdEoJzHtQ^~zbCYKFY^hTvASq7IULdE8Cm(-;-+%F2eE|v}4U;h4g zI5^zLa4b_xADM{OA97{CB^8f%VY}>*WH=#enmx8;oPYYO%B^mz=O%m~)%8 zGdJ6Wwh-d-eOiOTV5QW2W?T`+4sg$9zn^-Jl=m zq@I?V0l3b_Vb>YYtF!n2)Yp%SfC<42f{zYxxiB=#ksu?xfN~gEOWTW3{*!xs`}a{x1Hnc2-jt5RmA~)qq~I z$b~Sx=lc`W|2g+OYDF_7QH=C0MIxo-MH~T@>NNm-?Ir-3 z=~p|1qsQP!$1d&mRHSv5;d_wDeA!8emV8E)VX~*ajA~=(l^YwYPWQ-xTR0Wq013@7 z1L29}JdKy9s*<fueNw|eu-x!Ed89u0AX6>I$^)w?tWc<@xK50+&A3pNwOVNK;8|5b3Pg&pE$d3O z9gTZY3UvF>@Q(9hZF?*<-21^aUg#;6LCAcDHd+fhYD%ueIM~I-4Bd>Ljk5{_pJAbS zb+4gzYa6&v`%XhMa1S?R_GE^JzZB@|hc!tKG~+PoGz@#)7-+;15ywqF-z3i08z!OB z%WF~ldMJKQ1)=cvHN5(9aZJQo;?FStYceI2dOuf|p4rCJ?lm>+a}An-#T0XNpSM@H zZ#zDpJ2ZPkN$;eSEEB%3e*K(AePkf7X=eeA$0r(~a-2;Ydx~-NFFRVcFLa_j%p8Pw`YEtXFE6-hG20t~|fhwC*Ks{whLBSk$Wc!A=AGJ1oI zBw>JFx zUxNVq>pd*@j_~IE!f|vv{dBS-qA(qcDS4aX=IR;`PL8p^UgGr4E0pbs^V2sN@_=9d z)jz{xy@xliUgPlxAK?$*eudxu?l*Yy;V0NXTw@rPsK|Kx_6#Wj&tH6ttE&qd|bPp-;a0Kc>4GI&y9B)4lJHR7dWM#e>~4^A9{Oi zf9mT;bwQ%Ah=GWQb4Qef;V!Le1Qp@E1gZ{xxEZREW2!nW$osAd-t}3(aevI3Hz$A) z>N|`k^Lwg*og4q?7f z4Ml-jpHoUGqk*3xH!myzx2g(K8lnYVqc3fptLzOV$rEH_1_n8U$K<&exfE;92NethM{G1QxJW2)XLKqS)tJS-P6+dIWk#H9+TD<|=>731|kcTC5UK(93 z)}CXNaB#3hsR^5H1u!BntX1?dWGn{r$}Lt)q-{l!jB&GZKss1Tl2oLeA;PTU6t#10 ziq=BgV1Py!mDYvEB8gaFj3yh)17|0r4!j2wZAvcyivmIW8|b1Gl8sZ{BPQAQppR7W zV-i5OzmvegI9Z6aT{nfJ1IfK`>R(j)C!J?H{@5!N{Jbd`V!04(MuDfmXaPBtw{Y!BK+6D7~Hr; zT6T6og+f~AK<#8|tH01IvBG3pZu4I$!9|$}B=P#%@Z2igM~+UxmgC zg%(p3RB9X^drk)2AT2tL;2RQ9#|_4@psFC1g2(p{aCEf9+p{ZN-E6VF+2Z=@64%#P zI5>HX*Kf}7^37Y^yLS&KM+Yd?La!?K>rhnP$-y)23U_L1j1`J(N=(!} zNC<-!jqS}?EdDq7Qy<|%1F?30$0`TkYQ-=+W!hzG5^>&XKTSTd9)o+WyM$EZn*xZf zeL_JTm#D+yJSaJFsd@9O_PqqDaZf=mqt}ezC9fypw5-I8Q*4&oq~r5OkIiWlJc)o? zx`>VLIjU`x;)YS)o2JO=OC}w2ps>hJ+vJcnaJiCmvNd-6WzWsd4=hbuRU>@^5JYXS zpa-BuD#3ZO%O9Vcb%5)Drl>1r?1b4zFd^2II4sV$%d57wejjOT5&Ta`*ETkG6>Rub zQ0i#3BZEJe(QYkD#poVFf-d(J< zqZ`$#8(p*-ttWRvF+DIZJgv1BNXj00XX`Nx1JYnbvdGrOvep|whHwt$yg(j&75q7L z^tJR@F=Y=xc4wX@U~pxyD7DyD> zKR&^yPd>u8Prrm>z|H0cWwXKI!2xczTRSVm0@vFO4(mEVTS97k<@9S213=C-1qNuucZ~YRe2FRDo z1*&?hMnnDBjC${yuLOfp0zApEfnGS#VX53I%F?c0D`e; zq>~wpwR;q_DO^;r_#Q86Odhj+?9Mh|WnGm@nWq-|`uECN4}ANdT1qGuK^Xy#O{ovF zqcjDHf71ZO@6aG(`{@hQ)f1pVz0120hMI1UfE!w9RJwJaYSC)7wRvr-sDriGji&HX zL7=k3E+q-nAZ-7w{p2(X%t!3u@wFKhc>iur90TX8{TkwSWC=s9t5W85-tgHo;yqWa zJJIMbGY{ULIl>4;v=*?=OBDc{mZ94(+XwFeSgbI)1%$SBUM#Ey^>P^S_Ual>zxfiY^%9G` z!h_=@Q%CeZR(l5+Z#FnMIK=V&6Regil(D+u&zuueZ@SY_ZpVj_Y>krK00D_w7u?@Q zH_ZTc+kwis{w0dDtXcQ^u z=`-U}My(P&x1kfKR_6iL=`Lw^jnl5^)TmjUF9_^;IriJvjWo~eW*D!R1APhuoQ$M1 za)&b4T|=jly5a{+?{gccHHC#wX%VufAZ1_Ws&tx|vjD<6VB(5SGp@8MWmE9aXf_UG zigo!t#J2y> zW;8$&FW}$yw>RhjjKrV};P^;s?qKnCP!W^@XJVOk*^w$HUH;UKRe)k!M#H({4k4!* zmllQys(=x@rI?&lsgPWe1~bfWOTkJ9H?9~DP;E+^@H|$5!i*H9j^-@%HEXo0kZj7B za|=7a?wpH`NeZyAbE`Mo%W2XAFV11q9P9r;+@JSIlVxX~*zeqX&CJ~+;@xwp%q-Tz zB8#jho84;lOi!z4dIk`n1sKfyAz&c=Cw&2Z41EfH2tkkl38TjJ0ycZ5XS&&JE>&c) z*4#7SEf#mPyBz&-&NcUlj5od(U~!^E{{XEm&Dz4PAYlv>H&; z=S%Op)NUQafsnp81R*B#Yr;(sZAND4g39pPrrE1u)Uy`N@L;B0!<=+ z{KFsf`o(iOYv=0VKv3pfc=GIRzVp4`;NkfNXtiKHsF`umgR^_8`BK+wxBKG~jV>qAljOcv zr97xgG4kNSIbVGKAwT(xKVvyulZJ^}&E-6$k(kCcNLL9og%p#Ugtaw@8pn~8l8se0 zN_q@t4rZ}YB*sLHK8IT1(X+SNo;_gBJ>lf`BGrwG#h*zJW}~m;l^@l_DC>KB@Y(;i2GgG09Vf7^*K#DY`*HiYo;bJLd9r%?+)7qY zKdm;l9iUyWv0u@hwWbHJ>L0#K%RfENDR|M=WZLf2kssvee(Tu%zMmvC_pD#1z5{(d z=)XUGZ@*uHSLHY+fv1}q!U+&`JKgDtTn~oVyy{k!EZiF0yC6#cqFX`zF;calyr0j> zaZZoNo$|M<6lZFZ{m&Xt*Ty1(=w+wc9FN{O6Q@B8WKSMJlP_V_Mfs@kaZTBhGC zy&bT0VB~bqItJ&*3i$ErkN@sKf^X8ZKujBch?(u?B*6#}8 zI9?LEf0htaVi*Rms0OZX7OwZ1aX7HgnX}D?-F9L-4jgLc@#7uuz55Ph8d&BVdI!DJ zBFT}Vb-UYHzw5^&%AavL&RNvJKjo|YyG^k!BU(Q}Pr&R)ai z4T>18!mrmr61&dhIh_?WwI*UuD9r?>#Qjf=Hi(y&%xf zqkryrKeTTsT^@vd1(5D1)t8lZPMlnXO--A>29N|Fe`op~@7C>g?)3lb-JRk#pe_D? zO|+w6M&9cl7hT@t=Xh+b+#jl57+x>xc-+4BsiCyBC{-#kLakO@vo4+%JBe5q*j5V? z5{MF`8TZC0t3OO+{~4 zB1mtdaL(?n_s}}C-(T^kKl~GZ@3;O_reUCz#&wxFESbDCrkDuP)}-E;BbUbI z;lNLS@k{2pFr>uk+6%YW>eD^`^lzs*^67u?{{D9CaeM8!3yR$R`Ay)<>F4jBZ-1S} zn0LSDzTfv_hH&yb`cl>x^L1vf-eCW~c>T%%%z7c(sepQCAblfOf6xPj7(Ix`-y_so zZ{6r)Fm^ws=xz|#U-tzIrwe+!r+RfC&~kSIoPOumA$MO%u5N9nw6N`<+KD3#0_EXZZbp^CRAR_8#B**7v#DXQme`>Sg>T)q(1#7 zC{@ioRea-aO;O(nBbGp9DO_f&7 zpt)B-v>DJ~Yzo^@OP-Nn%#vF3>ZMR?rBx+}x1HYKpt~IpF3yaeUcJH@`r`q}9m6r; zl;V1f)oxdfReKU3thITUwVa(l;Lm^jXZ-#@{cky3UC~;lmWs81u56|qo6WgNrDAZ9 z$uz+RPS}Rhni&W~wN{?ZHrpZEwV0R2fOe3mRaz-LczD6z`n&%TXAj?IUNSw1GKRN- zk!x*9Y#fS$HUm^t?ReaImIU9BUDw+?Ux{mjV~hlBQc19?qDY{$LT!b}z>r4Tl70Ma zqp7Zg4$^^A3!<9GNC83OF_^Mg)`721$#o#^SlK-W<#&N3IAL}W*Mw7jzOQXS*I>GG z1aB=7>vFHVfo2b4;Fze``B_wSYvJzi_29Fq&85=>*%FNtN__v$K0$-5CSRw3ONWM< zM`Nep**eDlTwAbqoa<{qy#_a@;8b5fkLlmKo}VVno>=vbsbA-MKl@|Y-@1-LN;|SEdeYj_NI3#5{Th$+q~CQPzfLFi>%GmoIK7t+ zFZLicl1v1rx!Jd5LbFe4E$F=161+^bXuw=3I0N?Ln>-RsLkQb~H98_%` z6ibxyQYbky=Y`#N!*BfhcX|Hu6^EseOX2fRf64XhFZtGQe4lT8=eN0e@ex11dCm`h z>+e`g&?WoxXte)zY*u!Xja%q@7~P~-Pv%fVL-e^~)$(Jqo!05o6Ts63 z4wl^7+F+8EBU9pZ4zfNc$2g_7W3u1J@AQ7JYipl3(ksXvs6Jl56{b(YsM~Rtt!1!E z7f!EEjh)&%!Pu5=_wP9OPTuUVrbouZ8-a*hKY4PZR%|gAiYLx${1nT()~9wYcpcX^ zzYps%kKZ3ck3m`YpmkmJeL=f~h1)L+LDb^`lQi3--GdI3Odg8^r=RWjygy^cl5OqO z2h5o`eQgPHa+i*Kd7mGraqICqmCknSvEcETw{E$Qoq=A~R{l<^ApQQG!YIAdPzOBm z{mS!$lMSx}eCxke*RFe~bJku*DEzpdALoqt!K9o29v51AfmpGMb&3N@qt@zjJ?*1w zcZh+a&Eq`$ z`GDf{QdNl(Nx|Yi)IgwN2&7;>e-9o#;qCXnNeGdfn}hE|es8q1$of9kWp5`>_c~XP ztf$-go=KLfTG9IaY{w7;#to02JmSNj|B@fQe8Cup;{qG3V`WMMF$CYg`~If}TBXFk z7GJ|M%b}4%mzOt`(s=mz8Ot*B>Bm3g$+K_p;suQ37Fy)_%g=f8_%ZXWv;yZ3 z9`f1eAMxbDTWq%@H`g~jeDKgfl6;`n#?{p|56-uUDnI+_XCP()a3nBT9@4OTr0o?i z<}LsEfBk=AJU?T;x#kx?{vm((hyRBE)}XF8K1ZPxD~DIbPc}`pt<|{N)hkQciji(oWB1}44HKV z@9ZUa|J~!w)AjYM=HVx6Xn&u3q^&Kfx&Qk9y02dmfYG%Tt|di1xaf(jCG(w<3dewt zb+Ng!fr}@>-~q6YLRSN7Pe0|W|rlPQfZAbxdxJBwP`hNJ4xEA zZ|GJb@C7okR`i2|AzkvJt02_TSvTSA_o{1a#TVzMYCbi=j@BI1u)xA1^KOD0Jou@t z&j~wEld&skpivoyiI2Ycf)}q}AvloK&1KpN=(bo zmN*dDZYTE3!ax1z|2_Mw#?!ZcjZ&4IGqo(XNsCau`%4pBAX=!j7@UIXo;yQiuZ_|Q zF$BhGq?Al)o&vlU$S1Ox5+lpYhXJ_h|geXiO>u=k#xe;;){ zGC58Jgnk=suAEX|m17WUjF!$8>33XCl9IlMa$J*pK(4nLGN%DbKYpOMJ$3nqOW67? z?>DHO01ExS*^(K2plrPqF=nQ$`?ash4np<)GGKmUm3=kV;E zchM%)Fd<=NJ8cn!o5O7D=2{SGpq(Om1ZrY8p6!ez`gB<@gd@AfllN6NcAIt70h{%n z_q|H7^)8$yDScF{&sQ_w452fUdc4)=j{4j@O1E!?A#$vEufc4eaCC6gzDHD#^W5)w z)!q_V`eWt$ngI_n^e3f99X5|ZPCEbfHO8K{cKq$}oP8aY<{;NP!2)Jg*N#g}~*U60qt*@0JIYYlL? z1B}87QuTGSLmKOzu+FXiynC0PlVj;B>p6M{oq{m-9vefS3a2Aa2+3P<170NRV`WD_ z7PO@A&0@C5s?|@^3PAn&FxvhJ$nuq#e1#>rwf3&p>Rs;G zfoa?7W%Z9+$1wEqwBk~5KN%6$xorE76-pw_d^3WWydD7wNK;zQFbHyifzp3?$t_QS!hs&2= z^2-lD;&*@J1DAtE4u=DW{hs%pJzxlsi&C2z@GgfNzWt5w^7gZ*eD>kbc)icYSiT4J zI6kL+EtgyWJ$>CRrrrIyf6v{|9?kZ@`nUUX=)VeZIsMFO-rkS#rgQ7RJ8i6Sj8E#C zqnM1>S0b?l;}lf>RbIcU39m=(Yo_8S3&(A*zkIRwCsTB7N`E?WG^w@XL>DKYZm=C+%4ncVR>eAnXOL-!@X~Wg^ zHHZCvm8ob`D3y{=d{2UKC_P|P*67;hDY{k~Z3C#%O~8&7F59FmX}d+`>YMk;&VO31 z<0Kh?6Ls;r*q(X^_~zoBmAaXQ-ZEl4X)7euCJU!U6PAe7&T7D8~ zJs=XVO|3gv@4^x&^<-g15Uf%lYQUSQdHxJ3nL~fFU`t$69|$Cg=J61sfr4^$$X5)I zNWrdCFbUT*MXN+eT!Y5d<3u_W!2>3V$MZJ_HE{i|=L{CYyO%+0iZr~nqppvDx$w7D zUSyHEx_{1z@wZs=(SxtfZjs}`Y!P-4ARx4QMDn|tbDwvzj=lKtPM*`g*_~AEgq{@R ziL!#axwS9&t1k`yTw_6Q2P&;=fkJP$C<3h;_BzdZT?2Zx{kRSt6Z7NwTezxpyBy=4EU$@w-^TKxjBM3w&B z_4UXN;YCh`K{f|!^@_6g*vBqN`^{X(p>NWi*>eiG9RutYSnPm|&!^Q0^iCjf8_Lji z4eC|f70~nVL^`l}T;h%R)a9oq$MU&)WPZsy-;EvCTaa^nMmPrk-8g?;R{cKp&BD|< ze$L_*c6tj`OCYN@2Uw$-3A!9J(B)gQ&W}=UZUzCVjTV)090@5g4v|+^ule}n&mbkv zN9FJQ#<%(IH@{7-g%^j+$GMT08JYKd_!mE9|HWrKdi;=<23}<0brz=GBi?@N{gvfn zlyrX{tAW64S2|?9_x&Cnz3coMJNvSUn;pZkLVuD#N^m9^ZXAF7PQZ?RoB`{CathRG zYbVd1tr1-$*Qxf{hr*o})>hxtu{ef-W z6A*hIT9X?qlUV6AfJb#b4uZ2JZjVd9KWoJui=p&{wPdlu@#(+4w+SKW zy8d^tw8vTM^No{dy0?Yy|31F@Grfie4v_RUsC92s?y^_bE<`K4%D=zM)%$wK$+7#~ z?b08=_vezML3at^h*^SJs)bUWc}0?5VB!xWt+ROzd>mr_)bZtO1Jy|St? zI>h$9WB6|IUvJ@9;an^#kT* zrq$|zYTt!?-dS+6&icMa-fAPQ9gOjLM+dJ1!#J+bb}f}@hNn1qM54V1=i$X;zVq$hLPUA|=qaVl zeDdL+^W^CV)B+ILJ$yzOBWJSZ_3KN=E3=65Me(roNXo!`@I{5tzQ0Z_HSXrgck}JL-|;3EOn>j2 zK%b5W9NCRuJ-@rhyqm9|en*Va_X!8peEqt;qMa09d_Q}$1)#?=?m&~LXJz&u_NwQOJ27ag;+e%xh zy?t|!AyP`^;d$b}{NMaTa%l`9u-Wb`EcZfhOm^(UAroX^97Z$Vs)b=lXqnkB2SQA2 zHW#E6*_$8;m29OeO-&}`LI1(S8o*wE=SSi-I#h85@*8TU0o#NhkijHZRxG4y;K>4e0b{W-V8 zJLuqMrLDNho&y!EfAdsI4bTyE)!v^PI`?bt$-LkFRd?T$D+|Ut3&qzMwXqUzGb4L7 zP!N}T^);qC@F8oUZmx#KzrWw#Cb3HjzrK+G>u&Ux{tclh*nISX+Lp4_A5Nupj%@EV* z{grB59*~B##%9_u4T;B(9`NC3U-0QCAM@>B`=$l@=ZsN;q| z|LHFvjy!wkd*pGXPCK@n9kD3f>>*C1569Yv4&yMSJw- zfoZ>|;^vJjTgUIYUuSRaYxlQ<9JUs(k|J;!7yfK=^pEnjHN=rtj&cJA@$u;Lir{Oh zfrR~e0(CicAjW~(3LO+(WyZd?#XiP;AJpF6(VmUyY|d`v-N#Xacad~?jqR%rO!w{u z>V+S{*45T!_%<(^CsN6!k$sO-mtso@mLID6ns009Q7YSwSRMZBpoEW+Eosge_Yn^# z%q!#wKJ|swK={7FsIBq7A=qBzNsX||AyjbixbL36_SEX zysR?Yo}rFeEcEr zyz>sFJf*0R^9?O4&)$8P&1T>){^Z{fH;=fydV$t0Z$Eq2*xN%yYvuClB{5E@Dy1wi zP1ZVkirx~sKTF<5TP<$ueqkX(Zk5e;v~Htg;`z&$>=#IJU^BwUpWiTyiHBzsecKR$ z*RNlr0_P8&^4S-!IV=m)v@`Fhll=NFE9EY0yI`T7ySIwFIY-uyD`K$^q~6I>6|2J_>y-j6Yt5E^gvKN z=)O(QT7z-=;^}Eazy407VzTg3V`_r9vaew6eq&8 z<)8e)|D7NH=ui3ncfZRtM$AkX5AN-nQkN9D27Qd)2}wYr1(yNSHuvORTs~z0&5?E* zg0S7}`1lte@@N0{f8xP*pp}^r6Wi?uZH-*LiaZ#okaHys1Dk1BeG8^>A}^VeXW}rB zrY%Tde|1F+f!%J$yv!c38v0!nwnJi!!XamV`Nl-+{}9(Z6+?SUzk?j^iEiSYzcO+mTkt-0S_}8jNny# zL1Pb9Y>us()L_z?&}0qnnvX|tUl5boMOrCNxjnK0=z$|W$#P?AH?NB))b?=F)k|B$ z-Na39PiBAtAg9L9E*iYYH{MLGW%2ClvFZSGePy#GdGj156hD_H`x&XbuK+ZX7{ZUt@1?tz#3ny_{mqPGRKd)(AJv3XDxLSRS(InT6KNgjJ-3&Bgm^j=|EjOvMgDpSv`?<87GbHF!AZ)wLyT_2*VX2ZQXhvL@~hxcYGfPoTB1vOH?{8uLKj%~X3r+fI_37y?zz+|mGM zo6QoFRk52r1D~y^b=q-;$uSnT`A}*lx|FB+_=I+h;g0Sh9*_Ec>1%XVipSx;T zDFxL;DN0C*VTfzopgv#66d1<=k;qc4kYMe_?OYBuQ%kkEpAt)H#8!R20d#g_WsHHV zt7{I|pYZJ6573q!$UEi`q81}m2kopVY;#fEI9RoJqYCRBLm&;2v)u)|vvY(-&V|ip zVmnQgy6}QauCEpzoj)+(LX}+V3R0%zxd_IJHXk=(Su)!V{Mt9)v`tl@lzu3%g;aM$&;ttT)hH4`5W8+YIc+GiUGV3QF3(+8o3)l|qzA?JWJ)c=GrOpMCNf zKl#}&`Tn=xr_snonZ^yfX%+P%!0VT<*^V2&`>l8R^7F`NpMC1--&b?)(|@1-{3i0l z*8wkIm*b!2$M?PGzH_;I%)bA<>Addd+WmKH>*-SovhAe)=3_hqeH0P{wro9OWx({jlIu!5p)vtYhpD5eyh8HhhFdz18wp+4y$a!$KA*I00VbA&Yj9x7e z?SZyhfW_^}y++&q|JQS*Z?ulYbLS!C^xFD3ux`K|4TsJ$=oc0Gf#}sB_4|PKs-FiZ zsw;pK6hr34mK;?G-4IZPv$G5S;?pnrgFpUbu5K>*?2rDC+A6Nba>FWx+RU6UScN9} zb+%Gy0wG1Ho|N`L%f~Ny4aFtF)Mg3eIBlud2VVT;UyzoWv$K&_;jk>!l8M8_wAmo; ze=?+Gn|-lhR^=#HieiEid4MBZJ%=}BG-x%X%eY5Gxfk*>^njTt2 zXsrw>p;}B%C1!GNhjvznL>kSKC?CJhYUs=(vsJgMCG^U4->(cXt;GTX)7@tS2>m_} zF*3x!Trwib631rpkb>IN6@=u>jq38F4j_wn;?O8ijCrCV*fEoJXR9?6(}0$;?iV4N z2UQdI1<*#5V4ne`Wdk63*3gucqWcU*kYL_e*0zSynoA}7x*t)h%@j9>?_LE_g3lRa zHl%SN=R!l64}0Evbito~^b0i4RI&9n4FeJbEzVq_$aKapU%lcNUwq0SmX>MFHXoX@ zN$&H}inR(=^EDW~sL&i#Q-vWVtGv&9-0Ss4Aq0 zgG~ln1PN5157GAu8>6}ggfZBAt%{pCGMIuV(YDs~qLu+<1|-$iXtj}o*mLDctbf25 zmnaEn5-jm<1++3mdv;4Rvr<8AeeL($=V$Olx;cmwJO;?6c-2v%+H=riT7luF&Ey%? z85hAiuB2phua-jZRwL4@?~NF2A6K>W57HQ-C9I_dgh~wNKesZ>g2`#MDa2?M{W7yG zGp#fvCYO0QFzKFhwG_gb))*(cQD$|Zm1f^xYIPqtHwKozCm3iS186CvA+DfE&edg` z&X7@%bYw!*TG49bVO?fye~GA@$6}sZYH?RUi9sn2K#xB5H4H4J5>!bsxU5@N=5&mS z-X%uWnTmQLRK236bK@d!X;Hwl>BfRAu#(iYd{6 zR%|XI${^T12DDX@cbHUZW>#I5CN|gelEK$!ZHhY6L$G;RRft10zm{g_(+!t<5w?^{ zNQqJk*ULihqS^AomOINzR;YvT|8+y5K? z*Z<3(5rzpZCbdVbu|nTJVzBO%9hi_1X#vJKc>Dk{!JMm)Yoz9kBE&RUEKg_P+FH=D zzpI-oKKQNQ=6iqlKVn&CTi3e0Fxay)blR&58BmEtM;^46A%(E5c?f@q}Cp{$| zoaV5-$bC1*y^p-`bwq{J_nhYMcUdl92g-CpKu1Boj#cv&g25YrBx7i`6@0`&ypCxj zdQ?z|T5;@e`1+Mic>8~kRNF>(dN7HeVtD1yXVp?rnMB#&d>PdlaF}u;!ED&{hC#EYGpGd zcAE`1*E8+WNyjPFH%ok?bVQ-&XJ4gl-TLhM{`=n7@9*&%1PeZzU=z^)*qI9s&aUWS zunDjNH+?d%piVDo`E?v6wAQi|kK7<0CEf78Tv z7z_mpfk6b-;$xQlcKH4GM7=ddL!@=tOEbE53V}2Xv=EI|p+bnl{`G5ynmJF2aW}C| zk+~=(2#00i@@mgP0Rj>dmL{fmLg<5Az z`n-J}SVbv^0f|^Op&I+65NY21NJMB+=%%ejeD1`+p$6(SFvymo5T*?!SWr5;Jf z)si!bgE1e(k`ieqJCmM#6VRBDW&vg#MgzG}ZosRmktJsXl9CddGQ?z{U{s3Fi8M|I z`e2>Gv{f`Fo6wCDz;{G zT9lg2k0hwBA{j59(+;z<424Q{K*!EmL!#CL0Z7AObG*(77v9uZa{4w5CU^+l-h{WSstJ*6W#Bj_#QYod!*G$uFaCl zC{PMhwC4bY8WV`^HNBHc@eX>{XRPKIb~om`LaYFZ2#s#Q{oCS^!mH^zoi^2Gqtn1ahfJ3{Z*}T9UB>qOm+f2uMogd4XC< z&QzB~x9_MkAPWaB3mBh|_gY?T07h#0%|kQc5qJiK_!2k(4? zi|qwLMpBEQiM(W=Boy%4m!`Z zn%re<1T_F5mxWpigG->Q$F6~9b^yhII33UV(+@xAPhR|YD-*j_+gH^K1J(sqNW+GC zS%Ajm1s+wbP}iS3f5w&MvLf|(+S+-S=Gatyy%#aIX8hHBf8S`vF@OHf5YQJZhuldF| z-sAd836i+HyyVfNCj^0gDZKmM`;=UG{`?CbKYGf1*b}!K(lAnL<9ynZ^TN%%u-y#y zT&U$Hy#tkJUrrcLXzh$)E0*of&bXduZZgE$(2{w4evUY(7zyN(d3AZo_kZKJ0Lb%| zvu(QK;A`?;)>GirQ}Vu?_f2{C>36;3i=Q0DtwGze;DiZ<5`I0Nyv*PJb3(%(v4936bY7U-Hho z@7p8@!Vuu$*^XacT~VASfBX2y>nNpbqVJe&-a65*&X!ZA58&PmVXf@p0F2Mflke#) znjTmI(FhjAFq=t9I-vg9W#xk?>8~gRayHp;bfz^Srp<1E4})GVL%!GVmmqOv%}d zBjX5K5v*Nmt;#q}gpjCe_E@n?LMj|;A*IBy-Jr$g3na7~(R|qt*>gy^I0EU<-8YtD0A<;&R14N}3tLCIQ5Lz)Widw9i7>p2( zHnbY0UxFLusRvnzF;c{Q5Hu#L2ZS_Bs5Jy~DYRzQo8oefB%pQi&CgglV{1gMlxo2t zYQPH3+O1}wWea8{1ZvJCsYGoQ4UFSNsg05k3?ZV)+T!kWhSo?3ts1)`h+4u`D-x6E z2sQ^Y_FVMJxJaZeGYDRp6jUpzHA~)0Bd1_vU8|*lS{$%2;G}9{i1u7HvE;C-S4C@e z34|qTHOM;7gIHQAwJ~@CT1us;F@OeO6ewPC!~vd2wIJO;Pe?YeS|O>hcvrHZT>{~i zj^MQU(oB9O!)h!%5S-Oz*P1cFP*f@Iv6EsTs*>Vh0Efa50@5686C;if?!lk|BqR%9 zRmkGcr5a;KQ47Mv=2DeLRETNxU{XOFld~Bl8i+>h^0NwIunVY-kOEOzC8E{6AJRC0 zSY=Xc_SHK;NLH8#fx#s!pbWzRw3V$8ilXB{Xpz!jZiNI40SGo91R!l$nz2r{?xCdM zt(OgetlC_PSV>~+Dp`#EacG5txN3W4$Lf}+N*ETyh{2`C!PpuC3U zprG1184{W%Tcczq43SN<>U=3>m6n8Pj45N&D8ai*B1*ylm8fd0q+u{{O&xr6L-(f2 z7!w5dCknxU#-;C_f#%G>AtXfY8Y;p3d?XSRNHcJw1z?hm8y*+zA z{euLXgQziM6-bHFssYD?&4bVAp~#+s9;jzFj%ZsqZPDTOfE08{b=+g zI;KP`#_-WrkX9j@Z%In#HE6PVR{5$bG>;t&L=-5-SgDEuT*-K%wv;-sBOI1O+MS~%BO#DdFeX$S%qK)Et(kYz7$Z`YTr>L|_{|^u zJ^uE$zr~|ijfqtnhkZsg6Gh3IkZ~l2!1?((=Z_y-Ox>*vnvJ*R2_|DRSx#^UsQ9zl zj144i6;YL33TJX|plz}Byg9ou1)=1HGG{m652W1%*F$6}nUuU;x36t|{Y8Yh*$_+h zI6x?#XP}kXY!0sgf9y=5gvJPJc~EIeL`VT-yJcQxe)_YY@apgVC(hogW=G(2CAsW* zb<1!mzKC7hH^1>NKmOS-IPCYFpI>lun0a+|Nuu)X*;86mzI^c+Z@v8%(~!tHb9H^q z+1UkezxzI|6kfgff@$}NtE*R>Z8wbLImEy;CYF*}mdv!-5S^V|>q2WAi!F-9oMT8{ zcySrESmBLoEREUUxgR_yQu;j`#O}u*jg86XZ?RVY=AkTZt z=T398uX)|gSx>=~Q^w5cd;dDGzlu0YxPMNdEhI96{wtJb4l+`tCcNjGo3SdU%_aC>0h1bW;j zLF%9Rt0Y5j0#n|^U|BEuWUTLFv)nz8H~sDI=fUW%o3ky$F!JS>&j~S*)R6TQm4_E+ z{On`paF{teJ7ZaTuq+_Awcw}6=o6$lG9g|O5znnfPs}7w&i7Q7)v?mmnVUXVE-mt7 z_24T2p$S#IBGA9r(Zux_!2?!>?moJrvh>#0-hNV*5UpKcN`X=`+Kg$TD#(yrYu`+d z3${>crME;Du?oKH=MYJxBuL9vy$sO|&9I7KQ)Qr;-$f9l3FA1f44?ks8j=q4)eWsK z#55vRX?ag*1+B%rNQTk;KcZDpnlGfL1s+I)F)m6r5UmN+>`A-?4)e@DKexatM5HxK zypk}#e$MiXKj$}p>+f^;w24$>PX;JANl>#k@i-#~S zYO;V|9SO#R;vlv$m^8(L-(CT#;)y`ZSUPD-O+YBQQiBrZ*tSulOMTR&EI}%{c%mN7 z!zbR;*ijsxwK0W8AuxOO%c>fd04pX>N(ix-M6ETf z673*`y4Oq13xWmdv_j5pWrKyC`E#SY0?bbQ@_?$ z(OQX;C?YI5Thbl`Y9TZsro^mxQtj)huOUjawM}eY2oe~ENNCN#Bulrc!9b^=l^9Kq z&_rDJL)Gw{OapJv;~j2{z7qG<4W-S_a^XaOtS zc)}yKlGWDMp#@{X;c`8(ac@$Im<+>qY)?#a@HmJumDCJun*$buSMC-ES3??5nn^~Z zOD#)9Lm{XcvbSm=NcCeU6@pZ1uE&=rW-w#>%V?AjGJaP)1`s z8E}C~FT!^>P_s)zgC&AZj17~TLapRQAq?m;TWo^BVxD$~<>19hTW5tnt~Qx# zwSD61=7#sb`F(!xn;-D794I$4i!(J%-q_gBGqn}AAm;m@~8j4PhxcUcW>%yY(sM!GdLwsbmGQ+ z605tfTNWnZ>%vj_v1Y?QAMJa2fX_va4k^s%z<5$Q?3FLK%wS{q|o$JY@cSx?(P6W5K<2vq8uDCT}o zD1kyZ zHWf=4oOT?d1)1ugq97E6w&bJ4M3s47P_0C*ez_3|7GN6~<>q6~m`H9RBoQlS@X%(a>HAENVMi?2A?iBz5|p3_kYm z7Xw!PbY@Of0jEhfqOnz)I&c?V%4Hy!ZL9WN3wHjYA5*P5-ROV>;O(dby;AODsgYPK z17iLRDukwt!77EhVc-LupQUc{?43$n0x8u3!$s{llbE@jc4KDu0g>8hK|LYC6H##) zoG}lqLL&%?NMoTfU~MM54n72<5l0jjVzL#1HcPS>T zs?9q>1sJqZLh?Xfd%43qJs1;AFgb>?TV(aANn&0ztrVl+c1kv~uJuV0RV!7j3!0)x zH9*zYTa%5Tc5WdwCJ$7yGw)QaWw3c#7*oMy2sRO#1jk5wyI1V}$>NLFD$+0?hEkxp z^Z@U67lMcGfi#YkL$&dVgC(xP46uVjQIn5E4aSn|L6yr#Iy<8%sF^sCVx-n?d)eF* z2QDu!ZKHFkVQIEUh?>E0#GZrJ;`%ICaieFuvjmi)k);$yr}Rs&(7Q~rsrh?|I-^B| zrFr)gRdCsZNp6HnBbEhm51(UFYRnOwAz@f!wEZ>Nv6p6TZC8H`zdwi|&7`RzI-rEu ze(PHJ0cAj`B!c)et>!}#Fi)l=I3Uf+L=bJp2n@!`Dcui6j3q&j6KxkkB)QZ>L5$s3 zRhZSJ3*y&D^?RT~ReN5N0%bljfU51wF-BhltF7lavr60)*FmSM(1=jGiFz|XE02+a zR#0Pb1)NP}S5FLTvQ{%SvDgP;QK4!ufX>-*4V-P_%qg*X(poUNtzfb)6{=Q9(GuL6 z8A5-48?6d+gD4F&B9zsmCSt59+i%=l8H=B+O>$Z#BccWxh*oM<+bgjLMYMypjoO41 z)WB~wMyYws7^_tY^4_ACXs)N}c=OSvXQ;)ZUK<{khTwhYK zpR1UQ?WtcSV%Ewk0a_-y9Jl& z+%zGVw)#@hnRTkD6hy7?pdpxSx)u(HnQ4MeS@_ob@AKmy{}gGByzFVfZ~W#D2x;Ii zfAq&J2j!bjpYr0x=bZ0$y!+03q%@M38*cVD#34E0QV=5h`M~4H52?#SBkMrJbULI1UOt@vtJ4!jkj{Y{=fUW0^O(PIQ?9Io#w*#fhPC8_rAZK{_Z}|_tTZfWxVYfz>a|rt`}MEqV*fft2G?uB znI&s*(`mr>k5NC*Dz+huFP42e_UUXkAMQ5qsqp$oH+kw{O+UvjSvnp$LKDn8$Z2Kk z`vS%?P~Ls^7D(cH&em4C1tccZ*#~I7JAqS`iv%*>38GYrdo6T9TpSWo%)qmSfw7NO z9MIBe(ip}G#42~Tk(!}uEoMAArh!rlZCNbzOva3`&N)uU74ryat-z27(JBi`jK0~z z#uHI%+gcPP*aoMr3lG6sl-H(QU0svnNG-iT11xzagpstn;MLV@mi?Zv-H=)%4vF9( zPH;<_7KAZGuQmi^Fr!sMuL!zwK`nV2)aOoc8m?xN7%)%?6fn)R%UP_#ZULYR{SBCC z)n#2>c4aqP)&CZ)1+96gi21Grp)?`k2|#NIiK>AClW8>Yz|$UxDBdPmy0+6>h^kkq z+|Q*s&_ala214^|_5``ua%;yyoo0_!cYP-1<}=@uBDb6KtRQ9rk`}ZnSu80Ou?MRmWDinP2n1<1@32H@h~8rB5r;qM0OSOJIp+z!|Cs5AOYgx>rwdX@cM5(|ck2RPZhiK4+Sm z3=At4R3JKa+d~p4mf)b)l?9j21pi+LqI-H}Mw2i&xL(yj-c}Zf!db9@I~So)NM58c z#fX7YAt<9%W-K6&>OCAvERV&NHi!Yd4oxOPs}?k=z=gS#TRgtvz-wM&TFt zZ5c)ginL5pGg>V*lbbz{f+h3@lt+lRo(39Pk4b;7g&1&y+r{K35=kE56d#`sz6e0F zsxv6DskPv4nI^(q%?qhY0OnK zb!JchT%CoXN~^_LAZrwB>x?^TU~BONGi)=ebV;yyJdwM@yR zNJT4S2Y#A?NDV8LfDoz$Iu$d9-N;N;GXV~s{FUYm5rh)FD5SP$g`mULRX~faSZZ=ciRKHV&dzF7uJ_kGdhj-5iY#-cRHa1= z3~lW;c{BbjTK)&yopmO?y1wMm#RcE^#=BhZ_gtK9nXj)2JLP2UTWtLM9O zzWDMB8p5}J?OU`ub2v0|DLf!hb7h!D9zA}_k`KIk^@7KbpRg=5!*)X&660Y1T}oyt zl{mUl`ALSVGvi#8^gfW9eLJ(>UiXiIdNS!J7?D9r;@XIccYmdE>RF3z6t(eqE3LP0R2umYlu? zMa_#~lENsEE9{#EDoLzjpHgy<3VR?oVu_O(Llz_^mSU!U%p`MXv_gd04k3tIU|Qz5PAc!cwf;j~Qu-x$Fo~ax(xZ1jxD&Br>A3P?<|5i7>@L z6QirMGl2n;`(3%oj)GL}s)9uSsYTluu-y>|(?B;f=SoIl4eEgm_5u{dz7%cHn zTQ4j*YrtX#b2WQK%R=r-TXmU10&%dU(#&ox;i#g7V0M*4qh6OasI01MYtIz1fLMeZ ztyHhs_v8kJ6da69BPC}B?D=U$eI4wTU`u$TV%0(dp%nv-JlG9_8TwVFXoWNw^FqX> z6Kc#NG1@wqY$gR^t`(K!Af_@Bs2pV>crfE7Ns3EBx_^L4m5LkunlFy|R9F?boK%ov zN@P`9X+&w5Z>Y1)B$@AtRuuOLYK={_l(aa@r-&yAYSqD2$|zoi^l_=Gq}H5$Av7`1 zhQ2wP^uw5TA*i2oq-tY9+=$rNSWfOH;6XmFvse7yDxqbQr>HX?ssV;w0_DpRL4xVz zg8^~PC1oktTvzwg=+Cx+LDr3fVPLGriW_`B7f-xG@XBjlAetAzfK{C(jn2Zd`A)0m zmlJGV-UfT#51vR2PV-i+w%>;CC((>4zyWb7#>;F{7A=LcxRI$E+e1P#7EcgoV%Qc? znn|N#TnM#N)w(Hh!U7$ceo zf66x=Z29R=f5Z?0@ora`l^dJMvFpT3beu|*}7b5 zNHoSws|~JisLjV%8%dY$qq_PcpoJ#peG!=e|3Co0Ic?<}N~N`Gojuf&Mx_v<8aUIz zX2roawS5drpw*KiS|t`;tKCq&=ups{Atsh)-QR+WC1KH4h3Ip%IG8+$&5MeFn6DKg zYhoS5SfY|F3G1B$34uc?M6m=ayPQQ3nnZu1AYG! z;!Q0iU1%}U$VLvtK=ejNXj~hNVv-K`BS|PFH?kxai3TQxLh^*ZsKp{fGNwl^CZP+p zxL=bo%jEh7!eG7;Wu`3F8Dto&z|!NCic7SsQUne(mS*=cgy@N%m8DWhsBve>&SY?| zm@^(Kqv?GuVDMyG#K6%KB4Y@IWwF>TL_{J%%y4~CTLZ@?)b59+-s)X2;-NNV9L*;$ zK$OM2id5Z<)LAYe8L(9|O|4*2flW##PmMP3TJb`KGfIjVE>dfxaik#(O$nM&iDZx0 zw%%!Q&dVyv6lrdHYEs=SCQ~Vh&kx&wTQdj=ZRA?K^N7m?2Z5rrGJEV&S(-q!w#eCI zm%eu)-W|?@T>~*ZGs)E$j>+SX=7oqGXN{wkJyBUgj+16fpB)-;d4L$$X_G1WGG?lf^pi+500(fz>}QRFKe!DL9)p zQfuS-`oO#IyibzAAmDS zaoQ4NB8I^A{ssxwkyEqD_)5#DB*10M-IK)kxFA+I?o7s#XK!V_fn@!}w#ThSOzVz-ThFnXlJbQQ!apd{SOFnq#EwV4zef&DL>Xh|jRq}w; zE`>N9w>Mv_0ezOxbcq(HbJWk9b!_fT<~w6#Xr?W@1}KUHL?$;dS+P|N?c>1`97)_9 z4ro5`pZ~poz{f9N@#6I>=91A6nYI(>XIn-$1&lGFaUhpMLcJZanBk=aBtprVS_-5Q zB#}HltIgW7hBzQGF)uR;^n{me^A;?95zVJl*5e$M{#sLxGh@6gYe!tNYs7~ z!(`Xr8fg?X3blB^1omAa5St*;+_@DX1X37?gH?xXt1O2DV+;)AK*^afUNDz}#7th4 zZ-4u@`K|B#CO6sj&(U01DwWbq?vkXj9isye1}0F<8zMXHcQPXZcm5B?grK=IeIz%d zNDsq6u2w+|;@zJ5VyH&r?}>bN#$K;_6*UqsF5hYtGYwQdw!aiZ$FeFzO3rYps5?D3 zr}m5yO0Cpdh%%UQ8(uY2t6GON0A?^~2F-nv2KrN@4(25>v)^VsD^#1A2RDUa8@ASt z))>&H7`Udzy)1%(LjfBr#1f6%noGhgpb5c%$foLDIb2Q`OukXgaK{1$45X0OY<-Pp zde~dKPGVqG*1onFIA~peOuHJSud%_pG|{}zvTe^P${I8_0o16>oivnuejAC&R-s%y zX{W9<L>W^gtF>~rl-QD)B*w(D@9?(I+E6jGW7N_bn^(=+(F@dh zrnO4iY%Q5nd#1-Upw0M9Ay}JaFaXkSq7Y)BmAq0@b9K3z2d*OKQ6rwPfSU22vdU|l z8~)*QT4JKMYNoyoYxx{H2hTbt)!ay9Bn3107O4j0s5k>E*_=Q)8%#D-NEFR0>q&Sekj?q~NBi&00LiC?>1u z`>+a2t7HO0GO(qUVpUE*_fA+{nljN1+{NTJqDV~SYU@OmhSI!CSzX(|Mq!mUlw7H5 zT|Q8yE;BJGLmJ2pE+jIDvgAw+fo%xPxj+nTqOmcuniN3|+a(2f)GT8#;LhY7hn$Ht zCbj26YVq}|OUN4IkQj$8dA{+u(!hWwW_BIK7)wnn8-$G-Al4a*t-++$MQ(I2l}VkE z7XnN31U*L3Y5;dIQ`=#(qKCw=8h}P|u%MZ|MhV0i*ua^DkFdwrjcHY%nZg&t3SUXo~HTG{N z(3*8j3YcWd7*88oHP5sZCPH=dW^L?CqjyvalF(fE&9^tCpya$*98)VI%FXpPxGcC4 z8HdSbP0`?GLz|2d=G~Qw`@HmLLcG902oCZZQ@k_LqXdh!EFB;W$y#o!nhYpfTjU^S z{ySe?@mhsz+3~6@e!qh!4#AobI2$a6gr<>Fi+QfuARrpZ6&5c{jS>hYGmZn}Fq+A_ zHd;PVTcIt+uB=3!KK(X-`!|1=HZSDGSUac-0z*K9#gQVoBC1n zd+>n2`1vpS;9K9}Y#L~#@rzGC9H*Q9ZB=Fn}jLqFc#*3kF;{H9#*O=O8T5gh(1xzK&D zuY1qefiI_@J;en&E5E}fj$EwyfO)7v7s4twNBcYeCa+&jg5>C#*S6_KsE|O+#LZ5$ z52{tAV=&XFyEpYN`gIDP#2D9s`s>`#yVRyrB<{ZNdDG`ke|LJG_nq^bC{J&qblv*9 zn|cN{bXTa=SZ#`pPY?Hd!3_N=HfY&ddBk!ogboeIV=VS*sX`kgg zM7V?Sy#dWU{ay6U-0(GBHyR^8nK_)IG^f0u61ct&DYaiTmzr4NQ$oYqn0xDJZDz6? zEWz3h1HbdZyZpWH{RWrI%%X64^_tH<{+KU5{{n3bL&yZI!oQW~K@m&=cDo&TRSF5r zIg=zX3?r>oZstrYjZtULr;Sz3TcL);r!PL@c|FjgnN_xCCRia2G$P`Ohqd&x_Jg%G zXXdEWLGi@ZK!dszDpqmpFukfJH__?PqKz~pPvEWN%l=@2`I57-Zek>c5ye_k&xbv~ z`@vIw?*|{S-`}w0LL;y&i(dB5RHbNLYDvE>{sAaJLWtgmudK0Uv1PEcEcn21(m0HcnH-v#0qa|Jb zNNO+-qE;avlr&oB7;m1@aZ{o-*#YdnlnTpi3;_wo?23lVIH+fztKbz*Z!atMe(!#3 z>sLFTN5}1L%>^iiA%C?Oe$R@oi zWar=1msmf?TVsOT1#tW>C;6_*wNALbmZG|1duuglm8}R2$xPf!wx2_A z`yU_I_2+iHm(lD{aHhW*TQ<9=Vq3y`t$q7gDl8eEJc8f<7yp`bD*xN>f18XfFL?dSpYVVF-~TrZTVdWGuLBIsOEI{}#^<5nt$ppwykJeOuy^T=N%|mH^YgJ{ zf~^@#t>oS@568Tz~T5O#fDCSYD>s#eaX z$s{=17=n2P8Iv;+#X)s3iB8kRoC`v837=3zxH&8Y5q6u2-qpnJ|FMHmkYKDqHII_o ztVoc6=BCt1+%L#0$2MW@=7y-$yimQxX!A8SCdfIXH5f3V#h!hQUgaageZfX*B+oppr;xP0f!;bJQB_Bv)#Q>}G2>ba!3xR3c8at&Fs#XrmYyz}kt8tk#X&BKUv{K3Y8=yH*Y5zSm zW9wSO0Ao80Ltq#-gfucbNT?Nl@o#>g%hxX%pFFkWip`apD=$9(97H&K{M6)2t&v7E zVVyS7yUby8UrcVC7r>>f4F^K>Xn0*Ime;uVTT}HW0r;ALd0C~=_PPk7E~S>ngWUzc z{=0vN>EaQB$%#uXl$sf$5YlA9Mp*!1pEH%fl%mO-;z*0#qr`%YniuvrH!O!euV20* zY%U0Cpk|X0K6>zwCy$=+^uamroNu`~>@DqS#wfORvT-Pjbs8N87|hBcG-C!C(MZv% z%!fVCo<8B{AAbg|ahMlkOnmn{zsWcb{OJ$>h_myjgpjzoxgy5K!v_!Dh5&AEE`ekQ z-Yrl{qDZBd!ZH`mb~}b4u;iJljoq*z*UI&NPu!iEVgzS{C7W9*jZD+P?rdc6ZfHX^ z*;HASw;o;4)Ve5bH-W2t=Ce<~6R{@OPeQ*dVc0u!inw6Uw5qD^!n8# zNFKOoj}SQmAy&PzDAXQ&S%b3Hst32X*(~=-jo>(_ca3(wLLEAJ-Mw-5QJ(tG_ub^v zbNQ=eNneNF-S@qBugyQSB5s0Gmcsej897%z{OFgw_0BUJOCW{DH{W^0zy0EKUSD2v zasI$4o}9GS!sdrnO&u)zn(Mgt+SC^6#?h;%0|>{r9I-z+0etS>hx;EqPbfSocPQ3^ zb#>~fA49ytS94~!o!F#83a~%S6g`mVYkvIWA93~N=j>$RPz%prykra@L0Jx&%`{P~ zRlI`xxP%Z`@LJ%%s!23t*ZyOjERuC#@03(KjK}U0YCIsTB)A)H~Bi z@C+>w5TZmR2Je((5)v)d&&TJH2xT!t(h!V^Ltdz@aySUDFK?{!wlt8&I7O>UySDbh zg9rTa|M0K)pZ?jupjK;5+%!H)G`F2On)*%5gYf}kAiplP~8g#fMzpLy_7;J#Th-enhYtD2J^mXtr}ZK6?3LA zFjG}&bs-G~mckXTBk)#3@fwGaKXfBz>u|M?T@>*rj*x?Ecjqsq8D z<3~UJkmp~%;D>+s2TVz*H9MGNRl&SuHbY{2z9V2DxFj<%zTWT6cS8)UD6Mfb&ls!H zSPrPL-E2rHIvY?Kr;)+C@}*%Sgvh5id-FO#7?UuK1G&O{I1mS;e=phOZbORJjx-nI zFcKsgL!ugBo02#fC{P-?DdRMO8e^n03u|-lCrpNrQew`tc`2!Nxf#cam@FY(x~xzV zOPfh?fH06tWu6bj2zgmZ!^kvldGX>)mSrKNL~fPO7iW2jS%+3tYBjUuVG2ych}L5F zyu0F5^Mq*4#%;5iIF!QWVP;H$oEPThXdc;`QflRFyQQjij%j6KnTvHJ8%7WV)y84t zD%MD;#rNYzDcM+8!8~W8h|7a*)f&bLfZ7Vvb~JyjTv=`o%z1Gp%YhgZ!#Hp_T(iHq zar0@5Ri^D02;^e1SW2UTBf<8H)@*$b;&D(_rg4NMT;Cix91h0T+3ctl=DaYDks(;e zrc$ipuM(IxI}V3ESC=nIl8D2=yeyoZ?QCxu2F7v2&8t^z$B8&@C{?K?qnP)OR%N^0 zusfTKIY%VVH!RgUIqmlgd8sUAVP0lynd>ae?G$MR_RC^xRWWvz$@#6AqiO(ONCVe7 z6I&ysxJu3UOSbzsC1Y3zAr9v4QEK!!*t+eMd9iliVvLa14CuQ)EKI}5RmlV!1IKL)D+u$bgInD<*P)mgqUL?Nkl7Rt4<8O*OK&TTA_`c)bOhF>c zJoDbWZ}aIFFM09$lC$$24amzi+vy?iz5g8)rR=X6QsVim7pO+I=M!bgjN=(0M3(&( zmp2PfpS{H}3j3=|mZft3;LIiF)`hU)2KCiImvM-`hZqnxjU(T9_brAY+1^zfIcJ8b zyqpVRBX)&~H*e)WPmH?{)~)T>8sxAW$|3WT|jTyXmO zI`HFe&fS04|NC{h>em59cU3QUyCmIruD5|2^NL#k+|+TGV-9?a03vwz=KntcFg{#% z^Bhe!^vI}j+LLOa(HPuOe92|)VgcM5sMFt_{`@AC?)2yXAQY;XD}{9v{%c?N9pvt> zgRtHG{8!FZ?f><6wpN)o1H0{pPd@pC`7m3{+-B6nw;pWRZg+hC{F29y9+S&g#|PgG z)q$H{)eUz5lCPY1uRh}56{s79aV$yp_j)SXD_!d2$vtCqe5k_8mgx+O<9p=RgJ|Dj z5Ezd6FBF2Q4C?k(8pF_f3S0 zGw+HJ$;A?%izL)#L1IEBu@sXB4%WXLLSm_A@@i6_X3`0tryI+{P#GwP8-klHhLAXv zMp+ipW(zLG3Bwi{&ydgvR5s&8NCPrGG{8a4K<%c%?))L!apLlNPbmu*=UYl`)X|tE z*Vos4vHt|EBM%-wr45mCIMAwXUMXIXOC_|x*@FvShmo88Ya~_1abnruFl{!B!`3c! zYnt(!{SC`P4@Z2N2Fzj=b7#9j3Jn0 zjc99tHua=<<}e@Fh`3I@A~LS#tRY3lX|$?!vTgy(%{8dd%EGi6I6pfhq=|j5Ivg{eh4Ki(*&UOTZF(k%m;;_uj^Fnd&rrmbK zl4lSD9y|Sd+-zudCWV2ko0(cF=ev=-?8zF~o?j4EIqa_}r4iGF)B=;S%<8gd8b?$c zInNZVz5JAlwf7}*lMB5Eo~(eF)Vw>v|mQF1mP z6q*BBjXBR&-K^%9B_Tpg=7mukhtk+xTrdrTcXl(6f>I7>i%iqRyc}3sW4GBD)2^6o zVzb*)TzYhNw&UvZC6_N>5uz{-1Jicr33-L8)DYQhHf+a%T9uo*FpeXGDA!j9-g@gD z_Oj*o|M16bQnLN67RGTyu9ws}^5EU?^8frN|BUNbS4_Jz(qLU_%97db&d3LA{~Kds zSzMx-5=tfKj1Z{=FJd&3Oh_x#b|9sV8Pa0r#9E7Y(5d9=UI@W_M}~2-#4yij&1P;L zI%81H$Q`^}nI%XX23oZi?IDcLPRL}n{dN!`O_PslW;1T7c_F)Rf?|N^7$ZYUR4tys z7ne;AW^^7SwOD7n&1UCah+vr)uCFdF@$wexo86hQB3(|oltL>Nkx0ha9kF3df@nTN zl1$ziLL8VzXv=||D=|&%uU|7C z_T*AI%rl#5@^KW>xFyEGvcEO}xnXRTVA5cT0_xB6G$Juk7W1Tt1X46wSQ9HCq%<&& z1A)RJXNGA<8V9s0Et{0$=CG&C3%lLU*bB9A$b}FS(~!`*m|u&tq_&%pS{ln-I6pr_ zsVwu1s>wH7IUrF8DRQ`;Q34MhKS7&vbF+7VNjS{20e8S4%K7;Nt`B>bQV0^*j5go) z8Rq#wlEyGZN>)nAoSkp2Q`J()`%7bfMgzZx5ZO)>1Y>s0MW9*Sl|mvni!xIT?$Z-V z(*_BNoDb#|mqzpI5o2zavVdBWJq*_2Y)BiF#xgG^S=??YHS_A#3m!dsi?YAsuq+Hg zsE3)W!!@uFS|iUH)O?O=U0l+Qv9^l=76gmyl9;@(5LQ!jiK7*fl7oHuz-HQ-F|`9g zH#d96A(Gv~9uC1+HX;wJZtR`*K@#39mJX3jGjCrY+1Oyg!l z#sr1G{P+cb`O{B`V$TxNrBNpNs(Cg)nv@uaf%$M_G)e@CjqTeEH>PeCIpA&a0O%smp<9Z+(M+^77?#l)$sM z-)0jNUw-~M&5Ps9Jd@lD5@!Z2C9~OVK#kp3OW}|gHsjWc7-9t@)y9L}#56>5$>a(t z4(#`PN>Sc<`w802vusR}*5DVv_>gB$p7P*g1KPN}eoaW@?fh>ge0&v9In62WHg~!E zdXp;TE}-&t>X-X+`!`9N?t9OD9h~mI|L!>)KX)wft$8S&8(?{@<*SV#VywHr8TJDF z$`T~2l8f|zS@7JAvetoAU6~DF6i`Ev+gAqRE&|sFv-_Hy-o#S|&0mFZ^#NVwN66Y` z=Pvki-)HaseE0otV!hnw;QS^xX+8OGt(CDg9zA-%#~*#l7oUH@`yaedohz*hyCLxY zJ8$u4Kl%yRH#bbvWRB?FA(9irQMIaUAJcWl+&Kl+-b&H_)cicGT_X;v+(t`}z?=9d z?nhBi$H};;+x<44*&cQ{w4q8AN)Rp7WT`YoBf`WGi{mK z?++|BlTv_YT>{RwXEsq?GBdYIj%vwFHpAoEGQ*Ha55hm7V;OpC4wXwYhUTLNv))=siDiW(n)7%h>i znXuXM`Ky<_{_+c?7H+;gkcWY{zVRLxPv7DE!Ip7%K{`L@i(meXFMjeT#5gjHMw_OZ z`J%d!W*kOiBD98TCdA}g?7|=xOqEv5r-X#I?10K>!KOq~HG{_D)slp@2OV@~6exLS zck$Q(szPmnHdmHf8HO9ew4)7YXiRLkJI>Fa5@TQ(FOV2W!2)`1!aOewdcJ5fQTBX z5u(Y4Byh-uX)`d!k^SXMLJ+2LLR<6BGu9=b8y41TbFHDyzNt*Z!1>uZH#d7!mCa_$ zet*qUGSlE48BjLk$YyiK<0qL`3ol8Y>b`#%{9XinWimGl`$D* z_0{ETVhT)YV8368DR90uKceyM86Rvn47)AQpT8ghnuuw1_Lxz=Yg0?qtNG+u4+zuzCkHK~2)pgp zJRhn(L;Gc6`y?>Nk>zm1G>weTD2T-je24RQIP9+&h7kqEVZ(#7GplrMt_TEDJ}@tZ z?e>iA*#@Oh=fY+f*qxsv^UUNej%%6OZbr^9kFGfv>xMLrjA=ycOpFt{y+M8z>T0TIuM6TtTFj`U^XX~UhO#`(Yd_6W@{AL;*jJEY77e(8`STdvy zqcboz(;2NT91b@$jf`^GH4T;n$GFQfE27B|8VV((|93qg0M5~Rn2M<7cBD)llJp77GW)g!9fSr#@^XlDGm%5Gtw-$`uk*o}Me+KY+?35^=d13`G7J-?EGBaf zW*WRKHphl(G&56_Gz&jGHa<&7LI}Dv>h(!5C*a+bM_|P`5S-FxEnfwkOX^wy}+ZQk_LN zuD)xfR7%dqQWNvJXo|%e!D6g#bX!YdHw?rCuMZ|+eQgIWu9#ws1ou5#r(>e+S(FRvhG2c0es1OG$v9Qt=pX1dZ;xUXspePA%Ww-R{r>?sc(jHLXUo9DkkuRrD$4O->LKl%}0TMfw#Z@ssvS99R|^5@gvzv=s1YhO9meXskz=aePW z|7%TxZYxQSFL4}WZ98MRa0q{64CYttf^=jsoLrWPr_~Z?MNsb&p`O5-kyHhDp6)mF z6s$PC@u%QRf1S3ce$!35b*6VFz%8BF*IeQ0%{`?y-IX2P2dcbjV%&$^-I^Fq#xFVZ z_|ap2`O&BR{AWMqz4zZU)J+x9!Z)5h;4gnZ^4S;9`PK*T*>~Fk?7RK@qRQ&ptelLO zLn)^SUsE+br1Zqgj77z%SWOJL>Yd{ZP`8SAbYnF{R?tLU2j0)7_RcU4-WaObDPDJP zno-`>k8_xB_~3(Y@crNU2mHVPeVZ1ZM2fnvd~)Pl^ShQ z1C(Vk*-|dF+AP^0h7}~V%^Zek!K-zkH3K!##`S(N{jC<1KyN47{PUqTZz*hK5((la z)rPc-I~n)uH#5$5oX4%1{$CACYkh7=GgGpqaJac%yDo?bzx>Oe@Z|Ah-hSt8-uu=! z3DX%r{G&hN!yo@C+iAxz+9qTRhSO1lH>s>H@6x*7dkF!->6u#n8FcWiw{yhWQ`%-- z(I&lcVfVdQtH9hMB~Ebm2+rSv^T#}R_d7g&>s{V|@RWDndzWW#y~UHKPx#>dcR4?w z*bae<%|uq~%rOLEjFFT;lFNoGELB;SJt7eiGc}tR>%tN}@KdQ4=ntdIBF(sW$)Z(}vQlMYlCv#;Qi8 zdgrDjpORNG7XI}4a~U_hd>DK|DmSS?`_lrR_zrnjKkm>b7O|3F`-!dVj8q!zBefi z=4(j4VF=tDW~OP&ZaZ>)b?I`yfJ7@m1guj{@0?Nz%y}lX#&+5Okr!8&CKaPG z4ih65EP^_tq4Dzd3)hivXf-2J330=|RD%0r1R^P!56Co}nJfcnrL7$mN&^*R(pl?e z(7L$SMnFj>#dLFQC7E1P8fmm-W;DW=M@u*_%W{Fg8|*)@>$u zw~!PK=m=`TZ*F3q5URAcaIsB%_=}(L_JfIk{P*AE^ToPW55eI7bKuWDx#ExhyZ@2@ z-GBXG5w>S6OJSKaXQP>rFSRjkHc&IUfkyXFVO%>sMGCddobS$oz>*Jc+KNlW%r~RB zL3R`~v~8F#1qij7&p^cY)nEx~s}-|rE(YN2&Y%|NQcWrr)f0G|FN!fFQy2`muaz_o z1PLrI6RM2lTG$R=jFQA5XJhlkffyUHEhLFlgxA*xVj4N$Z5am$YSLL$2<|m-eW;Wc z2?5S0*e1x;e5ks~^f(M_p$A|o)qwILG7O2bn1ry_il}n7-7$=TIa{I5x|4|;6T|D9 z*OXd0-)u<=AqAFFDejH4+fJNs0yp!*oK3E%RDvWMb1f`~8L)(Ziq^TMXoOm5dC#JW zu-Wl&XJ(++H-*D~PaFnF-c=3Axw73%Y$t0~zhv`DD#hm7Ho5nbD{!}ySF2JFKK`JB-@f* z`C-59?&n10aOZp9ee(rSI9`h_yBNgCEoI5^(J|B`u3Wi7Nf~ovJ#Uy}{Ts~L ztWTJmF)a333`GnwSNm_BC-xVK)q2IFlNl%iB9nA@Hq!?4%JItC|JCVg^r{jb%2=5uZ04oopBuOH#10i&ZWhUI-0>#N)?DEcXr>mIoXkJzy+_ z^(3}s-E6|8X{NTBTRBBbtzk((?_#mU%7WGUm^K{n%4cu$?6Wu6Y>pJSHsv)v=OMHp zrI{z@S~eHsBn8M2EjsJfiWi=}$-{?_x9z*`mF04uG8C>{zAn+|%58i4jEZGPs?Z7yB8&bq1c zb$KuG;&adN`kQa_%=Ht7VN^9j7t}EYJjKI=lB*7)bT&&01V-d44}izOMYHPeGZ+~0 zH?t534Oa;Z>pdr^jy>I%i;Ju`pL`hHdqN*{$S0814({nZ-JL{cvzmBt=WX76^9OwJ zmZ*eojW#znlXL`5R+ZL_k_TFoj)bq*Y)(!@Jr^wq<~5`$N2502tzj8GY%SWaQV1A_ z#y}%)nLt7tf-98L7BN442Dj3f{Z0crYM@Wfikq@tfAEaw=^?B?QzsQT9{yd-f_{aI#&wYlQ&tB#Fl?&XsyvN>PY#tvmJ-*M$ zfF@p?XoPd z^>jYh))_h<_<+NMC1%d5!8nf0 z^UQh^mDpNodBO3#VQw3S{R1910hO|pS7ppyYkNz^xICoR4L-RXh*Be!1-I`%CNDcSoE~fSUr4npQ#y_F5JZ1hP!v(#g_Z*A713>{$mag_Smm(Jb+;Km7x~`{m4spZ^Sx9^GSDETCz9JJ`F(x4!>6Z#{U#jc1-A-QR@f zj`N^S0zp$@qY?*=#CkJpeV{Qem&{XRzYNs%F~|FR42zLs&i*Jt$VW%Vtf!gf-bf;| zUQb*&SYVcTe00nz=7UjLZ#FFU_sQdedk-Ga>crvxzHE}41ZVGCB8N@*roje13#~~T zX?=VwTh=_X-c<0Jz5RU-_V!4w@0iJKrkNp&5%T!aL#C#x{A#`8!r=i!tW(F+%*ix! zuz!f}A5b?(TQJhiSPohCv=p)DjvjHlL9)i)-af-($l94R8qnQ5A&Su~m$t0dO5T1A$Nk;;M(Z@$2?rkjJYt_Oe+h>1B)b(gEVme z&Ih#CSnglscoKzwTAeVIk;8)nR_hI|xpav(YZA_K5L0VvMw{0xhJnr8$l+M`@aTvk z7qzGrwQaPN^t&b|#yp9MwHV~EH+y5AV^0dexYjkRlNF1lT5jekw9u-A@ZAOyvT9z8 zhgdz4&fBnLo=!p-wbAMt$YLD1Y@+jGA+rn*1A+Op8k~z-uv<7A8WMA#Yl{-0bZPeX z4)9hvK0d~Vk!;R#TuS7Bu?If0UOl2L4kN*xC5G&#wvRfqn$}9BJ#oBQvsmntNUT;z zWNR$rIm=btz)YOw&rj?xk?` zndf-n`Dgj~s~_W)mtW;)Uj7Wf`5V8;{l_PK_lK|Z!#CdJz3;!y_rLW`KKTAO;H_^1 zwvEPdGHm{W&?pxKRaaT;@+ASrnJ@X8g zZal;NyZ5Q{EOF{u!5Z!v^1#92J{K=vWqI*X|6PpugM#hf;M$EB)YeMehY+;xLJ!h( zv&b*>wq8iqt;)SJ%{*mw4fYmoc2T-~JKHy+bZux=5|g$!4v&P^2%m*;E$Gg@n3;S#i8ti2;g4 z`74?CZ{Nj%mtT6GM-Lxh1~;!?Wl`W@sTgs+sl4;{JG}J5%amgHyyn5fhg-d?o==>Y zyPnr-RJ)5_IUg?gC>G0k%$kpSo%g=8$KU<=X`;Pn-GyRV;QGrjws-QS%R z-939#p2mAQ4;(rB@24?W&K_qMMf|9V_rx(Qj^7AiV&>-c>)gF}o44P2hs)P)#BsGS zzVOT?-hBIAZr!`jOV2$^nWJ~{bO#WFMPIISFyD?rZ%NwXeo4r84Qgu1h3so z8%ph9S)hZCG#qy6eP$+9-1^+u1rnn6YfrY3SrW+O0Sfi8-dpVP)_WiF=U@LiuYU2@ zS&So(9z5je(F2YiJ)j;xB>BWM*AFJ9gV(w}hk@DMLcL}gVEb@PU`Zm->0 zLX}SK^llU46>A-F-MNpkC2tW}!6osmX){@GPM^U#smcno%4|Nz=hJrl>4>j4EHCgh z!e)ii0N;79-d~$zRaHfuk3xn)K1~HCxs(vc-7w@qItBAg0u=H}_oSu7Jk1!g2nS~zQuJ%K)0QJ3hE{7VhjBZW z$uu`Sn*@+slXYy^t=6h>^lp{uu?!8FoU(*Pllst;MR6b{F}|9Ui|F;)oiRX@X;#AA zf`MaUW8IouJ{-A=V1~Di+C3&!b}LQ>h9p78)+D@F#(ip?WWi~foE^NfnZZh-B&W{C z++3{#(R)050SgOklZ#IEN}JV^CfBH$u@o%JZ6&!%Ba_dRvXt=etR!Tf1SsaDc~e^> zFP5|>ZKz>fFmGno>l4Z_P|C>3>X@x}Knq*UG|0v~06&4&0-Q=Jlv0@I%~q6mD2Y^- zYK3TPk|pxEWV2pNs5cjlWwxlk4_vr(fr|%w{FndiHf1oH1+y|hP#X`}B;!B*C;uBf zL~V6%1)_UcGqqrA*1ku33)Uaf5usv%bXtO9X=^{NX_!d1kt19lMG>FH3M}J7pyWJL zLX)IL5}T8S6O43Y3?;j*Nx;_Y49PKNWJudch>o>V`Bf zwdaIxLq_1`Bf-F!!s zTSD_ST#1@Xw@+IDuu+S2%Eq)=(?tEmUH0v%$5z2k6$?~s z!MQ;m7t-)+RW|LGw>d|fo5rYuTgEEf`rh~X?zg|m_}`9Pyl|1rm#^^r^EbJ6{W8zJ z^hv((+rP@<-})WyZ{UaT-seyL@Xz?#m%qyGZ~hVU555f3ZMAjhM$3@M+mN?Kr0EVe zt5w*RBP~hvxL-#z59mg&NEK+-bnf2B)o7cE`wveb6;dg&hni|$fU5PqY-d=`cxf`7 zz%b9-A}JQ#3w_>2;mz6{g?<%T&Bc(j6w(b%fRLOTYk(>_0GLX4!&BjCRcThmTylrC z0Q4Lnm4ueHmeeK(xL$aLi4WfYfXAB&tGd@}6BJleQezk@>*E!7H@8W5?(+WYKg6u? zc&hxyzy6PS<)xcU4{w8u?@HI}FsNY@#)X9Kgf~vcy$qMU~U_V85j01Q1+IzR=2IZdr2*o?gDt7 z%CwmomJ8OC;hrg@OfSqkkY(Ju_kdjO4NM||ej{{{db zzWWXjRujwpahnUmkAoSzzuRTAMi+F=JvRE#H z1=HCPyKP8ksZeKMXDL!odp)iH>!-cpPg7Pt?>x_IUwYbgY|kkcKQox7HHUlqT)B9K zAH4P>UU~IZ4h}9bt=CZD%E8Es&)?+rH{apvmCG!LrTW;nI4|m5HQkNb8>w}gawO{R z+l?Amz+nl>ID*4OD2T^mKL>!ei(x4-J_VVykOnXSyg)<0;|l7{I0Y4~lSZ0?kv`#3 zkmV38nt&UM`d43g`2~LI*Z&^h{^S222X@5uXAU^IU_3rqaP;_qI&VVTKmwhtPq@6u z9NfG?BXe~BE}P?r_-4ITude;!N-eA#b%M!D05A$xt<_)%g@tL07L_a}h=s~=3tLdC z#OY*X>sT(BDZw&JY+YS6LBhV!B-GZFFnav0&8|vDmN1w%7jWbPWo$dFk?4m_W(<0O zaE@6HV5`4Zl?TVLae*xsw7_4i^&knbVDdOnH~KdPhG*duU*hI3e2HKB*M5`F|H9Al z;?)Bd4{x!#_a5*4;qUV2Kl~ma-nzs6J9k-6qJz81YLz^)Tn>~Amnq>0(`sdJxlc;6 zJ8iCDKBKiFzbP8>9`mf>dZ7ddnGGSHBnV9O8{DoCbnXc7_=(8%aTfr zpDI&xo1^ztqn48res4rD;kpNw@mSwltKkwQFw}xV3S#~>hy4nR_DJh3*QiqZ|aHUCc&mbR z@9TmYE$O~k59+*;5Ie3;8{Co-P?z%xwKB~T@XTRZsd8g5AES3ykDx5QMNikdQ6kwW zNxBhMXMtR?o&YQ?F`L$fWs=zJw3(n7gSmhg>m>jIHDd4qVcDBYR)4!I5VSU9DKojV zCCizkbIY$@SYbH_|VsR>nWz*+XiyE!Q%pHtU(iVIkEJvkk|WoD8?3_LXuF&M&a_-&@-c>nM+LuxGd7F;^G7+ME4w2&Z|k(0?;uh!Uj35wxi z^KJ-Cw{X=exx~3ibUe8;)GFYL##^rj9(fCT*HJ(JC4z4{ecUW0|02i#AMS9K=XiPa9I$#tO%u z>G+3x-Oi7MEQ?!40-veSkdsZ0f|HyHGi`P%RZcA_MTIfgi%Hhb?hR7Yni+wE+MKjk zxfL-WP^CMC=zb%SQVr+7tmk;zNF&XCn|w77@W^7P3*!KH3DY-JcxfgJ>|_IbK(?m$ z_V!%UT3zvFn5vBwZJ)t)F>o?wT1#4U zT1$9#Jxea}S{pJRT5Q@oQ|v1i_bTq432@gM0jUM(+JQ&JG;dKvO$eB)mhq%mq1FI$ zOy?uX0NI0AGZJzP?eo$sTvO46wdUkt(t9?li%C(E1mv4JHR3z7tb)o=@ckFqTzHo8 zV8QtCZI18XW5C$Ibcsv*OBN+@bh5(Al1n$9=i-%XEcW+9DBc*xiMgKe{EIJg_x3HO z&BSKC5lh>|$V<6lQIPD+2eq)73R2#4Q5B;$)6BtgSn|KrAxGFX4Rad&6+Fw2OOWs?)>mzpH?^Ad%WTL^=m3tO;g0^ zn!D2sw7@H-D1?Ucb!3G{>jfXjFdqgYR?m+I22plF0bO z`*$R+eM;OJ?S<@62ue-?!oRl8@dU%$$CzH^5+ z-*}VH|J>)iHJzL#grw1)niK$bA;iQ1G%M!I#app+Cgw9Hqywce>UC7x?l}zo$&M=Pb zFZUT1a^k@%4<3KOC$3EV-fLgytsnd)&p!V$X}M&*+0bTZIyqu>^oTZ}ked3c^Zq_{ zb{>5AF4NMl zb*n{nLD%NykQ%9}ve%N50SU)AHWbx|+@WOhLies!k2VqC28?FlvuMOFS_9ji*tF*9Hl4cIGyb@)$Jbro@(D{$K%2n%bG> zjhQM_268eswcoGAh{Q3QH$BfLNGuHlcw)xMsW1*= zE|jeMXGz;C5Y&%{O+#ko)|5k47^#4O4wW2r^rr^E^~$}lPku!4D}l!T=*D7Je$g`KCHR6;-wD@H&5_?WYX21u{ zjVvjP(8{sct7e=MES^m9wq2LAK{7eUkfkeS4U);KUd^lmcPdRfP$f$*uQ^@>F1OG= zfg1Z~W*9*1SS7M0FGv>PYspflX(o0Y($E>RvmA>=&;tM94Akw%9( z2{<&l=A@L#xoryx6(xvrh^kxQWXlL`6GK4>W-uyx+qHKFw%3^RL#)v?s^MD4!KH;^ zNtOIq-KRQ>4Nxbr0OY;KnjN}nQ6V)K^Q_exEQk(_M=aDVfU00AL|cR+Wlk%v9_*Rg z*Z*E<(chemGYibGM3NQSbaNl4wq+@#kOQF20tO(esbW27Zo7&R&8R-b-@Pk*(5AJ* zff9Qva@Xmt{VCeATNQwlvW2tE9L%23IEm|MDDg?wV-D~doL@^J#_URxgQ=!b7O>E& zic>W}d8;){2T~M8<|qayFfn6f&)#TLq%tlysEb#w;dXf*Od*&Lie)>~9|MAcA@Bf`I@ppgg*ZJyq-{H^y;17B25B>}M2VbU6k0mIbGnRX6 ze&NM5KO)W|(K;G1uC`K`(C8xQOwG0Kgqp=jzPl@F` zOE<}AYCXb3uOtPa+`-Kp?Q$sq)+nx~q!wmo3Tc}&q&_EOt@AiX^G5ZIyKDb(g6S^i zpT!1K=>a&S->7mfAF zdwlr4x48HCnBTi}m0$anUm-6B_V+GucyN(v);_SAqAi*zB82#A4g-cRjnVZSJ?7(j zp16GZ0ta`O+`s>j%a^X;R@rQhxp(`0E*xGYmyzR>W5#hw+pElV&Aq$#xpD0V&)$57 zI#=%8xySVzH`rSaJU)IbKq6~zYqR#xsa8^LYIB@t(x7v@c<~aKAD=L7HY}Hiyz+Qxw!i5YR8%kDtb0 z>-AFsFtygHx5IQ4TYyATG8q3SBwZW67U#I09Ux*?%{T>W-CyScF}ttx*e_2Eynb=+ zHJryvc^Z@Di5rh?Sta(g_nf);XRrC(31|JLp1#x*eaQ7^uJirxf1ekhe}Rjaudv>% zp;fLP41DaRoBZ&HZ*cwcBhq4w;7!^bmPDOafO49OoAuVOIJfP2Xz|y^*#VKA)1yz; zNN%HY?dkO1Zh=Er%Yw(4ZKe(5u-Iz8lrlszAO--)5n#C(nbs$~`_60Jx_ghe-+YZX zzx|iI``){pl#ASbwBpg@6P6Djb734A_72#r*4%mb7LVS06Xs2v#{w>2C0nLGK7wfU zaLd~Yp%AJ7DqQzQROGg$UAJge=!00RK&2s)_S&L#M5(5<3v$L{d<@$HP#SktHEQd@ zP!rw}0f_s&1*NcF<%_@d%5e;JHG?tO3N4aZS1^)57MAlYa3=shmAs+?XNE;`oo$Zl zUB#*d^|oU)-h|Kp2A}-3zr&Y)>$mvC%Qv}je4BUw@?Y@h-~CJ8ee*pY-oHmKiRHyB zO0+?mVu_-(%h(FTS;}G>MABld>K2S6UNgB> zl1qTllK>)7<(-9PHci(xBjChbHqJQP{m49HRlung@NBvt9iW=kDpt}cpcPq`;$f2< zRQX{@YTYy^c~WAYjMk>8Rtl&XEJe$cK^j!K_atCtb_*6k&I>Gun@%n?5)C;) zSpaCsd@FotDJ!vb2~?*9HfugNhg1f#SE|XGXk zvh04nMbOr1?=9B(_#RJ6$kb~u5G2JO)k2q~I%*$CDGC=!dJ(m0|LqMAS=Du=bOLX* zwu-sgw5_l(!x#m8Q?Z(uFz}|9r6R^z2QLW5i92}MUY9&*|3@ujZ4|4*yirPHLK;=2 zlwiNj*f5A)lM)#fE*eD^&LPCz_sVgxpw`|JE7n)eaujOT+0Vgr%+Nf=Rwq_sDT(TG zC`?Vv1B(_>?_h|d=PBZ$)Ph-(q=lh%FOfTA*QM#w0R$!Z+7C|SgxH~bH8q`bUR7-9 zv8lAH#4;gtHPlk*M3PF{y0lnSU6fGws5|0i7ntfe+3F5x5&&1$y)%n#D(RjId+ESX zfc5V9EXJu}!RB&m>_4gvDXI4ATfkg#g)Duunf9C}wnZwP^{6%$g84c3akLU_UXS-^ z7t#Dw+@xosKmI*P3f6S&u{8kZY-0F!^8m55lNcdp5?z*jD(-Sq1AG8q~HHTu3x*si?6)GCqD6ce(it!k9h6h{G0sQzx;Fl{P%y4`i+0d z=J*y&m7KH|B&yiH(?o3(sc5SpxFhZXjHd9w$VKg40qP|SVQ!aRNpcih`YeDa<}1;1 z>^M{BrA#f_kHFg|iu-DrZwM%{C~yf#lqfk>V4~Wjw5LjncGj*NA*I$NXC?(h3rIv^ zDFB*ng0Ed6?XYl|CcHKtzy5W~gV$mIDo3job#=sgb;R*q4g95|Mq{*zxco1;F%X*=B3Ykk&l1sbKJObQw~Z82W-}B=^0tt z?oFr63}+bRD7V?jnW&2wmy~$*<(K)3Z+}NFiU&(lb4nh`YCRt{6e5^gX42t9C6+;?iK(bnc50qROOXg=@eVGq$ z-{!%i6P~?tkqe7VPR_j1ocX~IzR$tIB`#gPNZYJ<^S!t6ES<8jp?%_Y9%ynF7}>t8 zv#!tn`usflEI9Kt;>C7R*?I5TW1ROok7@H!?>+D5v!IX)t2^M3sk1>Kq~gvv;fVJ> z`g?!U>!;L!*-h993_K8UHE9Ul_m1qlDCnhZ(ev}DME%%5X;9A_G~eznJHO+v29kW# z?|ys|ZvV3V;k@%ak4kmkzfV8VwqTKmYl}C>>%_IIm-*&6Zi8a*8!x zx_*Us-@C&*AKu{;ufB}WmDMKv=A*o1n0g3Jm9u^!eG%SXP3+E{&ty;Bm#Dn9{@DnC z9NHWpk9CAlpjkrVS(Y=|G}Tg40OOHlW6-GVUpU}oz2<9w`2)W4m9O!GAAXy+U;9gL zz4a!y-}w>0@|%B~U;o>Ghvmha99+D_>gbV#3D-w#rita{n_PPCQygBsN!dRj@9lAN z|2FUb@SCg;FOn+MlZVvfBYdh@04LJ1(d<=#*@C9ot6(uYR-vgE(@a$r>lKHdL`J{0 zs?~m~0TT4tHus-9Tfk_u`oOi3xf0VXcL-+D%vHI{VO<^bEhln-DZL*smmC!8aKLE| z8%(k;HFRkDChdJPH|lDI&y89WOyDF)SK!9S+55RK@oT^JxA^=o{~XWkCmz1_J-+>) z{h#^K_kYO4$H(MhV6nf?-i_z*H0XTHw?Ii3f3w_zO=8s0P_bg_YpqIx3n?@*T&+9J zLOVrO0*PU1uxbDoCBQ$An_vkESGoeN<};Y6=t?dU!>}ZqF|QSzS8t@I;H(A6QbLrj z2ZzlBytOvTw$%z|(mGM_bh?o=ObJ$?k;w&~CCky?)t9OkZEYFLy<^;HZ4>7p2PR!h zp)tiW(btZsf~BVPYZmjWS;AtD2OK3a*^DU(ILwiLRPzYH5gR3ftgMNoz`O*=lfLQRc|TFgpIOfGk|)A#29 z@q@YGdKR|~#a{hRYJqcivgd73mknAqN*YQ1`9@BCV;Q&lSh8*R0IpiFG=|V~3hdvy z4vt*cbYG#RhUY|g-_R&(J|@f3%D|KiRu2#mX;isdxT=Xw;;uF<2dYmokJLikwI4FU z6w(C7T8Zknp1(GoYD2WFUq#Wz9s`A_M$pOl^V>X9Ql*PE7K7T#- z(D4Pxc?cp~6k%|=ra9q|=ZYz{PAP_)TO_L9Tc$b?44*H}h{-_p|W6$g@0j6i)rGez$@DfY_ z*BB?V08tl+D{Y>b%TeHQ9CFNoRvAp}#7c?$KXqq(r;SPLd@o9=m?HLa zfKR5_wslm;Qm~M;Zq@*M6-%2Kc^VBmgS~5c)hHHf=g=C^EoXBQjNuH7!^m*us)|N+ z;?}*#y#K*h$Y1>$H=n!CCqMl;{_)@a8~ny^{|?{y=6Ct_SO0`}zWgWD_g;ge_nD^& zEC6~-3^|8vK$^z}d1^AeQuy@LbFrON^xj*hwi-~sK?n&Z~^rN8qJ zxcA|^y!J2tKfHIVGQ8(}hUZxwzQnr^)?9k`13vfZPjLP671rw`L_Y07gi(t209nR2 z#&L{zNI8v{!sSbsc=hF%c=NpvIXOC}3?rAWT<7p$j}PB}i-UthGR|f*v0lqT@6pi_ zYJD>(5=y7RrOQ{LRgR7xlTza9wd-QcwZ_IL1{*O;Y^F*nGY1P5Y#u#6=H}I_T)j9_ zinCcylySlP@4m;K+jse$-}!qS?C#l%YKZ_;oCo)K4|ebqir=1o(8OJZLiKl$aemHE}GT9-aGhmI#6)_4ca~Tv%t?& znJRh%+j#mZuXB;Lr~Iw|&ik?B#yhZDuefsIkQ>iD!}ni*jgP(h2`*i}!g?JnnEioI zef%Z9@|WM`-krNVbL|@Qpw+Z_JG}?-zu7$k0+cvdqC?gm3%O~ZI|XHoZPMIPVr}k1 z?djSl?DPsoz>|UoE;oSFF=(6y63e}PZr!=ZUwq@6ymR{@N8@D{FZ}{{-+9EH!Du%w z@fSb5$NlyHi_6!paPh`-l#5qcojm68g9n_rQw|TgeDgW3T)D&;o9&yoH>_G_9uMSv z^k9P3n%*wrC~#6kg8~h0=3Q_q+LX)`w6vgOMg>_>l%Y&j6*|1hsmKFyP6{>^m|HNM zXT7&ZVBW{h1clnPT@0O^)r2cL(1ONta2L?Eb7afj%`Y zsLLy`e+@2OWAXB~V}+dk)~mFGEJ(LAWsQf$v=N^ZPcdiO{`Nn8r5t{5Nc!Ix@(k+re6Ehy2v zsoy!i(*Ua)yPUIZVOvzTsdRS>i@uhvhCvLQx_(*+(4HPs6~kbedFpT_y1JmfM`YBk z?pjjfpZ)ZGIjU58PV<(QOClEj7(=$4LjC^+U7$CI2yAuP$P&+0!f5lPRtHPcVYaBY z6093nQjpR}^@)U$%OKqVS8GTL7M9m4{eVG2a3Ua8hNPA`N>ZCuGHE$@Xs>v;!xW&C zk|>_liYV|E3cbZFDd|fo1QfLpwJuH3E-)*&L-62X3eOE zD%Qw$-UAS6^GsR{RF|6x!I(0nGZL|-k}Azgwbj!1s|6|Ur$@!MIg!wNuo5$KRlj`7 z8hcgaCH9#KrB) zyiH0nd&QUpA=+ELVX1`Ou0!Z4m<_mBS*v6CTq(2=ARn}*&5e?a#FZ0d2DNb{$7W{; zZLc~D;LAIpR0>eF)&?mOT65-k!hCP5B{8IeTwh&Q&(p;5!^cd~BCGiuaY(CLf5u3_ zJwz)bk+nW>S` z_N1`Qv#f(l-!o8~Ys*;4w6V(7aMy5z`!P2MJr&O-hP$6sV#|rE&3X%qeXX3*DExWn&0K zh&`YF9L8Fod<)*{u!fpZV!hiYkZS2o&4IGj7->-$kfu?@ZZSuz3#b?9RHAKMj1{*b zRycwDibs>~lTevx1#)`v3J;c{!R*G2kyC7>9zc}aR$r;NgGUpSz>ie0C@et-LA2kr zCf1(C1J*n-o3z;Sw(z9CDJBpI>TTY(etD0PyoW~tyhO~`ZD+;A$pHj~{-4QmA(@lL zkzp7RX79ZF-aT%;`@4MW&;E#4Kl5|^`p^C%fAdTK9uNM*e}`{=?>%1o#$R&(!2=%L zzsKhO9oVb{U^HM@LK$E_fxLvpsG>y6z#0|{>gojQBxY8fsWw1eLryeC*c`)r#CYum z*uTi;_M6z^5~+;Thqv*A3$WOOyB|PW!g7gEM;eDJ!FU$?@=F7GvFAZDZbGnqhC`(Q0OOT&Wj68S%A( zvqvWU{zW7<{)sHb=xWWrByv)a+xy+RV7_)Ko;67jf z_ILQ@FMNTE2ZwB?lfc7mo6|KrofF9{q!^R~P4fm$oR^-v$??gGH{N}p7oLBXTq;M$ z#~iJli?)>w``drX;og$xZ>Zhq z@$oTcBZT%@2b<2zMR$Kb+lqAF@1F*2?1CP<@6)q(J`mDkP3mIsPtA|d!43YZzx%8F zUEjL{#DMm}C}3zk=9HM=Ie_w?{`FIe@OB(V38Qt2PEd65E-~tK*9Wg{>!ErUdF!vU zH}$-m@lm3?yEJ`H=i5^Oo!y_$yV*}e;m-E9oo9u$9jP-D@jL)1-mpd0EGUPdN`kxd z(hD!~^>2Nh?|$!l{K~KVsw%#%k$vXbtB2gUdX@J+xXY!B7Z}UHG*3_Tr|I8KRhTWh z;@hCtA}Q40ilWEdYZEX>@UvA6kI$zZ)&I~6$y>kzC1lu^5!3IB?v)akVH|mMwB`@~ z^vm3Re8S~x*SWU0&&zqAS3dO_9m8 zeI7o#%l!|ISg&WiHXh9*%U6GugTsqVCr2z6`;5arsSKQ~PvSZ<(>&qHlnl)?HESCm zandj{L#X!~V^;DpyR-I*0VkIQ8#Q1vEcQqi&Ij|Xpf0s=s?oLY4FmH$ON`O0Xu>9R zyUmkms?{MX@yTng9`EJG{WvvGTziU+1-d_AmI}x4*^x+xHn4OO{uzFkHAscUDSf z5tNCo(oAhA?n;1r3yme&HV2*;phE$gXxJpyO~6VCP$Lz!Ax*BJxKqv(sw9i1T_MXA zMhU!jg*+t2 zAyd~;l_ik|iAN_ZjJYt?HL1;0BYChz#vHl_ zA#~VghC%Fxsm@Aa%;MTx1X?auQbJBiLy2f+iVi@O(k@7WNn?Z1X(=|>@5eT?u-?4NWzvD;@-xTbfrUfTInub zMG0Dnn3^|+JTjYrlI#_#;e)pRVhcUH!K!I3;j`nY_Z+mRFUx=xIW~-0%pyw$b7fAM zJdRYaq)8%!c`*Ws$)%q~Z4w>MnW-pIF6Oi$N$)qA))eQojbT*dMoPwNvthZ|=hEdX zNEfNb8rTHrm~6!NuU}_AddT|dh>MplYki_yudB$Rc_nbh(nv!saV3Z7bhFs!D&sIf zE=<$JB9C}dD~eer`z$~(8_g5BWQL(or!`hXpgGAUXGj(Ra+Q#1>tf4>e9@hhf)Uyj zse^6GLD7XOlx?Zs8w!LB@4`GDN#a0WS)t{(#d%NxXcNXT9kAS zG6QZ+K!1wy?Xg&k7BuoGc9=^58jF@=v#oxXMEBW(ub4PSDO-eHodqS=xfVCyB92Yl z-G3xW~E zGNJ%+TB)`G*`$3X1g??8K@iX3G}ZGI-KHTx#chHadneJd=3b&8pmQ8bFu0JWO2#<6 zd=0#@S|9NjU;T5w`jtQ9`4?{T%fI?-{9C{D^Q7PY74{DotQs7jtnr$dys)m7d7fA- z_VDVgRwo=DEEtB&@o}T14c9I&xO@MI`$ucWy?sh@rrFr6R%G8W<^^?jj@QRrzjld| zjYr38rgi0Txxj5=N{L~yM?Kk4%93V{qvMC{?O&j6)*P=-D2qKY*6AdNqFbM16H-+bK~Ur2&1r`XExJ@;xlcAlhrX^C&pZ8WY%>Z_Vi$z z6;pL41`aP?=5YUjhj(vrxPO5wSFiK%=tyZpF0Ag~W_7aW;L6Kbb`CEVZeD*zx=bez zxOlkF-~aFYZB9-eQ<&I1yvKTV3}nXT!2LT9c;|!nxc)Og%Un07`C2d7xd&it^rGfA zcXY+{b)I?g`DeLx_a5ujihOWDtBvbduad`+AHDX7aqj{bN)p3;I$^Qcr?r{35hy;6 z3s$QW%qz8Jnmc#z-{D~I0(;9nF^D!B6~2<(z3$w<%bf=g`P?Ty$&E`ntl8n>#epAu z?=>Dge8eYSdXDdW`x_iCWL;mvETYGG+oIkXV5DE?S!T?xBuIaqWzFpVe)kw>O>54} z>#gq}XO90-Aj(sT5obR8wEVpnMV>qllvBi7U84%?IGx@n(n97V(6CLtcF0WnO#jb)J9z1zvpdMK6RLrn#}VH*)Rj zHBwkto}4^pnj6ntf1ZuQJ0Ct~VUM`Hza*!D3kL@@jN`enKdQ2Gyq?H;V2om2c95uc>blTH_Sv(SKX6>=)4{rE;m16d+B1^hLHB zq67`PwT7ocHfguG+19o_F>Zt2W{Ghz$jG(uXIh@*VN zaT6iDl!4^47#7H8HY1SEiZeU-JTYz>vrkMeCQAZ_QWy$MwXvC1(Hu%vvEXDTWwon- z1e9^u%oDZ|uq!tx@K)7!<-ynT4VI)0CFaH~T6_gk#eBn<1da&4RM6@;IakbQtVkoH zu1?stOvHNX!8)J`NF`^cd1lo*Q?CH)pqIz}qe)As3}A&iuP_GMW-Y;3Qi!QLx#)@Y zxiNV#YKRJao=L$VkX%XuYfRJ3>`fpxP!eovqZI2?IQF#4Fcg7t(=2=9VbJsi5oUYc}(Tr)9 zmPKoF!pi~fx+RTAa~35rW@j@`Vp-M3uowhLuh!&TXxZ>IGfgvzLT+MmwH)n(-itQj ze3JyKNXsZQ6gfq;CiX`=UdK}sMXk+5^|hEgsj^%Q?3F=#NHT$^&7-wbZBdrfRzpOr zK*cH9WAn7WKlHdxmdDLBhRHi97a)Bv%2^1+2B>x#7mFq%wqx1q$e2Mhttr#%|SEX3}kV9+|Y5;=xN3o5mW z6_qmGnMxRz{#(#9ChZ1`zj@dvt1Xi_uE+0MbLpv+EXEk=)F~^3Xgj0Qx0KnuZLdj- zea3O0I?ufO!9)J@|NDQzh5diV)vK3z>G_*nxqgZL!$U4!xXR@V*Ezg+$Z9_3=>9SL z2bV}Rjz4(7No{=k`m?m7`#idJ%*zLtxjCP3|Gi_D3khGZHwHd&>0n7-T;%{I&L@h!UHd#ok4T$|oD1vY$1ur93CFBe{g~O_dewErE47QUEtw^hm7NbwGSNT1@~@!$l-+x z49g{r%!lv4&!wwZxqkgB>-CBUkB-^jQ!KJst>m6L_2O;iz4zbe#pj;o6E9qWT3M|p z_V<=-RufWk=uZz1aRZyz;TeiAKe`@8=6 zEhzQl$2m{%Xcu|x$!u$tX>-D}*DrJD*86<(8{go@wd)Lvk!hOAb>{kofseoREZ_gp z+uVI{pUW37Q>Pj5)7GxoH1rh_Aa5sj?DrA481b_!xijN*!|4E1@B4JMk9b0xX#CDv z4@!1`n?BFuP}Skl@tV0AtK%bT%1q;u`S=c=6Z_*H%l&<-I|pNCxxZjFZFsysVKENu zjU)S)E^_JeCF)>|2Lon_ciw-G&3et7Kl~AIeEn;z*T=j*t?)Lp7zQOCz+4-L`wM|8 z&9n&DiS=g1OP~HMuYB%{eDKY0^4530!NGE57)CB$x}x!{mF3|9$rAIl1{bK)!m(x? z1|>f^vss_erisP4V49i)?JUVw+#7k^kD&Xps@%hX<;?MFVl!=I!&{wA3%WYSVlk4b z%ymNz;BHYe7xot9l$hp8AV?tk*}zg_Js zQ5vdjG9}ruJK@rkQZQTO_|t`f+gw#Ki)X*8pFwPx_YL@RI2$w?NkHTTxU zMyf_L1^Z%>Bqa@P78}Eos^_7>lTv1$1qvBXi-^~f>`d1L69H)*2q}oR0xb@tlo%X2 z#F&yrZ;d)dt5wM%HmiGHJ6P7@UK`u>W=2d8Gh?$lQR_t>D6>PWOs%n5tyxbKK3-t& z@Cq09FR*{GWU*Yb7-YTKk|}|&m7x^1ptSfdvh*9WN#~=fr#dIu;nKRZk_5GL9@$JA z%$#h6d79`gleIPK>>)QEno`X{ts}z;m8kBKso%tXzF~@ytO+wx|B{uUERrW-kTd)`-qQqqH zZgRayG_VFa(>AmA@vSXm*lednsugdIp(OUQ0M(PQIv&bEit}-Eyh=Pchrn%{E6cq@ z>D)+Ugw`5E2TM(Z*xVV*C0_Z|3w-KhALG}5{!`q3`*pr|?;d3sAv^9HhC!hGW}2B! zj%at^=fD5kzsVOq`Dy<0JFoH1dml(I#d{*D`?Ccm9f`#Piz2&d2Q0%Fw)D-!7_`1W zTeMLm+wPscbtIX_#ywnRI>?Lw>70gOY3NvU#7biVCmb-M&9_#qkC@sOlVu_37`3iR z78*;Yc8)n*e#FpHYt|6f@U2$TkhLGBeA*V}zEgzJoH3;FK?s+cw_pgswB9flxmnGX zZA)tn@Ftt7YHLQ$qg;!;F=jodA#OvRW)ji>^CT9On4?|LoM2Q&lGo>-L#wd{M#8RG zBFQ4VR@R$|hes>!J-Ww(`ycT5=pl7lk*%@J#@=FpaF$C9^zPtJf&|nKOV><`I5XOH za3|H7W|{7cg(GH6)wAbBD{|MK2~an=$1^KU2JIgcwK+aeyDy|-GaMb}&`q-`7;MICkGdH}q#d#+Q(dTX4et;1%;qeV|+ zuVpL6eCe@bi|0)PJXfP+xp>xSiL$PpgvQDp*|KjFodhW}lLG+r({*J{KXDP>4vLPg)IfTGx2yGx@X*5%^j zNaG@_)rQyJ`HM#{!KZ8WZf_H4CO3iNJ`?X%Yi=Bz{W0#r(CTDP)cvtmwcAbBN?58){w4b!yyWIqp%$gbhKtY&+HcgFuk~CvF@a85iuMYlR)(@YmsoK&6;PfUFE|MKH%N=-{;`M zKAX*&_3Z3l*yq9B+kANYE}#4S=XvSn3p}`gj|UGQv47zb7cXDp=;3`<>til+nN};S z&5X~9lhp~+yk;y*(jfNs?3KH>9&q8{5+8f+S?cP9X-F&=3zh@?;h+Bn3D_Sq@4fdf z*RI{*_~d9O4(~DKlR4(@zdwpCvitfdz~;Qqo_E~cym$A#AJwXK9OK zm+8}b?6^BFdNag5?_l`$J+AoXU-k9V05GPL=xm7{M2aU*sQ<0SCZ8?Kg7VDY&kpYH ztAht0HEDJyz)u2E&VKKC?|m9#wgn!}pm9(8cMM9$Ud}}ZcR%wqIu}XIfVoXvx^k6w z-+7PkeDB-*;xGIHwQ6I})_CRS6&@TP^Y#aK8HYWV%S9v?JLsM}3Kk>T-A>?MVe!~Z zyInO(KurDkI!tVqv6)LpM(4kEMDf%1C5)(gq*GwY!&}{rlx4T($H%0cm>)gD=85IF zU{j9BMBq+>96h{?&oiBgv%eh3Lt+>gY}S=K_wF#yjd@1jn=+Q=-0*m< z;3oJBJUBkVKR9Bp+MLR;ggWtqH~$+B{_syQPaLf`qH?#yYW*6uo-h=#0<6w>1c4F` zMVr4>RqUH6twRE7bTt^wxtABTNcHY^`=FV z;P|{2*cpMbs$r3+23yGtfkh|?EQ>wz@*?{euR~eteI6o+b;fLv&I1x+GY<>hs2cCG zs|*G5H7XokN5>_Iq1`U}`{fD}463a2Qj1TPi;>TarI2ihF$yQBNHDObKJ%DsK5fvl zR>(Q#b?9fvsX`UNh5#w~Ml@)Nnlsg50F8AD(d9O4tdoUgPZxPqVB64nG_NX)nyIRa z5_Yvg5CXqDE*uV(8kPr%sjev|hS%hcoQ6nfLO;iPb+s^ueYxd=Pdaw<6k0zDPR)sg zP5dJ_mkqBuo+2Tl3v`C~vC<;wpljc#QZx~mSWP*LCO!u%BAWvFiju$7+pj3h%&O#@ z^kU{VG1p2Smt=?a@na^-49iO#UOM1o&s^c9S3bc@&wqkvuU_Wje!;xbXkmF9Np4?X zB0(YxoNKd~1cs=+0!CW2UuZM+&;lwc<4LVy(z@yMN^^0pVM!nJG$EHM1!!U0Z8DEd z$(RH5?t#2nlF)Am_MWmp^<2TJm0@QgP{lG!Ic!xWxIuLRc?rY3GS^dTad)S(7UJ6` z=Ga_M-7ko>SFRdud(P&r>UYeU-p-=DQRk$!Ugz3c6*%QiNrqKtHEklHhn#Z&Fk+4< zS95CkIU62{YYJ*WfDuuZ&q*yv&p-P-AAjj4xt{QiKmSwy8>eIW0lEN3;Tv1gE`w)yS5W+B_l*|TRdAz-ct$?1BErYWJ)gN zRG17ty#J6Nz4110y!AHU|G^J=|NVDZuQx0bSSjRjAt$d^v7)t?R>OHGz(B-4qqX>6 zXJE#%#hxOU69+sBU2~jwvE8^>S0!uZF*0O1&80=5nv{}gb?YvKITnsZvy0Us@GgoQ z!;&`Zc^CYVXwkwaEY&FCM1-NWK$BDJdH{z6JFqnvMisG|%>u}_?C5xQQYJO0rM3nC znj-)tXCUMTFfj`QWb`a2Bmoo}hZe%1rC6*zzSUh(?5r4}^zk#pLu5In)BO!1R%woh zc(=r@1Gq{O)5mj&mRpw<(OQ|bmurs~OD3goW{TLqQAutLS^jYXWlr1Sa^6-MrrFHPB(x!=ZHMzMlbJCo7++!FLV=|^`!iq7ZX!kNBFCzwNg0-^A8K~6_ z%L4%=^SmLKAwbVrZN*W9XtmNz`(haejDcELSgTMLVr(8iW~`2l3w&BLZzh=t#9CQ$ zQG0PP$x0cB2SjT@8knawxdoUXY^j`_gJ`?MO}cN%NO{1@z#?5{S{-xm{v#gU|2EU+ zgpx9QhX<@yC#+VJwC?i2I3!ZOM;aF-GfG)d=BmQ%UQ)3pRTely9`NSTRuUSQroy(& zLgKmX@qu-4t?0Q)Gww6ZjZ6`6RcG?N;nk0SjHC66yZ7&L`NCzcT)BoN#9#fn1!t)md5;o`F z-Mf^s$ERO?h5hWTr-|l?3x^Bdc9FV@mF~LR16p`NcvxMi{w+`aD^vO#1@vT0hm*3Wf)G+xntV9$Ns1r2B)CR z+4n!~#+?Uv?EY=n{`%~DpFrIrL{Ur{VZKQN}^ckP5jZRd3>%>n>OxAVb&p@LBF5!zin} z&Aeu?M2Xd)wh(X*Mn-QD5xYs+0O3F$zXz!lxte)n=#wZ!CX-q8^{Z->Lor`Wu64}t@bG$sBi?n%L;QmwgCQ-ZYE zCM^bPE3!mMF`tmVI8ALZmXw6llptKn%*AqW*d`8g#&QPJv0FUHDSpaP-PX9pIJaoI zN(J{B23b$1s>)K!noGs?9O9a%-TB2;e#3H!JWtv+K5Zm?n=`A`nyDr(Tz;PCpS#J6 z&%emeyz()gzp{t%n9a#;IJw7E_em%Lu5OGuF)T_rVCcMC)!622JR%HF^3@3?Sg6RkbOV((QURcUlM%Zn}A2io{-Es3o8_<~phB=pJLM>ze18xlX|{(7g7!G0!U;*{8ZYtx8X!hO>foXR^>g z(D_wT(^}D7?I3Q!;ECs<#j(aRkg_qgIRF&fCPKC-+r(}r<^%>qj=5lXTVjI(U$s&S zFwzdvU5gY;48zENh1=hIop;}Tj}LC&=VPDvg0zCFGaYYO&$Ad3%Km6WV^b!+|HeZe z9Q`quE?*>(w&JlVK;RmB6^%x%l@fO5DT~2T=f*V4{VJ!#9E_u(4DnsT-i@{&f;}M1 zbC0!0KyPhgdgwlN&5sn$K;Ac7h&Ier!c7Oq!?np$VjPEPql)*Ls_`i_PD+-j`8?0e zbKO4QB->tF3Nh>y+ANDj(YhK;lbjOcpaf&RUNKMMxDrgCl=60NEf)ieK^k5jpim2Y z>RM-7^SC#P_i`zW*I{ZDn!APc{tUDb$3j8H`7|#>Aof%v8~YLcI_Z^!is>j&{fMcvHUuK z75B0jY7uQfDv>8Dku9O0;-OuVtVN6DX{*n!FnQ{1sYpg~55{xo$#^)ubx=Mu*J`Yh zsU(t1TPcMRK(&c23p9ZxxfYtA>en<6O`-sSA?&%^>9a4OP#}Rh(q{6;w3!4xWHBK- zv!>MsIZ?9a3oWTF*?Ze_#8h1sLt zF={=mrL%eZ7kHJ6qBW;j0BA!detxBJ3PRK5Y4aJ z_j57EQW%y8Dg+IK_M}`ejP)+?_;w4^q=@qVAs=W914`~%%xO|;w zuI}-{J3nBxnW#-}pLJf7Qs&{K2kb4E)VZ;lW`5=qFR-7h#Nkt792eZVf5cb*;wwCR z^M-()#geO6F0)ymkh}XZ@PtGJPX||?N<298yWOA9f-;>fu?wV}mv^7-Tsiw4KSpFY zbFNQ&on^_K_dRFd)7^X_$R5PKZ>M`R$*{JA3+N=VPGkQmU;hdkFnyCur)?#4!N`8& z*#aD9l{!yba6$>b0#)@^`V*kTdFa{h>q+2c=gdB0l05CEo}D01`@6eum732xmyepP zXRqt*v47I>9N9tU(6Gn`-~7fm`1}_V*Ka(-RJF)oKV0xLAAga*{Nayy|HC`HaPu18 zl6*rtis!UXE-X{mjl4KyE)(Q|(iP!dvu?3i>b$%Q!D;h$OUzaM;$;})fr}u- zEZ89lh902m{8M&vT2ht=>RSdSak(&z5<5(7$uulj9~?4I6Z14v>%`nv6zl!w@wBvw z+VovswJ5slUY3-oE_$q4P;zIr@U%KhbTk>ch+^#O>yenFD~)wS2IonH?__5#G^S>3-*MELyQ@; zM&-29prnNmG@In~GKTxZ)G(WIZ*sNBqbi6&cb&JQrp|08Fe@r(0#0KlM}L2dz)H5E zCZM6JO~*|^Zb?~sNrnQP+T^TYMU^d|7;=$5jZ3>C)9o%(2=Pt=Z&D@=7S-xmJ?N=W zk#A2SEI*00(HrW|x3G9k%rQPGWd+balhY8vuR=rfhP8_0BpO3Xav+IEHL#>;O@b1^ zs)GNdO0oroGAYW)Q3a@xP-Qo{Aq^q?TU`||JH7YTptxxD6}f6;Z*+$!PmWt7H$yWn zQ88A>#~hth_OD#$=fCjteBra7=jy(5bpL&h?|h%vK7b1cmwDmFRm!4~%7(?@)b)|t zlQ8BJxj{$-SAdGl$BZ-#0WdV`l*JB;R+=f7*lVf;lXur@7n8!RyLh#&J`o-*Tcv|_ zk(8RVYNu`_Q>-s4KClIw9RXK{*fcYd$O9#q292;xsY=f z%dEgOYfZ^1s@j&wLy4+zBpu%OXSF{~bB*f|6KbB6JO_Rn)o88NdwWZUp|IXeVh@!Z ztVx(Bxy}s3K#N4c;~sdl8T1xK(At%wokQzL+X6}@sd2!qQR_ahyz&xPt{kwxmjxO+ ztR`5=a6@d2VE4@tTWmIKxqz9@LDu57jWq-q2zVg$Ml67m7CIW)lw34-wK3XGYqUPL zol6SvEv`x8po(?oh~Ml~|H)lr7nmGL(x}O-6X<9)j_YDE3`5zrYj&_O_jNCwu6?x$ zEUO;LrLnElV{Gq0NhvhoB1r9Ng^BqwO~y3OPXwwGw$*r+R1qgG*j#-aNLzqJ&ldEm zv-G!2Yo0qkw@Ao4P+93MKdl1_p_>D~1^6u54|4zp&9!eWhRou^B@PewSx=33-@DEC ze)uN8_j`XrY8yWJnUC`;pZ*MAeDybZ>%%YdcJbL;MHE*$K!w?Bxu;Yzrh zORyJQdN4=;T*XI_y|vD5t^(aLCD{TNI~&-TEi)Jx+8foXC=ff*@LB_Gl1p3jQxW^- zQKSF?3>eaRigstz#w0_IfHPBp08Lad70@Dv6~TV%@Q&7oYFfKg!0`acdP1Uy&0;@u z1Ug~TAo2hQBu1{dkeyi938DEM^D&Ah{b{PTG865VE@n{_IdmqWL#i_lAuyzenRR%@}B}y-M6@K@e&6I z7n!Dsd-oo&+S_0aKJ~FzxqflU;|KTHTa4^4mz0wD@}GVMuQPi?;qCX{fLCj-*S1m^)QPa`Ot_d1UqvwN;*&*$vl&-?do zXH5rGykiq4lusB&j<}(-m)XYfKc4yi^lUBva8>V{UU7w zwMJ-j4OBOPnzR2t_at_1o&c!R8}d=W$Jw7hif+~42W;6aJ^AZ@Yfs#)Cy#sHggARH zyYK1WaUMvd;1#?%8=rai)`v{%V@er$<414s?9JzJS7lGGNc$YEpX0SR-sOXGmz&qF z!90r=%W1-*cqVq0Xgd&?5{O`mz&x6DdS>U_@!okzH7R!f7Mp%tUx|gfUz%D0JbK4d zIKx=b{6!(oL#EC%b8U=!BWW?PT5qT;wJLRSRnKgb1cD0OC`!6B)un@A^itzYqt%&`Q)ox@Ah4abZXkyW^Qg`iRee(x*wURr zRB=gIhN{?5Ykca( zYkdFPU*#KL{~nhPF7nb#&vE(60gG{9%p-N4n2$EpK{`H*V0@ZED&v-=LIm4XXT8~M zlfqTqD5*p(E-m&at$*6idO~ifg-GpVu}zUS!+092kqME zr3W!%v4IoE_kR8<7$9zx6f7d5HN@gAX|olW+${#2YJkiErbK&`g%e1u$=xN;dy*Eq z5Mn8cZd;P!Nv>1Pa=_=2*t8X^69wf%hBi;pR;TwQQ@4q`+N4s-kpzXvA-)wu^#BUQ z3P5Z?OIQU9eU*{~4*TXYM|ba&5BAyHTVg43u_P`IT9b8ejLkgr=m%x`rhko6J>903qp1<3unu?iwIJ)J3vLvHMU^XSr^CFTVt|V zZFAW7d56zpA8Nrckh7xwSo~r;EzMouifCI(>GZy(u;We<_jzCIQ^Y5|$heLBr0q54 zzUHa5WvZ3U>Vyv--XoWp7oNMq$6op(o9Rn@{rhk5hkx|@{I|dNhy2W^KE~&N`Ahu! z|KPXz%`g2b|Ll+dly7|bkC`4Earxp!0S`V?TaPVb?2_h9vVvu4fVDP@k(i3Wa?4ty z)w`->s0GlN74P5z7d#neC3Kc7_O`gsR%I#eaV6q-SX1dRoc?ytC*IwO%*^(l* zi@Dapn9u_0{bSY>Wu(uP6L<{fo8P1%qp_ zwmSP$VTU7huS8o@Xud?jBqgFNPS93T^}BQNPbg+FcR15W0-Dp5DLpv|+v3P)ZT-7kgK4Ik9N3 zbyusDS_h%GL8el~d|j=%arq)Y^Qlkr7vKIa_wL-~>J^yU%;jgF<a`*0ge0b|a%HBT9{X<4i-1*=waw+WXA22NTXh+99essjoeBxDJdiFZ&M|a67 zv0AM;JlN;;*S^Pxx8CPdue{9LZ@$fqXKpeMus*3z1yg8& zUM_zYB-zcMf6{CBTF!zoyL;Jr*L2>sKbfb;T!JmEvIU{Ul1&6W7Ff_ca!RdE!%_`ZQnt>Q^~FddP>~_|Esf!>2#}8CCq7Gv>z2H!ic@ zOuX~{`;0^3^2N){lfVHbAO>4I)C+2?@lh=Q`ohA6)cY3~X6EDYK>NVqFweNn9#PZ46#ROe!M{6>a=9 z>7+DA4m#*sytZux+sv4{tBd7$PW8-M1Qa=v_~w-vwNaRhNnt6GYNK03H&?4dDXP+Y zjU+#24>n^(Lb%ib{&LcBQ4{u*-Wh(9paPyv79{=tZdYB5Om^JCrGk~PECMSvMfEH zR;4#%3J`o;uoZ0ds{Z=WEpqqh_wKUhr;IEmLjg90qJPRz${@h8%^0fC_cksiloAz9 zJ+UwWOU1-esV3c_?p)E#v6RV*h4nrQ<;+yJJ;!+~78%t%8fCGd)yjj%M?CY~C-_JI z_#g5&e(@9BdF?g+tN-l($hsO|{Dm)Z{mMR>8Ji)k*DMZ=%gu%3)wV_wGx}5 zvt22DXTfP(Ok)RVj&a*LJLQ7(z&t{7-+i|ZY5hAxZ)02aZ!I)t(i89Bo}R@s8iA^g zefoI?#Vvx|9RXUlFt*Mq&dmcHunvp(4lo}1c1-py0Nyu}J`Smm)i#J=3s8hGNi5A4 z!y7Eea7FQ;{hNDSQ=F&SR?h)W3l>kT|8d;y{W|lq*mv{_5ZR$?R2WKJTB~fPx($St zZ|HpX7fY7IGS25T*&^=ER%AB@=k$C|Z(g5DW;;40+dB-WtmD%$?1@ek(sjMIJ3n{d zuR??6XccTVz_HV!R_6GI*wzoUkDTBh|{I=H@=b0>R$4X0qT6Q*d zX1&_*=ylC(R;L1`Sizc@VU?YhkWHPzQw=zZ+@4*{Qhfv;q#y2*MIZ3`A`0% ze}g~x;urYO{-6I3-h1nN+`N2=yjSELU}d1S>2yxSI0+1LLO~W9m}i_S5mN8&OaiRjD=E1c z0jN+>K>N+iwp~p^%teeOn*+QkBxVWW#^PC(v5M8(#Dq~&F7fl6wWrqrC8Z%4b2X`` zHKUdsRihK$W*nD|b?71G03|wuOpHXeNu?wZs#f0~BWng(APPsD z+F}Wz^S;*lycNH$F3lP&3&22NVr!;W!Q9#6a@0y*B(`pqoU+)@mN8mfSGZ)EONf|L zvHY&z)xmk|%%q+57ZTo*+_L)fkqekfNZysF+^)A3vwN~Ttqwq6-2!$_Sslrjp?0@a~HFHqAp zVg*y%b`3V8_8TMRS&v;yJU+V5vp25ttIJ>DkN)HjdFP#Xc;@*RD8s<*_wVxDv(It+ z!?(F`VV`lam9q?#KB>;4Wgqp~kNS=+ z(q-qrf46$n0f^meIq&sR6R!1R1x0&zlFaK37cX4mlb`w&-~8HFd3mwm2S4~ehX)sU z>7^GqSxwZraxfHLeeOD|&CG|l?~rrh@Zf;WG{yII=FsUR+hSnBFv!FPfau_&=K3~3 zQ+yQ^=Iv$LwikN$*k_qRz3zus#>Eh{YQu_p?(tcoZjsDef-w&yJ7L&gQs&*u8O2;QOeZ(b}pd)HKS(Fs#P^mO#JX!~$q9et;pydN3=4q08y3f$UeWZ*E7V8w$ zeJ#l5m1>QaAiFV^gnOa-={!zJLa!vpOyaAWSiuG}*)hAzna43{5X=@aZGt5GVo_(+ zx0;JlS`J7@mYTV5aMY-p<*1UYN=J6gPQ~|(%2;x2##I%OY#0KJJbHzj=U@yec$&?q z9%6Bh0)-Yo2TN>-z;iYPd<1(#i8@qswFHqV$S~LuJE*x)%%DyR%o*q|COVB4G~oDt zw2{)PG+XlcqO8nRrBOzlo3Y<8=J26GSQb7n{ixN2{;i)2F1PKjX{ zSRXy&Xtn0EU-(7-hyULHfETVOe)qrnFZh!`_!B<=bHBph`qD3Q^5`C`hxd5)`Xyd^ z^#!h8x}di8sPZ>=EKNJWuAQ>hBE;4>c+87P4R_Y#CllwsIuQw6yr+v)c5r`p%{J6R zk)pzx^h}wF&b>|MaS;l8+PEV;e|kVwUM=aom1s7#ZS}2m`+@$U&cf23+t*u&J+Ab$ z)_0fo-BrHNAhv*0?AxvZ+9vpHlYSzBPZ7lD9pI;*Lhf1kc5ksHFzJbF4t8REna<@j zv)=w*dqoGW;>tY|O+P)H+v&M_0AVTgoUDT%cKZFD4dC8s`hDNpy~{l*Xs6?``+YlO z9u0*3&cuqDdN&-$9!c z+7hR8pvM`$bCIV2OPum{@@%i>$$8GsJc*IC*mwGTJDr24_j@Y}d^$=ybD(a=U07ko zJX-W(zxFn7pZ_k$oelO9?C%w>99-k&=dQDwDj(c_$Qy6I!@boz{QS?n!Y4oRKjNSM z^WWvq{^Fbb=#3xo3t#*azw>whK419k|Ahb5KlwlLoxk`aZd^VfFP0L5w+2-aQKQjp zjxiEL$dgAKYue7eWJ>fW2==4yeM-{JA;j_Zya)3M#abo79Bh>uzjLI0l;}kmrv$@o zss^=@P0vkj0)KJ>Y6tMEn0;-=vowInT1Q$77G0LZR@-dwq!vb66>Ap21sYcG>@~$8 zo2va;?L#p$v)pwIC9gIwVwq#NY7l!xNmjlT&`FtV{}u4{_v#$o`4$6wBEwr zJVY}k)Gv)7v4|~f=-oRjNUhpA8FjN_z1lD=iq>?`7=;#$#S}#+yr{LSiPhn*ShKp? zWlHW~Y`D!0BU;6^H%p5xLw5p7iYvW#!FoND9_yxc^NRrH1&*YhHcGW^rX;|jMZ8OI zA8!JUQdtP_#nl?uz*2YkiF@t>7HbXNyC_bX4Vr&VOf^S#^cH3jsm&xSxYy8{8r7!P zdU1RbNacnlv5~#@*d^A6-961DSP+QoxRn!%m2?)-y{`5i#p{bNavQ9mZkv3;%$ltqnn+q2%@@v2RD}3c! z-{QTu-{k7$YutS1Dx13DM{nF>@+%zdUE<*I5)W?OW-$!hx%DBJu3X{qG;?&kV%~hf z&;9Jr@H4Nz%!hA&pY`!0Hmem^uU=!hEd0e+zsB{;*VvpKbLYW*e)h9J%VvE7+hXaD z11V1f9iE6E{tEeW|NB|6<*Y7i|J!cvclH>)AmF{ga!ww<``Po@HM{xtdBDq0!VPx+ z*F){T8EgKs9iwfW;KW+rUEY5B*G~t)Y%j`oPEbLw7KadUiiOf0Xf;sCT?T|691VVt zMa~CA&ZAT1oSzu*vp43W5V^D8^HD4n1=JRc%F~a1_Po!ci@UGg-=DI8ViLz-*mjKi zXzS;bqer~>>@z%k@JT*+`*ohZd4q3!^Do%j-{b0)s}gmpGZzAQ!83Hn>0mg?SgRHxV1>Gwb+_i6(vir zpa;fVc0_Ho*`t>?>wMhR7L}tC;bM46&mkBFmYBR^WcBJg3?-4h;(qF+(?uEuLMaKa zv%<(+LcnvIJZU3T>qnmhdd3_j-@-jLXWTQ@l)WXOi_o+tn){Gck(6 zx(GFuErpn3G}I1buAr|g$IorXld4qGMQNnTaf6%-vpZ9r7zP0_)ES@U+F>m;Nrr;e zjR1+{%pqLa(dOO{j1RF`XL26E=V&`hcrt3$oVBdr)!Z0J0!A9!mNlYMMuTp%>#bs0 z_ofBtBAe!}UDMkbRK-b_w-kG^$}`!D+Vw(2*iyl*P^Zc`7Ap1B8oZQXn}rI06^Pv} zWPO?`mUTUmIH~2!H1E1rMGfIFH%MX|NeiS(1Tkl<1;~{eq+zQ;lyX!Ss|VAlF*VV} zO9x4FIu*`C>xE~eQ-TsZO)-}ak)I}fsbp3sCp>uYn7{Fxe~bUC3gT6k($U7ytMZ-YqP$u6 zr}m_LXZ<;?qDKX}udlo5WT(JK^4?kYea%x6Xms1{Fm)1Py9$W1K?MYoY^R%b_^!j(NJ-*R$d{ciP(T#T65=*R78l_%+ z?fY97Ptu1iG_+3ly3Ug6>p$D~?*bcJu)?2Yr)=*~ABWnt5MoTW;JFT7=lJZ-wG#8V zg9*}0aRGazQvfH?!VFv)(0U!E3?)UWc z*HLx%o`l;r9_1U(cc@i?|+w?9_@?@;Eb1rdBVsG_8bO-3%PGGsB z_4C)CqVTZ|+=KBL075_I&S~*Dc1n3Ww>xN9Q`$Litz(+^r=ds61J-6*bq04{x_OzG zp1a0d@7?12ul)dQwcS~_^cqF*3R!TT}S?R5IVzEgRNW;+u-pwUEy>YqFjX5*V6`{sr z9eA`pTSD4omS9?AR)LY(8aAW=FvLih(LAh8tRdMK%ay75hVEw7HL7yh5*syQbg?=t zE8+J!TGYCjW)su3v*wH(AV(t&8K2d9!>G2BHqWH2BEwuIJe^Rij%iO!mZ)u`LPh93 z;T}%cnIY$`EqR+8ZBVl!k_d5QEV0MAM{%XH7)QDowoC&!Ww31*KHKiQ zc*Hhn)5^K;n_3wAvb{Q~+`fH>8_(S2SAOw}eB)c+;oW!N=JM6c)Y^FFxtF+m=R+Pm zTye8ip1EKXZhc;UefGF#Su0O_?J`Je z=vbzB2G8d5XJ2Q5nB8lC8t8NWctl(sdOsGiMBBLzt$W+dcN@6$xZq#q_0zi`?Vicb z%Oa8OTdBJclV+x_yXVf##i~$ zFaIk02ZyXz8)}`ovX=$Iyji(@`#v|WU1o28$uv!hws%kI%*5~QA_@XegHr3sK@a5p zv?xuXfdJA}sL|Wn z=c`EjC7-%3xETdw-X>;*7_kBs%X!Q48HORKy)z|iOO~#j?sDU_{e;lRP_mNAqlMzU z*fwPtm@&Kwm@V09B}CRdQ7kj21+!N+HF}&=CN)_P>vD`C4`B-|&57C!a;6Lms@Kp) zR8`uNK%-uJSr{O!NTfAsJE+Z=uH2LG4;!~Y&n3;vyd?>}a=$K3tk4Sws_zr;&7 zFLAQoFrQ3eH9d2(I%2b#*=#mjp;O$qRl*b{i|x9-O$<|PqV0)w{*+13_m|Y=Z{l;Y z?soA#J7xE!UP-VBbhmg!Uo$mFJDAWR1lVbp@SWwhgY4UVBKESBx0!E$mbZ))>*Bzt zfqn}&ldhfNJCyJ(h|q@xw%Qvyq(uT7rxVwsaXk$&`{(A{i9Tq2j z&0q@#nDOyNqsL?@Vq^*xJmuq$A|~` z(~rA7_0v7ky9j>7GkqFp=rB74yEMM+yp+`7JxM!lz3ywACYzGCF7gY~k(lFNZ*NxL zgDvK3>vOEf3Xv=)%?thcG|=3xQ-JwdHcc-wv{;ucTHMeTZ^5VCcq(*{O!K0}A3U^7 zY^y!fw$qPCAx28Lv`x}nbS%FU}h zd*fI5)^}gy{U3dczxC_C$kmIN`GY_CL*9J<9{=Q@{HOez|K>mBKlzXUA^!_DeEAQ5 zkHxr@3t%#=D%nhyXj$zUUAIXYtf}?Yv_E<|)oF6KaD&lAX=d9sKDmc8q5%6wBSj1D zFfMVNd2W;}O*==fO*NA4G0WPxp#qN9^qHIrwMY=V&sWQtVipWV0m;&9Z1_AgKjst+C3G4cjfqcVYDbY$e=EXIXQm6() zG4cSOV^QmCWY-f#z@%J;3Z)EMKZ;~9ltZ58TK)QZS0;V^a|6qyOtem+?}`lCn{jY( z$jR!62M_MBzqij9KKp4dTsY*1KYX1{Z5$pPaOv&Qxq6*jAKYQw zTkBIFMN`F@4n9Wzx{2lT)9Rb2VQ*Tcbn&uc&U0k3@Q<6OOb z$jRf!iW}Gk4Nmv1-T$7=Q_m6!J_@{fT8?|(u|5g}Iqx$mWu|FEN^D({ch7zIyU(&; zo_6lL=W*WOTWHhmzIv?C)BUa)h<ODBzG%TXu&Qd-MfIR zZ3Cv>Z`YY0yF#{BPU&?$KsgJ#oJHWy13I2gMe+!W;wjttTfZT@$J`#rk!aRVFt&Rh zPeTKr_WP&3=Lxoqb*6-1kS8V_#xytB9P#tN@Oi%cXMf6ryZ5Nm%%6Yd%lz7}{|3WY z*lc8Rdi9|2*;k(DdvCnUoqPAVmaecIm&|k92|DB2cE&{rzBZg5p!Q&Riw?(U6H4&i zPXpQA2cX4<(1j=GrqDG{nZ4!6VjMW$9D{p^v_{P@8Fdyda&Nh0v)OSm)r#d5eg zh1NyVrZfzrlF=5|krU(*s|{L>-swbFp|7EYUyz_l<${8NW(p*SK|;8-IxQ#Mw4sO~G9yvqU`PZTBqb!uR-OLXrf8Xpj%2ky znPyE5JK3IO zrQrDFLF@`s)j3zSvlvbxL5A50IC}q82yHW`IjUnxqPkAoR$70l7^4z2EvR4)!yv;}eX=wApZc ze9UIEVcJYoSLJUQ3&Sv|Eh$!`6dEh;eN#Ra6U`~nTwq48UiFntjyaJ;hnVOV2ncWo zb6Tgzo2i)ZEo^jm+>v6l#!tmdQ-FGv5Tm8i!B$IhgD{g5jrLHFIUTJ6L~$WXYspXP zu`Lpo&L~h=pgnbV39!@L7OitM+) z4;`VcLtLztdVaM|cT5I5y^|f7paV($a<^@8x}Re21#>9p?LO8Yp>0Ewu}!#o(WJF1 zEtVMH4vh4PWT%;!o~vzQ*cn48dD^0XnkUkq8UjF{x95v{?AL7zq?yY~*(3IHn&KW{ z$ST_8aRE8UUN~*jw=J?|q#weD3F%r!{}} zl|Lhu%zycx|CjvMqsRQa|LEV~@%=lz_PuX$>GD+_U*;%!?#xwnj~IgnWf-O%T7*^$niX+017SE zh<@*5y$#16Lr#*-9h%nRYL)D#NiZ1R1@_UNSc7!DYx;Xc&4FS!Chv^tL0B9W~F@egkdmuB5i{u920}K(@*}tu@!n zfaN0gbz9R?!^w0jWKyOk-Pbx-<|<+I0mauCq)A$Rme~RTkhY9WwHD7&s8oDsy&WtB zd}kEHXIWbq5eP&2N8lm~tCDbrI4`^-9DTjT#)9Q@53XE1EEixr1dQ~6~DV+HN)NeC6 z3UOM#6u(-p$n4~x+a1t52p{d)39U~qK&`f!Tnh8lc<;Rr7={Hu_vugb%+;&B{?2>6 z^X_}pd18I?7;@(R!y}SO@?d#zkzf7gU*>bKKF1Hf@nznA;|=!q5BSkLZ}aNMK0|VN zaOZtK_UsMbcXuPFR%0&;R@v`O`o8 zBPoBU%vZnsWq#>PUlQ|R5>@fa!N@0Hd5#~x^**<6-{6XydOzjdb+d(r^B5es$QIDwU;VP!!w|S>=XN);cE7M&~%Fw+i(1x9j$yFiB z-YbcddS4?^&9PaL5?-|#4B3FpRAbYus@Rn>2Fptp)iqTK4~MnjT>t;P{aLJSS(cuM zePhnK);`VcB5pJp*=JT&R-08Sl2s%NYA73mU_TkKU>h(5Js9wlMFO@T4cHI1Y{S-r zVA+6QYzvlT(tu!5He?xsD1kIZvREXmimWaxE8FP%+ni>vHRl|D_{Us($H{XdQdx#J z5SjO$yU*Th%{7}b{_+3+Xf)516rLx^NH#^GU2ChEDl|lO)RHPHQ%W>Ci+6S@Gfzf! zV{M6)TwQBSV2^bn8zn)>lcXJk>Yfx-uC0>-d{BkVu%vzmJq+m*V09%YRR|sV-}I(L zum#Q4;yDZx-)3N4%%_wyX$xQ=dw{U!K^e^$k~Xwx&1hzn&Ce%8A8nw;vg!2AdkRYe zQqWmkqSMx#4e}1jDAZcfd#7ZzFlKZB=pCCh=1O3)zGE{)R%KHXGQbL{#C&WA8OLtg zG1_aF5+=-UVX)XSU+Fd~N|E+O$D6#Z`Nrr|>phyBri}MhRlzdhvG=LAvxK@zb8q8M5~gB<&4L7rsV9>gm+_grv~_A4wqL~y!6`V`161M z*Z9uw{{#NP|Mzc_%6 zZ-HNTq5~0_+6o0wd0^QOmW9sG^?Vei%n#l(HDu4)%LWrg3ZDVUuU*gc|d7zweU%kbk|d2cQSMD-U;{b zKj7t;zrf3%{}Qi!?kk+!dy!`so&A2#Znxv~%APAUnj8uv7wXUMO;$GMb>_JA?h9WhU%MmHJ_ScADtR1#h* z$!o})!W`9|(fQ1}1yfQjCy4puuxG8O2+e}>$X$)7pfyy{)eJl`E{u>SX)2Xr0Xl?DN6*7Rl`bKP! zrit7gAE3x!dwL2`4=-vgAP5nyW-6Wd4mwr}SgQKVr2wE-SGDkS;< zL`Pl{?cWFM%v~KUQwqrlooN~d$?{b3Fm^VFo_zCmYz~d2SbXGqLUF>MW_?*!$(F+fkf>R1aTW(#FlSPM-RT+gG;#6lBVK%P#)Ajn;L(GJJbChzkDff?{f|E6 z^7$pp)t(0rAMhK0;n(?8cj z|8ZF_pEk)p`!f`a?k_JnKYze4|LULNcYfy|aXO#y;Ro;Xn}6@`@oT^K>&$auT`TmJ z7tbgBi=X4WZ@j~YPoMID``kaJ76tyogK zUmUXOTCFEqGc0=qyhh10!;C8%Od)l0#GatbaJAP(2V+RVq+|_`1VeHm>PC5VH$0?0 zc(8li*}Jp$&ZOv!?o}0I%eX1frQ0@_TLkwxsWno|jno?13NWk(V3}zXG%Q-L=G%IHeMEv8ZZWyJMlU>z2Y%ziJQ3 zs!}!gZT_X+sYbNnMggC7%51n9wF!8V&@E(ZCZJJ3hZ3UJ#41TNJUgi;YK5Lv-HSq6 zFaaI!ivX(lN@b)*ZH;8|0}#6>%$u!mmASzV$Z3koP*-iw0dJzphQCt}7E5-6GJ^cU z2-HDhM>_VAv|HDny(QsYDtl6)o7%9dJ44xMu=GmG#!vmiFYx@`H~Ev_{YNbQgunRb z{~{;ll83YL8-MoCQs}JL*IZp(@a)+I%ep`*Jb3hw^PThd55L9V`K`alw|?&r_>=E^ zk2l_Whv&~PBm)fJA}fiK3saKjJDeU#sgqi#RcJ14x&__a97-xOct$Ok6FQKaZ~O!{ zxw}_+GNi=XD(WMaX}w3=QP30@+vXrE37{+zZ8yVNnJm*iv+oP@G?5%y9nT_IR2beM z50mKTtli0Z0C&bn$2O;Z+N)g|%mT|kf8Kx=N)!Qk@&U@91| zwNlJzIL*|dHIbNhCozXBtyO9ZR$4Z^$Gy03tehTZs%k$B#GDliz_bqxNo9aeN}T%P ze2RJ5O+bg(Vgg%oNIyC*|0?`YX|-S+wN(WmBjkGFoe?INk_zhI($GYBB+XkeCa^~Z zg%RM50!+vlkVK_fq!i&pCAOcE&XJrp_w5|fbMXNBED8|>=fLSLm^sZ)`MtV{Qm zIds%odnczv$;R5&4ICPVr|Jfy;N6nUPl53L=+tS! z%*~CE_(YuRwn-IOF-KkUigo5nClVWNr2+R>BssjF+O%9PJ-W43p$4E)F{Yiz24GZ|t^yRP5~C zHWo^O%)WPs5ZRAIs(v>JYI$eeKUc2#m5RX<|#!H(nF?Y zjOGkF1P8M>@*7XI+5|eL9CAF(6PYVlycc!6LR~XcnBn%W!eTDk|3_ZZt61ZflZpo= zGfgv|OB78rm5L!oc?vMIbpf`P3)2))T=(s)*$mW;O?EOoxy+FPc35L{?_%Gi6iqF( zPZxqwl`}Q==(Ts&t4ne+zVPxxe)g+h;(A&6==pP=JblLdAAHD{zwmi}_RF7xt0$aK znZNjJzsma`J*AVmcba(XhkwW`4`1NJ_ugaQ;OBqg8?^O0&Zt8Sza?cO_BhH*;ge*6 z;p;>?GvAM7kS|>#?H-ay^REM8_Q-esJD?k z`gpSg3nnmP{K!M;Cw={-3>e#PYOp?tt_4WO0Brsn43sHlN-oErqvY}^kQuMzE=G4D zc{f3fn~2ztOQ9OCn@p6uzUTP;BSF6T=bN8-^UoV{&5usno3EQ7)1k|f-oF5+Kv=)O ze8S5wKH?XC;hX%)w|<{jUw)al-hP{Z@DG2JU;DLRr{uy?SLiD*oloq(@_GK`&G-4> z!;h)UDGwi>g=|H@bh05fioln&fgR%>d+O9+bC?e;UW`c}Mtqu}t&1`^s;R_hjSe!? zlsTEh=VYLcy+e;GPDzkwE-$Y|Gt3h?7qLdXGN&T*+Xx6%AxJv(T$FgVuTv|+-baO1 zU3^3h7PYW%_bzmOGMU8IrP6CBmx*SgQs*T0NcE1PphjA86MG{i8P4JxL&O5;5Q1MA zZTsZUVWifn?m^QvFi|HhXKYGaGA3=Z*fS}-I}#~c;tr$W=(>}_fYc(HiwbBzzDI*z zS3Re^!^pG|G`kY3E~h?nndsNoP>ek7R4vYBlS=ida^@WExb@ZKtV7*8blZ`Nv91eF zv<^2%S+6MkwZ#~uU^)4L+FxD!Oz%37v#FwyvIik49p{E7m6P;YZepZK|BWT9FS zco!^7>gA!waEB%K{gNQND*OBWC13o~Px0vFlsCWkJ+3c1zxeY%$LC%-C+(l{Yk%f1 zvYTPKzUJApXIwtNVC|jx^o*BZyvJML`$PWvfA}BppZv%FDc}0mcX{%B&z=-gEP15*bWPv9+g>l4Cf{oS}Emh$Ym#Y95APgW(}Pka?g$#OPiX zd)t(QO`=RAp8t?rh@F@O5RI=s0|H?xtKhOhS|YK$w+kTTc?QK5kVF=iYExysyE9p|fwquq1h^`*)i6PIC!_PMqohT1zex@rSxgH5 zsE*hQ1+A0n9$m9W@o5BY@>(LssgCO-VcjDk*S!tjneJks)jEJEojWo#k8#`>PaW%y z*%VAer`aq(WmN7w&LkTK)oYA-2^=_*?&h(Vsq0S~#gLq-0gk1kC_!<9Bq+prtCiRx zIclC8;9S-iD^D7Q76eDd_&XlBmIbp_;F#PtoJ!{Ey76>ryz$|i{G&g5o&V@>{9XRs zul`5;H~*#o7Junq{BQ8|-Z>wA@DaXWlqlE6&O2ZK+84OIzT)+FKICV9<{SLu-~Y#) zKA1VNN4)v%Z}X+szQpUVyu#BDKHzNk0!wX5oWtM5J@!i%195HYyq7mAMzlz_@U;lw zTf1%e)KLT23QwGJc&{gQ=CR(xgRN_wB*Q53Hp9lTLr;aCBwI00EM5M1DUaQOLmXDb z(lVdG6UnMtM<)}|7tB+Qf0z)P3DmD13{G{u6ZAHK2jY239KH4qX>?BPSe`K^dVrnT z1Yq|R&%W!ky-f1P=~X@B$_!REpr&Mj8qHUoK_1P#ONJOPcjy7ySr)h}N}gj(jE*-s zE+Pj5u)BQTT3Z>ufcsVRb#k%IQc?F#OeVUm2ZNYI>M5EbM1JHR`>=0gngeX=K<(O# zQwqSw41C2sSd&@onw-QC?k?7Hj)FoTtc{Y8LB5AHImFO40KcHT<}ARsdB>=Djl3lp zCA^v{GK0=3K@`wi#F%5R8l4)A7KI#rflL0N{Au&O3dwl@LnX~I{?URvhZ-UoloKq@ zye7I9lY%4YdiE)=0V-Pf?1&wjlJ*|4DV&@%z0AA98kj%1aM+Oupv}FMXBAj~wVkMCl9+`NbJx(UAA zmVe$wa5x@|yT}B$<>0sG$OhU&aFSq>nHVq5pOvtgEq#Xa762n;Q`iEPu^>`v3kA;vri*asTJ%7UIUU>xm zvwZ6h|B#noe#~2MzQf=7&;N6N?a%!McDo(d`vq^6`*Y@Jzw|Ocm33=)Vsdd<*7NbIC`NTXc{)B>&!>GO5K}8?>-s zxRg>L+6LCPiXz(@!{l$b+fm9)-CqJQ&8H-w%!PWj;C01aT2Imx`e)GuiR5bForWq3 z1fstN#XqMEwW;lMPOJ#Fp2 zj66*gcb2uvysS*r^}v#7tGK3OTL`R~iczLn(jFi37^9S(^x0YMjg@%j2*d=~%tgWW zvaT3CfP^O6y%lzo4%D?*8Q4ZIp*&BzMN^_aydcabJT<1I>faLd{*o-@7h$NHc9`jK zV#;xtw+)QSR=|6B^+bS%f;!xxwJ=;1fZJM?R3>1NR0?aa3S5)gK_39&09axp)d3uU z+%#!gGf2i6YmJhj6iJnO2eW+eNywSe_S2%$R`QNzT@(amNv6n&W#3tQCFe;gOH$Rn zxj~L#M;m|u&YacFVQWV@D=9QxgHTK*e+vErOt4^hblqzX9lLpBbZ}~2!9MQbb@SnA zG%U%a&a$ec!>1yUCsoAJ|F3IRf|fJamyMJ8j4!?RDwprQ$#S*l#g|^;%U}Ege({Vy z^DDo|gL^aA7nfW-f6DXc7t~rgyLZlmdozFYKl*?0Z~veEEq?2F{)mfem`yC^thM25 z1xEo{$YkgvXA4?&Qg^6j-y(rjYv&yDK(naEni7{5$!?+HRqTj9KtmVY$6#$(lgB`} zc!v`6)vFTTFz4%A(wOMYSyCf4?J~^`^NJvX#D2L{8{4Z@`)D6$YJ7h+2t!D71(Y73 z9dQ=)4ok56RPpXiu^(B020hM{99+8YNgDVTbo(e8dy?69YQ|L$7-KSB$D>6DWZ9t7k&uD0=PF+Wp?XPT`w|Zbl*T4 zu`HbK1#1CtXCpRxMzw0lDYGB;eud&`1(se2O z>jLMZ`aA$MI$}KF8|ai>O_+y~y=`%&#T@nXi9^kDq?bV?XDMufE1R@4my?gNM|0;rgR@ z`TXa<$i+t=VZF&aZ-8mh!nZXig)B#fq2TTe5Po28334gHyc9#MMFrhM#$0-^CR}WT z$qY<$P14sEEYcFmymq3w5a4f-oC5iWh-KBOrp`7wCuos{yCi1W4Yw-hT9Sco3Xn19 z2}=m@4z`WtQ!r&4#0?rbigicrT$SmQ!v6ywel-(eAR{(GAwUeVQa4KrQw%8rXp^CR zZ&bu)@Bq9eQ4a4bOY#OAbIX;`CtTg5y3WR&f_)p)GN%;mF99yrCHEWNP+*iv^&xrX zNV1qij??0rvK7VaR+)!vF(*OEK`~>pgjN2c{6)IJ%-$Q%E}rq^#b05{on^l;d|z&|SU!#Iar1RozWQnGmE-TbE#E!{IBw?GcYXG-khqI9 z@oCp{*XNHv-v-d!1dG&p_y`l|1ItK^$>b61jo%tyyLB%7319i>&vwgyDgMjxUjg#} z;9vbK|Loc6$u~Vt6-f$u+(e3G>~N*S6XfG3AJbZ8$};=f+9BUS;>KVd57Yo(jvxAM ztd!f{bMxQsnsA@?x{Jkf7v1T$d$<)WilpWy5Oho}y9rWlpQ(5yz#F42*F1XR5ozA> z_FHfB=)prSF0Xj^y|;Po)z5KqauO6;XU@i>d*|e7;-e?ed473KDT$Ml9quxQBC6Yg zcaFRIk^6NI%6GJDOlgdj#et~mSWd>22mLmxW+~w*^PSh<;JuGOqD%#^AvH@Ozv-h> z$4IzzfX&kkTjfFDJ58CoRBD%L9VOFK3cdW6T4AQ7UylCqFWoku3o985wpQ(3eg@&R zo=vSFYwt1!Ein!VAjl@OQv#HbFNE$`>;YYC8eSi$d?`RN(-{>}u{p#VOVDIU*mIc5 zdHD6f$x;M?b|f!|pn_=G4O=j&vv3kn;5C9A1DEzX1@*g>Br2&XxJsA|^iouB zlg3-mTERlfnNl?NIZI-al98qYT~(cuM~4Shcq5(w2Ixt&n2x!fkl8ODRM{{j&62G2 zqaaReTtG?AS!Q;zhI2eq9|=Yzmy(Et|CJGb14@!|F7a@*1sJL(4VFvyNWc)#KFOO*T2YebR3M~4;5-4NX!XFAwDaJ+PG$NBj#mdPZ^K}+@szhP^fY6YSR3)&F{-o$TCQW`AVkNNC33E#NPJFIUM}6 z0QbW1cgRr^n?wsnjHG=@EJLl?gi+(#H9m^Rl4tUOINY`|Bi4pDB~N_}ydhKdU^~dO zMdLV3`ITJiUflx*ir-%uth+6688cJ&+qG_50GTP~zt*42V?n5%zA54h@2Y1D8%~-t zWTxGHo<6_k@BF>L$9r%73IFu3|2bZK`3rpX{CUiIz&ocWCzNUCqmMoyrycKo^s$aN z%hYw@{=S(nts0*nF0 zAC^Y53dc6ECMh{iNoT;A-wt^Srev2{Zu3s79m>qKo3YkZF-@kJ$i_J{z)q)H1rv|S zV+|w=b8rT;A(8@%ZjHTRD7I-thdxu8Cj~pGA-S;ps3M+pb^LMflFKErTzZSuFT8=? zR@@|~$wz>wS%Fbd=wjz4Oa->hTS+^qr0kM@Oa3e7p)fnG*AY|5FkgG9`W19?o?^EP zc55op`g`mNv0%%{PxN2T(GANL$0xR9mmZ*yCztFehqT8+3D6>Tu}qqZo`01ow+bH} zY*JflOL69S#CH}MnURW~gZ9H&h80d?Cnwa=kbFgy|> zc{u`KZvOe^M7j&`xb5$s^}6|>k3o{VCid~)KW$=~jpX~Vz(4IX?*yEV{_5_uw(|J# zV|FL!y#4lDoS&U?ae2k-Z@kXSFF)q~y?a~dlw!_<`)8b=o$}Xm!tSD&#pF zsr!#k-rG!uaUtrTFiVb~Z}V@XUykuq;*-nF58r&3_uhSvG6lF`#CNNrZMfJeZODIA z&8c;vR_Tm$5$MyaGby`P^$b<`awN$$s&xrfWEy=-l=BoF6#(x76DnT*XS-A)u}0-z0G(*f z!;DikxQv-Ra=J&%8P8?RQ;YA_4+Jh5Myr+kXA_@$>1F2Lu-eF_@bcq_y!P?~zWMXt zAiHyQb;0wC3zoIA+nsR#Ug6*ScmL1)8~@w?J@0;W#cG9%MTU8b`;6X4BCWBgu0XMv zj23$UBO3^o4+(=cC9Pv_>2y4*yl-bEk(ip;7aYE;r=$ey$U9>&#XOSb;R!UDW*Q%X z#R@q6aJbshzNi~u(6_q}Hd)%tB|RiE7*U}K-vsyY5Fi*Mk+i#q#}mO!G;%Nmnk$(- z4h`Z_Zp^TnJlGYHJfy?f*HV|aM-wn!3E8o>MujRpD7+hjWdX*-7kLW5CI2Lo$%afU z&c+Ud@a~94PQ(exf^881{a{^;@>KUQOf{3K3z51zGNX*~@$vo0G*;DmXXVHbYD?HX zm>{Cl5A)N8_CO0TBru5JxA^A$y6^1@6#1ZTn~I zqr*qvN)0O7MzU>(?;0Qxac&6o()w~YT0dORm`^I)y8tB@z&AjbWI4vMed6~9h}H3} zjkYm{Q^1XNNrJ^nYw?^T8N)ZvxDB|mcqCg|Nl1LP?mT3GWt#DnxL!N|=pX(b@BQH0 z{8#?j-{6H;UgP5@Psa=+nepi1BR+cal&2S0wBEV6xP&~BEOR=~EDj%j^sW-(JTYLi z7D?b}YwRvoDshG>Sx-?!(Aa9;Mu43LFyY%-O5Q1P96&N7sk1GS7pqY*a?Zm$%>|AW zNt@Ln5X_xC&8Xe6ktV1M)riM~^%5+# zsVF(m!BVuir)=7%yUX9qisXql=7~vilYHzm8B!4lm(;n@dOLu-Lz&?#@%+W!q zxz5g<SYYoG@xV92k7(csH6V!thY{*n-Al7u#RO(H@gnqh1lJkU^l^yPkVhD zt!e!GUDtct&yPRz=H$BzEV_#>b=zzE-eh<;uAe>QmB$ZA>8JSix4*@M^Lwnz%HR51 zf15x1XMdH?fBuW?7wP?rRbG8?!r4!Kp6|bX!Vlhhi>DV?JbvLJ56EE+*#?dbD(H|0c<-c~LBc*OH}OmFz)5)~F=qA`r-Hr<9DPSrs#qD8g%v zV6~7;T4R~qDa5vvp-(`=GK_YP7EG7!kkd&}mJ{AhrpTkR)WW;K!q5?uXG(IlsthTL znvNLs5uitP$xY_SK2mbA5Y(|HZ}`)$*aifql2|P|E&;uykV?f~7&>_+0qSm|=bR2; zOhU$}Rw4+DZWuiSby&A(PPpjJmQ(otNS=`A9j!XGOAeKb)?JSMb^{SC(X+bjOg%{- zUJ7+xNjWp;#M&BbB%o6+O4>M2pSAI*~cnP|FBO zP~tim1ueRjSO8r%XdQ(&;y;{j4-k^ z=weTJQ?Ht2k+7No=`ltt2d2vK`f%jOGCDpvzA1_a@S%<`O~#6Gwso^D;h<-#>0B&H zoj|%LtOf*{nvx<07@!1futczfOm#@zx{rO6jh(SEc1-M?F;Tn^kSHqR&bAiR=}tO* zg0Z3@G^`8Ib<3;{-X)*(IJf8)Or-%lh^aDK>GoiW$S_wU)?FS|VuC7&voKPXnXpV* z9Yt$&i+xq)g4&0c(JA8{zj*^ZoxY7=u`ukiW%lzbC*cOwc42DysXh0R)(pE@wGH!gdXPK zMo&7gMio6$;W!uL;5Ch{n~FeMlg{<^h1P$O*Tlc`*Z*Ty`ycUd{O|q`y!^^5y!Yr0E+cty5+mA-T;wT*%`lY!7L6$#ybpWK zgmY96VT%DYDX#(%mtTmWAP8Z0eF)`!`A~{#XLQ^+gm%*h!adPiFal)%`(+HQ-(S1*oT)$o@5Q*yV`b_-HsHWH0Q#h|8x(J7D%*^ zzmN4{m=(op$?#g`!DnLN8WyI!qq|=%{)(=$KIjnf5=sRoH;1ZIpmf)Pd1>lX^%XX&x~`+UF^1;)nWGP zYN4);2M_P@_`yAjceqxAi~aSUX`Xm+e$My5{~h+1&-n7IFZ1?WZ?QUj^P9iKY{u2q za}||tr1GByi`*tZx=ALu`QDpg#?ANN^_jQ5ZvO6VdG5`;{Wi1UyWW5N@3&pUF*a~h zX>;@Uns3gV;~nwbx4%K;Y=aT}nGfJIUOy27<}d%{zw(z(Pfxx%0%JQm`psL%Xw^;H5|B z+&h23^Xrwj-~Et_t4mH!3nz04NfM;cJr6^{@y&g!S_t)Rv0;hOiGrkU(-ZwXnGzjx z=JoeJ;`?vB!8A>hg_to+Qgg^Idaty;s-sPeW2-$pX^=X#O9Ifs{4}A@a?7M?!pcN4 z8KL5;7^i6#MOJsH3Qj2-h>^;qjX!0&B~r=Kmi8VMsdO0skRj;cmos~kW(7-!s$w#u zloAY^k;t2Z8iUkbNxB66qH3BS0zR}Mbcd;^3ycvo`Chlf2S$>UNakXw1Y-jmOokS1 zmTi*?++p#xN14+x_=Cl}aFs&dr;cTyScwUyG((-e|QRbF!FrzI=OkuXM8 zJDVudWBvBZh;J=|sZN&m9zw#PYMSKzVxk*}b zmZ^Ej4th|>4Y}}3K15V%42q;lC53Fs6i8S~8`EY~)-iR_DM8{G?6A3%gJ+1Lj$YP- z-IUms%quTE;^FBDtvRi(y!Ogte(FoF^0Qz2A{n^2c+T^SOB{Il<;VQ?-}#&Tum9)& zOWt_<30K`@*k~5rtq8DXB-2q5^r*?2I;$PFKaUO@nzvi6$Oeuxqzmy2P6VNX#;#L?p21T z`Fso{B0!FakB6LV!#6yD02BKo+mWQxBc*o^ z-zzhjV6W)8n9%A7VY*tbBM@Y)?eU#DUxM{9jye}W){%4}%B#qj+cudJ46x7`9$ns{ zr@mE~Qwj!IjF0Y9XJ?L^t}f}s27L4`VA;@G2DP${Mb})+%wafbJ*xR|qxaL@j{Uy!@y8!h=AEv2o2N03G4=-zmoXF0KV5rOM0&yMaRIk+&&PX+ zeCD+O^)%@6VU!*TF+eH;PNWR(hx-qP+E#pteIc7+WsIHtIGPLi?I@V8&LKD8G>Dt$~Frb9UL*kvtp9gwD;vCc2q8zO00E4+BKx}CV#>{7>$TMl1v_2 zX)t%m+dgCgB#iEvW{D|x8kLo_Nmm`kpQ#5>70-UsN>%4KLx5m96q1@-06+S)Iyocd%zC|$(j=KwFiv_?7t5S7Qm!)8}4YJ>%Z_J%8kekAHu|6AaGJ?s0OmqxGizFmh1L2jMf&Tge9BeCv(B@z?(9 zU;Qsvg7jDZ%3t|Qr>Cdi)UOR%&LM~z@}uz&W(ta*KYLDZl64$9`4EWV5xplS(QSas z=+}L$-@5BT-zJs0ZKB=v88;EKPea;1Z8F@POgB-*Py77aC|kEqw21H(Oa#DHS58k) zc=Y09o?Sf0+rqv35BT2qf57`6yw6LIU*P=g9CwR~S?9s&j+b6|#O~yjk1w9{{)bPw zxVR!G=lt}H)>X|Ifms`J3saC|5m*EXKLP^7=*1(VB-JiM%$zqMaY6Dv}wI zKt*t8(FJFCKo|-J({RJr4UeFqh3`n7WSA$mrIMwD$uq+g6qTlcqK9uxvT$_I3bido zQqj8BN>a**Lo&6Gq{@lDAqk0@<89Spmr_)*Qv{3x3@i&s2%HBHOQM8CWesh5Ns=5{ zc-LSu`yEryyZoHO3RTqM7!ylyXqzdTv1p&M7euu$-1t!2#}o|C2ohSL&Y^!zqRrqy z?G>mow&BC!(K*UO;xopvtHX&O0k=5<1AV8qK4Y{Y)^oQ2Pr7?_Um!rX5gd-hLLC@H z@mCvelF_QxO}0czQ+OeS7{N7<4WdG*b)BNSjyI*_7LvMvor2cf<2lFvnTD(=DCM;s zK&@_gQ+sq;Q(NLxpleQbk8!d@$r-gr2ZBPM4A!;Md%|5%gvEGZ#{H9-FTC^$$s4UL zob7hJ`uHK=`0{JK`pV1f_t(LGS$Xi_Ay3|ai+}BZ_CMu!|H&Uw$_bZCjpWnC_-NW^ zIvzow5>Jb{I>3K{F&5j)AyAPD$*FGJT#=85+KP^6EO{RTWr>6+I@E-xQpYxGyc5AD z0i1b?^%&b?8gz3dxlQHktwMI-;KbSh28Z5#n4`LCgps7jx5alKF)ibN`Zx>5(GZx4 zQpjL_jYHbw3__r;hJ3IT?I@}CL#KfFc`_vvgQC09#qH<|Y2Z=eG!Fyeab%1{-9vQ~ zlCVCQEy0q(sljK!+0^EwU*v7IQ$eC}6oe`$CJ^~hnIUY}Iuu2jK zsnZDPL7R{D3AXJ@v zl;CDF%w@puxyRZXYX&SG=0DrG>sj^jOgxOJW90iV;2~J3F0i`?VmMj|dyMCZE(c(3 zW>Lt={PyqsLw@GVukmMpUv=}oo#ZHlk;=ldG}p9 zi8RleV_86#_H`x2kZ7)RQZiB7q-WY~($?g_EKy2{h69~iYk>UP|12azDQEh+;=nY| zSW>%T#tb%f>X8w*+6Y^;q9497$;4VygmH)|od8~P4q2v;V=#PY*v?Q!{F1bn9((oQS=Zn z;M&WhP(hdHOAfFhWhM6|iWcN7Hf7vHN{J~ut#wMCVx9)up(r&fOn?1iKU_OOhawXr9SYxY}gdW(=cGtyuvNo{Xi5ou*=0?42oCAHfFCqlit2 zAuW?l%(UDMwN2O9Gxp@ztUcnP(Q-XcJH^x<(y>nXNy_hJtySxPn(0GwjWLzMrV~S2 zK8XQZ7^6rzfCb~I@B~D>lfw>RoE_N`Qz;u*>%HT(a&~&o&wTxB zOy0yq4j}X5@-o&z#EBeouVH*omO(JE<*TEssGg60-g;wyy=Pq;5AL7y(hK)Fn-i%n z&>OusO3s{}op5>egzx{K>cfg!B84`Nq%vG<|=m{UF`QC;8;xKkIcf zU%X2ubQ`E~TaJ30n&jsDZW0VWEyvx~>mAo{^E2;Km)xaX+4AizHuCNB#%D^-oj5%` z{{hAIG#DJF-rw&T40*4uyMum08lk|jvvBAmm4zkx4X^>7RV=z#m=gN_f`{+v?! z1Sm6JcRi3#L&V0vZ`WdZ}a4%54nHu5$}BPA^*vL@}Kgn|J1MWm9Kq`T47yR z5-azn%s0OHh*uw<^Cxe7$eV9{z_Ncv?}>Z&PMNY%TZ-U34kJZtj03s{oi*#UjG%X@ zUlj1v-og-40YFI;bILMjw!v&rHN2tZU@AMVmrJ$QzS@(f%<1Vli(Tn($TGM~HY!W1 zW^Npn&HE#XHc6n?Nr%3rsNz*67(-=Va-!8Hntps(p5{nWbz^f>Lt67Kn8rKSB1ldG zfkwhHQ3&%5mbL_-q}Cd?bt?!*e-}ugvFTgoduV`b(2`41*Bm!j6{*Y{11(u*sg+y` zc|HJSsu0VV)*^wi?j-Lz#E@hnS&2YJ!7nKoK-gAcvo5sQUBKB?CTg#=bzz!z#0f9M zJhM0eH4n<5{jQE>W6o0;JXTuHk!pt=s_WW%&a9nHudG(BGq?bcBn_1qP4i4&mZ%`3 zAi@gfGN#TZ+0SaD+rx_W=)i&~-b%dWzmik10FpXEb$3?R=PFUn0T4K;#P>zzI~#~b z>`W&)4sWfWVum`ssHI}JBEO7C>U3?gwb%lfiI8~qE%@nOCdTTdq#$Ie>R7NVGi8?H z?7CcQyyufh>I`$JeJ5E!lGZ#qoRwKXju}qlbaz6cvR+;B@cfKv%CvPMnQ?kD@#y|3 zFTL;r)*IK?mt0-RL~ge;{;&V{f0w`go4-Zngu1Vhutaj}0T3|6L#E_?J5RPtO?$LTPo$#r$O2(^6DT&MQ-hT#u+hnskkY9&H)x&=e-Dp&lq^7)-el|< zOHuQu(n463f8*c$Kk^s;+5ajpz4S%id-q+|6|S$YdHIEhy!_%r-u>uf@D=a7 zV1z8(KRe}x2M>7v>2v0@Git~~avpMNw(~r7txMd4mFvtYZ!Aw$+4Y`zDzqkf_cZPB zBty|=eM?5YI#!BykyvYGH*IU8N1Q+xAW?GGGY+OhP8RE4dy{*^O}<*OV?-->!jQEv zMVyj?Jz{3+(A66`NophMMaDd9UoZjq38+3yb`!%)*z%XBxYo z!|-`ZW?}cwnKiDmh1+ z^yp0H4p|*1i2^|>MJ$n~PIxJgPHvrS(ys*KN4mJV+_J?+tL-4lEtzCnNDrVL-0^ zvh^NpVZ~gnHhRPkZFDbdTC*qf#Qn1~&UX{Lth2OwV|C~LgZoUydE2flF(KHLN{2EE$sc)&;C=G*Tc10=@-eB2G__;oDfIi46dUq0AkQ87CH{m-H- zjeEH5cW!&gp9P-W1Zi&m{&?SQlkK+m-@KM&hq{u+O~J}~eND=R&%gFEyLslx(~o%N z#TU6+R{s8P{R7^7^DTbmo4?Gx2M^dUD{HM->O9WR0NCH93W{r$|B~dD;Rc9qy4%MOD7Dt_x?oiOFPEHIj2tqviL~TcxjJ zcwp+3P)<+jZNYG+(^KlQuzEWzv@S6~vP`i=ZL8`4L7|X@=y~s&7}cwo6g?=T^AriUm?}*t3+sLn zFpvZoX&sGL)9O64UsizuQ|N8IQc{LIg?Zbd0934yQ?#lKMu$g*%le>wtJS6F6sB}2 zs0_c1;rTQwNXd-4u0sCts|;zULUyRj0=Y1i%+j3NDxD^Ogb3oZ z9Wn7trpeu9{MteSkqmN4vf!u(`n&ePNc;jAQya<94M*Z*k>ob-^00{@xSLTuky}%M zFzukLg?ULR!A(&yG_8r<$&TJu2oIlbnP&3F=}qm}@1M-nrUa&#Q9Ln~b4t?KENfMz zp2D1p(nxRWFh$VGL;v1eVmBvfg-+$c=?S<~o8#+B@`)Gk-{ax^b81~#mp!dj9=-Si zzyFW_G5`L*_aAU|-RU_zAR_B+?%|tYNP-}6x4YAid^PYPbqxT+mHZR`?~O1_L;<*a z1gNUiV@XoU0FpiAEMmwhDb^ke5YmhYh_w%eG$Mj}NRjC4dPqvlBM*yfjbcR{Oov0? zV?K@V2!`39)j0SD@VIpIXpika%rM0iN+W2D|J^!D^n;8mnT6L#g!I9$?Oie-3kj$8 za53(z2U9gbr>%QL?8hT6(A1Aj!)#mgJ;u$3+{AK7n!>BXqMGiS52;%){}3C<24QzF zc=|RK5{fZ~Uzn2hjwOMZE*qa1r4-VazA+@mS=*b=)Sg`6O$1sZXdHoo2w;<^OUMp< z#6Q7I5mvJWc*83>=fSRGi=-vAsan?-En|I4-!vyIo0$=B-i-Xs3uKm0xZ?SJe4 z!vFAp^1tBYd8V!l%YM)O-5D=DdJs=KQGL-fXqmZoUViaKK6?5@XK^m1*3=T))S_GD zP$ws`9eXVNjmaDB;k$X9HL$iWHcQf8JwRJ|MQQxKuS`Yet4nQ=g544^m!%|d9b+mv z)`39C?oMA_YdFrEAw99NCjm9aZki-(8WL7Kd|~j7Y23qmD@7uZyl8m}PoVcTngu9M zO(}2eDLm=S?vj6HdN5g1NZ7{yqC$mRB89YAh3c3y6p}@Jk^06e>eV$qNyUuZs{m@3 zygiS^WyH^|#@y6BW>=E#W6iHC$()u#mhN%yfgrl6NI`&zX@EE(rN_Wq(|RfitB&Vj zv-U>m^1;DnsNUnVM+YZ*$9vjrUpfMR)Mcy30Tms~{IJ)FkQmQzz@TmHd0!fTHobNT#&XZwYp`uf*7JDIq;daiwNFv7N+@c<2cg6+zW=BeXn zcjcSMuTKLqK8;9l{5pQ$O?Auh^&RK*Hwg=$c6~SR=hNPQCl+wycaL-OJepE;vFDFI zE!O)ug^tD_Nck2z(0_r~PXxexVm|mM2N!PsU7M4NLn9&o`QuD7P=Gcjg$4NXBi9$S;od6r%BwFw z;?bjXKKS56&UQ2BufEC;-*|&J-+7CF>X&|*uYT=ooSvMpUl!V0DcShk!yS+AeSr_2 zzR2ruzt4vsKjp)ZpL74-Irr|Jv)h#|!H_AWZ~D@~#RA7ur$iO91@%3}(Qq=)U-OBn6Jm zfFZwd1z1Jr5le*>(w$^7s!d4Wtqu;V-1TgbwV=%_EhpAs4D2Sc2l}$Af`nKP*>nJQ zgTB@PdL}BJG|lP~(N;X?tqm`hu%dwc^kj!6`Ndetw7OUD<%jhqwuV5b-X%Xts7-P= zwG#B!upGvtmT9?AQUo9=M{s3yORT+-y;Fvt%vhhhiBe{0*Ghy@Ri#XmSW)pj%q5YN zWIzDI(xO_4=;CS0EGg65DgdLeq*9nmVd|Ov+NlOpk`bhe>j|S_X_8hv&D7R|iYu@p zPgAhv)fJ$ak;)wUUQ>|bRhx1us&4egG)+(nyJ^R|UueCN(!r3pyQEjXB{CH#8*pVk zQ?kb2Q-QV!1hY)es(O}Ww7$~QM4J;6tLACS)UFncyHeEoD+Kp7j55TaE_juuyhp3a zDbK8Hr6eP##){+78O!OEc?YXH`kc-?%qwOyv7(V2AedN#W#dhirUq@bb&~a2UGl*R+|t7Upc*imDGHb znuNBAVC_`Mj@I7bxRrOiOtl9(X4lk!n;3OI>c ziMF?nFr>J4uedjX9j3K3*j|Pt4Qt4TlY8_pKY&Cnq)_CMsAw-srcNz}Gi>NBOf*;b z02)U+p8`+@UECNGJ^$9BTV=8^QQx=$2CgoI+A}?h9x-Pv;>Ku;JA7$Wbstkn-Vwx_ z&M6~aW$a)83cRGE6ugxTUA2VT>~U2 zr9f5jB9)j2CZIZn$Didy?ZNodVo?#RS9;3o6xkZJH*%TCqd;Ma&08z3RbfVt0!A=( zbJAy251Dde$}?7y{B@#hriGETx;-}S2{~g~@p5Z}xfdXQFkb-DG|ocX;#?c-kKQOr zroC>8Z5<#{?b})Cu>hy!p zsH+%ya(2pGGRrbz<0$Ne5H2>CxVA*fhulvs$(`DonquTcDT*DYT)203!lQdTPGlPt z%sGLJ4<6jZ+rsz$iF-sO{%SGc?Ur9U*zXMN`*Sy z*DxnO*3*3gIdmlq7P{1%i^*>+V1RXhMYhDtFFoejvn!rFd&1{F{{^03?)lB%`Uiaf z2jAlte&H8+^>eSmY%EJ-U03Fucb*)g;tHDt>Al$-S6xRI;2G^HYQTbGW zU2>tshFMdGRH8jz*xv>9v$sLZ?93%&t^kpgn0Gr(nzqp0C}H?JrMiI=DMj_hMftYw zP-dB1CTCgpk++V+PY2>WCE8l?DKpKxNKT@<+A8h^o00STlsK_OwwZNVX;pF~auPVw zRbiV$v)oN4V`fI06YBuoKq9}gDoC1@$c^^09+cwg^ps@Ae!s_E60o*w(N!S@KvFSZ z$!BLu_)T4&I^b0)aWR9EIh}WL5T;=9oQcVh^c>f^lF~#DL*v;;qET3vYe>eNvh>TX z;!CCMPS~NYJH?z@jA?g5Cb29Fc@8#Em4ROKPFq*zvr{^@8LIAbW}el`dS7J%e)jM_ zy>@zSq+BFtBBTIBrx7!}b*5<+c#$);t$43YQ)Xwg4Ed6vM4zLQ-=nOYcho8`8fsM~ z%Nx77gg?!F>begvmn40Ba!T5fNvsiMC_~FQvpPO?XPW0oaywI&?5eeul%SSO?dk$F zPlnWNMP13PfQkN`icFF-9YXsIvKLfCYUGzg!q=u?t0dM&R2NleuHM+4p0YbVC9R8O zW2JDiJK@3odn8Y+b*1&j>AeU1@CQHSZ~x7|6&?;stj$zm>QOD<$R9~TQi>pplvv}r zrsR>ZG{r0CA^Ri1fdEp*`Gjr%-oi98xlyAwZRS)T_KE{giA@%Z3VcxU&9?c1=NQ{K zp1XFq0RpKt@u+ByLp-i68Io2R-j3QGMmI1K48+utl*JFhZC8>t1;xMFp>mDOSkWRu zb{D{#iT$o3Lq!|R0-cfFP`m9U5G%Prtz&Nhww`ec0&Sc{1OOEYvVz$mA7um7s|x^2 zu4JtbF10)KLjW|Gl_McA>jIj4-*ovhI1aby%$B{ndw5>RFcQ>_#=7EODNVY|IWl8?^k@ zkA?6tIWU=qLo0)nr6O!2`%h)iw!IE=GQ|u*{sDw1g0t7a7;p!o~6mT!lBPYc?#N4o41oX6~ zd!3Ekv^SQV>7E3pm|8fyWtvj`XdW;+`-CBV7qcsy3*3vuhlmaH6n3z+sxX$4l5z(b zzzfLA)2!l-9eg5<#7dJ?+{HHOm|AQnSLd**_YWYZOj+J&-NRs412{aZa?TM0jShB- zyF5q1FKO(sEU+_+-RkIf+v@5}*cBH$Mh{7>xtM9KNy3Lg?~RjbrkRV8ZCPtO$s5J! z^aktF)F>gAcFKk|N#1hO9!yBrX~+b<}(jHW%iMFl+(NfYp$phq-7gfs8)5O}vTPfT6PPsT%>A=drjn>hbK6PKy1J*OB*4jx)+(0On87!rYyrTM z!0VY+@7zB<<AnKF2#H?;&mxpzWJEAPDdI^X%ucS+uP?bR2#dVayx#dBVJ z_47uFkD7pE&$A2HJmQVA48GpWM0(@KEJ^uXT zuypQX&)h}SxH(35jo~L*J2CH#Sif=IcKE#B1zv`3ogwo6)4YB{3DQk4KunevV>WQR($0CC5x64t*SkCN{Jc>zLH~OE;+gc9CWqG6=0X$6jiNo^UvT@!GU6R)9lXE zjJM9d?J)``(FU`1!hkKSa%0ZC+tI5kU?nBIby8|fKwXiICTJB#;DnRGA1v`)97Ihsll zgQ+(KDirxQBktD9^d=+cOblSyc=r^ zSx@YTc^GjxY%&7`#t2M|N@I*gGK&f(fIhQD!Xw5HklbGmLVTdq*;3 zVz5XeGvJ3fe$BUzMU$(0SaT0grX0VY*uK$6vZ%wnqE0@oYF?^ErnCA;L+cXVcm;@2%YrsNb=NmYEaM|*J}m7u0BKr$NIf)kxf^2U;c zA(C_RAZ7@38C^Ua0!7d~R&r?ZOpg+D7vKdRz8~O`utq+ucE?@G%rKd?H2goJ_lyO@ zt~Y7^-6#xJk!y2_I{*QE!vNSgdPq^c*o;ocQR`xu0Fo4V+vEenk70KfDEar~SKmr*#3RM8J>2e!7SEXZ)TG8Q4+5Dp^v+nvmm0u`362OV7o(YZLy{ zq3VntnBA+O)Oev95W?f;LPdUGTkB^_dLq0vHXTw@2yNWvyT_Qggnf#A7?qgB4uK0u$G9<4I z+i_6xSn`B-C7@+0m>aFBee_rkC8;%f4e-ZeH**AJ<^pRYfERS{uG!bjaO-$mS-OQE zoeEwuNh~A*SmEy$+Ptgi7w8p}C8pg(#mUi0t0N|Dq6;us7O`l{r026MTFb4|liFSzoxWCz7i@xb zeqVb@c)Vd?r!w|!t+Cd|`RO@k&h>sT`O(?Am@uu&tIiT<4^A|n+sd??qlJ4944tKF%Zj&F~^^kA=+ieV%yO=OH!IIz>>%VOxeqtQrfH<3r&}!>U8NTx6FHrke_|~`ni0{AtHb3>%FY&dneVG?t zdYP0mYwh@|PC_r=n|bNp%Y5x~FY>|DE8c(djAzd;dGhp1!9|YBWN~ty4z%N}?hzvN z>5!c#Ih!xn3z;3>7Ev7!7!!3_Nolx}H}0LE(9GChJJ}~%78@(oPD%y$Do`b=Fln%G zOgg_(O2`BnwaO7ab&XH$y4sQwIqN$yQRXGzG5Nfh)7pZ!sz9e1Da~?a&ur*pE+yDA zFzpJ)mE=fGk}7pjKmbX~GW}iEPP~+ax!yA*EVVV}`IKJkHdd)C9nxm0!(=K7u=W>Ow{XJW71rKF z$2J*1DzTGMacYFVIx|g~b%}t^@^&D$y6E?iaom&;@V1+2O&%a18gO^VGt6byS~iVQ znq-*g?yNyywJfl&uc~6Iy=P@|0(5ewGJ$1U8{j}A7s;*ilm!a7#&ar}Tn-^}w>c6m zBEUXCglST3M2@Ej?(lzeS7*dJw zsOfP*c(8a{038}%eZ~ve!GqviZC%e9cu%5-!1au7(Lz#Oya z*g^Pgj2JBitBQ~T_3&5#YRolZg2vFtL?5XL0pq)39XT>-9o^UlK$?@De?=Zv+R9?> zna)5-@lI1wNdfCmH2@c>7`y@r2>ZP#cfW=BdPH%!Lv^n~B}?ce6j z*I(!U3$Ji>c|olU(|p3cvlH?JFp}MdS)nB3-q{&*NpUE;n7QG#BH2r4%9&l!+Vn)o zj13Rdd0cZdQ|Ku?NGM=RB)bROAlT$&fuXINJllMwAYjNX(d#O(EvrB{B?}3R;+$Gn zt^X2c&8Sg+w+9=;#8o1e%ZHRik!DyPcSG7NBZtc+TWL6~>}d*oip3G%#C(WF-9DeG(w_LL*vwb z^U|GSlGsxUby=BOVxD%2(R?LYqoj$e%S+rcIqSTiPEKJ}+%e~*uC=JB)!kY4jb5F3 zcM?XyiM9r3G-Z66F^A$}7F(W`YqULcp4BGXLW*m{6c^}5T~ibrAg97K6(&<>q+KcW z`5vt`8Q~@a)L@`wu~xq zxPQ(&Z@tAUFTKW#j~}wE*P92|UCfm2Jh&tGJ7$F3m22Mi89xr$;P^9d%U6Gr*KGuZ zoBw{>wIBbR*4QT%9(R4l&3hcLo4}JLqc4Mj7iYI0pj;h*63lXpR~ zak#-Jh9Pn-U9e2x>=QryQFQMnBWC;z*yy7-AKG0&#_{joeD7To>MmN-@n?-c-!?&R zL;G(2`A5gjKk@hD*D>?wE;R7d7Qv@2j)C}%mjc?L>{;jZ^o;JA-}no^4r%6he*1U% z!Rx=ptFOGk*S`8iUU~Hu=J}Lbomv-g=QJn2^!SX=KR)MbsXV`0xwzbOy)3NjO0Avl z+5jyjQqEjouhg}2y)0ZVd-l~i*`4y>#g|atp} zs)J%_q>?z<&GhE1Yh_nvuuAVM#gqgn4p-3GF;(_^->P`tI^@J|mI0pil@YC{r$U)_x=!n47f6*$&{1>7cg|8P(3nhGYR5^b z&~wo|m_+5IC5mTSuj=@Nle5#4-2Kz2HkPDBHD~52TE*g#kQ=Fm`Cc}>$KmMUOqc~N z*OVAanIQlmS)p5_FBR)h=1KI9)&jgBykdp~Kwd1ZYrN z?tnK&$EReOx+;;$*_ft@>J@7+g+5tpM1Tczlq^?NjEfNM-8%yF^e1KvCQLGF~ zGV+fK^V9|05h~HE#(6iZ736*|6U01aKK$TAzWbf;(X+r# zd9;Y7B@Yu3`iTTHkCpGk6C$d=Da19w01#_Sl|*Q?pwp)S4G8Jh@J!MEsn2qQrAL=3 z7kgvm*~u(CGJ-icWF4-Aw+%oi$=hLdeb){pzC%he%oux%Lmngd@H)U5EtESg_TrG* z5R9r|^H~pxmN{!*$#z)70*Pb<++0a-kQDTD>j6RpKh<@0aXaz*y1v#TSybk_ty>Ea z#c@_os<4LaB86_uO$;Q-Qk3jgz?z+?c zU)!0M`WDRU4|*Qv0)vwoq!yLn=zbQS3?wxPPQkp;z7}jpIFkAp;c_hLk%!7zxdYy0 zIc676$hm7cmif*xYX&Z@zt>aC`@e>b^JQ2W-1>ds>9+T7u>jmwXu0l&H%R zjE5;$p^Z`|Es>_~X{Crcl)KopxlC9vC+M9yrOm9?M#9=VttK&V>&iS$xS3ccQOxSS zV^d~#I@8t_dc(Z2n=-9cS|3tYpjF!1sYy){YG3fG@90TP)>aqAYq_wrwlyg5+E`no zOet~+u(qf7#BR#;Wlwi!E^~OrIH?eF6(WwEw>jd#Y0S}OQKJZ(1ni`i+0Ca==GZSg z(^NLHB_#5sJ$CU`J~Yc-aeYZN-{Y$!R7u6A6x}z0QgrUN23AOYm7K_S)Y>G! zNkFey>(o^xifNv(GI4!7_jbK?-zP^PUjQJ$`-|rOYLMDC7TL*UCf|m-3y?lbGp;Hb=?-sjDo39<4G)P z=}jq5ozMe3O}S z*Xy?T+@|)pE8qTX1F9;_!28K|i)FAk0B=tuAA)aXKHfU~V!%#_Kjz@c8GqA%?e2 zq?^Bgm+#75j2ReGm;*3!Tbr%hKI7)=HdfEgN&jiTfAjnAdhczYe-p&|#JvH#oN4=O z)^+8lzx;XbJ$%R)UVVx6df|8f@Vor(Klv`xAAXnDUVWJ_eCajrJ$yhib=p~$1;aQi zSrowsJ6dy=wQ;pp*4k)I8*wL5EVIvzlqbr3O6{FTzx)DLX4a*$U&M5{yu9YSum6A# zKmLI1#y6{AWbxvxZ`9J&9=KU znG!)as2G+eu0DL1y1!)36QxWv>)`{_w|PAR)>3wHO#)wRE_Pj zx$actGXv{Vam!dKa`RsW*12`sYE)mDdft-%wO3>+2yI{K{Tbam>*WR47ti_1&wi8B z-6_}4pNJuoJAL0pM~@b!y?1tJr@Z^_JG}Mgo7#iMzQjl}M{PZA6^CwPYy=DpsSpv$ zHI&rmje0-)L537ZEq`+)<{s6<-UIY7NWsSHW~$D|Gw#+lUyS3#*exjDUL_&e7zrVT zBE=eu7Rw~DpR9%dsV0Hj(HS!idJPC|baNxvPHuP;DBZEmXk6E>F;Gj;IFI8D)+Ypt z8i58T6Hb@&spN@6JL8fqbs|9y8BjzV0wO_vGmVhNs4A3DlHNlOkz!%W@JdaRi={j?KD@b(2{b1W?|spS|tA3n>45N z?1mgD;`bo4XpVLtOen%ovLN%)3f4lJqrf%Uq4We!@9|tBNk2Z%O-@XhB}p^8#>-61 zi5{(JM>j_ShY9Z?hznNdcIpe+)S8cT$rB;}Qa7aG3pRQ@44|!>(UQid_>g!>`V>Bw z;cF%!?!a)2jzyLwoycIQP!P8(&?e5=(Wy~@m*hJsSU}A~D&dC?Wj1I^;RjWF4+GCQ zlDh7(nc54FpS2kOl!|b$i4=SyGzCGNd=8go!mQ!yPC8|35xTB}-Krh>PXwysR`PCj$2cyG+} z8S8ot1|^qZE2)xS<^rCfN%G;Y!qPCwoo9gnbPH2$0g~O+zPcEZT!yr|B!Q3a8q++< zN5$oVk|(hc+S*vw%34E?;44#+*{zJxS3qD-k2&CdJj9F@?LgTkTCZI1FPQQ~&MAs6 z`rMq2RvoffxwT$vzK1Mtnr0Ht)%BIU%n)<8loTvp#aSix_=LRLTI=-c*i^KR>dLa; za>;GiOU0jJ92$9= zIX%C}X*v_zVwF@#G1ba66|zlWiTx^e(mdz*Of_~`m&!bqSnrj(E|BN&)r&oFnA)BX zDO-m$$$Kd4Ii!@;$;X`iKDv1(F=ytT>;l@llB|+}>n7vwDLIuLB?@sfjeGY_Ws07h zhb1xZW~_@@yRIwK?v%5852G!&bGA6^BWNtxlTRW@@cKWu9g=I_QlW z@(>VXyZ6Mjn-vdqXDTyhqpO?RfR}k@H=S@cpD|BamI%qnxpVI%Q<5>Co@uS8#FP^B z6P7B5(&;>Ly4!JjGBHnyDamKey;EDIu8=6v>9q0D$4~f!AHK;)?|;a%%ctz7nXi2L zr+9FG$CHmf!pbS<=R4ZEk26Ty*lkLWV-mtm0>OBFmMY>lCd={f-u&6k&%6s*x%vJd z_dPeS5Qu`2P3*m-{hB?eD(Hx4-*CzW@3^RBxAJ8{mS)z z50PZ$6vk!|VAm@7NhyieR0oVUSx5j)Ro+w8bN)|k{7Mjx!$j&Nj@c_ z;HpbP*-fOLxV*YxtqQbLU+KQb3{r-a%f=v?^2F}+oax>JS~qZ`*2>50`!q6FR}1P0 z1oO00CAG{%ULljQY%-X_~mWxY#OoQ;x1LbUayfbqbab0`QtI zc#BkM19T+IaM%1NWv;ImaxU!Vnch45s=xQzC9L7ot?v*c3v=;J3`JbNB<*I2t`X$m}v!|t$u zwE!DOn_+VoZ~%i}pf%9D_JjZ=Mn@Rp+iO&tk}w)Q%b?%8hqpxFKFI>U@Fc@n3l>c~ z7zR6iKnE;Hx|GTEKD;kN>K7dU<|<6&B!HsUO7^_T`pm?f^I$!skenDMlR|oY#sDP3 zpt&y^g8>5hNW_(-3oy{;J6e&BE>T#p$GWPgqf?QXfnw&R!N%AmFbYOS;$317cNhD~ zhp$@}LjfoD{-&fJC@!z5=8dhhk;OTL+M4DhW78he6Wo;FZ=G;7_ANlVL_ACrkf_)q zIz8c=H;cJspB9WO%r+*9s$A-9c8GF_`?c_$=_I$U`Px($kdi%NUblH*F+LuZRe|r2X?m1sOyzr} z`7H17U|Zlr!3H*%T6!gqdDP`;VX<#{0Al0SUC^%)EE_ZEac>YHjN@ch7nrB+eOd<% zoyUgS6`P9*J)Vqb%|Uf5^Ns1};RUgEMl#!EjUI8R)5t0F{OX$b-+f=>o8=cW#>>o^ z19&EYRqKYgJwN?3KZTv$XHVu=e)*fc@bL7|!76!3umuvVi$L-rrNg2^-<)X*1MO&m zlWd|^`JUyXV$HgWWhSsmiL+UR*S&A(rdmWNB@fvT{~kQTIV%RbS}JClJmszPTQ(pz9=0dBD?kzl8 zni>ftv2*IWhBPLzh6!)UiKSK6MMaZZ7ly>IM`26gspeTRoq=nGYR+00d|9~O@43ES zX?5kao^iEYb9Hq|?($D_gEG&&^0}A!+Lyk<$t-rvx?b_lyYKSBhaYos@tpl$lEAe} z(&@?YlsHQ}m=X`~pK^Y3sx!(3fLTuLc8YCkcd!)Q0S#QNX(vTtuW@y8!M(FHcDqSQ zVJf6lSl7Ln#3okx)2C1P<3Ii*mPK;p>OBCAtoGMv58a1cEtkxG{~U9uwNckq(!-Gl zI_zVzyg{PdT;sAq(%)9pKb$j%eD%|SjoU~Hcm3OKpZ!^%dHlNTvv2$N zALn=OB1rtWdF@@Ff7j1Gi;OXT7C()c5{R=%e;*A8*su+iXxRSuCw=|I5+vP+HaZ2t z7QvovfwU5PB}k(UHE~D_wagpv0*67l31kegWlY$+%y)HNKRVHFL&b)%(9M6pZIa%` zS{a{l8w2OZVe{PfU7xn_ZZc!;Vzb=5=g&evx6gw^kZOsG>xK3D1J3dv@Zu|9pqxM8 zWM}-+&wYVk`nfOg&U?@K&iCKsdvCnYcYg3D=Vv=!c=V7L9>2i-2lpw{j4BlJ%eVeo)LkCq(^SXJfFs4D7|%7*F18WaZCrXsip5U73$h~>HMK|*~M_r)avS- z)0}nkDQjwqY+B*U*1_tPvff0tpaVB0 zY`a<2sq5=KOKa2`5|YsICo6FnN=kKpdKwP=My;aOLgMP)Ob4#k6*D*~lY-P_Sdl5} zT#5|T>Pne+k|Q|u@Q_Gh7)b<1z1PUS6r`7_;9b?0-k_Q>7wNGj|6;3>j_w929QfRo z*eiGsi9xglFQP&KnSGVHt}1v9Ptnx?RouEM(Mu^ux<6P)nG_~^v_N11Me-Wk%H z*0GE{Tsrg@U%*{5lHqZXCv{Cx1v8VfMZ(KLx9|19I&j|-=-RsE7^Z5Lp(=6LAmd|= z=84w!z-U=s5h|bn<^JjCINR;GzPP4$XYxJE@)S@tieB+%^i?ge4ek2|q*D0!$upL{ zx?YKqGkjRaGd=8yb?E!uw^r?k_P>1tQ{6cnoIMx=8*?@SN0)4e_%poJfnO6cADsz31#8bi?Q8qBds9D@;)NE-u1V|FmfCIFzGj#*qiK(vW* z$O`xv34V%T&bJOWwJouK3N&Mz2RkvY&AU8ZB4!-( zceJd)pDb2P|_+A6v1 zSgRyCDJ-)3(#X3#f9~hM&b4k8IMCN``0DoM>$SXz%Z z*5P-DSXI3>wVTF6`ZD~N#JCvGM_q!%AJsk~Ppj6|s#y{)(|YX@Bc#k)R~5R(dPs@Z zS1}e$o+f5#dzRhXRt)Le`c*fZ=2cS7aSutLo_AQA_736*&QsyxE`@Y_W?xU>;fqS@ zOCiyqRkANUdFQ*lyWh)KF70R}&ZffIqZcJb?g4!E#=fqQm8f4mf6Ds&Iam8@?RhTG zqHKxF0N#hhn=l#9NnUO8*;7}ux-?3y_$;>boKNW~)9M~i6D1hoxi(&T;U!v4%qKIo zSN8j=XJ|&sg&rWeVsQbE$&$QmOcJEp#RMu@UTae>lG`+O$poWbU6G0yS0x+M=}h** zet*fu2OkEDx>7f96*?3BzQwt=y@#@12oWdXMs|iCHt1 z#ImkDefErx-v5B-PoHt|{DN9n=Cb2A{`{ZgbDw*-eby_dnGfbd?M*W4qtxt>w~cM= zM{~{bzE8^)$ItEp9`1VYvH$69BvKqV;khmE{c%VKH`zls z-+z-har3p^-*)ye4(W)b;y$+j9cNbjxi{PX`6qb&WB|;7e@4Q-5uYO%j@)HbK9lLq z@+SBshyiz_bH_}WEtvQW^lyM7cY!K5C*NH_#ivo4j^BSgdG103KMOg$3EJG8RG+n2 z?qUTUzvt#PkN+RQ*73NwJ99aq_sVzQd7ro6|Cq;*9`NYJ$DBWY8OXf)@;zRC`5QRj z;DZk?`N13S^6vW|@$P%yWh%zJD?EDih3>gQhL zJKy^uAAEF4o-{pk7TqtKQT-ZR0ep3}betX9vukbG?(}8lbaw{HNglMyWyNV?MKm3z zsdsj%$gnFVSg+`s z-VC#jM{D6+!owmLuC6Zd=InMeOI=7gbF$mfTVua0;NhgdE+{e%=X*=sKRcn)>1`pU z&e@~;hoJ*|d zEQydio{hR+;i@y|%rrJujQc>D3+ZIXT3034y)mB_x;OThD}H^&6rMG}y$#|17UrFi^)Ouqs6MoUb2mpXu7jjk|x%+5sn%Fn7kWvJ` zDQB4-0x3CoX3hn#74MCl6N``a(%I$AQai0Kq-iFTsOuiflj!;gxDw+^!CEHEIMnrh zI9RQuBnq*SnexoKj?PxdSKvZihuu7ZA&of3knrS4NQOzN-3GLd$ZghoE!DmsiaQef+$ieAhj?mjvjEt2^L z^tP4CT5pHq1`)J`RBD_ADU#+feq!(pz(LZv18@*55}C0u;03nxfw(_)ax%-bWhG~e z?pKlQd-z2ql-w|wHONp~*j_vrHb#Zhq$jepF^e`*OS9NR1t`%sfm$Rh9;S&A#Med? zkr}4GDbBKDsI|N4x`vNziUi$5HZ{huGrV{N%5^h}Zg=wz@&Ie{08q!d*mt2INgW+Zjx^U#^?-%w*x!M+W~xwgdu1+jP4&g>Z29&CZX(* zC9fQ_$71|DI-@i<0vJVWxCekXK2u_1-9t~>&w5AlyOsMxN#3I8L!$6_swimz?!akvH(Q}r5qOta$Dy^B4AuzE|fM= zO5ytYnrYs}dpax&mrvhd_{R0#X)Xpq$>Cvmt^H8S6R<9nn#yo4V&&$aT$FHjqtSm}+a_3uP)S-2`$?MRRv; zVk9I<6m?!qP?G!vdu6xVQDRRX83?_Q*3Q-vyJ;UYGur-wk_%_M6ZTad15b*)eV$!j zGIi(Sy&cur0OvC8sJ*dVI_5jF!DKVV5@-2DEDzN2GG#IAvI*d*-r?+A@kA#;DB_-s zwk+hb(>hMN{*uB(zdM#P%d)2=WA``;hEZs;Zpf1C35=V#PXjUAelvgtNRtel51;%|jcU|($@5JnrT@@nGPnNLhjJ7NVv{n^Rbx{#p?@XsN)0A04m%G*lyoHuHV#&Uc!${WqDji{l+E!IvY$7G2v(POV zBS|fKElXRiKt17J$+W0<6= zT$WYfLCTSw`IbPG2(H)Oum?j%<046$HaW3q6bwtltbzA1?na7!H<%4 z1x4;-{oIekJyUDd_01FWG}D{>XN=BN;=av7Uewr4Gi|LbZK0G|$&M>=u%w{UY*1$* zaWb-4h6JE@XDSL_dspD=Nr_d;hPSGYIHn*?)xDW)iIVmCYh59dF(Qe!PRz|l>s9n= zSCTL^vMHJ9@S|F1yA3p`)gev#3|GbG{QR6+D_7TxIx!gZ8tcv@NzDqF2*$=VPb~ZE zNc@~9Nm4Iw&Y_T_iRf=Z@^o%Kle z;-Zd?>D>c(LFSsxCEIYY7Jy8*U^Zy}d&p-}xU6mg!N`|=bhq+AJ_L$%56K46@xg;p zs#UHj?fOp5k2qU0Ay4WYsVGQYEGiK_M(_kO?2QyXzTbhdsLhnvd8`k!=%5DCQKU!m zYmcH+vTeVVY_{v%HnB!ZfJ@!7B);BgtGqmNQs=>C6+oPFVlHCj)pfxv zlahSaJUWEQP&m)p|h?vd~*`LR&q(~7sV@i%8a0PDw!S~YjPH#;~w3p z`YKkC6YLkops$)W_8DEy>TAKyzT>j(5bH4t{tGxR1i=3SYn=xMX;t8++;0GAa{(9y4eorfEr9O7~L!$npfP>D` z9|bm!UpIl0+dzfm_uhORgA})Y|83tjKIb+is- zA2f7arrl^=0Sc>Y5LqKI8fGE7oP@$;Z!G*M)ssSob=FOBM^Jx+?Vd=N-vA zeXX>0p%#alzO6OpdFDF7 zvMzLStVt=eXHRXCmejT)Uy3YkV2XJg6zycAHLMcvoU(#!nyOe`)$pY?GKslNEXkrp zaCA%w=gJg%krw)98!ZD}RQg)0zzxNvArne*?VWu>wC9}F(yw>5n@2V6`^UToxS94kug*c@|T@+;JlEdKCSz3UD88I(vRcq!X zc93!~0f4F;bXrp&BUZ&sL>x*OHeVmdyB=mv3DyYuV;=Wp^wqJ}DBaa1X`1x=En196 zU%=5qwIri2mn4UoPG9!{xM=)aRVI)p`KY)%r0@^Iw#3U&fM%vHM`IjEl1J+s+ry%I z3u60_rE}heC}gX&2Nm7-`b-(7t{d3V!h{h7>P)3bU%pjOouX`!dL<*moC~#G&{}6U z=*t2QyEJKnApo(*JU=}@VRw2GMu-ooZN*#S?QQ~RY7^%0C8+#nXWHcrD zTzD77c*vRJJn?P_J{+@G?T}^KtIE8``ZbR zl?kB3d%{t7C{GPXZL9OBB#mS&WJE)m<%s1V8H=bh+FvYyZB@A$vLmZXh+{^LL{gqD zN&>nZXk%VLPa$&|9Z{k>nQW{rkMYxUD`u>{(R+$E%0mnkqmok0X^Z*Ug4q>o93?68 zK8j95fsMJPFfY~l;;wy?Bu@!z!Op=cE#X$M)I-i8Ing$7@oas}z-VaiVW8mG#b9hoW8ZH@o0*ja#4Yyn-~rj4#rXuI!+dQx62fH+Y1u{ zxx!?BGH4giC1H+)E{;#k61$w_n**k|#&*~m-BdJK*1Z}UWHmzw7ZCZ8 z#QCs{LZex9Mhk{T6dJlqYMX*(f{jAsCoK$c#ZW8PA+HuVauMdtt@V&ZMQlER$b9gV zYP|uSoHAFg`7CB3o4oPkx@wK(DA+`9B<~}(YgSH_xypithc$zTH%V^?fJxI!~e(NNWIPIYcW0KA9wbn|tWQsK7OU96a!K+dOuI5+?8E@sH5$&QZS zdowS+?RxI|oV%|3|Hs~+hugMgb%Ee-%(?bC_ultjv;+_l7$_njDmH+KVoIslAShBP zeko>ZB~@msnSN=LmYMdHmQ87Drdrv2X_Gdorl|;Ni4>-Q4V@qcgdv0hK>$V&MECl8 z@7ZgPQGfi#m}{=N=UnH!8}(1s$ATC4?7i2TYc^w!ZjSLAZT~h$+4fssc6&I!w8`aB zMo8S>jfgr87Y=|qeKupP8j$a)`EuR)x~l_b_o`C#EMX-2rZ;DX)fXckiW?Hw84v~w z5)3c|Ve7!@7jYI15`VYf_kSNnx{R*7I8$!B`gccrqY_`Ez8{TocA3#f-`+XfblXGi zv+cXWV58D=b#)Jb;jtCG=ff}JeII@f_pjfFSKYslS3UC-?mhJkuJ1nsc)0HU2MPBd zJitf2{=x2b=d)3XxuLguJ+HV~&kPR*QvuFvaY08QuLL(Dl^*_O@e-`A)D&YRh#K)( z@*-H*4d*99m$Q4&ITt==-6Duo6?vhMb{!SOt)W6$Txn+>EtLv0!?K(l%*)HuI`#!% zNDFg|%UV#p`WHZ@Ygs%`)(Rrb+E#j7mxb)S6!mBjmfFqX@ThHB(|_NWzLvy2A@;}3 z%?%{c>7)KEfn6!oPkC{J$QpY(u!O2Rf~-Xy+eK`PE(=~-BP@_y`8U9L!k;tw zOYv$-Xq-~X?NFfqL$X-*o%|Bj;S(5J$xkckz~l*b4p7I!^4>XTTwgO{toYtoi$ROx z-c{a&na`PoK^dp=1w1`Io$&I@H(1XvV^ufO{ZwJe9{rV5eRqr%@UrI>E5R7y;m(DK zBoa$C(lX3R#R~PZ$*UQ*dV9g%7)1i}>N=q)hm3Nysh*C65HDVhNEj(5P)K&8poH>F zCD_c3Ujg=K_N49NYnxJXCjpgDF{V5g(K_BRmKYvRZL|bkx?x!?Oxq~6?Z%W__TtW4 zE9#XiNE53T6oa2c3J4+M)s6r}`RL?)jbhuQw!&)wa|Z|=pCPN^P7E`l!xLbt40um* z{#p%Y1b_8;uS#pkUUo^<0P&*a=7x~a&=2EB0ag!64oD31$VTp~j7aGqwhF`Gokv?8 zl`CQD4m3CZg+3N_ter?331^&%Az4J5kb4xzp%DKnrKRdbQFfe$5M>mj)CfYGWy7(M z#M5K3LLuiA6;V+&nhIX39iX9~wsfm{rYGWb9O{_3MgFt7T@@xzG2 z6b=zB#hoV#LxE^S283=H@AdY4zh;jD2;3?Nux0NlvM`DXn(9JH_JbBkt-fRM6eIIk z5^hUKaYs^a3prR-N!8_;_`ZGl;X{1P>t2tK|JaYig9pSn%OE&(Sqs)u@Zw7k@zTpr z7Io^>JVj7h>OC){gOG9VcsWND zs>|!(l~7|e>(y0t#EI(^RVNnmRz_er##AY6#~rL{rLh;x^-2w+XR}v?x~RAlxajIB zamkH2AktIf*>HeeovwM07cMaWeL0?FahYwLC;d)sZHRgPt_&kYi3)2809D@jhFRxCF;LakrzPiE#uXMCrEx5YR z3Yz1~u)g#nUVi>VI6r=b7hZaZ7au;x5zPp?A9wry z?YmG`s6fXFVn)VtMNrZg&jl~tynr8h_C+l6Be;LP;Hd}qaD9Ek)%7*5?>~*J>!%>s z_v(92C&B4-1w7RzQU2)lqt}mK|G$0tetz{m0I$OHFT6-6s4L)1wV^^>gyF-w8c&c! zz!aX~KMC-Zf&w}$vO7xXKxIk_3k|WdgBYX#Rv1na2fQ?tiGu=IR)}WkDlFHU1>&L1 zn~_MT!(uMZ5Kb|5XCMGg9{tAZL0ZjJAba`;#PLcXtn^}FH6Jsu&P${&d5V&S-1F0F zF^|-ygCUHba6qNXx+B8C!$`n##$nLL0ZCE^6!tNu7%U>y#81HkOT@uDLUu=lhD5&u z*caipXWu!lh@uogGs_@LI1HFW&9iQg9&y7Z(utCUQ#N5`Yz49_Ln$S9yemXb?l>uE zPpYDFL6!_LIt9#~HZ;Ed6gJU0M(Ifq+X~Vg*f|y_?f#5J^b@}$@N%Sc!DH2EFpC<} z!U#h%ryKYBl%k$y>v$OoUj@?60s@yp6_N#5l)9A~Qh?AJ9Ipd7OE|FToXqafVs$R7 zMj+g{gbCBV%#f2~&qx^cVK{SSr2_SM`k3nxs>Rb;98_E2ZDfe7D8{I;C1C@|4s4uo z*r}p;+Orcf``~-nd@dV;Mm;SFAXc!S3`D{c%(%L$Sf zNTOp{5b3?m0lS4f#MIvE59LP=iS|0O7e@#5y$w2YxQqf3j@kq+GcVd_`f<+MkIyrw zQrkuZE5uko0iq z>JT&3P)LtdFa#|Sr-P|3%Z`px$~;{m(qO#C!FIzqVKhLnR-P>Q8s?LqHf})ERD3y} z6l*Agnwf95UssQ~4?tLR1r!z$O@?}6>(6gQqLI*1!;OoTEmu22{Vl@zt&R_s;ck78 zld)}n;>6dsaBP?>*ZJ- zvdvGkl%$z(;=<~f_k3Y90wa2<3dqFB4OoQK$AWEpE!MX>mAk#4+^CDERSP!$pfIHY zlu|?pah#E|30S4iFk(^r*gBW7^{eh*p+QW`b2s)4jh@Z~L^K23;Md|&OtQ6S=K0zL z_sl8dtcr3z1A4~en@5ne;QCdstIn4sz~h_8Zpgt;5`fs?hT_;3K+42>eLV{AbHhLdLD zJPQB{VCrRa4Ml)t4D%Ow*tyeLfw7dkJ7J=Fkx1<8RvfcbjUE5XAZrbPzW zF`aCVNh0tl1JXbi0EHEC492J=S6Yi1{W^(G5wTRUV%$RqiGw9^eNw<^1tEPgg(tbp z0Mw+)SIH3_M>;bV8cLFCCJ6D9oVLVZ=4qQwZ`arN@k8%^C$4S9GY{?&yDKr2N!)pN zzB%LZ!4`eJi^PJ*Nzuv4(!S$c0MCj z8=#&_(N<-ZghN#w)5~=@EgSAa(KAFc6ymATt=`9W=v4HGJdaR?2&+NDST)g{Kc?st zqjWb6DA!}q>b_ckk2Q>x(@lriqzl{F7to;0h-XT*mldT;F4V zUATw-?^PGY4Pvc0qXzdFdo`>WFW0IR_9y0ZQ<1IW4RU|1(6T~T78h8x;H8I;@%Zr> z3gXMGMNyPs>?vpDdr#qHy6NqqYw7lmElR^(*bQFkC}O3qMq{wuB|u>2uo*>~j8ckb zcJauz9(^7?)3(?4Zxj%56o4@MJND|fooqL!HM+6VC$AA^Z0l!;66A2*8`W}Ybb4%U zirWW{dv`f0ui$zj2TU+dai!68GfJu4v+H!sn1jDgmPBGs04o&NIv38A-Rn9YR& zRnawPpppMiyKn(KyttU+MOE0%iWeWBsrVYfLj$-hl&9@6wJA5ipf(#iWWE8V=hTI4joY)T^gxk(nhFJ8IMw^*=vSW3b(-Mjr!F%f*d-Q@=$*SXWI>?poTS0DhT-6a5&(s#Eyub~mP*wyCJ!eIW zHjFrUiSsvaLtWWnIIyptcp5rf*-3)6DAuw9YIyO*7x3EGy&j+Vx;NAg83~MZAzn34 zil+&HprQ#C0Mx<=5zbYNQJvPpouG{}EI4Q$5u5|Ku^djy#56`Xfpt8Ahg=>})ogmyZsLyeE#OJ-Z zY#_cC8b!4LfO(1w9B5G@G9e5Vk0CNTFjuEfI+K8;0FnblhO;<{J1$s}gzY9Q!W1n! zcHkkyBs38~9fnsbaFC#Y{DM_b5>!}qgqwd87J*6ajI;^SsTyWpjgOT6~*?Cu_ z4N2&k#Ap)Tj06EI^>JR|*lplGFy|%7bp&%Bjm98@)F`-3_i$48w2$C#p$o`PHy%#_i?% zl9~QTvUBVrj}8ki9P_(5WB_q~rJ+bvHQB1}6YuBvt(ga@?VE2i!=_Wg$FuQ6IH!V- z!`lx`H0SX_yL;Orbb0>_sfRu(C8o6@>?dr_+E{1PE&baGYSX>QU$am~FikN|*u((qDpIISxI(Olkzt{fZjOgO1*!L*wcq#N#P;u**RG%Z9+;;m(iu_QWx_{} zjagqu?T%`hx7Uc!!ywx`tKtD?5E4v$&Im?xS3Qs2MvYC;dKYV7LYO;Y=d?8Gk(I5w z0%6ff61l()c>z!2{j4#$=e)7xOSM&-@upM7K;a3v)0^YP_~={-0Ci9`Ke#1E2dc7> zt8jo2B-12Qu|zni0$n}~WEaYQ%4a|Y&l6-sYPwYsQ#C(Sh9aS8ZP6^Tf20D=ZB7H1 z>Dwu_OLuVqwGvGN)yzT$&i;wum<%B|(Hb>m7L3wdH*mpK(-A<$(4A!F9g6W;T`$|y zk57DHsUUC4Npf6}V2D6Oik!Cj!8Sh@LJ^4}>=1}6jHod|UamB-4_qEgFjsewi5Wbw z!dHi5W88XFx7mufy0u&9g*r=&lQ{v>;0v&-aiYPXLCR8xM0$%8FyW%frb8go3w)1= zi5m`#j;O-QPlf}y7GN1~3wrVSg7+Iir`6N~1W`gg(mw^Ow;8~F$xjeg-}90dA76o- zu8>Yk)lVhp!Ra>oOL}r)X5!${G&aH^vQ;bciAV-kUSN*6i6U76{8{p?f>Lf^pY|UE z_dgZ(9e*6f-iMs8Awp~fCZ(twhH%G?ue~Jqr@8@{1QGQqAYIe22gE^{6&yo0(#@L9 z(;ebO*NroSr|1A;H^^B4$&fW7D%>fX=&ZA}z>H4iBnnCD2DL#fAxc>XFUanG!a~}0 z#40MX$@#9|XYR1hWKu8~Qr-?mQWApnf~h>*Q5(=T4H1JydE|;Y;e02t?h;F+ha$Cb zoz2|oo{>zTUSdfB$a2DB#9R<0&S43oyEs@mX*e&7M?2C4XCPF;6=;BKHUuV$$rX|b zuCNlr0Oq0OxxtoN$XAS*0;V+bseic8NI<|X)3w7Cp<<{soJBU@3xLI`%QW~o9Tj1u zhdeQlY^tE{?3G8|xJ*<=iKPe&L!9`VJ3hE?7RSvIOR(f1xuC5t9ton}XAvNlFr|ej z?2^EzyskLJ9cRv?Jdz;i1x0uemv(x@hSNrR@{?^f{6-+vm{PrnXpS#gu^K}8^G zsRpb8SWfJ-iaWHOZ{Jj%!Rc2~_epN(UQA$T^=RA#TRHy;UIJdoM?ycz;!%j6!e!zW zfYw4>jw<{12bltKY;M<+sw1tLt1yUR4ge2W34M?9m^zXh;UV{;FNk&Ky_BtdPf9d1 zqOe0uj+nUa;ihZ^*1{uUd_Ow5RoW16>?LH2AHKoIR~o%e9LH~K8!kW%Hvnw$LtZT? zB_f_ZRLZnEom@-hUQejs)x({77~;{2DWZW4aWbjaR-Mxt`Bh!6jlSL5A?G@023fY> z1MJt#Dkj`6+F_^ahd$;Y#mO;$EaokoC)l)rTJkPv$0d2#aDeasaCW3R2gxI`AgI_g zRhPx4@^T7%o$Fo=eK{OiTZB?^oGD-Ut-sbfnl^pa9F)Fd@0gQ=NVP4Jg&c4AgO4SN zFzOmxB!!dDM#-Ji0=_=pbCz3QOV1ubBg8)@uEpgld%m zI&0fDr^ObcUaYH^z-*iZA(TfB7m;m~UUZ@%?0CL+w8VYw-!l(id+(8h0>IP(G$MlD zfi!FXws+p!&F3T5j6BpA&e5Pd(ogND2&`ZB3(+0Sv4_P#J*>d>EBFQ2C~^!&CIS=*c4_ z01xZk=ys!lxZMqo+MfR=G3I7`KogPCIB4KC4(eECQ~1I%bg3QRXD`(UVZ{j2wF9p; zbehnBal6vLDVKaF?c+>?#(W)RTf0RvHfKiT;kG#OmAVG)7$AUV1JM{{Qyk)d+WjR! z;D`i26l&C*I*oBJ_4!I)K6V;EshLAS#jD>KwzY{X$Mc+ryd>=Sg|`tvxx0PpL}lLp za2$o9Y1Jrf_M8EyWlul3f_QSi%oQey6S25&VHDWe(}QYAmx+fkDIm1LrpbZ~ZGM zAe@hCTtS#B1u)$Ltbn2bmRvllh>n0H?sSNC3H}8toJq6-IT~cgn{vunO>tHlAt&d1 z^ldZxONcw#zXr!ZNRt1(Gt(wxWpQ$`&9QiWp$UcX6iULIgNb z2GP5dfTUF(G2%j5;VET5C}Y%wqF{lz14d!e@S^OMi=cF}xRDzvJ8?>{#>2O$Or%0* zqaeXbX4!IezqQNs9DZvNm%Zj0M`74Y#eZ4NQ?atA61wBLn0mwzxNhz^r@(e+QskYw z(U=6twNlf<2#h3LH<&wfWOGaafUW#5o==hPWX`*}zvDz4)~2aha9ltLI!@!$SPs}7 z6icc}Y~({O!?Mf%C7n=DO5<4452UeA zIK6S$mUsGHlID=c9q6=Cw9-MfPhd6T2-xU&^!LaCF>(s@cG}E&)7rN)r$&3t93$=D zN0o0!S6}kEb?VOS<(B4Vm&ICJr*C(-HO7l0=^Ugj|j%_`L3N~(`JPEUc$iP$fKG)B~ZVSDGx%(2oE z^2lM*FV4tF?fcpGwXZYYk6}dkcX75HdGmrON8@Vyephd3&p1X5p&HFDa7JNXqw!z7 zLu>pz8KZ}vd-C;`nnTVBvk+?0kiP@2I3U)}e{@FLZlEdz!_Y)4aP}9+9(mFsV8W1X z-`8jn+XU^5S`n$`Z+CNFjpfvR+xEd?G&=?nf+3WO`g9G)j5gTgC|4=|cQCLEhVS?E zw7`G2@mi@4INsV-3Ob+h zoVITosiCsA<@)ybw~bWv1gKvD7Vb>5d_Pzewdun@69utIQxAOZJY1$=A<-VY!pq>>=WC>T)9ENnt7yv(b13q6dHLzMiD?0jkT14#SCZV zi9HQiUt>`qEf5VbQaZulifsl96)O@Wt5QP1h%Ta7ka?<^q*g4Flv#$_R##RzPsy=D zJcisOwKmIzP98d=v$&e-cpnC)j?|ND5Hp0v5&#v)I1q@PxTc=E#S};AD;+ zC59z~M!wQfUaLC^Bao6T{#>bc=}El&Y5@wU{3m22Q#v!!6>`#`Z~%q{0iG*J?&yU@ zd1>xN^C-7S8K`?99Ja-21&PT@Nl|1)u?$Js0}%@rOAsvXIA5zQima@LwQ4~|!jip&uh1A32-iE9vv&48XPYFwz5mHu+MU{kUD2$e{8ZjYY1+BAg zGMns9tNgv>yHY#(P^MF?6-|3T>j| zMOOLGKh0R>ucnoEGOp?pjows8E!edcvfWjXfn37)v&)KB{)gPN%A49gZ4Ynjz&#vM zDe!OV0agVJ@7ie$@b6J7S#sb>7c-a--kS=-V+#F}j$q&}*x1+v)F}q1x)Q z+bdo7!s2qbY`Q9YkD$$345hA5&~4~c(&!zi`oUhmY;{SiI@b0`=z5`qch89IXv3Oz z+c~e8kj;&Cjx2PzOWmjKL)Bvqh;&zxP|LQpv1WY)Q50ZoUyU3w?eC-C+P?MQwf7y_ zp(AI_sI7huM)%J8F>+M&V`_E6-kzK}rZ&AXUCiHTUM{K}RX%jN2BT+Z&Z3!f%FpyX zO2*&l&!|T@0urtcK99?Hzmn^TL3jnI#tnkFEeFf-FEngl)KK=;V8{Yn71jXav?(}@ zXo#?{yxX7-rqxRdyo;gx_hsSKp{rG22 zrCEP!8Q;Nt_2Y?(6c?8>_!@rfMvA#2QvtUSr!I4+*=~$LWxD-lZ|69iHzAyl=CEI< zk<-iqaM#9tqrhnd{D9H)W89^jT_`L2^=sE)L>n!bbr{*giLVAizh6S2Z$@$#B{88r zziAVI0@se^viQmyx9@xY+WtG*AN+u-c(saV z?9WZ9s?rK5id2}6aZZM^IzVj#pa!+%*fOpVN^zF}4I@oL1J0NVVQ@f0V+ee~4Wq*% z#PObrFtKg11VzG)&{@1dkfE%)V**XYvmx#e zBZ4#sZUUWc2^o+Kb>xKxro7x-{rt-a6n&GB@iG@XGX|m$HEato}Azy)FfuO zMF&7w`6%!_M5=gBIl}^fWa0N}V2(Wu3lednG@>HtykXXXKvdl5_S6+sXcFYih%K>% zlmlBUBjqqi=x~7>M~v=CJhe1Bc zRkjjSQJ~o!Ra<4I)j;Z4Ji-%-tF+Mxu}l`GaaduQRbxdW$pR5#y%2MO8!kDxs^SXS z;&j}{gbY;&m38O!mC|vz%_8YlOo_K6Oa%%@6Qg0cauC4@zzK@g6j@F@^-h3ct?3R> zI$9D0n)BvFF-BoU1vE{-2`sU^x}mwAS3o#tWH_7~N!;V=#%E3fXsM}@s-$P}C_!!u zkCGFnU8*XI7CL)u;fZ4Qy;}R|JZjn0<*EE{czMgKH-5b7I_D`h<*XMyP zF*1hRwIRyeH=n%g59ob9XI?95qvNAuC{v9`@y*Xf|2qw6+(yyq8{-wKd(ay??RH@1dJl$+CT zaL+5eK7uMpAds9el$MD8QZYBkT8x5TNX=R(z;j~#cMD^&O-tG_RK6$-!!3``>UY4N zU}nsrWvU}1qb2^nJa*)ZjoO}FtufqxchruVIVkB^AoRkizh-UUCJ6ez^+w!?$?p0h z&53&37xcn2yy9pNd4}U?-#ZFv8*Qxq`_USW4E%nhmQv6<{@Uku#<7`);7euby#SB) zM>H{Fpgld0ejknRf;qMfU$ALfxTN`hzWb}+Up+n63Gp=_^>b6!8Y6K)0HZP8rj$(u ziNE4VfveNw*g^x>Pcz0rRs)WTe`xZFqm-y{As6ZkX5r=OFNpzg2Pp6Kt{ zeQ&XwBWk8e4T~dapjM+2;VE>$=)!26dZ0lyw!>IrNowDN4!#G3Gr{8*l}tfhio9#3Mvf5-ba&I?AeyJdr|hW;epK(csp^N>3FB zrf|%b5+pJIgjhHRm6PMVgu+6=%ffR;pBuvBB4q#xYQU9;q8H0pL8n5JNEst$3=wA3 zF?|SFtVRdhz2v1UEg|KE)l(2)z{%4-3<6}2jFAK@OfSkgRb||Af(mdQSVT!+mSKgd zaHPKlFb6re1k0pv6;IKy6P6@kw805pK!!lxMlefT95YE6^-(0jvLLI2%YtYuqMnwS z;Go?G$y!r##PM+=N-X$a!WCASV;UG8d}>HZYuLYe8W52zvPTATz6?y4S{ZzTlSY&( z_Y4idf&~h}31`c6K#AaLxnk8cV=A8}xWmgMdwfo%I5>P}-vZcKf_I64kvd!>H3f=# zv?=j%pym-mtQ2NR{qP~xs6&8hc3$+Up7=-goV663&*yqD2w}ugq|M%@3--Kr#9v$E z|1QQ++|x!u_qxKU{Ug4A(O*xsf^k2@ZzJ7vCx5WX%3hY;rc<=?7TvuwJ*{uE*BM5K zcr$s~8??aS5Ch{i9N8Vco-oqgs2qu)_qM)$UM z+P-f#q=}Ip*}i$CB93(Cqdv~`$-CGKqxJ3k&_C1LQkQ`O99d6yqjqNN(Ch7eUvIlE z=6s-MkKQM;Gb8FVGu?Q!PW@gunv+p~+wTLnD|saI$ey_y5A*|t_I-_R8@7w9t=0A$ zY=>8LJy{A;@J*p23S-wx@>3hnW$tR^NQc%nVA7JBh>gj z3Wpjwc18?&#-Lk5A(&dac;?V(Yv$mt2ItXcI=XMR;YQE4-|vVrJm@@(8}?%-H2ogK z`sI9?Teva*TGw*do{*=tSTb#U zwF=opko0pLe~+BKw<)l<36-|p*>`>Wx9x|Eb@h3ZZcK6Fs?Hza&z6cy5}?LH$Gyt} z5mZ%P1a45HfZiK+ZLMU`O!w{9+fGc;fsvGKyBotxf&dH-}Lh-7gw@6HP z5+GwC_Kz5l>0k;ofk3DWG$|tOAv;coh#?CrkF6%2&P6AQf?%*&2;95c+Ps(1QCS3M zPeKb!pkx3RpeR5b80UkM=|lp;tKjp)G@-y) zw)*#uWWtf2+&??giFcRCmNwhzR$u7*exV~y=CW<49Ye3j_gBAW z{ef=#++E~h|DL0DJNn%BdnW5gW9ZwO>4%|L9Yy>1`ea)g-XJfbk1AeVGEvm<76(9VRwW%OpYfK_qh1`oY$oHhs7)G~as zGSt?6aD^w83iAjS0xf_8h5_!ePul|a3w9Tx>Y0Ch*FQHRpw?I%ZJhad$3!}c66v2| z6^Kjb)(dD=^?YV$C;{0&>C0%GBLX}c=SV=7Qfi-i1~6k}l?mR$PI{jarB)2LTDM# zi@Dj}88NqAU-wgV!Q^&cCgem|nAQ6DbG38~6!Pa{WT+FdolJO1ZV2%Gma`52Cv zx{7(E{B{C;bdC1x?)QUtj;@gc9{)-xHQjz5@rGHx?(T13J7sqsLIXKo!PE8lq7-0} z1!qyL#g!~GgmaEdz!O*zU)zXv2)~#sF~mZt9blA921HrCr-B6fJ0uM2l-;ocK#8TX zq>Q2h#fl_`6QZ(FBHqWMTR)K@D!5WWvv?W~9S#dJk`yrEgY#0H-jl%{28Jl#DWF3@ zKttOgj=Q2B9^~M=QO?{kAXGWu$&We!rdXLqH7w3oPRjTR6V55vrX|XlY)s8jO-KQB zzPPg)Oo1u>y`kU+MqCjyPbI;2a_+#kf109wdnQbJNjwTL_8veTy*JI9h3DaF>*l{BvLQn4IJ zhi7gEg3}5B@n{cskccu(?S`i zlALZN1GSbI*MZ^O4yG)w|8cQk;=ID2NkR~0u|j8*I|qZysJap?V5*|gVOsgVFd9j< zaKGmSEunup;pFVALKK{JEp>lxVpI#Kvvy|BjrKIZ3qiBD0owldV?TDH z#AlZaKKz-ke>o3KW$vc!*7a-q!2dckNg_QDTR(-M@BsX9<|DtJJOv2=T*w!rfDY4GI3WTEO|}rNo>+QA^oV(163C@PVsvl(-hO|?RObYF*+zR`j6Mvp{@$UG zYOL51)2SQJ6_c4Dn)UB!d@VM~NVr}IzhE3{b)DP3w{yK?bU>p?qn-?n-VMyd@kUC? zT{dq&ukClEYxsU=^1UGJ=X|DI%oMr4f9<^^8oh0Q+{S#vc8N6@wKr1g+CJCckH*;T zf$H?}fBF*avv>9P7_6y;^8{D4W#*+ZQMvK1FBabRV?~8SB2z~*h9hOL|J=`;hiOc& z8Lyl1nqV=#qWVhtZBM7|Jt>^ee*Vrwcdyoxk~>67#a2oiBoy5w2Bad6^C9kh@%LzA zbcC!fcTz1aFHyZ?$&k}EOW3Evvc8fX(}d9|W-DNfq_C9m@Udcjcm>!!01F@r`852%}AXt5xOSSaa`z#$V-Mfy(t(~ zQ^c^QjHR%e+^W=v2(TRQ$qp@=M=aDTPy; zu8#!4*_{iL3ygKC5e`4TL%ot8b+lj#8daqr4peN6tO>^@+!5t~#t_c2xe~j?J&9$! zBN{6?UeTr=L>x=MrWN^%OetlON5cSs zl@+!SldveGx#}~<^?>cu(6=a?OJ~MNKkBcZ-|l{EjFSHM7SCm*uk>xi_jfToj(+RG z@NaAHF7Gk3;FOT=zP)@3xf0{NJZ$Ur&{=<4`PAySBiTFEX_|K*8K|Q%?__Q_PcxZ) zWQQE-tE0IY-8Y(>nGG@eyMM=#K6}*n(eJZ&UEmvb_o=GyFY>lzmxoNFamVM`+TNvq z{nfX7+x+t()IB%T$!0V-v!mnLmiA|RXmqvTZ}!hv*K6DDqri`NV*rQ?;u*M}J$G=DXk zA05yGk;Of6#rL-vGA*&y#W1UzadfKNw>J`om-8^++`In3sGx*G(~LLzTc7vHm)ise z0G=r3x#4ugBuC>rV(ufM)<#VnF`B;raZlv(U$i@6K5aWA=F>kndVk9#+J3ir)fZ#v z)@?SnU0_x~YLlR;x>7WQOcOF8x z;UCdOUP`(eI521du2RMV;KmGCZ(w!@2E z^b7p^eIIxp@BFTB$D{B17Cd_4ZFuUzW1LQgVob%+Od~p#wk$o`)UFPeCz{yjl{YIu-$ndD&z`#RXBJ zL7p5tWijzI5U=wh0=QE{3zgd?Es&F-tT!G>aEcixEqU8a%K?&0`7 z20%l{Aw5{mFpmT>=W$VhEZ4Eqsgx`+T_|Ud-Z2O=k`{<$q?`#}46tgLc*+ho+ENwA z?gL;Gho6c)ra(xy`@^#*q{wy|SP0KK-Yqo!j0y z`mMDmMs`O(_P#&056+M3vjaUS{vJiX9QCpNem3``c^v6W@mrGQDU-*vGt#~K{XOCp zM|@`Y9@opI(=RXkLMdVP%A)gUbFkglw1I8^kNVxW)zil8x4uuiwM-rz-08o2`rG7! zcuI6*89mYU5k9%!Pn!wF-Dm6#q4;gq z*3m}0%f{NxQFzZV-U$6h=?UQ2nDY4^ynnY@yZ2wVGaiRwJQtf{G2a-Y_w4%T3faXw z&4y7heSb%IA)~b&jk90p*|YqDGn~5bqY7A@O!sZqEHcB607ooIJq95#s7Z#UF!Jz+E$SKwiMi_mmcw5#}tIX`CriyRuY% z<+}jp5loBY0j;_j_C*}1*H~(NK?3ZILP~_0JHR{wO;zcX6aaw_wza*`v7#@wKQcVh_4{>d=XW!^UGDl=MzlH7 z<&NGR`p@j1QF|jBWP5;otA5|D>xJ!dhdn*hi;k|{{7w41^|5JJq^7yY^X+wH+w^oi zqS3aU-d@`DDyehW-rK)_H0IG>0>)4smu)qUG1pd7*XBsiY<1|~7916BY2VrIubA~J zmxI_7Ra4n=j5j{1z>=3!-~8~Un+dgBlqbZ1+1fH!7{pedF!(8&S)l-QKwdt_$NFk8 zIp>`drd_O&XS!$HqsBzGxb1KK)jx9^09eIFiFKU{>+QRlBW2$(Dnp+U zr- zMh0(x|Blhc=Z=OfZ3dSA|K?Ms1FM+bMGnVu^$K+$q{3bY?Y}R7ZTV{VydNL|RV@0^b5m z2~lO5iU?MhCt?CgQ5xDH;xtrJU0ad`jo+jOM#*?--~?D0dU*lz6`(HwG?5|3h>~J7 zJYRVEWt^9{;8*{FKZ8H`TRsyXT7j?rU%nFWe%o7-)`xidHLt~u6lmrI{X4M`>F&H$_>|WJa z9Z3uY1pzphPyoxwNLbYqNRr1NiNDtp)B@yGVM=@(^+TBqI-ZIWXNP(Yok%pygEtA( z&L~1)*cE3$7T8)?ayw@fD6ABqDDw3ckktXb6H8*7da6=HNpXB-y@BKt=e?Lm7a5%$ zmF@sm%~7mkIGI3j24J4fwL1S66eK{1&O*jW%ojz1$^uIXW@pNYC<`s>@IIa<#+)b^ z3?+FK2$F+Pubv*}jzeYYoqBn6w#WI6)FM#~Dn8H3J|zguSVlTnhzU@X4j`teG0jkom7L5cG6|h} znn3=e;(X92+ThqTY`p3rTEJ>rE)&F4w@d&@P%j;SauKthSLcJ9gcAoe4siKC61nBw z!)VpSNGNk)@%~i#!l*EF+#~YAC|0pO07mdNhYD>UdilANL(zWw?ig*{H*om0<87~z z0X4dBWH25%64Yfu=v&)!z7}qn)heMLJ~%ZNU02$)%l-S>fBW?u;SL_j>i*u*cjiW2 zt4p`~+YBgowibQgcJCLd?;4G}U61yCyE)|4Z8{t_y>E22y84m*GMgg<>Oq;z!Ty zp6?jbY=gD!_USD<1`G^|svTP&e%hk7&zRY$e|4PHk1rWOclFQGaY43gcIl}Ora4^` z*{{K9UfcJINX3L1-PhjV_VtMAkLD!CHfyJjtrtdZA1s917A8AVhPDU%y{;I9V%+w3 zmKs<8ZiK5C>M8PQ{B3{QF~>MZ_l$&dT&)rs!x6~k#58TyQ*LP;EMvtdk*VzV58@q7 z8zW}g(q1TNkj*(L;*K<*bE|D*+W*;m+VQs>I;M*Bz88W=4#bh-(>^;>kVe8ip6ls- zSFI|li40EF`gW_)Vc4CJwBv~9`hJHpB%mP@E2+ka$YL(=+&C}(F}XnpaiM4qMw;BP zGR2awVE{_G0ZS9Nv=~kibKr*rlocfr=i%BNP)UHI1&{3+e9`atWBB90r>uDS;WhM>uwH#E zvYzqa@q1yiKu@nizV|BF&4;nr%Q&lp`@%fRZ{hfKg`@>Hvf$6<1qr7rM@{MAqpM``IH*En6D-5LrBAsz zACbtO!lVMnlOeWDDgto_h`?aU)lVVzN($PvkOo$ zh}tnRXxb_x3HG{R?1PgCiaTC%N_3!=Ri`uDcZj2A?|dfKV&R~sU?6!?e6xD%=um0&Ruw7`O0U{OxlW`xX?C7wcEGSU8TrK6+*M z&IBswf?9h0g6XHreR=V@nZ6y3pu75%q``B&z}b|Iju-g<2ENIzWj9b-KDPULK(n)} zpSRsJ-hRI?X2S4@Pxb5Aw>!G0pR7&*X51V+b*7Gz~QNbx*}mtK4cKk;+E0Ds`W{Q3BXw?B{H z_-lV1{?*rh6|UqS?mhJyEcc(ra{Vg2^r1)itN-;M!|(pjeiwe;M*;uAZ~YH|H~bX5 zaI@0BOAay$p3Fvco0GTyM~oPQr`(u04h7S%B+>X@0j6;zR)I-rp|i;pC|hKgy0b!1 zkTi^Zj#a@@z~^KY)K|`#Hd^c+rsHv?3@v$DQAvH&RwGFX;7X+pN8I#eQpCqVzq zX`F{~2G_E@Dv+<2r+zDxsb=u^05B7eQgX+LFinT)J_*VDL0X^_uBbsR%1RfHJOCht z0L)_UzC%46SRwR+7%O5e0#A+Nej!GZ>np|xE=k^s0%i%4NHbN%P#~h2)FPIYPO0q9 zL{1N@FPSJzS9nYr%fPE57xIQ;z~fTfDeH1A$quWq<#nTLs5`peR3nJ}_o+Sz7aP@} z(%t<6VdO_E+Xi~o$dHldC9R&=>jAy|Z@)Xz7iW5FJ1=P<-)DyINcS+a%ZTEM*xWtq z8p&-kwjIMvmm6K9F|=*;|BmGJtSw-EST?%1xp8RBAN6n8Omo0C(pP5sV@heL_w2hk zI8^eerYN*CvUNr}OMSO&`@$NXcy@n3OLwu=`m5bXvu8)+4*Q0-)A9U_`cc~zxE`?G z(+2=Z$m5Pz%=+8U^-MP&^>?HX_Vd&8p#J^Q|ENGmT^H2>GqT@jpGR}o-^=gg0E<_W zHoK;M27s~XgYeZ3mG=A2(_49{f80P0$FoMk3JMt{$|qitJUQ`E5BsRA-!H9jmX2Pm}*In8P8k;Ffaj9cY>=-S4PGArsPOx1J=4Cr{gpIT* z7pf3chn0--p?3^2B0X&XQ+%G3ckiDEJOtLf-=NXksK1r@MyySxhZxOh-zIOP8N!S{ zM}2Ca(VD^`mFM(ycbj4`?a$`H4G>f{4Jq4mK*Nie!ot z>%N0kR>3fU+wW5?WT+av#6^*ZfK>0KWVW{WrMz z?r+0uU;EKOW~H6QfRq*T)HBe0jeqdh{}O)A|KaoT>%Qa{;wwJ)^YQ+_`OSFsi4GmJ z1*;W^r|*~nP#UHQMu?j*USUY$2Cx<4-GBzCc`8*rOUe>tW2GFXClywxk*(^DGj~Q8 zK%qh~;zfjTCLpk{sGtbgAE9y~;OFEK3+9G7lF{h1#Z#3OnE@ic4>D8~jE6~BOMwVe zyrMFZI4%gh@8L8c^e+38MHr4B|2rO+_hWtIrz5@PQ*ipIx8TKhy$w&l=@aqd3y*bMECUc$AhHQ_n0xlOU-=lGIYuDl8Elds@`@E*6-1S#bbJ z3B?3!q|zBnP%~1JpwQ|VNh0c1k_6`af)Op*>D+;eMFCp?<4LDFpFXcEabPMVGz891 zt6Pk&kuKW*)`Hv5^uO)iEwW>zkHuxGuv!M4S1Dz8z*J?pxbucT@a71RFn9gPW&e9) zY{ct(eRMR&(cc%w_dr)0wR2%a4`ip+K%38(G_@a=yb_mJsh{WIVI51Q?KvzpL-ga&3&); zAMO9qJ9>NWa4wpAM_4P;@k`QG*K4r?%@k(o_()ZUR^*xz?Fk990g%l(v%p6&ZQ z>-Wqa^>4rk)Xf3IHE222&Ibc*vp`cgy^v7w0ciox|P*0sb zF>mK=J7RdVP1^o_q!)%*Pr+cvFz!VcJsf3nxwOBQ`~>9f!EGd6#iU_&A?yJn`K% z6x6W=db$}X5^Xyp;nOPwb>5oyUhoB6(m)+ooJKnMNf1#X0)Z5^yZU{i3bO(@UJN{{f;b|c!lcIEPOQM9rmUVLbo`vx z8@%zyeF}cwFZkK`y6-;Yo&V(PasA+FU`g(XSYfvDxOBS0!)HH?ulxI7i7)z+Ux?56 z^iRcC|IPbQybQIIpNJ^&KGbnqqGX{0SrU>MR!i=9D8xg_bnF2OGsIu%4jpK*lUIimV`b#P_8CAe8e(wYJ7jFTBF>=x{D*y6ykrR3~0d` zcrd25q6uY>q9m?q^7c$vc3NsAPJ+il!6ZW*uo#{ywIx9%Jbi6Aou9?J7Kkmt`aB+_ zm+}0wAHZpSjL-SGpMV#>=|i~x(#Jqv|3os1TuRI+_SRyQfg;I*?CC`TWW6xCt#T_Es}Gqe*B=?;4x22373$!? z#qoXw;vqDod4XAipy5_WQ0x^ZAgyhbp)hky?a%Z9f zMGDjkvLu`#Sj}l#;1N<8YAgA6G8A{PCUN~JajzFMcjnMBRE*d?OYkn?J`0fL;xaD< zSi7^ds9FX9IVZ1jLea4eN?UGV*K4WM)%g=oB7}FkvfOHxIQunM>_pXx0>o^oji|! zN9*5Tqq&?J!bknttu@Buo$08z8R@gDpS!#2!0o-2Zm_9Is^Pr9)MfS^haW_Z@jd%} zF)$!jm4-G07 zq`S`MX7rAJo_k&wQGqkag}YosJ6k+x!6ztu8Ce9D#Ldc7^&^RPM5>PWq}LNYt*R@~ zdVzcp9lL8c694tE^2UHWa(v7d>ge}=Gq-&iJwI}e%&yk(Y@cuco;njcW49ke+xI$0 z_4~9V;tRvo%x$qtKqfPu;(N+WE@0)TvVw#l>x;@y2v2KC{HKj0SNc_Ll?F#_A)4|$z=|1=Uz<5u>g?r2A z+P+`RL-XDlADhuf|64mhZJP=EzO;Sl+Z+iyb4zB%UuS!`S0-nIbhj@cfGLg0L=aUI zVM$-dWT2CV##PSlB#LMb5l>U0sY(n8=UK@C8eJJF2wq%(0?7FcRipDDDI^QD03ZGO z*WpcXIN@LXoA&@OJcsM6jAFoQg;6NTScMK1Nh_9n_we3#{~%s`1ia==9|b&k3THc` zz)-|s#)JyXsmeryV{HJC0xayguPK~=QNT0xLLB5Z(OCtipeSg3t{^5!LPA!*4@_}_ zJ8mEu37Msq9njQ$GO)y0qt)17(}W}$NivFqlV(d>xoR`V3LzfYQo#QJ0;USw>E$J> z6)gX3DG=2uAI#`r17OGyOYUIsXboY=u>d&309|pF40x1qewd+lLRudqVZrr1!8_mk zL-^-^@sH!Dzv_&S|Jc{#lYjg(c+Gpi4nOce{Z72`-Xr{*zxMm_@y~t}KJL?hE?)PV zPrz&5_AU6-*Sw66e)C7+-kR|xzxtQqt>5;y@M^q-&-tBQK`pUn6AO7k;hu`>Hek;E0xBp)JZ~x0*!`J_XKZ1YvH~klQ%YX7`@OS>s-@!ln zfBa|oNpE}#U-V_a8E^R|zZReLN4^60f5y+pU-?7-3EuhDe-2;t%YP|8_t*b+Joo4k z%rRXQ1vh2IS&IDSJK08iFA{lB6_mCPRv%hB$N%@c=H~|oe6YzwhWm_!;L4M5~9N4fLyb@J{A+b~NG$6P`N5K>% z1lE(mz;fh83=n1dnuteunNNd(DMXn#Fw)=$D#snsKa3KKs4yzX@qImjf^nZ1kW`o! zsp_TB)s49*Cb2)T;`3lsSr|ZJ=-U=CGrNv-hRR$0An7j8x7UpCx9`&6#o@0X7%?*g zbo9BG=RHF_ZOoy?UK^)XrU;9sMNP-Y*q7N&B*o#RL}q?NN}wyi~^Yl$aJBww-{1Jo-g6lGcx z=Ms-tcj_dh|p1(4%MZH~;qkfOmZRH{kVed;^~Q z;D_VL_kP<~Lhj$kzx>AU#CyK`Tk*wT{CRjOzY5>_4e!Rkc>53GEw6et zUU%~jJpB#72M^x;&G^^vy~aQL+HZy2ybquHmMell!*DV{&Wf_eNgmfaQh2ivB>H4R zydnbtMv=KGi6o40DhVha?N!XMngO!FWFh@|b)zWxOza+l?y9HQM4g~Dse5rUR0JjP zgrxZX22={2aGr7|R*({~EWWQq+zGeBDwJ^Gw~z;%z;`C2K0wDL?^FXIEZ;4r1peJI zDLO|~0uPPRz6jhU4S7nZQ$oQaq*d<;{8iW-pNtsOQYt=E3Xv=y8U?iHE-LV^>3yt9O)~4JAKFZ7@zY`@Vv`nkKf&wM=Ck8P%Z^>f$W z+ph-?$#F4veSb#t){pmSeC_u$J8(81?dPdoa)d#8fZ$|1ofei&pm8DfH;#Ef*=_pKMbihHHNL;ii0c~MJvSqu zena=fKkMU2NKY5KV_dB>YP3OTLSrE<> zf#?RB4BpvZ{harV@V3p_-V@LFOf7yJ^}jWGM^Qihv%9hSUWq1J$5_u)XZT2m?PE_D z7wa&fqocWQf3JP@%{-*ZMtJN5hd6K0F*9$U>G#~OFPnoMN;cL7AKrh)a2%u%~DMaYk%9HI>Jxn0W)G=pft{O7{gwaj+k1C6<`GyGnf`u5AsSsk_<@$ z$AL~3Isr%*h8sqr^0|&gSdu8C9b_Q@ zEm(G2Vb#eaX2_D<0pjAyF*@J?!;*YvQ~+^&B?)?ttx#&jK}qK2hy_c|z{;FvUEJ)Y zho?GwGD}0Dc#234Tn-e7W51jM2SHXW9x0;`6r)2$lHi04H5%{mbr(o2yKKhjhb(jf zFyfP?!ILSj?%l_`|MlDP9e?Y;#+Uw@UysMv&)~cM!T*2{zxAu}@@M~Syzd?V0`6quAKl2xT8Q%8v319m^{t5i7FZ@sO9rxdiZ~MwWjfda$4{`sw z?+4a`ulb7q7uNHO_}Rbax8YO&-QNy9zl`tr^S>9L_U32sNx%KS!mFRj_?Q3SFXOxa z!QaIBxewvq>wgTs;5YmceEiS)#dzsG@5DF!g+Gj^t|L7xB1m%uATFs?a9#QaD6>~D zt*%D*K~7B1iAbXx6Br6|5IWEoUm%as)nnjag&dt)ARQwurA!wYn0wlRF$0)wqV}2ozLwzoXJ*1H z{_gvAw9$_Q=7{l(t`URm-!CGy6fY18@G&#pgqLgbx7;5MTLP7~avA2yTdW$!+5y2` zC<0**-l8<@jL-oyy317=!UQ01D2TkA<__wlGoD8ZQj9&6ox9TLtl$3HSk#(VKN4cR zmsXHKc75;rd?@UIZ5@3sdgsKZSa0psyjk1aO{4MMM(LF(@n@zT)) zA=>YWe&4kU!2X>)L64l}^UV!Q9%rcz6hVgQ3K&q76;F~o*ECM9Q3&ZQG&mKCl8qW8k%)gwOlpe-GdP67X&R`0pVv1RD+K38WwqSOZY46|w*j+rn6a7nT@( zAYMPvd>zE+91&%uNSW9ltddI$Iw5TioSH0UyGl@MfD}?^5yOotfsP$-L>xg~k`P-5 zf^2XeM_!!N>U^1@rlByVfU7`w9(RNWmk`G{@%Kx12ak6{Yk1dp{&SRK_`&b}Zk(0{U;W>GIbM4CMZEv*-;Sq0`YZ6_%a8E>M=!!o zC!E(6Ccszyss94&%P-*@|IVMm`TR1T%2#;snm6D_KKvmdt@yxOzX{KN_&I$2yT2PH zUE%va^nSemd%hEoP4M&w|1G}uFZ^M=d}Fx(^sAB0ptz5BedAZ*(ZBsJJbLj({NOvk z8&ALHwUEE~8?ZkA5`OUe-ifQ_8V_FoaS%~_@7MfEeBe9&XT10Oe*hnP?|X3n!K+cU zG@dn&WD0&@8$Z`X&|(v1Ir6$6Ilqd8EWpWAWJ+1dV}&|b_UIOXkulj*(bU!&Nyg|J ztIkW&>}zfuub>uJaF&T|xjLT-ZtGDqNbXA&kM2zFM2g&bWp$@2>2XFsNPUZR~IBPyf?(q7*m^>Jo9-L~$td-wC&&4C{gjDi7f;~jDTZSWoa_wCsF z^4hIK)uPfK0EEsda>+*;JHwj8^e4TZI07b^ysK-Vx>>4WnL)JfdMgU2voZm zB(SfF)Dl9wV6zQ;v>|T$t|zv>&6!X*V$dVuHeyOM20rTRFz+^U=9+%B*XUGXv_2!j zQO6w{Nj>_Y1Hh_%6bF1vLfn8sn_+N?$j(sjH|2~mkLIzxW@{9*bsOqDfD7k^n#F;?2~fwSJoQJ%V`lW+3ZEmM)ZY6_ zs#DyyUhQ}Ny)&f@4G%Z)ggPMsmWb0r)KzGqahXIRj)9_HA&U+bMPm2_G74A;W}zV@ zlrc#K$nM~^L@-=&91;UDTmV=xKolu05F~unU;m5v<^SH7;P?LSUxnZCo!^D$-u4x^ z_tXnG<;)FVfEOOE!18hU!r%6P#~1(nH{cKdKfewi`qr<(Ywthfr}I8O6PRu(9ttW) z8A2daBnJ{E5UL+7(3#}2lp8ubm~Md-K}13n0ZtCi3oC9=(;?#pw&*}nROgSHI=vc7 zVl^)*R*hIiEIkKXl8EONs0 zSed(>GURj*&p&*O``7pI+y_34EWl&A!3RI^9I_bB%Ly;4;k0Bt`~GLKw-OC}AS@`4vX{4BobhrbaS3r^P$@Y3@y;d{R2Td+vR)zvj56PZZO z@I&AG1Ni>8{~M&`gsTS+u&xFHBTE=5Mc0Q4agbP+nKX0;026njDY9gbU|AuiC?>G% zYh?*c>6zJIc`t_MCty58NseIz^EzG$k_x-5uCDN1%U_Y&>^j8?0Ydt zUQmjIE(=RWn|kU*b`4I_CLck3n^R_lRq24&1#HU_BY;v$kTZBPY>HA0C!yoYlFMG> zh+_}t96XiZh_|m&%@POgN5wfZRpj7R9yJr7NiC0S4+AS-vrBzuWB{0%SBB`|skJQkZfo2Q*sk5PsOr zb17vw{wJZUSwBZ~-#V~FWH(`E(PucenYmt1M-BVgYPe5!J$VXJjDOdIO$iVLD*R>u zwsTgE3BZ+$MhK;0Qvd??_y zFF52R+t^JQDvp`6qrG%LmVWI<`*pV#nD42U!AXkeC&I%}oy@&*(f->D&YFJJ^tu1L zS7v8QLVvYmzD)sY+Z*j0^5mv(2!LGz;Hna%A{iqhiYZc3q)b3kh_du?jTVUZ18>C* zdl*DYjZDhPfV472DbX>3vI5SlvH(OSDY$?CX}tBHelz~qfBAdx<$wB*;pPAN%kd}v zz)!&Q-}Ddh=-Gb@=nZiF0Qi)jh+q2~|1bDGzu{ByRbT%u{H;IvyKvuDAYDVnumDJA z5Cv8U&R&UVp@|1eO!rx_LKhNz(5opcAsGs^012$@BF4%_2J{un!C#c`E>2kR_xQwd4*)) z^wfO_7C;FAz1)95XIRb+?HiV>E1a&bwlt$~5GXv)Tq|~tsgFGpkfOR2se~u9YINeI zjA90j11op3B@gK%eo+83o5w~8#8`sd)UaykW1h-qu2eeBA@RNL4uqJXmZCP&N0J1| z31%y@Bvy;ig0;|L6qU)A1n3Gn1(P606$_U|D?Ft~m=*{tD9$Ug2vh}UO9UnM($P#} z5>965kjf~ntjY>xNx0XO)3GIZKBKQ4_f7(xSvQYYlyyaxdw^_dM@W~vbL0fSi!l)L zL2Bd|c6v~II@`XlzBA;p(7_fUy1l_oXR~Yv6wUi@)1yUVzhCnD_H)cZ@8BOf{rmap z+v|1CU0*smcBFT;-;KU+`#rjDGur$2-{rafzV_kH$Z&xWxs88GP;j~A!r^3Dp zZHbY+5g@?hs@rgyZo57r3>a$+Z!t&7cO`^_NQl!F5kK{cXDxEiFDz&@>P2OoBcmYx zJ|du=iTBR~mtOE!avd3(@!ipeY5O}H$Ea^d_toEQZxpsyA5kvKhEH1|h_>vVlBPP$t2^WgsGef_)d!f5;N zhJB5-5y5B%W(pZZnpaQ(tn@9|DR`qj2e^~LCXCN<1jy{}pu{4E(eN3MygEuh+U0I{ zPOrN#^4ey%E*I-#R_EBvJip{}ZQIh_{T}UkR^Kmw@S?Ay{X3$gnUXqv*Dcpc$njrjb} zegl5yj|2YR-~T53(ck&Iu>SBn@zjH-VKAIT>%LG)H1;80$pi#)Q}I+y)D&Ga5MAFR zRv>~UpSC@g1W6NE-kA|9$*V{$5j$h3dr%Uv#st9~JM>&ZBtzVo7vtv&EG-S~8UPhW z4~5)zU^?@DCjnMnA(8?IYx9)IF-((IHiQAudi@( zKI800c=q;+JA4EnSg|NmUkq5%;!#s80fWUsi2 zVPziknoR;2=TOL7ON2%|m31ZFFuzLEt_8|V!*Fk)%(YE;~Jp7xZ1qJjV#T#z7A zygVl(!)TD?idzFQF!;pk_9~^gqjlW9-~TW0L`K)>x%mEQ?Pp`RV7%M$=k)%b=0?|uCXd#s*WnKJ#Ad8Bo1)hj zds-azueW!?wou>#n&4>uXLECe`P9?bY_4YWGMeZ3oI1Ulzr*!&+l;yhIxs`pyt=m= zFoWv}9WaspU=*hAaDk$R6hkTGc{*)CX`&3;P$i?FP=q&eZ=}u^YQ(r(1|6Sg#CaQY z8MS>|{qYeKo)L53KH$noEVgc8TC*|q?;54+jQTy>6r*t;eP72Ln-G`aRQ2xz**4o~ zN6Ld4l`FpAjl8~ZUJe$@>JKwxaY|!w&`7;BT1S2I9~|$*>Iq_Rgdbfa#cp;_d&N5U zecIh?Jk^cuI%}_ju?lv1q}5@7OGWZxKZzU0M#Z!Jy{(Xj**y0BvpS=jf}`f8N`<^9 zXkj3rqRih#yWJROe4#psntcV?+(vc3d6{J{`lTYaQwAsFp9$BU;M;72Vc$n|K2mCD z^F9-Rqx;%6`hV5DvXC38tLGMlv36!&5uFXiKr>Nbmb|nwYMKmCkW+S}Hclm&_^RR5 zNCL5pYpl4b{ih5Hheh_2?P7|&EI5}F{@Q>4-{9?U{SN$!Fa1*diqHD_xc-b!L^=WM zi-sTg?(e|w{*(VJzV7e-uXy;}2XOzXXE^<;k<9_q#MAq%I6(j;tO7TNxmF?qEAX@r zf~f)o-eqb6X-Yj!Gr?Fg+XTg{B~^t_2u>Q2F|6X^=|l!bAAk`YG^(Vg{RG;ERiI#Z zAQ?RJp&*NFC6RsJq8zviLuCZw=^O=W0y=sCm?A4tdrH;{yh?q*=uk*pZ%Z6Q<_5*h z!y6O@9aqK_D6`@Kv_N(~u$qIesxW=TR$PIRR0Y&cR6Qyv0cSHTpd+XV4fSMn)6hv) zP(12mL2^f*`uq|d;0&K9td8Zg&;g)f^n2PCSmGHe3jTlB^II z`=G(*14j;<|>r9Lx~O=u2MnNe+u~&fZ&!4@cZa&-0N;5p4#%dUgr{# z8eT7<7Z_@Ef1V$8)3wF704LZ%xLZBxtF1@#K7I@j<-BFde3c!@kmGPpBuGvWE}T8;LIKf{Xede zt!ANdn2)CO(m;D7Itd7Rt z2#aObrx~Lj5%K7~GlAJN!Wqa?FYJ!m=-649Wl&MZl+AoHb>d; z_rcU1(eEoUlKc1d%&t`iw>3@|bJZe~TNnbNSe1<)bwUfnK1h6a<8mMz{H)h+yoG^^ zK&pdxixObRJS5xsOiu?Rg#{Qyz9n(g^Zgo(sx~!4ycqY4#&>I(WH;s+&uH7;?O7l! z@97>r`fkQ6`}el@wX`%7;J{vSN&ufsq8rf|RJOE6+a#cZ5&@o~OS3-kt)q)H`%E^II zQKMIq5c4dh1QkF^@sk&GM+O}^$pA^fDKAh3ZVKH@j9>sFchCJ6H7X?|L9;>aOk5n{ zo+us>67dcxq7{^}0CcR7n2xCHUThMuv;LeRu{)8h?b#!6LOhpTXRqrp?MO;=G7IEO(#5qtyjckY! zrp8DQhXO^B^XZgOys}qH0z<+LSQ#t<&;>O$Nf0cb<3yBfcCcjV20$quofbGFj2ttk z2dTXLN){|ewp$Faln~X3G7PEsf+eOMN?~#83<3qi_c?hASav%4n;4xVLMK)-A-;@H zsz^j+#r!g@s3<|Jeda(t$gp_yS*aSdp@c4K3f1D3NSIodWYcBa%gioA@4Krv7OXi!pT1_0?LY`rzQ%-81nWwy7P-+L=Dsj(sMhXV@X5xeS>< z8~a^spi#d^`bU4|U25m0eF4DT-IoVvBI5(;_T4jmt8FK2&VCF>dQv-=uOz!-MEj#> zZ?g$z_w}^dj;DWS^c#RiXW!hjo6Q=!`n)f*Yh)Ytw9(Sr=6GE+5hLN*plF``QWN24E*G{+|j~a1q;b=!R8`1kj>8LW$qn%`&Ta6n~EhDTH0L zb(^s z5;TvHy9Ge%2FsOBC_K*GloB#hxXmA)2w}FCLm^Q zjlQq+-Sr{D;ms>!v;cp}wf9~KweOlLp=kKPK%iVO%!r(;_w`Dq(0J(QBid^Xmcd?a z!?AZB{M~$9I@9X9Ie@GNjzNv$nKR-G9?>8gYn^bTYH#h| z{kld!121XD-+Fo{EqN4!WSmRE`FyUCD9rF6-I@4aHUo^Q5HLe=c+!;U3{cftg@=n} zZp=s2hay~iVoV!BQFqobHKY_r+z_Ers)}R@53ZiVYKHH9=X>zI@A!VKYk{VWWx2-H z{reCI@(RGyjuHfGtWQKOG)6n;fEX+aX^P-%>&GAhFLhm=BJOm6p{$Bj1WWSjK`v@( zR2LdZNXYP$a;HX<1Wo=brJ%Q*0R>7)#NTQJ z&#V9xzGj%0N>;eT2S7^D!Ud4T=aT6LGVAmbw0(vmdqpCHZ6zzC8ju8D5uHDDFr2Zd z0G4nD9H^MMDJpcbnE=I8C@rFDiUko`_tJJ}1BcYa0XMrO+*t59%&j^5gE-Tjc>1pnIpa(*R9dlesI~gGQvV6ZF9}q{`TV;$*Hb$zNzrjnCQ$9vpMJtF#!=?i;WQfCPI!O{W^14IWtJ=9VT*i#3+_=`$!rtWu{zPVr!+^5 z-qwbb(N*%Sgxy*Q5RON41%Gr86?Cg1 z>h0W&=y3Mii22p;nzNFAeTG81T7ev+I3tO+V($An8nt)0&+7V(qeuEN^R>21d20L8 zj-jRPUbq~+qn+C!@9_#4!-yTAraSR;U0gXweSgfMpnEp4BuHWj=*5jk%LXySY6`Q0ESV*y zO+BiIrHEC4oHEmzKt8NRA*;etA~r}czWhh?j? zW5=8w=Z2-GnX;2}@@ODZKO-X=S`n+6HCDEWn=7x5xVrNK9toqyNRcdF$;o#X9b1*%c?3k7ylP=4pd)WuB1(EAxUF?c+K>qpE;X3|9%h9&~t6 zXN$chCepoG1pARoj-(7 zK{6nx1*XLvXuv8t&WlkB9cv5#5aU9BKlpr8o%1z4^;yEmX?C>(=hN|)HkFcF?G?aA+4)y5st}V;&?kM{9C~EE-grz42HBv zFC=wnk8%O&KS3ZV;YJH1qB=QIK{{pAHACjK&gi=s`TgG`CqV!F>^T~udo`yu21fFr z{#^mOFR$G=Mn(^;0{Qp99vOEdIWsce>)cNwrAOC=K6ppHvz@>8-kCizdRKc5buG+6 zhNtaHRL2{SZKU7brcd^I`H}n{jiY_8H(L8=+J27uel)Kmdn10^oKD!;H!;3G{VJYm z`#8Erb9{9FXv|0ZE&4XJLGGw~wRZ5xJ{;-$qp^%=WVDv?{pcM4n&ZIZeLtF~84brZ zi#Y1vwWHaz*{}T^jjo>Ft2tx_D^2EzgT{&Lmzy4sS`^|Vy>4^B7BfS zVaw4ih^%yh2?6FxDLYWz84LJ<(k(_5soN#%cTAU9UZ( zGKf^YuC_f(1AusdSR-BfxssIpR5S##{}V><0rPFE0@UB&{Tpo7KCEoS0CwYUqaeip za|6kMIuRF)i<-1MU!;D%?P`TeG-Ku!L+aR3fnwXS0UqKj&15$h6{1l3=QMCv+8qf} z{!sttZdcoG^7mMrFnV9jsv))Orx~Ab7+(A~O4CWQ-)H^b0IDU_=BvAnTZP?_r&O!U zNSx|RrZMbv$TXW3n#Ivz&z{p0HXLz9LJMbETRI+0J&MB;48n92HzG|q>1L?qrv>UK z>Ks@06ML6}SONCK#LZ1bQgOpxu|jElLvZ5}Az2d41lh%Naf}vmlT`&UOMskMRvQXS z5rrZE7T?#5)KORkBZaY2l$qjex~1Gn5R@Wmf?9zH_nP=-ERLD7!ogUv?bFoI9oJ_; zN=!E~ODHSv%_(6mOfgzSoL?)w1`<(286_8(DH1X+Dfq`K({Cq%~s9qCSork?f!g3$sfYAlB=!GjKmZ-cc`zW0BHpz@kHH# zY@T9?!sw{Ps2hv9Fsn`%S3j!$wi@@12-u!d*F&Py#E_AlA11Ksl0vYUI{J;d<0W~S zazR#Z$W;`J3EYoDN1!__A;}$i*bYOyXtaQP(gdEmXb{QHpEg=FrxixS7*^r40>K%S z?@kPG zjD+5dPskj$ z!{DHd2adDqiub+^jWqr}*5p z_$1N3nn8=uYRVZUu`F%HkbqaxF-AHFzzCZpHHyH}iS%$@X^7Xox*ZItipwfRLV*+m zl+IHa+Y-mwF#srvv(l){A~f)nMq93iGlg>kQyj7toDeAcQT>mJ90YMmUX4Y`11p4x z(+n6P10*C_aODO#5d0DjZp+jE)4h9GPS>o;~VjE$*l zXa=mZ;w)RVf|(o9k+z{qM+l;FQKSP&UH>EmrxgfTEhsziEpc*0Hkv-c!H5CQv(HBY zqbZm|l>w(C^IwrNEM*6B6=H}87`c)7U0{!>c;tw{s|AUdL>JD7WTvfvRh3Qu7L<^%R^F8A{$4Fsi}*vyag;12lmd5jl0N9P^)Yyqk^o3b_g4Y1inyrdt}+Gb zT;t}L1Mz?uML1e=A|}&%ULiv49dQR}tGBj?UcvMFYovc($VTs{buMhOS_SskB7^!3P ztq~6IeP1v2_K|To8fU9Nw%^U3xlND1&1UFz!}^YauGqhSmu@kU<9%E0_al0(&v=UX z$ml(q*Sk1Ej^^iRO-Aeyr-iE{Ti2%zp3N81=Co-|l^>8$Ynwl_Hxi8$zhDrl0is z$lNywV>E<5uMh@tdJBXsvmLYDwbT*wNGOendPa=>754?O)9C%}y+_19YPbJwR3>kC z&jFFuc5yimeOTRWynTOm4BM4n3xy1qjCwfU#vmW{yC3h2IgPIBWErftHht*(S(TYI zrkUMXwjm5R^uFEwbu>5qIK`Dw1oP|q-?w|~ym#O4emj`&k%OcvIxdK-!^Ka|tZ@vr zPK!%La;y-I1nuFv^y9DkpY7It|H$QO%udkKBT^M_en*=n=w#&7m??en`QrD<+{~2C z(dYL2ksxh<8@&s`r_$}THX6(9T}OOnwr6&~RolgBAn}A=*E2yv-O&(ojLsC$E)qdb z$q#rH$rL96u|7OvD&Cut8%;SPA|jOuY>XBpvfp0{BP$Ht139y_GBiO7`x}n$a5jWN zAgYcbk!th;2S8&_yd1JAoySE4knE8xQu$BdS7@YU`LLN1kvRnhPX`Hk!FEIDnt*9N z&NBlDm}4t|6(lPzN0K`P=)4l)ywb5j>>2pn2?UxLNs%Q()%)kVMoM&5BqT=hY%9z$ z8W>#xg=vD>f-q*y9aAD(Ic0#(6ibZMQE!UQGUA|^SK*N;7wzseGpTq9ChjgWU^UW` zdXz?hs`7gQ+BOYBt3iguJ3F4sF0)r++ zNpt`~3D^rEVD%z`Q}S39^F>G;;o0fapjbT(u83ikgd!QH#BtHawO}fqdgu{2AP$Zt z!^zWk*Tmi~Lf){NS6(tkXe9?i&Y9_lu{IDua;bC_(QokfB^lSIDG@I~joL97M3}mj z%vg=fHQA{6Lh&)zi-2QZd*mQ6wF^Bi{vA0&su9&T(&&R1vcphQe|tUDqHi+RJ^F*U zUBqO}@qu1I-fzz9_6KJdI)eXCE4QP+?K9H%qiv(p)sOTKjeh%{oEfc0Yu8^R<8(By zy?rxtPK?Gl8qfCIHkT2tcqI3Cr~^%jjC>o-GV+n?*lGV1$mJni>AEsW-6cFpFr zfBrT-M@_Mo6(QT&-)_^Rj`}-wmOR1zz5YAXvuEwit`SeWtF6-Vk9J?p);)E!d!c({ zTdQGkK-0eq8?l=^G;LJ~6M@TlD9$4R77h`_NsYg!;(m4l>Y&dv zVAoyW2gKBR%12{u$92TS+WsE()f)QP34}p^`@T@?x7~NGkqx6kgS2g&+ZaMSceATE zwEGHIvp&bOqtB!3HbE7?&Cu#E_LN!0Xd<2YGvsnp1>#7#xXU$U;5OdBWRA@oiFg!+ z)s!%frJGB^hOyGjT>!$4l&o8Y7shl~51VB!oZ5CTl7_-j5bZ)Vg*nBa@sspjbtvFD?bk*X3$LYSgN!`>_3R3)S? zI94HuY@}*HGO_~M>6@i+4eMs@<7|R+0z{NXIRs35iRc{mzGRQWzzUO64P+6z2a4ZE z$H>N83xIPtQ3Tcb4R;Dc3q$}-f*V*Z&unI1#v49%!`V}d7I(xHOEiKZ5ChKV$H02V z0y+*93Dp;1L@p}A6yX3H&xzoq#IA9C7)d^l%E%8_LPZ3USV@Q#l^87oBTfjh5pz{8 zvIF|UL6Dh|RiIY5_KukXZKH~~Hl|wlyP7)iFVkqwUJ5dcAY+AyqDYj=j5RSNDIR6T z)mS|}NKEL|3%W)`U&0+PhLq^Ek-{@`I*CX#Sq%$-lX#Ep3|K)H!?G|6m>9i=MXdrg zr<4H+cN3#F=weBnBQG5;#;76~+cE=FffA#jl5q`h#2Crtm?%jF3k*4Vx|(l8(Qv?- z0}=ztl!y1%qMI5l2r%V}dPUfWu}?@h=BL|c<_0X&tmqvc z>r6A=IJ>Xj3#%ZHxqH6lsqOvM2oBciT48&l-O7b3%R6JSeb-1PUH0{M5-ta?j0!5jT&^vvkl=*Q8V+~vN0zDM^Q=_CDG)nAir zG-0nujA>W@fjIJ|EqGG^hI?_)p3U zP$QT327J3`XAXhU93EXW8*BD_ANAbxl)$09%UsO*b95cqGVS~v>DV(HXB*@(P29x} zon0|UNs=Eh0T}4g@iq6(W|w><*AqElsxpX{IP#`nOAWwf|B`?*aBm34g!L2G6A&=Q z6B!ZyXu;dxjtrgNDC@sJGD!M&%$QEwelWf1eG**E$E9-yeVefCzw6t%i(x9Vj~r;% zsV@&c>eEb_+1($)pqYb_fgazrPKTaR)zAL8=p_xdYdca#hJt*;K>9iC+wQ*`_H{6C zasQKCZQHe736F227RAluJ<+dOyWS&ZZq`Qs9UtUB7oo9%Gb7ZTCE5@^RNv7$C7bd2 zmrcLaL+I}dEx)(rKttN^d1Oc*_YN}Mu!CrWNLB63|ic36yi&8UIfKchSVI4 zb4dZDmv_VuVGI#6<#8aGzUqc)Yb>-h_$)d~(6;Fbx zF2oa21FXPob&DjN%f-iFUVhgANh}GiVK^F+Di+~MJd>7UDq{_bD*_z9nK_mR4f!JK zDL?Afga|I%I(I|_qU$WCOB!bQJhsF@w58B3oeSFl~katQjH;-{SJX zM4*yTd_5&57oKVZKt$akBXzH*sxk>wIXg+<0NrWx)TujIBqPDopxlX4RNUE!S_gLua=&g)E){VjJB2p@5OMI`RHJy3)5?0jsq0GKEaq0Z2fpdws$0HWjA)ZUEk*ATyAqHwwHbFoV3s0#h@GMMk77+HeLA0 zD4p3Qeiv8DXk?H2JJTmCtzy%QH=WDs)V8!e8vDiEK`M?DdC-0xX7*IC6Zh{tv>OiY zF|!)|G_pZv>#RmBuqCm~J;`mpPQ9K#(}#geeYf7@yzXe+x7mOF+zsE+>2ybSS$p3s zDrt0Y+ppW^eDt~1;{hJP90DsJm`F3bt^Gc#9R(-d*)-jd^OLzwF0LnwfC-~WI&zc{ zl3hB9ZNK+~IjaBNzUPAJV+#|Ft6kg?0`};>*|#Le|AS}%wF^lmz!+FoZ9}}c2D2_=-Ju3+qOsj zZ@-%{(b?5A-}afI60vJfw$ll0G=CR`gB4gVz4~3}?%eM%7z5n7rLQpisAWc<{rMO# znb4**9KE}ts=9tj9Gui@>eZ~@!+m4>aqWH@C}nYP|9K`%H-R}2I_>k59n;h15l`v+ z&4E;C#fVr7A%sPG9_Lu#snt;u8dTj!AdZ>F_d#k73bleBxG*2>WmNou;S?z{H$(x zE{SslFxrEqb*)B=Wb+cno>pbzl?4S*6cjgTL-AOWA!p`(z*KS}q3qUYh_90SPk~@neGrA@x;3_e)Ly(ajKgSF( zVX9L$I1Y+HLD1j=Is_I0u0*joAajNwTP6(}>nIV=L=lM5#XMq00F%rLUZN;SSd(ng zR-QfsF{~&APWI$7bM39=VPsicT7W;t?&6N{{sOnIlu#Ad@6VYdC$_S`%0MWa+=>Vb1Nb!_Ef!YaYB;1H_9X#ra zl@=Wixni*;k9cys5so7R#VRY5If__}XLCx11~@W5V$UfDT~ak*)rEt1%sI%y=&*#=-FJ_?~@h-&^@|XNcd$;Ek&~ zWjc9jE+cn(f_ddK+d5m@w$OKu^}W7dM{=|MzBPz?9dI<}-MeJ4Mm~ggEr-s9fo?tO z>#X0S-`mf#^orSBHB%3`%<1W(mFc5#Kxo`nJrF=d3jzDX@4o*%?&YRixs!LGtB&p; z&EZV%YVD?xe%?O2=}Xwn!KmE}n~@Bijz62P-)OC7w#cpaMnuGRZO!z@`rKf>r}NiD z=H6y!w(a$Qk3QFTV8E9Kh}VOZ&R#mHX%6`zGYT zc+7mx|BV-Y1lJRnp$26eH*YOh5;@^ip{PqZpc%GF$T^`DtH4Gg`%v}~@%9@nuw42H zo*AcYTXEgRiEu=Gz$J6)`y6d02YbUd6&GVV+SD_`zckidNdcPh{Jpa(+HXsKjTr3i zcXCr2s?8ep1buA8+opsbgn-hNxOCQxm~G$pBge|fVKKVexokmSFXqVa*?l*7-)N5R z^!|f>&gi^vJ96U#IJ459T|U$HvH!m2*ke;v`e%Bfj)V2;X$ReV&9*simAWwF{05-j z7xW@%vc0E$*v914=4;)~-G~PbY0wSM-l>&psq#zuoGO<6dYUW4JuSJSG*$Y$-)4O5 zh=1NT{`mU>XCS^eQp>j1!jn-bBTk$xsh0~TuTg@~nql?4ndBCqgjd4X3>5>!wryyJ zqK15Z52t6}gY{4SVc;F#33>X}9!hLbS$PVc6U3u5M9pzzn0jMw@G1;9HN2w`NEC2< z4g!Ee60s->a25*2E60(^QFa=IRf-~X157Vb0g!-V1wdx$Wf;per;ODAQ#w#8CJLg& zvw=0JnM%WHTivBZs7XkqEYMI*w6?u^3@zXiP8E1;g|Sq!IH;o4eF%c-g2rnpE6f%| zDQ-Zv^3)<&!BnROtWst02pb6O#B`dZq~l})BzJIl#VOJdI2NJoSd9??RR%#I;!YnC zHzt5EvMP`E^d$feI4q8LVs5Y}Ea0=NuvB`2;bfV3x1uP@2&=^xpM|U#g|vX-N28p4&}(+upkd2#SoVFVOvw@{$cc_KwbZNwBeukiCP;Ecl_`2jlFyqvh0LQ{ zQmP7{`d%c+FxM02V7sBKVMR)eAYvSiF{;bcvLFj`<~}c=QJf`GQdriRshw)<;A;qotZa&j8(Wc`Q-?|5kE)DYSD`u2MBHSFXg;F07}S&mh>YT9$ibgM5L z%H@&a+BzC~qo&F<&`1x^?>bk`zu+=>dro$apqV~4)0am2<$j*9bA;`Fo9Kg6W7GM` zMr-_T6uCOn4YYSo4X(ZqN4ndQytbxqt!@Ntc!?3g)!x&N=PuQ`W=?~4oU=09yLk`0 zCt(*+cJcmLfUYkyy}!L@uRC5w-3UJnpP7w+WZMO8%=F|VeYbCaw%!Kor{UITN3`0m z&91-LB5Ln?wIJlPed(g6zQeHJ+q1pg_HRUEZFH6^+to>R!Na^G0cszvrru_0`@!$U zBZpCGuwR$WjwYAj0h4TBJi+xu4j5Hk+W`Q<`*kw8Ylm z*D{2j7~6K{&VggbcQ6-2;nM!THMkLI%iu?SzRfU;E5@a&yP0nV$z8`1^E8|z7pT3$ z&|a~rTi~UOHU>hc9iKY07D<)SjVim$)^bEgqs>1PDs?QaL$G;weeU3$Q$f_-6aPhN z_PGD9#`$P2dL~IluIZ-}Mq9;bG%pwP>VpbUbSXb4g7-pvt=cow_4Hejouwv8*n zSk0ngZSlw^NSwT*-fL~5uGqyms(xCTyW3~p_uJ;Y7xd9)gnb9Kw0ocz67Bde1bowG z-Mh_P%QguYiooc)jb>*2ZlrwmV>wdLXjp2UTi!z_fPkmerf#??Wrqk2-~cHN=K|p2 zz~Z{H2_SBigLoG3Qo{nKpqx(Ekmo*#^i_Wfdi{Fj>-&(hdUQkvj0T})4{f)24`2{v zOEuyP0M;Z($T*cmhfVS-F%IM@$$;#EskY(_;9MA)L#JWl>Aqz;9Yk0@+owDkPvJ8J zMYUqsL?j94-`@?UqGaQSTqSunl0x0#lY~whgOF4tcz3EoNI=Jji9t}H;s#y;L|8RP z0hAJS$P^9{8sQlki*XH#7?NeKffOuxp|f5AB*UpX5!eMWaNUKGDpZ=NMI=cmjt7){ zQACufT`6N#;*%^PGhFM)@Z=kdh@j-BRGjO`t`cFHXjnKs3bH$8A|gq>q8@Q^;En+= zNve@91(*-UBqIyF!jaFefe}_xYW1MN-EkG)@r1mLbi{&4!pU4lB!MZ@^_XGj<=6qJ zWI)e=FpbXO5j8%qf}S-xc9N(&Ssc_FuAd3$d?UIqAPr8C@3n}ZF;SS(LBSWT5KDAC zy5lk{9fnKFfXg{L9L4d7oY$DY8wn`t`!1nCyuuX+AtH1DHGE!NzHMh);h=X1j#s|Q z5}LK|cLmg=e^ynT&nwG``$VL)z|63&1=rW9$|;fk!Hbz8CsyM+(k*@Zci3y;YzYxY z%HtZFa=%^33ytsKfYt(tZ?kBjk^duy%U&kfTyIu|-vnjIK+)@e+u1BaE6eS2;HX7cUpC#cZKUO~eV?4Ifp%xi%YN~;Z6%uj4AF~*SLS@|yCAZ{?8Bgt0s{t1CJ~CA6iw8zS&v@g$qksFhdjobP zB#szD-;XKtw&sLsvT;W5X}=4B8H{HO8}0gW!0<1awO@U8sruduyytzFW-THw+Ai0t zn)3KMxo0I+$k~45^X4dwMbe;(7G@=P%RBS|&KVLJ{5H7IeDx zw;hkgj|hblJG$+al-WA%CuURrcl63mT=0VJF$ri8w8M=xYsb0KetdTce%rt22zw@! zw|?AJ3Fvsuh)(18;n+lwmt}X4nqe&fBzp>rzbknJhgUcPC|7dwh#6zlLeUHb(}-v} z12Co`DWV)V0IbO1>HfW^UXQ0vSIEj#5QU&v@)K}%JPM{jt?tYr;4f1#Oa+T&EMAGn z2+iQ zFK)04n3^M6p8uD) zQPLX~gVhp>()n}}rujtmF-FD+OM7Mj*~^Lxoo^YWh3xq$l7K|iR?=)H7KfTZQJ`6| zCh;_&f~1C%0E^?|z{u>(oj**uDJV=V0zq2|(rf~(EI);8z#*oWjBv;zTwvvc;W9OxdE7Q9Qy3j%5>k zZS}({LR2MK@#wJl~!LY;j%TldX->%!- zl8=^v?%BzNYFrFt)RB(2l{(KCvK``B9*yqW_CKyHiOf)Ki`cZiE;TdoMtwbUSj@&W zvkAufSm+n~`}?`Mi+(;E*O4B4p))n(?Vs!Q-;u7iYnwk*y`t6A`*)7?xMThOQtpoq zNBjH2ftqYT$2+?Y-q+UfMSx;o>a`-5k+-A1AK7fP`)-?q(LEzQY-C@}-re3mdS+%5 zj{Y6lcKsan^xyv5wl#W}hTa6!eeMl}i!WZ|@-x^27O^S+~Y+Yjm>$u=L^Wi#)mhzknpOH<-f|fe~=7?U|e3Fyo8& z8*#HhW&}KAs4+>s2W>>iciEsLV{5j_X9(1B<($!P)Sl7ZM))&hr#G-h9>me#J10Z) zj=qY{us@A~TYq=Ko17_*@>tENNbIViH1FOiCEayl9J{hRyYDu^HxfKsA3-H1Np+O< z^RYcU|E-^2GaLDxE}rlEJL>|?d6DEBFr)maeyT`t>eF(cX^ zL&&vP8wuC;b2P^>p7B^{aQ7A0M&m!CS2L}9J$9>DsvxG-Aanc-nDOMoF+1run zH~4@RND4_?1(f6y814!PQ&mbzINOTV&UG5q2#QI_P~w=VEsWk!V!~t)2PBDj+K1xG z7Gx8gp|~+(gG4nF%LaqO))iLh2ocBH;Ay*hHJ%8K1(nMP=@$nL_0r5K0}F@Y(K!z8 z3xe`8N~YBE&@sOjgyaUenPZR`Q+)zYqj;6VMOXny0p!H=i~R_6X<%0}_%0 znWm~#3DSDyz{du0Je-@dB?CS-!PyKDmak5kyD$~Q$q3u4DPv8E<%nUpHpOyQhOI*@ zNN+Ml!ir)As}gVIo(O|BbS3snTA<<7(;H+fkd+ZFS0He#UP)+}rnSJznZU1F92nME^~jixV@sg}%u8G@x*`|i^dwI~TWy8d4H6XD z9SfG3)+J@dDH)wmc81|GikzYTTLM$OPT~#+z(JPD?`dCWkJ{n9TMFl=Lb5VFt>M=i z0J>05!F4cVNmv2gn83_y4~c^}EB6h=SZ&GZSUP0`P++oE+5%Xu%1I3g1xr$-q>ho~ zx(Eb|nU|r?bTKB68S>O4GxJpVx&vJA+eHbYV-2Gh_;pM7WhK0{FTxtU(c z=UhL)Ht+4VmG&O%v7`QS&c_D`vvKx@Ui-|xPt@%$-ffo-mDbQ7&FRsaT&#bmU(BAp zO%JU4RHs(}v9GaJf*fGE#phAg&c1)Q(%ry@@rME1qD!d<>24l&bO2Px-FEG5g|=U# z@yzU+nO-~kyM1od=BP}1-$s9kebndKnC@a5#x=TUrn8Rhp3(b9?=Yi$-(j1k{XF&a zHEXZ`t?!FqZ(9p*iW~NQYTg%2_JXbl=BQ+_0ZB~$tUbI`B!#5 zF$Rnzfvt_FE7n-hSQE4OY^NFJ)KG;-=n&D(V0h7f6=FF^jO3XG{Yk=$)Z}5Bj z{@z$`@0kge(MA|CzMe2H7?f;+c=lYRvdrGww%?ASzo#9;>}tK7eS5P_4s2vHoA-R-P%+jjf)$tl+p z$=lvL<3-8WvlU9CzRtp1<8)I*7$IQIZ??@*zY6W?LO+M?yY3>q`_S#}W#ciA=Dg)= zmx>5CRPcK(k5O#pcvVq~hLz}{$~WMnZkhAlt z!gKzpsKT_B0yAQHumVuRDu$wfX<`@z&0tpF5y4^tm4ci2E}5r}@Ps=#ktY$X#`3vW zLg&gFCSGD{t|dP$(uiMSpUvn1A#d?0CBmZ8fHfNV>N$SuQ7;L+-@qzCNeiN6vM&Ne znlNWirCKyW)&MeIph}0&38BRE`QrVsL?GP{zMO$_pLN77LF_S}KCL)iC7c1g_%H!- zkBt!A1=r6Qp1}<=uJFRkXFT`l9M^s5AL6}W#(yKHA>Uv zxJ~r5ZoDI#Vl>XW_WZy@rp8Ea|DEZ^?R7N&eVZdX+0*yHux zACA`fXk6`QGpoT7aetC6`sR|K_G<@VvuK|9dU6on<`Q6UFiPOs_b0X6_wZ**Db;Y` z0FMR7j3K;|P174Gv-gdR%6=2{jBqA2`tRCy`|-8!-!WW-{kom3hfqIaI=3;OQGZ5r zGrPB+zu5)^PEI#< zvFc8jYt)XvZA-)Pa=={sXWQ|&UD&N@5@=CG1O&SKW^>x#7vJv;`>JiiEob;i`6Zo% zqSE&Hw)eI7P8}hQvIAfT0N#)3!YR?u6)yMNs4t`Tcl`kz&!l^g2*j1+3zE5o)>~TY z$1&mqQ>Dt4p_!65s*{|8RbS}56j-4{K@3>ksEddcg0%89yZDI*qe$52h`K4O zHAZEOU71hfV8doi^+0kA7Xfm`)1urgLfjci3Nxv|xWpw8zW#Ap}{9e599bbvlOW%d7*S`^;_A7rY?!We9@#x`WJb3CF&p)5=um0bE68C@P zTk*IEUi*{37@zrhUyS#@l<|(Q{Y!ZI2fr3c45z0)8b9?H|0djf)2HC(alx~XZt$)j z`~kf1UH=#_z3(66wQu=E{DNQqJMeukK81Jvy*~wg?pv@fufZpN(f<{1_=!IiZ~f}O zj)&j*mvG90w|x4q#%n(7OYy=>FQe!RNW%ToH6E4)-~V-g9`=r}gg*0=fuH`%@P<$P zsmRy&@x$Nu4xGR1>mkqobG+tyWz>)v&gaLt|GJOIr~H!t2<0_z!p)lS!IvN6#qWG8 zZr=63;rj7=@#dfM1$gQ+eidH0Ipc$m6fgYH_u_@O|L;g2_%?jpPx-lc{m=Qe_|Rj) z^}VO?!S8=7p8v-G32*w`Ux|A^`E&5S?|dho`)B_ho+|Ifk{{s5|B~N~=U(>~{O~{g zAMuu-`m=EFQ$7P9eDMZXiHI?F^#C9G(0lOwKl^id^@~3Yq+H9315_xp0>mGRNDmUa z0d)4k7!M#)gQv3j;f4S}foVASfNbhuyTryJCJ-PnPs0)zGR%a4$w*k$kfBHk4*pA? zQ$%16(rc^Yl*kK;GI|IpL5p~4c7SPto_6Jjdydpp`6zc3X_h>}%V0NbR*Mn9gq(YD7zF$u?1Wvmy6Cc!0r!QjhMWtD~l%B-gD?(BcQ zWOu?ll4*0fwKs6LA5e9Zww;izeft-B#zfy8$)k~B)!uhx9L{v6ek|12;Az=@PT?j>bJI)jhgbfJ$#IR?Croe)RoF54?+g zHq$XKp6w2nqyZA{#}{LoUANghqxO!R0i*Wg*`DTiHrvR)==#&s;Vx=?pxgED8DXBb z?-=c|-tpM)?W1|vMYA>S_x)`9GV0gpJ^fsa#x>H<`|F5)`~J}nu||&o<3Yy$VkEM& zMN|&lvBKuxI}y@t4wy+p1{gz=Rig|f7038JfdU#Z2mF*O=y|fR&jmBH>Wrx`mn{TP zL@qpd?Z5GNzu~6_&|sn4Hv5!;_L8)?Etr@3~l3aK4i8j`RZq;)PCw|zG`gX zEo^dRl=d5MHio`WqxosSZ~q+%YZq>tv9i$s9~-~3{f-e;TONEoGg1WZLJR$vd%A4L zTl*5PHB^P{V(oBATlF513PxG?@`OQN&M}1(Y2Xr+x}U$%`t?_oVKzTQJQBV=Gam`| z(RZ1eis%-Svh^Mvf@A$RjQ6htB~cub5Y)uP=#5Fp{r!@dt0qRHiP0DpCqyNhC?F~bNDPyR%re+b({uw(&(rBM zz0wyQm>3o2uT7~hX>%}C-D8>^X>S;FWAAuyTCqxoA3K_{L0Ipjhp`Kcj3hL zN&KrHcoA;A9yr?rmmdS(cPH?@@A?(&eex_0Hm}6DzvO%Iec$yY+};DMz0%{Hy2VQwPBP_W+w4!1Y%F_pX3% z|C(3h^ljJRYkuIT@poSQEL?ge5EJn5!(WWwd&4*4^*{F$ICtx-aBNdUY7YAqHZHjw z|KeZ07>~UHI4{6%1pe~o7vN|A^VK;0yFZNQe)(78CI9wIapp9z+5~n_1F!hq7vSwb z^xe4nhHLOcKlC*?Tmi=~0shM`EbzK_{5rn-pMDo!@HJQ9_dfF1@k8J9Nt}Q8%dm6# zCHVe-@sEKUcJRILeFMJpAAJ|T<(saDprRF2s_6$ zw8=t>01LUZkRpMg<6wDMZ0|5YNy}NQ9H%raiJ%9tP{e^i^Y^F`G(k9kqgX{M^VTxs zJrP-Wn&w*qX>w_ZWIS6W4QGm)X~qs@2rOzWWix=TL_?gCu!W-8UN$eE&b3*AWtFk8 zyqrtL{f&VROQzX+kjJj9<%-Wh77qVET%U~xOpVpb5RJ)+2r^|66@n=<3N@^#ea2m4 z2f(=ZOv^K+T(s6ZlR-8ZOMP8P<)YRmZf(qR5&b=HI8FGEqus>@4HH_+w(AscK0!)SQ(=bSs-{!GPVdbyJTCYppUJdJ- z0JMmG&1Bq|gzCK!j2i2nv+U<%Cd?}Cqdut&6S8rxF_`%GqnOP(!auQ?-pl@7SrM-MJ+~+7t6aMpK4m*j*NIG!g;zNuAThv6qe0C0l#l>n zaO$7|JN7pxHL4^Gi8e@s`p9P>80TLu)gdPY% ztXhsBB3bnt*OU@s0w4ze|{M@<0<^JfB7$Q)jxeUzV*$|0#1Dj#}|S- z?jii#&;1;Z@85wlXCjsl|0QC#z*SHB0zB^vpM;xk+r`e-0)OkTe+ho`)i)xYejk=y zhhtj@c<9suFaMQSf*<-IUii%~!B2eKQ^4|{@O$6?mH6#n{6YNKPyYnI`J280zyA9F zi7$EKx8jCNfggX_n{nzdeiz42Tnxb>KKhQ|!GGDi6^l!*!wX*cV%+!eK7Q)ge+`HC zdbI6QqXzV@5H6F>gq8*n!L-+24K{vdF0Gs1EM zL;)L1!Nr?`Q}+VD`tv`Er(AynzVn}a8%}@k-^0(o`F~*>=Wq!FzyJI1#jPLwef)!$ z{3HDM_g#nY`q)eG-rxAAc+=-T8Q=N+FUFhS{UF})%l{6?wiB+rEa31o@bxzU@BVxL z0B`uf8qADt$N)(p5{A*jz)01&N|a{!>#sv|!pkyT6=`$c=xwn{*f`F3Fbp=5m>rl+~r_4_G<`iQ8Cb^tqvOxAk$j!No zY&0~7D`8{^sLLE{-)is`@wraNQ2txe=++#XN)1gF-PT-dUQ^3pBVHj;k2|J8(ZSKm z<2sqX9@mKH@femU)^t)m$>orlYa+KEai5AcCvrZe9hD`S=Va>E{kb(;C!4qC(7M*a zxO~Q}22mH>vPbU)=rZ+tbsy{IJ~#fBu2LUj*0NJm55W|dT5xIYv(|iDW1U+&zlNeJ z-j41aVy!5r^H6^6!OJ29b}!%t{L1mvGW{GJTCkh0*FYyqud+G#tkVTsa9xM{b=z}$ zvh{t5$Aj`=@4%cysFd%GZo$MYxSTiXqP_}EVtUOPV2*B9P@Mdy;RM%_7|#6E^)&X# zS^d`$fC~Y--vf?T5GNBW-W_>yn0z{9lOHxfYm9Z@bA)h?33}hPfx^Ej$2KM;K;I{X zMZosf1~xn3;Zu*`)amo!pf2v)J3H9gI)=X24WhB9WVscl2BaE!=Qc_?|2bkhB1RP! z%`y6Hd>{-0T!Zk|_cFo5fZ?qT-I`ZT_%4LdPw-F%W6Iq7S@3XUOF34Vhx-IR<|>nn zY1hjBR5)vUO4Pr-joI?!)m46XA+CUP1ihZ4kv)Y@0>HL^EU{-f#e#l`kNLG z`!f;AMw~g^SV!z93=RV^AB_hzl#2ClY%e%ox0p@~?hb3mxfYJM@S}Xzf?4aDdw(=G z@}$Q)Q^bVSuMm^i>8=d(76FN#f@>mLf+P(;prGk@i6|Q?QZi2tVoNEc$|YHhy(*%r zSPB?o#sm-{T4+$87C4aOkhHDiqX6!Eun77bN|0R20Icc|^B#714}o@(5YYkX!AO#{ z`cBppRS_lj)Wbt%b`kZsQB=235a$(!FY2Z)h)KN(y3m0vsV=d-Lz;Y65)es1tfini z!K(b|_=yNX)WauQh?7st;hIv?yd3tv3mwG4j57q%Ot%0cSQroO0l)u0UJu-RE57E1 z--FM2&c)cdWD5uPAA$%ta}Id>J3j-QIt6qA;eZe&;jcgc3-RP90zdrkUyJ8H{xNvL zmwW+!^Ks9_-k-e>0!@y4W*>O%tKSIx#s9!Z{_MT@v6sIF-}l1H@aETi6JGlpKZ6(i z?SF)C{GuE2*1!9``1-H=QvCU?E4=Du{|%dQA7SG-y2UYk@SX3*+h6xuJn_rE1%LbR zzZf6>_&$E?r+yW9;QhGxhUeq&e#zlMX|J#Ylj1-$8h{wm)1KMwJAKkzg7;%|69{`>=E#aDHXjt(F+Xf7 zsQSmG?>V`leBM@2E4Ww)lY27G@lhXt9qOCMkqwJ&nRcB_cA@XIu8|Bzj(u&}d``~D zuxPwnK5Ml(7dlh7RlZXi_UiZ7>6`0hqw?L>yj$~_gLw;o^+WxZK{z-Qi`K_*7Ltm++DX8Q(Vm_Kq#B8arUTi__exIMWfci{3!TNQjbwBo^%6aM_Ut2YLTt@W2C(M(E9HPWJY21Y5`8vFw}KJ@CL1S- z<|b|>?s!NP>x)qmnWpktA=DhhJL-P@cX@vxYRE)I%oPURtz}MR>!ODZ(LJ7OqSji6l5v)JRgSeLtaTelJ!`&S zj-iE9DdlQ5Ws#Yd>}icJ>(;G&9;_sN zi5mhb_G8=dGE00*J~qSj=is77HNq4kQ~2b*Y+Z66WJU%1O{AxuIRd1b|8!2{_k5nM zd_VP=j-sP;ea_Lhb;T^=jwXPI81^K=qVdSmBmBp(H{a|^bh|nF4-nr``C-|*0rF1qA0oI1OQ zYff(C*IxPKxcJe)|9%_r-(UQEU~wtF_^ZDL_n!h@|36=ib6@xzeASD-0FU|nufV7N z>~~;(L%NQ~E=9jMflvMA$MC-QeiYyFlrOf z0sO>|{Tj~R@-AF<;skmS;wpkUAZ%TVjjiK2Tmk5|fn%2d4=^sd;u>6k?GA3e1^A2i zdLpV`BjV$ zePS0l{Yk{oVSgX^!sk67w{86nzWB?&5+6AY-1N@Z0qC%?wSz+di{%EexdrJXcDg0* zJpBlM>knRwpZ!nYhj0JxZ^y5H_?>y$jn~P)Ehs|U25^OCG5fcHa zx4lO2*j95Uga{!S9%QOUiUNv{stg7SNFrFpq~(=`wK^04O#)5o_2PRj3W#mbCqV?E zBbRGn`CSZW%-A?6+?vZMLEs)7>J^{~o;$NjXbvdDN%8rad}gPBL>`W8`$fd^Edic{ z7_cxW0Bm82LIR|OP5>e|^9vwmpkf9P)bq~0GL0{Zc%t5^lIJwjewg=hWiW8jNTe`$ zR>~pN$dqZb3~ig#)+`M@=i%IvPg+-&gJsG)Ygwh1H|l;{^0q+CNImxIsb#%-%yUZC zg}h(4Tl2xWZ|bX5Cd;wRDeDutGD!|!D`mlY{c2@=D#H}I5T-KPoZi!V)`Cgv{gzzU zlC4@-OYbOgsny0&`0Mh}oQ^U##(FGsGPL8Q-*4@!dM@i=(vqXr;w}LjOTzBi)7U$A zfNrscMOZ-MK0bZh&A8;EleqNqtI&4~tRj$N+fZ>K`==g%YrnVVVt( zgEmZALGr8g1giTwin#0JcI@dohR_=S?na_^bV<@jnrnAqkE)( zvm2nt9Sq+YF8SiE6Eq7OBmj(<#Zk7cD`#2*(!-oZMPqJ4E#T0q)y*6dsVj zhSdjN33>1U(rti96;uY0Lck)blA1`=Sdw*KH|q&H((nLD+KX6@4TyS0fMD?L5rP)=7O}*=i9wy_ zh3Sm<#H6N?Z2$)cD;y8Nzj^UU9tt zyW}do^-cd9Z~KZb#y|Y}r{hh(^HuoNeW!6b0471l3#1N1UZ^0zQjKGFG>1rn6oHNy zj0nmhI%<^AJV^`)p%bk(l*=$%h&w@ZBoREBEdj*nAX>@GaK?FGOqx?AhitEW3-y(zMvfN}XU>&vD*DcPRF=KX;VnhM$jq2^MyT8tGG51Xghq8wJ2sxQ?xPO4f z<__NYwzuPzFaHf(w6%%He$F#+?ojZ1zxzromK!*?FL=j0-;ejd@7=iblb^&3UighT zdDRVwSb)(1n78B}Mfm-jx`gw@@KXLR=L_VMsg{xFC;~bi3lDuG!l)9g~@9MSORyMfQ0`+w4PUy?Bc#_7( z>UCo%uUb%;q6-0$2^0lO1au5+5aIR@ycK6(|L<^Fh}d0h zU~&By;q37n(D#H#RuAILr~d>y`=@YlcpjI1?e{~T{`YaGM8v>AN(dZutwaVdoFvdh z52SfAKtR&44aZl8CbtC?4wN+UDUo{YXj=>*YLeXq2qjgt0d(ZvHpWvy2&AwK$$t5lzxdfEmV+Yvd$LvShT;WRSc!|WofBw-Q#kYU+H{gmVJQcFM z5x_Pe2^SylaQg$mPyCyo1nziuwthVKZ+ta&wt%ZH1%Bn1{xOh%y>q~`pRj{hJn30D z|C#q-3E*G_?C(Y(ZUWao4^Mfra;+>zw z*2dDjimddoB!nbbm_gv6SM8@mz`>b&u(!L18?W8MvB!NL&i&<|qDR2-Cp`^UUvo7c zISZWn^sQKMu(KsCG_bMQz#)KJ-uWl^i~DZFd*1dgeBf=b2W@R5bd2pwfM5L8-^I0; z^!S#4@U1wIi-EmuNMf8nh)4i-`vj~s*yb=Mgkv3W{uEyQ%RhZ9Xx*r8k?W{_l1GKtx+pMr`GxabDMwac1RZf6oR# z$D3QSN(+W_`a^MJFj&|CG>?PJTI&xb=HOb-^Fn0MK^B-A6Ux8mc$UO``n9f@M5#E}b6yAYTE3rq|3dC%xww$+S~xh_do(>H zo{WP?X}qo9v8CYF555b(@~gjrGlzuBF1r$4e-3xv{TbYU--GznUH9T6x7~%)`yFKS zB3!q3fUB;)8q4JdM20+n_554w(weuwd(=DmZ-P~jT)z)mV{p8vbs~QUnC4V1$I%+w z+&!1G>vz&fwrzax<1cW40zp#3VzKr78*aFvyZ!dt4{)^MWz@ks15C4_Cq3yYU%FbY zj%{skBJ~pG>OmHf8;4wzakha-kz=IaymFaMp-Bjz``C2e6->o-ZtDRAwv|g=l zF}w+G3`fh&ja%YWt>Cui;eD7aW5Q{IyCjz$Fy$p8&D~O0o$7G_ZIG)Znytqe$_m3=JPxa3o8P0A_g!6~3$k!F8c zsB)Hs(RBtot!V^F5fnCnK4Skf_u?TOV1L=+OFsWwvH5vlgL|>S10Q$`?s?1Y*gZTC z^gXUTFIWNqA|eN{B@zyuCLEQNaEJs3!E+5uv?EVuK%#lVcmZ93fDuKNU?#HjdQA)* z#ggKZhVtf63(~UH9u@;4FeR`=^kDQv=#m>`R;sL$8dS7m4V!_}+Tws!wCo`y-~b3m zNK_*d!9()^j=HwTk8c1=;QzesUD&(#6ZrL4{Tg2S!!N~8zVJ_R*b8D(1JjA;ek1r9 zUy0pwjKjPC9MAjw&%@Rdc-8Cw2>W+^5V7y^%rE?UeC}606Hof0FT=-v`y=S%0hob{ zpYr)gSKWwjeaW}sD?axGUi+sH;EATc2(4J+9fO~eZ6H5AJ{Lt=`6M06B^ z0AR5Ix`2(1CGNlD7QFjye}tF3I`jfkH|IP2f z)^ZaX(xa|amtC^J$Ibvh{v$tu?zVSfbGgLU#ukKu%?-l#61e5o`|yUh{}R6LtG^CE z@_)Y&FS+Sv^l$$a>~36z!#!ZRI02lz5qtCqSOi;J!1Bb!c<1Y1i$8wt|G`&&{g>g> zw_b`n&I7SyL?)yN1ZE9~NfAg<6G0Z`8y&Mps<+q6}+(xrg!%Z3tpkp(v8V_Xic+{@vPx+nvHUUW1xz5U1PCPPMH2+O04TN} zQ5nEV3ED_;X&EqDyrRW-r*wT@CMfTfe8AHHtdmoVK{VSMzcQAcYF^-q5-`oH0VUfb zV@fS!4EgZN_r?lO7IUmEAL3Ett>@;?2j1?O*5-??zw7Hlp0{AOj_1~JZS_^pb50=! z1}t`2Xcjgu%VOGQ-CqmNHLjMpT=!chJD!6>OK!iAt5r~CPWJJ?k6M$a3`yjOJNc$5 zqg8#j`lx@qk#wU2K7R9u@e4oy3fz9@DLnRxPr%#X^k6ffwwQhMv`q7Hx8wI=5#8h=b-D%jr3^Q)>v>kQ@8M&CmVZ&)vTL_S<*$PGL81YKFu0*_NTE zz5T;Dl#8ZS+$Y6ZQ#G;sVtuWUcc}{^@2lN)Qw>DYpn^H+`E6C$9 zwTPC&V1NgJi6zv5X9qW1oCn-SR;rNvz)ev$< zG07a?U^;W~cNh@UsFRvt*Dx*NJSe+^wcpEbkHI>Hc}*zSF$O2R1e$oilf~|IMdPq;$7-nV;KJ<+B<;=hkd)pE`d~~)u-7waV+&i| z209Yl{_fwwiBG))heSAg$DP>h7-4Y&bg+lzViV^D8d{LlD51ts4YN?s6jDzWBm_~f zon#dQ+0=kT(j>VWmZeG315qyLn*j9CunHAi+yl)|=p)=v>b#YOk-%nbQBN2KfW%hp z*JzX(Vl?DLmDofoL@i7hfoR1ekcU43L|VohDIxSLTyg0Rj$a0xxa?}Y>$Sg*-+%Sr z!w`Si-4<-6Rvh%*Q0@%z8{6ZqUO_#%AX(-v6W1sunD?52bmBaUqV zR{=P=wE!LNA#Sn-cbnXjjAtdN=O&Yx1J$r16f`%CB*O{!KqK_fFq~lYj||%wy?LAH z9`|RIVr~P7DzGF%Fnc4h)7S(AtE40e9qZnSr1bbu0A|6GpgHK08b$&Q)h05I2TS&t zOa=%_W3=qNmQ5!o=9>Oj9b^J&*p?D`4KZQ@5+U|L-+L}9ZA;ss!Dx)B>`>CW`mm*? zXq2?l=_P<-^~P6AzAXDdHfUq?&N?M;B(GF_siow!`kvhPyn&)Leonse9B+PDRLT(f zJvdqOHj$OPqxetBvqtbP`XU!QlW4`MLS~yYJk&COHhdj%*xmZRwVo}5Lyco|GIrU1 zR)l5n>Zx_B{0)!~nzFvukVhT2YrU+#YI&>md`^BYpRJRN=3r6ligRNt-)+6W&M4uy z-CFY2xaN!o<@;m2jR+hN;|e5#yYAh^gS#DWe9}|!gvZ~AnD(&8jOFnipzk3>i(o*?L$xpC~u@eCdtoHVC<5iEsa%%$s@cuvlGu-jX zn{n~wSK`43AHumakKi%aU4@4qco^Msfn&#x;W3ZC8mFJ~WZZM-Cvn@S?!eVIJON>2 zfer8^r-UooM3y+WfiBMQ=a9UHWsUHYwUc3kuVXGmDvgw1|#~SpJ{l-2$&NU$B$#b&RAG8b)iv>UAJg2vOxY z$oRE71kJY9+Dj!oW(5kG8VO`>Jq-3laLcvW8?0RqC>#1 z{nsDD>fF7!^4xvs;sf}Dm;ETNeC&1Tqu>+UTlmz6Z^hN;KZ)1><`uZg2iG358rtk{@4F_HE#LXZP+@0 zH_$Dyxm@DjPu+o^dg(vJ`~T!?@x-S+1JreR$1S(w?QeVw9{j{j*xA}fPYguDqU*4G z>R!D1H(!Hud*^Y{?%mj1Y-%nM0ydV6+urjQ{LgEz!e>762JqHJc*`IBE`Ih2&%nit zfTv!17N2_GYjN*Kfa4ckf=8WwKQ>km;q|}!8(3U&EtY5R#>wNC*9k!P+!4bN{V?s;<3PGK%pox|R07-|K zqR~s`sZ8p9=3$h<_;Q3`B@tE#-Kq)&2pESF4R-`!t-v9~=xoc98@EXSXzm=Xa-?|Q ziF%~;W=3QtEJY2DT85r-dE#hE`yD5}q8AWs&|-LN#z+%WjdOv>J?hvF!jyKpfknO4 z2%H2lYV4yM5=6{Ry+K=aqTrC`+ZTE3TMjbc19Zfh-nlj5{IO=ST%TwNX>eLCe~ttz6EcLg8{5XurmerFwg zOF5#H<4ES$XC41+DW|PITk`b<%McIwNA->j-p#FRPKm8${rY!nZml`^ zJEI&NjWM+Fwxu7s-c`zD>)NQ>8=ep3z{gjPw}f^1drt1EVVET1{N8zNEO+o_f8%R$ z?x4rVZn+7|C9t=ju)lwZZm~d&JyH@J+u1>k3*7v%oAKzYug1v}C$P77fW^iJ`qc`d z>zevzE7GBf_oly(@P;Z~#l>mNM|ITwm+&t8n?$8lSB6t%R9dI+90h;BJoHu%T`e$M zDji)L0PJ_igs=!WcmBK_92|-_158NQ?Dg3s;S~TBW8Wo-c~D}^6*l;6)h@ge#)~ol zgjt31mZAZ)2~%l2mfhse)SDsCSH{dHE7tkA*B`4!2#a==kEY7H?TT9z{UpQU*tsMobt}0$leuZhKG`TOKzp=1~*>5hJkwBO;>;7(FXzG-9N-wo(Xg zc|5r~54rd{hDXJeTNI|XUt7tn!#41}LI-K%MNtM3U7)L+UYEC++; z4r5hZP01n}*1$%n%9zIWSR%{-4QUV!LrBhh7^-L{u#hT^fsxolb^xXTkscI8y#p*U zXa~Xq5wMVmjtLzWScHXT$$*y5jS*2ayeNtQK@geL%O+_;NKL7$^GFiF#wQ_3^H(ru z4;L6O46I=^DDn*3FHjZzfz%powWm=xVhu-mLc;8#!0S=6@Ifw%nbZ|P}gU~KJdffMll zxBf9c@Yes0lzMD#Y^r;g5Fimj5fLj>!h@P%5J>a0B-@WfuGm)4%l{Ezf4#q9i)&pTRk_reU(ek#CCf^m4QIqALfwClmdWeaQF(o%XYBew4 z!;p|^xz?T+uqF1^Tt}pBGlN+1UFnd~m#D@Z?mQ$)%t*zVkCMGi!HmQeDICc%M7d}| znZsbNa>}e?QSmVmOWaRW<)j`5k<(Xcha?pW(@5dMya#Yi(?_z8D`nMTlgqK?z53r# zKklj3;HPu)_XKWP2^2L%VmLf1FEN8R*&CiSs3m9B{ZC{^mqV*@)%>?!tCq4~{(F>% zMk@zK4KopgY4KhQc6m)j%NK*Eu!kTg#BcRi!)cui(CWK{L(5ayjW6ru`^lIaSG|6v z9NF4Wb8WQnB!n>1U(0^#y|Yfw(Dy9Qmk>H6oY(;_KEYs$(EQ*FNZf<0&S7U`iPcIF z_s`(i&M}-my@$`-eGmTH)1HDyPVa&(q?u>dq9up7bez_{ShpT6J#X&&8cr?PuY=oB z`d^E7lw+$MDRS7nhgepy$#iCz4Up_`B6PI1wXF$Oi=krP5R+A!IRgyM{D+v5q-b%q z1t5k6^uae;^r1Hhb*<2rjHi|9cy2Mvf9v<>7-CKQ)@_=j7|U3xl%c9eHQ#kgR;>Vb z%T1g*zl%%Hp2a)g@n^W}-g|J#C70pO&)kEwI)#nnOWgU%yU-(HYxB|AJ$D`(#}?Sw z4EUVSc><0dKZdjCcd@k-Au?uoC4y;5(m_d|D4a&%*CmvyH%JYa`hJs{>j%X%KZ`?^Akt*!x=*I#Am)jH+QNkrK1~78`38@fQe}t|Y0e0?ewc=^vtV?H03<;Nt=yrQY&b=o$tFl0-FnPykr*#xjY5pw)U3X}R3Q00~1@2}u%| zNfUvRhicf#9+_uMGOqy94EuoLpm_i>I48IZ1?&_iQIn#Y3{9fr2@F-Ab6yk=QW-)> zLF#=#$rN!{ctt&eyzqBaRZYK>2r)Xi5&`i7ohjdudUV|;Qd%Ic)Huf7rk4H|%Ugm7 zL}0;72!LKRI0n5MuG9mFKs|v{0HZ^sh_KvL*pr5#tzuLtiP;~Lu2W3C&IzPdrqmRt zf>Htyf#ildBE;0A0|UIka^nP$2&+S2vDmgU;ZYMC3n2*5r-&X2SoKI?tQMM_m;v-6 zAW3#QnGm|A8W%$Vts>GPAb}xqg_u@q*y#c~0%9aJ)^$CKAc~A=7_Uh6y0cB;~2> zoN_TMPd0c~vOQTVwIg~RD*42DsatKy#z#{*uY_HT-q(MRWEr5TT1YXcIlhwNXKh`` zH3zq>#TbuE1+2@y^nLe+GT)!ZI@Rsh{g<$t8(&K)9nHI%%Tcn@x~p!hb)e&8u4N{m z5aFaP<78ZFz@nU6ez%fyFH~7?;fLR!d)Cr7=5TpV&pT>P^|SJwx`I{NS8K1f+OKt? zK1L9LKCbZL4}TE1-F_R6AKw8pV`B-NSZ?CfL-!+w4t*k=J&btNH79ZG_%a2r6H^>4;U)`oeZL7fSI2)K#N7U7{Vc{fw^+8XBm16a1q?6tPRoFgQd)r?SS!r z=LmXhfm$21{M{mgb4qdR`8vd^`$MDMdIOaA%MIxX4^vEts~#sVy$CxePok#{#IVGI z7Z4CW{E^$RI(rId&YryIpWR%!3WjCz= zBtN~jxr&xhPNYH%M<4~txu%5Js@#z(lgwC9hrp!Yq@-Rktay>ka3Z4Pm5_j*uuUC$ zwOL?cV-W^a$z!Ylu=H32f?xwFMu3tU3CN5f#A-wVAkslNf>VMZWIE@iq{Y`8qO==B znPpX?)$KqNkg5{M?iE3U7eE3bfYp;l7}A@y<-9+H5?>H`Ly8^bC{B(exNg<}K& zVo8?1y~5Z2skjf>r^m?dK)ce15u(zu}K6HBZLJc zSk<5?;FJ&<8nUxoY9%Z~|6ni^P!=9R36Tg3(L6+n{KQWXsDnt-a;Fra8J7joJb5g& zn$md^Fm;1FdQ?MT@~AYi;2{p6m_X#-Goqd~hBta5iy?#s1i&g_Sl+ED4B;Mth_{rBFD{f8dFXYTk6?z{g%beqTU z+`s-6IDX;;`nZpSgFPJdJt(NxZxA&wcRYq^EmxLsn3F;4dDe7moh-RdzMO+seQ%vE zb|HF8t=}})(|>3Ei1MwLj5~}CD%%_$=9Y?o>p^8L%x_H1xkp|U#FCtN`aNLE$ebWfMm~c8<@0qLzcs#ficLL-xvDr##Rt&1 zWK!uk_A3B_OD{f-!-Ex8s}&w~%4=}XnHA0+E^zkz3g^z9$JXX1h=9GlJsdlJ z96LKZ2%)p4#)u?La4D&&Kv#2VZPF18FfirT{98<;wYi-cDBesNd6ZLhiT)oNQC)R^iP>RV2VdT*^; zC**(H%xBa$XiRbRgM4!3`hx$X-wH&cy(d{{AK^d&Gd&$1&s$_Do84NEByAVhe9DRG zTCNj_YMAL5b?=J7pLy(7US72@I!6=d@V6cds&!i@gv|+sHLkaWjk@0~kV3*gIUH-w)V7B=qQjzK0q&5`?2F!=ef3 zgV^{eU~^|1o5yxcX;X!liP42llkSQdJk$$Za}*>*gv>V;|JjvJ?jTGEl5`HlIsb?n zqwMg5f+;bYTvtOSoD8S|N=RXxSdBqm+M3ZpdNouD1%Lpy@K?$usMXtp1z-g9knBmJ zRdON(Jra8LZi-R8DTuI23BBd=5K&j@p41SLq8b%?N>~ZdM-BH{i5gvWZoc=u?6*~% z;(&mF-rDN{^hpC{yn@bB7;I(AgQ$|;yN4BdADVVn;K^Q<4<*B17WJNyUb0aliJmle ztYwc$64V<@-E$-*#MA?vEZRkl69EE}9wHIMM)N@E*bp))5Ay1fot@&lG0i=w074{% zN?@R)UXaf*{5NCXKG&1eEix?e>NN*!q^#7ZQu4TCgUws>9uv4psmd|S**gtdZ~CW3VX z6ijZ?gw)FSR0idQd?>*LESy}LkB>8>(i1Y%L%W-4r1LKz^NTWcVnS=BYHvoUpYWuH ztU+uUq*6iKU0xC~21qcdzFbdI zS#m5xuER-xp7RZvJVy>67tSg;w8k~(QB?j{jv==*npYbS=XALmesg%)(#^7bGo#DZ zeV22dcqqZivDouqIoAYNTz(0*w>A;u0hZko8=Dv7BcHevw|w#eJa9VU^ge^a0&HQ= zAuIvnT$F<;fJ)jx1AS_Zu?343KG!g9%Fq?ej)LhrovroGoPISo9&e{*fXnZS%1zEH z0F0bM+_7MU)7Dl99s1a}Cv3x*dvKvSV1_M4p;tHG%6(r{a3&BE>B*MvPyJLU%ZM>4 zW2k^M%12TlxdWhFxE5h-F*N|AcKv>Ff&33c<|I& zTy^?>FsqPyaIlN6TjE*Id?qfr>@utl4shwk$MDE1Vu3vz930}}OD@4>mt2lhryfy- zMi|UtoE6&yr<@VJF_t+vl=B+FN076y&%w7vp#0lGiB#dP zgHG-25!WH)!8j)?; z)%-{87ud3HP3Ca+LYR&}tM`*6AkiSG)Xy*EWYVu12WmmJhJg#{$i^&PqqI^(OfTx& zM9knoxdI8RzGo%Q2YTR)k-UF5;-n$0Cno?BGaevJjEhkSoq4D9E6rE7mw?@KXVG2# zM98(z#BPLn1gwC#0z<=X1lz!!AI5{{&tjR>816|*qd1Rq<&F{iLv)7+*jNMz305Jf zaUlU5QZBvCq{*b!yGOk;B6=iKR)Z_IWCo?Q0u#_Pp%-8w!&-J;;medO5P?$x7ZCJR zTr31nLu<$@HcgtL?W0v^#Vy6at6_`o!<65n~@*@8bX$m<|PqZ2O%WVylWiQ z6KF_fq?LuBTOlV$*0g|(KC*d0tsrKk&=N%!21A|;gTY8h#8?q5*OKVk5&&q}AH)@E z&GVyh6-WYl(w;p;DUcw7(c57~K+;@9PftKJx|KfkW;pRQ znGDwOsg4KZdUE5Nwyja{lq)T1nedqR30XhJs6ZMg2MPp`pffM0s6++G%A^w-g2ak0 z!i8}dy`rpqI;yu6%=`^w@a%;e83y`kY`&tg-ey*yT=^8y|Ln?#D>a|+HXIcG8iui7 zh&(;wMb?kgr8xz(lzHomh;rXPXJDWq`n#9|xr|!K(4&{e@3xe#mNItKyeDJ!y*i_p zh%aK6uiLzQR<0OLWgI?KkEMLCB`ZwEXrZqhc<>x*=p=J8vg>I?%Dctlbfx^Jp7We+ zRLrStx0>Y0&@#faVH0LJ*Yj`9qn^*)vpLw7_v^mOxwT}Yk!+Wx7-aepH~>O;=%I&k z{P=NfZtvjKnN#Qv7C3qQIG+97FT@>p+=Z^&#P!!-kCVqcoZmf%z5N4Re#O<;+}y&s zbLSuy`aIIB8grSGkLU72wP3PN&dxMzP3YIUJ#e9Vf4x8a_qqzVlf>zsBEWdG_nX@g zm8Z+Z=f=VyOtf?Cn3kQg=4K~VU7t-7UI35~Sp38zZ)y{^opEK|M191Djas-t<|+j= z7+-3p1I!bzR%xx#KpV`fMQB^^uY0G3#B)eg&TZW_*Ef4&F$y@pyTS)Q^p{9Nxb~WB zaPNJ0BBn#^Y#+l@p7?ki9PVLzYl$;w&*04IGhkle$xnV7c6N4f=bfLy&dy0Jmm3Hn zWcX?4&{Jj_Jde9q4B>+#eZHE()AOX1q- zxiSQ5!p9B&qi4pAJXEMfEQWaOp*lcrw-A~LGq_GpH!OT(MsO;>i7}3hJ99)-oFJ9$ zx+SmXv^AG!KXX8h(_O%>Y5b0tAwT z%^1O98+_SQ5UzPLuv*!G5+D(rR^|k;1DrdL-Ec8vb*OHq6tF2;7CKS{M8l+r)C(sa z9%8lfoM`5~AYyqi1d+wi!xeXEUK`f=1Za}t-i&F?j0Lgf`4DU%S)yH2f=%FM^BRh5 zx;bh4ROJ4M6p>N@BWTVT3m@(+ge#;7t?&d$QqPKL$yVIp5-Aschn0Flgh0KX2AzqZ zULQRy2`UlbIeG+|*q1sG1JUPbUJV~EP}>Tnl+@be6&p<2QBN547!s3v6#sd6$K)hN zQaHKyFp~;*2FI8Z$USgK4Mdu?NW(H!$+L3g<|Rc5YK$P#&>}E2iBhlLE(n^GH;KmQ z5gB|eluZN_)nJ4GL>8D+6(Wg-+Zm5zm*qrqn2scYq#lWZ9{OF!x>miK7tvWMy#!cz zk|0?a1F>j6920ESLrm#GZ#bSL>UzMuX^pb5dAE%CJE>3|MG&k43e4Nf(gOPmK%(F< zhalNr!IYmIbxmXfjAh1}N|Jj->Wf(^tL3D!s08^sczd;6mW`f+a_l^)ROs_u*YJW; zP8`W|FwoGDcShxqwp`WXiz1Shp3)HIK$`Ft@wFagfjPNb3R!K^XM<;z@N)zyYF5w2V8sYb-4TP zyRljwV6hN9`kE_o)uXONzlzx0=&*mVk27a>(Ji*|xW_#KF|D$k0gxTu^V)q<%Job< zUgHM*TJUP=dgVJUy`VLQ@|x3QTG!lus>fNr8)HNWp;&X|&_|K>UXYYB?FDpS$3B6> za`U2-7kLeYnrp1taTjKQ88tw}XO_!N&vBNM3Azik3c1KimMeyqSJ4o{CpB2c=7jkc z(zo2!F61gV*k`Lu)MKc>YhAO(gW`D|JKB1e2v{sO@xa3m0*H9bW3NXad)#~PXK*f^ z1BV6r{s8;C=dkJ{`V{c2r#};qdCU#C^UhBp#)RX?kD)8dl9kML5L(6?Zk-Zc6P@zi zqvl<BRy zyb2Gb(R2Mv$+V1#nk%y^7!Ug>8~qyVIDtzV=3UMw&&hdq$vDUaMKPtphEeZ?3t6MG zY;!rrx}rl1k49}#(Z)p79nY=&W^_UZRrbYlE40R*;n^VUHS=o0+wa%HX=|SAXiEzg z>tIykL2In_b=3Z!>r?+8a7Q2@)(R`B7xXEaLZ^!Os@D(@J?-}(A~C@nxeT)=%2rPV zcXOuvsv2bkjKG8tR6$HJfja^uXyQ>$V01`P^NvL7a8W#u>z?#Xe2Nx01fkpP&`HFh z_20(>0GhKVJ@t9$u73_zyQcwJfl@@@Ex^wE%nL^>hl!*W4q?f4Q?yDn3YcKqPKd00 zI)khhH&XzUg=r80tOgG~fUyM9rqqfV4m=^Tj#a&KL>QXs3GQ|9KeauLm-sYnkf2qX)T25AP=i}%^o0{ZkpNL6YBW%kIn4#S zH#+JR*OL;H1P$MUxN*i_1Z8g|7nfy-VO9PYvMFPbmHY+`B(9`;b4q;vJEd$S0+Hnb z@_!{`JJHlLbHryL1K-RB7mOIN*f^a=*8F9}o2WRAulQP$=|RM3o`UQ>Tf(A*Wz9!Q z9$JoP;Kf5bJ}sAbN(0qo9@b~QJaY>3-0!tzdTD^U%UW~t)qtO7C}_wBEm^jdAtr@OtiKDbK-+?CE#LdTh0j~s^PUs; zYFu8Y$FvmmR#NSjv%uWn4v*1TtM{qcxG_d7mJ96coWLWGJc6sPeiR<}xF_P{AO9HQ zhM?~wkOG*1{euJS@2|kTz;i$Exw!1|OL6yIcVTPu7`jCV?s)8^Kx1=cEq6lJ>uUbZ zI^AwU?~1=`x>Mt2-A)b1dfXRs)pB>MeH{xS#$!{ZJ5yoJnWusT*jBSOC#rIPpo9jHac` zxRuK9uiHT7<}a_-Mz0kTU~KW6Q#jTsI&;Q?b&6T5{9Fkiz)pKQ#^%;GcF&%}V$tDo zkGm1e#R3mK^bq#;cX4nKk&y7HE3U#bpZQE&ef3qi@4owS?>+b9iYp$4ot<8|${_KTmmjD({aXAB==jOe43mPi3>rKp$a7sMI_fUNsR=7)X3mvfP;x`8eOCw7HoSVaWc;o zJKce1Vq#8?7?}?5T zFEx3l(la;i#l-L}Z^frHtkLb*;GE=_h!CZR8mOE&dkT0^GH)NlA=8*mDHjAy zjs%2Y^G)ixL&4prK#3ugOwk^W_yk(v$WNdJ1eSmW1S=8?!?N5?K+b7ImY*OYY58bz zk2uP>mfVYp$ifm6B(dc`b2`CbVGE!0lS`wEE|*#&X=0-%2I~}{UU5m`zQ_&xMd{pcYlKZ1uJI*lhi z=}Fk!+`=b5aVu7bEA;&y0QHvH**S(MKjkU7=Gv?9;QjaE@Sw-Zim= zG-vUzkfrDJ*HQ-b_evVq(hbSyS?E4;pgxByMOQRzv-z@ed;rkDJ(%5JRty3uLG~4UiQ>f80rDdEXKs6qa1iabjp9r_F84!%6I2BJJ1eeX%Xx>Vn2q% z^jQ}IHkKQRTRr;K3X8=8PkQo`u)DjD)#?D7TiaMJH*n3>SK#EyWBA0ax8wHPZ^y}# zCvnAtsZK zJnr${&{JsS`IiKI)PL3&=Pli>E5otw*T-YR;~*Osuo?;4(|NS^+6Xs{*HCePMCe?| z^bg2|-uZp&V{74ZNeAZct!4OB2-0?YU5sljWM%j|qh+$N0nsp@;@jqT#azqKsg^Kj z!v&2_{!|HvB(07ERTxE}$wo=DZK&QLfOx2pp3W!IFg*>4NdWOcCRy`Zh$)kLHlM(O z)$@{~=FXBL?28z@+Qr@{{~V8;y9fIZ-w)w{6#@GpAgy|IhY^>ggs@(MgT9?(wPz1ic#zJSn)Lq>eph!;OkX$qsY^k|~q{VhXx@O=*t; z21$S#ff!)*HegYuR8P5FcyfZGVcVweX{9R2PFWI=Ch*CnT4>ILMvN;H{j#LA2#1X%cudv@vW=@kU_eZYdEr8|xg4hX{NMB81( z1PKat5Cb0K4n`j}5dk8UNb-2<&c>Q@X0|W-TK5loo<`k}yW|q9GR8bwCRS|z__1*Dj_KdWIf@eHZ@n3o4tr*Qu4@F~ZzPT?(K)$+uc z^fn`(uY2FyPle1mrwFby0Muj1@7v_v;EbxD+M3qVb!wjNeXOelRO9LdFOKMIZoaKK z&$Ts&7y0`FEn5oqQOfwbaVWV<#eN*WQ}$Qyr=#XHd#~`e_F@wo8ymRv(o6BksYh`3 z>^VH`X-~n^pYc@Ocke?ubLOmtWk+0m(M33RYzq%P@Bq%9+rx(0{W)BkTeq^GmL61Ib8`9c%2ltK zzQbT(|C?X`+TWy)S@!Poy7 z-C~K2%{XjW5ga?Vg^Mq`0vBC!5mu{+vu97^lb^l~4?OS)PF{QxkG}3Y>>N9W7~@(3 zG$GE6VdFO7)7lI*{A=Q0{%&ow@?XiIxUh9;Wf%tx7iSCyErq?@Xh)5^brDgvxn!zW zjh&KYh00@mTS-w(;u>(Q(N{f}@^95?=93{O@dT4YJ7pDzQkbi+5kvFjpDw%!vJzRt zvDHrNw~ob5OlADO;$_32Y2Z#&D*ogBu5`#7vp&}d8G2w!*^5ewz1<3vx)6q(X_kl6 z%zQhVGYWyUXlsO54dHY$FB-P0g?~TXcxtIK1#jV))fFwlsa3P<+C5KIF zC<)N;hZO{V(yie($r!l79%`HsQKN*z6~ahS^dO(i_21W!faS&(&Y!vy?|H?)#%k{@ zq>t#$*xCiQET5Vmg{_P<32=C92Pp zU;!io6x3tKJ>xtKk<2T>!h*yU5|IR9MhKE~h7t&r1c8wcI>ZDCaRrP3VO4Y^CCz~X zJw5oHXR8 zS96;thb2L0Ca*+{C_Ms^h84L`OANL>6A_B3tuSpOYn#;*~R1YCWMr0ZI@DO6A35m5I&2OZb3k^w>LV18qx2_^m@rR?r z$JZGNRDN`$k$+e4eSb9zLeLO6=j-~>pbS(e_L2r~+dTGygqN~LdD<^BB-$VGPbZw1 z@}c^2el_}=1_P81XDlbSGJ+awy*gAoVR&vjpmFBWLd~P$JILdrS*I6 z{Sl8P%FFJ5=l3uigw^f%_}8UqnCl-Hd*95poyW5V@5`$tXV?AtGn-(Z_#lhho+srM zCO8z{kYgB^-_~SqoHt#OqGB}f5#d$zs3jTn3E?e5{snMd%bM_qxZ zJmp4oiv>~w?!D(8Jo3m{JpAw@*xTF3=JqjMbjc;ySZt<{~?4x3b0;nV#eiUOb(S4~7i z=o0kX(ltI0qF{WJ0TzfbM?^DWZqXd_|zGkdgKiD5B9OYcL+&>D=xnRmtJ})Ha0f^0XlQxF6T36v2+EO zeBLG~v^MQ%V~*(;YzBG5mEjTfHDUI^MDecsTzC55a{!RXK7w~4sFiEbS~Gv%BJyRD ztqhC7YBlb&9J@bnjC+bZZ8(+pCdLSFUxs!|;t621AItqSR@lcl?|qE-fwu;ifAhX5 z6V{biIes?|*7r>Z$xI1Ja@llGj9jFf$bzc&>tPl{pDm9k0QqFI>^L{)!kg0P*+gaa z)m#%Bo5vT6PpuaEI$|giCnJD=) zKe+~w5*C5cXGCH1)=<9&_kS=A3g+-)`&Ac0Ljg7EX-ZKOiRwrYV*)Q2Ni<+707%J7 zJHzM{3l0x<(eL(PUV_6CODk(i#OS+#ULu$Xixl;wpMZ0x_mCjymYb%GM|9BA#)(r_ zAXf$xtrQe=A4Ea#r|S0qB7#UdHWF3DfutcJrucHw{20lS;`XEl7lN*5Z=pPDDAY4_ z600f0WT|Nus;Emc1BQlY2JtFPn&Q}3R+3iD2{yJqS!|98!NKyH_?jh4s;V?aVZa2- zMWdb!3@wi-@TAEewxeZ>bABJuaHXiO9*8D+sHcxAKfx<4f+KF9zvOV zA`90FYVaaaEF!JuxYAVEfvLsO>f&B%Mn_ zM6ghk-b!q<89K#wk4VhMJO;oV3`Tm8&_e*};pO3VS6I#%g6=8tki2C1pI8lQ1i0Ua z8+ydNn3x4DgnhABt+Ez@K&&|LNv}1`Nj;SyT9#aF&t@J5HG%@k))eSGK{v*^9SD*` z%QCHVT2|5Fnl{ezQh%N|8L~2+pBMMt1Jo0Ymokm+QIpMREH~7$QYjB;$l5^XbAf_2 z%BIVJt@+M<*Lu$jZ|H07llI&W74x;fvwYd$nN9hxcs|El>yV1s%QrP_NYFi2$~Scz zbv~3iqf9Md0q}HUd8$K&M{COyhBiXmKAS2wtufSxff=>}^-i-jY2m<}9(BRS2`5!N z7+3^=9?+P=pN&vcIb^Qyb?ckwjt1VZS+lY~B3i4Dl(OKQ_e^dZgynLH&Fvis2>bgz zKK<#tu(`3sa@j!y*xlR5!Ql!q0h>D~aM8sVV{>z12O_#rd~erqDD_w0PlMO#(HD`? zCc)rrQPZZGeO=S1a%?sIYT+S((R*|Izy8MGX^qb*uSo$}UM&_R??uHyO_Gl+vlh)v zf3Ll21I!UrNE?rRTnYJR@g?&UNk&3%#L`MHs^nZWZ$f{0!T_(DF;=WY2xZ@Sq2>r% z{XMW=IcYM;N_ncULD4Q|R1mLq1gBPBYi6a(N-i%cWG#0Lrk( zo!~8T^5ix)mm64aF0tI$(EdR19E~M`tvAGoSV@VDI@w{ZEO}vR2(!(cOpz#!8@@4H z#J%iyxJT1XP+aSXZTU{we+_G)A-t;oezK9_^kobWhm#9c#y^HO$T#d159@i4D;-Rk zbIbU%ZtnS>5#%|>w62|2yREU+mow{(zy=>~jN?cl$OwDhkANCtfRT&t(4Yz9X5277 zvvwa(?&tjh^{Oy|aTw1O7Lq%{C`sO^4<{I2T2&+)px4oh*0Xxw`WRYxlE>i!e||-i zZI*kj^f+wML=PQp&d=e161NOqq+pSxiE*O~P_Eos=nMsbVpQWsGJ${rK>~5IB(MYy z0T2x-U=HR*A|N}37jO<$aTcV;9X-8|=B2UVC03#(s}~{VP(ZB=qqs+b^|T`~LMH&J zVogCQbw}m}17yK;;TEHTnp?76K*W^woZCiW6D)*o zhUC7T#%gIvUe??lF#$m|j3-JFrVFdaV|iVK37UyDWzRXbJx5-ROd)7{lGQs#dL&vv z-EtyC)YE{uGg=_%!a#?TB=pd4JE-?TlB8ZdfEi0v;a~y40pJCs-vh-6GS4a&F+q@_ zz9m4N*MwyIgeBxq zsAL8cZ8sT~Tizl`=-f%e|I2cehFb+9#V^F1Ymb9IT;t8oI;? z2mm4y2lRmLf7_?%5s6HYRNTq<3#}d%E$qg+fLC`4=&%y4A%D(0dEeY_5XUO2iLdBYtS5`7fJ(@hs%R{aR z$c7h{rKchAEVunho-koc=WuTaoy$3i!@r2w z*=lAS<(lRu3E3Fdl08a%7{T!2~ITq9X}LaXIyFBFLCB5!weH^1hL^` zYff{xH~Nk7`pm73ALgpw#n{^1MhF4t&!0noxWewN&PxRleKmul0Pcops-o<0{9- zM3%!2D*Ip@apMljfFDZe;ehU?kSSVo5Jz0K0Y+y2_1{JpBr0LjQ?KZX4x%C{EE$+d zzr?|WFHdfyp-UwoYM=m9SEF@A=Ci$8#IJ0(HsG`flW)w?y*2HZbF1I44N4xGGICKB z6=cF<0cOS`EYQall9iYW%vdg$x!RF3WgeL|=U_KyaBAJp%(jFlU|FQ~kF501Y|8qY z5Mn7U-R-NT1h>WtOyd)qLeI! z!uktrMhg3%tw*aIUaM_yzs{-T_vbRZ*ZX!XxQ->w3pOB?_jrJ>b~Lc#Qm6qO=7WwR zkWmgKYZNd>Uha7|VyxAOO~4UtW8LY6;w>iYJO+{({v>xlfkBH|*4NxAS8GjLxI-q? zxRJzR*ran+TXTX9Z0h=)G8i-gE1=#N>hZCXgh((WhMs1VNIuo=iP2HaZHXyV%$?@) z005HgjgT2AYe)o1LP&xbf`(yG)chD^2|-1`TF4lbv`{bD%GD+Tqj&Vuy(4OLUND1% z5v^P^C(X05VrV-_+4R!9wN^w32|&`E96DE1L`W5@jv2iWBt-NQ(NPCbuuwi@yyD&= zGVCYI$wH)|AOcnSJ7V-EgmF@@h)lywk@Yo+Oq%FdbR80@@k(=g6#ONgfW~>ts1qc$BL30N6iL|N>0D!?k z;5C_;$&5L+@kvW~ClcGo?miqrRtB81kyb;52m#T;k^+%he-fbHAZjccc(NWtfIO3n zu7V?@$2 z&nXEy%ZH+2Y#NFr$-}gg?VC;wAp#BQ2?COoOU7%cR?r02Vj)zX|0ifQtiz;6q!2jM z++eA**x0z!Ea=MQA?p~=?krQ%HN;O zct`Wp2Thob#&5WhjcP@oNzB6_uakaoln&!EX33xP@)t7I9M2xL)qr39T`k*<;6wxd zFxv3*wp(E$tq`XYp2J2V<(H#z`@U(*%5!s_%%K6F8b3xdzVm9V?+qU5CJo5Ipyl>fYr0zmhLUd#7Cc z23*$BG&r9Hpr`xO(=Rlo*L<-T~O208W&s!V&N`i`mUd(mdSXX08oXjD@S8*YY?QDJiH^upd^1;RYR(|w0&P8}x$&1{E}EFdg+Z7;f^pT~8newC z)xspk8)Zynl@U*?C^hGe^|;sa8quVM1@4LqwqrQ8Dvtqe`1p6Cs7o-{ zS8J_mqmc_DQufynxIi=<6jwrnd-vBB6Uy&%^#*T&+Bk=d7XxFP6N)@DeM?AaUF%@f z8uJK7{~ws$QTwfho5RIfa8by!Dc2gB#N6e*phkw2li>NubxNECRuwF~T)ens1coa5 zo)9sK<}wK)5VpjuU}%a^T^j~K+<{ZVN;C{20O*p2PKek>=9Cpo7B$=)B+)!7Ea)Uz zGTj6ut;LMN3a* zBP@@Ixr!$tEWkMz2*bi_M7>{fBGQzy@mBH3f`dc_%asxKCG_<`9fX0tH%}Q$IT0{a z2NE$85ff4bK!Oz#f)Ex6Ac{!PTsVO&w+qP-nhYyOfAaIIcbcSQxIu}axrjuKCCLm!WNos|Lu}6x1c@R*>gm<7 z3VKP*qsruGFN>apLFfTlDa{Zy*d)>wbs8H4AWrB>PltmH_W?p8!cr%K9DvT6XqWOk zuzWsZo<+dQJS~PWLZuiA<}NEWzEqh8!|+7|qabRkf!R`_xsVBJ$O8a1PAUH+xF-XF zv-h>qJvO|V^8AuswmhvTG`*maB@Z)Sr4hw+A>S`mbevQS?WST(BOW*i+lAq-;#UPv ztaE6z6PbFzFo;+ z#y;HR_2RGXdqwn(j z1)k68oWp%JczUHrUM#>Npj#Opk%e8ddImD{NdE(debmyS^O)i6Y|snV=|EH2&*_za zH`cr6^z?PGJZf!P@2rz?Tk>!dPsHR7%SYzxnDv0-ZzAP!qJ%ehg+bA2O`_&(sP_wvzgfqyj)L*rJy%|vb8}u#rijq+eQu}K z=1iZh@y%_zx(y;KLQ3?$Cz`bv@_6T9H7X%ld{>UN%x#o$(~KWwyX!o9TGt%o7|j6% zOp~YpT#i|#^zqy(+${0nsI{-hv<|21d0Yt7FLA%_hq!201_7oNcJf$V=`kT{GA|c$ zXpEE2Oey=>j^V&;VGLZ11IWodNBzt!_nZ)PlmVr_Tu3_VowDs(XqmfO^lTk7E$Nuz z|Den!7YNdzfOY~6Z5jNTZC~kWxrRUJe=5f(z;Zr70wUDYgAmO9Ru>|X=Ks*cffZP^ zgl#l|*F7V8>qm2dsD}WF8htd6Niy9bm@=Vr4FqMtiP1@iC4z?jh=zLP(?L>&pCsl} zDOJ>Myw+z-n!AFMm+VcMcB*m6_82pk#8_(UnhQnLGlSs9s)R@aO)wiZEvzWrQ3tZ9 z!UO{%gjEzogGVAlOt8EL>Q~W=dZz>;HFCgD{9&FL(Gm@txxxf#fq>b(YErcE5td-h z$s>{l9p(g~9wKpY!4@-=As|J8B(1Td#!CQ{(D#-<#_4+>^@NZZ(GsdMse> z0E$RFNK82&6O6Axa_$r&3&RqlFr*cP2m{?q=4GmavwBfQi4a5xiB?e}#jRvH^8%@X zY2^kS35eDXa{`Hm+9h4690Hb-g@-{)h7TTKg2YkxP!g=7g(aI|>i`LnqvpjSGj?!< zo|FOVYj1`J^O|Avtc|8DdVT>SLLgl;vfMr*9_FRqL>f*9Q@$ev>hb2)74x1q;aALP zlSs_yqycga!NPLkB(cysXkuW`L8K{cEWI!hM3LCu1>6XlMBz%71BQhl3Fr%B2*E>! zhohNV{wrx?OMYs}BISE-h@do9Y!uI+UQ%v|C?xc($dyurz{32IX_S!39R>4AR`JgH zerbFu^Vs<{;wu%u@Nro8quhd{D+weHC*x|&k@2BPaXHV1@zIe?(&%HNPtMv#uRB<1-Oy>I}iC zLRl;KXltzg+Yiy5HY_^9VnNtgc33VtJ#2vHdMOLllOA(`dQz z%$ANeM?2@>IH!-c-kZ}2=VWLRY0^1==6Kzdrkx%M57CpPG95FQlc^`R7V)~`rFAnN zZH*9E)Ka9(TLmd^z)y906~x$;85Ir%0ughCZ72k_F`~ThSGloAE^}gXgi5+2qHAJ& zR<``E#*iCvk-=M|9#4I>talf(sYXDZMeO#xeeRCHWdb$dgmaeX&&C*3kFRXQVdyc| zv&IW?CN?DIL(Le`-ZXP#%T1SxZ?O(eUQfn)kdJTOcwL&JAqZz zelt&z2}Qc_Lv9_QSjVEjVK3M{l5DVnDOKcO^H|8!=3$fz`* zud|2N=v;T-A@5^OysL36^;pzuc{K*TY7lnAskk;eII4iC+h0T2ZH zwk||T6?D<0Bq?w9r1?iEAw-?y%1)zH7*UT6KMNy@YRqx?n3>RbOiTi?#I}+%@ar5P zJt%S(<`Xn`Mn?>$L-qa;ASTw_8bEe-rIh*ob(${b=>%u1XvjN>m^x5sbH>7gg&jz$QbOF8APDoBuRPz0f8AD)qo-9 zRg(ZU)<^^=^EMMfm(7PV-!-%dC3&SWYJld0WIht6h#`CcCG&PVi}% zDaG>rDBnwpD=54o0f?=6N^!7vuvYuZ#U)(uHr`#+ zRaOd(wz?>ly)poG-(w7+OjQUH72{|blgi(2$Y6uHF}nfP^`%0_8P6{pnP%j@@)~(r zWd5jgPb6xDk}1$o8#6Mv-y&ufrhk zFT3x!R2GM*_s}|>;V2_YtzVAjEQ1F*0nLN0IoLYvC*l1nMK;G=>wooFy)7$I01BPK z(c^dhfJ+Vhn&f_}JJ;jR_dpg3OxfT&r*q6*u1ff9hx_CCw$^Z+5vKL-+&4#!d5%t_ zILsv?^#+D|Nh+sIb{OFDr^*upg&z6m`7WITqcJ4^aa0IW*(jmzgj|`l(OC?#mcR-& z(5QkdNMmn}@u1qIt<6&ctA0Mo=}{<2KH7TWYGyZQaym*8FZ*3P)=D8Pznkl?^{lS6 zLqs_dX5OUsZ>wx~D}1BwyY;*z3_~(DTkB$Ot$Ac7m|>zfO9i{0(=1`E#|Kw>r}LRG zAX9!3!VDhBpx0N|di)vYF)>ux_*5vRTtUa-$IM!K)Ciw=Zj&rH7(*q=gt4V;mqfco zX!Coi4J$y2_c}vJJ@<*Q159938V_a#(5j#Md2>D{)Cdo5ouhFy+6QU@(ifsITA5MI zzxVu0E_l_*Ax=An4huXPyWkEd_B0vaI)>Cbm0ZWL{CR6F=lb)1>-|1Qi%VQ{VWZx| z`TH_Kw|T9&(ZvM@$qS=vnLJF@Dz8x1DqIs{2z6G*-Qk~1@gjBl5X=4#Ew-z|i+RrE z1hfGusnRc^9w33#@FG^>B}7wXMZluo1jJrdAfXdLz*vzg?4YC`8y4a~5DlR+loJaF z(UkIpRf_7J!J5k>03Z?@qn_ReX!&i3dLbzFkepDJE!n6*^^=%@PSnt_B27BmgR$fQ zMnX&xfeGS<1M=K2gQ3U6ofPnZ4r&PBN2hI@$wkOUC28NKwX45VH=fmsu}#t5K8;-$(U5-sK% z2pkpuWIRFyZ27Z#JMmU~RHtD-lE5ioMOuMq5wt9ICu-k_!9##-Enx;W(S*+kg{K7p z%fLu%VGg}jID(h>R@l1dP2?dIieT$*bSjXB-608tdVm6!Rz)fia*}F}qzXDrElfdk z$XIoyg|!Qi8jj6?62$tnu)mamo`8-mY`|RM$!MtNc~Y+|q5zm-49UH)Y(IjKxYImH zhHnWBFsYG;SaZM_u4aWHK@GNKN`#F!LH7?^J{V6Z?O{d$EFmzVvv<_P%MFSsc#6jp z>)%5;>p>D@^Abs9XwL(7AUd>}#?`VizcGX1r=`o7g$CTGCYJPSNKDp|i}bx{~E z8*(?V=Nn9e;lVEh$uOrp+%}JK*p&U1&+Bh8UruTO^4}=tR$fgxMUZuYEN^0feT(1e zyTxd{SgL=I|&Rkg;{!vB_W zrG8$wQLkI8Fv92?DE@lxvDRK|;V_w@S)8s`a@-t#*X_=|+uY;THTP~yuV}&PC|z={ z`YvId4k4yP17yt4RgOzO0H1VgJ|%if_3!L;p=GFn!83=lGH!Ir6DX#pD(mAKKPwrQ zpPV)kq(IRmKClhiY>!Z`E3gZ<$Hcotbe%T zJ#M3=5SL?VZKx@u8Z)>0`2YZ93$Wv+W5H2OAUDwiBB>iU#UC0!KkpE0&0jfwZyl&>7qI(mf`(%ig5kBH4lXwvLFXj-b!I*P9R%n zP-zm~3Z_Rfs`_ft0wT)R-{A!J!meyIMBB)h$)=a}^jlIST99InBJfH^o>VjUgF;Zw z?bve~)2DU9RG#yU;Mcn9z1ymG;D4`6h*!Q>)13+Is__q1k~B;p?@_>%BvMa+purlR zCoX2f;BL(tHo^!h5R-;6<^3WsVMDYZat5V$7j}XlNtJE(=KT$^y-9?aR2b7(YC%c_ zp#T;eBYF!>($o4r=>da>mN3Xu(m-8Qbsw{^r9ntQ)UcLhMhix;WTBcwR{cs0j;cK& zf^zi}Z~&)>kn_&K^0n9rY8p-$S)j@^!>ZXtHGCM$=m8{Byd&;y6dHtO3 zQ6Lju)bnx)XxIk`S{htH$wODf^0;WycJC} zy4Dv4aG_GMQllSSkt8=b0x2pEkvRaVXP7Ccq#03qQNtQLZxo=4&h8f2NgcN%PB^3&<8!U{5VZ#4rWIGY_{H83c)Jo~_ zhw(H^1euK+ZdPH*p+i)DE64_H&Lhz)=glhmKoYAWZ=Ntz@HyZA#d!1d2%~wSl3&Yf zP7x~=<#qgDL^AIrDm;DQbbEMSwzW>4Z`~UkJxbZtym^Ylm|9-1;V@??Q25)LF#u6| znML$8z#b4&6RNy81f&?7b0}e0^XR$1*R@f$Q@1^gvc#7vlq-u^H1GOmi~p_#=!q$VBd&d|w?p+xtg_5Y5f8i9^Nv>(;5HD39AP6EwWF(&1`j zMLh;xpMkcOS823pg=m$(TYGnNo&CvPTFAwpcqo5HOgW1feUVnzi|!k}m;QGJi=%SBcw0x}@62{+G20gXYBA{H9S_A(=|@k~d-Q3gC4yNH zdl&j)Ne;EVUn&&{vab^Xk`PGql@Ln0X6lOBReB^WlNxf8skKQ+Ly?$sj2@r&%DL$$+1{d7Pyxk*#NlhE-xj=|QAQDiD2##wcSinMz zv=_^rF_iLk14|&qiNgeRP=iqPqywa3F2Otql4#P{&J7^Ya12VrHnNokn+J%R987T_ zR&S9^BQ!S+lP39<1kLv%5rl3;qadPHbfDf9kiqyIlPd9vG^8b%_Yx8;Uy7iMrYyVX zR8}&z;*Y2H*Zejr5Csy;0ixsWBS4nO76OF)m72p761>tBgmgTefKvhsqhrm<(o+Pp zpi69FK?w^txJguZWex~p9t=sVZ4oQHJJEnFh$`7ZNUIgG6&D(|9j2B|2HPTQw z3J3^rPZoh-1=jF5H`b9oF=j1TRCa>r7dM(DnsDZvw%i2*aFL&K;c$Y-7^4T(;jMl(qT1?lhrI)py*E-CFgj*369*2hNDve;c zSL_ife%JWXs_N7-F3jm3g;|l_WL?LP((d^1p%Pd;JHNue6`SlA``)3=rQV_{WWKR zS=(6O_bY-X*%KiPp#Tuoozn|W64V8hjEQ;j$|hl}khD}XN&@fS9JTq%w&v~~g*4t* zZG@;d#q0(%S9vl;tj?KHw$pqiNR9MSw85;V65dI51j-)VKCCB(vqYiYk=MVjeRohqN&v8za=ELkd#!0P1_ zl9r0@AvsYlCsTYKC!rnyJ+$<&HrSiu$B5D+Se}!}5l93#mt_);aXkVEn1X6p9$Po5Jnx{k+pr|lH0FoNWl!gTh znKI)Kla}F*7E%@%37VgXNsS4qPe>t{2TC#;MTW1sE`fE8J?yQ6dZ~deG)#QuO=vZP zfhu46EC{0)076>nNd%f0T0O>s<2(#c)Q4+i;7Cc8I?dOTvZJEe@zhJmctS$3s!h6f z3`QpeG7lpSL-5>1Z~_j3;i1ikTzm8IN260@-_U^d_BS%`%U&H7&2)LdXPS9hrnmOI zI>>XSU?N*_p+ve;<#gY|#i%U#o-isNK(?=QXhu0!jQa$}G{2S==faNi{0y8E8NHBY zl|PLSp=w!XB+s$0LtE*p$G1-IFGEhI$|nYSqiCW}3CraMF248#c2D1jn?7(8?!Wf| z93J*qY;54h8*jvQH$DMz=VF{bbK1fq387nHb0gqS-}**;`V+Tfu~_2#{vjUw_|L)T zf6l8uVe{BVJ9p|dQuZDks9)1(PZH~{HWYSwV~oDL5AtPVmn2)8bQlAH(&yo@Q;BZl|-?;Wae2D zA4hY82|}X$N$UCfTs;x357u>SPcEzu_bxb(j~S->c{E2KM`CGVmAc)Qa5M~Rii2g2r#ZUA-p(Ao zv;V|HO=jrrWK7l767-lDfrAPE1AWvy6+lV^Q+_QuCnX3mqLvxb z{7RnJ0fh>~nvPW!2E|RW5ZMI?3n3&*=2b$bBqkHcbVDpfm0e*qmPF5C23vni08OqO zQvwI*W=gDyP+1l0AVCdHuoG@(4UP~R_MO8n>A^k`nUZTInwf_!C4wLdj~QBMa#=bkXs0p`izo(4&^ScQ9171)izQK(Q(VTKv9q`m@A(m`;q zSj}uA40IA92v{Y-qSH0$J%K7ItT{P85fVkD5D`*9PueHP2uf;*@%2vj4R;AqG>2A}fR=nWiiTlzaQqN6bZIrT zO#2BYY@63f!E98-B~-V~2!ql4zE^9WR?qJ&c1(X_WCCgYaF}HZ;5J^e*MMr}t zJ%C*B!1at0*13Nibqd}-@%I7bbtvWnC?DP2+`wfgkK+UHc^m%l4R6G4pST-)dn+U% zfCZObdJ<21(hc~=Z+aoFz2>pF|G`t}W5i{b9K$_#d;+ih?LWYYD?SI8U3oFiof=lGlEZHSJ($+W@U96_D{Ywvw!M) zb-&~d7Grc-uGzk{<(-y%>hBB2I8)2wb9&fl%16jpwVpBhTc&N4x^)QV!Ar5S!kFI0cbYie+v8(Tew8Yfz7Rl{+L=T>2`v%uL*tNd7@qFaNEEkKK! z4O5(Z@Y&>mk>Woa_@EeN{7uOc`>3{}Ywn9t=6acKsFYHZAvhA%%6v&+THmbOv~?SE zh*-B-ztci`|D6}P0RP-Zo>O{8_cJ4y8eUpNX>Oe5HOEBD`}w3JCQvZP`!(OS#y(5H z8sD@w$?&WV=cC3kxB1HV2jV<7@c1>s%_3QA4UaTZzSCsQN;s5Cdv3e&?j(;52Gq*3 zS25NkWD0=^HLS@;hrT4chpJHF!Z-%TFX|PbVIfHqI{H{_8;f|9j3%4ZB2QEAjCyTm znNCH{vcUr~WXHEtIPl7dp zD4C)lYNXc+DKRGwjiR|Az@nvk$#P^^Wfrg|)D20!0f;rX3X*_0W&G(a6AA|)B?h7< zFa-~l!gO7u9r|~GXtL300r2KGVu@aLQ{+k_l0ZSjFp>sWWEd$5NZ!Lleu@tONyjUi zLxo8_DpHE*A(s4<0SX{W8sZ`vVv;PxiirRj%0p{l4%vyhhTo7RNF0h$@~dNk|mY1yiE!h*$_#mRaZ45vf-Y5>i?raIj>;mVB0it_c(R)PrLH z1p)`n=_5(uNUX-dAesnSjgA73_tJCj!8i%g(>8YF9|64?GL(iyXc_KK4L-*6)M%i2 zR#Xw}z(`232L(naf~d|bI&XG?QO9DYCd(J3xE;*^E6&f%`bI+LEkl+YMi3!j%l{L^ z=)BQ7&$rY|%E?B_3_ot<7l?W#nK3C^Sd^mL;2GAF8oL(I{UsDMxie`3XZKo@-YB!> zmU7%x8XT+)wT3gf(m`bYMjUJ(D4^WK!0kDT_O5&ap7$o9@IH3=x0D}?|94tBN5ji! z(N?E?J||ZOZ}se%XOVIPj@{>bk9qktG*Wvf8g@Hk%YNxPRXo8ML0|q zzm}{Hn(}fujzUAn=Hz)bl*}LIt&NZjhI18Gx8^^2Bkx;~js3Uwip);^DJ` zuz4{qx#21-w>EImKts z{OC{pBsP|W^SgUE{oq5`JAGRBi0`R^VGQtVtj9jXUm9G9KHse-Dc+_fpw+>v!v_*JI8| z)B4V@`T(tzz4P1|*=R7QFShWdoLj96x7MUw%+j6X}ed_oyr?0hes?|$5SyXc zMpj}PYM;Bn8$uW@L<@j5A)Z5k=42Z8%l`a(VC?2U=XKE{bagx9cPT6ASToKn2)K|w zX=gg0dR#T*YsGrD`p?LoV`TjO!G&y{%jr5|vQ9Y3{|d4N(owusrnm)8RV75OiN(O!2MiXmEvY8K$k($i}W z{ibNiGTHVv0kMyO39w{IOk#FyBcv3uwY7oGjSZYVzmIcg_dpVJiM%;NUp~wC496(B zGCNrKspOb&;og{EL4@n8#{c}=W`883b7cmCeEvKm6dV->RDR?0b%8)olou@DW6s8* zi7-JlGQ!v}bw9eWVh|@|j-47qBw5Z9oukvFAf|X&Rj~y#VuTtYSd9V%1zbAwEQEHdwAswkpV|LQZI%SqvpT?z@#Ou9ae*wE1D-w z%!^c18HCdu(s8>X%hx?5&iOoyW2uqD^1CD<2ogF7h!P+HNfLTf%ogN{zjaJv+B1<= zdIBRr4G&-qFZMX;VBSL-G8y#X1*{4p3y=uCX!7Nb#7aO1dkavH4<(gk0*^qeI;r9) zkVrsM<;wsF1|ufjmlOj+P%k16hY0{g7!(Nz0ttc;-Ge4#No;FOS~Z9n2*9EbX2=o^ znE^n-42V{NCvnOG7z5_LL)qw|-Y>Q{#lmRp=(_W*PTDsmGa`7}8cFCZ#>I1#1hI7_ ztFC31YQ+u3g^t|drrs&ZXqJ}S)`Z7o?MbhA9RbZNW1&QDI2nqzuvQ|1mVVb{v7H%{ zw5)g{4JC39!C>-Af*M4FC){N@>g>F6WOO1*JJgr-Eoz$>=ibh~`<6VpqO_(ZNeeC*4@A>u++)gjd_d6|lwr zRUS|(k}bJ$9gj<7cxY>0%8FS_C>2rPYZ(z0zJ^sJo>6k&IeB-EkK}&I4HxsL;w^c3 zG}o%_I{XRc@1kD~!%Kw$Wvs8X_*i|Pjpinid^+y$ZSQR3!TayVt6uXuJiHrl?6S-8 zwcq$Hc+PYF8Wsz}BafWLt6%*)_}GU(h`a7Rhu`_#-^2g)1K*G1$B$wE;1IWd=05D6 z*~M#K{xXELk0(C$$++=}Plv2_u)nvf{(dFC6@S;E>-D(jWbBdrRlv5ncEAv>){sx< z)@+?}-MVtc8jOuxN8v^5YWAD;*6};FAz<1Ujq$f=$My=J zFba#z2T_eKFJxSwtpO(QQyO@i4Tj*y2N^IfxEQk;PuNe_$AWwLPWdk|ManuRtfg#~ zZ5(C1sr#w#t>2*Q5pl#M>VEtLkL--HP@G2J&>-9~adt6l^}P-`TkqC=wcz5498s1s z@;zX%kRPSgwl-7UceA`41rt)H%!P=G#B$S(I>+40du}kyZ5Qp;--?KN1HlACjQShB zHv^Ay>=zOZF zC*s!=DuHGk$@3YQAw;xNC#3uTUpNS3yd6(ON4PS0LG1}3%y zV}+}I`FzH})TV*ONvycOK>!VgboDGzCKv5JcID6V-naom1mIvox3JMdF~fkZVNfMr zpw&Yb1gu!lCqf4`779{^e~RV_K+(KN^m_z@TVyp}kZCre?+Jhatzr|67L$3NNHUGk zi~9_f>kmWn`twFJ2N`0 z^sC4)Z=MJa+E$bR^yU%3z$%%sNz5qCmUjjLJ@*PP&A5z^0`m4FfGj|cM8mO2fCxq; z%jaWzg{)c=U`d4`X8B+ordZ^2GvUwNS_A}lG&VA*e7fb{m1 zjXVG5EWfd^dZ&qnIazslp$;kastg7-4zh(8X<}djS;$!;Xc=%bs3R$b?MRakiwKBA zrm#A5hL}$hQ9>^a5vMuGG+T{>3Fh8ac-RCxKma85Aaux-oiq$hO@0EBh+zDi0Q50h zAC8wsbc}YEa*ZESj0`9?i_BI;26@fyy8$02Do2mJFBejF!1_Qug}UD3Im*8UGAMb9`h@q0GF|zb!s*Dcd91l;J{I zhBbcDIAyOx+x0_}546M49JjVN@ve7#1ozzY5V+gISANak#CN>tZ{pnjcj5407uVl- z1s?YU--jRh_dkYr|M|P{r|)_nzW6I{#&e(Z*Kn{O@#SCr_4t#wy%X=f^#SajJ%v}i z`VIJ7FZdd~=v%)X5?5ID{h;tSbRv5&!I3##W+J=5!7&6BP2iZ;;9jM})$m-WW7V?w zT)F3#QHH%-d;_1#wnsQ6>+!iMFNz-+#9@y+JZLOm)v(Ai-%LME{T0*s*JGX2EnE8Z zI(W3;(AtAy9`f~E>+#k5sMUU{bI#3Y-R0k}^UAJ`Ij!%9zm4?Kpnpi&mXOKtKE z++pk3F}(f{{uuw~jjzXb*IbS7{_ekz=YQ3UaMyiWouekB*|1P0-V%|VAE8{Iyx&SX z0rN^Ap5RS>Co}9egEgr$ulI<#lM>u)P=r^j7&D}rcD1h29Q~e&ab*Hr$Ejq$o%ZSX zrhm;On`;?@^4Q?a0+V+}@bI?^m`tA6g4-IiPR`9BmceG+Kx z6RUQ{s_sj2$N_L8g9|_6dqW_phok@rWD24A7?CP@obqf{?IZOvAW^Ru5j$~bK_rGG zAlM0gB2~i4_?HHOXub-U$D~))uY-EiBvIvBJU51ssU#YB(0MiwM$UnwC)v7!UddsE z-z1sOMgR+F^3~ov5(tc#oL;KZp2&lNbl+&@CN;F=To{Ib00x5x0I-HusUjD$_YnzY zC67R$kf7l%ic3r#Bnf)bkRDxMHPCp1-=*bh31DzcTGBcMRmUY^<=TPRy4x(Gp%|0b zsS<;+CNmDOd`VC*AH)Ps0-?^zRjVRc3op^HpcGN7EK7jH5VTlgxXC4DL{4L9~!AB4A-l z)T_W}I8hq62@<0vdd*uH&|RqoG}C}fodUv=9(K0t7@P(zb(na)B6$ z^hT^8IYyMq+sF9osq~4KliKxe;H@z!>*mUrpJ{=U&QWGSHV< zqk%|H44lVZFsIg`Kq(u$yyoK=`+?3Yv^6hvWlxQ7(-30@#7Yk)k7OoC0mgm4wQ07{Kj#a*{;CET(;ZxWBo!X?=b#j#SoA#3@xr$a6K#gq z+8}j;&$&<-Hxdjo<`nVf!kk>$E*Z!iqD%nQm}5o?6Ox8|D)_W6aRMorFp){SM58K6 z>#mvz9JPtoA+j^|5>9j9x7sgH9h@K@m94&ftl^!5{qhc<+a9#j~IK zWb9nDz-K=Faa?rzA*2{_*!QZa^%0@#G_OyJT8cMC#AHby6A)uW*O}))ih557yZeVo zDPghbka!cfef$&HzT`@L;!~&a&tLjO_@zr9g{M60D{%LH_h&_N%3LbkbOPt{W&`de zT-PP`o%H2GZ*3rh^(m6q-tow-JY)oIwC6@BKiwB4W2rx3&?rAp9*ZH6-!18wh(H)a zctA@bpTTp5rOl!7&SV@IsCqre5zJ?po5PaG!kduU0+K|_Di3(&zF-PdvIM0uS(pcD z(#+s&#d(WWFaa#=!P_<(oswW7mhe^x9NChZ>VhUx#f(@zO8fv`Se-SH1k$f`+zd!q zpz9du6^k?>u8y5Z4bGlq)?*aNs?rE)DO)$R1r7+Jm4`g1g|26^Fka6&rA8oe#nio7 z%z&W-wvxRN%cYYnPYNMH4Xb`yOhzMmk77cyaD$+EY8aYGH)`ldM`ZbHV0%E@cM3+r zxfD1^*DWPNk{%rN?HIMsU>++0>M5f_j#fq@(#k`gtEZ2I1K9>Ik0Hb};J6EfWFAI> z{l#&_#Yrp=9|FZa^>2`b$b!fnDD?;=NC-&1fI8DHgb@>P5H82o)^UWrd(ov`5HVIn zNHJiUfW#4zRiW*m;=;;|!XycSHH=HEV=X~}(8UD70tXEAasW;N9dBzMAUZ^(4(Jx3 zeixmrupyolSNRT0v~V_6I-K^1Se_UNyyQJG5F`NT(ZNI0f+d}{M7QL75@12$30*A_$^5l)b)uOzOF& zxmn1lDbz(>4!d698?dou^^ojZxWgat?0f&!|P=p9dfIM%TS?DOkprIJK zOjwL%-8HMo0V6|9IlfjK<+Jkd*4$Md0~VXxkgmi2{w_}4cL$C={fP*?RD+}l?z#7V zJp1WS!ZV-oG$bL!!(E);J;1^F(?Gw6@`u8lFS!`Hbv`*l=NlyVOF zfYjx^!rP~1@G1M9D`Q^L%340I;aASLr9XRHEf1So9&PP+Z>Q9=k_^(X=Eb5B-OvvK zKP6#I=l|JWpREB#03gN~U7^W@Un#cjVtbnW>`{eTjp-ebZ8+f_BWVlCN`^NgBIc@I zDzSO6{mk5PkN`Piv6lb*9Dxo^b+jt@8aS6 z@5Zy9{`q+DkyH5JfAV%b{n=lp#4)9ugxklS;o~sM!j%cnunHo@Nk_}uLKPRzi4$uY zQVJT$i~=LK?rOi5V-!eMP$opYFy#8X&H2e-kkmp5+F+hdiR;DsPjIc+LoMU$ItEp? zoncWJIqNGks7Cvv_X}^O>HblrkBauxeBnk1fW$mg#LD`~uq`{Q7?WLvMTTuc&LrJ( zV))?~A%YBgm+Fy1I)UPVv?31C%koKQ0oB@?KX3NWenLSle|=Eg`a3`dwks%-=_HhDr@ zke0-Tg>^9p5EkfD%EBrnK~tZ0i0)wqLQeutE9@?>M7r`hz|J4$PYf z@hrm0E0C^z7Ir`WC)hYR2ZR#{)Fbc;lz;=-(6ym0&?VPl@8EuP`}boz0BQ44NRRz; z9PXb2-*ykscUUYsKn@`>;E*;D(;*PF0@gB_cas2z5Nvpe83|$}9A5Wa;P4SF@BRP| zuX`R=s|D!ck7IfLbI>g}ad`L55IzrF^IYs593sR$;DMX5ICr;tiw51NQ9SoiKrg*j zqEZYet12n*oO7D|Hc)^_L?qK?O(7swrJqgFkD~G!Q!tM-=q@BcjYzisR+_jY0ZWfV zGjb9Y^u*k*S+tr`6wS>P2o@5ehM5pFltz+{Es_U{k%jXq{s&KL$mW41gMtqc2qiFe zNHJ!*kdsIz%PW=G(@d!+lMFtkYP?k(Q~u*QhLXvrTAhqj0!u(l5h+Cgp1(=w;s&3Z zwvO^{O^B$!Yw~cG-73X$hLkmu& ztkL&99&_Uj*xuU0;o%BzdgGh$_#3as_19gGv%3e_+uO(C{tBz}XAwHqJ)a^@o;ZQG zyzf1D$J_oGFMa9%A6Hy)1^T!`+J6LcxB_pSw90dU@K9>LC4**px4I@ub^X2ZnYnq_ zIO*SH!-pGfDmb?Knp6J$N)Qe0uX5}t*LPl3%aAGS94+HOozHWfj#1BjjEALO*2XC^ z9hZ4t|7+>EEst%YMpVyhGAEUPETNI}MOyn!tNnW2n~b9xi~pX@oNZbLxlfr1Ev+$_ zytHT_k&!^D&oY7YCNggy zA=1z;0wndg=*q|7+?9{P;hpbA+IiL0S?i3ZfV? zEnB3lkS)RVA9jSpvSrzl=&&6Q+9DhdNntxkNkIyvxIh3%iXb2f1Tceb0LeRX4n}*KJ>+jxs&Z#9UE4Rw!`omb>@r^jY{XF=%BES2)KsTSo z@|B;(dT|T%p7(*CemjnzeGYi+gTS??f#WT#uYUpQsgI&O@+8);eF4jxFX75FAI7c2 zH2`NQcisS9xeg}5`tq;h?BZpdrRz9<;ypNjz2I=X03Ll3<%01#^=`cW#a{%S zzl8GiyHFl`9C-dWkl+4s+ zc{MauR#}a~)eBA&11BO2r&7bPCt$1wUFT2e>F7wCN19ph0h*5|$}7qUo6=9coHLbg zBp5D$nwQQ!CP#k7%$6gk{npX%D8srj#=n#IH<9b=n1=%gYfsqui;Zu>I|@7`(2QZ; zx8>7uxiRuXSI%Zie1%tKcY|U$n@5C&v?Y#kh~IsEfbE)#v|=1L-?)iy`le6dTfg(W z@L&Jb&)`eXehvS}fA25i_k8bn;#v!{u0$GhK zdpe2HWeb|Dzn;M1IKH}egVA?1cEolz~FdT z6WwYY;|QBkwmvo2X8Zws#Tz;q{T4g>=0F3@7;|F!4|KgH1B{oFizwQI)~JDM4It>L zdSwcOQZTyP*ER$2qvr{;A~{<>f*#l#hR8dXe$0_5K3fH(Iwy4ZMn(G+62<2>xnrcU zwOL%<9&BctjA|P_@$23220 z+lVEbw-!_N&>H9AFq~aec#klcjCDfIr$l0pc+B1{rIoG}29E0yOJaQMH@y#^{P0uw zM`xN;_uIeY8}U6q@I$zH?+Ad9mIX`FGPv$WPVoE|D(EvY%DP%f5mkyA3b!nRD_5@I z>e+&Ox8B6hT|L7?H?HIA6#`#Pc=YjyG&y9MROyNHqGr@=XP2l#a0l}}IM};|04Ww$ zGJ&nQBH)tW1fG$Q`&cO4b44j${=1KHgQM~Ouy4k`$KPk)BOu0pT6hpTa^)d;8@jf0 z%>=yoy@LtR?HfS(0~PWYKty(GFV!$xAP_6HqNUN52@*2al96b!;p>S56;<$Z$pEG5 zMMk8RiZosz~$*RT&DNT7y0M_GO+`9fg$T$2`z!!fJ;0wI#JANOoe&_>u z{q_Z3`^*QhzW4>a`SCvlyzyo53;zgL4%rk*^)S%T41pUuG@?do=#T&?Rt{d3Q5!Sq zcoV4R!^j%1hg8H;hbT@H0V|4y`LKqIXqQ;68qk^rnSn(#_l)NZaKjfT^+NIRua^8B z@TfZ?t{xTO)N;*`%O3(TYbkD(G%F5dQ1ymT0VN|@+aQ7<;aCJj8AuD*3_XPe%m*xY zpU0K+ci`gcQ#e094?O(=;B6npmGv&J-nfqSwJ$^N-U6<_4dtC5*X%oIk731vJ5RkG zN$%sy-IsCY>Z7=G5b)iXaJ8JPF>bvN+=(-{}J-jA4GZT1CTpk z1m63ND9YF0!}9ubIDhQ@kmGAOyznA;0c1S~j)GMZ1Q}q#@%Bq7 zx4w$w;c=Apc@2Fz-o=%(hmenVaJ>H-5;F>G2#}XW=aNClR&v>l2%^=OnAC$sPvZz8 zYS;U-C)$O2 zk7%yH)$-`5w~g3d0MuBd?%WyfiDFC`3^myKZikS1zK|MEVVR-SV1;@R0g&PrpmfAv zhsy<3g|V>3RO9*eS)(yTx)%#TP|r{i&m|>Qo^kp*D&wPqpzzdAsB)0DNA}*G@qLk& z;;APd!aLsmc6{QSz6sy{9lsklo_+!^zw#R1y!i&+`|fw(-}tfr zC4T%b|3C3#|Lgw)uBJ2GJSY5_|H+@nM?d&(+rtCu1A& zGqyd_j6FGSS6>>|dyj@qWp!-j!ck}1oAa!#_{?4(d*MYs=J;Hk$EkuQqdqxJ63iOv zm%UV<2zs-#qlF#}{7$alVG>@u$fW&1;DppIT+CA6jNEM+W0vt5a{6b#U!Qd}mp-RL ze0CP%60cMAY-1cS_wWZLB$0WXGV;{F*y@EHBOY>soFX=-CPxvKM0=fWfEm%;V@|XC zdkW|LzMuMqXAPw`eGY)CjQvEP^)*2_ks>>F-_Bt+_gT2ctW^2f_l$UsV;lQFb$|R! zWUhH8(MS>Gd>8lcy@9jCg0nNim8*<*y!{zGbn710HG{dufD?&^RTK@W(5N9R6HHg& zPRof>Rm2E{Wl1|a$n=z+Xunedp#))< znh~!T{!qPTXc$jNSKQlVJJ|LSjgIZ@$r+ye-ix_Z_V(HAnlWpj(b0B~t6BOoMmX-< zh^MnWJ0j-NKfrH0U*iM*9#oNc2Pf0>+6jGys_!-BA0SP1>Iv{b79IgHH(^zCVsPKlF(LqHWkbun*Aemz`(VMZ!25|&z4I)OANN7x7IdCFA*Sx6vA>H%OUHr^Mg z2^R#-qHTy#BzunLe}7LcO3 zrHG)}N*CO``zFpm|0&@8-wpoI@4{6%#|51u-}(|tJ|eS*9P&fgAlDa^mp+HHyy9?` zaQ^5cxc^(90lo4(uD$aioY9I~ufK@%^@zlR!`(Nr<_qBd>$q}QaO>s^pyxh~M?U#S zac8}Q{H5PQ`s90Y?cwWK`H1z^XTkY4=;6mvzWQmh(>&AE|$Sc5V2+^um5LrV}vret|$E6w#DpAAL;du5I)R)+xEp`}Aiw zN}(&hV)O7R8}07+ReVnb9!}!)KYEAyy_p;}gP)J3j@iZ&`F8aldH`i}2Ae(pu5%N- z_S%bh@~OAs$NsH<6F>2{|1N(1XMYZ_zx*hK~EdUahaiG)|bA#ok#3v{M-gK-(#bZV5WzRW1Yz}ovw01 zo@&C)e4aCS?ZRSboMT(3u2auwu00n6hJ8KJg=V_Uo?Jiv-J9R;nvQe8gyx0&V_W?^ z=bkuQ+z4Y1<0kVKtun9ih=Tc?zMkv97XyqEAcjx@N=ziHr1k|`{7kFlC-U$j(fj5C^oLhaz1tC`g zO?lhfbM@z8J-px9yz4sp)I!^*-1p*|6ssIV1zJJH6j(}Un)go%9zU#j;>LX(m>?yg z$lB(sNdy2CNXba)43rW|UR6LP%PBzF_Ogca91euE6dW(^BcI>IdU1{`Nd@t>Sd|Y6 z)_9*wTqB>eO6PSvp3kYn@j!rmO~lt9PH~j>`0rKuKoiikh|0cD#TrQ25*3EN{p`1& zz?`t}?n`VLmw%q+mzwRJQ_nbzV@{%~APoC-ubq*`jB}i|5yz$P4BmH84@lL=tcnd4 z^U}~86^B(rH#x?PDg;={A4oC7iwnQRn%#voQKv{!&yXZoH3v9i7h_`<9L1# z&wT77c>i~P5ALq7;K}d$T{!>Rui)lS{kwSMwHI;q#v^#o$G#UgzV`>QT-?L+Pkj(~ zp8wsDFaIRydIAi zJ7h>mpt4%IVZa@YitVA6q>zzH0b4iz8hU&R@paQgEG)xJ809UUBW-InIS}?UmmGJ@W}%eEj=xIKK(G^&B2~ z;_b*6_mIvKWW9$w$0MG^Ij-{s?vvm`E^z(T-^OE)e*^L-{~#V=!TIx_#^aB_3-=!T zAPQw1j&I<~dLMj+aqE#s@yJ3rwJ7egi=IrHN2mx2 z4k!>TlmVERni=wHg(_&C7Nx6B0|*zAK?E#BmUP?pz8R)emMS1x6e>kM&@>d(am@v1 zQXCfvAxO|Y$uI+9sdPSR{2TImH1E2e#b-SRorVd;&&~72=i0V*d{(5Du&zdxAoO?n z(tJ0Ov&cY$!)E|WGmC}-HG)7*x$@BK;krz&Qw4-73}?z}8!vhp&Bqo#OPPCRE8nv3 z1d#i;U!v&E`1tn0{_7r}j_vw$kw;3Mqc?<>A3_MAgg0J$4c8yKfgk;2KaAh^J>Q4V zeD(|Y>=(a;7ryc=Uik9oaDML&KK+?5o@WGD=*`T8`tmy z-~CM}5Ul4Hxck~GO&$WOanFt?x&y!&O!p25AC#|mf9*5xJU{9qQ~5!f{4trB!j@x-w$9LI0V;kKDv69VuZJk2y*{S%R(m5l> zfD2hBMlfkk0e6`POqc*v7NbrmqA0zb7;D50mdU-4=xdyh>rwVYl&w0vZSVRR2BzdE zQUg3Tge|-|Q?mCM9)Un?C(Z!UVFB|2>ngZ+_YN{K{;Qw*`}o`!KZk4Au7U_SzkiO5 zf~(7-UJ0w7GM1cib~r;i9B@2d;NtuOg%hqM733CPkV{s#@xuWC@YNS@;#0r!c`Szo z=j#zyuiQZ5rLIxWfVTvet3nuI+^(L*exkO>cZy34jML>jh{Nke+n z%ft*8P$K|k;0hh^+GjtF{HdSBhraJ$#25bI{|C?it^W(;rB|?=ElBACSAOB|;N8Fb zzs380=r7>>#oq>g?Qes4f$##)RU^4zA;xMXC5bA9ep14QBVyrHs;d<+B~ye~YmYRC zjpbPZ#EdnBBIfDB8C1+G24u*d;mW*T>M@W>Lodj3cC4g(w~&R+0PwQAWJ+%lKqS{( z66)ZhVIphM5T|50ZI}~0ABbxs1&!7c6+kpJDkU9X5&=Xj2iZcKXX35J&?SK^uj%y-TVr0?{!>xXr{p2Yd9&q40o zMES}u;`*IipeqUM3%7Bg75vp-#hv3ltN@mD9mkixh^xo@;D;Ya`Pz%Pc;h*oJ^C~r z<<}sueig^BeHn)Ds|_X`7huF(uL3^$Sy)+!4Cu)IxN7>Ns3F3>c} zR=&D;m7rpBAVJu0-^o`_mM1CDX!GU*sW;T28n`v=NuYaxZT}Wa0PKoFN(3>uRr(}; zpr`ZA7XGK?)I81vS{=#YeOMA0gmb5>qJ|-n5Oz1@q1*EcY~zqBBkal~V(?97L@G@_ z96dC}gvu(f`tQNG9u*+Zp;4ol&UF4{Kabvj!Ndp*P>i|O74n62$Lpc*ZgJ-P{OsVS0HP_ zty^EkGmqTBd%ypa_<`^LHr%~`foGq45&zj={VRC(^S^~JeEB*2)KC3A{K-H0&tX{@ zLV`QDZ-a0f%^QeO3?Cpam-q_y3gncB)4q4pDZOE;WZQqD+z<^^Q6}EJJNY~_zKmly zWke`mlAB4xKxCdPbcQQ?`qfMZ0?;Y}4P%(XmfF4=jVOEl%*MM1lkqyG_ey|C)nP+Q z0HyJEkTvL2-qs9SwnZPYiiFw%PBqd$TsC3fwTB?%n|J z_;>b9yuY`Z>ScKzD9#U|FE64MHoQO6IpdlzBDmc8IsP`gcH4lBiS)!*q=1T9*{Oej z)83HHpFq7~DJaq27fmo@h6gjH*IzA`023lu30QMR5kaN{KJ}UB@k_t@Y{b6 zK<~xz>Fc2Nb4d4JRmB-TK7nT>6cj91uOLyv@y-#D3&6ZZiYd#G3=l(&Bwm_1S&~wt zd?f>1K%6Y}Afq5_?hTa$%~up~we#IKKE>;I9clXQ0DXoZoo^xcv&20)Q6ed;$5=Z)%cfB4kc2=L!J_Q;drM zs~NyN4+keQPp=H3g0mzL(Yo41>fxf*r7ZW3D21#-p`Z%drDO>!E~L@3C}R;fsnHmfq5 zS-CzJ?fY1YdfpJ5byI0&$+;#^R9e85hGD=ZLr61t->_*`ts>#n?2Ca(AL|N=;xbZs4o@i96 zF)z-;uOdNqA(xjV^y9D#Y(2^X=GITq@FA`M`5NxaX;DU+!M~Fq2YG7qY*_0k1B@Q4 zvCZNYIY+aO<@far@0Xxc=DNaQoJqxO@8+(qX|NE%@$l{U{!L_+P?*@PGPu zaQn@h`0dYq8Mp7;!__NSaDINSvL;*juoXFJv8QCuU_Ld_ z?8pP%XRjVu4=|Z|bL{C`r{u8N+=$$>Pd4)!<)t{E zv7eb9Hnuf>R}gGvvQZBnWn$fzjH-#qd|%Aq6=5;eOLpKjduIlRI9CqujANR$d1_1% zH)3u|ds36w3m~4{g`o$Cr6~zyfwqk%y!C5q7Wz7_W`%bG#@+93HYM^rEh_xETW1|a zwa9G|uM_Mcc5>V=GO!KVcu*zW@6oWxBF*~WD;N97=E|D7k%xv&1X;f zYQJ5M@TcJ9a}<;C#uI}n(Ke&Sp5ezD=-w9-hNSdJzDJn$m_^^k*8g@)z*O7yKLicg zV%7wflPd~f(QCpPO?yr!;Z-c+JN0>Nabk}tx)7)a9;8@PM#0_Vp9 zELV}!8RT#cM>^p4{eoNf1aI60?qESVJ0KI|f(ZAKa3Kj9XDC>3K@08+<8EfWc`kV4 z{t*|nK$feJbcXZuBhJq+RPd#Y#DtirY{ZX>>kf}9ob}Ic)|H?qgzoMw`+Kt6OJP}K z+-8MW7}MCFDw%WKpkxtFpIweeW{rluY8{;@n z;U?hlFi;Qy#9&_FiOY%`CX1_TD!{PiebC%q=8?f_q0K1ugkDpa6!p+xfsiIG&DoR& zX2>d5+F7)U4>PC$ixeCZ@V4LgFW`ND_AlaXAO0?U;TL`e_{!(-@o)Y%eDI(9)4&h@ zkFh@XNt~ZAINo3J#b5qOJbIn**q{1e0e|q{!ZAGpoS&yjMix>CKM?+UoWsy zK}n?H8$>l^#JzsNi{^=uY+ewyju5K^L*_N335qi_vLyud%0-Z(d1z<_MEUDdR9`Km z?XW34VmUP`f=h1ugb|W^sP>hMm+~zj(Q>^R8JVThG@@iBg*8dHm%>h@dAv(*UOg%- zYP)%Dzb%vioPn?aT!01ObcSq34;`{5kj>1X!x^rDa3~oh3rbmVu@*J%EC{fFaI}1Bhe9?Uv z=y)=z$5uHW;iZ=K72O*#WG2!(&pWtLp3Inn&`zZFE<5QVkGP23+L@J3cB8-ZN@~>E z6|iP7=ycKzZyCp1N@jm>>N5O}PB49b;GJ$HLWSA(`LnYtAOimG-~Q|PcmC~v8~?>m`~)6(^iiChT~*`i zT5#{)eZ2VGv-r?Ep1_CR|2`a7(el*yk5B`+^UVEu5w(Sn@=cX@FqgIB-*_(?CT1{o z+=%lX=RVUpMj0;hvxqlw|Ln;VGrdWSFGb_xOmUC%7-ht$fbYp$<8OPq*(rm`sN48> zX0&5`ZoGE)L4fb6XJ;)t#gNH9^Qe^Vl;$$&xbCyLI8M#*Wizih_8mCS)?`n&o%I#l zaC&GntGUWNx{ZwI0*q+uxSSI0U+w`@>3R6vjWK>%*YyYhTq{`kBnb>~eFoyVU_gxxNZPtRD+wlyph)N>* zb()QFglmJ(gUvGvQ3R?>FVsJ`ZLEhwm8^y_d4s3IP5k?q6Qt|g$IAdG)^c&sIQnNE zeHf2Eauv63-@>}CxP9j?(*2_~J0Hepsc7OvN&wa%7`78La!DA?I7l|H z?jox4524ho;}XM?2D}eTQkBTqo)N5$sQJ5_{SfzX+y^EzoQ&N!zuoM4kr*v}m$l5-=yw zQ2=DfqNtmz!bc#_=0VV$G9qSBadqCfS`~H^Rw^J1J1G`tl!Sp|d3sp)D6rZX6+UFY zDsarxLb4j$L_i)wWg#OhgcU^<;{p&4y3U3SDP=p9Vhw|`_6ut%Zy&vTkEr`FV966u zBxz_*$q+0Sk^&r$ngfKayiO{Bpg=f*i(sLG6`FhH41z*x;PK2YObLtyD>AYf!DJ!d zP@4M#9#*FLaR9AuwJ?w}%oqbmF)s_FkEOthR+(FQQ3IwVF<#Y5E<>zp5CMy<>LFz( z>%a>299%R3FbfhLU_nhtW_TzlR_a>uk3`IKi>z{y@(6Wb&$h;7E3Rn`6r=T>k@!ZIZ)6&WTMRs z?WLFSp7*{N&prPF$WG6%UB8C)cn^1Pz5z%BkPJgu#0+290L$!5trRydJ^VJ`iT26U#R1xP}4sMZESn8^h}N!b*(+w zelte*@ZNdW_o189_H>oK-}crCP<=^B-k=w_+!0>u_JohaaClEX9M^ef00VmcCH6Ny z_n^4*AXi+c(c3BR`Pi>NJLb-r&tEq=Ro%^WQ?Who;gZHH4X9It$%9^RDFjJM5uMm@ zgL=!B=DTEbJv9}qv=Ah>u#Hx+s6^C013*W@pncafy(yB<7}|*FdrHV&IpvP&!EwJa ztjhRyxbcYivCUahqlj?v`7vywwj1izH_nX?X)jaoHupvU<4K0c&DIfNew6wo7W7Xaq#<2!a zRY6t^@ej*_yB8}y^^boU7wa9|xqTB4Kl~UZACWHvckiDAIO6wz&o|@7Q}4jf|H7wn zeB&nW-8-UC!i{SSq+B2^3y2rw^$02pZr#1WB7%o+oZm! zH?BO2N1u2UzxXS^fuH)X{sFE&`Zhdt{TlAvxgUf;g8>Z$xxxffVy2}-#pURh^NN6) zm9+q4#fb_#6Y?BTHJ!u~0u7fTFXC$@m<^{jZ<1DsDJDrR%$Bg2tjdsjn0Vq)VfFSIJkN^UP zWc3P4o^G@N1S@Ck>L+<3)MOjWgHM!RI$vW_b$d<5m^L< zOgZPI;S~@_ArvMgDVEDbutMb&67}K{5yh*b>&J9JBEc#dXHd_Hm6XSNWD$xg8i$&f z1&|D6VNg=Y{cA~Ayi-1U5`EX-(bc+_&apjJJ!{{trBR4+V% zhO@Y$sCsMlv~Z(Dos)-2`?^V!x7fT?fPp_pxrw*@vbAkW4S6_NJ8;v!?9jHcp7tNo zP^Ze9Cf7{KtH&R1bRO1fI__wBfK`yQ49-fT#z6?%h_~3^>iW@hs%ZP=4j3hm zMkMPZOPE+t_SGN(Wk{HB$~|QkXh8DM@wxtL7H@xcT+|0qxTEs#a~a>6Dqqrw|Hv#N z!+VelH{%r%Wlxdnkf|y)4qx+pp&r@BdCW4Wj|8U1S5Nl*96qB_rBzu_;ea-HWiN>$FD+4o6kPc8ew>HZayZ@fgvg& zUw!Ot`04-p)A;pI{}R6AyZ=dCoZrX8kG~C%J^4-)TnEc-eB#6Jz_mx8Mtb7ic=L_> zc;n`qxOR4iYY$z+)rX$I&6l6U?blw!Q%}Da=jA@`oM$}q%-iwk;fN2v|6Mp-dl-*@ z^mpOm$DYJH-uZTX?l(S#o1gndJo|;0@Sp#eKaGF&U;a_txN!rwZr|-J&D9`cu%KaW zr}GvurN9L?7ciTmO$05$xz|PzN)hlf(uvvLYS@SRz3v@qd8jU9?3?`_=*eg-VH1Mm z@23*;#&-Sl9@84HnNi3+jnifZwGr8LN(V2SaYjn*TGQ0qG2e9M99qe{NMXek$?X0fdBCS0$0E3`*82S z`)lBPU&8HIZ{yX!_t){EKm8qe_q4q%3&hq3Sg>d*YA{Gv%QZnQhmKg;W3dzZB9>DLYCO$F_d2UG$wCk_Dy5vOz)Mgb zm0iUo{dd2LTX)X!3@c2pZ^+;03Lh$d$1hNkS&~W9Q#Zln8AX`6mv&0Y5S(mo$3O2 zOsEr7gP<9OW8a%2Gm2|Aj=O%Qfa8<(@jWqPLeuAAgxv#9Cp2SxcgAEpA#}L6SG{9z zFU-cdM>EEGHX0zNbVz`iDlsa2176Xj@&(Z_lx$ft6035n{LvIpveRYDGa<0k`=X_7 z3yLNN^>Vc(0aTE2p@xGj_}a72V}1B(yyO0TJo({|;Q8g z4-VZQ9!?e2Ce6A-$VnC5y@s;Fi&hG$X3gqkBXq=!V|C}IjGY`&%748TDQ9<0J1 zn+FCVspnG>z`d8uwTqKhrz+MT+uDfa8kUtakSwXJ9+_K3C1LZ_DlPO$%U;=9a;+f7 zptko|t9NDdtXYfhQzkXaRo$J7{ECi7%eJJMsCZc(L^uG2Tv4f|QzkAfh4m%!HgXmc>e7yJB@h@dPk6 z=o@LP-Bw;jRC)N=-t2n}JpshLffVX3bZxju|3g4$e5vi)`bT-R+HwM@s;7)G<~G0i znCsjHEh+s>-io{%s0T|hJhJU?A!IvV@I3bhpDBG`&$Y)Ns|-VgH{N&!?|SB4`1Akj zzmC83<9`{?e)bFaZ$I@V{Q75}!=pDA9FKyVckbiP1@QP&@5I0SXa5y^`2BCg>n}bB zP};P+>(%aaHayhBx2+62hKTIVd8XLT?(Z2KE`!%)eeShAGjjN|Gey2@E687Co8uT} z_x5mQPYyd}NQw>zV&0Xb0b!PEVAJ+MBW7@!eV&c`)SQZ#_x_};_bH>uUf-9^ZBOsq zGoZvP{@yZ9PGt3fGrRK;Km4=9&S0?iXoea?S81I+cpBYLUNZws=#afP45u?<7~+%7 zwA_%na*U&DX=mwz0e``oYM>`O1>FaGcT_n@l@eB}&G2OL1)gW%e=Yq+>`fj95m zg`i*oaO?IRym`Oin?Lalc=U;fargCGI9nK*2yecA6TB82j~BRoevUV8-Uf-_3txE^ zpa05z+`MxY-}n7LfIsjf{|s*4e6tAx)RaFK7v$&6vs>H9L}h24{T?=`)Gl#5eo=WH2;^om=h- z0U_rC${FwbhHuA5zTw;O#>LCH^N0QjUVqnnaq+321>SrSuiZ-c%DsDd|Km^L{>@kL zfe(KI7vKDQvHtWwhO8PcLq`0?3@Kb{&=okV(g>?mqyHE4+R_rEsPiOC>a4~E5Sh|$ zrU=m_AWDyQ9w0SRK!8N*DU$425tso;s)7fxgrkUN--k>X`Yi(1iUCNiV=+Ji6GFVvRo|pkP34oMZ=n);YFM^ zv&{;H8JU5k6gw0FTFG=`tfEzp7C25W)sqNN10z^N))FVMXf6`vC!WB#m@f%ih=%Vq z0g>gw0Z=SgN)aJ305B&MWN4aPT@&_PO<tA!Lcfgk`eoPHGyf2K}O*UX8wmver(gg{t>We+oLkcIl_U1}x^C zizcXHUqPBHjg~is+R?-)SE^D9)Wngbp}REn>-@}?(r`tpCtj2#qx>`bzJWy>c-IDz z(Gi4`@2W9G88ONQ?T{qOU}9lurxeW@pPT9dCQ$6j*8bfOYM2+i`pQfA*!$msANx1{ zP5hTX`P2B+FMk?uzVRAfynBu%9dP66x8WN<@^Sp|KlO+4EgyRZH(!1Mr7(D*%J=ui zSo>^dDO1SXeK%%T_-bQQPxlc%aZMC|(6tp05y$W2dOnDbG1Ce5u01^4m2@T^UAPlIfLUtlW{*wvKCF5{7K&+0o=L-O~N=bANihvxG^lGn!;iuhAeM;foBO;@s z6FP2$P^4Rs_*^VwO(qb#;6~v+askl<7dHUF1g1JqHkbyFo)In*2=WXDlvojgoK zEyMc+s$m8viK(MWRJ3?Y)cmia&3t3;t)b@pD3*B8#xqQhh$`%rac`MTBm|HiMq>#% zII-=Y0dRgi0=HkqpZsHg0N?V7Z^1wK2S1DFzw}xB z%=6D7A8&zJaOJSz?Cc8EMf-JAb~EnWxsT(j1o!HdGraIiU&I*$SPtOj3dj;qWm*7E zSomS&vq!L&g2%t*6Zqrb@lWI7hp*yCe)tdIo$q)OufF(vji)SBf!KMT>@sX8A(h@u z1SET`7~EC*o)cSpxfeDkOx9r>f9sx!ySq%sO&rzW#+hm4)i@2z# zxY`c@Xf6>p14LyzqPbMmLxn*lYv=*5`~=FCRKqz6yIZO%x?BZe0*Gq`Co%kI zG2;qD)k_Z{5CKFD)1@fu7lUac^&}918MWM#AcHI+uL}v;;K!mli&oJPj0GCbqLp(L zdDU@;sy9m!SZQ7l9U)i$t;&-w8%fI-A(SfLOO~`wjUQ(4vh`X8C`IW#Ndd|cLW?D& z6|7=qmFE*ATrHWap@{NY5(pMkPHcWC zSXdy1kx8E?JKf^q{DhD>t0Bemv=sBKT!=K}g9Qg7tfF2QCA1~%6~8h_1WAB}5`1P zuYe%JIGDjAizQQL^X3vU1F9eurR|Bb88283tyY~6jzOfxubc%rX)c~cvh7J@p9PRb zz>+Nwk|%L4SUp@(iVDYm=tv$0Wjt37^N{&H0&^w2%0&nQh~GiI$h8qN^my?S3C@1@>W3RHRo7|&44``4`#eO&fgC> z5Y}+a|1yqm_I;G4XU_vLPdRMl^u24(nB+QRDIOBr#<9E30dL3lXYksa!`KIdELW53 z!ese+=DN`aj^3{LL9`keN7@4fxpUZi^%2|M!}o68CFq*w=|UDNjABOUiIJ4Zjcaf1dP@eFra+x~sy9Z& zciq4+mZORQ+e7*;W()m->?;KmY>Q znBlI18QUstZ6Y@6n3caM^V-?qzW$SOjmCqBD;vV?$DUcQhYiTEFY) zXXv?EkMXw|zP8gWu4C$iT9A#;ZaA|6taNJVYxq8f@=fUssC#ClJLCK|Fg)>lY$qC5 z{CkASDM4%YT;I3TA%}G`IN3kR0K_VHWn>G_Q6r-5MPtJ1$wC^k!v$3I88V#G^ZEoL zfgAK8YDCDCpf{O$iY&cE~--1*g?!Sx$Y;3NN|{}<9X ze-JlszK&o2FaBd3zx+!$eD$~SmCt_)$FF=ASFT$34&o8LE9Hvf#L=q31iWc#cCE>k+ZE6 z5jZPO74vE_^-KRJq+=0O95@3|WW{1RcOaV6LkdCbE2}pLo8baQ=SNaNDVs7Q`m6wg z70G;rG`X`0g_MwyEqp})7FEtgkj!HSqJ}bvh940K*}OrBRHj=9kmTWC%9TnvLUIAb z_JQ>cOG?VnRVDu)z-)b1H@?1 z5K2C%$)avg%I^jNz0t(sBC+uUKf}%_Z!LoiX0Y6dew80tjwqpx@g&MPu0Xk> z=3{e@F|ocO(h}MOwUQCR_^91?48KFlZcGR?{BsaHj!8d z#8^3RqYQHCgAR+FuJs($h>M}Bzn zu%;1ivulr;`{xQ{W1NV6FhMgxVv~rvCxw1WxLwDQPF+?89tZ|+^e~KVkHW~z+vZfZ z{gL^fQXo#fi*B`@HW7#u)^)9Jt^N$LngMhhlHs2TI-ZEpa;va0_>!{T+YaP8U+G-XYyp`v0YK@*NzxQxmrY+)UmF{QzbHNHi%<_FPfK%hpX#F|4z ztFl=27V|7EVDqL?V}^J_R4proLWBzlTNMLsb256Ouk8_NlXc@gmk}Lj$>8smiD3M{De|((TcfrwE6{53gf(F$ zH5t&j!A9{NmE=)I+Q^k=0PW@!lw39^iZM6V*5sVZk{PEEB+ido6Tqv|7hHZ{V=;SzfVlPsnHr671`N1W{kZu=y9xp ziOT?=+J<>9xrfELo<6_o77W0P$(B`c|9DSNs*H$>gn1G{Ps`PH+~--ugh~QB$3;I~ zhu8_D!P-W_=JKv%i;!>Yg0Pl?)~Ca{?(ra$Ivn>B`i|F#f8B3`Ah*|E9X>a&92Z)v z;nRp60Txo?lR^!J^HYR9dh(#cMx=e?TrY!5q@}a|X3xjJajd(0oyNFu^Nt9iM_#_q z6VYjP8$rpAMZW%aR9XZDD?paBK~$kqAS4T=5k*U*sU%{#Hw=GROwi40*dPsY;F3UA zF^HE0<||125RT^;_|2dAe*lMr=6PAqA=j@%a#jz9HG`2rtX>g*s+=s}iAYfok3^PR zgEhwqEWsUX-V4jKs`Q(6?yIPxE<##mgaFC~jHIDN0WF6s`+BEieqfN-HlEf9wKJM(02fxM`AVb zl%kb>bPTzcHCAm`X-pxiNn9ZUh!!9g5ly^G0*HEN=w8#3+^!^wh=n*2$VzJ~ofGpS zA|R>vkrbV?o36R&PzskuE5QJZdU{9!k(RM8AS|pQQiUvuGR!z=9z#hXU}e?;kk(T= zKtpMcwr>(GX4uvNXyqP8!D=BlmOvN)3F9DKpBH2ZB{le1f?OdD>GIOQ#L!aPI!p5o zS`@}BpkZ*DyX8P?B*_AZ86Pr>Ru3aJWECiGE3HoEL6yN3#8ktm$!g>(zf2a^W_f8~ z)v?{J-Y^HGUPU>pL9bZiWy%&-2CCr&YOGQIs%2>bkX1{9RG236Q%f{?DFJHWRa$dg zkB0Zn_~;B`CVx~TdkCM{QwTS*6EMUBpt~Pwx>V%=ChvJTTK~?57rIaz+IOxS-)$2j zGX}R@6K>dy^LLYDEC20s1VH3QD*sKYr;g?hLMOwFauedX98P|(DjU8>CQ45yD~kbG^Z{tg6WZz5#(Dn?W}EdV3w3d+ z(C6axSq8&dm}8VJN5zxKDwk5j_i>yvIPV!YHgMjN!Dll5Y)|!LarrT%ei-z#nO+e4 z^xrqK{4lPK*E9kha3)FafxA7F8~g8M&ym2O)gcZkppp@K2jJ|TI@mn|;YcCd#pxy+#yO6$ z->CC;s+W$qlF@JcQTd-SR6;BH2sEo`DaG((!i=y*{8NO?Vd56!4i>{&`Mpzyui8Gg z{q1e+PCyY$yS2x#T&J~{ux`pWoHhMTz!-SwQx*)XeF%bW@%U~$NirQM%`$0~r zb8OGe_T`M(?g`NwydT=yo!77@;%~FPv?rv_#ys8|1rZne#1g@>KW{lH#Eb<@2M7wX zc_IK7asz=BJFWI|&So6UlA($0*z^~#h~n!5OSq|qij^2zK}OHPg|mi&07?gn`pOg` z2o|6)78K;;1B%ef+86i3CX-@ zh|Eyy`MU^pzr)%z7aRyBNK$&E?+O-EsPtK@q$7Y=M$)}NDCV&N00o)@gvrVTTL_P^ zh9;4yX9qxP@DhDXO)3z6$TcKQn(ASj}kyRc|Ti><%+nu>Lq4 zm2vhI0wOZ_R3QV7)vk0Cly(+!*~s{BLiOIA(SN6jMv?VKkvi6m+$;d9amVNrHTe#< z7hSqhnxkW`a*FE$L@HKz7#}kN2=^r; zDYvJ1v&&rKFlgs_LH_X1fS8vXoRUrC@!92b6`#kBe$@Sikw=X^Z2c4mWxw`Q`5H3+ zJ;RW`(^R->s8YpLDk2-18nZ~Qq z%Mv>d_a}1M?DIIzd4Dd%7nRBN;SG+&Hnw`~Rv$TaT_!tvyRm)m(+?}Eo)z?is87|o z`WjC5b?j$$jr%wj-f%tE50=F7#`$*~zxHhV&?0k2MEW|e{aA?yxGr0;s!WGcni8z) z@wpiR1kel~Oa>q$NW*jEraZNARp@QRYDRP;p^5i=!A`*>hDwY`?+ZMRbEXh^Tbd}3 zDiiCh_fzvhYyTASaEty7{t^f^hP8@guW%<+Zw+mnR9UAs!f~x!uo+<$72RDXu(R&t z{KwzbQ`zPZLNs8`;kD0X;x!YrYFp@WRT`(Rib}!Oa5UnQu7$1*CEA6NUw&c%BN83{ zn_wzJ)m0K8O}0owS?0hPU9j-@Yb}AR0+!DOA>T<|H>Qe5agS-DvbhleCft&R{#4v3 zt_lPJjV1)y6NgR&4YBq|!LEa~{Ud3KRXGS&C^mj}7`AnuozRUqG8!-U7=DH04i3h> z=pKPH+C1)?kp_=#&aN4q?VqKJa@D~>;})XLi z64tt$L1YS$?`bg)5V3b{O&A)&!G3}cKN*81p>^*YCu5A|khI(`04fa5NewKen zp&>z*6&VcBf+U)^M*V$kt^^3NDZ+LlSCvQYGl^iS+|2@&j11Pqr=*{==Lm5!0$5m; zjxk9B%R$X7W*Evs8XiIh8vv^mtd?Mwh|G9Ua4-R`i0-|G8Hqdxi4KGS%i{s-XR)3S`s{N>2CPLRq6DJjg&z~FGI zC-Fs%HEUsHuo*{^dVL5XQF6@F+(X5}bv&mCni8XlquIPUn&4hUa3sx3~h!raeYgo|%8cxQf-a{~)TOGa(6ixsg zAYe7V6~!-JmYOIX!dSD?Ic69iAZuU4PC**Bvw%QSkmZO(io;$jf}zGfr4^bGIT3-7 zA+j28@z7S%P<1zuvE~$8NxhFKOAWswQbU_2y(W;bdC)MjwhlEmsv)U*9!Y^nw!LDT z4dxvKsAk0Kp;fB4EkNYPpq}nU`ETUcGev!Nb@bikEB_}!4sy@&I_$XNdwFp~hr@4m z?AB*N&ldp%;}h!GIt9%Z*Q;_WqHpKa8bDL1faWm~-ajRy`$5jg?J6If^jkmMeX-A{ z=C!A{jGhhCH8r>hG=$HM7ER8p!!bEZ!msmdJcM@ISr4{?JUz}U_FE67hB=J#tRoB z4e!&iUNfC9-W%Vaec#n{p!?X3+Tw@2rI!WAb(-lNqrS5zn~rlDpPAVQ`gPZ}h-o4= zI*)%2I>gxKNPjo<>%=(qOB|B%GK$`~dT5||Em=z^lEmPiw{*Rw5TrWhi9%&JDqySf z2Xn#hU_W2f*j802tYm4tW}CZf(jPBXYiE^SeC&vA`!iJ<+QNF=4?Ua*LtNb-u+vtBrwSOT;Vli$I&M$W zsntmOb*ZuO0ZuJum#Q!l$K@r@U7?C_7inwPU~FA5AbEQlGh5$!`J4@tJ779k5%*2JE>jq0W7%i8kv_Wiplfu@tLY7v7`M({l`vgTOCU?Ia0>G> zVM`Dx1zcStT5?^KqIp+Elb@~`S{;W4x$+&xYU&;fR3SEK6A6S@Tx9iDNL;X(Z%!tC zzj)jLYvms%F)s;$CInSOKxP0rgUyXg=TWTkiZXK@uNn);Jw`l#8mZTUr#TQ)D&6=4 z^8g_!H8CngkXdnPQN)I1RVKB9k=BEO6o3a7FDku{kv@(PlJuUK| zG^wx&W_HY+3jh) zs#qy(M9etO0?Df>h#H_ug&|q@(vk!v3&^}<6srZvJfwsO@Uajq|Bb?vuvkU1R`rV5 z6v^TqHad$$!NXQ`TmVokMDWNS;^Mhs5Nr+ZMU0w%1ZL(e1xgd3I7YnG`w9ttUvYTV zB-2i-6>vBnS4dgwd$AybU!$C{QMM-nKQUWKKre4bSwsy+&c_C&!+E9ARp))g<*iBZi2B32A| z=wrOb%{+(A{P|j~1^XMu1taMj=}%?sf$e6S!ztd|vpUDNHl?5^&qdGQhO)_l#z0c<^zUkX73U*{)DiKE@L;-eM4wf{5<^wY2{w9C`dx+F9s_9xeUIlmS&0ZBWk2aAe6b2-n?h|bH z!;H3crznBB0~8H>&nUfD+A z!5*GNAp8C|fjD#;+$Q+g{+&5|HC?m_@lHe-Prei9K<;=0cl zGx%k-9N|FfB_xReW-Mkba3e0mJavjzCKB>Qr2wq7vKntHInkt)=CxFqtH+1pl7{Up zqG4kqs%&N=DX--^m+dZT-VY5JUI;jDVHI~oV7^4(5YPlg%!w}g{G~HmO`#{X|CFdhVay!%z z!kTk4qys1Eag-@GMhQ6KYR`AZ9YOWrivm4awZUG_a0y3$%ls zXhiYn>u2$QVb0c$+Bv9dnw9_h80;@)J)s}r5&7N-Tm2klk|3|_@j5r!Ol6{aznA4l zh4GZ^EH-~JxL{`-<9kGAl-kij_Qo3ft#gvC{CjFXm+1|su2B~o+uj>*_gh#B8b53o zzkRF{fVVx7vxjlUdv%;~3?dt^8K)24gJ1}k8{r&qd>qeKkG1<272vwY%-dsJ&$_0b zO@NFx?!W+WIUQyk-(UN2ynF2dn2y<>-;)Pt#cV1KwDz<}!M3f|f&CHXClS47RgT7M zHmIbErGu1NK|)QW@xDk_b-zVTMCx8woiwm*;4?1-ps5QZgfvNuX#E}Rl1 zXXKn)n?zk%>QMy4mJe5F-@u)RG*uN6pZ6Jbzkfhwb7d`F^jP1 zu9|Pd0BD-RUIOFT##qs0qudT-^}ic~LO{%!B~~X#n{mwMGaJv0*^LUK;|P&ew*W51 zky(E;xWwnGAQA)!6;sBGOz3L@=B7Qz%i&% zVG#MrEYrv{WTn`LnJmu|xP7D3C^{l$0gK!J;8# zg#{#J-XVH&q|q)?^QUm}a1}Gq2mt14SjeRq1OOqe7F3`fMr3%S-h+ap01G8RvgVi( ztNddd)4i$OTceP!pNQ1_ys&+z6?{rC5CV&6?j3;|HKY_I7D&-hBgaK1w%jBzo=*S@ zvcZ-Gpseylp$tFW)vzS>MPgEUsZfH4K3PHoi)hldEXfQIr5LUjGg_HhhKqW(6adMx zPY6)0WQ9P*=WgtU!*@rS6;NZJuQ@@oJttz-ssPXTp(rDoGYA>Nj8fRj#zWzb49RYE zG~->RE35`tDY}=ea*?kO1IWg63DCV-ik305vu{E}cR~Rr%Uc5TB4ahYFneVrGNZ3? zObh4pE0Ef{j}2q&9@YF~D&$tqrq7MMfv9VbYrnQb%_vB2b(Y?P*MtU8B8X-&H_>1s zr9?X2Qo1y`s?&^}zj>7}Q$U9KyG#pFI`90?>%omxqZ*Glg=NAk$>brxWFMEmYTsnM zV}W|$7=UaMoL2WRDh&??-C#2xpXUUI8-s@NqY}fMi|D!~ZU(18E+muPn8=DuirXzg zc`J8pWITm{U~%E|Kv@`Kk5gYqsCO-rgOrF@f~h=YtqiPU&Q` zXZMUYGdX#$tv!7U=)pS9tMyf?9bvECQI_8`I?Z%tvQmLm+&CSuvXG&L?jUiV3~Kpy z-oo|P3@|?8Vt|3Pm^&Bbt;&IBqIdUJbc#g;&Zszyo7#W#>MK!k3ghaYueH<%1;JhP zM&%*C+uC#D)q6@rMScfHvB6-&MPPD*yskJL4x4#y$3f=O?t;ZYY(jr~)lNpmdql!f zA*;{2U=f+v*nWh4seW2h#W#*;9Lt^pIKpPOx%Lb?)w5;5kEx-iwyy-gJ`;#<+nT|P z23wcq9y}wyhq;MMPj=#8Gj6NLfP3urIE8>t9jVyBEy8BJSI1Oj!(Zy2!?t}1?QE2I z^lLG@MgxH>`6CU8iFc7qsPp}=QVv@6h0`(H8|FyT3!f9%jr(yV-kx&=9qv#!kN8`+ z4uOu1bZ-W`&HL<<=N#S}+z!9b_Wg_wj(h6V9@+?9M(A=8J3ipYi<#Z7Sg|#g;(3O=oOHfN|X2-Ix$V1 z%bJ|DxWOWuY0Kcq0%B1^Qg*wR8ULY14WewJGYLc*7E++eVV&wIaZ*((iKwT92ugwS zbZFHlh#Ha9v(J*|rlR3a;>v;f{ICGRSVbVDCIUv%kQoxoX=Qt=03sRWiB(y>h*k>; z)+j1QS_o2(3PxJA6tqDMARwh0wnU|9B_s20ayS0WTDe4_vjU>|Wki~hJ3+lJj->d& zqI8-`L%FhOl4UkJOq@}21~G6f!HZA?;G_mNvD_mrl9MF_h6-hhn{0`23(gcZuu9Q2 zwiU|Nkf&675zSqap?Jn5){pxuG=qu4mTKRL7l<4c3z#*G&J^(iF@uOzHWPR>lzHP2 z3Y#&2R58`yF<8zXqYsguF~Jk}LSPk|G*66I%wYhlm`9DRN0y>=qL2kkvH1u&@39;+ zhU0>w#f!xS0pp;Pcd$jOWO)5!E`8Q?G*VhQ6 z?dZDObX7deTmqjFcZ`whwc`6zpzEqirM@qr%~6J~f6)#dHZnoQMHoFmJ^Vu_3pKuj z0GHX-cr6TPxV;Ob<2F}hX=w)HxJ1w%!tQ#?Wa=-?3>o<9A3!ecuO2Dnwqy+EF@Y^`v zp}lTwL8mxn1n_4S(p2fnMh_6FmB~cfp(92d4)g5WuM_F!xhE%%@YvHyiBLm~fo=Z3 znH)C5o9-Zd(oY;qY&XiMv*)5LKDIqwx5@8Qqs9rHXdHi>e}qq8_+|o^2kc`Ux4)}> z)Qod*;$HnnVEiD?8Pf3L>+C;XZ^;0Yb3VrJ(N^F`2n~!R&3mkx>X~ z5;2}E_TGly5YN!Z%!O%2_+-+iU!#&W8cu>=YoL5e9LAW*ePojQen)VOpl;_FnL`tP za?W689MaNx4UC2hXDQKYJ*$v35>^*lqgiS+hPbdYix-_SkC8x+{f_s$bq{5_BYbks zQ{}Sqd3W8j4Hog+;p1~GrBwGogx~holP&x}H*sjQx``>X7@2XWxKB`PkI0T~>=DP> z)C3nJ0}}gB+fFuq6DR-^eopjV+mh|tjQ2Ks&4pWSSOwI8SEm^Ih~rg&!xne^@{)BM zoEsPg2%EC$BSSrPwd!(htlhlrZ)-C!ft~A8p(em&#$X?m(QWYsHa>-ODFq3Zz|;1X zhYM6(PcVf_6?P8`aiKUvKqN>>PeTA@k|~*hC;&n8U=RU`j-aw48%``z)L3A7NQg9% zr~RT{B{e~}*x6}9DTIZN5D~0ohY28vWUwiao{%*OfNZ7O?POg`NM|hO`69N(sUA+i z#!R4;a1a7YK`uum*3!X86b-8?+E+H?ji2I!36uepj6?#Y#h%fGuZgJ2e=LNxWKdFX zAWdef3SKGMO8**+nE_;Ea{N;-BlkRE(e@B33o$apm17q376OWUz7VXWbw(jeGG7=( ziX)CdOcCczAvP`5LtC;Ljt{iLG(4XpkvI5U0-^MF|Ms!A~x^FmAT z3RGV1oTXqjb)UrOJ_?qUAfg@>>fy6mZY_ey3W0`=l~RzcN*9X&xt`A}FCncMl?yD? zN!6arD!Vhxd#GgS6Is1{f>N6$8xAO*s1Z_cFMt{%U@q`(P_)y%OaP%^fgm5v+lSRV z$7w_kQV2qS7ZC1yy`(5;K(inXlm6{Al&W`U7gy%~%IRH>cI z4_kgE&TpN)>#K&FY`Kf1Dp-*}6DD*SE+W`L4jFAeArD256qgI9d@;!B2ntW@d*Dwd zUty&GgN)%ESNjfqwqYZBHFUg|?T`cRg==}YT-K;zVtz_(tqfx@`@S)xeO?#KZ^!%N zb5-F-e+hbdiHHy(=t{I->@W!jLLJOUImqSI0LK{*A7zkTd2S+mjq@3=Ze1%M;;yea zp4h%0e8m1^ySs7{x!i1ropO5Y?rBx^4C}k9q?>*Qy#v12_B=B7WviJiTRkR*dv0WK z9SEI_*WM?f>Q{Snc3oWiYbt@$$x${P=QHYcaUEyl-qR;G`U>Xq{Wy-<_nEG_nfCrY*8$pBNDxu_L($tu?Z`!87n}yw>?Gzi>;9C!arZ9GJvA%sxU9f3l ziBAyKdt*kFE+bUF*CJuu8W$RK4KLe#(WFR!HyAKn7}~Jz9U?y_tRCT4$1UMInjl>9 zVP_xh!Dq(8;#$=*dmo`8e~^f26M6U$YeQJgmAR+aUzQlywETu7#$>N2#!t~Yte9@45 z3@z<0AP9~y{$z70MXZvM8u7$3%6d)^uvM(m@>5uBe-s}>(KVo?au7f$vf@~Z?G4>) zwV*sfJ*A4+9;Ax*0D%huS#%sQ?+Fsa7pv??YGf@I(yO?s;f+_4&Lywe@~nu3i{(b^ zMDe1C?t96l=43%>#TkgGdA$VsSm~^V6;@a|IRu$kNITq%hl)xW+W`$3u66uVq~=`TSJ#r&qI}~=kVrBqmP6aaE&w$o=suvzYd@%v11 z-+d=;gb0XX!S@KLAWwfEH~y*d_-|1mWSHR!sLT+6d9DoY!Gr~A?av50leuM^Z~-H- zGeq9NOSS|)j(?ncfeD0V3%8M(ACJdr>Fn86i5oYDI^N$MUw|Rdh&Lg?olL03mch;1 z?fO7u;6YKY?O2ahi0D}skynIh9!GV~-6XNuqut(9Y6F*F3Di7!iYt0Bp+kjp7ZRND zQ*6^2-8hWEc%y(gu5ZVr2VAQHSsfHwsMZOZu^GcC6jET$VH_gOg%~j5G6-b8l4QiX za|JIA1W_m*b2iz)Iws%(LS3WD+Kuq??<3sy-W{*mv*UOlq^-sbGbo0O*(vG8dSUyg zDg7V|VX>Se5Q1=Ni91apFBWD)Pyp+RH=6*cUJFR3P?rok*W|mJELO*L)J&gx!pWq$ zG%U-!7JgStYBU&9HP_={Y4U_5E2W>yhq)z48w6E zFSE`~A%YqQlwXME^kFq@)SNe-PX!thvH+Iq z8uA1Sc_FAFg;(nnTxnB5(Nfr^q=S;>n$a*855qc=)xR?QD=C3daD+gL?<4KACO&6E zmLni)fKWvd$ZJ-IWXrv?7SX~~mayZvsZwNKr@{vO(o}l8I;^OPBmm( z52n-`gCHOl^R`L)s`(?c{qD-&nsrVVjaQ8TZ2PV1s}{fK0>3jAg@PGAEiX}F=N(B; z49Scfur&}MA6*dxXf<i z-WXU`>rad(C^6m6*Lg=3o5+O-%YoGlTTRIUoPe}YCNtO*!7Y`-P{U?W_@+h_>??nr z_%c&8oIgv{TZVRn*Y&stq?4<%&|Vd&nPFs)?~47;4-*`IQe%3tk>AI^?uAA+As@qN zRGaE(<9lt1H**5e=_vl*-W)f2-5^)b){)4KMP#z`L`<vvR!+OLgbgSe1X zg(YQjCMX(!XA_7-dnDi~rP|*t|4LH=`o)U(Hj3)-u74iqa%%p2ZR-6}nGI3%{#lk~ z(^njew>{h7BT=g8B9Q=X1`>#?f9(;n>&KKQitP{m$9FH=oHL`1wxzHc=M~2?BMh@~ zbg(pL9q)~6JZ|I;Ukqk0jJu*ub$n9y>1dog1&bMT^}dWlH{xpmia1}#@8Sw!Glx1R z+QPCvR}=iTK~yi`2$#V4VjCL;ozUfI@@Kp82Btn8Q|J=`*V^e;2TUc1PGG3DHyR_n zj~QJ6Pz9nmp3WE(O3^hT(Q|RAlEGy85#YIDw&76q3JC}6IB1Ti@!Ay}!@U_j8GoNW zJNA8JBvrjDnxxZ=1S))}23m8GtX4`_+}NWA3Qa7p0?@G-^SAzCAto9&#Slxr%5K;Y zspXZyRw5f><6^){_7)PgD`l9HXhs2`;uTq_3`E;rS(E0nsPPA)3inz>ljc&g3Nvaz zU{1)nKqTupSVLHnSBWW_gN1Z*HP3=q{&TnZoYgR-vfo(!04DJ3-q7T~fN^`EB)EZ+ zsi5Qnrlh%YELVsMn#PR8iEGid7Ovh%khSq5Qv%7#fqTxEg_BitDaPMa2xX%=MSzqR zQ#1%-$%ay;W26aVA*N`m;Ui&zNVZ%$34W$nw~iYnp!tZH6@QNckY)BOq<8|yRgoab zK~KOXTeYlYxkqeoaMA?n+J&y2hpwOiv^2O&0j$E4E`u5lsji*n74lNd0uantO4+;w zTv$&U&IW>T>f{FZ7GXPbtrgEG>v~bLY&>44sd@U58Ir{13tLB%$j!s1d6l@rtzKnj z7D~x-wCO?!)BvQs!p@CtOv${Rs>dl&WMcR5=W|Yb=eMKUDEg_!C4Qn;(%J~?hdb&oM>as;V3~@e6jz+F=hA*;GHkq`E+8NaKS z=gOnnTwJxOOkH_xc*DmylYRCMKI+(Hzy~9naX?aWvw~+McXqg_KTP{OW$dVV`?OOB z!)7d9o8hZJI+Hc(eB%{lzFoP!j%VIZTMu+{?o3aNa?UJo%c-j~TzKEZGf^%jYDndJ zTolhC{hH6l;51tAt03cFN4b5}%Vs)!FC#zrr7~BW{|3(~=m>(EuPKg?h_Ecn{NNH> z+?h7~Wr z7rh;rdE-tr+f?!Mo&pp5@XveCgQ%AL+gsQ@BS~B^3K``A)G9uE{q7-LH{LMDy?Ms( zol!=5rF1$M>KZr+gV|W)hMFpcU1gvBx%&80O1lc*Y|cIfMnCSEu_Ct7Der@6Wq5ki z?u?1dU=pAAc4L|9sS$zucAbgGz0~y)e($bGw2HJ4GaHB|rHr%euT%4L;u`z)?ngm& z^KMT>I`?9r5v$qy*W`Vny+}i1BCmLVh6|C=cIycY*r309o+J@bMK&;>**wSoXN+(r z6vVE__h+~j-yg3kfLwA#W9Ml2oH6Z&gAScQ>oTfQ5TtoYRC$of{B)WsjJu$g?MTRz zYnnA*A5MZo*@S;?l{+k)M31py8Sd5XzE-zk0Tw@Ahm+I*N7EScae)_2DgqF^BDcihvtTGdXkX=|K z%~b>oohTwm%t#D~6s)94a0!A1o(vgk9O4B!84XJ++Gg8B4fy;1;lb*pT(tcE!mTKtAnQ#=b=qbyuLuO(NIjU?i)l0(?7neZ$I;F^} zgl&}WjSxSVJDu0JInCtQK-&!_^>7H%Dp(=#Yy0jc$_1ixw#n6QyzMyesu`N%6nJ{a zM~rSHU%PNK&cgZSChY)GUAGN^lLlHvE#bNd$Us5as0D;BhGYlR%>0Q*K%n)&Y6B^*YDT> z1DIgGGZIH~5drf8H$(uvk}x9e3^`}WJ=@fNe`eV6Yh08W5;_yD3~ADi&;Jy`jPJO` zttut9iZg?Px6u*Uj#;52b|WDeiDp%}fen$|8+TQVjqW&1BeC)4qs!Xf+II8cphN~Z zjqS#b7~yeh+z}q}-b`8DJc~f!EXz9;Y|Av;-?7Tl3`gTQ_89LhSBJtTz$bW*6be>m zZ1J-A9uQy0t?;2C2!RaHgcwd>b(!GOGb0mNG2kT)uwm|nMxyG@3qdFFB0MJu_hg_e zJ7XK+MVK)0;T@LAnzDMqke3q=>syTZ&1i$iqwnGKW>2HvOEX$H6*dB##&*X&H+~-B zFn*5AaAcHXuu`KUK$LKP%vvc{0I`ZRF3$)Al$!BIOn}#FNMuP#U11K)k6g+vAk9Ln z1_dyL2rDedNFoT?i4Z}JzajKQkc~&d%H9Gbnq*YeT4Xd6+OrY_6lP?0r5I|YNwAzO znkbft%p<1l-Ndl@vp#F9ET;Swv9Sx()+DBsLX*L&w~i^-1_Jlw@%eBvV~Q}yj5yBE z)ayn)WD2r*jW7}|C|NYQt%V!`D;TRLkEJG}C@ic$Dr(3TQ5ah|38!sO%c%i10xK6&;|gW<_R-x6wq(;P=v)6J@bb*OBzUfcVj($1 zhJVDU^~cG|?ZVz83jry*Qf3RX3^!h0T+L(}c4)Lamx{N(enkkxi}hh1KZe;%wkd4Q zK&8Q+jE`cg2AXF`*w#7~R^hVeMpi)Yz-#G6}M){dEIk zm5~T-QCyC!Kbum=jh>(ymIEv%@?c%h9T~(8eh5bVsDRGo!tQEx2MwBjVq;;&3Av++uEx{ z?EQ^%nf>1Fu?WBE8nyj58biO{>n#~zJgf*&`KK+S=mvB5LOY2lQr+~mt@-90f1f4U znJQYLsm(?R#Qd_2<_e$+dfR!8@9r{6CvNuJSar$WFfrK(DkScYlZIiRGAamAU2@0G zF}^=C3;^|%7eL3HMgn=tz_3vooq5|SHPD#zxG_eCI3vdKG-ZUx-tV)qR<8*YOxvkX zW2hs%T=5^7-3$)nxJTH;u~F4frNa}pq?@yL>O4$%YB*%$c7u^q&|#@(@E_;UF$qh`F?}j9JDu@|upe1ov z5-iE8EwO0gUm?w{!&(-ZK{^iGC`z+n^Hb%nK!Bp(3GQ9NppL5`3pbWsn+;oo_g@U)}?-Hz{7D}V9F!s(?m5J04qqyb9S?9Nw z^F<%?jfRD^ij}e|AEV(KVDPtp5X%FCN*|Kt;E@6gPEs#y80sft$STOWG`{MLs6Iw& zSnx1O|IK6t_bQ3cE1&{SIL&tjaNswt+$rpg&c{Ql$TRF82jhr!VrydUa+G6HjfQnj z+Mre#3p|Rc$w0;CK%`!OZZ38?0utubK-TyRp@x1U8WQ$&#;8F9LE{s-tZ#Qm)S$kuipAJ?ctvWzygm}Cz zV-DkQv;M}h$8l68*xyGJ9{n(KMg;2|h&1_X{2mC#IPclGXG-ii&v>sfm^kUEwlW>r07VX-YshUZ>9x3rRx|7`H~(`rRueOscTOF^+`u z6tmo;7i5YYCXB56KENa5)p*4*mQpqhSCyZzzjt6g;>2uxyK5r@E!zE74E37*xC zpzbMRn_8f!?*f8|dPzVmJcUd#urZh$4T>;w@#;Z>gQyXtCWI!kcc^-+5p!$9HlN6< zDM$$ruqxJ;0yTt~V%>btK zM3vk{8?ZbZy3WOnS%zI^6cfO3y+CnE4H~8>_*#(DDzb1OO@ItDKoP-=rm%1iqsT!j|YC^I1*)Q#Wq3@x=K~JI>VuZ=eT?X{7~(c{)^OujB0WKF+f=uo9wn6L5N z_q*>W{<|F4d9&W*eBw33ry2d4jcZ(wnaukjI``~e`_1?!(DFN$M)w`pJ(O6Pp4%yk z>3N2?r$Kk}dP^QK>v}wny)tM+K*g9{wbV^E�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 ZWju75pp0D0O9h+_Jso3h>^;Yb{{Vm%*lhp+ literal 0 HcmV?d00001 From 7340b6dbc49d3bf8da73b87d121e0f3c367d8ccb Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Tue, 27 Nov 2018 10:53:00 +0100 Subject: [PATCH 0432/1240] Update CameraButton.qml Contributes to CL-1150 --- plugins/UM3NetworkPrinting/resources/qml/CameraButton.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/CameraButton.qml b/plugins/UM3NetworkPrinting/resources/qml/CameraButton.qml index 7e5c254e5c..618dbed81c 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/CameraButton.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/CameraButton.qml @@ -9,10 +9,10 @@ import Cura 1.0 as Cura Rectangle { property var iconSource: null; - color: clickArea.containsMouse ? UM.Theme.getColor("primary_hover") : UM.Theme.getColor("primary"); // "Cura Blue" + color: "#0a0850" // TODO: Theme! height: width; radius: Math.round(0.5 * width); - width: 36 * screenScaleFactor; + width: 24 * screenScaleFactor; UM.RecolorImage { id: icon; From 616ec13457f12fc3b0396f3872ec29024bd4ed9f Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Tue, 27 Nov 2018 10:54:26 +0100 Subject: [PATCH 0433/1240] Change context menu button size Contributes to CL-1150 --- .../UM3NetworkPrinting/resources/qml/PrintJobContextMenu.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/PrintJobContextMenu.qml b/plugins/UM3NetworkPrinting/resources/qml/PrintJobContextMenu.qml index 11bc913d06..02a8e7ae69 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/PrintJobContextMenu.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/PrintJobContextMenu.qml @@ -25,7 +25,7 @@ Item { } contentItem: Label { color: UM.Theme.getColor("monitor_context_menu_dots"); - font.pixelSize: 25 * screenScaleFactor; + font.pixelSize: 32 * screenScaleFactor; horizontalAlignment: Text.AlignHCenter; text: button.text; verticalAlignment: Text.AlignVCenter; @@ -41,7 +41,7 @@ Item { var states = ["queued", "sent_to_printer", "pre_print", "printing", "pausing", "paused", "resuming"]; return states.indexOf(printJob.state) !== -1; } - width: 35 * screenScaleFactor; // TODO: Theme! + width: 36 * screenScaleFactor; // TODO: Theme! } Popup { From 8965695a59916b6895d90468257007a5dd29a403 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Tue, 27 Nov 2018 10:54:46 +0100 Subject: [PATCH 0434/1240] Update MonitorPrintJobProgressBar.qml Contributes to CL-1150 --- .../qml/MonitorPrintJobProgressBar.qml | 202 ++++++++++-------- 1 file changed, 112 insertions(+), 90 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml index e7f9799310..6e16d026a1 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml @@ -6,107 +6,129 @@ import QtQuick.Controls.Styles 1.3 import QtQuick.Controls 1.4 import UM 1.3 as UM -ProgressBar +/** + * NOTE: For most labels, a fixed height with vertical alignment is used to make + * layouts more deterministic (like the fixed-size textboxes used in original + * mock-ups). This is also a stand-in for CSS's 'line-height' property. Denoted + * with '// FIXED-LINE-HEIGHT:'. + */ +Item { + id: base property var printJob: null - property var progress: { - if (!printJob) { - return 0; + property var progress: + { + if (!printJob) + { + return 0 } - var result = printJob.timeElapsed / printJob.timeTotal; - if (result > 1.0) { - result = 1.0; + var result = printJob.timeElapsed / printJob.timeTotal + if (result > 1.0) + { + result = 1.0 } - return result; + return result } - width: 180 * screenScaleFactor // TODO: Theme! - value: progress; - style: ProgressBarStyle { - property var remainingTime: { - if (!printJob) { - return 0; - } - /* Sometimes total minus elapsed is less than 0. Use Math.max() to prevent remaining - time from ever being less than 0. Negative durations cause strange behavior such - as displaying "-1h -1m". */ - return Math.max(printer.activePrintJob.timeTotal - printer.activePrintJob.timeElapsed, 0); + property var remainingTime: + { + if (!printJob) { + return 0 } - property var progressText: { - if (!printJob) { - return ""; - } - switch (printJob.state) { - case "wait_cleanup": - if (printJob.timeTotal > printJob.timeElapsed) { - return catalog.i18nc("@label:status", "Aborted"); - } - return catalog.i18nc("@label:status", "Finished"); - case "pre_print": - case "sent_to_printer": - return catalog.i18nc("@label:status", "Preparing"); - case "aborted": - return catalog.i18nc("@label:status", "Aborted"); - case "wait_user_action": - return catalog.i18nc("@label:status", "Aborted"); - case "pausing": - return catalog.i18nc("@label:status", "Pausing"); - case "paused": - return OutputDevice.formatDuration( remainingTime ); - case "resuming": - return catalog.i18nc("@label:status", "Resuming"); - case "queued": - return catalog.i18nc("@label:status", "Action required"); - default: - return OutputDevice.formatDuration( remainingTime ); - } + /* Sometimes total minus elapsed is less than 0. Use Math.max() to prevent remaining + time from ever being less than 0. Negative durations cause strange behavior such + as displaying "-1h -1m". */ + return Math.max(printer.activePrintJob.timeTotal - printer.activePrintJob.timeElapsed, 0) + } + property var progressText: + { + if (!printJob) + { + return ""; } - background: Rectangle { - color: "#e4e4f2" // TODO: Theme! - implicitHeight: visible ? 8 : 0; - implicitWidth: 180; - radius: 4 - } - progress: Rectangle { - id: progressItem; - color: { - if (!printJob) { - return "black"; + switch (printJob.state) + { + case "wait_cleanup": + if (printJob.timeTotal > printJob.timeElapsed) + { + return catalog.i18nc("@label:status", "Aborted") } - var state = printJob.state - var inactiveStates = [ - "pausing", - "paused", - "resuming", - "wait_cleanup" - ]; - if (inactiveStates.indexOf(state) > -1 && remainingTime > 0) { - return UM.Theme.getColor("monitor_progress_fill_inactive"); - } else { + return catalog.i18nc("@label:status", "Finished") + case "pre_print": + case "sent_to_printer": + return catalog.i18nc("@label:status", "Preparing") + case "aborted": + return catalog.i18nc("@label:status", "Aborted") + case "wait_user_action": + return catalog.i18nc("@label:status", "Aborted") + case "pausing": + return catalog.i18nc("@label:status", "Pausing") + case "paused": + return OutputDevice.formatDuration( remainingTime ) + case "resuming": + return catalog.i18nc("@label:status", "Resuming") + case "queued": + return catalog.i18nc("@label:status", "Action required") + default: + return OutputDevice.formatDuration( remainingTime ) + } + } + width: childrenRect.width + height: 18 * screenScaleFactor // TODO: Theme! + + ProgressBar + { + id: progressBar + anchors + { + verticalCenter: parent.verticalCenter + } + value: progress; + style: ProgressBarStyle + { + background: Rectangle + { + color: "#e4e4f2" // TODO: Theme! + implicitHeight: visible ? 8 * screenScaleFactor : 0 // TODO: Theme! + implicitWidth: 180 * screenScaleFactor // TODO: Theme! + radius: 4 * screenScaleFactor // TODO: Theme! + } + progress: Rectangle + { + id: progressItem; + color: + { + var state = printJob.state + var inactiveStates = [ + "pausing", + "paused", + "resuming", + "wait_cleanup" + ] + if (inactiveStates.indexOf(state) > -1 && remainingTime > 0) + { + return UM.Theme.getColor("monitor_progress_fill_inactive") + } return "#0a0850" // TODO: Theme! } - } - radius: 4 - - Label { - id: progressLabel; - anchors { - left: parent.left; - leftMargin: getTextOffset(); - } - text: progressText; - anchors.verticalCenter: parent.verticalCenter; - color: progressItem.width + progressLabel.width < control.width ? UM.Theme.getColor("text") : UM.Theme.getColor("monitor_progress_fill_text"); - width: contentWidth; - font: UM.Theme.getFont("default"); - } - - function getTextOffset() { - if (progressItem.width + progressLabel.width + 16 < control.width) { - return progressItem.width + UM.Theme.getSize("default_margin").width; - } else { - return progressItem.width - progressLabel.width - UM.Theme.getSize("default_margin").width; - } + radius: 4 * screenScaleFactor // TODO: Theme! } } } + Label + { + id: progressLabel + anchors + { + left: progressBar.right + leftMargin: 18 * screenScaleFactor // TODO: Theme! + } + text: progressText + color: "#374355" // TODO: Theme! + width: contentWidth + font: UM.Theme.getFont("medium") // 14pt, regular + + // FIXED-LINE-HEIGHT: + height: 18 * screenScaleFactor // TODO: Theme! + verticalAlignment: Text.AlignVCenter + } } \ No newline at end of file From 475a94d10d4e7fadb7fc43978c59349cda6bb000 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Tue, 27 Nov 2018 10:55:08 +0100 Subject: [PATCH 0435/1240] Add printer images Commit to CL-1150 --- .../resources/qml/MonitorPrinterCard.qml | 48 +++++++++++++------ 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml index 2fd3f2ead2..8089ea2d30 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml @@ -1,7 +1,7 @@ // Copyright (c) 2018 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.2 +import QtQuick 2.3 import QtQuick.Controls 2.0 import UM 1.3 as UM @@ -50,12 +50,18 @@ Item } spacing: 18 * screenScaleFactor // TODO: Theme! - Rectangle + Image { id: printerImage - color: "#eeeeee" - width: 108 - height: 108 + width: 108 * screenScaleFactor // TODO: Theme! + height: 108 * screenScaleFactor // TODO: Theme! + fillMode: Image.PreserveAspectFit + source: + { + console.log(printer) + return "../png/ultimaker_s5.png" + } + mipmap: true } Item @@ -111,16 +117,28 @@ Item PrintJobContextMenu { id: contextButton - // anchors - // { - // right: parent.right - // rightMargin: 8 * screenScaleFactor // TODO: Theme! - // top: parent.top - // topMargin: 8 * screenScaleFactor // TODO: Theme! - // } - printJob: base.printJob - width: 32 * screenScaleFactor // TODO: Theme! - height: 32 * screenScaleFactor // TODO: Theme! + anchors + { + right: parent.right + rightMargin: 12 * screenScaleFactor // TODO: Theme! + top: parent.top + topMargin: 12 * screenScaleFactor // TODO: Theme! + } + printJob: printer.activePrintJob + width: 36 * screenScaleFactor // TODO: Theme! + height: 36 * screenScaleFactor // TODO: Theme! + } + CameraButton + { + id: cameraButton; + anchors + { + right: parent.right + rightMargin: 20 * screenScaleFactor // TODO: Theme! + bottom: parent.bottom + bottomMargin: 20 * screenScaleFactor // TODO: Theme! + } + iconSource: "../svg/camera-icon.svg" } } From c489f911fb2b16bef7dfa5a4bac76ee83620d13b Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 27 Nov 2018 11:04:41 +0100 Subject: [PATCH 0436/1240] Use QtQuick 2.0 and simplify ConfigurationItem It now uses a ButtonGroup (from QtQuick2). Also, all the mess with when the border and background colours are updated and force-updated and such is now cleaned up. Contributes to issue CURA-5876. --- .../ConfigurationMenu/ConfigurationItem.qml | 189 +++++++----------- .../ConfigurationListView.qml | 10 +- 2 files changed, 83 insertions(+), 116 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 7427b5ddff..d825a8cc6e 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -7,143 +7,108 @@ import QtQuick.Controls 2.0 import UM 1.2 as UM import Cura 1.0 as Cura -Rectangle +Button { id: configurationItem property var configuration: null - property var selected: false signal activateConfiguration() height: childrenRect.height - border.width: UM.Theme.getSize("default_lining").width - border.color: updateBorderColor() - color: selected ? UM.Theme.getColor("configuration_item_active") : UM.Theme.getColor("configuration_item") - property var textColor: selected ? UM.Theme.getColor("configuration_item_text_active") : UM.Theme.getColor("configuration_item_text") - function updateBorderColor() + property var textColor: checked ? UM.Theme.getColor("configuration_item_text_active") : UM.Theme.getColor("configuration_item_text") + + contentItem: Rectangle { - border.color = selected ? UM.Theme.getColor("configuration_item_border_active") : UM.Theme.getColor("configuration_item_border") - } + height: childrenRect.height + color: parent.checked ? UM.Theme.getColor("configuration_item_active") : UM.Theme.getColor("configuration_item") + border.color: (parent.checked || parent.hovered) ? UM.Theme.getColor("primary") : UM.Theme.getColor("lining") + border.width: UM.Theme.getSize("default_lining").width - Column - { - id: contentColumn - width: parent.width - padding: UM.Theme.getSize("default_margin").width - spacing: Math.round(UM.Theme.getSize("default_margin").height / 2) - - Row + Column { - id: extruderRow + id: contentColumn + width: parent.width + padding: UM.Theme.getSize("default_margin").width + spacing: Math.round(UM.Theme.getSize("default_margin").height / 2) - width: parent.width - 2 * parent.padding - height: childrenRect.height - - spacing: UM.Theme.getSize("default_margin").width - - Repeater + Row { - id: repeater + id: extruderRow + + width: parent.width - 2 * parent.padding height: childrenRect.height - model: configuration.extruderConfigurations - delegate: PrintCoreConfiguration + + spacing: UM.Theme.getSize("default_margin").width + + Repeater { - width: Math.round(parent.width / 2) - printCoreConfiguration: modelData - mainColor: textColor + id: repeater + height: childrenRect.height + model: configuration.extruderConfigurations + delegate: PrintCoreConfiguration + { + width: Math.round(parent.width / 2) + printCoreConfiguration: modelData + mainColor: textColor + } + } + } + + //Buildplate row separator + Rectangle + { + id: separator + + visible: buildplateInformation.visible + width: parent.width - 2 * parent.padding + height: visible ? Math.round(UM.Theme.getSize("thick_lining").height / 2) : 0 + color: textColor + } + + Item + { + id: buildplateInformation + width: parent.width - 2 * parent.padding + height: childrenRect.height + visible: configuration.buildplateConfiguration != "" + + UM.RecolorImage { + id: buildplateIcon + anchors.left: parent.left + width: UM.Theme.getSize("main_window_header_button_icon").width + height: UM.Theme.getSize("main_window_header_button_icon").height + sourceSize.width: width + sourceSize.height: height + source: UM.Theme.getIcon("buildplate") + color: textColor + } + + Label + { + id: buildplateLabel + anchors.left: buildplateIcon.right + anchors.verticalCenter: buildplateIcon.verticalCenter + anchors.leftMargin: Math.round(UM.Theme.getSize("default_margin").height / 2) + text: configuration.buildplateConfiguration + renderType: Text.NativeRendering + color: textColor } } } - //Buildplate row separator - Rectangle + Connections { - id: separator - - visible: buildplateInformation.visible - width: parent.width - 2 * parent.padding - height: visible ? Math.round(UM.Theme.getSize("thick_lining").height / 2) : 0 - color: textColor - } - - Item - { - id: buildplateInformation - width: parent.width - 2 * parent.padding - height: childrenRect.height - visible: configuration.buildplateConfiguration != "" - - UM.RecolorImage { - id: buildplateIcon - anchors.left: parent.left - width: UM.Theme.getSize("main_window_header_button_icon").width - height: UM.Theme.getSize("main_window_header_button_icon").height - sourceSize.width: width - sourceSize.height: height - source: UM.Theme.getIcon("buildplate") - color: textColor - } - - Label + target: Cura.MachineManager + onCurrentConfigurationChanged: { - id: buildplateLabel - anchors.left: buildplateIcon.right - anchors.verticalCenter: buildplateIcon.verticalCenter - anchors.leftMargin: Math.round(UM.Theme.getSize("default_margin").height / 2) - text: configuration.buildplateConfiguration - renderType: Text.NativeRendering - color: textColor + configurationItem.checked = Cura.MachineManager.matchesConfiguration(configuration) } } - } - MouseArea - { - id: mouse - anchors.fill: parent - onClicked: activateConfiguration() - cursorShape: Qt.PointingHandCursor - hoverEnabled: true - onEntered: + Component.onCompleted: { - parent.border.color = UM.Theme.getColor("configuration_item_border_hover") - if (configurationItem.selected == false) - { - configurationItem.color = UM.Theme.getColor("wide_lining") - } - } - onExited: - { - updateBorderColor() - if (configurationItem.selected == false) - { - configurationItem.color = UM.Theme.getColor("configuration_item") - } - } - } - - Connections - { - target: Cura.MachineManager - onCurrentConfigurationChanged: { - configurationItem.selected = Cura.MachineManager.matchesConfiguration(configuration) - updateBorderColor() - } - } - - Component.onCompleted: - { - configurationItem.selected = Cura.MachineManager.matchesConfiguration(configuration) - updateBorderColor() - } - - onVisibleChanged: - { - if(visible) - { - // I cannot trigger function updateBorderColor() after visibility change - color = selected ? UM.Theme.getColor("configuration_item_active") : UM.Theme.getColor("configuration_item") + configurationItem.checked = Cura.MachineManager.matchesConfiguration(configuration) } } } \ No newline at end of file diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml index 2f5bf0b1a2..7df93b9cbe 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml @@ -2,8 +2,7 @@ // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.7 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.4 import UM 1.2 as UM import Cura 1.0 as Cura @@ -31,8 +30,10 @@ Column width: parent.width - parent.padding height: Math.min(configurationList.contentHeight, 350 * screenScaleFactor) - style: UM.Theme.styles.scrollview - __wheelAreaScrollSpeed: 75 // Scroll three lines in one scroll event + ButtonGroup + { + buttons: configurationList.children + } ListView { @@ -64,6 +65,7 @@ Column } model: (outputDevice != null) ? outputDevice.uniqueConfigurations : [] + delegate: ConfigurationItem { width: parent.width - UM.Theme.getSize("default_margin").width From e05566865a9d5333d1dbf444b62a3ecbd4a29445 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 27 Nov 2018 11:19:03 +0100 Subject: [PATCH 0437/1240] Use ExtruderIcon component to display extruder icon Since we already have that component pre-designed, let's use it. Contributes to issue CURA-5876. --- .../PrintCoreConfiguration.qml | 34 +++---------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml index 97b5bee745..e8cadda05e 100644 --- a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml @@ -5,7 +5,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.0 import UM 1.2 as UM - +import Cura 1.0 as Cura Column { @@ -33,37 +33,13 @@ Column color: mainColor } - // Rounded item to show the extruder number - Item + Cura.ExtruderIcon { - id: extruderIconItem - anchors.verticalCenter: extruderLabel.verticalCenter - anchors.left: extruderLabel.right - anchors.leftMargin: Math.round(UM.Theme.getSize("default_margin").width / 2) - width: UM.Theme.getSize("section_icon").width height: UM.Theme.getSize("section_icon").height - - UM.RecolorImage { - id: mainCircle - anchors.fill: parent - - anchors.centerIn: parent - sourceSize.width: parent.width - sourceSize.height: parent.height - source: UM.Theme.getIcon("extruder_button") - color: mainColor - } - - Label - { - id: extruderNumberText - anchors.centerIn: parent - text: printCoreConfiguration.position + 1 - renderType: Text.NativeRendering - font: UM.Theme.getFont("default") - color: mainColor - } + materialColor: mainColor + anchors.left: extruderLabel.right + anchors.leftMargin: UM.Theme.getSize("narrow_margin").width } } From a3f0630ee9a1ba106969dada676c1d045cd5e762 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Tue, 27 Nov 2018 11:19:30 +0100 Subject: [PATCH 0438/1240] Rename printer images Contributes to CL-1150 --- ...ultimaker_3_ext.png => Ultimaker 3 Extended.png} | Bin .../png/{ultimaker_3.png => Ultimaker 3.png} | Bin .../png/{ultimaker_s5.png => Ultimaker S5.png} | Bin .../resources/qml/MonitorPrinterCard.qml | 4 ++-- 4 files changed, 2 insertions(+), 2 deletions(-) rename plugins/UM3NetworkPrinting/resources/png/{ultimaker_3_ext.png => Ultimaker 3 Extended.png} (100%) rename plugins/UM3NetworkPrinting/resources/png/{ultimaker_3.png => Ultimaker 3.png} (100%) rename plugins/UM3NetworkPrinting/resources/png/{ultimaker_s5.png => Ultimaker S5.png} (100%) diff --git a/plugins/UM3NetworkPrinting/resources/png/ultimaker_3_ext.png b/plugins/UM3NetworkPrinting/resources/png/Ultimaker 3 Extended.png similarity index 100% rename from plugins/UM3NetworkPrinting/resources/png/ultimaker_3_ext.png rename to plugins/UM3NetworkPrinting/resources/png/Ultimaker 3 Extended.png diff --git a/plugins/UM3NetworkPrinting/resources/png/ultimaker_3.png b/plugins/UM3NetworkPrinting/resources/png/Ultimaker 3.png similarity index 100% rename from plugins/UM3NetworkPrinting/resources/png/ultimaker_3.png rename to plugins/UM3NetworkPrinting/resources/png/Ultimaker 3.png diff --git a/plugins/UM3NetworkPrinting/resources/png/ultimaker_s5.png b/plugins/UM3NetworkPrinting/resources/png/Ultimaker S5.png similarity index 100% rename from plugins/UM3NetworkPrinting/resources/png/ultimaker_s5.png rename to plugins/UM3NetworkPrinting/resources/png/Ultimaker S5.png diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml index 8089ea2d30..bc8144bcd6 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml @@ -58,8 +58,8 @@ Item fillMode: Image.PreserveAspectFit source: { - console.log(printer) - return "../png/ultimaker_s5.png" + console.log(printer.type) + return "../png/"+printer.type+".png" } mipmap: true } From fd723e10842f2fe4d77d9c010495c72aea8dfd20 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Tue, 27 Nov 2018 11:19:45 +0100 Subject: [PATCH 0439/1240] Improve camera icon Contributes to CL-1150 --- .../UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml | 2 +- plugins/UM3NetworkPrinting/resources/svg/icons/camera.svg | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 plugins/UM3NetworkPrinting/resources/svg/icons/camera.svg diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml index bc8144bcd6..d1d9bec351 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml @@ -138,7 +138,7 @@ Item bottom: parent.bottom bottomMargin: 20 * screenScaleFactor // TODO: Theme! } - iconSource: "../svg/camera-icon.svg" + iconSource: "../svg/icons/camera.svg" } } diff --git a/plugins/UM3NetworkPrinting/resources/svg/icons/camera.svg b/plugins/UM3NetworkPrinting/resources/svg/icons/camera.svg new file mode 100644 index 0000000000..2eaebb812d --- /dev/null +++ b/plugins/UM3NetworkPrinting/resources/svg/icons/camera.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file From e6c2238fd51a5eec44a645ad4a1e798120bf58a5 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Tue, 27 Nov 2018 11:24:09 +0100 Subject: [PATCH 0440/1240] Added popup spacing by Y coordinate CURA-5941 --- resources/qml/ExpandableComponent.qml | 5 ++++- resources/qml/PrintSetupSelector.qml | 12 ++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 262c6bfd3f..3b1105ddd6 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -24,6 +24,9 @@ Item // How much spacing is needed around the popupItem property alias popupPadding: popup.padding + // How much spacing is needed for the popupItem by Y coordinate + property var popupSpacingY: 0 + // How much padding is needed around the header & button property alias headerPadding: background.padding @@ -143,7 +146,7 @@ Item id: popup // Ensure that the popup is located directly below the headerItem - y: headerItemLoader.height + 2 * background.padding + y: headerItemLoader.height + 2 * background.padding + popupSpacingY // Make the popup right aligned with the rest. The 3x padding is due to left, right and padding between // the button & text. diff --git a/resources/qml/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector.qml index 8271484e6a..5f593f9ba2 100644 --- a/resources/qml/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector.qml @@ -29,6 +29,7 @@ Cura.ExpandableComponent iconSource: UM.Theme.getIcon("pencil") popupPadding : 0 + popupSpacingY: 10 onCurrentModeIndexChanged: UM.Preferences.setValue("cura/active_mode", currentModeIndex) @@ -150,10 +151,13 @@ Cura.ExpandableComponent verticalAlignment: Text.AlignVCenter color: UM.Theme.getColor("text") height: parent.height - anchors.topMargin: UM.Theme.getSize("sidebar_margin").height -// anchors.bottomMargin: UM.Theme.getSize("sidebar_margin").height - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("print_setup_selector_margin").height + + anchors + { + topMargin: UM.Theme.getSize("sidebar_margin").height + left: parent.left + leftMargin: UM.Theme.getSize("print_setup_selector_margin").height + } } Rectangle From e2f85fcdc4922fada1ec7d259cab11a9c452df87 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 27 Nov 2018 11:25:43 +0100 Subject: [PATCH 0441/1240] Add extra space to printer button. Contributes to CURA-5942. --- resources/qml/PrinterSelector/MachineSelector.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index 93e5103aa8..15cd773c90 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -90,11 +90,13 @@ Cura.ExpandableComponent id: scroll width: parent.width clip: true + leftPadding: UM.Theme.getSize("default_lining").width + rightPadding: UM.Theme.getSize("default_lining").width MachineSelectorList { // Can't use parent.width since the parent is the flickable component and not the ScrollView - width: scroll.width + width: scroll.width - scroll.leftPadding - scroll.rightPadding property real maximumHeight: UM.Theme.getSize("machine_selector_widget_content").height - buttonRow.height onHeightChanged: From 75b827d3732f128bd3eacb14d8803d90fa057a4c Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 27 Nov 2018 11:26:20 +0100 Subject: [PATCH 0442/1240] Modify the hover behavior by removing the mouse area. Contributes to CURA-5942. --- resources/qml/PrinterSelector/MachineSelectorButton.qml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/resources/qml/PrinterSelector/MachineSelectorButton.qml b/resources/qml/PrinterSelector/MachineSelectorButton.qml index 992ea55b1d..369e75cede 100644 --- a/resources/qml/PrinterSelector/MachineSelectorButton.qml +++ b/resources/qml/PrinterSelector/MachineSelectorButton.qml @@ -16,6 +16,7 @@ Button leftPadding: UM.Theme.getSize("thick_margin").width rightPadding: UM.Theme.getSize("thick_margin").width checkable: true + hoverEnabled: true property var outputDevice: null property var printerTypesList: [] @@ -86,14 +87,6 @@ Button Cura.MachineManager.setActiveMachine(model.id) } - MouseArea - { - id: mouseArea - anchors.fill: parent - onPressed: mouse.accepted = false - hoverEnabled: true - } - Connections { target: outputDevice From 6876c12106e32d74a1d492eac6d95fc9130a8474 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 27 Nov 2018 11:43:32 +0100 Subject: [PATCH 0443/1240] Remove weird padding from button I don't know why the default Button has padding everywhere, but I don't want it. Contributes to issue CURA-5876. --- resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index d825a8cc6e..a6778702d1 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -15,6 +15,9 @@ Button signal activateConfiguration() height: childrenRect.height + padding: 0 //Stupid QML button has spacing by default. + rightPadding: 0 + leftPadding: 0 property var textColor: checked ? UM.Theme.getColor("configuration_item_text_active") : UM.Theme.getColor("configuration_item_text") @@ -73,7 +76,8 @@ Button height: childrenRect.height visible: configuration.buildplateConfiguration != "" - UM.RecolorImage { + UM.RecolorImage + { id: buildplateIcon anchors.left: parent.left width: UM.Theme.getSize("main_window_header_button_icon").width From a2b1f5397962d5e4da7511e54eef4e7a1ad235a0 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 27 Nov 2018 11:53:17 +0100 Subject: [PATCH 0444/1240] Use normal text colour instead of specialised configuration item text They were the same anyway, and they should always be the same. Contributes to issue CURA-5876. --- .../qml/Menus/ConfigurationMenu/ConfigurationItem.qml | 10 ++++------ .../Menus/ConfigurationMenu/ConfigurationListView.qml | 2 +- resources/themes/cura-light/theme.json | 2 -- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index a6778702d1..6a6f0ea834 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -19,8 +19,6 @@ Button rightPadding: 0 leftPadding: 0 - property var textColor: checked ? UM.Theme.getColor("configuration_item_text_active") : UM.Theme.getColor("configuration_item_text") - contentItem: Rectangle { height: childrenRect.height @@ -53,7 +51,7 @@ Button { width: Math.round(parent.width / 2) printCoreConfiguration: modelData - mainColor: textColor + mainColor: UM.Theme.getColor("text") } } } @@ -66,7 +64,7 @@ Button visible: buildplateInformation.visible width: parent.width - 2 * parent.padding height: visible ? Math.round(UM.Theme.getSize("thick_lining").height / 2) : 0 - color: textColor + color: UM.Theme.getColor("text") } Item @@ -85,7 +83,7 @@ Button sourceSize.width: width sourceSize.height: height source: UM.Theme.getIcon("buildplate") - color: textColor + color: UM.Theme.getColor("text") } Label @@ -96,7 +94,7 @@ Button anchors.leftMargin: Math.round(UM.Theme.getSize("default_margin").height / 2) text: configuration.buildplateConfiguration renderType: Text.NativeRendering - color: textColor + color: UM.Theme.getColor("text") } } } diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml index 7df93b9cbe..2b7444be01 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml @@ -58,7 +58,7 @@ Column { text: section font: UM.Theme.getFont("small") - color: UM.Theme.getColor("configuration_item_text") + color: UM.Theme.getColor("text") padding: UM.Theme.getSize("narrow_margin").width } } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index a51b397262..383159fd91 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -319,8 +319,6 @@ "configuration_item": [255, 255, 255, 0], "configuration_item_active": [12, 169, 227, 32], - "configuration_item_text": [0, 0, 0, 255], - "configuration_item_text_active": [0, 0, 0, 255], "configuration_item_border": [127, 127, 127, 255], "configuration_item_border_active": [12, 169, 227, 32], "configuration_item_border_hover": [50, 130, 255, 255], From 44c415ff78fbc085a81e2fc6da08e91c44c0ea8e Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 27 Nov 2018 11:58:32 +0100 Subject: [PATCH 0445/1240] Add shadow to ExpandableComponent --- resources/qml/ExpandableComponent.qml | 23 ++++++++++++++++++++++- resources/themes/cura-light/theme.json | 2 +- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 9b2826daed..b438f0398c 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -4,6 +4,8 @@ import QtQuick.Controls 2.3 import UM 1.2 as UM import Cura 1.0 as Cura +import QtGraphicalEffects 1.0 // For the dropshadow + // The expandable component has 3 major sub components: // * The headerItem; Always visible and should hold some info about what happens if the component is expanded // * The popupItem; The content that needs to be shown if the component is expanded. @@ -58,6 +60,12 @@ Item // On what side should the header corners be shown? 1 is down, 2 is left, 3 is up and 4 is right. property alias headerCornerSide: background.cornerSide + property alias headerShadowColor: shadow.color + + property alias enableHeaderShadow: shadow.visible + + property int shadowOffset: 2 + function togglePopup() { if(popup.visible) @@ -149,13 +157,26 @@ Item onExited: background.color = headerBackgroundColor } } + DropShadow + { + id: shadow + // Don't blur the shadow + radius: 0 + anchors.fill: background + source: background + verticalOffset: base.shadowOffset + visible: true + color: UM.Theme.getColor("action_button_shadow") + // Should always be drawn behind the background. + z: background.z - 1 + } Popup { id: popup // Ensure that the popup is located directly below the headerItem - y: headerItemLoader.height + 2 * background.padding + y: headerItemLoader.height + 2 * background.padding + base.shadowOffset // Make the popup aligned with the rest, using the property popupAlignment to decide whether is right or left. // In case of right alignment, the 3x padding is due to left, right and padding between the button & text. diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 0cfa36fd2e..cc33d82b7d 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -178,7 +178,7 @@ "action_button_disabled": [245, 245, 245, 255], "action_button_disabled_text": [127, 127, 127, 255], "action_button_disabled_border": [245, 245, 245, 255], - "action_button_shadow": [64, 47, 205, 255], + "action_button_shadow": [223, 223, 223, 255], "action_button_disabled_shadow": [228, 228, 228, 255], "scrollbar_background": [255, 255, 255, 255], From e04f14b50c8967d063c90731b6e367ba5c996a9e Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 27 Nov 2018 12:01:05 +0100 Subject: [PATCH 0446/1240] Also add shadow to openFile button --- plugins/PrepareStage/PrepareMenu.qml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index 81799206a0..31a78ed290 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -8,6 +8,7 @@ import QtQuick.Controls 2.3 import UM 1.3 as UM import Cura 1.1 as Cura +import QtGraphicalEffects 1.0 // For the dropshadow Item { @@ -107,12 +108,26 @@ Item background: Rectangle { + id: background height: UM.Theme.getSize("stage_menu").height width: UM.Theme.getSize("stage_menu").height radius: UM.Theme.getSize("default_radius").width color: openFileButton.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button") } + DropShadow + { + id: shadow + // Don't blur the shadow + radius: 0 + anchors.fill: background + source: background + verticalOffset: 2 + visible: true + color: UM.Theme.getColor("action_button_shadow") + // Should always be drawn behind the background. + z: background.z - 1 + } } } } From 1a6822436ddd22d53553c0d2ddcf549084bf763c Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 27 Nov 2018 12:01:43 +0100 Subject: [PATCH 0447/1240] Add missing HoverEnabled property Some systems, like mine, don't have the hoverEnabled default set to true. --- plugins/PrepareStage/PrepareMenu.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index 31a78ed290..4212911011 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -93,6 +93,7 @@ Item height: UM.Theme.getSize("stage_menu").height width: UM.Theme.getSize("stage_menu").height onClicked: Cura.Actions.open.trigger() + hoverEnabled: true contentItem: UM.RecolorImage { @@ -115,6 +116,7 @@ Item radius: UM.Theme.getSize("default_radius").width color: openFileButton.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button") } + DropShadow { id: shadow From e58409b1ef07885a7e7d1807cbf243fc15deaf47 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 27 Nov 2018 12:51:52 +0100 Subject: [PATCH 0448/1240] Correct colours for the selected configuration This should always be the same as the ExpandableComponent's background, so white when it's not active and light blue when it's active, regardless of theme. The naming of this theme entry is a bit weird because the ActionButton theme is used for ExpandableComponent and now for ConfigurationItem as well, but that's why we should NEVER name these theme entries to something specific to one item. Oh well, I'm not about to refactor that now with all the branches going on everywhere. Contributes to issue CURA-5876. --- resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 6a6f0ea834..98f777d3f3 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -22,7 +22,7 @@ Button contentItem: Rectangle { height: childrenRect.height - color: parent.checked ? UM.Theme.getColor("configuration_item_active") : UM.Theme.getColor("configuration_item") + color: parent.checked ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button") border.color: (parent.checked || parent.hovered) ? UM.Theme.getColor("primary") : UM.Theme.getColor("lining") border.width: UM.Theme.getSize("default_lining").width From 0be2453daf29080111f622e371708fd970946336 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 27 Nov 2018 12:55:07 +0100 Subject: [PATCH 0449/1240] Update style of border of configuration items Contributes to issue CURA-5876. --- resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 98f777d3f3..278381dc5a 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -23,8 +23,8 @@ Button { height: childrenRect.height color: parent.checked ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button") - border.color: (parent.checked || parent.hovered) ? UM.Theme.getColor("primary") : UM.Theme.getColor("lining") - border.width: UM.Theme.getSize("default_lining").width + border.color: parent.hovered ? UM.Theme.getColor("primary") : UM.Theme.getColor("lining") + border.width: parent.hovered ? UM.Theme.getSize("thick_lining").width : UM.Theme.getSize("default_lining").width Column { From e751b59b1cd13855b413d79ef8a950bb53b7287b Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 27 Nov 2018 12:55:58 +0100 Subject: [PATCH 0450/1240] Remove unused configuration item theme entries The theme of configuration items is now in sync with the rest of the interface. Contributes to issue CURA-5876. --- resources/themes/cura-dark/theme.json | 8 -------- resources/themes/cura-light/theme.json | 6 ------ 2 files changed, 14 deletions(-) diff --git a/resources/themes/cura-dark/theme.json b/resources/themes/cura-dark/theme.json index 62b1dbfa0f..40016c5a94 100644 --- a/resources/themes/cura-dark/theme.json +++ b/resources/themes/cura-dark/theme.json @@ -197,14 +197,6 @@ "layerview_support_interface": [64, 192, 255, 255], "layerview_nozzle": [181, 166, 66, 120], - "configuration_item": [0, 0, 0, 0], - "configuration_item_active": [12, 169, 227, 179], - "configuration_item_text": [255, 255, 255, 255], - "configuration_item_text_active": [255, 255, 255, 255], - "configuration_item_border": [255, 255, 255, 255], - "configuration_item_border_active": [12, 169, 227, 179], - "configuration_item_border_hover": [12, 169, 227, 179], - "material_compatibility_warning": [255, 255, 255, 255], "quality_slider_unavailable": [179, 179, 179, 255], diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 383159fd91..de9b48c7f0 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -317,12 +317,6 @@ "layerview_support_interface": [64, 192, 255, 255], "layerview_nozzle": [181, 166, 66, 50], - "configuration_item": [255, 255, 255, 0], - "configuration_item_active": [12, 169, 227, 32], - "configuration_item_border": [127, 127, 127, 255], - "configuration_item_border_active": [12, 169, 227, 32], - "configuration_item_border_hover": [50, 130, 255, 255], - "tab_status_connected": [50, 130, 255, 255], "tab_status_disconnected": [200, 200, 200, 255], From fb84b344ecac3683848dbdd71d20e5079bd11818 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 27 Nov 2018 12:58:06 +0100 Subject: [PATCH 0451/1240] Add gradient to header bar --- resources/qml/Cura.qml | 27 ++++++++++++++++++- resources/qml/MainWindow/MainWindowHeader.qml | 27 ++++++++++++++++++- resources/themes/cura-light/theme.json | 1 + 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 2814bb9eb2..63888a4ee4 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -6,6 +6,7 @@ import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import QtQuick.Layouts 1.1 import QtQuick.Dialogs 1.2 +import QtGraphicalEffects 1.0 import UM 1.3 as UM import Cura 1.1 as Cura @@ -153,7 +154,31 @@ UM.MainWindow } visible: stageMenu.source != "" height: Math.round(UM.Theme.getSize("stage_menu").height / 2) - color: UM.Theme.getColor("main_window_header_background") + + LinearGradient + { + anchors.fill: parent + start: Qt.point(0, 0) + end: Qt.point(parent.width, 0) + gradient: Gradient + { + GradientStop + { + position: 0.0 + color: UM.Theme.getColor("main_window_header_background") + } + GradientStop + { + position: 0.5 + color: UM.Theme.getColor("main_window_header_background_gradient") + } + GradientStop + { + position: 1.0 + color: UM.Theme.getColor("main_window_header_background") + } + } + } } Connections diff --git a/resources/qml/MainWindow/MainWindowHeader.qml b/resources/qml/MainWindow/MainWindowHeader.qml index a24af7ee45..2942a9feb3 100644 --- a/resources/qml/MainWindow/MainWindowHeader.qml +++ b/resources/qml/MainWindow/MainWindowHeader.qml @@ -8,6 +8,7 @@ import QtQuick.Controls.Styles 1.1 import UM 1.4 as UM import Cura 1.0 as Cura +import QtGraphicalEffects 1.0 import "../Account" @@ -17,7 +18,31 @@ Rectangle implicitHeight: UM.Theme.getSize("main_window_header").height implicitWidth: UM.Theme.getSize("main_window_header").width - color: UM.Theme.getColor("main_window_header_background") + + LinearGradient + { + anchors.fill: parent + start: Qt.point(0, 0) + end: Qt.point(parent.width, 0) + gradient: Gradient + { + GradientStop + { + position: 0.0 + color: UM.Theme.getColor("main_window_header_background") + } + GradientStop + { + position: 0.5 + color: UM.Theme.getColor("main_window_header_background_gradient") + } + GradientStop + { + position: 1.0 + color: UM.Theme.getColor("main_window_header_background") + } + } + } Image { diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index cc33d82b7d..157adcf6a5 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -100,6 +100,7 @@ "secondary_button_text": [30, 102, 215, 255], "main_window_header_background": [10, 8, 80, 255], + "main_window_header_background_gradient": [25, 23, 91, 255], "main_window_header_button_text_active": [10, 8, 80, 255], "main_window_header_button_text_inactive": [255, 255, 255, 255], "main_window_header_button_text_hovered": [255, 255, 255, 255], From 81cadf702cedea664c1071d46e5261c1dc592b93 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 27 Nov 2018 13:00:51 +0100 Subject: [PATCH 0452/1240] Give configuration items a rounded radius Contributes to issue CURA-5876. --- resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 278381dc5a..f04e856705 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -25,6 +25,7 @@ Button color: parent.checked ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button") border.color: parent.hovered ? UM.Theme.getColor("primary") : UM.Theme.getColor("lining") border.width: parent.hovered ? UM.Theme.getSize("thick_lining").width : UM.Theme.getSize("default_lining").width + radius: UM.Theme.getSize("default_radius").width Column { From 0794a2c8c9197c536ffcba090df72edd7d6e7fc6 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 27 Nov 2018 13:03:36 +0100 Subject: [PATCH 0453/1240] Remove not needed printer and extruder definitions. Contributes to CURA-5902. --- .../bibo2_single_extruder_0.def.json | 98 ------------------- .../bibo2_single_extruder_1.def.json | 98 ------------------- .../bibo2_single_extruder_0_0.def.json | 40 -------- .../bibo2_single_extruder_0_1.def.json | 40 -------- .../bibo2_single_extruder_1_0.def.json | 40 -------- .../bibo2_single_extruder_1_1.def.json | 40 -------- 6 files changed, 356 deletions(-) delete mode 100644 resources/definitions/bibo2_single_extruder_0.def.json delete mode 100644 resources/definitions/bibo2_single_extruder_1.def.json delete mode 100644 resources/extruders/bibo2_single_extruder_0_0.def.json delete mode 100644 resources/extruders/bibo2_single_extruder_0_1.def.json delete mode 100644 resources/extruders/bibo2_single_extruder_1_0.def.json delete mode 100644 resources/extruders/bibo2_single_extruder_1_1.def.json diff --git a/resources/definitions/bibo2_single_extruder_0.def.json b/resources/definitions/bibo2_single_extruder_0.def.json deleted file mode 100644 index 93c7a4e5ae..0000000000 --- a/resources/definitions/bibo2_single_extruder_0.def.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "id": "BIBO2 single E1", - "version": 2, - "name": "BIBO2 single E1", - "inherits": "fdmprinter", - "metadata": { - "visible": true, - "author": "na", - "manufacturer": "BIBO", - "category": "Other", - "file_formats": "text/x-gcode", - "has_materials": true, - "machine_extruder_trains": { - "0": "bibo2_single_extruder_0_0", - "1": "bibo2_single_extruder_0_1" - }, - "first_start_actions": [ - "MachineSettingsAction" - ] - }, - "overrides": { - "machine_name": { - "default_value": "BIBO2 single Extruder 1 (right)" - }, - "machine_width": { - "default_value": 214 - }, - "machine_height": { - "default_value": 160 - }, - "machine_depth": { - "default_value": 186 - }, - "machine_center_is_zero": { - "default_value": true - }, - "machine_heated_bed": { - "default_value": true - }, - "machine_nozzle_size": { - "default_value": 0.4 - }, - "machine_nozzle_heat_up_speed": { - "default_value": 2 - }, - "machine_nozzle_cool_down_speed": { - "default_value": 2 - }, - "machine_head_with_fans_polygon": { - "default_value": [ - [ - -68.18, - 64.63 - ], - [ - -68.18, - -47.38 - ], - [ - 35.18, - 64.63 - ], - [ - 35.18, - -47.38 - ] - ] - }, - "material_diameter": { - "default_value": 1.75 - }, - "gantry_height": { - "default_value": 12 - }, - "machine_use_extruder_offset_to_offset_coords": { - "default_value": true - }, - "machine_gcode_flavor": { - "default_value": "RepRap (Marlin/Sprinter)" - }, - "machine_start_gcode": { - "default_value": ";Startcode BIBO printers\nM109 T1 S170 ;preheat the other extruder, so it will not knock or ruin the print\nG90 ; absolute mode\nG21 ; metric values\nM82 ; Extruder in absolute mode\nM107\nG28\nG1 Z2 F400\nT0\nG90\nG92 E0\nG28\nG1 Y0 F1200 E0\nG92 E0\nG1 X-15.0 Y-92.9 Z0.3 F2400.0\t\t; move to start-line position\nG1 X15.0 Y-92.9 Z0.3 F1000.0 E2\t\t; draw 1st line\nG1 X15.0 Y-92.6 Z0.3 F3000.0\t\t; move to side a little\nG1 X-15.0 Y-92.6 Z0.3 F1000.0 E4\t\t; draw 2nd line\nG1 X-15.0 Y-92.3 Z0.3 F3000.0\t\t; move to side a little\nG1 X15.0 Y-92.3 Z0.3 F1000.0 E6\t\t; draw 3rd line\nG1 X15.0 Y-92 Z0.3 F3000.0\t\t; move to side a little\nG1 X-15.0 Y-92 Z0.3 F1000.0 E8\t\t; draw 4th line\nG1 X-16.0 Y-91.7 Z0.3 F3000.0\t\t; move to side a little\nG1 X16.0 Y-91.7 Z0.3 F1000.0 E10\t\t; draw 5th line\nG1 X16.0 Y-91.4 Z0.3 F3000.0\t\t; move to side a little\nG1 X-16.0 Y-91.4 Z0.3 F1000.0 E12\t\t; draw 5th line\nG1 E11.5 F2400\t\t\t\t; retract filament 0.5mm\nG92 E0\nM117 BIBO Printing..." - }, - "machine_end_gcode": { - "default_value": ";BIBO End GCode\nM107\nG91 ; Relative positioning\nG1 Z1 F100\nM104 T0 S0\nM104 T1 S0\nG1 X-20 Y-20 F3000\nG28 X0 Y0\nG90 ; Absolute positioning\nG92 E0 ; Reset extruder position\nM140 S0 ; Disable heated bed\nM84 ; Turn steppers off\nM117 BIBO Print complete\n " - }, - "machine_extruder_count": { - "default_value": 2 - }, - "prime_tower_position_x": { - "default_value": 50 - }, - "prime_tower_position_y": { - "default_value": 50 - } - } -} - diff --git a/resources/definitions/bibo2_single_extruder_1.def.json b/resources/definitions/bibo2_single_extruder_1.def.json deleted file mode 100644 index 246add09ab..0000000000 --- a/resources/definitions/bibo2_single_extruder_1.def.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "id": "BIBO2 single E2", - "version": 2, - "name": "BIBO2 single E2", - "inherits": "fdmprinter", - "metadata": { - "visible": true, - "author": "na", - "manufacturer": "BIBO", - "category": "Other", - "file_formats": "text/x-gcode", - "has_materials": true, - "machine_extruder_trains": { - "0": "bibo2_single_extruder_1_0", - "1": "bibo2_single_extruder_1_1" - }, - "first_start_actions": [ - "MachineSettingsAction" - ] - }, - "overrides": { - "machine_name": { - "default_value": "BIBO2 single Extruder 2 (left)" - }, - "machine_width": { - "default_value": 214 - }, - "machine_height": { - "default_value": 160 - }, - "machine_depth": { - "default_value": 186 - }, - "machine_center_is_zero": { - "default_value": true - }, - "machine_heated_bed": { - "default_value": true - }, - "machine_nozzle_size": { - "default_value": 0.4 - }, - "machine_nozzle_heat_up_speed": { - "default_value": 2 - }, - "machine_nozzle_cool_down_speed": { - "default_value": 2 - }, - "machine_head_with_fans_polygon": { - "default_value": [ - [ - -68.18, - 64.63 - ], - [ - -68.18, - -47.38 - ], - [ - 35.18, - 64.63 - ], - [ - 35.18, - -47.38 - ] - ] - }, - "material_diameter": { - "default_value": 1.75 - }, - "gantry_height": { - "default_value": 12 - }, - "machine_use_extruder_offset_to_offset_coords": { - "default_value": true - }, - "machine_gcode_flavor": { - "default_value": "RepRap (Marlin/Sprinter)" - }, - "machine_start_gcode": { - "default_value": ";Startcode BIBO printers\nM109 T0 S170 ;preheat the other extruder, so it will not knock or ruin the print\nG90 ; absolute mode\nG21 ; metric values\nM82 ; Extruder in absolute mode\nM107\nG28\nG1 Z2 F400\nT0\nG90\nG92 E0\nG28\nG1 Y0 F1200 E0\nG92 E0\nT1\nG92 E0\nG1 X-15.0 Y-92.9 Z0.3 F2400.0\t\t; move to start-line position\nG1 X15.0 Y-92.9 Z0.3 F1000.0 E2\t\t; draw 1st line\nG1 X15.0 Y-92.6 Z0.3 F3000.0\t\t; move to side a little\nG1 X-15.0 Y-92.6 Z0.3 F1000.0 E4\t\t; draw 2nd line\nG1 X-15.0 Y-92.3 Z0.3 F3000.0\t\t; move to side a little\nG1 X15.0 Y-92.3 Z0.3 F1000.0 E6\t\t; draw 3rd line\nG1 X15.0 Y-92 Z0.3 F3000.0\t\t; move to side a little\nG1 X-15.0 Y-92 Z0.3 F1000.0 E8\t\t; draw 4th line\nG1 X-16.0 Y-91.7 Z0.3 F3000.0\t\t; move to side a little\nG1 X16.0 Y-91.7 Z0.3 F1000.0 E10\t\t; draw 5th line\nG1 X16.0 Y-91.4 Z0.3 F3000.0\t\t; move to side a little\nG1 X-16.0 Y-91.4 Z0.3 F1000.0 E12\t\t; draw 5th line\nG1 E11.5 F2400\t\t\t\t; retract filament 0.5mm\nG92 E0\nM117 BIBO Printing..." - }, - "machine_end_gcode": { - "default_value": ";BIBO End GCode\nM107\nG91 ; Relative positioning\nG1 Z1 F100\nM104 T0 S0\nM104 T1 S0\nG1 X-20 Y-20 F3000\nG28 X0 Y0\nG90 ; Absolute positioning\nG92 E0 ; Reset extruder position\nM140 S0 ; Disable heated bed\nM84 ; Turn steppers off\nM117 BIBO Print complete\n " - }, - "machine_extruder_count": { - "default_value": 2 - }, - "prime_tower_position_x": { - "default_value": 50 - }, - "prime_tower_position_y": { - "default_value": 50 - } - } -} - diff --git a/resources/extruders/bibo2_single_extruder_0_0.def.json b/resources/extruders/bibo2_single_extruder_0_0.def.json deleted file mode 100644 index 7d0b246131..0000000000 --- a/resources/extruders/bibo2_single_extruder_0_0.def.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "id": "BIBO2 E1a", - "version": 2, - "name": "BIBO2 E1", - "inherits": "fdmextruder", - "metadata": { - "machine": "BIBO2 single E1", - "position": "0" - }, - "overrides": { - "extruder_nr": { - "default_value": 0, - "maximum_value": "1" - }, - "machine_nozzle_offset_x": { - "default_value": 0.0 - }, - "machine_nozzle_offset_y": { - "default_value": 0.0 - }, - "machine_extruder_start_pos_abs": { - "default_value": true - }, - "machine_extruder_start_pos_x": { - "value": "prime_tower_position_x" - }, - "machine_extruder_start_pos_y": { - "value": "prime_tower_position_y" - }, - "machine_extruder_end_pos_abs": { - "default_value": true - }, - "machine_extruder_end_pos_x": { - "value": "prime_tower_position_x" - }, - "machine_extruder_end_pos_y": { - "value": "prime_tower_position_y" - } - } -} diff --git a/resources/extruders/bibo2_single_extruder_0_1.def.json b/resources/extruders/bibo2_single_extruder_0_1.def.json deleted file mode 100644 index 76187696fc..0000000000 --- a/resources/extruders/bibo2_single_extruder_0_1.def.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "id": "BIBO2 E1b", - "version": 2, - "name": "E2 not used", - "inherits": "fdmextruder", - "metadata": { - "machine": "BIBO2 single E1", - "position": "1" - }, - "overrides": { - "extruder_nr": { - "default_value": 1, - "maximum_value": "1" - }, - "machine_nozzle_offset_x": { - "default_value": 0.0 - }, - "machine_nozzle_offset_y": { - "default_value": 0.0 - }, - "machine_extruder_start_pos_abs": { - "default_value": true - }, - "machine_extruder_start_pos_x": { - "value": "prime_tower_position_x" - }, - "machine_extruder_start_pos_y": { - "value": "prime_tower_position_y" - }, - "machine_extruder_end_pos_abs": { - "default_value": true - }, - "machine_extruder_end_pos_x": { - "value": "prime_tower_position_x" - }, - "machine_extruder_end_pos_y": { - "value": "prime_tower_position_y" - } - } -} diff --git a/resources/extruders/bibo2_single_extruder_1_0.def.json b/resources/extruders/bibo2_single_extruder_1_0.def.json deleted file mode 100644 index 3cf667de82..0000000000 --- a/resources/extruders/bibo2_single_extruder_1_0.def.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "id": "BIBO2 E2a", - "version": 2, - "name": "E1 not used", - "inherits": "fdmextruder", - "metadata": { - "machine": "BIBO2 single E2", - "position": "0" - }, - "overrides": { - "extruder_nr": { - "default_value": 0, - "maximum_value": "1" - }, - "machine_nozzle_offset_x": { - "default_value": 0 - }, - "machine_nozzle_offset_y": { - "default_value": 0 - }, - "machine_extruder_start_pos_abs": { - "default_value": true - }, - "machine_extruder_start_pos_x": { - "value": "prime_tower_position_x" - }, - "machine_extruder_start_pos_y": { - "value": "prime_tower_position_y" - }, - "machine_extruder_end_pos_abs": { - "default_value": true - }, - "machine_extruder_end_pos_x": { - "value": "prime_tower_position_x" - }, - "machine_extruder_end_pos_y": { - "value": "prime_tower_position_y" - } - } -} diff --git a/resources/extruders/bibo2_single_extruder_1_1.def.json b/resources/extruders/bibo2_single_extruder_1_1.def.json deleted file mode 100644 index e8f3ec7054..0000000000 --- a/resources/extruders/bibo2_single_extruder_1_1.def.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "id": "BIBO2 E2b", - "version": 2, - "name": "BIBO2 E2", - "inherits": "fdmextruder", - "metadata": { - "machine": "BIBO2 single E2", - "position": "1" - }, - "overrides": { - "extruder_nr": { - "default_value": 1, - "maximum_value": "1" - }, - "machine_nozzle_offset_x": { - "default_value": 0 - }, - "machine_nozzle_offset_y": { - "default_value": 0 - }, - "machine_extruder_start_pos_abs": { - "default_value": true - }, - "machine_extruder_start_pos_x": { - "value": "prime_tower_position_x" - }, - "machine_extruder_start_pos_y": { - "value": "prime_tower_position_y" - }, - "machine_extruder_end_pos_abs": { - "default_value": true - }, - "machine_extruder_end_pos_x": { - "value": "prime_tower_position_x" - }, - "machine_extruder_end_pos_y": { - "value": "prime_tower_position_y" - } - } -} From c6a9aca34602c0e5a32c7f308055fcd9815a2bbb Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 27 Nov 2018 13:05:32 +0100 Subject: [PATCH 0454/1240] Switch checked and hovered themes I misunderstood the theme as it was in the screenshots. Contributes to issue CURA-5876. --- resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index f04e856705..2988ab9eec 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -22,9 +22,9 @@ Button contentItem: Rectangle { height: childrenRect.height - color: parent.checked ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button") - border.color: parent.hovered ? UM.Theme.getColor("primary") : UM.Theme.getColor("lining") - border.width: parent.hovered ? UM.Theme.getSize("thick_lining").width : UM.Theme.getSize("default_lining").width + color: parent.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button") + border.color: parent.checked ? UM.Theme.getColor("primary") : UM.Theme.getColor("lining") + border.width: parent.checked ? UM.Theme.getSize("thick_lining").width : UM.Theme.getSize("default_lining").width radius: UM.Theme.getSize("default_radius").width Column From 89126c7323a6764cbe3c961953e3361f4581b641 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 27 Nov 2018 13:08:41 +0100 Subject: [PATCH 0455/1240] Use background instead of contentItem This way the padding and such doesn't matter any more. And the original background disappears, which was unthemed and still behind the original contentItem. Contributes to issue CURA-5876. --- resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 2988ab9eec..297c95d1d1 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -15,11 +15,8 @@ Button signal activateConfiguration() height: childrenRect.height - padding: 0 //Stupid QML button has spacing by default. - rightPadding: 0 - leftPadding: 0 - contentItem: Rectangle + background: Rectangle { height: childrenRect.height color: parent.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button") From a6a364c337efb4f732247f30e60d14884aabdadc Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 27 Nov 2018 13:12:47 +0100 Subject: [PATCH 0456/1240] Move material diameter and machine nozzle size to the extruder definition. Contributes to CURA-5902. --- resources/definitions/bibo2_dual.def.json | 6 ------ resources/extruders/bibo2_dual_extruder_0.def.json | 6 ++++++ resources/extruders/bibo2_dual_extruder_1.def.json | 10 ++++++++-- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/resources/definitions/bibo2_dual.def.json b/resources/definitions/bibo2_dual.def.json index e57fb64ec0..2036290ebd 100644 --- a/resources/definitions/bibo2_dual.def.json +++ b/resources/definitions/bibo2_dual.def.json @@ -37,9 +37,6 @@ "machine_heated_bed": { "default_value": true }, - "machine_nozzle_size": { - "default_value": 0.4 - }, "machine_nozzle_heat_up_speed": { "default_value": 2 }, @@ -66,9 +63,6 @@ ] ] }, - "material_diameter": { - "default_value": 1.75 - }, "gantry_height": { "default_value": 12 }, diff --git a/resources/extruders/bibo2_dual_extruder_0.def.json b/resources/extruders/bibo2_dual_extruder_0.def.json index 7cdc03d504..f83801fa0c 100644 --- a/resources/extruders/bibo2_dual_extruder_0.def.json +++ b/resources/extruders/bibo2_dual_extruder_0.def.json @@ -12,6 +12,12 @@ "default_value": 0, "maximum_value": "1" }, + "material_diameter": { + "default_value": 1.75 + }, + "machine_nozzle_size": { + "default_value": 0.4 + }, "machine_nozzle_offset_x": { "default_value": 0.0 }, diff --git a/resources/extruders/bibo2_dual_extruder_1.def.json b/resources/extruders/bibo2_dual_extruder_1.def.json index daa1504220..5f479ba54b 100644 --- a/resources/extruders/bibo2_dual_extruder_1.def.json +++ b/resources/extruders/bibo2_dual_extruder_1.def.json @@ -12,11 +12,17 @@ "default_value": 1, "maximum_value": "1" }, + "material_diameter": { + "default_value": 1.75 + }, + "machine_nozzle_size": { + "default_value": 0.4 + }, "machine_nozzle_offset_x": { - "default_value": 0 + "default_value": 0.0 }, "machine_nozzle_offset_y": { - "default_value": 0 + "default_value": 0.0 }, "machine_extruder_start_pos_abs": { "default_value": true From b15c272d235c36edc5056aace5293f287d430144 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 27 Nov 2018 13:24:13 +0100 Subject: [PATCH 0457/1240] Fix applying configuration when clicking Also no longer use that signal for it. It was completely unnecessary. Contributes to issue CURA-5876. --- resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml | 6 +++++- .../qml/Menus/ConfigurationMenu/ConfigurationListView.qml | 5 ----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 297c95d1d1..69c0b11882 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -12,7 +12,6 @@ Button id: configurationItem property var configuration: null - signal activateConfiguration() height: childrenRect.height @@ -111,4 +110,9 @@ Button configurationItem.checked = Cura.MachineManager.matchesConfiguration(configuration) } } + + onClicked: + { + Cura.MachineManager.applyRemoteConfiguration(configuration) + } } \ No newline at end of file diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml index 2b7444be01..86deae9c4e 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml @@ -70,11 +70,6 @@ Column { width: parent.width - UM.Theme.getSize("default_margin").width configuration: modelData - onActivateConfiguration: - { - switchPopupState() - Cura.MachineManager.applyRemoteConfiguration(configuration) - } } } } From fcde6e3a34d7b8f51da08473fedf7aac98b6565f Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 27 Nov 2018 13:28:28 +0100 Subject: [PATCH 0458/1240] Fix the open file button Now it has the correct size and it doesn't look blurry. Contributes to CURA-5942. --- plugins/PrepareStage/PrepareMenu.qml | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index 4212911011..10b4262f01 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -95,16 +95,21 @@ Item onClicked: Cura.Actions.open.trigger() hoverEnabled: true - contentItem: UM.RecolorImage + contentItem: Item { - id: buttonIcon - source: UM.Theme.getIcon("load") - width: UM.Theme.getSize("button_icon").width - height: UM.Theme.getSize("button_icon").height - color: UM.Theme.getColor("toolbar_button_text") + anchors.fill: parent + UM.RecolorImage + { + id: buttonIcon + anchors.centerIn: parent + source: UM.Theme.getIcon("load") + width: UM.Theme.getSize("button_icon").width + height: UM.Theme.getSize("button_icon").height + color: UM.Theme.getColor("toolbar_button_text") - sourceSize.width: width - sourceSize.height: height + sourceSize.width: width + sourceSize.height: height + } } background: Rectangle From cf8d88054d8ded00237ee95587b5e0366060794e Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 27 Nov 2018 13:29:57 +0100 Subject: [PATCH 0459/1240] Use proper colour for material in configuration item No idea this would be so easy. Contributes to issue CURA-5876. --- .../qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml index e8cadda05e..ae21a19f5e 100644 --- a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml @@ -37,7 +37,7 @@ Column { width: UM.Theme.getSize("section_icon").width height: UM.Theme.getSize("section_icon").height - materialColor: mainColor + materialColor: printCoreConfiguration.material.color anchors.left: extruderLabel.right anchors.leftMargin: UM.Theme.getSize("narrow_margin").width } From e863c34f68b3fbe69218d76b3e6b549362468bf1 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 27 Nov 2018 13:33:35 +0100 Subject: [PATCH 0460/1240] Align the text to the center of the button Contributes to CURA-5942. --- resources/qml/MainWindow/MainWindowHeader.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/qml/MainWindow/MainWindowHeader.qml b/resources/qml/MainWindow/MainWindowHeader.qml index 2942a9feb3..34936e9b5a 100644 --- a/resources/qml/MainWindow/MainWindowHeader.qml +++ b/resources/qml/MainWindow/MainWindowHeader.qml @@ -122,6 +122,8 @@ Rectangle text: marketplaceButton.text color: marketplaceButton.hovered ? UM.Theme.getColor("main_window_header_background") : UM.Theme.getColor("primary_text") width: contentWidth + verticalAlignment: Text.AlignVCenter + renderType: Text.NativeRendering } anchors From 6956a732a0453f6f51dea1f381451346fe241009 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Tue, 27 Nov 2018 13:49:04 +0100 Subject: [PATCH 0461/1240] Use tapbar component from UM, change the print_setup_widget width --- resources/qml/PrintSetupSelector.qml | 17 +++++------------ resources/themes/cura-light/theme.json | 2 +- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/resources/qml/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector.qml index 5f593f9ba2..279254ec64 100644 --- a/resources/qml/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector.qml @@ -5,7 +5,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Layouts 1.3 -import UM 1.2 as UM +import UM 1.3 as UM import Cura 1.0 as Cura import "Menus" import "Menus/ConfigurationMenu" @@ -217,25 +217,18 @@ Cura.ExpandableComponent rightMargin: 5 } - - TabBar + UM.TabRow { id: tabBar + anchors.top: popupItemHeader.bottom + anchors.topMargin: UM.Theme.getSize("default_margin").height onCurrentIndexChanged: Cura.ExtruderManager.setActiveExtruderIndex(currentIndex) - width: parent.width - height: UM.Theme.getSize("print_setup_tap_bar").width z: 1 Repeater { - id: extruder_model_repeater model: extrudersModel - - delegate: TabButton + delegate: UM.TabRowButton { - z: 2 - width: ListView.view != null ? Math.round(ListView.view.width / extrudersModel.rowCount()): 0 - implicitHeight: parent.height - contentItem: Rectangle { z: 2 diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 029da9c14b..e6889b50c0 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -381,7 +381,7 @@ "account_button": [12, 3], - "print_setup_widget": [30.0, 42.0], + "print_setup_widget": [38.0, 42.0], "print_setup_mode_toggle": [0.0, 2.0], "print_setup_item": [0.0, 2.0], "print_setup_extruder_box": [0.0, 6.0], From bb9598afd1c2162d461ea8c456da6f0b54e228e9 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 27 Nov 2018 13:55:54 +0100 Subject: [PATCH 0462/1240] Redesign layout of configuration item contents This is the new design. Contributes to issue CURA-5876. --- .../ConfigurationMenu/ConfigurationItem.qml | 1 - .../PrintCoreConfiguration.qml | 73 ++++++++----------- resources/themes/cura-light/theme.json | 2 +- 3 files changed, 33 insertions(+), 43 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 69c0b11882..ffe3b32926 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -48,7 +48,6 @@ Button { width: Math.round(parent.width / 2) printCoreConfiguration: modelData - mainColor: UM.Theme.getColor("text") } } } diff --git a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml index ae21a19f5e..dc908fdb0b 100644 --- a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml @@ -7,61 +7,52 @@ import QtQuick.Controls 2.0 import UM 1.2 as UM import Cura 1.0 as Cura -Column +Row { id: extruderInfo property var printCoreConfiguration - property var mainColor: "black" - spacing: Math.round(UM.Theme.getSize("default_margin").height / 2) height: childrenRect.height + spacing: UM.Theme.getSize("default_margin").width + //Extruder icon. Item { - id: extruder - width: parent.width - height: childrenRect.height - - Label - { - id: extruderLabel - text: catalog.i18nc("@label:extruder label", "Extruder") - renderType: Text.NativeRendering - elide: Text.ElideRight - anchors.left: parent.left - font: UM.Theme.getFont("default") - color: mainColor - } - + width: childrenRect.width + height: information.height Cura.ExtruderIcon { - width: UM.Theme.getSize("section_icon").width - height: UM.Theme.getSize("section_icon").height materialColor: printCoreConfiguration.material.color - anchors.left: extruderLabel.right - anchors.leftMargin: UM.Theme.getSize("narrow_margin").width + anchors.verticalCenter: parent.verticalCenter } } - Label + Column { - id: materialLabel - text: printCoreConfiguration.material.name - renderType: Text.NativeRendering - elide: Text.ElideRight - width: parent.width - font: UM.Theme.getFont("default_bold") - color: mainColor - } - - Label - { - id: printCoreTypeLabel - text: printCoreConfiguration.hotendID - renderType: Text.NativeRendering - elide: Text.ElideRight - width: parent.width - font: UM.Theme.getFont("default") - color: mainColor + id: information + Label + { + text: printCoreConfiguration.material.brand + renderType: Text.NativeRendering + elide: Text.ElideRight + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + } + Label + { + text: printCoreConfiguration.material.name + renderType: Text.NativeRendering + elide: Text.ElideRight + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + } + Label + { + text: printCoreConfiguration.hotendID + renderType: Text.NativeRendering + elide: Text.ElideRight + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + } } } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index de9b48c7f0..824d4cf3ee 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -397,7 +397,7 @@ "thin_margin": [0.71, 0.71], "narrow_margin": [0.5, 0.5], - "extruder_icon": [1.8, 1.8], + "extruder_icon": [2.5, 2.5], "simple_mode_infill_caption": [0.0, 5.0], "simple_mode_infill_height": [0.0, 8.0], From 1555a1ab1787def2427449b1fbcde94d41893bbe Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 27 Nov 2018 14:03:05 +0100 Subject: [PATCH 0463/1240] Make manufacturer and extruder labels lighter The proper colour would've been 'detail' but that seems to not be used much at all, and it's too light. In other places, 'inactive' is used in this place more often. Contributes to issue CURA-5876. --- resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml | 2 +- .../qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index 4aa8d66caa..202bc22ce0 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -64,7 +64,7 @@ Cura.ExpandableComponent text: model.material_brand elide: Text.ElideRight font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") + color: UM.Theme.getColor("text_inactive") anchors { diff --git a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml index dc908fdb0b..5709cc948c 100644 --- a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml @@ -36,7 +36,7 @@ Row renderType: Text.NativeRendering elide: Text.ElideRight font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") + color: UM.Theme.getColor("text_inactive") } Label { @@ -52,7 +52,7 @@ Row renderType: Text.NativeRendering elide: Text.ElideRight font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") + color: UM.Theme.getColor("text_inactive") } } } From b0e9f23eee3c053c6179beeabbd1687a4dba0543 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 27 Nov 2018 14:06:07 +0100 Subject: [PATCH 0464/1240] Correct line colour when hovered A detail that I almost missed in the design examples. Contributes to issue CURA-5876. --- resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index ffe3b32926..2fbbef7130 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -19,7 +19,7 @@ Button { height: childrenRect.height color: parent.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button") - border.color: parent.checked ? UM.Theme.getColor("primary") : UM.Theme.getColor("lining") + border.color: (parent.checked || parent.hovered) ? UM.Theme.getColor("primary") : UM.Theme.getColor("lining") border.width: parent.checked ? UM.Theme.getSize("thick_lining").width : UM.Theme.getSize("default_lining").width radius: UM.Theme.getSize("default_radius").width From 8784ecd49acad58d0cbbebb8b06bebfe520145e1 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Tue, 27 Nov 2018 14:09:47 +0100 Subject: [PATCH 0465/1240] Change native rendering CURA-5941 --- resources/qml/PrintSetupSelector.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/qml/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector.qml index 279254ec64..371232372d 100644 --- a/resources/qml/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector.qml @@ -148,6 +148,7 @@ Cura.ExpandableComponent id: popupItemHeaderText text: catalog.i18nc("@label", "Print settings"); font: UM.Theme.getFont("default") + renderType: Text.NativeRendering verticalAlignment: Text.AlignVCenter color: UM.Theme.getColor("text") height: parent.height From 609f07399e7735801f728826990e53a390a5f206 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 27 Nov 2018 14:20:47 +0100 Subject: [PATCH 0466/1240] Show extruder as disabled if either material or core is missing It basically just means you can't print with it. Contributes to issue CURA-5876. --- resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml index 5709cc948c..cc6461ebc4 100644 --- a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml @@ -24,6 +24,7 @@ Row { materialColor: printCoreConfiguration.material.color anchors.verticalCenter: parent.verticalCenter + extruderEnabled: printCoreConfiguration.material.name !== "" && printCoreConfiguration.hotendID !== "" } } From 3ad1802ab68021d4672c874dfaa7481f79212bb3 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 27 Nov 2018 14:34:29 +0100 Subject: [PATCH 0467/1240] Prevent a KeyError from messing CURA-5978 --- cura/Settings/MachineManager.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index f7ad108ad4..03afc7edd0 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -915,9 +915,12 @@ class MachineManager(QObject): if settable_per_extruder: limit_to_extruder = int(self._global_container_stack.getProperty(setting_key, "limit_to_extruder")) - extruder_position = str(max(0, limit_to_extruder)) - extruder_stack = self._global_container_stack.extruders[extruder_position] - extruder_stack.userChanges.setProperty(setting_key, "value", global_user_container.getProperty(setting_key, "value")) + extruder_position = max(0, limit_to_extruder) + extruder_stack = self.getExtruder(extruder_position) + if extruder_stack: + extruder_stack.userChanges.setProperty(setting_key, "value", global_user_container.getProperty(setting_key, "value")) + else: + Logger.log("e", "Unable to find extruder on position %s", extruder_position) global_user_container.removeInstance(setting_key) # Signal that the global stack has changed @@ -926,10 +929,9 @@ class MachineManager(QObject): @pyqtSlot(int, result = QObject) def getExtruder(self, position: int) -> Optional[ExtruderStack]: - extruder = None if self._global_container_stack: - extruder = self._global_container_stack.extruders.get(str(position)) - return extruder + return self._global_container_stack.extruders.get(str(position)) + return None def updateDefaultExtruder(self) -> None: if self._global_container_stack is None: From 854755277c705d3eb7e01e21525b2fe5be570426 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 27 Nov 2018 14:37:01 +0100 Subject: [PATCH 0468/1240] Fix styling of comments Because sentences should start with capitals --- cura/Settings/MachineManager.py | 43 ++++++++++++++++----------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 03afc7edd0..f22029e083 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -64,7 +64,7 @@ class MachineManager(QObject): self.machine_extruder_material_update_dict = collections.defaultdict(list) #type: Dict[str, List[Callable[[], None]]] - self._instance_container_timer = QTimer() #type: QTimer + self._instance_container_timer = QTimer() # type: QTimer self._instance_container_timer.setInterval(250) self._instance_container_timer.setSingleShot(True) self._instance_container_timer.timeout.connect(self.__emitChangedSignals) @@ -74,7 +74,7 @@ class MachineManager(QObject): self._application.globalContainerStackChanged.connect(self._onGlobalContainerChanged) self._container_registry.containerLoadComplete.connect(self._onContainersChanged) - ## When the global container is changed, active material probably needs to be updated. + # When the global container is changed, active material probably needs to be updated. self.globalContainerChanged.connect(self.activeMaterialChanged) self.globalContainerChanged.connect(self.activeVariantChanged) self.globalContainerChanged.connect(self.activeQualityChanged) @@ -115,15 +115,15 @@ class MachineManager(QObject): self._material_incompatible_message = Message(catalog.i18nc("@info:status", "The selected material is incompatible with the selected machine or configuration."), - title = catalog.i18nc("@info:title", "Incompatible Material")) #type: Message + title = catalog.i18nc("@info:title", "Incompatible Material")) # type: Message - containers = CuraContainerRegistry.getInstance().findInstanceContainers(id = self.activeMaterialId) #type: List[InstanceContainer] + containers = CuraContainerRegistry.getInstance().findInstanceContainers(id = self.activeMaterialId) # type: List[InstanceContainer] if containers: containers[0].nameChanged.connect(self._onMaterialNameChanged) - self._material_manager = self._application.getMaterialManager() #type: MaterialManager - self._variant_manager = self._application.getVariantManager() #type: VariantManager - self._quality_manager = self._application.getQualityManager() #type: QualityManager + self._material_manager = self._application.getMaterialManager() # type: MaterialManager + self._variant_manager = self._application.getVariantManager() # type: VariantManager + self._quality_manager = self._application.getQualityManager() # type: QualityManager # When the materials lookup table gets updated, it can mean that a material has its name changed, which should # be reflected on the GUI. This signal emission makes sure that it happens. @@ -156,7 +156,7 @@ class MachineManager(QObject): blurSettings = pyqtSignal() # Emitted to force fields in the advanced sidebar to un-focus, so they update properly outputDevicesChanged = pyqtSignal() - currentConfigurationChanged = pyqtSignal() # Emitted every time the current configurations of the machine changes + currentConfigurationChanged = pyqtSignal() # Emitted every time the current configurations of the machine changes printerConnectedStatusChanged = pyqtSignal() # Emitted every time the active machine change or the outputdevices change rootMaterialChanged = pyqtSignal() @@ -201,7 +201,7 @@ class MachineManager(QObject): extruder_configuration.hotendID = extruder.variant.getName() if extruder.variant != empty_variant_container else None self._current_printer_configuration.extruderConfigurations.append(extruder_configuration) - # an empty build plate configuration from the network printer is presented as an empty string, so use "" for an + # An empty build plate configuration from the network printer is presented as an empty string, so use "" for an # empty build plate. self._current_printer_configuration.buildplateConfiguration = self._global_container_stack.getProperty("machine_buildplate_type", "value") if self._global_container_stack.variant != empty_variant_container else "" self.currentConfigurationChanged.emit() @@ -247,7 +247,7 @@ class MachineManager(QObject): self.updateNumberExtrudersEnabled() self.globalContainerChanged.emit() - # after switching the global stack we reconnect all the signals and set the variant and material references + # After switching the global stack we reconnect all the signals and set the variant and material references if self._global_container_stack: self._application.getPreferences().setValue("cura/active_machine", self._global_container_stack.getId()) @@ -261,7 +261,7 @@ class MachineManager(QObject): if global_variant.getMetaDataEntry("hardware_type") != "buildplate": self._global_container_stack.setVariant(empty_variant_container) - # set the global material to empty as we now use the extruder stack at all times - CURA-4482 + # Set the global material to empty as we now use the extruder stack at all times - CURA-4482 global_material = self._global_container_stack.material if global_material != empty_material_container: self._global_container_stack.setMaterial(empty_material_container) @@ -419,7 +419,7 @@ class MachineManager(QObject): # Not a very pretty solution, but the extruder manager doesn't really know how many extruders there are machine_extruder_count = self._global_container_stack.getProperty("machine_extruder_count", "value") extruder_stacks = ExtruderManager.getInstance().getActiveExtruderStacks() - count = 1 # we start with the global stack + count = 1 # We start with the global stack for stack in extruder_stacks: md = stack.getMetaData() if "position" in md and int(md["position"]) >= machine_extruder_count: @@ -646,7 +646,7 @@ class MachineManager(QObject): new_value = self._active_container_stack.getProperty(key, "value") extruder_stacks = [stack for stack in ExtruderManager.getInstance().getActiveExtruderStacks()] - # check in which stack the value has to be replaced + # Check in which stack the value has to be replaced for extruder_stack in extruder_stacks: if extruder_stack != self._active_container_stack and extruder_stack.getProperty(key, "value") != new_value: extruder_stack.userChanges.setProperty(key, "value", new_value) # TODO: nested property access, should be improved @@ -662,7 +662,7 @@ class MachineManager(QObject): for key in self._active_container_stack.userChanges.getAllKeys(): new_value = self._active_container_stack.getProperty(key, "value") - # check if the value has to be replaced + # Check if the value has to be replaced extruder_stack.userChanges.setProperty(key, "value", new_value) @pyqtProperty(str, notify = activeVariantChanged) @@ -731,7 +731,7 @@ class MachineManager(QObject): # If the machine that is being removed is the currently active machine, set another machine as the active machine. activate_new_machine = (self._global_container_stack and self._global_container_stack.getId() == machine_id) - # activate a new machine before removing a machine because this is safer + # Activate a new machine before removing a machine because this is safer if activate_new_machine: machine_stacks = CuraContainerRegistry.getInstance().findContainerStacksMetadata(type = "machine") other_machine_stacks = [s for s in machine_stacks if s["id"] != machine_id] @@ -997,12 +997,12 @@ class MachineManager(QObject): if not enabled and position == ExtruderManager.getInstance().activeExtruderIndex: ExtruderManager.getInstance().setActiveExtruderIndex(int(self._default_extruder_position)) - # ensure that the quality profile is compatible with current combination, or choose a compatible one if available + # Ensure that the quality profile is compatible with current combination, or choose a compatible one if available self._updateQualityWithMaterial() self.extruderChanged.emit() - # update material compatibility color + # Update material compatibility color self.activeQualityGroupChanged.emit() - # update items in SettingExtruder + # Update items in SettingExtruder ExtruderManager.getInstance().extrudersChanged.emit(self._global_container_stack.getId()) # Make sure the front end reflects changes self.forceUpdateAllSettings() @@ -1076,7 +1076,6 @@ class MachineManager(QObject): return result - # # Sets all quality and quality_changes containers to empty_quality and empty_quality_changes containers # for all stacks in the currently active machine. # @@ -1135,7 +1134,7 @@ class MachineManager(QObject): def _setQualityChangesGroup(self, quality_changes_group: "QualityChangesGroup") -> None: if self._global_container_stack is None: - return #Can't change that. + return # Can't change that. quality_type = quality_changes_group.quality_type # A custom quality can be created based on "not supported". # In that case, do not set quality containers to empty. @@ -1205,7 +1204,7 @@ class MachineManager(QObject): self.rootMaterialChanged.emit() def activeMaterialsCompatible(self) -> bool: - # check material - variant compatibility + # Check material - variant compatibility if self._global_container_stack is not None: if Util.parseBool(self._global_container_stack.getMetaDataEntry("has_materials", False)): for position, extruder in self._global_container_stack.extruders.items(): @@ -1415,7 +1414,7 @@ class MachineManager(QObject): material_diameter, root_material_id) self.setMaterial(position, material_node) - ## global_stack: if you want to provide your own global_stack instead of the current active one + ## Global_stack: if you want to provide your own global_stack instead of the current active one # if you update an active machine, special measures have to be taken. @pyqtSlot(str, "QVariant") def setMaterial(self, position: str, container_node, global_stack: Optional["GlobalStack"] = None) -> None: From 956741922d561e2e7405554cf68b6c634c00f616 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 27 Nov 2018 14:41:48 +0100 Subject: [PATCH 0469/1240] Make elements retain their position if hotend or material is missing Just a space makes it keep the correct height. Contributes to issue CURA-5876. --- .../qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml index cc6461ebc4..8d2f0c0f7c 100644 --- a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml @@ -33,7 +33,7 @@ Row id: information Label { - text: printCoreConfiguration.material.brand + text: printCoreConfiguration.material.brand ? printCoreConfiguration.material.brand : " " //Use space so that the height is still correct. renderType: Text.NativeRendering elide: Text.ElideRight font: UM.Theme.getFont("default") @@ -41,7 +41,7 @@ Row } Label { - text: printCoreConfiguration.material.name + text: printCoreConfiguration.material.name ? printCoreConfiguration.material.name : " " //Use space so that the height is still correct. renderType: Text.NativeRendering elide: Text.ElideRight font: UM.Theme.getFont("default") @@ -49,7 +49,7 @@ Row } Label { - text: printCoreConfiguration.hotendID + text: printCoreConfiguration.hotendID ? printCoreConfiguration.hotendID : " " //Use space so that the height is still correct. renderType: Text.NativeRendering elide: Text.ElideRight font: UM.Theme.getFont("default") From 0a71a9dd3ecd5c16fa82a498698f3e937619b842 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 27 Nov 2018 14:47:18 +0100 Subject: [PATCH 0470/1240] Increase margins around configuration item As per the design. Contributes to issue CURA-5876. --- resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 2fbbef7130..3ea220c91e 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -27,7 +27,7 @@ Button { id: contentColumn width: parent.width - padding: UM.Theme.getSize("default_margin").width + padding: UM.Theme.getSize("wide_margin").width spacing: Math.round(UM.Theme.getSize("default_margin").height / 2) Row From fb019ba987fc3542a178c6ff926d4fc11a115043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Tue, 27 Nov 2018 14:48:47 +0100 Subject: [PATCH 0471/1240] In CloudOutputDeviceManager start a thread to periodically check for changes in the connected clusters --- .../src/Cloud/CloudOutputDevice.py | 1 + .../src/Cloud/CloudOutputDeviceManager.py | 51 ++++++++++++++----- 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index accc8429b1..79a3d46949 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -30,6 +30,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): I18N_CATALOG = i18nCatalog("cura") # The cloud URL to use for this remote cluster. + # TODO: Make sure that this url goes to the live api before release API_ROOT_PATH_FORMAT = "https://api-staging.ultimaker.com/connect/v1/clusters/{cluster_id}" # Signal triggered when the printers in the remote cluster were changed. diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index a252f9e4d3..546b9b270f 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -1,6 +1,8 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import json +from time import sleep +from threading import Thread from typing import Dict, Optional, List from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply @@ -24,6 +26,9 @@ class CloudOutputDeviceManager(NetworkClient): # The cloud URL to use for remote clusters. API_ROOT_PATH = "https://api.ultimaker.com/connect/v1" + + # The interval with wich the remote clusters are checked + CHECK_CLUSTER_INTERVAL = 5 # seconds def __init__(self): super().__init__() @@ -42,6 +47,11 @@ class CloudOutputDeviceManager(NetworkClient): # TODO: update remote clusters periodically self._account.loginStateChanged.connect(self._getRemoteClusters) + # Periodically check the cloud for an update on the clusters connected to the user's account + self._update_clusters_thread = Thread(target=self._updateClusters, daemon=True) + self._update_clusters_thread.start() + + ## Override _createEmptyRequest to add the needed authentication header for talking to the Ultimaker Cloud API. def _createEmptyRequest(self, path: str, content_type: Optional[str] = "application/json") -> QNetworkRequest: request = super()._createEmptyRequest(self.API_ROOT_PATH + path, content_type = content_type) @@ -50,6 +60,12 @@ class CloudOutputDeviceManager(NetworkClient): # TODO: don't create the client when not signed in? request.setRawHeader(b"Authorization", "Bearer {}".format(self._account.accessToken).encode()) return request + + ## Update the clusters + def _updateClusters(self) -> None: + while True: + self._getRemoteClusters() + sleep(self.CHECK_CLUSTER_INTERVAL) ## Gets all remote clusters from the API. def _getRemoteClusters(self) -> None: @@ -64,36 +80,47 @@ class CloudOutputDeviceManager(NetworkClient): return # Parse the response (returns the "data" field from the body). - clusters = self._parseStatusResponse(reply) - if not clusters: + found_clusters = self._parseStatusResponse(reply) + if not found_clusters: return - - # Add an output device for each remote cluster. - # The clusters are an array of objects in a field called "data". - for cluster in clusters: - self._addCloudOutputDevice(cluster) - + + known_cluster_ids = set(self._remote_clusters.keys()) + found_clusters_ids = set(found_clusters.keys()) + + # Add an output device for each new remote cluster. + for cluster_id in found_clusters_ids.difference(known_cluster_ids): + self._addCloudOutputDevice(found_clusters[cluster_id]) + + # Remove output devices that are gone + for cluster_id in known_cluster_ids.difference(found_clusters_ids): + self._removeCloudOutputDevice(found_clusters[cluster_id]) + # For testing we add a dummy device: # self._addCloudOutputDevice(CloudCluster(cluster_id = "LJ0tciiuZZjarrXAvFLEZ6ox4Cvx8FvtXUlQv4vIhV6w")) @staticmethod - def _parseStatusResponse(reply: QNetworkReply) -> List[CloudCluster]: + def _parseStatusResponse(reply: QNetworkReply) -> Dict[str, CloudCluster]: try: - return [CloudCluster(**c) for c in json.loads(reply.readAll().data().decode("utf-8"))] + return {c["guid"]: CloudCluster(**c) for c in json.loads(reply.readAll().data().decode("utf-8"))} except UnicodeDecodeError: Logger.log("w", "Unable to read server response") except json.decoder.JSONDecodeError: Logger.logException("w", "Unable to decode JSON from reply.") except ValueError: Logger.logException("w", "Response was missing values.") - return [] + return {} ## Adds a CloudOutputDevice for each entry in the remote cluster list from the API. def _addCloudOutputDevice(self, cluster: CloudCluster): device = CloudOutputDevice(cluster.cluster_id) self._output_device_manager.addOutputDevice(device) self._remote_clusters[cluster.cluster_id] = device - + + ## Remove a CloudOutputDevice + def _removeCloudOutputDevice(self, cluster: CloudCluster): + self._output_device_manager.removeOutputDevice(cluster.cluster_id) + del self._remote_clusters[cluster.cluster_id] + ## Callback for when the active machine was changed by the user. def _activeMachineChanged(self): active_machine = CuraApplication.getInstance().getGlobalContainerStack() From 077b34b4336fee026ee37f43a27e7bb13c9b9b4a Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 27 Nov 2018 14:49:01 +0100 Subject: [PATCH 0472/1240] Fix size of the extruder icon Now it should look the same in all places. --- resources/qml/ExtruderButton.qml | 14 +++++++++----- resources/qml/ExtruderIcon.qml | 2 -- resources/themes/cura-light/theme.json | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/resources/qml/ExtruderButton.qml b/resources/qml/ExtruderButton.qml index 8a1f0af44a..d7cbdc52bc 100644 --- a/resources/qml/ExtruderButton.qml +++ b/resources/qml/ExtruderButton.qml @@ -19,12 +19,16 @@ Button enabled: UM.Selection.hasSelection && extruder.stack.isEnabled background: Item {} - contentItem: ExtruderIcon + contentItem: Item { - width: UM.Theme.getSize("button_icon").width - materialColor: model.color - extruderEnabled: extruder.stack.isEnabled - property int index: extruder.index + // For some reason if the extruder icon is not enclosed to the item, the size changes to fill the size of the button + ExtruderIcon + { + anchors.centerIn: parent + materialColor: model.color + extruderEnabled: extruder.stack.isEnabled + property int index: extruder.index + } } onClicked: diff --git a/resources/qml/ExtruderIcon.qml b/resources/qml/ExtruderIcon.qml index 8f312adb85..c1a202050b 100644 --- a/resources/qml/ExtruderIcon.qml +++ b/resources/qml/ExtruderIcon.qml @@ -51,8 +51,6 @@ Item anchors.centerIn: parent text: index + 1 font: UM.Theme.getFont("extruder_icon") - width: contentWidth - height: contentHeight visible: extruderEnabled renderType: Text.NativeRendering horizontalAlignment: Text.AlignHCenter diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 157adcf6a5..d2358e36ff 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -402,7 +402,7 @@ "thin_margin": [0.71, 0.71], "narrow_margin": [0.5, 0.5], - "extruder_icon": [1.8, 1.8], + "extruder_icon": [2.33, 2.33], "section": [0.0, 2.2], "section_icon": [1.6, 1.6], From 172e003e1b65650d7f8150f5c6077f97f0c46685 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Tue, 27 Nov 2018 14:53:23 +0100 Subject: [PATCH 0473/1240] Removed the inital call to clusters --- .../src/Cloud/CloudOutputDeviceManager.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 546b9b270f..3ebaeea9a4 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -43,11 +43,7 @@ class CloudOutputDeviceManager(NetworkClient): # When switching machines we check if we have to activate a remote cluster. application.globalContainerStackChanged.connect(self._activeMachineChanged) - # Fetch all remote clusters for the authenticated user. - # TODO: update remote clusters periodically - self._account.loginStateChanged.connect(self._getRemoteClusters) - - # Periodically check the cloud for an update on the clusters connected to the user's account + # Periodically check all remote clusters for the authenticated user. self._update_clusters_thread = Thread(target=self._updateClusters, daemon=True) self._update_clusters_thread.start() From 82ea562dc389b6e3f8872b24c84e4a70f382ffe3 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 27 Nov 2018 14:54:49 +0100 Subject: [PATCH 0474/1240] Add missing hoverEnabled property --- resources/qml/Settings/SettingCategory.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/qml/Settings/SettingCategory.qml b/resources/qml/Settings/SettingCategory.qml index 7995619af0..00d23580b2 100644 --- a/resources/qml/Settings/SettingCategory.qml +++ b/resources/qml/Settings/SettingCategory.qml @@ -14,6 +14,7 @@ Button anchors.right: parent.right anchors.leftMargin: UM.Theme.getSize("thick_margin").width anchors.rightMargin: UM.Theme.getSize("thick_margin").width + hoverEnabled: true background: Rectangle { id: backgroundRectangle From c7fd147f59e1d6bf3fb71930ef6c3346c3373b4f Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 27 Nov 2018 14:55:06 +0100 Subject: [PATCH 0475/1240] Show website for support --- plugins/Toolbox/resources/qml/ToolboxDetailPage.qml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml index 437a2ef351..7b48aaebef 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml @@ -144,10 +144,6 @@ Item { return "" } - if (details.author_email) - { - return "" + details.author_name + "" - } else { return "" + details.author_name + "" From 92d07170964a8a4906ca2742eeb3675e4619487b Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 27 Nov 2018 15:00:50 +0100 Subject: [PATCH 0476/1240] Hide the toolbar and don't show time and material information when loading a gcode Contributes to CURA-5979. --- resources/qml/ActionPanel/OutputProcessWidget.qml | 8 +++++++- resources/qml/Cura.qml | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml index ddbe709a84..45cb1fdb41 100644 --- a/resources/qml/ActionPanel/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -18,6 +18,7 @@ Column id: widget spacing: UM.Theme.getSize("thin_margin").height + property bool preSlicedData: PrintInformation.preSliced UM.I18nCatalog { @@ -48,7 +49,7 @@ Column id: estimatedTime width: parent.width - text: PrintInformation.currentPrintTime.getDisplayString(UM.DurationFormat.Long) + text: preSlicedData ? catalog.i18nc("@label", "No time estimation available") : PrintInformation.currentPrintTime.getDisplayString(UM.DurationFormat.Long) source: UM.Theme.getIcon("clock") font: UM.Theme.getFont("small") } @@ -63,6 +64,10 @@ Column text: { + if (preSlicedData) + { + return catalog.i18nc("@label", "No cost estimation available") + } var totalLengths = 0 var totalWeights = 0 if (printMaterialLengths) @@ -86,6 +91,7 @@ Column PrintInformationWidget { id: printInformationPanel + visible: !preSlicedData anchors { diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 63888a4ee4..36f5758fa3 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -212,7 +212,7 @@ UM.MainWindow verticalCenter: parent.verticalCenter left: parent.left } - visible: CuraApplication.platformActivity + visible: CuraApplication.platformActivity && !PrintInformation.preSliced } ObjectsList From 4fab546425450142f17a3aaf05511fe1de98b019 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 27 Nov 2018 15:00:57 +0100 Subject: [PATCH 0477/1240] Fix codestyle issues Boyscouting --- resources/qml/Settings/SettingCategory.qml | 199 +++++++++++---------- 1 file changed, 109 insertions(+), 90 deletions(-) diff --git a/resources/qml/Settings/SettingCategory.qml b/resources/qml/Settings/SettingCategory.qml index 00d23580b2..aafe36c546 100644 --- a/resources/qml/Settings/SettingCategory.qml +++ b/resources/qml/Settings/SettingCategory.qml @@ -15,23 +15,31 @@ Button anchors.leftMargin: UM.Theme.getSize("thick_margin").width anchors.rightMargin: UM.Theme.getSize("thick_margin").width hoverEnabled: true + background: Rectangle { id: backgroundRectangle implicitHeight: UM.Theme.getSize("section").height - color: { - if (base.color) { - return base.color; - } else if (!base.enabled) { - return UM.Theme.getColor("setting_category_disabled"); - } else if (base.hovered && base.checkable && base.checked) { - return UM.Theme.getColor("setting_category_active_hover"); - } else if (base.pressed || (base.checkable && base.checked)) { - return UM.Theme.getColor("setting_category_active"); - } else if (base.hovered) { - return UM.Theme.getColor("setting_category_hover"); - } else { - return UM.Theme.getColor("setting_category"); + color: + { + if (base.color) + { + return base.color + } else if (!base.enabled) + { + return UM.Theme.getColor("setting_category_disabled") + } else if (base.hovered && base.checkable && base.checked) + { + return UM.Theme.getColor("setting_category_active_hover") + } else if (base.pressed || (base.checkable && base.checked)) + { + return UM.Theme.getColor("setting_category_active") + } else if (base.hovered) + { + return UM.Theme.getColor("setting_category_hover") + } else + { + return UM.Theme.getColor("setting_category") } } Behavior on color { ColorAnimation { duration: 50; } } @@ -41,17 +49,23 @@ Button height: UM.Theme.getSize("default_lining").height width: parent.width anchors.bottom: parent.bottom - color: { - if (!base.enabled) { - return UM.Theme.getColor("setting_category_disabled_border"); - } else if ((base.hovered || base.activeFocus) && base.checkable && base.checked) { - return UM.Theme.getColor("setting_category_active_hover_border"); - } else if (base.pressed || (base.checkable && base.checked)) { - return UM.Theme.getColor("setting_category_active_border"); - } else if (base.hovered || base.activeFocus) { - return UM.Theme.getColor("setting_category_hover_border"); - } else { - return UM.Theme.getColor("setting_category_border"); + color: + { + if (!base.enabled) + { + return UM.Theme.getColor("setting_category_disabled_border") + } else if ((base.hovered || base.activeFocus) && base.checkable && base.checked) + { + return UM.Theme.getColor("setting_category_active_hover_border") + } else if (base.pressed || (base.checkable && base.checked)) + { + return UM.Theme.getColor("setting_category_active_border") + } else if (base.hovered || base.activeFocus) + { + return UM.Theme.getColor("setting_category_hover_border") + } else + { + return UM.Theme.getColor("setting_category_border") } } } @@ -66,18 +80,19 @@ Button property var focusItem: base - contentItem: Item { + contentItem: Item + { anchors.fill: parent - anchors.left: parent.left - Label { + Label + { id: settingNameLabel anchors { left: parent.left leftMargin: 2 * UM.Theme.getSize("default_margin").width + UM.Theme.getSize("section_icon").width - right: parent.right; - verticalCenter: parent.verticalCenter; + right: parent.right + verticalCenter: parent.verticalCenter } text: definition.label textFormat: Text.PlainText @@ -85,21 +100,27 @@ Button font: UM.Theme.getFont("setting_category") color: { - if (!base.enabled) { - return UM.Theme.getColor("setting_category_disabled_text"); - } else if ((base.hovered || base.activeFocus) && base.checkable && base.checked) { - return UM.Theme.getColor("setting_category_active_hover_text"); - } else if (base.pressed || (base.checkable && base.checked)) { - return UM.Theme.getColor("setting_category_active_text"); - } else if (base.hovered || base.activeFocus) { - return UM.Theme.getColor("setting_category_hover_text"); - } else { - return UM.Theme.getColor("setting_category_text"); + if (!base.enabled) + { + return UM.Theme.getColor("setting_category_disabled_text") + } else if ((base.hovered || base.activeFocus) && base.checkable && base.checked) + { + return UM.Theme.getColor("setting_category_active_hover_text") + } else if (base.pressed || (base.checkable && base.checked)) + { + return UM.Theme.getColor("setting_category_active_text") + } else if (base.hovered || base.activeFocus) + { + return UM.Theme.getColor("setting_category_hover_text") + } else + { + return UM.Theme.getColor("setting_category_text") } } fontSizeMode: Text.HorizontalFit minimumPointSize: 8 } + UM.RecolorImage { id: category_arrow @@ -112,16 +133,21 @@ Button sourceSize.height: width color: { - if (!base.enabled) { - return UM.Theme.getColor("setting_category_disabled_text"); - } else if ((base.hovered || base.activeFocus) && base.checkable && base.checked) { - return UM.Theme.getColor("setting_category_active_hover_text"); - } else if (base.pressed || (base.checkable && base.checked)) { - return UM.Theme.getColor("setting_category_active_text"); - } else if (base.hovered || base.activeFocus) { - return UM.Theme.getColor("setting_category_hover_text"); - } else { - return UM.Theme.getColor("setting_category_text"); + if (!base.enabled) + { + return UM.Theme.getColor("setting_category_disabled_text") + } else if ((base.hovered || base.activeFocus) && base.checkable && base.checked) + { + return UM.Theme.getColor("setting_category_active_hover_text") + } else if (base.pressed || (base.checkable && base.checked)) + { + return UM.Theme.getColor("setting_category_active_text") + } else if (base.hovered || base.activeFocus) + { + return UM.Theme.getColor("setting_category_hover_text") + } else + { + return UM.Theme.getColor("setting_category_text") } } source: base.checked ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") @@ -136,21 +162,26 @@ Button anchors.leftMargin: UM.Theme.getSize("default_margin").width color: { - if (!base.enabled) { - return UM.Theme.getColor("setting_category_disabled_text"); - } else if((base.hovered || base.activeFocus) && base.checkable && base.checked) { - return UM.Theme.getColor("setting_category_active_hover_text"); - } else if(base.pressed || (base.checkable && base.checked)) { - return UM.Theme.getColor("setting_category_active_text"); - } else if(base.hovered || base.activeFocus) { - return UM.Theme.getColor("setting_category_hover_text"); - } else { - return UM.Theme.getColor("setting_category_text"); + if (!base.enabled) + { + return UM.Theme.getColor("setting_category_disabled_text") + } else if((base.hovered || base.activeFocus) && base.checkable && base.checked) + { + return UM.Theme.getColor("setting_category_active_hover_text") + } else if(base.pressed || (base.checkable && base.checked)) + { + return UM.Theme.getColor("setting_category_active_text") + } else if(base.hovered || base.activeFocus) + { + return UM.Theme.getColor("setting_category_hover_text") + } else + { + return UM.Theme.getColor("setting_category_text") } } source: UM.Theme.getIcon(definition.icon) - width: UM.Theme.getSize("section_icon").width; - height: UM.Theme.getSize("section_icon").height; + width: UM.Theme.getSize("section_icon").width + height: UM.Theme.getSize("section_icon").height sourceSize.width: width + 15 * screenScaleFactor sourceSize.height: width + 15 * screenScaleFactor } @@ -160,31 +191,26 @@ Button onClicked: { - if (definition.expanded) { - settingDefinitionsModel.collapse(definition.key); + if (definition.expanded) + { + settingDefinitionsModel.collapse(definition.key) } else { - settingDefinitionsModel.expandRecursive(definition.key); + settingDefinitionsModel.expandRecursive(definition.key) } //Set focus so that tab navigation continues from this point on. //NB: This must be set AFTER collapsing/expanding the category so that the scroll position is correct. - forceActiveFocus(); + forceActiveFocus() } onActiveFocusChanged: { if(activeFocus) { - base.focusReceived(); + base.focusReceived() } } - Keys.onTabPressed: - { - base.setActiveFocusToNextSetting(true) - } - Keys.onBacktabPressed: - { - base.setActiveFocusToNextSetting(false) - } + Keys.onTabPressed: base.setActiveFocusToNextSetting(true) + Keys.onBacktabPressed: base.setActiveFocusToNextSetting(false) UM.SimpleButton { @@ -194,9 +220,10 @@ Button height: Math.round(base.height * 0.6) width: Math.round(base.height * 0.6) - anchors { + anchors + { right: inheritButton.visible ? inheritButton.left : parent.right - // use 1.9 as the factor because there is a 0.1 difference between the settings and inheritance warning icons + // Use 1.9 as the factor because there is a 0.1 difference between the settings and inheritance warning icons rightMargin: inheritButton.visible ? Math.round(UM.Theme.getSize("default_margin").width / 2) : category_arrow.width + Math.round(UM.Theme.getSize("default_margin").width * 1.9) verticalCenter: parent.verticalCenter } @@ -205,9 +232,7 @@ Button hoverColor: UM.Theme.getColor("setting_control_button_hover") iconSource: UM.Theme.getIcon("settings") - onClicked: { - Cura.Actions.configureSettingVisibility.trigger(definition) - } + onClicked: Cura.Actions.configureSettingVisibility.trigger(definition) } UM.SimpleButton @@ -240,24 +265,18 @@ Button onClicked: { - settingDefinitionsModel.expandRecursive(definition.key); - base.checked = true; - base.showAllHiddenInheritedSettings(definition.key); + settingDefinitionsModel.expandRecursive(definition.key) + base.checked = true + base.showAllHiddenInheritedSettings(definition.key) } color: UM.Theme.getColor("setting_control_button") hoverColor: UM.Theme.getColor("setting_control_button_hover") iconSource: UM.Theme.getIcon("notice") - onEntered: - { - base.showTooltip(catalog.i18nc("@label","Some hidden settings use values different from their normal calculated value.\n\nClick to make these settings visible.")) - } + onEntered: base.showTooltip(catalog.i18nc("@label","Some hidden settings use values different from their normal calculated value.\n\nClick to make these settings visible.")) - onExited: - { - base.hideTooltip(); - } + onExited: base.hideTooltip() UM.I18nCatalog { id: catalog; name: "cura" } } From d47806de509d9aeeeebff5c5c410fb17ab35f26d Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Tue, 27 Nov 2018 15:05:35 +0100 Subject: [PATCH 0478/1240] Change style, added close popitem close behaviour CURA-5941 --- resources/qml/ExpandableComponent.qml | 3 +++ resources/qml/PrintSetupSelector.qml | 37 ++++++++++++-------------- resources/themes/cura-light/theme.json | 2 -- 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 3b1105ddd6..707b947c7f 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -49,6 +49,9 @@ Item // On what side should the header corners be shown? 1 is down, 2 is left, 3 is up and 4 is right. property alias headerCornerSide: background.cornerSide + // Change the popupItem close behaviour + property alias popupClosePolicy : popup.closePolicy + function togglePopup() { if(popup.visible) diff --git a/resources/qml/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector.qml index 371232372d..b20d8e116b 100644 --- a/resources/qml/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector.qml @@ -29,7 +29,9 @@ Cura.ExpandableComponent iconSource: UM.Theme.getIcon("pencil") popupPadding : 0 - popupSpacingY: 10 + popupSpacingY: UM.Theme.getSize("narrow_margin").width + + popupClosePolicy: Popup.CloseOnEscape onCurrentModeIndexChanged: UM.Preferences.setValue("cura/active_mode", currentModeIndex) @@ -124,7 +126,7 @@ Cura.ExpandableComponent popupItem: Rectangle { - property var total_height: popupItemHeader.height + popupItemContent.height + footerControll.height + UM.Theme.getSize("print_setup_selector_margin").height * 2 + property var total_height: popupItemHeader.height + popupItemContent.height + footerControll.height + UM.Theme.getSize("narrow_margin").height * 2 id: popupItemWrapper height: total_height @@ -157,7 +159,7 @@ Cura.ExpandableComponent { topMargin: UM.Theme.getSize("sidebar_margin").height left: parent.left - leftMargin: UM.Theme.getSize("print_setup_selector_margin").height + leftMargin: UM.Theme.getSize("narrow_margin").height } } @@ -194,7 +196,7 @@ Cura.ExpandableComponent source: UM.Theme.getIcon("cross1") } - onClicked: base.model.hideMessage(model.id) + onClicked: base.togglePopup() // Will hide the popup item background: Rectangle { @@ -211,17 +213,16 @@ Cura.ExpandableComponent anchors { top: popupItemHeader.bottom - topMargin: UM.Theme.getSize("print_setup_selector_margin").height + topMargin: UM.Theme.getSize("narrow_margin").height right: parent.right left: parent.left - leftMargin: 5 - rightMargin: 5 + leftMargin: UM.Theme.getSize("thick_margin").width / 1.5 + rightMargin: UM.Theme.getSize("thick_margin").width / 1.5 } UM.TabRow { id: tabBar - anchors.top: popupItemHeader.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height onCurrentIndexChanged: Cura.ExtruderManager.setActiveExtruderIndex(currentIndex) z: 1 @@ -309,7 +310,7 @@ Cura.ExpandableComponent { id: footerControll anchors.top: popupItemContent.bottom - anchors.topMargin: UM.Theme.getSize("print_setup_selector_margin").height * 2 + anchors.topMargin: UM.Theme.getSize("narrow_margin").height * 2 width: parent.width height: settingControlButton.height + UM.Theme.getSize("default_lining").height * 4 Rectangle @@ -337,10 +338,10 @@ Cura.ExpandableComponent anchors { top: parent.top - topMargin: UM.Theme.getSize("print_setup_selector_margin").height * 2 - bottomMargin: UM.Theme.getSize("print_setup_selector_margin").height * 2 + topMargin: UM.Theme.getSize("narrow_margin").height * 2 + bottomMargin: UM.Theme.getSize("narrow_margin").height * 2 right: parent.right - rightMargin: UM.Theme.getSize("print_setup_selector_margin").height + rightMargin: UM.Theme.getSize("narrow_margin").height } onClicked: currentModeIndex = 1 @@ -361,19 +362,15 @@ Cura.ExpandableComponent anchors { top: parent.top - topMargin: UM.Theme.getSize("print_setup_selector_margin").height * 2 - bottomMargin: UM.Theme.getSize("print_setup_selector_margin").height * 2 + topMargin: UM.Theme.getSize("narrow_margin").height * 2 + bottomMargin: UM.Theme.getSize("narrow_margin").height * 2 left: parent.left - leftMargin: UM.Theme.getSize("print_setup_selector_margin").height + leftMargin: UM.Theme.getSize("narrow_margin").height } -// onClicked: currentModeIndex = 0 MouseArea { anchors.fill: parent - onClicked: { - currentModeIndex = 0 - - } + onClicked: currentModeIndex = 0 } } } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index e6889b50c0..1228e52b9d 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -535,9 +535,7 @@ "monitor_shadow_radius": [0.4, 0.4], "monitor_shadow_offset": [0.15, 0.15], - "print_setup_selector_margin": [0.5, 0.5], "print_setup_action_button": [13, 5], - "print_setup_tap_bar": [4, 4], "print_setup_content_top_margin": [3, 3] } } From 55dd9be0629b5ac091628ea9184416da7edae972 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 27 Nov 2018 15:11:13 +0100 Subject: [PATCH 0479/1240] Move PrinterTypeLabel to a more central location So that we can re-use it in other places too. Contributes to issue CURA-5876. --- resources/qml/{PrinterSelector => }/PrinterTypeLabel.qml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename resources/qml/{PrinterSelector => }/PrinterTypeLabel.qml (100%) diff --git a/resources/qml/PrinterSelector/PrinterTypeLabel.qml b/resources/qml/PrinterTypeLabel.qml similarity index 100% rename from resources/qml/PrinterSelector/PrinterTypeLabel.qml rename to resources/qml/PrinterTypeLabel.qml From c1bb1a9b982d7e44b9dd0bc68b9b7e993dfc3712 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 27 Nov 2018 15:12:44 +0100 Subject: [PATCH 0480/1240] Use normal font size for shorthand label This element had been designed when very_small was in the theme equal to default. I changed the entire application to use default instead of very_small and made very_small a smaller font, but didn't change that on the branch that this element was designed on. So now also change it here. Contributes to issue CURA-5876. --- resources/qml/PrinterTypeLabel.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/PrinterTypeLabel.qml b/resources/qml/PrinterTypeLabel.qml index cd9f3b9743..7feae32e16 100644 --- a/resources/qml/PrinterTypeLabel.qml +++ b/resources/qml/PrinterTypeLabel.qml @@ -28,7 +28,7 @@ Item anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter renderType: Text.NativeRendering - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") } } \ No newline at end of file From 3ad2e4f62a5a7612ecf2372e0230a2362d7f3f66 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 27 Nov 2018 15:24:58 +0100 Subject: [PATCH 0481/1240] Re-use PrinterTypeLabel component and use abbreviated name Contributes to issue CURA-5876. --- .../ConfigurationListView.qml | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml index 393c2715b2..c40eb5b9e9 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml @@ -46,21 +46,11 @@ Column section.criteria: ViewSection.FullString section.delegate: Item { - height: printerTypeLabelBox.height + UM.Theme.getSize("default_margin").height - Rectangle + height: printerTypeLabel.height + UM.Theme.getSize("default_margin").height + Cura.PrinterTypeLabel { - id: printerTypeLabelBox - color: UM.Theme.getColor("text_detail") - width: childrenRect.width - height: childrenRect.height - - Label - { - text: section - font: UM.Theme.getFont("small") - color: UM.Theme.getColor("text") - padding: UM.Theme.getSize("narrow_margin").width - } + id: printerTypeLabel + text: Cura.MachineManager.getAbbreviatedMachineName(section) } } From fd4d1113b9c45d697d817c53d64aab9be6c19a3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Tue, 27 Nov 2018 15:42:26 +0100 Subject: [PATCH 0482/1240] use right plural in variable name --- .../src/Cloud/CloudOutputDeviceManager.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 3ebaeea9a4..97b0787c73 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -81,14 +81,14 @@ class CloudOutputDeviceManager(NetworkClient): return known_cluster_ids = set(self._remote_clusters.keys()) - found_clusters_ids = set(found_clusters.keys()) + found_cluster_ids = set(found_clusters.keys()) # Add an output device for each new remote cluster. - for cluster_id in found_clusters_ids.difference(known_cluster_ids): + for cluster_id in found_cluster_ids.difference(known_cluster_ids): self._addCloudOutputDevice(found_clusters[cluster_id]) # Remove output devices that are gone - for cluster_id in known_cluster_ids.difference(found_clusters_ids): + for cluster_id in known_cluster_ids.difference(found_cluster_ids): self._removeCloudOutputDevice(found_clusters[cluster_id]) # For testing we add a dummy device: @@ -122,6 +122,11 @@ class CloudOutputDeviceManager(NetworkClient): active_machine = CuraApplication.getInstance().getGlobalContainerStack() if not active_machine: return + + local_device_id = active_machine.getMetaDataEntry("um_network_key") + if local_device_id: + active_output_device = CuraApplication.getInstance().getOutputDeviceManager().getActiveDevice() + active_output_device.id stored_cluster_id = active_machine.getMetaDataEntry("um_cloud_cluster_id") if stored_cluster_id not in self._remote_clusters.keys(): From f8f498b90cd584a98943d1735dc519d94b228870 Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 27 Nov 2018 15:45:58 +0100 Subject: [PATCH 0483/1240] Increase the lines that are shown to 12 --- plugins/Toolbox/resources/qml/ToolboxDetailTile.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailTile.qml b/plugins/Toolbox/resources/qml/ToolboxDetailTile.qml index 9061a8e06b..7425ab2ba7 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailTile.qml @@ -37,7 +37,7 @@ Item anchors.top: packageName.bottom width: parent.width text: model.description - maximumLineCount: 6 + maximumLineCount: 12 elide: Text.ElideRight wrapMode: Text.WordWrap color: UM.Theme.getColor("text") From b98df0f22f04b04cc2c9f324d47611bb07d22a4e Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 27 Nov 2018 15:46:23 +0100 Subject: [PATCH 0484/1240] Show one website link where to get the material --- .../qml/ToolboxCompatibilityChart.qml | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml b/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml index a48cb2ee3f..9121b97eca 100644 --- a/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml +++ b/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml @@ -36,12 +36,19 @@ Item var pg_name = "printingGuidelines" return (pg_name in packageData.links) ? packageData.links[pg_name] : undefined } + + property var materialWebsiteUrl: + { + var pg_name = "website" + return (pg_name in packageData.links) ? packageData.links[pg_name] : undefined + } anchors.topMargin: UM.Theme.getSize("default_margin").height height: visible ? childrenRect.height : 0 visible: packageData.type == "material" && (packageData.has_configs || technicalDataSheetUrl !== undefined || - safetyDataSheetUrl !== undefined || printingGuidelinesUrl !== undefined) + safetyDataSheetUrl !== undefined || printingGuidelinesUrl !== undefined || + materialWebsiteUrl !== undefined) Item { @@ -180,7 +187,8 @@ Item anchors.top: combatibilityItem.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height / 2 visible: base.technicalDataSheetUrl !== undefined || - base.safetyDataSheetUrl !== undefined || base.printingGuidelinesUrl !== undefined + base.safetyDataSheetUrl !== undefined || base.printingGuidelinesUrl !== undefined || + base.materialWebsiteUrl !== undefined height: visible ? contentHeight : 0 text: { @@ -208,6 +216,16 @@ Item var pg_name = catalog.i18nc("@action:label", "Printing Guidelines") result += "%2".arg(base.printingGuidelinesUrl).arg(pg_name) } + if (base.materialWebsiteUrl !== undefined) + { + if (result.length > 0) + { + result += "
    " + } + var pg_name = catalog.i18nc("@action:label", "Website") + result += "%2".arg(base.materialWebsiteUrl).arg(pg_name) + } + return result } font: UM.Theme.getFont("very_small") From c9c7ef7d67f693da7dea77cf0d699dd76a7e8d43 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 26 Nov 2018 13:32:01 +0100 Subject: [PATCH 0485/1240] Add "is_experimental" for experimental qualities CURA-5879 --- .../quality/ultimaker3/um3_aa0.25_PC_Normal_Quality.inst.cfg | 1 + .../quality/ultimaker3/um3_aa0.25_PP_Normal_Quality.inst.cfg | 1 + resources/quality/ultimaker3/um3_aa0.8_CPEP_Fast_Print.inst.cfg | 1 + .../quality/ultimaker3/um3_aa0.8_CPEP_Superdraft_Print.inst.cfg | 1 + .../quality/ultimaker3/um3_aa0.8_CPEP_Verydraft_Print.inst.cfg | 1 + resources/quality/ultimaker3/um3_aa0.8_PC_Fast_Print.inst.cfg | 1 + .../quality/ultimaker3/um3_aa0.8_PC_Superdraft_Print.inst.cfg | 1 + .../quality/ultimaker3/um3_aa0.8_PC_Verydraft_Print.inst.cfg | 1 + .../quality/ultimaker_s5/um_s5_aa0.25_PC_Normal_Quality.inst.cfg | 1 + .../quality/ultimaker_s5/um_s5_aa0.25_PP_Normal_Quality.inst.cfg | 1 + .../quality/ultimaker_s5/um_s5_aa0.8_CPEP_Fast_Print.inst.cfg | 1 + .../ultimaker_s5/um_s5_aa0.8_CPEP_Superdraft_Print.inst.cfg | 1 + .../ultimaker_s5/um_s5_aa0.8_CPEP_Verydraft_Print.inst.cfg | 1 + .../quality/ultimaker_s5/um_s5_aa0.8_PC_Fast_Print.inst.cfg | 1 + .../ultimaker_s5/um_s5_aa0.8_PC_Superdraft_Print.inst.cfg | 1 + .../quality/ultimaker_s5/um_s5_aa0.8_PC_Verydraft_Print.inst.cfg | 1 + .../ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Fast_Print.inst.cfg | 1 + .../ultimaker_s5/um_s5_aa0.8_aluminum_PC_Fast_Print.inst.cfg | 1 + 18 files changed, 18 insertions(+) diff --git a/resources/quality/ultimaker3/um3_aa0.25_PC_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.25_PC_Normal_Quality.inst.cfg index e08fa27dc9..4d585b54c1 100644 --- a/resources/quality/ultimaker3/um3_aa0.25_PC_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.25_PC_Normal_Quality.inst.cfg @@ -10,6 +10,7 @@ quality_type = normal weight = 0 material = generic_pc variant = AA 0.25 +is_experimental = True [values] acceleration_enabled = True diff --git a/resources/quality/ultimaker3/um3_aa0.25_PP_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.25_PP_Normal_Quality.inst.cfg index 6e9bbdce27..bee345e302 100644 --- a/resources/quality/ultimaker3/um3_aa0.25_PP_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.25_PP_Normal_Quality.inst.cfg @@ -10,6 +10,7 @@ quality_type = normal weight = 0 material = generic_pp variant = AA 0.25 +is_experimental = True [values] acceleration_enabled = True diff --git a/resources/quality/ultimaker3/um3_aa0.8_CPEP_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_CPEP_Fast_Print.inst.cfg index 73df9637f7..fc4acf3cb0 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_CPEP_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_CPEP_Fast_Print.inst.cfg @@ -10,6 +10,7 @@ quality_type = draft weight = -2 material = generic_cpe_plus variant = AA 0.8 +is_experimental = True [values] brim_width = 14 diff --git a/resources/quality/ultimaker3/um3_aa0.8_CPEP_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_CPEP_Superdraft_Print.inst.cfg index d59bfe7cea..36b3ef603f 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_CPEP_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_CPEP_Superdraft_Print.inst.cfg @@ -10,6 +10,7 @@ quality_type = superdraft weight = -4 material = generic_cpe_plus variant = AA 0.8 +is_experimental = True [values] brim_width = 14 diff --git a/resources/quality/ultimaker3/um3_aa0.8_CPEP_Verydraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_CPEP_Verydraft_Print.inst.cfg index 368317019f..14e08cb14d 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_CPEP_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_CPEP_Verydraft_Print.inst.cfg @@ -10,6 +10,7 @@ quality_type = verydraft weight = -3 material = generic_cpe_plus variant = AA 0.8 +is_experimental = True [values] brim_width = 14 diff --git a/resources/quality/ultimaker3/um3_aa0.8_PC_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_PC_Fast_Print.inst.cfg index 5b81532977..c2bb6d4988 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_PC_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_PC_Fast_Print.inst.cfg @@ -10,6 +10,7 @@ quality_type = draft weight = 0 material = generic_pc variant = AA 0.8 +is_experimental = True [values] brim_width = 14 diff --git a/resources/quality/ultimaker3/um3_aa0.8_PC_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_PC_Superdraft_Print.inst.cfg index 317b89ea85..e815b673d1 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_PC_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_PC_Superdraft_Print.inst.cfg @@ -10,6 +10,7 @@ quality_type = superdraft weight = -2 material = generic_pc variant = AA 0.8 +is_experimental = True [values] brim_width = 14 diff --git a/resources/quality/ultimaker3/um3_aa0.8_PC_Verydraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_PC_Verydraft_Print.inst.cfg index 2fd6bd7609..c50cee576d 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_PC_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_PC_Verydraft_Print.inst.cfg @@ -10,6 +10,7 @@ quality_type = verydraft weight = -1 material = generic_pc variant = AA 0.8 +is_experimental = True [values] brim_width = 14 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.25_PC_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.25_PC_Normal_Quality.inst.cfg index 55d53c6c71..53c319d6e6 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.25_PC_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.25_PC_Normal_Quality.inst.cfg @@ -10,6 +10,7 @@ quality_type = normal weight = 0 material = generic_pc variant = AA 0.25 +is_experimental = True [values] acceleration_enabled = True diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.25_PP_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.25_PP_Normal_Quality.inst.cfg index c925845dc1..b4d34cc392 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.25_PP_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.25_PP_Normal_Quality.inst.cfg @@ -10,6 +10,7 @@ quality_type = normal weight = 0 material = generic_pp variant = AA 0.25 +is_experimental = True [values] acceleration_enabled = True diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Fast_Print.inst.cfg index e78006689b..4bdd09e9b3 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Fast_Print.inst.cfg @@ -10,6 +10,7 @@ quality_type = draft weight = -2 material = generic_cpe_plus variant = AA 0.8 +is_experimental = True [values] brim_width = 14 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Superdraft_Print.inst.cfg index c6d0962157..f9b6b0c7a6 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Superdraft_Print.inst.cfg @@ -10,6 +10,7 @@ quality_type = superdraft weight = -4 material = generic_cpe_plus variant = AA 0.8 +is_experimental = True [values] brim_width = 14 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Verydraft_Print.inst.cfg index b80f773594..9c6c7de7f0 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Verydraft_Print.inst.cfg @@ -10,6 +10,7 @@ quality_type = verydraft weight = -3 material = generic_cpe_plus variant = AA 0.8 +is_experimental = True [values] brim_width = 14 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Fast_Print.inst.cfg index 0ed4e3d994..c24aa9a98d 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Fast_Print.inst.cfg @@ -10,6 +10,7 @@ quality_type = draft weight = 0 material = generic_pc variant = AA 0.8 +is_experimental = True [values] brim_width = 14 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Superdraft_Print.inst.cfg index 53bf1d3107..5fc306b1f1 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Superdraft_Print.inst.cfg @@ -10,6 +10,7 @@ quality_type = superdraft weight = -2 material = generic_pc variant = AA 0.8 +is_experimental = True [values] brim_width = 14 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Verydraft_Print.inst.cfg index d9c45c2634..996adf5be7 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Verydraft_Print.inst.cfg @@ -10,6 +10,7 @@ quality_type = verydraft weight = -1 material = generic_pc variant = AA 0.8 +is_experimental = True [values] brim_width = 14 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Fast_Print.inst.cfg index 3e74390840..4098c86ad9 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Fast_Print.inst.cfg @@ -11,6 +11,7 @@ weight = -2 material = generic_cpe_plus variant = AA 0.8 buildplate = Aluminum +is_experimental = True [values] brim_width = 14 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Fast_Print.inst.cfg index 747e2fe8a5..6fdd22c6b0 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Fast_Print.inst.cfg @@ -11,6 +11,7 @@ weight = 0 material = generic_pc variant = AA 0.8 buildplate = Aluminum +is_experimental = True [values] brim_width = 10 From 2c8ed992813f2ae56f988bc16b97e87e94bcc529 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 26 Nov 2018 14:48:05 +0100 Subject: [PATCH 0486/1240] Add is_experimental to Qt models and QMLs CURA-5879 --- .../Models/QualityProfilesDropDownMenuModel.py | 5 ++++- cura/Machines/QualityGroup.py | 18 ++++++++++++++++++ cura/Machines/QualityManager.py | 6 +++--- cura/Settings/MachineManager.py | 8 ++++++++ resources/qml/Menus/ProfileMenu.qml | 6 +++++- resources/qml/Settings/SettingView.qml | 3 +++ 6 files changed, 41 insertions(+), 5 deletions(-) diff --git a/cura/Machines/Models/QualityProfilesDropDownMenuModel.py b/cura/Machines/Models/QualityProfilesDropDownMenuModel.py index a01cc1194f..747882b041 100644 --- a/cura/Machines/Models/QualityProfilesDropDownMenuModel.py +++ b/cura/Machines/Models/QualityProfilesDropDownMenuModel.py @@ -21,6 +21,7 @@ class QualityProfilesDropDownMenuModel(ListModel): AvailableRole = Qt.UserRole + 5 QualityGroupRole = Qt.UserRole + 6 QualityChangesGroupRole = Qt.UserRole + 7 + IsExperimentalRole = Qt.UserRole + 8 def __init__(self, parent = None): super().__init__(parent) @@ -32,6 +33,7 @@ class QualityProfilesDropDownMenuModel(ListModel): self.addRoleName(self.AvailableRole, "available") #Whether the quality profile is available in our current nozzle + material. self.addRoleName(self.QualityGroupRole, "quality_group") self.addRoleName(self.QualityChangesGroupRole, "quality_changes_group") + self.addRoleName(self.IsExperimentalRole, "is_experimental") self._application = Application.getInstance() self._machine_manager = self._application.getMachineManager() @@ -74,7 +76,8 @@ class QualityProfilesDropDownMenuModel(ListModel): "layer_height": layer_height, "layer_height_unit": self._layer_height_unit, "available": quality_group.is_available, - "quality_group": quality_group} + "quality_group": quality_group, + "is_experimental": quality_group.is_experimental} item_list.append(item) diff --git a/cura/Machines/QualityGroup.py b/cura/Machines/QualityGroup.py index 535ba453f8..f5bcbb0de8 100644 --- a/cura/Machines/QualityGroup.py +++ b/cura/Machines/QualityGroup.py @@ -4,6 +4,9 @@ from typing import Dict, Optional, List, Set from PyQt5.QtCore import QObject, pyqtSlot + +from UM.Util import parseBool + from cura.Machines.ContainerNode import ContainerNode @@ -29,6 +32,7 @@ class QualityGroup(QObject): self.nodes_for_extruders = {} # type: Dict[int, ContainerNode] self.quality_type = quality_type self.is_available = False + self.is_experimental = False @pyqtSlot(result = str) def getName(self) -> str: @@ -51,3 +55,17 @@ class QualityGroup(QObject): for extruder_node in self.nodes_for_extruders.values(): result.append(extruder_node) return result + + def setGlobalNode(self, node: "ContainerNode") -> None: + self.node_for_global = node + + # Update is_experimental flag + is_experimental = parseBool(node.getMetaDataEntry("is_experimental", False)) + self.is_experimental |= is_experimental + + def setExtruderNode(self, position: int, node: "ContainerNode") -> None: + self.nodes_for_extruders[position] = node + + # Update is_experimental flag + is_experimental = parseBool(node.getMetaDataEntry("is_experimental", False)) + self.is_experimental |= is_experimental diff --git a/cura/Machines/QualityManager.py b/cura/Machines/QualityManager.py index a784d17f0b..34cc9ce4b2 100644 --- a/cura/Machines/QualityManager.py +++ b/cura/Machines/QualityManager.py @@ -235,7 +235,7 @@ class QualityManager(QObject): for quality_type, quality_node in node.quality_type_map.items(): quality_group = QualityGroup(quality_node.getMetaDataEntry("name", ""), quality_type) - quality_group.node_for_global = quality_node + quality_group.setGlobalNode(quality_node) quality_group_dict[quality_type] = quality_group break @@ -337,7 +337,7 @@ class QualityManager(QObject): quality_group = quality_group_dict[quality_type] if position not in quality_group.nodes_for_extruders: - quality_group.nodes_for_extruders[position] = quality_node + quality_group.setExtruderNode(position, quality_node) # If the machine has its own specific qualities, for extruders, it should skip the global qualities # and use the material/variant specific qualities. @@ -367,7 +367,7 @@ class QualityManager(QObject): if node and node.quality_type_map: for quality_type, quality_node in node.quality_type_map.items(): quality_group = QualityGroup(quality_node.getMetaDataEntry("name", ""), quality_type) - quality_group.node_for_global = quality_node + quality_group.setGlobalNode(quality_node) quality_group_dict[quality_type] = quality_group break diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index f7ad108ad4..527d5d2679 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -616,6 +616,14 @@ class MachineManager(QObject): is_supported = self._current_quality_group.is_available return is_supported + @pyqtProperty(bool, notify = activeQualityGroupChanged) + def isActiveQualityExperimental(self) -> bool: + is_experimental = False + if self._global_container_stack: + if self._current_quality_group: + is_experimental = self._current_quality_group.is_experimental + return is_experimental + ## Returns whether there is anything unsupported in the current set-up. # # The current set-up signifies the global stack and all extruder stacks, diff --git a/resources/qml/Menus/ProfileMenu.qml b/resources/qml/Menus/ProfileMenu.qml index ffd3c556b6..b503b7de85 100644 --- a/resources/qml/Menus/ProfileMenu.qml +++ b/resources/qml/Menus/ProfileMenu.qml @@ -17,7 +17,11 @@ Menu MenuItem { - text: (model.layer_height != "") ? model.name + " - " + model.layer_height + model.layer_height_unit : model.name + text: { + var full_text = (model.layer_height != "") ? model.name + " - " + model.layer_height + model.layer_height_unit : model.name; + full_text += model.is_experimental ? " - Experimental" : ""; + return full_text; + } checkable: true checked: Cura.MachineManager.activeQualityOrQualityChangesName == model.name exclusiveGroup: group diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index da50b430ac..5d916aed7c 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -66,6 +66,9 @@ Item function generateActiveQualityText () { var result = Cura.MachineManager.activeQualityOrQualityChangesName; + if (Cura.MachineManager.isActiveQualityExperimental) { + result += " (Experimental)"; + } if (Cura.MachineManager.isActiveQualitySupported) { if (Cura.MachineManager.activeQualityLayerHeight > 0) { From 41a5f05391dbc06dc4afde0321364e596ab8452b Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 27 Nov 2018 15:57:26 +0100 Subject: [PATCH 0487/1240] Remove SyncButton It hasn't been used now for a while. Contributes to issue CURA-5876. --- .../Menus/ConfigurationMenu/SyncButton.qml | 102 ------------------ 1 file changed, 102 deletions(-) delete mode 100644 resources/qml/Menus/ConfigurationMenu/SyncButton.qml diff --git a/resources/qml/Menus/ConfigurationMenu/SyncButton.qml b/resources/qml/Menus/ConfigurationMenu/SyncButton.qml deleted file mode 100644 index 558ae1e477..0000000000 --- a/resources/qml/Menus/ConfigurationMenu/SyncButton.qml +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) 2018 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.7 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 - -import UM 1.2 as UM -import Cura 1.0 as Cura - -Button -{ - id: base - property var outputDevice: null - property var matched: updateOnSync() - text: matched == true ? catalog.i18nc("@label:extruder label", "Yes") : catalog.i18nc("@label:extruder label", "No") - width: parent.width - height: parent.height - - function updateOnSync() - { - if (outputDevice != undefined) - { - for (var index in outputDevice.uniqueConfigurations) - { - var configuration = outputDevice.uniqueConfigurations[index] - if (Cura.MachineManager.matchesConfiguration(configuration)) - { - base.matched = true; - return; - } - } - } - base.matched = false; - } - - style: ButtonStyle - { - background: Rectangle - { - color: - { - if(control.pressed) - { - return UM.Theme.getColor("machine_selector_active"); - } - else if(control.hovered) - { - return UM.Theme.getColor("machine_selector_hover"); - } - else - { - return UM.Theme.getColor("machine_selector_bar"); - } - } - Behavior on color { ColorAnimation { duration: 50; } } - - UM.RecolorImage - { - id: downArrow - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("default_margin").width - width: UM.Theme.getSize("standard_arrow").width - height: UM.Theme.getSize("standard_arrow").height - sourceSize.width: width - sourceSize.height: height - color: UM.Theme.getColor("text_emphasis") - source: UM.Theme.getIcon("arrow_bottom") - } - UM.RecolorImage - { - id: sidebarComboBoxLabel - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("default_margin").width - anchors.verticalCenter: parent.verticalCenter; - - width: UM.Theme.getSize("printer_sync_icon").width - height: UM.Theme.getSize("printer_sync_icon").height - - color: control.matched ? UM.Theme.getColor("printer_config_matched") : UM.Theme.getColor("printer_config_mismatch") - source: UM.Theme.getIcon("tab_status_connected") - sourceSize.width: width - sourceSize.height: height - } - } - label: Label {} - } - - Connections - { - target: outputDevice - onUniqueConfigurationsChanged: updateOnSync() - } - - Connections - { - target: Cura.MachineManager - onCurrentConfigurationChanged: updateOnSync() - onOutputDevicesChanged: updateOnSync() - } -} \ No newline at end of file From dad2031e4b5b8750a6721e6fdb3a24620eaf3cc7 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 27 Nov 2018 16:06:37 +0100 Subject: [PATCH 0488/1240] Switch to the preview stage before to switch to simulation view ... since now the list of views are in a different stage. Contributes to CURA-5979. --- cura/CuraApplication.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 6e52a97518..b43e4d5e0e 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1663,7 +1663,9 @@ class CuraApplication(QtApplication): is_non_sliceable = "." + file_extension in self._non_sliceable_extensions if is_non_sliceable: - self.callLater(lambda: self.getController().setActiveView("SimulationView")) + # Need to switch first to the preview stage and then to layer view + self.callLater(lambda: (self.getController().setActiveStage("PreviewStage"), + self.getController().setActiveView("SimulationView"))) block_slicing_decorator = BlockSlicingDecorator() node.addDecorator(block_slicing_decorator) From ff851b0382b688b6c190d48127be3e0a369a2960 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Tue, 27 Nov 2018 16:36:27 +0100 Subject: [PATCH 0489/1240] Added margins CURA-5941 --- resources/qml/PrintSetupSelector.qml | 4 ++-- resources/qml/SidebarSimple.qml | 10 ++-------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/resources/qml/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector.qml index b20d8e116b..6edb297721 100644 --- a/resources/qml/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector.qml @@ -216,8 +216,8 @@ Cura.ExpandableComponent topMargin: UM.Theme.getSize("narrow_margin").height right: parent.right left: parent.left - leftMargin: UM.Theme.getSize("thick_margin").width / 1.5 - rightMargin: UM.Theme.getSize("thick_margin").width / 1.5 + leftMargin: UM.Theme.getSize("default_margin").width + rightMargin: UM.Theme.getSize("default_margin").width } UM.TabRow diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index 2e0f0d6c4c..f2e998f526 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -38,6 +38,8 @@ Item width: childrenRect.width height: childrenRect.height + anchors.left: parent.left + anchors.leftMargin: UM.Theme.getSize("default_margin").width // // Quality profile // @@ -48,7 +50,6 @@ Item height: UM.Theme.getSize("thick_margin").height anchors.topMargin: UM.Theme.getSize("thick_margin").height anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("thick_margin").width anchors.right: parent.right Timer @@ -193,7 +194,6 @@ Item source: UM.Theme.getIcon("category_layer_height") text: catalog.i18nc("@label", "Layer Height") anchors.bottom: speedSlider.bottom - } // Show titles for the each quality slider ticks @@ -507,7 +507,6 @@ Item top: parent.top topMargin: Math.round(UM.Theme.getSize("thick_margin").height * 1.7) left: parent.left - leftMargin: UM.Theme.getSize("thick_margin").width } } } @@ -526,9 +525,7 @@ Item Label { id: selectedInfillRateText - //anchors.top: parent.top anchors.left: infillSlider.left - anchors.leftMargin: Math.round((infillSlider.value / infillSlider.stepSize) * (infillSlider.width / (infillSlider.maximumValue / infillSlider.stepSize)) - 10 * screenScaleFactor) anchors.right: parent.right text: parseInt(infillDensity.properties.value) + "%" @@ -833,7 +830,6 @@ Item top: infillCellRight.bottom topMargin: Math.round(UM.Theme.getSize("thick_margin").height * 1.5) left: parent.left - leftMargin: UM.Theme.getSize("thick_margin").width right: infillCellLeft.right rightMargin: UM.Theme.getSize("thick_margin").width verticalCenter: enableSupportCheckBox.verticalCenter @@ -956,7 +952,6 @@ Item anchors { left: parent.left - leftMargin: UM.Theme.getSize("thick_margin").width right: infillCellLeft.right rightMargin: UM.Theme.getSize("thick_margin").width verticalCenter: adhesionCheckBox.verticalCenter @@ -1037,7 +1032,6 @@ Item { id: tipsText anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("thick_margin").width anchors.right: parent.right anchors.rightMargin: UM.Theme.getSize("thick_margin").width anchors.top: parent.top From 26c7558a53b571deaea91d8d2acd39ee9f5a005d Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 27 Nov 2018 16:44:16 +0100 Subject: [PATCH 0490/1240] Don't show labels if controls themselves are not shown The height of the popup is luckily automatically adjusted now. Contributes to issue CURA-5876. --- resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index 78448d5be5..87096c3a14 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -136,6 +136,7 @@ Item color: UM.Theme.getColor("text") height: parent.height width: selectors.textWidth + visible: materialSelection.visible } OldControls.ToolButton @@ -178,6 +179,7 @@ Item color: UM.Theme.getColor("text") height: parent.height width: selectors.textWidth + visible: variantSelection.visible } OldControls.ToolButton From 26f107d1763387848e4faf9b7b9726ac9cee90f9 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 27 Nov 2018 17:03:19 +0100 Subject: [PATCH 0491/1240] Use cluster_id as dict key, remove unneeded white space --- .../src/Cloud/CloudOutputDeviceManager.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 3ebaeea9a4..e20a658994 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -47,7 +47,6 @@ class CloudOutputDeviceManager(NetworkClient): self._update_clusters_thread = Thread(target=self._updateClusters, daemon=True) self._update_clusters_thread.start() - ## Override _createEmptyRequest to add the needed authentication header for talking to the Ultimaker Cloud API. def _createEmptyRequest(self, path: str, content_type: Optional[str] = "application/json") -> QNetworkRequest: request = super()._createEmptyRequest(self.API_ROOT_PATH + path, content_type = content_type) @@ -81,14 +80,14 @@ class CloudOutputDeviceManager(NetworkClient): return known_cluster_ids = set(self._remote_clusters.keys()) - found_clusters_ids = set(found_clusters.keys()) + found_cluster_ids = set(found_clusters.keys()) # Add an output device for each new remote cluster. - for cluster_id in found_clusters_ids.difference(known_cluster_ids): + for cluster_id in found_cluster_ids.difference(known_cluster_ids): self._addCloudOutputDevice(found_clusters[cluster_id]) # Remove output devices that are gone - for cluster_id in known_cluster_ids.difference(found_clusters_ids): + for cluster_id in known_cluster_ids.difference(found_cluster_ids): self._removeCloudOutputDevice(found_clusters[cluster_id]) # For testing we add a dummy device: @@ -97,7 +96,7 @@ class CloudOutputDeviceManager(NetworkClient): @staticmethod def _parseStatusResponse(reply: QNetworkReply) -> Dict[str, CloudCluster]: try: - return {c["guid"]: CloudCluster(**c) for c in json.loads(reply.readAll().data().decode("utf-8"))} + return {c["cluster_id"]: CloudCluster(**c) for c in json.loads(reply.readAll().data().decode("utf-8"))} except UnicodeDecodeError: Logger.log("w", "Unable to read server response") except json.decoder.JSONDecodeError: From 0b8a56458d5d6bea6dc88c8674c3459fa5eee402 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 27 Nov 2018 17:11:32 +0100 Subject: [PATCH 0492/1240] Remove unused CustomConfigurationSelector I checked. It is no longer used. Contributes to issue CURA-5876. --- resources/qml/CustomConfigurationSelector.qml | 357 ------------------ 1 file changed, 357 deletions(-) delete mode 100644 resources/qml/CustomConfigurationSelector.qml diff --git a/resources/qml/CustomConfigurationSelector.qml b/resources/qml/CustomConfigurationSelector.qml deleted file mode 100644 index c78ca700da..0000000000 --- a/resources/qml/CustomConfigurationSelector.qml +++ /dev/null @@ -1,357 +0,0 @@ -// Copyright (c) 2018 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.7 -import QtQuick.Controls 1.1 -import QtQuick.Controls.Styles 1.1 - -import UM 1.2 as UM -import Cura 1.0 as Cura - -Rectangle -{ - implicitWidth: parent.width - implicitHeight: parent.height - - id: base - color: UM.Theme.getColor("main_background") - - // Height has an extra 2x margin for the top & bottom margin. - height: childrenRect.height + 2 * UM.Theme.getSize("default_margin").width - - Cura.ExtrudersModel { id: extrudersModel } - - ListView - { - // Horizontal list that shows the extruders - id: extrudersList - visible: extrudersModel.items.length > 1 - property var index: 0 - - height: UM.Theme.getSize("configuration_selector_mode_tabs").height - boundsBehavior: Flickable.StopAtBounds - - anchors - { - left: parent.left - right: parent.right - top: parent.top - margins: UM.Theme.getSize("thick_margin").width - } - - ExclusiveGroup { id: extruderMenuGroup } - - orientation: ListView.Horizontal - - model: extrudersModel - - Connections - { - target: Cura.MachineManager - onGlobalContainerChanged: forceActiveFocus() // Changing focus applies the currently-being-typed values so it can change the displayed setting values. - } - - delegate: Button - { - height: parent.height - width: Math.round(ListView.view.width / extrudersModel.rowCount()) - - text: model.name - tooltip: model.name - exclusiveGroup: extruderMenuGroup - checked: Cura.ExtruderManager.activeExtruderIndex == index - - property bool extruder_enabled: true - - MouseArea // TODO; This really should be fixed. It makes absolutely no sense to have a button AND a mouse area. - { - anchors.fill: parent - acceptedButtons: Qt.LeftButton | Qt.RightButton - onClicked: - { - switch (mouse.button) - { - case Qt.LeftButton: - extruder_enabled = Cura.MachineManager.getExtruder(model.index).isEnabled - if (extruder_enabled) - { - forceActiveFocus() // Changing focus applies the currently-being-typed values so it can change the displayed setting values. - Cura.ExtruderManager.setActiveExtruderIndex(index) - } - break - case Qt.RightButton: - extruder_enabled = Cura.MachineManager.getExtruder(model.index).isEnabled - extruderMenu.popup() - break - } - } - } - - Menu - { - id: extruderMenu - - MenuItem - { - text: catalog.i18nc("@action:inmenu", "Enable Extruder") - onTriggered: Cura.MachineManager.setExtruderEnabled(model.index, true) - visible: !extruder_enabled // using an intermediate variable prevents an empty popup that occured now and then - } - - MenuItem - { - text: catalog.i18nc("@action:inmenu", "Disable Extruder") - onTriggered: Cura.MachineManager.setExtruderEnabled(model.index, false) - visible: extruder_enabled - enabled: Cura.MachineManager.numberExtrudersEnabled > 1 - } - } - - style: ButtonStyle - { - background: Rectangle - { - anchors.fill: parent - border.width: control.checked ? UM.Theme.getSize("default_lining").width * 2 : UM.Theme.getSize("default_lining").width - border.color: - { - if (Cura.MachineManager.getExtruder(index).isEnabled) - { - if(control.checked || control.pressed) - { - return UM.Theme.getColor("action_button_active_border") - } - else if (control.hovered) - { - return UM.Theme.getColor("action_button_hovered_border") - } - return UM.Theme.getColor("action_button_border") - } - return UM.Theme.getColor("action_button_disabled_border") - } - color: - { - if (Cura.MachineManager.getExtruder(index).isEnabled) - { - if(control.checked || control.pressed) - { - return UM.Theme.getColor("action_button_active"); - } - else if (control.hovered) - { - return UM.Theme.getColor("action_button_hovered") - } - return UM.Theme.getColor("action_button") - } - return UM.Theme.getColor("action_button_disabled") - } - Behavior on color { ColorAnimation { duration: 50; } } - - Item - { - id: extruderButtonFace - anchors.centerIn: parent - width: childrenRect.width - - Label - { - // Static text that holds the "Extruder" label - id: extruderStaticText - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - - color: - { - if (Cura.MachineManager.getExtruder(index).isEnabled) - { - if(control.checked || control.pressed) - { - return UM.Theme.getColor("action_button_active_text"); - } - else if (control.hovered) - { - return UM.Theme.getColor("action_button_hovered_text") - } - return UM.Theme.getColor("action_button_text") - } - return UM.Theme.getColor("action_button_disabled_text") - } - - font: UM.Theme.getFont("large_nonbold") - text: catalog.i18nc("@label", "Extruder") - visible: width < (control.width - extruderIcon.width - UM.Theme.getSize("default_margin").width) - elide: Text.ElideRight - } - - ExtruderIcon - { - // Round icon with the extruder number and material color indicator. - id: extruderIcon - - anchors.verticalCenter: parent.verticalCenter - anchors.left: extruderStaticText.right - anchors.leftMargin: UM.Theme.getSize("default_margin").width - width: control.height - Math.round(UM.Theme.getSize("default_margin").width / 2) - height: width - - checked: control.checked - materialColor: model.color - textColor: extruderStaticText.color - } - } - } - - label: Item {} - } - } - } - - Item - { - id: materialRow - height: UM.Theme.getSize("print_setup_item").height - visible: Cura.MachineManager.hasMaterials - - anchors - { - left: parent.left - right: parent.right - top: extrudersList.bottom - margins: UM.Theme.getSize("thick_margin").width - } - - Label - { - id: materialLabel - text: catalog.i18nc("@label", "Material"); - width: Math.round(parent.width * 0.45 - UM.Theme.getSize("default_margin").width) - height: parent.height - verticalAlignment: Text.AlignVCenter - font: UM.Theme.getFont("default"); - color: UM.Theme.getColor("text"); - } - - ToolButton - { - id: materialSelection - - property var activeExtruder: Cura.MachineManager.activeStack - property var hasActiveExtruder: activeExtruder != null - property var currentRootMaterialName: hasActiveExtruder ? activeExtruder.material.name : "" - - text: currentRootMaterialName - tooltip: currentRootMaterialName - visible: Cura.MachineManager.hasMaterials - - enabled: !extrudersList.visible || Cura.ExtruderManager.activeExtruderIndex > -1 - - height: UM.Theme.getSize("setting_control").height - width: Math.round(parent.width * 0.7) + UM.Theme.getSize("thick_margin").width - anchors.right: parent.right - style: UM.Theme.styles.sidebar_header_button - activeFocusOnPress: true; - menu: Cura.MaterialMenu - { - extruderIndex: Cura.ExtruderManager.activeExtruderIndex - } - - property var valueError: hasActiveExtruder ? Cura.ContainerManager.getContainerMetaDataEntry(activeExtruder.material.id, "compatible", "") != "True" : true - property var valueWarning: ! Cura.MachineManager.isActiveQualitySupported - } - } - - Item - { - id: variantRow - height: UM.Theme.getSize("print_setup_item").height - visible: Cura.MachineManager.hasVariants - - anchors - { - left: parent.left - right: parent.right - top: materialRow.bottom - margins: UM.Theme.getSize("thick_margin").width - } - - Label - { - id: variantLabel - text: Cura.MachineManager.activeDefinitionVariantsName; - width: Math.round(parent.width * 0.45 - UM.Theme.getSize("default_margin").width) - height: parent.height - verticalAlignment: Text.AlignVCenter - font: UM.Theme.getFont("default"); - color: UM.Theme.getColor("text"); - } - - ToolButton - { - id: variantSelection - text: Cura.MachineManager.activeVariantName - tooltip: Cura.MachineManager.activeVariantName; - visible: Cura.MachineManager.hasVariants - - height: UM.Theme.getSize("setting_control").height - width: Math.round(parent.width * 0.7 + UM.Theme.getSize("thick_margin").width) - anchors.right: parent.right - style: UM.Theme.styles.sidebar_header_button - activeFocusOnPress: true; - - menu: Cura.NozzleMenu { extruderIndex: Cura.ExtruderManager.activeExtruderIndex } - } - } - - Item - { - id: materialCompatibilityLink - height: UM.Theme.getSize("print_setup_item").height - - anchors.right: parent.right - anchors.top: variantRow.bottom - anchors.margins: UM.Theme.getSize("thick_margin").width - UM.RecolorImage - { - id: warningImage - - anchors.right: materialInfoLabel.left - anchors.rightMargin: UM.Theme.getSize("default_margin").width - - source: UM.Theme.getIcon("warning") - width: UM.Theme.getSize("section_icon").width - height: UM.Theme.getSize("section_icon").height - - sourceSize.width: width - sourceSize.height: 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.right: parent.right - - MouseArea - { - anchors.fill: parent - - onClicked: - { - // open the material URL with web browser - Qt.openUrlExternally("https://ultimaker.com/incoming-links/cura/material-compatibilty"); - } - } - } - } -} From 309061ce3113df6dd7f7c9e9762526b1ce0973c5 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 27 Nov 2018 17:16:52 +0100 Subject: [PATCH 0493/1240] Add a new ToolbarButton Now also the Extruder button is a toolbar button since it will show in the toolbar. --- resources/qml/ExtruderButton.qml | 18 +++---- resources/qml/Toolbar.qml | 25 ++++++--- resources/qml/ToolbarButton.qml | 92 ++++++++++++++++++++++++++++++++ resources/qml/qmldir | 3 +- 4 files changed, 117 insertions(+), 21 deletions(-) create mode 100644 resources/qml/ToolbarButton.qml diff --git a/resources/qml/ExtruderButton.qml b/resources/qml/ExtruderButton.qml index d7cbdc52bc..feb399d528 100644 --- a/resources/qml/ExtruderButton.qml +++ b/resources/qml/ExtruderButton.qml @@ -7,7 +7,7 @@ import QtQuick.Controls 2.0 import UM 1.2 as UM import Cura 1.0 as Cura -Button +Cura.ToolbarButton { id: base @@ -18,22 +18,16 @@ Button checked: Cura.ExtruderManager.selectedObjectExtruders.indexOf(extruder.id) != -1 enabled: UM.Selection.hasSelection && extruder.stack.isEnabled - background: Item {} - contentItem: Item + toolItem: ExtruderIcon { - // For some reason if the extruder icon is not enclosed to the item, the size changes to fill the size of the button - ExtruderIcon - { - anchors.centerIn: parent - materialColor: model.color - extruderEnabled: extruder.stack.isEnabled - property int index: extruder.index - } + materialColor: extruder.color + extruderEnabled: extruder.stack.isEnabled + property int index: extruder.index } onClicked: { forceActiveFocus() //First grab focus, so all the text fields are updated - CuraActions.setExtruderForSelection(extruder.id); + CuraActions.setExtruderForSelection(extruder.id) } } diff --git a/resources/qml/Toolbar.qml b/resources/qml/Toolbar.qml index 0240aaab26..3a4e7704c0 100644 --- a/resources/qml/Toolbar.qml +++ b/resources/qml/Toolbar.qml @@ -55,17 +55,25 @@ Item model: UM.ToolModel { id: toolsModel } width: childrenRect.width height: childrenRect.height - Button + + delegate: ToolbarButton { text: model.name + (model.shortcut ? (" (" + model.shortcut + ")") : "") - iconSource: (UM.Theme.getIcon(model.icon) != "") ? UM.Theme.getIcon(model.icon) : "file:///" + model.location + "/" + model.icon checkable: true checked: model.active enabled: model.enabled && UM.Selection.hasSelection && UM.Controller.toolsEnabled - style: UM.Theme.styles.toolbar_button - property bool isFirstElement: toolsModel.getItem(0).id == model.id - property bool isLastElement: toolsModel.getItem(toolsModel.rowCount() - 1).id == model.id + topElement: toolsModel.getItem(0).id == model.id + bottomElement: toolsModel.getItem(toolsModel.rowCount() - 1).id == model.id + + toolItem: UM.RecolorImage + { + opacity: parent.enabled ? 1.0 : 0.2 + source: (UM.Theme.getIcon(model.icon) != "") ? UM.Theme.getIcon(model.icon) : "file:///" + model.location + "/" + model.icon + color: UM.Theme.getColor("toolbar_button_text") + + sourceSize: UM.Theme.getSize("button_icon") + } onCheckedChanged: { @@ -128,11 +136,12 @@ Item height: childrenRect.height property var _model: Cura.ExtrudersModel { id: extrudersModel } model: _model.items.length > 1 ? _model : 0 - ExtruderButton + + delegate: ExtruderButton { extruder: model - height: UM.Theme.getSize("button").width - width: UM.Theme.getSize("button").width + topElement: extrudersModel.getItem(0).id == model.id + bottomElement: extrudersModel.getItem(extrudersModel.rowCount() - 1).id == model.id } } } diff --git a/resources/qml/ToolbarButton.qml b/resources/qml/ToolbarButton.qml new file mode 100644 index 0000000000..7241b489b6 --- /dev/null +++ b/resources/qml/ToolbarButton.qml @@ -0,0 +1,92 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 2.3 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +Button +{ + id: base + + property alias toolItem: contentItemLoader.sourceComponent + property bool topElement: false + property bool bottomElement: false + + hoverEnabled: true + + background: Rectangle + { + implicitWidth: UM.Theme.getSize("button").width + implicitHeight: UM.Theme.getSize("button").height + color: + { + if (base.checked && base.hovered) + { + return UM.Theme.getColor("toolbar_button_active_hover") + } + else if (base.checked) + { + return "red" //UM.Theme.getColor("toolbar_button_active") + } + else if(base.hovered) + { + return UM.Theme.getColor("toolbar_button_hover") + } + return UM.Theme.getColor("toolbar_background") + } + radius: UM.Theme.getSize("default_radius").width + + Rectangle + { + id: topSquare + anchors + { + left: parent.left + right: parent.right + top: parent.top + } + height: parent.radius + color: base.topElement ? "transparent" : parent.color + } + + Rectangle + { + id: bottomSquare + anchors + { + left: parent.left + right: parent.right + bottom: parent.bottom + } + height: parent.radius + color: base.bottomElement ? "transparent" : parent.color + } + + Rectangle + { + id: leftSquare + anchors + { + left: parent.left + top: parent.top + bottom: parent.bottom + } + width: parent.radius + color: parent.color + } + } + + contentItem: Item + { + Loader + { + id: contentItemLoader + anchors.centerIn: parent + width: UM.Theme.getSize("button_icon").width + height: UM.Theme.getSize("button_icon").height + } + } +} diff --git a/resources/qml/qmldir b/resources/qml/qmldir index 43ae4411af..2475f398f8 100644 --- a/resources/qml/qmldir +++ b/resources/qml/qmldir @@ -12,4 +12,5 @@ IconLabel 1.0 IconLabel.qml OutputDevicesActionButton 1.0 OutputDevicesActionButton.qml ExpandableComponent 1.0 ExpandableComponent.qml PrinterTypeLabel 1.0 PrinterTypeLabel.qml -ViewsSelector 1.0 ViewsSelector.qml \ No newline at end of file +ViewsSelector 1.0 ViewsSelector.qml +ToolbarButton 1.0 ToolbarButton.qml \ No newline at end of file From 14b460a626bfbb58b0bc8eff6621f46e8d400ecb Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 27 Nov 2018 17:25:10 +0100 Subject: [PATCH 0494/1240] Remove unused styles --- resources/qml/Toolbar.qml | 5 +- resources/qml/ToolbarButton.qml | 3 +- resources/themes/cura-light/styles.qml | 117 ------------------------- 3 files changed, 3 insertions(+), 122 deletions(-) diff --git a/resources/qml/Toolbar.qml b/resources/qml/Toolbar.qml index 3a4e7704c0..0207c8ec49 100644 --- a/resources/qml/Toolbar.qml +++ b/resources/qml/Toolbar.qml @@ -2,9 +2,7 @@ // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.2 -import QtQuick.Controls 1.1 -import QtQuick.Controls.Styles 1.1 -import QtQuick.Layouts 1.1 +import QtQuick.Controls 2.3 import UM 1.2 as UM import Cura 1.0 as Cura @@ -68,7 +66,6 @@ Item toolItem: UM.RecolorImage { - opacity: parent.enabled ? 1.0 : 0.2 source: (UM.Theme.getIcon(model.icon) != "") ? UM.Theme.getIcon(model.icon) : "file:///" + model.location + "/" + model.icon color: UM.Theme.getColor("toolbar_button_text") diff --git a/resources/qml/ToolbarButton.qml b/resources/qml/ToolbarButton.qml index 7241b489b6..b5e5aab475 100644 --- a/resources/qml/ToolbarButton.qml +++ b/resources/qml/ToolbarButton.qml @@ -29,7 +29,7 @@ Button } else if (base.checked) { - return "red" //UM.Theme.getColor("toolbar_button_active") + return UM.Theme.getColor("toolbar_button_active") } else if(base.hovered) { @@ -81,6 +81,7 @@ Button contentItem: Item { + opacity: parent.enabled ? 1.0 : 0.2 Loader { id: contentItemLoader diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index f00aab44c0..e040d91df9 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -171,123 +171,6 @@ QtObject } } - property Component toolbar_button: Component - { - ButtonStyle - { - background: Rectangle - { - implicitWidth: Theme.getSize("button").width - implicitHeight: Theme.getSize("button").height - color: - { - if (control.checked && control.hovered) - { - return Theme.getColor("toolbar_button_active_hover") - } - else if (control.checked) - { - return Theme.getColor("toolbar_button_active") - } - else if(control.hovered) - { - return Theme.getColor("toolbar_button_hover") - } - return Theme.getColor("toolbar_background") - } - radius: UM.Theme.getSize("default_radius").width - - Rectangle - { - id: topSquare - anchors - { - left: parent.left - right: parent.right - top: parent.top - } - height: parent.radius - color: control.isFirstElement ? "transparent" : parent.color - } - - Rectangle - { - id: bottomSquare - anchors - { - left: parent.left - right: parent.right - bottom: parent.bottom - } - height: parent.radius - color: control.isLastElement ? "transparent" : parent.color - } - - Rectangle - { - id: leftSquare - anchors - { - left: parent.left - top: parent.top - bottom: parent.bottom - } - width: parent.radius - color: parent.color - } - - // This is the tooltip - UM.PointingRectangle - { - id: button_tooltip - - anchors.left: parent.right - anchors.leftMargin: Theme.getSize("button_tooltip_arrow").width * 2 - anchors.verticalCenter: parent.verticalCenter - - target: Qt.point(parent.x, y + Math.round(height/2)) - arrowSize: Theme.getSize("button_tooltip_arrow").width - color: Theme.getColor("button_tooltip") - opacity: control.hovered ? 1.0 : 0.0; - visible: control.text != "" - - width: control.hovered ? button_tip.width + Theme.getSize("button_tooltip").width : 0 - height: Theme.getSize("button_tooltip").height - - Behavior on width { NumberAnimation { duration: 100; } } - Behavior on opacity { NumberAnimation { duration: 100; } } - - Label - { - id: button_tip - - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter; - - text: control.text; - font: Theme.getFont("button_tooltip"); - color: Theme.getColor("tooltip_text"); - } - } - } - - label: Item - { - UM.RecolorImage - { - anchors.centerIn: parent; - opacity: !control.enabled ? 0.2 : 1.0 - source: control.iconSource; - width: Theme.getSize("button_icon").width; - height: Theme.getSize("button_icon").height; - color: Theme.getColor("toolbar_button_text"); - - sourceSize: Theme.getSize("button_icon") - } - } - } - } - property Component tool_button: Component { ButtonStyle From d8c430abf61c2537720270603796b368f00642d2 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 27 Nov 2018 17:54:53 +0100 Subject: [PATCH 0495/1240] Fix typing --- cura/CuraActions.py | 14 ++++++++------ cura/PrintInformation.py | 7 +++---- .../PostProcessingPlugin/PostProcessingPlugin.py | 4 ++-- plugins/USBPrinting/AutoDetectBaudJob.py | 11 ++++++----- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/cura/CuraActions.py b/cura/CuraActions.py index 93a18318df..49f7e740a9 100644 --- a/cura/CuraActions.py +++ b/cura/CuraActions.py @@ -3,7 +3,7 @@ from PyQt5.QtCore import QObject, QUrl from PyQt5.QtGui import QDesktopServices -from typing import List, TYPE_CHECKING +from typing import List, TYPE_CHECKING, cast from UM.Event import CallFunctionEvent from UM.FlameProfiler import pyqtSlot @@ -61,8 +61,10 @@ class CuraActions(QObject): operation = GroupedOperation() for node in Selection.getAllSelectedObjects(): current_node = node - while current_node.getParent() and current_node.getParent().callDecoration("isGroup"): - current_node = current_node.getParent() + parent_node = current_node.getParent() + while parent_node and parent_node.callDecoration("isGroup"): + current_node = parent_node + parent_node = current_node.getParent() # This was formerly done with SetTransformOperation but because of # unpredictable matrix deconstruction it was possible that mirrors @@ -150,13 +152,13 @@ class CuraActions(QObject): root = cura.CuraApplication.CuraApplication.getInstance().getController().getScene().getRoot() - nodes_to_change = [] + nodes_to_change = [] # type: List[SceneNode] for node in Selection.getAllSelectedObjects(): parent_node = node # Find the parent node to change instead while parent_node.getParent() != root: - parent_node = parent_node.getParent() + parent_node = cast(SceneNode, parent_node.getParent()) - for single_node in BreadthFirstIterator(parent_node): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. + for single_node in BreadthFirstIterator(parent_node): # type: ignore #Ignore type error because iter() should get called automatically by Python syntax. nodes_to_change.append(single_node) if not nodes_to_change: diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py index e11f70a54c..22c3eb1734 100644 --- a/cura/PrintInformation.py +++ b/cura/PrintInformation.py @@ -14,8 +14,7 @@ from UM.Logger import Logger from UM.Qt.Duration import Duration from UM.Scene.SceneNode import SceneNode from UM.i18n import i18nCatalog -from UM.MimeTypeDatabase import MimeTypeDatabase - +from UM.MimeTypeDatabase import MimeTypeDatabase, MimeTypeNotFoundError from typing import TYPE_CHECKING @@ -361,7 +360,7 @@ class PrintInformation(QObject): try: mime_type = MimeTypeDatabase.getMimeTypeForFile(name) data = mime_type.stripExtension(name) - except: + except MimeTypeNotFoundError: Logger.log("w", "Unsupported Mime Type Database file extension %s", name) if data is not None and check_name is not None: @@ -416,7 +415,7 @@ class PrintInformation(QObject): return ''.join(char for char in unicodedata.normalize('NFD', to_strip) if unicodedata.category(char) != 'Mn') @pyqtSlot(result = "QVariantMap") - def getFeaturePrintTimes(self): + def getFeaturePrintTimes(self) -> Dict[str, Duration]: result = {} if self._active_build_plate not in self._print_times_per_feature: self._initPrintTimesPerFeature(self._active_build_plate) diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.py b/plugins/PostProcessingPlugin/PostProcessingPlugin.py index 11ee610bec..78f9cc0516 100644 --- a/plugins/PostProcessingPlugin/PostProcessingPlugin.py +++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.py @@ -55,14 +55,14 @@ class PostProcessingPlugin(QObject, Extension): def selectedScriptDefinitionId(self) -> Optional[str]: try: return self._script_list[self._selected_script_index].getDefinitionId() - except: + except IndexError: return "" @pyqtProperty(str, notify=selectedIndexChanged) def selectedScriptStackId(self) -> Optional[str]: try: return self._script_list[self._selected_script_index].getStackId() - except: + except IndexError: return "" ## Execute all post-processing scripts on the gcode. diff --git a/plugins/USBPrinting/AutoDetectBaudJob.py b/plugins/USBPrinting/AutoDetectBaudJob.py index 8b37c4b29d..6f1af6727a 100644 --- a/plugins/USBPrinting/AutoDetectBaudJob.py +++ b/plugins/USBPrinting/AutoDetectBaudJob.py @@ -3,6 +3,7 @@ from UM.Job import Job from UM.Logger import Logger +from plugins.USBPrinting.avr_isp import ispBase from .avr_isp.stk500v2 import Stk500v2 @@ -14,12 +15,12 @@ from serial import Serial, SerialException # It tries a pre-set list of baud rates. All these baud rates are validated by requesting the temperature a few times # and checking if the results make sense. If getResult() is not None, it was able to find a correct baud rate. class AutoDetectBaudJob(Job): - def __init__(self, serial_port): + def __init__(self, serial_port: int) -> None: super().__init__() self._serial_port = serial_port self._all_baud_rates = [115200, 250000, 230400, 57600, 38400, 19200, 9600] - def run(self): + def run(self) -> None: Logger.log("d", "Auto detect baud rate started.") wait_response_timeouts = [3, 15, 30] wait_bootloader_times = [1.5, 5, 15] @@ -32,7 +33,7 @@ class AutoDetectBaudJob(Job): try: programmer.connect(self._serial_port) serial = programmer.leaveISP() - except: + except ispBase.IspError: programmer.close() for retry in range(tries): @@ -58,7 +59,7 @@ class AutoDetectBaudJob(Job): # We already have a serial connection, just change the baud rate. try: serial.baudrate = baud_rate - except: + except ValueError: continue sleep(wait_bootloader) # Ensure that we are not talking to the boot loader. 1.5 seconds seems to be the magic number successful_responses = 0 @@ -81,5 +82,5 @@ class AutoDetectBaudJob(Job): return serial.write(b"M105\n") - sleep(15) # Give the printer some time to init and try again. + sleep(15) # Give the printer some time to init and try again. self.setResult(None) # Unable to detect the correct baudrate. From c1c5eb221913dd7ce7538ef9d4120f7b69866729 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 28 Nov 2018 09:44:37 +0100 Subject: [PATCH 0496/1240] Rename the properties to quickly identify that they are a boolean Contributes to CURA-5984. --- resources/qml/Toolbar.qml | 8 ++++---- resources/qml/ToolbarButton.qml | 12 ++++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/resources/qml/Toolbar.qml b/resources/qml/Toolbar.qml index 0207c8ec49..d16f949014 100644 --- a/resources/qml/Toolbar.qml +++ b/resources/qml/Toolbar.qml @@ -61,8 +61,8 @@ Item checked: model.active enabled: model.enabled && UM.Selection.hasSelection && UM.Controller.toolsEnabled - topElement: toolsModel.getItem(0).id == model.id - bottomElement: toolsModel.getItem(toolsModel.rowCount() - 1).id == model.id + isTopElement: toolsModel.getItem(0).id == model.id + isBottomElement: toolsModel.getItem(toolsModel.rowCount() - 1).id == model.id toolItem: UM.RecolorImage { @@ -137,8 +137,8 @@ Item delegate: ExtruderButton { extruder: model - topElement: extrudersModel.getItem(0).id == model.id - bottomElement: extrudersModel.getItem(extrudersModel.rowCount() - 1).id == model.id + isTopElement: extrudersModel.getItem(0).id == model.id + isBottomElement: extrudersModel.getItem(extrudersModel.rowCount() - 1).id == model.id } } } diff --git a/resources/qml/ToolbarButton.qml b/resources/qml/ToolbarButton.qml index b5e5aab475..9e81939ba2 100644 --- a/resources/qml/ToolbarButton.qml +++ b/resources/qml/ToolbarButton.qml @@ -12,8 +12,12 @@ Button id: base property alias toolItem: contentItemLoader.sourceComponent - property bool topElement: false - property bool bottomElement: false + + // These two properties indicate whether the toolbar button is at the top of the toolbar column or at the bottom. + // If it is somewhere in the middle, then both has to be false. If there is only one element in the column, then + // both properties have to be set to true. This is used to create a rounded corner. + property bool isTopElement: false + property bool isBottomElement: false hoverEnabled: true @@ -49,7 +53,7 @@ Button top: parent.top } height: parent.radius - color: base.topElement ? "transparent" : parent.color + color: base.isTopElement ? "transparent" : parent.color } Rectangle @@ -62,7 +66,7 @@ Button bottom: parent.bottom } height: parent.radius - color: base.bottomElement ? "transparent" : parent.color + color: base.isBottomElement ? "transparent" : parent.color } Rectangle From bfebb33123f34894887719f114c942485a48b540 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 28 Nov 2018 10:28:16 +0100 Subject: [PATCH 0497/1240] Code style CURA-5984 Co-Authored-By: diegopradogesto --- resources/qml/Toolbar.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Toolbar.qml b/resources/qml/Toolbar.qml index d16f949014..07522dd535 100644 --- a/resources/qml/Toolbar.qml +++ b/resources/qml/Toolbar.qml @@ -66,7 +66,7 @@ Item toolItem: UM.RecolorImage { - source: (UM.Theme.getIcon(model.icon) != "") ? UM.Theme.getIcon(model.icon) : "file:///" + model.location + "/" + model.icon + source: UM.Theme.getIcon(model.icon) != "" ? UM.Theme.getIcon(model.icon) : "file:///" + model.location + "/" + model.icon color: UM.Theme.getColor("toolbar_button_text") sourceSize: UM.Theme.getSize("button_icon") From 454b47e3a0e7c9d50a5e8ebad5933182872f0782 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 28 Nov 2018 10:29:35 +0100 Subject: [PATCH 0498/1240] Change visibility behavior of the rectangle CURA-5984 Co-Authored-By: diegopradogesto --- resources/qml/ToolbarButton.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/qml/ToolbarButton.qml b/resources/qml/ToolbarButton.qml index 9e81939ba2..90e9160d3d 100644 --- a/resources/qml/ToolbarButton.qml +++ b/resources/qml/ToolbarButton.qml @@ -66,7 +66,8 @@ Button bottom: parent.bottom } height: parent.radius - color: base.isBottomElement ? "transparent" : parent.color + color: parent.color + visible: base.isBottomElement } Rectangle From d0da70a7eea8c6991afebc5488e7a2c39fae9ab1 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 28 Nov 2018 10:29:53 +0100 Subject: [PATCH 0499/1240] Change visibility of the rectangle CURA-5984 Co-Authored-By: diegopradogesto --- resources/qml/ToolbarButton.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/qml/ToolbarButton.qml b/resources/qml/ToolbarButton.qml index 90e9160d3d..157c6a34ac 100644 --- a/resources/qml/ToolbarButton.qml +++ b/resources/qml/ToolbarButton.qml @@ -53,7 +53,8 @@ Button top: parent.top } height: parent.radius - color: base.isTopElement ? "transparent" : parent.color + color: parent.color + visible: base.isTopElement } Rectangle From 709750c9e2c4fac776651039ac56055714056276 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 28 Nov 2018 10:39:32 +0100 Subject: [PATCH 0500/1240] Add global quality to the Anycubic i3 Mega Fixes #4876. --- .../quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg | 1 + .../quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg | 1 + .../quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg | 1 + 3 files changed, 3 insertions(+) diff --git a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg index bb47f68574..e94b9f01d1 100644 --- a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg +++ b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg @@ -8,6 +8,7 @@ setting_version = 5 type = quality quality_type = draft weight = 0 +global_quality = True [values] acceleration_enabled = True diff --git a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg index a3ae98deba..c8c4bf9a81 100644 --- a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg +++ b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg @@ -8,6 +8,7 @@ setting_version = 5 type = quality quality_type = high weight = 2 +global_quality = True [values] acceleration_enabled = True diff --git a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg index 13846b9702..399c3ebc55 100644 --- a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg +++ b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg @@ -8,6 +8,7 @@ setting_version = 5 type = quality quality_type = normal weight = 1 +global_quality = True [values] acceleration_enabled = True From 255a7fa1fbf97ea237c1bda9259480732b4e3398 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Wed, 28 Nov 2018 10:45:46 +0100 Subject: [PATCH 0501/1240] Fix binding loop Contributes to CL-1150 --- .../UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml index d1d9bec351..8507c8d2c8 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml @@ -180,12 +180,12 @@ Item verticalCenter: parent.verticalCenter } width: printerImage.width - height: childrenRect.height + height: 60 * screenScaleFactor // TODO: Theme! MonitorPrintJobPreview { anchors.centerIn: parent printJob: base.printer.activePrintJob - size: 60 * screenScaleFactor // TODO: Theme! + size: parent.height } } From bfa2ff5f5ed84a06460f29a79f1b5cd0e1979187 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 28 Nov 2018 10:46:07 +0100 Subject: [PATCH 0502/1240] Invert visibility of bottom & topsquare It got derped. --- resources/qml/ToolbarButton.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/ToolbarButton.qml b/resources/qml/ToolbarButton.qml index 157c6a34ac..adff73fb7c 100644 --- a/resources/qml/ToolbarButton.qml +++ b/resources/qml/ToolbarButton.qml @@ -54,7 +54,7 @@ Button } height: parent.radius color: parent.color - visible: base.isTopElement + visible: !base.isTopElement } Rectangle @@ -68,7 +68,7 @@ Button } height: parent.radius color: parent.color - visible: base.isBottomElement + visible: !base.isBottomElement } Rectangle From d54a1cb41b815831e8b745cc0c03e590d08abc6a Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Wed, 28 Nov 2018 10:46:12 +0100 Subject: [PATCH 0503/1240] Handle null print job more elegantly Contributes to CL-1150 --- .../qml/MonitorPrintJobProgressBar.qml | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml index 6e16d026a1..f70e1175a1 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml @@ -97,16 +97,19 @@ Item id: progressItem; color: { - var state = printJob.state - var inactiveStates = [ - "pausing", - "paused", - "resuming", - "wait_cleanup" - ] - if (inactiveStates.indexOf(state) > -1 && remainingTime > 0) + if (printJob) { - return UM.Theme.getColor("monitor_progress_fill_inactive") + var state = printJob.state + var inactiveStates = [ + "pausing", + "paused", + "resuming", + "wait_cleanup" + ] + if (inactiveStates.indexOf(state) > -1 && remainingTime > 0) + { + return UM.Theme.getColor("monitor_progress_fill_inactive") + } } return "#0a0850" // TODO: Theme! } From 370fa0fd14a9893a09da9a72827221d59a88b8f4 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Wed, 28 Nov 2018 10:46:22 +0100 Subject: [PATCH 0504/1240] Remove old sidebar Contributes to CL-1150 --- plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index 8314b0f089..3b124faf66 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -65,7 +65,6 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): self._received_print_jobs = False # type: bool self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../resources/qml/ClusterMonitorItem.qml") - self._control_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../resources/qml/ClusterControlItem.qml") # See comments about this hack with the clusterPrintersChanged signal self.printersChanged.connect(self.clusterPrintersChanged) From 9029d10ed01dff02a28679935c6f15cb7a87869c Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Wed, 28 Nov 2018 10:47:34 +0100 Subject: [PATCH 0505/1240] Remove debug statement Contributes to CL-1150 --- .../UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml index 8507c8d2c8..975fe12244 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml @@ -56,11 +56,7 @@ Item width: 108 * screenScaleFactor // TODO: Theme! height: 108 * screenScaleFactor // TODO: Theme! fillMode: Image.PreserveAspectFit - source: - { - console.log(printer.type) - return "../png/"+printer.type+".png" - } + source: "../png/" + printer.type + ".png" mipmap: true } From 5ed2acadd09fa062553f0fc4fddd2a79610e1142 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 28 Nov 2018 11:09:34 +0100 Subject: [PATCH 0506/1240] Re-add wizard_progress size I was a bit over enthosiastic deleting it as it was still used for the UMO first run wizard --- resources/themes/cura-light/theme.json | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index d2358e36ff..dfad5cfd17 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -470,6 +470,7 @@ "modal_window_minimum": [60.0, 45], "license_window_minimum": [45, 45], + "wizard_progress": [10.0, 0.0], "message": [30.0, 5.0], "message_close": [1, 1], From d09d8a6ae34f75b3a7a3d6bc0a20add4671c7e9c Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 28 Nov 2018 11:18:36 +0100 Subject: [PATCH 0507/1240] Minor codestyle fixes CURA-5879 --- resources/qml/Menus/ProfileMenu.qml | 17 ++++++++--------- resources/themes/cura-light/theme.json | 2 +- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/resources/qml/Menus/ProfileMenu.qml b/resources/qml/Menus/ProfileMenu.qml index b503b7de85..fd46d2ef72 100644 --- a/resources/qml/Menus/ProfileMenu.qml +++ b/resources/qml/Menus/ProfileMenu.qml @@ -17,22 +17,21 @@ Menu MenuItem { - text: { - var full_text = (model.layer_height != "") ? model.name + " - " + model.layer_height + model.layer_height_unit : model.name; - full_text += model.is_experimental ? " - Experimental" : ""; - return full_text; + text: + { + var full_text = (model.layer_height != "") ? model.name + " - " + model.layer_height + model.layer_height_unit : model.name + full_text += model.is_experimental ? " - Experimental" : "" + return full_text } checkable: true checked: Cura.MachineManager.activeQualityOrQualityChangesName == model.name exclusiveGroup: group - onTriggered: { - Cura.MachineManager.setQualityGroup(model.quality_group) - } + onTriggered: Cura.MachineManager.setQualityGroup(model.quality_group) visible: model.available } - onObjectAdded: menu.insertItem(index, object); - onObjectRemoved: menu.removeItem(object); + onObjectAdded: menu.insertItem(index, object) + onObjectRemoved: menu.removeItem(object) } MenuSeparator diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 25c9a678c1..303383e6a4 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -480,7 +480,7 @@ "toolbox_footer_button": [8.0, 2.5], "toolbox_showcase_spacing": [1.0, 1.0], "toolbox_header_tab": [8.0, 4.0], - "toolbox_detail_header": [1.0, 14.0], + "toolbox_detail_header": [1.0, 14.0],https://github.com/Ultimaker/Cura/pull/4883 "toolbox_detail_tile": [1.0, 8.0], "toolbox_back_column": [6.0, 1.0], "toolbox_back_button": [4.0, 2.0], From d9c3faaae2e78e543cacfb19f8ba295b15540d38 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 28 Nov 2018 11:19:37 +0100 Subject: [PATCH 0508/1240] Codestyle CURA-5879 --- resources/qml/Settings/SettingView.qml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 5d916aed7c..5d79eef249 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -64,14 +64,18 @@ Item activeFocusOnPress: true menu: ProfileMenu { } - function generateActiveQualityText () { - var result = Cura.MachineManager.activeQualityOrQualityChangesName; - if (Cura.MachineManager.isActiveQualityExperimental) { - result += " (Experimental)"; + function generateActiveQualityText () + { + var result = Cura.MachineManager.activeQualityOrQualityChangesName + if (Cura.MachineManager.isActiveQualityExperimental) + { + result += " (Experimental)" } - if (Cura.MachineManager.isActiveQualitySupported) { - if (Cura.MachineManager.activeQualityLayerHeight > 0) { + if (Cura.MachineManager.isActiveQualitySupported) + { + if (Cura.MachineManager.activeQualityLayerHeight > 0) + { result += " " result += " - " result += Cura.MachineManager.activeQualityLayerHeight + "mm" From c0d1c35a2ba8c20d8988c5e07dfce8eb9cff23ae Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 28 Nov 2018 11:35:48 +0100 Subject: [PATCH 0509/1240] Removed accidental copy paste --- resources/themes/cura-light/theme.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 303383e6a4..25c9a678c1 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -480,7 +480,7 @@ "toolbox_footer_button": [8.0, 2.5], "toolbox_showcase_spacing": [1.0, 1.0], "toolbox_header_tab": [8.0, 4.0], - "toolbox_detail_header": [1.0, 14.0],https://github.com/Ultimaker/Cura/pull/4883 + "toolbox_detail_header": [1.0, 14.0], "toolbox_detail_tile": [1.0, 8.0], "toolbox_back_column": [6.0, 1.0], "toolbox_back_button": [4.0, 2.0], From 0baa4d20b3f426fef2e40123c10c2a779dc28775 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 28 Nov 2018 11:42:54 +0100 Subject: [PATCH 0510/1240] Gracefully handle the case where machine definition is no in the map --- cura/Machines/VariantManager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/Machines/VariantManager.py b/cura/Machines/VariantManager.py index f6feb70e09..eaaa9fc5f0 100644 --- a/cura/Machines/VariantManager.py +++ b/cura/Machines/VariantManager.py @@ -107,7 +107,7 @@ class VariantManager: break return variant_node - return self._machine_to_variant_dict_map[machine_definition_id].get(variant_type, {}).get(variant_name) + return self._machine_to_variant_dict_map.get(machine_definition_id, {}).get(variant_type, {}).get(variant_name) def getVariantNodes(self, machine: "GlobalStack", variant_type: "VariantType") -> Dict[str, ContainerNode]: machine_definition_id = machine.definition.getId() From 7d42fdf183656e39e1f5a90e8436d5bb6e8d198d Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 28 Nov 2018 11:52:30 +0100 Subject: [PATCH 0511/1240] Add missing typing --- cura/Scene/ConvexHullNode.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/cura/Scene/ConvexHullNode.py b/cura/Scene/ConvexHullNode.py index 4c79c7d5dc..886ed93ad3 100644 --- a/cura/Scene/ConvexHullNode.py +++ b/cura/Scene/ConvexHullNode.py @@ -1,7 +1,10 @@ # Copyright (c) 2015 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from typing import Optional from UM.Application import Application +from UM.Math.Polygon import Polygon +from UM.Qt.QtApplication import QtApplication from UM.Scene.SceneNode import SceneNode from UM.Resources import Resources from UM.Math.Color import Color @@ -16,7 +19,7 @@ class ConvexHullNode(SceneNode): # location an object uses on the buildplate. This area (or area's in case of one at a time printing) is # then displayed as a transparent shadow. If the adhesion type is set to raft, the area is extruded # to represent the raft as well. - def __init__(self, node, hull, thickness, parent = None): + def __init__(self, node: SceneNode, hull: Optional[Polygon], thickness: float, parent: Optional[SceneNode] = None) -> None: super().__init__(parent) self.setCalculateBoundingBox(False) @@ -25,7 +28,11 @@ class ConvexHullNode(SceneNode): # Color of the drawn convex hull if not Application.getInstance().getIsHeadLess(): - self._color = Color(*Application.getInstance().getTheme().getColor("convex_hull").getRgb()) + theme = QtApplication.getInstance().getTheme() + if theme: + self._color = Color(*theme.getColor("convex_hull").getRgb()) + else: + self._color = Color(0, 0, 0) else: self._color = Color(0, 0, 0) @@ -75,7 +82,7 @@ class ConvexHullNode(SceneNode): return True - def _onNodeDecoratorsChanged(self, node): + def _onNodeDecoratorsChanged(self, node: SceneNode) -> None: convex_hull_head = self._node.callDecoration("getConvexHullHead") if convex_hull_head: convex_hull_head_builder = MeshBuilder() From 14cee32ceccb85ae034c3f9cc5689822a249663c Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Wed, 28 Nov 2018 12:20:09 +0100 Subject: [PATCH 0512/1240] Improve monitor tab temporary scroll bar --- .../resources/qml/ClusterMonitorItem.qml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml b/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml index 6bbc338c17..adf5ea5e1c 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml @@ -52,12 +52,13 @@ Component id: printers anchors { - left: parent.left - right: parent.right + left: queue.left + right: queue.right top: parent.top topMargin: 48 * screenScaleFactor // TODO: Theme! } height: 264 * screenScaleFactor // TODO: Theme! + Row { spacing: 60 * screenScaleFactor // TODO: Theme! @@ -77,13 +78,13 @@ Component Item { id: queue + width: Math.min(834 * screenScaleFactor, maximumWidth) anchors { bottom: parent.bottom - left: parent.left - right: parent.right + horizontalCenter: parent.horizontalCenter top: printers.bottom - topMargin: 48 + topMargin: 48 * screenScaleFactor // TODO: Theme! } Label @@ -134,7 +135,6 @@ Component text: catalog.i18nc("@label link to connect manager", "Manage queue in Cura Connect") } } - MouseArea { @@ -217,7 +217,7 @@ Component } style: UM.Theme.styles.scrollview visible: OutputDevice.receivedPrintJobs - width: Math.min(834 * screenScaleFactor, maximumWidth) + width: parent.width ListView { @@ -244,5 +244,4 @@ Component visible: OutputDevice.activeCameraUrl != "" } } - } From 85b34d60053b5c2c3d2f6cf279d2ecc243c2929b Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 28 Nov 2018 13:07:39 +0100 Subject: [PATCH 0513/1240] Refactor the PrintSetupSelector The file was splitted in several other files to improve readability. There is a new folder called PrintSetupSelector where all those files will be. Contributes to CURA-5941. --- plugins/PrepareStage/PrepareMenu.qml | 1 - resources/qml/PrintSetupSelector.qml | 392 ------------------ .../CustomPrintSetup.qml} | 4 +- .../PrintSetupSelector/PrintSetupSelector.qml | 68 +++ .../PrintSetupSelectorContents.qml | 276 ++++++++++++ .../PrintSetupSelectorHeader.qml | 69 +++ .../RecommendedPrintSetup.qml} | 8 +- 7 files changed, 419 insertions(+), 399 deletions(-) delete mode 100644 resources/qml/PrintSetupSelector.qml rename resources/qml/{SidebarAdvanced.qml => PrintSetupSelector/CustomPrintSetup.qml} (68%) create mode 100644 resources/qml/PrintSetupSelector/PrintSetupSelector.qml create mode 100644 resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml create mode 100644 resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml rename resources/qml/{SidebarSimple.qml => PrintSetupSelector/RecommendedPrintSetup.qml} (99%) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index 10b4262f01..7c01b1f8b0 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -45,7 +45,6 @@ Item Cura.MachineSelector { id: machineSelection - z: openFileButton.z - 1 //Ensure that the tooltip of the open file button stays above the item row. headerCornerSide: Cura.RoundedRectangle.Direction.Left Layout.minimumWidth: UM.Theme.getSize("machine_selector_widget").width Layout.maximumWidth: UM.Theme.getSize("machine_selector_widget").width diff --git a/resources/qml/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector.qml deleted file mode 100644 index 253a13c0f0..0000000000 --- a/resources/qml/PrintSetupSelector.qml +++ /dev/null @@ -1,392 +0,0 @@ -// Copyright (c) 2018 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.7 -import QtQuick.Controls 2.0 -import QtQuick.Layouts 1.3 - -import UM 1.3 as UM -import Cura 1.0 as Cura -import "Menus" -import "Menus/ConfigurationMenu" - -Cura.ExpandableComponent -{ - id: base - - property int currentModeIndex: -1 - property bool hideSettings: PrintInformation.preSliced - - property string enabledText: catalog.i18nc("@label:Should be short", "On") - property string disabledText: catalog.i18nc("@label:Should be short", "Off") - - // This widget doesn't show tooltips by itself. Instead it emits signals so others can do something with it. - signal showTooltip(Item item, point location, string text) - signal hideTooltip() - - implicitWidth: 200 * screenScaleFactor - height: childrenRect.height - iconSource: UM.Theme.getIcon("pencil") - - popupPadding : 0 - popupSpacingY: UM.Theme.getSize("narrow_margin").width - - popupClosePolicy: Popup.CloseOnEscape - - onCurrentModeIndexChanged: UM.Preferences.setValue("cura/active_mode", currentModeIndex) - - Component.onCompleted: - { - popupItemWrapper.width = base.width - } - - UM.I18nCatalog - { - id: catalog - name: "cura" - } - - Timer - { - id: tooltipDelayTimer - interval: 500 - repeat: false - property var item - property string text - - onTriggered: base.showTooltip(base, {x: 0, y: item.y}, text) - } - - headerItem: RowLayout - { - anchors.fill: parent - - IconWithText - { - source: UM.Theme.getIcon("category_layer_height") - text: Cura.MachineManager.activeStack ? Cura.MachineManager.activeQualityOrQualityChangesName + " " + layerHeight.properties.value + "mm" : "" - - UM.SettingPropertyProvider - { - id: layerHeight - containerStack: Cura.MachineManager.activeStack - key: "layer_height" - watchedProperties: ["value"] - } - } - - IconWithText - { - source: UM.Theme.getIcon("category_infill") - text: Cura.MachineManager.activeStack ? parseInt(infillDensity.properties.value) + "%" : "0%" - - UM.SettingPropertyProvider - { - id: infillDensity - containerStack: Cura.MachineManager.activeStack - key: "infill_sparse_density" - watchedProperties: ["value"] - } - } - - IconWithText - { - source: UM.Theme.getIcon("category_support") - text: supportEnabled.properties.value == "True" ? enabledText : disabledText - - - UM.SettingPropertyProvider - { - id: supportEnabled - containerStack: Cura.MachineManager.activeMachine - key: "support_enable" - watchedProperties: ["value"] - } - } - - IconWithText - { - source: UM.Theme.getIcon("category_adhesion") - text: platformAdhesionType.properties.value != "skirt" && platformAdhesionType.properties.value != "none" ? enabledText : disabledText - - UM.SettingPropertyProvider - { - id: platformAdhesionType - containerStack: Cura.MachineManager.activeMachine - key: "adhesion_type" - watchedProperties: [ "value"] - } - } - } - - Cura.ExtrudersModel - { - id: extrudersModel - } - - popupItem: Rectangle - { - property var total_height: popupItemHeader.height + popupItemContent.height + footerControll.height + UM.Theme.getSize("narrow_margin").height * 2 - id: popupItemWrapper - height: total_height - - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("lining") - - Item - { - id: popupItemHeader - height: 36 - - anchors - { - top: parent.top - right: parent.right - left: parent.left - } - - Label - { - id: popupItemHeaderText - text: catalog.i18nc("@label", "Print settings"); - font: UM.Theme.getFont("default") - renderType: Text.NativeRendering - verticalAlignment: Text.AlignVCenter - color: UM.Theme.getColor("text") - height: parent.height - - anchors - { - topMargin: UM.Theme.getSize("sidebar_margin").height - left: parent.left - leftMargin: UM.Theme.getSize("narrow_margin").height - } - } - - Rectangle - { - width: parent.width - height: UM.Theme.getSize("default_lining").height - anchors.top: popupItemHeaderText.bottom - color: UM.Theme.getColor("action_button_border") - - - } - - Button - { - id: closeButton; - width: UM.Theme.getSize("message_close").width; - height: UM.Theme.getSize("message_close").height; - - anchors - { - right: parent.right; - rightMargin: UM.Theme.getSize("default_margin").width; - top: parent.top; - topMargin: 10 - } - - UM.RecolorImage - { - anchors.fill: parent; - sourceSize.width: width - sourceSize.height: width - color: UM.Theme.getColor("message_text") - source: UM.Theme.getIcon("cross1") - } - - onClicked: base.togglePopup() // Will hide the popup item - - background: Rectangle - { - color: UM.Theme.getColor("message_background") - } - } - } - - Rectangle - { - id: popupItemContent - width: parent.width - height: tabBar.height + sidebarContents.height - anchors - { - top: popupItemHeader.bottom - topMargin: UM.Theme.getSize("narrow_margin").height - right: parent.right - left: parent.left - leftMargin: UM.Theme.getSize("default_margin").width - rightMargin: UM.Theme.getSize("default_margin").width - } - - UM.TabRow - { - id: tabBar - anchors.topMargin: UM.Theme.getSize("default_margin").height - onCurrentIndexChanged: Cura.ExtruderManager.setActiveExtruderIndex(currentIndex) - z: 1 - Repeater - { - model: extrudersModel - delegate: UM.TabRowButton - { - contentItem: Rectangle - { - z: 2 - Cura.ExtruderIcon - { - anchors.horizontalCenter: parent.horizontalCenter - materialColor: model.color - extruderEnabled: model.enabled - width: parent.height - height: parent.height - } - } - - background: Rectangle - { - - width: parent.width - z: 1 - border.width: UM.Theme.getSize("default_lining").width * 2 - border.color: UM.Theme.getColor("action_button_border") - - visible: - { - return index == tabBar.currentIndex - } - - // overlap bottom border - Rectangle - { - width: parent.width - UM.Theme.getSize("default_lining").width * 4 - height: UM.Theme.getSize("default_lining").width * 4 - anchors.bottom: parent.bottom - anchors.bottomMargin: - (UM.Theme.getSize("default_lining").width * 2) - anchors.leftMargin: UM.Theme.getSize("default_lining").width * 2 - anchors.left: parent.left - - } - } - } - } - } - - Rectangle - { - id: sidebarContents - anchors.top: tabBar.bottom - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.right: parent.right - height: UM.Theme.getSize("print_setup_widget").height - - border.width: UM.Theme.getSize("default_lining").width * 2 - border.color: UM.Theme.getColor("action_button_border") - - SidebarSimple - { - anchors.topMargin: UM.Theme.getSize("print_setup_content_top_margin").height - anchors.fill: parent - visible: currentModeIndex != 1 - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() - } - - SidebarAdvanced - { - anchors.topMargin: UM.Theme.getSize("print_setup_content_top_margin").height - anchors.bottomMargin: 2 //don't overlap bottom border - anchors.fill: parent - visible: currentModeIndex == 1 - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() - } - } - } - - Item - { - id: footerControll - anchors.top: popupItemContent.bottom - anchors.topMargin: UM.Theme.getSize("narrow_margin").height * 2 - width: parent.width - height: settingControlButton.height + UM.Theme.getSize("default_lining").height * 4 - Rectangle - { - width: parent.width - height: UM.Theme.getSize("default_lining").height - color: UM.Theme.getColor("action_button_border") - } - - Cura.ActionButton - { - id: settingControlButton - leftPadding: UM.Theme.getSize("default_margin").width - rightPadding: UM.Theme.getSize("default_margin").width - height: UM.Theme.getSize("action_panel_button").height - text: catalog.i18nc("@button", "Custom") - color: UM.Theme.getColor("secondary") - hoverColor: UM.Theme.getColor("secondary") - textColor: UM.Theme.getColor("primary") - textHoverColor: UM.Theme.getColor("text") - iconSourceRight: UM.Theme.getIcon("arrow_right") - width: UM.Theme.getSize("print_setup_action_button").width - fixedWidthMode: true - visible: currentModeIndex == 0 - anchors - { - top: parent.top - topMargin: UM.Theme.getSize("narrow_margin").height * 2 - bottomMargin: UM.Theme.getSize("narrow_margin").height * 2 - right: parent.right - rightMargin: UM.Theme.getSize("narrow_margin").height - } - - onClicked: currentModeIndex = 1 - } - - Cura.ActionButton - { - height: UM.Theme.getSize("action_panel_button").height - text: catalog.i18nc("@button", "Recommended") - color: UM.Theme.getColor("secondary") - hoverColor: UM.Theme.getColor("secondary") - textColor: UM.Theme.getColor("primary") - textHoverColor: UM.Theme.getColor("text") - iconSource: UM.Theme.getIcon("arrow_left") - width: UM.Theme.getSize("print_setup_action_button").width - fixedWidthMode: true - visible: currentModeIndex == 1 - anchors - { - top: parent.top - topMargin: UM.Theme.getSize("narrow_margin").height * 2 - bottomMargin: UM.Theme.getSize("narrow_margin").height * 2 - left: parent.left - leftMargin: UM.Theme.getSize("narrow_margin").height - } - - MouseArea { - anchors.fill: parent - onClicked: currentModeIndex = 0 - } - } - } - - Component.onCompleted: - { - var index = Math.round(UM.Preferences.getValue("cura/active_mode")) - - if(index != null && !isNaN(index)) - { - currentModeIndex = index - } - else - { - currentModeIndex = 0 - } - } - } -} \ No newline at end of file diff --git a/resources/qml/SidebarAdvanced.qml b/resources/qml/PrintSetupSelector/CustomPrintSetup.qml similarity index 68% rename from resources/qml/SidebarAdvanced.qml rename to resources/qml/PrintSetupSelector/CustomPrintSetup.qml index ff5f545c80..f58695b48f 100644 --- a/resources/qml/SidebarAdvanced.qml +++ b/resources/qml/PrintSetupSelector/CustomPrintSetup.qml @@ -1,10 +1,10 @@ -// Copyright (c) 2015 Ultimaker B.V. +// Copyright (c) 2018 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.7 import QtQuick.Controls 2.0 -import "Settings" +import "../Settings" SettingView { } diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector/PrintSetupSelector.qml new file mode 100644 index 0000000000..c69dd56520 --- /dev/null +++ b/resources/qml/PrintSetupSelector/PrintSetupSelector.qml @@ -0,0 +1,68 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 2.0 + +import UM 1.3 as UM +import Cura 1.0 as Cura + +Cura.ExpandableComponent +{ + id: base + + property int currentModeIndex: -1 + property bool hideSettings: PrintInformation.preSliced + + property string enabledText: catalog.i18nc("@label:Should be short", "On") + property string disabledText: catalog.i18nc("@label:Should be short", "Off") + + // This widget doesn't show tooltips by itself. Instead it emits signals so others can do something with it. + signal showTooltip(Item item, point location, string text) + signal hideTooltip() + + iconSource: UM.Theme.getIcon("pencil") + popupPadding: UM.Theme.getSize("default_lining").width + popupSpacingY: UM.Theme.getSize("narrow_margin").width + + popupClosePolicy: Popup.CloseOnEscape + + onCurrentModeIndexChanged: UM.Preferences.setValue("cura/active_mode", currentModeIndex) + + Component.onCompleted: + { + popupItemWrapper.width = base.width + } + + UM.I18nCatalog + { + id: catalog + name: "cura" + } + + Timer + { + id: tooltipDelayTimer + interval: 500 + repeat: false + property var item + property string text + + onTriggered: base.showTooltip(base, {x: 0, y: item.y}, text) + } + + headerItem: PrintSetupSelectorHeader + { + anchors.fill: parent + } + + Cura.ExtrudersModel + { + id: extrudersModel + } + + popupItem: PrintSetupSelectorContents + { + + } +} \ No newline at end of file diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml new file mode 100644 index 0000000000..e7b3b94b11 --- /dev/null +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml @@ -0,0 +1,276 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 + +import UM 1.3 as UM +import Cura 1.0 as Cura + +Rectangle +{ + property var total_height: popupItemHeader.height + popupItemContent.height + footerControll.height + UM.Theme.getSize("narrow_margin").height * 2 + id: popupItemWrapper + height: total_height + + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") + + Item + { + id: popupItemHeader + height: 36 + + anchors + { + top: parent.top + right: parent.right + left: parent.left + } + + Label + { + id: popupItemHeaderText + text: catalog.i18nc("@label", "Print settings"); + font: UM.Theme.getFont("default") + renderType: Text.NativeRendering + verticalAlignment: Text.AlignVCenter + color: UM.Theme.getColor("text") + height: parent.height + + anchors + { + topMargin: UM.Theme.getSize("sidebar_margin").height + left: parent.left + leftMargin: UM.Theme.getSize("narrow_margin").height + } + } + + Rectangle + { + width: parent.width + height: UM.Theme.getSize("default_lining").height + anchors.top: popupItemHeaderText.bottom + color: UM.Theme.getColor("action_button_border") + + + } + + Button + { + id: closeButton; + width: UM.Theme.getSize("message_close").width; + height: UM.Theme.getSize("message_close").height; + + anchors + { + right: parent.right; + rightMargin: UM.Theme.getSize("default_margin").width; + top: parent.top; + topMargin: 10 + } + + UM.RecolorImage + { + anchors.fill: parent; + sourceSize.width: width + sourceSize.height: width + color: UM.Theme.getColor("message_text") + source: UM.Theme.getIcon("cross1") + } + + onClicked: base.togglePopup() // Will hide the popup item + + background: Rectangle + { + color: UM.Theme.getColor("message_background") + } + } + } + + Rectangle + { + id: popupItemContent + width: parent.width + height: tabBar.height + sidebarContents.height + + anchors + { + top: popupItemHeader.bottom + topMargin: UM.Theme.getSize("narrow_margin").height + right: parent.right + left: parent.left + leftMargin: UM.Theme.getSize("default_margin").width + rightMargin: UM.Theme.getSize("default_margin").width + } + + UM.TabRow + { + id: tabBar + anchors.topMargin: UM.Theme.getSize("default_margin").height + onCurrentIndexChanged: Cura.ExtruderManager.setActiveExtruderIndex(currentIndex) + z: 1 + Repeater + { + model: extrudersModel + delegate: UM.TabRowButton + { + contentItem: Rectangle + { + z: 2 + Cura.ExtruderIcon + { + anchors.horizontalCenter: parent.horizontalCenter + materialColor: model.color + extruderEnabled: model.enabled + width: parent.height + height: parent.height + } + } + + background: Rectangle + { + + width: parent.width + z: 1 + border.width: UM.Theme.getSize("default_lining").width * 2 + border.color: UM.Theme.getColor("action_button_border") + + visible: + { + return index == tabBar.currentIndex + } + + // overlap bottom border + Rectangle + { + width: parent.width - UM.Theme.getSize("default_lining").width * 4 + height: UM.Theme.getSize("default_lining").width * 4 + anchors.bottom: parent.bottom + anchors.bottomMargin: - (UM.Theme.getSize("default_lining").width * 2) + anchors.leftMargin: UM.Theme.getSize("default_lining").width * 2 + anchors.left: parent.left + + } + } + } + } + } + + Rectangle + { + id: sidebarContents + anchors.top: tabBar.bottom + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + height: UM.Theme.getSize("print_setup_widget").height + + border.width: UM.Theme.getSize("default_lining").width * 2 + border.color: UM.Theme.getColor("action_button_border") + + RecommendedPrintSetup + { + anchors.topMargin: UM.Theme.getSize("print_setup_content_top_margin").height + anchors.fill: parent + visible: currentModeIndex != 1 + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() + } + + CustomPrintSetup + { + anchors.topMargin: UM.Theme.getSize("print_setup_content_top_margin").height + anchors.bottomMargin: 2 //don't overlap bottom border + anchors.fill: parent + visible: currentModeIndex == 1 + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() + } + } + } + + Item + { + id: footerControll + anchors.top: popupItemContent.bottom + anchors.topMargin: UM.Theme.getSize("narrow_margin").height * 2 + width: parent.width + height: settingControlButton.height + UM.Theme.getSize("default_lining").height * 4 + Rectangle + { + width: parent.width + height: UM.Theme.getSize("default_lining").height + color: UM.Theme.getColor("action_button_border") + } + + Cura.ActionButton + { + id: settingControlButton + leftPadding: UM.Theme.getSize("default_margin").width + rightPadding: UM.Theme.getSize("default_margin").width + height: UM.Theme.getSize("action_panel_button").height + text: catalog.i18nc("@button", "Custom") + color: UM.Theme.getColor("secondary") + hoverColor: UM.Theme.getColor("secondary") + textColor: UM.Theme.getColor("primary") + textHoverColor: UM.Theme.getColor("text") + iconSourceRight: UM.Theme.getIcon("arrow_right") + width: UM.Theme.getSize("print_setup_action_button").width + fixedWidthMode: true + visible: currentModeIndex == 0 + anchors + { + top: parent.top + topMargin: UM.Theme.getSize("narrow_margin").height * 2 + bottomMargin: UM.Theme.getSize("narrow_margin").height * 2 + right: parent.right + rightMargin: UM.Theme.getSize("narrow_margin").height + } + + onClicked: currentModeIndex = 1 + } + + Cura.ActionButton + { + height: UM.Theme.getSize("action_panel_button").height + text: catalog.i18nc("@button", "Recommended") + color: UM.Theme.getColor("secondary") + hoverColor: UM.Theme.getColor("secondary") + textColor: UM.Theme.getColor("primary") + textHoverColor: UM.Theme.getColor("text") + iconSource: UM.Theme.getIcon("arrow_left") + width: UM.Theme.getSize("print_setup_action_button").width + fixedWidthMode: true + visible: currentModeIndex == 1 + anchors + { + top: parent.top + topMargin: UM.Theme.getSize("narrow_margin").height * 2 + bottomMargin: UM.Theme.getSize("narrow_margin").height * 2 + left: parent.left + leftMargin: UM.Theme.getSize("narrow_margin").height + } + + MouseArea { + anchors.fill: parent + onClicked: currentModeIndex = 0 + } + } + } + + Component.onCompleted: + { + var index = Math.round(UM.Preferences.getValue("cura/active_mode")) + + if(index != null && !isNaN(index)) + { + currentModeIndex = index + } + else + { + currentModeIndex = 0 + } + } +} \ No newline at end of file diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml new file mode 100644 index 0000000000..f8fb246085 --- /dev/null +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml @@ -0,0 +1,69 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 2.3 +import QtQuick.Layouts 1.3 + +import UM 1.3 as UM +import Cura 1.0 as Cura + +RowLayout +{ + Cura.IconLabel + { + source: UM.Theme.getIcon("category_layer_height") + text: Cura.MachineManager.activeStack ? Cura.MachineManager.activeQualityOrQualityChangesName + " " + layerHeight.properties.value + "mm" : "" + + UM.SettingPropertyProvider + { + id: layerHeight + containerStack: Cura.MachineManager.activeStack + key: "layer_height" + watchedProperties: ["value"] + } + } + + Cura.IconLabel + { + source: UM.Theme.getIcon("category_infill") + text: Cura.MachineManager.activeStack ? parseInt(infillDensity.properties.value) + "%" : "0%" + + UM.SettingPropertyProvider + { + id: infillDensity + containerStack: Cura.MachineManager.activeStack + key: "infill_sparse_density" + watchedProperties: ["value"] + } + } + + Cura.IconLabel + { + source: UM.Theme.getIcon("category_support") + text: supportEnabled.properties.value == "True" ? enabledText : disabledText + + + UM.SettingPropertyProvider + { + id: supportEnabled + containerStack: Cura.MachineManager.activeMachine + key: "support_enable" + watchedProperties: ["value"] + } + } + + Cura.IconLabel + { + source: UM.Theme.getIcon("category_adhesion") + text: platformAdhesionType.properties.value != "skirt" && platformAdhesionType.properties.value != "none" ? enabledText : disabledText + + UM.SettingPropertyProvider + { + id: platformAdhesionType + containerStack: Cura.MachineManager.activeMachine + key: "adhesion_type" + watchedProperties: [ "value"] + } + } +} \ No newline at end of file diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/PrintSetupSelector/RecommendedPrintSetup.qml similarity index 99% rename from resources/qml/SidebarSimple.qml rename to resources/qml/PrintSetupSelector/RecommendedPrintSetup.qml index f2e998f526..a11eae3218 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/PrintSetupSelector/RecommendedPrintSetup.qml @@ -188,7 +188,7 @@ Item } } - IconWithText + Cura.IconLabel { id: qualityRowTitle source: UM.Theme.getIcon("category_layer_height") @@ -496,7 +496,7 @@ Item width: Math.round(UM.Theme.getSize("print_setup_widget").width * .45) - UM.Theme.getSize("thick_margin").width - IconWithText + Cura.IconLabel { id: infillLabel source: UM.Theme.getIcon("category_infill") @@ -818,7 +818,7 @@ Item // // Enable support // - IconWithText + Cura.IconLabel { id: enableSupportLabel visible: enableSupportCheckBox.visible @@ -942,7 +942,7 @@ Item } - IconWithText + Cura.IconLabel { id: adhesionHelperLabel visible: adhesionCheckBox.visible From 0fdaebaaf83b3d34d94995dd628e246f63c8322c Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Wed, 28 Nov 2018 13:51:54 +0100 Subject: [PATCH 0514/1240] Updated action button CURA-5941 --- resources/qml/ActionButton.qml | 50 +++++++++++----------------- resources/qml/PrintSetupSelector.qml | 24 +++++++------ 2 files changed, 33 insertions(+), 41 deletions(-) diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index 732858c67f..e754365033 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -1,22 +1,17 @@ // Copyright (c) 2018 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. - import QtQuick 2.7 import QtQuick.Controls 2.1 - import QtGraphicalEffects 1.0 // For the dropshadow - import UM 1.1 as UM - Button { id: button - property alias iconSource: buttonIcon.source - property alias iconSourceRight: buttonIconRight.source + property alias iconSource: buttonIconLeft.source + property var iconOnRightSide: false property alias textFont: buttonText.font property alias cornerRadius: backgroundRect.radius property alias tooltip: tooltip.text - property color color: UM.Theme.getColor("primary") property color hoverColor: UM.Theme.getColor("primary_hover") property color disabledColor: color @@ -26,33 +21,31 @@ Button property color outlineColor: color property color outlineHoverColor: hoverColor property color outlineDisabledColor: outlineColor - hoverEnabled: true - property alias shadowColor: shadow.color property alias shadowEnabled: shadow.visible - // This property is used to indicate whether the button has a fixed width or the width would depend on the contents // Be careful when using fixedWidthMode, the translated texts can be too long that they won't fit. In any case, // we elide the text to the right so the text will be cut off with the three dots at the end. property var fixedWidthMode: false - - width: buttonIcon.width + buttonText.width + buttonIconRight.width - contentItem: Item + leftPadding: UM.Theme.getSize("default_margin").width + rightPadding: UM.Theme.getSize("default_margin").width + height: UM.Theme.getSize("action_button").height + contentItem: Row { + //Icon if displayed on the left side. UM.RecolorImage { - id: buttonIcon + id: buttonIconLeft source: "" - height: Math.round(0.6 * parent.height) - width: height + height: buttonText.height + width: visible ? height : 0 sourceSize.width: width sourceSize.height: height color: button.hovered ? button.textHoverColor : button.textColor - visible: source != "" + visible: source != "" && !button.iconOnRightSide anchors.verticalCenter: parent.verticalCenter } - Label { id: buttonText @@ -66,22 +59,21 @@ Button horizontalAlignment: Text.AlignHCenter elide: Text.ElideRight } - + //Icon if displayed on the right side. UM.RecolorImage { id: buttonIconRight - source: "" - height: Math.round(0.6 * parent.height) - width: height + source: buttonIconLeft.source + anchors.right: parent.right + height: buttonText.height + width: visible ? height : 0 sourceSize.width: width sourceSize.height: height - color: button.hovered ? button.textHoverColor : button.textColor - visible: source != "" - anchors.verticalCenter: parent.verticalCenter - anchors.right: source != "" ? parent.right : undefined + color: buttonIconLeft.color + visible: source != "" && button.iconOnRightSide + anchors.verticalCenter: buttonIconLeft.verticalCenter } } - background: Rectangle { id: backgroundRect @@ -90,7 +82,6 @@ Button border.width: UM.Theme.getSize("default_lining").width border.color: button.enabled ? (button.hovered ? button.outlineHoverColor : button.outlineColor) : button.outlineDisabledColor } - DropShadow { id: shadow @@ -103,7 +94,6 @@ Button // Should always be drawn behind the background. z: backgroundRect.z - 1 } - ToolTip { id: tooltip @@ -111,4 +101,4 @@ Button delay: 500 visible: text != "" && button.hovered } -} +} \ No newline at end of file diff --git a/resources/qml/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector.qml index 253a13c0f0..85472bae8d 100644 --- a/resources/qml/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector.qml @@ -323,18 +323,9 @@ Cura.ExpandableComponent Cura.ActionButton { id: settingControlButton - leftPadding: UM.Theme.getSize("default_margin").width - rightPadding: UM.Theme.getSize("default_margin").width - height: UM.Theme.getSize("action_panel_button").height - text: catalog.i18nc("@button", "Custom") - color: UM.Theme.getColor("secondary") - hoverColor: UM.Theme.getColor("secondary") - textColor: UM.Theme.getColor("primary") - textHoverColor: UM.Theme.getColor("text") - iconSourceRight: UM.Theme.getIcon("arrow_right") - width: UM.Theme.getSize("print_setup_action_button").width - fixedWidthMode: true visible: currentModeIndex == 0 + text: catalog.i18nc("@button", "Custom") + width: UM.Theme.getSize("print_setup_action_button").width anchors { top: parent.top @@ -344,6 +335,13 @@ Cura.ExpandableComponent rightMargin: UM.Theme.getSize("narrow_margin").height } + color: UM.Theme.getColor("secondary") + hoverColor: UM.Theme.getColor("secondary") + textColor: UM.Theme.getColor("primary") + textHoverColor: UM.Theme.getColor("text") + iconSource: UM.Theme.getIcon("arrow_right") + iconOnRightSide: true + onClicked: currentModeIndex = 1 } @@ -351,13 +349,17 @@ Cura.ExpandableComponent { height: UM.Theme.getSize("action_panel_button").height text: catalog.i18nc("@button", "Recommended") + color: UM.Theme.getColor("secondary") hoverColor: UM.Theme.getColor("secondary") textColor: UM.Theme.getColor("primary") textHoverColor: UM.Theme.getColor("text") + iconSource: UM.Theme.getIcon("arrow_left") + width: UM.Theme.getSize("print_setup_action_button").width fixedWidthMode: true + visible: currentModeIndex == 1 anchors { From 990c653af4eaf11e22dc49a9f9ea1c96d61a43d4 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 28 Nov 2018 17:29:25 +0100 Subject: [PATCH 0515/1240] Refactor the code a bit more to better align the components in the recommended mode. Contributes to CURA-5941. --- resources/qml/ActionButton.qml | 18 +- resources/qml/ExpandableComponent.qml | 2 +- resources/qml/IconLabel.qml | 5 +- .../PrintSetupSelector/PrintSetupSelector.qml | 13 +- .../PrintSetupSelectorContents.qml | 294 +-- .../PrintSetupSelectorHeader.qml | 8 +- .../RecommendedPrintSetup.qml | 2034 ++++++++--------- resources/qml/qmldir | 1 + resources/themes/cura-light/theme.json | 1 + 9 files changed, 1134 insertions(+), 1242 deletions(-) diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index 732858c67f..b9a04f3b46 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -12,7 +12,6 @@ Button { id: button property alias iconSource: buttonIcon.source - property alias iconSourceRight: buttonIconRight.source property alias textFont: buttonText.font property alias cornerRadius: backgroundRect.radius property alias tooltip: tooltip.text @@ -37,8 +36,7 @@ Button // we elide the text to the right so the text will be cut off with the three dots at the end. property var fixedWidthMode: false - width: buttonIcon.width + buttonText.width + buttonIconRight.width - contentItem: Item + contentItem: Row { UM.RecolorImage { @@ -66,20 +64,6 @@ Button horizontalAlignment: Text.AlignHCenter elide: Text.ElideRight } - - UM.RecolorImage - { - id: buttonIconRight - source: "" - height: Math.round(0.6 * parent.height) - width: height - sourceSize.width: width - sourceSize.height: height - color: button.hovered ? button.textHoverColor : button.textColor - visible: source != "" - anchors.verticalCenter: parent.verticalCenter - anchors.right: source != "" ? parent.right : undefined - } } background: Rectangle diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 897d44d941..0c3a8f80b9 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -74,7 +74,7 @@ Item function togglePopup() { - if(popup.visible) + if (popup.visible) { popup.close() } diff --git a/resources/qml/IconLabel.qml b/resources/qml/IconLabel.qml index 0941254e7b..ee4a1254ec 100644 --- a/resources/qml/IconLabel.qml +++ b/resources/qml/IconLabel.qml @@ -2,8 +2,7 @@ // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.7 -import QtQuick.Controls 2.1 -import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.3 import UM 1.1 as UM @@ -48,7 +47,7 @@ Item text: "Empty label" elide: Text.ElideRight color: UM.Theme.getColor("text") - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") renderType: Text.NativeRendering } } \ No newline at end of file diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector/PrintSetupSelector.qml index c69dd56520..1794a54cdf 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelector.qml @@ -11,7 +11,6 @@ Cura.ExpandableComponent { id: base - property int currentModeIndex: -1 property bool hideSettings: PrintInformation.preSliced property string enabledText: catalog.i18nc("@label:Should be short", "On") @@ -27,13 +26,6 @@ Cura.ExpandableComponent popupClosePolicy: Popup.CloseOnEscape - onCurrentModeIndexChanged: UM.Preferences.setValue("cura/active_mode", currentModeIndex) - - Component.onCompleted: - { - popupItemWrapper.width = base.width - } - UM.I18nCatalog { id: catalog @@ -61,8 +53,5 @@ Cura.ExpandableComponent id: extrudersModel } - popupItem: PrintSetupSelectorContents - { - - } + popupItem: PrintSetupSelectorContents {} } \ No newline at end of file diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml index e7b3b94b11..c36a0fbb80 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml @@ -3,24 +3,26 @@ import QtQuick 2.7 import QtQuick.Controls 2.3 -import QtQuick.Layouts 1.3 import UM 1.3 as UM import Cura 1.0 as Cura -Rectangle +Item { - property var total_height: popupItemHeader.height + popupItemContent.height + footerControll.height + UM.Theme.getSize("narrow_margin").height * 2 - id: popupItemWrapper - height: total_height + id: popup - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("lining") + width: UM.Theme.getSize("print_setup_widget").width + height: childrenRect.height - Item + property int currentModeIndex: -1 + onCurrentModeIndexChanged: UM.Preferences.setValue("cura/active_mode", currentModeIndex) + + // Header of the popup + Rectangle { - id: popupItemHeader - height: 36 + id: header + height: UM.Theme.getSize("print_setup_widget_header").height + color: UM.Theme.getColor("action_button_hovered") // TODO: It's not clear the color that we need to use here anchors { @@ -31,8 +33,8 @@ Rectangle Label { - id: popupItemHeaderText - text: catalog.i18nc("@label", "Print settings"); + id: headerLabel + text: catalog.i18nc("@label", "Print settings") font: UM.Theme.getFont("default") renderType: Text.NativeRendering verticalAlignment: Text.AlignVCenter @@ -47,221 +49,153 @@ Rectangle } } - Rectangle - { - width: parent.width - height: UM.Theme.getSize("default_lining").height - anchors.top: popupItemHeaderText.bottom - color: UM.Theme.getColor("action_button_border") - - - } - Button { - id: closeButton; - width: UM.Theme.getSize("message_close").width; - height: UM.Theme.getSize("message_close").height; + id: closeButton + width: UM.Theme.getSize("message_close").width + height: UM.Theme.getSize("message_close").height anchors { - right: parent.right; - rightMargin: UM.Theme.getSize("default_margin").width; - top: parent.top; - topMargin: 10 + right: parent.right + rightMargin: UM.Theme.getSize("default_margin").width + verticalCenter: parent.verticalCenter } - UM.RecolorImage + contentItem: UM.RecolorImage { - anchors.fill: parent; + anchors.fill: parent sourceSize.width: width sourceSize.height: width color: UM.Theme.getColor("message_text") source: UM.Theme.getIcon("cross1") } - onClicked: base.togglePopup() // Will hide the popup item + background: Item {} - background: Rectangle - { - color: UM.Theme.getColor("message_background") - } + onClicked: togglePopup() // Will hide the popup item } } Rectangle { - id: popupItemContent - width: parent.width - height: tabBar.height + sidebarContents.height + id: topSeparator + anchors.bottom: header.bottom + width: parent.width + height: UM.Theme.getSize("default_lining").height + color: UM.Theme.getColor("lining") + } + + Loader + { + id: loader + width: parent.width anchors { - top: popupItemHeader.bottom - topMargin: UM.Theme.getSize("narrow_margin").height - right: parent.right - left: parent.left + top: header.bottom leftMargin: UM.Theme.getSize("default_margin").width rightMargin: UM.Theme.getSize("default_margin").width } + sourceComponent: currentModeIndex == 0 ? recommendedPrintSetup : customPrintSetup + } - UM.TabRow - { - id: tabBar - anchors.topMargin: UM.Theme.getSize("default_margin").height - onCurrentIndexChanged: Cura.ExtruderManager.setActiveExtruderIndex(currentIndex) - z: 1 - Repeater - { - model: extrudersModel - delegate: UM.TabRowButton - { - contentItem: Rectangle - { - z: 2 - Cura.ExtruderIcon - { - anchors.horizontalCenter: parent.horizontalCenter - materialColor: model.color - extruderEnabled: model.enabled - width: parent.height - height: parent.height - } - } +// Item +// { +// id: content +// width: parent.width +// height: 100 +////// height: tabBar.height + sidebarContents.height +//// +// anchors +// { +// top: header.bottom +// leftMargin: UM.Theme.getSize("default_margin").width +// rightMargin: UM.Theme.getSize("default_margin").width +// } +//// +//// Rectangle +//// { +//// id: sidebarContents +//// anchors.top: tabBar.bottom +//// anchors.bottom: parent.bottom +//// anchors.left: parent.left +//// anchors.right: parent.right +//// height: UM.Theme.getSize("print_setup_widget").height +//// +//// border.width: UM.Theme.getSize("default_lining").width * 2 +//// border.color: UM.Theme.getColor("action_button_border") +//// } +// } - background: Rectangle - { + Rectangle + { + id: buttonsSeparator - width: parent.width - z: 1 - border.width: UM.Theme.getSize("default_lining").width * 2 - border.color: UM.Theme.getColor("action_button_border") - - visible: - { - return index == tabBar.currentIndex - } - - // overlap bottom border - Rectangle - { - width: parent.width - UM.Theme.getSize("default_lining").width * 4 - height: UM.Theme.getSize("default_lining").width * 4 - anchors.bottom: parent.bottom - anchors.bottomMargin: - (UM.Theme.getSize("default_lining").width * 2) - anchors.leftMargin: UM.Theme.getSize("default_lining").width * 2 - anchors.left: parent.left - - } - } - } - } - } - - Rectangle - { - id: sidebarContents - anchors.top: tabBar.bottom - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.right: parent.right - height: UM.Theme.getSize("print_setup_widget").height - - border.width: UM.Theme.getSize("default_lining").width * 2 - border.color: UM.Theme.getColor("action_button_border") - - RecommendedPrintSetup - { - anchors.topMargin: UM.Theme.getSize("print_setup_content_top_margin").height - anchors.fill: parent - visible: currentModeIndex != 1 - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() - } - - CustomPrintSetup - { - anchors.topMargin: UM.Theme.getSize("print_setup_content_top_margin").height - anchors.bottomMargin: 2 //don't overlap bottom border - anchors.fill: parent - visible: currentModeIndex == 1 - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() - } - } + anchors.top: loader.bottom + width: parent.width + height: UM.Theme.getSize("default_lining").height + color: UM.Theme.getColor("lining") } Item { - id: footerControll - anchors.top: popupItemContent.bottom - anchors.topMargin: UM.Theme.getSize("narrow_margin").height * 2 - width: parent.width - height: settingControlButton.height + UM.Theme.getSize("default_lining").height * 4 - Rectangle + id: buttonRow + height: childrenRect.height + + // The buttonsSeparator is inside the buttonRow. This is to avoid some weird behaviours with the scroll bar. + anchors { - width: parent.width - height: UM.Theme.getSize("default_lining").height - color: UM.Theme.getColor("action_button_border") + top: buttonsSeparator.top + left: parent.left + right: parent.right } - Cura.ActionButton + Cura.SecondaryButton { - id: settingControlButton + anchors.left: parent.left leftPadding: UM.Theme.getSize("default_margin").width rightPadding: UM.Theme.getSize("default_margin").width - height: UM.Theme.getSize("action_panel_button").height - text: catalog.i18nc("@button", "Custom") - color: UM.Theme.getColor("secondary") - hoverColor: UM.Theme.getColor("secondary") - textColor: UM.Theme.getColor("primary") - textHoverColor: UM.Theme.getColor("text") - iconSourceRight: UM.Theme.getIcon("arrow_right") - width: UM.Theme.getSize("print_setup_action_button").width - fixedWidthMode: true - visible: currentModeIndex == 0 - anchors - { - top: parent.top - topMargin: UM.Theme.getSize("narrow_margin").height * 2 - bottomMargin: UM.Theme.getSize("narrow_margin").height * 2 - right: parent.right - rightMargin: UM.Theme.getSize("narrow_margin").height - } - - onClicked: currentModeIndex = 1 + text: catalog.i18nc("@button", "Recommended") + visible: currentModeIndex == 1 + onClicked: currentModeIndex = 0 } - Cura.ActionButton + Cura.SecondaryButton { - height: UM.Theme.getSize("action_panel_button").height - text: catalog.i18nc("@button", "Recommended") - color: UM.Theme.getColor("secondary") - hoverColor: UM.Theme.getColor("secondary") - textColor: UM.Theme.getColor("primary") - textHoverColor: UM.Theme.getColor("text") - iconSource: UM.Theme.getIcon("arrow_left") - width: UM.Theme.getSize("print_setup_action_button").width - fixedWidthMode: true - visible: currentModeIndex == 1 - anchors - { - top: parent.top - topMargin: UM.Theme.getSize("narrow_margin").height * 2 - bottomMargin: UM.Theme.getSize("narrow_margin").height * 2 - left: parent.left - leftMargin: UM.Theme.getSize("narrow_margin").height - } - - MouseArea { - anchors.fill: parent - onClicked: currentModeIndex = 0 - } + anchors.right: parent.right + leftPadding: UM.Theme.getSize("default_margin").width + rightPadding: UM.Theme.getSize("default_margin").width + text: catalog.i18nc("@button", "Custom") + visible: currentModeIndex == 0 + onClicked: currentModeIndex = 1 } } + Component + { + id: recommendedPrintSetup + RecommendedPrintSetup + { + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() + } + } + + Component + { + id: customPrintSetup + CustomPrintSetup + { + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() + } + } + + Component.onCompleted: { + print(height, "!!!!!!!!!!!!!!!!!!!!!!!") var index = Math.round(UM.Preferences.getValue("cura/active_mode")) if(index != null && !isNaN(index)) diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml index f8fb246085..d4057289f6 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml @@ -10,7 +10,7 @@ import Cura 1.0 as Cura RowLayout { - Cura.IconLabel + Cura.IconWithText { source: UM.Theme.getIcon("category_layer_height") text: Cura.MachineManager.activeStack ? Cura.MachineManager.activeQualityOrQualityChangesName + " " + layerHeight.properties.value + "mm" : "" @@ -24,7 +24,7 @@ RowLayout } } - Cura.IconLabel + Cura.IconWithText { source: UM.Theme.getIcon("category_infill") text: Cura.MachineManager.activeStack ? parseInt(infillDensity.properties.value) + "%" : "0%" @@ -38,7 +38,7 @@ RowLayout } } - Cura.IconLabel + Cura.IconWithText { source: UM.Theme.getIcon("category_support") text: supportEnabled.properties.value == "True" ? enabledText : disabledText @@ -53,7 +53,7 @@ RowLayout } } - Cura.IconLabel + Cura.IconWithText { source: UM.Theme.getIcon("category_adhesion") text: platformAdhesionType.properties.value != "skirt" && platformAdhesionType.properties.value != "none" ? enabledText : disabledText diff --git a/resources/qml/PrintSetupSelector/RecommendedPrintSetup.qml b/resources/qml/PrintSetupSelector/RecommendedPrintSetup.qml index a11eae3218..5d195bdde4 100644 --- a/resources/qml/PrintSetupSelector/RecommendedPrintSetup.qml +++ b/resources/qml/PrintSetupSelector/RecommendedPrintSetup.qml @@ -4,7 +4,6 @@ import QtQuick 2.7 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 -import QtQuick.Layouts 1.3 import UM 1.2 as UM import Cura 1.0 as Cura @@ -15,6 +14,7 @@ Item signal showTooltip(Item item, point location, string text) signal hideTooltip() + height: childrenRect.height property Action configureSettings @@ -26,1088 +26,1072 @@ Item name: "cura" } - ScrollView + // + // Quality profile + // + Item { - visible: Cura.MachineManager.activeMachineName != "" // If no printers added then the view is invisible - anchors.fill: parent - style: UM.Theme.styles.scrollview - flickableItem.flickableDirection: Flickable.VerticalFlick + id: qualityRow - Item + height: UM.Theme.getSize("thick_margin").height + anchors.topMargin: UM.Theme.getSize("thick_margin").height + anchors.left: parent.left + anchors.right: parent.right + + Timer { - width: childrenRect.width - height: childrenRect.height - - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("default_margin").width - // - // Quality profile - // - Item + id: qualitySliderChangeTimer + interval: 50 + running: false + repeat: false + onTriggered: { - id: qualityRow + var item = Cura.QualityProfilesDropDownMenuModel.getItem(qualitySlider.value); + Cura.MachineManager.activeQualityGroup = item.quality_group; + } + } - height: UM.Theme.getSize("thick_margin").height - anchors.topMargin: UM.Theme.getSize("thick_margin").height - anchors.left: parent.left - anchors.right: parent.right + Component.onCompleted: qualityModel.update() - Timer + Connections + { + target: Cura.QualityProfilesDropDownMenuModel + onItemsChanged: qualityModel.update() + } + + Connections { + target: base + onVisibleChanged: + { + // update needs to be called when the widgets are visible, otherwise the step width calculation + // will fail because the width of an invisible item is 0. + if (visible) { - id: qualitySliderChangeTimer - interval: 50 - running: false - repeat: false - onTriggered: - { - var item = Cura.QualityProfilesDropDownMenuModel.getItem(qualitySlider.value); - Cura.MachineManager.activeQualityGroup = item.quality_group; - } + qualityModel.update(); } + } + } - Component.onCompleted: qualityModel.update() + ListModel + { + id: qualityModel - Connections + property var totalTicks: 0 + property var availableTotalTicks: 0 + property var existingQualityProfile: 0 + + property var qualitySliderActiveIndex: 0 + property var qualitySliderStepWidth: 0 + property var qualitySliderAvailableMin: 0 + property var qualitySliderAvailableMax: 0 + property var qualitySliderMarginRight: 0 + + function update () + { + reset() + + var availableMin = -1 + var availableMax = -1 + + for (var i = 0; i < Cura.QualityProfilesDropDownMenuModel.rowCount(); i++) { - target: Cura.QualityProfilesDropDownMenuModel - onItemsChanged: qualityModel.update() - } + var qualityItem = Cura.QualityProfilesDropDownMenuModel.getItem(i) - Connections { - target: base - onVisibleChanged: + // Add each quality item to the UI quality model + qualityModel.append(qualityItem) + + // Set selected value + if (Cura.MachineManager.activeQualityType == qualityItem.quality_type) { - // update needs to be called when the widgets are visible, otherwise the step width calculation - // will fail because the width of an invisible item is 0. - if (visible) + // set to -1 when switching to user created profile so all ticks are clickable + if (Cura.SimpleModeSettingsManager.isProfileUserCreated) { - qualityModel.update(); - } - } - } - - ListModel - { - id: qualityModel - - property var totalTicks: 0 - property var availableTotalTicks: 0 - property var existingQualityProfile: 0 - - property var qualitySliderActiveIndex: 0 - property var qualitySliderStepWidth: 0 - property var qualitySliderAvailableMin: 0 - property var qualitySliderAvailableMax: 0 - property var qualitySliderMarginRight: 0 - - function update () - { - reset() - - var availableMin = -1 - var availableMax = -1 - - for (var i = 0; i < Cura.QualityProfilesDropDownMenuModel.rowCount(); i++) - { - var qualityItem = Cura.QualityProfilesDropDownMenuModel.getItem(i) - - // Add each quality item to the UI quality model - qualityModel.append(qualityItem) - - // Set selected value - if (Cura.MachineManager.activeQualityType == qualityItem.quality_type) - { - // set to -1 when switching to user created profile so all ticks are clickable - if (Cura.SimpleModeSettingsManager.isProfileUserCreated) - { - qualityModel.qualitySliderActiveIndex = -1 - } - else - { - qualityModel.qualitySliderActiveIndex = i - } - - qualityModel.existingQualityProfile = 1 - } - - // Set min available - if (qualityItem.available && availableMin == -1) - { - availableMin = i - } - - // Set max available - if (qualityItem.available) - { - availableMax = i - } - } - - // Set total available ticks for active slider part - if (availableMin != -1) - { - qualityModel.availableTotalTicks = availableMax - availableMin + 1 - } - - // Calculate slider values - calculateSliderStepWidth(qualityModel.totalTicks) - calculateSliderMargins(availableMin, availableMax, qualityModel.totalTicks) - - qualityModel.qualitySliderAvailableMin = availableMin - qualityModel.qualitySliderAvailableMax = availableMax - } - - function calculateSliderStepWidth (totalTicks) - { - qualityModel.qualitySliderStepWidth = totalTicks != 0 ? Math.round((base.width * 0.55) / (totalTicks)) : 0 - } - - function calculateSliderMargins (availableMin, availableMax, totalTicks) - { - if (availableMin == -1 || (availableMin == 0 && availableMax == 0)) - { - qualityModel.qualitySliderMarginRight = Math.round(base.width * 0.55) - } - else if (availableMin == availableMax) - { - qualityModel.qualitySliderMarginRight = Math.round((totalTicks - availableMin) * qualitySliderStepWidth) + qualityModel.qualitySliderActiveIndex = -1 } else { - qualityModel.qualitySliderMarginRight = Math.round((totalTicks - availableMax) * qualitySliderStepWidth) + qualityModel.qualitySliderActiveIndex = i } + + qualityModel.existingQualityProfile = 1 } - function reset () { - qualityModel.clear() - qualityModel.availableTotalTicks = 0 - qualityModel.existingQualityProfile = 0 + // Set min available + if (qualityItem.available && availableMin == -1) + { + availableMin = i + } - // check, the ticks count cannot be less than zero - qualityModel.totalTicks = Math.max(0, Cura.QualityProfilesDropDownMenuModel.rowCount() - 1) + // Set max available + if (qualityItem.available) + { + availableMax = i } } - Cura.IconLabel + // Set total available ticks for active slider part + if (availableMin != -1) { - id: qualityRowTitle - source: UM.Theme.getIcon("category_layer_height") - text: catalog.i18nc("@label", "Layer Height") - anchors.bottom: speedSlider.bottom + qualityModel.availableTotalTicks = availableMax - availableMin + 1 } - // Show titles for the each quality slider ticks - Item + // Calculate slider values + calculateSliderStepWidth(qualityModel.totalTicks) + calculateSliderMargins(availableMin, availableMax, qualityModel.totalTicks) + + qualityModel.qualitySliderAvailableMin = availableMin + qualityModel.qualitySliderAvailableMax = availableMax + } + + function calculateSliderStepWidth (totalTicks) + { + qualityModel.qualitySliderStepWidth = totalTicks != 0 ? Math.round((base.width * 0.55) / (totalTicks)) : 0 + } + + function calculateSliderMargins (availableMin, availableMax, totalTicks) + { + if (availableMin == -1 || (availableMin == 0 && availableMax == 0)) { - anchors.left: speedSlider.left - anchors.top: speedSlider.bottom - Repeater - { - model: qualityModel - - Label - { - anchors.verticalCenter: parent.verticalCenter - anchors.top: parent.bottom - color: (Cura.MachineManager.activeMachine != null && Cura.QualityProfilesDropDownMenuModel.getItem(index).available) ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") - text: - { - var result = "" - if(Cura.MachineManager.activeMachine != null) - { - result = Cura.QualityProfilesDropDownMenuModel.getItem(index).layer_height - - if(result == undefined) - { - result = ""; - } - else - { - result = Number(Math.round(result + "e+2") + "e-2"); //Round to 2 decimals. Javascript makes this difficult... - if (result == undefined || result != result) //Parse failure. - { - result = ""; - } - } - } - return result - } - - x: - { - // Make sure the text aligns correctly with each tick - if (qualityModel.totalTicks == 0) - { - // If there is only one tick, align it centrally - return Math.round(((base.width * 0.55) - width) / 2) - } - else if (index == 0) - { - return Math.round(base.width * 0.55 / qualityModel.totalTicks) * index - } - else if (index == qualityModel.totalTicks) - { - return Math.round(base.width * 0.55 / qualityModel.totalTicks) * index - width - } - else - { - return Math.round((base.width * 0.55 / qualityModel.totalTicks) * index - (width / 2)) - } - } - } - } + qualityModel.qualitySliderMarginRight = Math.round(base.width * 0.55) } - - //Print speed slider - Rectangle + else if (availableMin == availableMax) { - id: speedSlider - width: Math.round(base.width * 0.55) - height: UM.Theme.getSize("thick_margin").height - anchors.right: parent.right - anchors.top: parent.top - anchors.topMargin: UM.Theme.getSize("thick_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("thick_margin").width, customisedSettings.height), content) - } - else - { - base.hideTooltip() - } - } - - id: unavailableLineToolTip - height: 20 * screenScaleFactor // 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 qualityModel.qualitySliderStepWidth * qualityModel.totalTicks - } - 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) - } - } - - Item - { - id: rightArea - width: - { - if(qualityModel.availableTotalTicks == 0) - return 0 - - return qualityModel.qualitySliderMarginRight - 10 - } - height: parent.height - 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 - { - id: groovechildrect - width: Math.round(base.width * 0.55) - height: 2 * screenScaleFactor - color: UM.Theme.getColor("quality_slider_unavailable") - anchors.verticalCenter: qualitySlider.verticalCenter - x: 0 - } - - // Draw ticks - Repeater - { - id: qualityRepeater - model: qualityModel.totalTicks > 0 ? qualityModel : 0 - - Rectangle - { - color: Cura.QualityProfilesDropDownMenuModel.getItem(index).available ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") - implicitWidth: 5 * screenScaleFactor - implicitHeight: implicitWidth - anchors.verticalCenter: qualitySlider.verticalCenter - x: Math.round(qualityModel.qualitySliderStepWidth * index) - radius: Math.round(implicitWidth / 2) - } - } - - Slider - { - id: qualitySlider - height: UM.Theme.getSize("thick_margin").height - anchors.bottom: speedSlider.bottom - enabled: qualityModel.totalTicks > 0 && !Cura.SimpleModeSettingsManager.isProfileCustomized - visible: qualityModel.availableTotalTicks > 0 - updateValueWhileDragging : false - - minimumValue: qualityModel.qualitySliderAvailableMin >= 0 ? qualityModel.qualitySliderAvailableMin : 0 - // maximumValue must be greater than minimumValue to be able to see the handle. While the value is strictly - // speaking not always correct, it seems to have the correct behavior (switching from 0 available to 1 available) - maximumValue: qualityModel.qualitySliderAvailableMax >= 1 ? qualityModel.qualitySliderAvailableMax : 1 - stepSize: 1 - - value: qualityModel.qualitySliderActiveIndex - - width: qualityModel.qualitySliderStepWidth * (qualityModel.availableTotalTicks - 1) - - anchors.right: parent.right - anchors.rightMargin: qualityModel.qualitySliderMarginRight - - style: SliderStyle - { - //Draw Available line - groove: Rectangle - { - implicitHeight: 2 * screenScaleFactor - color: UM.Theme.getColor("quality_slider_available") - radius: Math.round(height / 2) - } - handle: Item - { - Rectangle - { - id: qualityhandleButton - anchors.centerIn: parent - color: UM.Theme.getColor("quality_slider_available") - implicitWidth: 10 * screenScaleFactor - implicitHeight: implicitWidth - radius: Math.round(implicitWidth / 2) - visible: !Cura.SimpleModeSettingsManager.isProfileCustomized && !Cura.SimpleModeSettingsManager.isProfileUserCreated && qualityModel.existingQualityProfile - } - } - } - - onValueChanged: - { - // only change if an active machine is set and the slider is visible at all. - if (Cura.MachineManager.activeMachine != null && visible) - { - // prevent updating during view initializing. Trigger only if the value changed by user - if (qualitySlider.value != qualityModel.qualitySliderActiveIndex && qualityModel.qualitySliderActiveIndex != -1) - { - // start updating with short delay - qualitySliderChangeTimer.start() - } - } - } - } - - MouseArea - { - id: speedSliderMouseArea - anchors.fill: parent - hoverEnabled: true - enabled: Cura.SimpleModeSettingsManager.isProfileUserCreated - - onEntered: - { - var content = catalog.i18nc("@tooltip","A custom profile is currently active. To enable the quality slider, choose a default quality profile in Custom tab") - base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, customisedSettings.height), content) - } - onExited: base.hideTooltip() - } + qualityModel.qualitySliderMarginRight = Math.round((totalTicks - availableMin) * qualitySliderStepWidth) } - - UM.SimpleButton + else { - id: customisedSettings - - visible: Cura.SimpleModeSettingsManager.isProfileCustomized || Cura.SimpleModeSettingsManager.isProfileUserCreated - height: Math.round(speedSlider.height * 0.8) - width: Math.round(speedSlider.height * 0.8) - - anchors.verticalCenter: speedSlider.verticalCenter - anchors.right: speedSlider.left - anchors.rightMargin: Math.round(UM.Theme.getSize("thick_margin").width / 2) - - color: hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button"); - iconSource: UM.Theme.getIcon("reset"); - - onClicked: - { - // if the current profile is user-created, switch to a built-in quality - Cura.MachineManager.resetToUseDefaultQuality() - } - onEntered: - { - var content = catalog.i18nc("@tooltip","You have modified some profile settings. If you want to change these go to custom mode.") - base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, customisedSettings.height), content) - } - onExited: base.hideTooltip() + qualityModel.qualitySliderMarginRight = Math.round((totalTicks - availableMax) * qualitySliderStepWidth) } } - // - // Infill - // - Item - { - id: infillCellLeft + function reset () { + qualityModel.clear() + qualityModel.availableTotalTicks = 0 + qualityModel.existingQualityProfile = 0 - anchors.top: qualityRow.bottom - anchors.topMargin: UM.Theme.getSize("thick_margin").height * 2 - anchors.left: parent.left - - width: Math.round(UM.Theme.getSize("print_setup_widget").width * .45) - UM.Theme.getSize("thick_margin").width - - Cura.IconLabel - { - id: infillLabel - source: UM.Theme.getIcon("category_infill") - text: catalog.i18nc("@label", "Infill") + " (%)" - - anchors - { - top: parent.top - topMargin: Math.round(UM.Theme.getSize("thick_margin").height * 1.7) - left: parent.left - } - } + // check, the ticks count cannot be less than zero + qualityModel.totalTicks = Math.max(0, Cura.QualityProfilesDropDownMenuModel.rowCount() - 1) } + } - Item + Cura.IconWithText + { + id: qualityRowTitle + source: UM.Theme.getIcon("category_layer_height") + text: catalog.i18nc("@label", "Layer Height") + anchors.bottom: speedSlider.bottom + } + + // Show titles for the each quality slider ticks + Item + { + anchors.left: speedSlider.left + anchors.top: speedSlider.bottom + Repeater { - id: infillCellRight - - height: infillSlider.height + UM.Theme.getSize("thick_margin").height + enableGradualInfillCheckBox.visible * (enableGradualInfillCheckBox.height + UM.Theme.getSize("thick_margin").height) - width: Math.round(UM.Theme.getSize("print_setup_widget").width * .55) - - anchors.left: infillCellLeft.right - anchors.top: infillCellLeft.top - anchors.topMargin: UM.Theme.getSize("thick_margin").height - - Label { - id: selectedInfillRateText - - anchors.left: infillSlider.left - anchors.right: parent.right - - text: parseInt(infillDensity.properties.value) + "%" - horizontalAlignment: Text.AlignLeft - - color: infillSlider.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") - } - - // We use a binding to make sure that after manually setting infillSlider.value it is still bound to the property provider - Binding - { - target: infillSlider - property: "value" - value: parseInt(infillDensity.properties.value) - } - - Slider - { - id: infillSlider - - anchors.top: selectedInfillRateText.bottom - anchors.left: parent.left - anchors.right: infillIcon.left - anchors.rightMargin: UM.Theme.getSize("thick_margin").width - - height: UM.Theme.getSize("thick_margin").height - width: parseInt(infillCellRight.width - UM.Theme.getSize("thick_margin").width - style.handleWidth) - - minimumValue: 0 - maximumValue: 100 - stepSize: 1 - tickmarksEnabled: true - - // disable slider when gradual support is enabled - enabled: parseInt(infillSteps.properties.value) == 0 - - // set initial value from stack - value: parseInt(infillDensity.properties.value) - - onValueChanged: - { - - // Don't round the value if it's already the same - if (parseInt(infillDensity.properties.value) == infillSlider.value) - { - return - } - - // Round the slider value to the nearest multiple of 10 (simulate step size of 10) - var roundedSliderValue = Math.round(infillSlider.value / 10) * 10 - - // Update the slider value to represent the rounded value - infillSlider.value = roundedSliderValue - - // Update value only if the Recomended mode is Active, - // Otherwise if I change the value in the Custom mode the Recomended view will try to repeat - // same operation - var active_mode = UM.Preferences.getValue("cura/active_mode") - - if (active_mode == 0 || active_mode == "simple") - { - Cura.MachineManager.setSettingForAllExtruders("infill_sparse_density", "value", roundedSliderValue) - Cura.MachineManager.resetSettingForAllExtruders("infill_line_distance") - } - } - - style: SliderStyle - { - groove: Rectangle - { - id: groove - implicitWidth: 200 * screenScaleFactor - implicitHeight: 2 * screenScaleFactor - color: control.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") - radius: 1 - } - - handle: Item - { - Rectangle - { - id: handleButton - anchors.centerIn: parent - color: control.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") - implicitWidth: 10 * screenScaleFactor - implicitHeight: 10 * screenScaleFactor - radius: 10 * screenScaleFactor - } - } - - tickmarks: Repeater - { - id: repeater - model: control.maximumValue / control.stepSize + 1 - - // check if a tick should be shown based on it's index and wether the infill density is a multiple of 10 (slider step size) - function shouldShowTick (index) - { - if (index % 10 == 0) - { - return true - } - return false - } - - Rectangle - { - anchors.verticalCenter: parent.verticalCenter - color: control.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") - width: 1 * screenScaleFactor - height: 6 * screenScaleFactor - x: Math.round(styleData.handleWidth / 2 + index * ((repeater.width - styleData.handleWidth) / (repeater.count-1))) - visible: shouldShowTick(index) - } - } - } - } - - Rectangle - { - id: infillIcon - - width: Math.round((parent.width / 5) - (UM.Theme.getSize("thick_margin").width)) - height: width - - anchors.right: parent.right - anchors.top: parent.top - anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height / 2) - - // we loop over all density icons and only show the one that has the current density and steps - Repeater - { - id: infillIconList - model: infillModel - anchors.fill: parent - - function activeIndex () - { - for (var i = 0; i < infillModel.count; i++) - { - var density = Math.round(infillDensity.properties.value) - var steps = Math.round(infillSteps.properties.value) - var infillModelItem = infillModel.get(i) - - if (infillModelItem != "undefined" - && density >= infillModelItem.percentageMin - && density <= infillModelItem.percentageMax - && steps >= infillModelItem.stepsMin - && steps <= infillModelItem.stepsMax) - { - return i - } - } - return -1 - } - - Rectangle - { - anchors.fill: parent - visible: infillIconList.activeIndex() == index - - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("quality_slider_unavailable") - - UM.RecolorImage - { - anchors.fill: parent - anchors.margins: 2 * screenScaleFactor - sourceSize.width: width - sourceSize.height: width - source: UM.Theme.getIcon(model.icon) - color: UM.Theme.getColor("quality_slider_unavailable") - } - } - } - } - - // Gradual Support Infill Checkbox - CheckBox - { - id: enableGradualInfillCheckBox - property alias _hovered: enableGradualInfillMouseArea.containsMouse - - anchors.top: infillSlider.bottom - anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height / 2) // closer to slider since it belongs to the same category - anchors.left: infillCellRight.left - - style: UM.Theme.styles.checkbox - enabled: base.settingsEnabled - visible: infillSteps.properties.enabled == "True" - checked: parseInt(infillSteps.properties.value) > 0 - - MouseArea - { - id: enableGradualInfillMouseArea - - anchors.fill: parent - hoverEnabled: true - enabled: true - - property var previousInfillDensity: parseInt(infillDensity.properties.value) - - onClicked: - { - // Set to 90% only when enabling gradual infill - var newInfillDensity; - if (parseInt(infillSteps.properties.value) == 0) - { - previousInfillDensity = parseInt(infillDensity.properties.value) - newInfillDensity = 90 - } else { - newInfillDensity = previousInfillDensity - } - Cura.MachineManager.setSettingForAllExtruders("infill_sparse_density", "value", String(newInfillDensity)) - - var infill_steps_value = 0 - if (parseInt(infillSteps.properties.value) == 0) - { - infill_steps_value = 5 - } - - Cura.MachineManager.setSettingForAllExtruders("gradual_infill_steps", "value", infill_steps_value) - } - - onEntered: base.showTooltip(enableGradualInfillCheckBox, Qt.point(-infillCellRight.x, 0), - catalog.i18nc("@label", "Gradual infill will gradually increase the amount of infill towards the top.")) - - onExited: base.hideTooltip() - - } - - Label - { - id: gradualInfillLabel - height: parent.height - anchors.left: enableGradualInfillCheckBox.right - anchors.leftMargin: Math.round(UM.Theme.getSize("thick_margin").width / 2) - verticalAlignment: Text.AlignVCenter; - text: catalog.i18nc("@label", "Enable gradual") - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - } - } - - // Infill list model for mapping icon - ListModel - { - id: infillModel - Component.onCompleted: - { - infillModel.append({ - percentageMin: -1, - percentageMax: 0, - stepsMin: -1, - stepsMax: 0, - icon: "hollow" - }) - infillModel.append({ - percentageMin: 0, - percentageMax: 40, - stepsMin: -1, - stepsMax: 0, - icon: "sparse" - }) - infillModel.append({ - percentageMin: 40, - percentageMax: 89, - stepsMin: -1, - stepsMax: 0, - icon: "dense" - }) - infillModel.append({ - percentageMin: 90, - percentageMax: 9999999999, - stepsMin: -1, - stepsMax: 0, - icon: "solid" - }) - infillModel.append({ - percentageMin: 0, - percentageMax: 9999999999, - stepsMin: 1, - stepsMax: 9999999999, - icon: "gradual" - }) - } - } - } - - // - // Enable support - // - Cura.IconLabel - { - id: enableSupportLabel - visible: enableSupportCheckBox.visible - source: UM.Theme.getIcon("category_support") - text: catalog.i18nc("@label", "Support") - - anchors - { - top: infillCellRight.bottom - topMargin: Math.round(UM.Theme.getSize("thick_margin").height * 1.5) - left: parent.left - right: infillCellLeft.right - rightMargin: UM.Theme.getSize("thick_margin").width - verticalCenter: enableSupportCheckBox.verticalCenter - } - } - - CheckBox - { - id: enableSupportCheckBox - property alias _hovered: enableSupportMouseArea.containsMouse - - anchors.top: enableSupportLabel.top - anchors.left: infillCellRight.left - - style: UM.Theme.styles.checkbox - enabled: base.settingsEnabled - - visible: supportEnabled.properties.enabled == "True" - checked: supportEnabled.properties.value == "True" - - MouseArea - { - id: enableSupportMouseArea - anchors.fill: parent - hoverEnabled: true - onClicked: supportEnabled.setPropertyValue("value", supportEnabled.properties.value != "True") - - onEntered: base.showTooltip(enableSupportCheckBox, Qt.point(-enableSupportCheckBox.x, 0), - catalog.i18nc("@label", "Generate structures to support parts of the model which have overhangs. Without these structures, such parts would collapse during printing.")) - - onExited: base.hideTooltip() - - } - } - - ComboBox - { - id: supportExtruderCombobox - visible: enableSupportCheckBox.visible && (supportEnabled.properties.value == "True") && (extrudersEnabledCount.properties.value > 1) - model: extruderModel - - property string color_override: "" // for manually setting values - property string color: // is evaluated automatically, but the first time is before extruderModel being filled - { - var current_extruder = extruderModel.get(currentIndex); - color_override = ""; - if (current_extruder === undefined) return "" - return (current_extruder.color) ? current_extruder.color : ""; - } - - textRole: "text" // this solves that the combobox isn't populated in the first time Cura is started - - anchors.top: enableSupportCheckBox.top - - anchors.left: enableSupportCheckBox.right - anchors.leftMargin: Math.round(UM.Theme.getSize("thick_margin").width / 2) - - width: Math.round(UM.Theme.getSize("print_setup_widget").width * .55) - Math.round(UM.Theme.getSize("thick_margin").width / 2) - enableSupportCheckBox.width - height: ((supportEnabled.properties.value == "True") && (machineExtruderCount.properties.value > 1)) ? UM.Theme.getSize("setting_control").height : 0 - - Behavior on height { NumberAnimation { duration: 100 } } - - style: UM.Theme.styles.combobox_color - enabled: base.settingsEnabled - property alias _hovered: supportExtruderMouseArea.containsMouse - - currentIndex: - { - if (supportExtruderNr.properties == null) - { - return Cura.MachineManager.defaultExtruderPosition - } - else - { - var extruder = parseInt(supportExtruderNr.properties.value) - if ( extruder === -1) - { - return Cura.MachineManager.defaultExtruderPosition - } - return extruder; - } - } - - onActivated: supportExtruderNr.setPropertyValue("value", String(index)) - - MouseArea - { - id: supportExtruderMouseArea - anchors.fill: parent - hoverEnabled: true - enabled: base.settingsEnabled - acceptedButtons: Qt.NoButton - onEntered: - { - base.showTooltip(supportExtruderCombobox, Qt.point(-supportExtruderCombobox.x, 0), - catalog.i18nc("@label", "Select which extruder to use for support. This will build up supporting structures below the model to prevent the model from sagging or printing in mid air.")); - } - onExited: base.hideTooltip() - - } - - function updateCurrentColor() - { - var current_extruder = extruderModel.get(currentIndex) - if (current_extruder !== undefined) - { - supportExtruderCombobox.color_override = current_extruder.color - } - } - - } - - Cura.IconLabel - { - id: adhesionHelperLabel - visible: adhesionCheckBox.visible - source: UM.Theme.getIcon("category_adhesion") - text: catalog.i18nc("@label", "Adhesion") - - anchors - { - left: parent.left - right: infillCellLeft.right - rightMargin: UM.Theme.getSize("thick_margin").width - verticalCenter: adhesionCheckBox.verticalCenter - } - } - - CheckBox - { - id: adhesionCheckBox - property alias _hovered: adhesionMouseArea.containsMouse - - anchors.top: enableSupportCheckBox.bottom - anchors.topMargin: UM.Theme.getSize("thick_margin").height - anchors.left: infillCellRight.left - - //: Setting enable printing build-plate adhesion helper checkbox - style: UM.Theme.styles.checkbox - enabled: base.settingsEnabled - - visible: platformAdhesionType.properties.enabled == "True" - checked: platformAdhesionType.properties.value != "skirt" && platformAdhesionType.properties.value != "none" - - MouseArea - { - id: adhesionMouseArea - anchors.fill: parent - hoverEnabled: true - enabled: base.settingsEnabled - onClicked: - { - var adhesionType = "skirt" - if(!parent.checked) - { - // Remove the "user" setting to see if the rest of the stack prescribes a brim or a raft - platformAdhesionType.removeFromContainer(0) - adhesionType = platformAdhesionType.properties.value - if(adhesionType == "skirt" || adhesionType == "none") - { - // If the rest of the stack doesn't prescribe an adhesion-type, default to a brim - adhesionType = "brim" - } - } - platformAdhesionType.setPropertyValue("value", adhesionType) - } - onEntered: - { - base.showTooltip(adhesionCheckBox, Qt.point(-adhesionCheckBox.x, 0), - catalog.i18nc("@label", "Enable printing a brim or raft. This will add a flat area around or under your object which is easy to cut off afterwards.")); - } - onExited: base.hideTooltip() - - } - } - - ListModel - { - id: extruderModel - Component.onCompleted: populateExtruderModel() - } - - //: Model used to populate the extrudelModel - Cura.ExtrudersModel - { - id: extruders - onModelChanged: populateExtruderModel() - } - - Item - { - id: tipsCell - anchors.top: adhesionCheckBox.visible ? adhesionCheckBox.bottom : (enableSupportCheckBox.visible ? supportExtruderCombobox.bottom : infillCellRight.bottom) - anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height * 2) - anchors.left: parent.left - width: parent.width - height: tipsText.contentHeight * tipsText.lineCount + model: qualityModel Label { - id: tipsText - anchors.left: parent.left - anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("thick_margin").width - anchors.top: parent.top - wrapMode: Text.WordWrap - text: catalog.i18nc("@label", "Need help improving your prints?
    Read the Ultimaker Troubleshooting Guides").arg("https://ultimaker.com/en/troubleshooting") - font: UM.Theme.getFont("default"); - color: UM.Theme.getColor("text"); - linkColor: UM.Theme.getColor("text_link") - onLinkActivated: Qt.openUrlExternally(link) + anchors.verticalCenter: parent.verticalCenter + anchors.top: parent.bottom + color: (Cura.MachineManager.activeMachine != null && Cura.QualityProfilesDropDownMenuModel.getItem(index).available) ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") + text: + { + var result = "" + if(Cura.MachineManager.activeMachine != null) + { + result = Cura.QualityProfilesDropDownMenuModel.getItem(index).layer_height + + if(result == undefined) + { + result = ""; + } + else + { + result = Number(Math.round(result + "e+2") + "e-2"); //Round to 2 decimals. Javascript makes this difficult... + if (result == undefined || result != result) //Parse failure. + { + result = ""; + } + } + } + return result + } + + x: + { + // Make sure the text aligns correctly with each tick + if (qualityModel.totalTicks == 0) + { + // If there is only one tick, align it centrally + return Math.round(((base.width * 0.55) - width) / 2) + } + else if (index == 0) + { + return Math.round(base.width * 0.55 / qualityModel.totalTicks) * index + } + else if (index == qualityModel.totalTicks) + { + return Math.round(base.width * 0.55 / qualityModel.totalTicks) * index - width + } + else + { + return Math.round((base.width * 0.55 / qualityModel.totalTicks) * index - (width / 2)) + } + } + } + } + } + + //Print speed slider + Rectangle + { + id: speedSlider + width: Math.round(base.width * 0.55) + height: UM.Theme.getSize("thick_margin").height + anchors.right: parent.right + anchors.top: parent.top + anchors.topMargin: UM.Theme.getSize("thick_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("thick_margin").width, customisedSettings.height), content) + } + else + { + base.hideTooltip() + } + } + + id: unavailableLineToolTip + height: 20 * screenScaleFactor // 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 qualityModel.qualitySliderStepWidth * qualityModel.totalTicks + } + 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) + } + } + + Item + { + id: rightArea + width: + { + if(qualityModel.availableTotalTicks == 0) + return 0 + + return qualityModel.qualitySliderMarginRight - 10 + } + height: parent.height + 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) + } } } - UM.SettingPropertyProvider + // Draw Unavailable line + Rectangle { - id: infillExtruderNumber - containerStackId: Cura.MachineManager.activeStackId - key: "infill_extruder_nr" - watchedProperties: [ "value" ] - storeIndex: 0 + id: groovechildrect + width: Math.round(base.width * 0.55) + height: 2 * screenScaleFactor + color: UM.Theme.getColor("quality_slider_unavailable") + anchors.verticalCenter: qualitySlider.verticalCenter + x: 0 } - UM.SettingPropertyProvider + // Draw ticks + Repeater { - id: infillDensity - containerStackId: Cura.MachineManager.activeStackId - key: "infill_sparse_density" - watchedProperties: [ "value" ] - storeIndex: 0 + id: qualityRepeater + model: qualityModel.totalTicks > 0 ? qualityModel : 0 + + Rectangle + { + color: Cura.QualityProfilesDropDownMenuModel.getItem(index).available ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") + implicitWidth: 5 * screenScaleFactor + implicitHeight: implicitWidth + anchors.verticalCenter: qualitySlider.verticalCenter + x: Math.round(qualityModel.qualitySliderStepWidth * index) + radius: Math.round(implicitWidth / 2) + } } - UM.SettingPropertyProvider + Slider { - id: infillSteps - containerStackId: Cura.MachineManager.activeStackId - key: "gradual_infill_steps" - watchedProperties: ["value", "enabled"] - storeIndex: 0 + id: qualitySlider + height: UM.Theme.getSize("thick_margin").height + anchors.bottom: speedSlider.bottom + enabled: qualityModel.totalTicks > 0 && !Cura.SimpleModeSettingsManager.isProfileCustomized + visible: qualityModel.availableTotalTicks > 0 + updateValueWhileDragging : false + + minimumValue: qualityModel.qualitySliderAvailableMin >= 0 ? qualityModel.qualitySliderAvailableMin : 0 + // maximumValue must be greater than minimumValue to be able to see the handle. While the value is strictly + // speaking not always correct, it seems to have the correct behavior (switching from 0 available to 1 available) + maximumValue: qualityModel.qualitySliderAvailableMax >= 1 ? qualityModel.qualitySliderAvailableMax : 1 + stepSize: 1 + + value: qualityModel.qualitySliderActiveIndex + + width: qualityModel.qualitySliderStepWidth * (qualityModel.availableTotalTicks - 1) + + anchors.right: parent.right + anchors.rightMargin: qualityModel.qualitySliderMarginRight + + style: SliderStyle + { + //Draw Available line + groove: Rectangle + { + implicitHeight: 2 * screenScaleFactor + color: UM.Theme.getColor("quality_slider_available") + radius: Math.round(height / 2) + } + handle: Item + { + Rectangle + { + id: qualityhandleButton + anchors.centerIn: parent + color: UM.Theme.getColor("quality_slider_available") + implicitWidth: 10 * screenScaleFactor + implicitHeight: implicitWidth + radius: Math.round(implicitWidth / 2) + visible: !Cura.SimpleModeSettingsManager.isProfileCustomized && !Cura.SimpleModeSettingsManager.isProfileUserCreated && qualityModel.existingQualityProfile + } + } + } + + onValueChanged: + { + // only change if an active machine is set and the slider is visible at all. + if (Cura.MachineManager.activeMachine != null && visible) + { + // prevent updating during view initializing. Trigger only if the value changed by user + if (qualitySlider.value != qualityModel.qualitySliderActiveIndex && qualityModel.qualitySliderActiveIndex != -1) + { + // start updating with short delay + qualitySliderChangeTimer.start() + } + } + } } - UM.SettingPropertyProvider + MouseArea { - id: platformAdhesionType - containerStack: Cura.MachineManager.activeMachine - removeUnusedValue: false //Doesn't work with settings that are resolved. - key: "adhesion_type" - watchedProperties: [ "value", "enabled" ] - storeIndex: 0 - } + id: speedSliderMouseArea + anchors.fill: parent + hoverEnabled: true + enabled: Cura.SimpleModeSettingsManager.isProfileUserCreated - UM.SettingPropertyProvider - { - id: supportEnabled - containerStack: Cura.MachineManager.activeMachine - key: "support_enable" - watchedProperties: [ "value", "enabled", "description" ] - storeIndex: 0 - } - - UM.SettingPropertyProvider - { - id: extrudersEnabledCount - containerStack: Cura.MachineManager.activeMachine - key: "extruders_enabled_count" - watchedProperties: [ "value" ] - storeIndex: 0 - } - - UM.SettingPropertyProvider - { - id: supportExtruderNr - containerStack: Cura.MachineManager.activeMachine - key: "support_extruder_nr" - watchedProperties: [ "value" ] - storeIndex: 0 + onEntered: + { + var content = catalog.i18nc("@tooltip","A custom profile is currently active. To enable the quality slider, choose a default quality profile in Custom tab") + base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, customisedSettings.height), content) + } + onExited: base.hideTooltip() } } + + UM.SimpleButton + { + id: customisedSettings + + visible: Cura.SimpleModeSettingsManager.isProfileCustomized || Cura.SimpleModeSettingsManager.isProfileUserCreated + height: Math.round(speedSlider.height * 0.8) + width: Math.round(speedSlider.height * 0.8) + + anchors.verticalCenter: speedSlider.verticalCenter + anchors.right: speedSlider.left + anchors.rightMargin: Math.round(UM.Theme.getSize("thick_margin").width / 2) + + color: hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button"); + iconSource: UM.Theme.getIcon("reset"); + + onClicked: + { + // if the current profile is user-created, switch to a built-in quality + Cura.MachineManager.resetToUseDefaultQuality() + } + onEntered: + { + var content = catalog.i18nc("@tooltip","You have modified some profile settings. If you want to change these go to custom mode.") + base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, customisedSettings.height), content) + } + onExited: base.hideTooltip() + } + } + + // + // Infill + // + Item + { + id: infillCellLeft + + anchors.top: qualityRow.bottom + anchors.topMargin: UM.Theme.getSize("thick_margin").height * 2 + anchors.left: parent.left + + width: Math.round(UM.Theme.getSize("print_setup_widget").width * .45) - UM.Theme.getSize("thick_margin").width + + Cura.IconWithText + { + id: infillLabel + source: UM.Theme.getIcon("category_infill") + text: catalog.i18nc("@label", "Infill") + " (%)" + + anchors + { + top: parent.top + topMargin: Math.round(UM.Theme.getSize("thick_margin").height * 1.7) + left: parent.left + } + } + } + + Item + { + id: infillCellRight + + height: infillSlider.height + UM.Theme.getSize("thick_margin").height + enableGradualInfillCheckBox.visible * (enableGradualInfillCheckBox.height + UM.Theme.getSize("thick_margin").height) + width: Math.round(UM.Theme.getSize("print_setup_widget").width * .55) + + anchors.left: infillCellLeft.right + anchors.top: infillCellLeft.top + anchors.topMargin: UM.Theme.getSize("thick_margin").height + + Label { + id: selectedInfillRateText + + anchors.left: infillSlider.left + anchors.right: parent.right + + text: parseInt(infillDensity.properties.value) + "%" + horizontalAlignment: Text.AlignLeft + + color: infillSlider.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") + } + + // We use a binding to make sure that after manually setting infillSlider.value it is still bound to the property provider + Binding + { + target: infillSlider + property: "value" + value: parseInt(infillDensity.properties.value) + } + + Slider + { + id: infillSlider + + anchors.top: selectedInfillRateText.bottom + anchors.left: parent.left + anchors.right: infillIcon.left + anchors.rightMargin: UM.Theme.getSize("thick_margin").width + + height: UM.Theme.getSize("thick_margin").height + width: parseInt(infillCellRight.width - UM.Theme.getSize("thick_margin").width - style.handleWidth) + + minimumValue: 0 + maximumValue: 100 + stepSize: 1 + tickmarksEnabled: true + + // disable slider when gradual support is enabled + enabled: parseInt(infillSteps.properties.value) == 0 + + // set initial value from stack + value: parseInt(infillDensity.properties.value) + + onValueChanged: + { + + // Don't round the value if it's already the same + if (parseInt(infillDensity.properties.value) == infillSlider.value) + { + return + } + + // Round the slider value to the nearest multiple of 10 (simulate step size of 10) + var roundedSliderValue = Math.round(infillSlider.value / 10) * 10 + + // Update the slider value to represent the rounded value + infillSlider.value = roundedSliderValue + + // Update value only if the Recomended mode is Active, + // Otherwise if I change the value in the Custom mode the Recomended view will try to repeat + // same operation + var active_mode = UM.Preferences.getValue("cura/active_mode") + + if (active_mode == 0 || active_mode == "simple") + { + Cura.MachineManager.setSettingForAllExtruders("infill_sparse_density", "value", roundedSliderValue) + Cura.MachineManager.resetSettingForAllExtruders("infill_line_distance") + } + } + + style: SliderStyle + { + groove: Rectangle + { + id: groove + implicitWidth: 200 * screenScaleFactor + implicitHeight: 2 * screenScaleFactor + color: control.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") + radius: 1 + } + + handle: Item + { + Rectangle + { + id: handleButton + anchors.centerIn: parent + color: control.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") + implicitWidth: 10 * screenScaleFactor + implicitHeight: 10 * screenScaleFactor + radius: 10 * screenScaleFactor + } + } + + tickmarks: Repeater + { + id: repeater + model: control.maximumValue / control.stepSize + 1 + + // check if a tick should be shown based on it's index and wether the infill density is a multiple of 10 (slider step size) + function shouldShowTick (index) + { + if (index % 10 == 0) + { + return true + } + return false + } + + Rectangle + { + anchors.verticalCenter: parent.verticalCenter + color: control.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") + width: 1 * screenScaleFactor + height: 6 * screenScaleFactor + x: Math.round(styleData.handleWidth / 2 + index * ((repeater.width - styleData.handleWidth) / (repeater.count-1))) + visible: shouldShowTick(index) + } + } + } + } + + Rectangle + { + id: infillIcon + + width: Math.round((parent.width / 5) - (UM.Theme.getSize("thick_margin").width)) + height: width + + anchors.right: parent.right + anchors.top: parent.top + anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height / 2) + + // we loop over all density icons and only show the one that has the current density and steps + Repeater + { + id: infillIconList + model: infillModel + anchors.fill: parent + + function activeIndex () + { + for (var i = 0; i < infillModel.count; i++) + { + var density = Math.round(infillDensity.properties.value) + var steps = Math.round(infillSteps.properties.value) + var infillModelItem = infillModel.get(i) + + if (infillModelItem != "undefined" + && density >= infillModelItem.percentageMin + && density <= infillModelItem.percentageMax + && steps >= infillModelItem.stepsMin + && steps <= infillModelItem.stepsMax) + { + return i + } + } + return -1 + } + + Rectangle + { + anchors.fill: parent + visible: infillIconList.activeIndex() == index + + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("quality_slider_unavailable") + + UM.RecolorImage + { + anchors.fill: parent + anchors.margins: 2 * screenScaleFactor + sourceSize.width: width + sourceSize.height: width + source: UM.Theme.getIcon(model.icon) + color: UM.Theme.getColor("quality_slider_unavailable") + } + } + } + } + + // Gradual Support Infill Checkbox + CheckBox + { + id: enableGradualInfillCheckBox + property alias _hovered: enableGradualInfillMouseArea.containsMouse + + anchors.top: infillSlider.bottom + anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height / 2) // closer to slider since it belongs to the same category + anchors.left: infillCellRight.left + + style: UM.Theme.styles.checkbox + enabled: base.settingsEnabled + visible: infillSteps.properties.enabled == "True" + checked: parseInt(infillSteps.properties.value) > 0 + + MouseArea + { + id: enableGradualInfillMouseArea + + anchors.fill: parent + hoverEnabled: true + enabled: true + + property var previousInfillDensity: parseInt(infillDensity.properties.value) + + onClicked: + { + // Set to 90% only when enabling gradual infill + var newInfillDensity; + if (parseInt(infillSteps.properties.value) == 0) + { + previousInfillDensity = parseInt(infillDensity.properties.value) + newInfillDensity = 90 + } else { + newInfillDensity = previousInfillDensity + } + Cura.MachineManager.setSettingForAllExtruders("infill_sparse_density", "value", String(newInfillDensity)) + + var infill_steps_value = 0 + if (parseInt(infillSteps.properties.value) == 0) + { + infill_steps_value = 5 + } + + Cura.MachineManager.setSettingForAllExtruders("gradual_infill_steps", "value", infill_steps_value) + } + + onEntered: base.showTooltip(enableGradualInfillCheckBox, Qt.point(-infillCellRight.x, 0), + catalog.i18nc("@label", "Gradual infill will gradually increase the amount of infill towards the top.")) + + onExited: base.hideTooltip() + + } + + Label + { + id: gradualInfillLabel + height: parent.height + anchors.left: enableGradualInfillCheckBox.right + anchors.leftMargin: Math.round(UM.Theme.getSize("thick_margin").width / 2) + verticalAlignment: Text.AlignVCenter; + text: catalog.i18nc("@label", "Enable gradual") + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + } + } + + // Infill list model for mapping icon + ListModel + { + id: infillModel + Component.onCompleted: + { + infillModel.append({ + percentageMin: -1, + percentageMax: 0, + stepsMin: -1, + stepsMax: 0, + icon: "hollow" + }) + infillModel.append({ + percentageMin: 0, + percentageMax: 40, + stepsMin: -1, + stepsMax: 0, + icon: "sparse" + }) + infillModel.append({ + percentageMin: 40, + percentageMax: 89, + stepsMin: -1, + stepsMax: 0, + icon: "dense" + }) + infillModel.append({ + percentageMin: 90, + percentageMax: 9999999999, + stepsMin: -1, + stepsMax: 0, + icon: "solid" + }) + infillModel.append({ + percentageMin: 0, + percentageMax: 9999999999, + stepsMin: 1, + stepsMax: 9999999999, + icon: "gradual" + }) + } + } + } + + // + // Enable support + // + Cura.IconWithText + { + id: enableSupportLabel + visible: enableSupportCheckBox.visible + source: UM.Theme.getIcon("category_support") + text: catalog.i18nc("@label", "Support") + + anchors + { + top: infillCellRight.bottom + topMargin: Math.round(UM.Theme.getSize("thick_margin").height * 1.5) + left: parent.left + right: infillCellLeft.right + rightMargin: UM.Theme.getSize("thick_margin").width + verticalCenter: enableSupportCheckBox.verticalCenter + } + } + + CheckBox + { + id: enableSupportCheckBox + property alias _hovered: enableSupportMouseArea.containsMouse + + anchors.top: enableSupportLabel.top + anchors.left: infillCellRight.left + + style: UM.Theme.styles.checkbox + enabled: base.settingsEnabled + + visible: supportEnabled.properties.enabled == "True" + checked: supportEnabled.properties.value == "True" + + MouseArea + { + id: enableSupportMouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: supportEnabled.setPropertyValue("value", supportEnabled.properties.value != "True") + + onEntered: base.showTooltip(enableSupportCheckBox, Qt.point(-enableSupportCheckBox.x, 0), + catalog.i18nc("@label", "Generate structures to support parts of the model which have overhangs. Without these structures, such parts would collapse during printing.")) + + onExited: base.hideTooltip() + + } + } + + ComboBox + { + id: supportExtruderCombobox + visible: enableSupportCheckBox.visible && (supportEnabled.properties.value == "True") && (extrudersEnabledCount.properties.value > 1) + model: extruderModel + + property string color_override: "" // for manually setting values + property string color: // is evaluated automatically, but the first time is before extruderModel being filled + { + var current_extruder = extruderModel.get(currentIndex); + color_override = ""; + if (current_extruder === undefined) return "" + return (current_extruder.color) ? current_extruder.color : ""; + } + + textRole: "text" // this solves that the combobox isn't populated in the first time Cura is started + + anchors.top: enableSupportCheckBox.top + + anchors.left: enableSupportCheckBox.right + anchors.leftMargin: Math.round(UM.Theme.getSize("thick_margin").width / 2) + + width: Math.round(UM.Theme.getSize("print_setup_widget").width * .55) - Math.round(UM.Theme.getSize("thick_margin").width / 2) - enableSupportCheckBox.width + height: ((supportEnabled.properties.value == "True") && (machineExtruderCount.properties.value > 1)) ? UM.Theme.getSize("setting_control").height : 0 + + Behavior on height { NumberAnimation { duration: 100 } } + + style: UM.Theme.styles.combobox_color + enabled: base.settingsEnabled + property alias _hovered: supportExtruderMouseArea.containsMouse + + currentIndex: + { + if (supportExtruderNr.properties == null) + { + return Cura.MachineManager.defaultExtruderPosition + } + else + { + var extruder = parseInt(supportExtruderNr.properties.value) + if ( extruder === -1) + { + return Cura.MachineManager.defaultExtruderPosition + } + return extruder; + } + } + + onActivated: supportExtruderNr.setPropertyValue("value", String(index)) + + MouseArea + { + id: supportExtruderMouseArea + anchors.fill: parent + hoverEnabled: true + enabled: base.settingsEnabled + acceptedButtons: Qt.NoButton + onEntered: + { + base.showTooltip(supportExtruderCombobox, Qt.point(-supportExtruderCombobox.x, 0), + catalog.i18nc("@label", "Select which extruder to use for support. This will build up supporting structures below the model to prevent the model from sagging or printing in mid air.")); + } + onExited: base.hideTooltip() + + } + + function updateCurrentColor() + { + var current_extruder = extruderModel.get(currentIndex) + if (current_extruder !== undefined) + { + supportExtruderCombobox.color_override = current_extruder.color + } + } + + } + + Cura.IconWithText + { + id: adhesionHelperLabel + visible: adhesionCheckBox.visible + source: UM.Theme.getIcon("category_adhesion") + text: catalog.i18nc("@label", "Adhesion") + + anchors + { + left: parent.left + right: infillCellLeft.right + rightMargin: UM.Theme.getSize("thick_margin").width + verticalCenter: adhesionCheckBox.verticalCenter + } + } + + CheckBox + { + id: adhesionCheckBox + property alias _hovered: adhesionMouseArea.containsMouse + + anchors.top: enableSupportCheckBox.bottom + anchors.topMargin: UM.Theme.getSize("thick_margin").height + anchors.left: infillCellRight.left + + //: Setting enable printing build-plate adhesion helper checkbox + style: UM.Theme.styles.checkbox + enabled: base.settingsEnabled + + visible: platformAdhesionType.properties.enabled == "True" + checked: platformAdhesionType.properties.value != "skirt" && platformAdhesionType.properties.value != "none" + + MouseArea + { + id: adhesionMouseArea + anchors.fill: parent + hoverEnabled: true + enabled: base.settingsEnabled + onClicked: + { + var adhesionType = "skirt" + if(!parent.checked) + { + // Remove the "user" setting to see if the rest of the stack prescribes a brim or a raft + platformAdhesionType.removeFromContainer(0) + adhesionType = platformAdhesionType.properties.value + if(adhesionType == "skirt" || adhesionType == "none") + { + // If the rest of the stack doesn't prescribe an adhesion-type, default to a brim + adhesionType = "brim" + } + } + platformAdhesionType.setPropertyValue("value", adhesionType) + } + onEntered: + { + base.showTooltip(adhesionCheckBox, Qt.point(-adhesionCheckBox.x, 0), + catalog.i18nc("@label", "Enable printing a brim or raft. This will add a flat area around or under your object which is easy to cut off afterwards.")); + } + onExited: base.hideTooltip() + + } + } + + ListModel + { + id: extruderModel + Component.onCompleted: populateExtruderModel() + } + + //: Model used to populate the extrudelModel + Cura.ExtrudersModel + { + id: extruders + onModelChanged: populateExtruderModel() + } + + Item + { + id: tipsCell + anchors.top: adhesionCheckBox.visible ? adhesionCheckBox.bottom : (enableSupportCheckBox.visible ? supportExtruderCombobox.bottom : infillCellRight.bottom) + anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height * 2) + anchors.left: parent.left + width: parent.width + height: tipsText.contentHeight * tipsText.lineCount + + Label + { + id: tipsText + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: UM.Theme.getSize("thick_margin").width + anchors.top: parent.top + wrapMode: Text.WordWrap + text: catalog.i18nc("@label", "Need help improving your prints?
    Read the Ultimaker Troubleshooting Guides").arg("https://ultimaker.com/en/troubleshooting") + font: UM.Theme.getFont("default"); + color: UM.Theme.getColor("text"); + linkColor: UM.Theme.getColor("text_link") + onLinkActivated: Qt.openUrlExternally(link) + } + } + + UM.SettingPropertyProvider + { + id: infillExtruderNumber + containerStackId: Cura.MachineManager.activeStackId + key: "infill_extruder_nr" + watchedProperties: [ "value" ] + storeIndex: 0 + } + + UM.SettingPropertyProvider + { + id: infillDensity + containerStackId: Cura.MachineManager.activeStackId + key: "infill_sparse_density" + watchedProperties: [ "value" ] + storeIndex: 0 + } + + UM.SettingPropertyProvider + { + id: infillSteps + containerStackId: Cura.MachineManager.activeStackId + key: "gradual_infill_steps" + watchedProperties: ["value", "enabled"] + storeIndex: 0 + } + + UM.SettingPropertyProvider + { + id: platformAdhesionType + containerStack: Cura.MachineManager.activeMachine + removeUnusedValue: false //Doesn't work with settings that are resolved. + key: "adhesion_type" + watchedProperties: [ "value", "enabled" ] + storeIndex: 0 + } + + UM.SettingPropertyProvider + { + id: supportEnabled + containerStack: Cura.MachineManager.activeMachine + key: "support_enable" + watchedProperties: [ "value", "enabled", "description" ] + storeIndex: 0 + } + + UM.SettingPropertyProvider + { + id: extrudersEnabledCount + containerStack: Cura.MachineManager.activeMachine + key: "extruders_enabled_count" + watchedProperties: [ "value" ] + storeIndex: 0 + } + + UM.SettingPropertyProvider + { + id: supportExtruderNr + containerStack: Cura.MachineManager.activeMachine + key: "support_extruder_nr" + watchedProperties: [ "value" ] + storeIndex: 0 } function populateExtruderModel() diff --git a/resources/qml/qmldir b/resources/qml/qmldir index 2475f398f8..6db8e0c544 100644 --- a/resources/qml/qmldir +++ b/resources/qml/qmldir @@ -9,6 +9,7 @@ MaterialMenu 1.0 MaterialMenu.qml NozzleMenu 1.0 NozzleMenu.qml ActionPanelWidget 1.0 ActionPanelWidget.qml IconLabel 1.0 IconLabel.qml +IconWithText 1.0 IconWithText.qml OutputDevicesActionButton 1.0 OutputDevicesActionButton.qml ExpandableComponent 1.0 ExpandableComponent.qml PrinterTypeLabel 1.0 PrinterTypeLabel.qml diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 08ea73a7c5..dfaa9008f7 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -372,6 +372,7 @@ "print_setup_mode_toggle": [0.0, 2.0], "print_setup_item": [0.0, 2.0], "print_setup_extruder_box": [0.0, 6.0], + "print_setup_widget_header": [0.0, 3.0], "configuration_selector_mode_tabs": [0.0, 3.0], From b9d9a3586809b7e517550d402d31a9cf419e59ba Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Thu, 29 Nov 2018 00:00:22 +0100 Subject: [PATCH 0516/1240] Move profile dropdown to another file CURA-5941 --- resources/qml/GlobalProfileButton.qml | 89 ++++++++++++++++++++++++++ resources/qml/PrintSetupSelector.qml | 46 +++++++------ resources/qml/Settings/SettingView.qml | 89 +------------------------- 3 files changed, 115 insertions(+), 109 deletions(-) create mode 100644 resources/qml/GlobalProfileButton.qml diff --git a/resources/qml/GlobalProfileButton.qml b/resources/qml/GlobalProfileButton.qml new file mode 100644 index 0000000000..bac2732037 --- /dev/null +++ b/resources/qml/GlobalProfileButton.qml @@ -0,0 +1,89 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 1.1 +import QtQuick.Controls.Styles 1.1 +import QtQuick.Layouts 1.2 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +import "Menus" + +Item +{ + id: globalProfileRow + height: UM.Theme.getSize("print_setup_item").height + + Label + { + id: globalProfileLabel + text: catalog.i18nc("@label","Profile:") + textFormat: Text.PlainText + width: Math.round(parent.width * 0.45 - UM.Theme.getSize("thick_margin").width - 2) + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + verticalAlignment: Text.AlignVCenter + anchors.top: parent.top + anchors.bottom: parent.bottom + } + + ToolButton + { + id: globalProfileSelection + + text: generateActiveQualityText() + width: Math.round(parent.width * 0.55) + height: UM.Theme.getSize("setting_control").height + anchors.left: globalProfileLabel.right + anchors.right: parent.right + tooltip: Cura.MachineManager.activeQualityOrQualityChangesName + style: UM.Theme.styles.sidebar_header_button + activeFocusOnPress: true + menu: ProfileMenu { } + + function generateActiveQualityText () { + var result = Cura.MachineManager.activeQualityOrQualityChangesName; + + if (Cura.MachineManager.isActiveQualitySupported) { + if (Cura.MachineManager.activeQualityLayerHeight > 0) { + result += " " + result += " - " + result += Cura.MachineManager.activeQualityLayerHeight + "mm" + result += "" + } + } + + return result + } + + UM.SimpleButton + { + id: customisedSettings + + visible: Cura.MachineManager.hasUserSettings + height: Math.round(parent.height * 0.6) + width: Math.round(parent.height * 0.6) + + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + anchors.rightMargin: Math.round(UM.Theme.getSize("setting_preferences_button_margin").width - UM.Theme.getSize("thick_margin").width) + + color: hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button"); + iconSource: UM.Theme.getIcon("star"); + + onClicked: + { + forceActiveFocus(); + Cura.Actions.manageProfiles.trigger() + } + onEntered: + { + var content = catalog.i18nc("@tooltip","Some setting/override values are different from the values stored in the profile.\n\nClick to open the profile manager.") + base.showTooltip(globalProfileRow, Qt.point(-UM.Theme.getSize("thick_margin").width, 0), content) + } + onExited: base.hideTooltip() + } + } +} \ No newline at end of file diff --git a/resources/qml/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector.qml index 85472bae8d..855af9f9fa 100644 --- a/resources/qml/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector.qml @@ -126,7 +126,9 @@ Cura.ExpandableComponent popupItem: Rectangle { - property var total_height: popupItemHeader.height + popupItemContent.height + footerControll.height + UM.Theme.getSize("narrow_margin").height * 2 + + property var totalMargins: UM.Theme.getSize("narrow_margin").height * 11 + property var total_height: popupItemHeader.height + popupItemContent.height + totalMargins id: popupItemWrapper height: total_height @@ -157,9 +159,9 @@ Cura.ExpandableComponent anchors { - topMargin: UM.Theme.getSize("sidebar_margin").height + //topMargin: UM.Theme.getSize("sidebar_margin").height left: parent.left - leftMargin: UM.Theme.getSize("narrow_margin").height + leftMargin: UM.Theme.getSize("narrow_margin").height * 2 } } @@ -169,8 +171,6 @@ Cura.ExpandableComponent height: UM.Theme.getSize("default_lining").height anchors.top: popupItemHeaderText.bottom color: UM.Theme.getColor("action_button_border") - - } Button @@ -209,21 +209,33 @@ Cura.ExpandableComponent { id: popupItemContent width: parent.width - height: tabBar.height + sidebarContents.height + height: globalProfileRow.height + tabBar.height + UM.Theme.getSize("print_setup_widget").height - UM.Theme.getSize("print_setup_item").height anchors { top: popupItemHeader.bottom - topMargin: UM.Theme.getSize("narrow_margin").height + topMargin: UM.Theme.getSize("default_margin").height * 1.5 right: parent.right left: parent.left leftMargin: UM.Theme.getSize("default_margin").width rightMargin: UM.Theme.getSize("default_margin").width } + GlobalProfileButton + { + id: globalProfileRow + anchors + { + top: parent.top + left: parent.left + right: parent.right + } + } + UM.TabRow { id: tabBar - anchors.topMargin: UM.Theme.getSize("default_margin").height + anchors.top: globalProfileRow.bottom + anchors.topMargin: UM.Theme.getSize("default_margin").height * 1.5 onCurrentIndexChanged: Cura.ExtruderManager.setActiveExtruderIndex(currentIndex) z: 1 Repeater @@ -280,8 +292,6 @@ Cura.ExpandableComponent anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: parent.right - height: UM.Theme.getSize("print_setup_widget").height - border.width: UM.Theme.getSize("default_lining").width * 2 border.color: UM.Theme.getColor("action_button_border") @@ -296,8 +306,8 @@ Cura.ExpandableComponent SidebarAdvanced { - anchors.topMargin: UM.Theme.getSize("print_setup_content_top_margin").height - anchors.bottomMargin: 2 //don't overlap bottom border + anchors.topMargin: Math.round(UM.Theme.getSize("wide_margin").height / 1.7) + anchors.bottomMargin: 2 //prevent bottom line overlapping anchors.fill: parent visible: currentModeIndex == 1 onShowTooltip: base.showTooltip(item, location, text) @@ -312,7 +322,6 @@ Cura.ExpandableComponent anchors.top: popupItemContent.bottom anchors.topMargin: UM.Theme.getSize("narrow_margin").height * 2 width: parent.width - height: settingControlButton.height + UM.Theme.getSize("default_lining").height * 4 Rectangle { width: parent.width @@ -330,9 +339,8 @@ Cura.ExpandableComponent { top: parent.top topMargin: UM.Theme.getSize("narrow_margin").height * 2 - bottomMargin: UM.Theme.getSize("narrow_margin").height * 2 right: parent.right - rightMargin: UM.Theme.getSize("narrow_margin").height + rightMargin: UM.Theme.getSize("narrow_margin").height * 2 } color: UM.Theme.getColor("secondary") @@ -349,14 +357,11 @@ Cura.ExpandableComponent { height: UM.Theme.getSize("action_panel_button").height text: catalog.i18nc("@button", "Recommended") - color: UM.Theme.getColor("secondary") hoverColor: UM.Theme.getColor("secondary") textColor: UM.Theme.getColor("primary") textHoverColor: UM.Theme.getColor("text") - iconSource: UM.Theme.getIcon("arrow_left") - width: UM.Theme.getSize("print_setup_action_button").width fixedWidthMode: true @@ -365,9 +370,8 @@ Cura.ExpandableComponent { top: parent.top topMargin: UM.Theme.getSize("narrow_margin").height * 2 - bottomMargin: UM.Theme.getSize("narrow_margin").height * 2 left: parent.left - leftMargin: UM.Theme.getSize("narrow_margin").height + leftMargin: UM.Theme.getSize("narrow_margin").height * 2 } MouseArea { @@ -376,7 +380,7 @@ Cura.ExpandableComponent } } } - + Component.onCompleted: { var index = Math.round(UM.Preferences.getValue("cura/active_mode")) diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index cd9701aab6..3960f99aaa 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -21,92 +21,6 @@ Item signal showTooltip(Item item, point location, string text) signal hideTooltip() - Item - { - id: globalProfileRow - height: UM.Theme.getSize("print_setup_item").height - - anchors - { - top: parent.top - left: parent.left - leftMargin: Math.round(UM.Theme.getSize("thick_margin").width) - right: parent.right - rightMargin: Math.round(UM.Theme.getSize("thick_margin").width) - } - - Label - { - id: globalProfileLabel - text: catalog.i18nc("@label","Profile:") - textFormat: Text.PlainText - width: Math.round(parent.width * 0.45 - UM.Theme.getSize("thick_margin").width - 2) - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - verticalAlignment: Text.AlignVCenter - anchors.top: parent.top - anchors.bottom: parent.bottom - } - - ToolButton - { - id: globalProfileSelection - - text: generateActiveQualityText() - width: Math.round(parent.width * 0.55) - height: UM.Theme.getSize("setting_control").height - anchors.left: globalProfileLabel.right - anchors.right: parent.right - tooltip: Cura.MachineManager.activeQualityOrQualityChangesName - style: UM.Theme.styles.sidebar_header_button - activeFocusOnPress: true - menu: ProfileMenu { } - - function generateActiveQualityText () { - var result = Cura.MachineManager.activeQualityOrQualityChangesName; - - if (Cura.MachineManager.isActiveQualitySupported) { - if (Cura.MachineManager.activeQualityLayerHeight > 0) { - result += " " - result += " - " - result += Cura.MachineManager.activeQualityLayerHeight + "mm" - result += "" - } - } - - return result - } - - UM.SimpleButton - { - id: customisedSettings - - visible: Cura.MachineManager.hasUserSettings - height: Math.round(parent.height * 0.6) - width: Math.round(parent.height * 0.6) - - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: Math.round(UM.Theme.getSize("setting_preferences_button_margin").width - UM.Theme.getSize("thick_margin").width) - - color: hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button"); - iconSource: UM.Theme.getIcon("star"); - - onClicked: - { - forceActiveFocus(); - Cura.Actions.manageProfiles.trigger() - } - onEntered: - { - var content = catalog.i18nc("@tooltip","Some setting/override values are different from the values stored in the profile.\n\nClick to open the profile manager.") - base.showTooltip(globalProfileRow, Qt.point(-UM.Theme.getSize("thick_margin").width, 0), content) - } - onExited: base.hideTooltip() - } - } - } - ToolButton { id: settingVisibilityMenu @@ -115,7 +29,6 @@ Item height: UM.Theme.getSize("setting_control").height anchors { - top: globalProfileRow.bottom topMargin: UM.Theme.getSize("thick_margin").height right: parent.right rightMargin: UM.Theme.getSize("thick_margin").width @@ -168,7 +81,7 @@ Item anchors { - top: globalProfileRow.bottom + //top: globalProfileRow.bottom topMargin: UM.Theme.getSize("thick_margin").height left: parent.left leftMargin: UM.Theme.getSize("thick_margin").width From 706fc311bccb730e82e48bf6c752e4bd0a3bf856 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Thu, 29 Nov 2018 00:47:25 +0100 Subject: [PATCH 0517/1240] Added margins to the setting list CURA-5941 --- resources/qml/Settings/SettingCategory.qml | 4 ++-- resources/qml/Settings/SettingItem.qml | 4 ++-- resources/qml/Settings/SettingView.qml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/resources/qml/Settings/SettingCategory.qml b/resources/qml/Settings/SettingCategory.qml index aafe36c546..4ec6294d61 100644 --- a/resources/qml/Settings/SettingCategory.qml +++ b/resources/qml/Settings/SettingCategory.qml @@ -12,8 +12,8 @@ Button id: base anchors.left: parent.left anchors.right: parent.right - anchors.leftMargin: UM.Theme.getSize("thick_margin").width - anchors.rightMargin: UM.Theme.getSize("thick_margin").width + anchors.leftMargin: UM.Theme.getSize("default_margin").width + anchors.rightMargin: UM.Theme.getSize("default_margin").width * 3 hoverEnabled: true background: Rectangle diff --git a/resources/qml/Settings/SettingItem.qml b/resources/qml/Settings/SettingItem.qml index cad6a28bd6..036dcfeba4 100644 --- a/resources/qml/Settings/SettingItem.qml +++ b/resources/qml/Settings/SettingItem.qml @@ -108,7 +108,7 @@ Item { id: label; anchors.left: parent.left; - anchors.leftMargin: doDepthIndentation ? Math.round((UM.Theme.getSize("section_icon_column").width + 5) + ((definition.depth - 1) * UM.Theme.getSize("setting_control_depth_margin").width)) : 0 + anchors.leftMargin: doDepthIndentation ? Math.round((UM.Theme.getSize("section_icon_column").width / 1.2) + ((definition.depth - 1) * UM.Theme.getSize("setting_control_depth_margin").width)) : 0 anchors.right: settingControls.left; anchors.verticalCenter: parent.verticalCenter @@ -290,7 +290,7 @@ Item { enabled: propertyProvider.isValueUsed anchors.right: parent.right; - anchors.rightMargin: UM.Theme.getSize("thick_margin").width + anchors.rightMargin: UM.Theme.getSize("default_margin").width * 3 anchors.verticalCenter: parent.verticalCenter; width: UM.Theme.getSize("setting_control").width; height: UM.Theme.getSize("setting_control").height diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 3960f99aaa..8a336a7ed1 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -81,10 +81,9 @@ Item anchors { - //top: globalProfileRow.bottom topMargin: UM.Theme.getSize("thick_margin").height left: parent.left - leftMargin: UM.Theme.getSize("thick_margin").width + leftMargin: UM.Theme.getSize("default_margin").width right: settingVisibilityMenu.left rightMargin: Math.floor(UM.Theme.getSize("default_margin").width / 2) } @@ -202,6 +201,7 @@ Item anchors.right: parent.right; anchors.left: parent.left; anchors.topMargin: UM.Theme.getSize("thick_margin").height + anchors.rightMargin: UM.Theme.getSize("narrow_margin").height / 3 style: UM.Theme.styles.scrollview; flickableItem.flickableDirection: Flickable.VerticalFlick; From b44ddec01fcae8d748914a22062f11413870c7e2 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 29 Nov 2018 08:35:38 +0100 Subject: [PATCH 0518/1240] Change normal layer height of cocoon to 0.10 CURA-5902 So it's different from Fine 0.15, otherwise you get duplicates. --- resources/definitions/cocoon_create_modelmaker.def.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/definitions/cocoon_create_modelmaker.def.json b/resources/definitions/cocoon_create_modelmaker.def.json index 204d5b9492..921b20624a 100644 --- a/resources/definitions/cocoon_create_modelmaker.def.json +++ b/resources/definitions/cocoon_create_modelmaker.def.json @@ -51,7 +51,7 @@ "default_value": 220 }, "layer_height": { - "default_value": 0.15 + "default_value": 0.10 }, "layer_height_0": { "default_value": 0.2 From 7639983925f712a48a3a64bd71d221d2dcf3b4f2 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 29 Nov 2018 08:54:04 +0100 Subject: [PATCH 0519/1240] Clean the recommended mode print setup Contributes to CURA-5941 --- .../PrintSetupSelectorContents.qml | 31 - .../RecommendedPrintSetup.qml | 1205 ++++++++--------- 2 files changed, 580 insertions(+), 656 deletions(-) diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml index c36a0fbb80..6e71526f25 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml @@ -94,40 +94,10 @@ Item anchors { top: header.bottom - leftMargin: UM.Theme.getSize("default_margin").width - rightMargin: UM.Theme.getSize("default_margin").width } sourceComponent: currentModeIndex == 0 ? recommendedPrintSetup : customPrintSetup } -// Item -// { -// id: content -// width: parent.width -// height: 100 -////// height: tabBar.height + sidebarContents.height -//// -// anchors -// { -// top: header.bottom -// leftMargin: UM.Theme.getSize("default_margin").width -// rightMargin: UM.Theme.getSize("default_margin").width -// } -//// -//// Rectangle -//// { -//// id: sidebarContents -//// anchors.top: tabBar.bottom -//// anchors.bottom: parent.bottom -//// anchors.left: parent.left -//// anchors.right: parent.right -//// height: UM.Theme.getSize("print_setup_widget").height -//// -//// border.width: UM.Theme.getSize("default_lining").width * 2 -//// border.color: UM.Theme.getColor("action_button_border") -//// } -// } - Rectangle { id: buttonsSeparator @@ -195,7 +165,6 @@ Item Component.onCompleted: { - print(height, "!!!!!!!!!!!!!!!!!!!!!!!") var index = Math.round(UM.Preferences.getValue("cura/active_mode")) if(index != null && !isNaN(index)) diff --git a/resources/qml/PrintSetupSelector/RecommendedPrintSetup.qml b/resources/qml/PrintSetupSelector/RecommendedPrintSetup.qml index 5d195bdde4..b48282135b 100644 --- a/resources/qml/PrintSetupSelector/RecommendedPrintSetup.qml +++ b/resources/qml/PrintSetupSelector/RecommendedPrintSetup.qml @@ -8,17 +8,22 @@ import QtQuick.Controls.Styles 1.4 import UM 1.2 as UM import Cura 1.0 as Cura -Item +Column { id: base signal showTooltip(Item item, point location, string text) signal hideTooltip() - height: childrenRect.height + height: childrenRect.height + 2 * padding + + padding: UM.Theme.getSize("thick_margin").width + spacing: UM.Theme.getSize("default_margin").height property Action configureSettings property bool settingsEnabled: Cura.ExtruderManager.activeExtruderStackId || extrudersEnabledCount.properties.value == 1 + property real labelColumnWidth: Math.round(width / 3) + property real settingsColumnWidth: width - labelColumnWidth UM.I18nCatalog { @@ -33,10 +38,9 @@ Item { id: qualityRow - height: UM.Theme.getSize("thick_margin").height - anchors.topMargin: UM.Theme.getSize("thick_margin").height anchors.left: parent.left anchors.right: parent.right + height: childrenRect.height Timer { @@ -145,14 +149,14 @@ Item function calculateSliderStepWidth (totalTicks) { - qualityModel.qualitySliderStepWidth = totalTicks != 0 ? Math.round((base.width * 0.55) / (totalTicks)) : 0 + qualityModel.qualitySliderStepWidth = totalTicks != 0 ? Math.round((settingsColumnWidth) / (totalTicks)) : 0 } function calculateSliderMargins (availableMin, availableMax, totalTicks) { if (availableMin == -1 || (availableMin == 0 && availableMax == 0)) { - qualityModel.qualitySliderMarginRight = Math.round(base.width * 0.55) + qualityModel.qualitySliderMarginRight = Math.round(settingsColumnWidth) } else if (availableMin == availableMax) { @@ -180,6 +184,7 @@ Item source: UM.Theme.getIcon("category_layer_height") text: catalog.i18nc("@label", "Layer Height") anchors.bottom: speedSlider.bottom + width: labelColumnWidth } // Show titles for the each quality slider ticks @@ -187,6 +192,7 @@ Item { anchors.left: speedSlider.left anchors.top: speedSlider.bottom + Repeater { model: qualityModel @@ -194,7 +200,7 @@ Item Label { anchors.verticalCenter: parent.verticalCenter - anchors.top: parent.bottom + anchors.top: parent.top color: (Cura.MachineManager.activeMachine != null && Cura.QualityProfilesDropDownMenuModel.getItem(index).available) ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") text: { @@ -225,19 +231,19 @@ Item if (qualityModel.totalTicks == 0) { // If there is only one tick, align it centrally - return Math.round(((base.width * 0.55) - width) / 2) + return Math.round(((settingsColumnWidth) - width) / 2) } else if (index == 0) { - return Math.round(base.width * 0.55 / qualityModel.totalTicks) * index + return Math.round(settingsColumnWidth / qualityModel.totalTicks) * index } else if (index == qualityModel.totalTicks) { - return Math.round(base.width * 0.55 / qualityModel.totalTicks) * index - width + return Math.round(settingsColumnWidth / qualityModel.totalTicks) * index - width } else { - return Math.round((base.width * 0.55 / qualityModel.totalTicks) * index - (width / 2)) + return Math.round((settingsColumnWidth / qualityModel.totalTicks) * index - (width / 2)) } } } @@ -248,126 +254,127 @@ Item Rectangle { id: speedSlider - width: Math.round(base.width * 0.55) - height: UM.Theme.getSize("thick_margin").height - anchors.right: parent.right - anchors.top: parent.top - anchors.topMargin: UM.Theme.getSize("thick_margin").height + + anchors + { + left: qualityRowTitle.right + right: parent.right + } // 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("thick_margin").width, customisedSettings.height), content) - } - else - { - base.hideTooltip() - } - } - - id: unavailableLineToolTip - height: 20 * screenScaleFactor // 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 qualityModel.qualitySliderStepWidth * qualityModel.totalTicks - } - 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) - } - } - - Item - { - id: rightArea - width: - { - if(qualityModel.availableTotalTicks == 0) - return 0 - - return qualityModel.qualitySliderMarginRight - 10 - } - height: parent.height - 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) - } - } - } +// 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("thick_margin").width, customisedSettings.height), content) +// } +// else +// { +// base.hideTooltip() +// } +// } +// +// id: unavailableLineToolTip +// height: 20 * screenScaleFactor // 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 qualityModel.qualitySliderStepWidth * qualityModel.totalTicks +// } +// 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) +// } +// } +// +// Item +// { +// id: rightArea +// width: +// { +// if(qualityModel.availableTotalTicks == 0) +// return 0 +// +// return qualityModel.qualitySliderMarginRight - 10 +// } +// height: parent.height +// 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 { id: groovechildrect - width: Math.round(base.width * 0.55) + width: parent.width height: 2 * screenScaleFactor color: UM.Theme.getColor("quality_slider_unavailable") anchors.verticalCenter: qualitySlider.verticalCenter - x: 0 - } - // Draw ticks - Repeater - { - id: qualityRepeater - model: qualityModel.totalTicks > 0 ? qualityModel : 0 - - Rectangle + // Draw ticks + Repeater { - color: Cura.QualityProfilesDropDownMenuModel.getItem(index).available ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") - implicitWidth: 5 * screenScaleFactor - implicitHeight: implicitWidth - anchors.verticalCenter: qualitySlider.verticalCenter - x: Math.round(qualityModel.qualitySliderStepWidth * index) - radius: Math.round(implicitWidth / 2) + id: qualityRepeater + model: qualityModel.totalTicks > 0 ? qualityModel : 0 + + Rectangle + { + color: Cura.QualityProfilesDropDownMenuModel.getItem(index).available ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") + implicitWidth: 4 * screenScaleFactor + implicitHeight: implicitWidth + anchors.verticalCenter: parent.verticalCenter + x: Math.round(qualityModel.qualitySliderStepWidth * index) + radius: Math.round(implicitWidth / 2) + } } } + // Draw available slider Slider { id: qualitySlider height: UM.Theme.getSize("thick_margin").height - anchors.bottom: speedSlider.bottom + anchors.bottom: parent.bottom enabled: qualityModel.totalTicks > 0 && !Cura.SimpleModeSettingsManager.isProfileCustomized visible: qualityModel.availableTotalTicks > 0 updateValueWhileDragging : false @@ -394,18 +401,15 @@ Item color: UM.Theme.getColor("quality_slider_available") radius: Math.round(height / 2) } - handle: Item + + handle: Rectangle { - Rectangle - { - id: qualityhandleButton - anchors.centerIn: parent - color: UM.Theme.getColor("quality_slider_available") - implicitWidth: 10 * screenScaleFactor - implicitHeight: implicitWidth - radius: Math.round(implicitWidth / 2) - visible: !Cura.SimpleModeSettingsManager.isProfileCustomized && !Cura.SimpleModeSettingsManager.isProfileUserCreated && qualityModel.existingQualityProfile - } + id: qualityhandleButton + color: UM.Theme.getColor("quality_slider_available") + implicitWidth: 12 * screenScaleFactor + implicitHeight: implicitWidth + radius: Math.round(implicitWidth / 2) + visible: !Cura.SimpleModeSettingsManager.isProfileCustomized && !Cura.SimpleModeSettingsManager.isProfileUserCreated && qualityModel.existingQualityProfile } } @@ -433,7 +437,7 @@ Item onEntered: { - var content = catalog.i18nc("@tooltip","A custom profile is currently active. To enable the quality slider, choose a default quality profile in Custom tab") + var content = catalog.i18nc("@tooltip", "A custom profile is currently active. To enable the quality slider, choose a default quality profile in Custom tab") base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, customisedSettings.height), content) } onExited: base.hideTooltip() @@ -474,329 +478,319 @@ Item // Item { - id: infillCellLeft - - anchors.top: qualityRow.bottom - anchors.topMargin: UM.Theme.getSize("thick_margin").height * 2 anchors.left: parent.left - - width: Math.round(UM.Theme.getSize("print_setup_widget").width * .45) - UM.Theme.getSize("thick_margin").width + anchors.right: parent.right + height: childrenRect.height Cura.IconWithText { - id: infillLabel + id: infillRowTitle source: UM.Theme.getIcon("category_infill") text: catalog.i18nc("@label", "Infill") + " (%)" - - anchors - { - top: parent.top - topMargin: Math.round(UM.Theme.getSize("thick_margin").height * 1.7) - left: parent.left - } + anchors.bottom: parent.bottom + width: labelColumnWidth } - } - Item - { - id: infillCellRight + Item + { + id: infillCellRight - height: infillSlider.height + UM.Theme.getSize("thick_margin").height + enableGradualInfillCheckBox.visible * (enableGradualInfillCheckBox.height + UM.Theme.getSize("thick_margin").height) - width: Math.round(UM.Theme.getSize("print_setup_widget").width * .55) + height: infillSlider.height + UM.Theme.getSize("thick_margin").height + enableGradualInfillCheckBox.visible * (enableGradualInfillCheckBox.height + UM.Theme.getSize("thick_margin").height) - anchors.left: infillCellLeft.right - anchors.top: infillCellLeft.top - anchors.topMargin: UM.Theme.getSize("thick_margin").height - - Label { - id: selectedInfillRateText - - anchors.left: infillSlider.left + anchors.left: infillRowTitle.right anchors.right: parent.right - text: parseInt(infillDensity.properties.value) + "%" - horizontalAlignment: Text.AlignLeft - - color: infillSlider.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") - } - - // We use a binding to make sure that after manually setting infillSlider.value it is still bound to the property provider - Binding - { - target: infillSlider - property: "value" - value: parseInt(infillDensity.properties.value) - } - - Slider - { - id: infillSlider - - anchors.top: selectedInfillRateText.bottom - anchors.left: parent.left - anchors.right: infillIcon.left - anchors.rightMargin: UM.Theme.getSize("thick_margin").width - - height: UM.Theme.getSize("thick_margin").height - width: parseInt(infillCellRight.width - UM.Theme.getSize("thick_margin").width - style.handleWidth) - - minimumValue: 0 - maximumValue: 100 - stepSize: 1 - tickmarksEnabled: true - - // disable slider when gradual support is enabled - enabled: parseInt(infillSteps.properties.value) == 0 - - // set initial value from stack - value: parseInt(infillDensity.properties.value) - - onValueChanged: - { - - // Don't round the value if it's already the same - if (parseInt(infillDensity.properties.value) == infillSlider.value) - { - return - } - - // Round the slider value to the nearest multiple of 10 (simulate step size of 10) - var roundedSliderValue = Math.round(infillSlider.value / 10) * 10 - - // Update the slider value to represent the rounded value - infillSlider.value = roundedSliderValue - - // Update value only if the Recomended mode is Active, - // Otherwise if I change the value in the Custom mode the Recomended view will try to repeat - // same operation - var active_mode = UM.Preferences.getValue("cura/active_mode") - - if (active_mode == 0 || active_mode == "simple") - { - Cura.MachineManager.setSettingForAllExtruders("infill_sparse_density", "value", roundedSliderValue) - Cura.MachineManager.resetSettingForAllExtruders("infill_line_distance") - } - } - - style: SliderStyle - { - groove: Rectangle - { - id: groove - implicitWidth: 200 * screenScaleFactor - implicitHeight: 2 * screenScaleFactor - color: control.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") - radius: 1 - } - - handle: Item - { - Rectangle - { - id: handleButton - anchors.centerIn: parent - color: control.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") - implicitWidth: 10 * screenScaleFactor - implicitHeight: 10 * screenScaleFactor - radius: 10 * screenScaleFactor - } - } - - tickmarks: Repeater - { - id: repeater - model: control.maximumValue / control.stepSize + 1 - - // check if a tick should be shown based on it's index and wether the infill density is a multiple of 10 (slider step size) - function shouldShowTick (index) - { - if (index % 10 == 0) - { - return true - } - return false - } - - Rectangle - { - anchors.verticalCenter: parent.verticalCenter - color: control.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") - width: 1 * screenScaleFactor - height: 6 * screenScaleFactor - x: Math.round(styleData.handleWidth / 2 + index * ((repeater.width - styleData.handleWidth) / (repeater.count-1))) - visible: shouldShowTick(index) - } - } - } - } - - Rectangle - { - id: infillIcon - - width: Math.round((parent.width / 5) - (UM.Theme.getSize("thick_margin").width)) - height: width - - anchors.right: parent.right - anchors.top: parent.top - anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height / 2) - - // we loop over all density icons and only show the one that has the current density and steps - Repeater - { - id: infillIconList - model: infillModel - anchors.fill: parent - - function activeIndex () - { - for (var i = 0; i < infillModel.count; i++) - { - var density = Math.round(infillDensity.properties.value) - var steps = Math.round(infillSteps.properties.value) - var infillModelItem = infillModel.get(i) - - if (infillModelItem != "undefined" - && density >= infillModelItem.percentageMin - && density <= infillModelItem.percentageMax - && steps >= infillModelItem.stepsMin - && steps <= infillModelItem.stepsMax) - { - return i - } - } - return -1 - } - - Rectangle - { - anchors.fill: parent - visible: infillIconList.activeIndex() == index - - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("quality_slider_unavailable") - - UM.RecolorImage - { - anchors.fill: parent - anchors.margins: 2 * screenScaleFactor - sourceSize.width: width - sourceSize.height: width - source: UM.Theme.getIcon(model.icon) - color: UM.Theme.getColor("quality_slider_unavailable") - } - } - } - } - - // Gradual Support Infill Checkbox - CheckBox - { - id: enableGradualInfillCheckBox - property alias _hovered: enableGradualInfillMouseArea.containsMouse - - anchors.top: infillSlider.bottom - anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height / 2) // closer to slider since it belongs to the same category - anchors.left: infillCellRight.left - - style: UM.Theme.styles.checkbox - enabled: base.settingsEnabled - visible: infillSteps.properties.enabled == "True" - checked: parseInt(infillSteps.properties.value) > 0 - - MouseArea - { - id: enableGradualInfillMouseArea - - anchors.fill: parent - hoverEnabled: true - enabled: true - - property var previousInfillDensity: parseInt(infillDensity.properties.value) - - onClicked: - { - // Set to 90% only when enabling gradual infill - var newInfillDensity; - if (parseInt(infillSteps.properties.value) == 0) - { - previousInfillDensity = parseInt(infillDensity.properties.value) - newInfillDensity = 90 - } else { - newInfillDensity = previousInfillDensity - } - Cura.MachineManager.setSettingForAllExtruders("infill_sparse_density", "value", String(newInfillDensity)) - - var infill_steps_value = 0 - if (parseInt(infillSteps.properties.value) == 0) - { - infill_steps_value = 5 - } - - Cura.MachineManager.setSettingForAllExtruders("gradual_infill_steps", "value", infill_steps_value) - } - - onEntered: base.showTooltip(enableGradualInfillCheckBox, Qt.point(-infillCellRight.x, 0), - catalog.i18nc("@label", "Gradual infill will gradually increase the amount of infill towards the top.")) - - onExited: base.hideTooltip() - - } - Label { - id: gradualInfillLabel - height: parent.height - anchors.left: enableGradualInfillCheckBox.right - anchors.leftMargin: Math.round(UM.Theme.getSize("thick_margin").width / 2) - verticalAlignment: Text.AlignVCenter; - text: catalog.i18nc("@label", "Enable gradual") - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - } - } + id: selectedInfillRateText - // Infill list model for mapping icon - ListModel - { - id: infillModel - Component.onCompleted: + anchors.left: infillSlider.left + anchors.right: parent.right + + text: parseInt(infillDensity.properties.value) + "%" + horizontalAlignment: Text.AlignLeft + + color: infillSlider.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") + } + + // We use a binding to make sure that after manually setting infillSlider.value it is still bound to the property provider + Binding { - infillModel.append({ - percentageMin: -1, - percentageMax: 0, - stepsMin: -1, - stepsMax: 0, - icon: "hollow" - }) - infillModel.append({ - percentageMin: 0, - percentageMax: 40, - stepsMin: -1, - stepsMax: 0, - icon: "sparse" - }) - infillModel.append({ - percentageMin: 40, - percentageMax: 89, - stepsMin: -1, - stepsMax: 0, - icon: "dense" - }) - infillModel.append({ - percentageMin: 90, - percentageMax: 9999999999, - stepsMin: -1, - stepsMax: 0, - icon: "solid" - }) - infillModel.append({ - percentageMin: 0, - percentageMax: 9999999999, - stepsMin: 1, - stepsMax: 9999999999, - icon: "gradual" - }) + target: infillSlider + property: "value" + value: parseInt(infillDensity.properties.value) + } + + Slider + { + id: infillSlider + + anchors.top: selectedInfillRateText.bottom + anchors.left: parent.left + anchors.right: infillIcon.left + anchors.rightMargin: UM.Theme.getSize("thick_margin").width + + height: UM.Theme.getSize("thick_margin").height + width: parseInt(infillCellRight.width - UM.Theme.getSize("thick_margin").width - style.handleWidth) + + minimumValue: 0 + maximumValue: 100 + stepSize: 1 + tickmarksEnabled: true + + // disable slider when gradual support is enabled + enabled: parseInt(infillSteps.properties.value) == 0 + + // set initial value from stack + value: parseInt(infillDensity.properties.value) + + onValueChanged: + { + + // Don't round the value if it's already the same + if (parseInt(infillDensity.properties.value) == infillSlider.value) + { + return + } + + // Round the slider value to the nearest multiple of 10 (simulate step size of 10) + var roundedSliderValue = Math.round(infillSlider.value / 10) * 10 + + // Update the slider value to represent the rounded value + infillSlider.value = roundedSliderValue + + // Update value only if the Recomended mode is Active, + // Otherwise if I change the value in the Custom mode the Recomended view will try to repeat + // same operation + var active_mode = UM.Preferences.getValue("cura/active_mode") + + if (active_mode == 0 || active_mode == "simple") + { + Cura.MachineManager.setSettingForAllExtruders("infill_sparse_density", "value", roundedSliderValue) + Cura.MachineManager.resetSettingForAllExtruders("infill_line_distance") + } + } + + style: SliderStyle + { + groove: Rectangle + { + id: groove + implicitWidth: 200 * screenScaleFactor + implicitHeight: 2 * screenScaleFactor + color: control.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") + radius: 1 + } + + handle: Item + { + Rectangle + { + id: handleButton + anchors.centerIn: parent + color: control.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") + implicitWidth: 10 * screenScaleFactor + implicitHeight: 10 * screenScaleFactor + radius: 10 * screenScaleFactor + } + } + + tickmarks: Repeater + { + id: repeater + model: control.maximumValue / control.stepSize + 1 + + // check if a tick should be shown based on it's index and wether the infill density is a multiple of 10 (slider step size) + function shouldShowTick (index) + { + if (index % 10 == 0) + { + return true + } + return false + } + + Rectangle + { + anchors.verticalCenter: parent.verticalCenter + color: control.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") + width: 1 * screenScaleFactor + height: 6 * screenScaleFactor + x: Math.round(styleData.handleWidth / 2 + index * ((repeater.width - styleData.handleWidth) / (repeater.count-1))) + visible: shouldShowTick(index) + } + } + } + } + + Rectangle + { + id: infillIcon + + width: Math.round((parent.width / 5) - (UM.Theme.getSize("thick_margin").width)) + height: width + + anchors.right: parent.right + anchors.top: parent.top + anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height / 2) + + // we loop over all density icons and only show the one that has the current density and steps + Repeater + { + id: infillIconList + model: infillModel + anchors.fill: parent + + function activeIndex () + { + for (var i = 0; i < infillModel.count; i++) + { + var density = Math.round(infillDensity.properties.value) + var steps = Math.round(infillSteps.properties.value) + var infillModelItem = infillModel.get(i) + + if (infillModelItem != "undefined" + && density >= infillModelItem.percentageMin + && density <= infillModelItem.percentageMax + && steps >= infillModelItem.stepsMin + && steps <= infillModelItem.stepsMax) + { + return i + } + } + return -1 + } + + Rectangle + { + anchors.fill: parent + visible: infillIconList.activeIndex() == index + + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("quality_slider_unavailable") + + UM.RecolorImage + { + anchors.fill: parent + anchors.margins: 2 * screenScaleFactor + sourceSize.width: width + sourceSize.height: width + source: UM.Theme.getIcon(model.icon) + color: UM.Theme.getColor("quality_slider_unavailable") + } + } + } + } + + // Gradual Support Infill Checkbox + CheckBox + { + id: enableGradualInfillCheckBox + property alias _hovered: enableGradualInfillMouseArea.containsMouse + + anchors.top: infillSlider.bottom + anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height / 2) // closer to slider since it belongs to the same category + anchors.left: infillCellRight.left + + style: UM.Theme.styles.checkbox + enabled: base.settingsEnabled + visible: infillSteps.properties.enabled == "True" + checked: parseInt(infillSteps.properties.value) > 0 + + MouseArea + { + id: enableGradualInfillMouseArea + + anchors.fill: parent + hoverEnabled: true + enabled: true + + property var previousInfillDensity: parseInt(infillDensity.properties.value) + + onClicked: + { + // Set to 90% only when enabling gradual infill + var newInfillDensity; + if (parseInt(infillSteps.properties.value) == 0) + { + previousInfillDensity = parseInt(infillDensity.properties.value) + newInfillDensity = 90 + } else { + newInfillDensity = previousInfillDensity + } + Cura.MachineManager.setSettingForAllExtruders("infill_sparse_density", "value", String(newInfillDensity)) + + var infill_steps_value = 0 + if (parseInt(infillSteps.properties.value) == 0) + { + infill_steps_value = 5 + } + + Cura.MachineManager.setSettingForAllExtruders("gradual_infill_steps", "value", infill_steps_value) + } + + onEntered: base.showTooltip(enableGradualInfillCheckBox, Qt.point(-infillCellRight.x, 0), + catalog.i18nc("@label", "Gradual infill will gradually increase the amount of infill towards the top.")) + + onExited: base.hideTooltip() + + } + + Label + { + id: gradualInfillLabel + height: parent.height + anchors.left: enableGradualInfillCheckBox.right + anchors.leftMargin: Math.round(UM.Theme.getSize("thick_margin").width / 2) + verticalAlignment: Text.AlignVCenter; + text: catalog.i18nc("@label", "Enable gradual") + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + } + } + + // Infill list model for mapping icon + ListModel + { + id: infillModel + Component.onCompleted: + { + infillModel.append({ + percentageMin: -1, + percentageMax: 0, + stepsMin: -1, + stepsMax: 0, + icon: "hollow" + }) + infillModel.append({ + percentageMin: 0, + percentageMax: 40, + stepsMin: -1, + stepsMax: 0, + icon: "sparse" + }) + infillModel.append({ + percentageMin: 40, + percentageMax: 89, + stepsMin: -1, + stepsMax: 0, + icon: "dense" + }) + infillModel.append({ + percentageMin: 90, + percentageMax: 9999999999, + stepsMin: -1, + stepsMax: 0, + icon: "solid" + }) + infillModel.append({ + percentageMin: 0, + percentageMax: 9999999999, + stepsMin: 1, + stepsMax: 9999999999, + icon: "gradual" + }) + } } } } @@ -804,191 +798,177 @@ Item // // Enable support // - Cura.IconWithText + Row { - id: enableSupportLabel - visible: enableSupportCheckBox.visible - source: UM.Theme.getIcon("category_support") - text: catalog.i18nc("@label", "Support") + anchors.left: parent.left + anchors.right: parent.right + height: childrenRect.height - anchors + Cura.IconWithText { - top: infillCellRight.bottom - topMargin: Math.round(UM.Theme.getSize("thick_margin").height * 1.5) - left: parent.left - right: infillCellLeft.right - rightMargin: UM.Theme.getSize("thick_margin").width - verticalCenter: enableSupportCheckBox.verticalCenter - } - } - - CheckBox - { - id: enableSupportCheckBox - property alias _hovered: enableSupportMouseArea.containsMouse - - anchors.top: enableSupportLabel.top - anchors.left: infillCellRight.left - - style: UM.Theme.styles.checkbox - enabled: base.settingsEnabled - - visible: supportEnabled.properties.enabled == "True" - checked: supportEnabled.properties.value == "True" - - MouseArea - { - id: enableSupportMouseArea - anchors.fill: parent - hoverEnabled: true - onClicked: supportEnabled.setPropertyValue("value", supportEnabled.properties.value != "True") - - onEntered: base.showTooltip(enableSupportCheckBox, Qt.point(-enableSupportCheckBox.x, 0), - catalog.i18nc("@label", "Generate structures to support parts of the model which have overhangs. Without these structures, such parts would collapse during printing.")) - - onExited: base.hideTooltip() - - } - } - - ComboBox - { - id: supportExtruderCombobox - visible: enableSupportCheckBox.visible && (supportEnabled.properties.value == "True") && (extrudersEnabledCount.properties.value > 1) - model: extruderModel - - property string color_override: "" // for manually setting values - property string color: // is evaluated automatically, but the first time is before extruderModel being filled - { - var current_extruder = extruderModel.get(currentIndex); - color_override = ""; - if (current_extruder === undefined) return "" - return (current_extruder.color) ? current_extruder.color : ""; + id: enableSupportLabel + visible: enableSupportCheckBox.visible + source: UM.Theme.getIcon("category_support") + text: catalog.i18nc("@label", "Support") + width: labelColumnWidth } - textRole: "text" // this solves that the combobox isn't populated in the first time Cura is started - - anchors.top: enableSupportCheckBox.top - - anchors.left: enableSupportCheckBox.right - anchors.leftMargin: Math.round(UM.Theme.getSize("thick_margin").width / 2) - - width: Math.round(UM.Theme.getSize("print_setup_widget").width * .55) - Math.round(UM.Theme.getSize("thick_margin").width / 2) - enableSupportCheckBox.width - height: ((supportEnabled.properties.value == "True") && (machineExtruderCount.properties.value > 1)) ? UM.Theme.getSize("setting_control").height : 0 - - Behavior on height { NumberAnimation { duration: 100 } } - - style: UM.Theme.styles.combobox_color - enabled: base.settingsEnabled - property alias _hovered: supportExtruderMouseArea.containsMouse - - currentIndex: + CheckBox { - if (supportExtruderNr.properties == null) + id: enableSupportCheckBox + property alias _hovered: enableSupportMouseArea.containsMouse + + style: UM.Theme.styles.checkbox + enabled: base.settingsEnabled + + visible: supportEnabled.properties.enabled == "True" + checked: supportEnabled.properties.value == "True" + + MouseArea { - return Cura.MachineManager.defaultExtruderPosition + id: enableSupportMouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: supportEnabled.setPropertyValue("value", supportEnabled.properties.value != "True") + + onEntered: base.showTooltip(enableSupportCheckBox, Qt.point(-enableSupportCheckBox.x, 0), + catalog.i18nc("@label", "Generate structures to support parts of the model which have overhangs. Without these structures, such parts would collapse during printing.")) + + onExited: base.hideTooltip() + } - else + } + + ComboBox + { + id: supportExtruderCombobox + visible: enableSupportCheckBox.visible && (supportEnabled.properties.value == "True") && (extrudersEnabledCount.properties.value > 1) + model: extruderModel + + property string color_override: "" // for manually setting values + property string color: // is evaluated automatically, but the first time is before extruderModel being filled { - var extruder = parseInt(supportExtruderNr.properties.value) - if ( extruder === -1) + var current_extruder = extruderModel.get(currentIndex); + color_override = ""; + if (current_extruder === undefined) return "" + return (current_extruder.color) ? current_extruder.color : ""; + } + + textRole: "text" // this solves that the combobox isn't populated in the first time Cura is started + + width: Math.round(UM.Theme.getSize("print_setup_widget").width * .55) - Math.round(UM.Theme.getSize("thick_margin").width / 2) - enableSupportCheckBox.width + height: ((supportEnabled.properties.value == "True") && (machineExtruderCount.properties.value > 1)) ? UM.Theme.getSize("setting_control").height : 0 + + Behavior on height { NumberAnimation { duration: 100 } } + + style: UM.Theme.styles.combobox_color + enabled: base.settingsEnabled + property alias _hovered: supportExtruderMouseArea.containsMouse + + currentIndex: + { + if (supportExtruderNr.properties == null) { return Cura.MachineManager.defaultExtruderPosition } - return extruder; - } - } - - onActivated: supportExtruderNr.setPropertyValue("value", String(index)) - - MouseArea - { - id: supportExtruderMouseArea - anchors.fill: parent - hoverEnabled: true - enabled: base.settingsEnabled - acceptedButtons: Qt.NoButton - onEntered: - { - base.showTooltip(supportExtruderCombobox, Qt.point(-supportExtruderCombobox.x, 0), - catalog.i18nc("@label", "Select which extruder to use for support. This will build up supporting structures below the model to prevent the model from sagging or printing in mid air.")); - } - onExited: base.hideTooltip() - - } - - function updateCurrentColor() - { - var current_extruder = extruderModel.get(currentIndex) - if (current_extruder !== undefined) - { - supportExtruderCombobox.color_override = current_extruder.color - } - } - - } - - Cura.IconWithText - { - id: adhesionHelperLabel - visible: adhesionCheckBox.visible - source: UM.Theme.getIcon("category_adhesion") - text: catalog.i18nc("@label", "Adhesion") - - anchors - { - left: parent.left - right: infillCellLeft.right - rightMargin: UM.Theme.getSize("thick_margin").width - verticalCenter: adhesionCheckBox.verticalCenter - } - } - - CheckBox - { - id: adhesionCheckBox - property alias _hovered: adhesionMouseArea.containsMouse - - anchors.top: enableSupportCheckBox.bottom - anchors.topMargin: UM.Theme.getSize("thick_margin").height - anchors.left: infillCellRight.left - - //: Setting enable printing build-plate adhesion helper checkbox - style: UM.Theme.styles.checkbox - enabled: base.settingsEnabled - - visible: platformAdhesionType.properties.enabled == "True" - checked: platformAdhesionType.properties.value != "skirt" && platformAdhesionType.properties.value != "none" - - MouseArea - { - id: adhesionMouseArea - anchors.fill: parent - hoverEnabled: true - enabled: base.settingsEnabled - onClicked: - { - var adhesionType = "skirt" - if(!parent.checked) + else { - // Remove the "user" setting to see if the rest of the stack prescribes a brim or a raft - platformAdhesionType.removeFromContainer(0) - adhesionType = platformAdhesionType.properties.value - if(adhesionType == "skirt" || adhesionType == "none") + var extruder = parseInt(supportExtruderNr.properties.value) + if ( extruder === -1) { - // If the rest of the stack doesn't prescribe an adhesion-type, default to a brim - adhesionType = "brim" + return Cura.MachineManager.defaultExtruderPosition } + return extruder; } - platformAdhesionType.setPropertyValue("value", adhesionType) } - onEntered: - { - base.showTooltip(adhesionCheckBox, Qt.point(-adhesionCheckBox.x, 0), - catalog.i18nc("@label", "Enable printing a brim or raft. This will add a flat area around or under your object which is easy to cut off afterwards.")); - } - onExited: base.hideTooltip() + onActivated: supportExtruderNr.setPropertyValue("value", String(index)) + + MouseArea + { + id: supportExtruderMouseArea + anchors.fill: parent + hoverEnabled: true + enabled: base.settingsEnabled + acceptedButtons: Qt.NoButton + onEntered: + { + base.showTooltip(supportExtruderCombobox, Qt.point(-supportExtruderCombobox.x, 0), + catalog.i18nc("@label", "Select which extruder to use for support. This will build up supporting structures below the model to prevent the model from sagging or printing in mid air.")); + } + onExited: base.hideTooltip() + + } + + function updateCurrentColor() + { + var current_extruder = extruderModel.get(currentIndex) + if (current_extruder !== undefined) + { + supportExtruderCombobox.color_override = current_extruder.color + } + } + + } + } + + // Adhesion + Row + { + anchors.left: parent.left + anchors.right: parent.right + height: childrenRect.height + + Cura.IconWithText + { + id: adhesionHelperLabel + visible: adhesionCheckBox.visible + source: UM.Theme.getIcon("category_adhesion") + text: catalog.i18nc("@label", "Adhesion") + width: labelColumnWidth + } + + CheckBox + { + id: adhesionCheckBox + property alias _hovered: adhesionMouseArea.containsMouse + + //: Setting enable printing build-plate adhesion helper checkbox + style: UM.Theme.styles.checkbox + enabled: base.settingsEnabled + + visible: platformAdhesionType.properties.enabled == "True" + checked: platformAdhesionType.properties.value != "skirt" && platformAdhesionType.properties.value != "none" + + MouseArea + { + id: adhesionMouseArea + anchors.fill: parent + hoverEnabled: true + enabled: base.settingsEnabled + onClicked: + { + var adhesionType = "skirt" + if(!parent.checked) + { + // Remove the "user" setting to see if the rest of the stack prescribes a brim or a raft + platformAdhesionType.removeFromContainer(0) + adhesionType = platformAdhesionType.properties.value + if(adhesionType == "skirt" || adhesionType == "none") + { + // If the rest of the stack doesn't prescribe an adhesion-type, default to a brim + adhesionType = "brim" + } + } + platformAdhesionType.setPropertyValue("value", adhesionType) + } + onEntered: + { + base.showTooltip(adhesionCheckBox, Qt.point(-adhesionCheckBox.x, 0), + catalog.i18nc("@label", "Enable printing a brim or raft. This will add a flat area around or under your object which is easy to cut off afterwards.")); + } + onExited: base.hideTooltip() + } } } @@ -1005,31 +985,6 @@ Item onModelChanged: populateExtruderModel() } - Item - { - id: tipsCell - anchors.top: adhesionCheckBox.visible ? adhesionCheckBox.bottom : (enableSupportCheckBox.visible ? supportExtruderCombobox.bottom : infillCellRight.bottom) - anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height * 2) - anchors.left: parent.left - width: parent.width - height: tipsText.contentHeight * tipsText.lineCount - - Label - { - id: tipsText - anchors.left: parent.left - anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("thick_margin").width - anchors.top: parent.top - wrapMode: Text.WordWrap - text: catalog.i18nc("@label", "Need help improving your prints?
    Read the Ultimaker Troubleshooting Guides").arg("https://ultimaker.com/en/troubleshooting") - font: UM.Theme.getFont("default"); - color: UM.Theme.getColor("text"); - linkColor: UM.Theme.getColor("text_link") - onLinkActivated: Qt.openUrlExternally(link) - } - } - UM.SettingPropertyProvider { id: infillExtruderNumber @@ -1096,8 +1051,8 @@ Item function populateExtruderModel() { - extruderModel.clear(); - for(var extruderNumber = 0; extruderNumber < extruders.rowCount() ; extruderNumber++) + extruderModel.clear() + for (var extruderNumber = 0; extruderNumber < extruders.rowCount(); extruderNumber++) { extruderModel.append({ text: extruders.getItem(extruderNumber).name, From d88a465737410f09b22498a8448f0ffdd550a5b3 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 29 Nov 2018 14:45:42 +0100 Subject: [PATCH 0520/1240] Increase possible visible description lines in the Marketplace --- plugins/Toolbox/resources/qml/ToolboxDetailTile.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailTile.qml b/plugins/Toolbox/resources/qml/ToolboxDetailTile.qml index 7425ab2ba7..1d701543ce 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailTile.qml @@ -37,7 +37,7 @@ Item anchors.top: packageName.bottom width: parent.width text: model.description - maximumLineCount: 12 + maximumLineCount: 25 elide: Text.ElideRight wrapMode: Text.WordWrap color: UM.Theme.getColor("text") From c545d9df7777db7e8d47ab3143997d310145b81d Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 29 Nov 2018 15:16:37 +0100 Subject: [PATCH 0521/1240] =?UTF-8?q?Add=20key=20check=20before=20accessin?= =?UTF-8?q?g=20in=20=C2=83StartSliceJob?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CURA-5901 --- plugins/CuraEngineBackend/StartSliceJob.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index 273dc0b6f6..9679360ad5 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -72,7 +72,7 @@ class GcodeStartEndFormatter(Formatter): # "-1" is global stack, and if the setting value exists in the global stack, use it as the fallback value. if key in kwargs["-1"]: value = kwargs["-1"] - if key in kwargs[str(extruder_nr)]: + if str(extruder_nr) in kwargs and key in kwargs[str(extruder_nr)]: value = kwargs[str(extruder_nr)][key] if value == default_value_str: From 90aa8c1264662ba176c4a696260afae7e80d7ba2 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 29 Nov 2018 15:29:51 +0100 Subject: [PATCH 0522/1240] point people to a good example --- .../scripts/ExampleScript.md | 3 ++ .../scripts/ExampleScript.py | 43 ------------------- 2 files changed, 3 insertions(+), 43 deletions(-) create mode 100644 plugins/PostProcessingPlugin/scripts/ExampleScript.md delete mode 100644 plugins/PostProcessingPlugin/scripts/ExampleScript.py diff --git a/plugins/PostProcessingPlugin/scripts/ExampleScript.md b/plugins/PostProcessingPlugin/scripts/ExampleScript.md new file mode 100644 index 0000000000..08652132aa --- /dev/null +++ b/plugins/PostProcessingPlugin/scripts/ExampleScript.md @@ -0,0 +1,3 @@ +A good example script is SearchAndReplace.py. +If you have any questions please ask them at: +https://github.com/Ultimaker/Cura/issues \ No newline at end of file diff --git a/plugins/PostProcessingPlugin/scripts/ExampleScript.py b/plugins/PostProcessingPlugin/scripts/ExampleScript.py deleted file mode 100644 index 416a5f5404..0000000000 --- a/plugins/PostProcessingPlugin/scripts/ExampleScript.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2015 Jaime van Kessel, Ultimaker B.V. -# The PostProcessingPlugin is released under the terms of the AGPLv3 or higher. -from ..Script import Script - -class ExampleScript(Script): - def __init__(self): - super().__init__() - - def getSettingDataString(self): - return """{ - "name":"Example script", - "key": "ExampleScript", - "metadata": {}, - "version": 2, - "settings": - { - "test": - { - "label": "Test", - "description": "None", - "unit": "mm", - "type": "float", - "default_value": 0.5, - "minimum_value": "0", - "minimum_value_warning": "0.1", - "maximum_value_warning": "1" - }, - "derp": - { - "label": "zomg", - "description": "afgasgfgasfgasf", - "unit": "mm", - "type": "float", - "default_value": 0.5, - "minimum_value": "0", - "minimum_value_warning": "0.1", - "maximum_value_warning": "1" - } - } - }""" - - def execute(self, data): - return data \ No newline at end of file From bc8bf8780959db152ba572a39a4dc545d1900006 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 29 Nov 2018 15:54:02 +0100 Subject: [PATCH 0523/1240] Align the buttons at the bottom of the panel Contributes to CURA-5941. --- resources/qml/ExpandableComponent.qml | 3 +- .../PrintSetupSelectorContents.qml | 71 +++++++++++-------- 2 files changed, 45 insertions(+), 29 deletions(-) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 0c3a8f80b9..47fa226e9d 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -163,6 +163,7 @@ Item onExited: background.color = headerBackgroundColor } } + DropShadow { id: shadow @@ -182,7 +183,7 @@ Item id: popup // Ensure that the popup is located directly below the headerItem - y: headerItemLoader.height + 2 * background.padding + base.shadowOffset + popupSpacingY + y: background.height + base.shadowOffset + popupSpacingY // Make the popup aligned with the rest, using the property popupAlignment to decide whether is right or left. // In case of right alignment, the 3x padding is due to left, right and padding between the button & text. diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml index 6e71526f25..309d612fae 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml @@ -11,7 +11,7 @@ Item { id: popup - width: UM.Theme.getSize("print_setup_widget").width + width: UM.Theme.getSize("print_setup_widget").width - 2 * UM.Theme.getSize("default_margin").width height: childrenRect.height property int currentModeIndex: -1 @@ -87,22 +87,50 @@ Item color: UM.Theme.getColor("lining") } - Loader + Item { - id: loader - width: parent.width + id: contents + height: childrenRect.height + anchors { top: header.bottom + left: parent.left + right: parent.right + } + + RecommendedPrintSetup + { + anchors + { + left: parent.left + right: parent.right + top: parent.top + } + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() + visible: currentModeIndex == 0 + } + + CustomPrintSetup + { + anchors + { + left: parent.left + right: parent.right + top: parent.top + } + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() + visible: currentModeIndex == 1 } - sourceComponent: currentModeIndex == 0 ? recommendedPrintSetup : customPrintSetup } Rectangle { id: buttonsSeparator - anchors.top: loader.bottom + anchors.top: contents.bottom width: parent.width height: UM.Theme.getSize("default_lining").height color: UM.Theme.getColor("lining") @@ -111,7 +139,8 @@ Item Item { id: buttonRow - height: childrenRect.height + property real padding: UM.Theme.getSize("default_margin").width + height: childrenRect.height + 2 * padding // The buttonsSeparator is inside the buttonRow. This is to avoid some weird behaviours with the scroll bar. anchors @@ -123,46 +152,32 @@ Item Cura.SecondaryButton { + anchors.top: parent.top anchors.left: parent.left + anchors.margins: parent.padding leftPadding: UM.Theme.getSize("default_margin").width rightPadding: UM.Theme.getSize("default_margin").width text: catalog.i18nc("@button", "Recommended") + iconSource: UM.Theme.getIcon("arrow_left") visible: currentModeIndex == 1 onClicked: currentModeIndex = 0 } Cura.SecondaryButton { + anchors.top: parent.top anchors.right: parent.right + anchors.margins: UM.Theme.getSize("default_margin").width leftPadding: UM.Theme.getSize("default_margin").width rightPadding: UM.Theme.getSize("default_margin").width text: catalog.i18nc("@button", "Custom") + iconSource: UM.Theme.getIcon("arrow_right") + iconOnRightSide: true visible: currentModeIndex == 0 onClicked: currentModeIndex = 1 } } - Component - { - id: recommendedPrintSetup - RecommendedPrintSetup - { - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() - } - } - - Component - { - id: customPrintSetup - CustomPrintSetup - { - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() - } - } - - Component.onCompleted: { var index = Math.round(UM.Preferences.getValue("cura/active_mode")) From 763291821f9b8afa3af231a7346fc56231619625 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Thu, 29 Nov 2018 17:03:11 +0100 Subject: [PATCH 0524/1240] Added models to process the data from the api results Added code to update the UI models --- .../src/Cloud/CloudOutputDevice.py | 208 +++++++++++++++--- .../src/Cloud/CloudOutputDeviceManager.py | 6 +- .../UM3NetworkPrinting/src/Cloud/Models.py | 63 ++++++ 3 files changed, 246 insertions(+), 31 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 79a3d46949..06e5656392 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -1,18 +1,23 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import json +import os from typing import List, Optional, Dict -from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, QUrl +from PyQt5.QtCore import QObject, pyqtSignal, QUrl, pyqtProperty from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest from UM import i18nCatalog from UM.FileHandler.FileHandler import FileHandler from UM.Logger import Logger from UM.Scene.SceneNode import SceneNode +from UM.Settings import ContainerRegistry from cura.CuraApplication import CuraApplication +from cura.PrinterOutput import PrinterOutputController, PrintJobOutputModel +from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel +from .Models import CloudClusterPrinter, CloudClusterPrinterConfiguration, CloudClusterPrinterConfigurationMaterial, CloudClusterPrintJob, CloudClusterPrintJobConstraint from .CloudOutputController import CloudOutputController from ..UM3PrintJobOutputModel import UM3PrintJobOutputModel @@ -38,17 +43,23 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # Signal triggered when the print jobs in the queue were changed. printJobsChanged = pyqtSignal() - + def __init__(self, device_id: str, parent: QObject = None): super().__init__(device_id = device_id, address = "", properties = {}, parent = parent) self._setInterfaceElements() self._device_id = device_id self._account = CuraApplication.getInstance().getCuraAPI().account + + # We re-use the Cura Connect monitor tab to get the most functionality right away. + self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), + "../../resources/qml/ClusterMonitorItem.qml") + self._control_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), + "../../resources/qml/ClusterControlItem.qml") # Properties to populate later on with received cloud data. - self._printers = [] - self._print_jobs = [] + self._printers = {} # type: Dict[str, PrinterOutputModel] + self._print_jobs = {} # type: Dict[str, PrintJobOutputModel] self._number_of_extruders = 2 # All networked printers are dual-extrusion Ultimaker machines. ## We need to override _createEmptyRequest to work for the cloud. @@ -90,8 +101,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Get remote print jobs. @pyqtProperty("QVariantList", notify = printJobsChanged) - def printJobs(self) -> List[UM3PrintJobOutputModel]: - return self._print_jobs + def queuedPrintJobs(self) -> List[UM3PrintJobOutputModel]: + return [print_job for print_job in self._print_jobs if print_job.state == "queued" or print_job.state == "error"] ## Called when the connection to the cluster changes. def connect(self) -> None: @@ -111,41 +122,182 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): .format(status_code, reply.readAll())) return - data = self._parseStatusResponse(reply) - if data is None: + printers, print_jobs = self._parseStatusResponse(reply) + if not printers and not print_jobs: return # Update all data from the cluster. - self._updatePrinters(data.get("printers", [])) - self._updatePrintJobs(data.get("print_jobs", [])) + self._updatePrinters(printers) + self._updatePrintJobs(print_jobs) @staticmethod - def _parseStatusResponse(reply: QNetworkReply) -> Optional[dict]: + def _parseStatusResponse(reply: QNetworkReply): # Optional[(CloudClusterPrinter, CloudClusterPrintJob)] doesn't work + + printers = [] + print_jobs = [] + s = '' try: - result = json.loads(bytes(reply.readAll()).decode("utf-8")) - # TODO: use model or named tuple here. - return result + s = json.loads(bytes(reply.readAll()).decode("utf-8")) + + for p in s["printers"]: + printer = CloudClusterPrinter(**p) + configuration = printer.configuration + printer.configuration = [] + for c in configuration: + extruder = CloudClusterPrinterConfiguration(**c) + extruder.material = CloudClusterPrinterConfigurationMaterial(extruder.material) + printer.configuration.append(extruder) + + printers.append(printer) + + for j in s["print_jobs"]: + job = CloudClusterPrintJob(**j) + constraints = job.constraints + job.constraints = [] + for c in constraints: + job.constraints.append(CloudClusterPrintJobConstraint(**c)) + + configuration = job.configuration + job.configuration = [] + for c in configuration: + configuration = CloudClusterPrinterConfiguration(**c) + configuration.material = CloudClusterPrinterConfigurationMaterial(configuration.material) + job.configuration.append(configuration) + + print_jobs.append(job) + except json.decoder.JSONDecodeError: Logger.logException("w", "Unable to decode JSON from reply.") return None - def _updatePrinters(self, remote_printers: List[Dict[str, any]]) -> None: - # TODO: use model or tuple for remote_printers data - for printer in remote_printers: - - # If the printer does not exist yet, create it. - if not self._getPrinterByKey(printer["uuid"]): - self._printers.append(PrinterOutputModel( - output_controller = CloudOutputController(self), - number_of_extruders = self._number_of_extruders - )) - + return printers, print_jobs + + def _updatePrinters(self, printers: List[CloudClusterPrinter]) -> None: + remote_printers = {p.uuid: p for p in printers} + + removed_printers = set(self._printers.keys()).difference(set(remote_printers.keys())) + new_printers = set(remote_printers.keys()).difference(set(self._printers.keys())) + updated_printers = set(self._printers.keys()).intersection(set(remote_printers.keys())) + + for p in removed_printers: + self._removePrinter(p) + + for p in new_printers: + self._addPrinter(printers[p]) + self._updatePrinter(printers[p]) + + for p in updated_printers: + self._updatePrinter(printers[p]) + # TODO: properly handle removed and updated printers self.printersChanged.emit() - def _updatePrintJobs(self, remote_print_jobs: List[Dict[str, any]]) -> None: - # TODO: use model or tuple for remote_print_jobs data - pass + + def _addPrinter(self, printer): + self._printers[printer.uuid] = self._createPrinterOutputModel(self, printer) + + def _createPrinterOutputModel(self, printer: CloudClusterPrinter) -> PrinterOutputModel: + return PrinterOutputModel(PrinterOutputController(self), len(printer.configuration), + firmware_version=printer.firmware_version) + + def _updatePrinter(self, guid : str, printer : CloudClusterPrinter): + model = self._printers[guid] + self._printers[guid] = self._updatePrinterOutputModel(self, printer) + + def _updatePrinterOutputModel(self, printer: CloudClusterPrinter, model : PrinterOutputModel) -> PrinterOutputModel: + model.updateKey(printer.uuid) + model.updateName(printer.friendly_name) + model.updateType(printer.machine_variant) + model.updateState(printer.status if printer.enabled else "disabled") + + for index in range(0, len(printer.configuration)): + try: + extruder = model.extruders[index] + extruder_data = printer.configuration[index] + except IndexError: + break + + extruder.updateHotendID(extruder_data.print_core_id) + + material_data = extruder_data.material + if extruder.activeMaterial is None or extruder.activeMaterial.guid != material.guid: + material = self._createMaterialOutputModel(material_data) + extruder.updateActiveMaterial(material) + + def _createMaterialOutputModel(self, material: CloudClusterPrinterConfigurationMaterial) -> MaterialOutputModel: + material_manager = CuraApplication.getInstance().getMaterialManager() + material_group_list = material_manager.getMaterialGroupListByGUID(material.guid) or [] + + # Sort the material groups by "is_read_only = True" first, and then the name alphabetically. + read_only_material_group_list = list(filter(lambda x: x.is_read_only, material_group_list)) + non_read_only_material_group_list = list(filter(lambda x: not x.is_read_only, material_group_list)) + material_group = None + if read_only_material_group_list: + read_only_material_group_list = sorted(read_only_material_group_list, key=lambda x: x.name) + material_group = read_only_material_group_list[0] + elif non_read_only_material_group_list: + non_read_only_material_group_list = sorted(non_read_only_material_group_list, key=lambda x: x.name) + material_group = non_read_only_material_group_list[0] + + if material_group: + container = material_group.root_material_node.getContainer() + color = container.getMetaDataEntry("color_code") + brand = container.getMetaDataEntry("brand") + material_type = container.getMetaDataEntry("material") + name = container.getName() + else: + Logger.log("w", + "Unable to find material with guid {guid}. Using data as provided by cluster".format( + guid=material.guid)) + color = material.color + brand = material.brand + material_type = material.material + name = "Empty" if material.material == "empty" else "Unknown" + + return MaterialOutputModel(guid=material.guid, type=material_type, brand=brand, color=color, name=name) + + + def _removePrinter(self, guid): + del self._printers[guid] + + def _updatePrintJobs(self, jobs: List[CloudClusterPrintJob]) -> None: + remote_jobs = {j.uuid: j for j in jobs} + + removed_jobs = set(self._print_jobs.keys()).difference(set(remote_jobs.keys())) + new_jobs = set(remote_jobs.keys()).difference(set(self._print_jobs.keys())) + updated_jobs = set(self._print_jobs.keys()).intersection(set(remote_jobs.keys())) + + for j in removed_jobs: + self._removePrintJob(j) + + for j in new_jobs: + self._addPrintJob(jobs[j]) + + for j in updated_jobs: + self._updatePrintJob(jobs[j]) + + # TODO: properly handle removed and updated printers + self.printJobsChanged() + + def _addPrintJob(self, job: CloudClusterPrintJob): + self._print_jobs[job.uuid] = self._createPrintJobOutputModel(job) + + def _createPrintJobOutputModel(self, job:CloudClusterPrintJob) -> PrintJobOutputModel: + controller = self._printers[job.printer_uuid]._controller # TODO: Can we access this property? + model = PrintJobOutputModel(controller, job.uuid, job.name) + assigned_printer = self._printes[job.printer_uuid] # TODO: Or do we have to use the assigned_to field? + model.updateAssignedPrinter(assigned_printer) + + def _updatePrintJobOutputModel(self, guid: str, job:CloudClusterPrintJob): + model =self._print_jobs[guid] + + model.updateTimeTotal(job.time_total) + model.updateTimeElapsed(job.time_elapsed) + model.updateOwner(job.owner) + model.updateState(job.status) + + def _removePrintJob(self, guid:str): + del self._print_jobs[guid] def _addPrintJobToQueue(self): # TODO: implement this diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 4de7263df1..f6542e3c76 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -124,9 +124,9 @@ class CloudOutputDeviceManager(NetworkClient): local_device_id = active_machine.getMetaDataEntry("um_network_key") if local_device_id: - active_output_device = CuraApplication.getInstance().getOutputDeviceManager().getActiveDevice() - active_output_device.id - + active_output_device = self._output_device_manager.getActiveDevice() + # We must find a match for the active machine and a cloud device + stored_cluster_id = active_machine.getMetaDataEntry("um_cloud_cluster_id") if stored_cluster_id not in self._remote_clusters.keys(): # Currently authenticated user does not have access to stored cluster or no user is signed in. diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models.py b/plugins/UM3NetworkPrinting/src/Cloud/Models.py index e98d848d51..7d6db9c8c0 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models.py @@ -16,3 +16,66 @@ class CloudCluster(BaseModel): def validate(self): if not self.cluster_id: raise ValueError("cluster_id is required on CloudCluster") + + +## Class representing a cloud cluster printer configuration +class CloudClusterPrinterConfigurationMaterial(BaseModel): + def __init__(self, **kwargs): + self.guid = None # type: str + self.brand = None # type: str + self.color = None # type: str + self.material = None # type: str + super().__init__(**kwargs) + + +## Class representing a cloud cluster printer configuration +class CloudClusterPrinterConfiguration(BaseModel): + def __init__(self, **kwargs): + self.extruder_index = None # type: str + self.material = None # type: CloudClusterPrinterConfigurationMaterial + self.nozzle_diameter = None # type: str + self.printer_core_id = None # type: str + super().__init__(**kwargs) + + +## Class representing a cluster printer +class CloudClusterPrinter(BaseModel): + def __init__(self, **kwargs): + self.configuration = None # type: CloudClusterPrinterConfiguration + self.enabled = None # type: str + self.firmware_version = None # type: str + self.friendly_name = None # type: str + self.ip_address = None # type: str + self.machine_variant = None # type: str + self.status = None # type: str + self.unique_name = None # type: str + self.uuid = None # type: str + super().__init__(**kwargs) + + +## Class representing a cloud cluster print job constraint +class CloudClusterPrintJobConstraint(BaseModel): + def __init__(self, **kwargs): + self.require_printer_name: None # type: str + super().__init__(**kwargs) + +## Class representing a print job +class CloudClusterPrintJob(BaseModel): + def __init__(self, **kwargs): + self.assigned_to = None # type: str + self.configuration = None # type: str + self.constraints = None # type: str + self.created_at = None # type: str + self.force = None # type: str + self.last_seen = None # type: str + self.machine_variant = None # type: str + self.name = None # type: str + self.network_error_count = None # type: str + self.owner = None # type: str + self.printer_uuid = None # type: str + self.started = None # type: str + self.status = None # type: str + self.time_elapsed = None # type: str + self.time_total = None # type: str + self.uuid = None # type: str + super().__init__(**kwargs) From 1c96c81ba98f9887cae38fe8de939db4131da9e4 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 30 Nov 2018 11:11:17 +0100 Subject: [PATCH 0525/1240] Remove unnecessary extra setMetaDataEntry This is a relic from when you first had to add the metadata entry. Now it does nothing any more because it's being set directly afterwards to the actual value that is desired in the end. Contributes to issue CURA-5876. --- cura/Settings/ExtruderStack.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/cura/Settings/ExtruderStack.py b/cura/Settings/ExtruderStack.py index d7faedb71c..ed758db183 100644 --- a/cura/Settings/ExtruderStack.py +++ b/cura/Settings/ExtruderStack.py @@ -52,8 +52,6 @@ class ExtruderStack(CuraContainerStack): return super().getNextStack() def setEnabled(self, enabled: bool) -> None: - if "enabled" not in self._metadata: - self.setMetaDataEntry("enabled", "True") self.setMetaDataEntry("enabled", str(enabled)) self.enabledChanged.emit() From 4e2f51e7e8cbc58b647260885cc96d06c00727df Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 30 Nov 2018 11:13:04 +0100 Subject: [PATCH 0526/1240] Use public activeExtruderIndex rather than internal _activeExtruderIndex So that this getter still does what it needs to do if we ever decide to add side-effects. Contributes to issue CURA-5876. --- cura/Settings/ExtruderManager.py | 4 ++-- cura/Settings/MachineManager.py | 2 +- plugins/CuraEngineBackend/ProcessSlicedLayersJob.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index 9089ba96e9..b0bcf3b100 100755 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -63,7 +63,7 @@ class ExtruderManager(QObject): if not self._application.getGlobalContainerStack(): return None # No active machine, so no active extruder. try: - return self._extruder_trains[self._application.getGlobalContainerStack().getId()][str(self._active_extruder_index)].getId() + return self._extruder_trains[self._application.getGlobalContainerStack().getId()][str(self.activeExtruderIndex)].getId() except KeyError: # Extruder index could be -1 if the global tab is selected, or the entry doesn't exist if the machine definition is wrong. return None @@ -144,7 +144,7 @@ class ExtruderManager(QObject): @pyqtSlot(result = QObject) def getActiveExtruderStack(self) -> Optional["ExtruderStack"]: - return self.getExtruderStack(self._active_extruder_index) + return self.getExtruderStack(self.activeExtruderIndex) ## Get an extruder stack by index def getExtruderStack(self, index) -> Optional["ExtruderStack"]: diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 226a352602..40a3bfc563 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -866,7 +866,7 @@ class MachineManager(QObject): caution_message = Message(catalog.i18nc( "@info:generic", "Settings have been changed to match the current availability of extruders: [%s]" % ", ".join(add_user_changes)), - lifetime=0, + lifetime = 0, title = catalog.i18nc("@info:title", "Settings updated")) caution_message.show() diff --git a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py index 594bf3a43e..71c96880e8 100644 --- a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py +++ b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py @@ -195,7 +195,7 @@ class ProcessSlicedLayersJob(Job): if extruders: material_color_map = numpy.zeros((len(extruders), 4), dtype=numpy.float32) for extruder in extruders: - position = int(extruder.getMetaDataEntry("position", default="0")) # Get the position + position = int(extruder.getMetaDataEntry("position", default = "0")) try: default_color = ExtrudersModel.defaultColors[position] except IndexError: From 17945c6b16ca3d0be7274a6946f68f4d381c8d48 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 30 Nov 2018 11:32:24 +0100 Subject: [PATCH 0527/1240] [WIP] Separate components in different files Contributes to CURA-5941 --- resources/qml/ExpandableComponent.qml | 3 + .../{ => Custom}/CustomPrintSetup.qml | 4 +- .../PrintSetupSelectorContents.qml | 31 +- .../RecommendedInfillDensitySelector.qml | 348 ++++++ .../Recommended/RecommendedPrintSetup.qml | 162 +++ .../RecommendedQualityProfileSelector.qml | 464 +++++++ .../RecommendedSupportSelector.qml | 175 +++ .../RecommendedPrintSetup.qml | 1064 ----------------- resources/qml/qmldir | 3 +- 9 files changed, 1173 insertions(+), 1081 deletions(-) rename resources/qml/PrintSetupSelector/{ => Custom}/CustomPrintSetup.qml (77%) create mode 100644 resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml create mode 100644 resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml create mode 100644 resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml create mode 100644 resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml delete mode 100644 resources/qml/PrintSetupSelector/RecommendedPrintSetup.qml diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 47fa226e9d..82747d1c5b 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -27,6 +27,9 @@ Item // The popupItem holds the QML item that is shown when the "open" button is pressed property var popupItem + // The popupItem holds the QML item that is shown when the "open" button is pressed + property var componentItem + property color popupBackgroundColor: UM.Theme.getColor("action_button") property color headerBackgroundColor: UM.Theme.getColor("action_button") diff --git a/resources/qml/PrintSetupSelector/CustomPrintSetup.qml b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml similarity index 77% rename from resources/qml/PrintSetupSelector/CustomPrintSetup.qml rename to resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml index f58695b48f..51cd8eff0d 100644 --- a/resources/qml/PrintSetupSelector/CustomPrintSetup.qml +++ b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml @@ -4,7 +4,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.0 -import "../Settings" +import Cura 1.0 as Cura -SettingView { +Cura.SettingView { } diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml index 309d612fae..f21253acd7 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml @@ -7,6 +7,9 @@ import QtQuick.Controls 2.3 import UM 1.3 as UM import Cura 1.0 as Cura +import "Recommended" +import "Custom" + Item { id: popup @@ -22,7 +25,7 @@ Item { id: header height: UM.Theme.getSize("print_setup_widget_header").height - color: UM.Theme.getColor("action_button_hovered") // TODO: It's not clear the color that we need to use here + color: "transparent" //UM.Theme.getColor("action_button_hovered") // TODO: It's not clear the color that we need to use here anchors { @@ -111,19 +114,19 @@ Item onHideTooltip: base.hideTooltip() visible: currentModeIndex == 0 } - - CustomPrintSetup - { - anchors - { - left: parent.left - right: parent.right - top: parent.top - } - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() - visible: currentModeIndex == 1 - } +// +// CustomPrintSetup +// { +// anchors +// { +// left: parent.left +// right: parent.right +// top: parent.top +// } +// onShowTooltip: base.showTooltip(item, location, text) +// onHideTooltip: base.hideTooltip() +// visible: currentModeIndex == 1 +// } } Rectangle diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml new file mode 100644 index 0000000000..34cb8f2f20 --- /dev/null +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml @@ -0,0 +1,348 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +import UM 1.2 as UM +import Cura 1.0 as Cura + + +// +// Infill +// +Item +{ + id: infillRow + + Cura.IconWithText + { + id: infillRowTitle + source: UM.Theme.getIcon("category_infill") + text: catalog.i18nc("@label", "Infill") + " (%)" + anchors.bottom: parent.bottom + width: labelColumnWidth + } + + Item + { + id: infillCellRight + + height: infillSlider.height + UM.Theme.getSize("thick_margin").height + enableGradualInfillCheckBox.visible * (enableGradualInfillCheckBox.height + UM.Theme.getSize("thick_margin").height) + + anchors.left: infillRowTitle.right + anchors.right: parent.right + + Label + { + id: selectedInfillRateText + + anchors.left: infillSlider.left + anchors.right: parent.right + + text: parseInt(infillDensity.properties.value) + "%" + horizontalAlignment: Text.AlignLeft + + color: infillSlider.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") + } + + // We use a binding to make sure that after manually setting infillSlider.value it is still bound to the property provider + Binding + { + target: infillSlider + property: "value" + value: parseInt(infillDensity.properties.value) + } + + Slider + { + id: infillSlider + + anchors.top: selectedInfillRateText.bottom + anchors.left: parent.left + anchors.right: infillIcon.left + anchors.rightMargin: UM.Theme.getSize("thick_margin").width + + height: UM.Theme.getSize("thick_margin").height + width: parseInt(infillCellRight.width - UM.Theme.getSize("thick_margin").width - style.handleWidth) + + minimumValue: 0 + maximumValue: 100 + stepSize: 1 + tickmarksEnabled: true + + // disable slider when gradual support is enabled + enabled: parseInt(infillSteps.properties.value) == 0 + + // set initial value from stack + value: parseInt(infillDensity.properties.value) + + onValueChanged: + { + + // Don't round the value if it's already the same + if (parseInt(infillDensity.properties.value) == infillSlider.value) + { + return + } + + // Round the slider value to the nearest multiple of 10 (simulate step size of 10) + var roundedSliderValue = Math.round(infillSlider.value / 10) * 10 + + // Update the slider value to represent the rounded value + infillSlider.value = roundedSliderValue + + // Update value only if the Recomended mode is Active, + // Otherwise if I change the value in the Custom mode the Recomended view will try to repeat + // same operation + var active_mode = UM.Preferences.getValue("cura/active_mode") + + if (active_mode == 0 || active_mode == "simple") + { + Cura.MachineManager.setSettingForAllExtruders("infill_sparse_density", "value", roundedSliderValue) + Cura.MachineManager.resetSettingForAllExtruders("infill_line_distance") + } + } + + style: SliderStyle + { + groove: Rectangle + { + id: groove + implicitWidth: 200 * screenScaleFactor + implicitHeight: 2 * screenScaleFactor + color: control.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") + radius: 1 + } + + handle: Item + { + Rectangle + { + id: handleButton + anchors.centerIn: parent + color: control.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") + implicitWidth: 10 * screenScaleFactor + implicitHeight: 10 * screenScaleFactor + radius: 10 * screenScaleFactor + } + } + + tickmarks: Repeater + { + id: repeater + model: control.maximumValue / control.stepSize + 1 + + // check if a tick should be shown based on it's index and wether the infill density is a multiple of 10 (slider step size) + function shouldShowTick (index) + { + if (index % 10 == 0) + { + return true + } + return false + } + + Rectangle + { + anchors.verticalCenter: parent.verticalCenter + color: control.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") + width: 1 * screenScaleFactor + height: 6 * screenScaleFactor + x: Math.round(styleData.handleWidth / 2 + index * ((repeater.width - styleData.handleWidth) / (repeater.count-1))) + visible: shouldShowTick(index) + } + } + } + } + + Rectangle + { + id: infillIcon + + width: Math.round((parent.width / 5) - (UM.Theme.getSize("thick_margin").width)) + height: width + + anchors.right: parent.right + anchors.top: parent.top + anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height / 2) + + // we loop over all density icons and only show the one that has the current density and steps + Repeater + { + id: infillIconList + model: infillModel + anchors.fill: parent + + function activeIndex () + { + for (var i = 0; i < infillModel.count; i++) + { + var density = Math.round(infillDensity.properties.value) + var steps = Math.round(infillSteps.properties.value) + var infillModelItem = infillModel.get(i) + + if (infillModelItem != "undefined" + && density >= infillModelItem.percentageMin + && density <= infillModelItem.percentageMax + && steps >= infillModelItem.stepsMin + && steps <= infillModelItem.stepsMax) + { + return i + } + } + return -1 + } + + Rectangle + { + anchors.fill: parent + visible: infillIconList.activeIndex() == index + + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("quality_slider_unavailable") + + UM.RecolorImage + { + anchors.fill: parent + anchors.margins: 2 * screenScaleFactor + sourceSize.width: width + sourceSize.height: width + source: UM.Theme.getIcon(model.icon) + color: UM.Theme.getColor("quality_slider_unavailable") + } + } + } + } + + // Gradual Support Infill Checkbox + CheckBox + { + id: enableGradualInfillCheckBox + property alias _hovered: enableGradualInfillMouseArea.containsMouse + + anchors.top: infillSlider.bottom + anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height / 2) // closer to slider since it belongs to the same category + anchors.left: infillCellRight.left + + style: UM.Theme.styles.checkbox + enabled: base.settingsEnabled + visible: infillSteps.properties.enabled == "True" + checked: parseInt(infillSteps.properties.value) > 0 + + MouseArea + { + id: enableGradualInfillMouseArea + + anchors.fill: parent + hoverEnabled: true + enabled: true + + property var previousInfillDensity: parseInt(infillDensity.properties.value) + + onClicked: + { + // Set to 90% only when enabling gradual infill + var newInfillDensity; + if (parseInt(infillSteps.properties.value) == 0) + { + previousInfillDensity = parseInt(infillDensity.properties.value) + newInfillDensity = 90 + } else { + newInfillDensity = previousInfillDensity + } + Cura.MachineManager.setSettingForAllExtruders("infill_sparse_density", "value", String(newInfillDensity)) + + var infill_steps_value = 0 + if (parseInt(infillSteps.properties.value) == 0) + { + infill_steps_value = 5 + } + + Cura.MachineManager.setSettingForAllExtruders("gradual_infill_steps", "value", infill_steps_value) + } + + onEntered: base.showTooltip(enableGradualInfillCheckBox, Qt.point(-infillCellRight.x, 0), + catalog.i18nc("@label", "Gradual infill will gradually increase the amount of infill towards the top.")) + + onExited: base.hideTooltip() + + } + + Label + { + id: gradualInfillLabel + height: parent.height + anchors.left: enableGradualInfillCheckBox.right + anchors.leftMargin: Math.round(UM.Theme.getSize("thick_margin").width / 2) + verticalAlignment: Text.AlignVCenter; + text: catalog.i18nc("@label", "Enable gradual") + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + } + } + + // Infill list model for mapping icon + ListModel + { + id: infillModel + Component.onCompleted: + { + infillModel.append({ + percentageMin: -1, + percentageMax: 0, + stepsMin: -1, + stepsMax: 0, + icon: "hollow" + }) + infillModel.append({ + percentageMin: 0, + percentageMax: 40, + stepsMin: -1, + stepsMax: 0, + icon: "sparse" + }) + infillModel.append({ + percentageMin: 40, + percentageMax: 89, + stepsMin: -1, + stepsMax: 0, + icon: "dense" + }) + infillModel.append({ + percentageMin: 90, + percentageMax: 9999999999, + stepsMin: -1, + stepsMax: 0, + icon: "solid" + }) + infillModel.append({ + percentageMin: 0, + percentageMax: 9999999999, + stepsMin: 1, + stepsMax: 9999999999, + icon: "gradual" + }) + } + } + } + + UM.SettingPropertyProvider + { + id: infillDensity + containerStackId: Cura.MachineManager.activeStackId + key: "infill_sparse_density" + watchedProperties: [ "value" ] + storeIndex: 0 + } + + UM.SettingPropertyProvider + { + id: infillSteps + containerStackId: Cura.MachineManager.activeStackId + key: "gradual_infill_steps" + watchedProperties: ["value", "enabled"] + storeIndex: 0 + } +} \ No newline at end of file diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml new file mode 100644 index 0000000000..2d4308c8be --- /dev/null +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml @@ -0,0 +1,162 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +Rectangle +{ + id: base + + signal showTooltip(Item item, point location, string text) + signal hideTooltip() +// width: parent.width + height: childrenRect.height + 2 * padding + color: "red" + opacity: 0.5 + + property Action configureSettings + + property bool settingsEnabled: Cura.ExtruderManager.activeExtruderStackId || extrudersEnabledCount.properties.value == 1 + property real padding: UM.Theme.getSize("thick_margin").width + + UM.I18nCatalog + { + id: catalog + name: "cura" + } + +// Rectangle +// { +// width: parent.width - 2 * parent.padding +// anchors +// { +// left: parent.left +// right: parent.right +// top: parent.top +// margins: parent.padding +// } +// color: "blue" +// height: 50 +// } + + Column + { + width: parent.width - 2 * parent.padding + spacing: UM.Theme.getSize("default_margin").height + anchors + { + left: parent.left + right: parent.right + top: parent.top + margins: parent.padding + } + + // TODO + property real labelColumnWidth: Math.round(width / 3) + property real settingsColumnWidth: width - labelColumnWidth + + RecommendedQualityProfileSelector + { + width: parent.width + // TODO Create a reusable component with these properties to not define them separately for each component + property real labelColumnWidth: parent.labelColumnWidth + property real settingsColumnWidth: parent.settingsColumnWidth + } + +// RecommendedInfillDensitySelector +// { +// width: parent.width +// height: childrenRect.height +// // TODO Create a reusable component with these properties to not define them separately for each component +// property real labelColumnWidth: parent.labelColumnWidth +// property real settingsColumnWidth: parent.settingsColumnWidth +// } +// +// RecommendedSupportSelector +// { +// width: parent.width +// height: childrenRect.height +// // TODO Create a reusable component with these properties to not define them separately for each component +// property real labelColumnWidth: parent.labelColumnWidth +// property real settingsColumnWidth: parent.settingsColumnWidth +// } + + +// +// // Adhesion +// Row +// { +// anchors.left: parent.left +// anchors.right: parent.right +// height: childrenRect.height +// +// Cura.IconWithText +// { +// id: adhesionHelperLabel +// visible: adhesionCheckBox.visible +// source: UM.Theme.getIcon("category_adhesion") +// text: catalog.i18nc("@label", "Adhesion") +// width: labelColumnWidth +// } +// +// CheckBox +// { +// id: adhesionCheckBox +// property alias _hovered: adhesionMouseArea.containsMouse +// +// //: Setting enable printing build-plate adhesion helper checkbox +// style: UM.Theme.styles.checkbox +// enabled: base.settingsEnabled +// +// visible: platformAdhesionType.properties.enabled == "True" +// checked: platformAdhesionType.properties.value != "skirt" && platformAdhesionType.properties.value != "none" +// +// MouseArea +// { +// id: adhesionMouseArea +// anchors.fill: parent +// hoverEnabled: true +// enabled: base.settingsEnabled +// onClicked: +// { +// var adhesionType = "skirt" +// if(!parent.checked) +// { +// // Remove the "user" setting to see if the rest of the stack prescribes a brim or a raft +// platformAdhesionType.removeFromContainer(0) +// adhesionType = platformAdhesionType.properties.value +// if(adhesionType == "skirt" || adhesionType == "none") +// { +// // If the rest of the stack doesn't prescribe an adhesion-type, default to a brim +// adhesionType = "brim" +// } +// } +// platformAdhesionType.setPropertyValue("value", adhesionType) +// } +// onEntered: +// { +// base.showTooltip(adhesionCheckBox, Qt.point(-adhesionCheckBox.x, 0), +// catalog.i18nc("@label", "Enable printing a brim or raft. This will add a flat area around or under your object which is easy to cut off afterwards.")); +// } +// onExited: base.hideTooltip() +// } +// } +// } + } + + + UM.SettingPropertyProvider + { + id: platformAdhesionType + containerStack: Cura.MachineManager.activeMachine + removeUnusedValue: false //Doesn't work with settings that are resolved. + key: "adhesion_type" + watchedProperties: [ "value", "enabled" ] + storeIndex: 0 + } +} diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml new file mode 100644 index 0000000000..3bf93c0c07 --- /dev/null +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml @@ -0,0 +1,464 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +import UM 1.2 as UM +import Cura 1.0 as Cura + + +// +// Quality profile +// +Item +{ + id: qualityRow + height: childrenRect.height + + Timer + { + id: qualitySliderChangeTimer + interval: 50 + running: false + repeat: false + onTriggered: + { + var item = Cura.QualityProfilesDropDownMenuModel.getItem(qualitySlider.value); + Cura.MachineManager.activeQualityGroup = item.quality_group; + } + } + + Component.onCompleted: qualityModel.update() + + Connections + { + target: Cura.QualityProfilesDropDownMenuModel + onItemsChanged: qualityModel.update() + } + + Connections { + target: base + onVisibleChanged: + { + // update needs to be called when the widgets are visible, otherwise the step width calculation + // will fail because the width of an invisible item is 0. + if (visible) + { + qualityModel.update(); + } + } + } + + ListModel + { + id: qualityModel + + property var totalTicks: 0 + property var availableTotalTicks: 0 + property var existingQualityProfile: 0 + + property var qualitySliderActiveIndex: 0 + property var qualitySliderStepWidth: 0 + property var qualitySliderAvailableMin: 0 + property var qualitySliderAvailableMax: 0 + property var qualitySliderMarginRight: 0 + + function update () + { + reset() + + var availableMin = -1 + var availableMax = -1 + + for (var i = 0; i < Cura.QualityProfilesDropDownMenuModel.rowCount(); i++) + { + var qualityItem = Cura.QualityProfilesDropDownMenuModel.getItem(i) + + // Add each quality item to the UI quality model + qualityModel.append(qualityItem) + + // Set selected value + if (Cura.MachineManager.activeQualityType == qualityItem.quality_type) + { + // set to -1 when switching to user created profile so all ticks are clickable + if (Cura.SimpleModeSettingsManager.isProfileUserCreated) + { + qualityModel.qualitySliderActiveIndex = -1 + } + else + { + qualityModel.qualitySliderActiveIndex = i + } + + qualityModel.existingQualityProfile = 1 + } + + // Set min available + if (qualityItem.available && availableMin == -1) + { + availableMin = i + } + + // Set max available + if (qualityItem.available) + { + availableMax = i + } + } + + // Set total available ticks for active slider part + if (availableMin != -1) + { + qualityModel.availableTotalTicks = availableMax - availableMin + 1 + } + + // Calculate slider values + calculateSliderStepWidth(qualityModel.totalTicks) + calculateSliderMargins(availableMin, availableMax, qualityModel.totalTicks) + + qualityModel.qualitySliderAvailableMin = availableMin + qualityModel.qualitySliderAvailableMax = availableMax + } + + function calculateSliderStepWidth (totalTicks) + { + qualityModel.qualitySliderStepWidth = totalTicks != 0 ? Math.round((settingsColumnWidth) / (totalTicks)) : 0 + } + + function calculateSliderMargins (availableMin, availableMax, totalTicks) + { + if (availableMin == -1 || (availableMin == 0 && availableMax == 0)) + { + qualityModel.qualitySliderMarginRight = Math.round(settingsColumnWidth) + } + else if (availableMin == availableMax) + { + qualityModel.qualitySliderMarginRight = Math.round((totalTicks - availableMin) * qualitySliderStepWidth) + } + else + { + qualityModel.qualitySliderMarginRight = Math.round((totalTicks - availableMax) * qualitySliderStepWidth) + } + } + + function reset () { + qualityModel.clear() + qualityModel.availableTotalTicks = 0 + qualityModel.existingQualityProfile = 0 + + // check, the ticks count cannot be less than zero + qualityModel.totalTicks = Math.max(0, Cura.QualityProfilesDropDownMenuModel.rowCount() - 1) + } + } + + Cura.IconWithText + { + id: qualityRowTitle + source: UM.Theme.getIcon("category_layer_height") + text: catalog.i18nc("@label", "Layer Height") +// anchors.bottom: speedSlider.bottom + width: labelColumnWidth + } + + //Print speed slider + Rectangle + { + id: speedSlider + + anchors + { + left: qualityRowTitle.right + right: parent.right + } + + color: "green" + height: 20 + } +// +// // Show titles for the each quality slider ticks +// Item +// { +// anchors.left: speedSlider.left +// anchors.top: speedSlider.bottom +// +// Repeater +// { +// model: qualityModel +// +// Label +// { +// anchors.verticalCenter: parent.verticalCenter +// anchors.top: parent.top +// color: (Cura.MachineManager.activeMachine != null && Cura.QualityProfilesDropDownMenuModel.getItem(index).available) ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") +// text: +// { +// var result = "" +// if(Cura.MachineManager.activeMachine != null) +// { +// result = Cura.QualityProfilesDropDownMenuModel.getItem(index).layer_height +// +// if(result == undefined) +// { +// result = ""; +// } +// else +// { +// result = Number(Math.round(result + "e+2") + "e-2"); //Round to 2 decimals. Javascript makes this difficult... +// if (result == undefined || result != result) //Parse failure. +// { +// result = ""; +// } +// } +// } +// return result +// } +// +// x: +// { +// // Make sure the text aligns correctly with each tick +// if (qualityModel.totalTicks == 0) +// { +// // If there is only one tick, align it centrally +// return Math.round(((settingsColumnWidth) - width) / 2) +// } +// else if (index == 0) +// { +// return Math.round(settingsColumnWidth / qualityModel.totalTicks) * index +// } +// else if (index == qualityModel.totalTicks) +// { +// return Math.round(settingsColumnWidth / qualityModel.totalTicks) * index - width +// } +// else +// { +// return Math.round((settingsColumnWidth / qualityModel.totalTicks) * index - (width / 2)) +// } +// } +// } +// } +// } +// +// //Print speed slider +// Rectangle +// { +// id: speedSlider +// +// anchors +// { +// left: qualityRowTitle.right +// right: parent.right +// } +// +// // 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("thick_margin").width, customisedSettings.height), content) +//// } +//// else +//// { +//// base.hideTooltip() +//// } +//// } +//// +//// id: unavailableLineToolTip +//// height: 20 * screenScaleFactor // 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 qualityModel.qualitySliderStepWidth * qualityModel.totalTicks +//// } +//// 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) +//// } +//// } +//// +//// Item +//// { +//// id: rightArea +//// width: +//// { +//// if(qualityModel.availableTotalTicks == 0) +//// return 0 +//// +//// return qualityModel.qualitySliderMarginRight - 10 +//// } +//// height: parent.height +//// 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 +// { +// id: groovechildrect +// width: parent.width +// height: 2 * screenScaleFactor +// color: UM.Theme.getColor("quality_slider_unavailable") +// anchors.verticalCenter: qualitySlider.verticalCenter +// +// // Draw ticks +// Repeater +// { +// id: qualityRepeater +// model: qualityModel.totalTicks > 0 ? qualityModel : 0 +// +// Rectangle +// { +// color: Cura.QualityProfilesDropDownMenuModel.getItem(index).available ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") +// implicitWidth: 4 * screenScaleFactor +// implicitHeight: implicitWidth +// anchors.verticalCenter: parent.verticalCenter +// x: Math.round(qualityModel.qualitySliderStepWidth * index) +// radius: Math.round(implicitWidth / 2) +// } +// } +// } +// +// // Draw available slider +// Slider +// { +// id: qualitySlider +// height: UM.Theme.getSize("thick_margin").height +// anchors.bottom: parent.bottom +// enabled: qualityModel.totalTicks > 0 && !Cura.SimpleModeSettingsManager.isProfileCustomized +// visible: qualityModel.availableTotalTicks > 0 +// updateValueWhileDragging : false +// +// minimumValue: qualityModel.qualitySliderAvailableMin >= 0 ? qualityModel.qualitySliderAvailableMin : 0 +// // maximumValue must be greater than minimumValue to be able to see the handle. While the value is strictly +// // speaking not always correct, it seems to have the correct behavior (switching from 0 available to 1 available) +// maximumValue: qualityModel.qualitySliderAvailableMax >= 1 ? qualityModel.qualitySliderAvailableMax : 1 +// stepSize: 1 +// +// value: qualityModel.qualitySliderActiveIndex +// +// width: qualityModel.qualitySliderStepWidth * (qualityModel.availableTotalTicks - 1) +// +// anchors.right: parent.right +// anchors.rightMargin: qualityModel.qualitySliderMarginRight +// +// style: SliderStyle +// { +// //Draw Available line +// groove: Rectangle +// { +// implicitHeight: 2 * screenScaleFactor +// color: UM.Theme.getColor("quality_slider_available") +// radius: Math.round(height / 2) +// } +// +// handle: Rectangle +// { +// id: qualityhandleButton +// color: UM.Theme.getColor("quality_slider_available") +// implicitWidth: 12 * screenScaleFactor +// implicitHeight: implicitWidth +// radius: Math.round(implicitWidth / 2) +// visible: !Cura.SimpleModeSettingsManager.isProfileCustomized && !Cura.SimpleModeSettingsManager.isProfileUserCreated && qualityModel.existingQualityProfile +// } +// } +// +// onValueChanged: +// { +// // only change if an active machine is set and the slider is visible at all. +// if (Cura.MachineManager.activeMachine != null && visible) +// { +// // prevent updating during view initializing. Trigger only if the value changed by user +// if (qualitySlider.value != qualityModel.qualitySliderActiveIndex && qualityModel.qualitySliderActiveIndex != -1) +// { +// // start updating with short delay +// qualitySliderChangeTimer.start() +// } +// } +// } +// } +// +// MouseArea +// { +// id: speedSliderMouseArea +// anchors.fill: parent +// hoverEnabled: true +// enabled: Cura.SimpleModeSettingsManager.isProfileUserCreated +// +// onEntered: +// { +// var content = catalog.i18nc("@tooltip", "A custom profile is currently active. To enable the quality slider, choose a default quality profile in Custom tab") +// base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, customisedSettings.height), content) +// } +// onExited: base.hideTooltip() +// } +// } +// +// UM.SimpleButton +// { +// id: customisedSettings +// +// visible: Cura.SimpleModeSettingsManager.isProfileCustomized || Cura.SimpleModeSettingsManager.isProfileUserCreated +// height: Math.round(speedSlider.height * 0.8) +// width: Math.round(speedSlider.height * 0.8) +// +// anchors.verticalCenter: speedSlider.verticalCenter +// anchors.right: speedSlider.left +// anchors.rightMargin: Math.round(UM.Theme.getSize("thick_margin").width / 2) +// +// color: hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button"); +// iconSource: UM.Theme.getIcon("reset"); +// +// onClicked: +// { +// // if the current profile is user-created, switch to a built-in quality +// Cura.MachineManager.resetToUseDefaultQuality() +// } +// onEntered: +// { +// var content = catalog.i18nc("@tooltip","You have modified some profile settings. If you want to change these go to custom mode.") +// base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, customisedSettings.height), content) +// } +// onExited: base.hideTooltip() +// } +} \ No newline at end of file diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml new file mode 100644 index 0000000000..9c4e5ed576 --- /dev/null +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml @@ -0,0 +1,175 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +// +// Enable support +// +Row +{ + + Cura.IconWithText + { + id: enableSupportLabel + visible: enableSupportCheckBox.visible + source: UM.Theme.getIcon("category_support") + text: catalog.i18nc("@label", "Support") + width: labelColumnWidth + } + + CheckBox + { + id: enableSupportCheckBox + property alias _hovered: enableSupportMouseArea.containsMouse + + style: UM.Theme.styles.checkbox + enabled: base.settingsEnabled + + visible: supportEnabled.properties.enabled == "True" + checked: supportEnabled.properties.value == "True" + + MouseArea + { + id: enableSupportMouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: supportEnabled.setPropertyValue("value", supportEnabled.properties.value != "True") + + onEntered: base.showTooltip(enableSupportCheckBox, Qt.point(-enableSupportCheckBox.x, 0), + catalog.i18nc("@label", "Generate structures to support parts of the model which have overhangs. Without these structures, such parts would collapse during printing.")) + + onExited: base.hideTooltip() + + } + } + + ComboBox + { + id: supportExtruderCombobox + visible: enableSupportCheckBox.visible && (supportEnabled.properties.value == "True") && (extrudersEnabledCount.properties.value > 1) + model: extruderModel + + property string color_override: "" // for manually setting values + property string color: // is evaluated automatically, but the first time is before extruderModel being filled + { + var current_extruder = extruderModel.get(currentIndex); + color_override = ""; + if (current_extruder === undefined) return "" + return (current_extruder.color) ? current_extruder.color : ""; + } + + textRole: "text" // this solves that the combobox isn't populated in the first time Cura is started + + width: Math.round(UM.Theme.getSize("print_setup_widget").width * .55) - Math.round(UM.Theme.getSize("thick_margin").width / 2) - enableSupportCheckBox.width + height: ((supportEnabled.properties.value == "True") && (machineExtruderCount.properties.value > 1)) ? UM.Theme.getSize("setting_control").height : 0 + + Behavior on height { NumberAnimation { duration: 100 } } + + style: UM.Theme.styles.combobox_color + enabled: base.settingsEnabled + property alias _hovered: supportExtruderMouseArea.containsMouse + + currentIndex: + { + if (supportExtruderNr.properties == null) + { + return Cura.MachineManager.defaultExtruderPosition + } + else + { + var extruder = parseInt(supportExtruderNr.properties.value) + if ( extruder === -1) + { + return Cura.MachineManager.defaultExtruderPosition + } + return extruder; + } + } + + onActivated: supportExtruderNr.setPropertyValue("value", String(index)) + + MouseArea + { + id: supportExtruderMouseArea + anchors.fill: parent + hoverEnabled: true + enabled: base.settingsEnabled + acceptedButtons: Qt.NoButton + onEntered: + { + base.showTooltip(supportExtruderCombobox, Qt.point(-supportExtruderCombobox.x, 0), + catalog.i18nc("@label", "Select which extruder to use for support. This will build up supporting structures below the model to prevent the model from sagging or printing in mid air.")); + } + onExited: base.hideTooltip() + + } + + function updateCurrentColor() + { + var current_extruder = extruderModel.get(currentIndex) + if (current_extruder !== undefined) + { + supportExtruderCombobox.color_override = current_extruder.color + } + } + } + + ListModel + { + id: extruderModel + Component.onCompleted: populateExtruderModel() + } + + //: Model used to populate the extrudelModel + Cura.ExtrudersModel + { + id: extruders + onModelChanged: populateExtruderModel() + } + + UM.SettingPropertyProvider + { + id: supportEnabled + containerStack: Cura.MachineManager.activeMachine + key: "support_enable" + watchedProperties: [ "value", "enabled", "description" ] + storeIndex: 0 + } + + UM.SettingPropertyProvider + { + id: extrudersEnabledCount + containerStack: Cura.MachineManager.activeMachine + key: "extruders_enabled_count" + watchedProperties: [ "value" ] + storeIndex: 0 + } + + UM.SettingPropertyProvider + { + id: supportExtruderNr + containerStack: Cura.MachineManager.activeMachine + key: "support_extruder_nr" + watchedProperties: [ "value" ] + storeIndex: 0 + } + + function populateExtruderModel() + { + extruderModel.clear() + for (var extruderNumber = 0; extruderNumber < extruders.rowCount(); extruderNumber++) + { + extruderModel.append({ + text: extruders.getItem(extruderNumber).name, + color: extruders.getItem(extruderNumber).color + }) + } + supportExtruderCombobox.updateCurrentColor() + } +} \ No newline at end of file diff --git a/resources/qml/PrintSetupSelector/RecommendedPrintSetup.qml b/resources/qml/PrintSetupSelector/RecommendedPrintSetup.qml deleted file mode 100644 index b48282135b..0000000000 --- a/resources/qml/PrintSetupSelector/RecommendedPrintSetup.qml +++ /dev/null @@ -1,1064 +0,0 @@ -// Copyright (c) 2018 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.7 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 - -import UM 1.2 as UM -import Cura 1.0 as Cura - -Column -{ - id: base - - signal showTooltip(Item item, point location, string text) - signal hideTooltip() - height: childrenRect.height + 2 * padding - - padding: UM.Theme.getSize("thick_margin").width - spacing: UM.Theme.getSize("default_margin").height - - property Action configureSettings - - property bool settingsEnabled: Cura.ExtruderManager.activeExtruderStackId || extrudersEnabledCount.properties.value == 1 - property real labelColumnWidth: Math.round(width / 3) - property real settingsColumnWidth: width - labelColumnWidth - - UM.I18nCatalog - { - id: catalog - name: "cura" - } - - // - // Quality profile - // - Item - { - id: qualityRow - - anchors.left: parent.left - anchors.right: parent.right - height: childrenRect.height - - Timer - { - id: qualitySliderChangeTimer - interval: 50 - running: false - repeat: false - onTriggered: - { - var item = Cura.QualityProfilesDropDownMenuModel.getItem(qualitySlider.value); - Cura.MachineManager.activeQualityGroup = item.quality_group; - } - } - - Component.onCompleted: qualityModel.update() - - Connections - { - target: Cura.QualityProfilesDropDownMenuModel - onItemsChanged: qualityModel.update() - } - - Connections { - target: base - onVisibleChanged: - { - // update needs to be called when the widgets are visible, otherwise the step width calculation - // will fail because the width of an invisible item is 0. - if (visible) - { - qualityModel.update(); - } - } - } - - ListModel - { - id: qualityModel - - property var totalTicks: 0 - property var availableTotalTicks: 0 - property var existingQualityProfile: 0 - - property var qualitySliderActiveIndex: 0 - property var qualitySliderStepWidth: 0 - property var qualitySliderAvailableMin: 0 - property var qualitySliderAvailableMax: 0 - property var qualitySliderMarginRight: 0 - - function update () - { - reset() - - var availableMin = -1 - var availableMax = -1 - - for (var i = 0; i < Cura.QualityProfilesDropDownMenuModel.rowCount(); i++) - { - var qualityItem = Cura.QualityProfilesDropDownMenuModel.getItem(i) - - // Add each quality item to the UI quality model - qualityModel.append(qualityItem) - - // Set selected value - if (Cura.MachineManager.activeQualityType == qualityItem.quality_type) - { - // set to -1 when switching to user created profile so all ticks are clickable - if (Cura.SimpleModeSettingsManager.isProfileUserCreated) - { - qualityModel.qualitySliderActiveIndex = -1 - } - else - { - qualityModel.qualitySliderActiveIndex = i - } - - qualityModel.existingQualityProfile = 1 - } - - // Set min available - if (qualityItem.available && availableMin == -1) - { - availableMin = i - } - - // Set max available - if (qualityItem.available) - { - availableMax = i - } - } - - // Set total available ticks for active slider part - if (availableMin != -1) - { - qualityModel.availableTotalTicks = availableMax - availableMin + 1 - } - - // Calculate slider values - calculateSliderStepWidth(qualityModel.totalTicks) - calculateSliderMargins(availableMin, availableMax, qualityModel.totalTicks) - - qualityModel.qualitySliderAvailableMin = availableMin - qualityModel.qualitySliderAvailableMax = availableMax - } - - function calculateSliderStepWidth (totalTicks) - { - qualityModel.qualitySliderStepWidth = totalTicks != 0 ? Math.round((settingsColumnWidth) / (totalTicks)) : 0 - } - - function calculateSliderMargins (availableMin, availableMax, totalTicks) - { - if (availableMin == -1 || (availableMin == 0 && availableMax == 0)) - { - qualityModel.qualitySliderMarginRight = Math.round(settingsColumnWidth) - } - else if (availableMin == availableMax) - { - qualityModel.qualitySliderMarginRight = Math.round((totalTicks - availableMin) * qualitySliderStepWidth) - } - else - { - qualityModel.qualitySliderMarginRight = Math.round((totalTicks - availableMax) * qualitySliderStepWidth) - } - } - - function reset () { - qualityModel.clear() - qualityModel.availableTotalTicks = 0 - qualityModel.existingQualityProfile = 0 - - // check, the ticks count cannot be less than zero - qualityModel.totalTicks = Math.max(0, Cura.QualityProfilesDropDownMenuModel.rowCount() - 1) - } - } - - Cura.IconWithText - { - id: qualityRowTitle - source: UM.Theme.getIcon("category_layer_height") - text: catalog.i18nc("@label", "Layer Height") - anchors.bottom: speedSlider.bottom - width: labelColumnWidth - } - - // Show titles for the each quality slider ticks - Item - { - anchors.left: speedSlider.left - anchors.top: speedSlider.bottom - - Repeater - { - model: qualityModel - - Label - { - anchors.verticalCenter: parent.verticalCenter - anchors.top: parent.top - color: (Cura.MachineManager.activeMachine != null && Cura.QualityProfilesDropDownMenuModel.getItem(index).available) ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") - text: - { - var result = "" - if(Cura.MachineManager.activeMachine != null) - { - result = Cura.QualityProfilesDropDownMenuModel.getItem(index).layer_height - - if(result == undefined) - { - result = ""; - } - else - { - result = Number(Math.round(result + "e+2") + "e-2"); //Round to 2 decimals. Javascript makes this difficult... - if (result == undefined || result != result) //Parse failure. - { - result = ""; - } - } - } - return result - } - - x: - { - // Make sure the text aligns correctly with each tick - if (qualityModel.totalTicks == 0) - { - // If there is only one tick, align it centrally - return Math.round(((settingsColumnWidth) - width) / 2) - } - else if (index == 0) - { - return Math.round(settingsColumnWidth / qualityModel.totalTicks) * index - } - else if (index == qualityModel.totalTicks) - { - return Math.round(settingsColumnWidth / qualityModel.totalTicks) * index - width - } - else - { - return Math.round((settingsColumnWidth / qualityModel.totalTicks) * index - (width / 2)) - } - } - } - } - } - - //Print speed slider - Rectangle - { - id: speedSlider - - anchors - { - left: qualityRowTitle.right - right: parent.right - } - - // 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("thick_margin").width, customisedSettings.height), content) -// } -// else -// { -// base.hideTooltip() -// } -// } -// -// id: unavailableLineToolTip -// height: 20 * screenScaleFactor // 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 qualityModel.qualitySliderStepWidth * qualityModel.totalTicks -// } -// 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) -// } -// } -// -// Item -// { -// id: rightArea -// width: -// { -// if(qualityModel.availableTotalTicks == 0) -// return 0 -// -// return qualityModel.qualitySliderMarginRight - 10 -// } -// height: parent.height -// 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 - { - id: groovechildrect - width: parent.width - height: 2 * screenScaleFactor - color: UM.Theme.getColor("quality_slider_unavailable") - anchors.verticalCenter: qualitySlider.verticalCenter - - // Draw ticks - Repeater - { - id: qualityRepeater - model: qualityModel.totalTicks > 0 ? qualityModel : 0 - - Rectangle - { - color: Cura.QualityProfilesDropDownMenuModel.getItem(index).available ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") - implicitWidth: 4 * screenScaleFactor - implicitHeight: implicitWidth - anchors.verticalCenter: parent.verticalCenter - x: Math.round(qualityModel.qualitySliderStepWidth * index) - radius: Math.round(implicitWidth / 2) - } - } - } - - // Draw available slider - Slider - { - id: qualitySlider - height: UM.Theme.getSize("thick_margin").height - anchors.bottom: parent.bottom - enabled: qualityModel.totalTicks > 0 && !Cura.SimpleModeSettingsManager.isProfileCustomized - visible: qualityModel.availableTotalTicks > 0 - updateValueWhileDragging : false - - minimumValue: qualityModel.qualitySliderAvailableMin >= 0 ? qualityModel.qualitySliderAvailableMin : 0 - // maximumValue must be greater than minimumValue to be able to see the handle. While the value is strictly - // speaking not always correct, it seems to have the correct behavior (switching from 0 available to 1 available) - maximumValue: qualityModel.qualitySliderAvailableMax >= 1 ? qualityModel.qualitySliderAvailableMax : 1 - stepSize: 1 - - value: qualityModel.qualitySliderActiveIndex - - width: qualityModel.qualitySliderStepWidth * (qualityModel.availableTotalTicks - 1) - - anchors.right: parent.right - anchors.rightMargin: qualityModel.qualitySliderMarginRight - - style: SliderStyle - { - //Draw Available line - groove: Rectangle - { - implicitHeight: 2 * screenScaleFactor - color: UM.Theme.getColor("quality_slider_available") - radius: Math.round(height / 2) - } - - handle: Rectangle - { - id: qualityhandleButton - color: UM.Theme.getColor("quality_slider_available") - implicitWidth: 12 * screenScaleFactor - implicitHeight: implicitWidth - radius: Math.round(implicitWidth / 2) - visible: !Cura.SimpleModeSettingsManager.isProfileCustomized && !Cura.SimpleModeSettingsManager.isProfileUserCreated && qualityModel.existingQualityProfile - } - } - - onValueChanged: - { - // only change if an active machine is set and the slider is visible at all. - if (Cura.MachineManager.activeMachine != null && visible) - { - // prevent updating during view initializing. Trigger only if the value changed by user - if (qualitySlider.value != qualityModel.qualitySliderActiveIndex && qualityModel.qualitySliderActiveIndex != -1) - { - // start updating with short delay - qualitySliderChangeTimer.start() - } - } - } - } - - MouseArea - { - id: speedSliderMouseArea - anchors.fill: parent - hoverEnabled: true - enabled: Cura.SimpleModeSettingsManager.isProfileUserCreated - - onEntered: - { - var content = catalog.i18nc("@tooltip", "A custom profile is currently active. To enable the quality slider, choose a default quality profile in Custom tab") - base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, customisedSettings.height), content) - } - onExited: base.hideTooltip() - } - } - - UM.SimpleButton - { - id: customisedSettings - - visible: Cura.SimpleModeSettingsManager.isProfileCustomized || Cura.SimpleModeSettingsManager.isProfileUserCreated - height: Math.round(speedSlider.height * 0.8) - width: Math.round(speedSlider.height * 0.8) - - anchors.verticalCenter: speedSlider.verticalCenter - anchors.right: speedSlider.left - anchors.rightMargin: Math.round(UM.Theme.getSize("thick_margin").width / 2) - - color: hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button"); - iconSource: UM.Theme.getIcon("reset"); - - onClicked: - { - // if the current profile is user-created, switch to a built-in quality - Cura.MachineManager.resetToUseDefaultQuality() - } - onEntered: - { - var content = catalog.i18nc("@tooltip","You have modified some profile settings. If you want to change these go to custom mode.") - base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, customisedSettings.height), content) - } - onExited: base.hideTooltip() - } - } - - // - // Infill - // - Item - { - anchors.left: parent.left - anchors.right: parent.right - height: childrenRect.height - - Cura.IconWithText - { - id: infillRowTitle - source: UM.Theme.getIcon("category_infill") - text: catalog.i18nc("@label", "Infill") + " (%)" - anchors.bottom: parent.bottom - width: labelColumnWidth - } - - Item - { - id: infillCellRight - - height: infillSlider.height + UM.Theme.getSize("thick_margin").height + enableGradualInfillCheckBox.visible * (enableGradualInfillCheckBox.height + UM.Theme.getSize("thick_margin").height) - - anchors.left: infillRowTitle.right - anchors.right: parent.right - - Label - { - id: selectedInfillRateText - - anchors.left: infillSlider.left - anchors.right: parent.right - - text: parseInt(infillDensity.properties.value) + "%" - horizontalAlignment: Text.AlignLeft - - color: infillSlider.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") - } - - // We use a binding to make sure that after manually setting infillSlider.value it is still bound to the property provider - Binding - { - target: infillSlider - property: "value" - value: parseInt(infillDensity.properties.value) - } - - Slider - { - id: infillSlider - - anchors.top: selectedInfillRateText.bottom - anchors.left: parent.left - anchors.right: infillIcon.left - anchors.rightMargin: UM.Theme.getSize("thick_margin").width - - height: UM.Theme.getSize("thick_margin").height - width: parseInt(infillCellRight.width - UM.Theme.getSize("thick_margin").width - style.handleWidth) - - minimumValue: 0 - maximumValue: 100 - stepSize: 1 - tickmarksEnabled: true - - // disable slider when gradual support is enabled - enabled: parseInt(infillSteps.properties.value) == 0 - - // set initial value from stack - value: parseInt(infillDensity.properties.value) - - onValueChanged: - { - - // Don't round the value if it's already the same - if (parseInt(infillDensity.properties.value) == infillSlider.value) - { - return - } - - // Round the slider value to the nearest multiple of 10 (simulate step size of 10) - var roundedSliderValue = Math.round(infillSlider.value / 10) * 10 - - // Update the slider value to represent the rounded value - infillSlider.value = roundedSliderValue - - // Update value only if the Recomended mode is Active, - // Otherwise if I change the value in the Custom mode the Recomended view will try to repeat - // same operation - var active_mode = UM.Preferences.getValue("cura/active_mode") - - if (active_mode == 0 || active_mode == "simple") - { - Cura.MachineManager.setSettingForAllExtruders("infill_sparse_density", "value", roundedSliderValue) - Cura.MachineManager.resetSettingForAllExtruders("infill_line_distance") - } - } - - style: SliderStyle - { - groove: Rectangle - { - id: groove - implicitWidth: 200 * screenScaleFactor - implicitHeight: 2 * screenScaleFactor - color: control.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") - radius: 1 - } - - handle: Item - { - Rectangle - { - id: handleButton - anchors.centerIn: parent - color: control.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") - implicitWidth: 10 * screenScaleFactor - implicitHeight: 10 * screenScaleFactor - radius: 10 * screenScaleFactor - } - } - - tickmarks: Repeater - { - id: repeater - model: control.maximumValue / control.stepSize + 1 - - // check if a tick should be shown based on it's index and wether the infill density is a multiple of 10 (slider step size) - function shouldShowTick (index) - { - if (index % 10 == 0) - { - return true - } - return false - } - - Rectangle - { - anchors.verticalCenter: parent.verticalCenter - color: control.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") - width: 1 * screenScaleFactor - height: 6 * screenScaleFactor - x: Math.round(styleData.handleWidth / 2 + index * ((repeater.width - styleData.handleWidth) / (repeater.count-1))) - visible: shouldShowTick(index) - } - } - } - } - - Rectangle - { - id: infillIcon - - width: Math.round((parent.width / 5) - (UM.Theme.getSize("thick_margin").width)) - height: width - - anchors.right: parent.right - anchors.top: parent.top - anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height / 2) - - // we loop over all density icons and only show the one that has the current density and steps - Repeater - { - id: infillIconList - model: infillModel - anchors.fill: parent - - function activeIndex () - { - for (var i = 0; i < infillModel.count; i++) - { - var density = Math.round(infillDensity.properties.value) - var steps = Math.round(infillSteps.properties.value) - var infillModelItem = infillModel.get(i) - - if (infillModelItem != "undefined" - && density >= infillModelItem.percentageMin - && density <= infillModelItem.percentageMax - && steps >= infillModelItem.stepsMin - && steps <= infillModelItem.stepsMax) - { - return i - } - } - return -1 - } - - Rectangle - { - anchors.fill: parent - visible: infillIconList.activeIndex() == index - - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("quality_slider_unavailable") - - UM.RecolorImage - { - anchors.fill: parent - anchors.margins: 2 * screenScaleFactor - sourceSize.width: width - sourceSize.height: width - source: UM.Theme.getIcon(model.icon) - color: UM.Theme.getColor("quality_slider_unavailable") - } - } - } - } - - // Gradual Support Infill Checkbox - CheckBox - { - id: enableGradualInfillCheckBox - property alias _hovered: enableGradualInfillMouseArea.containsMouse - - anchors.top: infillSlider.bottom - anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height / 2) // closer to slider since it belongs to the same category - anchors.left: infillCellRight.left - - style: UM.Theme.styles.checkbox - enabled: base.settingsEnabled - visible: infillSteps.properties.enabled == "True" - checked: parseInt(infillSteps.properties.value) > 0 - - MouseArea - { - id: enableGradualInfillMouseArea - - anchors.fill: parent - hoverEnabled: true - enabled: true - - property var previousInfillDensity: parseInt(infillDensity.properties.value) - - onClicked: - { - // Set to 90% only when enabling gradual infill - var newInfillDensity; - if (parseInt(infillSteps.properties.value) == 0) - { - previousInfillDensity = parseInt(infillDensity.properties.value) - newInfillDensity = 90 - } else { - newInfillDensity = previousInfillDensity - } - Cura.MachineManager.setSettingForAllExtruders("infill_sparse_density", "value", String(newInfillDensity)) - - var infill_steps_value = 0 - if (parseInt(infillSteps.properties.value) == 0) - { - infill_steps_value = 5 - } - - Cura.MachineManager.setSettingForAllExtruders("gradual_infill_steps", "value", infill_steps_value) - } - - onEntered: base.showTooltip(enableGradualInfillCheckBox, Qt.point(-infillCellRight.x, 0), - catalog.i18nc("@label", "Gradual infill will gradually increase the amount of infill towards the top.")) - - onExited: base.hideTooltip() - - } - - Label - { - id: gradualInfillLabel - height: parent.height - anchors.left: enableGradualInfillCheckBox.right - anchors.leftMargin: Math.round(UM.Theme.getSize("thick_margin").width / 2) - verticalAlignment: Text.AlignVCenter; - text: catalog.i18nc("@label", "Enable gradual") - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - } - } - - // Infill list model for mapping icon - ListModel - { - id: infillModel - Component.onCompleted: - { - infillModel.append({ - percentageMin: -1, - percentageMax: 0, - stepsMin: -1, - stepsMax: 0, - icon: "hollow" - }) - infillModel.append({ - percentageMin: 0, - percentageMax: 40, - stepsMin: -1, - stepsMax: 0, - icon: "sparse" - }) - infillModel.append({ - percentageMin: 40, - percentageMax: 89, - stepsMin: -1, - stepsMax: 0, - icon: "dense" - }) - infillModel.append({ - percentageMin: 90, - percentageMax: 9999999999, - stepsMin: -1, - stepsMax: 0, - icon: "solid" - }) - infillModel.append({ - percentageMin: 0, - percentageMax: 9999999999, - stepsMin: 1, - stepsMax: 9999999999, - icon: "gradual" - }) - } - } - } - } - - // - // Enable support - // - Row - { - anchors.left: parent.left - anchors.right: parent.right - height: childrenRect.height - - Cura.IconWithText - { - id: enableSupportLabel - visible: enableSupportCheckBox.visible - source: UM.Theme.getIcon("category_support") - text: catalog.i18nc("@label", "Support") - width: labelColumnWidth - } - - CheckBox - { - id: enableSupportCheckBox - property alias _hovered: enableSupportMouseArea.containsMouse - - style: UM.Theme.styles.checkbox - enabled: base.settingsEnabled - - visible: supportEnabled.properties.enabled == "True" - checked: supportEnabled.properties.value == "True" - - MouseArea - { - id: enableSupportMouseArea - anchors.fill: parent - hoverEnabled: true - onClicked: supportEnabled.setPropertyValue("value", supportEnabled.properties.value != "True") - - onEntered: base.showTooltip(enableSupportCheckBox, Qt.point(-enableSupportCheckBox.x, 0), - catalog.i18nc("@label", "Generate structures to support parts of the model which have overhangs. Without these structures, such parts would collapse during printing.")) - - onExited: base.hideTooltip() - - } - } - - ComboBox - { - id: supportExtruderCombobox - visible: enableSupportCheckBox.visible && (supportEnabled.properties.value == "True") && (extrudersEnabledCount.properties.value > 1) - model: extruderModel - - property string color_override: "" // for manually setting values - property string color: // is evaluated automatically, but the first time is before extruderModel being filled - { - var current_extruder = extruderModel.get(currentIndex); - color_override = ""; - if (current_extruder === undefined) return "" - return (current_extruder.color) ? current_extruder.color : ""; - } - - textRole: "text" // this solves that the combobox isn't populated in the first time Cura is started - - width: Math.round(UM.Theme.getSize("print_setup_widget").width * .55) - Math.round(UM.Theme.getSize("thick_margin").width / 2) - enableSupportCheckBox.width - height: ((supportEnabled.properties.value == "True") && (machineExtruderCount.properties.value > 1)) ? UM.Theme.getSize("setting_control").height : 0 - - Behavior on height { NumberAnimation { duration: 100 } } - - style: UM.Theme.styles.combobox_color - enabled: base.settingsEnabled - property alias _hovered: supportExtruderMouseArea.containsMouse - - currentIndex: - { - if (supportExtruderNr.properties == null) - { - return Cura.MachineManager.defaultExtruderPosition - } - else - { - var extruder = parseInt(supportExtruderNr.properties.value) - if ( extruder === -1) - { - return Cura.MachineManager.defaultExtruderPosition - } - return extruder; - } - } - - onActivated: supportExtruderNr.setPropertyValue("value", String(index)) - - MouseArea - { - id: supportExtruderMouseArea - anchors.fill: parent - hoverEnabled: true - enabled: base.settingsEnabled - acceptedButtons: Qt.NoButton - onEntered: - { - base.showTooltip(supportExtruderCombobox, Qt.point(-supportExtruderCombobox.x, 0), - catalog.i18nc("@label", "Select which extruder to use for support. This will build up supporting structures below the model to prevent the model from sagging or printing in mid air.")); - } - onExited: base.hideTooltip() - - } - - function updateCurrentColor() - { - var current_extruder = extruderModel.get(currentIndex) - if (current_extruder !== undefined) - { - supportExtruderCombobox.color_override = current_extruder.color - } - } - - } - } - - // Adhesion - Row - { - anchors.left: parent.left - anchors.right: parent.right - height: childrenRect.height - - Cura.IconWithText - { - id: adhesionHelperLabel - visible: adhesionCheckBox.visible - source: UM.Theme.getIcon("category_adhesion") - text: catalog.i18nc("@label", "Adhesion") - width: labelColumnWidth - } - - CheckBox - { - id: adhesionCheckBox - property alias _hovered: adhesionMouseArea.containsMouse - - //: Setting enable printing build-plate adhesion helper checkbox - style: UM.Theme.styles.checkbox - enabled: base.settingsEnabled - - visible: platformAdhesionType.properties.enabled == "True" - checked: platformAdhesionType.properties.value != "skirt" && platformAdhesionType.properties.value != "none" - - MouseArea - { - id: adhesionMouseArea - anchors.fill: parent - hoverEnabled: true - enabled: base.settingsEnabled - onClicked: - { - var adhesionType = "skirt" - if(!parent.checked) - { - // Remove the "user" setting to see if the rest of the stack prescribes a brim or a raft - platformAdhesionType.removeFromContainer(0) - adhesionType = platformAdhesionType.properties.value - if(adhesionType == "skirt" || adhesionType == "none") - { - // If the rest of the stack doesn't prescribe an adhesion-type, default to a brim - adhesionType = "brim" - } - } - platformAdhesionType.setPropertyValue("value", adhesionType) - } - onEntered: - { - base.showTooltip(adhesionCheckBox, Qt.point(-adhesionCheckBox.x, 0), - catalog.i18nc("@label", "Enable printing a brim or raft. This will add a flat area around or under your object which is easy to cut off afterwards.")); - } - onExited: base.hideTooltip() - } - } - } - - ListModel - { - id: extruderModel - Component.onCompleted: populateExtruderModel() - } - - //: Model used to populate the extrudelModel - Cura.ExtrudersModel - { - id: extruders - onModelChanged: populateExtruderModel() - } - - UM.SettingPropertyProvider - { - id: infillExtruderNumber - containerStackId: Cura.MachineManager.activeStackId - key: "infill_extruder_nr" - watchedProperties: [ "value" ] - storeIndex: 0 - } - - UM.SettingPropertyProvider - { - id: infillDensity - containerStackId: Cura.MachineManager.activeStackId - key: "infill_sparse_density" - watchedProperties: [ "value" ] - storeIndex: 0 - } - - UM.SettingPropertyProvider - { - id: infillSteps - containerStackId: Cura.MachineManager.activeStackId - key: "gradual_infill_steps" - watchedProperties: ["value", "enabled"] - storeIndex: 0 - } - - UM.SettingPropertyProvider - { - id: platformAdhesionType - containerStack: Cura.MachineManager.activeMachine - removeUnusedValue: false //Doesn't work with settings that are resolved. - key: "adhesion_type" - watchedProperties: [ "value", "enabled" ] - storeIndex: 0 - } - - UM.SettingPropertyProvider - { - id: supportEnabled - containerStack: Cura.MachineManager.activeMachine - key: "support_enable" - watchedProperties: [ "value", "enabled", "description" ] - storeIndex: 0 - } - - UM.SettingPropertyProvider - { - id: extrudersEnabledCount - containerStack: Cura.MachineManager.activeMachine - key: "extruders_enabled_count" - watchedProperties: [ "value" ] - storeIndex: 0 - } - - UM.SettingPropertyProvider - { - id: supportExtruderNr - containerStack: Cura.MachineManager.activeMachine - key: "support_extruder_nr" - watchedProperties: [ "value" ] - storeIndex: 0 - } - - function populateExtruderModel() - { - extruderModel.clear() - for (var extruderNumber = 0; extruderNumber < extruders.rowCount(); extruderNumber++) - { - extruderModel.append({ - text: extruders.getItem(extruderNumber).name, - color: extruders.getItem(extruderNumber).color - }) - } - supportExtruderCombobox.updateCurrentColor() - } -} diff --git a/resources/qml/qmldir b/resources/qml/qmldir index 6db8e0c544..c0a8bac0ae 100644 --- a/resources/qml/qmldir +++ b/resources/qml/qmldir @@ -14,4 +14,5 @@ OutputDevicesActionButton 1.0 OutputDevicesActionButton.qml ExpandableComponent 1.0 ExpandableComponent.qml PrinterTypeLabel 1.0 PrinterTypeLabel.qml ViewsSelector 1.0 ViewsSelector.qml -ToolbarButton 1.0 ToolbarButton.qml \ No newline at end of file +ToolbarButton 1.0 ToolbarButton.qml +SettingView 1.0 SettingView.qml \ No newline at end of file From 6d4a460e58d81c66e849bcf17b444c3b164272ef Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 30 Nov 2018 13:53:53 +0100 Subject: [PATCH 0528/1240] Move USB sidebar into the main view of the monitorstage CURA-5943 --- plugins/MonitorStage/MonitorMain.qml | 40 +++ plugins/MonitorStage/MonitorMainView.qml | 64 ----- plugins/MonitorStage/MonitorMenu.qml | 14 + plugins/MonitorStage/MonitorStage.py | 17 +- plugins/USBPrinting/MonitorItem.qml | 27 ++ plugins/USBPrinting/USBPrinterOutputDevice.py | 5 +- resources/qml/Cura.qml | 11 +- resources/qml/PrintMonitor.qml | 256 ++++++++++-------- 8 files changed, 250 insertions(+), 184 deletions(-) create mode 100644 plugins/MonitorStage/MonitorMain.qml delete mode 100644 plugins/MonitorStage/MonitorMainView.qml create mode 100644 plugins/MonitorStage/MonitorMenu.qml create mode 100644 plugins/USBPrinting/MonitorItem.qml diff --git a/plugins/MonitorStage/MonitorMain.qml b/plugins/MonitorStage/MonitorMain.qml new file mode 100644 index 0000000000..1f287fc0fa --- /dev/null +++ b/plugins/MonitorStage/MonitorMain.qml @@ -0,0 +1,40 @@ +// Copyright (c) 2017 Ultimaker B.V. + +import QtQuick 2.10 +import QtQuick.Controls 1.4 + +import UM 1.3 as UM +import Cura 1.0 as Cura + + +Item +{ + // We show a nice overlay on the 3D viewer when the current output device has no monitor view + Rectangle + { + id: viewportOverlay + + color: UM.Theme.getColor("viewport_overlay") + anchors.fill: parent + MouseArea + { + anchors.fill: parent + acceptedButtons: Qt.AllButtons + onWheel: wheel.accepted = true + } + } + + Loader + { + id: monitorViewComponent + + anchors.fill: parent + + height: parent.height + + property real maximumWidth: parent.width + property real maximumHeight: parent.height + + sourceComponent: Cura.MachineManager.printerOutputDevices.length > 0 ? Cura.MachineManager.printerOutputDevices[0].monitorItem: null + } +} diff --git a/plugins/MonitorStage/MonitorMainView.qml b/plugins/MonitorStage/MonitorMainView.qml deleted file mode 100644 index f5696bbf54..0000000000 --- a/plugins/MonitorStage/MonitorMainView.qml +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) 2017 Ultimaker B.V. - -import QtQuick 2.10 -import QtQuick.Controls 1.4 - -import UM 1.3 as UM -import Cura 1.0 as Cura - - -Item -{ - // parent could be undefined as this component is not visible at all times - width: parent ? parent.width : 0 - height: parent ? parent.height : 0 - - // We show a nice overlay on the 3D viewer when the current output device has no monitor view - Rectangle - { - id: viewportOverlay - - color: UM.Theme.getColor("viewport_overlay") - width: parent.width - height: parent.height - - MouseArea - { - anchors.fill: parent - acceptedButtons: Qt.AllButtons - onWheel: wheel.accepted = true - } - } - - Loader - { - id: monitorViewComponent - - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.left: parent.left - - // If the sidebar is not set, the view should take the complete space. - property var widthFactor: monitorSidebarComponent.source == "" ? 1.0 : 0.7 - - width: Math.round(parent.width * widthFactor) - height: parent.height - - property real maximumWidth: parent.width - property real maximumHeight: parent.height - - sourceComponent: Cura.MachineManager.printerOutputDevices.length > 0 ? Cura.MachineManager.printerOutputDevices[0].monitorItem: null - } - - Loader - { - id: monitorSidebarComponent - - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.left: monitorViewComponent.right - anchors.right: parent.right - - source: UM.Controller.activeStage.sidebarComponent != null ? UM.Controller.activeStage.sidebarComponent : "" - } -} diff --git a/plugins/MonitorStage/MonitorMenu.qml b/plugins/MonitorStage/MonitorMenu.qml new file mode 100644 index 0000000000..4dec719313 --- /dev/null +++ b/plugins/MonitorStage/MonitorMenu.qml @@ -0,0 +1,14 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 2.3 + +import UM 1.3 as UM +import Cura 1.1 as Cura + +Item +{ + signal showTooltip(Item item, point location, string text) + signal hideTooltip() +} \ No newline at end of file diff --git a/plugins/MonitorStage/MonitorStage.py b/plugins/MonitorStage/MonitorStage.py index ace201e994..69b7f20f4e 100644 --- a/plugins/MonitorStage/MonitorStage.py +++ b/plugins/MonitorStage/MonitorStage.py @@ -65,15 +65,10 @@ class MonitorStage(CuraStage): # We can only connect now, as we need to be sure that everything is loaded (plugins get created quite early) Application.getInstance().getMachineManager().outputDevicesChanged.connect(self._onOutputDevicesChanged) self._onOutputDevicesChanged() - self._updateMainOverlay() - self._updateSidebar() - def _updateMainOverlay(self): - main_component_path = os.path.join(PluginRegistry.getInstance().getPluginPath("MonitorStage"), - "MonitorMainView.qml") - self.addDisplayComponent("main", main_component_path) - - def _updateSidebar(self): - sidebar_component_path = os.path.join(Resources.getPath(Application.getInstance().ResourceTypes.QmlFiles), - "MonitorSidebar.qml") - self.addDisplayComponent("sidebar", sidebar_component_path) + plugin_path = Application.getInstance().getPluginRegistry().getPluginPath(self.getPluginId()) + if plugin_path is not None: + menu_component_path = os.path.join(plugin_path, "MonitorMenu.qml") + main_component_path = os.path.join(plugin_path, "MonitorMain.qml") + self.addDisplayComponent("menu", menu_component_path) + self.addDisplayComponent("main", main_component_path) diff --git a/plugins/USBPrinting/MonitorItem.qml b/plugins/USBPrinting/MonitorItem.qml new file mode 100644 index 0000000000..a93b3eb69b --- /dev/null +++ b/plugins/USBPrinting/MonitorItem.qml @@ -0,0 +1,27 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.10 +import QtQuick.Controls 2.0 +import QtQuick.Layouts 1.3 + +import UM 1.2 as UM +import Cura 1.0 as Cura +Component +{ + Item + { + Rectangle + { + anchors.right: parent.right + width: parent.width * 0.3 + anchors.top: parent.top + anchors.bottom: parent.bottom + + Cura.PrintMonitor + { + anchors.fill: parent + } + } + } +} \ No newline at end of file diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index e1c39ff8fa..1d42e70366 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -1,5 +1,6 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +import os from UM.Logger import Logger from UM.i18n import i18nCatalog @@ -64,7 +65,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): self._accepts_commands = True self._paused = False - self._printer_busy = False # when printer is preheating and waiting (M190/M109), or when waiting for action on the printer + self._printer_busy = False # When printer is preheating and waiting (M190/M109), or when waiting for action on the printer self.setConnectionText(catalog.i18nc("@info:status", "Connected via USB")) @@ -77,6 +78,8 @@ class USBPrinterOutputDevice(PrinterOutputDevice): self._firmware_name_requested = False self._firmware_updater = AvrFirmwareUpdater(self) + self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "MonitorItem.qml") + CuraApplication.getInstance().getOnExitCallbackManager().addCallback(self._checkActivePrintingUponAppExit) # This is a callback function that checks if there is any printing in progress via USB when the application tries diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 36f5758fa3..3578888886 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -146,6 +146,7 @@ UM.MainWindow Rectangle { + id: stageMenuBackground anchors { left: parent.left @@ -153,7 +154,7 @@ UM.MainWindow top: parent.top } visible: stageMenu.source != "" - height: Math.round(UM.Theme.getSize("stage_menu").height / 2) + height: visible ? Math.round(UM.Theme.getSize("stage_menu").height / 2) : 0 LinearGradient { @@ -254,7 +255,13 @@ UM.MainWindow // A stage can control this area. If nothing is set, it will therefore show the 3D view. id: main - anchors.fill: parent + anchors + { + top: stageMenuBackground.bottom + left: parent.left + right: parent.right + bottom: parent.bottom + } source: UM.Controller.activeStage != null ? UM.Controller.activeStage.mainComponent : "" } diff --git a/resources/qml/PrintMonitor.qml b/resources/qml/PrintMonitor.qml index 723279847a..3cc161cbe7 100644 --- a/resources/qml/PrintMonitor.qml +++ b/resources/qml/PrintMonitor.qml @@ -11,136 +11,180 @@ import Cura 1.0 as Cura import "PrinterOutput" -Column + +Rectangle { - id: printMonitor + id: base + UM.I18nCatalog { id: catalog; name: "cura"} + + function showTooltip(item, position, text) + { + tooltip.text = text; + position = item.mapToItem(base, position.x - UM.Theme.getSize("default_arrow").width, position.y); + tooltip.show(position); + } + + function hideTooltip() + { + tooltip.hide(); + } + + function strPadLeft(string, pad, length) { + return (new Array(length + 1).join(pad) + string).slice(-length); + } + + function getPrettyTime(time) + { + var hours = Math.floor(time / 3600) + time -= hours * 3600 + var minutes = Math.floor(time / 60); + time -= minutes * 60 + var seconds = Math.floor(time); + + var finalTime = strPadLeft(hours, "0", 2) + ":" + strPadLeft(minutes, "0", 2) + ":" + strPadLeft(seconds, "0", 2); + return finalTime; + } + property var connectedDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null property var activePrinter: connectedDevice != null ? connectedDevice.activePrinter : null property var activePrintJob: activePrinter != null ? activePrinter.activePrintJob: null - Cura.ExtrudersModel + SidebarTooltip { - id: extrudersModel - simpleNames: true + id: tooltip } - OutputDeviceHeader + Column { - outputDevice: connectedDevice - } + id: printMonitor - Rectangle - { - color: UM.Theme.getColor("wide_lining") - width: parent.width - height: childrenRect.height + anchors.fill: parent - Flow + Cura.ExtrudersModel { - id: extrudersGrid - spacing: UM.Theme.getSize("thick_lining").width + id: extrudersModel + simpleNames: true + } + + OutputDeviceHeader + { + outputDevice: connectedDevice + } + + Rectangle + { + color: UM.Theme.getColor("wide_lining") width: parent.width + height: childrenRect.height - Repeater + Flow { - id: extrudersRepeater - model: activePrinter != null ? activePrinter.extruders : null + id: extrudersGrid + spacing: UM.Theme.getSize("thick_lining").width + width: parent.width - ExtruderBox + Repeater { - color: UM.Theme.getColor("main_background") - width: index == machineExtruderCount.properties.value - 1 && index % 2 == 0 ? extrudersGrid.width : Math.round(extrudersGrid.width / 2 - UM.Theme.getSize("thick_lining").width / 2) - extruderModel: modelData + id: extrudersRepeater + model: activePrinter != null ? activePrinter.extruders : null + + ExtruderBox + { + color: UM.Theme.getColor("main_background") + width: index == machineExtruderCount.properties.value - 1 && index % 2 == 0 ? extrudersGrid.width : Math.round(extrudersGrid.width / 2 - UM.Theme.getSize("thick_lining").width / 2) + extruderModel: modelData + } } } } - } - Rectangle - { - color: UM.Theme.getColor("wide_lining") - width: parent.width - height: UM.Theme.getSize("thick_lining").width - } - - HeatedBedBox - { - visible: { - if(activePrinter != null && activePrinter.bedTemperature != -1) - { - return true - } - return false - } - printerModel: activePrinter - } - - UM.SettingPropertyProvider - { - id: bedTemperature - containerStack: Cura.MachineManager.activeMachine - key: "material_bed_temperature" - watchedProperties: ["value", "minimum_value", "maximum_value", "resolve"] - storeIndex: 0 - - property var resolve: Cura.MachineManager.activeStack != Cura.MachineManager.activeMachine ? properties.resolve : "None" - } - - UM.SettingPropertyProvider - { - id: machineExtruderCount - containerStack: Cura.MachineManager.activeMachine - key: "machine_extruder_count" - watchedProperties: ["value"] - } - - ManualPrinterControl - { - printerModel: activePrinter - visible: activePrinter != null ? activePrinter.canControlManually : false - } - - - MonitorSection - { - label: catalog.i18nc("@label", "Active print") - width: base.width - visible: activePrinter != null - } - - - MonitorItem - { - label: catalog.i18nc("@label", "Job Name") - value: activePrintJob != null ? activePrintJob.name : "" - width: base.width - visible: activePrinter != null - } - - MonitorItem - { - label: catalog.i18nc("@label", "Printing Time") - value: activePrintJob != null ? getPrettyTime(activePrintJob.timeTotal) : "" - width: base.width - visible: activePrinter != null - } - - MonitorItem - { - label: catalog.i18nc("@label", "Estimated time left") - value: activePrintJob != null ? getPrettyTime(activePrintJob.timeTotal - activePrintJob.timeElapsed) : "" - visible: + Rectangle { - if(activePrintJob == null) + color: UM.Theme.getColor("wide_lining") + width: parent.width + height: UM.Theme.getSize("thick_lining").width + } + + HeatedBedBox + { + visible: { + if(activePrinter != null && activePrinter.bedTemperature != -1) + { + return true + } return false } - - return (activePrintJob.state == "printing" || - activePrintJob.state == "resuming" || - activePrintJob.state == "pausing" || - activePrintJob.state == "paused") + printerModel: activePrinter + } + + UM.SettingPropertyProvider + { + id: bedTemperature + containerStack: Cura.MachineManager.activeMachine + key: "material_bed_temperature" + watchedProperties: ["value", "minimum_value", "maximum_value", "resolve"] + storeIndex: 0 + + property var resolve: Cura.MachineManager.activeStack != Cura.MachineManager.activeMachine ? properties.resolve : "None" + } + + UM.SettingPropertyProvider + { + id: machineExtruderCount + containerStack: Cura.MachineManager.activeMachine + key: "machine_extruder_count" + watchedProperties: ["value"] + } + + ManualPrinterControl + { + printerModel: activePrinter + visible: activePrinter != null ? activePrinter.canControlManually : false + } + + + MonitorSection + { + label: catalog.i18nc("@label", "Active print") + width: base.width + visible: activePrinter != null + } + + + MonitorItem + { + label: catalog.i18nc("@label", "Job Name") + value: activePrintJob != null ? activePrintJob.name : "" + width: base.width + visible: activePrinter != null + } + + MonitorItem + { + label: catalog.i18nc("@label", "Printing Time") + value: activePrintJob != null ? getPrettyTime(activePrintJob.timeTotal) : "" + width: base.width + visible: activePrinter != null + } + + MonitorItem + { + label: catalog.i18nc("@label", "Estimated time left") + value: activePrintJob != null ? getPrettyTime(activePrintJob.timeTotal - activePrintJob.timeElapsed) : "" + visible: + { + if(activePrintJob == null) + { + return false + } + + return (activePrintJob.state == "printing" || + activePrintJob.state == "resuming" || + activePrintJob.state == "pausing" || + activePrintJob.state == "paused") + } + width: base.width } - width: base.width } -} +} \ No newline at end of file From 48bdb735f2bfa9ef044065e5a9e0ca2a60e17361 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 30 Nov 2018 13:58:44 +0100 Subject: [PATCH 0529/1240] Added MachineSelector to monitor menu CURA-5943 --- plugins/MonitorStage/MonitorMenu.qml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/plugins/MonitorStage/MonitorMenu.qml b/plugins/MonitorStage/MonitorMenu.qml index 4dec719313..bc95c276e8 100644 --- a/plugins/MonitorStage/MonitorMenu.qml +++ b/plugins/MonitorStage/MonitorMenu.qml @@ -11,4 +11,13 @@ Item { signal showTooltip(Item item, point location, string text) signal hideTooltip() + + Cura.MachineSelector + { + id: machineSelection + headerCornerSide: Cura.RoundedRectangle.Direction.All + width: UM.Theme.getSize("machine_selector_widget").width + height: parent.height + anchors.centerIn: parent + } } \ No newline at end of file From 59a81be65c395628962d3587d24d62819d69259c Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 30 Nov 2018 14:10:42 +0100 Subject: [PATCH 0530/1240] Fixed some leftover graphical issues CURA-5943 --- plugins/USBPrinting/MonitorItem.qml | 19 +++++++++++++++++++ .../qml/PrinterOutput/OutputDeviceHeader.qml | 4 ++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/plugins/USBPrinting/MonitorItem.qml b/plugins/USBPrinting/MonitorItem.qml index a93b3eb69b..8041698ef0 100644 --- a/plugins/USBPrinting/MonitorItem.qml +++ b/plugins/USBPrinting/MonitorItem.qml @@ -22,6 +22,25 @@ Component { anchors.fill: parent } + + Rectangle + { + id: footerSeparator + width: parent.width + height: UM.Theme.getSize("wide_lining").height + color: UM.Theme.getColor("wide_lining") + anchors.bottom: monitorButton.top + anchors.bottomMargin: UM.Theme.getSize("thick_margin").height + } + + // MonitorButton is actually the bottom footer panel. + Cura.MonitorButton + { + id: monitorButton + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + } } } } \ No newline at end of file diff --git a/resources/qml/PrinterOutput/OutputDeviceHeader.qml b/resources/qml/PrinterOutput/OutputDeviceHeader.qml index b5ed1b7b4e..e6328546ef 100644 --- a/resources/qml/PrinterOutput/OutputDeviceHeader.qml +++ b/resources/qml/PrinterOutput/OutputDeviceHeader.qml @@ -45,8 +45,8 @@ Item text: (outputDevice != null && outputDevice.address != null) ? outputDevice.address : "" font: UM.Theme.getFont("small") color: UM.Theme.getColor("text_inactive") - anchors.top: parent.top - anchors.right: parent.right + anchors.top: outputDeviceNameLabel.bottom + anchors.left: parent.left anchors.margins: UM.Theme.getSize("default_margin").width } From 1a778b30782f1e6c19aeca2f4882936d98d9c3c6 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 30 Nov 2018 14:18:07 +0100 Subject: [PATCH 0531/1240] Fix qualiy slider alignments Contributes to CURA-5941. --- .../qml/MonitorBuildplateConfiguration.qml | 2 +- .../PrintSetupSelectorContents.qml | 27 +- .../RecommendedQualityProfileSelector.qml | 391 ++++++++++-------- resources/themes/cura-light/theme.json | 8 +- 4 files changed, 241 insertions(+), 187 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml index 9ffb1eabb4..75cbf3b11d 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml @@ -27,7 +27,7 @@ Item Row { height: parent.height - spacing: 12 * screenScaleFactor // TODO: Theme! (Should be same as extruder spacing) + spacing: UM.Theme.getSize("print_setup_slider_handle").width // TODO: Theme! (Should be same as extruder spacing) // This wrapper ensures that the buildplate icon is located centered // below an extruder icon. diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml index f21253acd7..5f22e4c337 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml @@ -114,19 +114,20 @@ Item onHideTooltip: base.hideTooltip() visible: currentModeIndex == 0 } -// -// CustomPrintSetup -// { -// anchors -// { -// left: parent.left -// right: parent.right -// top: parent.top -// } -// onShowTooltip: base.showTooltip(item, location, text) -// onHideTooltip: base.hideTooltip() -// visible: currentModeIndex == 1 -// } + + CustomPrintSetup + { + anchors + { + left: parent.left + right: parent.right + top: parent.top + } + height: 500 + onShowTooltip: base.showTooltip(item, location, text) + onHideTooltip: base.hideTooltip() + visible: currentModeIndex == 1 + } } Rectangle diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml index 3bf93c0c07..c7a9439b00 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml @@ -124,22 +124,27 @@ Item function calculateSliderStepWidth (totalTicks) { - qualityModel.qualitySliderStepWidth = totalTicks != 0 ? Math.round((settingsColumnWidth) / (totalTicks)) : 0 + // Do not use Math.round otherwise the tickmarks won't be aligned + qualityModel.qualitySliderStepWidth = totalTicks != 0 ? + ((settingsColumnWidth - UM.Theme.getSize("print_setup_slider_handle").width) / (totalTicks)) : 0 } function calculateSliderMargins (availableMin, availableMax, totalTicks) { if (availableMin == -1 || (availableMin == 0 && availableMax == 0)) { - qualityModel.qualitySliderMarginRight = Math.round(settingsColumnWidth) + // Do not use Math.round otherwise the tickmarks won't be aligned + qualityModel.qualitySliderMarginRight = settingsColumnWidth } else if (availableMin == availableMax) { - qualityModel.qualitySliderMarginRight = Math.round((totalTicks - availableMin) * qualitySliderStepWidth) + // Do not use Math.round otherwise the tickmarks won't be aligned + qualityModel.qualitySliderMarginRight = (totalTicks - availableMin) * qualitySliderStepWidth } else { - qualityModel.qualitySliderMarginRight = Math.round((totalTicks - availableMax) * qualitySliderStepWidth) + // Do not use Math.round otherwise the tickmarks won't be aligned + qualityModel.qualitySliderMarginRight = (totalTicks - availableMax) * qualitySliderStepWidth } } @@ -162,20 +167,6 @@ Item width: labelColumnWidth } - //Print speed slider - Rectangle - { - id: speedSlider - - anchors - { - left: qualityRowTitle.right - right: parent.right - } - - color: "green" - height: 20 - } // // // Show titles for the each quality slider ticks // Item @@ -240,104 +231,107 @@ Item // } // } // -// //Print speed slider -// Rectangle -// { -// id: speedSlider + //Print speed slider + Rectangle + { + id: speedSlider + + anchors + { + left: qualityRowTitle.right + right: parent.right + } + + color: "green" + height: childrenRect.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("thick_margin").width, customisedSettings.height), content) +// } +// else +// { +// base.hideTooltip() +// } +// } // -// anchors -// { -// left: qualityRowTitle.right -// right: parent.right -// } +// id: unavailableLineToolTip +// height: 20 * screenScaleFactor // hovered area height +// z: parent.z + 1 // should be higher, otherwise the area can be hovered +// x: 0 +// anchors.verticalCenter: qualitySlider.verticalCenter // -// // 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("thick_margin").width, customisedSettings.height), content) -//// } -//// else -//// { -//// base.hideTooltip() -//// } -//// } -//// -//// id: unavailableLineToolTip -//// height: 20 * screenScaleFactor // 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 qualityModel.qualitySliderStepWidth * qualityModel.totalTicks -//// } -//// 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) -//// } -//// } -//// -//// Item -//// { -//// id: rightArea -//// width: -//// { -//// if(qualityModel.availableTotalTicks == 0) -//// return 0 -//// -//// return qualityModel.qualitySliderMarginRight - 10 -//// } -//// height: parent.height -//// 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) -//// } -//// } -//// } +// Rectangle +// { +// id: leftArea +// width: +// { +// if (qualityModel.availableTotalTicks == 0) +// { +// return qualityModel.qualitySliderStepWidth * qualityModel.totalTicks +// } +// 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) +// } +// } +// +// Item +// { +// id: rightArea +// width: +// { +// if(qualityModel.availableTotalTicks == 0) +// return 0 +// +// return qualityModel.qualitySliderMarginRight - 10 +// } +// height: parent.height +// 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 // { // id: groovechildrect // width: parent.width -// height: 2 * screenScaleFactor +// height: 2 * UM.Theme.getSize("default_lining").height // color: UM.Theme.getColor("quality_slider_unavailable") // anchors.verticalCenter: qualitySlider.verticalCenter // @@ -358,66 +352,125 @@ Item // } // } // } -// -// // Draw available slider -// Slider -// { -// id: qualitySlider -// height: UM.Theme.getSize("thick_margin").height -// anchors.bottom: parent.bottom -// enabled: qualityModel.totalTicks > 0 && !Cura.SimpleModeSettingsManager.isProfileCustomized -// visible: qualityModel.availableTotalTicks > 0 -// updateValueWhileDragging : false -// -// minimumValue: qualityModel.qualitySliderAvailableMin >= 0 ? qualityModel.qualitySliderAvailableMin : 0 -// // maximumValue must be greater than minimumValue to be able to see the handle. While the value is strictly -// // speaking not always correct, it seems to have the correct behavior (switching from 0 available to 1 available) -// maximumValue: qualityModel.qualitySliderAvailableMax >= 1 ? qualityModel.qualitySliderAvailableMax : 1 -// stepSize: 1 -// -// value: qualityModel.qualitySliderActiveIndex -// -// width: qualityModel.qualitySliderStepWidth * (qualityModel.availableTotalTicks - 1) -// -// anchors.right: parent.right -// anchors.rightMargin: qualityModel.qualitySliderMarginRight -// -// style: SliderStyle -// { -// //Draw Available line -// groove: Rectangle -// { -// implicitHeight: 2 * screenScaleFactor -// color: UM.Theme.getColor("quality_slider_available") -// radius: Math.round(height / 2) -// } -// -// handle: Rectangle -// { -// id: qualityhandleButton -// color: UM.Theme.getColor("quality_slider_available") -// implicitWidth: 12 * screenScaleFactor -// implicitHeight: implicitWidth -// radius: Math.round(implicitWidth / 2) -// visible: !Cura.SimpleModeSettingsManager.isProfileCustomized && !Cura.SimpleModeSettingsManager.isProfileUserCreated && qualityModel.existingQualityProfile -// } -// } -// -// onValueChanged: -// { -// // only change if an active machine is set and the slider is visible at all. -// if (Cura.MachineManager.activeMachine != null && visible) -// { -// // prevent updating during view initializing. Trigger only if the value changed by user -// if (qualitySlider.value != qualityModel.qualitySliderActiveIndex && qualityModel.qualitySliderActiveIndex != -1) -// { -// // start updating with short delay -// qualitySliderChangeTimer.start() -// } -// } -// } -// } -// + + // Draw unavailable slider + Slider + { + id: unavailableSlider + height: UM.Theme.getSize("thick_margin").height + updateValueWhileDragging : false + tickmarksEnabled: true + + minimumValue: 0 + // maximumValue must be greater than minimumValue to be able to see the handle. While the value is strictly + // speaking not always correct, it seems to have the correct behavior (switching from 0 available to 1 available) + maximumValue: qualityModel.totalTicks + stepSize: 1 + + width: parent.width + + style: SliderStyle + { + //Draw Unvailable line + groove: Item + { + Rectangle + { + height: UM.Theme.getSize("print_setup_slider_groove").height + width: control.width - UM.Theme.getSize("print_setup_slider_handle").width + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + color: UM.Theme.getColor("quality_slider_unavailable") + } + } + + handle: Item {} + + tickmarks: Repeater + { + id: qualityRepeater + model: qualityModel.totalTicks > 0 ? qualityModel : 0 + + Rectangle + { + color: Cura.QualityProfilesDropDownMenuModel.getItem(index).available ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") + implicitWidth: UM.Theme.getSize("print_setup_slider_tickmarks").width + implicitHeight: UM.Theme.getSize("print_setup_slider_tickmarks").height + anchors.verticalCenter: parent.verticalCenter + + // Do not use Math.round otherwise the tickmarks won't be aligned + x: ((UM.Theme.getSize("print_setup_slider_handle").width / 2) - (UM.Theme.getSize("print_setup_slider_tickmarks").width / 2) + (qualityModel.qualitySliderStepWidth * index)) + radius: Math.round(implicitWidth / 2) + } + } + } + } + + // Draw available slider + Slider + { + id: qualitySlider + height: UM.Theme.getSize("thick_margin").height + enabled: qualityModel.totalTicks > 0 && !Cura.SimpleModeSettingsManager.isProfileCustomized + visible: qualityModel.availableTotalTicks > 0 + updateValueWhileDragging : false + + minimumValue: qualityModel.qualitySliderAvailableMin >= 0 ? qualityModel.qualitySliderAvailableMin : 0 + // maximumValue must be greater than minimumValue to be able to see the handle. While the value is strictly + // speaking not always correct, it seems to have the correct behavior (switching from 0 available to 1 available) + maximumValue: qualityModel.qualitySliderAvailableMax >= 1 ? qualityModel.qualitySliderAvailableMax : 1 + stepSize: 1 + + value: qualityModel.qualitySliderActiveIndex + + width: qualityModel.qualitySliderStepWidth * (qualityModel.availableTotalTicks - 1) + UM.Theme.getSize("print_setup_slider_handle").width + + anchors.right: parent.right + anchors.rightMargin: qualityModel.qualitySliderMarginRight + + style: SliderStyle + { + // Draw Available line + groove: Item + { + Rectangle + { + height: UM.Theme.getSize("print_setup_slider_groove").height + width: control.width - UM.Theme.getSize("print_setup_slider_handle").width + anchors.verticalCenter: parent.verticalCenter + + // Do not use Math.round otherwise the tickmarks won't be aligned + x: UM.Theme.getSize("print_setup_slider_handle").width / 2 + color: UM.Theme.getColor("quality_slider_available") + } + } + + handle: Rectangle + { + id: qualityhandleButton + color: UM.Theme.getColor("primary") + implicitWidth: UM.Theme.getSize("print_setup_slider_handle").width + implicitHeight: implicitWidth + radius: Math.round(implicitWidth / 2) + visible: !Cura.SimpleModeSettingsManager.isProfileCustomized && !Cura.SimpleModeSettingsManager.isProfileUserCreated && qualityModel.existingQualityProfile + } + } + + onValueChanged: + { + // only change if an active machine is set and the slider is visible at all. + if (Cura.MachineManager.activeMachine != null && visible) + { + // prevent updating during view initializing. Trigger only if the value changed by user + if (qualitySlider.value != qualityModel.qualitySliderActiveIndex && qualityModel.qualitySliderActiveIndex != -1) + { + // start updating with short delay + qualitySliderChangeTimer.start() + } + } + } + } + // MouseArea // { // id: speedSliderMouseArea @@ -432,7 +485,7 @@ Item // } // onExited: base.hideTooltip() // } -// } + } // // UM.SimpleButton // { diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index dfaa9008f7..9bf0bf19aa 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -373,6 +373,9 @@ "print_setup_item": [0.0, 2.0], "print_setup_extruder_box": [0.0, 6.0], "print_setup_widget_header": [0.0, 3.0], + "print_setup_slider_groove": [0.16, 0.16], + "print_setup_slider_handle": [1.0, 1.0], + "print_setup_slider_tickmarks": [0.32, 0.32], "configuration_selector_mode_tabs": [0.0, 3.0], @@ -521,9 +524,6 @@ "monitor_thick_lining": [0.16, 0.16], "monitor_corner_radius": [0.3, 0.3], "monitor_shadow_radius": [0.4, 0.4], - "monitor_shadow_offset": [0.15, 0.15], - - "print_setup_action_button": [13, 5], - "print_setup_content_top_margin": [3, 3] + "monitor_shadow_offset": [0.15, 0.15] } } From 73568d74737fb32006ece5355c6ef4c13ce97ae7 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Fri, 30 Nov 2018 14:51:55 +0100 Subject: [PATCH 0532/1240] Added icon to the search field which filters settings CURA-5941 --- resources/qml/Settings/SettingView.qml | 4 ++-- resources/themes/cura-light/icons/search.svg | 25 ++++++++++++++++---- resources/themes/cura-light/theme.json | 1 + 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 8a336a7ed1..0a7102ff45 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -105,12 +105,12 @@ Item anchors.right: clearFilterButton.left anchors.rightMargin: Math.round(UM.Theme.getSize("thick_margin").width) - placeholderText: catalog.i18nc("@label:textbox", "Search...") + placeholderText: "" + "

    " + catalog.i18nc("@label:textbox", "search settings") style: TextFieldStyle { textColor: UM.Theme.getColor("setting_control_text"); - placeholderTextColor: UM.Theme.getColor("setting_control_text") + placeholderTextColor: UM.Theme.getColor("setting_filter_field") font: UM.Theme.getFont("default"); background: Item {} } diff --git a/resources/themes/cura-light/icons/search.svg b/resources/themes/cura-light/icons/search.svg index 8272991300..a9ccb612fd 100644 --- a/resources/themes/cura-light/icons/search.svg +++ b/resources/themes/cura-light/icons/search.svg @@ -1,4 +1,21 @@ - - - + + + + Shape + Created with Sketch. + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index dfaa9008f7..4e39ffdf22 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -221,6 +221,7 @@ "setting_validation_warning_background": [255, 145, 62, 255], "setting_validation_warning": [127, 127, 127, 255], "setting_validation_ok": [255, 255, 255, 255], + "setting_filter_field" : [153, 153, 153, 255], "material_compatibility_warning": [0, 0, 0, 255], From e93bbf07a4e377bb55e7b5c62292487344fcc29a Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 30 Nov 2018 14:53:10 +0100 Subject: [PATCH 0533/1240] Fix circular dependency with tab index and active stack Only when actually clicking the tab should it change the active extruder index; not when the current index is changed for any other reason. Next up: Don't change the current index for any other reason. Contributes to issue CURA-5876. --- .../qml/Menus/ConfigurationMenu/CustomConfiguration.qml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index 87096c3a14..8ad512349b 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -35,7 +35,7 @@ Item anchors.top: header.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height - onCurrentIndexChanged: Cura.ExtruderManager.setActiveExtruderIndex(currentIndex) + currentIndex: Math.max(Cura.ExtruderManager.activeExtruderIndex, 0) Repeater { @@ -53,6 +53,10 @@ Item height: parent.height } } + onClicked: + { + Cura.ExtruderManager.setActiveExtruderIndex(tabBar.currentIndex) + } } } } From 1fbcff2cb9a0df2b35c054ffef638e863eeb54f4 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 30 Nov 2018 15:06:53 +0100 Subject: [PATCH 0534/1240] Fix the tooltips in the profile selector in the recommended mode Contributes to CURA-5941. --- .../Recommended/RecommendedPrintSetup.qml | 4 +- .../RecommendedQualityProfileSelector.qml | 226 ++++++------------ 2 files changed, 70 insertions(+), 160 deletions(-) diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml index 2d4308c8be..40a1910c69 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml @@ -8,7 +8,7 @@ import QtQuick.Controls.Styles 1.4 import UM 1.2 as UM import Cura 1.0 as Cura -Rectangle +Item { id: base @@ -16,8 +16,6 @@ Rectangle signal hideTooltip() // width: parent.width height: childrenRect.height + 2 * padding - color: "red" - opacity: 0.5 property Action configureSettings diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml index c7a9439b00..755af9311f 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml @@ -163,7 +163,6 @@ Item id: qualityRowTitle source: UM.Theme.getIcon("category_layer_height") text: catalog.i18nc("@label", "Layer Height") -// anchors.bottom: speedSlider.bottom width: labelColumnWidth } @@ -232,9 +231,10 @@ Item // } // //Print speed slider - Rectangle + Item { id: speedSlider + height: childrenRect.height anchors { @@ -242,122 +242,11 @@ Item right: parent.right } - color: "green" - height: childrenRect.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("thick_margin").width, customisedSettings.height), content) -// } -// else -// { -// base.hideTooltip() -// } -// } -// -// id: unavailableLineToolTip -// height: 20 * screenScaleFactor // 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 qualityModel.qualitySliderStepWidth * qualityModel.totalTicks -// } -// 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) -// } -// } -// -// Item -// { -// id: rightArea -// width: -// { -// if(qualityModel.availableTotalTicks == 0) -// return 0 -// -// return qualityModel.qualitySliderMarginRight - 10 -// } -// height: parent.height -// 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 -// { -// id: groovechildrect -// width: parent.width -// height: 2 * UM.Theme.getSize("default_lining").height -// color: UM.Theme.getColor("quality_slider_unavailable") -// anchors.verticalCenter: qualitySlider.verticalCenter -// -// // Draw ticks -// Repeater -// { -// id: qualityRepeater -// model: qualityModel.totalTicks > 0 ? qualityModel : 0 -// -// Rectangle -// { -// color: Cura.QualityProfilesDropDownMenuModel.getItem(index).available ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") -// implicitWidth: 4 * screenScaleFactor -// implicitHeight: implicitWidth -// anchors.verticalCenter: parent.verticalCenter -// x: Math.round(qualityModel.qualitySliderStepWidth * index) -// radius: Math.round(implicitWidth / 2) -// } -// } -// } - // Draw unavailable slider Slider { id: unavailableSlider - height: UM.Theme.getSize("thick_margin").height + height: qualitySlider.height // Same height as the slider that is on top updateValueWhileDragging : false tickmarksEnabled: true @@ -404,13 +293,27 @@ Item } } } + + // Create a mouse area on top of the unavailable profiles to show a specific tooltip + MouseArea + { + anchors.fill: parent + hoverEnabled: true + enabled: !Cura.SimpleModeSettingsManager.isProfileUserCreated + onEntered: + { + var tooltipContent: 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("thick_margin").width, customisedSettings.height), unavailableSlider.tooltipContent) + } + onExited: base.hideTooltip() + } } // Draw available slider Slider { id: qualitySlider - height: UM.Theme.getSize("thick_margin").height + height: UM.Theme.getSize("print_setup_slider_handle").height // The handle is the widest element of the slider enabled: qualityModel.totalTicks > 0 && !Cura.SimpleModeSettingsManager.isProfileCustomized visible: qualityModel.availableTotalTicks > 0 updateValueWhileDragging : false @@ -469,49 +372,58 @@ Item } } } + + // This mouse area is only used to capture the onHover state and don't propagate it to the unavailable mouse area + MouseArea + { + anchors.fill: parent + hoverEnabled: true + enabled: !Cura.SimpleModeSettingsManager.isProfileUserCreated + } } -// MouseArea -// { -// id: speedSliderMouseArea -// anchors.fill: parent -// hoverEnabled: true -// enabled: Cura.SimpleModeSettingsManager.isProfileUserCreated -// -// onEntered: -// { -// var content = catalog.i18nc("@tooltip", "A custom profile is currently active. To enable the quality slider, choose a default quality profile in Custom tab") -// base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, customisedSettings.height), content) -// } -// onExited: base.hideTooltip() -// } + // This mouse area will only take the mouse events and show a tooltip when the profile in use is + // a user created profile + MouseArea + { + anchors.fill: parent + hoverEnabled: true + visible: Cura.SimpleModeSettingsManager.isProfileUserCreated + + onEntered: + { + var content = catalog.i18nc("@tooltip", "A custom profile is currently active. To enable the quality slider, choose a default quality profile in Custom tab") + base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, customisedSettings.height), content) + } + onExited: base.hideTooltip() + } + } + + UM.SimpleButton + { + id: customisedSettings + + visible: Cura.SimpleModeSettingsManager.isProfileCustomized || Cura.SimpleModeSettingsManager.isProfileUserCreated + height: Math.round(speedSlider.height * 0.8) + width: Math.round(speedSlider.height * 0.8) + + anchors.verticalCenter: speedSlider.verticalCenter + anchors.right: speedSlider.left + anchors.rightMargin: Math.round(UM.Theme.getSize("thick_margin").width / 2) + + color: hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button"); + iconSource: UM.Theme.getIcon("reset"); + + onClicked: + { + // if the current profile is user-created, switch to a built-in quality + Cura.MachineManager.resetToUseDefaultQuality() + } + onEntered: + { + var content = catalog.i18nc("@tooltip","You have modified some profile settings. If you want to change these go to custom mode.") + base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, customisedSettings.height), content) + } + onExited: base.hideTooltip() } -// -// UM.SimpleButton -// { -// id: customisedSettings -// -// visible: Cura.SimpleModeSettingsManager.isProfileCustomized || Cura.SimpleModeSettingsManager.isProfileUserCreated -// height: Math.round(speedSlider.height * 0.8) -// width: Math.round(speedSlider.height * 0.8) -// -// anchors.verticalCenter: speedSlider.verticalCenter -// anchors.right: speedSlider.left -// anchors.rightMargin: Math.round(UM.Theme.getSize("thick_margin").width / 2) -// -// color: hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button"); -// iconSource: UM.Theme.getIcon("reset"); -// -// onClicked: -// { -// // if the current profile is user-created, switch to a built-in quality -// Cura.MachineManager.resetToUseDefaultQuality() -// } -// onEntered: -// { -// var content = catalog.i18nc("@tooltip","You have modified some profile settings. If you want to change these go to custom mode.") -// base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, customisedSettings.height), content) -// } -// onExited: base.hideTooltip() -// } } \ No newline at end of file From 1a79372afd3e231fbbcde1ee864d71e7f354d5ab Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 30 Nov 2018 15:16:37 +0100 Subject: [PATCH 0535/1240] Fixed name of cocoon create For some reason it was added as if it was 2 machines at once. Since we already have a wanhao I3, i've changed this one to only be a profile for the Cocoon. --- resources/definitions/cocoon_create_modelmaker.def.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/definitions/cocoon_create_modelmaker.def.json b/resources/definitions/cocoon_create_modelmaker.def.json index 921b20624a..22aa75d09e 100644 --- a/resources/definitions/cocoon_create_modelmaker.def.json +++ b/resources/definitions/cocoon_create_modelmaker.def.json @@ -1,11 +1,11 @@ { - "name": "Cocoon Create ModelMaker & Wanhao Duplicator i3 Mini", + "name": "Cocoon Create ModelMaker", "version": 2, "inherits": "fdmprinter", "metadata": { "visible": true, "author": "Samuel Pinches", - "manufacturer": "Cocoon Create / Wanhao", + "manufacturer": "Cocoon Create", "file_formats": "text/x-gcode", "preferred_quality_type": "fine", "machine_extruder_trains": @@ -15,7 +15,7 @@ }, "overrides": { "machine_name": { - "default_value": "Cocoon Create ModelMaker & Wanhao Duplicator i3 Mini" + "default_value": "Cocoon Create ModelMaker" }, "machine_start_gcode": { "default_value": "; -- START GCODE --\nG21 ;set units to millimetres\nG90 ;set to absolute positioning\nM106 S0 ;set fan speed to zero (turned off)\nG28 ;home all axis\nG92 E0 ;zero the extruded length\nG1 Z1 F1000 ;move up slightly\nG1 X60.0 Z0 E9.0 F1000.0;intro line\nG1 X100.0 E21.5 F1000.0 ;continue line\nG92 E0 ;zero the extruded length again\n; -- end of START GCODE --" From 47b7d7bed8f1953297a78628ec7571e40c1cef78 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 30 Nov 2018 15:17:59 +0100 Subject: [PATCH 0536/1240] Make the slider available for clicking This was accidentally removed in the previous commit. Contributes to CURA-5941. --- .../RecommendedQualityProfileSelector.qml | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml index 755af9311f..50da35814c 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml @@ -231,6 +231,8 @@ Item // } // //Print speed slider + // Two sliders are created, one at the bottom with the unavailable qualities + // and the other at the top with the available quality profiles and so the handle to select them. Item { id: speedSlider @@ -302,8 +304,8 @@ Item enabled: !Cura.SimpleModeSettingsManager.isProfileUserCreated onEntered: { - var tooltipContent: 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("thick_margin").width, customisedSettings.height), unavailableSlider.tooltipContent) + var tooltipContent = 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("thick_margin").width, customisedSettings.height), tooltipContent) } onExited: base.hideTooltip() } @@ -378,6 +380,7 @@ Item { anchors.fill: parent hoverEnabled: true + acceptedButtons: Qt.NoButton enabled: !Cura.SimpleModeSettingsManager.isProfileUserCreated } } @@ -392,8 +395,8 @@ Item onEntered: { - var content = catalog.i18nc("@tooltip", "A custom profile is currently active. To enable the quality slider, choose a default quality profile in Custom tab") - base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, customisedSettings.height), content) + var tooltipContent = catalog.i18nc("@tooltip", "A custom profile is currently active. To enable the quality slider, choose a default quality profile in Custom tab") + base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, customisedSettings.height), tooltipContent) } onExited: base.hideTooltip() } @@ -421,8 +424,8 @@ Item } onEntered: { - var content = catalog.i18nc("@tooltip","You have modified some profile settings. If you want to change these go to custom mode.") - base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, customisedSettings.height), content) + var tooltipContent = catalog.i18nc("@tooltip","You have modified some profile settings. If you want to change these go to custom mode.") + base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, customisedSettings.height), tooltipContent) } onExited: base.hideTooltip() } From a4a619370ea064609d04791b95c2881c3f41467a Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 30 Nov 2018 15:33:57 +0100 Subject: [PATCH 0537/1240] Fix QML warnings --- resources/qml/Preferences/GeneralPage.qml | 3 --- 1 file changed, 3 deletions(-) diff --git a/resources/qml/Preferences/GeneralPage.qml b/resources/qml/Preferences/GeneralPage.qml index bba2cf764a..5006ae2777 100644 --- a/resources/qml/Preferences/GeneralPage.qml +++ b/resources/qml/Preferences/GeneralPage.qml @@ -151,7 +151,6 @@ UM.PreferencesPage { id: languageLabel text: catalog.i18nc("@label","Language:") - anchors.verticalCenter: languageComboBox.verticalCenter } ComboBox @@ -219,7 +218,6 @@ UM.PreferencesPage { id: currencyLabel text: catalog.i18nc("@label","Currency:") - anchors.verticalCenter: currencyField.verticalCenter } TextField @@ -233,7 +231,6 @@ UM.PreferencesPage { id: themeLabel text: catalog.i18nc("@label","Theme:") - anchors.verticalCenter: themeComboBox.verticalCenter } ComboBox From 2c5f5170c2d05f5ffce1da5c471ff14b8289cc5b Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 30 Nov 2018 15:38:28 +0100 Subject: [PATCH 0538/1240] Add the labels of the layer height to the slider Contributes to CURA-5941. --- .../RecommendedQualityProfileSelector.qml | 205 +++++++++--------- 1 file changed, 108 insertions(+), 97 deletions(-) diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml index 50da35814c..f7e1d870c9 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml @@ -158,78 +158,116 @@ Item } } - Cura.IconWithText + Item { - id: qualityRowTitle - source: UM.Theme.getIcon("category_layer_height") - text: catalog.i18nc("@label", "Layer Height") + id: titleRow width: labelColumnWidth + height: childrenRect.height + + Cura.IconWithText + { + id: qualityRowTitle + source: UM.Theme.getIcon("category_layer_height") + text: catalog.i18nc("@label", "Layer Height") + anchors.left: parent.left + anchors.right: customisedSettings.left + } + + UM.SimpleButton + { + id: customisedSettings + + visible: Cura.SimpleModeSettingsManager.isProfileCustomized || Cura.SimpleModeSettingsManager.isProfileUserCreated + height: visible ? Math.round(0.8 * qualityRowTitle.height) : 0 + width: height + anchors + { + right: parent.right + rightMargin: UM.Theme.getSize("default_margin").width + leftMargin: UM.Theme.getSize("default_margin").width + verticalCenter: parent.verticalCenter + } + + color: hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button") + iconSource: UM.Theme.getIcon("reset") + + onClicked: + { + // if the current profile is user-created, switch to a built-in quality + Cura.MachineManager.resetToUseDefaultQuality() + } + onEntered: + { + var tooltipContent = catalog.i18nc("@tooltip","You have modified some profile settings. If you want to change these go to custom mode.") + base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, customisedSettings.height), tooltipContent) + } + onExited: base.hideTooltip() + } + } + + // Show titles for the each quality slider ticks + Item + { + anchors.left: speedSlider.left + anchors.top: speedSlider.bottom + + Repeater + { + model: qualityModel + + Label + { + anchors.verticalCenter: parent.verticalCenter + anchors.top: parent.top + color: (Cura.MachineManager.activeMachine != null && Cura.QualityProfilesDropDownMenuModel.getItem(index).available) ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") + text: + { + var result = "" + if(Cura.MachineManager.activeMachine != null) + { + result = Cura.QualityProfilesDropDownMenuModel.getItem(index).layer_height + + if(result == undefined) + { + result = ""; + } + else + { + result = Number(Math.round(result + "e+2") + "e-2"); //Round to 2 decimals. Javascript makes this difficult... + if (result == undefined || result != result) //Parse failure. + { + result = ""; + } + } + } + return result + } + + x: + { + // Make sure the text aligns correctly with each tick + if (qualityModel.totalTicks == 0) + { + // If there is only one tick, align it centrally + return Math.round(((settingsColumnWidth) - width) / 2) + } + else if (index == 0) + { + return Math.round(settingsColumnWidth / qualityModel.totalTicks) * index + } + else if (index == qualityModel.totalTicks) + { + return Math.round(settingsColumnWidth / qualityModel.totalTicks) * index - width + } + else + { + return Math.round((settingsColumnWidth / qualityModel.totalTicks) * index - (width / 2)) + } + } + } + } } -// -// // Show titles for the each quality slider ticks -// Item -// { -// anchors.left: speedSlider.left -// anchors.top: speedSlider.bottom -// -// Repeater -// { -// model: qualityModel -// -// Label -// { -// anchors.verticalCenter: parent.verticalCenter -// anchors.top: parent.top -// color: (Cura.MachineManager.activeMachine != null && Cura.QualityProfilesDropDownMenuModel.getItem(index).available) ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") -// text: -// { -// var result = "" -// if(Cura.MachineManager.activeMachine != null) -// { -// result = Cura.QualityProfilesDropDownMenuModel.getItem(index).layer_height -// -// if(result == undefined) -// { -// result = ""; -// } -// else -// { -// result = Number(Math.round(result + "e+2") + "e-2"); //Round to 2 decimals. Javascript makes this difficult... -// if (result == undefined || result != result) //Parse failure. -// { -// result = ""; -// } -// } -// } -// return result -// } -// -// x: -// { -// // Make sure the text aligns correctly with each tick -// if (qualityModel.totalTicks == 0) -// { -// // If there is only one tick, align it centrally -// return Math.round(((settingsColumnWidth) - width) / 2) -// } -// else if (index == 0) -// { -// return Math.round(settingsColumnWidth / qualityModel.totalTicks) * index -// } -// else if (index == qualityModel.totalTicks) -// { -// return Math.round(settingsColumnWidth / qualityModel.totalTicks) * index - width -// } -// else -// { -// return Math.round((settingsColumnWidth / qualityModel.totalTicks) * index - (width / 2)) -// } -// } -// } -// } -// } -// //Print speed slider // Two sliders are created, one at the bottom with the unavailable qualities // and the other at the top with the available quality profiles and so the handle to select them. @@ -240,8 +278,9 @@ Item anchors { - left: qualityRowTitle.right + left: titleRow.right right: parent.right + verticalCenter: titleRow.verticalCenter } // Draw unavailable slider @@ -401,32 +440,4 @@ Item onExited: base.hideTooltip() } } - - UM.SimpleButton - { - id: customisedSettings - - visible: Cura.SimpleModeSettingsManager.isProfileCustomized || Cura.SimpleModeSettingsManager.isProfileUserCreated - height: Math.round(speedSlider.height * 0.8) - width: Math.round(speedSlider.height * 0.8) - - anchors.verticalCenter: speedSlider.verticalCenter - anchors.right: speedSlider.left - anchors.rightMargin: Math.round(UM.Theme.getSize("thick_margin").width / 2) - - color: hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button"); - iconSource: UM.Theme.getIcon("reset"); - - onClicked: - { - // if the current profile is user-created, switch to a built-in quality - Cura.MachineManager.resetToUseDefaultQuality() - } - onEntered: - { - var tooltipContent = catalog.i18nc("@tooltip","You have modified some profile settings. If you want to change these go to custom mode.") - base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, customisedSettings.height), tooltipContent) - } - onExited: base.hideTooltip() - } } \ No newline at end of file From 1d9a13cac27c4e8c571c53757ec81882d1c8994a Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 30 Nov 2018 15:40:59 +0100 Subject: [PATCH 0539/1240] Reset tab index when repeater's model is rebuilt We can't prevent the model from being rebuilt. At least, not without a major refactor. Contributes to issue CURA-5876. --- .../Menus/ConfigurationMenu/CustomConfiguration.qml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index 8ad512349b..bdf5ccfaa1 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -39,6 +39,7 @@ Item Repeater { + id: repeater model: extrudersModel delegate: UM.TabRowButton { @@ -59,6 +60,18 @@ Item } } } + + //When the model of the extruders is rebuilt, the list of extruders is briefly emptied and rebuilt. + //This causes the currentIndex of the tab to be in an invalid position which resets it to 0. + //Therefore we need to change it back to what it was: The active extruder index. + Connections + { + target: repeater.model + onModelChanged: + { + tabBar.currentIndex = Math.max(Cura.ExtruderManager.activeExtruderIndex, 0) + } + } } Rectangle From 18bb403413330fa4891ec80d4cb6177b8caa71b3 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 30 Nov 2018 15:42:46 +0100 Subject: [PATCH 0540/1240] No longer switch away from extruder when disabling It feels confusing when this happens. Contributes to issue CURA-5876. --- cura/Settings/ExtrudersModel.py | 4 ++-- cura/Settings/MachineManager.py | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/cura/Settings/ExtrudersModel.py b/cura/Settings/ExtrudersModel.py index 14a8dadc69..955bd9dbb2 100644 --- a/cura/Settings/ExtrudersModel.py +++ b/cura/Settings/ExtrudersModel.py @@ -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. from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot, pyqtProperty, QTimer @@ -177,7 +177,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): machine_extruder_count = global_container_stack.getProperty("machine_extruder_count", "value") for extruder in Application.getInstance().getExtruderManager().getActiveExtruderStacks(): - position = extruder.getMetaDataEntry("position", default = "0") # Get the position + position = extruder.getMetaDataEntry("position", default = "0") try: position = int(position) except ValueError: diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 40a3bfc563..be4a4e2b4e 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -993,10 +993,6 @@ class MachineManager(QObject): self.updateNumberExtrudersEnabled() self.correctExtruderSettings() - # In case this extruder is being disabled and it's the currently selected one, switch to the default extruder - if not enabled and position == ExtruderManager.getInstance().activeExtruderIndex: - ExtruderManager.getInstance().setActiveExtruderIndex(int(self._default_extruder_position)) - # ensure that the quality profile is compatible with current combination, or choose a compatible one if available self._updateQualityWithMaterial() self.extruderChanged.emit() From ede50ef3cad83e1c6c6a8b282bf27d9b261f17a9 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 30 Nov 2018 15:44:15 +0100 Subject: [PATCH 0541/1240] Simplify dependencies on activeStack The properties, being formulas, were not properly updated when the active stack changed. Contributes to issue CURA-5876. --- .../Menus/ConfigurationMenu/CustomConfiguration.qml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index bdf5ccfaa1..8044d86347 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -135,7 +135,7 @@ Item OldControls.CheckBox { - checked: selectors.model != null ? Cura.MachineManager.getExtruder(selectors.model.index).isEnabled: false + checked: Cura.MachineManager.activeStack != null ? Cura.MachineManager.activeStack.isEnabled : false onClicked: Cura.MachineManager.setExtruderEnabled(selectors.model.index, checked) height: UM.Theme.getSize("setting_control").height style: UM.Theme.styles.checkbox @@ -160,14 +160,11 @@ Item { id: materialSelection - property var activeExtruder: Cura.MachineManager.activeStack - property var hasActiveExtruder: activeExtruder != null - property var currentRootMaterialName: hasActiveExtruder ? activeExtruder.material.name : "" - property var valueError: hasActiveExtruder ? Cura.ContainerManager.getContainerMetaDataEntry(activeExtruder.material.id, "compatible", "") != "True" : true + property var valueError: Cura.MachineManager.activeStack != null ? Cura.ContainerManager.getContainerMetaDataEntry(Cura.MachineManager.activeStack.material.id, "compatible", "") != "True" : true property var valueWarning: !Cura.MachineManager.isActiveQualitySupported - text: currentRootMaterialName - tooltip: currentRootMaterialName + text: Cura.MachineManager.activeStack != null ? Cura.MachineManager.activeStack.material.name : "" + tooltip: text visible: Cura.MachineManager.hasMaterials enabled: Cura.ExtruderManager.activeExtruderIndex > -1 From ed37e692a38e6effb5f25a97409623330c6ea78f Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 30 Nov 2018 15:45:04 +0100 Subject: [PATCH 0542/1240] Don't disable enable checkbox before machine is loaded It is unnecessary because you can't see it or click on it before adding a machine. Contributes to issue CURA-5876. --- resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml | 2 -- 1 file changed, 2 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index 8044d86347..605d0444e6 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -167,8 +167,6 @@ Item tooltip: text visible: Cura.MachineManager.hasMaterials - enabled: Cura.ExtruderManager.activeExtruderIndex > -1 - height: UM.Theme.getSize("setting_control").height width: selectors.controlWidth From ad26962cb91a3d7e4e9deb32fddb63e5955bae16 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 30 Nov 2018 15:49:50 +0100 Subject: [PATCH 0543/1240] Fix setting visibility not working in settingVisibilityPage CURA-5981 --- resources/qml/Preferences/SettingVisibilityPage.qml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/qml/Preferences/SettingVisibilityPage.qml b/resources/qml/Preferences/SettingVisibilityPage.qml index 8896d0611e..e319069502 100644 --- a/resources/qml/Preferences/SettingVisibilityPage.qml +++ b/resources/qml/Preferences/SettingVisibilityPage.qml @@ -117,7 +117,7 @@ UM.PreferencesPage { for(var i = 0; i < settingVisibilityPresetsModel.items.length; ++i) { - if(settingVisibilityPresetsModel.items[i].id == settingVisibilityPresetsModel.activePreset) + if(settingVisibilityPresetsModel.items[i].presetId == settingVisibilityPresetsModel.activePreset) { currentIndex = i; return; @@ -128,8 +128,8 @@ UM.PreferencesPage onActivated: { - var preset_id = settingVisibilityPresetsModel.items[index].id; - settingVisibilityPresetsModel.setActivePreset(preset_id); + var preset_id = settingVisibilityPresetsModel.items[index].presetId + settingVisibilityPresetsModel.setActivePreset(preset_id) } } From 8b42b8461846a7a11cb06bf2e984c8bf15c88750 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Fri, 30 Nov 2018 16:07:20 +0100 Subject: [PATCH 0544/1240] Made a start with uploading jobs to the printer --- .../src/Cloud/CloudOutputDevice.py | 122 ++++++++++++++++-- .../UM3NetworkPrinting/src/Cloud/Models.py | 68 ++++++---- 2 files changed, 151 insertions(+), 39 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 06e5656392..5d2d140704 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -1,25 +1,27 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +import io import json import os -from typing import List, Optional, Dict +from typing import List, Optional, Dict, cast from PyQt5.QtCore import QObject, pyqtSignal, QUrl, pyqtProperty from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest from UM import i18nCatalog +from UM.FileHandler import FileWriter from UM.FileHandler.FileHandler import FileHandler from UM.Logger import Logger +from UM.OutputDevice import OutputDeviceError from UM.Scene.SceneNode import SceneNode -from UM.Settings import ContainerRegistry +from UM.Version import Version from cura.CuraApplication import CuraApplication from cura.PrinterOutput import PrinterOutputController, PrintJobOutputModel from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel -from .Models import CloudClusterPrinter, CloudClusterPrinterConfiguration, CloudClusterPrinterConfigurationMaterial, CloudClusterPrintJob, CloudClusterPrintJobConstraint - -from .CloudOutputController import CloudOutputController +from .Models import CloudClusterPrinter, CloudClusterPrinterConfiguration, CloudClusterPrinterConfigurationMaterial, \ + CloudClusterPrintJob, CloudClusterPrintJobConstraint, JobUploadRequest from ..UM3PrintJobOutputModel import UM3PrintJobOutputModel @@ -36,8 +38,11 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # The cloud URL to use for this remote cluster. # TODO: Make sure that this url goes to the live api before release - API_ROOT_PATH_FORMAT = "https://api-staging.ultimaker.com/connect/v1/clusters/{cluster_id}" - + ROOT_PATH= "https://api-staging.ultimaker.com" + CLUSTER_API_ROOT = "{}/connect/v1/".format(ROOT_PATH) + CURA_API_ROOT = "{}/cura/v1/".format(ROOT_PATH) + CURA_DRIVE_API_ROOT = "{}/cura-drive/v1/".format(ROOT_PATH) + # Signal triggered when the printers in the remote cluster were changed. printersChanged = pyqtSignal() @@ -64,7 +69,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## We need to override _createEmptyRequest to work for the cloud. def _createEmptyRequest(self, path: str, content_type: Optional[str] = "application/json") -> QNetworkRequest: - url = QUrl(self.API_ROOT_PATH_FORMAT.format(cluster_id = self._device_id) + path) + #url = QUrl(self.CLUSTER_API_ROOT_PATH_FORMAT.format(cluster_id = self._device_id) + path) + url = QUrl(path) request = QNetworkRequest(url) request.setHeader(QNetworkRequest.ContentTypeHeader, content_type) request.setHeader(QNetworkRequest.UserAgentHeader, self._user_agent) @@ -92,7 +98,72 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mime_types: bool = False, file_handler: Optional[FileHandler] = None, **kwargs: str) -> None: self.writeStarted.emit(self) - self._addPrintJobToQueue() + file_format = self._determineFileFormat(file_handler) + writer = self._determineWriter(file_format) + + # This function pauses with the yield, waiting on instructions on which printer it needs to print with. + if not writer: + Logger.log("e", "Missing file or mesh writer!") + return + + stream = io.BytesIO() # type: Union[io.BytesIO, io.StringIO]# Binary mode. + if file_format["mode"] == FileWriter.OutputMode.TextMode: + stream = io.StringIO() + + writer.write(stream, nodes) + + stream.seek(0, io.SEEK_END) + size = stream.tell() + stream.seek(0, io.SEEK_SET) + + request = JobUploadRequest() + request.job_name = file_name + request.file_size = size + + self._addPrintJobToQueue(stream, request) + + # TODO: This is yanked right out of ClusterUM3OoutputDevice, great candidate for a utility or base class + def _determineFileFormat(self, file_handler) -> None: + # Formats supported by this application (file types that we can actually write). + if file_handler: + file_formats = file_handler.getSupportedFileTypesWrite() + else: + file_formats = CuraApplication.getInstance().getMeshFileHandler().getSupportedFileTypesWrite() + + global_stack = CuraApplication.getInstance().getGlobalContainerStack() + # Create a list from the supported file formats string. + if not global_stack: + Logger.log("e", "Missing global stack!") + return + + machine_file_formats = global_stack.getMetaDataEntry("file_formats").split(";") + machine_file_formats = [file_type.strip() for file_type in machine_file_formats] + # Exception for UM3 firmware version >=4.4: UFP is now supported and should be the preferred file format. + if "application/x-ufp" not in machine_file_formats and Version(self.firmwareVersion) >= Version("4.4"): + machine_file_formats = ["application/x-ufp"] + machine_file_formats + + # Take the intersection between file_formats and machine_file_formats. + format_by_mimetype = {format["mime_type"]: format for format in file_formats} + file_formats = [format_by_mimetype[mimetype] for mimetype in machine_file_formats] #Keep them ordered according to the preference in machine_file_formats. + + if len(file_formats) == 0: + Logger.log("e", "There are no file formats available to write with!") + raise OutputDeviceError.WriteRequestFailedError(self.I18N_CATALOG.i18nc("@info:status", "There are no file formats available to write with!")) + return file_formats[0] + + # TODO: This is yanked right out of ClusterUM3OoutputDevice, great candidate for a utility or base class + def _determineWriter(self, file_handler, file_format): + # Just take the first file format available. + if file_handler is not None: + writer = file_handler.getWriterByMimeType(cast(str, file_format["mime_type"])) + else: + writer = CuraApplication.getInstance().getMeshFileHandler().getWriterByMimeType(cast(str, file_format["mime_type"])) + + if not writer: + Logger.log("e", "Unexpected error when trying to get the FileWriter") + return + + return writer ## Get remote printers. @pyqtProperty("QVariantList", notify = printersChanged) @@ -111,7 +182,9 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Called when the network data should be updated. def _update(self) -> None: super()._update() - self.get("/status", on_finished = self._onStatusCallFinished) + self.get("{root}/cluster/{cluster_id}/status".format(self.CLUSTER_API_ROOT, self._device_id), + on_finished = self._onStatusCallFinished) + ## Method called when HTTP request to status endpoint is finished. # Contains both printers and print jobs statuses in a single response. @@ -201,7 +274,6 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): firmware_version=printer.firmware_version) def _updatePrinter(self, guid : str, printer : CloudClusterPrinter): - model = self._printers[guid] self._printers[guid] = self._updatePrinterOutputModel(self, printer) def _updatePrinterOutputModel(self, printer: CloudClusterPrinter, model : PrinterOutputModel) -> PrinterOutputModel: @@ -299,6 +371,28 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def _removePrintJob(self, guid:str): del self._print_jobs[guid] - def _addPrintJobToQueue(self): - # TODO: implement this - pass + def _addPrintJobToQueue(self, stream, request:JobUploadRequest): + self.put("{}/jobs/upload".format(self.CURA_API_ROOT), data = json.dumps(request.__dict__), + on_finished = lambda reply: self._onAddPrintJobToQueueFinished(stream, reply)) + + def _onAddPrintJobToQueueFinished(self, stream, reply: QNetworkReply) -> None: + s = json.loads(bytes(reply.readAll()).decode("utf-8")) + + self.put() + + # try: + # r = requests.put(self._job.output_url, data=data) + # if r.status_code == 200: + # Logger.log("d", "Finished writing %s to remote URL %s", "", self._job.output_url) + # self.onWriteSuccess.emit(r.text) + # else: + # Logger.log("d", "Error writing %s to remote URL %s", "", self._job.output_url) + # self.onWriteFailed.emit("Failed to export G-code to remote URL: {}".format(r.text)) + # except requests.ConnectionError as e: + # Logger.log("e", "There was a connection error when uploading the G-code to a remote URL: %s", e) + # self.onWriteFailed.emit("Failed to export G-code to remote URL: {}".format(e)) + # except requests.HTTPError as e: + # Logger.log("e", "There was an HTTP error when uploading the G-code to a remote URL: %s", e) + # self.onWriteFailed.emit("Failed to export G-code to remote URL: {}".format(e)) + + pass \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models.py b/plugins/UM3NetworkPrinting/src/Cloud/Models.py index 7d6db9c8c0..86a48fb1f2 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models.py @@ -41,15 +41,15 @@ class CloudClusterPrinterConfiguration(BaseModel): ## Class representing a cluster printer class CloudClusterPrinter(BaseModel): def __init__(self, **kwargs): - self.configuration = None # type: CloudClusterPrinterConfiguration - self.enabled = None # type: str - self.firmware_version = None # type: str - self.friendly_name = None # type: str - self.ip_address = None # type: str - self.machine_variant = None # type: str - self.status = None # type: str - self.unique_name = None # type: str - self.uuid = None # type: str + self.configuration = None # type: CloudClusterPrinterConfiguration + self.enabled = None # type: str + self.firmware_version = None # type: str + self.friendly_name = None # type: str + self.ip_address = None # type: str + self.machine_variant = None # type: str + self.status = None # type: str + self.unique_name = None # type: str + self.uuid = None # type: str super().__init__(**kwargs) @@ -62,20 +62,38 @@ class CloudClusterPrintJobConstraint(BaseModel): ## Class representing a print job class CloudClusterPrintJob(BaseModel): def __init__(self, **kwargs): - self.assigned_to = None # type: str - self.configuration = None # type: str - self.constraints = None # type: str - self.created_at = None # type: str - self.force = None # type: str - self.last_seen = None # type: str - self.machine_variant = None # type: str - self.name = None # type: str - self.network_error_count = None # type: str - self.owner = None # type: str - self.printer_uuid = None # type: str - self.started = None # type: str - self.status = None # type: str - self.time_elapsed = None # type: str - self.time_total = None # type: str - self.uuid = None # type: str + self.assigned_to = None # type: str + self.configuration = None # type: str + self.constraints = None # type: str + self.created_at = None # type: str + self.force = None # type: str + self.last_seen = None # type: str + self.machine_variant = None # type: str + self.name = None # type: str + self.network_error_count = None # type: str + self.owner = None # type: str + self.printer_uuid = None # type: str + self.started = None # type: str + self.status = None # type: str + self.time_elapsed = None # type: str + self.time_total = None # type: str + self.uuid = None # type: str + super().__init__(**kwargs) + + +class JobUploadRequest(BaseModel): + def __init__(self, **kwargs): + self.file_size = None # type: int + self.job_name = None # type: str + super().__init__(**kwargs) + + +class JobUploadResponse(BaseModel): + def __init__(self, **kwargs): + self.download_url = None # type: str + self.job_id = None # type: str + self.job_name = None # type: str + self.slicing_details = None # type: str + self.status = None # type: str + self.upload_url = None # type: str super().__init__(**kwargs) From 36a86adc0f216e9bd03378db7017a7d8822ad765 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 30 Nov 2018 16:08:19 +0100 Subject: [PATCH 0545/1240] Remove spammy log --- plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py index daea696cd1..b96c508d70 100644 --- a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py @@ -341,7 +341,6 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): # Request more data if info is not complete if not info.address: - Logger.log("d", "Trying to get address of %s", name) info = zero_conf.get_service_info(service_type, name) if info: From 82d4897ba4e24ba9e1ed52a9d79dff0fc88e0096 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 30 Nov 2018 16:18:46 +0100 Subject: [PATCH 0546/1240] Use MouseArea to catch click instead of onClicked This is necessary because when you click it, the 'checked' property no longer depends on the active extruder. So prevent it from being clicked at all and handle the click separately in this MouseArea. Contributes to issue CURA-5876. --- .../Menus/ConfigurationMenu/CustomConfiguration.qml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index 605d0444e6..08ba042948 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -136,9 +136,19 @@ Item OldControls.CheckBox { checked: Cura.MachineManager.activeStack != null ? Cura.MachineManager.activeStack.isEnabled : false - onClicked: Cura.MachineManager.setExtruderEnabled(selectors.model.index, checked) height: UM.Theme.getSize("setting_control").height style: UM.Theme.styles.checkbox + + /* Use a MouseArea to process the click on this checkbox. + This is necessary because actually clicking the checkbox + causes the "checked" property to be overwritten. After + it's been overwritten, the original link that made it + depend on the active extruder stack is broken. */ + MouseArea + { + anchors.fill: parent + onClicked: Cura.MachineManager.setExtruderEnabled(Cura.ExtruderManager.activeExtruderIndex, !parent.checked) + } } } From d5d49fcec8ea3888ff53b47285322c56407e555a Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 30 Nov 2018 16:43:48 +0100 Subject: [PATCH 0547/1240] Disable extruder enabled checkbox if it's the last one You can't disable the last extruder. Contributes to issue CURA-5876. --- .../qml/Menus/ConfigurationMenu/CustomConfiguration.qml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index 08ba042948..6a3ab82ce2 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -10,6 +10,12 @@ import UM 1.3 as UM Item { + UM.I18nCatalog + { + id: catalog + name: "cura" + } + width: parent.width height: visible ? childrenRect.height : 0 @@ -136,6 +142,7 @@ Item OldControls.CheckBox { checked: Cura.MachineManager.activeStack != null ? Cura.MachineManager.activeStack.isEnabled : false + enabled: !checked || Cura.MachineManager.numberExtrudersEnabled > 1 //Disable if it's the last enabled extruder. height: UM.Theme.getSize("setting_control").height style: UM.Theme.styles.checkbox @@ -148,6 +155,7 @@ Item { anchors.fill: parent onClicked: Cura.MachineManager.setExtruderEnabled(Cura.ExtruderManager.activeExtruderIndex, !parent.checked) + enabled: parent.enabled } } } From 6ab2ce76900777ca64597cf81125351492b8f0aa Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 30 Nov 2018 16:44:55 +0100 Subject: [PATCH 0548/1240] Decrease padding between time & material specification headers --- resources/qml/ActionPanel/PrintJobInformation.qml | 2 -- 1 file changed, 2 deletions(-) diff --git a/resources/qml/ActionPanel/PrintJobInformation.qml b/resources/qml/ActionPanel/PrintJobInformation.qml index e53a92a994..156111af4d 100644 --- a/resources/qml/ActionPanel/PrintJobInformation.qml +++ b/resources/qml/ActionPanel/PrintJobInformation.qml @@ -21,7 +21,6 @@ Column Column { id: timeSpecification - spacing: UM.Theme.getSize("thin_margin").width width: parent.width topPadding: UM.Theme.getSize("default_margin").height leftPadding: UM.Theme.getSize("default_margin").width @@ -71,7 +70,6 @@ Column Column { id: materialSpecification - spacing: UM.Theme.getSize("thin_margin").width width: parent.width bottomPadding: UM.Theme.getSize("default_margin").height leftPadding: UM.Theme.getSize("default_margin").width From f4950cf92b2ba797d62ff85a6c936762c68bb81f Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 30 Nov 2018 17:04:50 +0100 Subject: [PATCH 0549/1240] Give checkboxes a style if they're disabled Contributes to issue CURA-5876. --- resources/themes/cura-light/styles.qml | 2 +- resources/themes/cura-light/theme.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index f00aab44c0..97eae65d27 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -646,7 +646,7 @@ QtObject implicitWidth: Theme.getSize("checkbox").width implicitHeight: Theme.getSize("checkbox").height - color: (control.hovered || control._hovered) ? Theme.getColor("checkbox_hover") : Theme.getColor("checkbox") + color: (control.hovered || control._hovered) ? Theme.getColor("checkbox_hover") : (control.enabled ? Theme.getColor("checkbox") : Theme.getColor("checkbox_disabled")) Behavior on color { ColorAnimation { duration: 50; } } radius: control.exclusiveGroup ? Math.round(Theme.getSize("checkbox").width / 2) : 0 diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index bd7c632eb1..0d77ecf802 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -235,6 +235,7 @@ "checkbox_border": [64, 69, 72, 255], "checkbox_border_hover": [50, 130, 255, 255], "checkbox_mark": [119, 122, 124, 255], + "checkbox_disabled": [223, 223, 223, 255], "checkbox_text": [27, 27, 27, 255], "tooltip": [68, 192, 255, 255], From 0a6e420710ae3c76a9f707c69b0afaec5307ae8f Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 30 Nov 2018 17:05:25 +0100 Subject: [PATCH 0550/1240] Make active tab white Contributes to issue CURA-5876. --- resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index 6a3ab82ce2..b269b95df2 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -89,7 +89,7 @@ Item radius: UM.Theme.getSize("default_radius").width border.width: UM.Theme.getSize("default_lining").width border.color: UM.Theme.getColor("lining") - color: UM.Theme.getColor("secondary") + color: UM.Theme.getColor("main_background") //Remove rounding and lining at the top. Rectangle From 29b7f42e0e2cb15df004fcbf5df023fbf17b9b0c Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Fri, 30 Nov 2018 18:33:53 +0100 Subject: [PATCH 0551/1240] Chaged settings visibility icon CURA-5941 --- resources/qml/Settings/SettingView.qml | 30 ++++++++++++++++++-------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 0a7102ff45..28a64f3346 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -1,4 +1,4 @@ -// Copyright (c) 2017 Ultimaker B.V. +// Copyright (c) 2018 Ultimaker B.V. // Uranium is released under the terms of the LGPLv3 or higher. import QtQuick 2.7 @@ -25,13 +25,15 @@ Item { id: settingVisibilityMenu + property var toolButtonIconColor: UM.Theme.getColor("setting_category_text") + width: height height: UM.Theme.getSize("setting_control").height anchors { topMargin: UM.Theme.getSize("thick_margin").height - right: parent.right - rightMargin: UM.Theme.getSize("thick_margin").width + left: filterContainer.right + leftMargin: UM.Theme.getSize("default_margin").width } style: ButtonStyle { @@ -39,12 +41,12 @@ Item UM.RecolorImage { anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter - width: UM.Theme.getSize("standard_arrow").width - height: UM.Theme.getSize("standard_arrow").height + width: Math.round(parent.width * 0.6) + height: Math.round(parent.height * 0.6) 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") + color: settingVisibilityMenu.toolButtonIconColor + source: UM.Theme.getIcon("settings") } } label: Label{} @@ -57,6 +59,15 @@ Item filter.updateDefinitionModel(); } } + + MouseArea + { + anchors.fill: parent + hoverEnabled: true + acceptedButtons: Qt.RightButton + onEntered: settingVisibilityMenu.toolButtonIconColor = UM.Theme.getColor("setting_control_button_hover") + onExited: settingVisibilityMenu.toolButtonIconColor = UM.Theme.getColor("setting_category_text") + } } Rectangle @@ -84,8 +95,8 @@ Item topMargin: UM.Theme.getSize("thick_margin").height left: parent.left leftMargin: UM.Theme.getSize("default_margin").width - right: settingVisibilityMenu.left - rightMargin: Math.floor(UM.Theme.getSize("default_margin").width / 2) + right: scrollView.right + rightMargin: Math.floor(UM.Theme.getSize("wide_margin").width * 2) } height: UM.Theme.getSize("setting_control").height Timer @@ -196,6 +207,7 @@ Item ScrollView { + id: scrollView anchors.top: filterContainer.bottom; anchors.bottom: parent.bottom; anchors.right: parent.right; From 1238aa7304c24e3c7a706fdcceae09c14791409c Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Sun, 2 Dec 2018 12:03:58 +0100 Subject: [PATCH 0552/1240] Format the slider for the infill in the recommended mode Contributes to CURA-5941 --- .../PrintSetupSelectorContents.qml | 2 +- .../RecommendedInfillDensitySelector.qml | 488 +++++++++--------- .../Recommended/RecommendedPrintSetup.qml | 45 +- .../RecommendedQualityProfileSelector.qml | 28 +- 4 files changed, 268 insertions(+), 295 deletions(-) diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml index 5f22e4c337..0210ece977 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml @@ -25,7 +25,7 @@ Item { id: header height: UM.Theme.getSize("print_setup_widget_header").height - color: "transparent" //UM.Theme.getColor("action_button_hovered") // TODO: It's not clear the color that we need to use here + color: UM.Theme.getColor("action_button_hovered") // TODO: It's not clear the color that we need to use here anchors { diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml index 34cb8f2f20..8b0b87d997 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml @@ -15,57 +15,37 @@ import Cura 1.0 as Cura Item { id: infillRow + height: childrenRect.height + property real labelColumnWidth: Math.round(width / 3) + + // Here are the elements that are shown in the left column Cura.IconWithText { id: infillRowTitle source: UM.Theme.getIcon("category_infill") text: catalog.i18nc("@label", "Infill") + " (%)" - anchors.bottom: parent.bottom width: labelColumnWidth } Item { - id: infillCellRight + id: infillSliderContainer + height: childrenRect.height - height: infillSlider.height + UM.Theme.getSize("thick_margin").height + enableGradualInfillCheckBox.visible * (enableGradualInfillCheckBox.height + UM.Theme.getSize("thick_margin").height) - - anchors.left: infillRowTitle.right - anchors.right: parent.right - - Label + anchors { - id: selectedInfillRateText - - anchors.left: infillSlider.left - anchors.right: parent.right - - text: parseInt(infillDensity.properties.value) + "%" - horizontalAlignment: Text.AlignLeft - - color: infillSlider.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") - } - - // We use a binding to make sure that after manually setting infillSlider.value it is still bound to the property provider - Binding - { - target: infillSlider - property: "value" - value: parseInt(infillDensity.properties.value) + left: infillRowTitle.right + right: parent.right + verticalCenter: infillRowTitle.verticalCenter } Slider { id: infillSlider - anchors.top: selectedInfillRateText.bottom - anchors.left: parent.left - anchors.right: infillIcon.left - anchors.rightMargin: UM.Theme.getSize("thick_margin").width - - height: UM.Theme.getSize("thick_margin").height - width: parseInt(infillCellRight.width - UM.Theme.getSize("thick_margin").width - style.handleWidth) + width: parent.width + height: UM.Theme.getSize("print_setup_slider_handle").height // The handle is the widest element of the slider minimumValue: 0 maximumValue: 100 @@ -78,9 +58,62 @@ Item // set initial value from stack value: parseInt(infillDensity.properties.value) + style: SliderStyle + { + //Draw line + groove: Item + { + Rectangle + { + height: UM.Theme.getSize("print_setup_slider_groove").height + width: control.width - UM.Theme.getSize("print_setup_slider_handle").width + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + color: control.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") + } + } + + handle: Rectangle + { + id: handleButton + color: control.enabled ? UM.Theme.getColor("primary") : UM.Theme.getColor("quality_slider_unavailable") + implicitWidth: UM.Theme.getSize("print_setup_slider_handle").width + implicitHeight: implicitWidth + radius: Math.round(implicitWidth / 2) + opacity: 0.5 + } + + tickmarks: Repeater + { + id: repeater + model: control.maximumValue / control.stepSize + 1 + + Rectangle + { + color: control.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") + implicitWidth: UM.Theme.getSize("print_setup_slider_tickmarks").width + implicitHeight: UM.Theme.getSize("print_setup_slider_tickmarks").height + anchors.verticalCenter: parent.verticalCenter + + // Do not use Math.round otherwise the tickmarks won't be aligned + x: ((styleData.handleWidth / 2) - (implicitWidth / 2) + (index * ((repeater.width - styleData.handleWidth) / (repeater.count-1)))) + radius: Math.round(implicitWidth / 2) + visible: (index % 10) == 0 // Only show steps of 10% + + Label + { + text: index + visible: (index % 20) == 0 // Only show steps of 20% + anchors.horizontalCenter: parent.horizontalCenter + y: UM.Theme.getSize("thin_margin").height + renderType: Text.NativeRendering + } + } + } + } + onValueChanged: { - // Don't round the value if it's already the same if (parseInt(infillDensity.properties.value) == infillSlider.value) { @@ -104,228 +137,177 @@ Item Cura.MachineManager.resetSettingForAllExtruders("infill_line_distance") } } - - style: SliderStyle - { - groove: Rectangle - { - id: groove - implicitWidth: 200 * screenScaleFactor - implicitHeight: 2 * screenScaleFactor - color: control.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") - radius: 1 - } - - handle: Item - { - Rectangle - { - id: handleButton - anchors.centerIn: parent - color: control.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") - implicitWidth: 10 * screenScaleFactor - implicitHeight: 10 * screenScaleFactor - radius: 10 * screenScaleFactor - } - } - - tickmarks: Repeater - { - id: repeater - model: control.maximumValue / control.stepSize + 1 - - // check if a tick should be shown based on it's index and wether the infill density is a multiple of 10 (slider step size) - function shouldShowTick (index) - { - if (index % 10 == 0) - { - return true - } - return false - } - - Rectangle - { - anchors.verticalCenter: parent.verticalCenter - color: control.enabled ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") - width: 1 * screenScaleFactor - height: 6 * screenScaleFactor - x: Math.round(styleData.handleWidth / 2 + index * ((repeater.width - styleData.handleWidth) / (repeater.count-1))) - visible: shouldShowTick(index) - } - } - } } - Rectangle - { - id: infillIcon - - width: Math.round((parent.width / 5) - (UM.Theme.getSize("thick_margin").width)) - height: width - - anchors.right: parent.right - anchors.top: parent.top - anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height / 2) - - // we loop over all density icons and only show the one that has the current density and steps - Repeater - { - id: infillIconList - model: infillModel - anchors.fill: parent - - function activeIndex () - { - for (var i = 0; i < infillModel.count; i++) - { - var density = Math.round(infillDensity.properties.value) - var steps = Math.round(infillSteps.properties.value) - var infillModelItem = infillModel.get(i) - - if (infillModelItem != "undefined" - && density >= infillModelItem.percentageMin - && density <= infillModelItem.percentageMax - && steps >= infillModelItem.stepsMin - && steps <= infillModelItem.stepsMax) - { - return i - } - } - return -1 - } - - Rectangle - { - anchors.fill: parent - visible: infillIconList.activeIndex() == index - - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("quality_slider_unavailable") - - UM.RecolorImage - { - anchors.fill: parent - anchors.margins: 2 * screenScaleFactor - sourceSize.width: width - sourceSize.height: width - source: UM.Theme.getIcon(model.icon) - color: UM.Theme.getColor("quality_slider_unavailable") - } - } - } - } - - // Gradual Support Infill Checkbox - CheckBox - { - id: enableGradualInfillCheckBox - property alias _hovered: enableGradualInfillMouseArea.containsMouse - - anchors.top: infillSlider.bottom - anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height / 2) // closer to slider since it belongs to the same category - anchors.left: infillCellRight.left - - style: UM.Theme.styles.checkbox - enabled: base.settingsEnabled - visible: infillSteps.properties.enabled == "True" - checked: parseInt(infillSteps.properties.value) > 0 - - MouseArea - { - id: enableGradualInfillMouseArea - - anchors.fill: parent - hoverEnabled: true - enabled: true - - property var previousInfillDensity: parseInt(infillDensity.properties.value) - - onClicked: - { - // Set to 90% only when enabling gradual infill - var newInfillDensity; - if (parseInt(infillSteps.properties.value) == 0) - { - previousInfillDensity = parseInt(infillDensity.properties.value) - newInfillDensity = 90 - } else { - newInfillDensity = previousInfillDensity - } - Cura.MachineManager.setSettingForAllExtruders("infill_sparse_density", "value", String(newInfillDensity)) - - var infill_steps_value = 0 - if (parseInt(infillSteps.properties.value) == 0) - { - infill_steps_value = 5 - } - - Cura.MachineManager.setSettingForAllExtruders("gradual_infill_steps", "value", infill_steps_value) - } - - onEntered: base.showTooltip(enableGradualInfillCheckBox, Qt.point(-infillCellRight.x, 0), - catalog.i18nc("@label", "Gradual infill will gradually increase the amount of infill towards the top.")) - - onExited: base.hideTooltip() - - } - - Label - { - id: gradualInfillLabel - height: parent.height - anchors.left: enableGradualInfillCheckBox.right - anchors.leftMargin: Math.round(UM.Theme.getSize("thick_margin").width / 2) - verticalAlignment: Text.AlignVCenter; - text: catalog.i18nc("@label", "Enable gradual") - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - } - } - - // Infill list model for mapping icon - ListModel - { - id: infillModel - Component.onCompleted: - { - infillModel.append({ - percentageMin: -1, - percentageMax: 0, - stepsMin: -1, - stepsMax: 0, - icon: "hollow" - }) - infillModel.append({ - percentageMin: 0, - percentageMax: 40, - stepsMin: -1, - stepsMax: 0, - icon: "sparse" - }) - infillModel.append({ - percentageMin: 40, - percentageMax: 89, - stepsMin: -1, - stepsMax: 0, - icon: "dense" - }) - infillModel.append({ - percentageMin: 90, - percentageMax: 9999999999, - stepsMin: -1, - stepsMax: 0, - icon: "solid" - }) - infillModel.append({ - percentageMin: 0, - percentageMax: 9999999999, - stepsMin: 1, - stepsMax: 9999999999, - icon: "gradual" - }) - } - } +// Rectangle +// { +// id: infillIcon +// +// width: Math.round((parent.width / 5) - (UM.Theme.getSize("thick_margin").width)) +// height: width +// +// anchors.right: parent.right +// anchors.top: parent.top +// anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height / 2) +// +// // we loop over all density icons and only show the one that has the current density and steps +// Repeater +// { +// id: infillIconList +// model: infillModel +// anchors.fill: parent +// +// function activeIndex () +// { +// for (var i = 0; i < infillModel.count; i++) +// { +// var density = Math.round(infillDensity.properties.value) +// var steps = Math.round(infillSteps.properties.value) +// var infillModelItem = infillModel.get(i) +// +// if (infillModelItem != "undefined" +// && density >= infillModelItem.percentageMin +// && density <= infillModelItem.percentageMax +// && steps >= infillModelItem.stepsMin +// && steps <= infillModelItem.stepsMax) +// { +// return i +// } +// } +// return -1 +// } +// +// Rectangle +// { +// anchors.fill: parent +// visible: infillIconList.activeIndex() == index +// +// border.width: UM.Theme.getSize("default_lining").width +// border.color: UM.Theme.getColor("quality_slider_unavailable") +// +// UM.RecolorImage +// { +// anchors.fill: parent +// anchors.margins: 2 * screenScaleFactor +// sourceSize.width: width +// sourceSize.height: width +// source: UM.Theme.getIcon(model.icon) +// color: UM.Theme.getColor("quality_slider_unavailable") +// } +// } +// } +// } +// +// // Gradual Support Infill Checkbox +// CheckBox +// { +// id: enableGradualInfillCheckBox +// property alias _hovered: enableGradualInfillMouseArea.containsMouse +// +// anchors.top: infillSlider.bottom +// anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height / 2) // closer to slider since it belongs to the same category +// anchors.left: infillSliderContainer.left +// +// style: UM.Theme.styles.checkbox +// enabled: base.settingsEnabled +// visible: infillSteps.properties.enabled == "True" +// checked: parseInt(infillSteps.properties.value) > 0 +// +// MouseArea +// { +// id: enableGradualInfillMouseArea +// +// anchors.fill: parent +// hoverEnabled: true +// enabled: true +// +// property var previousInfillDensity: parseInt(infillDensity.properties.value) +// +// onClicked: +// { +// // Set to 90% only when enabling gradual infill +// var newInfillDensity; +// if (parseInt(infillSteps.properties.value) == 0) +// { +// previousInfillDensity = parseInt(infillDensity.properties.value) +// newInfillDensity = 90 +// } else { +// newInfillDensity = previousInfillDensity +// } +// Cura.MachineManager.setSettingForAllExtruders("infill_sparse_density", "value", String(newInfillDensity)) +// +// var infill_steps_value = 0 +// if (parseInt(infillSteps.properties.value) == 0) +// { +// infill_steps_value = 5 +// } +// +// Cura.MachineManager.setSettingForAllExtruders("gradual_infill_steps", "value", infill_steps_value) +// } +// +// onEntered: base.showTooltip(enableGradualInfillCheckBox, Qt.point(-infillSliderContainer.x, 0), +// catalog.i18nc("@label", "Gradual infill will gradually increase the amount of infill towards the top.")) +// +// onExited: base.hideTooltip() +// +// } +// +// Label +// { +// id: gradualInfillLabel +// height: parent.height +// anchors.left: enableGradualInfillCheckBox.right +// anchors.leftMargin: Math.round(UM.Theme.getSize("thick_margin").width / 2) +// verticalAlignment: Text.AlignVCenter; +// text: catalog.i18nc("@label", "Enable gradual") +// font: UM.Theme.getFont("default") +// color: UM.Theme.getColor("text") +// } +// } +// +// // Infill list model for mapping icon +// ListModel +// { +// id: infillModel +// Component.onCompleted: +// { +// infillModel.append({ +// percentageMin: -1, +// percentageMax: 0, +// stepsMin: -1, +// stepsMax: 0, +// icon: "hollow" +// }) +// infillModel.append({ +// percentageMin: 0, +// percentageMax: 40, +// stepsMin: -1, +// stepsMax: 0, +// icon: "sparse" +// }) +// infillModel.append({ +// percentageMin: 40, +// percentageMax: 89, +// stepsMin: -1, +// stepsMax: 0, +// icon: "dense" +// }) +// infillModel.append({ +// percentageMin: 90, +// percentageMax: 9999999999, +// stepsMin: -1, +// stepsMax: 0, +// icon: "solid" +// }) +// infillModel.append({ +// percentageMin: 0, +// percentageMax: 9999999999, +// stepsMin: 1, +// stepsMax: 9999999999, +// icon: "gradual" +// }) +// } +// } } UM.SettingPropertyProvider diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml index 40a1910c69..0dabc3ea1e 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml @@ -12,10 +12,10 @@ Item { id: base + height: childrenRect.height + 2 * padding + signal showTooltip(Item item, point location, string text) signal hideTooltip() -// width: parent.width - height: childrenRect.height + 2 * padding property Action configureSettings @@ -28,24 +28,11 @@ Item name: "cura" } -// Rectangle -// { -// width: parent.width - 2 * parent.padding -// anchors -// { -// left: parent.left -// right: parent.right -// top: parent.top -// margins: parent.padding -// } -// color: "blue" -// height: 50 -// } - Column { width: parent.width - 2 * parent.padding - spacing: UM.Theme.getSize("default_margin").height + spacing: UM.Theme.getSize("wide_margin").height + anchors { left: parent.left @@ -55,33 +42,27 @@ Item } // TODO - property real labelColumnWidth: Math.round(width / 3) - property real settingsColumnWidth: width - labelColumnWidth + property real firstColumnWidth: Math.round(width / 3) RecommendedQualityProfileSelector { width: parent.width // TODO Create a reusable component with these properties to not define them separately for each component - property real labelColumnWidth: parent.labelColumnWidth - property real settingsColumnWidth: parent.settingsColumnWidth + labelColumnWidth: parent.firstColumnWidth } -// RecommendedInfillDensitySelector -// { -// width: parent.width -// height: childrenRect.height -// // TODO Create a reusable component with these properties to not define them separately for each component -// property real labelColumnWidth: parent.labelColumnWidth -// property real settingsColumnWidth: parent.settingsColumnWidth -// } + RecommendedInfillDensitySelector + { + width: parent.width + // TODO Create a reusable component with these properties to not define them separately for each component + labelColumnWidth: parent.firstColumnWidth + } // // RecommendedSupportSelector // { // width: parent.width -// height: childrenRect.height // // TODO Create a reusable component with these properties to not define them separately for each component -// property real labelColumnWidth: parent.labelColumnWidth -// property real settingsColumnWidth: parent.settingsColumnWidth +// property real firstColumnWidth: parent.labelColumnWidth // } diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml index f7e1d870c9..cddf01a4dc 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml @@ -17,6 +17,9 @@ Item id: qualityRow height: childrenRect.height + property real labelColumnWidth: Math.round(width / 3) + property real settingsColumnWidth: width - labelColumnWidth + Timer { id: qualitySliderChangeTimer @@ -158,6 +161,7 @@ Item } } + // Here are the elements that are shown in the left column Item { id: titleRow @@ -210,6 +214,7 @@ Item { anchors.left: speedSlider.left anchors.top: speedSlider.bottom + height: childrenRect.height Repeater { @@ -219,6 +224,8 @@ Item { anchors.verticalCenter: parent.verticalCenter anchors.top: parent.top + // The height has to be set manually, otherwise it's not automatically calculated in the repeater + height: UM.Theme.getSize("default_margin").height color: (Cura.MachineManager.activeMachine != null && Cura.QualityProfilesDropDownMenuModel.getItem(index).available) ? UM.Theme.getColor("quality_slider_available") : UM.Theme.getColor("quality_slider_unavailable") text: { @@ -268,7 +275,7 @@ Item } } - //Print speed slider + // Print speed slider // Two sliders are created, one at the bottom with the unavailable qualities // and the other at the top with the available quality profiles and so the handle to select them. Item @@ -287,6 +294,8 @@ Item Slider { id: unavailableSlider + + width: parent.width height: qualitySlider.height // Same height as the slider that is on top updateValueWhileDragging : false tickmarksEnabled: true @@ -297,8 +306,6 @@ Item maximumValue: qualityModel.totalTicks stepSize: 1 - width: parent.width - style: SliderStyle { //Draw Unvailable line @@ -329,7 +336,7 @@ Item anchors.verticalCenter: parent.verticalCenter // Do not use Math.round otherwise the tickmarks won't be aligned - x: ((UM.Theme.getSize("print_setup_slider_handle").width / 2) - (UM.Theme.getSize("print_setup_slider_tickmarks").width / 2) + (qualityModel.qualitySliderStepWidth * index)) + x: ((UM.Theme.getSize("print_setup_slider_handle").width / 2) - (implicitWidth / 2) + (qualityModel.qualitySliderStepWidth * index)) radius: Math.round(implicitWidth / 2) } } @@ -354,11 +361,19 @@ Item Slider { id: qualitySlider + + width: qualityModel.qualitySliderStepWidth * (qualityModel.availableTotalTicks - 1) + UM.Theme.getSize("print_setup_slider_handle").width height: UM.Theme.getSize("print_setup_slider_handle").height // The handle is the widest element of the slider enabled: qualityModel.totalTicks > 0 && !Cura.SimpleModeSettingsManager.isProfileCustomized visible: qualityModel.availableTotalTicks > 0 updateValueWhileDragging : false + anchors + { + right: parent.right + rightMargin: qualityModel.qualitySliderMarginRight + } + minimumValue: qualityModel.qualitySliderAvailableMin >= 0 ? qualityModel.qualitySliderAvailableMin : 0 // maximumValue must be greater than minimumValue to be able to see the handle. While the value is strictly // speaking not always correct, it seems to have the correct behavior (switching from 0 available to 1 available) @@ -367,11 +382,6 @@ Item value: qualityModel.qualitySliderActiveIndex - width: qualityModel.qualitySliderStepWidth * (qualityModel.availableTotalTicks - 1) + UM.Theme.getSize("print_setup_slider_handle").width - - anchors.right: parent.right - anchors.rightMargin: qualityModel.qualitySliderMarginRight - style: SliderStyle { // Draw Available line From 4154ec2fe82a79334a2d1b10d2de745a1aa38b19 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Sun, 2 Dec 2018 12:39:52 +0100 Subject: [PATCH 0553/1240] Add enable gradual checkbox to the infill panel Contributes to CURA-5941. --- .../RecommendedInfillDensitySelector.qml | 146 +++++++++--------- 1 file changed, 77 insertions(+), 69 deletions(-) diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml index 8b0b87d997..4226acb790 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml @@ -23,11 +23,20 @@ Item Cura.IconWithText { id: infillRowTitle + anchors.top: parent.top + anchors.left: parent.left source: UM.Theme.getIcon("category_infill") text: catalog.i18nc("@label", "Infill") + " (%)" width: labelColumnWidth } + Rectangle + { + anchors.fill: infillSliderContainer + color: "red" + opacity: 0.5 + } + Item { id: infillSliderContainer @@ -80,7 +89,6 @@ Item implicitWidth: UM.Theme.getSize("print_setup_slider_handle").width implicitHeight: implicitWidth radius: Math.round(implicitWidth / 2) - opacity: 0.5 } tickmarks: Repeater @@ -138,6 +146,74 @@ Item } } } + } + + // Gradual Support Infill Checkbox + CheckBox + { + id: enableGradualInfillCheckBox + property alias _hovered: enableGradualInfillMouseArea.containsMouse + + anchors.top: infillSliderContainer.bottom + anchors.topMargin: UM.Theme.getSize("wide_margin").height + anchors.left: infillSliderContainer.left + + style: UM.Theme.styles.checkbox + enabled: base.settingsEnabled + visible: infillSteps.properties.enabled == "True" + checked: parseInt(infillSteps.properties.value) > 0 + + MouseArea + { + id: enableGradualInfillMouseArea + + anchors.fill: parent + hoverEnabled: true + enabled: true + + property var previousInfillDensity: parseInt(infillDensity.properties.value) + + onClicked: + { + // Set to 90% only when enabling gradual infill + var newInfillDensity; + if (parseInt(infillSteps.properties.value) == 0) + { + previousInfillDensity = parseInt(infillDensity.properties.value) + newInfillDensity = 90 + } else { + newInfillDensity = previousInfillDensity + } + Cura.MachineManager.setSettingForAllExtruders("infill_sparse_density", "value", String(newInfillDensity)) + + var infill_steps_value = 0 + if (parseInt(infillSteps.properties.value) == 0) + { + infill_steps_value = 5 + } + + Cura.MachineManager.setSettingForAllExtruders("gradual_infill_steps", "value", infill_steps_value) + } + + onEntered: base.showTooltip(enableGradualInfillCheckBox, Qt.point(-infillSliderContainer.x - UM.Theme.getSize("thick_margin").width, 0), + catalog.i18nc("@label", "Gradual infill will gradually increase the amount of infill towards the top.")) + + onExited: base.hideTooltip() + } + + Label + { + id: gradualInfillLabel + height: parent.height + anchors.left: enableGradualInfillCheckBox.right + anchors.leftMargin: UM.Theme.getSize("default_margin").width + verticalAlignment: Text.AlignVCenter + text: catalog.i18nc("@label", "Enable gradual") + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + renderType: Text.NativeRendering + } + } // Rectangle // { @@ -198,73 +274,6 @@ Item // } // } // -// // Gradual Support Infill Checkbox -// CheckBox -// { -// id: enableGradualInfillCheckBox -// property alias _hovered: enableGradualInfillMouseArea.containsMouse -// -// anchors.top: infillSlider.bottom -// anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height / 2) // closer to slider since it belongs to the same category -// anchors.left: infillSliderContainer.left -// -// style: UM.Theme.styles.checkbox -// enabled: base.settingsEnabled -// visible: infillSteps.properties.enabled == "True" -// checked: parseInt(infillSteps.properties.value) > 0 -// -// MouseArea -// { -// id: enableGradualInfillMouseArea -// -// anchors.fill: parent -// hoverEnabled: true -// enabled: true -// -// property var previousInfillDensity: parseInt(infillDensity.properties.value) -// -// onClicked: -// { -// // Set to 90% only when enabling gradual infill -// var newInfillDensity; -// if (parseInt(infillSteps.properties.value) == 0) -// { -// previousInfillDensity = parseInt(infillDensity.properties.value) -// newInfillDensity = 90 -// } else { -// newInfillDensity = previousInfillDensity -// } -// Cura.MachineManager.setSettingForAllExtruders("infill_sparse_density", "value", String(newInfillDensity)) -// -// var infill_steps_value = 0 -// if (parseInt(infillSteps.properties.value) == 0) -// { -// infill_steps_value = 5 -// } -// -// Cura.MachineManager.setSettingForAllExtruders("gradual_infill_steps", "value", infill_steps_value) -// } -// -// onEntered: base.showTooltip(enableGradualInfillCheckBox, Qt.point(-infillSliderContainer.x, 0), -// catalog.i18nc("@label", "Gradual infill will gradually increase the amount of infill towards the top.")) -// -// onExited: base.hideTooltip() -// -// } -// -// Label -// { -// id: gradualInfillLabel -// height: parent.height -// anchors.left: enableGradualInfillCheckBox.right -// anchors.leftMargin: Math.round(UM.Theme.getSize("thick_margin").width / 2) -// verticalAlignment: Text.AlignVCenter; -// text: catalog.i18nc("@label", "Enable gradual") -// font: UM.Theme.getFont("default") -// color: UM.Theme.getColor("text") -// } -// } -// // // Infill list model for mapping icon // ListModel // { @@ -308,7 +317,6 @@ Item // }) // } // } - } UM.SettingPropertyProvider { From 1caccfb57705ed75dc49d0d257a71204d9210827 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Sun, 2 Dec 2018 13:40:41 +0100 Subject: [PATCH 0554/1240] Add a binding to react when the infill density changes in the custom panel Also make the icon dinamic, that will change when the infill density changes. Contributes to CURA-5941. --- .../RecommendedInfillDensitySelector.qml | 160 ++++-------------- resources/themes/cura-light/styles.qml | 3 +- resources/themes/cura-light/theme.json | 9 +- 3 files changed, 44 insertions(+), 128 deletions(-) diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml index 4226acb790..0dd594176c 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml @@ -19,6 +19,42 @@ Item property real labelColumnWidth: Math.round(width / 3) + // Create a binding to update the icon when the infill density changes + Binding + { + target: infillRowTitle + property: "source" + value: + { + var density = parseInt(infillDensity.properties.value) + if (parseInt(infillSteps.properties.value) != 0) + { + return UM.Theme.getIcon("gradual") + } + if (density <= 0) + { + return UM.Theme.getIcon("hollow") + } + if (density < 40) + { + return UM.Theme.getIcon("sparse") + } + if (density < 90) + { + return UM.Theme.getIcon("dense") + } + return UM.Theme.getIcon("solid") + } + } + + // We use a binding to make sure that after manually setting infillSlider.value it is still bound to the property provider + Binding + { + target: infillSlider + property: "value" + value: parseInt(infillDensity.properties.value) + } + // Here are the elements that are shown in the left column Cura.IconWithText { @@ -30,13 +66,6 @@ Item width: labelColumnWidth } - Rectangle - { - anchors.fill: infillSliderContainer - color: "red" - opacity: 0.5 - } - Item { id: infillSliderContainer @@ -158,6 +187,7 @@ Item anchors.topMargin: UM.Theme.getSize("wide_margin").height anchors.left: infillSliderContainer.left + text: catalog.i18nc("@label", "Gradual infill") style: UM.Theme.styles.checkbox enabled: base.settingsEnabled visible: infillSteps.properties.enabled == "True" @@ -200,124 +230,8 @@ Item onExited: base.hideTooltip() } - - Label - { - id: gradualInfillLabel - height: parent.height - anchors.left: enableGradualInfillCheckBox.right - anchors.leftMargin: UM.Theme.getSize("default_margin").width - verticalAlignment: Text.AlignVCenter - text: catalog.i18nc("@label", "Enable gradual") - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - renderType: Text.NativeRendering - } } -// Rectangle -// { -// id: infillIcon -// -// width: Math.round((parent.width / 5) - (UM.Theme.getSize("thick_margin").width)) -// height: width -// -// anchors.right: parent.right -// anchors.top: parent.top -// anchors.topMargin: Math.round(UM.Theme.getSize("thick_margin").height / 2) -// -// // we loop over all density icons and only show the one that has the current density and steps -// Repeater -// { -// id: infillIconList -// model: infillModel -// anchors.fill: parent -// -// function activeIndex () -// { -// for (var i = 0; i < infillModel.count; i++) -// { -// var density = Math.round(infillDensity.properties.value) -// var steps = Math.round(infillSteps.properties.value) -// var infillModelItem = infillModel.get(i) -// -// if (infillModelItem != "undefined" -// && density >= infillModelItem.percentageMin -// && density <= infillModelItem.percentageMax -// && steps >= infillModelItem.stepsMin -// && steps <= infillModelItem.stepsMax) -// { -// return i -// } -// } -// return -1 -// } -// -// Rectangle -// { -// anchors.fill: parent -// visible: infillIconList.activeIndex() == index -// -// border.width: UM.Theme.getSize("default_lining").width -// border.color: UM.Theme.getColor("quality_slider_unavailable") -// -// UM.RecolorImage -// { -// anchors.fill: parent -// anchors.margins: 2 * screenScaleFactor -// sourceSize.width: width -// sourceSize.height: width -// source: UM.Theme.getIcon(model.icon) -// color: UM.Theme.getColor("quality_slider_unavailable") -// } -// } -// } -// } -// -// // Infill list model for mapping icon -// ListModel -// { -// id: infillModel -// Component.onCompleted: -// { -// infillModel.append({ -// percentageMin: -1, -// percentageMax: 0, -// stepsMin: -1, -// stepsMax: 0, -// icon: "hollow" -// }) -// infillModel.append({ -// percentageMin: 0, -// percentageMax: 40, -// stepsMin: -1, -// stepsMax: 0, -// icon: "sparse" -// }) -// infillModel.append({ -// percentageMin: 40, -// percentageMax: 89, -// stepsMin: -1, -// stepsMax: 0, -// icon: "dense" -// }) -// infillModel.append({ -// percentageMin: 90, -// percentageMax: 9999999999, -// stepsMin: -1, -// stepsMax: 0, -// icon: "solid" -// }) -// infillModel.append({ -// percentageMin: 0, -// percentageMax: 9999999999, -// stepsMin: 1, -// stepsMax: 9999999999, -// icon: "gradual" -// }) -// } -// } - UM.SettingPropertyProvider { id: infillDensity diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index e040d91df9..96bf334c43 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -532,7 +532,7 @@ QtObject color: (control.hovered || control._hovered) ? Theme.getColor("checkbox_hover") : Theme.getColor("checkbox") Behavior on color { ColorAnimation { duration: 50; } } - radius: control.exclusiveGroup ? Math.round(Theme.getSize("checkbox").width / 2) : 0 + radius: control.exclusiveGroup ? Math.round(Theme.getSize("checkbox").width / 2) : UM.Theme.getSize("checkbox_radius").width border.width: Theme.getSize("default_lining").width border.color: (control.hovered || control._hovered) ? Theme.getColor("checkbox_border_hover") : Theme.getColor("checkbox_border") @@ -557,6 +557,7 @@ QtObject color: Theme.getColor("checkbox_text") font: Theme.getFont("default") elide: Text.ElideRight + renderType: Text.NativeRendering } } } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 7cfd7b93e1..392b09303d 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -239,10 +239,10 @@ "checkbox": [255, 255, 255, 255], "checkbox_hover": [255, 255, 255, 255], - "checkbox_border": [64, 69, 72, 255], + "checkbox_border": [199, 199, 199, 255], "checkbox_border_hover": [50, 130, 255, 255], - "checkbox_mark": [119, 122, 124, 255], - "checkbox_text": [27, 27, 27, 255], + "checkbox_mark": [50, 130, 255, 255], + "checkbox_text": [35, 35, 35, 255], "tooltip": [68, 192, 255, 255], "tooltip_text": [255, 255, 255, 255], @@ -459,7 +459,8 @@ "layerview_row": [11.0, 1.5], "layerview_row_spacing": [0.0, 0.5], - "checkbox": [2.0, 2.0], + "checkbox": [1.5, 1.5], + "checkbox_radius": [0.08, 0.08], "tooltip": [20.0, 10.0], "tooltip_margins": [1.0, 1.0], From 1cea36b08a5f6f1a8899667a39ec87285abab9c6 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Sun, 2 Dec 2018 19:01:54 +0100 Subject: [PATCH 0555/1240] Add the row for the support selection in the recommended mode Contributes to CURA-5941. --- .../Recommended/RecommendedPrintSetup.qml | 15 +- .../RecommendedSupportSelector.qml | 189 +++++++++++------- resources/themes/cura-light/styles.qml | 15 +- resources/themes/cura-light/theme.json | 10 +- 4 files changed, 134 insertions(+), 95 deletions(-) diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml index 0dabc3ea1e..d246be560e 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml @@ -57,13 +57,13 @@ Item // TODO Create a reusable component with these properties to not define them separately for each component labelColumnWidth: parent.firstColumnWidth } -// -// RecommendedSupportSelector -// { -// width: parent.width -// // TODO Create a reusable component with these properties to not define them separately for each component -// property real firstColumnWidth: parent.labelColumnWidth -// } + + RecommendedSupportSelector + { + width: parent.width + // TODO Create a reusable component with these properties to not define them separately for each component + labelColumnWidth: parent.firstColumnWidth + } // @@ -128,7 +128,6 @@ Item // } } - UM.SettingPropertyProvider { id: platformAdhesionType diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml index 9c4e5ed576..2407b746b2 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml @@ -8,114 +8,142 @@ import QtQuick.Controls.Styles 1.4 import UM 1.2 as UM import Cura 1.0 as Cura + // // Enable support // -Row +Item { + id: enableSupportRow + height: childrenRect.height + + property real labelColumnWidth: Math.round(width / 3) Cura.IconWithText { - id: enableSupportLabel + id: enableSupportRowTitle + anchors.top: parent.top + anchors.left: parent.left visible: enableSupportCheckBox.visible source: UM.Theme.getIcon("category_support") text: catalog.i18nc("@label", "Support") width: labelColumnWidth } - CheckBox + Item { - id: enableSupportCheckBox - property alias _hovered: enableSupportMouseArea.containsMouse + id: enableSupportContainer + height: childrenRect.height - style: UM.Theme.styles.checkbox - enabled: base.settingsEnabled - - visible: supportEnabled.properties.enabled == "True" - checked: supportEnabled.properties.value == "True" - - MouseArea + anchors { - id: enableSupportMouseArea - anchors.fill: parent - hoverEnabled: true - onClicked: supportEnabled.setPropertyValue("value", supportEnabled.properties.value != "True") - - onEntered: base.showTooltip(enableSupportCheckBox, Qt.point(-enableSupportCheckBox.x, 0), - catalog.i18nc("@label", "Generate structures to support parts of the model which have overhangs. Without these structures, such parts would collapse during printing.")) - - onExited: base.hideTooltip() - - } - } - - ComboBox - { - id: supportExtruderCombobox - visible: enableSupportCheckBox.visible && (supportEnabled.properties.value == "True") && (extrudersEnabledCount.properties.value > 1) - model: extruderModel - - property string color_override: "" // for manually setting values - property string color: // is evaluated automatically, but the first time is before extruderModel being filled - { - var current_extruder = extruderModel.get(currentIndex); - color_override = ""; - if (current_extruder === undefined) return "" - return (current_extruder.color) ? current_extruder.color : ""; + left: enableSupportRowTitle.right + right: parent.right + verticalCenter: enableSupportRowTitle.verticalCenter } - textRole: "text" // this solves that the combobox isn't populated in the first time Cura is started - - width: Math.round(UM.Theme.getSize("print_setup_widget").width * .55) - Math.round(UM.Theme.getSize("thick_margin").width / 2) - enableSupportCheckBox.width - height: ((supportEnabled.properties.value == "True") && (machineExtruderCount.properties.value > 1)) ? UM.Theme.getSize("setting_control").height : 0 - - Behavior on height { NumberAnimation { duration: 100 } } - - style: UM.Theme.styles.combobox_color - enabled: base.settingsEnabled - property alias _hovered: supportExtruderMouseArea.containsMouse - - currentIndex: + CheckBox { - if (supportExtruderNr.properties == null) + id: enableSupportCheckBox + anchors.verticalCenter: parent.verticalCenter + + property alias _hovered: enableSupportMouseArea.containsMouse + + style: UM.Theme.styles.checkbox + enabled: base.settingsEnabled + + visible: supportEnabled.properties.enabled == "True" + checked: supportEnabled.properties.value == "True" + + MouseArea { - return Cura.MachineManager.defaultExtruderPosition + id: enableSupportMouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: supportEnabled.setPropertyValue("value", supportEnabled.properties.value != "True") + + onEntered: + { + base.showTooltip(enableSupportCheckBox, Qt.point(-enableSupportContainer.x - UM.Theme.getSize("thick_margin").width, 0), + catalog.i18nc("@label", "Generate structures to support parts of the model which have overhangs. Without these structures, such parts would collapse during printing.")) + } + onExited: base.hideTooltip() } - else + } + + ComboBox + { + id: supportExtruderCombobox + + height: UM.Theme.getSize("print_setup_support_extruder_selector").height + anchors { - var extruder = parseInt(supportExtruderNr.properties.value) - if ( extruder === -1) + left: enableSupportCheckBox.right + right: parent.right + leftMargin: UM.Theme.getSize("thick_margin").width + rightMargin: UM.Theme.getSize("thick_margin").width + verticalCenter: parent.verticalCenter + } + + style: UM.Theme.styles.combobox_color + enabled: base.settingsEnabled + visible: enableSupportCheckBox.visible && (supportEnabled.properties.value == "True") && (extrudersEnabledCount.properties.value > 1) + textRole: "text" // this solves that the combobox isn't populated in the first time Cura is started + + model: extruderModel + + property alias _hovered: supportExtruderMouseArea.containsMouse + property string color_override: "" // for manually setting values + property string color: // is evaluated automatically, but the first time is before extruderModel being filled + { + var current_extruder = extruderModel.get(currentIndex); + color_override = ""; + if (current_extruder === undefined) return "" + return (current_extruder.color) ? current_extruder.color : ""; + } + + currentIndex: + { + if (supportExtruderNr.properties == null) { return Cura.MachineManager.defaultExtruderPosition } - return extruder; + else + { + var extruder = parseInt(supportExtruderNr.properties.value) + if ( extruder === -1) + { + return Cura.MachineManager.defaultExtruderPosition + } + return extruder; + } } - } - onActivated: supportExtruderNr.setPropertyValue("value", String(index)) + onActivated: supportExtruderNr.setPropertyValue("value", String(index)) - MouseArea - { - id: supportExtruderMouseArea - anchors.fill: parent - hoverEnabled: true - enabled: base.settingsEnabled - acceptedButtons: Qt.NoButton - onEntered: + MouseArea { - base.showTooltip(supportExtruderCombobox, Qt.point(-supportExtruderCombobox.x, 0), - catalog.i18nc("@label", "Select which extruder to use for support. This will build up supporting structures below the model to prevent the model from sagging or printing in mid air.")); + id: supportExtruderMouseArea + anchors.fill: parent + hoverEnabled: true + enabled: base.settingsEnabled + acceptedButtons: Qt.NoButton + onEntered: + { + base.showTooltip(supportExtruderCombobox, Qt.point(-enableSupportContainer.x - supportExtruderCombobox.x - UM.Theme.getSize("thick_margin").width, 0), + catalog.i18nc("@label", "Select which extruder to use for support. This will build up supporting structures below the model to prevent the model from sagging or printing in mid air.")); + } + onExited: base.hideTooltip() + } - onExited: base.hideTooltip() - } - - function updateCurrentColor() - { - var current_extruder = extruderModel.get(currentIndex) - if (current_extruder !== undefined) + function updateCurrentColor() { - supportExtruderCombobox.color_override = current_extruder.color + var current_extruder = extruderModel.get(currentIndex) + if (current_extruder !== undefined) + { + supportExtruderCombobox.color_override = current_extruder.color + } } } } @@ -160,6 +188,15 @@ Row storeIndex: 0 } + UM.SettingPropertyProvider + { + id: machineExtruderCount + containerStack: Cura.MachineManager.activeMachine + key: "machine_extruder_count" + watchedProperties: ["value"] + storeIndex: 0 + } + function populateExtruderModel() { extruderModel.clear() diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index 96bf334c43..5203cb0b0b 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -413,11 +413,11 @@ QtObject border.width: Theme.getSize("default_lining").width; border.color: control.hovered ? Theme.getColor("setting_control_border_highlight") : Theme.getColor("setting_control_border"); + radius: UM.Theme.getSize("setting_control_radius").width } label: Item { - Label { anchors.left: parent.left @@ -465,11 +465,11 @@ QtObject color: !enabled ? UM.Theme.getColor("setting_control_disabled") : control._hovered ? UM.Theme.getColor("setting_control_highlight") : UM.Theme.getColor("setting_control") border.width: UM.Theme.getSize("default_lining").width border.color: !enabled ? UM.Theme.getColor("setting_control_disabled_border") : control._hovered ? UM.Theme.getColor("setting_control_border_highlight") : UM.Theme.getColor("setting_control_border") + radius: UM.Theme.getSize("setting_control_radius").width } label: Item { - Label { anchors.left: parent.left @@ -486,7 +486,7 @@ QtObject verticalAlignment: Text.AlignVCenter } - Rectangle + UM.RecolorImage { id: swatch height: Math.round(UM.Theme.getSize("setting_control").height / 2) @@ -494,9 +494,9 @@ QtObject anchors.right: downArrow.left anchors.verticalCenter: parent.verticalCenter anchors.margins: Math.round(UM.Theme.getSize("default_margin").width / 4) - radius: Math.round(width / 2) - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("lining") + sourceSize.width: width + sourceSize.height: height + source: UM.Theme.getIcon("extruder_button") color: (control.color_override !== "") ? control.color_override : control.color } @@ -575,7 +575,7 @@ QtObject color: (control.hovered || control._hovered) ? Theme.getColor("checkbox_hover") : Theme.getColor("checkbox"); Behavior on color { ColorAnimation { duration: 50; } } - radius: control.exclusiveGroup ? Math.round(Theme.getSize("checkbox").width / 2) : 0 + radius: control.exclusiveGroup ? Math.round(Theme.getSize("checkbox").width / 2) : UM.Theme.getSize("checkbox_radius").width border.width: Theme.getSize("default_lining").width; border.color: (control.hovered || control._hovered) ? Theme.getColor("checkbox_border_hover") : Theme.getColor("checkbox_border"); @@ -628,6 +628,7 @@ QtObject border.width: Theme.getSize("default_lining").width; border.color: control.hovered ? Theme.getColor("setting_control_border_highlight") : Theme.getColor("setting_control_border"); + radius: UM.Theme.getSize("setting_control_radius").width color: Theme.getColor("setting_validation_ok"); diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 392b09303d..afe40007f4 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -206,11 +206,11 @@ "setting_control": [255, 255, 255, 255], "setting_control_selected": [31, 36, 39, 255], "setting_control_highlight": [255, 255, 255, 255], - "setting_control_border": [127, 127, 127, 255], + "setting_control_border": [199, 199, 199, 255], "setting_control_border_highlight": [50, 130, 255, 255], - "setting_control_text": [27, 27, 27, 255], - "setting_control_depth_line": [127, 127, 127, 255], - "setting_control_button": [127, 127, 127, 255], + "setting_control_text": [35, 35, 35, 255], + "setting_control_depth_line": [199, 199, 199, 255], + "setting_control_button": [199, 199, 199, 255], "setting_control_button_hover": [70, 84, 113, 255], "setting_control_disabled": [245, 245, 245, 255], "setting_control_disabled_text": [127, 127, 127, 255], @@ -377,6 +377,7 @@ "print_setup_slider_groove": [0.16, 0.16], "print_setup_slider_handle": [1.0, 1.0], "print_setup_slider_tickmarks": [0.32, 0.32], + "print_setup_support_extruder_selector": [18.2, 2.5], "configuration_selector_mode_tabs": [0.0, 3.0], @@ -415,6 +416,7 @@ "setting": [25.0, 1.8], "setting_control": [10.0, 2.0], + "setting_control_radius": [0.15, 0.15], "setting_control_depth_margin": [1.4, 0.0], "setting_preferences_button_margin": [4, 0.0], "setting_control_margin": [0.0, 0.0], From adabb833e0b6dbc978bfb96f8b42004f03e28a84 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Sun, 2 Dec 2018 19:14:34 +0100 Subject: [PATCH 0556/1240] Add row for the adhesion setting in the recommended mode Contributes to CURA-5941. --- .../RecommendedAdhesionSelector.qml | 99 +++++++++++++++++++ .../Recommended/RecommendedPrintSetup.qml | 77 ++------------- .../RecommendedSupportSelector.qml | 2 +- 3 files changed, 106 insertions(+), 72 deletions(-) create mode 100644 resources/qml/PrintSetupSelector/Recommended/RecommendedAdhesionSelector.qml diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedAdhesionSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedAdhesionSelector.qml new file mode 100644 index 0000000000..a7e8b3ea75 --- /dev/null +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedAdhesionSelector.qml @@ -0,0 +1,99 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +import UM 1.2 as UM +import Cura 1.0 as Cura + + +// +// Adhesion +// +Item +{ + id: enableAdhesionRow + height: childrenRect.height + + property real labelColumnWidth: Math.round(width / 3) + + Cura.IconWithText + { + id: enableAdhesionRowTitle + anchors.top: parent.top + anchors.left: parent.left + source: UM.Theme.getIcon("category_adhesion") + text: catalog.i18nc("@label", "Adhesion") + width: labelColumnWidth + } + + Item + { + id: enableAdhesionContainer + height: childrenRect.height + + anchors + { + left: enableAdhesionRowTitle.right + right: parent.right + verticalCenter: enableAdhesionRowTitle.verticalCenter + } + + CheckBox + { + id: enableAdhesionCheckBox + anchors.verticalCenter: parent.verticalCenter + + //: Setting enable printing build-plate adhesion helper checkbox + style: UM.Theme.styles.checkbox + enabled: base.settingsEnabled + + visible: platformAdhesionType.properties.enabled == "True" + checked: platformAdhesionType.properties.value != "skirt" && platformAdhesionType.properties.value != "none" + + MouseArea + { + id: adhesionMouseArea + anchors.fill: parent + hoverEnabled: true + enabled: base.settingsEnabled + + onClicked: + { + var adhesionType = "skirt" + if (!parent.checked) + { + // Remove the "user" setting to see if the rest of the stack prescribes a brim or a raft + platformAdhesionType.removeFromContainer(0) + adhesionType = platformAdhesionType.properties.value + if(adhesionType == "skirt" || adhesionType == "none") + { + // If the rest of the stack doesn't prescribe an adhesion-type, default to a brim + adhesionType = "brim" + } + } + platformAdhesionType.setPropertyValue("value", adhesionType) + } + + onEntered: + { + base.showTooltip(enableAdhesionCheckBox, Qt.point(-enableAdhesionContainer.x - UM.Theme.getSize("thick_margin").width, 0), + catalog.i18nc("@label", "Enable printing a brim or raft. This will add a flat area around or under your object which is easy to cut off afterwards.")); + } + onExited: base.hideTooltip() + } + } + } + + UM.SettingPropertyProvider + { + id: platformAdhesionType + containerStack: Cura.MachineManager.activeMachine + removeUnusedValue: false //Doesn't work with settings that are resolved. + key: "adhesion_type" + watchedProperties: [ "value", "enabled" ] + storeIndex: 0 + } +} \ No newline at end of file diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml index d246be560e..a8fca47d6c 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml @@ -65,76 +65,11 @@ Item labelColumnWidth: parent.firstColumnWidth } - -// -// // Adhesion -// Row -// { -// anchors.left: parent.left -// anchors.right: parent.right -// height: childrenRect.height -// -// Cura.IconWithText -// { -// id: adhesionHelperLabel -// visible: adhesionCheckBox.visible -// source: UM.Theme.getIcon("category_adhesion") -// text: catalog.i18nc("@label", "Adhesion") -// width: labelColumnWidth -// } -// -// CheckBox -// { -// id: adhesionCheckBox -// property alias _hovered: adhesionMouseArea.containsMouse -// -// //: Setting enable printing build-plate adhesion helper checkbox -// style: UM.Theme.styles.checkbox -// enabled: base.settingsEnabled -// -// visible: platformAdhesionType.properties.enabled == "True" -// checked: platformAdhesionType.properties.value != "skirt" && platformAdhesionType.properties.value != "none" -// -// MouseArea -// { -// id: adhesionMouseArea -// anchors.fill: parent -// hoverEnabled: true -// enabled: base.settingsEnabled -// onClicked: -// { -// var adhesionType = "skirt" -// if(!parent.checked) -// { -// // Remove the "user" setting to see if the rest of the stack prescribes a brim or a raft -// platformAdhesionType.removeFromContainer(0) -// adhesionType = platformAdhesionType.properties.value -// if(adhesionType == "skirt" || adhesionType == "none") -// { -// // If the rest of the stack doesn't prescribe an adhesion-type, default to a brim -// adhesionType = "brim" -// } -// } -// platformAdhesionType.setPropertyValue("value", adhesionType) -// } -// onEntered: -// { -// base.showTooltip(adhesionCheckBox, Qt.point(-adhesionCheckBox.x, 0), -// catalog.i18nc("@label", "Enable printing a brim or raft. This will add a flat area around or under your object which is easy to cut off afterwards.")); -// } -// onExited: base.hideTooltip() -// } -// } -// } - } - - UM.SettingPropertyProvider - { - id: platformAdhesionType - containerStack: Cura.MachineManager.activeMachine - removeUnusedValue: false //Doesn't work with settings that are resolved. - key: "adhesion_type" - watchedProperties: [ "value", "enabled" ] - storeIndex: 0 + RecommendedAdhesionSelector + { + width: parent.width + // TODO Create a reusable component with these properties to not define them separately for each component + labelColumnWidth: parent.firstColumnWidth + } } } diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml index 2407b746b2..4e63242f05 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml @@ -33,7 +33,7 @@ Item Item { id: enableSupportContainer - height: childrenRect.height + height: enableSupportCheckBox.height anchors { From 7dc3320b0648142719b59c7a73dcf66da373335c Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Sun, 2 Dec 2018 20:32:55 +0100 Subject: [PATCH 0557/1240] Remove all the signal propagation for the tooltip and allow only Cura.qml to handle them Contributes to CURA-5941. --- plugins/PrepareStage/PrepareMenu.qml | 4 ---- plugins/PreviewStage/PreviewMenu.qml | 3 --- resources/qml/Cura.qml | 9 --------- .../Custom/CustomPrintSetup.qml | 13 ++++++++++++- .../PrintSetupSelector/PrintSetupSelector.qml | 17 +---------------- .../PrintSetupSelectorContents.qml | 9 +++------ .../Recommended/RecommendedAdhesionSelector.qml | 7 ++++--- .../RecommendedInfillDensitySelector.qml | 2 +- .../Recommended/RecommendedPrintSetup.qml | 5 +---- .../Recommended/RecommendedSupportSelector.qml | 7 ++++--- resources/qml/Settings/SettingView.qml | 6 ++---- 11 files changed, 28 insertions(+), 54 deletions(-) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index 7c01b1f8b0..0191396e7b 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -13,10 +13,6 @@ import QtGraphicalEffects 1.0 // For the dropshadow Item { id: prepareMenu - // This widget doesn't show tooltips by itself. Instead it emits signals so others can do something with it. - signal showTooltip(Item item, point location, string text) - signal hideTooltip() - UM.I18nCatalog { diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index a1f59cd4ca..1543536160 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -10,9 +10,6 @@ import Cura 1.1 as Cura Item { id: previewMenu - // This widget doesn't show tooltips by itself. Instead it emits signals so others can do something with it. - signal showTooltip(Item item, point location, string text) - signal hideTooltip() property real itemHeight: height - 2 * UM.Theme.getSize("default_lining").width diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 36f5758fa3..4effea5ba7 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -181,13 +181,6 @@ UM.MainWindow } } - Connections - { - target: stageMenu.item - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() - } - JobSpecs { id: jobSpecs @@ -280,8 +273,6 @@ UM.MainWindow // Every time the stage is changed. property var printSetupSelector: Cura.PrintSetupSelector { - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() width: UM.Theme.getSize("print_setup_widget").width height: UM.Theme.getSize("stage_menu").height headerCornerSide: RoundedRectangle.Direction.Right diff --git a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml index 51cd8eff0d..ea56a4471e 100644 --- a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml +++ b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml @@ -6,5 +6,16 @@ import QtQuick.Controls 2.0 import Cura 1.0 as Cura -Cura.SettingView { + +Item +{ + id: customPrintSetup + + // TODO: Hardcoded now but UX has to decide about the height of this item + height: 500 + + Cura.SettingView + { + anchors.fill: parent + } } diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector/PrintSetupSelector.qml index 1794a54cdf..5518ac6b60 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelector.qml @@ -9,17 +9,13 @@ import Cura 1.0 as Cura Cura.ExpandableComponent { - id: base + id: printSetupSelector property bool hideSettings: PrintInformation.preSliced property string enabledText: catalog.i18nc("@label:Should be short", "On") property string disabledText: catalog.i18nc("@label:Should be short", "Off") - // This widget doesn't show tooltips by itself. Instead it emits signals so others can do something with it. - signal showTooltip(Item item, point location, string text) - signal hideTooltip() - iconSource: UM.Theme.getIcon("pencil") popupPadding: UM.Theme.getSize("default_lining").width popupSpacingY: UM.Theme.getSize("narrow_margin").width @@ -32,17 +28,6 @@ Cura.ExpandableComponent name: "cura" } - Timer - { - id: tooltipDelayTimer - interval: 500 - repeat: false - property var item - property string text - - onTriggered: base.showTooltip(base, {x: 0, y: item.y}, text) - } - headerItem: PrintSetupSelectorHeader { anchors.fill: parent diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml index 0210ece977..77d1b59f4c 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml @@ -93,7 +93,7 @@ Item Item { id: contents - height: childrenRect.height + height: currentModeIndex == 0 ? recommendedPrintSetup.height : customPrintSetup.height anchors { @@ -104,28 +104,25 @@ Item RecommendedPrintSetup { + id: recommendedPrintSetup anchors { left: parent.left right: parent.right top: parent.top } - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() visible: currentModeIndex == 0 } CustomPrintSetup { + id: customPrintSetup anchors { left: parent.left right: parent.right top: parent.top } - height: 500 - onShowTooltip: base.showTooltip(item, location, text) - onHideTooltip: base.hideTooltip() visible: currentModeIndex == 1 } } diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedAdhesionSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedAdhesionSelector.qml index a7e8b3ea75..3092644d4e 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedAdhesionSelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedAdhesionSelector.qml @@ -32,7 +32,7 @@ Item Item { id: enableAdhesionContainer - height: childrenRect.height + height: enableAdhesionCheckBox.height anchors { @@ -46,9 +46,11 @@ Item id: enableAdhesionCheckBox anchors.verticalCenter: parent.verticalCenter + property alias _hovered: adhesionMouseArea.containsMouse + //: Setting enable printing build-plate adhesion helper checkbox style: UM.Theme.styles.checkbox - enabled: base.settingsEnabled + enabled: recommendedPrintSettup.settingsEnabled visible: platformAdhesionType.properties.enabled == "True" checked: platformAdhesionType.properties.value != "skirt" && platformAdhesionType.properties.value != "none" @@ -58,7 +60,6 @@ Item id: adhesionMouseArea anchors.fill: parent hoverEnabled: true - enabled: base.settingsEnabled onClicked: { diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml index 0dd594176c..7c026ac9de 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml @@ -189,7 +189,7 @@ Item text: catalog.i18nc("@label", "Gradual infill") style: UM.Theme.styles.checkbox - enabled: base.settingsEnabled + enabled: recommendedPrintSettup.settingsEnabled visible: infillSteps.properties.enabled == "True" checked: parseInt(infillSteps.properties.value) > 0 diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml index a8fca47d6c..194747271e 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml @@ -10,13 +10,10 @@ import Cura 1.0 as Cura Item { - id: base + id: recommendedPrintSettup height: childrenRect.height + 2 * padding - signal showTooltip(Item item, point location, string text) - signal hideTooltip() - property Action configureSettings property bool settingsEnabled: Cura.ExtruderManager.activeExtruderStackId || extrudersEnabledCount.properties.value == 1 diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml index 4e63242f05..25a5574a39 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml @@ -50,7 +50,7 @@ Item property alias _hovered: enableSupportMouseArea.containsMouse style: UM.Theme.styles.checkbox - enabled: base.settingsEnabled + enabled: recommendedPrintSettup.settingsEnabled visible: supportEnabled.properties.enabled == "True" checked: supportEnabled.properties.value == "True" @@ -60,6 +60,7 @@ Item id: enableSupportMouseArea anchors.fill: parent hoverEnabled: true + onClicked: supportEnabled.setPropertyValue("value", supportEnabled.properties.value != "True") onEntered: @@ -86,7 +87,7 @@ Item } style: UM.Theme.styles.combobox_color - enabled: base.settingsEnabled + enabled: recommendedPrintSettup.settingsEnabled visible: enableSupportCheckBox.visible && (supportEnabled.properties.value == "True") && (extrudersEnabledCount.properties.value > 1) textRole: "text" // this solves that the combobox isn't populated in the first time Cura is started @@ -126,7 +127,7 @@ Item id: supportExtruderMouseArea anchors.fill: parent hoverEnabled: true - enabled: base.settingsEnabled + enabled: recommendedPrintSettup.settingsEnabled acceptedButtons: Qt.NoButton onEntered: { diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 0a7102ff45..270f04e70f 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -13,13 +13,11 @@ import "../Menus" Item { - id: base; + id: settingsView property QtObject settingVisibilityPresetsModel: CuraApplication.getSettingVisibilityPresetsModel() property Action configureSettings property bool findingSettings - signal showTooltip(Item item, point location, string text) - signal hideTooltip() ToolButton { @@ -359,7 +357,7 @@ Item contextMenu.provider = provider contextMenu.popup(); } - onShowTooltip: base.showTooltip(delegate, { x: -UM.Theme.getSize("default_arrow").width, y: Math.round(delegate.height / 2) }, text) + onShowTooltip: base.showTooltip(delegate, Qt.point(- UM.Theme.getSize("default_arrow").width, 0), text) onHideTooltip: base.hideTooltip() onShowAllHiddenInheritedSettings: { From 82e66eeaa1957184e8cf9b4ce1359ec47690f946 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 3 Dec 2018 09:02:41 +0100 Subject: [PATCH 0558/1240] Remove tabs and lining with single-extruder printers There's no sense in showing tabs if the user can't use them. Contributes to issue CURA-5876. --- .../qml/Menus/ConfigurationMenu/CustomConfiguration.qml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index b269b95df2..957cd16b3e 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -40,6 +40,7 @@ Item id: tabBar anchors.top: header.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height + visible: Cura.MachineManager.numberExtrudersEnabled > 1 currentIndex: Math.max(Cura.ExtruderManager.activeExtruderIndex, 0) @@ -86,8 +87,8 @@ Item height: childrenRect.height anchors.top: tabBar.bottom - radius: UM.Theme.getSize("default_radius").width - border.width: UM.Theme.getSize("default_lining").width + radius: tabBar.visible ? UM.Theme.getSize("default_radius").width : 0 + border.width: tabBar.visible ? UM.Theme.getSize("default_lining").width : 0 border.color: UM.Theme.getColor("lining") color: UM.Theme.getColor("main_background") @@ -98,6 +99,7 @@ Item height: parent.radius anchors.top: parent.top color: UM.Theme.getColor("lining") + visible: tabBar.visible Rectangle { anchors From db05d7853a3567da6bb328f676c3f89dfb74bfcc Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 3 Dec 2018 09:04:35 +0100 Subject: [PATCH 0559/1240] Remove background colour from tab window The colour is the same as what is behind it anyway. Contributes to issue CURA-5876. --- resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index 957cd16b3e..eae160b48a 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -90,7 +90,6 @@ Item radius: tabBar.visible ? UM.Theme.getSize("default_radius").width : 0 border.width: tabBar.visible ? UM.Theme.getSize("default_lining").width : 0 border.color: UM.Theme.getColor("lining") - color: UM.Theme.getColor("main_background") //Remove rounding and lining at the top. Rectangle From 90281c455b74a56fc49c45724fee3e4b3aa88f85 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 3 Dec 2018 09:45:49 +0100 Subject: [PATCH 0560/1240] Add global profile selector to the custom print setup panel Also modify some styles to adjust to the designs. Contributes to CURA-5941. --- .../Custom/CustomPrintSetup.qml | 29 +++++++++- .../Custom/GlobalProfileSelector.qml} | 53 +++++++++++-------- .../PrintSetupSelector/PrintSetupSelector.qml | 2 - .../RecommendedQualityProfileSelector.qml | 4 +- .../RecommendedSupportSelector.qml | 2 +- resources/qml/qmldir | 3 +- resources/themes/cura-light/styles.qml | 6 ++- resources/themes/cura-light/theme.json | 3 +- 8 files changed, 71 insertions(+), 31 deletions(-) rename resources/qml/{GlobalProfileButton.qml => PrintSetupSelector/Custom/GlobalProfileSelector.qml} (67%) diff --git a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml index ea56a4471e..e0f0827b17 100644 --- a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml +++ b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml @@ -4,6 +4,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.0 +import UM 1.3 as UM import Cura 1.0 as Cura @@ -14,8 +15,34 @@ Item // TODO: Hardcoded now but UX has to decide about the height of this item height: 500 + property real padding: UM.Theme.getSize("default_margin").width + + // Profile selector row + GlobalProfileSelector + { + id: globalProfileRow + anchors + { + top: parent.top + topMargin: parent.padding + left: parent.left + leftMargin: parent.padding + right: parent.right + rightMargin: parent.padding + } + } + Cura.SettingView { - anchors.fill: parent + anchors + { + top: globalProfileRow.bottom + topMargin: UM.Theme.getSize("default_margin").height + left: parent.left + leftMargin: parent.padding + right: parent.right + rightMargin: parent.padding + bottom: parent.bottom + } } } diff --git a/resources/qml/GlobalProfileButton.qml b/resources/qml/PrintSetupSelector/Custom/GlobalProfileSelector.qml similarity index 67% rename from resources/qml/GlobalProfileButton.qml rename to resources/qml/PrintSetupSelector/Custom/GlobalProfileSelector.qml index bac2732037..91525d0f9e 100644 --- a/resources/qml/GlobalProfileButton.qml +++ b/resources/qml/PrintSetupSelector/Custom/GlobalProfileSelector.qml @@ -9,24 +9,25 @@ import QtQuick.Layouts 1.2 import UM 1.2 as UM import Cura 1.0 as Cura -import "Menus" - Item { id: globalProfileRow - height: UM.Theme.getSize("print_setup_item").height + height: childrenRect.height Label { id: globalProfileLabel - text: catalog.i18nc("@label","Profile:") - textFormat: Text.PlainText - width: Math.round(parent.width * 0.45 - UM.Theme.getSize("thick_margin").width - 2) + anchors + { + top: parent.top + bottom: parent.bottom + left: parent.left + right: globalProfileSelection.left + } + text: catalog.i18nc("@label", "Profile") font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") verticalAlignment: Text.AlignVCenter - anchors.top: parent.top - anchors.bottom: parent.bottom } ToolButton @@ -34,20 +35,30 @@ Item id: globalProfileSelection text: generateActiveQualityText() - width: Math.round(parent.width * 0.55) - height: UM.Theme.getSize("setting_control").height - anchors.left: globalProfileLabel.right - anchors.right: parent.right + width: UM.Theme.getSize("print_setup_big_dropdown").width + height: UM.Theme.getSize("print_setup_big_dropdown").height + anchors + { + top: parent.top + right: parent.right + } tooltip: Cura.MachineManager.activeQualityOrQualityChangesName style: UM.Theme.styles.sidebar_header_button activeFocusOnPress: true - menu: ProfileMenu { } + menu: Cura.ProfileMenu { } - function generateActiveQualityText () { - var result = Cura.MachineManager.activeQualityOrQualityChangesName; + function generateActiveQualityText() + { + var result = Cura.MachineManager.activeQualityOrQualityChangesName + if (Cura.MachineManager.isActiveQualityExperimental) + { + result += " (Experimental)" + } - if (Cura.MachineManager.isActiveQualitySupported) { - if (Cura.MachineManager.activeQualityLayerHeight > 0) { + if (Cura.MachineManager.isActiveQualitySupported) + { + if (Cura.MachineManager.activeQualityLayerHeight > 0) + { result += " " result += " - " result += Cura.MachineManager.activeQualityLayerHeight + "mm" @@ -63,15 +74,15 @@ Item id: customisedSettings visible: Cura.MachineManager.hasUserSettings - height: Math.round(parent.height * 0.6) - width: Math.round(parent.height * 0.6) + width: UM.Theme.getSize("print_setup_icon").width + height: UM.Theme.getSize("print_setup_icon").height anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right anchors.rightMargin: Math.round(UM.Theme.getSize("setting_preferences_button_margin").width - UM.Theme.getSize("thick_margin").width) color: hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button"); - iconSource: UM.Theme.getIcon("star"); + iconSource: UM.Theme.getIcon("star") onClicked: { @@ -81,7 +92,7 @@ Item onEntered: { var content = catalog.i18nc("@tooltip","Some setting/override values are different from the values stored in the profile.\n\nClick to open the profile manager.") - base.showTooltip(globalProfileRow, Qt.point(-UM.Theme.getSize("thick_margin").width, 0), content) + base.showTooltip(globalProfileRow, Qt.point(-UM.Theme.getSize("default_margin").width, 0), content) } onExited: base.hideTooltip() } diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector/PrintSetupSelector.qml index 5518ac6b60..f1b424f7f2 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelector.qml @@ -11,8 +11,6 @@ Cura.ExpandableComponent { id: printSetupSelector - property bool hideSettings: PrintInformation.preSliced - property string enabledText: catalog.i18nc("@label:Should be short", "On") property string disabledText: catalog.i18nc("@label:Should be short", "Off") diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml index cddf01a4dc..4963f10792 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml @@ -182,7 +182,7 @@ Item id: customisedSettings visible: Cura.SimpleModeSettingsManager.isProfileCustomized || Cura.SimpleModeSettingsManager.isProfileUserCreated - height: visible ? Math.round(0.8 * qualityRowTitle.height) : 0 + height: visible ? UM.Theme.getSize("print_setup_icon").height : 0 width: height anchors { @@ -203,7 +203,7 @@ Item onEntered: { var tooltipContent = catalog.i18nc("@tooltip","You have modified some profile settings. If you want to change these go to custom mode.") - base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, customisedSettings.height), tooltipContent) + base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, 0), tooltipContent) } onExited: base.hideTooltip() } diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml index 25a5574a39..d367510ef6 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml @@ -76,7 +76,7 @@ Item { id: supportExtruderCombobox - height: UM.Theme.getSize("print_setup_support_extruder_selector").height + height: UM.Theme.getSize("print_setup_big_dropdown").height anchors { left: enableSupportCheckBox.right diff --git a/resources/qml/qmldir b/resources/qml/qmldir index c0a8bac0ae..4dc9a34894 100644 --- a/resources/qml/qmldir +++ b/resources/qml/qmldir @@ -15,4 +15,5 @@ ExpandableComponent 1.0 ExpandableComponent.qml PrinterTypeLabel 1.0 PrinterTypeLabel.qml ViewsSelector 1.0 ViewsSelector.qml ToolbarButton 1.0 ToolbarButton.qml -SettingView 1.0 SettingView.qml \ No newline at end of file +SettingView 1.0 SettingView.qml +ProfileMenu 1.0 ProfileMenu.qml \ No newline at end of file diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index 5203cb0b0b..e6144bb6ec 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -38,6 +38,7 @@ QtObject } } + radius: UM.Theme.getSize("setting_control_radius").width border.width: Theme.getSize("default_lining").width border.color: { @@ -489,11 +490,12 @@ QtObject UM.RecolorImage { id: swatch - height: Math.round(UM.Theme.getSize("setting_control").height / 2) + height: Math.round(control.height / 2) width: height anchors.right: downArrow.left anchors.verticalCenter: parent.verticalCenter - anchors.margins: Math.round(UM.Theme.getSize("default_margin").width / 4) + anchors.rightMargin: UM.Theme.getSize("default_margin").width +// anchors.margins: Math.round(UM.Theme.getSize("default_margin").width / 4) sourceSize.width: width sourceSize.height: height source: UM.Theme.getIcon("extruder_button") diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index afe40007f4..2c5c7a360a 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -377,7 +377,8 @@ "print_setup_slider_groove": [0.16, 0.16], "print_setup_slider_handle": [1.0, 1.0], "print_setup_slider_tickmarks": [0.32, 0.32], - "print_setup_support_extruder_selector": [18.2, 2.5], + "print_setup_big_dropdown": [28, 2.5], + "print_setup_icon": [1.2, 1.2], "configuration_selector_mode_tabs": [0.0, 3.0], From 8066074a2fbb71b3d124ad8966a3327298872ccb Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Mon, 3 Dec 2018 11:02:49 +0100 Subject: [PATCH 0561/1240] STAR-332: Fixing warnings --- .../NetworkedPrinterOutputDevice.py | 6 +- .../src/Cloud/CloudOutputDevice.py | 263 +++++++++--------- .../src/Cloud/CloudOutputDeviceManager.py | 2 +- .../UM3NetworkPrinting/src/Cloud/Models.py | 6 +- 4 files changed, 146 insertions(+), 131 deletions(-) diff --git a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py index 35d2ce014a..7125de4002 100644 --- a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py +++ b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py @@ -41,7 +41,8 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): self._api_prefix = "" self._address = address self._properties = properties - self._user_agent = "%s/%s " % (CuraApplication.getInstance().getApplicationName(), CuraApplication.getInstance().getVersion()) + self._user_agent = "%s/%s " % (CuraApplication.getInstance().getApplicationName(), + CuraApplication.getInstance().getVersion()) self._onFinishedCallbacks = {} # type: Dict[str, Callable[[QNetworkReply], None]] self._authentication_state = AuthState.NotAuthenticated @@ -55,7 +56,8 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): self._gcode = [] # type: List[str] self._connection_state_before_timeout = None # type: Optional[ConnectionState] - def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False, file_handler: Optional[FileHandler] = None, **kwargs: str) -> None: + def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False, + file_handler: Optional[FileHandler] = None, **kwargs: str) -> None: raise NotImplementedError("requestWrite needs to be implemented") def setAuthenticationState(self, authentication_state: AuthState) -> None: diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 5d2d140704..008633e198 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -3,20 +3,20 @@ import io import json import os -from typing import List, Optional, Dict, cast +from typing import List, Optional, Dict, cast, Union from PyQt5.QtCore import QObject, pyqtSignal, QUrl, pyqtProperty from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest from UM import i18nCatalog -from UM.FileHandler import FileWriter +from UM.FileHandler.FileWriter import FileWriter from UM.FileHandler.FileHandler import FileHandler from UM.Logger import Logger from UM.OutputDevice import OutputDeviceError from UM.Scene.SceneNode import SceneNode from UM.Version import Version from cura.CuraApplication import CuraApplication -from cura.PrinterOutput import PrinterOutputController, PrintJobOutputModel +from cura.PrinterOutput.PrinterOutputController import PrinterOutputController from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel @@ -38,7 +38,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # The cloud URL to use for this remote cluster. # TODO: Make sure that this url goes to the live api before release - ROOT_PATH= "https://api-staging.ultimaker.com" + ROOT_PATH = "https://api-staging.ultimaker.com" CLUSTER_API_ROOT = "{}/connect/v1/".format(ROOT_PATH) CURA_API_ROOT = "{}/cura/v1/".format(ROOT_PATH) CURA_DRIVE_API_ROOT = "{}/cura-drive/v1/".format(ROOT_PATH) @@ -66,10 +66,26 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self._printers = {} # type: Dict[str, PrinterOutputModel] self._print_jobs = {} # type: Dict[str, PrintJobOutputModel] self._number_of_extruders = 2 # All networked printers are dual-extrusion Ultimaker machines. - + + @staticmethod + def _parseReply(reply: QNetworkReply) -> Tuple[int, Union[None, str, bytes]]: + """ + Parses a reply from the stardust server. + :param reply: The reply received from the server. + :return: The status code and the response dict. + """ + status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) + response = None + try: + response = bytes(reply.readAll()).decode("utf-8") + response = json.loads(response) + except JSONDecodeError: + Logger.logException("w", "Unable to decode JSON from reply.") + return status_code, response + ## We need to override _createEmptyRequest to work for the cloud. def _createEmptyRequest(self, path: str, content_type: Optional[str] = "application/json") -> QNetworkRequest: - #url = QUrl(self.CLUSTER_API_ROOT_PATH_FORMAT.format(cluster_id = self._device_id) + path) + # noinspection PyArgumentList url = QUrl(path) request = QNetworkRequest(url) request.setHeader(QNetworkRequest.ContentTypeHeader, content_type) @@ -98,8 +114,9 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mime_types: bool = False, file_handler: Optional[FileHandler] = None, **kwargs: str) -> None: self.writeStarted.emit(self) + file_format = self._determineFileFormat(file_handler) - writer = self._determineWriter(file_format) + writer = self._determineWriter(file_handler, file_format) # This function pauses with the yield, waiting on instructions on which printer it needs to print with. if not writer: @@ -123,7 +140,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self._addPrintJobToQueue(stream, request) # TODO: This is yanked right out of ClusterUM3OoutputDevice, great candidate for a utility or base class - def _determineFileFormat(self, file_handler) -> None: + def _determineFileFormat(self, file_handler) -> Optional[Dict[str, Union[str, int]]]: # Formats supported by this application (file types that we can actually write). if file_handler: file_formats = file_handler.getSupportedFileTypesWrite() @@ -143,21 +160,28 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): machine_file_formats = ["application/x-ufp"] + machine_file_formats # Take the intersection between file_formats and machine_file_formats. - format_by_mimetype = {format["mime_type"]: format for format in file_formats} - file_formats = [format_by_mimetype[mimetype] for mimetype in machine_file_formats] #Keep them ordered according to the preference in machine_file_formats. + format_by_mimetype = {f["mime_type"]: f for f in file_formats} + + # Keep them ordered according to the preference in machine_file_formats. + file_formats = [format_by_mimetype[mimetype] for mimetype in machine_file_formats] if len(file_formats) == 0: Logger.log("e", "There are no file formats available to write with!") - raise OutputDeviceError.WriteRequestFailedError(self.I18N_CATALOG.i18nc("@info:status", "There are no file formats available to write with!")) + raise OutputDeviceError.WriteRequestFailedError( + self.I18N_CATALOG.i18nc("@info:status", "There are no file formats available to write with!") + ) return file_formats[0] # TODO: This is yanked right out of ClusterUM3OoutputDevice, great candidate for a utility or base class - def _determineWriter(self, file_handler, file_format): + @staticmethod + def _determineWriter(file_handler, file_format) -> Optional[FileWriter]: # Just take the first file format available. if file_handler is not None: writer = file_handler.getWriterByMimeType(cast(str, file_format["mime_type"])) else: - writer = CuraApplication.getInstance().getMeshFileHandler().getWriterByMimeType(cast(str, file_format["mime_type"])) + writer = CuraApplication.getInstance().getMeshFileHandler().getWriterByMimeType( + cast(str, file_format["mime_type"]) + ) if not writer: Logger.log("e", "Unexpected error when trying to get the FileWriter") @@ -173,7 +197,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Get remote print jobs. @pyqtProperty("QVariantList", notify = printJobsChanged) def queuedPrintJobs(self) -> List[UM3PrintJobOutputModel]: - return [print_job for print_job in self._print_jobs if print_job.state == "queued" or print_job.state == "error"] + return [print_job for print_job in self._print_jobs.values() + if print_job.state == "queued" or print_job.state == "error"] ## Called when the connection to the cluster changes. def connect(self) -> None: @@ -182,20 +207,19 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Called when the network data should be updated. def _update(self) -> None: super()._update() - self.get("{root}/cluster/{cluster_id}/status".format(self.CLUSTER_API_ROOT, self._device_id), + self.get("{root}/cluster/{cluster_id}/status".format(root=self.CLUSTER_API_ROOT, cluster_id=self._device_id), on_finished = self._onStatusCallFinished) - ## Method called when HTTP request to status endpoint is finished. # Contains both printers and print jobs statuses in a single response. def _onStatusCallFinished(self, reply: QNetworkReply) -> None: - status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) - if status_code != 200: + status_code, response = self._parseReply(reply) + if status_code > 204: Logger.log("w", "Got unexpected response while trying to get cloud cluster data: {}, {}" - .format(status_code, reply.readAll())) + .format(status_code, status, response)) return - - printers, print_jobs = self._parseStatusResponse(reply) + + printers, print_jobs = self._parseStatusResponse(response) if not printers and not print_jobs: return @@ -204,79 +228,69 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self._updatePrintJobs(print_jobs) @staticmethod - def _parseStatusResponse(reply: QNetworkReply): # Optional[(CloudClusterPrinter, CloudClusterPrintJob)] doesn't work - + def _parseStatusResponse(response: dict) -> Optional[Tuple[CloudClusterPrinter, CloudClusterPrintJob]]: printers = [] print_jobs = [] - s = '' - try: - s = json.loads(bytes(reply.readAll()).decode("utf-8")) - for p in s["printers"]: - printer = CloudClusterPrinter(**p) - configuration = printer.configuration - printer.configuration = [] - for c in configuration: - extruder = CloudClusterPrinterConfiguration(**c) - extruder.material = CloudClusterPrinterConfigurationMaterial(extruder.material) - printer.configuration.append(extruder) + data = response["data"] + for p in data["printers"]: + printer = CloudClusterPrinter(**p) + configuration = printer.configuration + printer.configuration = [] + for c in configuration: + extruder = CloudClusterPrinterConfiguration(**c) + extruder.material = CloudClusterPrinterConfigurationMaterial(material=extruder.material) + printer.configuration.append(extruder) - printers.append(printer) + printers.append(printer) - for j in s["print_jobs"]: - job = CloudClusterPrintJob(**j) - constraints = job.constraints - job.constraints = [] - for c in constraints: - job.constraints.append(CloudClusterPrintJobConstraint(**c)) + for j in data["print_jobs"]: + job = CloudClusterPrintJob(**j) + constraints = job.constraints + job.constraints = [] + for c in constraints: + job.constraints.append(CloudClusterPrintJobConstraint(**c)) - configuration = job.configuration - job.configuration = [] - for c in configuration: - configuration = CloudClusterPrinterConfiguration(**c) - configuration.material = CloudClusterPrinterConfigurationMaterial(configuration.material) - job.configuration.append(configuration) + configuration = job.configuration + job.configuration = [] + for c in configuration: + configuration = CloudClusterPrinterConfiguration(**c) + configuration.material = CloudClusterPrinterConfigurationMaterial(material=configuration.material) + job.configuration.append(configuration) - print_jobs.append(job) - - except json.decoder.JSONDecodeError: - Logger.logException("w", "Unable to decode JSON from reply.") - return None + print_jobs.append(job) return printers, print_jobs def _updatePrinters(self, printers: List[CloudClusterPrinter]) -> None: - remote_printers = {p.uuid: p for p in printers} + remote_printers = {p.uuid: p for p in printers} # type: Dict[str, CloudClusterPrinter] - removed_printers = set(self._printers.keys()).difference(set(remote_printers.keys())) - new_printers = set(remote_printers.keys()).difference(set(self._printers.keys())) - updated_printers = set(self._printers.keys()).intersection(set(remote_printers.keys())) + removed_printer_ids = set(self._printers).difference(remote_printers) + new_printer_ids = set(remote_printers).difference(self._printers) + updated_printer_ids = set(self._printers).intersection(remote_printers) - for p in removed_printers: - self._removePrinter(p) + for printer_guid in removed_printer_ids: + self._removePrinter(printer_guid) - for p in new_printers: - self._addPrinter(printers[p]) - self._updatePrinter(printers[p]) + for printer_guid in new_printer_ids: + self._addPrinter(remote_printers[printer_guid]) + self._updatePrinter(remote_printers[printer_guid]) - for p in updated_printers: - self._updatePrinter(printers[p]) + for printer_guid in updated_printer_ids: + self._updatePrinter(remote_printers[printer_guid]) # TODO: properly handle removed and updated printers self.printersChanged.emit() - - def _addPrinter(self, printer): - self._printers[printer.uuid] = self._createPrinterOutputModel(self, printer) + def _addPrinter(self, printer: CloudClusterPrinter) -> None: + self._printers[printer.uuid] = self._createPrinterOutputModel(printer) def _createPrinterOutputModel(self, printer: CloudClusterPrinter) -> PrinterOutputModel: return PrinterOutputModel(PrinterOutputController(self), len(printer.configuration), firmware_version=printer.firmware_version) - def _updatePrinter(self, guid : str, printer : CloudClusterPrinter): - self._printers[guid] = self._updatePrinterOutputModel(self, printer) - - def _updatePrinterOutputModel(self, printer: CloudClusterPrinter, model : PrinterOutputModel) -> PrinterOutputModel: + def _updatePrinter(self, printer: CloudClusterPrinter) -> None: + model = self._printers[printer.uuid] model.updateKey(printer.uuid) model.updateName(printer.friendly_name) model.updateType(printer.machine_variant) @@ -291,43 +305,42 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): extruder.updateHotendID(extruder_data.print_core_id) - material_data = extruder_data.material - if extruder.activeMaterial is None or extruder.activeMaterial.guid != material.guid: - material = self._createMaterialOutputModel(material_data) + if extruder.activeMaterial is None or extruder.activeMaterial.guid != extruder_data.material.guid: + material = self._createMaterialOutputModel(extruder_data.material) extruder.updateActiveMaterial(material) - def _createMaterialOutputModel(self, material: CloudClusterPrinterConfigurationMaterial) -> MaterialOutputModel: - material_manager = CuraApplication.getInstance().getMaterialManager() - material_group_list = material_manager.getMaterialGroupListByGUID(material.guid) or [] + @staticmethod + def _createMaterialOutputModel(material: CloudClusterPrinterConfigurationMaterial) -> MaterialOutputModel: + material_manager = CuraApplication.getInstance().getMaterialManager() + material_group_list = material_manager.getMaterialGroupListByGUID(material.guid) or [] - # Sort the material groups by "is_read_only = True" first, and then the name alphabetically. - read_only_material_group_list = list(filter(lambda x: x.is_read_only, material_group_list)) - non_read_only_material_group_list = list(filter(lambda x: not x.is_read_only, material_group_list)) - material_group = None - if read_only_material_group_list: - read_only_material_group_list = sorted(read_only_material_group_list, key=lambda x: x.name) - material_group = read_only_material_group_list[0] - elif non_read_only_material_group_list: - non_read_only_material_group_list = sorted(non_read_only_material_group_list, key=lambda x: x.name) - material_group = non_read_only_material_group_list[0] + # Sort the material groups by "is_read_only = True" first, and then the name alphabetically. + read_only_material_group_list = list(filter(lambda x: x.is_read_only, material_group_list)) + non_read_only_material_group_list = list(filter(lambda x: not x.is_read_only, material_group_list)) + material_group = None + if read_only_material_group_list: + read_only_material_group_list = sorted(read_only_material_group_list, key=lambda x: x.name) + material_group = read_only_material_group_list[0] + elif non_read_only_material_group_list: + non_read_only_material_group_list = sorted(non_read_only_material_group_list, key=lambda x: x.name) + material_group = non_read_only_material_group_list[0] - if material_group: - container = material_group.root_material_node.getContainer() - color = container.getMetaDataEntry("color_code") - brand = container.getMetaDataEntry("brand") - material_type = container.getMetaDataEntry("material") - name = container.getName() - else: - Logger.log("w", - "Unable to find material with guid {guid}. Using data as provided by cluster".format( - guid=material.guid)) - color = material.color - brand = material.brand - material_type = material.material - name = "Empty" if material.material == "empty" else "Unknown" - - return MaterialOutputModel(guid=material.guid, type=material_type, brand=brand, color=color, name=name) + if material_group: + container = material_group.root_material_node.getContainer() + color = container.getMetaDataEntry("color_code") + brand = container.getMetaDataEntry("brand") + material_type = container.getMetaDataEntry("material") + name = container.getName() + else: + Logger.log("w", + "Unable to find material with guid {guid}. Using data as provided by cluster".format( + guid=material.guid)) + color = material.color + brand = material.brand + material_type = material.material + name = "Empty" if material.material == "empty" else "Unknown" + return MaterialOutputModel(guid=material.guid, type=material_type, brand=brand, color=color, name=name) def _removePrinter(self, guid): del self._printers[guid] @@ -346,53 +359,51 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self._addPrintJob(jobs[j]) for j in updated_jobs: - self._updatePrintJob(jobs[j]) + self._updatePrintJob(remote_jobs[j]) # TODO: properly handle removed and updated printers self.printJobsChanged() - def _addPrintJob(self, job: CloudClusterPrintJob): + def _addPrintJob(self, job: CloudClusterPrintJob) -> None: self._print_jobs[job.uuid] = self._createPrintJobOutputModel(job) - def _createPrintJobOutputModel(self, job:CloudClusterPrintJob) -> PrintJobOutputModel: + def _createPrintJobOutputModel(self, job: CloudClusterPrintJob) -> PrintJobOutputModel: controller = self._printers[job.printer_uuid]._controller # TODO: Can we access this property? model = PrintJobOutputModel(controller, job.uuid, job.name) assigned_printer = self._printes[job.printer_uuid] # TODO: Or do we have to use the assigned_to field? model.updateAssignedPrinter(assigned_printer) + return model - def _updatePrintJobOutputModel(self, guid: str, job:CloudClusterPrintJob): - model =self._print_jobs[guid] + def _updatePrintJobOutputModel(self, guid: str, job: CloudClusterPrintJob) -> None: + model = self._print_jobs[guid] model.updateTimeTotal(job.time_total) model.updateTimeElapsed(job.time_elapsed) model.updateOwner(job.owner) model.updateState(job.status) - def _removePrintJob(self, guid:str): + def _removePrintJob(self, guid: str): del self._print_jobs[guid] - def _addPrintJobToQueue(self, stream, request:JobUploadRequest): + def _addPrintJobToQueue(self, stream, request: JobUploadRequest): self.put("{}/jobs/upload".format(self.CURA_API_ROOT), data = json.dumps(request.__dict__), on_finished = lambda reply: self._onAddPrintJobToQueueFinished(stream, reply)) def _onAddPrintJobToQueueFinished(self, stream, reply: QNetworkReply) -> None: - s = json.loads(bytes(reply.readAll()).decode("utf-8")) + status_code, response = self._parseReply(reply) # type: Tuple[int, dict] + if status_code > 204 or not isinstance(dict, response) or "data" not in response: + Logger.error() + return - self.put() + job_response = JobUploadResponse(**response.get("data")) + self.put(job_response.upload_url, data=stream.getvalue(), on_finished=self._onPrintJobUploaded) - # try: - # r = requests.put(self._job.output_url, data=data) - # if r.status_code == 200: - # Logger.log("d", "Finished writing %s to remote URL %s", "", self._job.output_url) - # self.onWriteSuccess.emit(r.text) - # else: - # Logger.log("d", "Error writing %s to remote URL %s", "", self._job.output_url) - # self.onWriteFailed.emit("Failed to export G-code to remote URL: {}".format(r.text)) - # except requests.ConnectionError as e: - # Logger.log("e", "There was a connection error when uploading the G-code to a remote URL: %s", e) - # self.onWriteFailed.emit("Failed to export G-code to remote URL: {}".format(e)) - # except requests.HTTPError as e: - # Logger.log("e", "There was an HTTP error when uploading the G-code to a remote URL: %s", e) - # self.onWriteFailed.emit("Failed to export G-code to remote URL: {}".format(e)) + def _onPrintJobUploaded(self, reply: QNetworkReply) -> None: + status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) + if status_code > 204: + self.onWriteFailed.emit("Failed to export G-code to remote URL: {}".format(r.text)) + Logger.logException("w", "Received unexpected response from the job upload: %s, %s.", status_code, + bytes(reply.readAll()).decode()) + return - pass \ No newline at end of file + self.onWriteSuccess.emit(r.text) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index f6542e3c76..421f24bc25 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -3,7 +3,7 @@ import json from time import sleep from threading import Thread -from typing import Dict, Optional, List +from typing import Dict, Optional from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models.py b/plugins/UM3NetworkPrinting/src/Cloud/Models.py index 86a48fb1f2..5363f49c00 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models.py @@ -1,5 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from typing import List + from ..Models import BaseModel @@ -41,7 +43,7 @@ class CloudClusterPrinterConfiguration(BaseModel): ## Class representing a cluster printer class CloudClusterPrinter(BaseModel): def __init__(self, **kwargs): - self.configuration = None # type: CloudClusterPrinterConfiguration + self.configuration = None # type: List[CloudClusterPrinterConfiguration] self.enabled = None # type: str self.firmware_version = None # type: str self.friendly_name = None # type: str @@ -56,7 +58,7 @@ class CloudClusterPrinter(BaseModel): ## Class representing a cloud cluster print job constraint class CloudClusterPrintJobConstraint(BaseModel): def __init__(self, **kwargs): - self.require_printer_name: None # type: str + self.require_printer_name = None # type: str super().__init__(**kwargs) ## Class representing a print job From f3af5a72ad7b29f3e27197cff85c43bcff0519fd Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 3 Dec 2018 11:13:26 +0100 Subject: [PATCH 0562/1240] Use ListModel.count instead of rowCount The .count property properly updates when the model is changed. Contributes to issue CURA-5876. --- cura/Settings/ExtrudersModel.py | 2 +- cura/Settings/MachineManager.py | 2 +- .../MachineSettingsAction.qml | 4 ++-- resources/qml/Dialogs/AddMachineDialog.qml | 2 +- .../ConfigurationMenu/ConfigurationMenu.qml | 2 +- resources/qml/Menus/ProfileMenu.qml | 8 +++---- resources/qml/Preferences/MachinesPage.qml | 8 ++++--- .../Preferences/Materials/MaterialsList.qml | 8 +++---- resources/qml/Preferences/ProfilesPage.qml | 24 ++++++++++++------- .../qml/Preferences/SettingVisibilityPage.qml | 2 +- .../qml/Settings/SettingOptionalExtruder.qml | 13 ++++++---- resources/qml/SidebarSimple.qml | 6 ++--- resources/qml/Toolbar.qml | 2 +- resources/qml/ViewsSelector.qml | 2 +- 14 files changed, 49 insertions(+), 36 deletions(-) diff --git a/cura/Settings/ExtrudersModel.py b/cura/Settings/ExtrudersModel.py index 955bd9dbb2..5f10ac99d4 100644 --- a/cura/Settings/ExtrudersModel.py +++ b/cura/Settings/ExtrudersModel.py @@ -165,7 +165,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): def __updateExtruders(self): extruders_changed = False - if self.rowCount() != 0: + if self.count != 0: extruders_changed = True items = [] diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index be4a4e2b4e..7e1a06f45c 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -1540,7 +1540,7 @@ class MachineManager(QObject): elif word.isdigit(): abbr_machine += word else: - stripped_word = ''.join(char for char in unicodedata.normalize('NFD', word.upper()) if unicodedata.category(char) != 'Mn') + stripped_word = "".join(char for char in unicodedata.normalize("NFD", word.upper()) if unicodedata.category(char) != "Mn") # - use only the first character if the word is too long (> 3 characters) # - use the whole word if it's not too long (<= 3 characters) if len(stripped_word) > 3: diff --git a/plugins/MachineSettingsAction/MachineSettingsAction.qml b/plugins/MachineSettingsAction/MachineSettingsAction.qml index 004b4e3cfc..c88a721a84 100644 --- a/plugins/MachineSettingsAction/MachineSettingsAction.qml +++ b/plugins/MachineSettingsAction/MachineSettingsAction.qml @@ -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 QtQuick 2.2 @@ -23,7 +23,7 @@ Cura.MachineAction target: base.extrudersModel onModelChanged: { - var extruderCount = base.extrudersModel.rowCount(); + var extruderCount = base.extrudersModel.count; base.extruderTabsCount = extruderCount; } } diff --git a/resources/qml/Dialogs/AddMachineDialog.qml b/resources/qml/Dialogs/AddMachineDialog.qml index 8b2b9d1868..bcf5807949 100644 --- a/resources/qml/Dialogs/AddMachineDialog.qml +++ b/resources/qml/Dialogs/AddMachineDialog.qml @@ -170,7 +170,7 @@ UM.Dialog if (machineList.model.getItem(machineList.currentIndex).section != section) { // Find the first machine from this section - for(var i = 0; i < machineList.model.rowCount(); i++) + for(var i = 0; i < machineList.model.count; i++) { var item = machineList.model.getItem(i); if (item.section == section) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index 202bc22ce0..9927ad1498 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -44,7 +44,7 @@ Cura.ExpandableComponent delegate: Item { height: parent.height - width: Math.round(ListView.view.width / extrudersModel.rowCount()) + width: Math.round(ListView.view.width / extrudersModel.count) // Extruder icon. Shows extruder index and has the same color as the active material. Cura.ExtruderIcon diff --git a/resources/qml/Menus/ProfileMenu.qml b/resources/qml/Menus/ProfileMenu.qml index ffd3c556b6..e09b7930a8 100644 --- a/resources/qml/Menus/ProfileMenu.qml +++ b/resources/qml/Menus/ProfileMenu.qml @@ -34,7 +34,7 @@ Menu MenuSeparator { id: customSeparator - visible: Cura.CustomQualityProfilesDropDownMenuModel.rowCount > 0 + visible: Cura.CustomQualityProfilesDropDownMenuModel.count > 0 } Instantiator @@ -45,7 +45,7 @@ Menu Connections { target: Cura.CustomQualityProfilesDropDownMenuModel - onModelReset: customSeparator.visible = Cura.CustomQualityProfilesDropDownMenuModel.rowCount() > 0 + onModelReset: customSeparator.visible = Cura.CustomQualityProfilesDropDownMenuModel.count > 0 } MenuItem @@ -59,12 +59,12 @@ Menu onObjectAdded: { - customSeparator.visible = model.rowCount() > 0; + customSeparator.visible = model.count > 0; menu.insertItem(index, object); } onObjectRemoved: { - customSeparator.visible = model.rowCount() > 0; + customSeparator.visible = model.count > 0; menu.removeItem(object); } } diff --git a/resources/qml/Preferences/MachinesPage.qml b/resources/qml/Preferences/MachinesPage.qml index 4dc5465dc6..bc75b9bc72 100644 --- a/resources/qml/Preferences/MachinesPage.qml +++ b/resources/qml/Preferences/MachinesPage.qml @@ -21,8 +21,10 @@ UM.ManagementPage function activeMachineIndex() { - for(var i = 0; i < model.rowCount(); i++) { - if (model.getItem(i).id == Cura.MachineManager.activeMachineId) { + for(var i = 0; i < model.count; i++) + { + if (model.getItem(i).id == Cura.MachineManager.activeMachineId) + { return i; } } @@ -47,7 +49,7 @@ UM.ManagementPage { text: catalog.i18nc("@action:button", "Remove"); iconName: "list-remove"; - enabled: base.currentItem != null && model.rowCount() > 1 + enabled: base.currentItem != null && model.count > 1 onClicked: confirmDialog.open(); }, Button diff --git a/resources/qml/Preferences/Materials/MaterialsList.qml b/resources/qml/Preferences/Materials/MaterialsList.qml index 00bead9650..61f92db84c 100644 --- a/resources/qml/Preferences/Materials/MaterialsList.qml +++ b/resources/qml/Preferences/Materials/MaterialsList.qml @@ -57,7 +57,7 @@ Item var currentItemId = base.currentItem == null ? "" : base.currentItem.root_material_id search_root_id = currentItemId } - for (var material_idx = 0; material_idx < genericMaterialsModel.rowCount(); material_idx++) + for (var material_idx = 0; material_idx < genericMaterialsModel.count; material_idx++) { var material = genericMaterialsModel.getItem(material_idx) if (material.root_material_id == search_root_id) @@ -72,15 +72,15 @@ Item return true } } - for (var brand_idx = 0; brand_idx < materialsModel.rowCount(); brand_idx++) + for (var brand_idx = 0; brand_idx < materialsModel.count; brand_idx++) { var brand = materialsModel.getItem(brand_idx) var types_model = brand.material_types - for (var type_idx = 0; type_idx < types_model.rowCount(); type_idx++) + for (var type_idx = 0; type_idx < types_model.count; type_idx++) { var type = types_model.getItem(type_idx) var colors_model = type.colors - for (var material_idx = 0; material_idx < colors_model.rowCount(); material_idx++) + for (var material_idx = 0; material_idx < colors_model.count; material_idx++) { var material = colors_model.getItem(material_idx) if (material.root_material_id == search_root_id) diff --git a/resources/qml/Preferences/ProfilesPage.qml b/resources/qml/Preferences/ProfilesPage.qml index ba0c2848a5..d7ffbb3152 100644 --- a/resources/qml/Preferences/ProfilesPage.qml +++ b/resources/qml/Preferences/ProfilesPage.qml @@ -188,21 +188,27 @@ Item Connections { target: qualitiesModel - onItemsChanged: { + onItemsChanged: + { var toSelectItemName = base.currentItem == null ? "" : base.currentItem.name; - if (newQualityNameToSelect != "") { + if (newQualityNameToSelect != "") + { toSelectItemName = newQualityNameToSelect; } var newIdx = -1; // Default to nothing if nothing can be found - if (toSelectItemName != "") { + if (toSelectItemName != "") + { // Select the required quality name if given - for (var idx = 0; idx < qualitiesModel.rowCount(); ++idx) { + for (var idx = 0; idx < qualitiesModel.count; ++idx) + { var item = qualitiesModel.getItem(idx); - if (item.name == toSelectItemName) { + if (item.name == toSelectItemName) + { // Switch to the newly created profile if needed newIdx = idx; - if (base.toActivateNewQuality) { + if (base.toActivateNewQuality) + { // Activate this custom quality if required Cura.MachineManager.setQualityChangesGroup(item.quality_changes_group); } @@ -382,9 +388,11 @@ Item var selectedItemName = Cura.MachineManager.activeQualityOrQualityChangesName; // Select the required quality name if given - for (var idx = 0; idx < qualitiesModel.rowCount(); idx++) { + for (var idx = 0; idx < qualitiesModel.count; idx++) + { var item = qualitiesModel.getItem(idx); - if (item.name == selectedItemName) { + if (item.name == selectedItemName) + { currentIndex = idx; break; } diff --git a/resources/qml/Preferences/SettingVisibilityPage.qml b/resources/qml/Preferences/SettingVisibilityPage.qml index 8896d0611e..bb47301b69 100644 --- a/resources/qml/Preferences/SettingVisibilityPage.qml +++ b/resources/qml/Preferences/SettingVisibilityPage.qml @@ -54,7 +54,7 @@ UM.PreferencesPage { return Qt.Unchecked } - else if(definitionsModel.visibleCount == definitionsModel.rowCount(null)) + else if(definitionsModel.visibleCount == definitionsModel.count) { return Qt.Checked } diff --git a/resources/qml/Settings/SettingOptionalExtruder.qml b/resources/qml/Settings/SettingOptionalExtruder.qml index a3c1422b30..5f0d8327f8 100644 --- a/resources/qml/Settings/SettingOptionalExtruder.qml +++ b/resources/qml/Settings/SettingOptionalExtruder.qml @@ -1,5 +1,5 @@ -// Copyright (c) 2016 Ultimaker B.V. -// Uranium is released under the terms of the LGPLv3 or higher. +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.7 import QtQuick.Controls 2.0 @@ -31,12 +31,15 @@ SettingItem { forceActiveFocus(); propertyProvider.setPropertyValue("value", model.getItem(index).index); - } else + } + else { if (propertyProvider.properties.value == -1) { - control.currentIndex = model.rowCount() - 1; // we know the last item is "Not overriden" - } else { + control.currentIndex = model.count - 1; // we know the last item is "Not overriden" + } + else + { control.currentIndex = propertyProvider.properties.value; // revert to the old value } } diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index 5e723a3d70..e07810691e 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -106,7 +106,7 @@ Item var availableMin = -1 var availableMax = -1 - for (var i = 0; i < Cura.QualityProfilesDropDownMenuModel.rowCount(); i++) + for (var i = 0; i < Cura.QualityProfilesDropDownMenuModel.count; i++) { var qualityItem = Cura.QualityProfilesDropDownMenuModel.getItem(i) @@ -183,7 +183,7 @@ Item qualityModel.existingQualityProfile = 0 // check, the ticks count cannot be less than zero - qualityModel.totalTicks = Math.max(0, Cura.QualityProfilesDropDownMenuModel.rowCount() - 1) + qualityModel.totalTicks = Math.max(0, Cura.QualityProfilesDropDownMenuModel.count - 1) } } @@ -1156,7 +1156,7 @@ Item function populateExtruderModel() { extruderModel.clear(); - for(var extruderNumber = 0; extruderNumber < extruders.rowCount() ; extruderNumber++) + for(var extruderNumber = 0; extruderNumber < extruders.count; extruderNumber++) { extruderModel.append({ text: extruders.getItem(extruderNumber).name, diff --git a/resources/qml/Toolbar.qml b/resources/qml/Toolbar.qml index 0240aaab26..4e00bd7248 100644 --- a/resources/qml/Toolbar.qml +++ b/resources/qml/Toolbar.qml @@ -65,7 +65,7 @@ Item style: UM.Theme.styles.toolbar_button property bool isFirstElement: toolsModel.getItem(0).id == model.id - property bool isLastElement: toolsModel.getItem(toolsModel.rowCount() - 1).id == model.id + property bool isLastElement: toolsModel.getItem(toolsModel.count - 1).id == model.id onCheckedChanged: { diff --git a/resources/qml/ViewsSelector.qml b/resources/qml/ViewsSelector.qml index e9fdd57177..e239ea82a0 100644 --- a/resources/qml/ViewsSelector.qml +++ b/resources/qml/ViewsSelector.qml @@ -19,7 +19,7 @@ Cura.ExpandableComponent property var activeView: { - for (var i = 0; i < viewModel.rowCount(); i++) + for (var i = 0; i < viewModel.count; i++) { if (viewModel.items[i].active) { From 6012ea0b9edf78369bec7abc463869f226e26b23 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 3 Dec 2018 11:15:11 +0100 Subject: [PATCH 0563/1240] Don't use QtQuick imports from Qt 5.11 Our build system uses 5.10. Contributes to issue CURA-5876. --- resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml | 2 +- resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml index c40eb5b9e9..32be97f74e 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml @@ -2,7 +2,7 @@ // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.7 -import QtQuick.Controls 2.4 +import QtQuick.Controls 2.3 import UM 1.2 as UM import Cura 1.0 as Cura diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index 9927ad1498..816ac27470 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -4,7 +4,6 @@ import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Controls.Styles 1.4 -import QtQuick.Layouts 1.11 import UM 1.2 as UM import Cura 1.0 as Cura From af747ae09c254327dd5d85e90a2cea86cc5d5562 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 3 Dec 2018 11:19:25 +0100 Subject: [PATCH 0564/1240] Don't hide tab bar when disabling all but one extruder And make sure the checkbox for enabling also disappears. We don't want to make this dependent on the number of enabled extruders, but on the total number of extruders. This way you can actually re-enable an extruder again because the tab bar shouldn't disappear. Contributes to issue CURA-5876. --- resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index eae160b48a..7ff47ea16f 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -40,7 +40,7 @@ Item id: tabBar anchors.top: header.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height - visible: Cura.MachineManager.numberExtrudersEnabled > 1 + visible: extrudersModel.count > 1 currentIndex: Math.max(Cura.ExtruderManager.activeExtruderIndex, 0) @@ -138,6 +138,7 @@ Item color: UM.Theme.getColor("text") height: parent.height width: selectors.textWidth + visible: extrudersModel.count > 1 } OldControls.CheckBox @@ -146,6 +147,7 @@ Item enabled: !checked || Cura.MachineManager.numberExtrudersEnabled > 1 //Disable if it's the last enabled extruder. height: UM.Theme.getSize("setting_control").height style: UM.Theme.styles.checkbox + visible: extrudersModel.count > 1 /* Use a MouseArea to process the click on this checkbox. This is necessary because actually clicking the checkbox From 39411222da20db6a39f1341e6d42c9610c88c85a Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 3 Dec 2018 11:37:22 +0100 Subject: [PATCH 0565/1240] Use normal font size for Auto slicing... message It should've been the normal font size anyway. This is not small! Contributes to issue CURA-5876. --- resources/qml/ActionPanel/SliceProcessWidget.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index cae598fea6..dee099bc88 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -48,7 +48,7 @@ Column text: catalog.i18nc("@label:PrintjobStatus", "Auto slicing...") color: UM.Theme.getColor("text") - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") renderType: Text.NativeRendering } From c8cb3a094a216eac21b139ba6577541ea53046d1 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 3 Dec 2018 11:52:10 +0100 Subject: [PATCH 0566/1240] Re-use SecondaryButton instead of setting colours ourselves It has a pre-defined theme. Contributes to issue CURA-5876. --- .../Menus/ConfigurationMenu/ConfigurationMenu.qml | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index 816ac27470..fc8b31f125 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -147,7 +147,7 @@ Cura.ExpandableComponent width: parent.width height: childrenRect.height - Cura.ActionButton + Cura.SecondaryButton { id: goToCustom visible: popupItem.configuration_method === "auto" @@ -159,18 +159,13 @@ Cura.ExpandableComponent top: parent.top } - color: UM.Theme.getColor("secondary") - hoverColor: UM.Theme.getColor("secondary") - textColor: UM.Theme.getColor("primary") - textHoverColor: UM.Theme.getColor("text") - iconSource: UM.Theme.getIcon("arrow_right") iconOnRightSide: true onClicked: popupItem.configuration_method = "custom" } - Cura.ActionButton + Cura.SecondaryButton { id: goToAuto visible: popupItem.configuration_method === "custom" @@ -182,11 +177,6 @@ Cura.ExpandableComponent top: parent.top } - color: UM.Theme.getColor("secondary") - hoverColor: UM.Theme.getColor("secondary") - textColor: UM.Theme.getColor("primary") - textHoverColor: UM.Theme.getColor("text") - iconSource: UM.Theme.getIcon("arrow_left") onClicked: popupItem.configuration_method = "auto" From 65d3aa44802c740d06c19ebbc58a2e9f2732ee80 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 3 Dec 2018 11:59:03 +0100 Subject: [PATCH 0567/1240] Fix broken sizes due to merged theme entry Merge conflict went wrong, I think. Contributes to issue CURA-5876. --- resources/qml/ActionPanel/OutputProcessWidget.qml | 2 +- resources/qml/ActionPanel/SliceProcessWidget.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml index 3a85a13810..8ce5d13f17 100644 --- a/resources/qml/ActionPanel/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -111,7 +111,7 @@ Column { id: previewStageShortcut - height: UM.Theme.getSize("action_panel_button").height + height: UM.Theme.getSize("action_button").height text: catalog.i18nc("@button", "Preview") onClicked: UM.Controller.setActiveStage("PreviewStage") diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index dee099bc88..1143bb4c1a 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -101,7 +101,7 @@ Column // Disable the slice process when width: parent.width - height: UM.Theme.getSize("action_panel_button").height + height: UM.Theme.getSize("action_button").height visible: !autoSlice Cura.PrimaryButton { From 665e2d3060be1392695a89652e19015cd01c036a Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Mon, 3 Dec 2018 12:15:25 +0100 Subject: [PATCH 0568/1240] Move isActive and timeRemaining logic from QML to Python Contributes to CL-1153 --- cura/PrinterOutput/PrintJobOutputModel.py | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/cura/PrinterOutput/PrintJobOutputModel.py b/cura/PrinterOutput/PrintJobOutputModel.py index 25b168e6fd..c3e6b7d267 100644 --- a/cura/PrinterOutput/PrintJobOutputModel.py +++ b/cura/PrinterOutput/PrintJobOutputModel.py @@ -125,10 +125,34 @@ class PrintJobOutputModel(QObject): def timeElapsed(self): return self._time_elapsed + @pyqtProperty(int, notify = timeElapsedChanged) + def timeRemaining(self) -> int: + # Never get a negative time remaining + return max(self.timeTotal - self.timeElapsed, 0) + + @pyqtProperty(float, notify = timeElapsedChanged) + def progress(self) -> float: + result = self.timeElapsed / self.timeTotal + if result > 1.0: + result = 1.0 + return result + @pyqtProperty(str, notify=stateChanged) def state(self): return self._state + @pyqtProperty(bool, notify=stateChanged) + def isActive(self) -> bool: + inactiveStates = [ + "pausing", + "paused", + "resuming", + "wait_cleanup" + ] + if self.state in inactiveStates and self.timeRemaining > 0: + return False + return True + def updateTimeTotal(self, new_time_total): if self._time_total != new_time_total: self._time_total = new_time_total From 3d80e281743a34681fbb643239253dbab852d598 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Mon, 3 Dec 2018 12:15:38 +0100 Subject: [PATCH 0569/1240] Add some typings Contributes to CL-1153 --- cura/PrinterOutput/PrintJobOutputModel.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cura/PrinterOutput/PrintJobOutputModel.py b/cura/PrinterOutput/PrintJobOutputModel.py index c3e6b7d267..604fd8e0b8 100644 --- a/cura/PrinterOutput/PrintJobOutputModel.py +++ b/cura/PrinterOutput/PrintJobOutputModel.py @@ -118,11 +118,11 @@ class PrintJobOutputModel(QObject): self.nameChanged.emit() @pyqtProperty(int, notify = timeTotalChanged) - def timeTotal(self): + def timeTotal(self) -> int: return self._time_total @pyqtProperty(int, notify = timeElapsedChanged) - def timeElapsed(self): + def timeElapsed(self) -> int: return self._time_elapsed @pyqtProperty(int, notify = timeElapsedChanged) @@ -138,7 +138,7 @@ class PrintJobOutputModel(QObject): return result @pyqtProperty(str, notify=stateChanged) - def state(self): + def state(self) -> str: return self._state @pyqtProperty(bool, notify=stateChanged) From a28cae0a43ff5e6e3c8e4a5912f0935e7978b2de Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Mon, 3 Dec 2018 12:18:33 +0100 Subject: [PATCH 0570/1240] Improve date rendering - Use "Mon Dec 3 at 12:39" if 7 days or more away. - Use "Mon at 12:39" if within 7 days but more than one away. - Use "tomorrow at 12:39" if one day away. - Use "today at 12:39" if today. Contributes to CL-1153 --- .../src/ClusterUM3OutputDevice.py | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index 3b124faf66..292011929d 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -386,8 +386,24 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): @pyqtSlot(int, result = str) 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() + completed = datetime.fromtimestamp(current_time + time_remaining) + today = datetime.fromtimestamp(current_time) + + # If finishing date is more than 7 days out, using "Mon Dec 3 at HH:MM" format + if completed.toordinal() > today.toordinal() + 7: + return completed.strftime("%a %b ") + "{day}".format(day=completed.day) + + # If finishing date is within the next week, use "Monday at HH:MM" format + elif completed.toordinal() > today.toordinal() + 1: + return completed.strftime("%a") + + # If finishing tomorrow, use "tomorrow at HH:MM" format + elif completed.toordinal() > today.toordinal(): + return "tomorrow" + + # If finishing today, use "today at HH:MM" format + else: + return "today" @pyqtSlot(str) def sendJobToTop(self, print_job_uuid: str) -> None: From 4c468831f06fcc33de6b657985aed22907c2796c Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 3 Dec 2018 13:58:13 +0100 Subject: [PATCH 0571/1240] Add tabs for select the extruder in the custom print setup. Align some elements beneath the tabs. Contributes to CURA-5941. --- .../Custom/CustomPrintSetup.qml | 63 +++++- .../Custom/GlobalProfileSelector.qml | 4 +- .../Recommended/RecommendedPrintSetup.qml | 9 + .../RecommendedSupportSelector.qml | 11 +- resources/qml/Settings/SettingView.qml | 186 +++++++++--------- resources/themes/cura-light/styles.qml | 2 +- resources/themes/cura-light/theme.json | 4 +- 7 files changed, 164 insertions(+), 115 deletions(-) diff --git a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml index e0f0827b17..ae31976a34 100644 --- a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml +++ b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml @@ -13,9 +13,15 @@ Item id: customPrintSetup // TODO: Hardcoded now but UX has to decide about the height of this item - height: 500 + height: 480 property real padding: UM.Theme.getSize("default_margin").width + property bool multipleExtruders: extrudersModel.count > 1 + + Cura.ExtrudersModel + { + id: extrudersModel + } // Profile selector row GlobalProfileSelector @@ -32,11 +38,64 @@ Item } } + UM.TabRow + { + id: tabBar + + visible: multipleExtruders // The tab row is only visible when there are more than 1 extruder + + anchors + { + top: globalProfileRow.bottom + topMargin: UM.Theme.getSize("default_margin").height + left: parent.left + leftMargin: parent.padding + right: parent.right + rightMargin: parent.padding + } + + currentIndex: Math.max(Cura.ExtruderManager.activeExtruderIndex, 0) + + Repeater + { + id: repeater + model: extrudersModel + delegate: UM.TabRowButton + { + contentItem: Item + { + Cura.ExtruderIcon + { + anchors.horizontalCenter: parent.horizontalCenter + materialColor: model.color + extruderEnabled: model.enabled + } + } + onClicked: + { + Cura.ExtruderManager.setActiveExtruderIndex(tabBar.currentIndex) + } + } + } + + // When the model of the extruders is rebuilt, the list of extruders is briefly emptied and rebuilt. + // This causes the currentIndex of the tab to be in an invalid position which resets it to 0. + // Therefore we need to change it back to what it was: The active extruder index. + Connections + { + target: repeater.model + onModelChanged: + { + tabBar.currentIndex = Math.max(Cura.ExtruderManager.activeExtruderIndex, 0) + } + } + } + Cura.SettingView { anchors { - top: globalProfileRow.bottom + top: tabBar.visible ? tabBar.bottom : globalProfileRow.bottom topMargin: UM.Theme.getSize("default_margin").height left: parent.left leftMargin: parent.padding diff --git a/resources/qml/PrintSetupSelector/Custom/GlobalProfileSelector.qml b/resources/qml/PrintSetupSelector/Custom/GlobalProfileSelector.qml index 91525d0f9e..11b2d3608b 100644 --- a/resources/qml/PrintSetupSelector/Custom/GlobalProfileSelector.qml +++ b/resources/qml/PrintSetupSelector/Custom/GlobalProfileSelector.qml @@ -35,8 +35,8 @@ Item id: globalProfileSelection text: generateActiveQualityText() - width: UM.Theme.getSize("print_setup_big_dropdown").width - height: UM.Theme.getSize("print_setup_big_dropdown").height + width: UM.Theme.getSize("print_setup_big_item").width + height: UM.Theme.getSize("print_setup_big_item").height anchors { top: parent.top diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml index 194747271e..618c519d31 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml @@ -69,4 +69,13 @@ Item labelColumnWidth: parent.firstColumnWidth } } + + UM.SettingPropertyProvider + { + id: extrudersEnabledCount + containerStack: Cura.MachineManager.activeMachine + key: "extruders_enabled_count" + watchedProperties: [ "value" ] + storeIndex: 0 + } } diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml index d367510ef6..ce4aa6c195 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml @@ -76,7 +76,7 @@ Item { id: supportExtruderCombobox - height: UM.Theme.getSize("print_setup_big_dropdown").height + height: UM.Theme.getSize("print_setup_big_item").height anchors { left: enableSupportCheckBox.right @@ -171,15 +171,6 @@ Item storeIndex: 0 } - UM.SettingPropertyProvider - { - id: extrudersEnabledCount - containerStack: Cura.MachineManager.activeMachine - key: "extruders_enabled_count" - watchedProperties: [ "value" ] - storeIndex: 0 - } - UM.SettingPropertyProvider { id: supportExtruderNr diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 2c42d222ce..3250e847a3 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -19,70 +19,22 @@ Item property Action configureSettings property bool findingSettings - ToolButton - { - id: settingVisibilityMenu - - property var toolButtonIconColor: UM.Theme.getColor("setting_category_text") - - width: height - height: UM.Theme.getSize("setting_control").height - anchors - { - topMargin: UM.Theme.getSize("thick_margin").height - left: filterContainer.right - leftMargin: UM.Theme.getSize("default_margin").width - } - style: ButtonStyle - { - background: Item { - UM.RecolorImage { - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter - width: Math.round(parent.width * 0.6) - height: Math.round(parent.height * 0.6) - sourceSize.width: width - sourceSize.height: width - color: settingVisibilityMenu.toolButtonIconColor - source: UM.Theme.getIcon("settings") - } - } - label: Label{} - } - menu: SettingVisibilityPresetsMenu - { - onShowAllSettings: - { - definitionsModel.setAllVisible(true); - filter.updateDefinitionModel(); - } - } - - MouseArea - { - anchors.fill: parent - hoverEnabled: true - acceptedButtons: Qt.RightButton - onEntered: settingVisibilityMenu.toolButtonIconColor = UM.Theme.getColor("setting_control_button_hover") - onExited: settingVisibilityMenu.toolButtonIconColor = UM.Theme.getColor("setting_category_text") - } - } - Rectangle { id: filterContainer visible: true + radius: UM.Theme.getSize("setting_control_radius").width border.width: Math.round(UM.Theme.getSize("default_lining").width) border.color: { - if(hoverMouseArea.containsMouse || clearFilterButton.containsMouse) + if (hoverMouseArea.containsMouse || clearFilterButton.containsMouse) { - return UM.Theme.getColor("setting_control_border_highlight"); + return UM.Theme.getColor("setting_control_border_highlight") } else { - return UM.Theme.getColor("setting_control_border"); + return UM.Theme.getColor("setting_control_border") } } @@ -90,13 +42,12 @@ Item anchors { - topMargin: UM.Theme.getSize("thick_margin").height + top: parent.top left: parent.left - leftMargin: UM.Theme.getSize("default_margin").width - right: scrollView.right - rightMargin: Math.floor(UM.Theme.getSize("wide_margin").width * 2) + right: settingVisibilityMenu.left + rightMargin: UM.Theme.getSize("default_margin").width } - height: UM.Theme.getSize("setting_control").height + height: UM.Theme.getSize("print_setup_big_item").height Timer { id: settingsSearchTimer @@ -108,7 +59,7 @@ Item TextField { - id: filter; + id: filter height: parent.height anchors.left: parent.left anchors.right: clearFilterButton.left @@ -118,9 +69,9 @@ Item style: TextFieldStyle { - textColor: UM.Theme.getColor("setting_control_text"); + textColor: UM.Theme.getColor("setting_control_text") placeholderTextColor: UM.Theme.getColor("setting_filter_field") - font: UM.Theme.getFont("default"); + font: UM.Theme.getFont("default_italic") background: Item {} } @@ -134,38 +85,38 @@ Item onEditingFinished: { - definitionsModel.filter = {"i18n_label": "*" + text}; - findingSettings = (text.length > 0); - if(findingSettings != lastFindingSettings) + definitionsModel.filter = {"i18n_label": "*" + text} + findingSettings = (text.length > 0) + if (findingSettings != lastFindingSettings) { - updateDefinitionModel(); - lastFindingSettings = findingSettings; + updateDefinitionModel() + lastFindingSettings = findingSettings } } Keys.onEscapePressed: { - filter.text = ""; + filter.text = "" } function updateDefinitionModel() { - if(findingSettings) + if (findingSettings) { - expandedCategories = definitionsModel.expanded.slice(); - definitionsModel.expanded = [""]; // keep categories closed while to prevent render while making settings visible one by one - definitionsModel.showAncestors = true; - definitionsModel.showAll = true; - definitionsModel.expanded = ["*"]; + expandedCategories = definitionsModel.expanded.slice() + 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 { - if(expandedCategories) + if (expandedCategories) { - definitionsModel.expanded = expandedCategories; + definitionsModel.expanded = expandedCategories } - definitionsModel.showAncestors = false; - definitionsModel.showAll = false; + definitionsModel.showAncestors = false + definitionsModel.showAll = false } } } @@ -197,8 +148,45 @@ Item onClicked: { - filter.text = ""; - filter.forceActiveFocus(); + filter.text = "" + filter.forceActiveFocus() + } + } + } + + ToolButton + { + id: settingVisibilityMenu + + anchors + { + top: filterContainer.top + bottom: filterContainer.bottom + right: parent.right + } + + style: ButtonStyle + { + background: Item { + 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: height + color: control.enabled ? UM.Theme.getColor("setting_category_text") : UM.Theme.getColor("setting_category_disabled_text") + source: UM.Theme.getIcon("menu") + } + } + label: Label{} + } + menu: SettingVisibilityPresetsMenu + { + onShowAllSettings: + { + definitionsModel.setAllVisible(true) + filter.updateDefinitionModel() } } } @@ -206,33 +194,35 @@ Item ScrollView { id: scrollView - anchors.top: filterContainer.bottom; - anchors.bottom: parent.bottom; - anchors.right: parent.right; - anchors.left: parent.left; - anchors.topMargin: UM.Theme.getSize("thick_margin").height - anchors.rightMargin: UM.Theme.getSize("narrow_margin").height / 3 + anchors + { + top: filterContainer.bottom + topMargin: UM.Theme.getSize("default_margin").height + bottom: parent.bottom + right: parent.right + left: parent.left + } - style: UM.Theme.styles.scrollview; - flickableItem.flickableDirection: Flickable.VerticalFlick; - __wheelAreaScrollSpeed: 75; // Scroll three lines in one scroll event + style: UM.Theme.styles.scrollview + flickableItem.flickableDirection: Flickable.VerticalFlick + __wheelAreaScrollSpeed: 75 // Scroll three lines in one scroll event ListView { id: contents - spacing: Math.round(UM.Theme.getSize("default_lining").height); - cacheBuffer: 1000000; // Set a large cache to effectively just cache every list item. + spacing: Math.round(UM.Theme.getSize("default_lining").height) + cacheBuffer: 1000000 // Set a large cache to effectively just cache every list item. model: UM.SettingDefinitionsModel { - id: definitionsModel; + id: definitionsModel containerId: Cura.MachineManager.activeDefinitionId visibilityHandler: UM.SettingPreferenceVisibilityHandler { } exclude: ["machine_settings", "command_line_settings", "infill_mesh", "infill_mesh_order", "cutting_mesh", "support_mesh", "anti_overhang_mesh"] // TODO: infill_mesh settigns are excluded hardcoded, but should be based on the fact that settable_globally, settable_per_meshgroup and settable_per_extruder are false. expanded: CuraApplication.expandedCategories onExpandedChanged: { - if(!findingSettings) + if (!findingSettings) { // Do not change expandedCategories preference while filtering settings // because all categories are expanded while filtering @@ -248,7 +238,7 @@ Item { id: delegate - width: Math.round(UM.Theme.getSize("print_setup_widget").width); + width: scrollView.width height: provider.properties.enabled == "True" ? UM.Theme.getSize("section").height : - contents.spacing Behavior on height { NumberAnimation { duration: 100 } } opacity: provider.properties.enabled == "True" ? 1 : 0 @@ -318,17 +308,17 @@ Item // machine gets changed. var activeMachineId = Cura.MachineManager.activeMachineId; - if(!model.settable_per_extruder) + if (!model.settable_per_extruder) { //Not settable per extruder or there only is global, so we must pick global. return activeMachineId; } - if(inheritStackProvider.properties.limit_to_extruder != null && inheritStackProvider.properties.limit_to_extruder >= 0) + if (inheritStackProvider.properties.limit_to_extruder != null && inheritStackProvider.properties.limit_to_extruder >= 0) { //We have limit_to_extruder, so pick that stack. return Cura.ExtruderManager.extruderIds[String(inheritStackProvider.properties.limit_to_extruder)]; } - if(Cura.ExtruderManager.activeExtruderStackId) + if (Cura.ExtruderManager.activeExtruderStackId) { //We're on an extruder tab. Pick the current extruder. return Cura.ExtruderManager.activeExtruderStackId; @@ -390,14 +380,14 @@ Item } onSetActiveFocusToNextSetting: { - if(forward == undefined || forward) + if (forward == undefined || forward) { contents.currentIndex = contents.indexWithFocus + 1; while(contents.currentItem && contents.currentItem.height <= 0) { contents.currentIndex++; } - if(contents.currentItem) + if (contents.currentItem) { contents.currentItem.item.focusItem.forceActiveFocus(); } @@ -409,7 +399,7 @@ Item { contents.currentIndex--; } - if(contents.currentItem) + if (contents.currentItem) { contents.currentItem.item.focusItem.forceActiveFocus(); } diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index e6144bb6ec..ca95b6d373 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -495,7 +495,7 @@ QtObject anchors.right: downArrow.left anchors.verticalCenter: parent.verticalCenter anchors.rightMargin: UM.Theme.getSize("default_margin").width -// anchors.margins: Math.round(UM.Theme.getSize("default_margin").width / 4) + sourceSize.width: width sourceSize.height: height source: UM.Theme.getIcon("extruder_button") diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 2c5c7a360a..5af77d59c3 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -377,7 +377,7 @@ "print_setup_slider_groove": [0.16, 0.16], "print_setup_slider_handle": [1.0, 1.0], "print_setup_slider_tickmarks": [0.32, 0.32], - "print_setup_big_dropdown": [28, 2.5], + "print_setup_big_item": [28, 2.5], "print_setup_icon": [1.2, 1.2], "configuration_selector_mode_tabs": [0.0, 3.0], @@ -425,7 +425,7 @@ "setting_text_maxwidth": [40.0, 0.0], "standard_list_lineheight": [1.5, 1.5], - "standard_arrow": [0.8, 0.8], + "standard_arrow": [1.0, 1.0], "button": [4, 4], "button_icon": [2.5, 2.5], From c9ed044205ed92236dc344a6c2683b2e262f1826 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Mon, 3 Dec 2018 14:41:22 +0100 Subject: [PATCH 0572/1240] Improve printer status and progress bar Contributes to CL-1153 --- .../resources/qml/MonitorPrintJobPreview.qml | 38 ++++- .../qml/MonitorPrintJobProgressBar.qml | 136 ++++++++---------- .../resources/qml/MonitorPrinterCard.qml | 11 +- 3 files changed, 99 insertions(+), 86 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml index 1a69d2dc12..7ac2a1d8de 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml @@ -21,7 +21,18 @@ Item { id: previewImage anchors.fill: parent - opacity: printJob && printJob.state == "error" ? 0.5 : 1.0 + opacity: + { + if (!printJob) + { + return 0 + } + if (printJob.state == "error" || !printJob.isActive) + { + return 0.5 + } + return 1.0 + } source: printJob ? printJob.previewImageUrl : "" visible: printJob } @@ -47,11 +58,32 @@ Item UM.RecolorImage { - id: statusImage + id: overlayIcon anchors.centerIn: printJobPreview color: UM.Theme.getColor("monitor_image_overlay") height: 0.5 * printJobPreview.height - source: printJob && printJob.state == "error" ? "../svg/aborted-icon.svg" : "" + source: + { + switch(printJob.state) + { + case "error": + return "../svg/aborted-icon.svg" + case "wait_cleanup": + if (printJob.state == "wait_cleanup" && printJob.timeTotal > printJob.timeElapsed) + { + return "../svg/aborted-icon.svg" + } + break; + case "pausing": + return "../svg/paused-icon.svg" + case "paused": + return "../svg/paused-icon.svg" + case "resuming": + return "../svg/paused-icon.svg" + default: + return "" + } + } sourceSize { height: height diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml index f70e1175a1..a7055f4c52 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml @@ -15,63 +15,10 @@ import UM 1.3 as UM Item { id: base + + // The print job which all other information is dervied from property var printJob: null - property var progress: - { - if (!printJob) - { - return 0 - } - var result = printJob.timeElapsed / printJob.timeTotal - if (result > 1.0) - { - result = 1.0 - } - return result - } - property var remainingTime: - { - if (!printJob) { - return 0 - } - /* Sometimes total minus elapsed is less than 0. Use Math.max() to prevent remaining - time from ever being less than 0. Negative durations cause strange behavior such - as displaying "-1h -1m". */ - return Math.max(printer.activePrintJob.timeTotal - printer.activePrintJob.timeElapsed, 0) - } - property var progressText: - { - if (!printJob) - { - return ""; - } - switch (printJob.state) - { - case "wait_cleanup": - if (printJob.timeTotal > printJob.timeElapsed) - { - return catalog.i18nc("@label:status", "Aborted") - } - return catalog.i18nc("@label:status", "Finished") - case "pre_print": - case "sent_to_printer": - return catalog.i18nc("@label:status", "Preparing") - case "aborted": - return catalog.i18nc("@label:status", "Aborted") - case "wait_user_action": - return catalog.i18nc("@label:status", "Aborted") - case "pausing": - return catalog.i18nc("@label:status", "Pausing") - case "paused": - return OutputDevice.formatDuration( remainingTime ) - case "resuming": - return catalog.i18nc("@label:status", "Resuming") - case "queued": - return catalog.i18nc("@label:status", "Action required") - default: - return OutputDevice.formatDuration( remainingTime ) - } - } + width: childrenRect.width height: 18 * screenScaleFactor // TODO: Theme! @@ -82,12 +29,12 @@ Item { verticalCenter: parent.verticalCenter } - value: progress; + value: printJob ? printJob.progress : 0 style: ProgressBarStyle { background: Rectangle { - color: "#e4e4f2" // TODO: Theme! + color: printJob && printJob.isActive ? "#e4e4f2" : "#f3f3f9" // TODO: Theme! implicitHeight: visible ? 8 * screenScaleFactor : 0 // TODO: Theme! implicitWidth: 180 * screenScaleFactor // TODO: Theme! radius: 4 * screenScaleFactor // TODO: Theme! @@ -95,41 +42,72 @@ Item progress: Rectangle { id: progressItem; - color: - { - if (printJob) - { - var state = printJob.state - var inactiveStates = [ - "pausing", - "paused", - "resuming", - "wait_cleanup" - ] - if (inactiveStates.indexOf(state) > -1 && remainingTime > 0) - { - return UM.Theme.getColor("monitor_progress_fill_inactive") - } - } - return "#0a0850" // TODO: Theme! - } + color: printJob && printJob.isActive ? "#0a0850" : "#9392b2" // TODO: Theme! radius: 4 * screenScaleFactor // TODO: Theme! } } } Label { - id: progressLabel + id: percentLabel anchors { left: progressBar.right leftMargin: 18 * screenScaleFactor // TODO: Theme! } - text: progressText - color: "#374355" // TODO: Theme! + text: Math.round(printJob.progress * 100) + "%" + color: printJob && printJob.isActive ? "#374355" : "#babac1" // TODO: Theme! width: contentWidth font: UM.Theme.getFont("medium") // 14pt, regular + // FIXED-LINE-HEIGHT: + height: 18 * screenScaleFactor // TODO: Theme! + verticalAlignment: Text.AlignVCenter + } + Label + { + id: statusLabel + anchors + { + left: percentLabel.right + leftMargin: 18 * screenScaleFactor // TODO: Theme! + } + color: "#374355" // TODO: Theme! + font: UM.Theme.getFont("medium") // 14pt, regular + text: + { + if (!printJob) + { + return ""; + } + switch (printJob.state) + { + case "wait_cleanup": + if (printJob.timeTotal > printJob.timeElapsed) + { + return catalog.i18nc("@label:status", "Aborted") + } + return catalog.i18nc("@label:status", "Finished") + case "sent_to_printer": + return catalog.i18nc("@label:status", "Preparing...") + case "aborting": + return catalog.i18nc("@label:status", "Aborting...") + case "aborted": + return catalog.i18nc("@label:status", "Aborted") + case "pausing": + return catalog.i18nc("@label:status", "Pausing...") + case "paused": + return catalog.i18nc("@label:status", "Paused") + case "resuming": + return catalog.i18nc("@label:status", "Resuming...") + case "queued": + return catalog.i18nc("@label:status", "Action required") + default: + return catalog.i18nc("@label:status", "Finishes ") + OutputDevice.getDateCompleted( printJob.timeRemaining ) + " at " + OutputDevice.getTimeCompleted( printJob.timeRemaining ) + } + } + width: contentWidth + // FIXED-LINE-HEIGHT: height: 18 * screenScaleFactor // TODO: Theme! verticalAlignment: Text.AlignVCenter diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml index 975fe12244..ee7212760b 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml @@ -183,6 +183,7 @@ Item printJob: base.printer.activePrintJob size: parent.height } + visible: printer.activePrintJob } Item @@ -193,14 +194,15 @@ Item } width: 216 * screenScaleFactor // TODO: Theme! height: printerNameLabel.height + printerFamilyPill.height + 6 * screenScaleFactor // TODO: Theme! + visible: printer.activePrintJob Label { id: printerJobNameLabel - text: base.printer.activePrintJob ? base.printer.activePrintJob.name : "Untitled" // TODO: I18N - color: "#414054" // TODO: Theme! + color: printer.activePrintJob && printer.activePrintJob.isActive ? "#414054" : "#babac1" // TODO: Theme! elide: Text.ElideRight font: UM.Theme.getFont("large") // 16pt, bold + text: base.printer.activePrintJob ? base.printer.activePrintJob.name : "Untitled" // TODO: I18N width: parent.width // FIXED-LINE-HEIGHT: @@ -217,10 +219,10 @@ Item topMargin: 6 * screenScaleFactor // TODO: Theme! left: printerJobNameLabel.left } - text: printer.activePrintJob ? printer.activePrintJob.owner : "Anonymous" // TODO: I18N - color: "#53657d" // TODO: Theme! + color: printer.activePrintJob && printer.activePrintJob.isActive ? "#53657d" : "#babac1" // TODO: Theme! elide: Text.ElideRight font: UM.Theme.getFont("very_small") // 12pt, regular + text: printer.activePrintJob ? printer.activePrintJob.owner : "Anonymous" // TODO: I18N width: parent.width // FIXED-LINE-HEIGHT: @@ -236,6 +238,7 @@ Item verticalCenter: parent.verticalCenter } printJob: printer.activePrintJob + visible: printer.activePrintJob } } } From fc26ccd6fa91f016f1eec7d6bd80c4da742b7496 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Mon, 3 Dec 2018 14:41:36 +0100 Subject: [PATCH 0573/1240] STAR-332: Some improvements to get the job in connect --- cura/API/Account.py | 6 +- .../NetworkedPrinterOutputDevice.py | 11 +- .../src/Cloud/CloudOutputDevice.py | 167 ++++++++++-------- .../src/Cloud/CloudOutputDeviceManager.py | 19 +- .../UM3NetworkPrinting/src/Cloud/Models.py | 10 +- 5 files changed, 121 insertions(+), 92 deletions(-) diff --git a/cura/API/Account.py b/cura/API/Account.py index 64d63c7025..d78c7e8826 100644 --- a/cura/API/Account.py +++ b/cura/API/Account.py @@ -37,14 +37,16 @@ class Account(QObject): self._logged_in = False self._callback_port = 32118 - self._oauth_root = "https://account.ultimaker.com" + self._oauth_root = "https://account-staging.ultimaker.com" self._oauth_settings = OAuth2Settings( OAUTH_SERVER_URL= self._oauth_root, CALLBACK_PORT=self._callback_port, CALLBACK_URL="http://localhost:{}/callback".format(self._callback_port), CLIENT_ID="um----------------------------ultimaker_cura", - CLIENT_SCOPES="account.user.read drive.backup.read drive.backup.write packages.download packages.rating.read packages.rating.write", + CLIENT_SCOPES="account.user.read drive.backup.read drive.backup.write packages.download " + "packages.rating.read packages.rating.write connect.cluster.read connect.cluster.write " + "cura.printjob.read cura.printjob.write cura.mesh.read cura.mesh.write", AUTH_DATA_PREFERENCE_KEY="general/ultimaker_auth_data", AUTH_SUCCESS_REDIRECT="{}/app/auth-success".format(self._oauth_root), AUTH_FAILED_REDIRECT="{}/app/auth-error".format(self._oauth_root) diff --git a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py index 7125de4002..0a799d4cd3 100644 --- a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py +++ b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py @@ -11,7 +11,7 @@ from cura.PrinterOutputDevice import PrinterOutputDevice, ConnectionState from PyQt5.QtNetwork import QHttpMultiPart, QHttpPart, QNetworkRequest, QNetworkAccessManager, QNetworkReply, QAuthenticator from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QUrl, QCoreApplication from time import time -from typing import Any, Callable, Dict, List, Optional +from typing import Callable, Dict, List, Optional, Union from enum import IntEnum import os # To get the username @@ -180,12 +180,12 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): self._createNetworkManager() assert (self._manager is not None) - def put(self, target: str, data: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: + def put(self, target: str, data: Union[str, bytes], on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: self._validateManager() request = self._createEmptyRequest(target) self._last_request_time = time() if self._manager is not None: - reply = self._manager.put(request, data.encode()) + reply = self._manager.put(request, data if isinstance(data, bytes) else data.encode()) self._registerOnFinishedCallback(reply, on_finished) else: Logger.log("e", "Could not find manager.") @@ -210,12 +210,13 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): else: Logger.log("e", "Could not find manager.") - def post(self, target: str, data: str, on_finished: Optional[Callable[[QNetworkReply], None]], on_progress: Callable = None) -> None: + def post(self, target: str, data: Union[str, bytes], on_finished: Optional[Callable[[QNetworkReply], None]], + on_progress: Callable = None) -> None: self._validateManager() request = self._createEmptyRequest(target) self._last_request_time = time() if self._manager is not None: - reply = self._manager.post(request, data.encode()) + reply = self._manager.post(request, data if isinstance(data, bytes) else data.encode()) if on_progress is not None: reply.uploadProgress.connect(on_progress) self._registerOnFinishedCallback(reply, on_finished) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 008633e198..caffa64a95 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -3,7 +3,8 @@ import io import json import os -from typing import List, Optional, Dict, cast, Union +from json import JSONDecodeError +from typing import List, Optional, Dict, cast, Union, Tuple from PyQt5.QtCore import QObject, pyqtSignal, QUrl, pyqtProperty from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest @@ -20,9 +21,9 @@ from cura.PrinterOutput.PrinterOutputController import PrinterOutputController from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel +from plugins.UM3NetworkPrinting.src.UM3PrintJobOutputModel import UM3PrintJobOutputModel from .Models import CloudClusterPrinter, CloudClusterPrinterConfiguration, CloudClusterPrinterConfigurationMaterial, \ - CloudClusterPrintJob, CloudClusterPrintJobConstraint, JobUploadRequest -from ..UM3PrintJobOutputModel import UM3PrintJobOutputModel + CloudClusterPrintJob, CloudClusterPrintJobConstraint, JobUploadRequest, JobUploadResponse, PrintResponse ## The cloud output device is a network output device that works remotely but has limited functionality. @@ -37,11 +38,10 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): I18N_CATALOG = i18nCatalog("cura") # The cloud URL to use for this remote cluster. - # TODO: Make sure that this url goes to the live api before release + # TODO: Make sure that this URL goes to the live api before release ROOT_PATH = "https://api-staging.ultimaker.com" CLUSTER_API_ROOT = "{}/connect/v1/".format(ROOT_PATH) CURA_API_ROOT = "{}/cura/v1/".format(ROOT_PATH) - CURA_DRIVE_API_ROOT = "{}/cura-drive/v1/".format(ROOT_PATH) # Signal triggered when the printers in the remote cluster were changed. printersChanged = pyqtSignal() @@ -56,6 +56,9 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self._device_id = device_id self._account = CuraApplication.getInstance().getCuraAPI().account + # Cluster does not have authentication, so default to authenticated + self._authentication_state = AuthState.Authenticated + # We re-use the Cura Connect monitor tab to get the most functionality right away. self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../resources/qml/ClusterMonitorItem.qml") @@ -63,8 +66,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): "../../resources/qml/ClusterControlItem.qml") # Properties to populate later on with received cloud data. - self._printers = {} # type: Dict[str, PrinterOutputModel] - self._print_jobs = {} # type: Dict[str, PrintJobOutputModel] + self._print_jobs = [] # type: List[UM3PrintJobOutputModel] self._number_of_extruders = 2 # All networked printers are dual-extrusion Ultimaker machines. @staticmethod @@ -123,23 +125,11 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): Logger.log("e", "Missing file or mesh writer!") return - stream = io.BytesIO() # type: Union[io.BytesIO, io.StringIO]# Binary mode. - if file_format["mode"] == FileWriter.OutputMode.TextMode: - stream = io.StringIO() - + stream = io.StringIO() if file_format["mode"] == FileWriter.OutputMode.TextMode else io.BytesIO() writer.write(stream, nodes) + self._sendPrintJob(file_name + "." + file_format["extension"], stream) - stream.seek(0, io.SEEK_END) - size = stream.tell() - stream.seek(0, io.SEEK_SET) - - request = JobUploadRequest() - request.job_name = file_name - request.file_size = size - - self._addPrintJobToQueue(stream, request) - - # TODO: This is yanked right out of ClusterUM3OoutputDevice, great candidate for a utility or base class + # TODO: This is yanked right out of ClusterUM3OutputDevice, great candidate for a utility or base class def _determineFileFormat(self, file_handler) -> Optional[Dict[str, Union[str, int]]]: # Formats supported by this application (file types that we can actually write). if file_handler: @@ -172,7 +162,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ) return file_formats[0] - # TODO: This is yanked right out of ClusterUM3OoutputDevice, great candidate for a utility or base class + # TODO: This is yanked right out of ClusterUM3OutputDevice, great candidate for a utility or base class @staticmethod def _determineWriter(file_handler, file_format) -> Optional[FileWriter]: # Just take the first file format available. @@ -194,10 +184,14 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def printers(self): return self._printers + @pyqtProperty("QVariantList", notify = printJobsChanged) + def printJobs(self)-> List[UM3PrintJobOutputModel]: + return self._print_jobs + ## Get remote print jobs. @pyqtProperty("QVariantList", notify = printJobsChanged) def queuedPrintJobs(self) -> List[UM3PrintJobOutputModel]: - return [print_job for print_job in self._print_jobs.values() + return [print_job for print_job in self._print_jobs if print_job.state == "queued" or print_job.state == "error"] ## Called when the connection to the cluster changes. @@ -207,6 +201,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Called when the network data should be updated. def _update(self) -> None: super()._update() + Logger.log("i", "Calling the cloud cluster") self.get("{root}/cluster/{cluster_id}/status".format(root=self.CLUSTER_API_ROOT, cluster_id=self._device_id), on_finished = self._onStatusCallFinished) @@ -214,11 +209,12 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # Contains both printers and print jobs statuses in a single response. def _onStatusCallFinished(self, reply: QNetworkReply) -> None: status_code, response = self._parseReply(reply) - if status_code > 204: - Logger.log("w", "Got unexpected response while trying to get cloud cluster data: {}, {}" - .format(status_code, status, response)) + if status_code > 204 or not isinstance(response, dict): + Logger.log("w", "Got unexpected response while trying to get cloud cluster data: %s, %s", + status_code, response) return + Logger.log("d", "Got response form the cloud cluster %s, %s", status_code, response) printers, print_jobs = self._parseStatusResponse(response) if not printers and not print_jobs: return @@ -228,7 +224,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self._updatePrintJobs(print_jobs) @staticmethod - def _parseStatusResponse(response: dict) -> Optional[Tuple[CloudClusterPrinter, CloudClusterPrintJob]]: + def _parseStatusResponse(response: dict) -> Tuple[List[CloudClusterPrinter], List[CloudClusterPrintJob]]: printers = [] print_jobs = [] @@ -264,33 +260,31 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def _updatePrinters(self, printers: List[CloudClusterPrinter]) -> None: remote_printers = {p.uuid: p for p in printers} # type: Dict[str, CloudClusterPrinter] + current_printers = {p.key: p for p in self._printers} - removed_printer_ids = set(self._printers).difference(remote_printers) - new_printer_ids = set(remote_printers).difference(self._printers) - updated_printer_ids = set(self._printers).intersection(remote_printers) + removed_printer_ids = set(current_printers).difference(remote_printers) + new_printer_ids = set(remote_printers).difference(current_printers) + updated_printer_ids = set(current_printers).intersection(remote_printers) for printer_guid in removed_printer_ids: - self._removePrinter(printer_guid) + self._printers.remove(current_printers[printer_guid]) for printer_guid in new_printer_ids: self._addPrinter(remote_printers[printer_guid]) - self._updatePrinter(remote_printers[printer_guid]) for printer_guid in updated_printer_ids: - self._updatePrinter(remote_printers[printer_guid]) + self._updatePrinter(current_printers[printer_guid], remote_printers[printer_guid]) - # TODO: properly handle removed and updated printers self.printersChanged.emit() def _addPrinter(self, printer: CloudClusterPrinter) -> None: - self._printers[printer.uuid] = self._createPrinterOutputModel(printer) + model = PrinterOutputModel( + PrinterOutputController(self), len(printer.configuration), firmware_version=printer.firmware_version + ) + self._printers.append(model) + self._updatePrinter(model, printer) - def _createPrinterOutputModel(self, printer: CloudClusterPrinter) -> PrinterOutputModel: - return PrinterOutputModel(PrinterOutputController(self), len(printer.configuration), - firmware_version=printer.firmware_version) - - def _updatePrinter(self, printer: CloudClusterPrinter) -> None: - model = self._printers[printer.uuid] + def _updatePrinter(self, model: PrinterOutputModel, printer: CloudClusterPrinter) -> None: model.updateKey(printer.uuid) model.updateName(printer.friendly_name) model.updateType(printer.machine_variant) @@ -342,68 +336,85 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): return MaterialOutputModel(guid=material.guid, type=material_type, brand=brand, color=color, name=name) - def _removePrinter(self, guid): - del self._printers[guid] - def _updatePrintJobs(self, jobs: List[CloudClusterPrintJob]) -> None: remote_jobs = {j.uuid: j for j in jobs} + current_jobs = {j.key: j for j in self._print_jobs} - removed_jobs = set(self._print_jobs.keys()).difference(set(remote_jobs.keys())) - new_jobs = set(remote_jobs.keys()).difference(set(self._print_jobs.keys())) - updated_jobs = set(self._print_jobs.keys()).intersection(set(remote_jobs.keys())) + removed_job_ids = set(current_jobs).difference(set(remote_jobs)) + new_job_ids = set(remote_jobs.keys()).difference(set(current_jobs)) + updated_job_ids = set(current_jobs).intersection(set(remote_jobs)) - for j in removed_jobs: - self._removePrintJob(j) + for job_id in removed_job_ids: + self._print_jobs.remove(current_jobs[job_id]) - for j in new_jobs: - self._addPrintJob(jobs[j]) + for job_id in new_job_ids: + self._addPrintJob(remote_jobs[job_id]) - for j in updated_jobs: - self._updatePrintJob(remote_jobs[j]) + for job_id in updated_job_ids: + self._updateUM3PrintJobOutputModel(current_jobs[job_id], remote_jobs[job_id]) # TODO: properly handle removed and updated printers - self.printJobsChanged() + self.printJobsChanged.emit() def _addPrintJob(self, job: CloudClusterPrintJob) -> None: - self._print_jobs[job.uuid] = self._createPrintJobOutputModel(job) + try: + printer = next(p for p in self._printers if job.printer_uuid == p.key) + except StopIteration: + return Logger.log("w", "Missing printer %s for job %s in %s", job.printer_uuid, job.uuid, + [p.key for p in self._printers]) - def _createPrintJobOutputModel(self, job: CloudClusterPrintJob) -> PrintJobOutputModel: - controller = self._printers[job.printer_uuid]._controller # TODO: Can we access this property? - model = PrintJobOutputModel(controller, job.uuid, job.name) - assigned_printer = self._printes[job.printer_uuid] # TODO: Or do we have to use the assigned_to field? - model.updateAssignedPrinter(assigned_printer) - return model - - def _updatePrintJobOutputModel(self, guid: str, job: CloudClusterPrintJob) -> None: - model = self._print_jobs[guid] + model = UM3PrintJobOutputModel(printer.getController(), job.uuid, job.name) + model.updateAssignedPrinter(printer) + self._print_jobs.append(model) + @staticmethod + def _updateUM3PrintJobOutputModel(model: PrinterOutputModel, job: CloudClusterPrintJob) -> None: model.updateTimeTotal(job.time_total) model.updateTimeElapsed(job.time_elapsed) model.updateOwner(job.owner) model.updateState(job.status) - def _removePrintJob(self, guid: str): - del self._print_jobs[guid] + def _sendPrintJob(self, file_name: str, stream: Union[io.StringIO, io.BytesIO]) -> None: + mesh = stream.getvalue() - def _addPrintJobToQueue(self, stream, request: JobUploadRequest): - self.put("{}/jobs/upload".format(self.CURA_API_ROOT), data = json.dumps(request.__dict__), - on_finished = lambda reply: self._onAddPrintJobToQueueFinished(stream, reply)) + request = JobUploadRequest() + request.job_name = file_name + request.file_size = len(mesh) - def _onAddPrintJobToQueueFinished(self, stream, reply: QNetworkReply) -> None: - status_code, response = self._parseReply(reply) # type: Tuple[int, dict] - if status_code > 204 or not isinstance(dict, response) or "data" not in response: - Logger.error() + Logger.log("i", "Creating new cloud print job: %s", request.__dict__) + self.put("{}/jobs/upload".format(self.CURA_API_ROOT), data = json.dumps({"data": request.__dict__}), + on_finished = lambda reply: self._onPrintJobCreated(mesh, reply)) + + def _onPrintJobCreated(self, mesh: bytes, reply: QNetworkReply) -> None: + status_code, response = self._parseReply(reply) + if status_code > 204 or not isinstance(response, dict) or "data" not in response: + Logger.log("w", "Got unexpected response while trying to add print job to cluster: {}, {}" + .format(status_code, response)) return + # TODO: Multipart upload job_response = JobUploadResponse(**response.get("data")) - self.put(job_response.upload_url, data=stream.getvalue(), on_finished=self._onPrintJobUploaded) + Logger.log("i", "Print job created successfully: %s", job_response.__dict__) + self.put(job_response.upload_url, data=mesh, + on_finished=lambda r: self._onPrintJobUploaded(job_response.job_id, r)) - def _onPrintJobUploaded(self, reply: QNetworkReply) -> None: + def _onPrintJobUploaded(self, job_id: str, reply: QNetworkReply) -> None: status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) if status_code > 204: - self.onWriteFailed.emit("Failed to export G-code to remote URL: {}".format(r.text)) Logger.logException("w", "Received unexpected response from the job upload: %s, %s.", status_code, bytes(reply.readAll()).decode()) return - self.onWriteSuccess.emit(r.text) + Logger.log("i", "Print job uploaded successfully: %s", reply.readAll()) + url = "{}/cluster/{}/print/{}".format(self.CLUSTER_API_ROOT, self._device_id, job_id) + self.post(url, data="", on_finished=self._onPrintJobRequested) + + def _onPrintJobRequested(self, reply: QNetworkReply) -> None: + status_code, response = self._parseReply(reply) + if status_code > 204 or not isinstance(response, dict): + Logger.log("w", "Got unexpected response while trying to request printing: %s, %s", + status_code, response) + return + + print_response = PrintResponse(**response.get("data")) + Logger.log("i", "Print job requested successfully: %s", print_response.__dict__) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 421f24bc25..7c10cb4e50 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -25,9 +25,9 @@ from .Models import CloudCluster class CloudOutputDeviceManager(NetworkClient): # The cloud URL to use for remote clusters. - API_ROOT_PATH = "https://api.ultimaker.com/connect/v1" + API_ROOT_PATH = "https://api-staging.ultimaker.com/connect/v1" - # The interval with wich the remote clusters are checked + # The interval with which the remote clusters are checked CHECK_CLUSTER_INTERVAL = 5 # seconds def __init__(self): @@ -39,13 +39,14 @@ class CloudOutputDeviceManager(NetworkClient): application = CuraApplication.getInstance() self._output_device_manager = application.getOutputDeviceManager() self._account = application.getCuraAPI().account + self._account.loginStateChanged.connect(self._getRemoteClusters) # When switching machines we check if we have to activate a remote cluster. application.globalContainerStackChanged.connect(self._activeMachineChanged) # Periodically check all remote clusters for the authenticated user. - self._update_clusters_thread = Thread(target=self._updateClusters, daemon=True) - self._update_clusters_thread.start() + # self._update_clusters_thread = Thread(target=self._updateClusters, daemon=True) + # self._update_clusters_thread.start() ## Override _createEmptyRequest to add the needed authentication header for talking to the Ultimaker Cloud API. def _createEmptyRequest(self, path: str, content_type: Optional[str] = "application/json") -> QNetworkRequest: @@ -64,18 +65,22 @@ class CloudOutputDeviceManager(NetworkClient): ## Gets all remote clusters from the API. def _getRemoteClusters(self) -> None: + Logger.log("i", "Retrieving remote clusters") self.get("/clusters", on_finished = self._onGetRemoteClustersFinished) ## Callback for when the request for getting the clusters. is finished. def _onGetRemoteClustersFinished(self, reply: QNetworkReply) -> None: + Logger.log("i", "Received remote clusters") + status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) - if status_code != 200: + if status_code > 204: Logger.log("w", "Got unexpected response while trying to get cloud cluster data: {}, {}" .format(status_code, reply.readAll())) return # Parse the response (returns the "data" field from the body). found_clusters = self._parseStatusResponse(reply) + Logger.log("i", "Parsed remote clusters to %s", found_clusters) if not found_clusters: return @@ -96,7 +101,8 @@ class CloudOutputDeviceManager(NetworkClient): @staticmethod def _parseStatusResponse(reply: QNetworkReply) -> Dict[str, CloudCluster]: try: - return {c["cluster_id"]: CloudCluster(**c) for c in json.loads(reply.readAll().data().decode("utf-8"))} + response = bytes(reply.readAll()).decode() + return {c["cluster_id"]: CloudCluster(**c) for c in json.loads(response)["data"]} except UnicodeDecodeError: Logger.log("w", "Unable to read server response") except json.decoder.JSONDecodeError: @@ -110,6 +116,7 @@ class CloudOutputDeviceManager(NetworkClient): device = CloudOutputDevice(cluster.cluster_id) self._output_device_manager.addOutputDevice(device) self._remote_clusters[cluster.cluster_id] = device + device.connect() # TODO: Only connect the current device ## Remove a CloudOutputDevice def _removeCloudOutputDevice(self, cluster: CloudCluster): diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models.py b/plugins/UM3NetworkPrinting/src/Cloud/Models.py index 5363f49c00..435f265300 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models.py @@ -36,7 +36,7 @@ class CloudClusterPrinterConfiguration(BaseModel): self.extruder_index = None # type: str self.material = None # type: CloudClusterPrinterConfigurationMaterial self.nozzle_diameter = None # type: str - self.printer_core_id = None # type: str + self.print_core_id = None # type: str super().__init__(**kwargs) @@ -99,3 +99,11 @@ class JobUploadResponse(BaseModel): self.status = None # type: str self.upload_url = None # type: str super().__init__(**kwargs) + + +class PrintResponse(BaseModel): + def __init__(self, **kwargs): + self.cluster_job_id: str = None + self.job_id: str = None + self.status: str = None + super().__init__(**kwargs) From cced42a55bce1fbecdde3f8ce383daeec6185d92 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Mon, 3 Dec 2018 14:41:45 +0100 Subject: [PATCH 0574/1240] Handle idle, unavailable, and unreachable states Contributes to CL-1153 --- .../resources/qml/MonitorPrinterCard.qml | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml index ee7212760b..8659037cb8 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml @@ -169,6 +169,30 @@ Item height: childrenRect.height spacing: 18 * screenScaleFactor // TODO: Theme! + Label + { + id: printerStatus + anchors + { + verticalCenter: parent.verticalCenter + } + color: "#414054" // TODO: Theme! + font: UM.Theme.getFont("large") // 16pt, bold + text: { + if (printer && printer.state == "disabled"){ + return catalog.i18nc("@label:status", "Unavailable") + } + if (printer && printer.state == "unreachable"){ + return catalog.i18nc("@label:status", "Unavailable") + } + if (printer && !printer.activePrintJob) + { + return catalog.i18nc("@label:status", "Idle") + } + return "" + } + } + Item { anchors From ce6a0676dd029a8e4b6ce7d5e2640c8114505fa5 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 3 Dec 2018 14:43:35 +0100 Subject: [PATCH 0575/1240] Add a rectangle surrounding the settings listview Contributes to CURA-5941. --- .../Custom/CustomPrintSetup.qml | 23 +++++++++++++++++-- resources/qml/Settings/SettingCategory.qml | 20 ++++++++-------- resources/qml/Settings/SettingItem.qml | 11 +++++---- resources/qml/Settings/SettingView.qml | 3 ++- resources/themes/cura-light/styles.qml | 4 ++-- resources/themes/cura-light/theme.json | 2 +- 6 files changed, 43 insertions(+), 20 deletions(-) diff --git a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml index ae31976a34..b524bb5926 100644 --- a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml +++ b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml @@ -91,17 +91,36 @@ Item } } - Cura.SettingView + Rectangle { anchors { top: tabBar.visible ? tabBar.bottom : globalProfileRow.bottom - topMargin: UM.Theme.getSize("default_margin").height left: parent.left leftMargin: parent.padding right: parent.right rightMargin: parent.padding bottom: parent.bottom + topMargin: -UM.Theme.getSize("default_lining").width + bottomMargin: -UM.Theme.getSize("default_lining").width + } + z: tabBar.z - 1 + // Don't show the border when only one extruder + border.color: tabBar.visible ? UM.Theme.getColor("lining") : "transparent" + border.width: UM.Theme.getSize("default_lining").width + + Cura.SettingView + { + anchors + { + fill: parent + topMargin: UM.Theme.getSize("default_margin").height + leftMargin: UM.Theme.getSize("default_margin").width + // Small space for the scrollbar + rightMargin: UM.Theme.getSize("narrow_margin").width + // Compensate for the negative margin in the parent + bottomMargin: UM.Theme.getSize("default_lining").width + } } } } diff --git a/resources/qml/Settings/SettingCategory.qml b/resources/qml/Settings/SettingCategory.qml index 4ec6294d61..9a061304d4 100644 --- a/resources/qml/Settings/SettingCategory.qml +++ b/resources/qml/Settings/SettingCategory.qml @@ -12,8 +12,8 @@ Button id: base anchors.left: parent.left anchors.right: parent.right - anchors.leftMargin: UM.Theme.getSize("default_margin").width - anchors.rightMargin: UM.Theme.getSize("default_margin").width * 3 + // To avoid overlaping with the scrollBars + anchors.rightMargin: 2 * UM.Theme.getSize("thin_margin").width hoverEnabled: true background: Rectangle @@ -25,22 +25,24 @@ Button if (base.color) { return base.color - } else if (!base.enabled) + } + else if (!base.enabled) { return UM.Theme.getColor("setting_category_disabled") - } else if (base.hovered && base.checkable && base.checked) + } + else if (base.hovered && base.checkable && base.checked) { return UM.Theme.getColor("setting_category_active_hover") - } else if (base.pressed || (base.checkable && base.checked)) + } + else if (base.pressed || (base.checkable && base.checked)) { return UM.Theme.getColor("setting_category_active") - } else if (base.hovered) + } + else if (base.hovered) { return UM.Theme.getColor("setting_category_hover") - } else - { - return UM.Theme.getColor("setting_category") } + return UM.Theme.getColor("setting_category") } Behavior on color { ColorAnimation { duration: 50; } } Rectangle diff --git a/resources/qml/Settings/SettingItem.qml b/resources/qml/Settings/SettingItem.qml index 036dcfeba4..df90cffdf3 100644 --- a/resources/qml/Settings/SettingItem.qml +++ b/resources/qml/Settings/SettingItem.qml @@ -10,8 +10,9 @@ import Cura 1.0 as Cura import "." -Item { - id: base; +Item +{ + id: base height: UM.Theme.getSize("section").height @@ -105,11 +106,11 @@ Item { Label { - id: label; + id: label - anchors.left: parent.left; + anchors.left: parent.left anchors.leftMargin: doDepthIndentation ? Math.round((UM.Theme.getSize("section_icon_column").width / 1.2) + ((definition.depth - 1) * UM.Theme.getSize("setting_control_depth_margin").width)) : 0 - anchors.right: settingControls.left; + anchors.right: settingControls.left anchors.verticalCenter: parent.verticalCenter text: definition.label diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 3250e847a3..ff94c9168f 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -163,6 +163,7 @@ Item top: filterContainer.top bottom: filterContainer.bottom right: parent.right + rightMargin: UM.Theme.getSize("wide_margin").width } style: ButtonStyle @@ -210,7 +211,7 @@ Item ListView { id: contents - spacing: Math.round(UM.Theme.getSize("default_lining").height) + spacing: UM.Theme.getSize("default_lining").height cacheBuffer: 1000000 // Set a large cache to effectively just cache every list item. model: UM.SettingDefinitionsModel diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index ca95b6d373..eed5393a0b 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -384,13 +384,13 @@ QtObject { implicitWidth: Theme.getSize("scrollbar").width radius: Math.round(implicitWidth / 2) - color: Theme.getColor("scrollbar_background"); + color: Theme.getColor("scrollbar_background") } handle: Rectangle { id: scrollViewHandle - implicitWidth: Theme.getSize("scrollbar").width; + implicitWidth: Theme.getSize("scrollbar").width radius: Math.round(implicitWidth / 2) color: styleData.pressed ? Theme.getColor("scrollbar_handle_down") : styleData.hovered ? Theme.getColor("scrollbar_handle_hover") : Theme.getColor("scrollbar_handle"); diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 5af77d59c3..dbe5e7196e 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -411,7 +411,7 @@ "extruder_icon": [2.33, 2.33], - "section": [0.0, 2.2], + "section": [0.0, 2], "section_icon": [1.6, 1.6], "section_icon_column": [2.8, 0.0], From 95400282b907abf0a6d7ec707e209da50d0c1b36 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Mon, 3 Dec 2018 14:44:15 +0100 Subject: [PATCH 0576/1240] Simplify logic slightly Contributes to CL-1153 --- .../resources/qml/MonitorPrintJobPreview.qml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml index 7ac2a1d8de..b6b666cbf3 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml @@ -23,11 +23,7 @@ Item anchors.fill: parent opacity: { - if (!printJob) - { - return 0 - } - if (printJob.state == "error" || !printJob.isActive) + if (printJob && (printJob.state == "error" || !printJob.isActive)) { return 0.5 } From f8f6670cae4afc5069a8eeb8034d0c0074c2903b Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Mon, 3 Dec 2018 15:06:01 +0100 Subject: [PATCH 0577/1240] STAR-322: Letting models convert sub-models --- .../src/Cloud/CloudOutputDevice.py | 67 +++------- .../UM3NetworkPrinting/src/Cloud/Models.py | 115 +++++++++++------- 2 files changed, 84 insertions(+), 98 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index caffa64a95..c478f15ade 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -22,8 +22,10 @@ from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from plugins.UM3NetworkPrinting.src.UM3PrintJobOutputModel import UM3PrintJobOutputModel -from .Models import CloudClusterPrinter, CloudClusterPrinterConfiguration, CloudClusterPrinterConfigurationMaterial, \ - CloudClusterPrintJob, CloudClusterPrintJobConstraint, JobUploadRequest, JobUploadResponse, PrintResponse +from .Models import ( + CloudClusterPrinter, CloudClusterPrintJob, JobUploadRequest, JobUploadResponse, PrintResponse, CloudClusterStatus, + CloudClusterPrinterConfigurationMaterial +) ## The cloud output device is a network output device that works remotely but has limited functionality. @@ -209,58 +211,21 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # Contains both printers and print jobs statuses in a single response. def _onStatusCallFinished(self, reply: QNetworkReply) -> None: status_code, response = self._parseReply(reply) - if status_code > 204 or not isinstance(response, dict): + if status_code > 204 or not isinstance(response, dict) or "data" not in response: Logger.log("w", "Got unexpected response while trying to get cloud cluster data: %s, %s", status_code, response) return Logger.log("d", "Got response form the cloud cluster %s, %s", status_code, response) - printers, print_jobs = self._parseStatusResponse(response) - if not printers and not print_jobs: - return - + status = CloudClusterStatus(**response["data"]) + # Update all data from the cluster. - self._updatePrinters(printers) - self._updatePrintJobs(print_jobs) - - @staticmethod - def _parseStatusResponse(response: dict) -> Tuple[List[CloudClusterPrinter], List[CloudClusterPrintJob]]: - printers = [] - print_jobs = [] - - data = response["data"] - for p in data["printers"]: - printer = CloudClusterPrinter(**p) - configuration = printer.configuration - printer.configuration = [] - for c in configuration: - extruder = CloudClusterPrinterConfiguration(**c) - extruder.material = CloudClusterPrinterConfigurationMaterial(material=extruder.material) - printer.configuration.append(extruder) - - printers.append(printer) - - for j in data["print_jobs"]: - job = CloudClusterPrintJob(**j) - constraints = job.constraints - job.constraints = [] - for c in constraints: - job.constraints.append(CloudClusterPrintJobConstraint(**c)) - - configuration = job.configuration - job.configuration = [] - for c in configuration: - configuration = CloudClusterPrinterConfiguration(**c) - configuration.material = CloudClusterPrinterConfigurationMaterial(material=configuration.material) - job.configuration.append(configuration) - - print_jobs.append(job) - - return printers, print_jobs + self._updatePrinters(status.printers) + self._updatePrintJobs(status.print_jobs) def _updatePrinters(self, printers: List[CloudClusterPrinter]) -> None: - remote_printers = {p.uuid: p for p in printers} # type: Dict[str, CloudClusterPrinter] - current_printers = {p.key: p for p in self._printers} + remote_printers: Dict[str, CloudClusterPrinter] = {p.uuid: p for p in printers} + current_printers: Dict[str, PrinterOutputModel] = {p.key: p for p in self._printers} removed_printer_ids = set(current_printers).difference(remote_printers) new_printer_ids = set(remote_printers).difference(current_printers) @@ -337,8 +302,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): return MaterialOutputModel(guid=material.guid, type=material_type, brand=brand, color=color, name=name) def _updatePrintJobs(self, jobs: List[CloudClusterPrintJob]) -> None: - remote_jobs = {j.uuid: j for j in jobs} - current_jobs = {j.key: j for j in self._print_jobs} + remote_jobs: Dict[str, CloudClusterPrintJob] = {j.uuid: j for j in jobs} + current_jobs: Dict[str, UM3PrintJobOutputModel] = {j.key: j for j in self._print_jobs} removed_job_ids = set(current_jobs).difference(set(remote_jobs)) new_job_ids = set(remote_jobs.keys()).difference(set(current_jobs)) @@ -368,7 +333,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self._print_jobs.append(model) @staticmethod - def _updateUM3PrintJobOutputModel(model: PrinterOutputModel, job: CloudClusterPrintJob) -> None: + def _updateUM3PrintJobOutputModel(model: UM3PrintJobOutputModel, job: CloudClusterPrintJob) -> None: model.updateTimeTotal(job.time_total) model.updateTimeElapsed(job.time_elapsed) model.updateOwner(job.owner) @@ -411,10 +376,10 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def _onPrintJobRequested(self, reply: QNetworkReply) -> None: status_code, response = self._parseReply(reply) - if status_code > 204 or not isinstance(response, dict): + if status_code > 204 or not isinstance(response, dict) or "data" not in response: Logger.log("w", "Got unexpected response while trying to request printing: %s, %s", status_code, response) return - print_response = PrintResponse(**response.get("data")) + print_response = PrintResponse(**response["data"]) Logger.log("i", "Print job requested successfully: %s", print_response.__dict__) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models.py b/plugins/UM3NetworkPrinting/src/Cloud/Models.py index 435f265300..780fa06172 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models.py @@ -8,11 +8,11 @@ from ..Models import BaseModel ## Class representing a cloud connected cluster. class CloudCluster(BaseModel): def __init__(self, **kwargs): - self.cluster_id = None # type: str - self.host_guid = None # type: str - self.host_name = None # type: str - self.host_version = None # type: str - self.status = None # type: str + self.cluster_id: str = None + self.host_guid: str = None + self.host_name: str = None + self.host_version: str = None + self.status: str = None super().__init__(**kwargs) def validate(self): @@ -23,81 +23,102 @@ class CloudCluster(BaseModel): ## Class representing a cloud cluster printer configuration class CloudClusterPrinterConfigurationMaterial(BaseModel): def __init__(self, **kwargs): - self.guid = None # type: str - self.brand = None # type: str - self.color = None # type: str - self.material = None # type: str + self.guid: str = None + self.brand: str = None + self.color: str = None + self.material: str = None super().__init__(**kwargs) ## Class representing a cloud cluster printer configuration class CloudClusterPrinterConfiguration(BaseModel): def __init__(self, **kwargs): - self.extruder_index = None # type: str - self.material = None # type: CloudClusterPrinterConfigurationMaterial - self.nozzle_diameter = None # type: str - self.print_core_id = None # type: str + self.extruder_index: str = None + self.material: CloudClusterPrinterConfigurationMaterial = None + self.nozzle_diameter: str = None + self.print_core_id: str = None super().__init__(**kwargs) + if isinstance(self.material, dict): + self.material = CloudClusterPrinterConfigurationMaterial(**self.material) + ## Class representing a cluster printer class CloudClusterPrinter(BaseModel): def __init__(self, **kwargs): - self.configuration = None # type: List[CloudClusterPrinterConfiguration] - self.enabled = None # type: str - self.firmware_version = None # type: str - self.friendly_name = None # type: str - self.ip_address = None # type: str - self.machine_variant = None # type: str - self.status = None # type: str - self.unique_name = None # type: str - self.uuid = None # type: str + self.configuration: List[CloudClusterPrinterConfiguration] = [] + self.enabled: str = None + self.firmware_version: str = None + self.friendly_name: str = None + self.ip_address: str = None + self.machine_variant: str = None + self.status: str = None + self.unique_name: str = None + self.uuid: str = None super().__init__(**kwargs) + self.configuration = [CloudClusterPrinterConfiguration(**c) + if isinstance(c, dict) else c for c in self.configuration] + ## Class representing a cloud cluster print job constraint class CloudClusterPrintJobConstraint(BaseModel): def __init__(self, **kwargs): - self.require_printer_name = None # type: str + self.require_printer_name: str = None super().__init__(**kwargs) + ## Class representing a print job class CloudClusterPrintJob(BaseModel): def __init__(self, **kwargs): - self.assigned_to = None # type: str - self.configuration = None # type: str - self.constraints = None # type: str - self.created_at = None # type: str - self.force = None # type: str - self.last_seen = None # type: str - self.machine_variant = None # type: str - self.name = None # type: str - self.network_error_count = None # type: str - self.owner = None # type: str - self.printer_uuid = None # type: str - self.started = None # type: str - self.status = None # type: str - self.time_elapsed = None # type: str - self.time_total = None # type: str - self.uuid = None # type: str + self.assigned_to: str = None + self.configuration: List[CloudClusterPrinterConfiguration] = [] + self.constraints: List[CloudClusterPrintJobConstraint] = [] + self.created_at: str = None + self.force: str = None + self.last_seen: str = None + self.machine_variant: str = None + self.name: str = None + self.network_error_count: int = None + self.owner: str = None + self.printer_uuid: str = None + self.started: str = None + self.status: str = None + self.time_elapsed: str = None + self.time_total: str = None + self.uuid: str = None super().__init__(**kwargs) + self.printers = [CloudClusterPrinterConfiguration(**c) if isinstance(c, dict) else c + for c in self.configuration] + self.printers = [CloudClusterPrintJobConstraint(**p) if isinstance(p, dict) else p + for p in self.constraints] + + +class CloudClusterStatus(BaseModel): + def __init__(self, **kwargs): + self.printers: List[CloudClusterPrinter] = [] + self.print_jobs: List[CloudClusterPrintJob] = [] + super().__init__(**kwargs) + + self.printers = [CloudClusterPrinter(**p) if isinstance(p, dict) else p for p in self.printers] + self.print_jobs = [CloudClusterPrintJob(**j) if isinstance(j, dict) else j for j in self.print_jobs] class JobUploadRequest(BaseModel): def __init__(self, **kwargs): - self.file_size = None # type: int - self.job_name = None # type: str + self.file_size: int = None + self.job_name: str = None super().__init__(**kwargs) class JobUploadResponse(BaseModel): def __init__(self, **kwargs): - self.download_url = None # type: str - self.job_id = None # type: str - self.job_name = None # type: str - self.slicing_details = None # type: str - self.status = None # type: str - self.upload_url = None # type: str + self.download_url: str = None + self.job_id: str = None + self.job_name: str = None + self.slicing_details: str = None + self.status: str = None + self.upload_url: str = None super().__init__(**kwargs) From 4e9c595bdd3dec5ac4f3688a4a25554fcf6f980d Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 3 Dec 2018 15:34:33 +0100 Subject: [PATCH 0578/1240] Restyle the settings list Be sure that the alignments and the margins are corrects according to the designs. Contributes to CURA-5941. --- resources/qml/Settings/SettingCategory.qml | 34 ++--- resources/qml/Settings/SettingItem.qml | 149 ++++++++++++--------- resources/themes/cura-light/theme.json | 2 +- 3 files changed, 104 insertions(+), 81 deletions(-) diff --git a/resources/qml/Settings/SettingCategory.qml b/resources/qml/Settings/SettingCategory.qml index 9a061304d4..9b78683e14 100644 --- a/resources/qml/Settings/SettingCategory.qml +++ b/resources/qml/Settings/SettingCategory.qml @@ -138,19 +138,20 @@ Button if (!base.enabled) { return UM.Theme.getColor("setting_category_disabled_text") - } else if ((base.hovered || base.activeFocus) && base.checkable && base.checked) + } + else if ((base.hovered || base.activeFocus) && base.checkable && base.checked) { return UM.Theme.getColor("setting_category_active_hover_text") - } else if (base.pressed || (base.checkable && base.checked)) + } + else if (base.pressed || (base.checkable && base.checked)) { return UM.Theme.getColor("setting_category_active_text") - } else if (base.hovered || base.activeFocus) + } + else if (base.hovered || base.activeFocus) { return UM.Theme.getColor("setting_category_hover_text") - } else - { - return UM.Theme.getColor("setting_category_text") } + return UM.Theme.getColor("setting_category_text") } source: base.checked ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") } @@ -161,25 +162,26 @@ Button id: icon anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("default_margin").width + anchors.leftMargin: UM.Theme.getSize("thin_margin").width color: { if (!base.enabled) { return UM.Theme.getColor("setting_category_disabled_text") - } else if((base.hovered || base.activeFocus) && base.checkable && base.checked) + } + else if((base.hovered || base.activeFocus) && base.checkable && base.checked) { return UM.Theme.getColor("setting_category_active_hover_text") - } else if(base.pressed || (base.checkable && base.checked)) + } + else if(base.pressed || (base.checkable && base.checked)) { return UM.Theme.getColor("setting_category_active_text") - } else if(base.hovered || base.activeFocus) + } + else if(base.hovered || base.activeFocus) { return UM.Theme.getColor("setting_category_hover_text") - } else - { - return UM.Theme.getColor("setting_category_text") } + return UM.Theme.getColor("setting_category_text") } source: UM.Theme.getIcon(definition.icon) width: UM.Theme.getSize("section_icon").width @@ -196,7 +198,9 @@ Button if (definition.expanded) { settingDefinitionsModel.collapse(definition.key) - } else { + } + else + { settingDefinitionsModel.expandRecursive(definition.key) } //Set focus so that tab navigation continues from this point on. @@ -205,7 +209,7 @@ Button } onActiveFocusChanged: { - if(activeFocus) + if (activeFocus) { base.focusReceived() } diff --git a/resources/qml/Settings/SettingItem.qml b/resources/qml/Settings/SettingItem.qml index df90cffdf3..4dd53f8663 100644 --- a/resources/qml/Settings/SettingItem.qml +++ b/resources/qml/Settings/SettingItem.qml @@ -15,6 +15,10 @@ Item id: base height: UM.Theme.getSize("section").height + anchors.left: parent.left + anchors.right: parent.right + // To avoid overlaping with the scrollBars + anchors.rightMargin: 2 * UM.Theme.getSize("thin_margin").width property alias contents: controlContainer.children property alias hovered: mouse.containsMouse @@ -44,25 +48,25 @@ Item var affected_by = settingDefinitionsModel.getRequires(definition.key, "value") var affected_by_list = "" - for(var i in affected_by) + for (var i in affected_by) { affected_by_list += "

    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%0A`jGbBzf40jzn#+ltV|2}r62J1oQa2y8OOvT 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

  • %1
  • \n".arg(affected_by[i].label) } var affects_list = "" - for(var i in affects) + for (var i in affects) { affects_list += "
  • %1
  • \n".arg(affects[i].label) } var tooltip = "%1\n

    %2

    ".arg(definition.label).arg(definition.description) - if(affects_list != "") + if (affects_list != "") { tooltip += "
    %1\n
      \n%2
    ".arg(catalog.i18nc("@label Header for list of settings.", "Affects")).arg(affects_list) } - if(affected_by_list != "") + if (affected_by_list != "") { tooltip += "
    %1\n
      \n%2
    ".arg(catalog.i18nc("@label Header for list of settings.", "Affected By")).arg(affected_by_list) } @@ -72,35 +76,39 @@ Item MouseArea { - id: mouse; + id: mouse - anchors.fill: parent; + anchors.fill: parent - acceptedButtons: Qt.RightButton; + acceptedButtons: Qt.RightButton hoverEnabled: true; - onClicked: base.contextMenuRequested(); + onClicked: base.contextMenuRequested() - onEntered: { - hoverTimer.start(); + onEntered: + { + hoverTimer.start() } - onExited: { - if(controlContainer.item && controlContainer.item.hovered) { - return; + onExited: + { + if (controlContainer.item && controlContainer.item.hovered) + { + return } - hoverTimer.stop(); - base.hideTooltip(); + hoverTimer.stop() + base.hideTooltip() } - Timer { - id: hoverTimer; - interval: 500; - repeat: false; + Timer + { + id: hoverTimer + interval: 500 + repeat: false onTriggered: { - base.showTooltip(base.tooltipText); + base.showTooltip(base.tooltipText) } } @@ -109,16 +117,16 @@ Item id: label anchors.left: parent.left - anchors.leftMargin: doDepthIndentation ? Math.round((UM.Theme.getSize("section_icon_column").width / 1.2) + ((definition.depth - 1) * UM.Theme.getSize("setting_control_depth_margin").width)) : 0 + anchors.leftMargin: doDepthIndentation ? Math.round(UM.Theme.getSize("thin_margin").width + ((definition.depth - 1) * UM.Theme.getSize("setting_control_depth_margin").width)) : 0 anchors.right: settingControls.left anchors.verticalCenter: parent.verticalCenter text: definition.label - elide: Text.ElideMiddle; + elide: Text.ElideMiddle renderType: Text.NativeRendering textFormat: Text.PlainText - color: UM.Theme.getColor("setting_control_text"); + color: UM.Theme.getColor("setting_control_text") opacity: (definition.visible) ? 1 : 0.5 // emphasize the setting if it has a value in the user or quality profile font: base.doQualityUserSettingEmphasis && base.stackLevel != undefined && base.stackLevel <= 1 ? UM.Theme.getFont("default_italic") : UM.Theme.getFont("default") @@ -131,7 +139,8 @@ Item height: Math.round(parent.height / 2) spacing: Math.round(UM.Theme.getSize("thick_margin").height / 2) - anchors { + anchors + { right: controlContainer.left rightMargin: Math.round(UM.Theme.getSize("thick_margin").width / 2) verticalCenter: parent.verticalCenter @@ -151,112 +160,123 @@ Item iconSource: UM.Theme.getIcon("link") - onEntered: { - hoverTimer.stop(); - var tooltipText = catalog.i18nc("@label", "This setting is always shared between all extruders. Changing it here will change the value for all extruders."); - if ((resolve != "None") && (stackLevel != 0)) { + onEntered: + { + hoverTimer.stop() + var tooltipText = catalog.i18nc("@label", "This setting is always shared between all extruders. Changing it here will change the value for all extruders.") + if ((resolve != "None") && (stackLevel != 0)) + { // We come here if a setting has a resolve and the setting is not manually edited. - tooltipText += " " + catalog.i18nc("@label", "The value is resolved from per-extruder values ") + "[" + Cura.ExtruderManager.getInstanceExtruderValues(definition.key) + "]."; + tooltipText += " " + catalog.i18nc("@label", "The value is resolved from per-extruder values ") + "[" + Cura.ExtruderManager.getInstanceExtruderValues(definition.key) + "]." } - base.showTooltip(tooltipText); + base.showTooltip(tooltipText) } - onExited: base.showTooltip(base.tooltipText); + onExited: base.showTooltip(base.tooltipText) } UM.SimpleButton { - id: revertButton; + id: revertButton visible: base.stackLevel == 0 && base.showRevertButton - height: parent.height; - width: height; + height: parent.height + width: height color: UM.Theme.getColor("setting_control_button") hoverColor: UM.Theme.getColor("setting_control_button_hover") iconSource: UM.Theme.getIcon("reset") - onClicked: { + onClicked: + { revertButton.focus = true - if (externalResetHandler) { + if (externalResetHandler) + { externalResetHandler(propertyProvider.key) - } else { + } + else + { Cura.MachineManager.clearUserSettingAllCurrentStacks(propertyProvider.key) } } - onEntered: { hoverTimer.stop(); base.showTooltip(catalog.i18nc("@label", "This setting has a value that is different from the profile.\n\nClick to restore the value of the profile.")) } - onExited: base.showTooltip(base.tooltipText); + onEntered: + { + hoverTimer.stop() + base.showTooltip(catalog.i18nc("@label", "This setting has a value that is different from the profile.\n\nClick to restore the value of the profile.")) + } + onExited: base.showTooltip(base.tooltipText) } UM.SimpleButton { // This button shows when the setting has an inherited function, but is overriden by profile. - id: inheritButton; + id: inheritButton // Inherit button needs to be visible if; // - User made changes that override any loaded settings // - This setting item uses inherit button at all // - The type of the value of any deeper container is an "object" (eg; is a function) visible: { - if(!base.showInheritButton) + if (!base.showInheritButton) { - return false; + return false } - if(!propertyProvider.properties.enabled) + if (!propertyProvider.properties.enabled) { // Note: This is not strictly necessary since a disabled setting is hidden anyway. // But this will cause the binding to be re-evaluated when the enabled property changes. - return false; + return false } // There are no settings with any warning. - if(Cura.SettingInheritanceManager.settingsWithInheritanceWarning.length == 0) + if (Cura.SettingInheritanceManager.settingsWithInheritanceWarning.length == 0) { - return false; + return false } // This setting has a resolve value, so an inheritance warning doesn't do anything. - if(resolve != "None") + if (resolve != "None") { return false } // If the setting does not have a limit_to_extruder property (or is -1), use the active stack. - if(globalPropertyProvider.properties.limit_to_extruder == null || String(globalPropertyProvider.properties.limit_to_extruder) == "-1") + if (globalPropertyProvider.properties.limit_to_extruder == null || String(globalPropertyProvider.properties.limit_to_extruder) == "-1") { - return Cura.SettingInheritanceManager.settingsWithInheritanceWarning.indexOf(definition.key) >= 0; + return Cura.SettingInheritanceManager.settingsWithInheritanceWarning.indexOf(definition.key) >= 0 } // Setting does have a limit_to_extruder property, so use that one instead. if (definition.key === undefined) { // Observed when loading workspace, probably when SettingItems are removed. - return false; + return false } - return Cura.SettingInheritanceManager.getOverridesForExtruder(definition.key, String(globalPropertyProvider.properties.limit_to_extruder)).indexOf(definition.key) >= 0; + return Cura.SettingInheritanceManager.getOverridesForExtruder(definition.key, String(globalPropertyProvider.properties.limit_to_extruder)).indexOf(definition.key) >= 0 } - height: parent.height; - width: height; + height: parent.height + width: height - onClicked: { - focus = true; + onClicked: + { + focus = true // Get the most shallow function value (eg not a number) that we can find. var last_entry = propertyProvider.stackLevels[propertyProvider.stackLevels.length - 1] for (var i = 1; i < base.stackLevels.length; i++) { - var has_setting_function = typeof(propertyProvider.getPropertyValue("value", base.stackLevels[i])) == "object"; + var has_setting_function = typeof(propertyProvider.getPropertyValue("value", base.stackLevels[i])) == "object" if(has_setting_function) { last_entry = propertyProvider.stackLevels[i] - break; + break } } - if((last_entry == 4 || last_entry == 11) && base.stackLevel == 0 && base.stackLevels.length == 2) + if ((last_entry == 4 || last_entry == 11) && base.stackLevel == 0 && base.stackLevels.length == 2) { // Special case of the inherit reset. If only the definition (4th or 11th) container) and the first // entry (user container) are set, we can simply remove the container. @@ -277,23 +297,22 @@ Item color: UM.Theme.getColor("setting_control_button") hoverColor: UM.Theme.getColor("setting_control_button_hover") - iconSource: UM.Theme.getIcon("formula"); + iconSource: UM.Theme.getIcon("formula") onEntered: { hoverTimer.stop(); base.showTooltip(catalog.i18nc("@label", "This setting is normally calculated, but it currently has an absolute value set.\n\nClick to restore the calculated value.")) } - onExited: base.showTooltip(base.tooltipText); + onExited: base.showTooltip(base.tooltipText) } } Item { - id: controlContainer; + id: controlContainer enabled: propertyProvider.isValueUsed - anchors.right: parent.right; - anchors.rightMargin: UM.Theme.getSize("default_margin").width * 3 - anchors.verticalCenter: parent.verticalCenter; - width: UM.Theme.getSize("setting_control").width; + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + width: UM.Theme.getSize("setting_control").width height: UM.Theme.getSize("setting_control").height } } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index dbe5e7196e..57532d91e5 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -416,7 +416,7 @@ "section_icon_column": [2.8, 0.0], "setting": [25.0, 1.8], - "setting_control": [10.0, 2.0], + "setting_control": [11.0, 2.0], "setting_control_radius": [0.15, 0.15], "setting_control_depth_margin": [1.4, 0.0], "setting_preferences_button_margin": [4, 0.0], From d3b5b2717d726ff99e22c372e1eaf2da48b2a73b Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 3 Dec 2018 15:56:21 +0100 Subject: [PATCH 0579/1240] Add rounded corners to the settings Contributes to CURA-5941 --- .../RecommendedSupportSelector.qml | 6 +-- resources/qml/Settings/SettingCheckBox.qml | 43 +++++++++++-------- resources/qml/Settings/SettingComboBox.qml | 1 + resources/qml/Settings/SettingExtruder.qml | 40 ++++++++--------- .../qml/Settings/SettingOptionalExtruder.qml | 29 ++++++------- resources/qml/Settings/SettingTextField.qml | 37 +++++++++------- 6 files changed, 81 insertions(+), 75 deletions(-) diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml index ce4aa6c195..da46f2a735 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml @@ -97,10 +97,10 @@ Item property string color_override: "" // for manually setting values property string color: // is evaluated automatically, but the first time is before extruderModel being filled { - var current_extruder = extruderModel.get(currentIndex); - color_override = ""; + var current_extruder = extruderModel.get(currentIndex) + color_override = "" if (current_extruder === undefined) return "" - return (current_extruder.color) ? current_extruder.color : ""; + return (current_extruder.color) ? current_extruder.color : "" } currentIndex: diff --git a/resources/qml/Settings/SettingCheckBox.qml b/resources/qml/Settings/SettingCheckBox.qml index d37754d27c..f53a696343 100644 --- a/resources/qml/Settings/SettingCheckBox.qml +++ b/resources/qml/Settings/SettingCheckBox.qml @@ -28,37 +28,40 @@ SettingItem // 3: material -> user changed material in materials page // 4: variant // 5: machine - var value; - if ((base.resolve != "None") && (stackLevel != 0) && (stackLevel != 1)) { + var value + if ((base.resolve != "None") && (stackLevel != 0) && (stackLevel != 1)) + { // We have a resolve function. Indicates that the setting is not settable per extruder and that // we have to choose between the resolved value (default) and the global value // (if user has explicitly set this). - value = base.resolve; - } else { - value = propertyProvider.properties.value; + value = base.resolve + } + else + { + value = propertyProvider.properties.value } switch(value) { case "True": - return true; + return true case "False": - return false; + return false default: - return value; + return value } } Keys.onSpacePressed: { - forceActiveFocus(); - propertyProvider.setPropertyValue("value", !checked); + forceActiveFocus() + propertyProvider.setPropertyValue("value", !checked) } onClicked: { - forceActiveFocus(); - propertyProvider.setPropertyValue("value", !checked); + forceActiveFocus() + propertyProvider.setPropertyValue("value", !checked) } Keys.onTabPressed: @@ -72,9 +75,9 @@ SettingItem onActiveFocusChanged: { - if(activeFocus) + if (activeFocus) { - base.focusReceived(); + base.focusReceived() } } @@ -90,32 +93,34 @@ SettingItem color: { - if(!enabled) + if (!enabled) { return UM.Theme.getColor("setting_control_disabled") } - if(control.containsMouse || control.activeFocus) + if (control.containsMouse || control.activeFocus) { return UM.Theme.getColor("setting_control_highlight") } return UM.Theme.getColor("setting_control") } + radius: UM.Theme.getSize("setting_control_radius").width border.width: UM.Theme.getSize("default_lining").width border.color: { - if(!enabled) + if (!enabled) { return UM.Theme.getColor("setting_control_disabled_border") } - if(control.containsMouse || control.activeFocus) + if (control.containsMouse || control.activeFocus) { return UM.Theme.getColor("setting_control_border_highlight") } return UM.Theme.getColor("setting_control_border") } - UM.RecolorImage { + UM.RecolorImage + { anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter width: Math.round(parent.width / 2.5) diff --git a/resources/qml/Settings/SettingComboBox.qml b/resources/qml/Settings/SettingComboBox.qml index 76d458e427..13d2a0eb8f 100644 --- a/resources/qml/Settings/SettingComboBox.qml +++ b/resources/qml/Settings/SettingComboBox.qml @@ -35,6 +35,7 @@ SettingItem return UM.Theme.getColor("setting_control") } + radius: UM.Theme.getSize("setting_control_radius").width border.width: UM.Theme.getSize("default_lining").width border.color: { diff --git a/resources/qml/Settings/SettingExtruder.qml b/resources/qml/Settings/SettingExtruder.qml index a9427f863a..e1fedd9274 100644 --- a/resources/qml/Settings/SettingExtruder.qml +++ b/resources/qml/Settings/SettingExtruder.qml @@ -19,8 +19,9 @@ SettingItem model: Cura.ExtrudersModel { - onModelChanged: { - control.color = getItem(control.currentIndex).color; + onModelChanged: + { + control.color = getItem(control.currentIndex).color } } @@ -113,14 +114,15 @@ SettingItem { if (!enabled) { - return UM.Theme.getColor("setting_control_disabled"); + return UM.Theme.getColor("setting_control_disabled") } if (control.hovered || base.activeFocus) { - return UM.Theme.getColor("setting_control_highlight"); + return UM.Theme.getColor("setting_control_highlight") } - return UM.Theme.getColor("setting_control"); + return UM.Theme.getColor("setting_control") } + radius: UM.Theme.getSize("setting_control_radius").width border.width: UM.Theme.getSize("default_lining").width border.color: { @@ -153,20 +155,18 @@ SettingItem elide: Text.ElideLeft verticalAlignment: Text.AlignVCenter - background: Rectangle + background: UM.RecolorImage { id: swatch - height: Math.round(UM.Theme.getSize("setting_control").height / 2) + height: Math.round(parent.height / 2) width: height - anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter - anchors.margins: Math.round(UM.Theme.getSize("default_margin").width / 4) - - border.width: UM.Theme.getSize("default_lining").width - border.color: enabled ? UM.Theme.getColor("setting_control_border") : UM.Theme.getColor("setting_control_disabled_border") - radius: Math.round(width / 2) + anchors.rightMargin: UM.Theme.getSize("thin_margin").width + sourceSize.width: width + sourceSize.height: height + source: UM.Theme.getIcon("extruder_button") color: control.color } } @@ -219,20 +219,18 @@ SettingItem verticalAlignment: Text.AlignVCenter rightPadding: swatch.width + UM.Theme.getSize("setting_unit_margin").width - background: Rectangle + background: UM.RecolorImage { id: swatch - height: Math.round(UM.Theme.getSize("setting_control").height / 2) + height: Math.round(parent.height / 2) width: height - anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter - anchors.margins: Math.round(UM.Theme.getSize("default_margin").width / 4) - - border.width: UM.Theme.getSize("default_lining").width - border.color: enabled ? UM.Theme.getColor("setting_control_border") : UM.Theme.getColor("setting_control_disabled_border") - radius: Math.round(width / 2) + anchors.rightMargin: UM.Theme.getSize("thin_margin").width + sourceSize.width: width + sourceSize.height: height + source: UM.Theme.getIcon("extruder_button") color: control.model.getItem(index).color } } diff --git a/resources/qml/Settings/SettingOptionalExtruder.qml b/resources/qml/Settings/SettingOptionalExtruder.qml index a3c1422b30..53044b0f82 100644 --- a/resources/qml/Settings/SettingOptionalExtruder.qml +++ b/resources/qml/Settings/SettingOptionalExtruder.qml @@ -116,6 +116,7 @@ SettingItem } return UM.Theme.getColor("setting_control"); } + radius: UM.Theme.getSize("setting_control_radius").width border.width: UM.Theme.getSize("default_lining").width border.color: { @@ -148,20 +149,18 @@ SettingItem elide: Text.ElideRight verticalAlignment: Text.AlignVCenter - background: Rectangle + background: UM.RecolorImage { id: swatch - height: Math.round(UM.Theme.getSize("setting_control").height / 2) + height: Math.round(parent.height / 2) width: height - anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter - anchors.margins: Math.round(UM.Theme.getSize("default_margin").width / 4) - - border.width: UM.Theme.getSize("default_lining").width - border.color: enabled ? UM.Theme.getColor("setting_control_border") : UM.Theme.getColor("setting_control_disabled_border") - radius: Math.round(width / 2) + anchors.rightMargin: UM.Theme.getSize("thin_margin").width + sourceSize.width: width + sourceSize.height: height + source: UM.Theme.getIcon("extruder_button") color: control.color } } @@ -215,20 +214,18 @@ SettingItem verticalAlignment: Text.AlignVCenter rightPadding: swatch.width + UM.Theme.getSize("setting_unit_margin").width - background: Rectangle + background: UM.RecolorImage { id: swatch - height: Math.round(UM.Theme.getSize("setting_control").height / 2) + height: Math.round(parent.height / 2) width: height - anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter - anchors.margins: Math.round(UM.Theme.getSize("default_margin").width / 4) - - border.width: UM.Theme.getSize("default_lining").width - border.color: enabled ? UM.Theme.getColor("setting_control_border") : UM.Theme.getColor("setting_control_disabled_border") - radius: Math.round(width / 2) + anchors.rightMargin: UM.Theme.getSize("thin_margin").width + sourceSize.width: width + sourceSize.height: height + source: UM.Theme.getIcon("extruder_button") color: control.model.getItem(index).color } } diff --git a/resources/qml/Settings/SettingTextField.qml b/resources/qml/Settings/SettingTextField.qml index 9ec9338316..770ef53900 100644 --- a/resources/qml/Settings/SettingTextField.qml +++ b/resources/qml/Settings/SettingTextField.qml @@ -32,6 +32,7 @@ SettingItem anchors.fill: parent + radius: UM.Theme.getSize("setting_control_radius").width border.width: Math.round(UM.Theme.getSize("default_lining").width) border.color: { @@ -81,10 +82,10 @@ SettingItem Rectangle { - anchors.fill: parent; - anchors.margins: Math.round(UM.Theme.getSize("default_lining").width); + anchors.fill: parent + anchors.margins: Math.round(UM.Theme.getSize("default_lining").width) color: UM.Theme.getColor("setting_control_highlight") - opacity: !control.hovered ? 0 : propertyProvider.properties.validationState == "ValidatorState.Valid" ? 1.0 : 0.35; + opacity: !control.hovered ? 0 : propertyProvider.properties.validationState == "ValidatorState.Valid" ? 1.0 : 0.35 } Label @@ -145,11 +146,11 @@ SettingItem } color: !enabled ? UM.Theme.getColor("setting_control_disabled_text") : UM.Theme.getColor("setting_control_text") - font: UM.Theme.getFont("default"); + font: UM.Theme.getFont("default") - selectByMouse: true; + selectByMouse: true - maximumLength: (definition.type == "str" || definition.type == "[int]") ? -1 : 10; + maximumLength: (definition.type == "str" || definition.type == "[int]") ? -1 : 10 clip: true; //Hide any text that exceeds the width of the text box. validator: RegExpValidator { regExp: (definition.type == "[int]") ? /^\[?(\s*-?[0-9]{0,9}\s*,)*(\s*-?[0-9]{0,9})\s*\]?$/ : (definition.type == "int") ? /^-?[0-9]{0,10}$/ : (definition.type == "float") ? /^-?[0-9]{0,9}[.,]?[0-9]{0,3}$/ : /^.*$/ } // definition.type property from parent loader used to disallow fractional number entry @@ -158,7 +159,8 @@ SettingItem { target: input property: "text" - value: { + value: + { // Stacklevels // 0: user -> unsaved change // 1: quality changes -> saved change @@ -167,13 +169,15 @@ SettingItem // 4: variant // 5: machine_changes // 6: machine - if ((base.resolve != "None" && base.resolve) && (stackLevel != 0) && (stackLevel != 1)) { + if ((base.resolve != "None" && base.resolve) && (stackLevel != 0) && (stackLevel != 1)) + { // We have a resolve function. Indicates that the setting is not settable per extruder and that // we have to choose between the resolved value (default) and the global value // (if user has explicitly set this). - return base.resolve; - } else { - return propertyProvider.properties.value; + return base.resolve + } + else { + return propertyProvider.properties.value } } when: !input.activeFocus @@ -182,16 +186,17 @@ SettingItem MouseArea { id: mouseArea - anchors.fill: parent; + anchors.fill: parent cursorShape: Qt.IBeamCursor onPressed: { - if(!input.activeFocus) { - base.focusGainedByClick = true; - input.forceActiveFocus(); + if (!input.activeFocus) + { + base.focusGainedByClick = true + input.forceActiveFocus() } - mouse.accepted = false; + mouse.accepted = false } } } From ddf958d39ab3fe5c8a9712cfbe441d44dc853bd8 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 3 Dec 2018 16:23:44 +0100 Subject: [PATCH 0580/1240] Fix typing in CloudOutputDevice --- plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index c478f15ade..3d577d0991 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -224,8 +224,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self._updatePrintJobs(status.print_jobs) def _updatePrinters(self, printers: List[CloudClusterPrinter]) -> None: - remote_printers: Dict[str, CloudClusterPrinter] = {p.uuid: p for p in printers} - current_printers: Dict[str, PrinterOutputModel] = {p.key: p for p in self._printers} + remote_printers = {p.uuid: p for p in printers} # type: Dict[str, CloudClusterPrinter] + current_printers = {p.key: p for p in self._printers} # type: Dict[str, PrinterOutputModel] removed_printer_ids = set(current_printers).difference(remote_printers) new_printer_ids = set(remote_printers).difference(current_printers) @@ -302,8 +302,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): return MaterialOutputModel(guid=material.guid, type=material_type, brand=brand, color=color, name=name) def _updatePrintJobs(self, jobs: List[CloudClusterPrintJob]) -> None: - remote_jobs: Dict[str, CloudClusterPrintJob] = {j.uuid: j for j in jobs} - current_jobs: Dict[str, UM3PrintJobOutputModel] = {j.key: j for j in self._print_jobs} + remote_jobs = {j.uuid: j for j in jobs} # type: Dict[str, CloudClusterPrintJob] + current_jobs = {j.key: j for j in self._print_jobs} # type: Dict[str, UM3PrintJobOutputModel] removed_job_ids = set(current_jobs).difference(set(remote_jobs)) new_job_ids = set(remote_jobs.keys()).difference(set(current_jobs)) From 186c2cf3f5e7430fd50fbd7dc83a40f6d29be023 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Mon, 3 Dec 2018 16:35:51 +0100 Subject: [PATCH 0581/1240] START-322: Python 3.5 compatibility --- .../src/Cloud/CloudOutputDevice.py | 8 +- .../UM3NetworkPrinting/src/Cloud/Models.py | 104 +++++++++--------- 2 files changed, 56 insertions(+), 56 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index c478f15ade..3d577d0991 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -224,8 +224,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self._updatePrintJobs(status.print_jobs) def _updatePrinters(self, printers: List[CloudClusterPrinter]) -> None: - remote_printers: Dict[str, CloudClusterPrinter] = {p.uuid: p for p in printers} - current_printers: Dict[str, PrinterOutputModel] = {p.key: p for p in self._printers} + remote_printers = {p.uuid: p for p in printers} # type: Dict[str, CloudClusterPrinter] + current_printers = {p.key: p for p in self._printers} # type: Dict[str, PrinterOutputModel] removed_printer_ids = set(current_printers).difference(remote_printers) new_printer_ids = set(remote_printers).difference(current_printers) @@ -302,8 +302,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): return MaterialOutputModel(guid=material.guid, type=material_type, brand=brand, color=color, name=name) def _updatePrintJobs(self, jobs: List[CloudClusterPrintJob]) -> None: - remote_jobs: Dict[str, CloudClusterPrintJob] = {j.uuid: j for j in jobs} - current_jobs: Dict[str, UM3PrintJobOutputModel] = {j.key: j for j in self._print_jobs} + remote_jobs = {j.uuid: j for j in jobs} # type: Dict[str, CloudClusterPrintJob] + current_jobs = {j.key: j for j in self._print_jobs} # type: Dict[str, UM3PrintJobOutputModel] removed_job_ids = set(current_jobs).difference(set(remote_jobs)) new_job_ids = set(remote_jobs.keys()).difference(set(current_jobs)) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models.py b/plugins/UM3NetworkPrinting/src/Cloud/Models.py index 780fa06172..7b9ad460c5 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models.py @@ -8,11 +8,11 @@ from ..Models import BaseModel ## Class representing a cloud connected cluster. class CloudCluster(BaseModel): def __init__(self, **kwargs): - self.cluster_id: str = None - self.host_guid: str = None - self.host_name: str = None - self.host_version: str = None - self.status: str = None + self.cluster_id = None # type: str + self.host_guid = None # type: str + self.host_name = None # type: str + self.host_version = None # type: str + self.status = None # type: str super().__init__(**kwargs) def validate(self): @@ -23,20 +23,20 @@ class CloudCluster(BaseModel): ## Class representing a cloud cluster printer configuration class CloudClusterPrinterConfigurationMaterial(BaseModel): def __init__(self, **kwargs): - self.guid: str = None - self.brand: str = None - self.color: str = None - self.material: str = None + self.guid = None # type: str + self.brand = None # type: str + self.color = None # type: str + self.material = None # type: str super().__init__(**kwargs) ## Class representing a cloud cluster printer configuration class CloudClusterPrinterConfiguration(BaseModel): def __init__(self, **kwargs): - self.extruder_index: str = None - self.material: CloudClusterPrinterConfigurationMaterial = None - self.nozzle_diameter: str = None - self.print_core_id: str = None + self.extruder_index = None # type: str + self.material = None # type: CloudClusterPrinterConfigurationMaterial + self.nozzle_diameter = None # type: str + self.print_core_id = None # type: str super().__init__(**kwargs) if isinstance(self.material, dict): @@ -46,15 +46,15 @@ class CloudClusterPrinterConfiguration(BaseModel): ## Class representing a cluster printer class CloudClusterPrinter(BaseModel): def __init__(self, **kwargs): - self.configuration: List[CloudClusterPrinterConfiguration] = [] - self.enabled: str = None - self.firmware_version: str = None - self.friendly_name: str = None - self.ip_address: str = None - self.machine_variant: str = None - self.status: str = None - self.unique_name: str = None - self.uuid: str = None + self.configuration = [] # type: List[CloudClusterPrinterConfiguration] + self.enabled = None # type: str + self.firmware_version = None # type: str + self.friendly_name = None # type: str + self.ip_address = None # type: str + self.machine_variant = None # type: str + self.status = None # type: str + self.unique_name = None # type: str + self.uuid = None # type: str super().__init__(**kwargs) self.configuration = [CloudClusterPrinterConfiguration(**c) @@ -64,29 +64,29 @@ class CloudClusterPrinter(BaseModel): ## Class representing a cloud cluster print job constraint class CloudClusterPrintJobConstraint(BaseModel): def __init__(self, **kwargs): - self.require_printer_name: str = None + self.require_printer_name = None # type: str super().__init__(**kwargs) ## Class representing a print job class CloudClusterPrintJob(BaseModel): def __init__(self, **kwargs): - self.assigned_to: str = None - self.configuration: List[CloudClusterPrinterConfiguration] = [] - self.constraints: List[CloudClusterPrintJobConstraint] = [] - self.created_at: str = None - self.force: str = None - self.last_seen: str = None - self.machine_variant: str = None - self.name: str = None - self.network_error_count: int = None - self.owner: str = None - self.printer_uuid: str = None - self.started: str = None - self.status: str = None - self.time_elapsed: str = None - self.time_total: str = None - self.uuid: str = None + self.assigned_to = None # type: str + self.configuration = [] # type: List[CloudClusterPrinterConfiguration] + self.constraints = [] # type: List[CloudClusterPrintJobConstraint] + self.created_at = None # type: str + self.force = None # type: str + self.last_seen = None # type: str + self.machine_variant = None # type: str + self.name = None # type: str + self.network_error_count = None # type: int + self.owner = None # type: str + self.printer_uuid = None # type: str + self.started = None # type: str + self.status = None # type: str + self.time_elapsed = None # type: str + self.time_total = None # type: str + self.uuid = None # type: str super().__init__(**kwargs) self.printers = [CloudClusterPrinterConfiguration(**c) if isinstance(c, dict) else c for c in self.configuration] @@ -96,8 +96,8 @@ class CloudClusterPrintJob(BaseModel): class CloudClusterStatus(BaseModel): def __init__(self, **kwargs): - self.printers: List[CloudClusterPrinter] = [] - self.print_jobs: List[CloudClusterPrintJob] = [] + self.printers = [] # type: List[CloudClusterPrinter] + self.print_jobs = [] # type: List[CloudClusterPrintJob] super().__init__(**kwargs) self.printers = [CloudClusterPrinter(**p) if isinstance(p, dict) else p for p in self.printers] @@ -106,25 +106,25 @@ class CloudClusterStatus(BaseModel): class JobUploadRequest(BaseModel): def __init__(self, **kwargs): - self.file_size: int = None - self.job_name: str = None + self.file_size = None # type: int + self.job_name = None # type: str super().__init__(**kwargs) class JobUploadResponse(BaseModel): def __init__(self, **kwargs): - self.download_url: str = None - self.job_id: str = None - self.job_name: str = None - self.slicing_details: str = None - self.status: str = None - self.upload_url: str = None + self.download_url = None # type: str + self.job_id = None # type: str + self.job_name = None # type: str + self.slicing_details = None # type: str + self.status = None # type: str + self.upload_url = None # type: str super().__init__(**kwargs) class PrintResponse(BaseModel): def __init__(self, **kwargs): - self.cluster_job_id: str = None - self.job_id: str = None - self.status: str = None + self.cluster_job_id = None # type: str + self.job_id = None # type: str + self.status = None # type: str super().__init__(**kwargs) From 15415dc3b98e70a2dd299c4d2cdef1a2065c1185 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 3 Dec 2018 16:53:52 +0100 Subject: [PATCH 0582/1240] Fix position of the tooltips for the settings Contributes to CURA-5941 --- resources/qml/Settings/SettingView.qml | 2 +- resources/themes/cura-light/theme.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index ff94c9168f..867d662edc 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -360,7 +360,7 @@ Item contextMenu.provider = provider contextMenu.popup(); } - onShowTooltip: base.showTooltip(delegate, Qt.point(- UM.Theme.getSize("default_arrow").width, 0), text) + onShowTooltip: base.showTooltip(delegate, Qt.point(- settingsView.x - UM.Theme.getSize("default_margin").width, 0), text) onHideTooltip: base.hideTooltip() onShowAllHiddenInheritedSettings: { diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 57532d91e5..3d0169dac9 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -35,7 +35,7 @@ "family": "Noto Sans" }, "default_italic": { - "size": 1.15, + "size": 1.0, "weight": 50, "italic": true, "family": "Noto Sans" From 5db6bd9a9bd7334e2f8d27de892094a39df806d9 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 3 Dec 2018 17:09:04 +0100 Subject: [PATCH 0583/1240] Send content type to API when uploading print job --- .../UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py | 9 +++++++-- plugins/UM3NetworkPrinting/src/Cloud/Models.py | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 3d577d0991..dbb5ebf263 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -129,7 +129,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): stream = io.StringIO() if file_format["mode"] == FileWriter.OutputMode.TextMode else io.BytesIO() writer.write(stream, nodes) - self._sendPrintJob(file_name + "." + file_format["extension"], stream) + self._sendPrintJob(file_name + "." + file_format["extension"], file_format["mime_type"], stream) # TODO: This is yanked right out of ClusterUM3OutputDevice, great candidate for a utility or base class def _determineFileFormat(self, file_handler) -> Optional[Dict[str, Union[str, int]]]: @@ -339,12 +339,13 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): model.updateOwner(job.owner) model.updateState(job.status) - def _sendPrintJob(self, file_name: str, stream: Union[io.StringIO, io.BytesIO]) -> None: + def _sendPrintJob(self, file_name: str, content_type: str, stream: Union[io.StringIO, io.BytesIO]) -> None: mesh = stream.getvalue() request = JobUploadRequest() request.job_name = file_name request.file_size = len(mesh) + request.content_type = content_type Logger.log("i", "Creating new cloud print job: %s", request.__dict__) self.put("{}/jobs/upload".format(self.CURA_API_ROOT), data = json.dumps({"data": request.__dict__}), @@ -355,6 +356,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): if status_code > 204 or not isinstance(response, dict) or "data" not in response: Logger.log("w", "Got unexpected response while trying to add print job to cluster: {}, {}" .format(status_code, response)) + self.writeError.emit() return # TODO: Multipart upload @@ -368,6 +370,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): if status_code > 204: Logger.logException("w", "Received unexpected response from the job upload: %s, %s.", status_code, bytes(reply.readAll()).decode()) + self.writeError.emit() return Logger.log("i", "Print job uploaded successfully: %s", reply.readAll()) @@ -379,7 +382,9 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): if status_code > 204 or not isinstance(response, dict) or "data" not in response: Logger.log("w", "Got unexpected response while trying to request printing: %s, %s", status_code, response) + self.writeError.emit() return print_response = PrintResponse(**response["data"]) Logger.log("i", "Print job requested successfully: %s", print_response.__dict__) + self.writeFinished.emit() diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models.py b/plugins/UM3NetworkPrinting/src/Cloud/Models.py index 7b9ad460c5..22a733c70e 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models.py @@ -108,6 +108,7 @@ class JobUploadRequest(BaseModel): def __init__(self, **kwargs): self.file_size = None # type: int self.job_name = None # type: str + self.content_type = None # type: str super().__init__(**kwargs) From 90ec3f6cf95e2d1d545320845c642f5af3901605 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 3 Dec 2018 17:22:21 +0100 Subject: [PATCH 0584/1240] Add TODO for progress messages --- plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index dbb5ebf263..28b219469a 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -359,6 +359,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self.writeError.emit() return + # TODO: add progress messages so we have visual feedback when uploading to cloud # TODO: Multipart upload job_response = JobUploadResponse(**response.get("data")) Logger.log("i", "Print job created successfully: %s", job_response.__dict__) From f320000ce5e5019d8f96af20b29aa266b54856f6 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 3 Dec 2018 17:23:44 +0100 Subject: [PATCH 0585/1240] Change default profile of Aurora and Alfawise printers Instead of the default layer height, we should change the default quality profile. This is necessary because the 'normal' quality profile doesn't define a layer height, so that should inherit from the 0.1mm default layer height. But if the printer turned the default into a 0.15mm layer height then that is wrong. Maybe we should let the normal quality profile overwrite it to 0.1mm, always? Contributes to issue CURA-5902. --- resources/definitions/alfawise_u20.def.json | 5 +---- resources/definitions/jgaurora_a1.def.json | 5 +---- resources/definitions/jgaurora_a5.def.json | 5 +---- resources/definitions/jgaurora_z_603s.def.json | 5 +---- 4 files changed, 4 insertions(+), 16 deletions(-) diff --git a/resources/definitions/alfawise_u20.def.json b/resources/definitions/alfawise_u20.def.json index 87726fec3d..de8525fa4d 100644 --- a/resources/definitions/alfawise_u20.def.json +++ b/resources/definitions/alfawise_u20.def.json @@ -7,7 +7,7 @@ "author": "Samuel Pinches", "manufacturer": "Alfawise", "file_formats": "text/x-gcode", - "preferred_quality_type": "fine", + "preferred_quality_type": "fast", "machine_extruder_trains": { "0": "alfawise_u20_extruder_0" @@ -53,9 +53,6 @@ "material_bed_temperature": { "default_value": 50 }, - "layer_height": { - "default_value": 0.15 - }, "layer_height_0": { "default_value": 0.2 }, diff --git a/resources/definitions/jgaurora_a1.def.json b/resources/definitions/jgaurora_a1.def.json index 4fd2eb4994..b9a921c311 100644 --- a/resources/definitions/jgaurora_a1.def.json +++ b/resources/definitions/jgaurora_a1.def.json @@ -7,7 +7,7 @@ "author": "Samuel Pinches", "manufacturer": "JGAurora", "file_formats": "text/x-gcode", - "preferred_quality_type": "fine", + "preferred_quality_type": "fast", "machine_extruder_trains": { "0": "jgaurora_a1_extruder_0" @@ -53,9 +53,6 @@ "material_bed_temperature": { "default_value": 67 }, - "layer_height": { - "default_value": 0.15 - }, "layer_height_0": { "default_value": 0.12 }, diff --git a/resources/definitions/jgaurora_a5.def.json b/resources/definitions/jgaurora_a5.def.json index 02d9a9db4f..d84a8440e6 100644 --- a/resources/definitions/jgaurora_a5.def.json +++ b/resources/definitions/jgaurora_a5.def.json @@ -9,7 +9,7 @@ "file_formats": "text/x-gcode", "platform": "jgaurora_a5.stl", "platform_offset": [-242, -101, 273], - "preferred_quality_type": "fine", + "preferred_quality_type": "fast", "machine_extruder_trains": { "0": "jgaurora_a5_extruder_0" @@ -55,9 +55,6 @@ "material_bed_temperature": { "default_value": 67 }, - "layer_height": { - "default_value": 0.15 - }, "layer_height_0": { "default_value": 0.12 }, diff --git a/resources/definitions/jgaurora_z_603s.def.json b/resources/definitions/jgaurora_z_603s.def.json index 59e0ff129c..3a78585240 100644 --- a/resources/definitions/jgaurora_z_603s.def.json +++ b/resources/definitions/jgaurora_z_603s.def.json @@ -7,7 +7,7 @@ "author": "Samuel Pinches", "manufacturer": "JGAurora", "file_formats": "text/x-gcode", - "preferred_quality_type": "fine", + "preferred_quality_type": "fast", "machine_extruder_trains": { "0": "jgaurora_z_603s_extruder_0" @@ -53,9 +53,6 @@ "material_bed_temperature": { "default_value": 55 }, - "layer_height": { - "default_value": 0.15 - }, "layer_height_0": { "default_value": 0.2 }, From 004405e5f97337c5acfafda2da0c75bc4a9e7004 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 3 Dec 2018 17:25:07 +0100 Subject: [PATCH 0586/1240] Change the colors to match the designs. Contributes to CURA-5941. --- .../PrintSetupSelectorContents.qml | 6 +-- resources/qml/Settings/SettingCategory.qml | 26 ----------- resources/themes/cura-light/styles.qml | 2 +- resources/themes/cura-light/theme.json | 46 +++++++++---------- 4 files changed, 27 insertions(+), 53 deletions(-) diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml index 77d1b59f4c..358cba8ad0 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml @@ -25,7 +25,7 @@ Item { id: header height: UM.Theme.getSize("print_setup_widget_header").height - color: UM.Theme.getColor("action_button_hovered") // TODO: It's not clear the color that we need to use here + color: UM.Theme.getColor("secondary") anchors { @@ -46,9 +46,9 @@ Item anchors { - topMargin: UM.Theme.getSize("sidebar_margin").height + topMargin: UM.Theme.getSize("default_margin").height left: parent.left - leftMargin: UM.Theme.getSize("narrow_margin").height + leftMargin: UM.Theme.getSize("default_margin").height } } diff --git a/resources/qml/Settings/SettingCategory.qml b/resources/qml/Settings/SettingCategory.qml index 9b78683e14..be93f8ffab 100644 --- a/resources/qml/Settings/SettingCategory.qml +++ b/resources/qml/Settings/SettingCategory.qml @@ -45,32 +45,6 @@ Button return UM.Theme.getColor("setting_category") } Behavior on color { ColorAnimation { duration: 50; } } - Rectangle - { - id: backgroundLiningRectangle - height: UM.Theme.getSize("default_lining").height - width: parent.width - anchors.bottom: parent.bottom - color: - { - if (!base.enabled) - { - return UM.Theme.getColor("setting_category_disabled_border") - } else if ((base.hovered || base.activeFocus) && base.checkable && base.checked) - { - return UM.Theme.getColor("setting_category_active_hover_border") - } else if (base.pressed || (base.checkable && base.checked)) - { - return UM.Theme.getColor("setting_category_active_border") - } else if (base.hovered || base.activeFocus) - { - return UM.Theme.getColor("setting_category_hover_border") - } else - { - return UM.Theme.getColor("setting_category_border") - } - } - } } signal showTooltip(string text) diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index eed5393a0b..66c000af62 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -393,7 +393,7 @@ QtObject implicitWidth: Theme.getSize("scrollbar").width radius: Math.round(implicitWidth / 2) - color: styleData.pressed ? Theme.getColor("scrollbar_handle_down") : styleData.hovered ? Theme.getColor("scrollbar_handle_hover") : Theme.getColor("scrollbar_handle"); + color: styleData.pressed ? Theme.getColor("scrollbar_handle_down") : styleData.hovered ? Theme.getColor("scrollbar_handle_hover") : Theme.getColor("scrollbar_handle") Behavior on color { ColorAnimation { duration: 50; } } } } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 3d0169dac9..8e440757aa 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -86,7 +86,7 @@ "primary_hover": [48, 182, 231, 255], "primary_text": [255, 255, 255, 255], "border": [127, 127, 127, 255], - "secondary": [240, 240, 240, 255], + "secondary": [245, 245, 245, 255], "secondary_shadow": [216, 216, 216, 255], "primary_button": [38, 113, 231, 255], @@ -183,25 +183,25 @@ "action_button_disabled_shadow": [228, 228, 228, 255], "scrollbar_background": [255, 255, 255, 255], - "scrollbar_handle": [31, 36, 39, 255], - "scrollbar_handle_hover": [12, 159, 227, 255], - "scrollbar_handle_down": [12, 159, 227, 255], + "scrollbar_handle": [10, 8, 80, 255], + "scrollbar_handle_hover": [50, 130, 255, 255], + "scrollbar_handle_down": [50, 130, 255, 255], "setting_category": [245, 245, 245, 255], "setting_category_disabled": [255, 255, 255, 255], - "setting_category_hover": [245, 245, 245, 255], + "setting_category_hover": [232, 242, 252, 255], "setting_category_active": [245, 245, 245, 255], - "setting_category_active_hover": [245, 245, 245, 255], - "setting_category_text": [31, 36, 39, 255], + "setting_category_active_hover": [232, 242, 252, 255], + "setting_category_text": [35, 35, 35, 255], "setting_category_disabled_text": [24, 41, 77, 101], - "setting_category_hover_text": [31, 36, 39, 255], - "setting_category_active_text": [31, 36, 39, 255], - "setting_category_active_hover_text": [31, 36, 39, 255], + "setting_category_hover_text": [35, 35, 35, 255], + "setting_category_active_text": [35, 35, 35, 255], + "setting_category_active_hover_text": [35, 35, 35, 255], "setting_category_border": [245, 245, 245, 255], "setting_category_disabled_border": [245, 245, 245, 255], - "setting_category_hover_border": [12, 159, 227, 255], - "setting_category_active_border": [245, 245, 245, 255], - "setting_category_active_hover_border": [12, 159, 227, 255], + "setting_category_hover_border": [50, 130, 255, 255], + "setting_category_active_border": [50, 130, 255, 255], + "setting_category_active_hover_border": [50, 130, 255, 255], "setting_control": [255, 255, 255, 255], "setting_control_selected": [31, 36, 39, 255], @@ -298,16 +298,16 @@ "xray_error": [255, 0, 0, 255], "layerview_ghost": [32, 32, 32, 96], - "layerview_none": [255, 255, 255, 255], - "layerview_inset_0": [255, 0, 0, 255], - "layerview_inset_x": [0, 255, 0, 255], - "layerview_skin": [255, 255, 0, 255], - "layerview_support": [0, 255, 255, 255], - "layerview_skirt": [0, 255, 255, 255], - "layerview_infill": [255, 192, 0, 255], - "layerview_support_infill": [0, 255, 255, 255], - "layerview_move_combing": [0, 0, 255, 255], - "layerview_move_retraction": [128, 128, 255, 255], + "layerview_none": [255, 255, 255, 255], + "layerview_inset_0": [255, 0, 0, 255], + "layerview_inset_x": [0, 255, 0, 255], + "layerview_skin": [255, 255, 0, 255], + "layerview_support": [0, 255, 255, 255], + "layerview_skirt": [0, 255, 255, 255], + "layerview_infill": [255, 192, 0, 255], + "layerview_support_infill": [0, 255, 255, 255], + "layerview_move_combing": [0, 0, 255, 255], + "layerview_move_retraction": [128, 128, 255, 255], "layerview_support_interface": [64, 192, 255, 255], "layerview_nozzle": [181, 166, 66, 50], From 9b8b91b6a41eef06b504f47474d213f54335a308 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 3 Dec 2018 17:34:08 +0100 Subject: [PATCH 0587/1240] More specific variable type for is_connected Contributes to issue CURA-5876. Co-Authored-By: Ghostkeeper --- resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index fc8b31f125..79b7c9bf66 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -102,7 +102,7 @@ Cura.ExpandableComponent height: implicitHeight //Required because ExpandableComponent will try to use this to determine the size of the background of the pop-up. spacing: UM.Theme.getSize("default_margin").height - property var is_connected: false //If current machine is connected to a printer. Only evaluated upon making popup visible. + property bool is_connected: false //If current machine is connected to a printer. Only evaluated upon making popup visible. onVisibleChanged: { is_connected = Cura.MachineManager.activeMachineNetworkKey !== "" && Cura.MachineManager.printerConnected //Re-evaluate. From d91efc656a28b4d5b94e9f2bff987a3c1ccca493 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 3 Dec 2018 17:38:31 +0100 Subject: [PATCH 0588/1240] Add some more todo's for UI messages --- .../src/Cloud/CloudOutputDevice.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 28b219469a..2f59b6aeea 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -13,6 +13,7 @@ from UM import i18nCatalog from UM.FileHandler.FileWriter import FileWriter from UM.FileHandler.FileHandler import FileHandler from UM.Logger import Logger +from UM.Message import Message from UM.OutputDevice import OutputDeviceError from UM.Scene.SceneNode import SceneNode from UM.Version import Version @@ -389,3 +390,17 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): print_response = PrintResponse(**response["data"]) Logger.log("i", "Print job requested successfully: %s", print_response.__dict__) self.writeFinished.emit() + + def _showUploadErrorMessage(self): + message = Message(self.I18N_CATALOG.i18nc( + "@info:status", "Sending new jobs (temporarily) blocked, still sending the previous print job.")) + message.show() + + def _showOrUpdateUploadProgressMessage(self, new_progress = 0): + # TODO: implement this + # See ClusterUM3OutputDevice for inspiration + pass + + def _showUploadSuccessMessage(self): + # TODO: implement this + pass From 0852d2ebefe7a6732124ead1498910feafafe845 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 3 Dec 2018 17:42:39 +0100 Subject: [PATCH 0589/1240] add one more TODO --- .../UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 7c10cb4e50..2df07fca77 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -132,7 +132,7 @@ class CloudOutputDeviceManager(NetworkClient): local_device_id = active_machine.getMetaDataEntry("um_network_key") if local_device_id: active_output_device = self._output_device_manager.getActiveDevice() - # We must find a match for the active machine and a cloud device + # TODO: We must find a match for the active machine and a cloud device stored_cluster_id = active_machine.getMetaDataEntry("um_cloud_cluster_id") if stored_cluster_id not in self._remote_clusters.keys(): From 8d6f109092619b7ff91ae88f6dc7781cc31e5e6d Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 3 Dec 2018 20:11:53 +0100 Subject: [PATCH 0590/1240] Set priority to 2 --- plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 2f59b6aeea..ee84762cf8 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -108,7 +108,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Set all the interface elements and texts for this output device. def _setInterfaceElements(self): - self.setPriority(3) + self.setPriority(2) # make sure we end up below the local networking and above 'save to file' self.setName(self._id) # TODO: how to name these? self.setShortDescription(self.I18N_CATALOG.i18nc("@action:button", "Print via Cloud")) From 08e1b4691b5791fe1ae19ccd56cbac226ae3813e Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 3 Dec 2018 20:19:16 +0100 Subject: [PATCH 0591/1240] Remove TODO --- plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index ee84762cf8..faca2472ad 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -110,7 +110,6 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def _setInterfaceElements(self): self.setPriority(2) # make sure we end up below the local networking and above 'save to file' self.setName(self._id) - # TODO: how to name these? self.setShortDescription(self.I18N_CATALOG.i18nc("@action:button", "Print via Cloud")) self.setDescription(self.I18N_CATALOG.i18nc("@properties:tooltip", "Print via Cloud")) self.setConnectionText(self.I18N_CATALOG.i18nc("@info:status", "Connected via Cloud")) From 5fdff1778261c2eb8a5267bca4711f50127a080e Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 3 Dec 2018 22:12:18 +0100 Subject: [PATCH 0592/1240] Add upload messages for UI feedback, needs some refactoring --- .../NetworkedPrinterOutputDevice.py | 5 +- .../src/Cloud/CloudOutputDevice.py | 98 ++++++++++++++----- .../UM3NetworkPrinting/src/Cloud/Models.py | 1 + 3 files changed, 76 insertions(+), 28 deletions(-) diff --git a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py index 0a799d4cd3..5677106782 100644 --- a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py +++ b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py @@ -180,13 +180,16 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): self._createNetworkManager() assert (self._manager is not None) - def put(self, target: str, data: Union[str, bytes], on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: + def put(self, target: str, data: Union[str, bytes], on_finished: Optional[Callable[[QNetworkReply], None]], + on_progress: Optional[Callable] = None) -> None: self._validateManager() request = self._createEmptyRequest(target) self._last_request_time = time() if self._manager is not None: reply = self._manager.put(request, data if isinstance(data, bytes) else data.encode()) self._registerOnFinishedCallback(reply, on_finished) + if on_progress is not None: + reply.uploadProgress.connect(on_progress) else: Logger.log("e", "Could not find manager.") diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index faca2472ad..db5ad21b06 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -71,6 +71,10 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # Properties to populate later on with received cloud data. self._print_jobs = [] # type: List[UM3PrintJobOutputModel] self._number_of_extruders = 2 # All networked printers are dual-extrusion Ultimaker machines. + + # We only allow a single upload at a time. + self._sending_job = False + self._progress_message = None # type: Optional[Message] @staticmethod def _parseReply(reply: QNetworkReply) -> Tuple[int, Union[None, str, bytes]]: @@ -117,14 +121,22 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Called when Cura requests an output device to receive a (G-code) file. def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mime_types: bool = False, file_handler: Optional[FileHandler] = None, **kwargs: str) -> None: + + # Show an error message if we're already sending a job. + if self._sending_job: + self._onUploadError(self.I18N_CATALOG.i18nc( + "@info:status", "Sending new jobs (temporarily) blocked, still sending the previous print job.")) + return + + # Indicate we have started sending a job. + self._sending_job = True self.writeStarted.emit(self) file_format = self._determineFileFormat(file_handler) writer = self._determineWriter(file_handler, file_format) - - # This function pauses with the yield, waiting on instructions on which printer it needs to print with. if not writer: Logger.log("e", "Missing file or mesh writer!") + self._onUploadError(self.I18N_CATALOG.i18nc("@info:status", "Could not export print job.")) return stream = io.StringIO() if file_format["mode"] == FileWriter.OutputMode.TextMode else io.BytesIO() @@ -186,11 +198,12 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def printers(self): return self._printers + ## Get remote print jobs. @pyqtProperty("QVariantList", notify = printJobsChanged) def printJobs(self)-> List[UM3PrintJobOutputModel]: return self._print_jobs - ## Get remote print jobs. + ## Get remote print jobs that are still in the print queue. @pyqtProperty("QVariantList", notify = printJobsChanged) def queuedPrintJobs(self) -> List[UM3PrintJobOutputModel]: return [print_job for print_job in self._print_jobs @@ -354,24 +367,27 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def _onPrintJobCreated(self, mesh: bytes, reply: QNetworkReply) -> None: status_code, response = self._parseReply(reply) if status_code > 204 or not isinstance(response, dict) or "data" not in response: - Logger.log("w", "Got unexpected response while trying to add print job to cluster: {}, {}" - .format(status_code, response)) - self.writeError.emit() + Logger.log("w", "Unexpected response while adding to queue: {}, {}".format(status_code, response)) + self._onUploadError(self.I18N_CATALOG.i18nc("@info:status", "Could not add print job to queue.")) return - # TODO: add progress messages so we have visual feedback when uploading to cloud # TODO: Multipart upload job_response = JobUploadResponse(**response.get("data")) Logger.log("i", "Print job created successfully: %s", job_response.__dict__) self.put(job_response.upload_url, data=mesh, - on_finished=lambda r: self._onPrintJobUploaded(job_response.job_id, r)) + on_finished=lambda r: self._onPrintJobUploaded(job_response.job_id, r), + on_progress = self._onUploadPrintJobProgress) + + def _onUploadPrintJobProgress(self, bytes_sent: int, bytes_total: int) -> None: + if bytes_total > 0: + self._updateUploadProgress(int((bytes_sent / bytes_total) * 100)) def _onPrintJobUploaded(self, job_id: str, reply: QNetworkReply) -> None: status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) if status_code > 204: - Logger.logException("w", "Received unexpected response from the job upload: %s, %s.", status_code, - bytes(reply.readAll()).decode()) - self.writeError.emit() + Logger.log("w", "Received unexpected response from the job upload: %s, %s.", status_code, + bytes(reply.readAll()).decode()) + self._onUploadError(self.I18N_CATALOG.i18nc("@info:status", "Could not add print job to queue.")) return Logger.log("i", "Print job uploaded successfully: %s", reply.readAll()) @@ -381,25 +397,53 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def _onPrintJobRequested(self, reply: QNetworkReply) -> None: status_code, response = self._parseReply(reply) if status_code > 204 or not isinstance(response, dict) or "data" not in response: - Logger.log("w", "Got unexpected response while trying to request printing: %s, %s", - status_code, response) - self.writeError.emit() + Logger.log("w", "Got unexpected response while trying to request printing: %s, %s", status_code, response) + self._onUploadError(self.I18N_CATALOG.i18nc("@info:status", "Could not add print job to queue.")) return print_response = PrintResponse(**response["data"]) Logger.log("i", "Print job requested successfully: %s", print_response.__dict__) - self.writeFinished.emit() + self._onUploadSuccess() - def _showUploadErrorMessage(self): - message = Message(self.I18N_CATALOG.i18nc( - "@info:status", "Sending new jobs (temporarily) blocked, still sending the previous print job.")) + def _updateUploadProgress(self, progress: int): + if not self._progress_message: + self._progress_message = Message( + text = self.I18N_CATALOG.i18nc("@info:status", "Sending data to remote cluster"), + title = self.I18N_CATALOG.i18nc("@info:title", "Sending Data..."), + progress = -1, + lifetime = 0, + dismissable = False, + use_inactivity_timer = False + ) + self._progress_message.setProgress(progress) + self._progress_message.show() + + def _resetUploadProgress(self): + if self._progress_message: + self._progress_message.hide() + self._progress_message = None + + def _onUploadError(self, message: str = None): + self._resetUploadProgress() + if message: + message = Message( + text = message, + title = self.I18N_CATALOG.i18nc("@info:title", "Error"), + lifetime = 10, + dismissable = True + ) + message.show() + self._sending_job = False # the upload has failed so we're not sending a job anymore + self.writeError.emit() + + def _onUploadSuccess(self): + self._resetUploadProgress() + message = Message( + text = self.I18N_CATALOG.i18nc("@info:status", "Print job was successfully sent to the printer."), + title = self.I18N_CATALOG.i18nc("@info:title", "Data Sent"), + lifetime = 5, + dismissable = True, + ) message.show() - - def _showOrUpdateUploadProgressMessage(self, new_progress = 0): - # TODO: implement this - # See ClusterUM3OutputDevice for inspiration - pass - - def _showUploadSuccessMessage(self): - # TODO: implement this - pass + self._sending_job = False # the upload has finished so we're not sending a job anymore + self.writeFinished.emit() diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models.py b/plugins/UM3NetworkPrinting/src/Cloud/Models.py index 22a733c70e..e1c25cc662 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models.py @@ -120,6 +120,7 @@ class JobUploadResponse(BaseModel): self.slicing_details = None # type: str self.status = None # type: str self.upload_url = None # type: str + self.content_type = None # type: str super().__init__(**kwargs) From c40d76f9ee6479b4c14c1b35bb061aaa282a1411 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 3 Dec 2018 23:13:24 +0100 Subject: [PATCH 0593/1240] Fix formatting according to Cura code style --- .../src/Cloud/CloudOutputDevice.py | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index db5ad21b06..e2957cacae 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -217,7 +217,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def _update(self) -> None: super()._update() Logger.log("i", "Calling the cloud cluster") - self.get("{root}/cluster/{cluster_id}/status".format(root=self.CLUSTER_API_ROOT, cluster_id=self._device_id), + self.get("{root}/cluster/{cluster_id}/status".format(root = self.CLUSTER_API_ROOT, + cluster_id = self._device_id), on_finished = self._onStatusCallFinished) ## Method called when HTTP request to status endpoint is finished. @@ -257,7 +258,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def _addPrinter(self, printer: CloudClusterPrinter) -> None: model = PrinterOutputModel( - PrinterOutputController(self), len(printer.configuration), firmware_version=printer.firmware_version + PrinterOutputController(self), len(printer.configuration), firmware_version = printer.firmware_version ) self._printers.append(model) self._updatePrinter(model, printer) @@ -291,10 +292,10 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): non_read_only_material_group_list = list(filter(lambda x: not x.is_read_only, material_group_list)) material_group = None if read_only_material_group_list: - read_only_material_group_list = sorted(read_only_material_group_list, key=lambda x: x.name) + read_only_material_group_list = sorted(read_only_material_group_list, key = lambda x: x.name) material_group = read_only_material_group_list[0] elif non_read_only_material_group_list: - non_read_only_material_group_list = sorted(non_read_only_material_group_list, key=lambda x: x.name) + non_read_only_material_group_list = sorted(non_read_only_material_group_list, key = lambda x: x.name) material_group = non_read_only_material_group_list[0] if material_group: @@ -304,15 +305,15 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): material_type = container.getMetaDataEntry("material") name = container.getName() else: - Logger.log("w", - "Unable to find material with guid {guid}. Using data as provided by cluster".format( - guid=material.guid)) + Logger.log("w", "Unable to find material with guid {guid}. Using data as provided by cluster" + .format(guid = material.guid)) color = material.color brand = material.brand material_type = material.material name = "Empty" if material.material == "empty" else "Unknown" - return MaterialOutputModel(guid=material.guid, type=material_type, brand=brand, color=color, name=name) + return MaterialOutputModel(guid = material.guid, type = material_type, brand = brand, color = color, + name = name) def _updatePrintJobs(self, jobs: List[CloudClusterPrintJob]) -> None: remote_jobs = {j.uuid: j for j in jobs} # type: Dict[str, CloudClusterPrintJob] @@ -374,8 +375,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # TODO: Multipart upload job_response = JobUploadResponse(**response.get("data")) Logger.log("i", "Print job created successfully: %s", job_response.__dict__) - self.put(job_response.upload_url, data=mesh, - on_finished=lambda r: self._onPrintJobUploaded(job_response.job_id, r), + self.put(job_response.upload_url, data = mesh, + on_finished = lambda r: self._onPrintJobUploaded(job_response.job_id, r), on_progress = self._onUploadPrintJobProgress) def _onUploadPrintJobProgress(self, bytes_sent: int, bytes_total: int) -> None: @@ -392,7 +393,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): Logger.log("i", "Print job uploaded successfully: %s", reply.readAll()) url = "{}/cluster/{}/print/{}".format(self.CLUSTER_API_ROOT, self._device_id, job_id) - self.post(url, data="", on_finished=self._onPrintJobRequested) + self.post(url, data = "", on_finished = self._onPrintJobRequested) def _onPrintJobRequested(self, reply: QNetworkReply) -> None: status_code, response = self._parseReply(reply) From b50e9427684d4bbc81eeca3cd7c7ef693749c7e1 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 3 Dec 2018 23:42:41 +0100 Subject: [PATCH 0594/1240] Describe TODO for association by hostname, only connect when online --- .../src/Cloud/CloudOutputDeviceManager.py | 39 +++++++++---------- .../UM3NetworkPrinting/src/Cloud/Models.py | 1 + 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 2df07fca77..6da6c4f4d7 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -20,8 +20,6 @@ from .Models import CloudCluster # # API spec is available on https://api.ultimaker.com/docs/connect/spec/. # -# TODO: figure out how to pair remote clusters, local networked clusters and local cura printer presets. -# TODO: for now we just have multiple output devices if the cluster is available both locally and remote. class CloudOutputDeviceManager(NetworkClient): # The cloud URL to use for remote clusters. @@ -42,8 +40,9 @@ class CloudOutputDeviceManager(NetworkClient): self._account.loginStateChanged.connect(self._getRemoteClusters) # When switching machines we check if we have to activate a remote cluster. - application.globalContainerStackChanged.connect(self._activeMachineChanged) - + application.globalContainerStackChanged.connect(self._connectToActiveMachine) + + # TODO: fix this # Periodically check all remote clusters for the authenticated user. # self._update_clusters_thread = Thread(target=self._updateClusters, daemon=True) # self._update_clusters_thread.start() @@ -89,15 +88,13 @@ class CloudOutputDeviceManager(NetworkClient): # Add an output device for each new remote cluster. for cluster_id in found_cluster_ids.difference(known_cluster_ids): - self._addCloudOutputDevice(found_clusters[cluster_id]) + if found_clusters[cluster_id].is_online: + self._addCloudOutputDevice(found_clusters[cluster_id]) # Remove output devices that are gone for cluster_id in known_cluster_ids.difference(found_cluster_ids): self._removeCloudOutputDevice(found_clusters[cluster_id]) - # For testing we add a dummy device: - # self._addCloudOutputDevice(CloudCluster(cluster_id = "LJ0tciiuZZjarrXAvFLEZ6ox4Cvx8FvtXUlQv4vIhV6w")) - @staticmethod def _parseStatusResponse(reply: QNetworkReply) -> Dict[str, CloudCluster]: try: @@ -116,7 +113,9 @@ class CloudOutputDeviceManager(NetworkClient): device = CloudOutputDevice(cluster.cluster_id) self._output_device_manager.addOutputDevice(device) self._remote_clusters[cluster.cluster_id] = device - device.connect() # TODO: Only connect the current device + if cluster.is_online: + # We found a new online cluster, we might need to connect to it. + self._connectToActiveMachine() ## Remove a CloudOutputDevice def _removeCloudOutputDevice(self, cluster: CloudCluster): @@ -124,20 +123,20 @@ class CloudOutputDeviceManager(NetworkClient): del self._remote_clusters[cluster.cluster_id] ## Callback for when the active machine was changed by the user. - def _activeMachineChanged(self): + def _connectToActiveMachine(self) -> None: active_machine = CuraApplication.getInstance().getGlobalContainerStack() if not active_machine: return - - local_device_id = active_machine.getMetaDataEntry("um_network_key") - if local_device_id: - active_output_device = self._output_device_manager.getActiveDevice() - # TODO: We must find a match for the active machine and a cloud device - + + # Check if the stored cluster_id for the active machine is in our list of remote clusters. stored_cluster_id = active_machine.getMetaDataEntry("um_cloud_cluster_id") - if stored_cluster_id not in self._remote_clusters.keys(): - # Currently authenticated user does not have access to stored cluster or no user is signed in. + if stored_cluster_id in self._remote_clusters.keys(): + self._remote_clusters.get(stored_cluster_id).connect() return - # We found the active machine as remote cluster so let's connect to it. - self._remote_clusters.get(stored_cluster_id).connect() + # TODO: See if this cloud cluster still has to be associated to the active machine. + # TODO: We have to get a common piece of data, like local network hostname, from the active machine and + # TODO: cloud cluster and then set the "um_cloud_cluster_id" meta data key on the active machine. + # TODO: If so, we can also immediate connect to it. + # active_machine.setMetaDataEntry("um_cloud_cluster_id", "") + # self._remote_clusters.get(stored_cluster_id).connect() diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models.py b/plugins/UM3NetworkPrinting/src/Cloud/Models.py index e1c25cc662..d7cb68e5d3 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models.py @@ -13,6 +13,7 @@ class CloudCluster(BaseModel): self.host_name = None # type: str self.host_version = None # type: str self.status = None # type: str + self.is_online = None # type: bool super().__init__(**kwargs) def validate(self): From 1bcabc6f42867fb8a352255fd572f67f66f84fbe Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 3 Dec 2018 23:44:52 +0100 Subject: [PATCH 0595/1240] Fix is_online logic --- .../UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 6da6c4f4d7..06beb8bce4 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -87,6 +87,7 @@ class CloudOutputDeviceManager(NetworkClient): found_cluster_ids = set(found_clusters.keys()) # Add an output device for each new remote cluster. + # We only add when is_online as we don't want the option in the drop down if the cluster is not online. for cluster_id in found_cluster_ids.difference(known_cluster_ids): if found_clusters[cluster_id].is_online: self._addCloudOutputDevice(found_clusters[cluster_id]) @@ -113,9 +114,7 @@ class CloudOutputDeviceManager(NetworkClient): device = CloudOutputDevice(cluster.cluster_id) self._output_device_manager.addOutputDevice(device) self._remote_clusters[cluster.cluster_id] = device - if cluster.is_online: - # We found a new online cluster, we might need to connect to it. - self._connectToActiveMachine() + self._connectToActiveMachine() ## Remove a CloudOutputDevice def _removeCloudOutputDevice(self, cluster: CloudCluster): From 6af1e72ea330c009feb3a3834233481a442db2c7 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 4 Dec 2018 07:45:34 +0100 Subject: [PATCH 0596/1240] Show progress bar while autoslice is preparing Previously it would say 'preparing to slice'. Now it just says 'auto slicing' but with an indeterminate progress bar. Only the indeterminate progress bar just appears empty right now... Contributes to issue CURA-5997. --- resources/qml/ActionPanel/SliceProcessWidget.qml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index 3329ac4b23..14e149dddb 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -44,7 +44,7 @@ Column { id: autoSlicingLabel width: parent.width - visible: prepareButtons.autoSlice && widget.backendState == UM.Backend.Processing + visible: prepareButtons.autoSlice && (widget.backendState == UM.Backend.Processing || widget.backendState == UM.Backend.NotStarted) text: catalog.i18nc("@label:PrintjobStatus", "Auto slicing...") color: UM.Theme.getColor("text") @@ -71,7 +71,8 @@ Column width: parent.width height: UM.Theme.getSize("progressbar").height value: progress - visible: widget.backendState == UM.Backend.Processing + indeterminate: widget.backendState == UM.Backend.NotStarted + visible: (widget.backendState == UM.Backend.Processing || (prepareButtons.autoSlice && widget.backendState == UM.Backend.NotStarted)) background: Rectangle { From 98ec6c66f2d486890b2d31c68bb8bd09e3bd911e Mon Sep 17 00:00:00 2001 From: THeijmans Date: Tue, 4 Dec 2018 08:25:05 +0100 Subject: [PATCH 0597/1240] CC06 profile fix removed line adding 10degrees to many profiles --- resources/variants/ultimaker_s5_cc06.inst.cfg | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/variants/ultimaker_s5_cc06.inst.cfg b/resources/variants/ultimaker_s5_cc06.inst.cfg index d41d08118a..7adf7ab7a0 100644 --- a/resources/variants/ultimaker_s5_cc06.inst.cfg +++ b/resources/variants/ultimaker_s5_cc06.inst.cfg @@ -13,7 +13,6 @@ brim_width = 7 machine_nozzle_cool_down_speed = 0.9 machine_nozzle_id = CC 0.6 machine_nozzle_size = 0.6 -material_print_temperature = =default_material_print_temperature + 10 raft_acceleration = =acceleration_print raft_airgap = 0.3 raft_base_thickness = =resolveOrValue('layer_height_0') * 1.2 From e9f8757fac1b5740f7daab3dea3288940f092400 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 4 Dec 2018 09:12:57 +0100 Subject: [PATCH 0598/1240] Change the info button in the action panel according to the designs Adjust the padding in the preview shortcut. --- .../qml/ActionPanel/OutputProcessWidget.qml | 2 + .../ActionPanel/PrintInformationWidget.qml | 3 -- resources/themes/cura-light/icons/info.svg | 48 ------------------- 3 files changed, 2 insertions(+), 51 deletions(-) delete mode 100644 resources/themes/cura-light/icons/info.svg diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml index 45cb1fdb41..6ab8dc6fbb 100644 --- a/resources/qml/ActionPanel/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -112,6 +112,8 @@ Column id: previewStageShortcut height: UM.Theme.getSize("action_panel_button").height + leftPadding: UM.Theme.getSize("default_margin").width + rightPadding: UM.Theme.getSize("default_margin").width text: catalog.i18nc("@button", "Preview") onClicked: UM.Controller.setActiveStage("PreviewStage") diff --git a/resources/qml/ActionPanel/PrintInformationWidget.qml b/resources/qml/ActionPanel/PrintInformationWidget.qml index 82707576e0..25e380dea8 100644 --- a/resources/qml/ActionPanel/PrintInformationWidget.qml +++ b/resources/qml/ActionPanel/PrintInformationWidget.qml @@ -11,9 +11,6 @@ UM.RecolorImage { id: widget - //implicitHeight: UM.Theme.getSize("section_icon").height - //implicitWidth: UM.Theme.getSize("section_icon").width - source: UM.Theme.getIcon("info") width: UM.Theme.getSize("section_icon").width height: UM.Theme.getSize("section_icon").height diff --git a/resources/themes/cura-light/icons/info.svg b/resources/themes/cura-light/icons/info.svg deleted file mode 100644 index 97e4fc4f35..0000000000 --- a/resources/themes/cura-light/icons/info.svg +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From c1f5c00a7e088fee409d7c7282607faf3976707f Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 4 Dec 2018 09:18:45 +0100 Subject: [PATCH 0599/1240] Forgot to add the icon --- resources/themes/cura-light/icons/info.svg | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 resources/themes/cura-light/icons/info.svg diff --git a/resources/themes/cura-light/icons/info.svg b/resources/themes/cura-light/icons/info.svg new file mode 100644 index 0000000000..9896b3dac8 --- /dev/null +++ b/resources/themes/cura-light/icons/info.svg @@ -0,0 +1,13 @@ + + + + Icon/ info + Created with Sketch. + + + + + + + + \ No newline at end of file From 57a852a15ae0ff9e292e3eff574c067399a29777 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 4 Dec 2018 09:19:08 +0100 Subject: [PATCH 0600/1240] Don't show the background if there are less than 2 extruders to show in the Toolbar It caused a very tiny rectangle if the printer has only 1 extruder. Contributes to CURA-5984. --- resources/qml/Toolbar.qml | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/resources/qml/Toolbar.qml b/resources/qml/Toolbar.qml index 07522dd535..5fbddea9ac 100644 --- a/resources/qml/Toolbar.qml +++ b/resources/qml/Toolbar.qml @@ -115,6 +115,7 @@ Item } radius: UM.Theme.getSize("default_radius").width color: UM.Theme.getColor("lining") + visible: extrudersModel.items.length > 1 } Column @@ -131,8 +132,7 @@ Item id: extruders width: childrenRect.width height: childrenRect.height - property var _model: Cura.ExtrudersModel { id: extrudersModel } - model: _model.items.length > 1 ? _model : 0 + model: extrudersModel.items.length > 1 ? extrudersModel : 0 delegate: ExtruderButton { @@ -144,13 +144,18 @@ Item } } + Cura.ExtrudersModel + { + id: extrudersModel + } + UM.PointingRectangle { - id: panelBorder; + id: panelBorder - anchors.left: parent.right; - anchors.leftMargin: UM.Theme.getSize("default_margin").width; - anchors.top: base.top; + anchors.left: parent.right + anchors.leftMargin: UM.Theme.getSize("default_margin").width + anchors.top: base.top anchors.topMargin: base.activeY z: buttons.z - 1 @@ -161,14 +166,14 @@ Item { if (panel.item && panel.width > 0) { - return Math.max(panel.width + 2 * UM.Theme.getSize("default_margin").width); + return Math.max(panel.width + 2 * UM.Theme.getSize("default_margin").width) } else { return 0; } } - height: panel.item ? panel.height + 2 * UM.Theme.getSize("default_margin").height : 0; + height: panel.item ? panel.height + 2 * UM.Theme.getSize("default_margin").height : 0 opacity: panel.item && panel.width > 0 ? 1 : 0 Behavior on opacity { NumberAnimation { duration: 100 } } @@ -186,11 +191,11 @@ Item { id: panel - x: UM.Theme.getSize("default_margin").width; - y: UM.Theme.getSize("default_margin").height; + x: UM.Theme.getSize("default_margin").width + y: UM.Theme.getSize("default_margin").height source: UM.ActiveTool.valid ? UM.ActiveTool.activeToolPanel : "" - enabled: UM.Controller.toolsEnabled; + enabled: UM.Controller.toolsEnabled } } From 12b3f0088d49c2fb064f775a6c6d26177a32f41d Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 4 Dec 2018 10:07:43 +0100 Subject: [PATCH 0601/1240] Add content type to file upload --- cura/PrinterOutput/NetworkedPrinterOutputDevice.py | 5 +++-- plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py index 5677106782..72b6319020 100644 --- a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py +++ b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py @@ -180,10 +180,11 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): self._createNetworkManager() assert (self._manager is not None) - def put(self, target: str, data: Union[str, bytes], on_finished: Optional[Callable[[QNetworkReply], None]], + def put(self, target: str, data: Union[str, bytes], content_type: str = None, + on_finished: Optional[Callable[[QNetworkReply], None]] = None, on_progress: Optional[Callable] = None) -> None: self._validateManager() - request = self._createEmptyRequest(target) + request = self._createEmptyRequest(target, content_type = content_type) self._last_request_time = time() if self._manager is not None: reply = self._manager.put(request, data if isinstance(data, bytes) else data.encode()) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index e2957cacae..63a109cf5c 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -375,7 +375,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # TODO: Multipart upload job_response = JobUploadResponse(**response.get("data")) Logger.log("i", "Print job created successfully: %s", job_response.__dict__) - self.put(job_response.upload_url, data = mesh, + self.put(job_response.upload_url, data = mesh, content_type = job_response.content_type, on_finished = lambda r: self._onPrintJobUploaded(job_response.job_id, r), on_progress = self._onUploadPrintJobProgress) From 0363c1257cf947aff14b2543a76edc7d4cd08b20 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Tue, 4 Dec 2018 10:18:09 +0100 Subject: [PATCH 0602/1240] Improve exposed progress prop Contributes to CL-1153 --- cura/PrinterOutput/PrintJobOutputModel.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cura/PrinterOutput/PrintJobOutputModel.py b/cura/PrinterOutput/PrintJobOutputModel.py index 604fd8e0b8..256c9dffe9 100644 --- a/cura/PrinterOutput/PrintJobOutputModel.py +++ b/cura/PrinterOutput/PrintJobOutputModel.py @@ -133,9 +133,8 @@ class PrintJobOutputModel(QObject): @pyqtProperty(float, notify = timeElapsedChanged) def progress(self) -> float: result = self.timeElapsed / self.timeTotal - if result > 1.0: - result = 1.0 - return result + # Never get a progress past 1.0 + return min(result, 1.0) @pyqtProperty(str, notify=stateChanged) def state(self) -> str: From ab245bbff6fe76d0647f9a8655bf0abbb3ee5595 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Tue, 4 Dec 2018 10:23:26 +0100 Subject: [PATCH 0603/1240] Make "finishes at" single translatable string Contributes to CL-1153 --- .../resources/qml/MonitorPrintJobProgressBar.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml index a7055f4c52..4ca3c24d87 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml @@ -103,7 +103,7 @@ Item case "queued": return catalog.i18nc("@label:status", "Action required") default: - return catalog.i18nc("@label:status", "Finishes ") + OutputDevice.getDateCompleted( printJob.timeRemaining ) + " at " + OutputDevice.getTimeCompleted( printJob.timeRemaining ) + return catalog.i18nc("@label:status", "Finishes %1 at %2".arg(OutputDevice.getDateCompleted( printJob.timeRemaining ), OutputDevice.getTimeCompleted( printJob.timeRemaining ))) } } width: contentWidth From 9ec7428620e4ea31b1b172aeda86a29db28fd7c8 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 4 Dec 2018 10:54:30 +0100 Subject: [PATCH 0604/1240] Fix setting visiblity current index CURA-5981 --- resources/qml/Preferences/SettingVisibilityPage.qml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/resources/qml/Preferences/SettingVisibilityPage.qml b/resources/qml/Preferences/SettingVisibilityPage.qml index e319069502..1b964cad0c 100644 --- a/resources/qml/Preferences/SettingVisibilityPage.qml +++ b/resources/qml/Preferences/SettingVisibilityPage.qml @@ -115,15 +115,16 @@ UM.PreferencesPage currentIndex: { + var idx = -1; for(var i = 0; i < settingVisibilityPresetsModel.items.length; ++i) { if(settingVisibilityPresetsModel.items[i].presetId == settingVisibilityPresetsModel.activePreset) { - currentIndex = i; - return; + idx = i; + break; } } - return -1 + return idx; } onActivated: From 249a90199bd2da38aac6974a03a1a07f624b0acf Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Tue, 4 Dec 2018 11:08:01 +0100 Subject: [PATCH 0605/1240] Improve printer status handling Contributes to CL-1153 --- .../resources/qml/MonitorPrintJobProgressBar.qml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml index 4ca3c24d87..0d159af21c 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml @@ -90,9 +90,11 @@ Item return catalog.i18nc("@label:status", "Finished") case "sent_to_printer": return catalog.i18nc("@label:status", "Preparing...") - case "aborting": + case "pre_print": + return catalog.i18nc("@label:status", "Preparing...") + case "aborting": // NOTE: Doesn't exist but maybe should someday return catalog.i18nc("@label:status", "Aborting...") - case "aborted": + case "aborted": // NOTE: Unused, see above return catalog.i18nc("@label:status", "Aborted") case "pausing": return catalog.i18nc("@label:status", "Pausing...") From 96b9c7f3ea045aaecf81fb3d79c72929f0ad8ce5 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Tue, 4 Dec 2018 11:40:56 +0100 Subject: [PATCH 0606/1240] Fix multi-argument i18n string Contributes to CL-1153 --- .../resources/qml/MonitorPrintJobProgressBar.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml index 0d159af21c..88418516ed 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml @@ -78,7 +78,7 @@ Item { if (!printJob) { - return ""; + return "" } switch (printJob.state) { @@ -105,7 +105,7 @@ Item case "queued": return catalog.i18nc("@label:status", "Action required") default: - return catalog.i18nc("@label:status", "Finishes %1 at %2".arg(OutputDevice.getDateCompleted( printJob.timeRemaining ), OutputDevice.getTimeCompleted( printJob.timeRemaining ))) + return catalog.i18nc("@label:status", "Finishes %1 at %2".arg(OutputDevice.getDateCompleted( printJob.timeRemaining )).arg(OutputDevice.getTimeCompleted( printJob.timeRemaining ))) } } width: contentWidth From 692686597cf9204748a25c38fc11392712e74ae4 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 4 Dec 2018 12:07:31 +0100 Subject: [PATCH 0607/1240] Fix an issues that didn't calculate the correct height of the popup when the custom mode was selected from previous runs. Contributes to CURA-5941. --- resources/qml/ExpandableComponent.qml | 42 ++++++++-------- .../PrintSetupSelectorContents.qml | 49 ++++++++++--------- 2 files changed, 48 insertions(+), 43 deletions(-) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 82747d1c5b..df7604015e 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -25,10 +25,7 @@ Item property alias headerItem: headerItemLoader.sourceComponent // The popupItem holds the QML item that is shown when the "open" button is pressed - property var popupItem - - // The popupItem holds the QML item that is shown when the "open" button is pressed - property var componentItem + property alias popupItem: popup.contentItem property color popupBackgroundColor: UM.Theme.getColor("action_button") @@ -87,23 +84,6 @@ Item } } - onPopupItemChanged: - { - // Since we want the size of the popup to be set by the size of the content, - // we need to do it like this. - popup.width = popupItem.width + 2 * popup.padding - popup.height = popupItem.height + 2 * popup.padding - popup.contentItem = popupItem - } - - Connections - { - // Since it could be that the popup is dynamically populated, we should also take these changes into account. - target: popupItem - onWidthChanged: popup.width = popupItem.width + 2 * popup.padding - onHeightChanged: popup.height = popupItem.height + 2 * popup.padding - } - implicitHeight: 100 * screenScaleFactor implicitWidth: 400 * screenScaleFactor @@ -202,5 +182,25 @@ Item border.color: UM.Theme.getColor("lining") radius: UM.Theme.getSize("default_radius").width } + + contentItem: Item { } + + onContentItemChanged: + { + // Since we want the size of the popup to be set by the size of the content, + // we need to do it like this. + popup.width = contentItem.width + 2 * popup.padding + popup.height = contentItem.height + 2 * popup.padding + } + } + + // DO NOT MOVE UP IN THE CODE: This connection has to be here, after the definition of the Popup item. + // Apparently the order in which these are handled matters and so the height is correctly updated if this is here. + Connections + { + // Since it could be that the popup is dynamically populated, we should also take these changes into account. + target: popup.contentItem + onWidthChanged: popup.width = popup.contentItem.width + 2 * popup.padding + onHeightChanged: popup.height = popup.contentItem.height + 2 * popup.padding } } diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml index 358cba8ad0..a2856e1fe5 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml @@ -17,9 +17,26 @@ Item width: UM.Theme.getSize("print_setup_widget").width - 2 * UM.Theme.getSize("default_margin").width height: childrenRect.height - property int currentModeIndex: -1 + enum Mode + { + Recommended = 0, + Custom = 1 + } + + // Set the current mode index to the value that is stored in the preferences or Recommended mode otherwise. + property int currentModeIndex: + { + var index = Math.round(UM.Preferences.getValue("cura/active_mode")) + + if(index != null && !isNaN(index)) + { + return index + } + return PrintSetupSelectorContents.Mode.Recommended + } onCurrentModeIndexChanged: UM.Preferences.setValue("cura/active_mode", currentModeIndex) + // Header of the popup Rectangle { @@ -93,7 +110,9 @@ Item Item { id: contents - height: currentModeIndex == 0 ? recommendedPrintSetup.height : customPrintSetup.height + // Use the visible property instead of checking the currentModeIndex. That creates a binding that + // evaluates the new height every time the visible property changes. + height: recommendedPrintSetup.visible ? recommendedPrintSetup.height : customPrintSetup.height anchors { @@ -111,7 +130,7 @@ Item right: parent.right top: parent.top } - visible: currentModeIndex == 0 + visible: currentModeIndex == PrintSetupSelectorContents.Mode.Recommended } CustomPrintSetup @@ -123,7 +142,7 @@ Item right: parent.right top: parent.top } - visible: currentModeIndex == 1 + visible: currentModeIndex == PrintSetupSelectorContents.Mode.Custom } } @@ -160,8 +179,8 @@ Item rightPadding: UM.Theme.getSize("default_margin").width text: catalog.i18nc("@button", "Recommended") iconSource: UM.Theme.getIcon("arrow_left") - visible: currentModeIndex == 1 - onClicked: currentModeIndex = 0 + visible: currentModeIndex == PrintSetupSelectorContents.Mode.Custom + onClicked: currentModeIndex = PrintSetupSelectorContents.Mode.Recommended } Cura.SecondaryButton @@ -174,22 +193,8 @@ Item text: catalog.i18nc("@button", "Custom") iconSource: UM.Theme.getIcon("arrow_right") iconOnRightSide: true - visible: currentModeIndex == 0 - onClicked: currentModeIndex = 1 - } - } - - Component.onCompleted: - { - var index = Math.round(UM.Preferences.getValue("cura/active_mode")) - - if(index != null && !isNaN(index)) - { - currentModeIndex = index - } - else - { - currentModeIndex = 0 + visible: currentModeIndex == PrintSetupSelectorContents.Mode.Recommended + onClicked: currentModeIndex = PrintSetupSelectorContents.Mode.Custom } } } \ No newline at end of file From 97607419cfaddaa0403e8c48775fcb2530dcf114 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 4 Dec 2018 12:14:21 +0100 Subject: [PATCH 0608/1240] Start with some fakes for monitor page --- .../src/Cloud/CloudOutputDevice.py | 31 ++++++++++++++++++- .../src/Cloud/CloudOutputDeviceManager.py | 1 + 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 63a109cf5c..747d911407 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -6,7 +6,7 @@ import os from json import JSONDecodeError from typing import List, Optional, Dict, cast, Union, Tuple -from PyQt5.QtCore import QObject, pyqtSignal, QUrl, pyqtProperty +from PyQt5.QtCore import QObject, pyqtSignal, QUrl, pyqtProperty, pyqtSlot from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest from UM import i18nCatalog @@ -209,6 +209,16 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): return [print_job for print_job in self._print_jobs if print_job.state == "queued" or print_job.state == "error"] + ## Get remote print jobs that are assigned to a printer. + @pyqtProperty("QVariantList", notify = printJobsChanged) + def activePrintJobs(self) -> List[UM3PrintJobOutputModel]: + return [print_job for print_job in self._print_jobs if + print_job.assignedPrinter is not None and print_job.state != "queued"] + + @pyqtProperty(bool, notify = printJobsChanged) + def receivedPrintJobs(self) -> bool: + return not self._sending_job + ## Called when the connection to the cluster changes. def connect(self) -> None: super().connect() @@ -448,3 +458,22 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): message.show() self._sending_job = False # the upload has finished so we're not sending a job anymore self.writeFinished.emit() + + ## TODO: The following methods are required by the monitor page QML, but are not actually available using cloud. + # TODO: We fake the methods here to not break the monitor page. + + @pyqtProperty(QObject, notify = printersChanged) + def activePrinter(self) -> Optional[PrinterOutputModel]: + return self._printers[0] or None + + @pyqtSlot(QObject) + def setActivePrinter(self, printer: Optional[PrinterOutputModel]) -> None: + pass + + @pyqtProperty(QUrl, notify = printersChanged) + def activeCameraUrl(self) -> "QUrl": + return QUrl() + + @pyqtSlot(QUrl) + def setActiveCameraUrl(self, camera_url: "QUrl") -> None: + pass diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 06beb8bce4..bc871ec7ac 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -114,6 +114,7 @@ class CloudOutputDeviceManager(NetworkClient): device = CloudOutputDevice(cluster.cluster_id) self._output_device_manager.addOutputDevice(device) self._remote_clusters[cluster.cluster_id] = device + device.connect() # TODO: remove this self._connectToActiveMachine() ## Remove a CloudOutputDevice From 894c69685a740641b62ab7431b338cd985e11009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Tue, 4 Dec 2018 13:06:27 +0100 Subject: [PATCH 0609/1240] Periodically update the remote clusters and printjobs --- .../src/Cloud/CloudOutputDeviceManager.py | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 06beb8bce4..e50cd6540c 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -8,6 +8,7 @@ from typing import Dict, Optional from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply from UM.Logger import Logger +from UM.Signal import Signal from cura.CuraApplication import CuraApplication from cura.NetworkClient import NetworkClient @@ -42,10 +43,14 @@ class CloudOutputDeviceManager(NetworkClient): # When switching machines we check if we have to activate a remote cluster. application.globalContainerStackChanged.connect(self._connectToActiveMachine) - # TODO: fix this # Periodically check all remote clusters for the authenticated user. - # self._update_clusters_thread = Thread(target=self._updateClusters, daemon=True) - # self._update_clusters_thread.start() + # This is done by emitting to _on_cluster_received by _update_clusters_thread + # The thread is only started after the user is authenticated, otherwise the api call results in + # an authentication error + self._on_cluster_received = Signal() + self._on_cluster_received.connect(self._getRemoteClusters) + self._update_clusters_thread = Thread(target=self._updateClusters, daemon=True) + ## Override _createEmptyRequest to add the needed authentication header for talking to the Ultimaker Cloud API. def _createEmptyRequest(self, path: str, content_type: Optional[str] = "application/json") -> QNetworkRequest: @@ -59,13 +64,25 @@ class CloudOutputDeviceManager(NetworkClient): ## Update the clusters def _updateClusters(self) -> None: while True: - self._getRemoteClusters() - sleep(self.CHECK_CLUSTER_INTERVAL) - + + # Stop if the application is shutting down + if CuraApplication.getInstance().isShuttingDown(): + return + + self._on_cluster_received.emit() + sleep(5) + ## Gets all remote clusters from the API. def _getRemoteClusters(self) -> None: Logger.log("i", "Retrieving remote clusters") - self.get("/clusters", on_finished = self._onGetRemoteClustersFinished) + if self._account.isLoggedIn: + self.get("/clusters", on_finished = self._onGetRemoteClustersFinished) + + # Only start the polling thread after the user is authenticated + # The first call to _getRemoteClusters comes from self._account.loginStateChanged + if not self._update_clusters_thread.is_alive(): + self._update_clusters_thread.start() + ## Callback for when the request for getting the clusters. is finished. def _onGetRemoteClustersFinished(self, reply: QNetworkReply) -> None: From e159cbdb1a8124ceac8c897d39a96556ba35da5a Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 4 Dec 2018 13:14:12 +0100 Subject: [PATCH 0610/1240] Rename all the references from 'popup' to 'content' Contributes to CURA-5941. --- .../SimulationViewMenuComponent.qml | 2 +- resources/qml/ExpandableComponent.qml | 82 ++++++++++--------- .../QuickConfigurationSelector.qml | 2 +- .../PrintSetupSelector/PrintSetupSelector.qml | 8 +- .../qml/PrinterSelector/MachineSelector.qml | 6 +- resources/qml/ViewsSelector.qml | 6 +- 6 files changed, 57 insertions(+), 49 deletions(-) diff --git a/plugins/SimulationView/SimulationViewMenuComponent.qml b/plugins/SimulationView/SimulationViewMenuComponent.qml index 53b64afb47..a9eb157f48 100644 --- a/plugins/SimulationView/SimulationViewMenuComponent.qml +++ b/plugins/SimulationView/SimulationViewMenuComponent.qml @@ -45,7 +45,7 @@ Cura.ExpandableComponent verticalAlignment: Text.AlignVCenter } - popupItem: Column + contentItem: Column { id: viewSettings diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index df7604015e..0b4076c04a 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -6,40 +6,48 @@ import Cura 1.0 as Cura import QtGraphicalEffects 1.0 // For the dropshadow -// The expandable component has 3 major sub components: +// The expandable component has 2 major sub components: // * The headerItem; Always visible and should hold some info about what happens if the component is expanded -// * The popupItem; The content that needs to be shown if the component is expanded. -// * The icon; An icon that is displayed on the right of the drawer. +// * The contentItem; The content that needs to be shown if the component is expanded. Item { id: base - // Enumeration with the different possible alignments of the popup with respect of the headerItem - enum PopupAlignment + // Enumeration with the different possible alignments of the content with respect of the headerItem + enum ContentAlignment { AlignLeft, AlignRight } + enum ContentType + { + Floating, + Fixed + } + // The headerItem holds the QML item that is always displayed. property alias headerItem: headerItemLoader.sourceComponent - // The popupItem holds the QML item that is shown when the "open" button is pressed - property alias popupItem: popup.contentItem + // The contentItem holds the QML item that is shown when the "open" button is pressed + property alias contentItem: content.contentItem - property color popupBackgroundColor: UM.Theme.getColor("action_button") + // Defines the type of the contents + property int contentType: ExpandableComponent.ContentType.Floating + + property color contentBackgroundColor: UM.Theme.getColor("action_button") property color headerBackgroundColor: UM.Theme.getColor("action_button") property color headerHoverColor: UM.Theme.getColor("action_button_hovered") - // Defines the alignment of the popup with respect of the headerItem, by default to the right - property int popupAlignment: ExpandableComponent.PopupAlignment.AlignRight + // Defines the alignment of the content with respect of the headerItem, by default to the right + property int contentAlignment: ExpandableComponent.ContentAlignment.AlignRight - // How much spacing is needed around the popupItem - property alias popupPadding: popup.padding + // How much spacing is needed around the contentItem + property alias contentPadding: content.padding - // How much spacing is needed for the popupItem by Y coordinate - property var popupSpacingY: 0 + // How much spacing is needed for the contentItem by Y coordinate + property var contentSpacingY: 0 // How much padding is needed around the header & button property alias headerPadding: background.padding @@ -53,7 +61,7 @@ Item property alias iconSize: collapseButton.height // Is the "drawer" open? - readonly property alias expanded: popup.visible + readonly property alias expanded: content.visible property alias expandedHighlightColor: expandedHighlight.color @@ -63,8 +71,8 @@ Item // On what side should the header corners be shown? 1 is down, 2 is left, 3 is up and 4 is right. property alias headerCornerSide: background.cornerSide - // Change the popupItem close behaviour - property alias popupClosePolicy : popup.closePolicy + // Change the contentItem close behaviour + property alias contentClosePolicy : content.closePolicy property alias headerShadowColor: shadow.color @@ -72,15 +80,15 @@ Item property int shadowOffset: 2 - function togglePopup() + function toggleContent() { - if (popup.visible) + if (content.visible) { - popup.close() + content.close() } else { - popup.open() + content.open() } } @@ -108,7 +116,7 @@ Item } } - // A highlight that is shown when the popup is expanded + // A highlight that is shown when the content is expanded Rectangle { id: expandedHighlight @@ -140,7 +148,7 @@ Item { id: mouseArea anchors.fill: parent - onClicked: togglePopup() + onClicked: toggleContent() hoverEnabled: true onEntered: background.color = headerHoverColor onExited: background.color = headerBackgroundColor @@ -163,21 +171,21 @@ Item Popup { - id: popup + id: content - // Ensure that the popup is located directly below the headerItem - y: background.height + base.shadowOffset + popupSpacingY + // Ensure that the content is located directly below the headerItem + y: background.height + base.shadowOffset + base.contentSpacingY - // Make the popup aligned with the rest, using the property popupAlignment to decide whether is right or left. + // Make the content aligned with the rest, using the property contentAlignment to decide whether is right or left. // In case of right alignment, the 3x padding is due to left, right and padding between the button & text. - x: popupAlignment == ExpandableComponent.PopupAlignment.AlignRight ? -width + collapseButton.width + headerItemLoader.width + 3 * background.padding : 0 + x: contentAlignment == ExpandableComponent.ContentAlignment.AlignRight ? -width + collapseButton.width + headerItemLoader.width + 3 * background.padding : 0 padding: UM.Theme.getSize("default_margin").width closePolicy: Popup.CloseOnPressOutsideParent background: Cura.RoundedRectangle { cornerSide: Cura.RoundedRectangle.Direction.Down - color: popupBackgroundColor + color: contentBackgroundColor border.width: UM.Theme.getSize("default_lining").width border.color: UM.Theme.getColor("lining") radius: UM.Theme.getSize("default_radius").width @@ -187,20 +195,20 @@ Item onContentItemChanged: { - // Since we want the size of the popup to be set by the size of the content, + // Since we want the size of the content to be set by the size of the content, // we need to do it like this. - popup.width = contentItem.width + 2 * popup.padding - popup.height = contentItem.height + 2 * popup.padding + content.width = contentItem.width + 2 * content.padding + content.height = contentItem.height + 2 * content.padding } } - // DO NOT MOVE UP IN THE CODE: This connection has to be here, after the definition of the Popup item. + // DO NOT MOVE UP IN THE CODE: This connection has to be here, after the definition of the content item. // Apparently the order in which these are handled matters and so the height is correctly updated if this is here. Connections { - // Since it could be that the popup is dynamically populated, we should also take these changes into account. - target: popup.contentItem - onWidthChanged: popup.width = popup.contentItem.width + 2 * popup.padding - onHeightChanged: popup.height = popup.contentItem.height + 2 * popup.padding + // Since it could be that the content is dynamically populated, we should also take these changes into account. + target: content.contentItem + onWidthChanged: content.width = content.contentItem.width + 2 * content.padding + onHeightChanged: content.height = content.contentItem.height + 2 * content.padding } } diff --git a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml index eb6800cb36..de92161bbb 100644 --- a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml +++ b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml @@ -93,7 +93,7 @@ Cura.ExpandableComponent } } - popupItem: Item + contentItem: Item { width: base.width - 2 * UM.Theme.getSize("default_margin").width height: 200 diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector/PrintSetupSelector.qml index f1b424f7f2..78bdbde542 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelector.qml @@ -15,10 +15,10 @@ Cura.ExpandableComponent property string disabledText: catalog.i18nc("@label:Should be short", "Off") iconSource: UM.Theme.getIcon("pencil") - popupPadding: UM.Theme.getSize("default_lining").width - popupSpacingY: UM.Theme.getSize("narrow_margin").width + contentPadding: UM.Theme.getSize("default_lining").width + contentSpacingY: UM.Theme.getSize("narrow_margin").width - popupClosePolicy: Popup.CloseOnEscape + contentClosePolicy: Popup.CloseOnEscape UM.I18nCatalog { @@ -36,5 +36,5 @@ Cura.ExpandableComponent id: extrudersModel } - popupItem: PrintSetupSelectorContents {} + contentItem: PrintSetupSelectorContents {} } \ No newline at end of file diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index 15cd773c90..f742d0bef3 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -15,8 +15,8 @@ Cura.ExpandableComponent property bool isPrinterConnected: Cura.MachineManager.printerConnected property var outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null - popupPadding: UM.Theme.getSize("default_lining").width - popupAlignment: Cura.ExpandableComponent.PopupAlignment.AlignLeft + contentPadding: UM.Theme.getSize("default_lining").width + contentAlignment: Cura.ExpandableComponent.ContentAlignment.AlignLeft iconSource: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") UM.I18nCatalog @@ -80,7 +80,7 @@ Cura.ExpandableComponent } } - popupItem: Item + contentItem: Item { id: popup width: UM.Theme.getSize("machine_selector_widget_content").width diff --git a/resources/qml/ViewsSelector.qml b/resources/qml/ViewsSelector.qml index e9fdd57177..97de60689b 100644 --- a/resources/qml/ViewsSelector.qml +++ b/resources/qml/ViewsSelector.qml @@ -11,8 +11,8 @@ Cura.ExpandableComponent { id: viewSelector - popupPadding: UM.Theme.getSize("default_lining").width - popupAlignment: Cura.ExpandableComponent.PopupAlignment.AlignLeft + contentPadding: UM.Theme.getSize("default_lining").width + contentAlignment: Cura.ExpandableComponent.ContentAlignment.AlignLeft iconSource: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") property var viewModel: UM.ViewModel { } @@ -70,7 +70,7 @@ Cura.ExpandableComponent } } - popupItem: Column + contentItem: Column { id: viewSelectorPopup width: viewSelector.width - 2 * viewSelector.popupPadding From 02963eb9bfdeac576786889e5568203ee11ab764 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Tue, 4 Dec 2018 13:19:12 +0100 Subject: [PATCH 0611/1240] Use a timer for the periodic update of the remote clusters and printjobs --- .../src/Cloud/CloudOutputDeviceManager.py | 22 +++---------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 37198fd7c6..85e734f7a3 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -2,7 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. import json from time import sleep -from threading import Thread +from threading import Timer from typing import Dict, Optional from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply @@ -43,13 +43,8 @@ class CloudOutputDeviceManager(NetworkClient): # When switching machines we check if we have to activate a remote cluster. application.globalContainerStackChanged.connect(self._connectToActiveMachine) - # Periodically check all remote clusters for the authenticated user. - # This is done by emitting to _on_cluster_received by _update_clusters_thread - # The thread is only started after the user is authenticated, otherwise the api call results in - # an authentication error self._on_cluster_received = Signal() self._on_cluster_received.connect(self._getRemoteClusters) - self._update_clusters_thread = Thread(target=self._updateClusters, daemon=True) ## Override _createEmptyRequest to add the needed authentication header for talking to the Ultimaker Cloud API. @@ -61,17 +56,6 @@ class CloudOutputDeviceManager(NetworkClient): request.setRawHeader(b"Authorization", "Bearer {}".format(self._account.accessToken).encode()) return request - ## Update the clusters - def _updateClusters(self) -> None: - while True: - - # Stop if the application is shutting down - if CuraApplication.getInstance().isShuttingDown(): - return - - self._on_cluster_received.emit() - sleep(5) - ## Gets all remote clusters from the API. def _getRemoteClusters(self) -> None: Logger.log("i", "Retrieving remote clusters") @@ -80,8 +64,8 @@ class CloudOutputDeviceManager(NetworkClient): # Only start the polling thread after the user is authenticated # The first call to _getRemoteClusters comes from self._account.loginStateChanged - if not self._update_clusters_thread.is_alive(): - self._update_clusters_thread.start() + timer = Timer(5.0, self._on_cluster_received.emit) + timer.start() ## Callback for when the request for getting the clusters. is finished. From 4dcce7616bb8286c265a392ed21d62fabd10e6a7 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 4 Dec 2018 13:21:26 +0100 Subject: [PATCH 0612/1240] Fix some leftover renamings Contributes to CURA-5941 --- resources/qml/Account/AccountDetails.qml | 4 ++-- resources/qml/ExpandableComponent.qml | 4 ++-- .../qml/PrintSetupSelector/PrintSetupSelectorContents.qml | 2 +- resources/qml/PrinterSelector/MachineSelector.qml | 4 ++-- resources/qml/PrinterSelector/MachineSelectorButton.qml | 2 +- resources/qml/ViewsSelector.qml | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/resources/qml/Account/AccountDetails.qml b/resources/qml/Account/AccountDetails.qml index a288426e0c..bfb23930c6 100644 --- a/resources/qml/Account/AccountDetails.qml +++ b/resources/qml/Account/AccountDetails.qml @@ -13,8 +13,8 @@ Column property var loggedIn: false property var profileImage: "" - padding: 2 * UM.Theme.getSize("default_margin").height - spacing: 2 * UM.Theme.getSize("default_margin").height + padding: UM.Theme.getSize("wide_margin").height + spacing: UM.Theme.getSize("wide_margin").height AvatarImage { diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 0b4076c04a..89f6cf9f25 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -139,8 +139,8 @@ Item sourceSize.width: width sourceSize.height: height visible: source != "" - width: height - height: Math.round(0.2 * base.height) + width: UM.Theme.getSize("standard_arrow").width + height: UM.Theme.getSize("standard_arrow").height color: UM.Theme.getColor("text") } diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml index a2856e1fe5..0524a6de9e 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml @@ -93,7 +93,7 @@ Item background: Item {} - onClicked: togglePopup() // Will hide the popup item + onClicked: toggleContent() // Will hide the popup item } } diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index f742d0bef3..49b4f87267 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -134,7 +134,7 @@ Cura.ExpandableComponent text: catalog.i18nc("@button", "Add printer") onClicked: { - togglePopup() + toggleContent() Cura.Actions.addMachine.trigger() } } @@ -146,7 +146,7 @@ Cura.ExpandableComponent text: catalog.i18nc("@button", "Manage printers") onClicked: { - togglePopup() + toggleContent() Cura.Actions.configureMachines.trigger() } } diff --git a/resources/qml/PrinterSelector/MachineSelectorButton.qml b/resources/qml/PrinterSelector/MachineSelectorButton.qml index 369e75cede..b88af35f82 100644 --- a/resources/qml/PrinterSelector/MachineSelectorButton.qml +++ b/resources/qml/PrinterSelector/MachineSelectorButton.qml @@ -83,7 +83,7 @@ Button onClicked: { - togglePopup() + toggleContent() Cura.MachineManager.setActiveMachine(model.id) } diff --git a/resources/qml/ViewsSelector.qml b/resources/qml/ViewsSelector.qml index 97de60689b..71a29e4949 100644 --- a/resources/qml/ViewsSelector.qml +++ b/resources/qml/ViewsSelector.qml @@ -120,7 +120,7 @@ Cura.ExpandableComponent onClicked: { - viewSelector.togglePopup() + toggleContent() UM.Controller.setActiveView(id) } } From 8e3e0c149e6963f01485ff411902ad5e932b764d Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 4 Dec 2018 13:23:31 +0100 Subject: [PATCH 0613/1240] fixes --- .../src/Cloud/CloudOutputDevice.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 747d911407..d17728f513 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -215,10 +215,6 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): return [print_job for print_job in self._print_jobs if print_job.assignedPrinter is not None and print_job.state != "queued"] - @pyqtProperty(bool, notify = printJobsChanged) - def receivedPrintJobs(self) -> bool: - return not self._sending_job - ## Called when the connection to the cluster changes. def connect(self) -> None: super().connect() @@ -464,7 +460,9 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): @pyqtProperty(QObject, notify = printersChanged) def activePrinter(self) -> Optional[PrinterOutputModel]: - return self._printers[0] or None + if not self._printers: + return None + return self._printers[0] @pyqtSlot(QObject) def setActivePrinter(self, printer: Optional[PrinterOutputModel]) -> None: @@ -477,3 +475,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): @pyqtSlot(QUrl) def setActiveCameraUrl(self, camera_url: "QUrl") -> None: pass + + @pyqtProperty(bool, notify = printJobsChanged) + def receivedPrintJobs(self) -> bool: + return True From 47ff95b1f3cfb793b2b4af6114aa1688e864d8a3 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 4 Dec 2018 13:53:11 +0100 Subject: [PATCH 0614/1240] Set contentType to the different expandable components. Also add a color for the active state when the contentType is 'Fixed'. Contributes to CURA-5941. --- .../SimulationViewMenuComponent.qml | 2 +- resources/qml/ExpandableComponent.qml | 28 +++++++++++-------- .../QuickConfigurationSelector.qml | 1 - .../PrintSetupSelector/PrintSetupSelector.qml | 2 +- .../qml/PrinterSelector/MachineSelector.qml | 1 - resources/qml/ViewsSelector.qml | 1 - 6 files changed, 18 insertions(+), 17 deletions(-) diff --git a/plugins/SimulationView/SimulationViewMenuComponent.qml b/plugins/SimulationView/SimulationViewMenuComponent.qml index a9eb157f48..9dc3b67658 100644 --- a/plugins/SimulationView/SimulationViewMenuComponent.qml +++ b/plugins/SimulationView/SimulationViewMenuComponent.qml @@ -16,7 +16,7 @@ Cura.ExpandableComponent id: base width: UM.Theme.getSize("layerview_menu_size").width - iconSource: UM.Theme.getIcon("pencil") + contentType: Cura.ExpandableComponent.ContentType.Fixed Connections { diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 89f6cf9f25..f6e340c4ea 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -30,7 +30,7 @@ Item property alias headerItem: headerItemLoader.sourceComponent // The contentItem holds the QML item that is shown when the "open" button is pressed - property alias contentItem: content.contentItem + property var contentItem: content.contentItem // Defines the type of the contents property int contentType: ExpandableComponent.ContentType.Floating @@ -38,6 +38,7 @@ Item property color contentBackgroundColor: UM.Theme.getColor("action_button") property color headerBackgroundColor: UM.Theme.getColor("action_button") + property color headerActiveColor: UM.Theme.getColor("secondary") property color headerHoverColor: UM.Theme.getColor("action_button_hovered") // Defines the alignment of the content with respect of the headerItem, by default to the right @@ -123,7 +124,7 @@ Item width: parent.width height: UM.Theme.getSize("thick_lining").height color: UM.Theme.getColor("primary") - visible: expanded + visible: contentType == ExpandableComponent.ContentType.Floating && expanded anchors.bottom: parent.bottom } @@ -138,6 +139,9 @@ Item } sourceSize.width: width sourceSize.height: height + source: contentType == ExpandableComponent.ContentType.Floating ? + (expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left")) : + UM.Theme.getIcon("pencil") visible: source != "" width: UM.Theme.getSize("standard_arrow").width height: UM.Theme.getSize("standard_arrow").height @@ -151,7 +155,8 @@ Item onClicked: toggleContent() hoverEnabled: true onEntered: background.color = headerHoverColor - onExited: background.color = headerBackgroundColor + onExited: background.color = (contentType == ExpandableComponent.ContentType.Fixed && expanded) ? + headerActiveColor : headerBackgroundColor } } @@ -190,16 +195,15 @@ Item border.color: UM.Theme.getColor("lining") radius: UM.Theme.getSize("default_radius").width } + } - contentItem: Item { } - - onContentItemChanged: - { - // Since we want the size of the content to be set by the size of the content, - // we need to do it like this. - content.width = contentItem.width + 2 * content.padding - content.height = contentItem.height + 2 * content.padding - } + onContentItemChanged: + { + // Since we want the size of the content to be set by the size of the content, + // we need to do it like this. + content.width = contentItem.width + 2 * content.padding + content.height = contentItem.height + 2 * content.padding + content.contentItem = contentItem } // DO NOT MOVE UP IN THE CODE: This connection has to be here, after the definition of the content item. diff --git a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml index de92161bbb..ea82f4fe13 100644 --- a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml +++ b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml @@ -27,7 +27,6 @@ Cura.ExpandableComponent name: "cura" } - iconSource: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") headerItem: Item { // Horizontal list that shows the extruders diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector/PrintSetupSelector.qml index 78bdbde542..0eea697950 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelector.qml @@ -14,7 +14,7 @@ Cura.ExpandableComponent property string enabledText: catalog.i18nc("@label:Should be short", "On") property string disabledText: catalog.i18nc("@label:Should be short", "Off") - iconSource: UM.Theme.getIcon("pencil") + contentType: Cura.ExpandableComponent.ContentType.Fixed contentPadding: UM.Theme.getSize("default_lining").width contentSpacingY: UM.Theme.getSize("narrow_margin").width diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index 49b4f87267..69a1ac899c 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -17,7 +17,6 @@ Cura.ExpandableComponent contentPadding: UM.Theme.getSize("default_lining").width contentAlignment: Cura.ExpandableComponent.ContentAlignment.AlignLeft - iconSource: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") UM.I18nCatalog { diff --git a/resources/qml/ViewsSelector.qml b/resources/qml/ViewsSelector.qml index 71a29e4949..d469202606 100644 --- a/resources/qml/ViewsSelector.qml +++ b/resources/qml/ViewsSelector.qml @@ -13,7 +13,6 @@ Cura.ExpandableComponent contentPadding: UM.Theme.getSize("default_lining").width contentAlignment: Cura.ExpandableComponent.ContentAlignment.AlignLeft - iconSource: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") property var viewModel: UM.ViewModel { } From 5d77209cfbadbf5f43571bf6cf350273ed874c79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Tue, 4 Dec 2018 13:57:41 +0100 Subject: [PATCH 0615/1240] Be more efficient in updating the print jobs --- plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py | 6 ++++-- .../src/Cloud/CloudOutputDeviceManager.py | 1 - 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index d17728f513..0f3a92f9d8 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -338,8 +338,10 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): for job_id in updated_job_ids: self._updateUM3PrintJobOutputModel(current_jobs[job_id], remote_jobs[job_id]) - # TODO: properly handle removed and updated printers - self.printJobsChanged.emit() + # We only have to update when jobs are added or removed + # updated jobs push their changes via their outputmodel + if len(removed_job_ids) > 0 or len(new_job_ids) > 0: + self.printJobsChanged.emit() def _addPrintJob(self, job: CloudClusterPrintJob) -> None: try: diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 85e734f7a3..c6134d9a63 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -1,7 +1,6 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import json -from time import sleep from threading import Timer from typing import Dict, Optional From 82d8410d184d0727612158bf50e45a8b13ae31a9 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 4 Dec 2018 13:58:54 +0100 Subject: [PATCH 0616/1240] Don't emit enabledChanged signal if it didn't change Contributes to issue CURA-5876. --- cura/Settings/ExtruderStack.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cura/Settings/ExtruderStack.py b/cura/Settings/ExtruderStack.py index ed758db183..d626ef06da 100644 --- a/cura/Settings/ExtruderStack.py +++ b/cura/Settings/ExtruderStack.py @@ -52,6 +52,8 @@ class ExtruderStack(CuraContainerStack): return super().getNextStack() def setEnabled(self, enabled: bool) -> None: + if self.getMetaDataEntry("enabled", True) == enabled: #No change. + return #Don't emit a signal then. self.setMetaDataEntry("enabled", str(enabled)) self.enabledChanged.emit() From a1f579a5288a43dd48480f565ed8c2dd514f9b47 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 4 Dec 2018 14:01:49 +0100 Subject: [PATCH 0617/1240] Improve wording of iconOnRightSide documentation Contributes to issue CURA-5876. --- resources/qml/ActionButton.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index cd0a264766..116253ab7a 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -43,7 +43,7 @@ Button contentItem: Row { - //Icon if displayed on the left side. + //Left side icon. Only displayed if !iconOnRightSide. UM.RecolorImage { id: buttonIconLeft @@ -71,7 +71,7 @@ Button elide: Text.ElideRight } - //Icon if displayed on the right side. + //Right side icon. Only displayed if iconOnRightSide. UM.RecolorImage { id: buttonIconRight From 801701623eedd41681c4caf31251b2cd4bf33f84 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 4 Dec 2018 14:04:07 +0100 Subject: [PATCH 0618/1240] Remove unnecessary top anchor in first subitem of item It already gets aligned to the top anyway. Contributes to issue CURA-5876. --- resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml index 8e86549e17..58b6bac089 100644 --- a/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml @@ -22,7 +22,6 @@ Item anchors { - top: parent.top left: parent.left right: parent.right } From e98f3bff384c384eaab419c6997de52e55a7bb25 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 4 Dec 2018 14:09:20 +0100 Subject: [PATCH 0619/1240] Implement test version of showing cloud connected printers in list --- cura/Settings/MachineManager.py | 6 +++ .../src/Cloud/CloudOutputDeviceManager.py | 9 +++- resources/qml/Menus/CloudPrinterMenu.qml | 26 +++++++++++ resources/qml/Menus/PrinterMenu.qml | 17 +++++++ .../PrinterSelector/MachineSelectorList.qml | 44 ++++++++++++++++++- 5 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 resources/qml/Menus/CloudPrinterMenu.qml diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 53390ca88d..15e2c67c33 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -527,6 +527,12 @@ class MachineManager(QObject): return self._global_container_stack.getMetaDataEntry("um_network_key", "") return "" + @pyqtProperty(str, notify=printerConnectedStatusChanged) + def activeMachineCloudKey(self) -> str: + if self._global_container_stack: + return self._global_container_stack.getMetaDataEntry("um_cloud_cluster_id", "") + return "" + @pyqtProperty(str, notify = printerConnectedStatusChanged) def activeMachineNetworkGroupName(self) -> str: if self._global_container_stack: diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 85e734f7a3..6c5d681a39 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -116,7 +116,7 @@ class CloudOutputDeviceManager(NetworkClient): self._output_device_manager.addOutputDevice(device) self._remote_clusters[cluster.cluster_id] = device device.connect() # TODO: remove this - self._connectToActiveMachine() + self._connectToActiveMachine(cluster.cluster_id, cluster.host_name) ## Remove a CloudOutputDevice def _removeCloudOutputDevice(self, cluster: CloudCluster): @@ -124,10 +124,15 @@ class CloudOutputDeviceManager(NetworkClient): del self._remote_clusters[cluster.cluster_id] ## Callback for when the active machine was changed by the user. - def _connectToActiveMachine(self) -> None: + def _connectToActiveMachine(self, cluster_id: Optional[str] = None, host_name: Optional[str] = None) -> None: active_machine = CuraApplication.getInstance().getGlobalContainerStack() if not active_machine: return + + # TODO: Remove this once correct pairing has been added (see below). + if cluster_id: + active_machine.setMetaDataEntry("um_cloud_cluster_id", cluster_id) + active_machine.setMetaDataEntry("connect_group_name", host_name) # Check if the stored cluster_id for the active machine is in our list of remote clusters. stored_cluster_id = active_machine.getMetaDataEntry("um_cloud_cluster_id") diff --git a/resources/qml/Menus/CloudPrinterMenu.qml b/resources/qml/Menus/CloudPrinterMenu.qml new file mode 100644 index 0000000000..4ceebbfdfc --- /dev/null +++ b/resources/qml/Menus/CloudPrinterMenu.qml @@ -0,0 +1,26 @@ +// 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.4 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +Instantiator { + + model: UM.ContainerStacksModel { + filter: {"type": "machine", "um_cloud_cluster_id": "*", "hidden": "False"} + } + + MenuItem { + // iconSource: UM.Theme.getIcon("printer_single") TODO: use cloud icon here + text: model.name + checkable: true + checked: true // cloud printers are only listed if they are actually online + exclusiveGroup: group; + onTriggered: Cura.MachineManager.setActiveMachine(model.id); + } + + onObjectAdded: menu.insertItem(index, object) + onObjectRemoved: menu.removeItem(object) +} diff --git a/resources/qml/Menus/PrinterMenu.qml b/resources/qml/Menus/PrinterMenu.qml index 741d927c13..a924b0e589 100644 --- a/resources/qml/Menus/PrinterMenu.qml +++ b/resources/qml/Menus/PrinterMenu.qml @@ -37,6 +37,23 @@ Menu visible: networkPrinterMenu.count > 0 } + MenuItem + { + text: catalog.i18nc("@label:category menu label", "Cloud enabled printers") + enabled: false + visible: cloudPrinterMenu.count > 0 + } + + CloudPrinterMenu + { + id: cloudPrinterMenu + } + + MenuSeparator + { + visible: cloudPrinterMenu.count > 0 + } + MenuItem { text: catalog.i18nc("@label:category menu label", "Local printers") diff --git a/resources/qml/PrinterSelector/MachineSelectorList.qml b/resources/qml/PrinterSelector/MachineSelectorList.qml index 5ef04b7351..26c703fddd 100644 --- a/resources/qml/PrinterSelector/MachineSelectorList.qml +++ b/resources/qml/PrinterSelector/MachineSelectorList.qml @@ -50,6 +50,46 @@ Column } } + Label + { + text: catalog.i18nc("@label", "Cloud connected printers") + visible: cloudPrintersModel.items.length > 0 + leftPadding: UM.Theme.getSize("default_margin").width + height: visible ? contentHeight + 2 * UM.Theme.getSize("default_margin").height : 0 + renderType: Text.NativeRendering + font: UM.Theme.getFont("medium") + color: UM.Theme.getColor("text_medium") + verticalAlignment: Text.AlignVCenter + } + + Repeater + { + id: cloudPrinters + + model: UM.ContainerStacksModel + { + id: cloudPrintersModel + filter: + { + "type": "machine", + "um_cloud_cluster_id": "*" + } + } + + delegate: MachineSelectorButton + { + text: model.metadata["connect_group_name"] + checked: Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["connect_group_name"] + outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null + + Connections + { + target: Cura.MachineManager + onActiveMachineNetworkGroupNameChanged: checked = Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["connect_group_name"] + } + } + } + Label { text: catalog.i18nc("@label", "Preset printers") @@ -71,7 +111,9 @@ Column id: virtualPrintersModel filter: { - "type": "machine", "um_network_key": null + "type": "machine", + "um_network_key": null, + "um_cloud_cluster_id": null } } From 7de947f5fab60e8cc9d38c17d5f0c62653cd13f0 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 4 Dec 2018 14:15:19 +0100 Subject: [PATCH 0620/1240] use correct label text --- resources/qml/Menus/CloudPrinterMenu.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/Menus/CloudPrinterMenu.qml b/resources/qml/Menus/CloudPrinterMenu.qml index 4ceebbfdfc..bd03890642 100644 --- a/resources/qml/Menus/CloudPrinterMenu.qml +++ b/resources/qml/Menus/CloudPrinterMenu.qml @@ -9,12 +9,12 @@ import Cura 1.0 as Cura Instantiator { model: UM.ContainerStacksModel { - filter: {"type": "machine", "um_cloud_cluster_id": "*", "hidden": "False"} + filter: {"type": "machine", "um_cloud_cluster_id": "*"} } MenuItem { // iconSource: UM.Theme.getIcon("printer_single") TODO: use cloud icon here - text: model.name + text: model.metadata["connect_group_name"] checkable: true checked: true // cloud printers are only listed if they are actually online exclusiveGroup: group; From 759b5b58476c3d2be6c0faf5118fc052f1d6bec8 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 4 Dec 2018 14:23:41 +0100 Subject: [PATCH 0621/1240] Use anchors rather than a calculation with padding It's supposed to be slightly more efficient. Contributes to issue CURA-5876. --- .../ConfigurationMenu/ConfigurationItem.qml | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 3ea220c91e..7ba7202819 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -34,7 +34,13 @@ Button { id: extruderRow - width: parent.width - 2 * parent.padding + anchors + { + left: parent.left + leftMargin: parent.padding + right: parent.right + rightMargin: parent.padding + } height: childrenRect.height spacing: UM.Theme.getSize("default_margin").width @@ -58,7 +64,13 @@ Button id: separator visible: buildplateInformation.visible - width: parent.width - 2 * parent.padding + anchors + { + left: parent.left + leftMargin: parent.padding + right: parent.right + rightMargin: parent.padding + } height: visible ? Math.round(UM.Theme.getSize("thick_lining").height / 2) : 0 color: UM.Theme.getColor("text") } @@ -66,7 +78,14 @@ Button Item { id: buildplateInformation - width: parent.width - 2 * parent.padding + + anchors + { + left: parent.left + leftMargin: parent.padding + right: parent.right + rightMargin: parent.padding + } height: childrenRect.height visible: configuration.buildplateConfiguration != "" From 7cc1f021c1333fe962a2875167f6b1750bc238fb Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 4 Dec 2018 14:25:58 +0100 Subject: [PATCH 0622/1240] Fix right-alignment of configuration items in list The scrollbar will go on top of it now, but it looks nicer if you don't scroll. Maybe we have to make it adaptable? Contributes to issue CURA-5876. --- .../qml/Menus/ConfigurationMenu/ConfigurationListView.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml index 32be97f74e..5a9f72260c 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml @@ -27,7 +27,7 @@ Column ScrollView { id: container - width: parent.width - parent.padding + width: parent.width height: Math.min(configurationList.contentHeight, 350 * screenScaleFactor) ButtonGroup @@ -58,7 +58,7 @@ Column delegate: ConfigurationItem { - width: parent.width - UM.Theme.getSize("default_margin").width + width: parent.width configuration: modelData } } From 347240410ad1885d3dc237dc7f0db1f9e249671b Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 4 Dec 2018 14:35:28 +0100 Subject: [PATCH 0623/1240] Use enumeration to check and switch state of configuration menu Instead of a string. This is a bit more restrictive. Contributes to issue CURA-5876. --- .../ConfigurationMenu/ConfigurationMenu.qml | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index 79b7c9bf66..1b7dfe30e4 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -28,6 +28,12 @@ Cura.ExpandableComponent name: "cura" } + enum ConfigurationMethod + { + AUTO, + CUSTOM + } + iconSource: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") headerItem: Item { @@ -108,7 +114,7 @@ Cura.ExpandableComponent is_connected = Cura.MachineManager.activeMachineNetworkKey !== "" && Cura.MachineManager.printerConnected //Re-evaluate. } - property var configuration_method: is_connected ? "auto" : "custom" //Auto if connected to a printer at start-up, or Custom if not. + property int configuration_method: is_connected ? ConfigurationMenu.ConfigurationMethod.AUTO : ConfigurationMenu.ConfigurationMethod.CUSTOM //Auto if connected to a printer at start-up, or Custom if not. Item { @@ -117,13 +123,13 @@ Cura.ExpandableComponent AutoConfiguration { id: autoConfiguration - visible: popupItem.configuration_method === "auto" + visible: popupItem.configuration_method == ConfigurationMenu.ConfigurationMethod.AUTO } CustomConfiguration { id: customConfiguration - visible: popupItem.configuration_method === "custom" + visible: popupItem.configuration_method == ConfigurationMenu.ConfigurationMethod.CUSTOM } } @@ -150,7 +156,7 @@ Cura.ExpandableComponent Cura.SecondaryButton { id: goToCustom - visible: popupItem.configuration_method === "auto" + visible: popupItem.configuration_method == ConfigurationMenu.ConfigurationMethod.AUTO text: catalog.i18nc("@label", "Custom") anchors @@ -162,13 +168,13 @@ Cura.ExpandableComponent iconSource: UM.Theme.getIcon("arrow_right") iconOnRightSide: true - onClicked: popupItem.configuration_method = "custom" + onClicked: popupItem.configuration_method = ConfigurationMenu.ConfigurationMethod.CUSTOM } Cura.SecondaryButton { id: goToAuto - visible: popupItem.configuration_method === "custom" + visible: popupItem.configuration_method == ConfigurationMenu.ConfigurationMethod.CUSTOM text: catalog.i18nc("@label", "Configurations") anchors @@ -179,7 +185,7 @@ Cura.ExpandableComponent iconSource: UM.Theme.getIcon("arrow_left") - onClicked: popupItem.configuration_method = "auto" + onClicked: popupItem.configuration_method = ConfigurationMenu.ConfigurationMethod.AUTO } } } From 2dde2438b2a984e2dabf19edcb5164934e456050 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 4 Dec 2018 14:38:13 +0100 Subject: [PATCH 0624/1240] Remove unnecessary anchors Contributes to issue CURA-5876. --- .../Menus/ConfigurationMenu/ConfigurationMenu.qml | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index 1b7dfe30e4..a49fe5574f 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -159,11 +159,7 @@ Cura.ExpandableComponent visible: popupItem.configuration_method == ConfigurationMenu.ConfigurationMethod.AUTO text: catalog.i18nc("@label", "Custom") - anchors - { - right: parent.right - top: parent.top - } + anchors.right: parent.right iconSource: UM.Theme.getIcon("arrow_right") iconOnRightSide: true @@ -177,12 +173,6 @@ Cura.ExpandableComponent visible: popupItem.configuration_method == ConfigurationMenu.ConfigurationMethod.CUSTOM text: catalog.i18nc("@label", "Configurations") - anchors - { - left: parent.left - top: parent.top - } - iconSource: UM.Theme.getIcon("arrow_left") onClicked: popupItem.configuration_method = ConfigurationMenu.ConfigurationMethod.AUTO From 023100f4c3690e564d26ddf4bd6ffece955d4c38 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 4 Dec 2018 14:39:08 +0100 Subject: [PATCH 0625/1240] Changed valueError property to bool instead of var A bit more specific. Contributes to issue CURA-5876. Co-Authored-By: Ghostkeeper --- resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index 7ff47ea16f..e28478e9e2 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -181,7 +181,7 @@ Item { id: materialSelection - property var valueError: Cura.MachineManager.activeStack != null ? Cura.ContainerManager.getContainerMetaDataEntry(Cura.MachineManager.activeStack.material.id, "compatible", "") != "True" : true + property bool valueError: Cura.MachineManager.activeStack != null ? Cura.ContainerManager.getContainerMetaDataEntry(Cura.MachineManager.activeStack.material.id, "compatible", "") != "True" : true property var valueWarning: !Cura.MachineManager.isActiveQualitySupported text: Cura.MachineManager.activeStack != null ? Cura.MachineManager.activeStack.material.name : "" @@ -232,4 +232,4 @@ Item } } } -} \ No newline at end of file +} From 8a257f018486a2d4cc22ae980ea4e8dd8e5ffc57 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 4 Dec 2018 14:39:39 +0100 Subject: [PATCH 0626/1240] Change valueWarning property to bool instead of var A bit more specific. Contributes to issue CURA-5876. Co-Authored-By: Ghostkeeper --- resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index e28478e9e2..72b640e94a 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -182,7 +182,7 @@ Item id: materialSelection property bool valueError: Cura.MachineManager.activeStack != null ? Cura.ContainerManager.getContainerMetaDataEntry(Cura.MachineManager.activeStack.material.id, "compatible", "") != "True" : true - property var valueWarning: !Cura.MachineManager.isActiveQualitySupported + property bool valueWarning: !Cura.MachineManager.isActiveQualitySupported text: Cura.MachineManager.activeStack != null ? Cura.MachineManager.activeStack.material.name : "" tooltip: text From ef29fb0cfaaf22ccd582ad238cbc3259894fb137 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 4 Dec 2018 14:44:12 +0100 Subject: [PATCH 0627/1240] Remove unnecessary item wrapper Contributes to issue CURA-5876. --- .../ConfigurationMenu/PrintCoreConfiguration.qml | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml index 8d2f0c0f7c..4064a961d5 100644 --- a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml @@ -16,16 +16,11 @@ Row spacing: UM.Theme.getSize("default_margin").width //Extruder icon. - Item + Cura.ExtruderIcon { - width: childrenRect.width - height: information.height - Cura.ExtruderIcon - { - materialColor: printCoreConfiguration.material.color - anchors.verticalCenter: parent.verticalCenter - extruderEnabled: printCoreConfiguration.material.name !== "" && printCoreConfiguration.hotendID !== "" - } + materialColor: printCoreConfiguration.material.color + anchors.verticalCenter: parent.verticalCenter + extruderEnabled: printCoreConfiguration.material.name !== "" && printCoreConfiguration.hotendID !== "" } Column From 2fdfdaa00b4cc7cea04e754a25ad58f056d3d684 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 4 Dec 2018 14:47:34 +0100 Subject: [PATCH 0628/1240] Rename iconOnRightSide to isIconOnRightSide More accurately represents the type of value that's in this property. Contributes to issue CURA-5876. --- resources/qml/ActionButton.qml | 10 +++++----- .../qml/Menus/ConfigurationMenu/ConfigurationMenu.qml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index 116253ab7a..2107264dff 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -12,7 +12,7 @@ Button { id: button property alias iconSource: buttonIconLeft.source - property var iconOnRightSide: false + property bool isIconOnRightSide: false property alias textFont: buttonText.font property alias cornerRadius: backgroundRect.radius property alias tooltip: tooltip.text @@ -43,7 +43,7 @@ Button contentItem: Row { - //Left side icon. Only displayed if !iconOnRightSide. + //Left side icon. Only displayed if !isIconOnRightSide. UM.RecolorImage { id: buttonIconLeft @@ -53,7 +53,7 @@ Button sourceSize.width: width sourceSize.height: height color: button.hovered ? button.textHoverColor : button.textColor - visible: source != "" && !button.iconOnRightSide + visible: source != "" && !button.isIconOnRightSide anchors.verticalCenter: parent.verticalCenter } @@ -71,7 +71,7 @@ Button elide: Text.ElideRight } - //Right side icon. Only displayed if iconOnRightSide. + //Right side icon. Only displayed if isIconOnRightSide. UM.RecolorImage { id: buttonIconRight @@ -81,7 +81,7 @@ Button sourceSize.width: width sourceSize.height: height color: buttonIconLeft.color - visible: source != "" && button.iconOnRightSide + visible: source != "" && button.isIconOnRightSide anchors.verticalCenter: buttonIconLeft.verticalCenter } } diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index a49fe5574f..f81176ab1a 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -162,7 +162,7 @@ Cura.ExpandableComponent anchors.right: parent.right iconSource: UM.Theme.getIcon("arrow_right") - iconOnRightSide: true + isIconOnRightSide: true onClicked: popupItem.configuration_method = ConfigurationMenu.ConfigurationMethod.CUSTOM } From 5b4fad3c08fe089bf216f91ac2ce889c4cc16e83 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 4 Dec 2018 14:54:15 +0100 Subject: [PATCH 0629/1240] When toggling auto-slice, force a re-slice CURA-5997 --- resources/qml/ActionPanel/SliceProcessWidget.qml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index 14e149dddb..03d91db530 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -137,6 +137,10 @@ Column { var autoSlice = UM.Preferences.getValue("general/auto_slice") prepareButtons.autoSlice = autoSlice + if(autoSlice) + { + CuraApplication.backend.forceSlice() + } } } From 02e7f904734c3d0cae1a33f0a978d3c3aa088912 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 4 Dec 2018 15:02:24 +0100 Subject: [PATCH 0630/1240] Fix module importing in USBPrinting CURA-5943 --- plugins/USBPrinting/AutoDetectBaudJob.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/USBPrinting/AutoDetectBaudJob.py b/plugins/USBPrinting/AutoDetectBaudJob.py index 6f1af6727a..78de864e57 100644 --- a/plugins/USBPrinting/AutoDetectBaudJob.py +++ b/plugins/USBPrinting/AutoDetectBaudJob.py @@ -3,8 +3,8 @@ from UM.Job import Job from UM.Logger import Logger -from plugins.USBPrinting.avr_isp import ispBase +from .avr_isp import ispBase from .avr_isp.stk500v2 import Stk500v2 from time import time, sleep From 43096c1bafc0790bf265709d46eb1b88eb7445dc Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 4 Dec 2018 15:03:43 +0100 Subject: [PATCH 0631/1240] Update USBPrinting version to 1.0.1 CURA-5943 --- plugins/USBPrinting/plugin.json | 2 +- resources/bundled_packages/cura.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/USBPrinting/plugin.json b/plugins/USBPrinting/plugin.json index 3484c8a48a..5d3cba8415 100644 --- a/plugins/USBPrinting/plugin.json +++ b/plugins/USBPrinting/plugin.json @@ -1,7 +1,7 @@ { "name": "USB printing", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "api": 5, "description": "Accepts G-Code and sends them to a printer. Plugin can also update firmware.", "i18n-catalog": "cura" diff --git a/resources/bundled_packages/cura.json b/resources/bundled_packages/cura.json index ee82b17a75..d8a7df2478 100644 --- a/resources/bundled_packages/cura.json +++ b/resources/bundled_packages/cura.json @@ -515,7 +515,7 @@ "package_type": "plugin", "display_name": "USB Printing", "description": "Accepts G-Code and sends them to a printer. Plugin can also update firmware.", - "package_version": "1.0.0", + "package_version": "1.0.1", "sdk_version": 5, "website": "https://ultimaker.com", "author": { From 4d87c464237c4c92c2aa5dfd0c4909a94f49bf92 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 4 Dec 2018 15:04:51 +0100 Subject: [PATCH 0632/1240] Remove sourceSize.width: width from all RecolorImages It is now the default in Uranium. Contributes to issue CURA-5876. --- plugins/ModelChecker/ModelChecker.qml | 1 - .../PerObjectSettingsPanel.qml | 1 - .../PostProcessingPlugin/PostProcessingPlugin.qml | 4 ---- plugins/PrepareStage/PrepareMenu.qml | 1 - .../resources/qml/ToolboxDownloadsGridTile.qml | 1 - .../resources/qml/ToolboxDownloadsShowcaseTile.qml | 2 -- resources/qml/ActionButton.qml | 4 ---- .../qml/ActionPanel/PrintInformationWidget.qml | 3 --- resources/qml/Dialogs/AboutDialog.qml | 2 -- resources/qml/Dialogs/AddMachineDialog.qml | 3 +-- resources/qml/ExpandableComponent.qml | 2 -- resources/qml/ExtruderIcon.qml | 3 --- resources/qml/IconLabel.qml | 3 --- resources/qml/IconWithText.qml | 2 -- resources/qml/JobSpecs.qml | 1 - resources/qml/MainWindow/MainWindowHeader.qml | 3 --- .../Menus/ConfigurationMenu/ConfigurationItem.qml | 2 -- resources/qml/ObjectsList.qml | 3 +-- .../Preferences/Materials/MaterialsBrandSection.qml | 5 ++--- .../qml/Preferences/Materials/MaterialsSlot.qml | 2 -- .../Preferences/Materials/MaterialsTypeSection.qml | 2 -- resources/qml/PrinterSelector/MachineSelector.qml | 3 --- resources/qml/Settings/SettingCategory.qml | 13 ++++++++----- resources/qml/Settings/SettingCheckBox.qml | 4 ++-- resources/qml/Settings/SettingView.qml | 11 ++++++----- resources/qml/SidebarSimple.qml | 1 - resources/themes/cura-light/styles.qml | 5 ----- 27 files changed, 20 insertions(+), 67 deletions(-) diff --git a/plugins/ModelChecker/ModelChecker.qml b/plugins/ModelChecker/ModelChecker.qml index 5e41591d6b..437df29516 100644 --- a/plugins/ModelChecker/ModelChecker.qml +++ b/plugins/ModelChecker/ModelChecker.qml @@ -33,7 +33,6 @@ Button { width: UM.Theme.getSize("save_button_specs_icons").width; height: UM.Theme.getSize("save_button_specs_icons").height; - sourceSize.width: width; sourceSize.height: width; color: control.hovered ? UM.Theme.getColor("text_scene_hover") : UM.Theme.getColor("text_scene"); source: "model_checker.svg" diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml index 5d4e17a102..0e2bd88619 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml @@ -265,7 +265,6 @@ Item { anchors.verticalCenter: parent.verticalCenter width: parent.width height: width - sourceSize.width: width sourceSize.height: width color: control.hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button") source: UM.Theme.getIcon("minus") diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml index bd4d361d35..3fa10c23b9 100644 --- a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml +++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml @@ -141,7 +141,6 @@ UM.Dialog anchors.horizontalCenter: parent.horizontalCenter width: Math.round(control.width / 2.7) height: Math.round(control.height / 2.7) - sourceSize.width: width sourceSize.height: width color: palette.text source: UM.Theme.getIcon("cross1") @@ -176,7 +175,6 @@ UM.Dialog anchors.horizontalCenter: parent.horizontalCenter width: Math.round(control.width / 2.5) height: Math.round(control.height / 2.5) - sourceSize.width: width sourceSize.height: width color: control.enabled ? palette.text : disabledPalette.text source: UM.Theme.getIcon("arrow_bottom") @@ -211,7 +209,6 @@ UM.Dialog anchors.horizontalCenter: parent.horizontalCenter width: Math.round(control.width / 2.5) height: Math.round(control.height / 2.5) - sourceSize.width: width sourceSize.height: width color: control.enabled ? palette.text : disabledPalette.text source: UM.Theme.getIcon("arrow_top") @@ -498,7 +495,6 @@ UM.Dialog anchors.horizontalCenter: parent.horizontalCenter width: Math.round(parent.width / 2) height: Math.round(parent.height / 2) - sourceSize.width: width sourceSize.height: height color: !control.enabled ? UM.Theme.getColor("action_button_disabled_text") : control.pressed ? UM.Theme.getColor("action_button_active_text") : diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index abfe3080c2..fa94bc88b2 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -107,7 +107,6 @@ Item height: UM.Theme.getSize("button_icon").height color: UM.Theme.getColor("toolbar_button_text") - sourceSize.width: width sourceSize.height: height } } diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml index be44c0f374..61374f9d99 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml @@ -52,7 +52,6 @@ Item bottom: parent.bottom right: parent.right } - sourceSize.width: width sourceSize.height: height visible: installedPackages != 0 color: (installedPackages == packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border") diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml index 4fb70541d2..8a2fdc8bc8 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml @@ -48,8 +48,6 @@ Rectangle right: parent.right bottomMargin: UM.Theme.getSize("default_lining").width } - sourceSize.width: width - sourceSize.height: height visible: installedPackages != 0 color: (installedPackages == packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border") source: "../images/installed_check.svg" diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index 2107264dff..54d77f7d59 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -50,8 +50,6 @@ Button source: "" height: buttonText.height width: visible ? height : 0 - sourceSize.width: width - sourceSize.height: height color: button.hovered ? button.textHoverColor : button.textColor visible: source != "" && !button.isIconOnRightSide anchors.verticalCenter: parent.verticalCenter @@ -78,8 +76,6 @@ Button source: buttonIconLeft.source height: buttonText.height width: visible ? height : 0 - sourceSize.width: width - sourceSize.height: height color: buttonIconLeft.color visible: source != "" && button.isIconOnRightSide anchors.verticalCenter: buttonIconLeft.verticalCenter diff --git a/resources/qml/ActionPanel/PrintInformationWidget.qml b/resources/qml/ActionPanel/PrintInformationWidget.qml index 82707576e0..436649c4e1 100644 --- a/resources/qml/ActionPanel/PrintInformationWidget.qml +++ b/resources/qml/ActionPanel/PrintInformationWidget.qml @@ -18,9 +18,6 @@ UM.RecolorImage width: UM.Theme.getSize("section_icon").width height: UM.Theme.getSize("section_icon").height - sourceSize.width: width - sourceSize.height: height - color: popup.opened ? UM.Theme.getColor("primary") : UM.Theme.getColor("text_medium") MouseArea diff --git a/resources/qml/Dialogs/AboutDialog.qml b/resources/qml/Dialogs/AboutDialog.qml index 25c9bbf74b..94eca4e7c0 100644 --- a/resources/qml/Dialogs/AboutDialog.qml +++ b/resources/qml/Dialogs/AboutDialog.qml @@ -39,8 +39,6 @@ UM.Dialog source: UM.Theme.getImage("logo") - sourceSize.width: width - sourceSize.height: height anchors.top: parent.top anchors.topMargin: ((base.minimumWidth - width) / 2) | 0 anchors.horizontalCenter: parent.horizontalCenter diff --git a/resources/qml/Dialogs/AddMachineDialog.qml b/resources/qml/Dialogs/AddMachineDialog.qml index 918c717b94..f00359869c 100644 --- a/resources/qml/Dialogs/AddMachineDialog.qml +++ b/resources/qml/Dialogs/AddMachineDialog.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 @@ -156,7 +156,6 @@ UM.Dialog anchors.rightMargin: UM.Theme.getSize("default_margin").width width: UM.Theme.getSize("standard_arrow").width height: UM.Theme.getSize("standard_arrow").height - sourceSize.width: width sourceSize.height: width color: palette.windowText source: base.activeCategory == section ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_right") diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index b438f0398c..9bedaa940c 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -139,8 +139,6 @@ Item verticalCenter: parent.verticalCenter margins: background.padding } - sourceSize.width: width - sourceSize.height: height visible: source != "" width: height height: Math.round(0.2 * base.height) diff --git a/resources/qml/ExtruderIcon.qml b/resources/qml/ExtruderIcon.qml index 742c2a9dfe..49ad73a32e 100644 --- a/resources/qml/ExtruderIcon.qml +++ b/resources/qml/ExtruderIcon.qml @@ -22,8 +22,6 @@ Item id: mainIcon anchors.fill: parent - sourceSize.width: parent.width - sourceSize.height: parent.height source: UM.Theme.getIcon("extruder_button") color: extruderEnabled ? materialColor: "gray" } @@ -64,7 +62,6 @@ Item id: disabledIcon anchors.fill: parent anchors.margins: UM.Theme.getSize("thick_lining").width - sourceSize.width: width sourceSize.height: width source: UM.Theme.getIcon("cross1") visible: !extruderEnabled diff --git a/resources/qml/IconLabel.qml b/resources/qml/IconLabel.qml index 386ed0ae01..f925b6eab5 100644 --- a/resources/qml/IconLabel.qml +++ b/resources/qml/IconLabel.qml @@ -31,9 +31,6 @@ Item width: UM.Theme.getSize("section_icon").width height: width - sourceSize.width: width - sourceSize.height: height - color: label.color visible: source != "" } diff --git a/resources/qml/IconWithText.qml b/resources/qml/IconWithText.qml index dcb3ef7851..22599b3aed 100644 --- a/resources/qml/IconWithText.qml +++ b/resources/qml/IconWithText.qml @@ -37,8 +37,6 @@ Item width: UM.Theme.getSize("section_icon").width height: UM.Theme.getSize("section_icon").height - sourceSize.width: width - sourceSize.height: height color: "black" anchors diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index 717d6e925b..935cb723de 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -60,7 +60,6 @@ Item { { width: UM.Theme.getSize("save_button_specs_icons").width; height: UM.Theme.getSize("save_button_specs_icons").height; - sourceSize.width: width; sourceSize.height: width; color: control.hovered ? UM.Theme.getColor("text_scene_hover") : UM.Theme.getColor("text_scene"); source: UM.Theme.getIcon("pencil"); diff --git a/resources/qml/MainWindow/MainWindowHeader.qml b/resources/qml/MainWindow/MainWindowHeader.qml index 34936e9b5a..fa0594e2ae 100644 --- a/resources/qml/MainWindow/MainWindowHeader.qml +++ b/resources/qml/MainWindow/MainWindowHeader.qml @@ -54,9 +54,6 @@ Rectangle source: UM.Theme.getImage("logo") width: UM.Theme.getSize("logo").width height: UM.Theme.getSize("logo").height - - sourceSize.width: width - sourceSize.height: height } Row diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 7ba7202819..4a17e8eed8 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -95,8 +95,6 @@ Button anchors.left: parent.left width: UM.Theme.getSize("main_window_header_button_icon").width height: UM.Theme.getSize("main_window_header_button_icon").height - sourceSize.width: width - sourceSize.height: height source: UM.Theme.getIcon("buildplate") color: UM.Theme.getColor("text") } diff --git a/resources/qml/ObjectsList.qml b/resources/qml/ObjectsList.qml index 8c8eaa16ae..8f45b3744f 100644 --- a/resources/qml/ObjectsList.qml +++ b/resources/qml/ObjectsList.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 @@ -55,7 +55,6 @@ Rectangle { width: control.width height: control.height - sourceSize.width: width sourceSize.height: width color: UM.Theme.getColor("setting_control_text") source: collapsed ? UM.Theme.getIcon("arrow_left") : UM.Theme.getIcon("arrow_bottom") diff --git a/resources/qml/Preferences/Materials/MaterialsBrandSection.qml b/resources/qml/Preferences/Materials/MaterialsBrandSection.qml index c8f391dfb0..a3a0e4708f 100644 --- a/resources/qml/Preferences/Materials/MaterialsBrandSection.qml +++ b/resources/qml/Preferences/Materials/MaterialsBrandSection.qml @@ -55,7 +55,8 @@ Rectangle text: "" implicitWidth: UM.Theme.getSize("favorites_button").width implicitHeight: UM.Theme.getSize("favorites_button").height - UM.RecolorImage { + UM.RecolorImage + { anchors { verticalCenter: parent.verticalCenter @@ -63,8 +64,6 @@ Rectangle } width: UM.Theme.getSize("standard_arrow").width height: UM.Theme.getSize("standard_arrow").height - sourceSize.width: width - sourceSize.height: height color: "black" source: brand_section.expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") } diff --git a/resources/qml/Preferences/Materials/MaterialsSlot.qml b/resources/qml/Preferences/Materials/MaterialsSlot.qml index a5af17f47a..a706aaf2b9 100644 --- a/resources/qml/Preferences/Materials/MaterialsSlot.qml +++ b/resources/qml/Preferences/Materials/MaterialsSlot.qml @@ -95,8 +95,6 @@ Rectangle } width: UM.Theme.getSize("favorites_button_icon").width height: UM.Theme.getSize("favorites_button_icon").height - sourceSize.width: width - sourceSize.height: height color: { if (favorite_button.hovered) diff --git a/resources/qml/Preferences/Materials/MaterialsTypeSection.qml b/resources/qml/Preferences/Materials/MaterialsTypeSection.qml index f62fc4ee16..f98c19e0b3 100644 --- a/resources/qml/Preferences/Materials/MaterialsTypeSection.qml +++ b/resources/qml/Preferences/Materials/MaterialsTypeSection.qml @@ -74,8 +74,6 @@ Rectangle } width: UM.Theme.getSize("standard_arrow").width height: UM.Theme.getSize("standard_arrow").height - sourceSize.width: width - sourceSize.height: height color: "black" source: material_type_section.expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") } diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index 15cd773c90..91b5591cd8 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -59,9 +59,6 @@ Cura.ExpandableComponent width: UM.Theme.getSize("printer_status_icon").width height: UM.Theme.getSize("printer_status_icon").height - sourceSize.width: width - sourceSize.height: height - color: UM.Theme.getColor("primary") visible: isNetworkPrinter && isPrinterConnected diff --git a/resources/qml/Settings/SettingCategory.qml b/resources/qml/Settings/SettingCategory.qml index aafe36c546..196b2d6b97 100644 --- a/resources/qml/Settings/SettingCategory.qml +++ b/resources/qml/Settings/SettingCategory.qml @@ -129,23 +129,26 @@ Button anchors.rightMargin: UM.Theme.getSize("default_margin").width width: UM.Theme.getSize("standard_arrow").width height: UM.Theme.getSize("standard_arrow").height - sourceSize.width: width sourceSize.height: width color: { if (!base.enabled) { return UM.Theme.getColor("setting_category_disabled_text") - } else if ((base.hovered || base.activeFocus) && base.checkable && base.checked) + } + else if ((base.hovered || base.activeFocus) && base.checkable && base.checked) { return UM.Theme.getColor("setting_category_active_hover_text") - } else if (base.pressed || (base.checkable && base.checked)) + } + else if (base.pressed || (base.checkable && base.checked)) { return UM.Theme.getColor("setting_category_active_text") - } else if (base.hovered || base.activeFocus) + } + else if (base.hovered || base.activeFocus) { return UM.Theme.getColor("setting_category_hover_text") - } else + } + else { return UM.Theme.getColor("setting_category_text") } diff --git a/resources/qml/Settings/SettingCheckBox.qml b/resources/qml/Settings/SettingCheckBox.qml index d37754d27c..fb2d5a2f4d 100644 --- a/resources/qml/Settings/SettingCheckBox.qml +++ b/resources/qml/Settings/SettingCheckBox.qml @@ -115,12 +115,12 @@ SettingItem return UM.Theme.getColor("setting_control_border") } - UM.RecolorImage { + UM.RecolorImage + { anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter width: Math.round(parent.width / 2.5) height: Math.round(parent.height / 2.5) - sourceSize.width: width sourceSize.height: width color: !enabled ? UM.Theme.getColor("setting_control_disabled_text") : UM.Theme.getColor("setting_control_text"); source: UM.Theme.getIcon("check") diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index ef1f123953..bb624bcbde 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -1,5 +1,5 @@ -// Copyright (c) 2017 Ultimaker B.V. -// Uranium is released under the terms of the LGPLv3 or higher. +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.7 import QtQuick.Controls 1.1 @@ -129,13 +129,14 @@ Item } style: ButtonStyle { - background: Item { - UM.RecolorImage { + background: Item + { + 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") diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index e07810691e..fb4d52979d 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -731,7 +731,6 @@ Item { anchors.fill: parent anchors.margins: 2 * screenScaleFactor - sourceSize.width: width sourceSize.height: width source: UM.Theme.getIcon(model.icon) color: UM.Theme.getColor("quality_slider_unavailable") diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index 90344d4644..aaa8ec18f1 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -73,7 +73,6 @@ QtObject anchors.rightMargin: Theme.getSize("default_margin").width width: Theme.getSize("standard_arrow").width height: Theme.getSize("standard_arrow").height - sourceSize.width: width sourceSize.height: width color: control.enabled ? Theme.getColor("setting_category_text") : Theme.getColor("setting_category_disabled_text") source: Theme.getIcon("arrow_bottom") @@ -257,7 +256,6 @@ QtObject anchors.bottomMargin: Theme.getSize("button").height - Math.round(Theme.getSize("button_icon").height / 4) width: Theme.getSize("standard_arrow").width height: Theme.getSize("standard_arrow").height - sourceSize.width: width sourceSize.height: width visible: control.menu != null; color: @@ -543,7 +541,6 @@ QtObject anchors.horizontalCenter: parent.horizontalCenter width: Math.round(parent.width / 2.5) height: Math.round(parent.height / 2.5) - sourceSize.width: width sourceSize.height: width color: Theme.getColor("checkbox_mark") source: control.exclusiveGroup ? Theme.getIcon("dot") : Theme.getIcon("check") @@ -585,7 +582,6 @@ QtObject anchors.horizontalCenter: parent.horizontalCenter width: Math.round(parent.width / 2.5) height: Math.round(parent.height / 2.5) - sourceSize.width: width sourceSize.height: width color: Theme.getColor("checkbox_mark") source: @@ -836,7 +832,6 @@ QtObject anchors.horizontalCenter: parent.horizontalCenter width: Math.floor(control.width / 2) height: Math.floor(control.height / 2) - sourceSize.width: width sourceSize.height: width color: { From ebb31409b80fe94db4d5638ef9244bdb6599fe1f Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Tue, 4 Dec 2018 15:10:31 +0100 Subject: [PATCH 0633/1240] Always return a string for preview icon Contributes to CL-1153 --- .../UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml index b6b666cbf3..2043837ff6 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml @@ -69,7 +69,7 @@ Item { return "../svg/aborted-icon.svg" } - break; + return ""; case "pausing": return "../svg/paused-icon.svg" case "paused": From 1494daf6712cc9d028799d3fa115ec9e8f4f4b8c Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Tue, 4 Dec 2018 15:11:31 +0100 Subject: [PATCH 0634/1240] Simplify preview icon logic Contributes to CL-1153 --- .../resources/qml/MonitorPrintJobPreview.qml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml index 2043837ff6..84d325aa32 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml @@ -65,11 +65,7 @@ Item case "error": return "../svg/aborted-icon.svg" case "wait_cleanup": - if (printJob.state == "wait_cleanup" && printJob.timeTotal > printJob.timeElapsed) - { - return "../svg/aborted-icon.svg" - } - return ""; + return printJob.timeTotal > printJob.timeElapsed ? "../svg/aborted-icon.svg" : ""; case "pausing": return "../svg/paused-icon.svg" case "paused": From 014a138fda4fbd4776c0751b21ce2549cf5da1c8 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Tue, 4 Dec 2018 15:12:00 +0100 Subject: [PATCH 0635/1240] Remove semi-colon Contributes to CL-1153 --- .../UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml index 84d325aa32..ec26bbe568 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml @@ -65,7 +65,7 @@ Item case "error": return "../svg/aborted-icon.svg" case "wait_cleanup": - return printJob.timeTotal > printJob.timeElapsed ? "../svg/aborted-icon.svg" : ""; + return printJob.timeTotal > printJob.timeElapsed ? "../svg/aborted-icon.svg" : "" case "pausing": return "../svg/paused-icon.svg" case "paused": From a9273ec2b5563f57781eab7d096f7a8793b1a4d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Tue, 4 Dec 2018 15:20:24 +0100 Subject: [PATCH 0636/1240] Use QTimer instead of threading.Timer --- .../src/Cloud/CloudOutputDeviceManager.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 6f3f1fb9d7..f5f3555145 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -1,9 +1,9 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import json -from threading import Timer from typing import Dict, Optional +from PyQt5.QtCore import QTimer from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply from UM.Logger import Logger @@ -45,6 +45,11 @@ class CloudOutputDeviceManager(NetworkClient): self._on_cluster_received = Signal() self._on_cluster_received.connect(self._getRemoteClusters) + self.update_timer = QTimer(CuraApplication.getInstance()) + self.update_timer.setInterval(self.CHECK_CLUSTER_INTERVAL * 1000) + self.update_timer.setSingleShot(False) + self.update_timer.timeout.connect(self._on_cluster_received.emit) + ## Override _createEmptyRequest to add the needed authentication header for talking to the Ultimaker Cloud API. def _createEmptyRequest(self, path: str, content_type: Optional[str] = "application/json") -> QNetworkRequest: @@ -61,10 +66,9 @@ class CloudOutputDeviceManager(NetworkClient): if self._account.isLoggedIn: self.get("/clusters", on_finished = self._onGetRemoteClustersFinished) - # Only start the polling thread after the user is authenticated + # Only start the polling timer after the user is authenticated # The first call to _getRemoteClusters comes from self._account.loginStateChanged - timer = Timer(5.0, self._on_cluster_received.emit) - timer.start() + self.update_timer.start() ## Callback for when the request for getting the clusters. is finished. From b2238420fb2ee25d979bba5ef7015462298d340b Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 4 Dec 2018 15:46:13 +0100 Subject: [PATCH 0637/1240] Ensure that reset always correctly gets set to basic The old code that simply resetted the preferences was still active, but this could cause a race condition in some situations. In those cases it would first set it to basic and then clear the preferences (thus resulting in no settings being visible) CURA-5981 --- resources/qml/Preferences/SettingVisibilityPage.qml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/resources/qml/Preferences/SettingVisibilityPage.qml b/resources/qml/Preferences/SettingVisibilityPage.qml index 1b964cad0c..2edbeee960 100644 --- a/resources/qml/Preferences/SettingVisibilityPage.qml +++ b/resources/qml/Preferences/SettingVisibilityPage.qml @@ -25,11 +25,7 @@ UM.PreferencesPage function reset() { - UM.Preferences.resetPreference("general/visible_settings") - - // After calling this function update Setting visibility preset combobox. - // Reset should set default setting preset ("Basic") - visibilityPreset.currentIndex = 1 + settingVisibilityPresetsModel.setActivePreset("basic") } resetEnabled: true; From 9046b39b436ffaf067de668eabb3bbdf0bf56e29 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Tue, 4 Dec 2018 16:14:08 +0100 Subject: [PATCH 0638/1240] STAR-322: Creating a Cloud API client to handle the interaction --- cura/API/Account.py | 5 + cura/NetworkClient.py | 87 ++++++---- .../NetworkedPrinterOutputDevice.py | 111 ++++++++----- .../src/Cloud/CloudApiClient.py | 155 ++++++++++++++++++ .../src/Cloud/CloudOutputDevice.py | 138 ++++------------ .../src/Cloud/CloudOutputDeviceManager.py | 78 ++++----- .../UM3NetworkPrinting/src/Cloud/Models.py | 28 +++- 7 files changed, 367 insertions(+), 235 deletions(-) create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py diff --git a/cura/API/Account.py b/cura/API/Account.py index d78c7e8826..70881000a3 100644 --- a/cura/API/Account.py +++ b/cura/API/Account.py @@ -61,6 +61,11 @@ class Account(QObject): self._authorization_service.onAuthenticationError.connect(self._onLoginStateChanged) self._authorization_service.loadAuthDataFromPreferences() + ## Returns a boolean indicating whether the given authentication is applied against staging or not. + @property + def is_staging(self) -> bool: + return "staging" in self._oauth_root + @pyqtProperty(bool, notify=loginStateChanged) def isLoggedIn(self) -> bool: return self._logged_in diff --git a/cura/NetworkClient.py b/cura/NetworkClient.py index fbe0c63c36..8a321b6af4 100644 --- a/cura/NetworkClient.py +++ b/cura/NetworkClient.py @@ -1,7 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. from time import time -from typing import Optional, Dict, Callable, List +from typing import Optional, Dict, Callable, List, Union from PyQt5.QtCore import QUrl from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply, QHttpMultiPart, QNetworkRequest, QHttpPart, \ @@ -49,6 +49,8 @@ class NetworkClient: ## Create a new empty network request. # Automatically adds the required HTTP headers. + # \param url: The URL to request + # \param content_type: The type of the body contents. def _createEmptyRequest(self, url: str, content_type: Optional[str] = "application/json") -> QNetworkRequest: request = QNetworkRequest(QUrl(url)) if content_type: @@ -120,67 +122,82 @@ class NetworkClient: def createFormPart(self, content_header: str, data: bytes, content_type: Optional[str] = None) -> QHttpPart: return self._createFormPart(content_header, data, content_type) - ## Does a PUT request to the given URL. - def put(self, url: str, data: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: + ## Sends a put request to the given path. + # url: The path after the API prefix. + # data: The data to be sent in the body + # content_type: The content type of the body data. + # on_finished: The function to call when the response is received. + # on_progress: The function to call when the progress changes. Parameters are bytes_sent / bytes_total. + def put(self, url: str, data: Union[str, bytes], content_type: Optional[str] = None, + on_finished: Optional[Callable[[QNetworkReply], None]] = None, + on_progress: Optional[Callable[[int, int], None]] = None) -> None: self._validateManager() - - request = self._createEmptyRequest(url) - self._last_request_time = time() - - if not self._manager: - Logger.log("e", "No network manager was created to execute the PUT call with.") - return - reply = self._manager.put(request, data.encode()) + request = self._createEmptyRequest(url, content_type = content_type) + self._last_request_time = time() + + if not self._manager: + return Logger.log("e", "No network manager was created to execute the PUT call with.") + + body = data if isinstance(data, bytes) else data.encode() # type: bytes + reply = self._manager.put(request, body) self._registerOnFinishedCallback(reply, on_finished) - ## Does a DELETE request to the given URL. + if on_progress is not None: + reply.uploadProgress.connect(on_progress) + + ## Sends a delete request to the given path. + # url: The path after the API prefix. + # on_finished: The function to be call when the response is received. def delete(self, url: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: self._validateManager() - + request = self._createEmptyRequest(url) self._last_request_time = time() - + if not self._manager: - Logger.log("e", "No network manager was created to execute the DELETE call with.") - return - + return Logger.log("e", "No network manager was created to execute the DELETE call with.") + reply = self._manager.deleteResource(request) self._registerOnFinishedCallback(reply, on_finished) - ## Does a GET request to the given URL. + ## Sends a get request to the given path. + # \param url: The path after the API prefix. + # \param on_finished: The function to be call when the response is received. def get(self, url: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: self._validateManager() - + request = self._createEmptyRequest(url) self._last_request_time = time() - + if not self._manager: - Logger.log("e", "No network manager was created to execute the GET call with.") - return - + return Logger.log("e", "No network manager was created to execute the GET call with.") + reply = self._manager.get(request) self._registerOnFinishedCallback(reply, on_finished) - ## Does a POST request to the given URL. - def post(self, url: str, data: str, on_finished: Optional[Callable[[QNetworkReply], None]], - on_progress: Callable = None) -> None: + ## Sends a post request to the given path. + # \param url: The path after the API prefix. + # \param data: The data to be sent in the body + # \param on_finished: The function to call when the response is received. + # \param on_progress: The function to call when the progress changes. Parameters are bytes_sent / bytes_total. + def post(self, url: str, data: Union[str, bytes], + on_finished: Optional[Callable[[QNetworkReply], None]], + on_progress: Optional[Callable[[int, int], None]] = None) -> None: self._validateManager() - + request = self._createEmptyRequest(url) self._last_request_time = time() - + if not self._manager: - Logger.log("e", "No network manager was created to execute the GET call with.") - return - - reply = self._manager.post(request, data.encode()) - + return Logger.log("e", "Could not find manager.") + + body = data if isinstance(data, bytes) else data.encode() # type: bytes + reply = self._manager.post(request, body) if on_progress is not None: reply.uploadProgress.connect(on_progress) - self._registerOnFinishedCallback(reply, on_finished) - + ## Does a POST request with form data to the given URL. def postForm(self, url: str, header_data: str, body_data: bytes, on_finished: Optional[Callable[[QNetworkReply], None]], diff --git a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py index 72b6319020..300ed5194d 100644 --- a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py +++ b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py @@ -145,7 +145,7 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): url = QUrl("http://" + self._address + self._api_prefix + target) request = QNetworkRequest(url) if content_type is not None: - request.setHeader(QNetworkRequest.ContentTypeHeader, "application/json") + request.setHeader(QNetworkRequest.ContentTypeHeader, content_type) request.setHeader(QNetworkRequest.UserAgentHeader, self._user_agent) return request @@ -180,54 +180,85 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): self._createNetworkManager() assert (self._manager is not None) - def put(self, target: str, data: Union[str, bytes], content_type: str = None, + ## Sends a put request to the given path. + # url: The path after the API prefix. + # data: The data to be sent in the body + # content_type: The content type of the body data. + # on_finished: The function to call when the response is received. + # on_progress: The function to call when the progress changes. Parameters are bytes_sent / bytes_total. + def put(self, url: str, data: Union[str, bytes], content_type: Optional[str] = None, on_finished: Optional[Callable[[QNetworkReply], None]] = None, - on_progress: Optional[Callable] = None) -> None: + on_progress: Optional[Callable[[int, int], None]] = None) -> None: self._validateManager() - request = self._createEmptyRequest(target, content_type = content_type) - self._last_request_time = time() - if self._manager is not None: - reply = self._manager.put(request, data if isinstance(data, bytes) else data.encode()) - self._registerOnFinishedCallback(reply, on_finished) - if on_progress is not None: - reply.uploadProgress.connect(on_progress) - else: - Logger.log("e", "Could not find manager.") - def delete(self, target: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: + request = self._createEmptyRequest(url, content_type = content_type) + self._last_request_time = time() + + if not self._manager: + return Logger.log("e", "No network manager was created to execute the PUT call with.") + + body = data if isinstance(data, bytes) else data.encode() # type: bytes + reply = self._manager.put(request, body) + self._registerOnFinishedCallback(reply, on_finished) + + if on_progress is not None: + reply.uploadProgress.connect(on_progress) + + ## Sends a delete request to the given path. + # url: The path after the API prefix. + # on_finished: The function to be call when the response is received. + def delete(self, url: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: self._validateManager() - request = self._createEmptyRequest(target) - self._last_request_time = time() - if self._manager is not None: - reply = self._manager.deleteResource(request) - self._registerOnFinishedCallback(reply, on_finished) - else: - Logger.log("e", "Could not find manager.") - def get(self, target: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: + request = self._createEmptyRequest(url) + self._last_request_time = time() + + if not self._manager: + return Logger.log("e", "No network manager was created to execute the DELETE call with.") + + reply = self._manager.deleteResource(request) + self._registerOnFinishedCallback(reply, on_finished) + + ## Sends a get request to the given path. + # \param url: The path after the API prefix. + # \param on_finished: The function to be call when the response is received. + def get(self, url: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: self._validateManager() - request = self._createEmptyRequest(target) - self._last_request_time = time() - if self._manager is not None: - reply = self._manager.get(request) - self._registerOnFinishedCallback(reply, on_finished) - else: - Logger.log("e", "Could not find manager.") - def post(self, target: str, data: Union[str, bytes], on_finished: Optional[Callable[[QNetworkReply], None]], - on_progress: Callable = None) -> None: + request = self._createEmptyRequest(url) + self._last_request_time = time() + + if not self._manager: + return Logger.log("e", "No network manager was created to execute the GET call with.") + + reply = self._manager.get(request) + self._registerOnFinishedCallback(reply, on_finished) + + ## Sends a post request to the given path. + # \param url: The path after the API prefix. + # \param data: The data to be sent in the body + # \param on_finished: The function to call when the response is received. + # \param on_progress: The function to call when the progress changes. Parameters are bytes_sent / bytes_total. + def post(self, url: str, data: Union[str, bytes], + on_finished: Optional[Callable[[QNetworkReply], None]], + on_progress: Optional[Callable[[int, int], None]] = None) -> None: self._validateManager() - request = self._createEmptyRequest(target) - self._last_request_time = time() - if self._manager is not None: - reply = self._manager.post(request, data if isinstance(data, bytes) else data.encode()) - if on_progress is not None: - reply.uploadProgress.connect(on_progress) - self._registerOnFinishedCallback(reply, on_finished) - else: - Logger.log("e", "Could not find manager.") - def postFormWithParts(self, target: str, parts: List[QHttpPart], on_finished: Optional[Callable[[QNetworkReply], None]], on_progress: Callable = None) -> QNetworkReply: + request = self._createEmptyRequest(url) + self._last_request_time = time() + + if not self._manager: + return Logger.log("e", "Could not find manager.") + + body = data if isinstance(data, bytes) else data.encode() # type: bytes + reply = self._manager.post(request, body) + if on_progress is not None: + reply.uploadProgress.connect(on_progress) + self._registerOnFinishedCallback(reply, on_finished) + + def postFormWithParts(self, target: str, parts: List[QHttpPart], + on_finished: Optional[Callable[[QNetworkReply], None]], + on_progress: Callable = None) -> QNetworkReply: self._validateManager() request = self._createEmptyRequest(target, content_type=None) multi_post_part = QHttpMultiPart(QHttpMultiPart.FormDataType) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py new file mode 100644 index 0000000000..1d2de1d9bf --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py @@ -0,0 +1,155 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +import json +from json import JSONDecodeError +from typing import Callable, List, Type, TypeVar, Union, Optional, Tuple, Dict + +from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply + +from UM.Logger import Logger +from cura.API import Account +from cura.NetworkClient import NetworkClient +from plugins.UM3NetworkPrinting.src.Models import BaseModel +from plugins.UM3NetworkPrinting.src.Cloud.Models import ( + CloudCluster, CloudErrorObject, CloudClusterStatus, CloudJobUploadRequest, + CloudJobResponse, + CloudPrintResponse +) + + +## The cloud API client is responsible for handling the requests and responses from the cloud. +# Each method should only handle models instead of exposing any HTTP details. +class CloudApiClient(NetworkClient): + + # The cloud URL to use for this remote cluster. + # TODO: Make sure that this URL goes to the live api before release + ROOT_PATH = "https://api-staging.ultimaker.com" + CLUSTER_API_ROOT = "{}/connect/v1/".format(ROOT_PATH) + CURA_API_ROOT = "{}/cura/v1/".format(ROOT_PATH) + + ## Initializes a new cloud API client. + # \param account: The user's account object + # \param on_error: The callback to be called whenever we receive errors from the server. + def __init__(self, account: Account, on_error: Callable[[List[CloudErrorObject]], None]): + super().__init__() + self._account = account + self._on_error = on_error + + ## Retrieves all the clusters for the user that is currently logged in. + # \param on_finished: The function to be called after the result is parsed. + def getClusters(self, on_finished: Callable[[List[CloudCluster]], any]) -> None: + url = "/clusters" + self.get(url, on_finished=self._createCallback(on_finished, CloudCluster)) + + ## Retrieves the status of the given cluster. + # \param cluster_id: The ID of the cluster. + # \param on_finished: The function to be called after the result is parsed. + def getClusterStatus(self, cluster_id: str, on_finished: Callable[[CloudClusterStatus], any]) -> None: + url = "{}/cluster/{}/status".format(self.CLUSTER_API_ROOT, cluster_id) + self.get(url, on_finished=self._createCallback(on_finished, CloudClusterStatus)) + + ## Requests the cloud to register the upload of a print job mesh. + # \param request: The request object. + # \param on_finished: The function to be called after the result is parsed. + def requestUpload(self, request: CloudJobUploadRequest, on_finished: Callable[[CloudJobResponse], any]) -> None: + url = "{}/jobs/upload".format(self.CURA_API_ROOT) + body = json.dumps({"data": request.__dict__}) + self.put(url, body, on_finished=self._createCallback(on_finished, CloudJobResponse)) + + ## Requests the cloud to register the upload of a print job mesh. + # \param upload_response: The object received after requesting an upload with `self.requestUpload`. + # \param on_finished: The function to be called after the result is parsed. It receives the print job ID. + def uploadMesh(self, upload_response: CloudJobResponse, mesh: bytes, on_finished: Callable[[str], any], + on_progress: Callable[[int], any]): + + def progressCallback(bytes_sent: int, bytes_total: int) -> None: + if bytes_total: + on_progress(int((bytes_sent / bytes_total) * 100)) + + def finishedCallback(reply: QNetworkReply): + status_code, response = self._parseReply(reply) + if status_code < 300: + on_finished(upload_response.job_id) + else: + self._uploadMeshError(status_code, response) + + # TODO: Multipart upload + self.put(upload_response.upload_url, data = mesh, content_type = upload_response.content_type, + on_finished = finishedCallback, on_progress = progressCallback) + + # Requests a cluster to print the given print job. + # \param cluster_id: The ID of the cluster. + # \param job_id: The ID of the print job. + # \param on_finished: The function to be called after the result is parsed. + def requestPrint(self, cluster_id: str, job_id: str, on_finished: Callable[[], any]) -> None: + url = "{}/cluster/{}/print/{}".format(self.CLUSTER_API_ROOT, cluster_id, job_id) + self.post(url, data = "", on_finished=self._createCallback(on_finished, CloudPrintResponse)) + + ## We override _createEmptyRequest in order to add the user credentials. + # \param url: The URL to request + # \param content_type: The type of the body contents. + def _createEmptyRequest(self, path: str, content_type: Optional[str] = "application/json") -> QNetworkRequest: + request = super()._createEmptyRequest(path, content_type) + if self._account.isLoggedIn: + request.setRawHeader(b"Authorization", "Bearer {}".format(self._account.accessToken).encode()) + return request + + ## Parses the given JSON network reply into a status code and a dictionary, handling unexpected errors as well. + # \param reply: The reply from the server. + # \return A tuple with a status code and a dictionary. + @staticmethod + def _parseReply(reply: QNetworkReply) -> Tuple[int, Dict[str, any]]: + status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) + try: + response = bytes(reply.readAll()).decode() + Logger.log("i", "Received an HTTP %s from %s with %s", status_code, reply.url, response) + return status_code, json.loads(response) + except (UnicodeDecodeError, JSONDecodeError, ValueError) as err: + error = {"code": type(err).__name__, "title": str(err), "http_code": str(status_code)} + Logger.logException("e", "Could not parse the stardust response: %s", error) + return status_code, {"errors": [error]} + + ## Calls the error handler that is responsible for handling errors uploading meshes. + # \param http_status - The status of the HTTP request. + # \param response - The response received from the upload endpoint. This is not formatted according to the standard + # JSON-api response. + def _uploadMeshError(self, http_status: int, response: Dict[str, any]) -> None: + error = CloudErrorObject( + code = "uploadError", + http_status = str(http_status), + title = "Could not upload the mesh", + meta = response + ) + self._on_error([error]) + + ## The generic type variable used to document the methods below. + Model = TypeVar("Model", bound=BaseModel) + + ## Parses the given models and calls the correct callback depending on the result. + # \param response: The response from the server, after being converted to a dict. + # \param on_finished: The callback in case the response is successful. + # \param model: The type of the model to convert the response to. It may either be a single record or a list. + def _parseModels(self, response: Dict[str, any], + on_finished: Callable[[Union[Model, List[Model]]], any], + model: Type[Model]) -> None: + if "data" in response: + data = response["data"] + result = [model(**c) for c in data] if isinstance(data, list) else model(**data) + on_finished(result) + elif "error" in response: + self._on_error([CloudErrorObject(**error) for error in response["errors"]]) + else: + Logger.log("e", "Cannot find data or errors in the cloud response: %s", response) + + ## Creates a callback function that includes the parsing of the response into the correct model. + # \param on_finished: The callback in case the response is successful. + # \param model: The type of the model to convert the response to. It may either be a single record or a list. + # \return: A function that can be passed to the + def _createCallback(self, + on_finished: Callable[[Union[Model, List[Model]]], any], + model: Type[Model], + ) -> Callable[[QNetworkReply], None]: + def parse(reply: QNetworkReply) -> None: + status_code, response = self._parseReply(reply) + return self._parseModels(response, on_finished, model) + return parse diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index d17728f513..adc670ad1e 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -1,13 +1,10 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import io -import json import os -from json import JSONDecodeError -from typing import List, Optional, Dict, cast, Union, Tuple +from typing import List, Optional, Dict, cast, Union from PyQt5.QtCore import QObject, pyqtSignal, QUrl, pyqtProperty, pyqtSlot -from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest from UM import i18nCatalog from UM.FileHandler.FileWriter import FileWriter @@ -22,10 +19,11 @@ from cura.PrinterOutput.PrinterOutputController import PrinterOutputController from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel +from plugins.UM3NetworkPrinting.src.Cloud.CloudApiClient import CloudApiClient from plugins.UM3NetworkPrinting.src.UM3PrintJobOutputModel import UM3PrintJobOutputModel from .Models import ( - CloudClusterPrinter, CloudClusterPrintJob, JobUploadRequest, JobUploadResponse, PrintResponse, CloudClusterStatus, - CloudClusterPrinterConfigurationMaterial + CloudClusterPrinter, CloudClusterPrintJob, CloudJobUploadRequest, CloudJobResponse, CloudClusterStatus, + CloudClusterPrinterConfigurationMaterial, CloudErrorObject ) @@ -40,20 +38,16 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # The translation catalog for this device. I18N_CATALOG = i18nCatalog("cura") - # The cloud URL to use for this remote cluster. - # TODO: Make sure that this URL goes to the live api before release - ROOT_PATH = "https://api-staging.ultimaker.com" - CLUSTER_API_ROOT = "{}/connect/v1/".format(ROOT_PATH) - CURA_API_ROOT = "{}/cura/v1/".format(ROOT_PATH) - # Signal triggered when the printers in the remote cluster were changed. printersChanged = pyqtSignal() # Signal triggered when the print jobs in the queue were changed. printJobsChanged = pyqtSignal() - def __init__(self, device_id: str, parent: QObject = None): + def __init__(self, api_client: CloudApiClient, device_id: str, parent: QObject = None): super().__init__(device_id = device_id, address = "", properties = {}, parent = parent) + self._api = api_client + self._setInterfaceElements() self._device_id = device_id @@ -76,40 +70,6 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self._sending_job = False self._progress_message = None # type: Optional[Message] - @staticmethod - def _parseReply(reply: QNetworkReply) -> Tuple[int, Union[None, str, bytes]]: - """ - Parses a reply from the stardust server. - :param reply: The reply received from the server. - :return: The status code and the response dict. - """ - status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) - response = None - try: - response = bytes(reply.readAll()).decode("utf-8") - response = json.loads(response) - except JSONDecodeError: - Logger.logException("w", "Unable to decode JSON from reply.") - return status_code, response - - ## We need to override _createEmptyRequest to work for the cloud. - def _createEmptyRequest(self, path: str, content_type: Optional[str] = "application/json") -> QNetworkRequest: - # noinspection PyArgumentList - url = QUrl(path) - request = QNetworkRequest(url) - request.setHeader(QNetworkRequest.ContentTypeHeader, content_type) - request.setHeader(QNetworkRequest.UserAgentHeader, self._user_agent) - - if not self._account.isLoggedIn: - # TODO: show message to user to sign in - self.setAuthenticationState(AuthState.NotAuthenticated) - else: - # TODO: not execute call at all when not signed in? - self.setAuthenticationState(AuthState.Authenticated) - request.setRawHeader(b"Authorization", "Bearer {}".format(self._account.accessToken).encode()) - - return request - ## Set all the interface elements and texts for this output device. def _setInterfaceElements(self): self.setPriority(2) # make sure we end up below the local networking and above 'save to file' @@ -223,22 +183,16 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def _update(self) -> None: super()._update() Logger.log("i", "Calling the cloud cluster") - self.get("{root}/cluster/{cluster_id}/status".format(root = self.CLUSTER_API_ROOT, - cluster_id = self._device_id), - on_finished = self._onStatusCallFinished) + if self._account.isLoggedIn: + self.setAuthenticationState(AuthState.Authenticated) + self._api.getClusterStatus(self._device_id, self._onStatusCallFinished) + else: + self.setAuthenticationState(AuthState.NotAuthenticated) ## Method called when HTTP request to status endpoint is finished. # Contains both printers and print jobs statuses in a single response. - def _onStatusCallFinished(self, reply: QNetworkReply) -> None: - status_code, response = self._parseReply(reply) - if status_code > 204 or not isinstance(response, dict) or "data" not in response: - Logger.log("w", "Got unexpected response while trying to get cloud cluster data: %s, %s", - status_code, response) - return - - Logger.log("d", "Got response form the cloud cluster %s, %s", status_code, response) - status = CloudClusterStatus(**response["data"]) - + def _onStatusCallFinished(self, status: CloudClusterStatus) -> None: + Logger.log("d", "Got response form the cloud cluster: %s", status.__dict__) # Update all data from the cluster. self._updatePrinters(status.printers) self._updatePrintJobs(status.print_jobs) @@ -325,18 +279,14 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): remote_jobs = {j.uuid: j for j in jobs} # type: Dict[str, CloudClusterPrintJob] current_jobs = {j.key: j for j in self._print_jobs} # type: Dict[str, UM3PrintJobOutputModel] - removed_job_ids = set(current_jobs).difference(set(remote_jobs)) - new_job_ids = set(remote_jobs.keys()).difference(set(current_jobs)) - updated_job_ids = set(current_jobs).intersection(set(remote_jobs)) + for removed_job_id in set(current_jobs).difference(remote_jobs): + self._print_jobs.remove(current_jobs[removed_job_id]) - for job_id in removed_job_ids: - self._print_jobs.remove(current_jobs[job_id]) + for new_job_id in set(remote_jobs.keys()).difference(current_jobs): + self._addPrintJob(remote_jobs[new_job_id]) - for job_id in new_job_ids: - self._addPrintJob(remote_jobs[job_id]) - - for job_id in updated_job_ids: - self._updateUM3PrintJobOutputModel(current_jobs[job_id], remote_jobs[job_id]) + for updated_job_id in set(current_jobs).intersection(remote_jobs): + self._updateUM3PrintJobOutputModel(current_jobs[updated_job_id], remote_jobs[updated_job_id]) # TODO: properly handle removed and updated printers self.printJobsChanged.emit() @@ -362,56 +312,25 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def _sendPrintJob(self, file_name: str, content_type: str, stream: Union[io.StringIO, io.BytesIO]) -> None: mesh = stream.getvalue() - request = JobUploadRequest() + request = CloudJobUploadRequest() request.job_name = file_name request.file_size = len(mesh) request.content_type = content_type Logger.log("i", "Creating new cloud print job: %s", request.__dict__) - self.put("{}/jobs/upload".format(self.CURA_API_ROOT), data = json.dumps({"data": request.__dict__}), - on_finished = lambda reply: self._onPrintJobCreated(mesh, reply)) + self._api.requestUpload(request, lambda response: self._onPrintJobCreated(mesh, response)) - def _onPrintJobCreated(self, mesh: bytes, reply: QNetworkReply) -> None: - status_code, response = self._parseReply(reply) - if status_code > 204 or not isinstance(response, dict) or "data" not in response: - Logger.log("w", "Unexpected response while adding to queue: {}, {}".format(status_code, response)) - self._onUploadError(self.I18N_CATALOG.i18nc("@info:status", "Could not add print job to queue.")) - return - - # TODO: Multipart upload - job_response = JobUploadResponse(**response.get("data")) + def _onPrintJobCreated(self, mesh: bytes, job_response: CloudJobResponse) -> None: Logger.log("i", "Print job created successfully: %s", job_response.__dict__) - self.put(job_response.upload_url, data = mesh, content_type = job_response.content_type, - on_finished = lambda r: self._onPrintJobUploaded(job_response.job_id, r), - on_progress = self._onUploadPrintJobProgress) + self._api.uploadMesh(job_response, mesh, self._onPrintJobUploaded, self._onUploadPrintJobProgress) + + def _onPrintJobUploaded(self, job_id: str) -> None: + self._api.requestPrint(self._device_id, job_id, self._onUploadSuccess) def _onUploadPrintJobProgress(self, bytes_sent: int, bytes_total: int) -> None: if bytes_total > 0: self._updateUploadProgress(int((bytes_sent / bytes_total) * 100)) - def _onPrintJobUploaded(self, job_id: str, reply: QNetworkReply) -> None: - status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) - if status_code > 204: - Logger.log("w", "Received unexpected response from the job upload: %s, %s.", status_code, - bytes(reply.readAll()).decode()) - self._onUploadError(self.I18N_CATALOG.i18nc("@info:status", "Could not add print job to queue.")) - return - - Logger.log("i", "Print job uploaded successfully: %s", reply.readAll()) - url = "{}/cluster/{}/print/{}".format(self.CLUSTER_API_ROOT, self._device_id, job_id) - self.post(url, data = "", on_finished = self._onPrintJobRequested) - - def _onPrintJobRequested(self, reply: QNetworkReply) -> None: - status_code, response = self._parseReply(reply) - if status_code > 204 or not isinstance(response, dict) or "data" not in response: - Logger.log("w", "Got unexpected response while trying to request printing: %s, %s", status_code, response) - self._onUploadError(self.I18N_CATALOG.i18nc("@info:status", "Could not add print job to queue.")) - return - - print_response = PrintResponse(**response["data"]) - Logger.log("i", "Print job requested successfully: %s", print_response.__dict__) - self._onUploadSuccess() - def _updateUploadProgress(self, progress: int): if not self._progress_message: self._progress_message = Message( @@ -479,3 +398,6 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): @pyqtProperty(bool, notify = printJobsChanged) def receivedPrintJobs(self) -> bool: return True + + def _onApiError(self, errors: List[CloudErrorObject]) -> None: + pass # TODO: Show errors... diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 85e734f7a3..22e2d57b05 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -1,19 +1,17 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -import json -from time import sleep from threading import Timer -from typing import Dict, Optional - -from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply +from typing import Dict, List +from UM import i18nCatalog from UM.Logger import Logger +from UM.Message import Message from UM.Signal import Signal from cura.CuraApplication import CuraApplication -from cura.NetworkClient import NetworkClient +from plugins.UM3NetworkPrinting.src.Cloud.CloudApiClient import CloudApiClient from .CloudOutputDevice import CloudOutputDevice -from .Models import CloudCluster +from .Models import CloudCluster, CloudErrorObject ## The cloud output device manager is responsible for using the Ultimaker Cloud APIs to manage remote clusters. @@ -21,14 +19,14 @@ from .Models import CloudCluster # # API spec is available on https://api.ultimaker.com/docs/connect/spec/. # -class CloudOutputDeviceManager(NetworkClient): - - # The cloud URL to use for remote clusters. - API_ROOT_PATH = "https://api-staging.ultimaker.com/connect/v1" +class CloudOutputDeviceManager: # The interval with which the remote clusters are checked CHECK_CLUSTER_INTERVAL = 5 # seconds - + + # The translation catalog for this device. + I18N_CATALOG = i18nCatalog("cura") + def __init__(self): super().__init__() @@ -37,8 +35,10 @@ class CloudOutputDeviceManager(NetworkClient): application = CuraApplication.getInstance() self._output_device_manager = application.getOutputDeviceManager() + self._account = application.getCuraAPI().account self._account.loginStateChanged.connect(self._getRemoteClusters) + self._api = CloudApiClient(self._account, self._onApiError) # When switching machines we check if we have to activate a remote cluster. application.globalContainerStackChanged.connect(self._connectToActiveMachine) @@ -46,40 +46,21 @@ class CloudOutputDeviceManager(NetworkClient): self._on_cluster_received = Signal() self._on_cluster_received.connect(self._getRemoteClusters) - - ## Override _createEmptyRequest to add the needed authentication header for talking to the Ultimaker Cloud API. - def _createEmptyRequest(self, path: str, content_type: Optional[str] = "application/json") -> QNetworkRequest: - request = super()._createEmptyRequest(self.API_ROOT_PATH + path, content_type = content_type) - if self._account.isLoggedIn: - # TODO: add correct scopes to OAuth2 client to use remote connect API. - # TODO: don't create the client when not signed in? - request.setRawHeader(b"Authorization", "Bearer {}".format(self._account.accessToken).encode()) - return request - ## Gets all remote clusters from the API. def _getRemoteClusters(self) -> None: Logger.log("i", "Retrieving remote clusters") if self._account.isLoggedIn: - self.get("/clusters", on_finished = self._onGetRemoteClustersFinished) + self._api.getClusters(self._onGetRemoteClustersFinished) # Only start the polling thread after the user is authenticated # The first call to _getRemoteClusters comes from self._account.loginStateChanged timer = Timer(5.0, self._on_cluster_received.emit) timer.start() - ## Callback for when the request for getting the clusters. is finished. - def _onGetRemoteClustersFinished(self, reply: QNetworkReply) -> None: - Logger.log("i", "Received remote clusters") + def _onGetRemoteClustersFinished(self, clusters: List[CloudCluster]) -> None: + found_clusters = {c.cluster_id: c for c in clusters} - status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) - if status_code > 204: - Logger.log("w", "Got unexpected response while trying to get cloud cluster data: {}, {}" - .format(status_code, reply.readAll())) - return - - # Parse the response (returns the "data" field from the body). - found_clusters = self._parseStatusResponse(reply) Logger.log("i", "Parsed remote clusters to %s", found_clusters) if not found_clusters: return @@ -97,28 +78,17 @@ class CloudOutputDeviceManager(NetworkClient): for cluster_id in known_cluster_ids.difference(found_cluster_ids): self._removeCloudOutputDevice(found_clusters[cluster_id]) - @staticmethod - def _parseStatusResponse(reply: QNetworkReply) -> Dict[str, CloudCluster]: - try: - response = bytes(reply.readAll()).decode() - return {c["cluster_id"]: CloudCluster(**c) for c in json.loads(response)["data"]} - except UnicodeDecodeError: - Logger.log("w", "Unable to read server response") - except json.decoder.JSONDecodeError: - Logger.logException("w", "Unable to decode JSON from reply.") - except ValueError: - Logger.logException("w", "Response was missing values.") - return {} - ## Adds a CloudOutputDevice for each entry in the remote cluster list from the API. + # \param cluster: The cluster that was added. def _addCloudOutputDevice(self, cluster: CloudCluster): - device = CloudOutputDevice(cluster.cluster_id) + device = CloudOutputDevice(self._api, cluster.cluster_id) self._output_device_manager.addOutputDevice(device) self._remote_clusters[cluster.cluster_id] = device device.connect() # TODO: remove this self._connectToActiveMachine() ## Remove a CloudOutputDevice + # \param cluster: The cluster that was removed def _removeCloudOutputDevice(self, cluster: CloudCluster): self._output_device_manager.removeOutputDevice(cluster.cluster_id) del self._remote_clusters[cluster.cluster_id] @@ -141,3 +111,15 @@ class CloudOutputDeviceManager(NetworkClient): # TODO: If so, we can also immediate connect to it. # active_machine.setMetaDataEntry("um_cloud_cluster_id", "") # self._remote_clusters.get(stored_cluster_id).connect() + + ## Handles an API error received from the cloud. + # \param errors: The errors received + def _onApiError(self, errors: List[CloudErrorObject]) -> None: + message = ". ".join(e.title for e in errors) # TODO: translate errors + message = Message( + text = message, + title = self.I18N_CATALOG.i18nc("@info:title", "Error"), + lifetime = 10, + dismissable = True + ) + message.show() diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models.py b/plugins/UM3NetworkPrinting/src/Cloud/Models.py index d7cb68e5d3..27ff7df604 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models.py @@ -1,10 +1,22 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import List +from typing import List, Dict from ..Models import BaseModel +## Class representing errors generated by the cloud servers, according to the json-api standard. +class CloudErrorObject(BaseModel): + def __init__(self, **kwargs): + self.id = None # type: str + self.code = None # type: str + self.http_status = None # type: str + self.title = None # type: str + self.detail = None # type: str + self.meta = None # type: Dict[str, any] + super().__init__(**kwargs) + + ## Class representing a cloud connected cluster. class CloudCluster(BaseModel): def __init__(self, **kwargs): @@ -95,17 +107,23 @@ class CloudClusterPrintJob(BaseModel): for p in self.constraints] +# Model that represents the status of the cluster for the cloud class CloudClusterStatus(BaseModel): def __init__(self, **kwargs): + # a list of the printers self.printers = [] # type: List[CloudClusterPrinter] + # a list of the print jobs self.print_jobs = [] # type: List[CloudClusterPrintJob] + super().__init__(**kwargs) + # converting any dictionaries into models self.printers = [CloudClusterPrinter(**p) if isinstance(p, dict) else p for p in self.printers] self.print_jobs = [CloudClusterPrintJob(**j) if isinstance(j, dict) else j for j in self.print_jobs] -class JobUploadRequest(BaseModel): +# Model that represents the request to upload a print job to the cloud +class CloudJobUploadRequest(BaseModel): def __init__(self, **kwargs): self.file_size = None # type: int self.job_name = None # type: str @@ -113,7 +131,8 @@ class JobUploadRequest(BaseModel): super().__init__(**kwargs) -class JobUploadResponse(BaseModel): +# Model that represents the response received from the cloud after requesting to upload a print job +class CloudJobResponse(BaseModel): def __init__(self, **kwargs): self.download_url = None # type: str self.job_id = None # type: str @@ -125,7 +144,8 @@ class JobUploadResponse(BaseModel): super().__init__(**kwargs) -class PrintResponse(BaseModel): +# Model that represents the responses received from the cloud after requesting a job to be printed. +class CloudPrintResponse(BaseModel): def __init__(self, **kwargs): self.cluster_job_id = None # type: str self.job_id = None # type: str From b32d6812db2abfba8973ab3b3466c02357b038ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Tue, 4 Dec 2018 16:20:27 +0100 Subject: [PATCH 0639/1240] We don't need a Signal with QTimer --- .../src/Cloud/CloudOutputDeviceManager.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 6ab72d8ee3..b7ad4e9f6a 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -7,7 +7,6 @@ from PyQt5.QtCore import QTimer from UM import i18nCatalog from UM.Logger import Logger from UM.Message import Message -from UM.Signal import Signal from cura.CuraApplication import CuraApplication from plugins.UM3NetworkPrinting.src.Cloud.CloudApiClient import CloudApiClient from .CloudOutputDevice import CloudOutputDevice @@ -43,13 +42,10 @@ class CloudOutputDeviceManager: # When switching machines we check if we have to activate a remote cluster. application.globalContainerStackChanged.connect(self._connectToActiveMachine) - self._on_cluster_received = Signal() - self._on_cluster_received.connect(self._getRemoteClusters) - self.update_timer = QTimer(CuraApplication.getInstance()) self.update_timer.setInterval(self.CHECK_CLUSTER_INTERVAL * 1000) self.update_timer.setSingleShot(False) - self.update_timer.timeout.connect(self._on_cluster_received.emit) + self.update_timer.timeout.connect(self._getRemoteClusters) ## Gets all remote clusters from the API. def _getRemoteClusters(self) -> None: @@ -59,7 +55,8 @@ class CloudOutputDeviceManager: # Only start the polling timer after the user is authenticated # The first call to _getRemoteClusters comes from self._account.loginStateChanged - self.update_timer.start() + if not self.update_timer.isActive(): + self.update_timer.start() ## Callback for when the request for getting the clusters. is finished. def _onGetRemoteClustersFinished(self, clusters: List[CloudCluster]) -> None: From 0887817f7dd0c157c758a8459bd54a46196cb766 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 4 Dec 2018 16:35:09 +0100 Subject: [PATCH 0640/1240] use special icon for cloud connected --- resources/qml/PrinterSelector/MachineSelector.qml | 5 +++-- resources/qml/PrinterSelector/MachineSelectorList.qml | 2 +- .../themes/cura-light/icons/printer_cloud_connected.svg | 0 3 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 resources/themes/cura-light/icons/printer_cloud_connected.svg diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index 15cd773c90..780b5baa74 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -12,6 +12,7 @@ Cura.ExpandableComponent id: machineSelector property bool isNetworkPrinter: Cura.MachineManager.activeMachineNetworkKey != "" + property bool isCloudConnected: Cura.MachineManager.activeMachineCloudKey != "" property bool isPrinterConnected: Cura.MachineManager.printerConnected property var outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null @@ -55,7 +56,7 @@ Cura.ExpandableComponent leftMargin: UM.Theme.getSize("thick_margin").width } - source: UM.Theme.getIcon("printer_connected") + source: isCloudConnected ? UM.Theme.getIcon("printer_cloud_connected") : UM.Theme.getIcon("printer_connected") width: UM.Theme.getSize("printer_status_icon").width height: UM.Theme.getSize("printer_status_icon").height @@ -63,7 +64,7 @@ Cura.ExpandableComponent sourceSize.height: height color: UM.Theme.getColor("primary") - visible: isNetworkPrinter && isPrinterConnected + visible: isNetworkPrinter && (isPrinterConnected || isCloudConnected) // Make a themable circle in the background so we can change it in other themes Rectangle diff --git a/resources/qml/PrinterSelector/MachineSelectorList.qml b/resources/qml/PrinterSelector/MachineSelectorList.qml index 26c703fddd..e605f23f73 100644 --- a/resources/qml/PrinterSelector/MachineSelectorList.qml +++ b/resources/qml/PrinterSelector/MachineSelectorList.qml @@ -79,7 +79,7 @@ Column delegate: MachineSelectorButton { text: model.metadata["connect_group_name"] - checked: Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["connect_group_name"] + checked: true // cloud devices are always online if they are available outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null Connections diff --git a/resources/themes/cura-light/icons/printer_cloud_connected.svg b/resources/themes/cura-light/icons/printer_cloud_connected.svg new file mode 100644 index 0000000000..e69de29bb2 From 3a733bb0a3b11b02e4cae838b8809c593bee6120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Tue, 4 Dec 2018 16:35:48 +0100 Subject: [PATCH 0641/1240] Check before removing a printer --- .../UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index b7ad4e9f6a..5440795e5d 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -92,7 +92,8 @@ class CloudOutputDeviceManager: # \param cluster: The cluster that was removed def _removeCloudOutputDevice(self, cluster: CloudCluster): self._output_device_manager.removeOutputDevice(cluster.cluster_id) - del self._remote_clusters[cluster.cluster_id] + if cluster.cluster_id in self._remote_clusters: + del self._remote_clusters[cluster.cluster_id] ## Callback for when the active machine was changed by the user. def _connectToActiveMachine(self, cluster_id: Optional[str] = None, host_name: Optional[str] = None) -> None: From 9df49a1232152f22133f627a16c2c935aa5b49c7 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 4 Dec 2018 16:39:46 +0100 Subject: [PATCH 0642/1240] Add the actual icon contents --- .../cura-light/icons/printer_cloud_connected.svg | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/resources/themes/cura-light/icons/printer_cloud_connected.svg b/resources/themes/cura-light/icons/printer_cloud_connected.svg index e69de29bb2..fff2bf7c44 100644 --- a/resources/themes/cura-light/icons/printer_cloud_connected.svg +++ b/resources/themes/cura-light/icons/printer_cloud_connected.svg @@ -0,0 +1,13 @@ + + + + noun_Cloud_377836 + Created with Sketch. + + + + + + + + \ No newline at end of file From de78c44461d89061a5110d30d3b93066dc07bfc1 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 4 Dec 2018 16:43:13 +0100 Subject: [PATCH 0643/1240] Fix configuration drop-down for dark theme This actually changes the secondary colour to be darker for the dark theme, to be more in line with the general theme then. Contributes to issue CURA-5876. --- resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml | 1 + resources/themes/cura-dark/theme.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index 72b640e94a..fe2bf34e35 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -90,6 +90,7 @@ Item radius: tabBar.visible ? UM.Theme.getSize("default_radius").width : 0 border.width: tabBar.visible ? UM.Theme.getSize("default_lining").width : 0 border.color: UM.Theme.getColor("lining") + color: UM.Theme.getColor("main_background") //Remove rounding and lining at the top. Rectangle diff --git a/resources/themes/cura-dark/theme.json b/resources/themes/cura-dark/theme.json index 078f04db1a..d9ef74ebb9 100644 --- a/resources/themes/cura-dark/theme.json +++ b/resources/themes/cura-dark/theme.json @@ -15,7 +15,7 @@ "primary_hover": [48, 182, 231, 255], "primary_text": [255, 255, 255, 204], "border": [127, 127, 127, 255], - "secondary": [241, 242, 242, 255], + "secondary": [95, 95, 95, 255], "main_window_header_button_text_inactive": [128, 128, 128, 255], "main_window_header_button_text_hovered": [255, 255, 255, 255], From 27dc17f9930958a3a4682d7ac3dcbc586e2311ca Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Tue, 4 Dec 2018 16:49:26 +0100 Subject: [PATCH 0644/1240] STAR-322: Extracting translations --- .../src/Cloud/CloudOutputDevice.py | 75 ++++++++++++------- 1 file changed, 50 insertions(+), 25 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index c91944fe4d..af9324c9b0 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -2,7 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. import io import os -from typing import List, Optional, Dict, cast, Union +from typing import List, Optional, Dict, cast, Union, Set from PyQt5.QtCore import QObject, pyqtSignal, QUrl, pyqtProperty, pyqtSlot @@ -27,6 +27,32 @@ from .Models import ( ) +## Private class that contains all the translations for this component. +class T: + # The translation catalog for this device. + + _I18N_CATALOG = i18nCatalog("cura") + + PRINT_VIA_CLOUD_BUTTON = _I18N_CATALOG.i18nc("@action:button", "Print via Cloud") + PRINT_VIA_CLOUD_TOOLTIP = _I18N_CATALOG.i18nc("@properties:tooltip", "Print via Cloud") + + CONNECTED_VIA_CLOUD = _I18N_CATALOG.i18nc("@info:status", "Connected via Cloud") + BLOCKED_UPLOADING = _I18N_CATALOG.i18nc("@info:status", "Sending new jobs (temporarily) blocked, still sending " + "the previous print job.") + + COULD_NOT_EXPORT = _I18N_CATALOG.i18nc("@info:status", "Could not export print job.") + WRITE_FAILED = _I18N_CATALOG.i18nc("@info:status", "There are no file formats available to write with!") + + SENDING_DATA_TEXT = _I18N_CATALOG.i18nc("@info:status", "Sending data to remote cluster") + SENDING_DATA_TITLE = _I18N_CATALOG.i18nc("@info:status", "Sending data to remote cluster") + + ERROR = _I18N_CATALOG.i18nc("@info:title", "Error") + UPLOAD_ERROR = _I18N_CATALOG.i18nc("@info:text", "Could not upload the data to the printer.") + + UPLOAD_SUCCESS_TITLE = _I18N_CATALOG.i18nc("@info:title", "Data Sent") + UPLOAD_SUCCESS_TEXT = _I18N_CATALOG.i18nc("@info:status", "Print job was successfully sent to the printer.") + + ## The cloud output device is a network output device that works remotely but has limited functionality. # Currently it only supports viewing the printer and print job status and adding a new job to the queue. # As such, those methods have been implemented here. @@ -34,9 +60,6 @@ from .Models import ( # # TODO: figure our how the QML interface for the cluster networking should operate with this limited functionality. class CloudOutputDevice(NetworkedPrinterOutputDevice): - - # The translation catalog for this device. - I18N_CATALOG = i18nCatalog("cura") # Signal triggered when the printers in the remote cluster were changed. printersChanged = pyqtSignal() @@ -74,9 +97,9 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def _setInterfaceElements(self): self.setPriority(2) # make sure we end up below the local networking and above 'save to file' self.setName(self._id) - self.setShortDescription(self.I18N_CATALOG.i18nc("@action:button", "Print via Cloud")) - self.setDescription(self.I18N_CATALOG.i18nc("@properties:tooltip", "Print via Cloud")) - self.setConnectionText(self.I18N_CATALOG.i18nc("@info:status", "Connected via Cloud")) + self.setShortDescription(T.PRINT_VIA_CLOUD_BUTTON) + self.setDescription(T.PRINT_VIA_CLOUD_TOOLTIP) + self.setConnectionText(T.CONNECTED_VIA_CLOUD) ## Called when Cura requests an output device to receive a (G-code) file. def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mime_types: bool = False, @@ -84,8 +107,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # Show an error message if we're already sending a job. if self._sending_job: - self._onUploadError(self.I18N_CATALOG.i18nc( - "@info:status", "Sending new jobs (temporarily) blocked, still sending the previous print job.")) + self._onUploadError(T.BLOCKED_UPLOADING) return # Indicate we have started sending a job. @@ -96,7 +118,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): writer = self._determineWriter(file_handler, file_format) if not writer: Logger.log("e", "Missing file or mesh writer!") - self._onUploadError(self.I18N_CATALOG.i18nc("@info:status", "Could not export print job.")) + self._onUploadError(T.COULD_NOT_EXPORT) return stream = io.StringIO() if file_format["mode"] == FileWriter.OutputMode.TextMode else io.BytesIO() @@ -131,9 +153,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): if len(file_formats) == 0: Logger.log("e", "There are no file formats available to write with!") - raise OutputDeviceError.WriteRequestFailedError( - self.I18N_CATALOG.i18nc("@info:status", "There are no file formats available to write with!") - ) + raise OutputDeviceError.WriteRequestFailedError(T.WRITE_FAILED) return file_formats[0] # TODO: This is yanked right out of ClusterUM3OutputDevice, great candidate for a utility or base class @@ -279,18 +299,21 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): remote_jobs = {j.uuid: j for j in jobs} # type: Dict[str, CloudClusterPrintJob] current_jobs = {j.key: j for j in self._print_jobs} # type: Dict[str, UM3PrintJobOutputModel] - for removed_job_id in set(current_jobs).difference(remote_jobs): + remote_job_ids = set(remote_jobs) # type: Set[str] + current_job_ids = set(current_jobs) # type: Set[str] + + for removed_job_id in current_job_ids.difference(remote_job_ids): self._print_jobs.remove(current_jobs[removed_job_id]) - for new_job_id in set(remote_jobs.keys()).difference(current_jobs): + for new_job_id in remote_job_ids.difference(current_jobs): self._addPrintJob(remote_jobs[new_job_id]) - for updated_job_id in set(current_jobs).intersection(remote_jobs): + for updated_job_id in current_job_ids.intersection(remote_job_ids): self._updateUM3PrintJobOutputModel(current_jobs[updated_job_id], remote_jobs[updated_job_id]) # We only have to update when jobs are added or removed - # updated jobs push their changes via their outputmodel - if len(removed_job_ids) > 0 or len(new_job_ids) > 0: + # updated jobs push their changes via their output model + if remote_job_ids != current_job_ids: self.printJobsChanged.emit() def _addPrintJob(self, job: CloudClusterPrintJob) -> None: @@ -324,7 +347,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def _onPrintJobCreated(self, mesh: bytes, job_response: CloudJobResponse) -> None: Logger.log("i", "Print job created successfully: %s", job_response.__dict__) - self._api.uploadMesh(job_response, mesh, self._onPrintJobUploaded, self._onUploadPrintJobProgress) + self._api.uploadMesh(job_response, mesh, self._onPrintJobUploaded, self._onUploadPrintJobProgress, + lambda error: self._onUploadError(T.UPLOAD_ERROR)) def _onPrintJobUploaded(self, job_id: str) -> None: self._api.requestPrint(self._device_id, job_id, self._onUploadSuccess) @@ -336,8 +360,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def _updateUploadProgress(self, progress: int): if not self._progress_message: self._progress_message = Message( - text = self.I18N_CATALOG.i18nc("@info:status", "Sending data to remote cluster"), - title = self.I18N_CATALOG.i18nc("@info:title", "Sending Data..."), + text = T.SENDING_DATA_TEXT, + title = T.SENDING_DATA_TITLE, progress = -1, lifetime = 0, dismissable = False, @@ -356,19 +380,20 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): if message: message = Message( text = message, - title = self.I18N_CATALOG.i18nc("@info:title", "Error"), + title = T.ERROR, lifetime = 10, dismissable = True ) message.show() - self._sending_job = False # the upload has failed so we're not sending a job anymore + self._sending_job = False # the upload has finished so we're not sending a job anymore self.writeError.emit() + # Shows a message when the upload has succeeded def _onUploadSuccess(self): self._resetUploadProgress() message = Message( - text = self.I18N_CATALOG.i18nc("@info:status", "Print job was successfully sent to the printer."), - title = self.I18N_CATALOG.i18nc("@info:title", "Data Sent"), + text = T.UPLOAD_SUCCESS_TEXT, + title = T.UPLOAD_SUCCESS_TITLE, lifetime = 5, dismissable = True, ) From 02efc9e1a905d1345a493d17108f74ca68b82110 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 4 Dec 2018 16:54:36 +0100 Subject: [PATCH 0645/1240] Fix cloud status icon size --- resources/themes/cura-light/icons/printer_cloud_connected.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/themes/cura-light/icons/printer_cloud_connected.svg b/resources/themes/cura-light/icons/printer_cloud_connected.svg index fff2bf7c44..ef6f0f2910 100644 --- a/resources/themes/cura-light/icons/printer_cloud_connected.svg +++ b/resources/themes/cura-light/icons/printer_cloud_connected.svg @@ -1,5 +1,5 @@ - + noun_Cloud_377836 Created with Sketch. From 1544ab6cf0b5b5a746e2084dffce2da7a3172067 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 4 Dec 2018 17:07:59 +0100 Subject: [PATCH 0646/1240] Explicitly enable hover on ConfigurationItem Apparently the default for this depends on some system setting. In Nallath's computer that system setting makes it false by default for some reason. Contributes to issue CURA-5876. --- resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 4a17e8eed8..af2712be44 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -12,6 +12,7 @@ Button id: configurationItem property var configuration: null + hoverEnabled: true height: childrenRect.height From 780e5e16917192e8f639142e1058f399a5e291f5 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 4 Dec 2018 17:09:55 +0100 Subject: [PATCH 0647/1240] Fix binding loop in PrintCoreConfiguration Contributes to issue CURA-5876. --- .../qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml index 4064a961d5..885f02d740 100644 --- a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml @@ -12,7 +12,7 @@ Row id: extruderInfo property var printCoreConfiguration - height: childrenRect.height + height: information.height spacing: UM.Theme.getSize("default_margin").width //Extruder icon. From 4ff5e43a28f3fd7479cd60defa7935f74c2f5729 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 4 Dec 2018 17:24:48 +0100 Subject: [PATCH 0648/1240] Handle Empty and Unknown material cases CURA-5982 --- plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index 1e0f538d8d..e31229680c 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -608,14 +608,15 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): def _createMaterialOutputModel(self, material_data: Dict[str, Any]) -> "MaterialOutputModel": material_manager = CuraApplication.getInstance().getMaterialManager() material_group_list = material_manager.getMaterialGroupListByGUID(material_data["guid"]) - # This can happen if the connected machine has no material in one or more extruders, so we should return an - # "empty" material model. + # This can happen if the connected machine has no material in one or more extruders (if GUID is empty), or the + # material is unknown to Cura, so we should return an "empty" or "unknown" material model. if material_group_list is None: + material_name = "Empty" if len(material_data["guid"]) == 0 else "Unknown" return MaterialOutputModel(guid = material_data["guid"], type = material_data.get("type", ""), color = material_data.get("color", ""), brand = material_data.get("brand", ""), - name = material_data.get("name", "Empty") + name = material_data.get("name", material_name) ) # Sort the material groups by "is_read_only = True" first, and then the name alphabetically. From 8ea4edf67e576014c9ec321e5f847f3659d13e00 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Tue, 4 Dec 2018 17:37:58 +0100 Subject: [PATCH 0649/1240] STAR-322: Fixing job uploads --- cura/NetworkClient.py | 4 +- .../src/Cloud/CloudApiClient.py | 54 ++++++++----------- .../src/Cloud/CloudOutputDevice.py | 43 ++++++++------- .../src/Cloud/CloudOutputDeviceManager.py | 2 +- 4 files changed, 49 insertions(+), 54 deletions(-) diff --git a/cura/NetworkClient.py b/cura/NetworkClient.py index 8a321b6af4..5294813fb7 100644 --- a/cura/NetworkClient.py +++ b/cura/NetworkClient.py @@ -60,7 +60,7 @@ class NetworkClient: ## Executes the correct callback method when a network request finishes. def __handleOnFinished(self, reply: QNetworkReply) -> None: - + # Due to garbage collection, we need to cache certain bits of post operations. # As we don't want to keep them around forever, delete them if we get a reply. if reply.operation() == QNetworkAccessManager.PostOperation: @@ -79,6 +79,8 @@ class NetworkClient: callback_key = reply.url().toString() + str(reply.operation()) if callback_key in self._on_finished_callbacks: self._on_finished_callbacks[callback_key](reply) + else: + Logger.log("w", "Received reply to URL %s but no callbacks are registered", reply.url()) ## Removes all cached Multi-Part items. def _clearCachedMultiPart(self, reply: QNetworkReply) -> None: diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py index 1d2de1d9bf..d6c20d387b 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py @@ -11,9 +11,7 @@ from cura.API import Account from cura.NetworkClient import NetworkClient from plugins.UM3NetworkPrinting.src.Models import BaseModel from plugins.UM3NetworkPrinting.src.Cloud.Models import ( - CloudCluster, CloudErrorObject, CloudClusterStatus, CloudJobUploadRequest, - CloudJobResponse, - CloudPrintResponse + CloudCluster, CloudErrorObject, CloudClusterStatus, CloudJobUploadRequest, CloudPrintResponse, CloudJobResponse ) @@ -24,8 +22,8 @@ class CloudApiClient(NetworkClient): # The cloud URL to use for this remote cluster. # TODO: Make sure that this URL goes to the live api before release ROOT_PATH = "https://api-staging.ultimaker.com" - CLUSTER_API_ROOT = "{}/connect/v1/".format(ROOT_PATH) - CURA_API_ROOT = "{}/cura/v1/".format(ROOT_PATH) + CLUSTER_API_ROOT = "{}/connect/v1".format(ROOT_PATH) + CURA_API_ROOT = "{}/cura/v1".format(ROOT_PATH) ## Initializes a new cloud API client. # \param account: The user's account object @@ -38,15 +36,15 @@ class CloudApiClient(NetworkClient): ## Retrieves all the clusters for the user that is currently logged in. # \param on_finished: The function to be called after the result is parsed. def getClusters(self, on_finished: Callable[[List[CloudCluster]], any]) -> None: - url = "/clusters" - self.get(url, on_finished=self._createCallback(on_finished, CloudCluster)) + url = "{}/clusters".format(self.CLUSTER_API_ROOT) + self.get(url, on_finished=self._wrapCallback(on_finished, CloudCluster)) ## Retrieves the status of the given cluster. # \param cluster_id: The ID of the cluster. # \param on_finished: The function to be called after the result is parsed. def getClusterStatus(self, cluster_id: str, on_finished: Callable[[CloudClusterStatus], any]) -> None: url = "{}/cluster/{}/status".format(self.CLUSTER_API_ROOT, cluster_id) - self.get(url, on_finished=self._createCallback(on_finished, CloudClusterStatus)) + self.get(url, on_finished=self._wrapCallback(on_finished, CloudClusterStatus)) ## Requests the cloud to register the upload of a print job mesh. # \param request: The request object. @@ -54,13 +52,16 @@ class CloudApiClient(NetworkClient): def requestUpload(self, request: CloudJobUploadRequest, on_finished: Callable[[CloudJobResponse], any]) -> None: url = "{}/jobs/upload".format(self.CURA_API_ROOT) body = json.dumps({"data": request.__dict__}) - self.put(url, body, on_finished=self._createCallback(on_finished, CloudJobResponse)) + self.put(url, body, on_finished=self._wrapCallback(on_finished, CloudJobResponse)) ## Requests the cloud to register the upload of a print job mesh. # \param upload_response: The object received after requesting an upload with `self.requestUpload`. + # \param mesh: The mesh data to be uploaded. # \param on_finished: The function to be called after the result is parsed. It receives the print job ID. + # \param on_progress: A function to be called during upload progress. It receives a percentage (0-100). + # \param on_error: A function to be called if the upload fails. It receives a dict with the error. def uploadMesh(self, upload_response: CloudJobResponse, mesh: bytes, on_finished: Callable[[str], any], - on_progress: Callable[[int], any]): + on_progress: Callable[[int], any], on_error: Callable[[dict], any]): def progressCallback(bytes_sent: int, bytes_total: int) -> None: if bytes_total: @@ -71,7 +72,8 @@ class CloudApiClient(NetworkClient): if status_code < 300: on_finished(upload_response.job_id) else: - self._uploadMeshError(status_code, response) + Logger.log("e", "Received unexpected response %s uploading mesh: %s", status_code, response) + on_error(response) # TODO: Multipart upload self.put(upload_response.upload_url, data = mesh, content_type = upload_response.content_type, @@ -81,9 +83,9 @@ class CloudApiClient(NetworkClient): # \param cluster_id: The ID of the cluster. # \param job_id: The ID of the print job. # \param on_finished: The function to be called after the result is parsed. - def requestPrint(self, cluster_id: str, job_id: str, on_finished: Callable[[], any]) -> None: + def requestPrint(self, cluster_id: str, job_id: str, on_finished: Callable[[CloudPrintResponse], any]) -> None: url = "{}/cluster/{}/print/{}".format(self.CLUSTER_API_ROOT, cluster_id, job_id) - self.post(url, data = "", on_finished=self._createCallback(on_finished, CloudPrintResponse)) + self.post(url, data = "", on_finished=self._wrapCallback(on_finished, CloudPrintResponse)) ## We override _createEmptyRequest in order to add the user credentials. # \param url: The URL to request @@ -92,6 +94,7 @@ class CloudApiClient(NetworkClient): request = super()._createEmptyRequest(path, content_type) if self._account.isLoggedIn: request.setRawHeader(b"Authorization", "Bearer {}".format(self._account.accessToken).encode()) + Logger.log("i", "Created request for URL %s. Logged in = %s", path, self._account.isLoggedIn) return request ## Parses the given JSON network reply into a status code and a dictionary, handling unexpected errors as well. @@ -102,26 +105,13 @@ class CloudApiClient(NetworkClient): status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) try: response = bytes(reply.readAll()).decode() - Logger.log("i", "Received an HTTP %s from %s with %s", status_code, reply.url, response) + Logger.log("i", "Received a reply %s from %s with %s", status_code, reply.url().toString(), response) return status_code, json.loads(response) except (UnicodeDecodeError, JSONDecodeError, ValueError) as err: error = {"code": type(err).__name__, "title": str(err), "http_code": str(status_code)} Logger.logException("e", "Could not parse the stardust response: %s", error) return status_code, {"errors": [error]} - ## Calls the error handler that is responsible for handling errors uploading meshes. - # \param http_status - The status of the HTTP request. - # \param response - The response received from the upload endpoint. This is not formatted according to the standard - # JSON-api response. - def _uploadMeshError(self, http_status: int, response: Dict[str, any]) -> None: - error = CloudErrorObject( - code = "uploadError", - http_status = str(http_status), - title = "Could not upload the mesh", - meta = response - ) - self._on_error([error]) - ## The generic type variable used to document the methods below. Model = TypeVar("Model", bound=BaseModel) @@ -141,14 +131,14 @@ class CloudApiClient(NetworkClient): else: Logger.log("e", "Cannot find data or errors in the cloud response: %s", response) - ## Creates a callback function that includes the parsing of the response into the correct model. + ## Wraps a callback function so that it includes the parsing of the response into the correct model. # \param on_finished: The callback in case the response is successful. # \param model: The type of the model to convert the response to. It may either be a single record or a list. # \return: A function that can be passed to the - def _createCallback(self, - on_finished: Callable[[Union[Model, List[Model]]], any], - model: Type[Model], - ) -> Callable[[QNetworkReply], None]: + def _wrapCallback(self, + on_finished: Callable[[Union[Model, List[Model]]], any], + model: Type[Model], + ) -> Callable[[QNetworkReply], None]: def parse(reply: QNetworkReply) -> None: status_code, response = self._parseReply(reply) return self._parseModels(response, on_finished, model) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index af9324c9b0..27bf3a821e 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -2,6 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. import io import os +from time import time from typing import List, Optional, Dict, cast, Union, Set from PyQt5.QtCore import QObject, pyqtSignal, QUrl, pyqtProperty, pyqtSlot @@ -23,11 +24,12 @@ from plugins.UM3NetworkPrinting.src.Cloud.CloudApiClient import CloudApiClient from plugins.UM3NetworkPrinting.src.UM3PrintJobOutputModel import UM3PrintJobOutputModel from .Models import ( CloudClusterPrinter, CloudClusterPrintJob, CloudJobUploadRequest, CloudJobResponse, CloudClusterStatus, - CloudClusterPrinterConfigurationMaterial, CloudErrorObject + CloudClusterPrinterConfigurationMaterial, CloudErrorObject, + CloudPrintResponse ) -## Private class that contains all the translations for this component. +## Class that contains all the translations for this module. class T: # The translation catalog for this device. @@ -61,13 +63,20 @@ class T: # TODO: figure our how the QML interface for the cluster networking should operate with this limited functionality. class CloudOutputDevice(NetworkedPrinterOutputDevice): + # The interval with which the remote clusters are checked + CHECK_CLUSTER_INTERVAL = 2.0 # seconds + # Signal triggered when the printers in the remote cluster were changed. printersChanged = pyqtSignal() # Signal triggered when the print jobs in the queue were changed. printJobsChanged = pyqtSignal() - def __init__(self, api_client: CloudApiClient, device_id: str, parent: QObject = None): + ## Creates a new cloud output device + # \param api_client: The client that will run the API calls + # \param device_id: The ID of the device (i.e. the cluster_id for the cloud API) + # \param parent: The optional parent of this output device. + def __init__(self, api_client: CloudApiClient, device_id: str, parent: QObject = None) -> None: super().__init__(device_id = device_id, address = "", properties = {}, parent = parent) self._api = api_client @@ -76,10 +85,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self._device_id = device_id self._account = CuraApplication.getInstance().getCuraAPI().account - # Cluster does not have authentication, so default to authenticated - self._authentication_state = AuthState.Authenticated - - # We re-use the Cura Connect monitor tab to get the most functionality right away. + # We use the Cura Connect monitor tab to get most functionality right away. self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../resources/qml/ClusterMonitorItem.qml") self._control_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), @@ -118,11 +124,12 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): writer = self._determineWriter(file_handler, file_format) if not writer: Logger.log("e", "Missing file or mesh writer!") - self._onUploadError(T.COULD_NOT_EXPORT) - return + return self._onUploadError(T.COULD_NOT_EXPORT) stream = io.StringIO() if file_format["mode"] == FileWriter.OutputMode.TextMode else io.BytesIO() writer.write(stream, nodes) + + # TODO: Remove extension from the file name, since we are using content types now self._sendPrintJob(file_name + "." + file_format["extension"], file_format["mime_type"], stream) # TODO: This is yanked right out of ClusterUM3OutputDevice, great candidate for a utility or base class @@ -202,7 +209,9 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Called when the network data should be updated. def _update(self) -> None: super()._update() - Logger.log("i", "Calling the cloud cluster") + if self._last_response_time and time() - self._last_response_time < self.CHECK_CLUSTER_INTERVAL: + return # avoid calling the cloud too often + if self._account.isLoggedIn: self.setAuthenticationState(AuthState.Authenticated) self._api.getClusterStatus(self._device_id, self._onStatusCallFinished) @@ -212,7 +221,6 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Method called when HTTP request to status endpoint is finished. # Contains both printers and print jobs statuses in a single response. def _onStatusCallFinished(self, status: CloudClusterStatus) -> None: - Logger.log("d", "Got response form the cloud cluster: %s", status.__dict__) # Update all data from the cluster. self._updatePrinters(status.printers) self._updatePrintJobs(status.print_jobs) @@ -342,21 +350,15 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): request.file_size = len(mesh) request.content_type = content_type - Logger.log("i", "Creating new cloud print job: %s", request.__dict__) self._api.requestUpload(request, lambda response: self._onPrintJobCreated(mesh, response)) def _onPrintJobCreated(self, mesh: bytes, job_response: CloudJobResponse) -> None: - Logger.log("i", "Print job created successfully: %s", job_response.__dict__) - self._api.uploadMesh(job_response, mesh, self._onPrintJobUploaded, self._onUploadPrintJobProgress, - lambda error: self._onUploadError(T.UPLOAD_ERROR)) + self._api.uploadMesh(job_response, mesh, self._onPrintJobUploaded, self._updateUploadProgress, + lambda _: self._onUploadError(T.UPLOAD_ERROR)) def _onPrintJobUploaded(self, job_id: str) -> None: self._api.requestPrint(self._device_id, job_id, self._onUploadSuccess) - def _onUploadPrintJobProgress(self, bytes_sent: int, bytes_total: int) -> None: - if bytes_total > 0: - self._updateUploadProgress(int((bytes_sent / bytes_total) * 100)) - def _updateUploadProgress(self, progress: int): if not self._progress_message: self._progress_message = Message( @@ -389,7 +391,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self.writeError.emit() # Shows a message when the upload has succeeded - def _onUploadSuccess(self): + def _onUploadSuccess(self, response: CloudPrintResponse): + Logger.log("i", "The cluster will be printing this print job with the ID %s", response.cluster_job_id) self._resetUploadProgress() message = Message( text = T.UPLOAD_SUCCESS_TEXT, diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 5440795e5d..772d40edd4 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -21,7 +21,7 @@ from .Models import CloudCluster, CloudErrorObject class CloudOutputDeviceManager: # The interval with which the remote clusters are checked - CHECK_CLUSTER_INTERVAL = 5 # seconds + CHECK_CLUSTER_INTERVAL = 5.0 # seconds # The translation catalog for this device. I18N_CATALOG = i18nCatalog("cura") From d0513e40e15b831b41b5ec9821178e2b8634172b Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 4 Dec 2018 22:21:36 +0100 Subject: [PATCH 0650/1240] Remove crappy implementation --- cura/Settings/MachineManager.py | 6 --- .../src/Cloud/CloudOutputDeviceManager.py | 5 +- resources/qml/Menus/CloudPrinterMenu.qml | 26 ----------- resources/qml/Menus/PrinterMenu.qml | 17 ------- .../qml/PrinterSelector/MachineSelector.qml | 5 +- .../PrinterSelector/MachineSelectorList.qml | 46 ++----------------- .../icons/printer_cloud_connected.svg | 2 +- 7 files changed, 8 insertions(+), 99 deletions(-) delete mode 100644 resources/qml/Menus/CloudPrinterMenu.qml diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 15e2c67c33..53390ca88d 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -527,12 +527,6 @@ class MachineManager(QObject): return self._global_container_stack.getMetaDataEntry("um_network_key", "") return "" - @pyqtProperty(str, notify=printerConnectedStatusChanged) - def activeMachineCloudKey(self) -> str: - if self._global_container_stack: - return self._global_container_stack.getMetaDataEntry("um_cloud_cluster_id", "") - return "" - @pyqtProperty(str, notify = printerConnectedStatusChanged) def activeMachineNetworkGroupName(self) -> str: if self._global_container_stack: diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 772d40edd4..9f7e8fa74a 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -86,7 +86,7 @@ class CloudOutputDeviceManager: self._output_device_manager.addOutputDevice(device) self._remote_clusters[cluster.cluster_id] = device device.connect() # TODO: remove this - self._connectToActiveMachine(cluster.cluster_id, cluster.host_name) + self._connectToActiveMachine(cluster.cluster_id) ## Remove a CloudOutputDevice # \param cluster: The cluster that was removed @@ -96,7 +96,7 @@ class CloudOutputDeviceManager: del self._remote_clusters[cluster.cluster_id] ## Callback for when the active machine was changed by the user. - def _connectToActiveMachine(self, cluster_id: Optional[str] = None, host_name: Optional[str] = None) -> None: + def _connectToActiveMachine(self, cluster_id: Optional[str] = None) -> None: active_machine = CuraApplication.getInstance().getGlobalContainerStack() if not active_machine: return @@ -104,7 +104,6 @@ class CloudOutputDeviceManager: # TODO: Remove this once correct pairing has been added (see below). if cluster_id: active_machine.setMetaDataEntry("um_cloud_cluster_id", cluster_id) - active_machine.setMetaDataEntry("connect_group_name", host_name) # Check if the stored cluster_id for the active machine is in our list of remote clusters. stored_cluster_id = active_machine.getMetaDataEntry("um_cloud_cluster_id") diff --git a/resources/qml/Menus/CloudPrinterMenu.qml b/resources/qml/Menus/CloudPrinterMenu.qml deleted file mode 100644 index bd03890642..0000000000 --- a/resources/qml/Menus/CloudPrinterMenu.qml +++ /dev/null @@ -1,26 +0,0 @@ -// 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.4 - -import UM 1.2 as UM -import Cura 1.0 as Cura - -Instantiator { - - model: UM.ContainerStacksModel { - filter: {"type": "machine", "um_cloud_cluster_id": "*"} - } - - MenuItem { - // iconSource: UM.Theme.getIcon("printer_single") TODO: use cloud icon here - text: model.metadata["connect_group_name"] - checkable: true - checked: true // cloud printers are only listed if they are actually online - exclusiveGroup: group; - onTriggered: Cura.MachineManager.setActiveMachine(model.id); - } - - onObjectAdded: menu.insertItem(index, object) - onObjectRemoved: menu.removeItem(object) -} diff --git a/resources/qml/Menus/PrinterMenu.qml b/resources/qml/Menus/PrinterMenu.qml index a924b0e589..741d927c13 100644 --- a/resources/qml/Menus/PrinterMenu.qml +++ b/resources/qml/Menus/PrinterMenu.qml @@ -37,23 +37,6 @@ Menu visible: networkPrinterMenu.count > 0 } - MenuItem - { - text: catalog.i18nc("@label:category menu label", "Cloud enabled printers") - enabled: false - visible: cloudPrinterMenu.count > 0 - } - - CloudPrinterMenu - { - id: cloudPrinterMenu - } - - MenuSeparator - { - visible: cloudPrinterMenu.count > 0 - } - MenuItem { text: catalog.i18nc("@label:category menu label", "Local printers") diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index 780b5baa74..15cd773c90 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -12,7 +12,6 @@ Cura.ExpandableComponent id: machineSelector property bool isNetworkPrinter: Cura.MachineManager.activeMachineNetworkKey != "" - property bool isCloudConnected: Cura.MachineManager.activeMachineCloudKey != "" property bool isPrinterConnected: Cura.MachineManager.printerConnected property var outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null @@ -56,7 +55,7 @@ Cura.ExpandableComponent leftMargin: UM.Theme.getSize("thick_margin").width } - source: isCloudConnected ? UM.Theme.getIcon("printer_cloud_connected") : UM.Theme.getIcon("printer_connected") + source: UM.Theme.getIcon("printer_connected") width: UM.Theme.getSize("printer_status_icon").width height: UM.Theme.getSize("printer_status_icon").height @@ -64,7 +63,7 @@ Cura.ExpandableComponent sourceSize.height: height color: UM.Theme.getColor("primary") - visible: isNetworkPrinter && (isPrinterConnected || isCloudConnected) + visible: isNetworkPrinter && isPrinterConnected // Make a themable circle in the background so we can change it in other themes Rectangle diff --git a/resources/qml/PrinterSelector/MachineSelectorList.qml b/resources/qml/PrinterSelector/MachineSelectorList.qml index e605f23f73..445940ab50 100644 --- a/resources/qml/PrinterSelector/MachineSelectorList.qml +++ b/resources/qml/PrinterSelector/MachineSelectorList.qml @@ -32,7 +32,8 @@ Column id: networkedPrintersModel filter: { - "type": "machine", "um_network_key": "*", "hidden": "False" + "type": "machine", + "um_network_key": "*" } } @@ -50,46 +51,6 @@ Column } } - Label - { - text: catalog.i18nc("@label", "Cloud connected printers") - visible: cloudPrintersModel.items.length > 0 - leftPadding: UM.Theme.getSize("default_margin").width - height: visible ? contentHeight + 2 * UM.Theme.getSize("default_margin").height : 0 - renderType: Text.NativeRendering - font: UM.Theme.getFont("medium") - color: UM.Theme.getColor("text_medium") - verticalAlignment: Text.AlignVCenter - } - - Repeater - { - id: cloudPrinters - - model: UM.ContainerStacksModel - { - id: cloudPrintersModel - filter: - { - "type": "machine", - "um_cloud_cluster_id": "*" - } - } - - delegate: MachineSelectorButton - { - text: model.metadata["connect_group_name"] - checked: true // cloud devices are always online if they are available - outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null - - Connections - { - target: Cura.MachineManager - onActiveMachineNetworkGroupNameChanged: checked = Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["connect_group_name"] - } - } - } - Label { text: catalog.i18nc("@label", "Preset printers") @@ -112,8 +73,7 @@ Column filter: { "type": "machine", - "um_network_key": null, - "um_cloud_cluster_id": null + "um_network_key": null } } diff --git a/resources/themes/cura-light/icons/printer_cloud_connected.svg b/resources/themes/cura-light/icons/printer_cloud_connected.svg index ef6f0f2910..59ca67e93e 100644 --- a/resources/themes/cura-light/icons/printer_cloud_connected.svg +++ b/resources/themes/cura-light/icons/printer_cloud_connected.svg @@ -1,5 +1,5 @@ - + noun_Cloud_377836 Created with Sketch. From b57f6c5c6a6f7b227963bbbfb06633e7e306c113 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 4 Dec 2018 22:58:50 +0100 Subject: [PATCH 0651/1240] Do no stop when no clusters are found, we still might need to remove some --- .../src/Cloud/CloudOutputDeviceManager.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 9f7e8fa74a..0fbeeb82b6 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -63,8 +63,6 @@ class CloudOutputDeviceManager: found_clusters = {c.cluster_id: c for c in clusters} Logger.log("i", "Parsed remote clusters to %s", found_clusters) - if not found_clusters: - return known_cluster_ids = set(self._remote_clusters.keys()) found_cluster_ids = set(found_clusters.keys()) @@ -85,7 +83,6 @@ class CloudOutputDeviceManager: device = CloudOutputDevice(self._api, cluster.cluster_id) self._output_device_manager.addOutputDevice(device) self._remote_clusters[cluster.cluster_id] = device - device.connect() # TODO: remove this self._connectToActiveMachine(cluster.cluster_id) ## Remove a CloudOutputDevice @@ -95,13 +92,14 @@ class CloudOutputDeviceManager: if cluster.cluster_id in self._remote_clusters: del self._remote_clusters[cluster.cluster_id] - ## Callback for when the active machine was changed by the user. + ## Callback for when the active machine was changed by the user or a new remote cluster was found. def _connectToActiveMachine(self, cluster_id: Optional[str] = None) -> None: active_machine = CuraApplication.getInstance().getGlobalContainerStack() if not active_machine: return # TODO: Remove this once correct pairing has been added (see below). + # TODO: This just adds any available cluster to the active device for testing. if cluster_id: active_machine.setMetaDataEntry("um_cloud_cluster_id", cluster_id) From a62da4e5239b2f29d66d261d5405aa3e35f3f6d0 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 5 Dec 2018 09:13:48 +0100 Subject: [PATCH 0652/1240] Use setCurrentIndex instead of direct assignment This should prevent the binding from breaking and hopefully the segfault CURA-5876 --- resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index fe2bf34e35..2667c837ba 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -76,7 +76,7 @@ Item target: repeater.model onModelChanged: { - tabBar.currentIndex = Math.max(Cura.ExtruderManager.activeExtruderIndex, 0) + tabBar.setCurrentIndex(Cura.ExtruderManager.activeExtruderIndex) } } } From cdb8020029bec6ee1c6c4224ff5cb02159dd810e Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 5 Dec 2018 09:39:04 +0100 Subject: [PATCH 0653/1240] Add another expandable component Use one of them if the drop-panel has to act as a Popup and the other if it has to act as a standard component. Contributes to CURA-5941. --- .../SimulationViewMenuComponent.qml | 1 - resources/qml/ExpandableComponent.qml | 66 ++---- resources/qml/ExpandablePopup.qml | 204 ++++++++++++++++++ .../QuickConfigurationSelector.qml | 2 +- .../PrintSetupSelector/PrintSetupSelector.qml | 4 - .../qml/PrinterSelector/MachineSelector.qml | 4 +- resources/qml/ViewsSelector.qml | 8 +- 7 files changed, 227 insertions(+), 62 deletions(-) create mode 100644 resources/qml/ExpandablePopup.qml diff --git a/plugins/SimulationView/SimulationViewMenuComponent.qml b/plugins/SimulationView/SimulationViewMenuComponent.qml index 9dc3b67658..76875a035d 100644 --- a/plugins/SimulationView/SimulationViewMenuComponent.qml +++ b/plugins/SimulationView/SimulationViewMenuComponent.qml @@ -16,7 +16,6 @@ Cura.ExpandableComponent id: base width: UM.Theme.getSize("layerview_menu_size").width - contentType: Cura.ExpandableComponent.ContentType.Fixed Connections { diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index f6e340c4ea..4f4848ea8b 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -20,20 +20,11 @@ Item AlignRight } - enum ContentType - { - Floating, - Fixed - } - // The headerItem holds the QML item that is always displayed. property alias headerItem: headerItemLoader.sourceComponent // The contentItem holds the QML item that is shown when the "open" button is pressed - property var contentItem: content.contentItem - - // Defines the type of the contents - property int contentType: ExpandableComponent.ContentType.Floating + property alias contentItem: content.contentItem property color contentBackgroundColor: UM.Theme.getColor("action_button") @@ -48,7 +39,7 @@ Item property alias contentPadding: content.padding // How much spacing is needed for the contentItem by Y coordinate - property var contentSpacingY: 0 + property var contentSpacingY: UM.Theme.getSize("narrow_margin").width // How much padding is needed around the header & button property alias headerPadding: background.padding @@ -64,17 +55,12 @@ Item // Is the "drawer" open? readonly property alias expanded: content.visible - property alias expandedHighlightColor: expandedHighlight.color - // What should the radius of the header be. This is also influenced by the headerCornerSide property alias headerRadius: background.radius // On what side should the header corners be shown? 1 is down, 2 is left, 3 is up and 4 is right. property alias headerCornerSide: background.cornerSide - // Change the contentItem close behaviour - property alias contentClosePolicy : content.closePolicy - property alias headerShadowColor: shadow.color property alias enableHeaderShadow: shadow.visible @@ -83,14 +69,7 @@ Item function toggleContent() { - if (content.visible) - { - content.close() - } - else - { - content.open() - } + content.visible = !content.visible } implicitHeight: 100 * screenScaleFactor @@ -117,17 +96,6 @@ Item } } - // A highlight that is shown when the content is expanded - Rectangle - { - id: expandedHighlight - width: parent.width - height: UM.Theme.getSize("thick_lining").height - color: UM.Theme.getColor("primary") - visible: contentType == ExpandableComponent.ContentType.Floating && expanded - anchors.bottom: parent.bottom - } - UM.RecolorImage { id: collapseButton @@ -139,9 +107,7 @@ Item } sourceSize.width: width sourceSize.height: height - source: contentType == ExpandableComponent.ContentType.Floating ? - (expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left")) : - UM.Theme.getIcon("pencil") + source: UM.Theme.getIcon("pencil") visible: source != "" width: UM.Theme.getSize("standard_arrow").width height: UM.Theme.getSize("standard_arrow").height @@ -155,8 +121,7 @@ Item onClicked: toggleContent() hoverEnabled: true onEntered: background.color = headerHoverColor - onExited: background.color = (contentType == ExpandableComponent.ContentType.Fixed && expanded) ? - headerActiveColor : headerBackgroundColor + onExited: background.color = expanded ? headerActiveColor : headerBackgroundColor } } @@ -174,9 +139,10 @@ Item z: background.z - 1 } - Popup + Control { id: content + visible: false // Ensure that the content is located directly below the headerItem y: background.height + base.shadowOffset + base.contentSpacingY @@ -185,7 +151,6 @@ Item // In case of right alignment, the 3x padding is due to left, right and padding between the button & text. x: contentAlignment == ExpandableComponent.ContentAlignment.AlignRight ? -width + collapseButton.width + headerItemLoader.width + 3 * background.padding : 0 padding: UM.Theme.getSize("default_margin").width - closePolicy: Popup.CloseOnPressOutsideParent background: Cura.RoundedRectangle { @@ -195,15 +160,16 @@ Item border.color: UM.Theme.getColor("lining") radius: UM.Theme.getSize("default_radius").width } - } - onContentItemChanged: - { - // Since we want the size of the content to be set by the size of the content, - // we need to do it like this. - content.width = contentItem.width + 2 * content.padding - content.height = contentItem.height + 2 * content.padding - content.contentItem = contentItem + contentItem: Item {} + + onContentItemChanged: + { + // Since we want the size of the content to be set by the size of the content, + // we need to do it like this. + content.width = contentItem.width + 2 * content.padding + content.height = contentItem.height + 2 * content.padding + } } // DO NOT MOVE UP IN THE CODE: This connection has to be here, after the definition of the content item. diff --git a/resources/qml/ExpandablePopup.qml b/resources/qml/ExpandablePopup.qml new file mode 100644 index 0000000000..da79d9b77b --- /dev/null +++ b/resources/qml/ExpandablePopup.qml @@ -0,0 +1,204 @@ +import QtQuick 2.7 +import QtQuick.Controls 2.3 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +import QtGraphicalEffects 1.0 // For the dropshadow + +// The expandable component has 2 major sub components: +// * The headerItem; Always visible and should hold some info about what happens if the component is expanded +// * The contentItem; The content that needs to be shown if the component is expanded. +Item +{ + id: base + + // Enumeration with the different possible alignments of the content with respect of the headerItem + enum ContentAlignment + { + AlignLeft, + AlignRight + } + + // The headerItem holds the QML item that is always displayed. + property alias headerItem: headerItemLoader.sourceComponent + + // The contentItem holds the QML item that is shown when the "open" button is pressed + property alias contentItem: content.contentItem + + property color contentBackgroundColor: UM.Theme.getColor("action_button") + + property color headerBackgroundColor: UM.Theme.getColor("action_button") + property color headerActiveColor: UM.Theme.getColor("secondary") + property color headerHoverColor: UM.Theme.getColor("action_button_hovered") + + // Defines the alignment of the content with respect of the headerItem, by default to the right + property int contentAlignment: ExpandablePopup.ContentAlignment.AlignRight + + // How much spacing is needed around the contentItem + property alias contentPadding: content.padding + + // How much padding is needed around the header & button + property alias headerPadding: background.padding + + // What icon should be displayed on the right. + property alias iconSource: collapseButton.source + + property alias iconColor: collapseButton.color + + // The icon size (it's always drawn as a square) + property alias iconSize: collapseButton.height + + // Is the "drawer" open? + readonly property alias expanded: content.visible + + property alias expandedHighlightColor: expandedHighlight.color + + // What should the radius of the header be. This is also influenced by the headerCornerSide + property alias headerRadius: background.radius + + // On what side should the header corners be shown? 1 is down, 2 is left, 3 is up and 4 is right. + property alias headerCornerSide: background.cornerSide + + // Change the contentItem close behaviour + property alias contentClosePolicy : content.closePolicy + + property alias headerShadowColor: shadow.color + + property alias enableHeaderShadow: shadow.visible + + property int shadowOffset: 2 + + function toggleContent() + { + if (content.visible) + { + content.close() + } + else + { + content.open() + } + } + + implicitHeight: 100 * screenScaleFactor + implicitWidth: 400 * screenScaleFactor + + RoundedRectangle + { + id: background + property real padding: UM.Theme.getSize("default_margin").width + + color: headerBackgroundColor + anchors.fill: parent + + Loader + { + id: headerItemLoader + anchors + { + left: parent.left + right: collapseButton.visible ? collapseButton.left : parent.right + top: parent.top + bottom: parent.bottom + margins: background.padding + } + } + + // A highlight that is shown when the content is expanded + Rectangle + { + id: expandedHighlight + width: parent.width + height: UM.Theme.getSize("thick_lining").height + color: UM.Theme.getColor("primary") + visible: expanded + anchors.bottom: parent.bottom + } + + UM.RecolorImage + { + id: collapseButton + anchors + { + right: parent.right + verticalCenter: parent.verticalCenter + margins: background.padding + } + sourceSize.width: width + sourceSize.height: height + source: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") + visible: source != "" + width: UM.Theme.getSize("standard_arrow").width + height: UM.Theme.getSize("standard_arrow").height + color: UM.Theme.getColor("text") + } + + MouseArea + { + id: mouseArea + anchors.fill: parent + onClicked: toggleContent() + hoverEnabled: true + onEntered: background.color = headerHoverColor + onExited: background.color = headerBackgroundColor + } + } + + DropShadow + { + id: shadow + // Don't blur the shadow + radius: 0 + anchors.fill: background + source: background + verticalOffset: base.shadowOffset + visible: true + color: UM.Theme.getColor("action_button_shadow") + // Should always be drawn behind the background. + z: background.z - 1 + } + + Popup + { + id: content + + // Ensure that the content is located directly below the headerItem + y: background.height + base.shadowOffset + + // Make the content aligned with the rest, using the property contentAlignment to decide whether is right or left. + // In case of right alignment, the 3x padding is due to left, right and padding between the button & text. + x: contentAlignment == ExpandablePopup.ContentAlignment.AlignRight ? -width + collapseButton.width + headerItemLoader.width + 3 * background.padding : 0 + padding: UM.Theme.getSize("default_margin").width + closePolicy: Popup.CloseOnPressOutsideParent + + background: Cura.RoundedRectangle + { + cornerSide: Cura.RoundedRectangle.Direction.Down + color: contentBackgroundColor + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") + radius: UM.Theme.getSize("default_radius").width + } + + contentItem: Item {} + + onContentItemChanged: + { + // Since we want the size of the content to be set by the size of the content, + // we need to do it like this. + content.width = contentItem.width + 2 * content.padding + content.height = contentItem.height + 2 * content.padding + } + } + + // DO NOT MOVE UP IN THE CODE: This connection has to be here, after the definition of the content item. + // Apparently the order in which these are handled matters and so the height is correctly updated if this is here. + Connections + { + // Since it could be that the content is dynamically populated, we should also take these changes into account. + target: content.contentItem + onWidthChanged: content.width = content.contentItem.width + 2 * content.padding + onHeightChanged: content.height = content.contentItem.height + 2 * content.padding + } +} diff --git a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml index ea82f4fe13..138a1d6669 100644 --- a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml +++ b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml @@ -12,7 +12,7 @@ import UM 1.2 as UM import Cura 1.0 as Cura -Cura.ExpandableComponent +Cura.ExpandablePopup { id: base diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector/PrintSetupSelector.qml index 0eea697950..19c8067683 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelector.qml @@ -14,11 +14,7 @@ Cura.ExpandableComponent property string enabledText: catalog.i18nc("@label:Should be short", "On") property string disabledText: catalog.i18nc("@label:Should be short", "Off") - contentType: Cura.ExpandableComponent.ContentType.Fixed contentPadding: UM.Theme.getSize("default_lining").width - contentSpacingY: UM.Theme.getSize("narrow_margin").width - - contentClosePolicy: Popup.CloseOnEscape UM.I18nCatalog { diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index 69a1ac899c..ef766b6030 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -7,7 +7,7 @@ import QtQuick.Controls 2.3 import UM 1.2 as UM import Cura 1.0 as Cura -Cura.ExpandableComponent +Cura.ExpandablePopup { id: machineSelector @@ -16,7 +16,7 @@ Cura.ExpandableComponent property var outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null contentPadding: UM.Theme.getSize("default_lining").width - contentAlignment: Cura.ExpandableComponent.ContentAlignment.AlignLeft + contentAlignment: Cura.ExpandablePopup.ContentAlignment.AlignLeft UM.I18nCatalog { diff --git a/resources/qml/ViewsSelector.qml b/resources/qml/ViewsSelector.qml index d469202606..18d1f66759 100644 --- a/resources/qml/ViewsSelector.qml +++ b/resources/qml/ViewsSelector.qml @@ -7,12 +7,12 @@ import QtQuick.Controls 2.3 import UM 1.2 as UM import Cura 1.0 as Cura -Cura.ExpandableComponent +Cura.ExpandablePopup { id: viewSelector contentPadding: UM.Theme.getSize("default_lining").width - contentAlignment: Cura.ExpandableComponent.ContentAlignment.AlignLeft + contentAlignment: Cura.ExpandablePopup.ContentAlignment.AlignLeft property var viewModel: UM.ViewModel { } @@ -72,13 +72,13 @@ Cura.ExpandableComponent contentItem: Column { id: viewSelectorPopup - width: viewSelector.width - 2 * viewSelector.popupPadding + width: viewSelector.width - 2 * viewSelector.contentPadding // For some reason the height/width of the column gets set to 0 if this is not set... Component.onCompleted: { height = implicitHeight - width = viewSelector.width - 2 * viewSelector.popupPadding + width = viewSelector.width - 2 * viewSelector.contentPadding } Repeater From 5d95d1143762a72e7cfe9c384c9d5752f6b888c2 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 5 Dec 2018 09:52:43 +0100 Subject: [PATCH 0654/1240] Use setCurrentIndex to switch tabs at activeExtruderChanged This fixes a mysterious segfault. We still don't know why the segfault occurred though. All we know is that QML logs something about a binding loop on currentIndex, and Qt logs something about removing range [-1 through 0] from VisualItemModel. When the tab bar is then made visible, Cura crashes. It is a nondeterministic crash. After this change, we are not seeing it any more (but with any nondeterministic bug, it's hard to verify that it was actually fixed). Contributes to issue CURA-5876. --- .../ConfigurationMenu/CustomConfiguration.qml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index 2667c837ba..18c2dabb0f 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -42,8 +42,6 @@ Item anchors.topMargin: UM.Theme.getSize("default_margin").height visible: extrudersModel.count > 1 - currentIndex: Math.max(Cura.ExtruderManager.activeExtruderIndex, 0) - Repeater { id: repeater @@ -68,6 +66,18 @@ Item } } + //When active extruder changes for some other reason, switch tabs. + //Don't directly link currentIndex to Cura.ExtruderManager.activeExtruderIndex! + //This causes a segfault in Qt 5.11. Something with VisualItemModel removing index -1. We have to use setCurrentIndex instead. + Connections + { + target: Cura.ExtruderManager + onActiveExtruderChanged: + { + tabBar.setCurrentIndex(Cura.ExtruderManager.activeExtruderIndex); + } + } + //When the model of the extruders is rebuilt, the list of extruders is briefly emptied and rebuilt. //This causes the currentIndex of the tab to be in an invalid position which resets it to 0. //Therefore we need to change it back to what it was: The active extruder index. From 01e443049fb51943369d3f1eca376bfb03f82b5a Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Wed, 5 Dec 2018 10:37:58 +0100 Subject: [PATCH 0655/1240] STAR-322: Creating a subclass for connect devices --- .../src/BaseCuraConnectDevice.py | 8 ++++++++ .../src/Cloud/CloudOutputDevice.py | 6 ++++-- .../src/ClusterUM3OutputDevice.py | 19 +++++++++---------- .../src/UM3OutputDevicePlugin.py | 2 +- 4 files changed, 22 insertions(+), 13 deletions(-) create mode 100644 plugins/UM3NetworkPrinting/src/BaseCuraConnectDevice.py diff --git a/plugins/UM3NetworkPrinting/src/BaseCuraConnectDevice.py b/plugins/UM3NetworkPrinting/src/BaseCuraConnectDevice.py new file mode 100644 index 0000000000..dc3d577cd5 --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/BaseCuraConnectDevice.py @@ -0,0 +1,8 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice + + +## this is the base class for the UM3 output devices (via connect or cloud) +class BaseCuraConnectDevice(NetworkedPrinterOutputDevice): + pass diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 27bf3a821e..4fa8d3b376 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -2,6 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. import io import os +from datetime import datetime, timedelta from time import time from typing import List, Optional, Dict, cast, Union, Set @@ -18,8 +19,9 @@ from UM.Version import Version from cura.CuraApplication import CuraApplication from cura.PrinterOutput.PrinterOutputController import PrinterOutputController from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel -from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState +from cura.PrinterOutput.NetworkedPrinterOutputDevice import AuthState from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel +from plugins.UM3NetworkPrinting.src.BaseCuraConnectDevice import BaseCuraConnectDevice from plugins.UM3NetworkPrinting.src.Cloud.CloudApiClient import CloudApiClient from plugins.UM3NetworkPrinting.src.UM3PrintJobOutputModel import UM3PrintJobOutputModel from .Models import ( @@ -61,7 +63,7 @@ class T: # Note that this device represents a single remote cluster, not a list of multiple clusters. # # TODO: figure our how the QML interface for the cluster networking should operate with this limited functionality. -class CloudOutputDevice(NetworkedPrinterOutputDevice): +class CloudOutputDevice(BaseCuraConnectDevice): # The interval with which the remote clusters are checked CHECK_CLUSTER_INTERVAL = 2.0 # seconds diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index 8881584416..7015f71be4 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -1,7 +1,13 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import Any, cast, Optional, Set, Tuple, Union +from typing import Any, cast, Tuple, Union, Optional, Dict, List +from time import time +from datetime import datetime + +import io # To create the correct buffers for sending data to the printer. +import json +import os from UM.FileHandler.FileHandler import FileHandler from UM.FileHandler.FileWriter import FileWriter # To choose based on the output file mode (text vs. binary). @@ -22,6 +28,7 @@ from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationM from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel +from plugins.UM3NetworkPrinting.src.BaseCuraConnectDevice import BaseCuraConnectDevice from .ClusterUM3PrinterOutputController import ClusterUM3PrinterOutputController from .SendMaterialJob import SendMaterialJob @@ -32,18 +39,10 @@ from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply from PyQt5.QtGui import QDesktopServices, QImage from PyQt5.QtCore import pyqtSlot, QUrl, pyqtSignal, pyqtProperty, QObject -from time import time -from datetime import datetime -from typing import Optional, Dict, List - -import io # To create the correct buffers for sending data to the printer. -import json -import os - i18n_catalog = i18nCatalog("cura") -class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): +class ClusterUM3OutputDevice(BaseCuraConnectDevice): printJobsChanged = pyqtSignal() activePrinterChanged = pyqtSignal() activeCameraUrlChanged = pyqtSignal() diff --git a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py index 91c9cb32b9..6a80ae046e 100644 --- a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py @@ -363,4 +363,4 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): Logger.log("d", "Bonjour service removed: %s" % name) self.removeDeviceSignal.emit(str(name)) - return True \ No newline at end of file + return True From 700ae4bebb2218610fbf0095f57e7aabb81e52ba Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 5 Dec 2018 10:48:06 +0100 Subject: [PATCH 0656/1240] Removed super spammy logging CURA-6006 --- plugins/Toolbox/src/Toolbox.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 562a964f01..2b78581deb 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -491,11 +491,8 @@ class Toolbox(QObject, Extension): def canUpdate(self, package_id: str) -> bool: local_package = self._package_manager.getInstalledPackageInfo(package_id) if local_package is None: - Logger.log("i", "Could not find package [%s] as installed in the package manager, fall back to check the old plugins", - package_id) local_package = self.getOldPluginPackageMetadata(package_id) if local_package is None: - Logger.log("i", "Could not find package [%s] in the old plugins", package_id) return False remote_package = self.getRemotePackage(package_id) From 978a01e4c89873b42909991ae5ecc49988beab33 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 5 Dec 2018 10:51:05 +0100 Subject: [PATCH 0657/1240] Fix typing & codestyle CURA-6006 --- plugins/Toolbox/src/Toolbox.py | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 2b78581deb..1fe7c961ba 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -31,8 +31,8 @@ i18n_catalog = i18nCatalog("cura") ## The Toolbox class is responsible of communicating with the server through the API class Toolbox(QObject, Extension): - DEFAULT_CLOUD_API_ROOT = "https://api.ultimaker.com" #type: str - DEFAULT_CLOUD_API_VERSION = 1 #type: int + DEFAULT_CLOUD_API_ROOT = "https://api.ultimaker.com" # type: str + DEFAULT_CLOUD_API_VERSION = 1 # type: int def __init__(self, application: CuraApplication) -> None: super().__init__() @@ -192,9 +192,9 @@ class Toolbox(QObject, Extension): return self.DEFAULT_CLOUD_API_ROOT if not hasattr(cura.CuraVersion, "CuraCloudAPIRoot"): # type: ignore return self.DEFAULT_CLOUD_API_ROOT - if not cura.CuraVersion.CuraCloudAPIRoot: # type: ignore + if not cura.CuraVersion.CuraCloudAPIRoot: # type: ignore return self.DEFAULT_CLOUD_API_ROOT - return cura.CuraVersion.CuraCloudAPIRoot # type: ignore + return cura.CuraVersion.CuraCloudAPIRoot # type: ignore # Get the cloud API version from CuraVersion def _getCloudAPIVersion(self) -> int: @@ -202,9 +202,9 @@ class Toolbox(QObject, Extension): return self.DEFAULT_CLOUD_API_VERSION if not hasattr(cura.CuraVersion, "CuraCloudAPIVersion"): # type: ignore return self.DEFAULT_CLOUD_API_VERSION - if not cura.CuraVersion.CuraCloudAPIVersion: # type: ignore + if not cura.CuraVersion.CuraCloudAPIVersion: # type: ignore return self.DEFAULT_CLOUD_API_VERSION - return cura.CuraVersion.CuraCloudAPIVersion # type: ignore + return cura.CuraVersion.CuraCloudAPIVersion # type: ignore # Get the packages version depending on Cura version settings. def _getSDKVersion(self) -> Union[int, str]: @@ -231,12 +231,6 @@ class Toolbox(QObject, Extension): # Make remote requests: self._makeRequestByType("packages") self._makeRequestByType("authors") - # TODO: Uncomment in the future when the tag-filtered api calls work in the cloud server - # self._makeRequestByType("plugins_showcase") - # self._makeRequestByType("plugins_available") - # self._makeRequestByType("materials_showcase") - # self._makeRequestByType("materials_available") - # self._makeRequestByType("materials_generic") # Gather installed packages: self._updateInstalledModels() @@ -281,7 +275,7 @@ class Toolbox(QObject, Extension): "description": plugin_data["plugin"]["description"] } return formatted - except: + except KeyError: Logger.log("w", "Unable to convert plugin meta data %s", str(plugin_data)) return None From d9135ac72fa9aa4aa155179fb44e57cda2d1ad96 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 5 Dec 2018 11:06:14 +0100 Subject: [PATCH 0658/1240] Fix more codestyle issues CURA-6006 --- plugins/Toolbox/src/Toolbox.py | 62 +++++++++++++++------------------- 1 file changed, 27 insertions(+), 35 deletions(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 1fe7c961ba..b6cfafe40c 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -559,34 +559,30 @@ class Toolbox(QObject, Extension): # Check for plugins that were installed with the old plugin browser def isOldPlugin(self, plugin_id: str) -> bool: - if plugin_id in self._old_plugin_ids: - return True - return False + return plugin_id in self._old_plugin_ids def getOldPluginPackageMetadata(self, plugin_id: str) -> Optional[Dict[str, Any]]: return self._old_plugin_metadata.get(plugin_id) - def loadingComplete(self) -> bool: + def isLoadingComplete(self) -> bool: populated = 0 - for list in self._metadata.items(): - if len(list) > 0: + for metadata_list in self._metadata.items(): + if metadata_list: populated += 1 - if populated == len(self._metadata.items()): - return True - return False + return populated == len(self._metadata.items()) # Make API Calls # -------------------------------------------------------------------------- - def _makeRequestByType(self, type: str) -> None: - Logger.log("i", "Marketplace: Requesting %s metadata from server.", type) - request = QNetworkRequest(self._request_urls[type]) + def _makeRequestByType(self, request_type: str) -> None: + Logger.log("i", "Requesting %s metadata from server.", request_type) + request = QNetworkRequest(self._request_urls[request_type]) request.setRawHeader(*self._request_header) if self._network_manager: self._network_manager.get(request) @pyqtSlot(str) def startDownload(self, url: str) -> None: - Logger.log("i", "Marketplace: Attempting to download & install package from %s.", url) + Logger.log("i", "Attempting to download & install package from %s.", url) url = QUrl(url) self._download_request = QNetworkRequest(url) if hasattr(QNetworkRequest, "FollowRedirectsAttribute"): @@ -603,7 +599,7 @@ class Toolbox(QObject, Extension): @pyqtSlot() def cancelDownload(self) -> None: - Logger.log("i", "Marketplace: User cancelled the download of a package.") + Logger.log("i", "User cancelled the download of a package.") self.resetDownload() def resetDownload(self) -> None: @@ -647,10 +643,10 @@ class Toolbox(QObject, Extension): ] if reply.operation() == QNetworkAccessManager.GetOperation: - for type, url in self._request_urls.items(): + for response_type, url in self._request_urls.items(): # HACK: Do nothing because we'll handle these from the "packages" call - if type in do_not_handle: + if response_type in do_not_handle: continue if reply.url() == url: @@ -665,38 +661,35 @@ class Toolbox(QObject, Extension): return # Create model and apply metadata: - if not self._models[type]: - Logger.log("e", "Could not find the %s model.", type) + if not self._models[response_type]: + Logger.log("e", "Could not find the %s model.", response_type) break - self._metadata[type] = json_data["data"] - self._models[type].setMetadata(self._metadata[type]) + self._metadata[response_type] = json_data["data"] + self._models[response_type].setMetadata(self._metadata[response_type]) # Do some auto filtering # TODO: Make multiple API calls in the future to handle this - if type is "packages": - self._models[type].setFilter({"type": "plugin"}) + if response_type is "packages": + self._models[response_type].setFilter({"type": "plugin"}) self.buildMaterialsModels() self.buildPluginsModels() - if type is "authors": - self._models[type].setFilter({"package_types": "material"}) - if type is "materials_generic": - self._models[type].setFilter({"tags": "generic"}) + if response_type is "authors": + self._models[response_type].setFilter({"package_types": "material"}) + if response_type is "materials_generic": + self._models[response_type].setFilter({"tags": "generic"}) self.metadataChanged.emit() - if self.loadingComplete() is True: + if self.isLoadingComplete(): self.setViewPage("overview") - return except json.decoder.JSONDecodeError: - Logger.log("w", "Marketplace: Received invalid JSON for %s.", type) + Logger.log("w", "Received invalid JSON for %s.", response_type) break else: self.setViewPage("errored") self.resetDownload() - return - else: # Ignore any operation that is not a get operation pass @@ -717,10 +710,10 @@ class Toolbox(QObject, Extension): self._onDownloadComplete(file_path) def _onDownloadComplete(self, file_path: str) -> None: - Logger.log("i", "Marketplace: Download complete.") + Logger.log("i", "Download complete.") package_info = self._package_manager.getPackageInfo(file_path) if not package_info: - Logger.log("w", "Marketplace: Package file [%s] was not a valid CuraPackage.", file_path) + Logger.log("w", "Package file [%s] was not a valid CuraPackage.", file_path) return license_content = self._package_manager.getPackageLicense(file_path) @@ -729,7 +722,6 @@ class Toolbox(QObject, Extension): return self.install(file_path) - return # Getter & Setters for Properties: # -------------------------------------------------------------------------- @@ -847,7 +839,7 @@ class Toolbox(QObject, Extension): self._metadata["materials_available"] = [] self._metadata["materials_generic"] = [] - processed_authors = [] # type: List[str] + processed_authors = [] # type: List[str] for item in self._metadata["packages"]: if item["package_type"] == "material": From d99e2d15339b8638b6161fc4659e77f9eb618c19 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Wed, 5 Dec 2018 11:21:17 +0100 Subject: [PATCH 0659/1240] STAR-322: Extracting file handler methods --- .../src/BaseCuraConnectDevice.py | 68 ++++++++++++++++++- .../src/Cloud/CloudOutputDevice.py | 68 +++---------------- .../src/ClusterUM3OutputDevice.py | 53 +++------------ 3 files changed, 83 insertions(+), 106 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/BaseCuraConnectDevice.py b/plugins/UM3NetworkPrinting/src/BaseCuraConnectDevice.py index dc3d577cd5..0abf5955cf 100644 --- a/plugins/UM3NetworkPrinting/src/BaseCuraConnectDevice.py +++ b/plugins/UM3NetworkPrinting/src/BaseCuraConnectDevice.py @@ -1,8 +1,72 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from typing import Optional, Dict, Union + +from UM.FileHandler.FileHandler import FileHandler +from UM.FileHandler.FileWriter import FileWriter +from UM.Logger import Logger +from UM.OutputDevice import OutputDeviceError # To show that something went wrong when writing. +from UM.Version import Version # To check against firmware versions for support. +from UM.i18n import i18nCatalog +from cura.CuraApplication import CuraApplication from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice -## this is the base class for the UM3 output devices (via connect or cloud) +## Class that contains all the translations for this module. +class T: + # The translation catalog for this device. + + _I18N_CATALOG = i18nCatalog("cura") + NO_FORMATS_AVAILABLE = _I18N_CATALOG.i18nc("@info:status", "There are no file formats available to write with!") + + +## This is the base class for the UM3 output devices (via connect or cloud) class BaseCuraConnectDevice(NetworkedPrinterOutputDevice): - pass + + ## Gets the default file handler + @property + def defaultFileHandler(self) -> FileHandler: + return CuraApplication.getInstance().getMeshFileHandler() + + ## Chooses the preferred file format for the given file handler. + # \param file_handler: The file handler. + # \return A dict with the file format details, with format: + # {id: str, extension: str, description: str, mime_type: str, mode: int, hide_in_file_dialog: bool} + def _getPreferredFormat(self, file_handler: Optional[FileHandler]) -> Optional[Dict[str, Union[str, int, bool]]]: + # Formats supported by this application (file types that we can actually write). + application = CuraApplication.getInstance() + + file_handler = file_handler or self.defaultFileHandler + file_formats = file_handler.getSupportedFileTypesWrite() + + global_stack = application.getGlobalContainerStack() + # Create a list from the supported file formats string. + if not global_stack: + Logger.log("e", "Missing global stack!") + return + + machine_file_formats = global_stack.getMetaDataEntry("file_formats").split(";") + machine_file_formats = [file_type.strip() for file_type in machine_file_formats] + # Exception for UM3 firmware version >=4.4: UFP is now supported and should be the preferred file format. + if "application/x-ufp" not in machine_file_formats and Version(self.firmwareVersion) >= Version("4.4"): + machine_file_formats = ["application/x-ufp"] + machine_file_formats + + # Take the intersection between file_formats and machine_file_formats. + format_by_mimetype = {f["mime_type"]: f for f in file_formats} + + # Keep them ordered according to the preference in machine_file_formats. + file_formats = [format_by_mimetype[mimetype] for mimetype in machine_file_formats] + + if len(file_formats) == 0: + Logger.log("e", "There are no file formats available to write with!") + raise OutputDeviceError.WriteRequestFailedError(T.NO_FORMATS_AVAILABLE) + return file_formats[0] + + ## Gets the file writer for the given file handler and mime type. + # \param file_handler: The file handler. + # \param mime_type: The mine type. + # \return A file writer. + def _getWriter(self, file_handler: Optional[FileHandler], mime_type: str) -> Optional[FileWriter]: + # Just take the first file format available. + file_handler = file_handler or self.defaultFileHandler + return file_handler.getWriterByMimeType(mime_type) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 4fa8d3b376..0c6d11c708 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -2,9 +2,8 @@ # Cura is released under the terms of the LGPLv3 or higher. import io import os -from datetime import datetime, timedelta from time import time -from typing import List, Optional, Dict, cast, Union, Set +from typing import List, Optional, Dict, Union, Set from PyQt5.QtCore import QObject, pyqtSignal, QUrl, pyqtProperty, pyqtSlot @@ -13,9 +12,7 @@ from UM.FileHandler.FileWriter import FileWriter from UM.FileHandler.FileHandler import FileHandler from UM.Logger import Logger from UM.Message import Message -from UM.OutputDevice import OutputDeviceError from UM.Scene.SceneNode import SceneNode -from UM.Version import Version from cura.CuraApplication import CuraApplication from cura.PrinterOutput.PrinterOutputController import PrinterOutputController from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel @@ -45,7 +42,6 @@ class T: "the previous print job.") COULD_NOT_EXPORT = _I18N_CATALOG.i18nc("@info:status", "Could not export print job.") - WRITE_FAILED = _I18N_CATALOG.i18nc("@info:status", "There are no file formats available to write with!") SENDING_DATA_TEXT = _I18N_CATALOG.i18nc("@info:status", "Sending data to remote cluster") SENDING_DATA_TITLE = _I18N_CATALOG.i18nc("@info:status", "Sending data to remote cluster") @@ -69,7 +65,7 @@ class CloudOutputDevice(BaseCuraConnectDevice): CHECK_CLUSTER_INTERVAL = 2.0 # seconds # Signal triggered when the printers in the remote cluster were changed. - printersChanged = pyqtSignal() + clusterPrintersChanged = pyqtSignal() # Signal triggered when the print jobs in the queue were changed. printJobsChanged = pyqtSignal() @@ -122,8 +118,8 @@ class CloudOutputDevice(BaseCuraConnectDevice): self._sending_job = True self.writeStarted.emit(self) - file_format = self._determineFileFormat(file_handler) - writer = self._determineWriter(file_handler, file_format) + file_format = self._getPreferredFormat(file_handler) + writer = self._getWriter(file_handler, file_format["mime_type"]) if not writer: Logger.log("e", "Missing file or mesh writer!") return self._onUploadError(T.COULD_NOT_EXPORT) @@ -134,56 +130,8 @@ class CloudOutputDevice(BaseCuraConnectDevice): # TODO: Remove extension from the file name, since we are using content types now self._sendPrintJob(file_name + "." + file_format["extension"], file_format["mime_type"], stream) - # TODO: This is yanked right out of ClusterUM3OutputDevice, great candidate for a utility or base class - def _determineFileFormat(self, file_handler) -> Optional[Dict[str, Union[str, int]]]: - # Formats supported by this application (file types that we can actually write). - if file_handler: - file_formats = file_handler.getSupportedFileTypesWrite() - else: - file_formats = CuraApplication.getInstance().getMeshFileHandler().getSupportedFileTypesWrite() - - global_stack = CuraApplication.getInstance().getGlobalContainerStack() - # Create a list from the supported file formats string. - if not global_stack: - Logger.log("e", "Missing global stack!") - return - - machine_file_formats = global_stack.getMetaDataEntry("file_formats").split(";") - machine_file_formats = [file_type.strip() for file_type in machine_file_formats] - # Exception for UM3 firmware version >=4.4: UFP is now supported and should be the preferred file format. - if "application/x-ufp" not in machine_file_formats and Version(self.firmwareVersion) >= Version("4.4"): - machine_file_formats = ["application/x-ufp"] + machine_file_formats - - # Take the intersection between file_formats and machine_file_formats. - format_by_mimetype = {f["mime_type"]: f for f in file_formats} - - # Keep them ordered according to the preference in machine_file_formats. - file_formats = [format_by_mimetype[mimetype] for mimetype in machine_file_formats] - - if len(file_formats) == 0: - Logger.log("e", "There are no file formats available to write with!") - raise OutputDeviceError.WriteRequestFailedError(T.WRITE_FAILED) - return file_formats[0] - - # TODO: This is yanked right out of ClusterUM3OutputDevice, great candidate for a utility or base class - @staticmethod - def _determineWriter(file_handler, file_format) -> Optional[FileWriter]: - # Just take the first file format available. - if file_handler is not None: - writer = file_handler.getWriterByMimeType(cast(str, file_format["mime_type"])) - else: - writer = CuraApplication.getInstance().getMeshFileHandler().getWriterByMimeType( - cast(str, file_format["mime_type"]) - ) - - if not writer: - Logger.log("e", "Unexpected error when trying to get the FileWriter") - return - - return writer - ## Get remote printers. - @pyqtProperty("QVariantList", notify = printersChanged) + @pyqtProperty("QVariantList", notify = clusterPrintersChanged) def printers(self): return self._printers @@ -244,7 +192,7 @@ class CloudOutputDevice(BaseCuraConnectDevice): for printer_guid in updated_printer_ids: self._updatePrinter(current_printers[printer_guid], remote_printers[printer_guid]) - self.printersChanged.emit() + self.clusterPrintersChanged.emit() def _addPrinter(self, printer: CloudClusterPrinter) -> None: model = PrinterOutputModel( @@ -409,7 +357,7 @@ class CloudOutputDevice(BaseCuraConnectDevice): ## TODO: The following methods are required by the monitor page QML, but are not actually available using cloud. # TODO: We fake the methods here to not break the monitor page. - @pyqtProperty(QObject, notify = printersChanged) + @pyqtProperty(QObject, notify = clusterPrintersChanged) def activePrinter(self) -> Optional[PrinterOutputModel]: if not self._printers: return None @@ -419,7 +367,7 @@ class CloudOutputDevice(BaseCuraConnectDevice): def setActivePrinter(self, printer: Optional[PrinterOutputModel]) -> None: pass - @pyqtProperty(QUrl, notify = printersChanged) + @pyqtProperty(QUrl, notify = clusterPrintersChanged) def activeCameraUrl(self) -> "QUrl": return QUrl() diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index a1530c128d..c77c4b93c2 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -18,14 +18,12 @@ from UM.i18n import i18nCatalog from UM.Message import Message from UM.Qt.Duration import Duration, DurationFormat -from UM.OutputDevice import OutputDeviceError # To show that something went wrong when writing. from UM.Scene.SceneNode import SceneNode # For typing. -from UM.Version import Version # To check against firmware versions for support. from cura.CuraApplication import CuraApplication from cura.PrinterOutput.ConfigurationModel import ConfigurationModel from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationModel -from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState +from cura.PrinterOutput.NetworkedPrinterOutputDevice import AuthState from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel from plugins.UM3NetworkPrinting.src.BaseCuraConnectDevice import BaseCuraConnectDevice @@ -50,7 +48,7 @@ class ClusterUM3OutputDevice(BaseCuraConnectDevice): # This is a bit of a hack, as the notify can only use signals that are defined by the class that they are in. # Inheritance doesn't seem to work. Tying them together does work, but i'm open for better suggestions. - clusterPrintersChanged = pyqtSignal() + _clusterPrintersChanged = pyqtSignal() def __init__(self, device_id, address, properties, parent = None) -> None: super().__init__(device_id = device_id, address = address, properties=properties, parent = parent) @@ -66,7 +64,7 @@ class ClusterUM3OutputDevice(BaseCuraConnectDevice): self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../resources/qml/ClusterMonitorItem.qml") # See comments about this hack with the clusterPrintersChanged signal - self.printersChanged.connect(self.clusterPrintersChanged) + self.printersChanged.connect(self._clusterPrintersChanged) self._accepts_commands = True # type: bool @@ -99,47 +97,14 @@ class ClusterUM3OutputDevice(BaseCuraConnectDevice): self._active_camera_url = QUrl() # type: QUrl - def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False, file_handler: Optional[FileHandler] = None, **kwargs: str) -> None: + def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False, + file_handler: Optional[FileHandler] = None, **kwargs: str) -> None: self.writeStarted.emit(self) self.sendMaterialProfiles() - # Formats supported by this application (file types that we can actually write). - if file_handler: - file_formats = file_handler.getSupportedFileTypesWrite() - else: - file_formats = CuraApplication.getInstance().getMeshFileHandler().getSupportedFileTypesWrite() - - global_stack = CuraApplication.getInstance().getGlobalContainerStack() - # Create a list from the supported file formats string. - if not global_stack: - Logger.log("e", "Missing global stack!") - return - - machine_file_formats = global_stack.getMetaDataEntry("file_formats").split(";") - machine_file_formats = [file_type.strip() for file_type in machine_file_formats] - # Exception for UM3 firmware version >=4.4: UFP is now supported and should be the preferred file format. - if "application/x-ufp" not in machine_file_formats and Version(self.firmwareVersion) >= Version("4.4"): - machine_file_formats = ["application/x-ufp"] + machine_file_formats - - # Take the intersection between file_formats and machine_file_formats. - format_by_mimetype = {format["mime_type"]: format for format in file_formats} - file_formats = [format_by_mimetype[mimetype] for mimetype in machine_file_formats] #Keep them ordered according to the preference in machine_file_formats. - - if len(file_formats) == 0: - Logger.log("e", "There are no file formats available to write with!") - raise OutputDeviceError.WriteRequestFailedError(i18n_catalog.i18nc("@info:status", "There are no file formats available to write with!")) - preferred_format = file_formats[0] - - # Just take the first file format available. - if file_handler is not None: - writer = file_handler.getWriterByMimeType(cast(str, preferred_format["mime_type"])) - else: - writer = CuraApplication.getInstance().getMeshFileHandler().getWriterByMimeType(cast(str, preferred_format["mime_type"])) - - if not writer: - Logger.log("e", "Unexpected error when trying to get the FileWriter") - return + preferred_format = self._getPreferredFormat(file_handler) + writer = self._getWriter(file_handler, preferred_format["mime_type"]) # This function pauses with the yield, waiting on instructions on which printer it needs to print with. if not writer: @@ -355,7 +320,7 @@ class ClusterUM3OutputDevice(BaseCuraConnectDevice): def activePrintJobs(self) -> List[UM3PrintJobOutputModel]: 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) + @pyqtProperty("QVariantList", notify = _clusterPrintersChanged) def connectedPrintersTypeCount(self) -> List[Dict[str, str]]: printer_count = {} # type: Dict[str, int] for printer in self._printers: @@ -368,7 +333,7 @@ class ClusterUM3OutputDevice(BaseCuraConnectDevice): result.append({"machine_type": machine_type, "count": str(printer_count[machine_type])}) return result - @pyqtProperty("QVariantList", notify=clusterPrintersChanged) + @pyqtProperty("QVariantList", notify=_clusterPrintersChanged) def printers(self): return self._printers From abcc621cc62120a0f9f1e5f32599f8356e607dc2 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 5 Dec 2018 11:29:44 +0100 Subject: [PATCH 0660/1240] Added missing typing --- plugins/Toolbox/src/AuthorsModel.py | 43 ++++++++++++++-------------- plugins/Toolbox/src/PackagesModel.py | 2 +- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/plugins/Toolbox/src/AuthorsModel.py b/plugins/Toolbox/src/AuthorsModel.py index bea3893504..6f4b5bd280 100644 --- a/plugins/Toolbox/src/AuthorsModel.py +++ b/plugins/Toolbox/src/AuthorsModel.py @@ -2,18 +2,19 @@ # Cura is released under the terms of the LGPLv3 or higher. import re -from typing import Dict +from typing import Dict, List, Optional, Union from PyQt5.QtCore import Qt, pyqtProperty, pyqtSignal from UM.Qt.ListModel import ListModel + ## Model that holds cura packages. By setting the filter property the instances held by this model can be changed. class AuthorsModel(ListModel): - def __init__(self, parent = None): + def __init__(self, parent = None) -> None: super().__init__(parent) - self._metadata = None + self._metadata = None # type: Optional[List[Dict[str, Union[str, List[str], int]]]] self.addRoleName(Qt.UserRole + 1, "id") self.addRoleName(Qt.UserRole + 2, "name") @@ -25,39 +26,39 @@ class AuthorsModel(ListModel): self.addRoleName(Qt.UserRole + 8, "description") # List of filters for queries. The result is the union of the each list of results. - self._filter = {} # type: Dict[str,str] + self._filter = {} # type: Dict[str, str] - def setMetadata(self, data): + def setMetadata(self, data: List[Dict[str, Union[str, List[str], int]]]): self._metadata = data self._update() - def _update(self): - items = [] + def _update(self) -> None: + items = [] # type: List[Dict[str, Union[str, List[str], int, None]]] if not self._metadata: - self.setItems([]) + self.setItems(items) return for author in self._metadata: items.append({ - "id": author["author_id"], - "name": author["display_name"], - "email": author["email"] if "email" in author else None, - "website": author["website"], - "package_count": author["package_count"] if "package_count" in author else 0, - "package_types": author["package_types"] if "package_types" in author else [], - "icon_url": author["icon_url"] if "icon_url" in author else None, - "description": "Material and quality profiles from {author_name}".format(author_name = author["display_name"]) + "id": author.get("author_id"), + "name": author.get("display_name"), + "email": author.get("email"), + "website": author.get("website"), + "package_count": author.get("package_count", 0), + "package_types": author.get("package_types", []), + "icon_url": author.get("icon_url"), + "description": "Material and quality profiles from {author_name}".format(author_name = author.get("display_name", "")) }) # Filter on all the key-word arguments. for key, value in self._filter.items(): if key is "package_types": - key_filter = lambda item, value = value: value in item["package_types"] + key_filter = lambda item, value = value: value in item["package_types"] # type: ignore elif "*" in value: - key_filter = lambda item, key = key, value = value: self._matchRegExp(item, key, value) + key_filter = lambda item, key = key, value = value: self._matchRegExp(item, key, value) # type: ignore else: - key_filter = lambda item, key = key, value = value: self._matchString(item, key, value) - items = filter(key_filter, items) + key_filter = lambda item, key = key, value = value: self._matchString(item, key, value) # type: ignore + items = filter(key_filter, items) # type: ignore # Execute all filters. filtered_items = list(items) @@ -72,7 +73,7 @@ class AuthorsModel(ListModel): self._filter = filter_dict self._update() - @pyqtProperty("QVariantMap", fset = setFilter, constant = True) + @pyqtProperty("QStringMap", fset = setFilter, constant = True) def filter(self) -> Dict[str, str]: return self._filter diff --git a/plugins/Toolbox/src/PackagesModel.py b/plugins/Toolbox/src/PackagesModel.py index a31facf75a..2849a29c99 100644 --- a/plugins/Toolbox/src/PackagesModel.py +++ b/plugins/Toolbox/src/PackagesModel.py @@ -33,7 +33,7 @@ class PackagesModel(ListModel): self.addRoleName(Qt.UserRole + 12, "last_updated") self.addRoleName(Qt.UserRole + 13, "is_bundled") self.addRoleName(Qt.UserRole + 14, "is_active") - self.addRoleName(Qt.UserRole + 15, "is_installed") # Scheduled pkgs are included in the model but should not be marked as actually installed + self.addRoleName(Qt.UserRole + 15, "is_installed") # Scheduled pkgs are included in the model but should not be marked as actually installed self.addRoleName(Qt.UserRole + 16, "has_configs") self.addRoleName(Qt.UserRole + 17, "supported_configs") self.addRoleName(Qt.UserRole + 18, "download_count") From 163226f9400651b45a088af884a0edad383dc8e7 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Wed, 5 Dec 2018 12:02:04 +0100 Subject: [PATCH 0661/1240] STAR-322: Using composition rather than inheritance --- cura/PrinterOutputDevice.py | 9 ++- .../src/Cloud/CloudOutputDevice.py | 35 ++++----- .../src/ClusterUM3OutputDevice.py | 45 ++++++----- ...aConnectDevice.py => MeshFormatHandler.py} | 74 ++++++++++++++----- 4 files changed, 97 insertions(+), 66 deletions(-) rename plugins/UM3NetworkPrinting/src/{BaseCuraConnectDevice.py => MeshFormatHandler.py} (50%) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 99c48189cc..8c00ea1aa6 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -131,7 +131,8 @@ class PrinterOutputDevice(QObject, OutputDevice): return None - def requestWrite(self, nodes: List["SceneNode"], file_name: Optional[str] = None, limit_mimetypes: bool = False, file_handler: Optional["FileHandler"] = None, **kwargs: str) -> None: + def requestWrite(self, nodes: List["SceneNode"], file_name: Optional[str] = None, limit_mimetypes: bool = False, + file_handler: Optional["FileHandler"] = None, **kwargs: str) -> None: raise NotImplementedError("requestWrite needs to be implemented") @pyqtProperty(QObject, notify = printersChanged) @@ -207,8 +208,10 @@ class PrinterOutputDevice(QObject, OutputDevice): return self._unique_configurations def _updateUniqueConfigurations(self) -> None: - self._unique_configurations = list(set([printer.printerConfiguration for printer in self._printers if printer.printerConfiguration is not None])) - self._unique_configurations.sort(key = lambda k: k.printerType) + self._unique_configurations = sorted( + {printer.printerConfiguration for printer in self._printers if printer.printerConfiguration is not None}, + key=lambda config: config.printerType, + ) self.uniqueConfigurationsChanged.emit() # Returns the unique configurations of the printers within this output device diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 0c6d11c708..91f5721aa6 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -1,14 +1,12 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -import io import os from time import time -from typing import List, Optional, Dict, Union, Set +from typing import List, Optional, Dict, Set from PyQt5.QtCore import QObject, pyqtSignal, QUrl, pyqtProperty, pyqtSlot from UM import i18nCatalog -from UM.FileHandler.FileWriter import FileWriter from UM.FileHandler.FileHandler import FileHandler from UM.Logger import Logger from UM.Message import Message @@ -16,10 +14,10 @@ from UM.Scene.SceneNode import SceneNode from cura.CuraApplication import CuraApplication from cura.PrinterOutput.PrinterOutputController import PrinterOutputController from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel -from cura.PrinterOutput.NetworkedPrinterOutputDevice import AuthState +from cura.PrinterOutput.NetworkedPrinterOutputDevice import AuthState, NetworkedPrinterOutputDevice from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel -from plugins.UM3NetworkPrinting.src.BaseCuraConnectDevice import BaseCuraConnectDevice from plugins.UM3NetworkPrinting.src.Cloud.CloudApiClient import CloudApiClient +from plugins.UM3NetworkPrinting.src.MeshFormatHandler import MeshFormatHandler from plugins.UM3NetworkPrinting.src.UM3PrintJobOutputModel import UM3PrintJobOutputModel from .Models import ( CloudClusterPrinter, CloudClusterPrintJob, CloudJobUploadRequest, CloudJobResponse, CloudClusterStatus, @@ -59,7 +57,7 @@ class T: # Note that this device represents a single remote cluster, not a list of multiple clusters. # # TODO: figure our how the QML interface for the cluster networking should operate with this limited functionality. -class CloudOutputDevice(BaseCuraConnectDevice): +class CloudOutputDevice(NetworkedPrinterOutputDevice): # The interval with which the remote clusters are checked CHECK_CLUSTER_INTERVAL = 2.0 # seconds @@ -118,17 +116,20 @@ class CloudOutputDevice(BaseCuraConnectDevice): self._sending_job = True self.writeStarted.emit(self) - file_format = self._getPreferredFormat(file_handler) - writer = self._getWriter(file_handler, file_format["mime_type"]) - if not writer: + mesh_format = MeshFormatHandler(file_handler, self.firmwareVersion) + if not mesh_format.is_valid: Logger.log("e", "Missing file or mesh writer!") return self._onUploadError(T.COULD_NOT_EXPORT) - stream = io.StringIO() if file_format["mode"] == FileWriter.OutputMode.TextMode else io.BytesIO() - writer.write(stream, nodes) + mesh_bytes = mesh_format.getBytes(nodes) # TODO: Remove extension from the file name, since we are using content types now - self._sendPrintJob(file_name + "." + file_format["extension"], file_format["mime_type"], stream) + request = CloudJobUploadRequest( + job_name = file_name + "." + mesh_format.file_extension, + file_size = len(mesh_bytes), + content_type = mesh_format.mime_type, + ) + self._api.requestUpload(request, lambda response: self._onPrintJobCreated(mesh_bytes, response)) ## Get remote printers. @pyqtProperty("QVariantList", notify = clusterPrintersChanged) @@ -292,16 +293,6 @@ class CloudOutputDevice(BaseCuraConnectDevice): model.updateOwner(job.owner) model.updateState(job.status) - def _sendPrintJob(self, file_name: str, content_type: str, stream: Union[io.StringIO, io.BytesIO]) -> None: - mesh = stream.getvalue() - - request = CloudJobUploadRequest() - request.job_name = file_name - request.file_size = len(mesh) - request.content_type = content_type - - self._api.requestUpload(request, lambda response: self._onPrintJobCreated(mesh, response)) - def _onPrintJobCreated(self, mesh: bytes, job_response: CloudJobResponse) -> None: self._api.uploadMesh(job_response, mesh, self._onPrintJobUploaded, self._updateUploadProgress, lambda _: self._onUploadError(T.UPLOAD_ERROR)) diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index c77c4b93c2..64ac613723 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -10,7 +10,6 @@ import json import os from UM.FileHandler.FileHandler import FileHandler -from UM.FileHandler.FileWriter import FileWriter # To choose based on the output file mode (text vs. binary). from UM.FileHandler.WriteFileJob import WriteFileJob # To call the file writer asynchronously. from UM.Logger import Logger from UM.Settings.ContainerRegistry import ContainerRegistry @@ -23,10 +22,10 @@ from UM.Scene.SceneNode import SceneNode # For typing. from cura.CuraApplication import CuraApplication from cura.PrinterOutput.ConfigurationModel import ConfigurationModel from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationModel -from cura.PrinterOutput.NetworkedPrinterOutputDevice import AuthState +from cura.PrinterOutput.NetworkedPrinterOutputDevice import AuthState, NetworkedPrinterOutputDevice from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel -from plugins.UM3NetworkPrinting.src.BaseCuraConnectDevice import BaseCuraConnectDevice +from plugins.UM3NetworkPrinting.src.MeshFormatHandler import MeshFormatHandler from .ClusterUM3PrinterOutputController import ClusterUM3PrinterOutputController from .SendMaterialJob import SendMaterialJob @@ -40,7 +39,7 @@ from PyQt5.QtCore import pyqtSlot, QUrl, pyqtSignal, pyqtProperty, QObject i18n_catalog = i18nCatalog("cura") -class ClusterUM3OutputDevice(BaseCuraConnectDevice): +class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): printJobsChanged = pyqtSignal() activePrinterChanged = pyqtSignal() activeCameraUrlChanged = pyqtSignal() @@ -103,14 +102,13 @@ class ClusterUM3OutputDevice(BaseCuraConnectDevice): self.sendMaterialProfiles() - preferred_format = self._getPreferredFormat(file_handler) - writer = self._getWriter(file_handler, preferred_format["mime_type"]) + mesh_format = MeshFormatHandler(file_handler, self.firmwareVersion) # This function pauses with the yield, waiting on instructions on which printer it needs to print with. - if not writer: + if not mesh_format.is_valid: Logger.log("e", "Missing file or mesh writer!") return - self._sending_job = self._sendPrintJob(writer, preferred_format, nodes) + self._sending_job = self._sendPrintJob(mesh_format, nodes) if self._sending_job is not None: self._sending_job.send(None) # Start the generator. @@ -150,11 +148,8 @@ class ClusterUM3OutputDevice(BaseCuraConnectDevice): # greenlet in order to optionally wait for selectPrinter() to select a # printer. # The greenlet yields exactly three times: First time None, - # \param writer The file writer to use to create the data. - # \param preferred_format A dictionary containing some information about - # what format to write to. This is necessary to create the correct buffer - # types and file extension and such. - def _sendPrintJob(self, writer: FileWriter, preferred_format: Dict, nodes: List[SceneNode]): + # \param mesh_format Object responsible for choosing the right kind of format to write with. + def _sendPrintJob(self, mesh_format: MeshFormatHandler, nodes: List[SceneNode]): Logger.log("i", "Sending print job to printer.") if self._sending_gcode: self._error_message = Message( @@ -172,17 +167,17 @@ class ClusterUM3OutputDevice(BaseCuraConnectDevice): # Using buffering greatly reduces the write time for many lines of gcode - stream = io.BytesIO() # type: Union[io.BytesIO, io.StringIO]# Binary mode. - if preferred_format["mode"] == FileWriter.OutputMode.TextMode: - stream = io.StringIO() + stream = mesh_format.createStream() - job = WriteFileJob(writer, stream, nodes, preferred_format["mode"]) + job = WriteFileJob(mesh_format.writer, stream, nodes, mesh_format.file_mode) - self._write_job_progress_message = Message(i18n_catalog.i18nc("@info:status", "Sending data to printer"), lifetime = 0, dismissable = False, progress = -1, - title = i18n_catalog.i18nc("@info:title", "Sending Data"), use_inactivity_timer = False) + self._write_job_progress_message = Message(i18n_catalog.i18nc("@info:status", "Sending data to printer"), + lifetime = 0, dismissable = False, progress = -1, + title = i18n_catalog.i18nc("@info:title", "Sending Data"), + use_inactivity_timer = False) self._write_job_progress_message.show() - self._dummy_lambdas = (target_printer, preferred_format, stream) + self._dummy_lambdas = (target_printer, mesh_format.preferred_format, stream) job.finished.connect(self._sendPrintJobWaitOnWriteJobFinished) job.start() @@ -194,9 +189,11 @@ class ClusterUM3OutputDevice(BaseCuraConnectDevice): if self._write_job_progress_message: self._write_job_progress_message.hide() - self._progress_message = Message(i18n_catalog.i18nc("@info:status", "Sending data to printer"), lifetime = 0, dismissable = False, progress = -1, + self._progress_message = Message(i18n_catalog.i18nc("@info:status", "Sending data to printer"), lifetime = 0, + dismissable = False, progress = -1, title = i18n_catalog.i18nc("@info:title", "Sending Data")) - self._progress_message.addAction("Abort", i18n_catalog.i18nc("@action:button", "Cancel"), icon = None, description = "") + self._progress_message.addAction("Abort", i18n_catalog.i18nc("@action:button", "Cancel"), icon = None, + description = "") self._progress_message.actionTriggered.connect(self._progressMessageActionTriggered) self._progress_message.show() parts = [] @@ -220,7 +217,9 @@ class ClusterUM3OutputDevice(BaseCuraConnectDevice): parts.append(self._createFormPart("name=\"file\"; filename=\"%s\"" % file_name, output)) - self._latest_reply_handler = self.postFormWithParts("print_jobs/", parts, on_finished = self._onPostPrintJobFinished, on_progress = self._onUploadPrintJobProgress) + self._latest_reply_handler = self.postFormWithParts("print_jobs/", parts, + on_finished = self._onPostPrintJobFinished, + on_progress = self._onUploadPrintJobProgress) @pyqtProperty(QObject, notify = activePrinterChanged) def activePrinter(self) -> Optional[PrinterOutputModel]: diff --git a/plugins/UM3NetworkPrinting/src/BaseCuraConnectDevice.py b/plugins/UM3NetworkPrinting/src/MeshFormatHandler.py similarity index 50% rename from plugins/UM3NetworkPrinting/src/BaseCuraConnectDevice.py rename to plugins/UM3NetworkPrinting/src/MeshFormatHandler.py index 0abf5955cf..c2bd997fbb 100644 --- a/plugins/UM3NetworkPrinting/src/BaseCuraConnectDevice.py +++ b/plugins/UM3NetworkPrinting/src/MeshFormatHandler.py @@ -1,15 +1,16 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import Optional, Dict, Union +import io +from typing import Optional, Dict, Union, List from UM.FileHandler.FileHandler import FileHandler from UM.FileHandler.FileWriter import FileWriter from UM.Logger import Logger from UM.OutputDevice import OutputDeviceError # To show that something went wrong when writing. +from UM.Scene.SceneNode import SceneNode from UM.Version import Version # To check against firmware versions for support. from UM.i18n import i18nCatalog from cura.CuraApplication import CuraApplication -from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice ## Class that contains all the translations for this module. @@ -20,24 +21,63 @@ class T: NO_FORMATS_AVAILABLE = _I18N_CATALOG.i18nc("@info:status", "There are no file formats available to write with!") -## This is the base class for the UM3 output devices (via connect or cloud) -class BaseCuraConnectDevice(NetworkedPrinterOutputDevice): +## This class is responsible for choosing the formats used by the connected clusters. +class MeshFormatHandler: + + def __init__(self, file_handler: Optional[FileHandler], firmware_version: str) -> None: + self._file_handler = file_handler or CuraApplication.getInstance().getMeshFileHandler() + self._preferred_format = self._getPreferredFormat(firmware_version) + self._writer = self._getWriter(self._preferred_format["mime_type"]) if self._preferred_format else None - ## Gets the default file handler @property - def defaultFileHandler(self) -> FileHandler: - return CuraApplication.getInstance().getMeshFileHandler() + def is_valid(self) -> bool: + return bool(self._writer) + + ## Chooses the preferred file format. + # \return A dict with the file format details, with the following keys: + # {id: str, extension: str, description: str, mime_type: str, mode: int, hide_in_file_dialog: bool} + @property + def preferred_format(self) -> Optional[Dict[str, Union[str, int, bool]]]: + return self._preferred_format + + ## Gets the file writer for the given file handler and mime type. + # \return A file writer. + @property + def writer(self) -> Optional[FileWriter]: + return self._writer + + @property + def mime_type(self) -> str: + return self._preferred_format["mime_type"] + + ## Gets the file mode (FileWriter.OutputMode.TextMode or FileWriter.OutputMode.BinaryMode) + @property + def file_mode(self) -> int: + return self._preferred_format["mode"] + + ## Gets the file extension + @property + def file_extension(self) -> str: + return self._preferred_format["extension"] + + ## Creates the right kind of stream based on the preferred format. + def createStream(self) -> Union[io.BytesIO, io.StringIO]: + return io.StringIO() if self.file_mode == FileWriter.OutputMode.TextMode else io.BytesIO() + + ## Writes the mesh and returns its value. + def getBytes(self, nodes: List[SceneNode]) -> bytes: + stream = self.createStream() + self.writer.write(stream, nodes) + return stream.getvalue() ## Chooses the preferred file format for the given file handler. - # \param file_handler: The file handler. - # \return A dict with the file format details, with format: - # {id: str, extension: str, description: str, mime_type: str, mode: int, hide_in_file_dialog: bool} - def _getPreferredFormat(self, file_handler: Optional[FileHandler]) -> Optional[Dict[str, Union[str, int, bool]]]: + # \param firmware_version: The version of the firmware. + # \return A dict with the file format details. + def _getPreferredFormat(self, firmware_version: str) -> Optional[Dict[str, Union[str, int, bool]]]: # Formats supported by this application (file types that we can actually write). application = CuraApplication.getInstance() - file_handler = file_handler or self.defaultFileHandler - file_formats = file_handler.getSupportedFileTypesWrite() + file_formats = self._file_handler.getSupportedFileTypesWrite() global_stack = application.getGlobalContainerStack() # Create a list from the supported file formats string. @@ -48,7 +88,7 @@ class BaseCuraConnectDevice(NetworkedPrinterOutputDevice): machine_file_formats = global_stack.getMetaDataEntry("file_formats").split(";") machine_file_formats = [file_type.strip() for file_type in machine_file_formats] # Exception for UM3 firmware version >=4.4: UFP is now supported and should be the preferred file format. - if "application/x-ufp" not in machine_file_formats and Version(self.firmwareVersion) >= Version("4.4"): + if "application/x-ufp" not in machine_file_formats and Version(firmware_version) >= Version("4.4"): machine_file_formats = ["application/x-ufp"] + machine_file_formats # Take the intersection between file_formats and machine_file_formats. @@ -63,10 +103,8 @@ class BaseCuraConnectDevice(NetworkedPrinterOutputDevice): return file_formats[0] ## Gets the file writer for the given file handler and mime type. - # \param file_handler: The file handler. # \param mime_type: The mine type. # \return A file writer. - def _getWriter(self, file_handler: Optional[FileHandler], mime_type: str) -> Optional[FileWriter]: + def _getWriter(self, mime_type: str) -> Optional[FileWriter]: # Just take the first file format available. - file_handler = file_handler or self.defaultFileHandler - return file_handler.getWriterByMimeType(mime_type) + return self._file_handler.getWriterByMimeType(mime_type) From e8a933331c0be8ce2086a26a982ee220282001d7 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Wed, 5 Dec 2018 12:14:18 +0100 Subject: [PATCH 0662/1240] Clean-up printer status label Contributes to CL-1153 --- .../resources/qml/MonitorPrinterCard.qml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml index 8659037cb8..567fff8489 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml @@ -179,13 +179,15 @@ Item color: "#414054" // TODO: Theme! font: UM.Theme.getFont("large") // 16pt, bold text: { - if (printer && printer.state == "disabled"){ + if (printer && printer.state == "disabled") + { return catalog.i18nc("@label:status", "Unavailable") } - if (printer && printer.state == "unreachable"){ - return catalog.i18nc("@label:status", "Unavailable") + if (printer && printer.state == "unreachable") + { + return catalog.i18nc("@label:status", "Unreachable") } - if (printer && !printer.activePrintJob) + if (printer && printer.state == "idle") { return catalog.i18nc("@label:status", "Idle") } From 034b1660f766211bd658127e28f0dd35809c2f6b Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 5 Dec 2018 13:05:22 +0100 Subject: [PATCH 0663/1240] Adjust sizes in the custom print setup mode Contributes to CURA-5941. --- .../qml/PrintSetupSelector/Custom/CustomPrintSetup.qml | 9 +++------ .../PrintSetupSelector/PrintSetupSelectorContents.qml | 8 ++++---- resources/themes/cura-light/theme.json | 2 +- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml index b524bb5926..8b0f3524d7 100644 --- a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml +++ b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml @@ -11,9 +11,7 @@ import Cura 1.0 as Cura Item { id: customPrintSetup - - // TODO: Hardcoded now but UX has to decide about the height of this item - height: 480 + height: childrenRect.height + padding property real padding: UM.Theme.getSize("default_margin").width property bool multipleExtruders: extrudersModel.count > 1 @@ -93,16 +91,15 @@ Item Rectangle { + height: UM.Theme.getSize("print_setup_widget").height anchors { - top: tabBar.visible ? tabBar.bottom : globalProfileRow.bottom + top: tabBar.bottom left: parent.left leftMargin: parent.padding right: parent.right rightMargin: parent.padding - bottom: parent.bottom topMargin: -UM.Theme.getSize("default_lining").width - bottomMargin: -UM.Theme.getSize("default_lining").width } z: tabBar.z - 1 // Don't show the border when only one extruder diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml index 0524a6de9e..522b038423 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml @@ -28,7 +28,7 @@ Item { var index = Math.round(UM.Preferences.getValue("cura/active_mode")) - if(index != null && !isNaN(index)) + if (index != null && !isNaN(index)) { return index } @@ -150,7 +150,8 @@ Item { id: buttonsSeparator - anchors.top: contents.bottom + // The buttonsSeparator is inside the contents. This is to avoid a double line in the bottom + anchors.bottom: contents.bottom width: parent.width height: UM.Theme.getSize("default_lining").height color: UM.Theme.getColor("lining") @@ -162,10 +163,9 @@ Item property real padding: UM.Theme.getSize("default_margin").width height: childrenRect.height + 2 * padding - // The buttonsSeparator is inside the buttonRow. This is to avoid some weird behaviours with the scroll bar. anchors { - top: buttonsSeparator.top + top: buttonsSeparator.bottom left: parent.left right: parent.right } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 8e440757aa..f383a570a2 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -369,7 +369,7 @@ "account_button": [12, 3], - "print_setup_widget": [38.0, 42.0], + "print_setup_widget": [38.0, 30.0], "print_setup_mode_toggle": [0.0, 2.0], "print_setup_item": [0.0, 2.0], "print_setup_extruder_box": [0.0, 6.0], From b1440737e641a708683c06ea12c7874dd3fb8ab7 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 5 Dec 2018 13:21:17 +0100 Subject: [PATCH 0664/1240] Remove a whole bunch of unused code CURA-6006 --- plugins/Toolbox/src/AuthorsModel.py | 2 +- plugins/Toolbox/src/Toolbox.py | 70 +++++++++-------------------- 2 files changed, 22 insertions(+), 50 deletions(-) diff --git a/plugins/Toolbox/src/AuthorsModel.py b/plugins/Toolbox/src/AuthorsModel.py index 6f4b5bd280..8fafda54ec 100644 --- a/plugins/Toolbox/src/AuthorsModel.py +++ b/plugins/Toolbox/src/AuthorsModel.py @@ -73,7 +73,7 @@ class AuthorsModel(ListModel): self._filter = filter_dict self._update() - @pyqtProperty("QStringMap", fset = setFilter, constant = True) + @pyqtProperty("QVariantMap", fset = setFilter, constant = True) def filter(self) -> Dict[str, str]: return self._filter diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index b6cfafe40c..5db3dd934f 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -70,13 +70,6 @@ class Toolbox(QObject, Extension): self._metadata = { "authors": [], "packages": [], - "plugins_showcase": [], - "plugins_available": [], - "plugins_installed": [], - "materials_showcase": [], - "materials_available": [], - "materials_installed": [], - "materials_generic": [] } # type: Dict[str, List[Any]] # Models: @@ -178,12 +171,7 @@ class Toolbox(QObject, Extension): ) self._request_urls = { "authors": QUrl("{base_url}/authors".format(base_url = self._api_url)), - "packages": QUrl("{base_url}/packages".format(base_url = self._api_url)), - "plugins_showcase": QUrl("{base_url}/showcase".format(base_url = self._api_url)), - "plugins_available": QUrl("{base_url}/packages?package_type=plugin".format(base_url = self._api_url)), - "materials_showcase": QUrl("{base_url}/showcase".format(base_url = self._api_url)), - "materials_available": QUrl("{base_url}/packages?package_type=material".format(base_url = self._api_url)), - "materials_generic": QUrl("{base_url}/packages?package_type=material&tags=generic".format(base_url = self._api_url)) + "packages": QUrl("{base_url}/packages".format(base_url = self._api_url)) } # Get the API root for the packages API depending on Cura version settings. @@ -606,8 +594,8 @@ class Toolbox(QObject, Extension): if self._download_reply: try: self._download_reply.downloadProgress.disconnect(self._onDownloadProgress) - except TypeError: #Raised when the method is not connected to the signal yet. - pass #Don't need to disconnect. + except TypeError: # Raised when the method is not connected to the signal yet. + pass # Don't need to disconnect. self._download_reply.abort() self._download_reply = None self._download_request = None @@ -633,22 +621,8 @@ class Toolbox(QObject, Extension): self.resetDownload() return - # HACK: These request are not handled independently at this moment, but together from the "packages" call - do_not_handle = [ - "materials_available", - "materials_showcase", - "materials_generic", - "plugins_available", - "plugins_showcase", - ] - if reply.operation() == QNetworkAccessManager.GetOperation: for response_type, url in self._request_urls.items(): - - # HACK: Do nothing because we'll handle these from the "packages" call - if response_type in do_not_handle: - continue - if reply.url() == url: if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) == 200: try: @@ -672,11 +646,10 @@ class Toolbox(QObject, Extension): # TODO: Make multiple API calls in the future to handle this if response_type is "packages": self._models[response_type].setFilter({"type": "plugin"}) - self.buildMaterialsModels() + self.reBuildMaterialsModels() self.buildPluginsModels() - if response_type is "authors": + elif response_type is "authors": self._models[response_type].setFilter({"package_types": "material"}) - if response_type is "materials_generic": self._models[response_type].setFilter({"tags": "generic"}) self.metadataChanged.emit() @@ -834,10 +807,10 @@ class Toolbox(QObject, Extension): # HACK(S): # -------------------------------------------------------------------------- - def buildMaterialsModels(self) -> None: - self._metadata["materials_showcase"] = [] - self._metadata["materials_available"] = [] - self._metadata["materials_generic"] = [] + def reBuildMaterialsModels(self) -> None: + materials_showcase_metadata = [] + materials_available_metadata = [] + materials_generic_metadata = [] processed_authors = [] # type: List[str] @@ -850,30 +823,29 @@ class Toolbox(QObject, Extension): # Generic materials to be in the same section if "generic" in item["tags"]: - self._metadata["materials_generic"].append(item) + materials_generic_metadata.append(item) else: if "showcase" in item["tags"]: - self._metadata["materials_showcase"].append(author) + materials_showcase_metadata.append(author) else: - self._metadata["materials_available"].append(author) + materials_available_metadata.append(author) processed_authors.append(author["author_id"]) - self._models["materials_showcase"].setMetadata(self._metadata["materials_showcase"]) - self._models["materials_available"].setMetadata(self._metadata["materials_available"]) - self._models["materials_generic"].setMetadata(self._metadata["materials_generic"]) + self._models["materials_showcase"].setMetadata(materials_showcase_metadata) + self._models["materials_available"].setMetadata(materials_available_metadata) + self._models["materials_generic"].setMetadata(materials_generic_metadata) def buildPluginsModels(self) -> None: - self._metadata["plugins_showcase"] = [] - self._metadata["plugins_available"] = [] + plugins_showcase_metadata = [] + plugins_available_metadata = [] for item in self._metadata["packages"]: if item["package_type"] == "plugin": - if "showcase" in item["tags"]: - self._metadata["plugins_showcase"].append(item) + plugins_showcase_metadata.append(item) else: - self._metadata["plugins_available"].append(item) + plugins_available_metadata.append(item) - self._models["plugins_showcase"].setMetadata(self._metadata["plugins_showcase"]) - self._models["plugins_available"].setMetadata(self._metadata["plugins_available"]) + self._models["plugins_showcase"].setMetadata(plugins_showcase_metadata) + self._models["plugins_available"].setMetadata(plugins_available_metadata) From 5b57e6bf307eb2060b6c386c054401ab0625a779 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 5 Dec 2018 13:24:34 +0100 Subject: [PATCH 0665/1240] Code style in JobSpecs --- resources/qml/JobSpecs.qml | 82 +++++++++++++++++++++++--------------- 1 file changed, 49 insertions(+), 33 deletions(-) diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index 45111992c1..5f773f9b25 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -9,20 +9,27 @@ import QtQuick.Layouts 1.1 import UM 1.1 as UM import Cura 1.0 as Cura -Item { +Item +{ id: base property bool activity: CuraApplication.platformActivity property string fileBaseName: PrintInformation.baseName - UM.I18nCatalog { id: catalog; name: "cura"} + UM.I18nCatalog + { + id: catalog + name:"cura" + } height: childrenRect.height - onActivityChanged: { - if (activity == false) { + onActivityChanged: + { + if (!activity) + { //When there is no mesh in the buildplate; the printJobTextField is set to an empty string so it doesn't set an empty string as a jobName (which is later used for saving the file) - PrintInformation.baseName = '' + PrintInformation.baseName = "" } } @@ -49,21 +56,22 @@ Item { onClicked: { - printJobTextfield.selectAll(); - printJobTextfield.focus = true; + printJobTextfield.selectAll() + printJobTextfield.focus = true } + style: ButtonStyle { background: Item { UM.RecolorImage { - width: UM.Theme.getSize("save_button_specs_icons").width; - height: UM.Theme.getSize("save_button_specs_icons").height; - sourceSize.width: width; - sourceSize.height: width; - color: control.hovered ? UM.Theme.getColor("text_scene_hover") : UM.Theme.getColor("text_scene"); - source: UM.Theme.getIcon("pencil"); + width: UM.Theme.getSize("save_button_specs_icons").width + height: UM.Theme.getSize("save_button_specs_icons").height + sourceSize.width: width + sourceSize.height: width + color: control.hovered ? UM.Theme.getColor("text_scene_hover") : UM.Theme.getColor("text_scene") + source: UM.Theme.getIcon("pencil") } } } @@ -73,25 +81,31 @@ Item { { id: printJobTextfield anchors.right: printJobPencilIcon.left - anchors.rightMargin: Math.round(UM.Theme.getSize("default_margin").width / 2) + anchors.rightMargin: UM.Theme.getSize("narrow_margin").width height: UM.Theme.getSize("jobspecs_line").height width: Math.max(__contentWidth + UM.Theme.getSize("default_margin").width, 50) maximumLength: 120 property int unremovableSpacing: 5 text: PrintInformation.jobName horizontalAlignment: TextInput.AlignRight - onEditingFinished: { - var new_name = text == "" ? catalog.i18nc("@text Print job name", "Untitled") : text; - PrintInformation.setJobName(new_name, true); - printJobTextfield.focus = false; + + onEditingFinished: + { + var new_name = text == "" ? catalog.i18nc("@text Print job name", "Untitled") : text + PrintInformation.setJobName(new_name, true) + printJobTextfield.focus = false } + validator: RegExpValidator { regExp: /^[^\\\/\*\?\|\[\]]*$/ } - style: TextFieldStyle{ - textColor: UM.Theme.getColor("text_scene"); - font: UM.Theme.getFont("default_bold"); - background: Rectangle { + + style: TextFieldStyle + { + textColor: UM.Theme.getColor("text_scene") + font: UM.Theme.getFont("default_bold") + background: Rectangle + { opacity: 0 border.width: 0 } @@ -100,7 +114,8 @@ Item { } } - Row { + Row + { id: additionalComponentsRow anchors.top: jobNameRow.bottom anchors.right: parent.right @@ -117,10 +132,7 @@ Item { { return UM.Theme.getSize("default_margin").width } - else - { - return 0; - } + return 0 } height: UM.Theme.getSize("jobspecs_line").height verticalAlignment: Text.AlignVCenter @@ -129,21 +141,25 @@ Item { text: CuraApplication.getSceneBoundingBoxString } - Component.onCompleted: { + Component.onCompleted: + { base.addAdditionalComponents("jobSpecsButton") } - Connections { + Connections + { target: CuraApplication onAdditionalComponentsChanged: base.addAdditionalComponents("jobSpecsButton") } - function addAdditionalComponents (areaId) { - if(areaId == "jobSpecsButton") { - for (var component in CuraApplication.additionalComponents["jobSpecsButton"]) { + function addAdditionalComponents (areaId) + { + if (areaId == "jobSpecsButton") + { + for (var component in CuraApplication.additionalComponents["jobSpecsButton"]) + { CuraApplication.additionalComponents["jobSpecsButton"][component].parent = additionalComponentsRow } } } - } From 0e22f6b672a29c8b57309332a1f77355f151f870 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 5 Dec 2018 13:30:01 +0100 Subject: [PATCH 0666/1240] Fix an issue that caused a non-deterministic segfault Also add a topMargin only when there are tabs in the custom print setup. Contributes to CURA-5941. --- .../Custom/CustomPrintSetup.qml | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml index 8b0f3524d7..b28c9ceb46 100644 --- a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml +++ b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml @@ -52,8 +52,6 @@ Item rightMargin: parent.padding } - currentIndex: Math.max(Cura.ExtruderManager.activeExtruderIndex, 0) - Repeater { id: repeater @@ -76,15 +74,27 @@ Item } } - // When the model of the extruders is rebuilt, the list of extruders is briefly emptied and rebuilt. - // This causes the currentIndex of the tab to be in an invalid position which resets it to 0. - // Therefore we need to change it back to what it was: The active extruder index. + //When active extruder changes for some other reason, switch tabs. + //Don't directly link currentIndex to Cura.ExtruderManager.activeExtruderIndex! + //This causes a segfault in Qt 5.11. Something with VisualItemModel removing index -1. We have to use setCurrentIndex instead. + Connections + { + target: Cura.ExtruderManager + onActiveExtruderChanged: + { + tabBar.setCurrentIndex(Cura.ExtruderManager.activeExtruderIndex); + } + } + + //When the model of the extruders is rebuilt, the list of extruders is briefly emptied and rebuilt. + //This causes the currentIndex of the tab to be in an invalid position which resets it to 0. + //Therefore we need to change it back to what it was: The active extruder index. Connections { target: repeater.model onModelChanged: { - tabBar.currentIndex = Math.max(Cura.ExtruderManager.activeExtruderIndex, 0) + tabBar.setCurrentIndex(Cura.ExtruderManager.activeExtruderIndex) } } } @@ -94,7 +104,7 @@ Item height: UM.Theme.getSize("print_setup_widget").height anchors { - top: tabBar.bottom + top: tabBar.visible ? tabBar.bottom : globalProfileRow.bottom left: parent.left leftMargin: parent.padding right: parent.right From 07d210483c91d77c3198548a386247fdc993df2f Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 5 Dec 2018 13:42:13 +0100 Subject: [PATCH 0667/1240] Greatly decrease the bloat / complexity of the toolbox There was a lot of stuff going on that didn't need to happen, so I cut those parts out in order to improve the overview. --- plugins/Toolbox/src/AuthorsModel.py | 5 +-- plugins/Toolbox/src/PackagesModel.py | 5 +-- plugins/Toolbox/src/Toolbox.py | 54 ++++++++++++++-------------- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/plugins/Toolbox/src/AuthorsModel.py b/plugins/Toolbox/src/AuthorsModel.py index 8fafda54ec..877f8256ee 100644 --- a/plugins/Toolbox/src/AuthorsModel.py +++ b/plugins/Toolbox/src/AuthorsModel.py @@ -29,8 +29,9 @@ class AuthorsModel(ListModel): self._filter = {} # type: Dict[str, str] def setMetadata(self, data: List[Dict[str, Union[str, List[str], int]]]): - self._metadata = data - self._update() + if self._metadata != data: + self._metadata = data + self._update() def _update(self) -> None: items = [] # type: List[Dict[str, Union[str, List[str], int, None]]] diff --git a/plugins/Toolbox/src/PackagesModel.py b/plugins/Toolbox/src/PackagesModel.py index 2849a29c99..f941804653 100644 --- a/plugins/Toolbox/src/PackagesModel.py +++ b/plugins/Toolbox/src/PackagesModel.py @@ -45,8 +45,9 @@ class PackagesModel(ListModel): self._filter = {} # type: Dict[str, str] def setMetadata(self, data): - self._metadata = data - self._update() + if self._metadata != data: + self._metadata = data + self._update() def _update(self): items = [] diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 5db3dd934f..7b1442ff3c 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -66,8 +66,8 @@ class Toolbox(QObject, Extension): self._old_plugin_ids = set() # type: Set[str] self._old_plugin_metadata = dict() # type: Dict[str, Dict[str, Any]] - # Data: - self._metadata = { + # The responses as given by the server parsed to a list. + self._server_response_data = { "authors": [], "packages": [], } # type: Dict[str, List[Any]] @@ -301,13 +301,13 @@ class Toolbox(QObject, Extension): if plugin_id not in all_plugin_package_ids) self._old_plugin_metadata = {k: v for k, v in self._old_plugin_metadata.items() if k in self._old_plugin_ids} - self._metadata["plugins_installed"] = all_packages["plugin"] + list(self._old_plugin_metadata.values()) - self._models["plugins_installed"].setMetadata(self._metadata["plugins_installed"]) + self._server_response_data["plugins_installed"] = all_packages["plugin"] + list(self._old_plugin_metadata.values()) + self._models["plugins_installed"].setMetadata(self._server_response_data["plugins_installed"]) self.metadataChanged.emit() if "material" in all_packages: - self._metadata["materials_installed"] = all_packages["material"] + self._server_response_data["materials_installed"] = all_packages["material"] # TODO: ADD MATERIALS HERE ONCE MATERIALS PORTION OF TOOLBOX IS LIVE - self._models["materials_installed"].setMetadata(self._metadata["materials_installed"]) + self._models["materials_installed"].setMetadata(self._server_response_data["materials_installed"]) self.metadataChanged.emit() @pyqtSlot(str) @@ -461,7 +461,7 @@ class Toolbox(QObject, Extension): def getRemotePackage(self, package_id: str) -> Optional[Dict]: # TODO: make the lookup in a dict, not a loop. canUpdate is called for every item. remote_package = None - for package in self._metadata["packages"]: + for package in self._server_response_data["packages"]: if package["package_id"] == package_id: remote_package = package break @@ -524,7 +524,7 @@ class Toolbox(QObject, Extension): @pyqtSlot(str, result = int) def getNumberOfInstalledPackagesByAuthor(self, author_id: str) -> int: count = 0 - for package in self._metadata["materials_installed"]: + for package in self._server_response_data["materials_installed"]: if package["author"]["author_id"] == author_id: count += 1 return count @@ -533,7 +533,7 @@ class Toolbox(QObject, Extension): @pyqtSlot(str, result = int) def getTotalNumberOfMaterialPackagesByAuthor(self, author_id: str) -> int: count = 0 - for package in self._metadata["packages"]: + for package in self._server_response_data["packages"]: if package["package_type"] == "material": if package["author"]["author_id"] == author_id: count += 1 @@ -554,10 +554,10 @@ class Toolbox(QObject, Extension): def isLoadingComplete(self) -> bool: populated = 0 - for metadata_list in self._metadata.items(): + for metadata_list in self._server_response_data.items(): if metadata_list: populated += 1 - return populated == len(self._metadata.items()) + return populated == len(self._server_response_data.items()) # Make API Calls # -------------------------------------------------------------------------- @@ -639,15 +639,13 @@ class Toolbox(QObject, Extension): Logger.log("e", "Could not find the %s model.", response_type) break - self._metadata[response_type] = json_data["data"] - self._models[response_type].setMetadata(self._metadata[response_type]) + self._server_response_data[response_type] = json_data["data"] + self._models[response_type].setMetadata(self._server_response_data[response_type]) - # Do some auto filtering - # TODO: Make multiple API calls in the future to handle this if response_type is "packages": self._models[response_type].setFilter({"type": "plugin"}) self.reBuildMaterialsModels() - self.buildPluginsModels() + self.reBuildPluginsModels() elif response_type is "authors": self._models[response_type].setFilter({"package_types": "material"}) self._models[response_type].setFilter({"tags": "generic"}) @@ -743,39 +741,39 @@ class Toolbox(QObject, Extension): # Exposed Models: # -------------------------------------------------------------------------- - @pyqtProperty(QObject, notify = metadataChanged) + @pyqtProperty(QObject, constant=True) def authorsModel(self) -> AuthorsModel: return cast(AuthorsModel, self._models["authors"]) - @pyqtProperty(QObject, notify = metadataChanged) + @pyqtProperty(QObject, constant=True) def packagesModel(self) -> PackagesModel: return cast(PackagesModel, self._models["packages"]) - @pyqtProperty(QObject, notify = metadataChanged) + @pyqtProperty(QObject, constant=True) def pluginsShowcaseModel(self) -> PackagesModel: return cast(PackagesModel, self._models["plugins_showcase"]) - @pyqtProperty(QObject, notify = metadataChanged) + @pyqtProperty(QObject, constant=True) def pluginsAvailableModel(self) -> PackagesModel: return cast(PackagesModel, self._models["plugins_available"]) - @pyqtProperty(QObject, notify = metadataChanged) + @pyqtProperty(QObject, constant=True) def pluginsInstalledModel(self) -> PackagesModel: return cast(PackagesModel, self._models["plugins_installed"]) - @pyqtProperty(QObject, notify = metadataChanged) + @pyqtProperty(QObject, constant=True) def materialsShowcaseModel(self) -> AuthorsModel: return cast(AuthorsModel, self._models["materials_showcase"]) - @pyqtProperty(QObject, notify = metadataChanged) + @pyqtProperty(QObject, constant=True) def materialsAvailableModel(self) -> AuthorsModel: return cast(AuthorsModel, self._models["materials_available"]) - @pyqtProperty(QObject, notify = metadataChanged) + @pyqtProperty(QObject, constant=True) def materialsInstalledModel(self) -> PackagesModel: return cast(PackagesModel, self._models["materials_installed"]) - @pyqtProperty(QObject, notify=metadataChanged) + @pyqtProperty(QObject, constant=True) def materialsGenericModel(self) -> PackagesModel: return cast(PackagesModel, self._models["materials_generic"]) @@ -814,7 +812,7 @@ class Toolbox(QObject, Extension): processed_authors = [] # type: List[str] - for item in self._metadata["packages"]: + for item in self._server_response_data["packages"]: if item["package_type"] == "material": author = item["author"] @@ -836,11 +834,11 @@ class Toolbox(QObject, Extension): self._models["materials_available"].setMetadata(materials_available_metadata) self._models["materials_generic"].setMetadata(materials_generic_metadata) - def buildPluginsModels(self) -> None: + def reBuildPluginsModels(self) -> None: plugins_showcase_metadata = [] plugins_available_metadata = [] - for item in self._metadata["packages"]: + for item in self._server_response_data["packages"]: if item["package_type"] == "plugin": if "showcase" in item["tags"]: plugins_showcase_metadata.append(item) From 7d7dd6bdde7738e3b8e56a879e07a535557eaad6 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 5 Dec 2018 14:02:53 +0100 Subject: [PATCH 0668/1240] First idea for pairing local and remote clusters --- .../src/Cloud/CloudOutputDeviceManager.py | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 0fbeeb82b6..9405ea8490 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -77,13 +77,15 @@ class CloudOutputDeviceManager: for cluster_id in known_cluster_ids.difference(found_cluster_ids): self._removeCloudOutputDevice(found_clusters[cluster_id]) + # TODO: not pass clusters that are not online? + self._connectToActiveMachine(clusters) + ## Adds a CloudOutputDevice for each entry in the remote cluster list from the API. # \param cluster: The cluster that was added. def _addCloudOutputDevice(self, cluster: CloudCluster): device = CloudOutputDevice(self._api, cluster.cluster_id) self._output_device_manager.addOutputDevice(device) self._remote_clusters[cluster.cluster_id] = device - self._connectToActiveMachine(cluster.cluster_id) ## Remove a CloudOutputDevice # \param cluster: The cluster that was removed @@ -93,28 +95,28 @@ class CloudOutputDeviceManager: del self._remote_clusters[cluster.cluster_id] ## Callback for when the active machine was changed by the user or a new remote cluster was found. - def _connectToActiveMachine(self, cluster_id: Optional[str] = None) -> None: + def _connectToActiveMachine(self, clusters: List[CloudCluster]) -> None: active_machine = CuraApplication.getInstance().getGlobalContainerStack() if not active_machine: return - # TODO: Remove this once correct pairing has been added (see below). - # TODO: This just adds any available cluster to the active device for testing. - if cluster_id: - active_machine.setMetaDataEntry("um_cloud_cluster_id", cluster_id) - # Check if the stored cluster_id for the active machine is in our list of remote clusters. stored_cluster_id = active_machine.getMetaDataEntry("um_cloud_cluster_id") if stored_cluster_id in self._remote_clusters.keys(): - self._remote_clusters.get(stored_cluster_id).connect() + return self._remote_clusters.get(stored_cluster_id).connect() + + # Check if the active printer has a local network connection and match this key to the remote cluster. + # The local network key is formatted as ultimakersystem-xxxxxxxxxxxx._ultimaker._tcp.local. + # The optional remote host_name is formatted as ultimakersystem-xxxxxxxxxxxx. + # This means we can match the two by checking if the host_name is in the network key string. + + local_network_key = active_machine.getMetaDataEntry("um_network_key") + if not local_network_key: return - # TODO: See if this cloud cluster still has to be associated to the active machine. - # TODO: We have to get a common piece of data, like local network hostname, from the active machine and - # TODO: cloud cluster and then set the "um_cloud_cluster_id" meta data key on the active machine. - # TODO: If so, we can also immediate connect to it. - # active_machine.setMetaDataEntry("um_cloud_cluster_id", "") - # self._remote_clusters.get(stored_cluster_id).connect() + cluster_id = next(local_network_key in cluster.host_name for cluster in clusters) + if cluster_id in self._remote_clusters.keys(): + return self._remote_clusters.get(cluster_id).connect() ## Handles an API error received from the cloud. # \param errors: The errors received From a52f866f818324994b1a1b34f06805ffd3ed24d0 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 5 Dec 2018 14:03:32 +0100 Subject: [PATCH 0669/1240] Move most models out of dictionary CURA-6006 --- plugins/Toolbox/src/Toolbox.py | 47 ++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 7b1442ff3c..ada81dbc07 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -70,21 +70,25 @@ class Toolbox(QObject, Extension): self._server_response_data = { "authors": [], "packages": [], + "plugins_installed": [], + "materials_installed": [] } # type: Dict[str, List[Any]] # Models: self._models = { "authors": AuthorsModel(self), "packages": PackagesModel(self), - "plugins_showcase": PackagesModel(self), - "plugins_available": PackagesModel(self), - "plugins_installed": PackagesModel(self), - "materials_showcase": AuthorsModel(self), - "materials_available": AuthorsModel(self), - "materials_installed": PackagesModel(self), - "materials_generic": PackagesModel(self) } # type: Dict[str, ListModel] + self._plugins_showcase_model = PackagesModel(self) + self._plugins_available_model = PackagesModel(self) + self._plugins_installed_model = PackagesModel(self) + + self._materials_showcase_model = AuthorsModel(self) + self._materials_available_model = AuthorsModel(self) + self._materials_installed_model = PackagesModel(self) + self._materials_generic_model = PackagesModel(self) + # These properties are for keeping track of the UI state: # ---------------------------------------------------------------------- # View category defines which filter to use, and therefore effectively @@ -302,12 +306,11 @@ class Toolbox(QObject, Extension): self._old_plugin_metadata = {k: v for k, v in self._old_plugin_metadata.items() if k in self._old_plugin_ids} self._server_response_data["plugins_installed"] = all_packages["plugin"] + list(self._old_plugin_metadata.values()) - self._models["plugins_installed"].setMetadata(self._server_response_data["plugins_installed"]) + self._plugins_installed_model.setMetadata(self._server_response_data["plugins_installed"]) self.metadataChanged.emit() if "material" in all_packages: self._server_response_data["materials_installed"] = all_packages["material"] - # TODO: ADD MATERIALS HERE ONCE MATERIALS PORTION OF TOOLBOX IS LIVE - self._models["materials_installed"].setMetadata(self._server_response_data["materials_installed"]) + self._materials_installed_model.setMetadata(all_packages["material"]) self.metadataChanged.emit() @pyqtSlot(str) @@ -751,31 +754,31 @@ class Toolbox(QObject, Extension): @pyqtProperty(QObject, constant=True) def pluginsShowcaseModel(self) -> PackagesModel: - return cast(PackagesModel, self._models["plugins_showcase"]) + return self._plugins_showcase_model @pyqtProperty(QObject, constant=True) def pluginsAvailableModel(self) -> PackagesModel: - return cast(PackagesModel, self._models["plugins_available"]) + return self._plugins_available_model @pyqtProperty(QObject, constant=True) def pluginsInstalledModel(self) -> PackagesModel: - return cast(PackagesModel, self._models["plugins_installed"]) + return self._plugins_installed_model @pyqtProperty(QObject, constant=True) def materialsShowcaseModel(self) -> AuthorsModel: - return cast(AuthorsModel, self._models["materials_showcase"]) + return self._materials_showcase_model @pyqtProperty(QObject, constant=True) def materialsAvailableModel(self) -> AuthorsModel: - return cast(AuthorsModel, self._models["materials_available"]) + return self._materials_available_model @pyqtProperty(QObject, constant=True) def materialsInstalledModel(self) -> PackagesModel: - return cast(PackagesModel, self._models["materials_installed"]) + return self._materials_installed_model @pyqtProperty(QObject, constant=True) def materialsGenericModel(self) -> PackagesModel: - return cast(PackagesModel, self._models["materials_generic"]) + return self._materials_generic_model # Filter Models: # -------------------------------------------------------------------------- @@ -830,9 +833,9 @@ class Toolbox(QObject, Extension): processed_authors.append(author["author_id"]) - self._models["materials_showcase"].setMetadata(materials_showcase_metadata) - self._models["materials_available"].setMetadata(materials_available_metadata) - self._models["materials_generic"].setMetadata(materials_generic_metadata) + self._materials_showcase_model.setMetadata(materials_showcase_metadata) + self._materials_available_model.setMetadata(materials_available_metadata) + self._materials_generic_model.setMetadata(materials_generic_metadata) def reBuildPluginsModels(self) -> None: plugins_showcase_metadata = [] @@ -845,5 +848,5 @@ class Toolbox(QObject, Extension): else: plugins_available_metadata.append(item) - self._models["plugins_showcase"].setMetadata(plugins_showcase_metadata) - self._models["plugins_available"].setMetadata(plugins_available_metadata) + self._plugins_showcase_model.setMetadata(plugins_showcase_metadata) + self._plugins_available_model.setMetadata(plugins_available_metadata) From b693b9d98fa592ddde05f5049845b803fb0a405d Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Wed, 5 Dec 2018 14:08:40 +0100 Subject: [PATCH 0670/1240] STAR-322: Extracting models to be able for converting themselves --- __init__.py | 0 .../src/Cloud/CloudApiClient.py | 11 +- .../src/Cloud/CloudOutputDevice.py | 119 ++++---------- .../src/Cloud/CloudOutputDeviceManager.py | 5 +- .../UM3NetworkPrinting/src/Cloud/Models.py | 153 ------------------ .../src/Cloud/Models/CloudCluster.py | 21 +++ .../src/Cloud/Models/CloudClusterPrintJob.py | 52 ++++++ .../Models/CloudClusterPrintJobConstraint.py | 10 ++ .../src/Cloud/Models/CloudClusterPrinter.py | 44 +++++ .../CloudClusterPrinterConfiguration.py | 27 ++++ ...loudClusterPrinterConfigurationMaterial.py | 46 ++++++ .../src/Cloud/Models/CloudClusterStatus.py | 22 +++ .../src/Cloud/Models/CloudErrorObject.py | 17 ++ .../src/Cloud/Models/CloudJobResponse.py | 16 ++ .../src/Cloud/Models/CloudJobUploadRequest.py | 12 ++ .../src/Cloud/Models/CloudPrintResponse.py | 12 ++ .../src/Cloud/Models/__init__.py | 2 + .../src/ClusterUM3OutputDevice.py | 4 +- plugins/UM3NetworkPrinting/src/Models.py | 3 + .../tests/TestSendMaterialJob.py | 2 +- 20 files changed, 324 insertions(+), 254 deletions(-) create mode 100644 __init__.py delete mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Models.py create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Models/CloudCluster.py create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobConstraint.py create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinter.py create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfiguration.py create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfigurationMaterial.py create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterStatus.py create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Models/CloudErrorObject.py create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Models/CloudJobResponse.py create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Models/CloudJobUploadRequest.py create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Models/CloudPrintResponse.py create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Models/__init__.py diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py index d6c20d387b..3ede206d45 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py @@ -9,10 +9,13 @@ from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply from UM.Logger import Logger from cura.API import Account from cura.NetworkClient import NetworkClient -from plugins.UM3NetworkPrinting.src.Models import BaseModel -from plugins.UM3NetworkPrinting.src.Cloud.Models import ( - CloudCluster, CloudErrorObject, CloudClusterStatus, CloudJobUploadRequest, CloudPrintResponse, CloudJobResponse -) +from ..Models import BaseModel +from .Models.CloudCluster import CloudCluster +from .Models.CloudErrorObject import CloudErrorObject +from .Models.CloudClusterStatus import CloudClusterStatus +from .Models.CloudJobUploadRequest import CloudJobUploadRequest +from .Models.CloudPrintResponse import CloudPrintResponse +from .Models.CloudJobResponse import CloudJobResponse ## The cloud API client is responsible for handling the requests and responses from the cloud. diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 91f5721aa6..c6397fc41f 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -2,9 +2,9 @@ # Cura is released under the terms of the LGPLv3 or higher. import os from time import time -from typing import List, Optional, Dict, Set +from typing import Dict, List, Optional, Set -from PyQt5.QtCore import QObject, pyqtSignal, QUrl, pyqtProperty, pyqtSlot +from PyQt5.QtCore import QObject, QUrl, pyqtProperty, pyqtSignal, pyqtSlot from UM import i18nCatalog from UM.FileHandler.FileHandler import FileHandler @@ -12,18 +12,19 @@ from UM.Logger import Logger from UM.Message import Message from UM.Scene.SceneNode import SceneNode from cura.CuraApplication import CuraApplication -from cura.PrinterOutput.PrinterOutputController import PrinterOutputController -from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel from cura.PrinterOutput.NetworkedPrinterOutputDevice import AuthState, NetworkedPrinterOutputDevice +from cura.PrinterOutput.PrinterOutputController import PrinterOutputController from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel -from plugins.UM3NetworkPrinting.src.Cloud.CloudApiClient import CloudApiClient -from plugins.UM3NetworkPrinting.src.MeshFormatHandler import MeshFormatHandler -from plugins.UM3NetworkPrinting.src.UM3PrintJobOutputModel import UM3PrintJobOutputModel -from .Models import ( - CloudClusterPrinter, CloudClusterPrintJob, CloudJobUploadRequest, CloudJobResponse, CloudClusterStatus, - CloudClusterPrinterConfigurationMaterial, CloudErrorObject, - CloudPrintResponse -) +from ..MeshFormatHandler import MeshFormatHandler +from ..UM3PrintJobOutputModel import UM3PrintJobOutputModel +from .CloudApiClient import CloudApiClient +from .Models.CloudErrorObject import CloudErrorObject +from .Models.CloudClusterStatus import CloudClusterStatus +from .Models.CloudJobUploadRequest import CloudJobUploadRequest +from .Models.CloudPrintResponse import CloudPrintResponse +from .Models.CloudJobResponse import CloudJobResponse +from .Models.CloudClusterPrinter import CloudClusterPrinter +from .Models.CloudClusterPrintJob import CloudClusterPrintJob ## Class that contains all the translations for this module. @@ -180,80 +181,23 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): remote_printers = {p.uuid: p for p in printers} # type: Dict[str, CloudClusterPrinter] current_printers = {p.key: p for p in self._printers} # type: Dict[str, PrinterOutputModel] - removed_printer_ids = set(current_printers).difference(remote_printers) - new_printer_ids = set(remote_printers).difference(current_printers) - updated_printer_ids = set(current_printers).intersection(remote_printers) + remote_printer_ids = set(remote_printers) # type: Set[str] + current_printer_ids = set(current_printers) # type: Set[str] - for printer_guid in removed_printer_ids: - self._printers.remove(current_printers[printer_guid]) + for removed_printer_id in current_printer_ids.difference(remote_printer_ids): + removed_printer = current_printers[removed_printer_id] + self._printers.remove(removed_printer) - for printer_guid in new_printer_ids: - self._addPrinter(remote_printers[printer_guid]) + for new_printer_id in remote_printer_ids.difference(current_printer_ids): + new_printer = remote_printers[new_printer_id] + controller = PrinterOutputController(self) + self._printers.append(new_printer.createOutputModel(controller)) - for printer_guid in updated_printer_ids: - self._updatePrinter(current_printers[printer_guid], remote_printers[printer_guid]) + for updated_printer_guid in current_printer_ids.intersection(remote_printer_ids): + remote_printers[updated_printer_guid].updateOutputModel(current_printers[updated_printer_guid]) self.clusterPrintersChanged.emit() - def _addPrinter(self, printer: CloudClusterPrinter) -> None: - model = PrinterOutputModel( - PrinterOutputController(self), len(printer.configuration), firmware_version = printer.firmware_version - ) - self._printers.append(model) - self._updatePrinter(model, printer) - - def _updatePrinter(self, model: PrinterOutputModel, printer: CloudClusterPrinter) -> None: - model.updateKey(printer.uuid) - model.updateName(printer.friendly_name) - model.updateType(printer.machine_variant) - model.updateState(printer.status if printer.enabled else "disabled") - - for index in range(0, len(printer.configuration)): - try: - extruder = model.extruders[index] - extruder_data = printer.configuration[index] - except IndexError: - break - - extruder.updateHotendID(extruder_data.print_core_id) - - if extruder.activeMaterial is None or extruder.activeMaterial.guid != extruder_data.material.guid: - material = self._createMaterialOutputModel(extruder_data.material) - extruder.updateActiveMaterial(material) - - @staticmethod - def _createMaterialOutputModel(material: CloudClusterPrinterConfigurationMaterial) -> MaterialOutputModel: - material_manager = CuraApplication.getInstance().getMaterialManager() - material_group_list = material_manager.getMaterialGroupListByGUID(material.guid) or [] - - # Sort the material groups by "is_read_only = True" first, and then the name alphabetically. - read_only_material_group_list = list(filter(lambda x: x.is_read_only, material_group_list)) - non_read_only_material_group_list = list(filter(lambda x: not x.is_read_only, material_group_list)) - material_group = None - if read_only_material_group_list: - read_only_material_group_list = sorted(read_only_material_group_list, key = lambda x: x.name) - material_group = read_only_material_group_list[0] - elif non_read_only_material_group_list: - non_read_only_material_group_list = sorted(non_read_only_material_group_list, key = lambda x: x.name) - material_group = non_read_only_material_group_list[0] - - if material_group: - container = material_group.root_material_node.getContainer() - color = container.getMetaDataEntry("color_code") - brand = container.getMetaDataEntry("brand") - material_type = container.getMetaDataEntry("material") - name = container.getName() - else: - Logger.log("w", "Unable to find material with guid {guid}. Using data as provided by cluster" - .format(guid = material.guid)) - color = material.color - brand = material.brand - material_type = material.material - name = "Empty" if material.material == "empty" else "Unknown" - - return MaterialOutputModel(guid = material.guid, type = material_type, brand = brand, color = color, - name = name) - def _updatePrintJobs(self, jobs: List[CloudClusterPrintJob]) -> None: remote_jobs = {j.uuid: j for j in jobs} # type: Dict[str, CloudClusterPrintJob] current_jobs = {j.key: j for j in self._print_jobs} # type: Dict[str, UM3PrintJobOutputModel] @@ -264,11 +208,11 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): for removed_job_id in current_job_ids.difference(remote_job_ids): self._print_jobs.remove(current_jobs[removed_job_id]) - for new_job_id in remote_job_ids.difference(current_jobs): + for new_job_id in remote_job_ids.difference(current_job_ids): self._addPrintJob(remote_jobs[new_job_id]) for updated_job_id in current_job_ids.intersection(remote_job_ids): - self._updateUM3PrintJobOutputModel(current_jobs[updated_job_id], remote_jobs[updated_job_id]) + remote_jobs[updated_job_id].updateOutputModel(current_jobs[updated_job_id]) # We only have to update when jobs are added or removed # updated jobs push their changes via their output model @@ -282,16 +226,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): return Logger.log("w", "Missing printer %s for job %s in %s", job.printer_uuid, job.uuid, [p.key for p in self._printers]) - model = UM3PrintJobOutputModel(printer.getController(), job.uuid, job.name) - model.updateAssignedPrinter(printer) - self._print_jobs.append(model) - - @staticmethod - def _updateUM3PrintJobOutputModel(model: UM3PrintJobOutputModel, job: CloudClusterPrintJob) -> None: - model.updateTimeTotal(job.time_total) - model.updateTimeElapsed(job.time_elapsed) - model.updateOwner(job.owner) - model.updateState(job.status) + self._print_jobs.append(job.createOutputModel(printer)) def _onPrintJobCreated(self, mesh: bytes, job_response: CloudJobResponse) -> None: self._api.uploadMesh(job_response, mesh, self._onPrintJobUploaded, self._updateUploadProgress, diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 0fbeeb82b6..51c7f49074 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -8,9 +8,10 @@ from UM import i18nCatalog from UM.Logger import Logger from UM.Message import Message from cura.CuraApplication import CuraApplication -from plugins.UM3NetworkPrinting.src.Cloud.CloudApiClient import CloudApiClient +from .CloudApiClient import CloudApiClient from .CloudOutputDevice import CloudOutputDevice -from .Models import CloudCluster, CloudErrorObject +from .Models.CloudCluster import CloudCluster +from .Models.CloudErrorObject import CloudErrorObject ## The cloud output device manager is responsible for using the Ultimaker Cloud APIs to manage remote clusters. diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models.py b/plugins/UM3NetworkPrinting/src/Cloud/Models.py deleted file mode 100644 index 27ff7df604..0000000000 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models.py +++ /dev/null @@ -1,153 +0,0 @@ -# Copyright (c) 2018 Ultimaker B.V. -# Cura is released under the terms of the LGPLv3 or higher. -from typing import List, Dict - -from ..Models import BaseModel - - -## Class representing errors generated by the cloud servers, according to the json-api standard. -class CloudErrorObject(BaseModel): - def __init__(self, **kwargs): - self.id = None # type: str - self.code = None # type: str - self.http_status = None # type: str - self.title = None # type: str - self.detail = None # type: str - self.meta = None # type: Dict[str, any] - super().__init__(**kwargs) - - -## Class representing a cloud connected cluster. -class CloudCluster(BaseModel): - def __init__(self, **kwargs): - self.cluster_id = None # type: str - self.host_guid = None # type: str - self.host_name = None # type: str - self.host_version = None # type: str - self.status = None # type: str - self.is_online = None # type: bool - super().__init__(**kwargs) - - def validate(self): - if not self.cluster_id: - raise ValueError("cluster_id is required on CloudCluster") - - -## Class representing a cloud cluster printer configuration -class CloudClusterPrinterConfigurationMaterial(BaseModel): - def __init__(self, **kwargs): - self.guid = None # type: str - self.brand = None # type: str - self.color = None # type: str - self.material = None # type: str - super().__init__(**kwargs) - - -## Class representing a cloud cluster printer configuration -class CloudClusterPrinterConfiguration(BaseModel): - def __init__(self, **kwargs): - self.extruder_index = None # type: str - self.material = None # type: CloudClusterPrinterConfigurationMaterial - self.nozzle_diameter = None # type: str - self.print_core_id = None # type: str - super().__init__(**kwargs) - - if isinstance(self.material, dict): - self.material = CloudClusterPrinterConfigurationMaterial(**self.material) - - -## Class representing a cluster printer -class CloudClusterPrinter(BaseModel): - def __init__(self, **kwargs): - self.configuration = [] # type: List[CloudClusterPrinterConfiguration] - self.enabled = None # type: str - self.firmware_version = None # type: str - self.friendly_name = None # type: str - self.ip_address = None # type: str - self.machine_variant = None # type: str - self.status = None # type: str - self.unique_name = None # type: str - self.uuid = None # type: str - super().__init__(**kwargs) - - self.configuration = [CloudClusterPrinterConfiguration(**c) - if isinstance(c, dict) else c for c in self.configuration] - - -## Class representing a cloud cluster print job constraint -class CloudClusterPrintJobConstraint(BaseModel): - def __init__(self, **kwargs): - self.require_printer_name = None # type: str - super().__init__(**kwargs) - - -## Class representing a print job -class CloudClusterPrintJob(BaseModel): - def __init__(self, **kwargs): - self.assigned_to = None # type: str - self.configuration = [] # type: List[CloudClusterPrinterConfiguration] - self.constraints = [] # type: List[CloudClusterPrintJobConstraint] - self.created_at = None # type: str - self.force = None # type: str - self.last_seen = None # type: str - self.machine_variant = None # type: str - self.name = None # type: str - self.network_error_count = None # type: int - self.owner = None # type: str - self.printer_uuid = None # type: str - self.started = None # type: str - self.status = None # type: str - self.time_elapsed = None # type: str - self.time_total = None # type: str - self.uuid = None # type: str - super().__init__(**kwargs) - self.printers = [CloudClusterPrinterConfiguration(**c) if isinstance(c, dict) else c - for c in self.configuration] - self.printers = [CloudClusterPrintJobConstraint(**p) if isinstance(p, dict) else p - for p in self.constraints] - - -# Model that represents the status of the cluster for the cloud -class CloudClusterStatus(BaseModel): - def __init__(self, **kwargs): - # a list of the printers - self.printers = [] # type: List[CloudClusterPrinter] - # a list of the print jobs - self.print_jobs = [] # type: List[CloudClusterPrintJob] - - super().__init__(**kwargs) - - # converting any dictionaries into models - self.printers = [CloudClusterPrinter(**p) if isinstance(p, dict) else p for p in self.printers] - self.print_jobs = [CloudClusterPrintJob(**j) if isinstance(j, dict) else j for j in self.print_jobs] - - -# Model that represents the request to upload a print job to the cloud -class CloudJobUploadRequest(BaseModel): - def __init__(self, **kwargs): - self.file_size = None # type: int - self.job_name = None # type: str - self.content_type = None # type: str - super().__init__(**kwargs) - - -# Model that represents the response received from the cloud after requesting to upload a print job -class CloudJobResponse(BaseModel): - def __init__(self, **kwargs): - self.download_url = None # type: str - self.job_id = None # type: str - self.job_name = None # type: str - self.slicing_details = None # type: str - self.status = None # type: str - self.upload_url = None # type: str - self.content_type = None # type: str - super().__init__(**kwargs) - - -# Model that represents the responses received from the cloud after requesting a job to be printed. -class CloudPrintResponse(BaseModel): - def __init__(self, **kwargs): - self.cluster_job_id = None # type: str - self.job_id = None # type: str - self.status = None # type: str - super().__init__(**kwargs) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudCluster.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudCluster.py new file mode 100644 index 0000000000..dd1e2e85bf --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudCluster.py @@ -0,0 +1,21 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from ...Models import BaseModel + + +## Class representing a cloud connected cluster. +class CloudCluster(BaseModel): + def __init__(self, **kwargs): + self.cluster_id = None # type: str + self.host_guid = None # type: str + self.host_name = None # type: str + self.host_version = None # type: str + self.status = None # type: str + self.is_online = None # type: bool + super().__init__(**kwargs) + + # Validates the model, raising an exception if the model is invalid. + def validate(self) -> None: + super().validate() + if not self.cluster_id: + raise ValueError("cluster_id is required on CloudCluster") diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py new file mode 100644 index 0000000000..e2e3787435 --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py @@ -0,0 +1,52 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from typing import List + +from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel +from .CloudClusterPrinterConfiguration import CloudClusterPrinterConfiguration +from .CloudClusterPrintJobConstraint import CloudClusterPrintJobConstraint +from ...Models import BaseModel + + +## Class representing a print job +from plugins.UM3NetworkPrinting.src.UM3PrintJobOutputModel import UM3PrintJobOutputModel + + +class CloudClusterPrintJob(BaseModel): + def __init__(self, **kwargs) -> None: + self.assigned_to = None # type: str + self.configuration = [] # type: List[CloudClusterPrinterConfiguration] + self.constraints = [] # type: List[CloudClusterPrintJobConstraint] + self.created_at = None # type: str + self.force = None # type: str + self.last_seen = None # type: str + self.machine_variant = None # type: str + self.name = None # type: str + self.network_error_count = None # type: int + self.owner = None # type: str + self.printer_uuid = None # type: str + self.started = None # type: str + self.status = None # type: str + self.time_elapsed = None # type: str + self.time_total = None # type: str + self.uuid = None # type: str + super().__init__(**kwargs) + self.printers = [CloudClusterPrinterConfiguration(**c) if isinstance(c, dict) else c + for c in self.configuration] + self.printers = [CloudClusterPrintJobConstraint(**p) if isinstance(p, dict) else p + for p in self.constraints] + + ## Creates an UM3 print job output model based on this cloud cluster print job. + # \param printer: The output model of the printer + def createOutputModel(self, printer: PrinterOutputModel) -> UM3PrintJobOutputModel: + model = UM3PrintJobOutputModel(printer.getController(), self.uuid, self.name) + model.updateAssignedPrinter(printer) + return model + + ## Updates an UM3 print job output model based on this cloud cluster print job. + # \param model: The model to update. + def updateOutputModel(self, model: UM3PrintJobOutputModel) -> None: + model.updateTimeTotal(self.time_total) + model.updateTimeElapsed(self.time_elapsed) + model.updateOwner(self.owner) + model.updateState(self.status) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobConstraint.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobConstraint.py new file mode 100644 index 0000000000..884ff8f0c2 --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobConstraint.py @@ -0,0 +1,10 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from ...Models import BaseModel + + +## Class representing a cloud cluster print job constraint +class CloudClusterPrintJobConstraint(BaseModel): + def __init__(self, **kwargs) -> None: + self.require_printer_name = None # type: str + super().__init__(**kwargs) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinter.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinter.py new file mode 100644 index 0000000000..dd65dffa26 --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinter.py @@ -0,0 +1,44 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from typing import List + +from cura.PrinterOutput.PrinterOutputController import PrinterOutputController +from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel +from .CloudClusterPrinterConfiguration import CloudClusterPrinterConfiguration +from ...Models import BaseModel + + +## Class representing a cluster printer +class CloudClusterPrinter(BaseModel): + def __init__(self, **kwargs) -> None: + self.configuration = [] # type: List[CloudClusterPrinterConfiguration] + self.enabled = None # type: str + self.firmware_version = None # type: str + self.friendly_name = None # type: str + self.ip_address = None # type: str + self.machine_variant = None # type: str + self.status = None # type: str + self.unique_name = None # type: str + self.uuid = None # type: str + super().__init__(**kwargs) + + self.configuration = [CloudClusterPrinterConfiguration(**c) + if isinstance(c, dict) else c for c in self.configuration] + + ## Creates a new output model. + # \param controller - The controller of the model. + def createOutputModel(self, controller: PrinterOutputController) -> PrinterOutputModel: + model = PrinterOutputModel(controller, len(self.configuration), firmware_version = self.firmware_version) + self.updateOutputModel(model) + return model + + ## Updates the given output model. + # \param model - The output model to update. + def updateOutputModel(self, model: PrinterOutputModel) -> None: + model.updateKey(self.uuid) + model.updateName(self.friendly_name) + model.updateType(self.machine_variant) + model.updateState(self.status if self.enabled else "disabled") + + for configuration, extruder in zip(self.configuration, model.extruders): + configuration.updateOutputModel(extruder) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfiguration.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfiguration.py new file mode 100644 index 0000000000..be92549015 --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfiguration.py @@ -0,0 +1,27 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from cura.PrinterOutput.ExtruderOutputModel import ExtruderOutputModel +from .CloudClusterPrinterConfigurationMaterial import CloudClusterPrinterConfigurationMaterial +from ...Models import BaseModel + + +## Class representing a cloud cluster printer configuration +class CloudClusterPrinterConfiguration(BaseModel): + def __init__(self, **kwargs) -> None: + self.extruder_index = None # type: str + self.material = None # type: CloudClusterPrinterConfigurationMaterial + self.nozzle_diameter = None # type: str + self.print_core_id = None # type: str + super().__init__(**kwargs) + + if isinstance(self.material, dict): + self.material = CloudClusterPrinterConfigurationMaterial(**self.material) + + ## Updates the given output model. + # \param model - The output model to update. + def updateOutputModel(self, model: ExtruderOutputModel) -> None: + model.updateHotendID(self.print_core_id) + + if model.activeMaterial is None or model.activeMaterial.guid != self.material.guid: + material = self.material.createOutputModel() + model.updateActiveMaterial(material) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfigurationMaterial.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfigurationMaterial.py new file mode 100644 index 0000000000..8023784925 --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfigurationMaterial.py @@ -0,0 +1,46 @@ +from UM.Logger import Logger +from cura.CuraApplication import CuraApplication +from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel +from ...Models import BaseModel + + +## Class representing a cloud cluster printer configuration +class CloudClusterPrinterConfigurationMaterial(BaseModel): + def __init__(self, **kwargs) -> None: + self.guid = None # type: str + self.brand = None # type: str + self.color = None # type: str + self.material = None # type: str + super().__init__(**kwargs) + + ## Creates a material output model based on this cloud printer material. + def createOutputModel(self) -> MaterialOutputModel: + material_manager = CuraApplication.getInstance().getMaterialManager() + material_group_list = material_manager.getMaterialGroupListByGUID(self.guid) or [] + + # Sort the material groups by "is_read_only = True" first, and then the name alphabetically. + read_only_material_group_list = list(filter(lambda x: x.is_read_only, material_group_list)) + non_read_only_material_group_list = list(filter(lambda x: not x.is_read_only, material_group_list)) + material_group = None + if read_only_material_group_list: + read_only_material_group_list = sorted(read_only_material_group_list, key = lambda x: x.name) + material_group = read_only_material_group_list[0] + elif non_read_only_material_group_list: + non_read_only_material_group_list = sorted(non_read_only_material_group_list, key = lambda x: x.name) + material_group = non_read_only_material_group_list[0] + + if material_group: + container = material_group.root_material_node.getContainer() + color = container.getMetaDataEntry("color_code") + brand = container.getMetaDataEntry("brand") + material_type = container.getMetaDataEntry("material") + name = container.getName() + else: + Logger.log("w", "Unable to find material with guid {guid}. Using data as provided by cluster" + .format(guid = self.guid)) + color = self.color + brand = self.brand + material_type = self.material + name = "Empty" if self.material == "empty" else "Unknown" + + return MaterialOutputModel(guid = self.guid, type = material_type, brand = brand, color = color, name = name) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterStatus.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterStatus.py new file mode 100644 index 0000000000..a44b665973 --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterStatus.py @@ -0,0 +1,22 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from typing import List + +from .CloudClusterPrinter import CloudClusterPrinter +from .CloudClusterPrintJob import CloudClusterPrintJob +from ...Models import BaseModel + + +# Model that represents the status of the cluster for the cloud +class CloudClusterStatus(BaseModel): + def __init__(self, **kwargs) -> None: + # a list of the printers + self.printers = [] # type: List[CloudClusterPrinter] + # a list of the print jobs + self.print_jobs = [] # type: List[CloudClusterPrintJob] + + super().__init__(**kwargs) + + # converting any dictionaries into models + self.printers = [CloudClusterPrinter(**p) if isinstance(p, dict) else p for p in self.printers] + self.print_jobs = [CloudClusterPrintJob(**j) if isinstance(j, dict) else j for j in self.print_jobs] diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudErrorObject.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudErrorObject.py new file mode 100644 index 0000000000..813ef957e4 --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudErrorObject.py @@ -0,0 +1,17 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from typing import Dict + +from ...Models import BaseModel + + +## Class representing errors generated by the cloud servers, according to the json-api standard. +class CloudErrorObject(BaseModel): + def __init__(self, **kwargs) -> None: + self.id = None # type: str + self.code = None # type: str + self.http_status = None # type: str + self.title = None # type: str + self.detail = None # type: str + self.meta = None # type: Dict[str, any] + super().__init__(**kwargs) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudJobResponse.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudJobResponse.py new file mode 100644 index 0000000000..0b611dd2d3 --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudJobResponse.py @@ -0,0 +1,16 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from ...Models import BaseModel + + +# Model that represents the response received from the cloud after requesting to upload a print job +class CloudJobResponse(BaseModel): + def __init__(self, **kwargs) -> None: + self.download_url = None # type: str + self.job_id = None # type: str + self.job_name = None # type: str + self.slicing_details = None # type: str + self.status = None # type: str + self.upload_url = None # type: str + self.content_type = None # type: str + super().__init__(**kwargs) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudJobUploadRequest.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudJobUploadRequest.py new file mode 100644 index 0000000000..3e038b343e --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudJobUploadRequest.py @@ -0,0 +1,12 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from ...Models import BaseModel + + +# Model that represents the request to upload a print job to the cloud +class CloudJobUploadRequest(BaseModel): + def __init__(self, **kwargs) -> None: + self.file_size = None # type: int + self.job_name = None # type: str + self.content_type = None # type: str + super().__init__(**kwargs) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudPrintResponse.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudPrintResponse.py new file mode 100644 index 0000000000..ff05ad5b19 --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudPrintResponse.py @@ -0,0 +1,12 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from ...Models import BaseModel + + +# Model that represents the responses received from the cloud after requesting a job to be printed. +class CloudPrintResponse(BaseModel): + def __init__(self, **kwargs) -> None: + self.cluster_job_id = None # type: str + self.job_id = None # type: str + self.status = None # type: str + super().__init__(**kwargs) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/__init__.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/__init__.py new file mode 100644 index 0000000000..f3f6970c54 --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index 64ac613723..aa8be9ecd9 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -25,11 +25,11 @@ from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationM from cura.PrinterOutput.NetworkedPrinterOutputDevice import AuthState, NetworkedPrinterOutputDevice from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel -from plugins.UM3NetworkPrinting.src.MeshFormatHandler import MeshFormatHandler from .ClusterUM3PrinterOutputController import ClusterUM3PrinterOutputController -from .SendMaterialJob import SendMaterialJob from .ConfigurationChangeModel import ConfigurationChangeModel +from .MeshFormatHandler import MeshFormatHandler +from .SendMaterialJob import SendMaterialJob from .UM3PrintJobOutputModel import UM3PrintJobOutputModel from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply diff --git a/plugins/UM3NetworkPrinting/src/Models.py b/plugins/UM3NetworkPrinting/src/Models.py index 2bcac70766..c5b9b16665 100644 --- a/plugins/UM3NetworkPrinting/src/Models.py +++ b/plugins/UM3NetworkPrinting/src/Models.py @@ -8,6 +8,7 @@ class BaseModel: self.__dict__.update(kwargs) self.validate() + # Validates the model, raising an exception if the model is invalid. def validate(self) -> None: pass @@ -34,7 +35,9 @@ class LocalMaterial(BaseModel): self.version = version # type: int super().__init__(**kwargs) + # def validate(self) -> None: + super().validate() if not self.GUID: raise ValueError("guid is required on LocalMaterial") if not self.version: diff --git a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py index b669eb192a..e3ec9faeaf 100644 --- a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py @@ -10,7 +10,7 @@ from PyQt5.QtCore import QByteArray from UM.MimeTypeDatabase import MimeType from UM.Application import Application -from plugins.UM3NetworkPrinting.src.SendMaterialJob import SendMaterialJob +from ..src.SendMaterialJob import SendMaterialJob @patch("builtins.open", lambda _, __: io.StringIO("")) From d6630b68815781ba92edae3510aaf62cf77b4d0d Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 5 Dec 2018 14:09:55 +0100 Subject: [PATCH 0671/1240] Removed some more cases where data was duplicated and re-used for different purposes CURA-6006 --- plugins/Toolbox/src/Toolbox.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index ada81dbc07..f70543d5d7 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -69,16 +69,14 @@ class Toolbox(QObject, Extension): # The responses as given by the server parsed to a list. self._server_response_data = { "authors": [], - "packages": [], - "plugins_installed": [], - "materials_installed": [] + "packages": [] } # type: Dict[str, List[Any]] # Models: self._models = { "authors": AuthorsModel(self), "packages": PackagesModel(self), - } # type: Dict[str, ListModel] + } # type: Dict[str, Union[AuthorsModel, PackagesModel]] self._plugins_showcase_model = PackagesModel(self) self._plugins_available_model = PackagesModel(self) @@ -305,11 +303,9 @@ class Toolbox(QObject, Extension): if plugin_id not in all_plugin_package_ids) self._old_plugin_metadata = {k: v for k, v in self._old_plugin_metadata.items() if k in self._old_plugin_ids} - self._server_response_data["plugins_installed"] = all_packages["plugin"] + list(self._old_plugin_metadata.values()) - self._plugins_installed_model.setMetadata(self._server_response_data["plugins_installed"]) + self._plugins_installed_model.setMetadata(all_packages["plugin"] + list(self._old_plugin_metadata.values())) self.metadataChanged.emit() if "material" in all_packages: - self._server_response_data["materials_installed"] = all_packages["material"] self._materials_installed_model.setMetadata(all_packages["material"]) self.metadataChanged.emit() @@ -527,8 +523,8 @@ class Toolbox(QObject, Extension): @pyqtSlot(str, result = int) def getNumberOfInstalledPackagesByAuthor(self, author_id: str) -> int: count = 0 - for package in self._server_response_data["materials_installed"]: - if package["author"]["author_id"] == author_id: + for package in self._materials_installed_model.items: + if package["author_id"] == author_id: count += 1 return count From 5ba8820f185673f0486393187eafca826c657537 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 5 Dec 2018 14:10:51 +0100 Subject: [PATCH 0672/1240] Remove unnecessary setting height to 0 when invisible Turns out that it doesn't count for the childrenRect.height anyway when the item is invisible. Contributes to issue CURA-5876. --- resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml | 2 +- resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml index 58b6bac089..a09d6d2ba4 100644 --- a/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml @@ -10,7 +10,7 @@ import Cura 1.0 as Cura Item { width: parent.width - height: visible ? childrenRect.height : 0 + height: childrenRect.height Label { diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index 18c2dabb0f..19b7158929 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -17,7 +17,7 @@ Item } width: parent.width - height: visible ? childrenRect.height : 0 + height: childrenRect.height Label { From 218fa3aded7ed4e4fb77bd092ca7e798ca948dae Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 5 Dec 2018 14:22:04 +0100 Subject: [PATCH 0673/1240] Align the print info to the left --- resources/qml/Cura.qml | 26 ++++---- resources/qml/JobSpecs.qml | 126 +++++++++++++++++-------------------- 2 files changed, 71 insertions(+), 81 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 3578888886..6559fafa12 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -189,16 +189,6 @@ UM.MainWindow onHideTooltip: base.hideTooltip() } - JobSpecs - { - id: jobSpecs - anchors - { - bottom: parent.bottom - bottomMargin: UM.Theme.getSize("default_margin").height - } - } - Toolbar { // The toolbar is the left bar that is populated by all the tools (which are dynamicly populated by @@ -228,6 +218,19 @@ UM.MainWindow } } + JobSpecs + { + id: jobSpecs + visible: CuraApplication.platformActivity + anchors + { + left: parent.left + bottom: viewOrientationControls.top + margins: UM.Theme.getSize("wide_margin").width + bottomMargin: UM.Theme.getSize("default_margin").width + } + } + ViewOrientationControls { id: viewOrientationControls @@ -235,9 +238,8 @@ UM.MainWindow anchors { left: parent.left - margins: UM.Theme.getSize("default_margin").width - bottom: parent.bottom + margins: UM.Theme.getSize("wide_margin").width } } diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index 19b5b3f2de..97e12e15a3 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -19,9 +19,10 @@ Item UM.I18nCatalog { id: catalog - name:"cura" + name: "cura" } + width: childrenRect.width height: childrenRect.height onActivityChanged: @@ -33,82 +34,75 @@ Item } } - Rectangle + Item { id: jobNameRow anchors.top: parent.top - anchors.right: parent.right + anchors.left: parent.left height: UM.Theme.getSize("jobspecs_line").height - visible: base.activity - Item + Button { - width: parent.width - height: parent.height + id: printJobPencilIcon + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + width: UM.Theme.getSize("save_button_specs_icons").width + height: UM.Theme.getSize("save_button_specs_icons").height - Button + onClicked: { - id: printJobPencilIcon - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - width: UM.Theme.getSize("save_button_specs_icons").width - height: UM.Theme.getSize("save_button_specs_icons").height + printJobTextfield.selectAll() + printJobTextfield.focus = true + } - onClicked: + style: ButtonStyle + { + background: Item { - printJobTextfield.selectAll() - printJobTextfield.focus = true - } - - style: ButtonStyle - { - background: Item + UM.RecolorImage { - UM.RecolorImage - { - width: UM.Theme.getSize("save_button_specs_icons").width - height: UM.Theme.getSize("save_button_specs_icons").height - sourceSize.width: width - sourceSize.height: width - color: control.hovered ? UM.Theme.getColor("text_scene_hover") : UM.Theme.getColor("text_scene") - source: UM.Theme.getIcon("pencil") - } + width: UM.Theme.getSize("save_button_specs_icons").width + height: UM.Theme.getSize("save_button_specs_icons").height + sourceSize.width: width + sourceSize.height: width + color: control.hovered ? UM.Theme.getColor("text_scene_hover") : UM.Theme.getColor("text_scene") + source: UM.Theme.getIcon("pencil") } } } + } - TextField + TextField + { + id: printJobTextfield + anchors.left: printJobPencilIcon.right + anchors.leftMargin: UM.Theme.getSize("narrow_margin").width + height: UM.Theme.getSize("jobspecs_line").height + width: Math.max(__contentWidth + UM.Theme.getSize("default_margin").width, 50) + maximumLength: 120 + property int unremovableSpacing: 5 + text: PrintInformation.jobName + horizontalAlignment: TextInput.AlignLeft + + onEditingFinished: { - id: printJobTextfield - anchors.right: printJobPencilIcon.left - anchors.rightMargin: UM.Theme.getSize("narrow_margin").width - height: UM.Theme.getSize("jobspecs_line").height - width: Math.max(__contentWidth + UM.Theme.getSize("default_margin").width, 50) - maximumLength: 120 - property int unremovableSpacing: 5 - text: PrintInformation.jobName - horizontalAlignment: TextInput.AlignRight + var new_name = text == "" ? catalog.i18nc("@text Print job name", "Untitled") : text + PrintInformation.setJobName(new_name, true) + printJobTextfield.focus = false + } - onEditingFinished: + validator: RegExpValidator { + regExp: /^[^\\\/\*\?\|\[\]]*$/ + } + + style: TextFieldStyle + { + textColor: UM.Theme.getColor("text_scene") + font: UM.Theme.getFont("default_bold") + background: Rectangle { - var new_name = text == "" ? catalog.i18nc("@text Print job name", "Untitled") : text - PrintInformation.setJobName(new_name, true) - printJobTextfield.focus = false - } - - validator: RegExpValidator { - regExp: /^[^\\\/\*\?\|\[\]]*$/ - } - - style: TextFieldStyle - { - textColor: UM.Theme.getColor("text_scene") - font: UM.Theme.getFont("default_bold") - background: Rectangle - { - opacity: 0 - border.width: 0 - } + opacity: 0 + border.width: 0 } } } @@ -118,22 +112,16 @@ Item { id: additionalComponentsRow anchors.top: jobNameRow.bottom - anchors.right: parent.right + anchors.left: parent.left } Label { id: boundingSpec anchors.top: jobNameRow.bottom - anchors.right: additionalComponentsRow.left - anchors.rightMargin: - { - if (additionalComponentsRow.width > 0) - { - return UM.Theme.getSize("default_margin").width - } - return 0 - } + anchors.left: additionalComponentsRow.right + anchors.leftMargin: additionalComponentsRow.width > 0 ? UM.Theme.getSize("default_margin").width : 0 + height: UM.Theme.getSize("jobspecs_line").height verticalAlignment: Text.AlignVCenter font: UM.Theme.getFont("default_bold") From 00e95e68eb974e19ac3f6960b86965dd9ada8fbf Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 5 Dec 2018 14:26:47 +0100 Subject: [PATCH 0674/1240] Removed unneeded Marketplace tag in logging The logger does that all by itself already. CURA-6006 --- plugins/Toolbox/src/Toolbox.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index f70543d5d7..cd20d26eca 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -781,7 +781,7 @@ class Toolbox(QObject, Extension): @pyqtSlot(str, str, str) def filterModelByProp(self, model_type: str, filter_type: str, parameter: str) -> None: if not self._models[model_type]: - Logger.log("w", "Marketplace: Couldn't filter %s model because it doesn't exist.", model_type) + Logger.log("w", "Couldn't filter %s model because it doesn't exist.", model_type) return self._models[model_type].setFilter({filter_type: parameter}) self.filterChanged.emit() @@ -789,7 +789,7 @@ class Toolbox(QObject, Extension): @pyqtSlot(str, "QVariantMap") def setFilters(self, model_type: str, filter_dict: dict) -> None: if not self._models[model_type]: - Logger.log("w", "Marketplace: Couldn't filter %s model because it doesn't exist.", model_type) + Logger.log("w", "Couldn't filter %s model because it doesn't exist.", model_type) return self._models[model_type].setFilter(filter_dict) self.filterChanged.emit() @@ -797,7 +797,7 @@ class Toolbox(QObject, Extension): @pyqtSlot(str) def removeFilters(self, model_type: str) -> None: if not self._models[model_type]: - Logger.log("w", "Marketplace: Couldn't remove filters on %s model because it doesn't exist.", model_type) + Logger.log("w", "Couldn't remove filters on %s model because it doesn't exist.", model_type) return self._models[model_type].setFilter({}) self.filterChanged.emit() From cd67100097fe092bfc8fc96e4b8941c0f90b3e7d Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 5 Dec 2018 14:19:57 +0100 Subject: [PATCH 0675/1240] Comment out some currently broken code --- .../src/Cloud/CloudOutputDeviceManager.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 936ef03ddc..3ddd865c5f 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -79,7 +79,7 @@ class CloudOutputDeviceManager: self._removeCloudOutputDevice(found_clusters[cluster_id]) # TODO: not pass clusters that are not online? - self._connectToActiveMachine(clusters) + self._connectToActiveMachine() ## Adds a CloudOutputDevice for each entry in the remote cluster list from the API. # \param cluster: The cluster that was added. @@ -96,7 +96,7 @@ class CloudOutputDeviceManager: del self._remote_clusters[cluster.cluster_id] ## Callback for when the active machine was changed by the user or a new remote cluster was found. - def _connectToActiveMachine(self, clusters: List[CloudCluster]) -> None: + def _connectToActiveMachine(self) -> None: active_machine = CuraApplication.getInstance().getGlobalContainerStack() if not active_machine: return @@ -115,9 +115,10 @@ class CloudOutputDeviceManager: if not local_network_key: return - cluster_id = next(local_network_key in cluster.host_name for cluster in clusters) - if cluster_id in self._remote_clusters.keys(): - return self._remote_clusters.get(cluster_id).connect() + # TODO: get host_name in the output device so we can iterate here + # cluster_id = next(local_network_key in cluster.host_name for cluster in self._remote_clusters.items()) + # if cluster_id in self._remote_clusters.keys(): + # return self._remote_clusters.get(cluster_id).connect() ## Handles an API error received from the cloud. # \param errors: The errors received From 7e769137368af1b6d19416289040e5b4c953ce4e Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Wed, 5 Dec 2018 16:02:38 +0100 Subject: [PATCH 0676/1240] STAR-322: Fixing printer matching by network key --- .../src/Cloud/CloudOutputDevice.py | 43 ++++++--- .../src/Cloud/CloudOutputDeviceManager.py | 91 +++++++++---------- .../src/Cloud/Models/CloudCluster.py | 2 +- plugins/UM3NetworkPrinting/src/Cloud/Utils.py | 19 ++++ .../src/ClusterUM3OutputDevice.py | 6 +- 5 files changed, 97 insertions(+), 64 deletions(-) create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Utils.py diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index c6397fc41f..e8f7687b03 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -18,7 +18,6 @@ from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from ..MeshFormatHandler import MeshFormatHandler from ..UM3PrintJobOutputModel import UM3PrintJobOutputModel from .CloudApiClient import CloudApiClient -from .Models.CloudErrorObject import CloudErrorObject from .Models.CloudClusterStatus import CloudClusterStatus from .Models.CloudJobUploadRequest import CloudJobUploadRequest from .Models.CloudPrintResponse import CloudPrintResponse @@ -63,19 +62,21 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # The interval with which the remote clusters are checked CHECK_CLUSTER_INTERVAL = 2.0 # seconds - # Signal triggered when the printers in the remote cluster were changed. - clusterPrintersChanged = pyqtSignal() - # Signal triggered when the print jobs in the queue were changed. printJobsChanged = pyqtSignal() + # Notify can only use signals that are defined by the class that they are in, not inherited ones. + # Therefore we create a private signal used to trigger the printersChanged signal. + _clusterPrintersChanged = pyqtSignal() + ## Creates a new cloud output device # \param api_client: The client that will run the API calls # \param device_id: The ID of the device (i.e. the cluster_id for the cloud API) # \param parent: The optional parent of this output device. - def __init__(self, api_client: CloudApiClient, device_id: str, parent: QObject = None) -> None: + def __init__(self, api_client: CloudApiClient, device_id: str, host_name: str, parent: QObject = None) -> None: super().__init__(device_id = device_id, address = "", properties = {}, parent = parent) self._api = api_client + self._host_name = host_name self._setInterfaceElements() @@ -87,7 +88,10 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): "../../resources/qml/ClusterMonitorItem.qml") self._control_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../resources/qml/ClusterControlItem.qml") - + + # trigger the printersChanged signal when the private signal is triggered + self.printersChanged.connect(self._clusterPrintersChanged) + # Properties to populate later on with received cloud data. self._print_jobs = [] # type: List[UM3PrintJobOutputModel] self._number_of_extruders = 2 # All networked printers are dual-extrusion Ultimaker machines. @@ -96,6 +100,22 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self._sending_job = False self._progress_message = None # type: Optional[Message] + ## Gets the host name of this device + @property + def host_name(self) -> str: + return self._host_name + + ## Updates the host name of the output device + @host_name.setter + def host_name(self, value: str) -> None: + self._host_name = value + + ## Checks whether the given network key is found in the cloud's host name + def matchesNetworkKey(self, network_key: str) -> bool: + # A network key looks like "ultimakersystem-aabbccdd0011._ultimaker._tcp.local." + # the host name should then be "ultimakersystem-aabbccdd0011" + return network_key.startswith(self._host_name) + ## Set all the interface elements and texts for this output device. def _setInterfaceElements(self): self.setPriority(2) # make sure we end up below the local networking and above 'save to file' @@ -133,7 +153,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self._api.requestUpload(request, lambda response: self._onPrintJobCreated(mesh_bytes, response)) ## Get remote printers. - @pyqtProperty("QVariantList", notify = clusterPrintersChanged) + @pyqtProperty("QVariantList", notify = _clusterPrintersChanged) def printers(self): return self._printers @@ -196,7 +216,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): for updated_printer_guid in current_printer_ids.intersection(remote_printer_ids): remote_printers[updated_printer_guid].updateOutputModel(current_printers[updated_printer_guid]) - self.clusterPrintersChanged.emit() + self._clusterPrintersChanged.emit() def _updatePrintJobs(self, jobs: List[CloudClusterPrintJob]) -> None: remote_jobs = {j.uuid: j for j in jobs} # type: Dict[str, CloudClusterPrintJob] @@ -283,7 +303,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## TODO: The following methods are required by the monitor page QML, but are not actually available using cloud. # TODO: We fake the methods here to not break the monitor page. - @pyqtProperty(QObject, notify = clusterPrintersChanged) + @pyqtProperty(QObject, notify = _clusterPrintersChanged) def activePrinter(self) -> Optional[PrinterOutputModel]: if not self._printers: return None @@ -293,7 +313,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def setActivePrinter(self, printer: Optional[PrinterOutputModel]) -> None: pass - @pyqtProperty(QUrl, notify = clusterPrintersChanged) + @pyqtProperty(QUrl, notify = _clusterPrintersChanged) def activeCameraUrl(self) -> "QUrl": return QUrl() @@ -304,6 +324,3 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): @pyqtProperty(bool, notify = printJobsChanged) def receivedPrintJobs(self) -> bool: return True - - def _onApiError(self, errors: List[CloudErrorObject]) -> None: - pass # TODO: Show errors... diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 3ddd865c5f..f11d41a7bd 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -1,6 +1,6 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import Dict, List, Optional +from typing import Dict, List from PyQt5.QtCore import QTimer @@ -8,10 +8,12 @@ from UM import i18nCatalog from UM.Logger import Logger from UM.Message import Message from cura.CuraApplication import CuraApplication +from cura.Settings.GlobalStack import GlobalStack from .CloudApiClient import CloudApiClient from .CloudOutputDevice import CloudOutputDevice from .Models.CloudCluster import CloudCluster from .Models.CloudErrorObject import CloudErrorObject +from .Utils import findChanges ## The cloud output device manager is responsible for using the Ultimaker Cloud APIs to manage remote clusters. @@ -19,7 +21,9 @@ from .Models.CloudErrorObject import CloudErrorObject # # API spec is available on https://api.ultimaker.com/docs/connect/spec/. # + class CloudOutputDeviceManager: + META_CLUSTER_ID = "um_cloud_cluster_id" # The interval with which the remote clusters are checked CHECK_CLUSTER_INTERVAL = 5.0 # seconds @@ -42,59 +46,48 @@ class CloudOutputDeviceManager: # When switching machines we check if we have to activate a remote cluster. application.globalContainerStackChanged.connect(self._connectToActiveMachine) - - self.update_timer = QTimer(CuraApplication.getInstance()) - self.update_timer.setInterval(self.CHECK_CLUSTER_INTERVAL * 1000) - self.update_timer.setSingleShot(False) - self.update_timer.timeout.connect(self._getRemoteClusters) + + # create a timer to update the remote cluster list + self._update_timer = QTimer(application) + self._update_timer.setInterval(self.CHECK_CLUSTER_INTERVAL * 1000) + self._update_timer.setSingleShot(False) + self._update_timer.timeout.connect(self._getRemoteClusters) ## Gets all remote clusters from the API. def _getRemoteClusters(self) -> None: Logger.log("i", "Retrieving remote clusters") if self._account.isLoggedIn: self._api.getClusters(self._onGetRemoteClustersFinished) - - # Only start the polling timer after the user is authenticated - # The first call to _getRemoteClusters comes from self._account.loginStateChanged - if not self.update_timer.isActive(): - self.update_timer.start() + # Only start the polling timer after the user is authenticated + # The first call to _getRemoteClusters comes from self._account.loginStateChanged + if not self._update_timer.isActive(): + self._update_timer.start() ## Callback for when the request for getting the clusters. is finished. def _onGetRemoteClustersFinished(self, clusters: List[CloudCluster]) -> None: - found_clusters = {c.cluster_id: c for c in clusters} + online_clusters = {c.cluster_id: c for c in clusters if c.is_online} # type: Dict[str, CloudCluster] - Logger.log("i", "Parsed remote clusters to %s", found_clusters) + removed_devices, added_clusters, updates = findChanges(self._remote_clusters, online_clusters) - known_cluster_ids = set(self._remote_clusters.keys()) - found_cluster_ids = set(found_clusters.keys()) + Logger.log("i", "Parsed remote clusters to %s", online_clusters) + + # Remove output devices that are gone + for removed_cluster in removed_devices: + self._output_device_manager.removeOutputDevice(removed_cluster.key) + del self._remote_clusters[removed_cluster.key] # Add an output device for each new remote cluster. # We only add when is_online as we don't want the option in the drop down if the cluster is not online. - for cluster_id in found_cluster_ids.difference(known_cluster_ids): - if found_clusters[cluster_id].is_online: - self._addCloudOutputDevice(found_clusters[cluster_id]) + for added_cluster in added_clusters: + device = CloudOutputDevice(self._api, added_cluster.cluster_id, added_cluster.host_name) + self._output_device_manager.addOutputDevice(device) + self._remote_clusters[added_cluster.cluster_id] = device - # Remove output devices that are gone - for cluster_id in known_cluster_ids.difference(found_cluster_ids): - self._removeCloudOutputDevice(found_clusters[cluster_id]) + for device, cluster in updates: + device.host_name = cluster.host_name - # TODO: not pass clusters that are not online? self._connectToActiveMachine() - ## Adds a CloudOutputDevice for each entry in the remote cluster list from the API. - # \param cluster: The cluster that was added. - def _addCloudOutputDevice(self, cluster: CloudCluster): - device = CloudOutputDevice(self._api, cluster.cluster_id) - self._output_device_manager.addOutputDevice(device) - self._remote_clusters[cluster.cluster_id] = device - - ## Remove a CloudOutputDevice - # \param cluster: The cluster that was removed - def _removeCloudOutputDevice(self, cluster: CloudCluster): - self._output_device_manager.removeOutputDevice(cluster.cluster_id) - if cluster.cluster_id in self._remote_clusters: - del self._remote_clusters[cluster.cluster_id] - ## Callback for when the active machine was changed by the user or a new remote cluster was found. def _connectToActiveMachine(self) -> None: active_machine = CuraApplication.getInstance().getGlobalContainerStack() @@ -102,23 +95,27 @@ class CloudOutputDeviceManager: return # Check if the stored cluster_id for the active machine is in our list of remote clusters. - stored_cluster_id = active_machine.getMetaDataEntry("um_cloud_cluster_id") - if stored_cluster_id in self._remote_clusters.keys(): - return self._remote_clusters.get(stored_cluster_id).connect() + stored_cluster_id = active_machine.getMetaDataEntry(self.META_CLUSTER_ID) + if stored_cluster_id in self._remote_clusters: + device = self._remote_clusters[stored_cluster_id] + if not device.isConnected(): + device.connect() + else: + self._connectByNetworkKey(active_machine) + ## Tries to match the + def _connectByNetworkKey(self, active_machine: GlobalStack) -> None: # Check if the active printer has a local network connection and match this key to the remote cluster. - # The local network key is formatted as ultimakersystem-xxxxxxxxxxxx._ultimaker._tcp.local. - # The optional remote host_name is formatted as ultimakersystem-xxxxxxxxxxxx. - # This means we can match the two by checking if the host_name is in the network key string. - local_network_key = active_machine.getMetaDataEntry("um_network_key") if not local_network_key: return - # TODO: get host_name in the output device so we can iterate here - # cluster_id = next(local_network_key in cluster.host_name for cluster in self._remote_clusters.items()) - # if cluster_id in self._remote_clusters.keys(): - # return self._remote_clusters.get(cluster_id).connect() + device = next((c for c in self._remote_clusters.values() if c.matchesNetworkKey(local_network_key)), None) + if not device: + return + + active_machine.setMetaDataEntry(self.META_CLUSTER_ID, device.key) + return device.connect() ## Handles an API error received from the cloud. # \param errors: The errors received diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudCluster.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudCluster.py index dd1e2e85bf..28e95a097a 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudCluster.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudCluster.py @@ -11,7 +11,7 @@ class CloudCluster(BaseModel): self.host_name = None # type: str self.host_version = None # type: str self.status = None # type: str - self.is_online = None # type: bool + self.is_online = False # type: bool super().__init__(**kwargs) # Validates the model, raising an exception if the model is invalid. diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Utils.py b/plugins/UM3NetworkPrinting/src/Cloud/Utils.py new file mode 100644 index 0000000000..9a2a160492 --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/Utils.py @@ -0,0 +1,19 @@ +from typing import TypeVar, Dict, Tuple, List + +T = TypeVar("T") +U = TypeVar("U") + + +def findChanges(previous: Dict[str, T], received: Dict[str, U]) -> Tuple[List[T], List[U], List[Tuple[T, U]]]: + previous_ids = set(previous) + received_ids = set(received) + + removed_ids = previous_ids.difference(received_ids) + new_ids = received_ids.difference(previous_ids) + updated_ids = received_ids.intersection(previous_ids) + + removed = [previous[removed_id] for removed_id in removed_ids] + added = [received[new_id] for new_id in new_ids] + updated = [(previous[updated_id], received[updated_id]) for updated_id in updated_ids] + + return removed, added, updated diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index aa8be9ecd9..70f4d2d0ee 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -45,8 +45,8 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): activeCameraUrlChanged = pyqtSignal() receivedPrintJobsChanged = pyqtSignal() - # This is a bit of a hack, as the notify can only use signals that are defined by the class that they are in. - # Inheritance doesn't seem to work. Tying them together does work, but i'm open for better suggestions. + # Notify can only use signals that are defined by the class that they are in, not inherited ones. + # Therefore we create a private signal used to trigger the printersChanged signal. _clusterPrintersChanged = pyqtSignal() def __init__(self, device_id, address, properties, parent = None) -> None: @@ -62,7 +62,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../resources/qml/ClusterMonitorItem.qml") - # See comments about this hack with the clusterPrintersChanged signal + # trigger the printersChanged signal when the private signal is triggered self.printersChanged.connect(self._clusterPrintersChanged) self._accepts_commands = True # type: bool From 657e76331870f502adc6ce4424b6de20c67127ec Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Wed, 5 Dec 2018 16:15:51 +0100 Subject: [PATCH 0677/1240] STAR-322: Using findChanges method to simplify code --- .../src/Cloud/CloudOutputDevice.py | 41 +++++++++---------- plugins/UM3NetworkPrinting/src/Cloud/Utils.py | 7 ++++ .../src/MeshFormatHandler.py | 3 +- 3 files changed, 27 insertions(+), 24 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index e8f7687b03..980b9efa9e 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -24,6 +24,7 @@ from .Models.CloudPrintResponse import CloudPrintResponse from .Models.CloudJobResponse import CloudJobResponse from .Models.CloudClusterPrinter import CloudClusterPrinter from .Models.CloudClusterPrintJob import CloudClusterPrintJob +from .Utils import findChanges ## Class that contains all the translations for this module. @@ -198,45 +199,41 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self._updatePrintJobs(status.print_jobs) def _updatePrinters(self, printers: List[CloudClusterPrinter]) -> None: - remote_printers = {p.uuid: p for p in printers} # type: Dict[str, CloudClusterPrinter] - current_printers = {p.key: p for p in self._printers} # type: Dict[str, PrinterOutputModel] + previous = {p.key: p for p in self._printers} # type: Dict[str, PrinterOutputModel] + received = {p.uuid: p for p in printers} # type: Dict[str, CloudClusterPrinter] - remote_printer_ids = set(remote_printers) # type: Set[str] - current_printer_ids = set(current_printers) # type: Set[str] + removed_printers, added_printers, updated_printers = findChanges(previous, received) - for removed_printer_id in current_printer_ids.difference(remote_printer_ids): - removed_printer = current_printers[removed_printer_id] + for removed_printer in removed_printers: self._printers.remove(removed_printer) - for new_printer_id in remote_printer_ids.difference(current_printer_ids): - new_printer = remote_printers[new_printer_id] + for added_printer in added_printers: controller = PrinterOutputController(self) - self._printers.append(new_printer.createOutputModel(controller)) + self._printers.append(added_printer.createOutputModel(controller)) - for updated_printer_guid in current_printer_ids.intersection(remote_printer_ids): - remote_printers[updated_printer_guid].updateOutputModel(current_printers[updated_printer_guid]) + for model, printer in updated_printers: + printer.updateOutputModel(model) self._clusterPrintersChanged.emit() def _updatePrintJobs(self, jobs: List[CloudClusterPrintJob]) -> None: - remote_jobs = {j.uuid: j for j in jobs} # type: Dict[str, CloudClusterPrintJob] - current_jobs = {j.key: j for j in self._print_jobs} # type: Dict[str, UM3PrintJobOutputModel] + received = {j.uuid: j for j in jobs} # type: Dict[str, CloudClusterPrintJob] + previous = {j.key: j for j in self._print_jobs} # type: Dict[str, UM3PrintJobOutputModel] - remote_job_ids = set(remote_jobs) # type: Set[str] - current_job_ids = set(current_jobs) # type: Set[str] + removed_jobs, added_jobs, updated_jobs = findChanges(previous, received) - for removed_job_id in current_job_ids.difference(remote_job_ids): - self._print_jobs.remove(current_jobs[removed_job_id]) + for removed_job in removed_jobs: + self._print_jobs.remove(removed_job) - for new_job_id in remote_job_ids.difference(current_job_ids): - self._addPrintJob(remote_jobs[new_job_id]) + for added_job in added_jobs: + self._addPrintJob(added_job) - for updated_job_id in current_job_ids.intersection(remote_job_ids): - remote_jobs[updated_job_id].updateOutputModel(current_jobs[updated_job_id]) + for model, job in updated_jobs: + job.updateOutputModel(model) # We only have to update when jobs are added or removed # updated jobs push their changes via their output model - if remote_job_ids != current_job_ids: + if added_jobs or removed_jobs: self.printJobsChanged.emit() def _addPrintJob(self, job: CloudClusterPrintJob) -> None: diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Utils.py b/plugins/UM3NetworkPrinting/src/Cloud/Utils.py index 9a2a160492..58eaf5edb9 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Utils.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Utils.py @@ -4,6 +4,13 @@ T = TypeVar("T") U = TypeVar("U") +## Splits the given dictionaries into three lists (in a tuple): +# - `removed`: Items that were in the first argument but removed in the second one. +# - `added`: Items that were not in the first argument but were included in the second one. +# - `updated`: Items that were in both dictionaries. Both values are given in a tuple. +# \param previous: The previous items +# \param received: The received items +# \return: The tuple (removed, added, updated) as explained above. def findChanges(previous: Dict[str, T], received: Dict[str, U]) -> Tuple[List[T], List[U], List[Tuple[T, U]]]: previous_ids = set(previous) received_ids = set(received) diff --git a/plugins/UM3NetworkPrinting/src/MeshFormatHandler.py b/plugins/UM3NetworkPrinting/src/MeshFormatHandler.py index c2bd997fbb..d64861ea91 100644 --- a/plugins/UM3NetworkPrinting/src/MeshFormatHandler.py +++ b/plugins/UM3NetworkPrinting/src/MeshFormatHandler.py @@ -15,8 +15,7 @@ from cura.CuraApplication import CuraApplication ## Class that contains all the translations for this module. class T: - # The translation catalog for this device. - + # The translation catalog for this module. _I18N_CATALOG = i18nCatalog("cura") NO_FORMATS_AVAILABLE = _I18N_CATALOG.i18nc("@info:status", "There are no file formats available to write with!") From 117cf10a2c60fa4687044ef473303b184634660d Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Wed, 5 Dec 2018 16:26:20 +0100 Subject: [PATCH 0678/1240] STAR-322: Removing devices when logging off --- plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py | 2 +- .../src/Cloud/CloudOutputDeviceManager.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py index 3ede206d45..b4c8774140 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py @@ -129,7 +129,7 @@ class CloudApiClient(NetworkClient): data = response["data"] result = [model(**c) for c in data] if isinstance(data, list) else model(**data) on_finished(result) - elif "error" in response: + elif "errors" in response: self._on_error([CloudErrorObject(**error) for error in response["errors"]]) else: Logger.log("e", "Cannot find data or errors in the cloud response: %s", response) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index f11d41a7bd..29514870ac 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -62,6 +62,10 @@ class CloudOutputDeviceManager: # The first call to _getRemoteClusters comes from self._account.loginStateChanged if not self._update_timer.isActive(): self._update_timer.start() + else: + self._onGetRemoteClustersFinished([]) + if self._update_timer.isActive(): + self._update_timer.stop() ## Callback for when the request for getting the clusters. is finished. def _onGetRemoteClustersFinished(self, clusters: List[CloudCluster]) -> None: @@ -73,6 +77,8 @@ class CloudOutputDeviceManager: # Remove output devices that are gone for removed_cluster in removed_devices: + if removed_cluster.isConnected(): + removed_cluster.disconnect() self._output_device_manager.removeOutputDevice(removed_cluster.key) del self._remote_clusters[removed_cluster.key] From 5e15858cae6f36596b806841e94390c2b1fd13fb Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 5 Dec 2018 17:05:21 +0100 Subject: [PATCH 0679/1240] more mocks for monitor page, fix showing current print job in monitor page, add todo --- .../src/Cloud/CloudOutputDevice.py | 20 ++++++++++++++++--- .../src/Cloud/Models/CloudClusterPrintJob.py | 7 ++++--- .../src/ClusterUM3OutputDevice.py | 5 +++-- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 980b9efa9e..ec7e7f4acf 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -155,9 +155,13 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Get remote printers. @pyqtProperty("QVariantList", notify = _clusterPrintersChanged) - def printers(self): + def printers(self) -> List[PrinterOutputModel]: return self._printers + @pyqtProperty(int, notify = _clusterPrintersChanged) + def clusterSize(self) -> int: + return len(self._printers) + ## Get remote print jobs. @pyqtProperty("QVariantList", notify = printJobsChanged) def printJobs(self)-> List[UM3PrintJobOutputModel]: @@ -237,13 +241,15 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self.printJobsChanged.emit() def _addPrintJob(self, job: CloudClusterPrintJob) -> None: + # TODO: somehow we don't see the queued print jobs on the monitor page yet, we have to figure out why. try: - printer = next(p for p in self._printers if job.printer_uuid == p.key) + printer = next(p for p in self._printers if job.printer_uuid == p.key or job.assigned_to == p.key) except StopIteration: return Logger.log("w", "Missing printer %s for job %s in %s", job.printer_uuid, job.uuid, [p.key for p in self._printers]) - self._print_jobs.append(job.createOutputModel(printer)) + print_job = job.createOutputModel(printer) + self._print_jobs.append(print_job) def _onPrintJobCreated(self, mesh: bytes, job_response: CloudJobResponse) -> None: self._api.uploadMesh(job_response, mesh, self._onPrintJobUploaded, self._updateUploadProgress, @@ -321,3 +327,11 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): @pyqtProperty(bool, notify = printJobsChanged) def receivedPrintJobs(self) -> bool: return True + + @pyqtSlot() + def openPrintJobControlPanel(self) -> None: + pass + + @pyqtSlot() + def openPrinterControlPanel(self) -> None: + pass diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py index e2e3787435..36d878d46f 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py @@ -1,6 +1,6 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import List +from typing import List, Optional from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from .CloudClusterPrinterConfiguration import CloudClusterPrinterConfiguration @@ -33,14 +33,15 @@ class CloudClusterPrintJob(BaseModel): super().__init__(**kwargs) self.printers = [CloudClusterPrinterConfiguration(**c) if isinstance(c, dict) else c for c in self.configuration] - self.printers = [CloudClusterPrintJobConstraint(**p) if isinstance(p, dict) else p - for p in self.constraints] + self.print_jobs = [CloudClusterPrintJobConstraint(**p) if isinstance(p, dict) else p + for p in self.constraints] ## Creates an UM3 print job output model based on this cloud cluster print job. # \param printer: The output model of the printer def createOutputModel(self, printer: PrinterOutputModel) -> UM3PrintJobOutputModel: model = UM3PrintJobOutputModel(printer.getController(), self.uuid, self.name) model.updateAssignedPrinter(printer) + printer.updateActivePrintJob(model) return model ## Updates an UM3 print job output model based on this cloud cluster print job. diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index 70f4d2d0ee..965a698029 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -411,8 +411,9 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): ## Called when the connection to the cluster changes. def connect(self) -> None: - super().connect() - self.sendMaterialProfiles() + pass + # super().connect() + # self.sendMaterialProfiles() def _onGetPreviewImageFinished(self, reply: QNetworkReply) -> None: reply_url = reply.url().toString() From 66690dfef72e0e8466927d0d71d7fa84c86ff18e Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 5 Dec 2018 17:06:22 +0100 Subject: [PATCH 0680/1240] Add testing todo --- plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index 965a698029..cc5b128479 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -412,6 +412,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): ## Called when the connection to the cluster changes. def connect(self) -> None: pass + # TODO: uncomment this once cloud implementation works for testing # super().connect() # self.sendMaterialProfiles() From ce07e31bbf299f4ec86f24fd7acd66795ec14da3 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 5 Dec 2018 17:21:46 +0100 Subject: [PATCH 0681/1240] Implement active printer for cloud device to get monitor page functionality working --- .../src/Cloud/CloudOutputDevice.py | 86 ++++++++++++++++--- 1 file changed, 75 insertions(+), 11 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index ec7e7f4acf..9dab829825 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -1,6 +1,8 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import os +from datetime import datetime + from time import time from typing import Dict, List, Optional, Set @@ -10,6 +12,7 @@ from UM import i18nCatalog from UM.FileHandler.FileHandler import FileHandler from UM.Logger import Logger from UM.Message import Message +from UM.Qt.Duration import Duration, DurationFormat from UM.Scene.SceneNode import SceneNode from cura.CuraApplication import CuraApplication from cura.PrinterOutput.NetworkedPrinterOutputDevice import AuthState, NetworkedPrinterOutputDevice @@ -66,6 +69,9 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # Signal triggered when the print jobs in the queue were changed. printJobsChanged = pyqtSignal() + # Signal triggered when the selected printer in the UI should be changed. + activePrinterChanged = pyqtSignal() + # Notify can only use signals that are defined by the class that they are in, not inherited ones. # Therefore we create a private signal used to trigger the printersChanged signal. _clusterPrintersChanged = pyqtSignal() @@ -90,9 +96,12 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self._control_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../resources/qml/ClusterControlItem.qml") - # trigger the printersChanged signal when the private signal is triggered + # Trigger the printersChanged signal when the private signal is triggered. self.printersChanged.connect(self._clusterPrintersChanged) + # We keep track of which printer is visible in the monitor page. + self._active_printer = None # type: Optional[PrinterOutputModel] + # Properties to populate later on with received cloud data. self._print_jobs = [] # type: List[UM3PrintJobOutputModel] self._number_of_extruders = 2 # All networked printers are dual-extrusion Ultimaker machines. @@ -158,6 +167,18 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def printers(self) -> List[PrinterOutputModel]: return self._printers + ## Get the active printer in the UI (monitor page). + @pyqtProperty(QObject, notify = activePrinterChanged) + def activePrinter(self) -> Optional[PrinterOutputModel]: + return self._active_printer + + ## Set the active printer in the UI (monitor page). + @pyqtSlot(QObject) + def setActivePrinter(self, printer: Optional[PrinterOutputModel] = None) -> None: + if printer != self._active_printer: + self._active_printer = printer + self.activePrinterChanged.emit() + @pyqtProperty(int, notify = _clusterPrintersChanged) def clusterSize(self) -> int: return len(self._printers) @@ -179,6 +200,37 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): return [print_job for print_job in self._print_jobs if print_job.assignedPrinter is not None and print_job.state != "queued"] + @pyqtSlot(int, result = str) + def formatDuration(self, seconds: int) -> str: + # TODO: this really shouldn't be in this class + return Duration(seconds).getDisplayString(DurationFormat.Format.Short) + + @pyqtSlot(int, result = str) + def getTimeCompleted(self, time_remaining: int) -> str: + # TODO: this really shouldn't be in this class + 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: int) -> str: + # TODO: this really shouldn't be in this class + current_time = time() + completed = datetime.fromtimestamp(current_time + time_remaining) + today = datetime.fromtimestamp(current_time) + # If finishing date is more than 7 days out, using "Mon Dec 3 at HH:MM" format + if completed.toordinal() > today.toordinal() + 7: + return completed.strftime("%a %b ") + "{day}".format(day = completed.day) + # If finishing date is within the next week, use "Monday at HH:MM" format + elif completed.toordinal() > today.toordinal() + 1: + return completed.strftime("%a") + # If finishing tomorrow, use "tomorrow at HH:MM" format + elif completed.toordinal() > today.toordinal(): + return "tomorrow" + # If finishing today, use "today at HH:MM" format + else: + return "today" + ## Called when the connection to the cluster changes. def connect(self) -> None: super().connect() @@ -209,6 +261,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): removed_printers, added_printers, updated_printers = findChanges(previous, received) for removed_printer in removed_printers: + if self._active_printer == removed_printer: + self.setActivePrinter(None) self._printers.remove(removed_printer) for added_printer in added_printers: @@ -218,6 +272,10 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): for model, printer in updated_printers: printer.updateOutputModel(model) + # Always have an active printer + if not self._active_printer: + self.setActivePrinter(self._printers[0]) + self._clusterPrintersChanged.emit() def _updatePrintJobs(self, jobs: List[CloudClusterPrintJob]) -> None: @@ -306,16 +364,6 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## TODO: The following methods are required by the monitor page QML, but are not actually available using cloud. # TODO: We fake the methods here to not break the monitor page. - @pyqtProperty(QObject, notify = _clusterPrintersChanged) - def activePrinter(self) -> Optional[PrinterOutputModel]: - if not self._printers: - return None - return self._printers[0] - - @pyqtSlot(QObject) - def setActivePrinter(self, printer: Optional[PrinterOutputModel]) -> None: - pass - @pyqtProperty(QUrl, notify = _clusterPrintersChanged) def activeCameraUrl(self) -> "QUrl": return QUrl() @@ -335,3 +383,19 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): @pyqtSlot() def openPrinterControlPanel(self) -> None: pass + + @pyqtSlot(str) + def sendJobToTop(self, print_job_uuid: str) -> None: + pass + + @pyqtSlot(str) + def deleteJobFromQueue(self, print_job_uuid: str) -> None: + pass + + @pyqtSlot(str) + def forceSendJob(self, print_job_uuid: str) -> None: + pass + + @pyqtProperty("QVariantList", notify = _clusterPrintersChanged) + def connectedPrintersTypeCount(self) -> List[Dict[str, str]]: + return [] From 94f31378a68b8daa05cc3b7e3434107171fd2540 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 5 Dec 2018 17:24:47 +0100 Subject: [PATCH 0682/1240] Add a TODO --- .../UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py index 36d878d46f..f4d211f8f4 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py @@ -40,6 +40,7 @@ class CloudClusterPrintJob(BaseModel): # \param printer: The output model of the printer def createOutputModel(self, printer: PrinterOutputModel) -> UM3PrintJobOutputModel: model = UM3PrintJobOutputModel(printer.getController(), self.uuid, self.name) + # TODO: implement more data as shown in ClusterUM3OutputDevice._createPrintJobModel model.updateAssignedPrinter(printer) printer.updateActivePrintJob(model) return model From 57efca13769a9328c5e2ac31efc66201ef89c540 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 5 Dec 2018 19:31:58 +0100 Subject: [PATCH 0683/1240] Fix a possible division by zero error --- cura/PrinterOutput/PrintJobOutputModel.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cura/PrinterOutput/PrintJobOutputModel.py b/cura/PrinterOutput/PrintJobOutputModel.py index 256c9dffe9..a77ac81909 100644 --- a/cura/PrinterOutput/PrintJobOutputModel.py +++ b/cura/PrinterOutput/PrintJobOutputModel.py @@ -132,9 +132,9 @@ class PrintJobOutputModel(QObject): @pyqtProperty(float, notify = timeElapsedChanged) def progress(self) -> float: - result = self.timeElapsed / self.timeTotal - # Never get a progress past 1.0 - return min(result, 1.0) + time_elapsed = max(float(self.timeElapsed), 1.0) # Prevent a division by zero exception + result = time_elapsed / self.timeTotal + return min(result, 1.0) # Never get a progress past 1.0 @pyqtProperty(str, notify=stateChanged) def state(self) -> str: From c757bf128e9fae8895973956a671fc0bcc430b31 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 6 Dec 2018 09:12:43 +0100 Subject: [PATCH 0684/1240] Adjust colors and alignments in the print info panel --- resources/qml/Cura.qml | 6 +++--- resources/qml/ExpandableComponent.qml | 2 +- resources/themes/cura-light/theme.json | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 6559fafa12..ba1230f37d 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -226,8 +226,8 @@ UM.MainWindow { left: parent.left bottom: viewOrientationControls.top - margins: UM.Theme.getSize("wide_margin").width - bottomMargin: UM.Theme.getSize("default_margin").width + margins: UM.Theme.getSize("default_margin").width + bottomMargin: UM.Theme.getSize("thin_margin").width } } @@ -239,7 +239,7 @@ UM.MainWindow { left: parent.left bottom: parent.bottom - margins: UM.Theme.getSize("wide_margin").width + margins: UM.Theme.getSize("default_margin").width } } diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 9bedaa940c..3991ed74ba 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -142,7 +142,7 @@ Item visible: source != "" width: height height: Math.round(0.2 * base.height) - color: UM.Theme.getColor("text") + color: UM.Theme.getColor("small_button_text") } MouseArea diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 767b6eaccd..3cea54ac77 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -123,8 +123,8 @@ "text_subtext": [0, 0, 0, 255], "text_medium": [128, 128, 128, 255], "text_emphasis": [255, 255, 255, 255], - "text_scene": [31, 36, 39, 255], - "text_scene_hover": [70, 84, 113, 255], + "text_scene": [102, 102, 102, 255], + "text_scene_hover": [123, 123, 113, 255], "error": [255, 140, 0, 255], "warning": [255, 190, 35, 255], @@ -144,10 +144,10 @@ "button_text_active_hover": [255, 255, 255, 255], "small_button": [0, 0, 0, 0], - "small_button_hover": [10, 8, 80, 255], + "small_button_hover": [102, 102, 102, 255], "small_button_active": [10, 8, 80, 255], "small_button_active_hover": [10, 8, 80, 255], - "small_button_text": [171, 171, 191, 255], + "small_button_text": [102, 102, 102, 255], "small_button_text_hover": [255, 255, 255, 255], "small_button_text_active": [255, 255, 255, 255], "small_button_text_active_hover": [255, 255, 255, 255], From b0c3a4e17a558154807a7807218367cb1b158a64 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 6 Dec 2018 09:25:31 +0100 Subject: [PATCH 0685/1240] Align the additional components to the right of the job specs The model checker now shows centered --- plugins/ModelChecker/ModelChecker.qml | 12 +++++++----- resources/qml/JobSpecs.qml | 21 +++++++++++---------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/plugins/ModelChecker/ModelChecker.qml b/plugins/ModelChecker/ModelChecker.qml index 437df29516..ddeed063b1 100644 --- a/plugins/ModelChecker/ModelChecker.qml +++ b/plugins/ModelChecker/ModelChecker.qml @@ -4,19 +4,19 @@ import QtQuick 2.2 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.1 -import QtQuick.Layouts 1.1 -import QtQuick.Dialogs 1.1 -import QtQuick.Window 2.2 import UM 1.2 as UM -import Cura 1.0 as Cura Button { id: modelCheckerButton - UM.I18nCatalog{id: catalog; name: "cura"} + UM.I18nCatalog + { + id: catalog + name: "cura" + } visible: manager.hasWarnings tooltip: catalog.i18nc("@info:tooltip", "Some things could be problematic in this print. Click to see tips for adjustment.") @@ -25,6 +25,8 @@ Button width: UM.Theme.getSize("save_button_specs_icons").width height: UM.Theme.getSize("save_button_specs_icons").height + anchors.verticalCenter: parent ? parent.verticalCenter : undefined + style: ButtonStyle { background: Item diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index 97e12e15a3..8b06ab06db 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -108,19 +108,11 @@ Item } } - Row - { - id: additionalComponentsRow - anchors.top: jobNameRow.bottom - anchors.left: parent.left - } - Label { id: boundingSpec anchors.top: jobNameRow.bottom - anchors.left: additionalComponentsRow.right - anchors.leftMargin: additionalComponentsRow.width > 0 ? UM.Theme.getSize("default_margin").width : 0 + anchors.left: parent.left height: UM.Theme.getSize("jobspecs_line").height verticalAlignment: Text.AlignVCenter @@ -129,6 +121,15 @@ Item text: CuraApplication.getSceneBoundingBoxString } + Row + { + id: additionalComponentsRow + anchors.top: boundingSpec.top + anchors.bottom: boundingSpec.bottom + anchors.left: boundingSpec.right + anchors.leftMargin: UM.Theme.getSize("default_margin").width + } + Component.onCompleted: { base.addAdditionalComponents("jobSpecsButton") @@ -140,7 +141,7 @@ Item onAdditionalComponentsChanged: base.addAdditionalComponents("jobSpecsButton") } - function addAdditionalComponents (areaId) + function addAdditionalComponents(areaId) { if (areaId == "jobSpecsButton") { From 838949dac74831e38e9f07fe7d628af7807320db Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 6 Dec 2018 09:45:27 +0100 Subject: [PATCH 0686/1240] Moved qml pages of toolbox to a loader This dramatically improves the loading of the toolbox dialog CURA-6006 --- plugins/Toolbox/resources/qml/Toolbox.qml | 54 ++++++++++----------- plugins/Toolbox/src/Toolbox.py | 59 +++++++++++------------ 2 files changed, 52 insertions(+), 61 deletions(-) diff --git a/plugins/Toolbox/resources/qml/Toolbox.qml b/plugins/Toolbox/resources/qml/Toolbox.qml index 853cec399d..d3d980b0b3 100644 --- a/plugins/Toolbox/resources/qml/Toolbox.qml +++ b/plugins/Toolbox/resources/qml/Toolbox.qml @@ -44,36 +44,31 @@ Window top: header.bottom bottom: footer.top } - // TODO: This could be improved using viewFilter instead of viewCategory - ToolboxLoadingPage + + Loader { - id: viewLoading - visible: toolbox.viewCategory != "installed" && toolbox.viewPage == "loading" - } - ToolboxErrorPage - { - id: viewErrored - visible: toolbox.viewCategory != "installed" && toolbox.viewPage == "errored" - } - ToolboxDownloadsPage - { - id: viewDownloads - visible: toolbox.viewCategory != "installed" && toolbox.viewPage == "overview" - } - ToolboxDetailPage - { - id: viewDetail - visible: toolbox.viewCategory != "installed" && toolbox.viewPage == "detail" - } - ToolboxAuthorPage - { - id: viewAuthor - visible: toolbox.viewCategory != "installed" && toolbox.viewPage == "author" - } - ToolboxInstalledPage - { - id: installedPluginList - visible: toolbox.viewCategory == "installed" + anchors.fill:parent + source: + { + if(toolbox.viewCategory == "installed") + { + return "ToolboxInstalledPage.qml" + } + + switch (toolbox.viewPage) + { + case "loading": + return "ToolboxLoadingPage.qml" + case "errored": + return "ToolboxErrorPage.qml" + case "overview": + return "ToolboxDownloadsPage.qml" + case "detail": + return "ToolboxDetailPage.qml" + case "author": + return "ToolboxAuthorPage.qml" + } + } } } @@ -95,6 +90,7 @@ Window licenseDialog.show(); } } + ToolboxLicenseDialog { id: licenseDialog diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index cd20d26eca..c8349827a9 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -622,44 +622,39 @@ class Toolbox(QObject, Extension): if reply.operation() == QNetworkAccessManager.GetOperation: for response_type, url in self._request_urls.items(): - if reply.url() == url: - if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) == 200: - try: - json_data = json.loads(bytes(reply.readAll()).decode("utf-8")) + if reply.url() != url: + continue - # Check for errors: - if "errors" in json_data: - for error in json_data["errors"]: - Logger.log("e", "%s", error["title"]) - return + if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) == 200: + try: + json_data = json.loads(bytes(reply.readAll()).decode("utf-8")) + except json.decoder.JSONDecodeError: + Logger.log("w", "Received invalid JSON for %s.", response_type) + break - # Create model and apply metadata: - if not self._models[response_type]: - Logger.log("e", "Could not find the %s model.", response_type) - break - - self._server_response_data[response_type] = json_data["data"] - self._models[response_type].setMetadata(self._server_response_data[response_type]) + # Check for errors: + if "errors" in json_data: + for error in json_data["errors"]: + Logger.log("e", "%s", error["title"]) + return - if response_type is "packages": - self._models[response_type].setFilter({"type": "plugin"}) - self.reBuildMaterialsModels() - self.reBuildPluginsModels() - elif response_type is "authors": - self._models[response_type].setFilter({"package_types": "material"}) - self._models[response_type].setFilter({"tags": "generic"}) + self._server_response_data[response_type] = json_data["data"] + self._models[response_type].setMetadata(json_data["data"]) - self.metadataChanged.emit() + if response_type is "packages": + self._models["packages"].setFilter({"type": "plugin"}) + self.reBuildMaterialsModels() + self.reBuildPluginsModels() + elif response_type is "authors": + self._models["authors"].setFilter({"tags": "generic"}) - if self.isLoadingComplete(): - self.setViewPage("overview") + self.metadataChanged.emit() - except json.decoder.JSONDecodeError: - Logger.log("w", "Received invalid JSON for %s.", response_type) - break - else: - self.setViewPage("errored") - self.resetDownload() + if self.isLoadingComplete(): + self.setViewPage("overview") + else: + self.setViewPage("errored") + self.resetDownload() else: # Ignore any operation that is not a get operation pass From 6a466c99b241035a6dc6d9cbbf174f39d51aad3d Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 6 Dec 2018 09:54:32 +0100 Subject: [PATCH 0687/1240] Make the progressButton use signals instead of functions Although the naming is still a bit off, it's much cleaner to use signals instead of functions. It's also more in line with how default QML components handle these kind of situations CURA-6006 --- plugins/Toolbox/resources/qml/Toolbox.qml | 2 +- .../qml/ToolboxDetailTileActions.qml | 23 ++++++++----------- .../qml/ToolboxInstalledTileActions.qml | 8 +++---- .../resources/qml/ToolboxProgressButton.qml | 12 +++++----- 4 files changed, 19 insertions(+), 26 deletions(-) diff --git a/plugins/Toolbox/resources/qml/Toolbox.qml b/plugins/Toolbox/resources/qml/Toolbox.qml index d3d980b0b3..b2bab4355a 100644 --- a/plugins/Toolbox/resources/qml/Toolbox.qml +++ b/plugins/Toolbox/resources/qml/Toolbox.qml @@ -90,7 +90,7 @@ Window licenseDialog.show(); } } - + ToolboxLicenseDialog { id: licenseDialog diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml b/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml index cd1e4cdbda..72a9d14dcd 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml @@ -18,19 +18,15 @@ Column id: installButton active: toolbox.isDownloading && toolbox.activePackage == model complete: installed - readyAction: function() + onReadyAction: { toolbox.activePackage = model toolbox.startDownload(model.download_url) } - activeAction: function() - { - toolbox.cancelDownload() - } - completeAction: function() - { - toolbox.viewCategory = "installed" - } + onActiveAction: toolbox.cancelDownload() + + onCompleteAction: toolbox.viewCategory = "installed" + // Don't allow installing while another download is running enabled: installed || !(toolbox.isDownloading && toolbox.activePackage != model) opacity: enabled ? 1.0 : 0.5 @@ -44,20 +40,19 @@ Column readyLabel: catalog.i18nc("@action:button", "Update") activeLabel: catalog.i18nc("@action:button", "Updating") completeLabel: catalog.i18nc("@action:button", "Updated") - readyAction: function() + + onReadyAction: { toolbox.activePackage = model toolbox.update(model.id) } - activeAction: function() - { - toolbox.cancelDownload() - } + onActiveAction: toolbox.cancelDownload() // Don't allow installing while another download is running enabled: !(toolbox.isDownloading && toolbox.activePackage != model) opacity: enabled ? 1.0 : 0.5 visible: canUpdate } + Connections { target: toolbox diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml index 8fd88b1cfd..621ecd96ea 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml @@ -30,15 +30,13 @@ Column readyLabel: catalog.i18nc("@action:button", "Update") activeLabel: catalog.i18nc("@action:button", "Updating") completeLabel: catalog.i18nc("@action:button", "Updated") - readyAction: function() + onReadyAction: { toolbox.activePackage = model toolbox.update(model.id) } - activeAction: function() - { - toolbox.cancelDownload() - } + onActiveAction: toolbox.cancelDownload() + // Don't allow installing while another download is running enabled: !(toolbox.isDownloading && toolbox.activePackage != model) opacity: enabled ? 1.0 : 0.5 diff --git a/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml b/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml index 2744e40ec9..00b0b985f5 100644 --- a/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml +++ b/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml @@ -18,9 +18,9 @@ Item property var activeLabel: catalog.i18nc("@action:button", "Cancel") property var completeLabel: catalog.i18nc("@action:button", "Installed") - property var readyAction: null // Action when button is ready and clicked (likely install) - property var activeAction: null // Action when button is active and clicked (likely cancel) - property var completeAction: null // Action when button is complete and clicked (likely go to installed) + signal readyAction() // Action when button is ready and clicked (likely install) + signal activeAction() // Action when button is active and clicked (likely cancel) + signal completeAction() // Action when button is complete and clicked (likely go to installed) width: UM.Theme.getSize("toolbox_action_button").width height: UM.Theme.getSize("toolbox_action_button").height @@ -47,15 +47,15 @@ Item { if (complete) { - return completeAction() + completeAction() } else if (active) { - return activeAction() + activeAction() } else { - return readyAction() + readyAction() } } style: ButtonStyle From eb3777ed9fb48ed6f0385c230d9f12fc5aa338f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Thu, 6 Dec 2018 10:40:06 +0100 Subject: [PATCH 0688/1240] Cleaner login and update cluster flow, start update cluster timer at startup when the user is already logged in --- .../src/Cloud/CloudOutputDeviceManager.py | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 29514870ac..5d55d30548 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -41,7 +41,7 @@ class CloudOutputDeviceManager: self._output_device_manager = application.getOutputDeviceManager() self._account = application.getCuraAPI().account - self._account.loginStateChanged.connect(self._getRemoteClusters) + self._account.loginStateChanged.connect(self._onLoginStateChanged) self._api = CloudApiClient(self._account, self._onApiError) # When switching machines we check if we have to activate a remote cluster. @@ -49,23 +49,26 @@ class CloudOutputDeviceManager: # create a timer to update the remote cluster list self._update_timer = QTimer(application) - self._update_timer.setInterval(self.CHECK_CLUSTER_INTERVAL * 1000) + self._update_timer.setInterval(int(self.CHECK_CLUSTER_INTERVAL * 1000)) self._update_timer.setSingleShot(False) self._update_timer.timeout.connect(self._getRemoteClusters) + # Make sure the timer is started in case we missed the loginChanged signal + self._onLoginStateChanged() + + # Called when the uses logs in or out + def _onLoginStateChanged(self) -> None: + if self._account.isLoggedIn and not self._update_timer.isActive(): + self._update_timer.start() + else: + self._update_timer.stop() + # Notify that all clusters have disappeared + self._onGetRemoteClustersFinished([]) + ## Gets all remote clusters from the API. def _getRemoteClusters(self) -> None: Logger.log("i", "Retrieving remote clusters") - if self._account.isLoggedIn: - self._api.getClusters(self._onGetRemoteClustersFinished) - # Only start the polling timer after the user is authenticated - # The first call to _getRemoteClusters comes from self._account.loginStateChanged - if not self._update_timer.isActive(): - self._update_timer.start() - else: - self._onGetRemoteClustersFinished([]) - if self._update_timer.isActive(): - self._update_timer.stop() + self._api.getClusters(self._onGetRemoteClustersFinished) ## Callback for when the request for getting the clusters. is finished. def _onGetRemoteClustersFinished(self, clusters: List[CloudCluster]) -> None: From 2602d5bf02287745831d3d264bbc8e3dcda37541 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 6 Dec 2018 10:46:19 +0100 Subject: [PATCH 0689/1240] Add changed checks to prevent unneeded signals from being fired CURA-6006 --- plugins/Toolbox/src/Toolbox.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index c8349827a9..68919cf987 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -546,7 +546,7 @@ class Toolbox(QObject, Extension): # Check for plugins that were installed with the old plugin browser def isOldPlugin(self, plugin_id: str) -> bool: - return plugin_id in self._old_plugin_ids + return plugin_id in self._old_plugin_ids def getOldPluginPackageMetadata(self, plugin_id: str) -> Optional[Dict[str, Any]]: return self._old_plugin_metadata.get(plugin_id) @@ -709,8 +709,9 @@ class Toolbox(QObject, Extension): return self._is_downloading def setActivePackage(self, package: Dict[str, Any]) -> None: - self._active_package = package - self.activePackageChanged.emit() + if self._active_package != package: + self._active_package = package + self.activePackageChanged.emit() ## The active package is the package that is currently being downloaded @pyqtProperty(QObject, fset = setActivePackage, notify = activePackageChanged) @@ -718,16 +719,18 @@ class Toolbox(QObject, Extension): return self._active_package def setViewCategory(self, category: str = "plugin") -> None: - self._view_category = category - self.viewChanged.emit() + if self._view_category != category: + self._view_category = category + self.viewChanged.emit() @pyqtProperty(str, fset = setViewCategory, notify = viewChanged) def viewCategory(self) -> str: return self._view_category def setViewPage(self, page: str = "overview") -> None: - self._view_page = page - self.viewChanged.emit() + if self._view_page != page: + self._view_page = page + self.viewChanged.emit() @pyqtProperty(str, fset = setViewPage, notify = viewChanged) def viewPage(self) -> str: From f7f3c96f81d82cead247c3ecd0d3925d79047a48 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 6 Dec 2018 10:53:40 +0100 Subject: [PATCH 0690/1240] Fix typo CURA-5941 --- .../Recommended/RecommendedAdhesionSelector.qml | 2 +- .../Recommended/RecommendedInfillDensitySelector.qml | 2 +- .../Recommended/RecommendedPrintSetup.qml | 2 +- .../Recommended/RecommendedSupportSelector.qml | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedAdhesionSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedAdhesionSelector.qml index 3092644d4e..a5f35f333b 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedAdhesionSelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedAdhesionSelector.qml @@ -50,7 +50,7 @@ Item //: Setting enable printing build-plate adhesion helper checkbox style: UM.Theme.styles.checkbox - enabled: recommendedPrintSettup.settingsEnabled + enabled: recommendedPrintSetup.settingsEnabled visible: platformAdhesionType.properties.enabled == "True" checked: platformAdhesionType.properties.value != "skirt" && platformAdhesionType.properties.value != "none" diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml index 7c026ac9de..2971415948 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml @@ -189,7 +189,7 @@ Item text: catalog.i18nc("@label", "Gradual infill") style: UM.Theme.styles.checkbox - enabled: recommendedPrintSettup.settingsEnabled + enabled: recommendedPrintSetup.settingsEnabled visible: infillSteps.properties.enabled == "True" checked: parseInt(infillSteps.properties.value) > 0 diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml index 618c519d31..6885f8c041 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml @@ -10,7 +10,7 @@ import Cura 1.0 as Cura Item { - id: recommendedPrintSettup + id: recommendedPrintSetup height: childrenRect.height + 2 * padding diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml index da46f2a735..57e0c8ce6b 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml @@ -50,7 +50,7 @@ Item property alias _hovered: enableSupportMouseArea.containsMouse style: UM.Theme.styles.checkbox - enabled: recommendedPrintSettup.settingsEnabled + enabled: recommendedPrintSetup.settingsEnabled visible: supportEnabled.properties.enabled == "True" checked: supportEnabled.properties.value == "True" @@ -87,7 +87,7 @@ Item } style: UM.Theme.styles.combobox_color - enabled: recommendedPrintSettup.settingsEnabled + enabled: recommendedPrintSetup.settingsEnabled visible: enableSupportCheckBox.visible && (supportEnabled.properties.value == "True") && (extrudersEnabledCount.properties.value > 1) textRole: "text" // this solves that the combobox isn't populated in the first time Cura is started @@ -127,7 +127,7 @@ Item id: supportExtruderMouseArea anchors.fill: parent hoverEnabled: true - enabled: recommendedPrintSettup.settingsEnabled + enabled: recommendedPrintSetup.settingsEnabled acceptedButtons: Qt.NoButton onEntered: { From 48e15daf6410e19fd8eb658061a925c4e714ee1b Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 6 Dec 2018 11:16:42 +0100 Subject: [PATCH 0691/1240] Fix height of scroll view and make scrollable Contributes to issue CURA-5876. --- resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml | 2 +- .../qml/Menus/ConfigurationMenu/ConfigurationListView.qml | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index af2712be44..6ac1e6a2ad 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -14,7 +14,7 @@ Button property var configuration: null hoverEnabled: true - height: childrenRect.height + height: background.height background: Rectangle { diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml index 5a9f72260c..d07337f9c5 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml @@ -28,7 +28,9 @@ Column { id: container width: parent.width - height: Math.min(configurationList.contentHeight, 350 * screenScaleFactor) + height: Math.round(Math.min(configurationList.height, 350 * screenScaleFactor)) + contentHeight: configurationList.height + clip: true ButtonGroup { @@ -41,6 +43,7 @@ Column spacing: Math.round(UM.Theme.getSize("default_margin").height / 2) width: container.width contentHeight: childrenRect.height + height: childrenRect.height section.property: "modelData.printerType" section.criteria: ViewSection.FullString From f655e6c43ee115cdad81b7683cab1902cf930258 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 6 Dec 2018 11:30:59 +0100 Subject: [PATCH 0692/1240] Add a pattern in the background of the header. Adjust some main colors to the ones in the designs. --- resources/qml/Cura.qml | 65 +- resources/qml/MainWindow/MainWindowHeader.qml | 27 +- .../cura-light/images/header_pattern.svg | 1903 ++++++++++++++++- resources/themes/cura-light/theme.json | 22 +- 4 files changed, 1940 insertions(+), 77 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 3578888886..f16ae9a19b 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -88,6 +88,30 @@ UM.MainWindow window: base } + Rectangle + { + id: headerBackground + anchors + { + top: parent.top + left: parent.left + right: parent.right + } + height: stageMenu.source != "" ? Math.round(mainWindowHeader.height + stageMenu.height / 2) : mainWindowHeader.height + color: UM.Theme.getColor("main_window_header_background") + + // This is the new fancy pattern + Image + { + id: backgourndPattern + anchors.fill: parent + fillMode: Image.Tile + source: UM.Theme.getImage("header_pattern") + horizontalAlignment: Image.AlignLeft + verticalAlignment: Image.AlignTop + } + } + MainWindowHeader { id: mainWindowHeader @@ -144,44 +168,6 @@ UM.MainWindow } } - Rectangle - { - id: stageMenuBackground - anchors - { - left: parent.left - right: parent.right - top: parent.top - } - visible: stageMenu.source != "" - height: visible ? Math.round(UM.Theme.getSize("stage_menu").height / 2) : 0 - - LinearGradient - { - anchors.fill: parent - start: Qt.point(0, 0) - end: Qt.point(parent.width, 0) - gradient: Gradient - { - GradientStop - { - position: 0.0 - color: UM.Theme.getColor("main_window_header_background") - } - GradientStop - { - position: 0.5 - color: UM.Theme.getColor("main_window_header_background_gradient") - } - GradientStop - { - position: 1.0 - color: UM.Theme.getColor("main_window_header_background") - } - } - } - } - Connections { target: stageMenu.item @@ -257,7 +243,8 @@ UM.MainWindow anchors { - top: stageMenuBackground.bottom + // Align to the top of the stageMenu since the stageMenu may not exist + top: parent.top left: parent.left right: parent.right bottom: parent.bottom diff --git a/resources/qml/MainWindow/MainWindowHeader.qml b/resources/qml/MainWindow/MainWindowHeader.qml index fa0594e2ae..ae1c13d9c3 100644 --- a/resources/qml/MainWindow/MainWindowHeader.qml +++ b/resources/qml/MainWindow/MainWindowHeader.qml @@ -12,38 +12,13 @@ import QtGraphicalEffects 1.0 import "../Account" -Rectangle +Item { id: base implicitHeight: UM.Theme.getSize("main_window_header").height implicitWidth: UM.Theme.getSize("main_window_header").width - LinearGradient - { - anchors.fill: parent - start: Qt.point(0, 0) - end: Qt.point(parent.width, 0) - gradient: Gradient - { - GradientStop - { - position: 0.0 - color: UM.Theme.getColor("main_window_header_background") - } - GradientStop - { - position: 0.5 - color: UM.Theme.getColor("main_window_header_background_gradient") - } - GradientStop - { - position: 1.0 - color: UM.Theme.getColor("main_window_header_background") - } - } - } - Image { id: logo diff --git a/resources/themes/cura-light/images/header_pattern.svg b/resources/themes/cura-light/images/header_pattern.svg index 2a9de2f3e9..14ea9dc917 100644 --- a/resources/themes/cura-light/images/header_pattern.svg +++ b/resources/themes/cura-light/images/header_pattern.svg @@ -1 +1,1902 @@ -Pattern \ No newline at end of file + + + + Pattern_Cura_1 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 767b6eaccd..a4ed45fb24 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -93,14 +93,14 @@ "secondary_button_hover": [228, 228, 228, 255], "secondary_button_text": [30, 102, 215, 255], - "main_window_header_background": [10, 8, 80, 255], + "main_window_header_background": [8, 7, 63, 255], "main_window_header_background_gradient": [25, 23, 91, 255], - "main_window_header_button_text_active": [10, 8, 80, 255], + "main_window_header_button_text_active": [8, 7, 63, 255], "main_window_header_button_text_inactive": [255, 255, 255, 255], "main_window_header_button_text_hovered": [255, 255, 255, 255], "main_window_header_button_background_active": [255, 255, 255, 255], "main_window_header_button_background_inactive": [255, 255, 255, 0], - "main_window_header_button_background_hovered": [255, 255, 255, 102], + "main_window_header_button_background_hovered": [117, 114, 159, 255], "account_widget_outline_active": [70, 66, 126, 255], @@ -113,7 +113,7 @@ "toolbar_background": [255, 255, 255, 255], - "printer_type_label_background": [171, 171, 191, 255], + "printer_type_label_background": [228, 228, 242, 255], "text": [0, 0, 0, 255], "text_detail": [174, 174, 174, 128], @@ -127,9 +127,9 @@ "text_scene_hover": [70, 84, 113, 255], "error": [255, 140, 0, 255], - "warning": [255, 190, 35, 255], + "warning": [245, 166, 35, 255], - "toolbar_button_text": [10, 8, 80, 255], + "toolbar_button_text": [8, 7, 63, 255], "toolbar_button_hover": [232, 242, 252, 255], "toolbar_button_active": [232, 242, 252, 255], "toolbar_button_active_hover": [232, 242, 252, 255], @@ -144,9 +144,9 @@ "button_text_active_hover": [255, 255, 255, 255], "small_button": [0, 0, 0, 0], - "small_button_hover": [10, 8, 80, 255], - "small_button_active": [10, 8, 80, 255], - "small_button_active_hover": [10, 8, 80, 255], + "small_button_hover": [8, 7, 63, 255], + "small_button_active": [8, 7, 63, 255], + "small_button_active_hover": [8, 7, 63, 255], "small_button_text": [171, 171, 191, 255], "small_button_text_hover": [255, 255, 255, 255], "small_button_text_active": [255, 255, 255, 255], @@ -222,8 +222,8 @@ "progressbar_control": [50, 130, 255, 255], "slider_groove": [223, 223, 223, 255], - "slider_groove_fill": [10, 8, 80, 255], - "slider_handle": [10, 8, 80, 255], + "slider_groove_fill": [8, 7, 63, 255], + "slider_handle": [8, 7, 63, 255], "slider_handle_active": [50, 130, 255, 255], "slider_text_background": [255, 255, 255, 255], From aab61ce8dac70c8a24ded4db31641ff67f0fb8c7 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 6 Dec 2018 11:35:33 +0100 Subject: [PATCH 0693/1240] Modify the header pattern to be have an empty space in the left and bottom, so when repeating the pattern it doesn't overlap. --- .../cura-light/images/header_pattern.svg | 3789 ++++++++--------- 1 file changed, 1894 insertions(+), 1895 deletions(-) diff --git a/resources/themes/cura-light/images/header_pattern.svg b/resources/themes/cura-light/images/header_pattern.svg index 14ea9dc917..eff5f01cfa 100644 --- a/resources/themes/cura-light/images/header_pattern.svg +++ b/resources/themes/cura-light/images/header_pattern.svg @@ -1,1902 +1,1901 @@ - + - Pattern_Cura_1 + Desktop HD Created with Sketch. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 26a41c37265822414c3b79e58acce3fc2765a51a Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 6 Dec 2018 11:45:14 +0100 Subject: [PATCH 0694/1240] Adjust the color of the printer in the printer selector For that I needed to get rid of the IconLabel component, since in this case the color of the icon and the text is the same (and it makes sense) --- .../qml/PrinterSelector/MachineSelector.qml | 57 +++++++++++++------ resources/themes/cura-light/theme.json | 1 + 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index 91b5591cd8..95abfd6644 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -25,29 +25,52 @@ Cura.ExpandableComponent name: "cura" } - headerItem: Cura.IconLabel + headerItem: Item { - text: isNetworkPrinter ? Cura.MachineManager.activeMachineNetworkGroupName : Cura.MachineManager.activeMachineName - source: - { - if (isNetworkPrinter) - { - if (machineSelector.outputDevice != null && machineSelector.outputDevice.clusterSize > 1) - { - return UM.Theme.getIcon("printer_group") - } - return UM.Theme.getIcon("printer_single") - } - return "" - } - font: UM.Theme.getFont("medium") - color: UM.Theme.getColor("text") - iconSize: UM.Theme.getSize("machine_selector_icon").width + implicitHeight: icon.height UM.RecolorImage { id: icon + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + + source: + { + if (isNetworkPrinter) + { + if (machineSelector.outputDevice != null && machineSelector.outputDevice.clusterSize > 1) + { + return UM.Theme.getIcon("printer_group") + } + return UM.Theme.getIcon("printer_single") + } + return "" + } + width: UM.Theme.getSize("machine_selector_icon").width + height: width + + color: UM.Theme.getColor("machine_selector_printer_icon") + visible: source != "" + } + + Label + { + id: label + anchors.left: icon.visible ? icon.right : parent.left + anchors.right: parent.right + anchors.leftMargin: UM.Theme.getSize("thin_margin").width + anchors.verticalCenter: icon.verticalCenter + text: isNetworkPrinter ? Cura.MachineManager.activeMachineNetworkGroupName : Cura.MachineManager.activeMachineName + elide: Text.ElideRight + color: UM.Theme.getColor("text") + font: UM.Theme.getFont("medium") + renderType: Text.NativeRendering + } + + UM.RecolorImage + { anchors { bottom: parent.bottom diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index a4ed45fb24..2d7e92be4d 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -108,6 +108,7 @@ "machine_selector_active": [68, 72, 75, 255], "machine_selector_hover": [68, 72, 75, 255], "machine_selector_text_active": [255, 255, 255, 255], + "machine_selector_printer_icon": [8, 7, 63, 255], "action_panel_secondary": [27, 95, 202, 255], From 47626f603341dc8d71688e1722f2a498763707f6 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 6 Dec 2018 11:57:55 +0100 Subject: [PATCH 0695/1240] Fix the color and proportions of the cura logo in the about panel --- resources/qml/Dialogs/AboutDialog.qml | 4 +- .../themes/cura-light/images/logo_about.svg | 172 ++++++++++++++++++ 2 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 resources/themes/cura-light/images/logo_about.svg diff --git a/resources/qml/Dialogs/AboutDialog.qml b/resources/qml/Dialogs/AboutDialog.qml index 94eca4e7c0..add84614e0 100644 --- a/resources/qml/Dialogs/AboutDialog.qml +++ b/resources/qml/Dialogs/AboutDialog.qml @@ -35,9 +35,9 @@ UM.Dialog { id: logo width: (base.minimumWidth * 0.85) | 0 - height: (width * (1/4.25)) | 0 + height: (width * (UM.Theme.getSize("logo").height / UM.Theme.getSize("logo").width)) | 0 - source: UM.Theme.getImage("logo") + source: UM.Theme.getImage("logo_about") anchors.top: parent.top anchors.topMargin: ((base.minimumWidth - width) / 2) | 0 diff --git a/resources/themes/cura-light/images/logo_about.svg b/resources/themes/cura-light/images/logo_about.svg new file mode 100644 index 0000000000..34301fd6c9 --- /dev/null +++ b/resources/themes/cura-light/images/logo_about.svg @@ -0,0 +1,172 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 84c85439ff4db4db77eecc1657fcd78634eca16e Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Thu, 6 Dec 2018 12:00:26 +0100 Subject: [PATCH 0696/1240] The trigger_early_crash did not trigger early crash for testing purposes CURA-5939 --- cura/CuraApplication.py | 6 ++++-- cura_app.py | 6 ------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 47cc94f972..dfdb50515f 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -181,7 +181,6 @@ class CuraApplication(QtApplication): # Variables set from CLI self._files_to_open = [] self._use_single_instance = False - self._trigger_early_crash = False # For debug only self._single_instance = None @@ -292,7 +291,10 @@ class CuraApplication(QtApplication): sys.exit(0) self._use_single_instance = self._cli_args.single_instance - self._trigger_early_crash = self._cli_args.trigger_early_crash + # FOR TESTING ONLY + if self._cli_args.trigger_early_crash: + assert not "This crash is triggered by the trigger_early_crash command line argument." + for filename in self._cli_args.file: self._files_to_open.append(os.path.abspath(filename)) diff --git a/cura_app.py b/cura_app.py index 164e32e738..8df12d771a 100755 --- a/cura_app.py +++ b/cura_app.py @@ -17,12 +17,6 @@ parser.add_argument("--debug", default = False, help = "Turn on the debug mode by setting this option." ) -parser.add_argument("--trigger-early-crash", - dest = "trigger_early_crash", - action = "store_true", - default = False, - help = "FOR TESTING ONLY. Trigger an early crash to show the crash dialog." - ) known_args = vars(parser.parse_known_args()[0]) if not known_args["debug"]: From f1fec2f28082661e58b6fb0824521d69409bb4fb Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 6 Dec 2018 12:07:03 +0100 Subject: [PATCH 0697/1240] Theme the scroll bar We need to make this a reusable component at some point, I think. This is the first time we're using the QtQuick2 version of ScrollView. Contributes to issue CURA-5876. --- .../ConfigurationListView.qml | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml index d07337f9c5..e7936b69d2 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml @@ -28,10 +28,25 @@ Column { id: container width: parent.width - height: Math.round(Math.min(configurationList.height, 350 * screenScaleFactor)) + readonly property int maximumHeight: 350 * screenScaleFactor + height: Math.round(Math.min(configurationList.height, maximumHeight)) contentHeight: configurationList.height clip: true + ScrollBar.vertical.policy: (configurationList.height > maximumHeight) ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff //The AsNeeded policy also hides it when the cursor is away, and we don't want that. + ScrollBar.vertical.background: Rectangle + { + implicitWidth: UM.Theme.getSize("scrollbar").width + radius: width / 2 + color: UM.Theme.getColor("scrollbar_background") + } + ScrollBar.vertical.contentItem: Rectangle + { + implicitWidth: UM.Theme.getSize("scrollbar").width + radius: width / 2 + color: UM.Theme.getColor(parent.pressed ? "scrollbar_handle_down" : parent.hovered ? "scrollbar_handle_hover" : "scrollbar_handle") + } + ButtonGroup { buttons: configurationList.children @@ -41,7 +56,7 @@ Column { id: configurationList spacing: Math.round(UM.Theme.getSize("default_margin").height / 2) - width: container.width + width: container.width - ((height > container.maximumHeight) ? container.ScrollBar.vertical.background.width : 0) //Make room for scroll bar if there is any. contentHeight: childrenRect.height height: childrenRect.height From e8cf9e034db4bf232f29171908e144d6455f0936 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Thu, 6 Dec 2018 13:41:50 +0100 Subject: [PATCH 0698/1240] Catch All Exception except: SystemExit, KeyboardInterrupt, GeneratorException CURA-5939 --- cura/CrashHandler.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/cura/CrashHandler.py b/cura/CrashHandler.py index 46544ca0ef..d43743bc37 100644 --- a/cura/CrashHandler.py +++ b/cura/CrashHandler.py @@ -36,18 +36,14 @@ else: except ImportError: CuraDebugMode = False # [CodeStyle: Reflecting imported value] -# List of exceptions that should be considered "fatal" and abort the program. -# These are primarily some exception types that we simply cannot really recover from -# (MemoryError and SystemError) and exceptions that indicate grave errors in the -# code that cause the Python interpreter to fail (SyntaxError, ImportError). -fatal_exception_types = [ - MemoryError, - SyntaxError, - ImportError, - SystemError, +# List of exceptions that should not be considered "fatal" and abort the program. +# These are primarily some exception types that we simply skip +skip_exception_types = [ + SystemExit, + KeyboardInterrupt, + GeneratorExit ] - class CrashHandler: crash_url = "https://stats.ultimaker.com/api/cura" @@ -70,7 +66,7 @@ class CrashHandler: # If Cura has fully started, we only show fatal errors. # If Cura has not fully started yet, we always show the early crash dialog. Otherwise, Cura will just crash # without any information. - if has_started and exception_type not in fatal_exception_types: + if has_started and exception_type in skip_exception_types: return if not has_started: @@ -387,7 +383,7 @@ class CrashHandler: Application.getInstance().callLater(self._show) def _show(self): - # When the exception is not in the fatal_exception_types list, the dialog is not created, so we don't need to show it + # When the exception is in the skip_exception_types list, the dialog is not created, so we don't need to show it if self.dialog: self.dialog.exec_() os._exit(1) From 4b79770d58e4f3f608a1aa6b079f07b522262564 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 6 Dec 2018 13:42:29 +0100 Subject: [PATCH 0699/1240] Align the buttons in the output process widget The menu in the output device selector has now a rounded corner. --- resources/qml/ActionButton.qml | 5 ++++- .../ActionPanel/OutputDevicesActionButton.qml | 2 ++ .../qml/ActionPanel/OutputProcessWidget.qml | 17 ++++++++++++++--- resources/themes/cura-light/styles.qml | 2 +- 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index 54d77f7d59..fc4a1c05f4 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -7,6 +7,7 @@ import QtQuick.Controls 2.1 import QtGraphicalEffects 1.0 // For the dropshadow import UM 1.1 as UM +import Cura 1.0 as Cura Button { @@ -16,6 +17,7 @@ Button property alias textFont: buttonText.font property alias cornerRadius: backgroundRect.radius property alias tooltip: tooltip.text + property alias cornerSide: backgroundRect.cornerSide property color color: UM.Theme.getColor("primary") property color hoverColor: UM.Theme.getColor("primary_hover") @@ -82,9 +84,10 @@ Button } } - background: Rectangle + background: Cura.RoundedRectangle { id: backgroundRect + cornerSide: Cura.RoundedRectangle.Direction.All color: button.enabled ? (button.hovered ? button.hoverColor : button.color) : button.disabledColor radius: UM.Theme.getSize("action_button_radius").width border.width: UM.Theme.getSize("default_lining").width diff --git a/resources/qml/ActionPanel/OutputDevicesActionButton.qml b/resources/qml/ActionPanel/OutputDevicesActionButton.qml index 12e4ac42fd..95750e6d11 100644 --- a/resources/qml/ActionPanel/OutputDevicesActionButton.qml +++ b/resources/qml/ActionPanel/OutputDevicesActionButton.qml @@ -17,6 +17,7 @@ Item id: saveToButton height: parent.height fixedWidthMode: true + cornerSide: deviceSelectionMenu.visible ? Cura.RoundedRectangle.Direction.Left : Cura.RoundedRectangle.Direction.All anchors { @@ -44,6 +45,7 @@ Item shadowEnabled: true shadowColor: UM.Theme.getColor("primary_shadow") + cornerSide: Cura.RoundedRectangle.Direction.Right anchors { diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml index d1790b3791..1d1a1e44e1 100644 --- a/resources/qml/ActionPanel/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -101,16 +101,24 @@ Column } } - Row + Item { id: buttonRow - spacing: UM.Theme.getSize("default_margin").width - width: parent.width + anchors.right: parent.right + anchors.left: parent.left + height: UM.Theme.getSize("action_button").height Cura.SecondaryButton { id: previewStageShortcut + anchors + { + left: parent.left + right: outputDevicesButton.left + rightMargin: UM.Theme.getSize("default_margin").width + } + height: UM.Theme.getSize("action_button").height leftPadding: UM.Theme.getSize("default_margin").width rightPadding: UM.Theme.getSize("default_margin").width @@ -125,6 +133,9 @@ Column Cura.OutputDevicesActionButton { + id: outputDevicesButton + + anchors.right: parent.right width: previewStageShortcut.visible ? UM.Theme.getSize("action_button").width : parent.width height: UM.Theme.getSize("action_button").height } diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index aaa8ec18f1..30cf42859a 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -145,7 +145,7 @@ QtObject text: control.text anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter - font: UM.Theme.getFont("medium_bold") + font: UM.Theme.getFont("medium") color: { if (control.checked) From 76acb13f5962d9c185dd29aa8618f13db95c588a Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 6 Dec 2018 13:50:58 +0100 Subject: [PATCH 0700/1240] Change the play/pause button colors --- plugins/SimulationView/SimulationViewMainComponent.qml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/SimulationView/SimulationViewMainComponent.qml b/plugins/SimulationView/SimulationViewMainComponent.qml index 16b9aeaae6..16b049c921 100644 --- a/plugins/SimulationView/SimulationViewMainComponent.qml +++ b/plugins/SimulationView/SimulationViewMainComponent.qml @@ -61,10 +61,9 @@ Item iconSource: !is_simulation_playing ? "./resources/simulation_resume.svg": "./resources/simulation_pause.svg" width: UM.Theme.getSize("small_button").width height: UM.Theme.getSize("small_button").height - hoverBackgroundColor: UM.Theme.getColor("small_button_hover") - hoverColor: UM.Theme.getColor("small_button_text_hover") - color: UM.Theme.getColor("small_button_text") - iconMargin: 0.5 * UM.Theme.getSize("wide_lining").width + hoverColor: UM.Theme.getColor("slider_handle_active") + color: UM.Theme.getColor("slider_handle") + iconMargin: UM.Theme.getSize("thick_lining").width visible: !UM.SimulationView.compatibilityMode Connections From 1e699604642f69dda88185a382cae0f657580d93 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 6 Dec 2018 13:52:57 +0100 Subject: [PATCH 0701/1240] Make it possible to disable ExpandableComponent It won't show the drop-down icon then, won't do hovers and won't allow you to click on it. This will not remove the contents of the ExpandableComponent menu bar item though, so that has to be done in ConfigurationMenu.qml. Contributes to issue CURA-5876. --- resources/qml/ExpandableComponent.qml | 4 +++- resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 9bedaa940c..e42aa7e4a1 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -32,6 +32,8 @@ Item property color headerBackgroundColor: UM.Theme.getColor("action_button") property color headerHoverColor: UM.Theme.getColor("action_button_hovered") + property alias enabled: mouseArea.enabled + // Defines the alignment of the popup with respect of the headerItem, by default to the right property int popupAlignment: ExpandableComponent.PopupAlignment.AlignRight @@ -139,7 +141,7 @@ Item verticalCenter: parent.verticalCenter margins: background.padding } - visible: source != "" + visible: source != "" && base.enabled width: height height: Math.round(0.2 * base.height) color: UM.Theme.getColor("text") diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index f81176ab1a..c4671d3a3a 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -45,6 +45,7 @@ Cura.ExpandableComponent orientation: ListView.Horizontal anchors.fill: parent model: extrudersModel + visible: base.enabled delegate: Item { From 298c68c93b6a5d98f7a3847849045003f001a154 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 6 Dec 2018 13:56:06 +0100 Subject: [PATCH 0702/1240] Disable configuration menu if there are no configurations This then prevents you from dropping down into an empty menu. Contributes to issue CURA-5876. --- .../ConfigurationMenu/ConfigurationMenu.qml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index c4671d3a3a..edb74d0251 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -102,6 +102,23 @@ Cura.ExpandableComponent } } + //Disable the menu if there are no materials, variants or build plates to change. + function updateEnabled() + { + var active_definition_id = Cura.MachineManager.activeMachine.definition.id; + var has_materials = Cura.ContainerManager.getContainerMetaDataEntry(active_definition_id, "has_materials"); + var has_variants = Cura.ContainerManager.getContainerMetaDataEntry(active_definition_id, "has_variants"); + var has_buildplates = Cura.ContainerManager.getContainerMetaDataEntry(active_definition_id, "has_variant_buildplates"); + base.enabled = has_materials || has_variants || has_buildplates; //Only let it drop down if there is any configuration that you could change. + } + + Connections + { + target: Cura.MachineManager + onGlobalContainerChanged: base.updateEnabled(); + } + Component.onCompleted: updateEnabled(); + popupItem: Column { id: popupItem From 05ca0b372af64bdc198ed5cf3c64d605c7981f71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Thu, 6 Dec 2018 14:04:12 +0100 Subject: [PATCH 0703/1240] Updated TODO, printjobs are not displayed in the monitor page because data returned from cura connect api contains None's instead of printer uuid's --- plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 9dab829825..eb6fb3a789 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -299,7 +299,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self.printJobsChanged.emit() def _addPrintJob(self, job: CloudClusterPrintJob) -> None: - # TODO: somehow we don't see the queued print jobs on the monitor page yet, we have to figure out why. + # TODO: we don't see the queued print jobs on the monitor page yet because job.printer_uuid and job.assigned_to + # are always None try: printer = next(p for p in self._printers if job.printer_uuid == p.key or job.assigned_to == p.key) except StopIteration: From 85f2a7e8f692e5202f4821e25ea9a9a7d79d7501 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 6 Dec 2018 14:04:21 +0100 Subject: [PATCH 0704/1240] Move visible to Rows for extruder configs CURA-5941 So the whole row, such as "material", will be shown/hiden based on whether the machine has materials. --- .../Menus/ConfigurationMenu/CustomConfiguration.qml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index 19b7158929..ac40958a29 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -140,6 +140,7 @@ Item Row { height: UM.Theme.getSize("print_setup_item").height + visible: extrudersModel.count > 1 // If there is only one extruder, there is no point to enable/disable that. Label { @@ -149,7 +150,6 @@ Item color: UM.Theme.getColor("text") height: parent.height width: selectors.textWidth - visible: extrudersModel.count > 1 } OldControls.CheckBox @@ -158,7 +158,6 @@ Item enabled: !checked || Cura.MachineManager.numberExtrudersEnabled > 1 //Disable if it's the last enabled extruder. height: UM.Theme.getSize("setting_control").height style: UM.Theme.styles.checkbox - visible: extrudersModel.count > 1 /* Use a MouseArea to process the click on this checkbox. This is necessary because actually clicking the checkbox @@ -177,6 +176,8 @@ Item Row { height: UM.Theme.getSize("print_setup_item").height + visible: Cura.MachineManager.hasMaterials + Label { text: catalog.i18nc("@label", "Material") @@ -185,7 +186,6 @@ Item color: UM.Theme.getColor("text") height: parent.height width: selectors.textWidth - visible: materialSelection.visible } OldControls.ToolButton @@ -197,7 +197,6 @@ Item text: Cura.MachineManager.activeStack != null ? Cura.MachineManager.activeStack.material.name : "" tooltip: text - visible: Cura.MachineManager.hasMaterials height: UM.Theme.getSize("setting_control").height width: selectors.controlWidth @@ -214,6 +213,7 @@ Item Row { height: UM.Theme.getSize("print_setup_item").height + visible: Cura.MachineManager.hasVariants Label { @@ -223,15 +223,13 @@ Item color: UM.Theme.getColor("text") height: parent.height width: selectors.textWidth - visible: variantSelection.visible } OldControls.ToolButton { id: variantSelection text: Cura.MachineManager.activeVariantName - tooltip: Cura.MachineManager.activeVariantName; - visible: Cura.MachineManager.hasVariants + tooltip: Cura.MachineManager.activeVariantName height: UM.Theme.getSize("setting_control").height width: selectors.controlWidth From 54def4edee6d743eaf43988a8b2568bebc8fee53 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 6 Dec 2018 14:12:35 +0100 Subject: [PATCH 0705/1240] Revert "Moved qml pages of toolbox to a loader" This reverts commit 838949dac74831e38e9f07fe7d628af7807320db. --- plugins/Toolbox/resources/qml/Toolbox.qml | 55 +++++++++++---------- plugins/Toolbox/src/Toolbox.py | 59 ++++++++++++----------- 2 files changed, 62 insertions(+), 52 deletions(-) diff --git a/plugins/Toolbox/resources/qml/Toolbox.qml b/plugins/Toolbox/resources/qml/Toolbox.qml index b2bab4355a..7cc5a730f2 100644 --- a/plugins/Toolbox/resources/qml/Toolbox.qml +++ b/plugins/Toolbox/resources/qml/Toolbox.qml @@ -44,31 +44,36 @@ Window top: header.bottom bottom: footer.top } - - Loader + // TODO: This could be improved using viewFilter instead of viewCategory + ToolboxLoadingPage { - anchors.fill:parent - source: - { - if(toolbox.viewCategory == "installed") - { - return "ToolboxInstalledPage.qml" - } - - switch (toolbox.viewPage) - { - case "loading": - return "ToolboxLoadingPage.qml" - case "errored": - return "ToolboxErrorPage.qml" - case "overview": - return "ToolboxDownloadsPage.qml" - case "detail": - return "ToolboxDetailPage.qml" - case "author": - return "ToolboxAuthorPage.qml" - } - } + id: viewLoading + visible: toolbox.viewCategory != "installed" && toolbox.viewPage == "loading" + } + ToolboxErrorPage + { + id: viewErrored + visible: toolbox.viewCategory != "installed" && toolbox.viewPage == "errored" + } + ToolboxDownloadsPage + { + id: viewDownloads + visible: toolbox.viewCategory != "installed" && toolbox.viewPage == "overview" + } + ToolboxDetailPage + { + id: viewDetail + visible: toolbox.viewCategory != "installed" && toolbox.viewPage == "detail" + } + ToolboxAuthorPage + { + id: viewAuthor + visible: toolbox.viewCategory != "installed" && toolbox.viewPage == "author" + } + ToolboxInstalledPage + { + id: installedPluginList + visible: toolbox.viewCategory == "installed" } } @@ -90,7 +95,7 @@ Window licenseDialog.show(); } } - + ToolboxLicenseDialog { id: licenseDialog diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 68919cf987..ef67dc3c86 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -622,39 +622,44 @@ class Toolbox(QObject, Extension): if reply.operation() == QNetworkAccessManager.GetOperation: for response_type, url in self._request_urls.items(): - if reply.url() != url: - continue + if reply.url() == url: + if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) == 200: + try: + json_data = json.loads(bytes(reply.readAll()).decode("utf-8")) - if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) == 200: - try: - json_data = json.loads(bytes(reply.readAll()).decode("utf-8")) - except json.decoder.JSONDecodeError: - Logger.log("w", "Received invalid JSON for %s.", response_type) - break + # Check for errors: + if "errors" in json_data: + for error in json_data["errors"]: + Logger.log("e", "%s", error["title"]) + return - # Check for errors: - if "errors" in json_data: - for error in json_data["errors"]: - Logger.log("e", "%s", error["title"]) - return + # Create model and apply metadata: + if not self._models[response_type]: + Logger.log("e", "Could not find the %s model.", response_type) + break + + self._server_response_data[response_type] = json_data["data"] + self._models[response_type].setMetadata(self._server_response_data[response_type]) - self._server_response_data[response_type] = json_data["data"] - self._models[response_type].setMetadata(json_data["data"]) + if response_type is "packages": + self._models[response_type].setFilter({"type": "plugin"}) + self.reBuildMaterialsModels() + self.reBuildPluginsModels() + elif response_type is "authors": + self._models[response_type].setFilter({"package_types": "material"}) + self._models[response_type].setFilter({"tags": "generic"}) - if response_type is "packages": - self._models["packages"].setFilter({"type": "plugin"}) - self.reBuildMaterialsModels() - self.reBuildPluginsModels() - elif response_type is "authors": - self._models["authors"].setFilter({"tags": "generic"}) + self.metadataChanged.emit() - self.metadataChanged.emit() + if self.isLoadingComplete(): + self.setViewPage("overview") - if self.isLoadingComplete(): - self.setViewPage("overview") - else: - self.setViewPage("errored") - self.resetDownload() + except json.decoder.JSONDecodeError: + Logger.log("w", "Received invalid JSON for %s.", response_type) + break + else: + self.setViewPage("errored") + self.resetDownload() else: # Ignore any operation that is not a get operation pass From 05075c44ee31902bec6c9f9167acb7d00025c030 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 6 Dec 2018 14:19:34 +0100 Subject: [PATCH 0706/1240] Add renderType to some labels in the Configuration panel Contributes to CURA-5876. --- resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml | 1 + resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml | 5 ++++- .../qml/Menus/ConfigurationMenu/CustomConfiguration.qml | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml index a09d6d2ba4..68c56c7c4b 100644 --- a/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml @@ -19,6 +19,7 @@ Item font: UM.Theme.getFont("large") color: UM.Theme.getColor("text") height: contentHeight + renderType: Text.NativeRendering anchors { diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index edb74d0251..1d086acc67 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -71,6 +71,7 @@ Cura.ExpandableComponent elide: Text.ElideRight font: UM.Theme.getFont("default") color: UM.Theme.getColor("text_inactive") + renderType: Text.NativeRendering anchors { @@ -88,6 +89,7 @@ Cura.ExpandableComponent elide: Text.ElideRight font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") + renderType: Text.NativeRendering anchors { @@ -155,8 +157,9 @@ Cura.ExpandableComponent { id: separator visible: buttonBar.visible + x: -popupPadding - width: parent.width + width: base.width height: UM.Theme.getSize("default_lining").height color: UM.Theme.getColor("lining") diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index 19b7158929..8d8f84155a 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -26,6 +26,7 @@ Item font: UM.Theme.getFont("large") color: UM.Theme.getColor("text") height: contentHeight + renderType: Text.NativeRendering anchors { @@ -150,6 +151,7 @@ Item height: parent.height width: selectors.textWidth visible: extrudersModel.count > 1 + renderType: Text.NativeRendering } OldControls.CheckBox @@ -186,6 +188,7 @@ Item height: parent.height width: selectors.textWidth visible: materialSelection.visible + renderType: Text.NativeRendering } OldControls.ToolButton @@ -224,6 +227,7 @@ Item height: parent.height width: selectors.textWidth visible: variantSelection.visible + renderType: Text.NativeRendering } OldControls.ToolButton From 3c517d3fcce96c4f57f75bbbcf53a086d49f4055 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Thu, 6 Dec 2018 14:25:18 +0100 Subject: [PATCH 0707/1240] The top bar background overlaps settings bar --- resources/qml/Cura.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index f16ae9a19b..a75ab44b99 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -93,7 +93,7 @@ UM.MainWindow id: headerBackground anchors { - top: parent.top + top: applicationMenu.bottom left: parent.left right: parent.right } From 3ca749cdcbbc9526586d979acfa9e8ab9489bedc Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 6 Dec 2018 14:31:52 +0100 Subject: [PATCH 0708/1240] Add a bunch of renderTypes to labels in the DiscoverUM3Action. --- .../resources/qml/DiscoverUM3Action.qml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml b/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml index 967adfc029..bb710127fc 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml @@ -64,6 +64,7 @@ Cura.MachineAction width: parent.width text: catalog.i18nc("@title:window", "Connect to Networked Printer") wrapMode: Text.WordWrap + renderType: Text.NativeRendering font.pointSize: 18 } @@ -72,6 +73,7 @@ Cura.MachineAction id: pageDescription width: parent.width wrapMode: Text.WordWrap + renderType: Text.NativeRendering text: catalog.i18nc("@label", "To print directly to your printer over the network, please make sure your printer is connected to the network using a network cable or by connecting your printer to your WIFI network. If you don't connect Cura with your printer, you can still use a USB drive to transfer g-code files to your printer.\n\nSelect your printer from the list below:") } @@ -182,6 +184,7 @@ Cura.MachineAction text: listview.model[index].name color: parent.ListView.isCurrentItem ? palette.highlightedText : palette.text elide: Text.ElideRight + renderType: Text.NativeRendering } MouseArea @@ -204,6 +207,7 @@ Cura.MachineAction anchors.left: parent.left anchors.right: parent.right wrapMode: Text.WordWrap + renderType: Text.NativeRendering text: catalog.i18nc("@label", "If your printer is not listed, read the network printing troubleshooting guide").arg("https://ultimaker.com/en/troubleshooting"); onLinkActivated: Qt.openUrlExternally(link) } @@ -221,6 +225,7 @@ Cura.MachineAction text: base.selectedDevice ? base.selectedDevice.name : "" font: UM.Theme.getFont("large") elide: Text.ElideRight + renderType: Text.NativeRendering } Grid { @@ -231,12 +236,14 @@ Cura.MachineAction { width: Math.round(parent.width * 0.5) wrapMode: Text.WordWrap + renderType: Text.NativeRendering text: catalog.i18nc("@label", "Type") } Label { width: Math.round(parent.width * 0.5) wrapMode: Text.WordWrap + renderType: Text.NativeRendering text: { if(base.selectedDevice) @@ -268,24 +275,28 @@ Cura.MachineAction { width: Math.round(parent.width * 0.5) wrapMode: Text.WordWrap + renderType: Text.NativeRendering text: catalog.i18nc("@label", "Firmware version") } Label { width: Math.round(parent.width * 0.5) wrapMode: Text.WordWrap + renderType: Text.NativeRendering text: base.selectedDevice ? base.selectedDevice.firmwareVersion : "" } Label { width: Math.round(parent.width * 0.5) wrapMode: Text.WordWrap + renderType: Text.NativeRendering text: catalog.i18nc("@label", "Address") } Label { width: Math.round(parent.width * 0.5) wrapMode: Text.WordWrap + renderType: Text.NativeRendering text: base.selectedDevice ? base.selectedDevice.ipAddress : "" } } @@ -294,6 +305,7 @@ Cura.MachineAction { width: parent.width wrapMode: Text.WordWrap + renderType: Text.NativeRendering text:{ // The property cluster size does not exist for older UM3 devices. if(!base.selectedDevice || base.selectedDevice.clusterSize == null || base.selectedDevice.clusterSize == 1) @@ -315,6 +327,7 @@ Cura.MachineAction { width: parent.width wrapMode: Text.WordWrap + renderType: Text.NativeRendering visible: base.selectedDevice != null && !base.completeProperties text: catalog.i18nc("@label", "The printer at this address has not yet responded." ) } @@ -358,9 +371,10 @@ Cura.MachineAction Label { - text: catalog.i18nc("@alabel","Enter the IP address or hostname of your printer on the network.") + text: catalog.i18nc("@alabel", "Enter the IP address or hostname of your printer on the network.") width: parent.width wrapMode: Text.WordWrap + renderType: Text.NativeRendering } TextField From a77ad329993ae46799376689c4908de06292c801 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 6 Dec 2018 14:35:07 +0100 Subject: [PATCH 0709/1240] Move all the seperate tiles into loaders instead of the entire page Otherwise the details selection didn't work anymore and I didn't want to add more hacks. CURA-6006 --- .../resources/qml/ToolboxDetailList.qml | 7 ++++++- .../resources/qml/ToolboxDownloadsGrid.qml | 10 ++++++---- .../qml/ToolboxDownloadsShowcase.qml | 19 +++++++++++-------- .../resources/qml/ToolboxInstalledPage.qml | 12 ++++++++++-- 4 files changed, 33 insertions(+), 15 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailList.qml b/plugins/Toolbox/resources/qml/ToolboxDetailList.qml index 2e5eae098c..1700a58ebe 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailList.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailList.qml @@ -26,10 +26,15 @@ Item } height: childrenRect.height + 2 * UM.Theme.getSize("wide_margin").height spacing: UM.Theme.getSize("default_margin").height + Repeater { model: toolbox.packagesModel - delegate: ToolboxDetailTile {} + delegate: Loader + { + asynchronous: true + source: "ToolboxDetailTile.qml" + } } } } diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsGrid.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsGrid.qml index c586828969..3e2643938b 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsGrid.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsGrid.qml @@ -24,7 +24,7 @@ Column color: UM.Theme.getColor("text_medium") font: UM.Theme.getFont("medium") } - GridLayout + Grid { id: grid width: parent.width - 2 * parent.padding @@ -34,10 +34,12 @@ Column Repeater { model: gridArea.model - delegate: ToolboxDownloadsGridTile + delegate: Loader { - Layout.preferredWidth: (grid.width - (grid.columns - 1) * grid.columnSpacing) / grid.columns - Layout.preferredHeight: UM.Theme.getSize("toolbox_thumbnail_small").height + asynchronous: true + width: (grid.width - (grid.columns - 1) * grid.columnSpacing) / grid.columns + height: UM.Theme.getSize("toolbox_thumbnail_small").height + source: "ToolboxDownloadsGridTile.qml" } } } diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcase.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcase.qml index 46f5debfdd..9851128076 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcase.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcase.qml @@ -30,23 +30,26 @@ Rectangle height: childrenRect.height spacing: UM.Theme.getSize("wide_margin").width columns: 3 - anchors - { - horizontalCenter: parent.horizontalCenter - } + anchors.horizontalCenter: parent.horizontalCenter + Repeater { - model: { - if ( toolbox.viewCategory == "plugin" ) + model: + { + if (toolbox.viewCategory == "plugin") { return toolbox.pluginsShowcaseModel } - if ( toolbox.viewCategory == "material" ) + if (toolbox.viewCategory == "material") { return toolbox.materialsShowcaseModel } } - delegate: ToolboxDownloadsShowcaseTile {} + delegate: Loader + { + asynchronous: true + source: "ToolboxDownloadsShowcaseTile.qml" + } } } } diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml index e683f89823..145e544b19 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml @@ -64,7 +64,11 @@ ScrollView { id: materialList model: toolbox.pluginsInstalledModel - delegate: ToolboxInstalledTile {} + delegate: Loader + { + asynchronous: true + source: "ToolboxInstalledTile.qml" + } } } } @@ -101,7 +105,11 @@ ScrollView { id: pluginList model: toolbox.materialsInstalledModel - delegate: ToolboxInstalledTile {} + delegate: Loader + { + asynchronous: true + source: "ToolboxInstalledTile.qml" + } } } } From 46c209a9936c1b2e52a83f743f1a3b8c58aeb2d7 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 6 Dec 2018 14:45:24 +0100 Subject: [PATCH 0710/1240] Add one pixel margin to the view selector. --- resources/qml/PrinterSelector/MachineSelectorList.qml | 2 +- resources/qml/ViewsSelector.qml | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/resources/qml/PrinterSelector/MachineSelectorList.qml b/resources/qml/PrinterSelector/MachineSelectorList.qml index 5ef04b7351..54d766a6e0 100644 --- a/resources/qml/PrinterSelector/MachineSelectorList.qml +++ b/resources/qml/PrinterSelector/MachineSelectorList.qml @@ -13,7 +13,7 @@ Column Label { - text: catalog.i18nc("@label", "Network connected printers") + text: catalog.i18nc("@label", "Connected printers") visible: networkedPrintersModel.items.length > 0 leftPadding: UM.Theme.getSize("default_margin").width height: visible ? contentHeight + 2 * UM.Theme.getSize("default_margin").height : 0 diff --git a/resources/qml/ViewsSelector.qml b/resources/qml/ViewsSelector.qml index e239ea82a0..1e42a0b3ba 100644 --- a/resources/qml/ViewsSelector.qml +++ b/resources/qml/ViewsSelector.qml @@ -74,6 +74,8 @@ Cura.ExpandableComponent { id: viewSelectorPopup width: viewSelector.width - 2 * viewSelector.popupPadding + leftPadding: UM.Theme.getSize("default_lining").width + rightPadding: UM.Theme.getSize("default_lining").width // For some reason the height/width of the column gets set to 0 if this is not set... Component.onCompleted: @@ -91,7 +93,7 @@ Cura.ExpandableComponent { id: viewsSelectorButton text: model.name - width: parent.width + width: parent.width - viewSelectorPopup.leftPadding - viewSelectorPopup.rightPadding height: UM.Theme.getSize("action_button").height leftPadding: UM.Theme.getSize("default_margin").width rightPadding: UM.Theme.getSize("default_margin").width From 62c53989335d4d955be4bcb1ac07df234bbb2896 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 6 Dec 2018 14:58:28 +0100 Subject: [PATCH 0711/1240] Change buttons to use either secondary or primary CURA-6006 --- .../resources/qml/ToolboxInstalledPage.qml | 12 +-- .../qml/ToolboxInstalledTileActions.qml | 42 ++------- .../resources/qml/ToolboxProgressButton.qml | 93 +------------------ 3 files changed, 16 insertions(+), 131 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml index 145e544b19..e683f89823 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml @@ -64,11 +64,7 @@ ScrollView { id: materialList model: toolbox.pluginsInstalledModel - delegate: Loader - { - asynchronous: true - source: "ToolboxInstalledTile.qml" - } + delegate: ToolboxInstalledTile {} } } } @@ -105,11 +101,7 @@ ScrollView { id: pluginList model: toolbox.materialsInstalledModel - delegate: Loader - { - asynchronous: true - source: "ToolboxInstalledTile.qml" - } + delegate: ToolboxInstalledTile {} } } } diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml index 621ecd96ea..eb3a93f274 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml @@ -6,6 +6,8 @@ import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import UM 1.1 as UM +import Cura 1.0 as Cura + Column { property bool canUpdate: false @@ -43,44 +45,18 @@ Column visible: canUpdate } - Button + Cura.SecondaryButton { id: removeButton text: canDowngrade ? catalog.i18nc("@action:button", "Downgrade") : catalog.i18nc("@action:button", "Uninstall") visible: !model.is_bundled && model.is_installed enabled: !toolbox.isDownloading - style: ButtonStyle - { - background: Rectangle - { - implicitWidth: UM.Theme.getSize("toolbox_action_button").width - implicitHeight: UM.Theme.getSize("toolbox_action_button").height - color: "transparent" - border - { - width: UM.Theme.getSize("default_lining").width - color: - { - if (control.hovered) - { - return UM.Theme.getColor("primary_hover") - } - else - { - return UM.Theme.getColor("lining") - } - } - } - } - label: Label - { - text: control.text - color: UM.Theme.getColor("text") - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - font: UM.Theme.getFont("default") - } - } + + width: UM.Theme.getSize("toolbox_action_button").width + height: UM.Theme.getSize("toolbox_action_button").height + + fixedWidthMode: true + onClicked: toolbox.checkPackageUsageAndUninstall(model.id) Connections { diff --git a/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml b/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml index 00b0b985f5..0b574e8653 100644 --- a/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml +++ b/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml @@ -5,7 +5,7 @@ import QtQuick 2.2 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import UM 1.1 as UM - +import Cura 1.0 as Cura Item { @@ -25,9 +25,12 @@ Item width: UM.Theme.getSize("toolbox_action_button").width height: UM.Theme.getSize("toolbox_action_button").height - Button + Cura.PrimaryButton { id: button + width: UM.Theme.getSize("toolbox_action_button").width + height: UM.Theme.getSize("toolbox_action_button").height + fixedWidthMode: true text: { if (complete) @@ -58,92 +61,6 @@ Item readyAction() } } - style: ButtonStyle - { - background: Rectangle - { - implicitWidth: UM.Theme.getSize("toolbox_action_button").width - implicitHeight: UM.Theme.getSize("toolbox_action_button").height - color: - { - if (base.complete) - { - return "transparent" - } - else - { - if (control.hovered) - { - return UM.Theme.getColor("primary_hover") - } - else - { - return UM.Theme.getColor("primary") - } - } - } - border - { - width: - { - if (base.complete) - { - UM.Theme.getSize("default_lining").width - } - else - { - return 0 - } - } - color: - { - if (control.hovered) - { - return UM.Theme.getColor("primary_hover") - } - else - { - return UM.Theme.getColor("lining") - } - } - } - } - label: Label - { - text: control.text - color: - { - if (base.complete) - { - return UM.Theme.getColor("text") - } - else - { - if (control.hovered) - { - return UM.Theme.getColor("button_text_hover") - } - else - { - return UM.Theme.getColor("button_text") - } - } - } - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - font: - { - if (base.complete) - { - return UM.Theme.getFont("default") - } - else - { - return UM.Theme.getFont("default_bold") - } - } - } - } } AnimatedImage From b55ead8c89a331a2ce3902d2c7288587a1e675a7 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 6 Dec 2018 15:18:32 +0100 Subject: [PATCH 0712/1240] Fix typo Contributes to CURA-6010. --- resources/qml/Cura.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index f16ae9a19b..6a8493ff06 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -103,7 +103,7 @@ UM.MainWindow // This is the new fancy pattern Image { - id: backgourndPattern + id: backgroundPattern anchors.fill: parent fillMode: Image.Tile source: UM.Theme.getImage("header_pattern") From 4b9e6e7708b862eb30c5ddad91de6421b75f80c2 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 6 Dec 2018 16:06:22 +0100 Subject: [PATCH 0713/1240] Add the linear gradient back, that I removed in a previous commit Contributes to CURA-6010. --- resources/qml/Cura.qml | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 6a8493ff06..93d8bddf1e 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -98,7 +98,31 @@ UM.MainWindow right: parent.right } height: stageMenu.source != "" ? Math.round(mainWindowHeader.height + stageMenu.height / 2) : mainWindowHeader.height - color: UM.Theme.getColor("main_window_header_background") + + LinearGradient + { + anchors.fill: parent + start: Qt.point(0, 0) + end: Qt.point(parent.width, 0) + gradient: Gradient + { + GradientStop + { + position: 0.0 + color: UM.Theme.getColor("main_window_header_background") + } + GradientStop + { + position: 0.5 + color: UM.Theme.getColor("main_window_header_background_gradient") + } + GradientStop + { + position: 1.0 + color: UM.Theme.getColor("main_window_header_background") + } + } + } // This is the new fancy pattern Image From c39674cd0cbbc3cb8e71bd9ece5a72d0c4eefcd0 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 6 Dec 2018 16:22:26 +0100 Subject: [PATCH 0714/1240] Use upper camel case for enum options Contributes to issue CURA-5876. --- .../ConfigurationMenu/ConfigurationMenu.qml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index 1d086acc67..9bef2ca0be 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -30,8 +30,8 @@ Cura.ExpandableComponent enum ConfigurationMethod { - AUTO, - CUSTOM + Auto, + Custom } iconSource: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") @@ -134,7 +134,7 @@ Cura.ExpandableComponent is_connected = Cura.MachineManager.activeMachineNetworkKey !== "" && Cura.MachineManager.printerConnected //Re-evaluate. } - property int configuration_method: is_connected ? ConfigurationMenu.ConfigurationMethod.AUTO : ConfigurationMenu.ConfigurationMethod.CUSTOM //Auto if connected to a printer at start-up, or Custom if not. + property int configuration_method: is_connected ? ConfigurationMenu.ConfigurationMethod.Auto : ConfigurationMenu.ConfigurationMethod.Custom //Auto if connected to a printer at start-up, or Custom if not. Item { @@ -143,13 +143,13 @@ Cura.ExpandableComponent AutoConfiguration { id: autoConfiguration - visible: popupItem.configuration_method == ConfigurationMenu.ConfigurationMethod.AUTO + visible: popupItem.configuration_method == ConfigurationMenu.ConfigurationMethod.Auto } CustomConfiguration { id: customConfiguration - visible: popupItem.configuration_method == ConfigurationMenu.ConfigurationMethod.CUSTOM + visible: popupItem.configuration_method == ConfigurationMenu.ConfigurationMethod.Custom } } @@ -177,7 +177,7 @@ Cura.ExpandableComponent Cura.SecondaryButton { id: goToCustom - visible: popupItem.configuration_method == ConfigurationMenu.ConfigurationMethod.AUTO + visible: popupItem.configuration_method == ConfigurationMenu.ConfigurationMethod.Auto text: catalog.i18nc("@label", "Custom") anchors.right: parent.right @@ -185,18 +185,18 @@ Cura.ExpandableComponent iconSource: UM.Theme.getIcon("arrow_right") isIconOnRightSide: true - onClicked: popupItem.configuration_method = ConfigurationMenu.ConfigurationMethod.CUSTOM + onClicked: popupItem.configuration_method = ConfigurationMenu.ConfigurationMethod.Custom } Cura.SecondaryButton { id: goToAuto - visible: popupItem.configuration_method == ConfigurationMenu.ConfigurationMethod.CUSTOM + visible: popupItem.configuration_method == ConfigurationMenu.ConfigurationMethod.Custom text: catalog.i18nc("@label", "Configurations") iconSource: UM.Theme.getIcon("arrow_left") - onClicked: popupItem.configuration_method = ConfigurationMenu.ConfigurationMethod.AUTO + onClicked: popupItem.configuration_method = ConfigurationMenu.ConfigurationMethod.Auto } } } From 4e2ab163ed5dffec65a7328820e494434def97e5 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 6 Dec 2018 16:24:12 +0100 Subject: [PATCH 0715/1240] Add login fequired link to packages that have the login-required tag CURA-6006 --- .../qml/ToolboxDetailTileActions.qml | 22 ++++++++++++++++++- plugins/Toolbox/src/PackagesModel.py | 2 ++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml b/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml index 72a9d14dcd..8a11b402d2 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml @@ -5,11 +5,14 @@ import QtQuick 2.7 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import UM 1.1 as UM +import Cura 1.1 as Cura Column { property bool installed: toolbox.isInstalled(model.id) property bool canUpdate: toolbox.canUpdate(model.id) + property bool loginRequired: model.login_required && !Cura.API.account.isLoggedIn + width: UM.Theme.getSize("toolbox_action_button").width spacing: UM.Theme.getSize("narrow_margin").height @@ -28,11 +31,28 @@ Column onCompleteAction: toolbox.viewCategory = "installed" // Don't allow installing while another download is running - enabled: installed || !(toolbox.isDownloading && toolbox.activePackage != model) + enabled: installed || (!(toolbox.isDownloading && toolbox.activePackage != model) && !loginRequired) opacity: enabled ? 1.0 : 0.5 visible: !updateButton.visible // Don't show when the update button is visible } + + Label + { + wrapMode: Text.WordWrap + text:"Log in is required to install" + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + linkColor: UM.Theme.getColor("text_link") + visible: loginRequired + width: installButton.width + MouseArea + { + anchors.fill: parent + onClicked:Cura.API.account.login() + } + } + ToolboxProgressButton { id: updateButton diff --git a/plugins/Toolbox/src/PackagesModel.py b/plugins/Toolbox/src/PackagesModel.py index f941804653..bcc02955a2 100644 --- a/plugins/Toolbox/src/PackagesModel.py +++ b/plugins/Toolbox/src/PackagesModel.py @@ -40,6 +40,7 @@ class PackagesModel(ListModel): self.addRoleName(Qt.UserRole + 19, "tags") self.addRoleName(Qt.UserRole + 20, "links") self.addRoleName(Qt.UserRole + 21, "website") + self.addRoleName(Qt.UserRole + 22, "login_required") # List of filters for queries. The result is the union of the each list of results. self._filter = {} # type: Dict[str, str] @@ -100,6 +101,7 @@ class PackagesModel(ListModel): "tags": package["tags"] if "tags" in package else [], "links": links_dict, "website": package["website"] if "website" in package else None, + "login_required": "login-required" in package.get("tags", []) }) # Filter on all the key-word arguments. From 373c953dbf565abcda33919f226af6e101e7ca19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Thu, 6 Dec 2018 16:25:04 +0100 Subject: [PATCH 0716/1240] Showing print queue, works with multiple printers in cluster, Add TODO --- .../src/Cloud/CloudOutputDevice.py | 36 ++++++++++++------- .../src/Cloud/Models/CloudClusterPrintJob.py | 8 ++--- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index eb6fb3a789..fd21d32256 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -18,6 +18,7 @@ from cura.CuraApplication import CuraApplication from cura.PrinterOutput.NetworkedPrinterOutputDevice import AuthState, NetworkedPrinterOutputDevice from cura.PrinterOutput.PrinterOutputController import PrinterOutputController from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel +from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputController import CloudOutputController from ..MeshFormatHandler import MeshFormatHandler from ..UM3PrintJobOutputModel import UM3PrintJobOutputModel from .CloudApiClient import CloudApiClient @@ -266,8 +267,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self._printers.remove(removed_printer) for added_printer in added_printers: - controller = PrinterOutputController(self) - self._printers.append(added_printer.createOutputModel(controller)) + self._printers.append(added_printer.createOutputModel(CloudOutputController(self))) for model, printer in updated_printers: printer.updateOutputModel(model) @@ -276,7 +276,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): if not self._active_printer: self.setActivePrinter(self._printers[0]) - self._clusterPrintersChanged.emit() + if removed_printers or added_printers or updated_printers: + self._clusterPrintersChanged.emit() def _updatePrintJobs(self, jobs: List[CloudClusterPrintJob]) -> None: received = {j.uuid: j for j in jobs} # type: Dict[str, CloudClusterPrintJob] @@ -284,6 +285,9 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): removed_jobs, added_jobs, updated_jobs = findChanges(previous, received) + # TODO: we see that not all data in the UI is correctly updated when the queue and active jobs change. + # TODO: we need to fix this here somehow by updating the correct output models. + for removed_job in removed_jobs: self._print_jobs.remove(removed_job) @@ -292,24 +296,30 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): for model, job in updated_jobs: job.updateOutputModel(model) + self._updatePrintJobDetails(model) # We only have to update when jobs are added or removed # updated jobs push their changes via their output model - if added_jobs or removed_jobs: + if added_jobs or removed_jobs or updated_jobs: self.printJobsChanged.emit() def _addPrintJob(self, job: CloudClusterPrintJob) -> None: - # TODO: we don't see the queued print jobs on the monitor page yet because job.printer_uuid and job.assigned_to - # are always None - try: - printer = next(p for p in self._printers if job.printer_uuid == p.key or job.assigned_to == p.key) - except StopIteration: - return Logger.log("w", "Missing printer %s for job %s in %s", job.printer_uuid, job.uuid, - [p.key for p in self._printers]) - - print_job = job.createOutputModel(printer) + print_job = job.createOutputModel(CloudOutputController(self)) + self._updatePrintJobDetails(print_job) self._print_jobs.append(print_job) + def _updatePrintJobDetails(self, print_job: UM3PrintJobOutputModel): + printer = None + try: + printer = next(p for p in self._printers if print_job.assignedPrinter == p.key) + except StopIteration: + Logger.log("w", "Missing printer %s for job %s in %s", print_job.assignedPrinter, print_job.key, + [p.key for p in self._printers]) + + if printer: + printer.updateActivePrintJob(print_job) + print_job.updateAssignedPrinter(printer) + def _onPrintJobCreated(self, mesh: bytes, job_response: CloudJobResponse) -> None: self._api.uploadMesh(job_response, mesh, self._onPrintJobUploaded, self._updateUploadProgress, lambda _: self._onUploadError(T.UPLOAD_ERROR)) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py index f4d211f8f4..cf3e28aef7 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py @@ -2,7 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. from typing import List, Optional -from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel +from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputController import CloudOutputController from .CloudClusterPrinterConfiguration import CloudClusterPrinterConfiguration from .CloudClusterPrintJobConstraint import CloudClusterPrintJobConstraint from ...Models import BaseModel @@ -38,11 +38,9 @@ class CloudClusterPrintJob(BaseModel): ## Creates an UM3 print job output model based on this cloud cluster print job. # \param printer: The output model of the printer - def createOutputModel(self, printer: PrinterOutputModel) -> UM3PrintJobOutputModel: - model = UM3PrintJobOutputModel(printer.getController(), self.uuid, self.name) + def createOutputModel(self, controller: CloudOutputController) -> UM3PrintJobOutputModel: + model = UM3PrintJobOutputModel(controller, self.uuid, self.name) # TODO: implement more data as shown in ClusterUM3OutputDevice._createPrintJobModel - model.updateAssignedPrinter(printer) - printer.updateActivePrintJob(model) return model ## Updates an UM3 print job output model based on this cloud cluster print job. From 05b32548f38b279da211279d753016ac19ef74dd Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 6 Dec 2018 16:27:56 +0100 Subject: [PATCH 0717/1240] Code style: Start comments with a space Contributes to issue CURA-5876. --- cura/Settings/ExtruderStack.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/Settings/ExtruderStack.py b/cura/Settings/ExtruderStack.py index d626ef06da..edb0e7d41f 100644 --- a/cura/Settings/ExtruderStack.py +++ b/cura/Settings/ExtruderStack.py @@ -52,8 +52,8 @@ class ExtruderStack(CuraContainerStack): return super().getNextStack() def setEnabled(self, enabled: bool) -> None: - if self.getMetaDataEntry("enabled", True) == enabled: #No change. - return #Don't emit a signal then. + if self.getMetaDataEntry("enabled", True) == enabled: # No change. + return # Don't emit a signal then. self.setMetaDataEntry("enabled", str(enabled)) self.enabledChanged.emit() From 2af33738d17f95c87dcb8c826b0940576e4359c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Thu, 6 Dec 2018 16:32:00 +0100 Subject: [PATCH 0718/1240] Don't look at the timer --- .../src/Cloud/CloudOutputDeviceManager.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 5d55d30548..bc410a4a1d 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -58,10 +58,13 @@ class CloudOutputDeviceManager: # Called when the uses logs in or out def _onLoginStateChanged(self) -> None: - if self._account.isLoggedIn and not self._update_timer.isActive(): - self._update_timer.start() + if self._account.isLoggedIn: + if not self._update_timer.isActive(): + self._update_timer.start() else: - self._update_timer.stop() + if self._update_timer.isActive(): + self._update_timer.stop() + # Notify that all clusters have disappeared self._onGetRemoteClustersFinished([]) From 4b8e3c32cbd46ef2f4ff63ca6a53654d2c22b0ec Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 6 Dec 2018 16:37:47 +0100 Subject: [PATCH 0719/1240] Also show the login required if an update is needed CURA-6006 --- plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml b/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml index 8a11b402d2..37d9bce4c5 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml @@ -40,7 +40,7 @@ Column Label { wrapMode: Text.WordWrap - text:"Log in is required to install" + text: catalog.i18nc("@label:The string between and is the highlighted link", "Log in is required to install or update") font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") linkColor: UM.Theme.getColor("text_link") @@ -49,7 +49,7 @@ Column MouseArea { anchors.fill: parent - onClicked:Cura.API.account.login() + onClicked: Cura.API.account.login() } } @@ -68,7 +68,7 @@ Column } onActiveAction: toolbox.cancelDownload() // Don't allow installing while another download is running - enabled: !(toolbox.isDownloading && toolbox.activePackage != model) + enabled: !(toolbox.isDownloading && toolbox.activePackage != model) && !loginRequired opacity: enabled ? 1.0 : 0.5 visible: canUpdate } From 717fb260c15b2364e28d2de6ab4200d2c148a697 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 6 Dec 2018 16:58:01 +0100 Subject: [PATCH 0720/1240] Change toolbox tabs to controls2 CURA-6006 --- .../resources/qml/ToolboxTabButton.qml | 75 +++++++++---------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxTabButton.qml b/plugins/Toolbox/resources/qml/ToolboxTabButton.qml index b671d779f8..fa4f75d6fe 100644 --- a/plugins/Toolbox/resources/qml/ToolboxTabButton.qml +++ b/plugins/Toolbox/resources/qml/ToolboxTabButton.qml @@ -2,50 +2,49 @@ // Toolbox is released under the terms of the LGPLv3 or higher. import QtQuick 2.2 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.0 import UM 1.1 as UM Button { + id: control property bool active: false - style: ButtonStyle + hoverEnabled: true + + background: Item { - background: Rectangle + implicitWidth: UM.Theme.getSize("toolbox_header_tab").width + implicitHeight: UM.Theme.getSize("toolbox_header_tab").height + Rectangle { - color: "transparent" - implicitWidth: UM.Theme.getSize("toolbox_header_tab").width - implicitHeight: UM.Theme.getSize("toolbox_header_tab").height - Rectangle - { - visible: control.active - color: UM.Theme.getColor("toolbox_header_highlight_hover") - anchors.bottom: parent.bottom - width: parent.width - height: UM.Theme.getSize("toolbox_header_highlight").height - } - } - label: Label - { - text: control.text - color: - { - if(control.hovered) - { - return UM.Theme.getColor("toolbox_header_button_text_hovered"); - } - if(control.active) - { - return UM.Theme.getColor("toolbox_header_button_text_active"); - } - else - { - return UM.Theme.getColor("toolbox_header_button_text_inactive"); - } - } - font: control.enabled ? (control.active ? UM.Theme.getFont("medium_bold") : UM.Theme.getFont("medium")) : UM.Theme.getFont("default_italic") - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter + visible: control.active + color: UM.Theme.getColor("primary") + anchors.bottom: parent.bottom + width: parent.width + height: UM.Theme.getSize("toolbox_header_highlight").height } } -} + contentItem: Label + { + id: label + text: control.text + color: + { + if(control.hovered) + { + return UM.Theme.getColor("toolbox_header_button_text_hovered"); + } + if(control.active) + { + return UM.Theme.getColor("toolbox_header_button_text_active"); + } + else + { + return UM.Theme.getColor("toolbox_header_button_text_inactive"); + } + } + font: control.enabled ? (control.active ? UM.Theme.getFont("medium_bold") : UM.Theme.getFont("medium")) : UM.Theme.getFont("default_italic") + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } +} \ No newline at end of file From 3ebefa4f8a232b87a5885937aeaf5818f0573271 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Thu, 6 Dec 2018 16:59:09 +0100 Subject: [PATCH 0721/1240] Add more TODO comments to clarify --- plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py | 2 ++ .../UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index fd21d32256..9f5857dff6 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -287,6 +287,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # TODO: we see that not all data in the UI is correctly updated when the queue and active jobs change. # TODO: we need to fix this here somehow by updating the correct output models. + # TODO: also the configuration drop down in the slice window is not populated because we are missing some data. + # TODO: to fix this we need to implement more data as shown in ClusterUM3OutputDevice._createPrintJobModel for removed_job in removed_jobs: self._print_jobs.remove(removed_job) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py index cf3e28aef7..c9255b8da8 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py @@ -40,7 +40,6 @@ class CloudClusterPrintJob(BaseModel): # \param printer: The output model of the printer def createOutputModel(self, controller: CloudOutputController) -> UM3PrintJobOutputModel: model = UM3PrintJobOutputModel(controller, self.uuid, self.name) - # TODO: implement more data as shown in ClusterUM3OutputDevice._createPrintJobModel return model ## Updates an UM3 print job output model based on this cloud cluster print job. From 2c9c9d8c962607311488a9a14526fb286de543a9 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 6 Dec 2018 17:23:02 +0100 Subject: [PATCH 0722/1240] Handle non-happy path for downloading CURA-6006 --- plugins/Toolbox/src/Toolbox.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index ef67dc3c86..ab975548ce 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -671,6 +671,11 @@ class Toolbox(QObject, Extension): if bytes_sent == bytes_total: self.setIsDownloading(False) cast(QNetworkReply, self._download_reply).downloadProgress.disconnect(self._onDownloadProgress) + + # Check if the download was sucessfull + if self._download_reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200: + Logger.log("w", "Failed to download package. The following error was returned: %s", json.loads(bytes(self._download_reply.readAll()).decode("utf-8"))) + return # Must not delete the temporary file on Windows self._temp_plugin_file = tempfile.NamedTemporaryFile(mode = "w+b", suffix = ".curapackage", delete = False) file_path = self._temp_plugin_file.name From d4a255c9e5779c8131ebb9743a5adf472530f99f Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 6 Dec 2018 17:35:58 +0100 Subject: [PATCH 0723/1240] Also add the login required label at the installed plugin list CURA-6006 --- .../qml/ToolboxDetailTileActions.qml | 1 - .../qml/ToolboxInstalledTileActions.qml | 21 +++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml b/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml index 37d9bce4c5..cc32ff032d 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml @@ -36,7 +36,6 @@ Column visible: !updateButton.visible // Don't show when the update button is visible } - Label { wrapMode: Text.WordWrap diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml index eb3a93f274..39528f6437 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml @@ -6,12 +6,13 @@ import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import UM 1.1 as UM -import Cura 1.0 as Cura +import Cura 1.1 as Cura Column { property bool canUpdate: false property bool canDowngrade: false + property bool loginRequired: model.login_required && !Cura.API.account.isLoggedIn width: UM.Theme.getSize("toolbox_action_button").width spacing: UM.Theme.getSize("narrow_margin").height @@ -40,11 +41,27 @@ Column onActiveAction: toolbox.cancelDownload() // Don't allow installing while another download is running - enabled: !(toolbox.isDownloading && toolbox.activePackage != model) + enabled: !(toolbox.isDownloading && toolbox.activePackage != model) && !loginRequired opacity: enabled ? 1.0 : 0.5 visible: canUpdate } + Label + { + wrapMode: Text.WordWrap + text: catalog.i18nc("@label:The string between and is the highlighted link", "Log in is required to update") + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + linkColor: UM.Theme.getColor("text_link") + visible: loginRequired + width: updateButton.width + MouseArea + { + anchors.fill: parent + onClicked: Cura.API.account.login() + } + } + Cura.SecondaryButton { id: removeButton From eddf4e7f3d96296576e8124fcfd3a86e01adf7e3 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 6 Dec 2018 19:38:12 +0100 Subject: [PATCH 0724/1240] Simplify QML --- .../resources/qml/ToolboxInstalledPage.qml | 33 +++++++------------ 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml index e683f89823..738cdde323 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml @@ -21,44 +21,39 @@ ScrollView Column { spacing: UM.Theme.getSize("default_margin").height + visible: toolbox.pluginsInstalledModel.items.length > 0 + height: childrenRect.height + 4 * UM.Theme.getSize("default_margin").height + anchors { right: parent.right left: parent.left - leftMargin: UM.Theme.getSize("wide_margin").width - topMargin: UM.Theme.getSize("wide_margin").height - bottomMargin: UM.Theme.getSize("wide_margin").height + margins: UM.Theme.getSize("default_margin").width top: parent.top } - height: childrenRect.height + 4 * UM.Theme.getSize("default_margin").height + Label { - visible: toolbox.pluginsInstalledModel.items.length > 0 - width: parent.width + width: page.width text: catalog.i18nc("@title:tab", "Plugins") color: UM.Theme.getColor("text_medium") font: UM.Theme.getFont("medium") } Rectangle { - visible: toolbox.pluginsInstalledModel.items.length > 0 color: "transparent" width: parent.width - height: childrenRect.height + 1 * UM.Theme.getSize("default_lining").width + height: childrenRect.height + UM.Theme.getSize("default_lining").width border.color: UM.Theme.getColor("lining") border.width: UM.Theme.getSize("default_lining").width Column { - height: childrenRect.height anchors { top: parent.top right: parent.right left: parent.left - leftMargin: UM.Theme.getSize("default_margin").width - rightMargin: UM.Theme.getSize("default_margin").width - topMargin: UM.Theme.getSize("default_lining").width - bottomMargin: UM.Theme.getSize("default_lining").width + margins: UM.Theme.getSize("default_margin").width } Repeater { @@ -70,32 +65,26 @@ ScrollView } Label { - visible: toolbox.materialsInstalledModel.items.length > 0 - width: page.width text: catalog.i18nc("@title:tab", "Materials") color: UM.Theme.getColor("text_medium") font: UM.Theme.getFont("medium") } + Rectangle { - visible: toolbox.materialsInstalledModel.items.length > 0 color: "transparent" width: parent.width - height: childrenRect.height + 1 * UM.Theme.getSize("default_lining").width + height: childrenRect.height + UM.Theme.getSize("default_lining").width border.color: UM.Theme.getColor("lining") border.width: UM.Theme.getSize("default_lining").width Column { - height: Math.max( UM.Theme.getSize("wide_margin").height, childrenRect.height) anchors { top: parent.top right: parent.right left: parent.left - leftMargin: UM.Theme.getSize("default_margin").width - rightMargin: UM.Theme.getSize("default_margin").width - topMargin: UM.Theme.getSize("default_lining").width - bottomMargin: UM.Theme.getSize("default_lining").width + margins: UM.Theme.getSize("default_margin").width } Repeater { From b33ce7a50f891165fe71e964815804e2a9427f6a Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 7 Dec 2018 09:35:08 +0100 Subject: [PATCH 0725/1240] Fix alignment of download grid tile CURA-6006 --- plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml index 887140bbfa..d6eedbcde5 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml @@ -63,6 +63,8 @@ Item { width: parent.width - thumbnail.width - parent.spacing spacing: Math.floor(UM.Theme.getSize("narrow_margin").width) + anchors.top: parent.top + anchors.topMargin: UM.Theme.getSize("default_margin").height Label { id: name From 2a0954f24683066f28698b03b261721fc02847b0 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 7 Dec 2018 09:35:53 +0100 Subject: [PATCH 0726/1240] Gracefully handle the conectionError when logging in --- cura/OAuth2/AuthorizationHelpers.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/cura/OAuth2/AuthorizationHelpers.py b/cura/OAuth2/AuthorizationHelpers.py index f75ad9c9f9..762d0db069 100644 --- a/cura/OAuth2/AuthorizationHelpers.py +++ b/cura/OAuth2/AuthorizationHelpers.py @@ -81,9 +81,14 @@ class AuthorizationHelpers: # \param access_token: The encoded JWT token. # \return: Dict containing some profile data. def parseJWT(self, access_token: str) -> Optional["UserProfile"]: - token_request = requests.get("{}/check-token".format(self._settings.OAUTH_SERVER_URL), headers = { - "Authorization": "Bearer {}".format(access_token) - }) + try: + token_request = requests.get("{}/check-token".format(self._settings.OAUTH_SERVER_URL), headers = { + "Authorization": "Bearer {}".format(access_token) + }) + except ConnectionError: + # Connection was suddenly dropped. Nothing we can do about that. + Logger.logException("e", "Something failed while attempting to parse the JWT token") + return None if token_request.status_code not in (200, 201): Logger.log("w", "Could not retrieve token data from auth server: %s", token_request.text) return None From 9eb09132d66d989120d7002b212bbcd4f5816fa3 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 7 Dec 2018 09:38:02 +0100 Subject: [PATCH 0727/1240] Fix minor layout issue installed page CURA-6006 --- plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml index 738cdde323..3d5cd1c8d4 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml @@ -43,7 +43,7 @@ ScrollView { color: "transparent" width: parent.width - height: childrenRect.height + UM.Theme.getSize("default_lining").width + height: childrenRect.height + UM.Theme.getSize("default_margin").width border.color: UM.Theme.getColor("lining") border.width: UM.Theme.getSize("default_lining").width Column @@ -74,7 +74,7 @@ ScrollView { color: "transparent" width: parent.width - height: childrenRect.height + UM.Theme.getSize("default_lining").width + height: childrenRect.height + UM.Theme.getSize("default_margin").width border.color: UM.Theme.getColor("lining") border.width: UM.Theme.getSize("default_lining").width Column From eaf413997d0c5ca4b317a0d65b5aef5c44131627 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 7 Dec 2018 09:44:09 +0100 Subject: [PATCH 0728/1240] Changed the installed button to be a secondary button CURA-6006 --- .../qml/ToolboxDetailTileActions.qml | 40 ++++++++++++------- .../resources/qml/ToolboxProgressButton.qml | 1 + 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml b/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml index cc32ff032d..848acfbf4f 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml @@ -16,24 +16,36 @@ Column width: UM.Theme.getSize("toolbox_action_button").width spacing: UM.Theme.getSize("narrow_margin").height - ToolboxProgressButton + Item { - id: installButton - active: toolbox.isDownloading && toolbox.activePackage == model - complete: installed - onReadyAction: + width: installButton.width + height: installButton.height + ToolboxProgressButton { - toolbox.activePackage = model - toolbox.startDownload(model.download_url) + id: installButton + active: toolbox.isDownloading && toolbox.activePackage == model + onReadyAction: + { + toolbox.activePackage = model + toolbox.startDownload(model.download_url) + } + onActiveAction: toolbox.cancelDownload() + + // Don't allow installing while another download is running + enabled: installed || (!(toolbox.isDownloading && toolbox.activePackage != model) && !loginRequired) + opacity: enabled ? 1.0 : 0.5 + visible: !updateButton.visible && !installed// Don't show when the update button is visible } - onActiveAction: toolbox.cancelDownload() - onCompleteAction: toolbox.viewCategory = "installed" - - // Don't allow installing while another download is running - enabled: installed || (!(toolbox.isDownloading && toolbox.activePackage != model) && !loginRequired) - opacity: enabled ? 1.0 : 0.5 - visible: !updateButton.visible // Don't show when the update button is visible + Cura.SecondaryButton + { + visible: installed + onClicked: toolbox.viewCategory = "installed" + text: catalog.i18nc("@action:button", "Installed") + fixedWidthMode: true + width: installButton.width + height: installButton.height + } } Label diff --git a/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml b/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml index 0b574e8653..3ca18a52ed 100644 --- a/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml +++ b/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml @@ -7,6 +7,7 @@ import QtQuick.Controls.Styles 1.4 import UM 1.1 as UM import Cura 1.0 as Cura +// TODO; This is in quite some need for refactoring. Item { id: base From 4bffa6d90f2e6e9784c7e5508b61e00a92ecb74f Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Fri, 7 Dec 2018 10:22:32 +0100 Subject: [PATCH 0729/1240] Revert "fix merge conflict" This reverts commit ec03b012a77a766e45a43a224bbb76cd32d0915a, reversing changes made to 2af33738d17f95c87dcb8c826b0940576e4359c3. --- cura/Settings/ExtruderManager.py | 4 +- cura/Settings/ExtruderStack.py | 4 +- cura/Settings/ExtrudersModel.py | 6 +- cura/Settings/MachineManager.py | 4 +- .../ProcessSlicedLayersJob.py | 2 +- .../MachineSettingsAction.qml | 4 +- plugins/ModelChecker/ModelChecker.qml | 1 + .../PerObjectSettingsPanel.qml | 1 + .../PostProcessingPlugin.qml | 4 + plugins/PrepareStage/PrepareMenu.qml | 3 +- .../SimulationViewMainComponent.qml | 7 +- .../resources/qml/ToolboxAuthorPage.qml | 8 +- .../qml/ToolboxCompatibilityChart.qml | 2 +- .../resources/qml/ToolboxDetailPage.qml | 16 +- .../qml/ToolboxDownloadsGridTile.qml | 3 +- .../qml/ToolboxDownloadsShowcaseTile.qml | 2 + .../resources/qml/DiscoverUM3Action.qml | 16 +- .../qml/MonitorBuildplateConfiguration.qml | 2 +- .../qml/MonitorExtruderConfiguration.qml | 4 +- .../resources/qml/MonitorPrinterCard.qml | 10 +- .../src/ClusterUM3OutputDevice.py | 12 +- resources/qml/ActionButton.qml | 35 +- .../ActionPanel/OutputDevicesActionButton.qml | 4 - .../qml/ActionPanel/OutputProcessWidget.qml | 27 +- .../ActionPanel/PrintInformationWidget.qml | 3 + .../qml/ActionPanel/PrintJobInformation.qml | 8 +- .../qml/ActionPanel/SliceProcessWidget.qml | 7 +- resources/qml/Cura.qml | 89 +- resources/qml/CustomConfigurationSelector.qml | 357 ++++ resources/qml/Dialogs/AboutDialog.qml | 6 +- resources/qml/Dialogs/AddMachineDialog.qml | 5 +- resources/qml/ExpandableComponent.qml | 6 +- resources/qml/ExtruderIcon.qml | 7 +- resources/qml/IconLabel.qml | 5 +- resources/qml/IconWithText.qml | 2 + resources/qml/JobSpecs.qml | 3 +- resources/qml/MainWindow/MainWindowHeader.qml | 30 +- .../ConfigurationMenu/AutoConfiguration.qml | 39 - .../ConfigurationMenu/ConfigurationItem.qml | 212 +- .../ConfigurationListView.qml | 77 +- .../ConfigurationMenu/ConfigurationMenu.qml | 203 -- .../ConfigurationMenu/CustomConfiguration.qml | 250 --- .../PrintCoreConfiguration.qml | 107 +- .../QuickConfigurationSelector.qml | 243 +++ .../Menus/ConfigurationMenu/SyncButton.qml | 102 + resources/qml/Menus/ProfileMenu.qml | 8 +- resources/qml/ObjectsList.qml | 3 +- resources/qml/Preferences/MachinesPage.qml | 8 +- .../Materials/MaterialsBrandSection.qml | 5 +- .../Preferences/Materials/MaterialsList.qml | 8 +- .../Preferences/Materials/MaterialsSlot.qml | 2 + .../Materials/MaterialsTypeSection.qml | 2 + resources/qml/Preferences/ProfilesPage.qml | 24 +- .../qml/Preferences/SettingVisibilityPage.qml | 2 +- resources/qml/PrinterOutput/ExtruderBox.qml | 2 +- resources/qml/PrinterOutput/HeatedBedBox.qml | 2 +- .../qml/PrinterOutput/OutputDeviceHeader.qml | 4 +- .../qml/PrinterSelector/MachineSelector.qml | 60 +- .../PrinterSelector/MachineSelectorList.qml | 2 +- .../PrinterTypeLabel.qml | 2 +- resources/qml/Settings/SettingCategory.qml | 13 +- resources/qml/Settings/SettingCheckBox.qml | 4 +- .../qml/Settings/SettingOptionalExtruder.qml | 13 +- resources/qml/Settings/SettingView.qml | 11 +- resources/qml/SidebarSimple.qml | 7 +- resources/qml/Toolbar.qml | 2 +- resources/qml/ViewsSelector.qml | 6 +- resources/qml/qmldir | 1 + resources/themes/cura-dark/theme.json | 10 +- .../cura-light/images/header_pattern.svg | 1902 +---------------- .../themes/cura-light/images/logo_about.svg | 172 -- resources/themes/cura-light/styles.qml | 9 +- resources/themes/cura-light/theme.json | 51 +- 73 files changed, 1223 insertions(+), 3054 deletions(-) create mode 100644 resources/qml/CustomConfigurationSelector.qml delete mode 100644 resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml delete mode 100644 resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml delete mode 100644 resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml create mode 100644 resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml create mode 100644 resources/qml/Menus/ConfigurationMenu/SyncButton.qml rename resources/qml/{ => PrinterSelector}/PrinterTypeLabel.qml (95%) delete mode 100644 resources/themes/cura-light/images/logo_about.svg diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index b0bcf3b100..9089ba96e9 100755 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -63,7 +63,7 @@ class ExtruderManager(QObject): if not self._application.getGlobalContainerStack(): return None # No active machine, so no active extruder. try: - return self._extruder_trains[self._application.getGlobalContainerStack().getId()][str(self.activeExtruderIndex)].getId() + return self._extruder_trains[self._application.getGlobalContainerStack().getId()][str(self._active_extruder_index)].getId() except KeyError: # Extruder index could be -1 if the global tab is selected, or the entry doesn't exist if the machine definition is wrong. return None @@ -144,7 +144,7 @@ class ExtruderManager(QObject): @pyqtSlot(result = QObject) def getActiveExtruderStack(self) -> Optional["ExtruderStack"]: - return self.getExtruderStack(self.activeExtruderIndex) + return self.getExtruderStack(self._active_extruder_index) ## Get an extruder stack by index def getExtruderStack(self, index) -> Optional["ExtruderStack"]: diff --git a/cura/Settings/ExtruderStack.py b/cura/Settings/ExtruderStack.py index d626ef06da..d7faedb71c 100644 --- a/cura/Settings/ExtruderStack.py +++ b/cura/Settings/ExtruderStack.py @@ -52,8 +52,8 @@ class ExtruderStack(CuraContainerStack): return super().getNextStack() def setEnabled(self, enabled: bool) -> None: - if self.getMetaDataEntry("enabled", True) == enabled: #No change. - return #Don't emit a signal then. + if "enabled" not in self._metadata: + self.setMetaDataEntry("enabled", "True") self.setMetaDataEntry("enabled", str(enabled)) self.enabledChanged.emit() diff --git a/cura/Settings/ExtrudersModel.py b/cura/Settings/ExtrudersModel.py index 5f10ac99d4..14a8dadc69 100644 --- a/cura/Settings/ExtrudersModel.py +++ b/cura/Settings/ExtrudersModel.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018 Ultimaker B.V. +# Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot, pyqtProperty, QTimer @@ -165,7 +165,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): def __updateExtruders(self): extruders_changed = False - if self.count != 0: + if self.rowCount() != 0: extruders_changed = True items = [] @@ -177,7 +177,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): machine_extruder_count = global_container_stack.getProperty("machine_extruder_count", "value") for extruder in Application.getInstance().getExtruderManager().getActiveExtruderStacks(): - position = extruder.getMetaDataEntry("position", default = "0") + position = extruder.getMetaDataEntry("position", default = "0") # Get the position try: position = int(position) except ValueError: diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index a472cc7f09..53390ca88d 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -874,7 +874,7 @@ class MachineManager(QObject): caution_message = Message(catalog.i18nc( "@info:generic", "Settings have been changed to match the current availability of extruders: [%s]" % ", ".join(add_user_changes)), - lifetime = 0, + lifetime=0, title = catalog.i18nc("@info:title", "Settings updated")) caution_message.show() @@ -1553,7 +1553,7 @@ class MachineManager(QObject): elif word.isdigit(): abbr_machine += word else: - stripped_word = "".join(char for char in unicodedata.normalize("NFD", word.upper()) if unicodedata.category(char) != "Mn") + stripped_word = ''.join(char for char in unicodedata.normalize('NFD', word.upper()) if unicodedata.category(char) != 'Mn') # - use only the first character if the word is too long (> 3 characters) # - use the whole word if it's not too long (<= 3 characters) if len(stripped_word) > 3: diff --git a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py index 71c96880e8..594bf3a43e 100644 --- a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py +++ b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py @@ -195,7 +195,7 @@ class ProcessSlicedLayersJob(Job): if extruders: material_color_map = numpy.zeros((len(extruders), 4), dtype=numpy.float32) for extruder in extruders: - position = int(extruder.getMetaDataEntry("position", default = "0")) + position = int(extruder.getMetaDataEntry("position", default="0")) # Get the position try: default_color = ExtrudersModel.defaultColors[position] except IndexError: diff --git a/plugins/MachineSettingsAction/MachineSettingsAction.qml b/plugins/MachineSettingsAction/MachineSettingsAction.qml index c88a721a84..004b4e3cfc 100644 --- a/plugins/MachineSettingsAction/MachineSettingsAction.qml +++ b/plugins/MachineSettingsAction/MachineSettingsAction.qml @@ -1,4 +1,4 @@ -// Copyright (c) 2018 Ultimaker B.V. +// Copyright (c) 2016 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.2 @@ -23,7 +23,7 @@ Cura.MachineAction target: base.extrudersModel onModelChanged: { - var extruderCount = base.extrudersModel.count; + var extruderCount = base.extrudersModel.rowCount(); base.extruderTabsCount = extruderCount; } } diff --git a/plugins/ModelChecker/ModelChecker.qml b/plugins/ModelChecker/ModelChecker.qml index 437df29516..5e41591d6b 100644 --- a/plugins/ModelChecker/ModelChecker.qml +++ b/plugins/ModelChecker/ModelChecker.qml @@ -33,6 +33,7 @@ Button { width: UM.Theme.getSize("save_button_specs_icons").width; height: UM.Theme.getSize("save_button_specs_icons").height; + sourceSize.width: width; sourceSize.height: width; color: control.hovered ? UM.Theme.getColor("text_scene_hover") : UM.Theme.getColor("text_scene"); source: "model_checker.svg" diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml index 0e2bd88619..5d4e17a102 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml @@ -265,6 +265,7 @@ Item { anchors.verticalCenter: parent.verticalCenter width: parent.width height: width + sourceSize.width: width sourceSize.height: width color: control.hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button") source: UM.Theme.getIcon("minus") diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml index 3fa10c23b9..bd4d361d35 100644 --- a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml +++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml @@ -141,6 +141,7 @@ UM.Dialog anchors.horizontalCenter: parent.horizontalCenter width: Math.round(control.width / 2.7) height: Math.round(control.height / 2.7) + sourceSize.width: width sourceSize.height: width color: palette.text source: UM.Theme.getIcon("cross1") @@ -175,6 +176,7 @@ UM.Dialog anchors.horizontalCenter: parent.horizontalCenter width: Math.round(control.width / 2.5) height: Math.round(control.height / 2.5) + sourceSize.width: width sourceSize.height: width color: control.enabled ? palette.text : disabledPalette.text source: UM.Theme.getIcon("arrow_bottom") @@ -209,6 +211,7 @@ UM.Dialog anchors.horizontalCenter: parent.horizontalCenter width: Math.round(control.width / 2.5) height: Math.round(control.height / 2.5) + sourceSize.width: width sourceSize.height: width color: control.enabled ? palette.text : disabledPalette.text source: UM.Theme.getIcon("arrow_top") @@ -495,6 +498,7 @@ UM.Dialog anchors.horizontalCenter: parent.horizontalCenter width: Math.round(parent.width / 2) height: Math.round(parent.height / 2) + sourceSize.width: width sourceSize.height: height color: !control.enabled ? UM.Theme.getColor("action_button_disabled_text") : control.pressed ? UM.Theme.getColor("action_button_active_text") : diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index fa94bc88b2..10b4262f01 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -61,7 +61,7 @@ Item color: UM.Theme.getColor("lining") } - Cura.ConfigurationMenu + Cura.QuickConfigurationSelector { Layout.fillHeight: true Layout.fillWidth: true @@ -107,6 +107,7 @@ Item height: UM.Theme.getSize("button_icon").height color: UM.Theme.getColor("toolbar_button_text") + sourceSize.width: width sourceSize.height: height } } diff --git a/plugins/SimulationView/SimulationViewMainComponent.qml b/plugins/SimulationView/SimulationViewMainComponent.qml index 16b049c921..16b9aeaae6 100644 --- a/plugins/SimulationView/SimulationViewMainComponent.qml +++ b/plugins/SimulationView/SimulationViewMainComponent.qml @@ -61,9 +61,10 @@ Item iconSource: !is_simulation_playing ? "./resources/simulation_resume.svg": "./resources/simulation_pause.svg" width: UM.Theme.getSize("small_button").width height: UM.Theme.getSize("small_button").height - hoverColor: UM.Theme.getColor("slider_handle_active") - color: UM.Theme.getColor("slider_handle") - iconMargin: UM.Theme.getSize("thick_lining").width + hoverBackgroundColor: UM.Theme.getColor("small_button_hover") + hoverColor: UM.Theme.getColor("small_button_text_hover") + color: UM.Theme.getColor("small_button_text") + iconMargin: 0.5 * UM.Theme.getSize("wide_lining").width visible: !UM.SimulationView.compatibilityMode Connections diff --git a/plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml b/plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml index 9c1df0c49e..4aaea20813 100644 --- a/plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml @@ -86,13 +86,13 @@ Item Label { text: catalog.i18nc("@label", "Website") + ":" - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("very_small") color: UM.Theme.getColor("text_medium") } Label { text: catalog.i18nc("@label", "Email") + ":" - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("very_small") color: UM.Theme.getColor("text_medium") } } @@ -118,7 +118,7 @@ Item } return "" } - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("very_small") color: UM.Theme.getColor("text") linkColor: UM.Theme.getColor("text_link") onLinkActivated: Qt.openUrlExternally(link) @@ -134,7 +134,7 @@ Item } return "" } - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("very_small") color: UM.Theme.getColor("text") linkColor: UM.Theme.getColor("text_link") onLinkActivated: Qt.openUrlExternally(link) diff --git a/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml b/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml index d4c0ae14eb..4a6268df42 100644 --- a/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml +++ b/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml @@ -228,7 +228,7 @@ Item return result } - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("very_small") color: UM.Theme.getColor("text") linkColor: UM.Theme.getColor("text_link") onLinkActivated: Qt.openUrlExternally(link) diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml index 9e2e178b71..c5e9bb0a49 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml @@ -82,25 +82,25 @@ Item Label { text: catalog.i18nc("@label", "Version") + ":" - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("very_small") color: UM.Theme.getColor("text_medium") } Label { text: catalog.i18nc("@label", "Last updated") + ":" - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("very_small") color: UM.Theme.getColor("text_medium") } Label { text: catalog.i18nc("@label", "Author") + ":" - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("very_small") color: UM.Theme.getColor("text_medium") } Label { text: catalog.i18nc("@label", "Downloads") + ":" - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("very_small") color: UM.Theme.getColor("text_medium") } } @@ -119,7 +119,7 @@ Item Label { text: details === null ? "" : (details.version || catalog.i18nc("@label", "Unknown")) - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("very_small") color: UM.Theme.getColor("text") } Label @@ -133,7 +133,7 @@ Item var date = new Date(details.last_updated) return date.toLocaleString(UM.Preferences.getValue("general/language")) } - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("very_small") color: UM.Theme.getColor("text") } Label @@ -149,7 +149,7 @@ Item return "" + details.author_name + "" } } - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("very_small") color: UM.Theme.getColor("text") linkColor: UM.Theme.getColor("text_link") onLinkActivated: Qt.openUrlExternally(link) @@ -157,7 +157,7 @@ Item Label { text: details === null ? "" : (details.download_count || catalog.i18nc("@label", "Unknown")) - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("very_small") color: UM.Theme.getColor("text") } } diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml index 61374f9d99..887140bbfa 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml @@ -52,6 +52,7 @@ Item bottom: parent.bottom right: parent.right } + sourceSize.width: width sourceSize.height: height visible: installedPackages != 0 color: (installedPackages == packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border") @@ -80,7 +81,7 @@ Item width: parent.width wrapMode: Text.WordWrap color: UM.Theme.getColor("text_medium") - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("very_small") } } } diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml index 8a2fdc8bc8..4fb70541d2 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml @@ -48,6 +48,8 @@ Rectangle right: parent.right bottomMargin: UM.Theme.getSize("default_lining").width } + sourceSize.width: width + sourceSize.height: height visible: installedPackages != 0 color: (installedPackages == packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border") source: "../images/installed_check.svg" diff --git a/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml b/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml index bb710127fc..967adfc029 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml @@ -64,7 +64,6 @@ Cura.MachineAction width: parent.width text: catalog.i18nc("@title:window", "Connect to Networked Printer") wrapMode: Text.WordWrap - renderType: Text.NativeRendering font.pointSize: 18 } @@ -73,7 +72,6 @@ Cura.MachineAction id: pageDescription width: parent.width wrapMode: Text.WordWrap - renderType: Text.NativeRendering text: catalog.i18nc("@label", "To print directly to your printer over the network, please make sure your printer is connected to the network using a network cable or by connecting your printer to your WIFI network. If you don't connect Cura with your printer, you can still use a USB drive to transfer g-code files to your printer.\n\nSelect your printer from the list below:") } @@ -184,7 +182,6 @@ Cura.MachineAction text: listview.model[index].name color: parent.ListView.isCurrentItem ? palette.highlightedText : palette.text elide: Text.ElideRight - renderType: Text.NativeRendering } MouseArea @@ -207,7 +204,6 @@ Cura.MachineAction anchors.left: parent.left anchors.right: parent.right wrapMode: Text.WordWrap - renderType: Text.NativeRendering text: catalog.i18nc("@label", "If your printer is not listed, read the network printing troubleshooting guide").arg("https://ultimaker.com/en/troubleshooting"); onLinkActivated: Qt.openUrlExternally(link) } @@ -225,7 +221,6 @@ Cura.MachineAction text: base.selectedDevice ? base.selectedDevice.name : "" font: UM.Theme.getFont("large") elide: Text.ElideRight - renderType: Text.NativeRendering } Grid { @@ -236,14 +231,12 @@ Cura.MachineAction { width: Math.round(parent.width * 0.5) wrapMode: Text.WordWrap - renderType: Text.NativeRendering text: catalog.i18nc("@label", "Type") } Label { width: Math.round(parent.width * 0.5) wrapMode: Text.WordWrap - renderType: Text.NativeRendering text: { if(base.selectedDevice) @@ -275,28 +268,24 @@ Cura.MachineAction { width: Math.round(parent.width * 0.5) wrapMode: Text.WordWrap - renderType: Text.NativeRendering text: catalog.i18nc("@label", "Firmware version") } Label { width: Math.round(parent.width * 0.5) wrapMode: Text.WordWrap - renderType: Text.NativeRendering text: base.selectedDevice ? base.selectedDevice.firmwareVersion : "" } Label { width: Math.round(parent.width * 0.5) wrapMode: Text.WordWrap - renderType: Text.NativeRendering text: catalog.i18nc("@label", "Address") } Label { width: Math.round(parent.width * 0.5) wrapMode: Text.WordWrap - renderType: Text.NativeRendering text: base.selectedDevice ? base.selectedDevice.ipAddress : "" } } @@ -305,7 +294,6 @@ Cura.MachineAction { width: parent.width wrapMode: Text.WordWrap - renderType: Text.NativeRendering text:{ // The property cluster size does not exist for older UM3 devices. if(!base.selectedDevice || base.selectedDevice.clusterSize == null || base.selectedDevice.clusterSize == 1) @@ -327,7 +315,6 @@ Cura.MachineAction { width: parent.width wrapMode: Text.WordWrap - renderType: Text.NativeRendering visible: base.selectedDevice != null && !base.completeProperties text: catalog.i18nc("@label", "The printer at this address has not yet responded." ) } @@ -371,10 +358,9 @@ Cura.MachineAction Label { - text: catalog.i18nc("@alabel", "Enter the IP address or hostname of your printer on the network.") + text: catalog.i18nc("@alabel","Enter the IP address or hostname of your printer on the network.") width: parent.width wrapMode: Text.WordWrap - renderType: Text.NativeRendering } TextField diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml index 7edeb81a96..9ffb1eabb4 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml @@ -52,7 +52,7 @@ Item id: buildplateLabel color: "#191919" // TODO: Theme! elide: Text.ElideRight - font: UM.Theme.getFont("default") // 12pt, regular + font: UM.Theme.getFont("very_small") // 12pt, regular text: "" // FIXED-LINE-HEIGHT: diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorExtruderConfiguration.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorExtruderConfiguration.qml index 1e53191d8c..afbd4c3641 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorExtruderConfiguration.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorExtruderConfiguration.qml @@ -49,7 +49,7 @@ Item } color: "#191919" // TODO: Theme! elide: Text.ElideRight - font: UM.Theme.getFont("default") // 12pt, regular + font: UM.Theme.getFont("very_small") // 12pt, regular text: "" // FIXED-LINE-HEIGHT: @@ -66,7 +66,7 @@ Item } color: "#191919" // TODO: Theme! elide: Text.ElideRight - font: UM.Theme.getFont("default_bold") // 12pt, bold + font: UM.Theme.getFont("small") // 12pt, bold text: "" // FIXED-LINE-HEIGHT: diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml index 567fff8489..8659037cb8 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml @@ -179,15 +179,13 @@ Item color: "#414054" // TODO: Theme! font: UM.Theme.getFont("large") // 16pt, bold text: { - if (printer && printer.state == "disabled") - { + if (printer && printer.state == "disabled"){ return catalog.i18nc("@label:status", "Unavailable") } - if (printer && printer.state == "unreachable") - { - return catalog.i18nc("@label:status", "Unreachable") + if (printer && printer.state == "unreachable"){ + return catalog.i18nc("@label:status", "Unavailable") } - if (printer && printer.state == "idle") + if (printer && !printer.activePrintJob) { return catalog.i18nc("@label:status", "Idle") } diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index a5ee3bc650..cc5b128479 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -572,17 +572,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): def _createMaterialOutputModel(self, material_data: Dict[str, Any]) -> "MaterialOutputModel": material_manager = CuraApplication.getInstance().getMaterialManager() - material_group_list = material_manager.getMaterialGroupListByGUID(material_data["guid"]) - # This can happen if the connected machine has no material in one or more extruders (if GUID is empty), or the - # material is unknown to Cura, so we should return an "empty" or "unknown" material model. - if material_group_list is None: - material_name = "Empty" if len(material_data["guid"]) == 0 else "Unknown" - return MaterialOutputModel(guid = material_data["guid"], - type = material_data.get("type", ""), - color = material_data.get("color", ""), - brand = material_data.get("brand", ""), - name = material_data.get("name", material_name) - ) + material_group_list = material_manager.getMaterialGroupListByGUID(material_data["guid"]) or [] # Sort the material groups by "is_read_only = True" first, and then the name alphabetically. read_only_material_group_list = list(filter(lambda x: x.is_read_only, material_group_list)) diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index fc4a1c05f4..b9a04f3b46 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -7,17 +7,14 @@ import QtQuick.Controls 2.1 import QtGraphicalEffects 1.0 // For the dropshadow import UM 1.1 as UM -import Cura 1.0 as Cura Button { id: button - property alias iconSource: buttonIconLeft.source - property bool isIconOnRightSide: false + property alias iconSource: buttonIcon.source property alias textFont: buttonText.font property alias cornerRadius: backgroundRect.radius property alias tooltip: tooltip.text - property alias cornerSide: backgroundRect.cornerSide property color color: UM.Theme.getColor("primary") property color hoverColor: UM.Theme.getColor("primary_hover") @@ -39,21 +36,18 @@ Button // we elide the text to the right so the text will be cut off with the three dots at the end. property var fixedWidthMode: false - leftPadding: UM.Theme.getSize("default_margin").width - rightPadding: UM.Theme.getSize("default_margin").width - height: UM.Theme.getSize("action_button").height - contentItem: Row { - //Left side icon. Only displayed if !isIconOnRightSide. UM.RecolorImage { - id: buttonIconLeft + id: buttonIcon source: "" - height: buttonText.height - width: visible ? height : 0 + height: Math.round(0.6 * parent.height) + width: height + sourceSize.width: width + sourceSize.height: height color: button.hovered ? button.textHoverColor : button.textColor - visible: source != "" && !button.isIconOnRightSide + visible: source != "" anchors.verticalCenter: parent.verticalCenter } @@ -70,24 +64,11 @@ Button horizontalAlignment: Text.AlignHCenter elide: Text.ElideRight } - - //Right side icon. Only displayed if isIconOnRightSide. - UM.RecolorImage - { - id: buttonIconRight - source: buttonIconLeft.source - height: buttonText.height - width: visible ? height : 0 - color: buttonIconLeft.color - visible: source != "" && button.isIconOnRightSide - anchors.verticalCenter: buttonIconLeft.verticalCenter - } } - background: Cura.RoundedRectangle + background: Rectangle { id: backgroundRect - cornerSide: Cura.RoundedRectangle.Direction.All color: button.enabled ? (button.hovered ? button.hoverColor : button.color) : button.disabledColor radius: UM.Theme.getSize("action_button_radius").width border.width: UM.Theme.getSize("default_lining").width diff --git a/resources/qml/ActionPanel/OutputDevicesActionButton.qml b/resources/qml/ActionPanel/OutputDevicesActionButton.qml index 95750e6d11..2111038cfc 100644 --- a/resources/qml/ActionPanel/OutputDevicesActionButton.qml +++ b/resources/qml/ActionPanel/OutputDevicesActionButton.qml @@ -17,7 +17,6 @@ Item id: saveToButton height: parent.height fixedWidthMode: true - cornerSide: deviceSelectionMenu.visible ? Cura.RoundedRectangle.Direction.Left : Cura.RoundedRectangle.Direction.All anchors { @@ -45,7 +44,6 @@ Item shadowEnabled: true shadowColor: UM.Theme.getColor("primary_shadow") - cornerSide: Cura.RoundedRectangle.Direction.Right anchors { @@ -53,8 +51,6 @@ Item right: parent.right } - leftPadding: UM.Theme.getSize("narrow_margin").width //Need more space than usual here for wide text. - rightPadding: UM.Theme.getSize("narrow_margin").width tooltip: catalog.i18nc("@info:tooltip", "Select the active output device") iconSource: popup.opened ? UM.Theme.getIcon("arrow_top") : UM.Theme.getIcon("arrow_bottom") color: UM.Theme.getColor("action_panel_secondary") diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml index 1d1a1e44e1..6ab8dc6fbb 100644 --- a/resources/qml/ActionPanel/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -51,7 +51,7 @@ Column text: preSlicedData ? catalog.i18nc("@label", "No time estimation available") : PrintInformation.currentPrintTime.getDisplayString(UM.DurationFormat.Long) source: UM.Theme.getIcon("clock") - font: UM.Theme.getFont("default_bold") + font: UM.Theme.getFont("small") } Cura.IconLabel @@ -84,7 +84,7 @@ Column return totalWeights + "g · " + totalLengths.toFixed(2) + "m" } source: UM.Theme.getIcon("spool") - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("very_small") } } @@ -101,25 +101,17 @@ Column } } - Item + Row { id: buttonRow - anchors.right: parent.right - anchors.left: parent.left - height: UM.Theme.getSize("action_button").height + spacing: UM.Theme.getSize("default_margin").width + width: parent.width Cura.SecondaryButton { id: previewStageShortcut - anchors - { - left: parent.left - right: outputDevicesButton.left - rightMargin: UM.Theme.getSize("default_margin").width - } - - height: UM.Theme.getSize("action_button").height + height: UM.Theme.getSize("action_panel_button").height leftPadding: UM.Theme.getSize("default_margin").width rightPadding: UM.Theme.getSize("default_margin").width text: catalog.i18nc("@button", "Preview") @@ -133,11 +125,8 @@ Column Cura.OutputDevicesActionButton { - id: outputDevicesButton - - anchors.right: parent.right - width: previewStageShortcut.visible ? UM.Theme.getSize("action_button").width : parent.width - height: UM.Theme.getSize("action_button").height + width: previewStageShortcut.visible ? UM.Theme.getSize("action_panel_button").width : parent.width + height: UM.Theme.getSize("action_panel_button").height } } } \ No newline at end of file diff --git a/resources/qml/ActionPanel/PrintInformationWidget.qml b/resources/qml/ActionPanel/PrintInformationWidget.qml index 554273a818..25e380dea8 100644 --- a/resources/qml/ActionPanel/PrintInformationWidget.qml +++ b/resources/qml/ActionPanel/PrintInformationWidget.qml @@ -15,6 +15,9 @@ UM.RecolorImage width: UM.Theme.getSize("section_icon").width height: UM.Theme.getSize("section_icon").height + sourceSize.width: width + sourceSize.height: height + color: popup.opened ? UM.Theme.getColor("primary") : UM.Theme.getColor("text_medium") MouseArea diff --git a/resources/qml/ActionPanel/PrintJobInformation.qml b/resources/qml/ActionPanel/PrintJobInformation.qml index 8bd5d5a0d3..156111af4d 100644 --- a/resources/qml/ActionPanel/PrintJobInformation.qml +++ b/resources/qml/ActionPanel/PrintJobInformation.qml @@ -30,7 +30,7 @@ Column { text: catalog.i18nc("@label", "Time specification").toUpperCase() color: UM.Theme.getColor("primary") - font: UM.Theme.getFont("default_bold") + font: UM.Theme.getFont("small") renderType: Text.NativeRendering } @@ -61,7 +61,7 @@ Column } width: parent.width - 2 * UM.Theme.getSize("default_margin").width color: UM.Theme.getColor("text") - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("very_small") renderType: Text.NativeRendering textFormat: Text.RichText } @@ -79,7 +79,7 @@ Column { text: catalog.i18nc("@label", "Material specification").toUpperCase() color: UM.Theme.getColor("primary") - font: UM.Theme.getFont("default_bold") + font: UM.Theme.getFont("small") renderType: Text.NativeRendering } @@ -151,7 +151,7 @@ Column } width: parent.width - 2 * UM.Theme.getSize("default_margin").width color: UM.Theme.getColor("text") - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("very_small") renderType: Text.NativeRendering textFormat: Text.RichText } diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index 8f6608e15c..03d91db530 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -48,7 +48,7 @@ Column text: catalog.i18nc("@label:PrintjobStatus", "Auto slicing...") color: UM.Theme.getColor("text") - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("very_small") renderType: Text.NativeRendering } @@ -61,7 +61,7 @@ Column text: catalog.i18nc("@label:PrintjobStatus", "Unable to Slice") source: UM.Theme.getIcon("warning") color: UM.Theme.getColor("warning") - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("very_small") } // Progress bar, only visible when the backend is in the process of slice the printjob @@ -94,6 +94,7 @@ Column } } + Item { id: prepareButtons @@ -102,7 +103,7 @@ Column // Disable the slice process when width: parent.width - height: UM.Theme.getSize("action_button").height + height: UM.Theme.getSize("action_panel_button").height visible: !autoSlice Cura.PrimaryButton { diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 4e8e9ce788..3578888886 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -88,54 +88,6 @@ UM.MainWindow window: base } - Rectangle - { - id: headerBackground - anchors - { - top: applicationMenu.bottom - left: parent.left - right: parent.right - } - height: stageMenu.source != "" ? Math.round(mainWindowHeader.height + stageMenu.height / 2) : mainWindowHeader.height - - LinearGradient - { - anchors.fill: parent - start: Qt.point(0, 0) - end: Qt.point(parent.width, 0) - gradient: Gradient - { - GradientStop - { - position: 0.0 - color: UM.Theme.getColor("main_window_header_background") - } - GradientStop - { - position: 0.5 - color: UM.Theme.getColor("main_window_header_background_gradient") - } - GradientStop - { - position: 1.0 - color: UM.Theme.getColor("main_window_header_background") - } - } - } - - // This is the new fancy pattern - Image - { - id: backgroundPattern - anchors.fill: parent - fillMode: Image.Tile - source: UM.Theme.getImage("header_pattern") - horizontalAlignment: Image.AlignLeft - verticalAlignment: Image.AlignTop - } - } - MainWindowHeader { id: mainWindowHeader @@ -192,6 +144,44 @@ UM.MainWindow } } + Rectangle + { + id: stageMenuBackground + anchors + { + left: parent.left + right: parent.right + top: parent.top + } + visible: stageMenu.source != "" + height: visible ? Math.round(UM.Theme.getSize("stage_menu").height / 2) : 0 + + LinearGradient + { + anchors.fill: parent + start: Qt.point(0, 0) + end: Qt.point(parent.width, 0) + gradient: Gradient + { + GradientStop + { + position: 0.0 + color: UM.Theme.getColor("main_window_header_background") + } + GradientStop + { + position: 0.5 + color: UM.Theme.getColor("main_window_header_background_gradient") + } + GradientStop + { + position: 1.0 + color: UM.Theme.getColor("main_window_header_background") + } + } + } + } + Connections { target: stageMenu.item @@ -267,8 +257,7 @@ UM.MainWindow anchors { - // Align to the top of the stageMenu since the stageMenu may not exist - top: parent.top + top: stageMenuBackground.bottom left: parent.left right: parent.right bottom: parent.bottom diff --git a/resources/qml/CustomConfigurationSelector.qml b/resources/qml/CustomConfigurationSelector.qml new file mode 100644 index 0000000000..c78ca700da --- /dev/null +++ b/resources/qml/CustomConfigurationSelector.qml @@ -0,0 +1,357 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 1.1 +import QtQuick.Controls.Styles 1.1 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +Rectangle +{ + implicitWidth: parent.width + implicitHeight: parent.height + + id: base + color: UM.Theme.getColor("main_background") + + // Height has an extra 2x margin for the top & bottom margin. + height: childrenRect.height + 2 * UM.Theme.getSize("default_margin").width + + Cura.ExtrudersModel { id: extrudersModel } + + ListView + { + // Horizontal list that shows the extruders + id: extrudersList + visible: extrudersModel.items.length > 1 + property var index: 0 + + height: UM.Theme.getSize("configuration_selector_mode_tabs").height + boundsBehavior: Flickable.StopAtBounds + + anchors + { + left: parent.left + right: parent.right + top: parent.top + margins: UM.Theme.getSize("thick_margin").width + } + + ExclusiveGroup { id: extruderMenuGroup } + + orientation: ListView.Horizontal + + model: extrudersModel + + Connections + { + target: Cura.MachineManager + onGlobalContainerChanged: forceActiveFocus() // Changing focus applies the currently-being-typed values so it can change the displayed setting values. + } + + delegate: Button + { + height: parent.height + width: Math.round(ListView.view.width / extrudersModel.rowCount()) + + text: model.name + tooltip: model.name + exclusiveGroup: extruderMenuGroup + checked: Cura.ExtruderManager.activeExtruderIndex == index + + property bool extruder_enabled: true + + MouseArea // TODO; This really should be fixed. It makes absolutely no sense to have a button AND a mouse area. + { + anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton + onClicked: + { + switch (mouse.button) + { + case Qt.LeftButton: + extruder_enabled = Cura.MachineManager.getExtruder(model.index).isEnabled + if (extruder_enabled) + { + forceActiveFocus() // Changing focus applies the currently-being-typed values so it can change the displayed setting values. + Cura.ExtruderManager.setActiveExtruderIndex(index) + } + break + case Qt.RightButton: + extruder_enabled = Cura.MachineManager.getExtruder(model.index).isEnabled + extruderMenu.popup() + break + } + } + } + + Menu + { + id: extruderMenu + + MenuItem + { + text: catalog.i18nc("@action:inmenu", "Enable Extruder") + onTriggered: Cura.MachineManager.setExtruderEnabled(model.index, true) + visible: !extruder_enabled // using an intermediate variable prevents an empty popup that occured now and then + } + + MenuItem + { + text: catalog.i18nc("@action:inmenu", "Disable Extruder") + onTriggered: Cura.MachineManager.setExtruderEnabled(model.index, false) + visible: extruder_enabled + enabled: Cura.MachineManager.numberExtrudersEnabled > 1 + } + } + + style: ButtonStyle + { + background: Rectangle + { + anchors.fill: parent + border.width: control.checked ? UM.Theme.getSize("default_lining").width * 2 : UM.Theme.getSize("default_lining").width + border.color: + { + if (Cura.MachineManager.getExtruder(index).isEnabled) + { + if(control.checked || control.pressed) + { + return UM.Theme.getColor("action_button_active_border") + } + else if (control.hovered) + { + return UM.Theme.getColor("action_button_hovered_border") + } + return UM.Theme.getColor("action_button_border") + } + return UM.Theme.getColor("action_button_disabled_border") + } + color: + { + if (Cura.MachineManager.getExtruder(index).isEnabled) + { + if(control.checked || control.pressed) + { + return UM.Theme.getColor("action_button_active"); + } + else if (control.hovered) + { + return UM.Theme.getColor("action_button_hovered") + } + return UM.Theme.getColor("action_button") + } + return UM.Theme.getColor("action_button_disabled") + } + Behavior on color { ColorAnimation { duration: 50; } } + + Item + { + id: extruderButtonFace + anchors.centerIn: parent + width: childrenRect.width + + Label + { + // Static text that holds the "Extruder" label + id: extruderStaticText + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + + color: + { + if (Cura.MachineManager.getExtruder(index).isEnabled) + { + if(control.checked || control.pressed) + { + return UM.Theme.getColor("action_button_active_text"); + } + else if (control.hovered) + { + return UM.Theme.getColor("action_button_hovered_text") + } + return UM.Theme.getColor("action_button_text") + } + return UM.Theme.getColor("action_button_disabled_text") + } + + font: UM.Theme.getFont("large_nonbold") + text: catalog.i18nc("@label", "Extruder") + visible: width < (control.width - extruderIcon.width - UM.Theme.getSize("default_margin").width) + elide: Text.ElideRight + } + + ExtruderIcon + { + // Round icon with the extruder number and material color indicator. + id: extruderIcon + + anchors.verticalCenter: parent.verticalCenter + anchors.left: extruderStaticText.right + anchors.leftMargin: UM.Theme.getSize("default_margin").width + width: control.height - Math.round(UM.Theme.getSize("default_margin").width / 2) + height: width + + checked: control.checked + materialColor: model.color + textColor: extruderStaticText.color + } + } + } + + label: Item {} + } + } + } + + Item + { + id: materialRow + height: UM.Theme.getSize("print_setup_item").height + visible: Cura.MachineManager.hasMaterials + + anchors + { + left: parent.left + right: parent.right + top: extrudersList.bottom + margins: UM.Theme.getSize("thick_margin").width + } + + Label + { + id: materialLabel + text: catalog.i18nc("@label", "Material"); + width: Math.round(parent.width * 0.45 - UM.Theme.getSize("default_margin").width) + height: parent.height + verticalAlignment: Text.AlignVCenter + font: UM.Theme.getFont("default"); + color: UM.Theme.getColor("text"); + } + + ToolButton + { + id: materialSelection + + property var activeExtruder: Cura.MachineManager.activeStack + property var hasActiveExtruder: activeExtruder != null + property var currentRootMaterialName: hasActiveExtruder ? activeExtruder.material.name : "" + + text: currentRootMaterialName + tooltip: currentRootMaterialName + visible: Cura.MachineManager.hasMaterials + + enabled: !extrudersList.visible || Cura.ExtruderManager.activeExtruderIndex > -1 + + height: UM.Theme.getSize("setting_control").height + width: Math.round(parent.width * 0.7) + UM.Theme.getSize("thick_margin").width + anchors.right: parent.right + style: UM.Theme.styles.sidebar_header_button + activeFocusOnPress: true; + menu: Cura.MaterialMenu + { + extruderIndex: Cura.ExtruderManager.activeExtruderIndex + } + + property var valueError: hasActiveExtruder ? Cura.ContainerManager.getContainerMetaDataEntry(activeExtruder.material.id, "compatible", "") != "True" : true + property var valueWarning: ! Cura.MachineManager.isActiveQualitySupported + } + } + + Item + { + id: variantRow + height: UM.Theme.getSize("print_setup_item").height + visible: Cura.MachineManager.hasVariants + + anchors + { + left: parent.left + right: parent.right + top: materialRow.bottom + margins: UM.Theme.getSize("thick_margin").width + } + + Label + { + id: variantLabel + text: Cura.MachineManager.activeDefinitionVariantsName; + width: Math.round(parent.width * 0.45 - UM.Theme.getSize("default_margin").width) + height: parent.height + verticalAlignment: Text.AlignVCenter + font: UM.Theme.getFont("default"); + color: UM.Theme.getColor("text"); + } + + ToolButton + { + id: variantSelection + text: Cura.MachineManager.activeVariantName + tooltip: Cura.MachineManager.activeVariantName; + visible: Cura.MachineManager.hasVariants + + height: UM.Theme.getSize("setting_control").height + width: Math.round(parent.width * 0.7 + UM.Theme.getSize("thick_margin").width) + anchors.right: parent.right + style: UM.Theme.styles.sidebar_header_button + activeFocusOnPress: true; + + menu: Cura.NozzleMenu { extruderIndex: Cura.ExtruderManager.activeExtruderIndex } + } + } + + Item + { + id: materialCompatibilityLink + height: UM.Theme.getSize("print_setup_item").height + + anchors.right: parent.right + anchors.top: variantRow.bottom + anchors.margins: UM.Theme.getSize("thick_margin").width + UM.RecolorImage + { + id: warningImage + + anchors.right: materialInfoLabel.left + anchors.rightMargin: UM.Theme.getSize("default_margin").width + + source: UM.Theme.getIcon("warning") + width: UM.Theme.getSize("section_icon").width + height: UM.Theme.getSize("section_icon").height + + sourceSize.width: width + sourceSize.height: 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.right: parent.right + + MouseArea + { + anchors.fill: parent + + onClicked: + { + // open the material URL with web browser + Qt.openUrlExternally("https://ultimaker.com/incoming-links/cura/material-compatibilty"); + } + } + } + } +} diff --git a/resources/qml/Dialogs/AboutDialog.qml b/resources/qml/Dialogs/AboutDialog.qml index add84614e0..25c9bbf74b 100644 --- a/resources/qml/Dialogs/AboutDialog.qml +++ b/resources/qml/Dialogs/AboutDialog.qml @@ -35,10 +35,12 @@ UM.Dialog { id: logo width: (base.minimumWidth * 0.85) | 0 - height: (width * (UM.Theme.getSize("logo").height / UM.Theme.getSize("logo").width)) | 0 + height: (width * (1/4.25)) | 0 - source: UM.Theme.getImage("logo_about") + source: UM.Theme.getImage("logo") + sourceSize.width: width + sourceSize.height: height anchors.top: parent.top anchors.topMargin: ((base.minimumWidth - width) / 2) | 0 anchors.horizontalCenter: parent.horizontalCenter diff --git a/resources/qml/Dialogs/AddMachineDialog.qml b/resources/qml/Dialogs/AddMachineDialog.qml index f00359869c..8e966c3df7 100644 --- a/resources/qml/Dialogs/AddMachineDialog.qml +++ b/resources/qml/Dialogs/AddMachineDialog.qml @@ -1,4 +1,4 @@ -// Copyright (c) 2018 Ultimaker B.V. +// Copyright (c) 2017 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.2 @@ -156,6 +156,7 @@ UM.Dialog anchors.rightMargin: UM.Theme.getSize("default_margin").width width: UM.Theme.getSize("standard_arrow").width height: UM.Theme.getSize("standard_arrow").height + sourceSize.width: width sourceSize.height: width color: palette.windowText source: base.activeCategory == section ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_right") @@ -169,7 +170,7 @@ UM.Dialog if (machineList.model.getItem(machineList.currentIndex).section != section) { // Find the first machine from this section - for(var i = 0; i < machineList.model.count; i++) + for(var i = 0; i < machineList.model.rowCount(); i++) { var item = machineList.model.getItem(i); if (item.section == section) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index e42aa7e4a1..b438f0398c 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -32,8 +32,6 @@ Item property color headerBackgroundColor: UM.Theme.getColor("action_button") property color headerHoverColor: UM.Theme.getColor("action_button_hovered") - property alias enabled: mouseArea.enabled - // Defines the alignment of the popup with respect of the headerItem, by default to the right property int popupAlignment: ExpandableComponent.PopupAlignment.AlignRight @@ -141,7 +139,9 @@ Item verticalCenter: parent.verticalCenter margins: background.padding } - visible: source != "" && base.enabled + sourceSize.width: width + sourceSize.height: height + visible: source != "" width: height height: Math.round(0.2 * base.height) color: UM.Theme.getColor("text") diff --git a/resources/qml/ExtruderIcon.qml b/resources/qml/ExtruderIcon.qml index 49ad73a32e..c1a202050b 100644 --- a/resources/qml/ExtruderIcon.qml +++ b/resources/qml/ExtruderIcon.qml @@ -22,6 +22,8 @@ Item id: mainIcon anchors.fill: parent + sourceSize.width: parent.width + sourceSize.height: parent.height source: UM.Theme.getIcon("extruder_button") color: extruderEnabled ? materialColor: "gray" } @@ -48,9 +50,7 @@ Item id: extruderNumberText anchors.centerIn: parent text: index + 1 - font: UM.Theme.getFont("very_small") - width: contentWidth - height: contentHeight + font: UM.Theme.getFont("extruder_icon") visible: extruderEnabled renderType: Text.NativeRendering horizontalAlignment: Text.AlignHCenter @@ -62,6 +62,7 @@ Item id: disabledIcon anchors.fill: parent anchors.margins: UM.Theme.getSize("thick_lining").width + sourceSize.width: width sourceSize.height: width source: UM.Theme.getIcon("cross1") visible: !extruderEnabled diff --git a/resources/qml/IconLabel.qml b/resources/qml/IconLabel.qml index f925b6eab5..0941254e7b 100644 --- a/resources/qml/IconLabel.qml +++ b/resources/qml/IconLabel.qml @@ -31,6 +31,9 @@ Item width: UM.Theme.getSize("section_icon").width height: width + sourceSize.width: width + sourceSize.height: height + color: label.color visible: source != "" } @@ -45,7 +48,7 @@ Item text: "Empty label" elide: Text.ElideRight color: UM.Theme.getColor("text") - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("very_small") renderType: Text.NativeRendering } } \ No newline at end of file diff --git a/resources/qml/IconWithText.qml b/resources/qml/IconWithText.qml index 22599b3aed..dcb3ef7851 100644 --- a/resources/qml/IconWithText.qml +++ b/resources/qml/IconWithText.qml @@ -37,6 +37,8 @@ Item width: UM.Theme.getSize("section_icon").width height: UM.Theme.getSize("section_icon").height + sourceSize.width: width + sourceSize.height: height color: "black" anchors diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index 935cb723de..45111992c1 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -60,6 +60,7 @@ Item { { width: UM.Theme.getSize("save_button_specs_icons").width; height: UM.Theme.getSize("save_button_specs_icons").height; + sourceSize.width: width; sourceSize.height: width; color: control.hovered ? UM.Theme.getColor("text_scene_hover") : UM.Theme.getColor("text_scene"); source: UM.Theme.getIcon("pencil"); @@ -123,7 +124,7 @@ Item { } height: UM.Theme.getSize("jobspecs_line").height verticalAlignment: Text.AlignVCenter - font: UM.Theme.getFont("default_bold") + font: UM.Theme.getFont("small") color: UM.Theme.getColor("text_scene") text: CuraApplication.getSceneBoundingBoxString } diff --git a/resources/qml/MainWindow/MainWindowHeader.qml b/resources/qml/MainWindow/MainWindowHeader.qml index ae1c13d9c3..34936e9b5a 100644 --- a/resources/qml/MainWindow/MainWindowHeader.qml +++ b/resources/qml/MainWindow/MainWindowHeader.qml @@ -12,13 +12,38 @@ import QtGraphicalEffects 1.0 import "../Account" -Item +Rectangle { id: base implicitHeight: UM.Theme.getSize("main_window_header").height implicitWidth: UM.Theme.getSize("main_window_header").width + LinearGradient + { + anchors.fill: parent + start: Qt.point(0, 0) + end: Qt.point(parent.width, 0) + gradient: Gradient + { + GradientStop + { + position: 0.0 + color: UM.Theme.getColor("main_window_header_background") + } + GradientStop + { + position: 0.5 + color: UM.Theme.getColor("main_window_header_background_gradient") + } + GradientStop + { + position: 1.0 + color: UM.Theme.getColor("main_window_header_background") + } + } + } + Image { id: logo @@ -29,6 +54,9 @@ Item source: UM.Theme.getImage("logo") width: UM.Theme.getSize("logo").width height: UM.Theme.getSize("logo").height + + sourceSize.width: width + sourceSize.height: height } Row diff --git a/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml deleted file mode 100644 index 68c56c7c4b..0000000000 --- a/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2018 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.7 -import QtQuick.Controls 2.0 - -import UM 1.3 as UM -import Cura 1.0 as Cura - -Item -{ - width: parent.width - height: childrenRect.height - - Label - { - id: header - text: catalog.i18nc("@header", "Configurations") - font: UM.Theme.getFont("large") - color: UM.Theme.getColor("text") - height: contentHeight - renderType: Text.NativeRendering - - anchors - { - left: parent.left - right: parent.right - } - } - - ConfigurationListView - { - anchors.top: header.bottom - anchors.topMargin: UM.Theme.getSize("default_margin").width - width: parent.width - - outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null - } -} \ No newline at end of file diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 6ac1e6a2ad..7427b5ddff 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -7,129 +7,143 @@ import QtQuick.Controls 2.0 import UM 1.2 as UM import Cura 1.0 as Cura -Button +Rectangle { id: configurationItem property var configuration: null - hoverEnabled: true + property var selected: false + signal activateConfiguration() - height: background.height + height: childrenRect.height + border.width: UM.Theme.getSize("default_lining").width + border.color: updateBorderColor() + color: selected ? UM.Theme.getColor("configuration_item_active") : UM.Theme.getColor("configuration_item") + property var textColor: selected ? UM.Theme.getColor("configuration_item_text_active") : UM.Theme.getColor("configuration_item_text") - background: Rectangle + function updateBorderColor() { - height: childrenRect.height - color: parent.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button") - border.color: (parent.checked || parent.hovered) ? UM.Theme.getColor("primary") : UM.Theme.getColor("lining") - border.width: parent.checked ? UM.Theme.getSize("thick_lining").width : UM.Theme.getSize("default_lining").width - radius: UM.Theme.getSize("default_radius").width + border.color = selected ? UM.Theme.getColor("configuration_item_border_active") : UM.Theme.getColor("configuration_item_border") + } - Column + Column + { + id: contentColumn + width: parent.width + padding: UM.Theme.getSize("default_margin").width + spacing: Math.round(UM.Theme.getSize("default_margin").height / 2) + + Row { - id: contentColumn - width: parent.width - padding: UM.Theme.getSize("wide_margin").width - spacing: Math.round(UM.Theme.getSize("default_margin").height / 2) + id: extruderRow - Row + width: parent.width - 2 * parent.padding + height: childrenRect.height + + spacing: UM.Theme.getSize("default_margin").width + + Repeater { - id: extruderRow - - anchors - { - left: parent.left - leftMargin: parent.padding - right: parent.right - rightMargin: parent.padding - } + id: repeater height: childrenRect.height - - spacing: UM.Theme.getSize("default_margin").width - - Repeater + model: configuration.extruderConfigurations + delegate: PrintCoreConfiguration { - id: repeater - height: childrenRect.height - model: configuration.extruderConfigurations - delegate: PrintCoreConfiguration - { - width: Math.round(parent.width / 2) - printCoreConfiguration: modelData - } - } - } - - //Buildplate row separator - Rectangle - { - id: separator - - visible: buildplateInformation.visible - anchors - { - left: parent.left - leftMargin: parent.padding - right: parent.right - rightMargin: parent.padding - } - height: visible ? Math.round(UM.Theme.getSize("thick_lining").height / 2) : 0 - color: UM.Theme.getColor("text") - } - - Item - { - id: buildplateInformation - - anchors - { - left: parent.left - leftMargin: parent.padding - right: parent.right - rightMargin: parent.padding - } - height: childrenRect.height - visible: configuration.buildplateConfiguration != "" - - UM.RecolorImage - { - id: buildplateIcon - anchors.left: parent.left - width: UM.Theme.getSize("main_window_header_button_icon").width - height: UM.Theme.getSize("main_window_header_button_icon").height - source: UM.Theme.getIcon("buildplate") - color: UM.Theme.getColor("text") - } - - Label - { - id: buildplateLabel - anchors.left: buildplateIcon.right - anchors.verticalCenter: buildplateIcon.verticalCenter - anchors.leftMargin: Math.round(UM.Theme.getSize("default_margin").height / 2) - text: configuration.buildplateConfiguration - renderType: Text.NativeRendering - color: UM.Theme.getColor("text") + width: Math.round(parent.width / 2) + printCoreConfiguration: modelData + mainColor: textColor } } } - Connections + //Buildplate row separator + Rectangle { - target: Cura.MachineManager - onCurrentConfigurationChanged: - { - configurationItem.checked = Cura.MachineManager.matchesConfiguration(configuration) - } + id: separator + + visible: buildplateInformation.visible + width: parent.width - 2 * parent.padding + height: visible ? Math.round(UM.Theme.getSize("thick_lining").height / 2) : 0 + color: textColor } - Component.onCompleted: + Item { - configurationItem.checked = Cura.MachineManager.matchesConfiguration(configuration) + id: buildplateInformation + width: parent.width - 2 * parent.padding + height: childrenRect.height + visible: configuration.buildplateConfiguration != "" + + UM.RecolorImage { + id: buildplateIcon + anchors.left: parent.left + width: UM.Theme.getSize("main_window_header_button_icon").width + height: UM.Theme.getSize("main_window_header_button_icon").height + sourceSize.width: width + sourceSize.height: height + source: UM.Theme.getIcon("buildplate") + color: textColor + } + + Label + { + id: buildplateLabel + anchors.left: buildplateIcon.right + anchors.verticalCenter: buildplateIcon.verticalCenter + anchors.leftMargin: Math.round(UM.Theme.getSize("default_margin").height / 2) + text: configuration.buildplateConfiguration + renderType: Text.NativeRendering + color: textColor + } } } - onClicked: + MouseArea { - Cura.MachineManager.applyRemoteConfiguration(configuration) + id: mouse + anchors.fill: parent + onClicked: activateConfiguration() + cursorShape: Qt.PointingHandCursor + hoverEnabled: true + onEntered: + { + parent.border.color = UM.Theme.getColor("configuration_item_border_hover") + if (configurationItem.selected == false) + { + configurationItem.color = UM.Theme.getColor("wide_lining") + } + } + onExited: + { + updateBorderColor() + if (configurationItem.selected == false) + { + configurationItem.color = UM.Theme.getColor("configuration_item") + } + } + } + + Connections + { + target: Cura.MachineManager + onCurrentConfigurationChanged: { + configurationItem.selected = Cura.MachineManager.matchesConfiguration(configuration) + updateBorderColor() + } + } + + Component.onCompleted: + { + configurationItem.selected = Cura.MachineManager.matchesConfiguration(configuration) + updateBorderColor() + } + + onVisibleChanged: + { + if(visible) + { + // I cannot trigger function updateBorderColor() after visibility change + color = selected ? UM.Theme.getColor("configuration_item_active") : UM.Theme.getColor("configuration_item") + } } } \ No newline at end of file diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml index e7936b69d2..210ff6057f 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml @@ -2,7 +2,8 @@ // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.7 -import QtQuick.Controls 2.3 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 import UM 1.2 as UM import Cura 1.0 as Cura @@ -11,7 +12,9 @@ Column { id: base property var outputDevice: null + property var computedHeight: container.height + configurationListHeading.height + 3 * padding height: childrenRect.height + 2 * padding + padding: UM.Theme.getSize("default_margin").width spacing: Math.round(UM.Theme.getSize("default_margin").height / 2) function forceModelUpdate() @@ -24,60 +27,60 @@ Column } } + Label + { + id: configurationListHeading + text: catalog.i18nc("@label:header configurations", "Available configurations") + font: UM.Theme.getFont("large") + width: parent.width - 2 * parent.padding + color: UM.Theme.getColor("configuration_item_text") + } + + Component + { + id: sectionHeading + Rectangle + { + height: childrenRect.height + UM.Theme.getSize("default_margin").height + Label + { + text: section + font: UM.Theme.getFont("default_bold") + color: UM.Theme.getColor("configuration_item_text") + } + } + } + ScrollView { id: container - width: parent.width - readonly property int maximumHeight: 350 * screenScaleFactor - height: Math.round(Math.min(configurationList.height, maximumHeight)) - contentHeight: configurationList.height - clip: true + width: parent.width - parent.padding + height: Math.min(configurationList.contentHeight, 350 * screenScaleFactor) - ScrollBar.vertical.policy: (configurationList.height > maximumHeight) ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff //The AsNeeded policy also hides it when the cursor is away, and we don't want that. - ScrollBar.vertical.background: Rectangle - { - implicitWidth: UM.Theme.getSize("scrollbar").width - radius: width / 2 - color: UM.Theme.getColor("scrollbar_background") - } - ScrollBar.vertical.contentItem: Rectangle - { - implicitWidth: UM.Theme.getSize("scrollbar").width - radius: width / 2 - color: UM.Theme.getColor(parent.pressed ? "scrollbar_handle_down" : parent.hovered ? "scrollbar_handle_hover" : "scrollbar_handle") - } - - ButtonGroup - { - buttons: configurationList.children - } + style: UM.Theme.styles.scrollview + __wheelAreaScrollSpeed: 75 // Scroll three lines in one scroll event ListView { id: configurationList spacing: Math.round(UM.Theme.getSize("default_margin").height / 2) - width: container.width - ((height > container.maximumHeight) ? container.ScrollBar.vertical.background.width : 0) //Make room for scroll bar if there is any. + width: container.width contentHeight: childrenRect.height - height: childrenRect.height section.property: "modelData.printerType" section.criteria: ViewSection.FullString - section.delegate: Item - { - height: printerTypeLabel.height + UM.Theme.getSize("default_margin").height - Cura.PrinterTypeLabel - { - id: printerTypeLabel - text: Cura.MachineManager.getAbbreviatedMachineName(section) - } - } + section.delegate: sectionHeading model: (outputDevice != null) ? outputDevice.uniqueConfigurations : [] - delegate: ConfigurationItem { - width: parent.width + width: parent.width - UM.Theme.getSize("default_margin").width configuration: modelData + onActivateConfiguration: + { + switchPopupState() + Cura.MachineManager.applyRemoteConfiguration(configuration) + } } } } diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml deleted file mode 100644 index 1d086acc67..0000000000 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright (c) 2018 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.7 -import QtQuick.Controls 2.0 -import QtQuick.Controls.Styles 1.4 - -import UM 1.2 as UM -import Cura 1.0 as Cura - - -/** - * Menu that allows you to select the configuration of the current printer, such - * as the nozzle sizes and materials in each extruder. - */ -Cura.ExpandableComponent -{ - id: base - - Cura.ExtrudersModel - { - id: extrudersModel - } - - UM.I18nCatalog - { - id: catalog - name: "cura" - } - - enum ConfigurationMethod - { - AUTO, - CUSTOM - } - - iconSource: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") - headerItem: Item - { - // Horizontal list that shows the extruders - ListView - { - id: extrudersList - - orientation: ListView.Horizontal - anchors.fill: parent - model: extrudersModel - visible: base.enabled - - delegate: Item - { - height: parent.height - width: Math.round(ListView.view.width / extrudersModel.count) - - // Extruder icon. Shows extruder index and has the same color as the active material. - Cura.ExtruderIcon - { - id: extruderIcon - materialColor: model.color - extruderEnabled: model.enabled - height: parent.height - width: height - } - - // Label for the brand of the material - Label - { - id: brandNameLabel - - text: model.material_brand - elide: Text.ElideRight - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text_inactive") - renderType: Text.NativeRendering - - anchors - { - left: extruderIcon.right - leftMargin: UM.Theme.getSize("default_margin").width - right: parent.right - rightMargin: UM.Theme.getSize("default_margin").width - } - } - - // Label that shows the name of the material - Label - { - text: model.material - elide: Text.ElideRight - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - renderType: Text.NativeRendering - - anchors - { - left: extruderIcon.right - leftMargin: UM.Theme.getSize("default_margin").width - right: parent.right - rightMargin: UM.Theme.getSize("default_margin").width - top: brandNameLabel.bottom - } - } - } - } - } - - //Disable the menu if there are no materials, variants or build plates to change. - function updateEnabled() - { - var active_definition_id = Cura.MachineManager.activeMachine.definition.id; - var has_materials = Cura.ContainerManager.getContainerMetaDataEntry(active_definition_id, "has_materials"); - var has_variants = Cura.ContainerManager.getContainerMetaDataEntry(active_definition_id, "has_variants"); - var has_buildplates = Cura.ContainerManager.getContainerMetaDataEntry(active_definition_id, "has_variant_buildplates"); - base.enabled = has_materials || has_variants || has_buildplates; //Only let it drop down if there is any configuration that you could change. - } - - Connections - { - target: Cura.MachineManager - onGlobalContainerChanged: base.updateEnabled(); - } - Component.onCompleted: updateEnabled(); - - popupItem: Column - { - id: popupItem - width: base.width - 2 * UM.Theme.getSize("default_margin").width - height: implicitHeight //Required because ExpandableComponent will try to use this to determine the size of the background of the pop-up. - spacing: UM.Theme.getSize("default_margin").height - - property bool is_connected: false //If current machine is connected to a printer. Only evaluated upon making popup visible. - onVisibleChanged: - { - is_connected = Cura.MachineManager.activeMachineNetworkKey !== "" && Cura.MachineManager.printerConnected //Re-evaluate. - } - - property int configuration_method: is_connected ? ConfigurationMenu.ConfigurationMethod.AUTO : ConfigurationMenu.ConfigurationMethod.CUSTOM //Auto if connected to a printer at start-up, or Custom if not. - - Item - { - width: parent.width - height: childrenRect.height - AutoConfiguration - { - id: autoConfiguration - visible: popupItem.configuration_method == ConfigurationMenu.ConfigurationMethod.AUTO - } - - CustomConfiguration - { - id: customConfiguration - visible: popupItem.configuration_method == ConfigurationMenu.ConfigurationMethod.CUSTOM - } - } - - Rectangle - { - id: separator - visible: buttonBar.visible - x: -popupPadding - - width: base.width - height: UM.Theme.getSize("default_lining").height - - color: UM.Theme.getColor("lining") - } - - //Allow switching between custom and auto. - Item - { - id: buttonBar - visible: popupItem.is_connected //Switching only makes sense if the "auto" part is possible. - - width: parent.width - height: childrenRect.height - - Cura.SecondaryButton - { - id: goToCustom - visible: popupItem.configuration_method == ConfigurationMenu.ConfigurationMethod.AUTO - text: catalog.i18nc("@label", "Custom") - - anchors.right: parent.right - - iconSource: UM.Theme.getIcon("arrow_right") - isIconOnRightSide: true - - onClicked: popupItem.configuration_method = ConfigurationMenu.ConfigurationMethod.CUSTOM - } - - Cura.SecondaryButton - { - id: goToAuto - visible: popupItem.configuration_method == ConfigurationMenu.ConfigurationMethod.CUSTOM - text: catalog.i18nc("@label", "Configurations") - - iconSource: UM.Theme.getIcon("arrow_left") - - onClicked: popupItem.configuration_method = ConfigurationMenu.ConfigurationMethod.AUTO - } - } - } -} diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml deleted file mode 100644 index 8d8f84155a..0000000000 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ /dev/null @@ -1,250 +0,0 @@ -// Copyright (c) 2018 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.6 -import QtQuick.Controls 2.0 -import QtQuick.Controls 1.1 as OldControls - -import Cura 1.0 as Cura -import UM 1.3 as UM - -Item -{ - UM.I18nCatalog - { - id: catalog - name: "cura" - } - - width: parent.width - height: childrenRect.height - - Label - { - id: header - text: catalog.i18nc("@header", "Custom") - font: UM.Theme.getFont("large") - color: UM.Theme.getColor("text") - height: contentHeight - renderType: Text.NativeRendering - - anchors - { - top: parent.top - left: parent.left - right: parent.right - } - } - - UM.TabRow - { - id: tabBar - anchors.top: header.bottom - anchors.topMargin: UM.Theme.getSize("default_margin").height - visible: extrudersModel.count > 1 - - Repeater - { - id: repeater - model: extrudersModel - delegate: UM.TabRowButton - { - contentItem: Item - { - Cura.ExtruderIcon - { - anchors.horizontalCenter: parent.horizontalCenter - materialColor: model.color - extruderEnabled: model.enabled - width: parent.height - height: parent.height - } - } - onClicked: - { - Cura.ExtruderManager.setActiveExtruderIndex(tabBar.currentIndex) - } - } - } - - //When active extruder changes for some other reason, switch tabs. - //Don't directly link currentIndex to Cura.ExtruderManager.activeExtruderIndex! - //This causes a segfault in Qt 5.11. Something with VisualItemModel removing index -1. We have to use setCurrentIndex instead. - Connections - { - target: Cura.ExtruderManager - onActiveExtruderChanged: - { - tabBar.setCurrentIndex(Cura.ExtruderManager.activeExtruderIndex); - } - } - - //When the model of the extruders is rebuilt, the list of extruders is briefly emptied and rebuilt. - //This causes the currentIndex of the tab to be in an invalid position which resets it to 0. - //Therefore we need to change it back to what it was: The active extruder index. - Connections - { - target: repeater.model - onModelChanged: - { - tabBar.setCurrentIndex(Cura.ExtruderManager.activeExtruderIndex) - } - } - } - - Rectangle - { - width: parent.width - height: childrenRect.height - anchors.top: tabBar.bottom - - radius: tabBar.visible ? UM.Theme.getSize("default_radius").width : 0 - border.width: tabBar.visible ? UM.Theme.getSize("default_lining").width : 0 - border.color: UM.Theme.getColor("lining") - color: UM.Theme.getColor("main_background") - - //Remove rounding and lining at the top. - Rectangle - { - width: parent.width - height: parent.radius - anchors.top: parent.top - color: UM.Theme.getColor("lining") - visible: tabBar.visible - Rectangle - { - anchors - { - left: parent.left - leftMargin: parent.parent.border.width - right: parent.right - rightMargin: parent.parent.border.width - top: parent.top - } - height: parent.parent.radius - color: parent.parent.color - } - } - - Column - { - id: selectors - padding: UM.Theme.getSize("default_margin").width - spacing: UM.Theme.getSize("default_margin").height - - property var model: extrudersModel.items[tabBar.currentIndex] - - readonly property real paddedWidth: parent.width - padding * 2 - property real textWidth: Math.round(paddedWidth * 0.3) - property real controlWidth: paddedWidth - textWidth - - Row - { - height: UM.Theme.getSize("print_setup_item").height - - Label - { - text: catalog.i18nc("@label", "Enabled") - verticalAlignment: Text.AlignVCenter - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - height: parent.height - width: selectors.textWidth - visible: extrudersModel.count > 1 - renderType: Text.NativeRendering - } - - OldControls.CheckBox - { - checked: Cura.MachineManager.activeStack != null ? Cura.MachineManager.activeStack.isEnabled : false - enabled: !checked || Cura.MachineManager.numberExtrudersEnabled > 1 //Disable if it's the last enabled extruder. - height: UM.Theme.getSize("setting_control").height - style: UM.Theme.styles.checkbox - visible: extrudersModel.count > 1 - - /* Use a MouseArea to process the click on this checkbox. - This is necessary because actually clicking the checkbox - causes the "checked" property to be overwritten. After - it's been overwritten, the original link that made it - depend on the active extruder stack is broken. */ - MouseArea - { - anchors.fill: parent - onClicked: Cura.MachineManager.setExtruderEnabled(Cura.ExtruderManager.activeExtruderIndex, !parent.checked) - enabled: parent.enabled - } - } - } - - Row - { - height: UM.Theme.getSize("print_setup_item").height - Label - { - text: catalog.i18nc("@label", "Material") - verticalAlignment: Text.AlignVCenter - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - height: parent.height - width: selectors.textWidth - visible: materialSelection.visible - renderType: Text.NativeRendering - } - - OldControls.ToolButton - { - id: materialSelection - - property bool valueError: Cura.MachineManager.activeStack != null ? Cura.ContainerManager.getContainerMetaDataEntry(Cura.MachineManager.activeStack.material.id, "compatible", "") != "True" : true - property bool valueWarning: !Cura.MachineManager.isActiveQualitySupported - - text: Cura.MachineManager.activeStack != null ? Cura.MachineManager.activeStack.material.name : "" - tooltip: text - visible: Cura.MachineManager.hasMaterials - - height: UM.Theme.getSize("setting_control").height - width: selectors.controlWidth - - style: UM.Theme.styles.sidebar_header_button - activeFocusOnPress: true - menu: Cura.MaterialMenu - { - extruderIndex: Cura.ExtruderManager.activeExtruderIndex - } - } - } - - Row - { - height: UM.Theme.getSize("print_setup_item").height - - Label - { - text: Cura.MachineManager.activeDefinitionVariantsName - verticalAlignment: Text.AlignVCenter - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - height: parent.height - width: selectors.textWidth - visible: variantSelection.visible - renderType: Text.NativeRendering - } - - OldControls.ToolButton - { - id: variantSelection - text: Cura.MachineManager.activeVariantName - tooltip: Cura.MachineManager.activeVariantName; - visible: Cura.MachineManager.hasVariants - - height: UM.Theme.getSize("setting_control").height - width: selectors.controlWidth - style: UM.Theme.styles.sidebar_header_button - activeFocusOnPress: true; - - menu: Cura.NozzleMenu { extruderIndex: Cura.ExtruderManager.activeExtruderIndex } - } - } - } - } -} diff --git a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml index 885f02d740..73fc342d66 100644 --- a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml @@ -5,50 +5,87 @@ import QtQuick 2.7 import QtQuick.Controls 2.0 import UM 1.2 as UM -import Cura 1.0 as Cura -Row + +Column { id: extruderInfo property var printCoreConfiguration + property var mainColor: "black" + spacing: Math.round(UM.Theme.getSize("default_margin").height / 2) - height: information.height - spacing: UM.Theme.getSize("default_margin").width + height: childrenRect.height - //Extruder icon. - Cura.ExtruderIcon + Item { - materialColor: printCoreConfiguration.material.color - anchors.verticalCenter: parent.verticalCenter - extruderEnabled: printCoreConfiguration.material.name !== "" && printCoreConfiguration.hotendID !== "" + id: extruder + width: parent.width + height: childrenRect.height + + Label + { + id: extruderLabel + text: catalog.i18nc("@label:extruder label", "Extruder") + renderType: Text.NativeRendering + elide: Text.ElideRight + anchors.left: parent.left + font: UM.Theme.getFont("default") + color: mainColor + } + + // Rounded item to show the extruder number + Item + { + id: extruderIconItem + anchors.verticalCenter: extruderLabel.verticalCenter + anchors.left: extruderLabel.right + anchors.leftMargin: Math.round(UM.Theme.getSize("default_margin").width / 2) + + width: UM.Theme.getSize("section_icon").width + height: UM.Theme.getSize("section_icon").height + + UM.RecolorImage { + id: mainCircle + anchors.fill: parent + + anchors.centerIn: parent + sourceSize.width: parent.width + sourceSize.height: parent.height + source: UM.Theme.getIcon("extruder_button") + color: mainColor + } + + Label + { + id: extruderNumberText + anchors.centerIn: parent + text: printCoreConfiguration.position + 1 + renderType: Text.NativeRendering + font: UM.Theme.getFont("default") + color: mainColor + } + } } - Column + Label { - id: information - Label - { - text: printCoreConfiguration.material.brand ? printCoreConfiguration.material.brand : " " //Use space so that the height is still correct. - renderType: Text.NativeRendering - elide: Text.ElideRight - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text_inactive") - } - Label - { - text: printCoreConfiguration.material.name ? printCoreConfiguration.material.name : " " //Use space so that the height is still correct. - renderType: Text.NativeRendering - elide: Text.ElideRight - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - } - Label - { - text: printCoreConfiguration.hotendID ? printCoreConfiguration.hotendID : " " //Use space so that the height is still correct. - renderType: Text.NativeRendering - elide: Text.ElideRight - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text_inactive") - } + id: materialLabel + text: printCoreConfiguration.material == null ? "" : printCoreConfiguration.material.name + renderType: Text.NativeRendering + elide: Text.ElideRight + width: parent.width + font: UM.Theme.getFont("default_bold") + color: mainColor + } + + Label + { + id: printCoreTypeLabel + text: printCoreConfiguration.hotendID + renderType: Text.NativeRendering + elide: Text.ElideRight + width: parent.width + font: UM.Theme.getFont("default") + color: mainColor } } diff --git a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml new file mode 100644 index 0000000000..eb6800cb36 --- /dev/null +++ b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml @@ -0,0 +1,243 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 2.0 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.3 + +import QtQuick.Controls 1.1 as OldControls + +import UM 1.2 as UM +import Cura 1.0 as Cura + + +Cura.ExpandableComponent +{ + id: base + + Cura.ExtrudersModel + { + id: extrudersModel + } + + UM.I18nCatalog + { + id: catalog + name: "cura" + } + + iconSource: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") + headerItem: Item + { + // Horizontal list that shows the extruders + ListView + { + id: extrudersList + + orientation: ListView.Horizontal + anchors.fill: parent + model: extrudersModel + + delegate: Item + { + height: parent.height + width: Math.round(ListView.view.width / extrudersModel.rowCount()) + + // Extruder icon. Shows extruder index and has the same color as the active material. + Cura.ExtruderIcon + { + id: extruderIcon + materialColor: model.color + extruderEnabled: model.enabled + anchors.verticalCenter: parent.verticalCenter + } + + // Label for the brand of the material + Label + { + id: brandNameLabel + + text: model.material_brand + elide: Text.ElideRight + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + + anchors + { + left: extruderIcon.right + leftMargin: UM.Theme.getSize("default_margin").width + right: parent.right + rightMargin: UM.Theme.getSize("default_margin").width + } + } + + // Label that shows the name of the material + Label + { + text: model.material + elide: Text.ElideRight + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + + anchors + { + left: extruderIcon.right + leftMargin: UM.Theme.getSize("default_margin").width + right: parent.right + rightMargin: UM.Theme.getSize("default_margin").width + top: brandNameLabel.bottom + } + } + } + } + } + + popupItem: Item + { + width: base.width - 2 * UM.Theme.getSize("default_margin").width + height: 200 + + TabBar + { + id: tabBar + onCurrentIndexChanged: Cura.ExtruderManager.setActiveExtruderIndex(currentIndex) + width: parent.width + height: 50 + Repeater + { + model: extrudersModel + + delegate: TabButton + { + width: ListView.view != null ? Math.round(ListView.view.width / extrudersModel.rowCount()): 0 + height: parent.height + contentItem: Item + { + Cura.ExtruderIcon + { + anchors.horizontalCenter: parent.horizontalCenter + materialColor: model.color + extruderEnabled: model.enabled + width: parent.height + height: parent.height + } + } + } + } + } + + Item + { + id: tabControl + width: parent.width + anchors.top: tabBar.bottom + anchors.bottom: parent.bottom + property var model: extrudersModel.items[tabBar.currentIndex] + property real textWidth: Math.round(width * 0.3) + property real controlWidth: width - textWidth + Column + { + spacing: UM.Theme.getSize("default_margin").height + Row + { + height: UM.Theme.getSize("print_setup_item").height + + Label + { + text: catalog.i18nc("@label", "Enabled") + verticalAlignment: Text.AlignVCenter + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + height: parent.height + width: tabControl.textWidth + renderType: Text.NativeRendering + } + + OldControls.CheckBox + { + checked: tabControl.model != null ? Cura.MachineManager.getExtruder(tabControl.model.index).isEnabled: false + onClicked: Cura.MachineManager.setExtruderEnabled(tabControl.model.index, checked) + height: UM.Theme.getSize("setting_control").height + style: UM.Theme.styles.checkbox + } + } + + Row + { + height: UM.Theme.getSize("print_setup_item").height + Label + { + text: catalog.i18nc("@label", "Material") + verticalAlignment: Text.AlignVCenter + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + height: parent.height + width: tabControl.textWidth + renderType: Text.NativeRendering + } + + OldControls.ToolButton + { + id: materialSelection + + property var activeExtruder: Cura.MachineManager.activeStack + property var hasActiveExtruder: activeExtruder != null + property var currentRootMaterialName: hasActiveExtruder ? activeExtruder.material.name : "" + property var valueError: hasActiveExtruder ? Cura.ContainerManager.getContainerMetaDataEntry(activeExtruder.material.id, "compatible", "") != "True" : true + property var valueWarning: ! Cura.MachineManager.isActiveQualitySupported + + text: currentRootMaterialName + tooltip: currentRootMaterialName + visible: Cura.MachineManager.hasMaterials + + enabled: Cura.ExtruderManager.activeExtruderIndex > -1 + + height: UM.Theme.getSize("setting_control").height + width: tabControl.controlWidth + + style: UM.Theme.styles.sidebar_header_button + activeFocusOnPress: true + menu: Cura.MaterialMenu + { + extruderIndex: Cura.ExtruderManager.activeExtruderIndex + } + + } + } + + Row + { + height: UM.Theme.getSize("print_setup_item").height + + Label + { + text: Cura.MachineManager.activeDefinitionVariantsName + verticalAlignment: Text.AlignVCenter + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + height: parent.height + width: tabControl.textWidth + renderType: Text.NativeRendering + } + + OldControls.ToolButton + { + id: variantSelection + text: Cura.MachineManager.activeVariantName + tooltip: Cura.MachineManager.activeVariantName; + visible: Cura.MachineManager.hasVariants + + height: UM.Theme.getSize("setting_control").height + width: tabControl.controlWidth + style: UM.Theme.styles.sidebar_header_button + activeFocusOnPress: true; + + menu: Cura.NozzleMenu { extruderIndex: Cura.ExtruderManager.activeExtruderIndex } + } + } + } + + } + } +} diff --git a/resources/qml/Menus/ConfigurationMenu/SyncButton.qml b/resources/qml/Menus/ConfigurationMenu/SyncButton.qml new file mode 100644 index 0000000000..558ae1e477 --- /dev/null +++ b/resources/qml/Menus/ConfigurationMenu/SyncButton.qml @@ -0,0 +1,102 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +Button +{ + id: base + property var outputDevice: null + property var matched: updateOnSync() + text: matched == true ? catalog.i18nc("@label:extruder label", "Yes") : catalog.i18nc("@label:extruder label", "No") + width: parent.width + height: parent.height + + function updateOnSync() + { + if (outputDevice != undefined) + { + for (var index in outputDevice.uniqueConfigurations) + { + var configuration = outputDevice.uniqueConfigurations[index] + if (Cura.MachineManager.matchesConfiguration(configuration)) + { + base.matched = true; + return; + } + } + } + base.matched = false; + } + + style: ButtonStyle + { + background: Rectangle + { + color: + { + if(control.pressed) + { + return UM.Theme.getColor("machine_selector_active"); + } + else if(control.hovered) + { + return UM.Theme.getColor("machine_selector_hover"); + } + else + { + return UM.Theme.getColor("machine_selector_bar"); + } + } + Behavior on color { ColorAnimation { duration: 50; } } + + UM.RecolorImage + { + id: downArrow + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + anchors.rightMargin: UM.Theme.getSize("default_margin").width + width: UM.Theme.getSize("standard_arrow").width + height: UM.Theme.getSize("standard_arrow").height + sourceSize.width: width + sourceSize.height: height + color: UM.Theme.getColor("text_emphasis") + source: UM.Theme.getIcon("arrow_bottom") + } + UM.RecolorImage + { + id: sidebarComboBoxLabel + anchors.left: parent.left + anchors.leftMargin: UM.Theme.getSize("default_margin").width + anchors.verticalCenter: parent.verticalCenter; + + width: UM.Theme.getSize("printer_sync_icon").width + height: UM.Theme.getSize("printer_sync_icon").height + + color: control.matched ? UM.Theme.getColor("printer_config_matched") : UM.Theme.getColor("printer_config_mismatch") + source: UM.Theme.getIcon("tab_status_connected") + sourceSize.width: width + sourceSize.height: height + } + } + label: Label {} + } + + Connections + { + target: outputDevice + onUniqueConfigurationsChanged: updateOnSync() + } + + Connections + { + target: Cura.MachineManager + onCurrentConfigurationChanged: updateOnSync() + onOutputDevicesChanged: updateOnSync() + } +} \ No newline at end of file diff --git a/resources/qml/Menus/ProfileMenu.qml b/resources/qml/Menus/ProfileMenu.qml index bf950aa409..fd46d2ef72 100644 --- a/resources/qml/Menus/ProfileMenu.qml +++ b/resources/qml/Menus/ProfileMenu.qml @@ -37,7 +37,7 @@ Menu MenuSeparator { id: customSeparator - visible: Cura.CustomQualityProfilesDropDownMenuModel.count > 0 + visible: Cura.CustomQualityProfilesDropDownMenuModel.rowCount > 0 } Instantiator @@ -48,7 +48,7 @@ Menu Connections { target: Cura.CustomQualityProfilesDropDownMenuModel - onModelReset: customSeparator.visible = Cura.CustomQualityProfilesDropDownMenuModel.count > 0 + onModelReset: customSeparator.visible = Cura.CustomQualityProfilesDropDownMenuModel.rowCount() > 0 } MenuItem @@ -62,12 +62,12 @@ Menu onObjectAdded: { - customSeparator.visible = model.count > 0; + customSeparator.visible = model.rowCount() > 0; menu.insertItem(index, object); } onObjectRemoved: { - customSeparator.visible = model.count > 0; + customSeparator.visible = model.rowCount() > 0; menu.removeItem(object); } } diff --git a/resources/qml/ObjectsList.qml b/resources/qml/ObjectsList.qml index 8f45b3744f..8c8eaa16ae 100644 --- a/resources/qml/ObjectsList.qml +++ b/resources/qml/ObjectsList.qml @@ -1,4 +1,4 @@ -// Copyright (c) 2018 Ultimaker B.V. +// Copyright (c) 2017 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.2 @@ -55,6 +55,7 @@ Rectangle { width: control.width height: control.height + sourceSize.width: width sourceSize.height: width color: UM.Theme.getColor("setting_control_text") source: collapsed ? UM.Theme.getIcon("arrow_left") : UM.Theme.getIcon("arrow_bottom") diff --git a/resources/qml/Preferences/MachinesPage.qml b/resources/qml/Preferences/MachinesPage.qml index bc75b9bc72..4dc5465dc6 100644 --- a/resources/qml/Preferences/MachinesPage.qml +++ b/resources/qml/Preferences/MachinesPage.qml @@ -21,10 +21,8 @@ UM.ManagementPage function activeMachineIndex() { - for(var i = 0; i < model.count; i++) - { - if (model.getItem(i).id == Cura.MachineManager.activeMachineId) - { + for(var i = 0; i < model.rowCount(); i++) { + if (model.getItem(i).id == Cura.MachineManager.activeMachineId) { return i; } } @@ -49,7 +47,7 @@ UM.ManagementPage { text: catalog.i18nc("@action:button", "Remove"); iconName: "list-remove"; - enabled: base.currentItem != null && model.count > 1 + enabled: base.currentItem != null && model.rowCount() > 1 onClicked: confirmDialog.open(); }, Button diff --git a/resources/qml/Preferences/Materials/MaterialsBrandSection.qml b/resources/qml/Preferences/Materials/MaterialsBrandSection.qml index a3a0e4708f..c8f391dfb0 100644 --- a/resources/qml/Preferences/Materials/MaterialsBrandSection.qml +++ b/resources/qml/Preferences/Materials/MaterialsBrandSection.qml @@ -55,8 +55,7 @@ Rectangle text: "" implicitWidth: UM.Theme.getSize("favorites_button").width implicitHeight: UM.Theme.getSize("favorites_button").height - UM.RecolorImage - { + UM.RecolorImage { anchors { verticalCenter: parent.verticalCenter @@ -64,6 +63,8 @@ Rectangle } width: UM.Theme.getSize("standard_arrow").width height: UM.Theme.getSize("standard_arrow").height + sourceSize.width: width + sourceSize.height: height color: "black" source: brand_section.expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") } diff --git a/resources/qml/Preferences/Materials/MaterialsList.qml b/resources/qml/Preferences/Materials/MaterialsList.qml index 61f92db84c..00bead9650 100644 --- a/resources/qml/Preferences/Materials/MaterialsList.qml +++ b/resources/qml/Preferences/Materials/MaterialsList.qml @@ -57,7 +57,7 @@ Item var currentItemId = base.currentItem == null ? "" : base.currentItem.root_material_id search_root_id = currentItemId } - for (var material_idx = 0; material_idx < genericMaterialsModel.count; material_idx++) + for (var material_idx = 0; material_idx < genericMaterialsModel.rowCount(); material_idx++) { var material = genericMaterialsModel.getItem(material_idx) if (material.root_material_id == search_root_id) @@ -72,15 +72,15 @@ Item return true } } - for (var brand_idx = 0; brand_idx < materialsModel.count; brand_idx++) + for (var brand_idx = 0; brand_idx < materialsModel.rowCount(); brand_idx++) { var brand = materialsModel.getItem(brand_idx) var types_model = brand.material_types - for (var type_idx = 0; type_idx < types_model.count; type_idx++) + for (var type_idx = 0; type_idx < types_model.rowCount(); type_idx++) { var type = types_model.getItem(type_idx) var colors_model = type.colors - for (var material_idx = 0; material_idx < colors_model.count; material_idx++) + for (var material_idx = 0; material_idx < colors_model.rowCount(); material_idx++) { var material = colors_model.getItem(material_idx) if (material.root_material_id == search_root_id) diff --git a/resources/qml/Preferences/Materials/MaterialsSlot.qml b/resources/qml/Preferences/Materials/MaterialsSlot.qml index a706aaf2b9..a5af17f47a 100644 --- a/resources/qml/Preferences/Materials/MaterialsSlot.qml +++ b/resources/qml/Preferences/Materials/MaterialsSlot.qml @@ -95,6 +95,8 @@ Rectangle } width: UM.Theme.getSize("favorites_button_icon").width height: UM.Theme.getSize("favorites_button_icon").height + sourceSize.width: width + sourceSize.height: height color: { if (favorite_button.hovered) diff --git a/resources/qml/Preferences/Materials/MaterialsTypeSection.qml b/resources/qml/Preferences/Materials/MaterialsTypeSection.qml index f98c19e0b3..f62fc4ee16 100644 --- a/resources/qml/Preferences/Materials/MaterialsTypeSection.qml +++ b/resources/qml/Preferences/Materials/MaterialsTypeSection.qml @@ -74,6 +74,8 @@ Rectangle } width: UM.Theme.getSize("standard_arrow").width height: UM.Theme.getSize("standard_arrow").height + sourceSize.width: width + sourceSize.height: height color: "black" source: material_type_section.expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") } diff --git a/resources/qml/Preferences/ProfilesPage.qml b/resources/qml/Preferences/ProfilesPage.qml index d7ffbb3152..ba0c2848a5 100644 --- a/resources/qml/Preferences/ProfilesPage.qml +++ b/resources/qml/Preferences/ProfilesPage.qml @@ -188,27 +188,21 @@ Item Connections { target: qualitiesModel - onItemsChanged: - { + onItemsChanged: { var toSelectItemName = base.currentItem == null ? "" : base.currentItem.name; - if (newQualityNameToSelect != "") - { + if (newQualityNameToSelect != "") { toSelectItemName = newQualityNameToSelect; } var newIdx = -1; // Default to nothing if nothing can be found - if (toSelectItemName != "") - { + if (toSelectItemName != "") { // Select the required quality name if given - for (var idx = 0; idx < qualitiesModel.count; ++idx) - { + for (var idx = 0; idx < qualitiesModel.rowCount(); ++idx) { var item = qualitiesModel.getItem(idx); - if (item.name == toSelectItemName) - { + if (item.name == toSelectItemName) { // Switch to the newly created profile if needed newIdx = idx; - if (base.toActivateNewQuality) - { + if (base.toActivateNewQuality) { // Activate this custom quality if required Cura.MachineManager.setQualityChangesGroup(item.quality_changes_group); } @@ -388,11 +382,9 @@ Item var selectedItemName = Cura.MachineManager.activeQualityOrQualityChangesName; // Select the required quality name if given - for (var idx = 0; idx < qualitiesModel.count; idx++) - { + for (var idx = 0; idx < qualitiesModel.rowCount(); idx++) { var item = qualitiesModel.getItem(idx); - if (item.name == selectedItemName) - { + if (item.name == selectedItemName) { currentIndex = idx; break; } diff --git a/resources/qml/Preferences/SettingVisibilityPage.qml b/resources/qml/Preferences/SettingVisibilityPage.qml index 3f7571a170..2edbeee960 100644 --- a/resources/qml/Preferences/SettingVisibilityPage.qml +++ b/resources/qml/Preferences/SettingVisibilityPage.qml @@ -50,7 +50,7 @@ UM.PreferencesPage { return Qt.Unchecked } - else if(definitionsModel.visibleCount == definitionsModel.count) + else if(definitionsModel.visibleCount == definitionsModel.rowCount(null)) { return Qt.Checked } diff --git a/resources/qml/PrinterOutput/ExtruderBox.qml b/resources/qml/PrinterOutput/ExtruderBox.qml index 247bb3a27d..f5a1bd75c4 100644 --- a/resources/qml/PrinterOutput/ExtruderBox.qml +++ b/resources/qml/PrinterOutput/ExtruderBox.qml @@ -47,7 +47,7 @@ Item { id: extruderTargetTemperature text: Math.round(extruderModel.targetHotendTemperature) + "°C" - font: UM.Theme.getFont("default_bold") + font: UM.Theme.getFont("small") color: UM.Theme.getColor("text_inactive") anchors.right: parent.right anchors.rightMargin: UM.Theme.getSize("default_margin").width diff --git a/resources/qml/PrinterOutput/HeatedBedBox.qml b/resources/qml/PrinterOutput/HeatedBedBox.qml index 33cf5cd1e2..8c99814e02 100644 --- a/resources/qml/PrinterOutput/HeatedBedBox.qml +++ b/resources/qml/PrinterOutput/HeatedBedBox.qml @@ -35,7 +35,7 @@ Item { id: bedTargetTemperature text: printerModel != null ? printerModel.targetBedTemperature + "°C" : "" - font: UM.Theme.getFont("default_bold") + font: UM.Theme.getFont("small") color: UM.Theme.getColor("text_inactive") anchors.right: parent.right anchors.rightMargin: UM.Theme.getSize("default_margin").width diff --git a/resources/qml/PrinterOutput/OutputDeviceHeader.qml b/resources/qml/PrinterOutput/OutputDeviceHeader.qml index 16280eab5f..e6328546ef 100644 --- a/resources/qml/PrinterOutput/OutputDeviceHeader.qml +++ b/resources/qml/PrinterOutput/OutputDeviceHeader.qml @@ -43,7 +43,7 @@ Item { id: outputDeviceAddressLabel text: (outputDevice != null && outputDevice.address != null) ? outputDevice.address : "" - font: UM.Theme.getFont("default_bold") + font: UM.Theme.getFont("small") color: UM.Theme.getColor("text_inactive") anchors.top: outputDeviceNameLabel.bottom anchors.left: parent.left @@ -54,7 +54,7 @@ Item { text: outputDevice != null ? "" : catalog.i18nc("@info:status", "The printer is not connected.") color: outputDevice != null && outputDevice.acceptsCommands ? UM.Theme.getColor("setting_control_text") : UM.Theme.getColor("setting_control_disabled_text") - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("very_small") wrapMode: Text.WordWrap anchors.left: parent.left anchors.leftMargin: UM.Theme.getSize("default_margin").width diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index 95abfd6644..15cd773c90 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -25,52 +25,29 @@ Cura.ExpandableComponent name: "cura" } - headerItem: Item + headerItem: Cura.IconLabel { - implicitHeight: icon.height + text: isNetworkPrinter ? Cura.MachineManager.activeMachineNetworkGroupName : Cura.MachineManager.activeMachineName + source: + { + if (isNetworkPrinter) + { + if (machineSelector.outputDevice != null && machineSelector.outputDevice.clusterSize > 1) + { + return UM.Theme.getIcon("printer_group") + } + return UM.Theme.getIcon("printer_single") + } + return "" + } + font: UM.Theme.getFont("medium") + color: UM.Theme.getColor("text") + iconSize: UM.Theme.getSize("machine_selector_icon").width UM.RecolorImage { id: icon - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - - source: - { - if (isNetworkPrinter) - { - if (machineSelector.outputDevice != null && machineSelector.outputDevice.clusterSize > 1) - { - return UM.Theme.getIcon("printer_group") - } - return UM.Theme.getIcon("printer_single") - } - return "" - } - width: UM.Theme.getSize("machine_selector_icon").width - height: width - - color: UM.Theme.getColor("machine_selector_printer_icon") - visible: source != "" - } - - Label - { - id: label - anchors.left: icon.visible ? icon.right : parent.left - anchors.right: parent.right - anchors.leftMargin: UM.Theme.getSize("thin_margin").width - anchors.verticalCenter: icon.verticalCenter - text: isNetworkPrinter ? Cura.MachineManager.activeMachineNetworkGroupName : Cura.MachineManager.activeMachineName - elide: Text.ElideRight - color: UM.Theme.getColor("text") - font: UM.Theme.getFont("medium") - renderType: Text.NativeRendering - } - - UM.RecolorImage - { anchors { bottom: parent.bottom @@ -82,6 +59,9 @@ Cura.ExpandableComponent width: UM.Theme.getSize("printer_status_icon").width height: UM.Theme.getSize("printer_status_icon").height + sourceSize.width: width + sourceSize.height: height + color: UM.Theme.getColor("primary") visible: isNetworkPrinter && isPrinterConnected diff --git a/resources/qml/PrinterSelector/MachineSelectorList.qml b/resources/qml/PrinterSelector/MachineSelectorList.qml index d831f4eb5c..445940ab50 100644 --- a/resources/qml/PrinterSelector/MachineSelectorList.qml +++ b/resources/qml/PrinterSelector/MachineSelectorList.qml @@ -13,7 +13,7 @@ Column Label { - text: catalog.i18nc("@label", "Connected printers") + text: catalog.i18nc("@label", "Network connected printers") visible: networkedPrintersModel.items.length > 0 leftPadding: UM.Theme.getSize("default_margin").width height: visible ? contentHeight + 2 * UM.Theme.getSize("default_margin").height : 0 diff --git a/resources/qml/PrinterTypeLabel.qml b/resources/qml/PrinterSelector/PrinterTypeLabel.qml similarity index 95% rename from resources/qml/PrinterTypeLabel.qml rename to resources/qml/PrinterSelector/PrinterTypeLabel.qml index 7feae32e16..cd9f3b9743 100644 --- a/resources/qml/PrinterTypeLabel.qml +++ b/resources/qml/PrinterSelector/PrinterTypeLabel.qml @@ -28,7 +28,7 @@ Item anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter renderType: Text.NativeRendering - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("very_small") color: UM.Theme.getColor("text") } } \ No newline at end of file diff --git a/resources/qml/Settings/SettingCategory.qml b/resources/qml/Settings/SettingCategory.qml index 196b2d6b97..aafe36c546 100644 --- a/resources/qml/Settings/SettingCategory.qml +++ b/resources/qml/Settings/SettingCategory.qml @@ -129,26 +129,23 @@ Button anchors.rightMargin: UM.Theme.getSize("default_margin").width width: UM.Theme.getSize("standard_arrow").width height: UM.Theme.getSize("standard_arrow").height + sourceSize.width: width sourceSize.height: width color: { if (!base.enabled) { return UM.Theme.getColor("setting_category_disabled_text") - } - else if ((base.hovered || base.activeFocus) && base.checkable && base.checked) + } else if ((base.hovered || base.activeFocus) && base.checkable && base.checked) { return UM.Theme.getColor("setting_category_active_hover_text") - } - else if (base.pressed || (base.checkable && base.checked)) + } else if (base.pressed || (base.checkable && base.checked)) { return UM.Theme.getColor("setting_category_active_text") - } - else if (base.hovered || base.activeFocus) + } else if (base.hovered || base.activeFocus) { return UM.Theme.getColor("setting_category_hover_text") - } - else + } else { return UM.Theme.getColor("setting_category_text") } diff --git a/resources/qml/Settings/SettingCheckBox.qml b/resources/qml/Settings/SettingCheckBox.qml index fb2d5a2f4d..d37754d27c 100644 --- a/resources/qml/Settings/SettingCheckBox.qml +++ b/resources/qml/Settings/SettingCheckBox.qml @@ -115,12 +115,12 @@ SettingItem return UM.Theme.getColor("setting_control_border") } - UM.RecolorImage - { + UM.RecolorImage { anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter width: Math.round(parent.width / 2.5) height: Math.round(parent.height / 2.5) + sourceSize.width: width sourceSize.height: width color: !enabled ? UM.Theme.getColor("setting_control_disabled_text") : UM.Theme.getColor("setting_control_text"); source: UM.Theme.getIcon("check") diff --git a/resources/qml/Settings/SettingOptionalExtruder.qml b/resources/qml/Settings/SettingOptionalExtruder.qml index 5f0d8327f8..a3c1422b30 100644 --- a/resources/qml/Settings/SettingOptionalExtruder.qml +++ b/resources/qml/Settings/SettingOptionalExtruder.qml @@ -1,5 +1,5 @@ -// Copyright (c) 2018 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. +// Copyright (c) 2016 Ultimaker B.V. +// Uranium is released under the terms of the LGPLv3 or higher. import QtQuick 2.7 import QtQuick.Controls 2.0 @@ -31,15 +31,12 @@ SettingItem { forceActiveFocus(); propertyProvider.setPropertyValue("value", model.getItem(index).index); - } - else + } else { if (propertyProvider.properties.value == -1) { - control.currentIndex = model.count - 1; // we know the last item is "Not overriden" - } - else - { + control.currentIndex = model.rowCount() - 1; // we know the last item is "Not overriden" + } else { control.currentIndex = propertyProvider.properties.value; // revert to the old value } } diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index bb624bcbde..ef1f123953 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -1,5 +1,5 @@ -// Copyright (c) 2018 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. +// Copyright (c) 2017 Ultimaker B.V. +// Uranium is released under the terms of the LGPLv3 or higher. import QtQuick 2.7 import QtQuick.Controls 1.1 @@ -129,14 +129,13 @@ Item } style: ButtonStyle { - background: Item - { - UM.RecolorImage - { + background: Item { + 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") diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index fb4d52979d..5e723a3d70 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -106,7 +106,7 @@ Item var availableMin = -1 var availableMax = -1 - for (var i = 0; i < Cura.QualityProfilesDropDownMenuModel.count; i++) + for (var i = 0; i < Cura.QualityProfilesDropDownMenuModel.rowCount(); i++) { var qualityItem = Cura.QualityProfilesDropDownMenuModel.getItem(i) @@ -183,7 +183,7 @@ Item qualityModel.existingQualityProfile = 0 // check, the ticks count cannot be less than zero - qualityModel.totalTicks = Math.max(0, Cura.QualityProfilesDropDownMenuModel.count - 1) + qualityModel.totalTicks = Math.max(0, Cura.QualityProfilesDropDownMenuModel.rowCount() - 1) } } @@ -731,6 +731,7 @@ Item { anchors.fill: parent anchors.margins: 2 * screenScaleFactor + sourceSize.width: width sourceSize.height: width source: UM.Theme.getIcon(model.icon) color: UM.Theme.getColor("quality_slider_unavailable") @@ -1155,7 +1156,7 @@ Item function populateExtruderModel() { extruderModel.clear(); - for(var extruderNumber = 0; extruderNumber < extruders.count; extruderNumber++) + for(var extruderNumber = 0; extruderNumber < extruders.rowCount() ; extruderNumber++) { extruderModel.append({ text: extruders.getItem(extruderNumber).name, diff --git a/resources/qml/Toolbar.qml b/resources/qml/Toolbar.qml index 1e335472d4..5fbddea9ac 100644 --- a/resources/qml/Toolbar.qml +++ b/resources/qml/Toolbar.qml @@ -62,7 +62,7 @@ Item enabled: model.enabled && UM.Selection.hasSelection && UM.Controller.toolsEnabled isTopElement: toolsModel.getItem(0).id == model.id - isBottomElement: toolsModel.getItem(toolsModel.count - 1).id == model.id + isBottomElement: toolsModel.getItem(toolsModel.rowCount() - 1).id == model.id toolItem: UM.RecolorImage { diff --git a/resources/qml/ViewsSelector.qml b/resources/qml/ViewsSelector.qml index 1e42a0b3ba..e9fdd57177 100644 --- a/resources/qml/ViewsSelector.qml +++ b/resources/qml/ViewsSelector.qml @@ -19,7 +19,7 @@ Cura.ExpandableComponent property var activeView: { - for (var i = 0; i < viewModel.count; i++) + for (var i = 0; i < viewModel.rowCount(); i++) { if (viewModel.items[i].active) { @@ -74,8 +74,6 @@ Cura.ExpandableComponent { id: viewSelectorPopup width: viewSelector.width - 2 * viewSelector.popupPadding - leftPadding: UM.Theme.getSize("default_lining").width - rightPadding: UM.Theme.getSize("default_lining").width // For some reason the height/width of the column gets set to 0 if this is not set... Component.onCompleted: @@ -93,7 +91,7 @@ Cura.ExpandableComponent { id: viewsSelectorButton text: model.name - width: parent.width - viewSelectorPopup.leftPadding - viewSelectorPopup.rightPadding + width: parent.width height: UM.Theme.getSize("action_button").height leftPadding: UM.Theme.getSize("default_margin").width rightPadding: UM.Theme.getSize("default_margin").width diff --git a/resources/qml/qmldir b/resources/qml/qmldir index 7e57119bc6..2475f398f8 100644 --- a/resources/qml/qmldir +++ b/resources/qml/qmldir @@ -1,6 +1,7 @@ module Cura MachineSelector 1.0 MachineSelector.qml +QuickConfigurationSelector 1.0 QuickConfigurationSelector.qml CustomConfigurationSelector 1.0 CustomConfigurationSelector.qml PrintSetupSelector 1.0 PrintSetupSelector.qml ActionButton 1.0 ActionButton.qml diff --git a/resources/themes/cura-dark/theme.json b/resources/themes/cura-dark/theme.json index d9ef74ebb9..34b944b25b 100644 --- a/resources/themes/cura-dark/theme.json +++ b/resources/themes/cura-dark/theme.json @@ -15,7 +15,7 @@ "primary_hover": [48, 182, 231, 255], "primary_text": [255, 255, 255, 204], "border": [127, 127, 127, 255], - "secondary": [95, 95, 95, 255], + "secondary": [241, 242, 242, 255], "main_window_header_button_text_inactive": [128, 128, 128, 255], "main_window_header_button_text_hovered": [255, 255, 255, 255], @@ -196,6 +196,14 @@ "layerview_support_interface": [64, 192, 255, 255], "layerview_nozzle": [181, 166, 66, 120], + "configuration_item": [0, 0, 0, 0], + "configuration_item_active": [12, 169, 227, 179], + "configuration_item_text": [255, 255, 255, 255], + "configuration_item_text_active": [255, 255, 255, 255], + "configuration_item_border": [255, 255, 255, 255], + "configuration_item_border_active": [12, 169, 227, 179], + "configuration_item_border_hover": [12, 169, 227, 179], + "material_compatibility_warning": [255, 255, 255, 255], "quality_slider_unavailable": [179, 179, 179, 255], diff --git a/resources/themes/cura-light/images/header_pattern.svg b/resources/themes/cura-light/images/header_pattern.svg index eff5f01cfa..2a9de2f3e9 100644 --- a/resources/themes/cura-light/images/header_pattern.svg +++ b/resources/themes/cura-light/images/header_pattern.svg @@ -1,1901 +1 @@ - - - - Desktop HD - Created with Sketch. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file +Pattern \ No newline at end of file diff --git a/resources/themes/cura-light/images/logo_about.svg b/resources/themes/cura-light/images/logo_about.svg deleted file mode 100644 index 34301fd6c9..0000000000 --- a/resources/themes/cura-light/images/logo_about.svg +++ /dev/null @@ -1,172 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index 30cf42859a..f2ad2b6f4a 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -73,6 +73,7 @@ QtObject anchors.rightMargin: Theme.getSize("default_margin").width width: Theme.getSize("standard_arrow").width height: Theme.getSize("standard_arrow").height + sourceSize.width: width sourceSize.height: width color: control.enabled ? Theme.getColor("setting_category_text") : Theme.getColor("setting_category_disabled_text") source: Theme.getIcon("arrow_bottom") @@ -145,7 +146,7 @@ QtObject text: control.text anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter - font: UM.Theme.getFont("medium") + font: UM.Theme.getFont("medium_bold") color: { if (control.checked) @@ -256,6 +257,7 @@ QtObject anchors.bottomMargin: Theme.getSize("button").height - Math.round(Theme.getSize("button_icon").height / 4) width: Theme.getSize("standard_arrow").width height: Theme.getSize("standard_arrow").height + sourceSize.width: width sourceSize.height: width visible: control.menu != null; color: @@ -527,7 +529,7 @@ QtObject implicitWidth: Theme.getSize("checkbox").width implicitHeight: Theme.getSize("checkbox").height - color: (control.hovered || control._hovered) ? Theme.getColor("checkbox_hover") : (control.enabled ? Theme.getColor("checkbox") : Theme.getColor("checkbox_disabled")) + color: (control.hovered || control._hovered) ? Theme.getColor("checkbox_hover") : Theme.getColor("checkbox") Behavior on color { ColorAnimation { duration: 50; } } radius: control.exclusiveGroup ? Math.round(Theme.getSize("checkbox").width / 2) : 0 @@ -541,6 +543,7 @@ QtObject anchors.horizontalCenter: parent.horizontalCenter width: Math.round(parent.width / 2.5) height: Math.round(parent.height / 2.5) + sourceSize.width: width sourceSize.height: width color: Theme.getColor("checkbox_mark") source: control.exclusiveGroup ? Theme.getIcon("dot") : Theme.getIcon("check") @@ -582,6 +585,7 @@ QtObject anchors.horizontalCenter: parent.horizontalCenter width: Math.round(parent.width / 2.5) height: Math.round(parent.height / 2.5) + sourceSize.width: width sourceSize.height: width color: Theme.getColor("checkbox_mark") source: @@ -832,6 +836,7 @@ QtObject anchors.horizontalCenter: parent.horizontalCenter width: Math.floor(control.width / 2) height: Math.floor(control.height / 2) + sourceSize.width: width sourceSize.height: width color: { diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 2d7e92be4d..dfad5cfd17 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -41,12 +41,12 @@ "family": "Noto Sans" }, "small": { - "size": 0.85, - "weight": 50, + "size": 1.0, + "weight": 63, "family": "Noto Sans" }, "very_small": { - "size": 0.7, + "size": 1.0, "weight": 50, "family": "Noto Sans" }, @@ -64,6 +64,12 @@ "size": 1.15, "weight": 50, "family": "Noto Sans" + }, + "extruder_icon": + { + "size": 0.7, + "weight": 50, + "family": "Noto Sans" } }, @@ -93,14 +99,14 @@ "secondary_button_hover": [228, 228, 228, 255], "secondary_button_text": [30, 102, 215, 255], - "main_window_header_background": [8, 7, 63, 255], + "main_window_header_background": [10, 8, 80, 255], "main_window_header_background_gradient": [25, 23, 91, 255], - "main_window_header_button_text_active": [8, 7, 63, 255], + "main_window_header_button_text_active": [10, 8, 80, 255], "main_window_header_button_text_inactive": [255, 255, 255, 255], "main_window_header_button_text_hovered": [255, 255, 255, 255], "main_window_header_button_background_active": [255, 255, 255, 255], "main_window_header_button_background_inactive": [255, 255, 255, 0], - "main_window_header_button_background_hovered": [117, 114, 159, 255], + "main_window_header_button_background_hovered": [255, 255, 255, 102], "account_widget_outline_active": [70, 66, 126, 255], @@ -108,13 +114,12 @@ "machine_selector_active": [68, 72, 75, 255], "machine_selector_hover": [68, 72, 75, 255], "machine_selector_text_active": [255, 255, 255, 255], - "machine_selector_printer_icon": [8, 7, 63, 255], "action_panel_secondary": [27, 95, 202, 255], "toolbar_background": [255, 255, 255, 255], - "printer_type_label_background": [228, 228, 242, 255], + "printer_type_label_background": [171, 171, 191, 255], "text": [0, 0, 0, 255], "text_detail": [174, 174, 174, 128], @@ -128,9 +133,9 @@ "text_scene_hover": [70, 84, 113, 255], "error": [255, 140, 0, 255], - "warning": [245, 166, 35, 255], + "warning": [255, 190, 35, 255], - "toolbar_button_text": [8, 7, 63, 255], + "toolbar_button_text": [10, 8, 80, 255], "toolbar_button_hover": [232, 242, 252, 255], "toolbar_button_active": [232, 242, 252, 255], "toolbar_button_active_hover": [232, 242, 252, 255], @@ -145,9 +150,9 @@ "button_text_active_hover": [255, 255, 255, 255], "small_button": [0, 0, 0, 0], - "small_button_hover": [8, 7, 63, 255], - "small_button_active": [8, 7, 63, 255], - "small_button_active_hover": [8, 7, 63, 255], + "small_button_hover": [10, 8, 80, 255], + "small_button_active": [10, 8, 80, 255], + "small_button_active_hover": [10, 8, 80, 255], "small_button_text": [171, 171, 191, 255], "small_button_text_hover": [255, 255, 255, 255], "small_button_text_active": [255, 255, 255, 255], @@ -223,8 +228,8 @@ "progressbar_control": [50, 130, 255, 255], "slider_groove": [223, 223, 223, 255], - "slider_groove_fill": [8, 7, 63, 255], - "slider_handle": [8, 7, 63, 255], + "slider_groove_fill": [10, 8, 80, 255], + "slider_handle": [10, 8, 80, 255], "slider_handle_active": [50, 130, 255, 255], "slider_text_background": [255, 255, 255, 255], @@ -236,7 +241,6 @@ "checkbox_border": [64, 69, 72, 255], "checkbox_border_hover": [50, 130, 255, 255], "checkbox_mark": [119, 122, 124, 255], - "checkbox_disabled": [223, 223, 223, 255], "checkbox_text": [27, 27, 27, 255], "tooltip": [68, 192, 255, 255], @@ -306,6 +310,14 @@ "layerview_support_interface": [64, 192, 255, 255], "layerview_nozzle": [181, 166, 66, 50], + "configuration_item": [255, 255, 255, 0], + "configuration_item_active": [12, 169, 227, 32], + "configuration_item_text": [0, 0, 0, 255], + "configuration_item_text_active": [0, 0, 0, 255], + "configuration_item_border": [127, 127, 127, 255], + "configuration_item_border_active": [12, 169, 227, 32], + "configuration_item_border_hover": [50, 130, 255, 255], + "tab_status_connected": [50, 130, 255, 255], "tab_status_disconnected": [200, 200, 200, 255], @@ -365,6 +377,7 @@ "action_panel_widget": [25.0, 0.0], "action_panel_information_widget": [20.0, 0.0], + "action_panel_button": [15.0, 3.0], "machine_selector_widget": [20.0, 4.0], "machine_selector_widget_content": [25.0, 32.0], @@ -410,9 +423,6 @@ "button_icon": [2.5, 2.5], "button_lining": [0, 0], - "action_button": [15.0, 3.0], - "action_button_radius": [0.15, 0.15], - "small_button": [2, 2], "small_button_icon": [1.5, 1.5], @@ -501,6 +511,9 @@ "avatar_image": [6.8, 6.8], + "action_button": [15.0, 3.0], + "action_button_radius": [0.15, 0.15], + "monitor_config_override_box": [1.0, 14.0], "monitor_extruder_circle": [2.75, 2.75], "monitor_text_line": [1.16, 1.16], From a1c252d3a20de28ae5c3f81b660b260e28b9065f Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Fri, 7 Dec 2018 10:25:46 +0100 Subject: [PATCH 0730/1240] Revert "Revert "fix merge conflict"" This reverts commit 4bffa6d90f2e6e9784c7e5508b61e00a92ecb74f. --- cura/Settings/ExtruderManager.py | 4 +- cura/Settings/ExtruderStack.py | 4 +- cura/Settings/ExtrudersModel.py | 6 +- cura/Settings/MachineManager.py | 4 +- .../ProcessSlicedLayersJob.py | 2 +- .../MachineSettingsAction.qml | 4 +- plugins/ModelChecker/ModelChecker.qml | 1 - .../PerObjectSettingsPanel.qml | 1 - .../PostProcessingPlugin.qml | 4 - plugins/PrepareStage/PrepareMenu.qml | 3 +- .../SimulationViewMainComponent.qml | 7 +- .../resources/qml/ToolboxAuthorPage.qml | 8 +- .../qml/ToolboxCompatibilityChart.qml | 2 +- .../resources/qml/ToolboxDetailPage.qml | 16 +- .../qml/ToolboxDownloadsGridTile.qml | 3 +- .../qml/ToolboxDownloadsShowcaseTile.qml | 2 - .../resources/qml/DiscoverUM3Action.qml | 16 +- .../qml/MonitorBuildplateConfiguration.qml | 2 +- .../qml/MonitorExtruderConfiguration.qml | 4 +- .../resources/qml/MonitorPrinterCard.qml | 10 +- .../src/ClusterUM3OutputDevice.py | 12 +- resources/qml/ActionButton.qml | 35 +- .../ActionPanel/OutputDevicesActionButton.qml | 4 + .../qml/ActionPanel/OutputProcessWidget.qml | 27 +- .../ActionPanel/PrintInformationWidget.qml | 3 - .../qml/ActionPanel/PrintJobInformation.qml | 8 +- .../qml/ActionPanel/SliceProcessWidget.qml | 7 +- resources/qml/Cura.qml | 89 +- resources/qml/CustomConfigurationSelector.qml | 357 ---- resources/qml/Dialogs/AboutDialog.qml | 6 +- resources/qml/Dialogs/AddMachineDialog.qml | 5 +- resources/qml/ExpandableComponent.qml | 6 +- resources/qml/ExtruderIcon.qml | 7 +- resources/qml/IconLabel.qml | 5 +- resources/qml/IconWithText.qml | 2 - resources/qml/JobSpecs.qml | 3 +- resources/qml/MainWindow/MainWindowHeader.qml | 30 +- .../ConfigurationMenu/AutoConfiguration.qml | 39 + .../ConfigurationMenu/ConfigurationItem.qml | 210 +- .../ConfigurationListView.qml | 77 +- .../ConfigurationMenu/ConfigurationMenu.qml | 203 ++ .../ConfigurationMenu/CustomConfiguration.qml | 250 +++ .../PrintCoreConfiguration.qml | 95 +- .../QuickConfigurationSelector.qml | 243 --- .../Menus/ConfigurationMenu/SyncButton.qml | 102 - resources/qml/Menus/ProfileMenu.qml | 8 +- resources/qml/ObjectsList.qml | 3 +- resources/qml/Preferences/MachinesPage.qml | 8 +- .../Materials/MaterialsBrandSection.qml | 5 +- .../Preferences/Materials/MaterialsList.qml | 8 +- .../Preferences/Materials/MaterialsSlot.qml | 2 - .../Materials/MaterialsTypeSection.qml | 2 - resources/qml/Preferences/ProfilesPage.qml | 24 +- .../qml/Preferences/SettingVisibilityPage.qml | 2 +- resources/qml/PrinterOutput/ExtruderBox.qml | 2 +- resources/qml/PrinterOutput/HeatedBedBox.qml | 2 +- .../qml/PrinterOutput/OutputDeviceHeader.qml | 4 +- .../qml/PrinterSelector/MachineSelector.qml | 60 +- .../PrinterSelector/MachineSelectorList.qml | 2 +- .../PrinterTypeLabel.qml | 2 +- resources/qml/Settings/SettingCategory.qml | 13 +- resources/qml/Settings/SettingCheckBox.qml | 4 +- .../qml/Settings/SettingOptionalExtruder.qml | 13 +- resources/qml/Settings/SettingView.qml | 11 +- resources/qml/SidebarSimple.qml | 7 +- resources/qml/Toolbar.qml | 2 +- resources/qml/ViewsSelector.qml | 6 +- resources/qml/qmldir | 1 - resources/themes/cura-dark/theme.json | 10 +- .../cura-light/images/header_pattern.svg | 1902 ++++++++++++++++- .../themes/cura-light/images/logo_about.svg | 172 ++ resources/themes/cura-light/styles.qml | 9 +- resources/themes/cura-light/theme.json | 51 +- 73 files changed, 3047 insertions(+), 1216 deletions(-) delete mode 100644 resources/qml/CustomConfigurationSelector.qml create mode 100644 resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml create mode 100644 resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml create mode 100644 resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml delete mode 100644 resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml delete mode 100644 resources/qml/Menus/ConfigurationMenu/SyncButton.qml rename resources/qml/{PrinterSelector => }/PrinterTypeLabel.qml (95%) create mode 100644 resources/themes/cura-light/images/logo_about.svg diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index 9089ba96e9..b0bcf3b100 100755 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -63,7 +63,7 @@ class ExtruderManager(QObject): if not self._application.getGlobalContainerStack(): return None # No active machine, so no active extruder. try: - return self._extruder_trains[self._application.getGlobalContainerStack().getId()][str(self._active_extruder_index)].getId() + return self._extruder_trains[self._application.getGlobalContainerStack().getId()][str(self.activeExtruderIndex)].getId() except KeyError: # Extruder index could be -1 if the global tab is selected, or the entry doesn't exist if the machine definition is wrong. return None @@ -144,7 +144,7 @@ class ExtruderManager(QObject): @pyqtSlot(result = QObject) def getActiveExtruderStack(self) -> Optional["ExtruderStack"]: - return self.getExtruderStack(self._active_extruder_index) + return self.getExtruderStack(self.activeExtruderIndex) ## Get an extruder stack by index def getExtruderStack(self, index) -> Optional["ExtruderStack"]: diff --git a/cura/Settings/ExtruderStack.py b/cura/Settings/ExtruderStack.py index d7faedb71c..d626ef06da 100644 --- a/cura/Settings/ExtruderStack.py +++ b/cura/Settings/ExtruderStack.py @@ -52,8 +52,8 @@ class ExtruderStack(CuraContainerStack): return super().getNextStack() def setEnabled(self, enabled: bool) -> None: - if "enabled" not in self._metadata: - self.setMetaDataEntry("enabled", "True") + if self.getMetaDataEntry("enabled", True) == enabled: #No change. + return #Don't emit a signal then. self.setMetaDataEntry("enabled", str(enabled)) self.enabledChanged.emit() diff --git a/cura/Settings/ExtrudersModel.py b/cura/Settings/ExtrudersModel.py index 14a8dadc69..5f10ac99d4 100644 --- a/cura/Settings/ExtrudersModel.py +++ b/cura/Settings/ExtrudersModel.py @@ -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. from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot, pyqtProperty, QTimer @@ -165,7 +165,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): def __updateExtruders(self): extruders_changed = False - if self.rowCount() != 0: + if self.count != 0: extruders_changed = True items = [] @@ -177,7 +177,7 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): machine_extruder_count = global_container_stack.getProperty("machine_extruder_count", "value") for extruder in Application.getInstance().getExtruderManager().getActiveExtruderStacks(): - position = extruder.getMetaDataEntry("position", default = "0") # Get the position + position = extruder.getMetaDataEntry("position", default = "0") try: position = int(position) except ValueError: diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 53390ca88d..a472cc7f09 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -874,7 +874,7 @@ class MachineManager(QObject): caution_message = Message(catalog.i18nc( "@info:generic", "Settings have been changed to match the current availability of extruders: [%s]" % ", ".join(add_user_changes)), - lifetime=0, + lifetime = 0, title = catalog.i18nc("@info:title", "Settings updated")) caution_message.show() @@ -1553,7 +1553,7 @@ class MachineManager(QObject): elif word.isdigit(): abbr_machine += word else: - stripped_word = ''.join(char for char in unicodedata.normalize('NFD', word.upper()) if unicodedata.category(char) != 'Mn') + stripped_word = "".join(char for char in unicodedata.normalize("NFD", word.upper()) if unicodedata.category(char) != "Mn") # - use only the first character if the word is too long (> 3 characters) # - use the whole word if it's not too long (<= 3 characters) if len(stripped_word) > 3: diff --git a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py index 594bf3a43e..71c96880e8 100644 --- a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py +++ b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py @@ -195,7 +195,7 @@ class ProcessSlicedLayersJob(Job): if extruders: material_color_map = numpy.zeros((len(extruders), 4), dtype=numpy.float32) for extruder in extruders: - position = int(extruder.getMetaDataEntry("position", default="0")) # Get the position + position = int(extruder.getMetaDataEntry("position", default = "0")) try: default_color = ExtrudersModel.defaultColors[position] except IndexError: diff --git a/plugins/MachineSettingsAction/MachineSettingsAction.qml b/plugins/MachineSettingsAction/MachineSettingsAction.qml index 004b4e3cfc..c88a721a84 100644 --- a/plugins/MachineSettingsAction/MachineSettingsAction.qml +++ b/plugins/MachineSettingsAction/MachineSettingsAction.qml @@ -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 QtQuick 2.2 @@ -23,7 +23,7 @@ Cura.MachineAction target: base.extrudersModel onModelChanged: { - var extruderCount = base.extrudersModel.rowCount(); + var extruderCount = base.extrudersModel.count; base.extruderTabsCount = extruderCount; } } diff --git a/plugins/ModelChecker/ModelChecker.qml b/plugins/ModelChecker/ModelChecker.qml index 5e41591d6b..437df29516 100644 --- a/plugins/ModelChecker/ModelChecker.qml +++ b/plugins/ModelChecker/ModelChecker.qml @@ -33,7 +33,6 @@ Button { width: UM.Theme.getSize("save_button_specs_icons").width; height: UM.Theme.getSize("save_button_specs_icons").height; - sourceSize.width: width; sourceSize.height: width; color: control.hovered ? UM.Theme.getColor("text_scene_hover") : UM.Theme.getColor("text_scene"); source: "model_checker.svg" diff --git a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml index 5d4e17a102..0e2bd88619 100644 --- a/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml +++ b/plugins/PerObjectSettingsTool/PerObjectSettingsPanel.qml @@ -265,7 +265,6 @@ Item { anchors.verticalCenter: parent.verticalCenter width: parent.width height: width - sourceSize.width: width sourceSize.height: width color: control.hovered ? UM.Theme.getColor("setting_control_button_hover") : UM.Theme.getColor("setting_control_button") source: UM.Theme.getIcon("minus") diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml index bd4d361d35..3fa10c23b9 100644 --- a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml +++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml @@ -141,7 +141,6 @@ UM.Dialog anchors.horizontalCenter: parent.horizontalCenter width: Math.round(control.width / 2.7) height: Math.round(control.height / 2.7) - sourceSize.width: width sourceSize.height: width color: palette.text source: UM.Theme.getIcon("cross1") @@ -176,7 +175,6 @@ UM.Dialog anchors.horizontalCenter: parent.horizontalCenter width: Math.round(control.width / 2.5) height: Math.round(control.height / 2.5) - sourceSize.width: width sourceSize.height: width color: control.enabled ? palette.text : disabledPalette.text source: UM.Theme.getIcon("arrow_bottom") @@ -211,7 +209,6 @@ UM.Dialog anchors.horizontalCenter: parent.horizontalCenter width: Math.round(control.width / 2.5) height: Math.round(control.height / 2.5) - sourceSize.width: width sourceSize.height: width color: control.enabled ? palette.text : disabledPalette.text source: UM.Theme.getIcon("arrow_top") @@ -498,7 +495,6 @@ UM.Dialog anchors.horizontalCenter: parent.horizontalCenter width: Math.round(parent.width / 2) height: Math.round(parent.height / 2) - sourceSize.width: width sourceSize.height: height color: !control.enabled ? UM.Theme.getColor("action_button_disabled_text") : control.pressed ? UM.Theme.getColor("action_button_active_text") : diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index 10b4262f01..fa94bc88b2 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -61,7 +61,7 @@ Item color: UM.Theme.getColor("lining") } - Cura.QuickConfigurationSelector + Cura.ConfigurationMenu { Layout.fillHeight: true Layout.fillWidth: true @@ -107,7 +107,6 @@ Item height: UM.Theme.getSize("button_icon").height color: UM.Theme.getColor("toolbar_button_text") - sourceSize.width: width sourceSize.height: height } } diff --git a/plugins/SimulationView/SimulationViewMainComponent.qml b/plugins/SimulationView/SimulationViewMainComponent.qml index 16b9aeaae6..16b049c921 100644 --- a/plugins/SimulationView/SimulationViewMainComponent.qml +++ b/plugins/SimulationView/SimulationViewMainComponent.qml @@ -61,10 +61,9 @@ Item iconSource: !is_simulation_playing ? "./resources/simulation_resume.svg": "./resources/simulation_pause.svg" width: UM.Theme.getSize("small_button").width height: UM.Theme.getSize("small_button").height - hoverBackgroundColor: UM.Theme.getColor("small_button_hover") - hoverColor: UM.Theme.getColor("small_button_text_hover") - color: UM.Theme.getColor("small_button_text") - iconMargin: 0.5 * UM.Theme.getSize("wide_lining").width + hoverColor: UM.Theme.getColor("slider_handle_active") + color: UM.Theme.getColor("slider_handle") + iconMargin: UM.Theme.getSize("thick_lining").width visible: !UM.SimulationView.compatibilityMode Connections diff --git a/plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml b/plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml index 4aaea20813..9c1df0c49e 100644 --- a/plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml @@ -86,13 +86,13 @@ Item Label { text: catalog.i18nc("@label", "Website") + ":" - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text_medium") } Label { text: catalog.i18nc("@label", "Email") + ":" - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text_medium") } } @@ -118,7 +118,7 @@ Item } return "" } - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") linkColor: UM.Theme.getColor("text_link") onLinkActivated: Qt.openUrlExternally(link) @@ -134,7 +134,7 @@ Item } return "" } - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") linkColor: UM.Theme.getColor("text_link") onLinkActivated: Qt.openUrlExternally(link) diff --git a/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml b/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml index 4a6268df42..d4c0ae14eb 100644 --- a/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml +++ b/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml @@ -228,7 +228,7 @@ Item return result } - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") linkColor: UM.Theme.getColor("text_link") onLinkActivated: Qt.openUrlExternally(link) diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml index c5e9bb0a49..9e2e178b71 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml @@ -82,25 +82,25 @@ Item Label { text: catalog.i18nc("@label", "Version") + ":" - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text_medium") } Label { text: catalog.i18nc("@label", "Last updated") + ":" - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text_medium") } Label { text: catalog.i18nc("@label", "Author") + ":" - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text_medium") } Label { text: catalog.i18nc("@label", "Downloads") + ":" - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text_medium") } } @@ -119,7 +119,7 @@ Item Label { text: details === null ? "" : (details.version || catalog.i18nc("@label", "Unknown")) - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") } Label @@ -133,7 +133,7 @@ Item var date = new Date(details.last_updated) return date.toLocaleString(UM.Preferences.getValue("general/language")) } - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") } Label @@ -149,7 +149,7 @@ Item return "" + details.author_name + "" } } - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") linkColor: UM.Theme.getColor("text_link") onLinkActivated: Qt.openUrlExternally(link) @@ -157,7 +157,7 @@ Item Label { text: details === null ? "" : (details.download_count || catalog.i18nc("@label", "Unknown")) - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") } } diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml index 887140bbfa..61374f9d99 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml @@ -52,7 +52,6 @@ Item bottom: parent.bottom right: parent.right } - sourceSize.width: width sourceSize.height: height visible: installedPackages != 0 color: (installedPackages == packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border") @@ -81,7 +80,7 @@ Item width: parent.width wrapMode: Text.WordWrap color: UM.Theme.getColor("text_medium") - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") } } } diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml index 4fb70541d2..8a2fdc8bc8 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml @@ -48,8 +48,6 @@ Rectangle right: parent.right bottomMargin: UM.Theme.getSize("default_lining").width } - sourceSize.width: width - sourceSize.height: height visible: installedPackages != 0 color: (installedPackages == packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border") source: "../images/installed_check.svg" diff --git a/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml b/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml index 967adfc029..bb710127fc 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml @@ -64,6 +64,7 @@ Cura.MachineAction width: parent.width text: catalog.i18nc("@title:window", "Connect to Networked Printer") wrapMode: Text.WordWrap + renderType: Text.NativeRendering font.pointSize: 18 } @@ -72,6 +73,7 @@ Cura.MachineAction id: pageDescription width: parent.width wrapMode: Text.WordWrap + renderType: Text.NativeRendering text: catalog.i18nc("@label", "To print directly to your printer over the network, please make sure your printer is connected to the network using a network cable or by connecting your printer to your WIFI network. If you don't connect Cura with your printer, you can still use a USB drive to transfer g-code files to your printer.\n\nSelect your printer from the list below:") } @@ -182,6 +184,7 @@ Cura.MachineAction text: listview.model[index].name color: parent.ListView.isCurrentItem ? palette.highlightedText : palette.text elide: Text.ElideRight + renderType: Text.NativeRendering } MouseArea @@ -204,6 +207,7 @@ Cura.MachineAction anchors.left: parent.left anchors.right: parent.right wrapMode: Text.WordWrap + renderType: Text.NativeRendering text: catalog.i18nc("@label", "If your printer is not listed, read the network printing troubleshooting guide").arg("https://ultimaker.com/en/troubleshooting"); onLinkActivated: Qt.openUrlExternally(link) } @@ -221,6 +225,7 @@ Cura.MachineAction text: base.selectedDevice ? base.selectedDevice.name : "" font: UM.Theme.getFont("large") elide: Text.ElideRight + renderType: Text.NativeRendering } Grid { @@ -231,12 +236,14 @@ Cura.MachineAction { width: Math.round(parent.width * 0.5) wrapMode: Text.WordWrap + renderType: Text.NativeRendering text: catalog.i18nc("@label", "Type") } Label { width: Math.round(parent.width * 0.5) wrapMode: Text.WordWrap + renderType: Text.NativeRendering text: { if(base.selectedDevice) @@ -268,24 +275,28 @@ Cura.MachineAction { width: Math.round(parent.width * 0.5) wrapMode: Text.WordWrap + renderType: Text.NativeRendering text: catalog.i18nc("@label", "Firmware version") } Label { width: Math.round(parent.width * 0.5) wrapMode: Text.WordWrap + renderType: Text.NativeRendering text: base.selectedDevice ? base.selectedDevice.firmwareVersion : "" } Label { width: Math.round(parent.width * 0.5) wrapMode: Text.WordWrap + renderType: Text.NativeRendering text: catalog.i18nc("@label", "Address") } Label { width: Math.round(parent.width * 0.5) wrapMode: Text.WordWrap + renderType: Text.NativeRendering text: base.selectedDevice ? base.selectedDevice.ipAddress : "" } } @@ -294,6 +305,7 @@ Cura.MachineAction { width: parent.width wrapMode: Text.WordWrap + renderType: Text.NativeRendering text:{ // The property cluster size does not exist for older UM3 devices. if(!base.selectedDevice || base.selectedDevice.clusterSize == null || base.selectedDevice.clusterSize == 1) @@ -315,6 +327,7 @@ Cura.MachineAction { width: parent.width wrapMode: Text.WordWrap + renderType: Text.NativeRendering visible: base.selectedDevice != null && !base.completeProperties text: catalog.i18nc("@label", "The printer at this address has not yet responded." ) } @@ -358,9 +371,10 @@ Cura.MachineAction Label { - text: catalog.i18nc("@alabel","Enter the IP address or hostname of your printer on the network.") + text: catalog.i18nc("@alabel", "Enter the IP address or hostname of your printer on the network.") width: parent.width wrapMode: Text.WordWrap + renderType: Text.NativeRendering } TextField diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml index 9ffb1eabb4..7edeb81a96 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml @@ -52,7 +52,7 @@ Item id: buildplateLabel color: "#191919" // TODO: Theme! elide: Text.ElideRight - font: UM.Theme.getFont("very_small") // 12pt, regular + font: UM.Theme.getFont("default") // 12pt, regular text: "" // FIXED-LINE-HEIGHT: diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorExtruderConfiguration.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorExtruderConfiguration.qml index afbd4c3641..1e53191d8c 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorExtruderConfiguration.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorExtruderConfiguration.qml @@ -49,7 +49,7 @@ Item } color: "#191919" // TODO: Theme! elide: Text.ElideRight - font: UM.Theme.getFont("very_small") // 12pt, regular + font: UM.Theme.getFont("default") // 12pt, regular text: "" // FIXED-LINE-HEIGHT: @@ -66,7 +66,7 @@ Item } color: "#191919" // TODO: Theme! elide: Text.ElideRight - font: UM.Theme.getFont("small") // 12pt, bold + font: UM.Theme.getFont("default_bold") // 12pt, bold text: "" // FIXED-LINE-HEIGHT: diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml index 8659037cb8..567fff8489 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml @@ -179,13 +179,15 @@ Item color: "#414054" // TODO: Theme! font: UM.Theme.getFont("large") // 16pt, bold text: { - if (printer && printer.state == "disabled"){ + if (printer && printer.state == "disabled") + { return catalog.i18nc("@label:status", "Unavailable") } - if (printer && printer.state == "unreachable"){ - return catalog.i18nc("@label:status", "Unavailable") + if (printer && printer.state == "unreachable") + { + return catalog.i18nc("@label:status", "Unreachable") } - if (printer && !printer.activePrintJob) + if (printer && printer.state == "idle") { return catalog.i18nc("@label:status", "Idle") } diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index cc5b128479..a5ee3bc650 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -572,7 +572,17 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): def _createMaterialOutputModel(self, material_data: Dict[str, Any]) -> "MaterialOutputModel": material_manager = CuraApplication.getInstance().getMaterialManager() - material_group_list = material_manager.getMaterialGroupListByGUID(material_data["guid"]) or [] + material_group_list = material_manager.getMaterialGroupListByGUID(material_data["guid"]) + # This can happen if the connected machine has no material in one or more extruders (if GUID is empty), or the + # material is unknown to Cura, so we should return an "empty" or "unknown" material model. + if material_group_list is None: + material_name = "Empty" if len(material_data["guid"]) == 0 else "Unknown" + return MaterialOutputModel(guid = material_data["guid"], + type = material_data.get("type", ""), + color = material_data.get("color", ""), + brand = material_data.get("brand", ""), + name = material_data.get("name", material_name) + ) # Sort the material groups by "is_read_only = True" first, and then the name alphabetically. read_only_material_group_list = list(filter(lambda x: x.is_read_only, material_group_list)) diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index b9a04f3b46..fc4a1c05f4 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -7,14 +7,17 @@ import QtQuick.Controls 2.1 import QtGraphicalEffects 1.0 // For the dropshadow import UM 1.1 as UM +import Cura 1.0 as Cura Button { id: button - property alias iconSource: buttonIcon.source + property alias iconSource: buttonIconLeft.source + property bool isIconOnRightSide: false property alias textFont: buttonText.font property alias cornerRadius: backgroundRect.radius property alias tooltip: tooltip.text + property alias cornerSide: backgroundRect.cornerSide property color color: UM.Theme.getColor("primary") property color hoverColor: UM.Theme.getColor("primary_hover") @@ -36,18 +39,21 @@ Button // we elide the text to the right so the text will be cut off with the three dots at the end. property var fixedWidthMode: false + leftPadding: UM.Theme.getSize("default_margin").width + rightPadding: UM.Theme.getSize("default_margin").width + height: UM.Theme.getSize("action_button").height + contentItem: Row { + //Left side icon. Only displayed if !isIconOnRightSide. UM.RecolorImage { - id: buttonIcon + id: buttonIconLeft source: "" - height: Math.round(0.6 * parent.height) - width: height - sourceSize.width: width - sourceSize.height: height + height: buttonText.height + width: visible ? height : 0 color: button.hovered ? button.textHoverColor : button.textColor - visible: source != "" + visible: source != "" && !button.isIconOnRightSide anchors.verticalCenter: parent.verticalCenter } @@ -64,11 +70,24 @@ Button horizontalAlignment: Text.AlignHCenter elide: Text.ElideRight } + + //Right side icon. Only displayed if isIconOnRightSide. + UM.RecolorImage + { + id: buttonIconRight + source: buttonIconLeft.source + height: buttonText.height + width: visible ? height : 0 + color: buttonIconLeft.color + visible: source != "" && button.isIconOnRightSide + anchors.verticalCenter: buttonIconLeft.verticalCenter + } } - background: Rectangle + background: Cura.RoundedRectangle { id: backgroundRect + cornerSide: Cura.RoundedRectangle.Direction.All color: button.enabled ? (button.hovered ? button.hoverColor : button.color) : button.disabledColor radius: UM.Theme.getSize("action_button_radius").width border.width: UM.Theme.getSize("default_lining").width diff --git a/resources/qml/ActionPanel/OutputDevicesActionButton.qml b/resources/qml/ActionPanel/OutputDevicesActionButton.qml index 2111038cfc..95750e6d11 100644 --- a/resources/qml/ActionPanel/OutputDevicesActionButton.qml +++ b/resources/qml/ActionPanel/OutputDevicesActionButton.qml @@ -17,6 +17,7 @@ Item id: saveToButton height: parent.height fixedWidthMode: true + cornerSide: deviceSelectionMenu.visible ? Cura.RoundedRectangle.Direction.Left : Cura.RoundedRectangle.Direction.All anchors { @@ -44,6 +45,7 @@ Item shadowEnabled: true shadowColor: UM.Theme.getColor("primary_shadow") + cornerSide: Cura.RoundedRectangle.Direction.Right anchors { @@ -51,6 +53,8 @@ Item right: parent.right } + leftPadding: UM.Theme.getSize("narrow_margin").width //Need more space than usual here for wide text. + rightPadding: UM.Theme.getSize("narrow_margin").width tooltip: catalog.i18nc("@info:tooltip", "Select the active output device") iconSource: popup.opened ? UM.Theme.getIcon("arrow_top") : UM.Theme.getIcon("arrow_bottom") color: UM.Theme.getColor("action_panel_secondary") diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml index 6ab8dc6fbb..1d1a1e44e1 100644 --- a/resources/qml/ActionPanel/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -51,7 +51,7 @@ Column text: preSlicedData ? catalog.i18nc("@label", "No time estimation available") : PrintInformation.currentPrintTime.getDisplayString(UM.DurationFormat.Long) source: UM.Theme.getIcon("clock") - font: UM.Theme.getFont("small") + font: UM.Theme.getFont("default_bold") } Cura.IconLabel @@ -84,7 +84,7 @@ Column return totalWeights + "g · " + totalLengths.toFixed(2) + "m" } source: UM.Theme.getIcon("spool") - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") } } @@ -101,17 +101,25 @@ Column } } - Row + Item { id: buttonRow - spacing: UM.Theme.getSize("default_margin").width - width: parent.width + anchors.right: parent.right + anchors.left: parent.left + height: UM.Theme.getSize("action_button").height Cura.SecondaryButton { id: previewStageShortcut - height: UM.Theme.getSize("action_panel_button").height + anchors + { + left: parent.left + right: outputDevicesButton.left + rightMargin: UM.Theme.getSize("default_margin").width + } + + height: UM.Theme.getSize("action_button").height leftPadding: UM.Theme.getSize("default_margin").width rightPadding: UM.Theme.getSize("default_margin").width text: catalog.i18nc("@button", "Preview") @@ -125,8 +133,11 @@ Column Cura.OutputDevicesActionButton { - width: previewStageShortcut.visible ? UM.Theme.getSize("action_panel_button").width : parent.width - height: UM.Theme.getSize("action_panel_button").height + id: outputDevicesButton + + anchors.right: parent.right + width: previewStageShortcut.visible ? UM.Theme.getSize("action_button").width : parent.width + height: UM.Theme.getSize("action_button").height } } } \ No newline at end of file diff --git a/resources/qml/ActionPanel/PrintInformationWidget.qml b/resources/qml/ActionPanel/PrintInformationWidget.qml index 25e380dea8..554273a818 100644 --- a/resources/qml/ActionPanel/PrintInformationWidget.qml +++ b/resources/qml/ActionPanel/PrintInformationWidget.qml @@ -15,9 +15,6 @@ UM.RecolorImage width: UM.Theme.getSize("section_icon").width height: UM.Theme.getSize("section_icon").height - sourceSize.width: width - sourceSize.height: height - color: popup.opened ? UM.Theme.getColor("primary") : UM.Theme.getColor("text_medium") MouseArea diff --git a/resources/qml/ActionPanel/PrintJobInformation.qml b/resources/qml/ActionPanel/PrintJobInformation.qml index 156111af4d..8bd5d5a0d3 100644 --- a/resources/qml/ActionPanel/PrintJobInformation.qml +++ b/resources/qml/ActionPanel/PrintJobInformation.qml @@ -30,7 +30,7 @@ Column { text: catalog.i18nc("@label", "Time specification").toUpperCase() color: UM.Theme.getColor("primary") - font: UM.Theme.getFont("small") + font: UM.Theme.getFont("default_bold") renderType: Text.NativeRendering } @@ -61,7 +61,7 @@ Column } width: parent.width - 2 * UM.Theme.getSize("default_margin").width color: UM.Theme.getColor("text") - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") renderType: Text.NativeRendering textFormat: Text.RichText } @@ -79,7 +79,7 @@ Column { text: catalog.i18nc("@label", "Material specification").toUpperCase() color: UM.Theme.getColor("primary") - font: UM.Theme.getFont("small") + font: UM.Theme.getFont("default_bold") renderType: Text.NativeRendering } @@ -151,7 +151,7 @@ Column } width: parent.width - 2 * UM.Theme.getSize("default_margin").width color: UM.Theme.getColor("text") - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") renderType: Text.NativeRendering textFormat: Text.RichText } diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index 03d91db530..8f6608e15c 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -48,7 +48,7 @@ Column text: catalog.i18nc("@label:PrintjobStatus", "Auto slicing...") color: UM.Theme.getColor("text") - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") renderType: Text.NativeRendering } @@ -61,7 +61,7 @@ Column text: catalog.i18nc("@label:PrintjobStatus", "Unable to Slice") source: UM.Theme.getIcon("warning") color: UM.Theme.getColor("warning") - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") } // Progress bar, only visible when the backend is in the process of slice the printjob @@ -94,7 +94,6 @@ Column } } - Item { id: prepareButtons @@ -103,7 +102,7 @@ Column // Disable the slice process when width: parent.width - height: UM.Theme.getSize("action_panel_button").height + height: UM.Theme.getSize("action_button").height visible: !autoSlice Cura.PrimaryButton { diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 3578888886..4e8e9ce788 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -88,6 +88,54 @@ UM.MainWindow window: base } + Rectangle + { + id: headerBackground + anchors + { + top: applicationMenu.bottom + left: parent.left + right: parent.right + } + height: stageMenu.source != "" ? Math.round(mainWindowHeader.height + stageMenu.height / 2) : mainWindowHeader.height + + LinearGradient + { + anchors.fill: parent + start: Qt.point(0, 0) + end: Qt.point(parent.width, 0) + gradient: Gradient + { + GradientStop + { + position: 0.0 + color: UM.Theme.getColor("main_window_header_background") + } + GradientStop + { + position: 0.5 + color: UM.Theme.getColor("main_window_header_background_gradient") + } + GradientStop + { + position: 1.0 + color: UM.Theme.getColor("main_window_header_background") + } + } + } + + // This is the new fancy pattern + Image + { + id: backgroundPattern + anchors.fill: parent + fillMode: Image.Tile + source: UM.Theme.getImage("header_pattern") + horizontalAlignment: Image.AlignLeft + verticalAlignment: Image.AlignTop + } + } + MainWindowHeader { id: mainWindowHeader @@ -144,44 +192,6 @@ UM.MainWindow } } - Rectangle - { - id: stageMenuBackground - anchors - { - left: parent.left - right: parent.right - top: parent.top - } - visible: stageMenu.source != "" - height: visible ? Math.round(UM.Theme.getSize("stage_menu").height / 2) : 0 - - LinearGradient - { - anchors.fill: parent - start: Qt.point(0, 0) - end: Qt.point(parent.width, 0) - gradient: Gradient - { - GradientStop - { - position: 0.0 - color: UM.Theme.getColor("main_window_header_background") - } - GradientStop - { - position: 0.5 - color: UM.Theme.getColor("main_window_header_background_gradient") - } - GradientStop - { - position: 1.0 - color: UM.Theme.getColor("main_window_header_background") - } - } - } - } - Connections { target: stageMenu.item @@ -257,7 +267,8 @@ UM.MainWindow anchors { - top: stageMenuBackground.bottom + // Align to the top of the stageMenu since the stageMenu may not exist + top: parent.top left: parent.left right: parent.right bottom: parent.bottom diff --git a/resources/qml/CustomConfigurationSelector.qml b/resources/qml/CustomConfigurationSelector.qml deleted file mode 100644 index c78ca700da..0000000000 --- a/resources/qml/CustomConfigurationSelector.qml +++ /dev/null @@ -1,357 +0,0 @@ -// Copyright (c) 2018 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.7 -import QtQuick.Controls 1.1 -import QtQuick.Controls.Styles 1.1 - -import UM 1.2 as UM -import Cura 1.0 as Cura - -Rectangle -{ - implicitWidth: parent.width - implicitHeight: parent.height - - id: base - color: UM.Theme.getColor("main_background") - - // Height has an extra 2x margin for the top & bottom margin. - height: childrenRect.height + 2 * UM.Theme.getSize("default_margin").width - - Cura.ExtrudersModel { id: extrudersModel } - - ListView - { - // Horizontal list that shows the extruders - id: extrudersList - visible: extrudersModel.items.length > 1 - property var index: 0 - - height: UM.Theme.getSize("configuration_selector_mode_tabs").height - boundsBehavior: Flickable.StopAtBounds - - anchors - { - left: parent.left - right: parent.right - top: parent.top - margins: UM.Theme.getSize("thick_margin").width - } - - ExclusiveGroup { id: extruderMenuGroup } - - orientation: ListView.Horizontal - - model: extrudersModel - - Connections - { - target: Cura.MachineManager - onGlobalContainerChanged: forceActiveFocus() // Changing focus applies the currently-being-typed values so it can change the displayed setting values. - } - - delegate: Button - { - height: parent.height - width: Math.round(ListView.view.width / extrudersModel.rowCount()) - - text: model.name - tooltip: model.name - exclusiveGroup: extruderMenuGroup - checked: Cura.ExtruderManager.activeExtruderIndex == index - - property bool extruder_enabled: true - - MouseArea // TODO; This really should be fixed. It makes absolutely no sense to have a button AND a mouse area. - { - anchors.fill: parent - acceptedButtons: Qt.LeftButton | Qt.RightButton - onClicked: - { - switch (mouse.button) - { - case Qt.LeftButton: - extruder_enabled = Cura.MachineManager.getExtruder(model.index).isEnabled - if (extruder_enabled) - { - forceActiveFocus() // Changing focus applies the currently-being-typed values so it can change the displayed setting values. - Cura.ExtruderManager.setActiveExtruderIndex(index) - } - break - case Qt.RightButton: - extruder_enabled = Cura.MachineManager.getExtruder(model.index).isEnabled - extruderMenu.popup() - break - } - } - } - - Menu - { - id: extruderMenu - - MenuItem - { - text: catalog.i18nc("@action:inmenu", "Enable Extruder") - onTriggered: Cura.MachineManager.setExtruderEnabled(model.index, true) - visible: !extruder_enabled // using an intermediate variable prevents an empty popup that occured now and then - } - - MenuItem - { - text: catalog.i18nc("@action:inmenu", "Disable Extruder") - onTriggered: Cura.MachineManager.setExtruderEnabled(model.index, false) - visible: extruder_enabled - enabled: Cura.MachineManager.numberExtrudersEnabled > 1 - } - } - - style: ButtonStyle - { - background: Rectangle - { - anchors.fill: parent - border.width: control.checked ? UM.Theme.getSize("default_lining").width * 2 : UM.Theme.getSize("default_lining").width - border.color: - { - if (Cura.MachineManager.getExtruder(index).isEnabled) - { - if(control.checked || control.pressed) - { - return UM.Theme.getColor("action_button_active_border") - } - else if (control.hovered) - { - return UM.Theme.getColor("action_button_hovered_border") - } - return UM.Theme.getColor("action_button_border") - } - return UM.Theme.getColor("action_button_disabled_border") - } - color: - { - if (Cura.MachineManager.getExtruder(index).isEnabled) - { - if(control.checked || control.pressed) - { - return UM.Theme.getColor("action_button_active"); - } - else if (control.hovered) - { - return UM.Theme.getColor("action_button_hovered") - } - return UM.Theme.getColor("action_button") - } - return UM.Theme.getColor("action_button_disabled") - } - Behavior on color { ColorAnimation { duration: 50; } } - - Item - { - id: extruderButtonFace - anchors.centerIn: parent - width: childrenRect.width - - Label - { - // Static text that holds the "Extruder" label - id: extruderStaticText - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - - color: - { - if (Cura.MachineManager.getExtruder(index).isEnabled) - { - if(control.checked || control.pressed) - { - return UM.Theme.getColor("action_button_active_text"); - } - else if (control.hovered) - { - return UM.Theme.getColor("action_button_hovered_text") - } - return UM.Theme.getColor("action_button_text") - } - return UM.Theme.getColor("action_button_disabled_text") - } - - font: UM.Theme.getFont("large_nonbold") - text: catalog.i18nc("@label", "Extruder") - visible: width < (control.width - extruderIcon.width - UM.Theme.getSize("default_margin").width) - elide: Text.ElideRight - } - - ExtruderIcon - { - // Round icon with the extruder number and material color indicator. - id: extruderIcon - - anchors.verticalCenter: parent.verticalCenter - anchors.left: extruderStaticText.right - anchors.leftMargin: UM.Theme.getSize("default_margin").width - width: control.height - Math.round(UM.Theme.getSize("default_margin").width / 2) - height: width - - checked: control.checked - materialColor: model.color - textColor: extruderStaticText.color - } - } - } - - label: Item {} - } - } - } - - Item - { - id: materialRow - height: UM.Theme.getSize("print_setup_item").height - visible: Cura.MachineManager.hasMaterials - - anchors - { - left: parent.left - right: parent.right - top: extrudersList.bottom - margins: UM.Theme.getSize("thick_margin").width - } - - Label - { - id: materialLabel - text: catalog.i18nc("@label", "Material"); - width: Math.round(parent.width * 0.45 - UM.Theme.getSize("default_margin").width) - height: parent.height - verticalAlignment: Text.AlignVCenter - font: UM.Theme.getFont("default"); - color: UM.Theme.getColor("text"); - } - - ToolButton - { - id: materialSelection - - property var activeExtruder: Cura.MachineManager.activeStack - property var hasActiveExtruder: activeExtruder != null - property var currentRootMaterialName: hasActiveExtruder ? activeExtruder.material.name : "" - - text: currentRootMaterialName - tooltip: currentRootMaterialName - visible: Cura.MachineManager.hasMaterials - - enabled: !extrudersList.visible || Cura.ExtruderManager.activeExtruderIndex > -1 - - height: UM.Theme.getSize("setting_control").height - width: Math.round(parent.width * 0.7) + UM.Theme.getSize("thick_margin").width - anchors.right: parent.right - style: UM.Theme.styles.sidebar_header_button - activeFocusOnPress: true; - menu: Cura.MaterialMenu - { - extruderIndex: Cura.ExtruderManager.activeExtruderIndex - } - - property var valueError: hasActiveExtruder ? Cura.ContainerManager.getContainerMetaDataEntry(activeExtruder.material.id, "compatible", "") != "True" : true - property var valueWarning: ! Cura.MachineManager.isActiveQualitySupported - } - } - - Item - { - id: variantRow - height: UM.Theme.getSize("print_setup_item").height - visible: Cura.MachineManager.hasVariants - - anchors - { - left: parent.left - right: parent.right - top: materialRow.bottom - margins: UM.Theme.getSize("thick_margin").width - } - - Label - { - id: variantLabel - text: Cura.MachineManager.activeDefinitionVariantsName; - width: Math.round(parent.width * 0.45 - UM.Theme.getSize("default_margin").width) - height: parent.height - verticalAlignment: Text.AlignVCenter - font: UM.Theme.getFont("default"); - color: UM.Theme.getColor("text"); - } - - ToolButton - { - id: variantSelection - text: Cura.MachineManager.activeVariantName - tooltip: Cura.MachineManager.activeVariantName; - visible: Cura.MachineManager.hasVariants - - height: UM.Theme.getSize("setting_control").height - width: Math.round(parent.width * 0.7 + UM.Theme.getSize("thick_margin").width) - anchors.right: parent.right - style: UM.Theme.styles.sidebar_header_button - activeFocusOnPress: true; - - menu: Cura.NozzleMenu { extruderIndex: Cura.ExtruderManager.activeExtruderIndex } - } - } - - Item - { - id: materialCompatibilityLink - height: UM.Theme.getSize("print_setup_item").height - - anchors.right: parent.right - anchors.top: variantRow.bottom - anchors.margins: UM.Theme.getSize("thick_margin").width - UM.RecolorImage - { - id: warningImage - - anchors.right: materialInfoLabel.left - anchors.rightMargin: UM.Theme.getSize("default_margin").width - - source: UM.Theme.getIcon("warning") - width: UM.Theme.getSize("section_icon").width - height: UM.Theme.getSize("section_icon").height - - sourceSize.width: width - sourceSize.height: 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.right: parent.right - - MouseArea - { - anchors.fill: parent - - onClicked: - { - // open the material URL with web browser - Qt.openUrlExternally("https://ultimaker.com/incoming-links/cura/material-compatibilty"); - } - } - } - } -} diff --git a/resources/qml/Dialogs/AboutDialog.qml b/resources/qml/Dialogs/AboutDialog.qml index 25c9bbf74b..add84614e0 100644 --- a/resources/qml/Dialogs/AboutDialog.qml +++ b/resources/qml/Dialogs/AboutDialog.qml @@ -35,12 +35,10 @@ UM.Dialog { id: logo width: (base.minimumWidth * 0.85) | 0 - height: (width * (1/4.25)) | 0 + height: (width * (UM.Theme.getSize("logo").height / UM.Theme.getSize("logo").width)) | 0 - source: UM.Theme.getImage("logo") + source: UM.Theme.getImage("logo_about") - sourceSize.width: width - sourceSize.height: height anchors.top: parent.top anchors.topMargin: ((base.minimumWidth - width) / 2) | 0 anchors.horizontalCenter: parent.horizontalCenter diff --git a/resources/qml/Dialogs/AddMachineDialog.qml b/resources/qml/Dialogs/AddMachineDialog.qml index 8e966c3df7..f00359869c 100644 --- a/resources/qml/Dialogs/AddMachineDialog.qml +++ b/resources/qml/Dialogs/AddMachineDialog.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 @@ -156,7 +156,6 @@ UM.Dialog anchors.rightMargin: UM.Theme.getSize("default_margin").width width: UM.Theme.getSize("standard_arrow").width height: UM.Theme.getSize("standard_arrow").height - sourceSize.width: width sourceSize.height: width color: palette.windowText source: base.activeCategory == section ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_right") @@ -170,7 +169,7 @@ UM.Dialog if (machineList.model.getItem(machineList.currentIndex).section != section) { // Find the first machine from this section - for(var i = 0; i < machineList.model.rowCount(); i++) + for(var i = 0; i < machineList.model.count; i++) { var item = machineList.model.getItem(i); if (item.section == section) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index b438f0398c..e42aa7e4a1 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -32,6 +32,8 @@ Item property color headerBackgroundColor: UM.Theme.getColor("action_button") property color headerHoverColor: UM.Theme.getColor("action_button_hovered") + property alias enabled: mouseArea.enabled + // Defines the alignment of the popup with respect of the headerItem, by default to the right property int popupAlignment: ExpandableComponent.PopupAlignment.AlignRight @@ -139,9 +141,7 @@ Item verticalCenter: parent.verticalCenter margins: background.padding } - sourceSize.width: width - sourceSize.height: height - visible: source != "" + visible: source != "" && base.enabled width: height height: Math.round(0.2 * base.height) color: UM.Theme.getColor("text") diff --git a/resources/qml/ExtruderIcon.qml b/resources/qml/ExtruderIcon.qml index c1a202050b..49ad73a32e 100644 --- a/resources/qml/ExtruderIcon.qml +++ b/resources/qml/ExtruderIcon.qml @@ -22,8 +22,6 @@ Item id: mainIcon anchors.fill: parent - sourceSize.width: parent.width - sourceSize.height: parent.height source: UM.Theme.getIcon("extruder_button") color: extruderEnabled ? materialColor: "gray" } @@ -50,7 +48,9 @@ Item id: extruderNumberText anchors.centerIn: parent text: index + 1 - font: UM.Theme.getFont("extruder_icon") + font: UM.Theme.getFont("very_small") + width: contentWidth + height: contentHeight visible: extruderEnabled renderType: Text.NativeRendering horizontalAlignment: Text.AlignHCenter @@ -62,7 +62,6 @@ Item id: disabledIcon anchors.fill: parent anchors.margins: UM.Theme.getSize("thick_lining").width - sourceSize.width: width sourceSize.height: width source: UM.Theme.getIcon("cross1") visible: !extruderEnabled diff --git a/resources/qml/IconLabel.qml b/resources/qml/IconLabel.qml index 0941254e7b..f925b6eab5 100644 --- a/resources/qml/IconLabel.qml +++ b/resources/qml/IconLabel.qml @@ -31,9 +31,6 @@ Item width: UM.Theme.getSize("section_icon").width height: width - sourceSize.width: width - sourceSize.height: height - color: label.color visible: source != "" } @@ -48,7 +45,7 @@ Item text: "Empty label" elide: Text.ElideRight color: UM.Theme.getColor("text") - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") renderType: Text.NativeRendering } } \ No newline at end of file diff --git a/resources/qml/IconWithText.qml b/resources/qml/IconWithText.qml index dcb3ef7851..22599b3aed 100644 --- a/resources/qml/IconWithText.qml +++ b/resources/qml/IconWithText.qml @@ -37,8 +37,6 @@ Item width: UM.Theme.getSize("section_icon").width height: UM.Theme.getSize("section_icon").height - sourceSize.width: width - sourceSize.height: height color: "black" anchors diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index 45111992c1..935cb723de 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -60,7 +60,6 @@ Item { { width: UM.Theme.getSize("save_button_specs_icons").width; height: UM.Theme.getSize("save_button_specs_icons").height; - sourceSize.width: width; sourceSize.height: width; color: control.hovered ? UM.Theme.getColor("text_scene_hover") : UM.Theme.getColor("text_scene"); source: UM.Theme.getIcon("pencil"); @@ -124,7 +123,7 @@ Item { } height: UM.Theme.getSize("jobspecs_line").height verticalAlignment: Text.AlignVCenter - font: UM.Theme.getFont("small") + font: UM.Theme.getFont("default_bold") color: UM.Theme.getColor("text_scene") text: CuraApplication.getSceneBoundingBoxString } diff --git a/resources/qml/MainWindow/MainWindowHeader.qml b/resources/qml/MainWindow/MainWindowHeader.qml index 34936e9b5a..ae1c13d9c3 100644 --- a/resources/qml/MainWindow/MainWindowHeader.qml +++ b/resources/qml/MainWindow/MainWindowHeader.qml @@ -12,38 +12,13 @@ import QtGraphicalEffects 1.0 import "../Account" -Rectangle +Item { id: base implicitHeight: UM.Theme.getSize("main_window_header").height implicitWidth: UM.Theme.getSize("main_window_header").width - LinearGradient - { - anchors.fill: parent - start: Qt.point(0, 0) - end: Qt.point(parent.width, 0) - gradient: Gradient - { - GradientStop - { - position: 0.0 - color: UM.Theme.getColor("main_window_header_background") - } - GradientStop - { - position: 0.5 - color: UM.Theme.getColor("main_window_header_background_gradient") - } - GradientStop - { - position: 1.0 - color: UM.Theme.getColor("main_window_header_background") - } - } - } - Image { id: logo @@ -54,9 +29,6 @@ Rectangle source: UM.Theme.getImage("logo") width: UM.Theme.getSize("logo").width height: UM.Theme.getSize("logo").height - - sourceSize.width: width - sourceSize.height: height } Row diff --git a/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml new file mode 100644 index 0000000000..68c56c7c4b --- /dev/null +++ b/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml @@ -0,0 +1,39 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 2.0 + +import UM 1.3 as UM +import Cura 1.0 as Cura + +Item +{ + width: parent.width + height: childrenRect.height + + Label + { + id: header + text: catalog.i18nc("@header", "Configurations") + font: UM.Theme.getFont("large") + color: UM.Theme.getColor("text") + height: contentHeight + renderType: Text.NativeRendering + + anchors + { + left: parent.left + right: parent.right + } + } + + ConfigurationListView + { + anchors.top: header.bottom + anchors.topMargin: UM.Theme.getSize("default_margin").width + width: parent.width + + outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null + } +} \ No newline at end of file diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 7427b5ddff..6ac1e6a2ad 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -7,143 +7,129 @@ import QtQuick.Controls 2.0 import UM 1.2 as UM import Cura 1.0 as Cura -Rectangle +Button { id: configurationItem property var configuration: null - property var selected: false - signal activateConfiguration() + hoverEnabled: true - height: childrenRect.height - border.width: UM.Theme.getSize("default_lining").width - border.color: updateBorderColor() - color: selected ? UM.Theme.getColor("configuration_item_active") : UM.Theme.getColor("configuration_item") - property var textColor: selected ? UM.Theme.getColor("configuration_item_text_active") : UM.Theme.getColor("configuration_item_text") + height: background.height - function updateBorderColor() + background: Rectangle { - border.color = selected ? UM.Theme.getColor("configuration_item_border_active") : UM.Theme.getColor("configuration_item_border") - } + height: childrenRect.height + color: parent.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button") + border.color: (parent.checked || parent.hovered) ? UM.Theme.getColor("primary") : UM.Theme.getColor("lining") + border.width: parent.checked ? UM.Theme.getSize("thick_lining").width : UM.Theme.getSize("default_lining").width + radius: UM.Theme.getSize("default_radius").width - Column - { - id: contentColumn - width: parent.width - padding: UM.Theme.getSize("default_margin").width - spacing: Math.round(UM.Theme.getSize("default_margin").height / 2) - - Row + Column { - id: extruderRow + id: contentColumn + width: parent.width + padding: UM.Theme.getSize("wide_margin").width + spacing: Math.round(UM.Theme.getSize("default_margin").height / 2) - width: parent.width - 2 * parent.padding - height: childrenRect.height - - spacing: UM.Theme.getSize("default_margin").width - - Repeater + Row { - id: repeater - height: childrenRect.height - model: configuration.extruderConfigurations - delegate: PrintCoreConfiguration + id: extruderRow + + anchors { - width: Math.round(parent.width / 2) - printCoreConfiguration: modelData - mainColor: textColor + left: parent.left + leftMargin: parent.padding + right: parent.right + rightMargin: parent.padding + } + height: childrenRect.height + + spacing: UM.Theme.getSize("default_margin").width + + Repeater + { + id: repeater + height: childrenRect.height + model: configuration.extruderConfigurations + delegate: PrintCoreConfiguration + { + width: Math.round(parent.width / 2) + printCoreConfiguration: modelData + } + } + } + + //Buildplate row separator + Rectangle + { + id: separator + + visible: buildplateInformation.visible + anchors + { + left: parent.left + leftMargin: parent.padding + right: parent.right + rightMargin: parent.padding + } + height: visible ? Math.round(UM.Theme.getSize("thick_lining").height / 2) : 0 + color: UM.Theme.getColor("text") + } + + Item + { + id: buildplateInformation + + anchors + { + left: parent.left + leftMargin: parent.padding + right: parent.right + rightMargin: parent.padding + } + height: childrenRect.height + visible: configuration.buildplateConfiguration != "" + + UM.RecolorImage + { + id: buildplateIcon + anchors.left: parent.left + width: UM.Theme.getSize("main_window_header_button_icon").width + height: UM.Theme.getSize("main_window_header_button_icon").height + source: UM.Theme.getIcon("buildplate") + color: UM.Theme.getColor("text") + } + + Label + { + id: buildplateLabel + anchors.left: buildplateIcon.right + anchors.verticalCenter: buildplateIcon.verticalCenter + anchors.leftMargin: Math.round(UM.Theme.getSize("default_margin").height / 2) + text: configuration.buildplateConfiguration + renderType: Text.NativeRendering + color: UM.Theme.getColor("text") } } } - //Buildplate row separator - Rectangle + Connections { - id: separator - - visible: buildplateInformation.visible - width: parent.width - 2 * parent.padding - height: visible ? Math.round(UM.Theme.getSize("thick_lining").height / 2) : 0 - color: textColor - } - - Item - { - id: buildplateInformation - width: parent.width - 2 * parent.padding - height: childrenRect.height - visible: configuration.buildplateConfiguration != "" - - UM.RecolorImage { - id: buildplateIcon - anchors.left: parent.left - width: UM.Theme.getSize("main_window_header_button_icon").width - height: UM.Theme.getSize("main_window_header_button_icon").height - sourceSize.width: width - sourceSize.height: height - source: UM.Theme.getIcon("buildplate") - color: textColor - } - - Label + target: Cura.MachineManager + onCurrentConfigurationChanged: { - id: buildplateLabel - anchors.left: buildplateIcon.right - anchors.verticalCenter: buildplateIcon.verticalCenter - anchors.leftMargin: Math.round(UM.Theme.getSize("default_margin").height / 2) - text: configuration.buildplateConfiguration - renderType: Text.NativeRendering - color: textColor + configurationItem.checked = Cura.MachineManager.matchesConfiguration(configuration) } } - } - MouseArea - { - id: mouse - anchors.fill: parent - onClicked: activateConfiguration() - cursorShape: Qt.PointingHandCursor - hoverEnabled: true - onEntered: + Component.onCompleted: { - parent.border.color = UM.Theme.getColor("configuration_item_border_hover") - if (configurationItem.selected == false) - { - configurationItem.color = UM.Theme.getColor("wide_lining") - } - } - onExited: - { - updateBorderColor() - if (configurationItem.selected == false) - { - configurationItem.color = UM.Theme.getColor("configuration_item") - } + configurationItem.checked = Cura.MachineManager.matchesConfiguration(configuration) } } - Connections + onClicked: { - target: Cura.MachineManager - onCurrentConfigurationChanged: { - configurationItem.selected = Cura.MachineManager.matchesConfiguration(configuration) - updateBorderColor() - } - } - - Component.onCompleted: - { - configurationItem.selected = Cura.MachineManager.matchesConfiguration(configuration) - updateBorderColor() - } - - onVisibleChanged: - { - if(visible) - { - // I cannot trigger function updateBorderColor() after visibility change - color = selected ? UM.Theme.getColor("configuration_item_active") : UM.Theme.getColor("configuration_item") - } + Cura.MachineManager.applyRemoteConfiguration(configuration) } } \ No newline at end of file diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml index 210ff6057f..e7936b69d2 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml @@ -2,8 +2,7 @@ // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.7 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.3 import UM 1.2 as UM import Cura 1.0 as Cura @@ -12,9 +11,7 @@ Column { id: base property var outputDevice: null - property var computedHeight: container.height + configurationListHeading.height + 3 * padding height: childrenRect.height + 2 * padding - padding: UM.Theme.getSize("default_margin").width spacing: Math.round(UM.Theme.getSize("default_margin").height / 2) function forceModelUpdate() @@ -27,60 +24,60 @@ Column } } - Label - { - id: configurationListHeading - text: catalog.i18nc("@label:header configurations", "Available configurations") - font: UM.Theme.getFont("large") - width: parent.width - 2 * parent.padding - color: UM.Theme.getColor("configuration_item_text") - } - - Component - { - id: sectionHeading - Rectangle - { - height: childrenRect.height + UM.Theme.getSize("default_margin").height - Label - { - text: section - font: UM.Theme.getFont("default_bold") - color: UM.Theme.getColor("configuration_item_text") - } - } - } - ScrollView { id: container - width: parent.width - parent.padding - height: Math.min(configurationList.contentHeight, 350 * screenScaleFactor) + width: parent.width + readonly property int maximumHeight: 350 * screenScaleFactor + height: Math.round(Math.min(configurationList.height, maximumHeight)) + contentHeight: configurationList.height + clip: true - style: UM.Theme.styles.scrollview - __wheelAreaScrollSpeed: 75 // Scroll three lines in one scroll event + ScrollBar.vertical.policy: (configurationList.height > maximumHeight) ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff //The AsNeeded policy also hides it when the cursor is away, and we don't want that. + ScrollBar.vertical.background: Rectangle + { + implicitWidth: UM.Theme.getSize("scrollbar").width + radius: width / 2 + color: UM.Theme.getColor("scrollbar_background") + } + ScrollBar.vertical.contentItem: Rectangle + { + implicitWidth: UM.Theme.getSize("scrollbar").width + radius: width / 2 + color: UM.Theme.getColor(parent.pressed ? "scrollbar_handle_down" : parent.hovered ? "scrollbar_handle_hover" : "scrollbar_handle") + } + + ButtonGroup + { + buttons: configurationList.children + } ListView { id: configurationList spacing: Math.round(UM.Theme.getSize("default_margin").height / 2) - width: container.width + width: container.width - ((height > container.maximumHeight) ? container.ScrollBar.vertical.background.width : 0) //Make room for scroll bar if there is any. contentHeight: childrenRect.height + height: childrenRect.height section.property: "modelData.printerType" section.criteria: ViewSection.FullString - section.delegate: sectionHeading + section.delegate: Item + { + height: printerTypeLabel.height + UM.Theme.getSize("default_margin").height + Cura.PrinterTypeLabel + { + id: printerTypeLabel + text: Cura.MachineManager.getAbbreviatedMachineName(section) + } + } model: (outputDevice != null) ? outputDevice.uniqueConfigurations : [] + delegate: ConfigurationItem { - width: parent.width - UM.Theme.getSize("default_margin").width + width: parent.width configuration: modelData - onActivateConfiguration: - { - switchPopupState() - Cura.MachineManager.applyRemoteConfiguration(configuration) - } } } } diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml new file mode 100644 index 0000000000..1d086acc67 --- /dev/null +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -0,0 +1,203 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 2.0 +import QtQuick.Controls.Styles 1.4 + +import UM 1.2 as UM +import Cura 1.0 as Cura + + +/** + * Menu that allows you to select the configuration of the current printer, such + * as the nozzle sizes and materials in each extruder. + */ +Cura.ExpandableComponent +{ + id: base + + Cura.ExtrudersModel + { + id: extrudersModel + } + + UM.I18nCatalog + { + id: catalog + name: "cura" + } + + enum ConfigurationMethod + { + AUTO, + CUSTOM + } + + iconSource: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") + headerItem: Item + { + // Horizontal list that shows the extruders + ListView + { + id: extrudersList + + orientation: ListView.Horizontal + anchors.fill: parent + model: extrudersModel + visible: base.enabled + + delegate: Item + { + height: parent.height + width: Math.round(ListView.view.width / extrudersModel.count) + + // Extruder icon. Shows extruder index and has the same color as the active material. + Cura.ExtruderIcon + { + id: extruderIcon + materialColor: model.color + extruderEnabled: model.enabled + height: parent.height + width: height + } + + // Label for the brand of the material + Label + { + id: brandNameLabel + + text: model.material_brand + elide: Text.ElideRight + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text_inactive") + renderType: Text.NativeRendering + + anchors + { + left: extruderIcon.right + leftMargin: UM.Theme.getSize("default_margin").width + right: parent.right + rightMargin: UM.Theme.getSize("default_margin").width + } + } + + // Label that shows the name of the material + Label + { + text: model.material + elide: Text.ElideRight + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + renderType: Text.NativeRendering + + anchors + { + left: extruderIcon.right + leftMargin: UM.Theme.getSize("default_margin").width + right: parent.right + rightMargin: UM.Theme.getSize("default_margin").width + top: brandNameLabel.bottom + } + } + } + } + } + + //Disable the menu if there are no materials, variants or build plates to change. + function updateEnabled() + { + var active_definition_id = Cura.MachineManager.activeMachine.definition.id; + var has_materials = Cura.ContainerManager.getContainerMetaDataEntry(active_definition_id, "has_materials"); + var has_variants = Cura.ContainerManager.getContainerMetaDataEntry(active_definition_id, "has_variants"); + var has_buildplates = Cura.ContainerManager.getContainerMetaDataEntry(active_definition_id, "has_variant_buildplates"); + base.enabled = has_materials || has_variants || has_buildplates; //Only let it drop down if there is any configuration that you could change. + } + + Connections + { + target: Cura.MachineManager + onGlobalContainerChanged: base.updateEnabled(); + } + Component.onCompleted: updateEnabled(); + + popupItem: Column + { + id: popupItem + width: base.width - 2 * UM.Theme.getSize("default_margin").width + height: implicitHeight //Required because ExpandableComponent will try to use this to determine the size of the background of the pop-up. + spacing: UM.Theme.getSize("default_margin").height + + property bool is_connected: false //If current machine is connected to a printer. Only evaluated upon making popup visible. + onVisibleChanged: + { + is_connected = Cura.MachineManager.activeMachineNetworkKey !== "" && Cura.MachineManager.printerConnected //Re-evaluate. + } + + property int configuration_method: is_connected ? ConfigurationMenu.ConfigurationMethod.AUTO : ConfigurationMenu.ConfigurationMethod.CUSTOM //Auto if connected to a printer at start-up, or Custom if not. + + Item + { + width: parent.width + height: childrenRect.height + AutoConfiguration + { + id: autoConfiguration + visible: popupItem.configuration_method == ConfigurationMenu.ConfigurationMethod.AUTO + } + + CustomConfiguration + { + id: customConfiguration + visible: popupItem.configuration_method == ConfigurationMenu.ConfigurationMethod.CUSTOM + } + } + + Rectangle + { + id: separator + visible: buttonBar.visible + x: -popupPadding + + width: base.width + height: UM.Theme.getSize("default_lining").height + + color: UM.Theme.getColor("lining") + } + + //Allow switching between custom and auto. + Item + { + id: buttonBar + visible: popupItem.is_connected //Switching only makes sense if the "auto" part is possible. + + width: parent.width + height: childrenRect.height + + Cura.SecondaryButton + { + id: goToCustom + visible: popupItem.configuration_method == ConfigurationMenu.ConfigurationMethod.AUTO + text: catalog.i18nc("@label", "Custom") + + anchors.right: parent.right + + iconSource: UM.Theme.getIcon("arrow_right") + isIconOnRightSide: true + + onClicked: popupItem.configuration_method = ConfigurationMenu.ConfigurationMethod.CUSTOM + } + + Cura.SecondaryButton + { + id: goToAuto + visible: popupItem.configuration_method == ConfigurationMenu.ConfigurationMethod.CUSTOM + text: catalog.i18nc("@label", "Configurations") + + iconSource: UM.Theme.getIcon("arrow_left") + + onClicked: popupItem.configuration_method = ConfigurationMenu.ConfigurationMethod.AUTO + } + } + } +} diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml new file mode 100644 index 0000000000..8d8f84155a --- /dev/null +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -0,0 +1,250 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.6 +import QtQuick.Controls 2.0 +import QtQuick.Controls 1.1 as OldControls + +import Cura 1.0 as Cura +import UM 1.3 as UM + +Item +{ + UM.I18nCatalog + { + id: catalog + name: "cura" + } + + width: parent.width + height: childrenRect.height + + Label + { + id: header + text: catalog.i18nc("@header", "Custom") + font: UM.Theme.getFont("large") + color: UM.Theme.getColor("text") + height: contentHeight + renderType: Text.NativeRendering + + anchors + { + top: parent.top + left: parent.left + right: parent.right + } + } + + UM.TabRow + { + id: tabBar + anchors.top: header.bottom + anchors.topMargin: UM.Theme.getSize("default_margin").height + visible: extrudersModel.count > 1 + + Repeater + { + id: repeater + model: extrudersModel + delegate: UM.TabRowButton + { + contentItem: Item + { + Cura.ExtruderIcon + { + anchors.horizontalCenter: parent.horizontalCenter + materialColor: model.color + extruderEnabled: model.enabled + width: parent.height + height: parent.height + } + } + onClicked: + { + Cura.ExtruderManager.setActiveExtruderIndex(tabBar.currentIndex) + } + } + } + + //When active extruder changes for some other reason, switch tabs. + //Don't directly link currentIndex to Cura.ExtruderManager.activeExtruderIndex! + //This causes a segfault in Qt 5.11. Something with VisualItemModel removing index -1. We have to use setCurrentIndex instead. + Connections + { + target: Cura.ExtruderManager + onActiveExtruderChanged: + { + tabBar.setCurrentIndex(Cura.ExtruderManager.activeExtruderIndex); + } + } + + //When the model of the extruders is rebuilt, the list of extruders is briefly emptied and rebuilt. + //This causes the currentIndex of the tab to be in an invalid position which resets it to 0. + //Therefore we need to change it back to what it was: The active extruder index. + Connections + { + target: repeater.model + onModelChanged: + { + tabBar.setCurrentIndex(Cura.ExtruderManager.activeExtruderIndex) + } + } + } + + Rectangle + { + width: parent.width + height: childrenRect.height + anchors.top: tabBar.bottom + + radius: tabBar.visible ? UM.Theme.getSize("default_radius").width : 0 + border.width: tabBar.visible ? UM.Theme.getSize("default_lining").width : 0 + border.color: UM.Theme.getColor("lining") + color: UM.Theme.getColor("main_background") + + //Remove rounding and lining at the top. + Rectangle + { + width: parent.width + height: parent.radius + anchors.top: parent.top + color: UM.Theme.getColor("lining") + visible: tabBar.visible + Rectangle + { + anchors + { + left: parent.left + leftMargin: parent.parent.border.width + right: parent.right + rightMargin: parent.parent.border.width + top: parent.top + } + height: parent.parent.radius + color: parent.parent.color + } + } + + Column + { + id: selectors + padding: UM.Theme.getSize("default_margin").width + spacing: UM.Theme.getSize("default_margin").height + + property var model: extrudersModel.items[tabBar.currentIndex] + + readonly property real paddedWidth: parent.width - padding * 2 + property real textWidth: Math.round(paddedWidth * 0.3) + property real controlWidth: paddedWidth - textWidth + + Row + { + height: UM.Theme.getSize("print_setup_item").height + + Label + { + text: catalog.i18nc("@label", "Enabled") + verticalAlignment: Text.AlignVCenter + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + height: parent.height + width: selectors.textWidth + visible: extrudersModel.count > 1 + renderType: Text.NativeRendering + } + + OldControls.CheckBox + { + checked: Cura.MachineManager.activeStack != null ? Cura.MachineManager.activeStack.isEnabled : false + enabled: !checked || Cura.MachineManager.numberExtrudersEnabled > 1 //Disable if it's the last enabled extruder. + height: UM.Theme.getSize("setting_control").height + style: UM.Theme.styles.checkbox + visible: extrudersModel.count > 1 + + /* Use a MouseArea to process the click on this checkbox. + This is necessary because actually clicking the checkbox + causes the "checked" property to be overwritten. After + it's been overwritten, the original link that made it + depend on the active extruder stack is broken. */ + MouseArea + { + anchors.fill: parent + onClicked: Cura.MachineManager.setExtruderEnabled(Cura.ExtruderManager.activeExtruderIndex, !parent.checked) + enabled: parent.enabled + } + } + } + + Row + { + height: UM.Theme.getSize("print_setup_item").height + Label + { + text: catalog.i18nc("@label", "Material") + verticalAlignment: Text.AlignVCenter + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + height: parent.height + width: selectors.textWidth + visible: materialSelection.visible + renderType: Text.NativeRendering + } + + OldControls.ToolButton + { + id: materialSelection + + property bool valueError: Cura.MachineManager.activeStack != null ? Cura.ContainerManager.getContainerMetaDataEntry(Cura.MachineManager.activeStack.material.id, "compatible", "") != "True" : true + property bool valueWarning: !Cura.MachineManager.isActiveQualitySupported + + text: Cura.MachineManager.activeStack != null ? Cura.MachineManager.activeStack.material.name : "" + tooltip: text + visible: Cura.MachineManager.hasMaterials + + height: UM.Theme.getSize("setting_control").height + width: selectors.controlWidth + + style: UM.Theme.styles.sidebar_header_button + activeFocusOnPress: true + menu: Cura.MaterialMenu + { + extruderIndex: Cura.ExtruderManager.activeExtruderIndex + } + } + } + + Row + { + height: UM.Theme.getSize("print_setup_item").height + + Label + { + text: Cura.MachineManager.activeDefinitionVariantsName + verticalAlignment: Text.AlignVCenter + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + height: parent.height + width: selectors.textWidth + visible: variantSelection.visible + renderType: Text.NativeRendering + } + + OldControls.ToolButton + { + id: variantSelection + text: Cura.MachineManager.activeVariantName + tooltip: Cura.MachineManager.activeVariantName; + visible: Cura.MachineManager.hasVariants + + height: UM.Theme.getSize("setting_control").height + width: selectors.controlWidth + style: UM.Theme.styles.sidebar_header_button + activeFocusOnPress: true; + + menu: Cura.NozzleMenu { extruderIndex: Cura.ExtruderManager.activeExtruderIndex } + } + } + } + } +} diff --git a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml index 73fc342d66..885f02d740 100644 --- a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml @@ -5,87 +5,50 @@ import QtQuick 2.7 import QtQuick.Controls 2.0 import UM 1.2 as UM +import Cura 1.0 as Cura - -Column +Row { id: extruderInfo property var printCoreConfiguration - property var mainColor: "black" - spacing: Math.round(UM.Theme.getSize("default_margin").height / 2) - height: childrenRect.height + height: information.height + spacing: UM.Theme.getSize("default_margin").width - Item + //Extruder icon. + Cura.ExtruderIcon { - id: extruder - width: parent.width - height: childrenRect.height + materialColor: printCoreConfiguration.material.color + anchors.verticalCenter: parent.verticalCenter + extruderEnabled: printCoreConfiguration.material.name !== "" && printCoreConfiguration.hotendID !== "" + } + Column + { + id: information Label { - id: extruderLabel - text: catalog.i18nc("@label:extruder label", "Extruder") + text: printCoreConfiguration.material.brand ? printCoreConfiguration.material.brand : " " //Use space so that the height is still correct. renderType: Text.NativeRendering elide: Text.ElideRight - anchors.left: parent.left font: UM.Theme.getFont("default") - color: mainColor + color: UM.Theme.getColor("text_inactive") } - - // Rounded item to show the extruder number - Item + Label { - id: extruderIconItem - anchors.verticalCenter: extruderLabel.verticalCenter - anchors.left: extruderLabel.right - anchors.leftMargin: Math.round(UM.Theme.getSize("default_margin").width / 2) - - width: UM.Theme.getSize("section_icon").width - height: UM.Theme.getSize("section_icon").height - - UM.RecolorImage { - id: mainCircle - anchors.fill: parent - - anchors.centerIn: parent - sourceSize.width: parent.width - sourceSize.height: parent.height - source: UM.Theme.getIcon("extruder_button") - color: mainColor - } - - Label - { - id: extruderNumberText - anchors.centerIn: parent - text: printCoreConfiguration.position + 1 - renderType: Text.NativeRendering - font: UM.Theme.getFont("default") - color: mainColor - } + text: printCoreConfiguration.material.name ? printCoreConfiguration.material.name : " " //Use space so that the height is still correct. + renderType: Text.NativeRendering + elide: Text.ElideRight + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + } + Label + { + text: printCoreConfiguration.hotendID ? printCoreConfiguration.hotendID : " " //Use space so that the height is still correct. + renderType: Text.NativeRendering + elide: Text.ElideRight + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text_inactive") } - } - - Label - { - id: materialLabel - text: printCoreConfiguration.material == null ? "" : printCoreConfiguration.material.name - renderType: Text.NativeRendering - elide: Text.ElideRight - width: parent.width - font: UM.Theme.getFont("default_bold") - color: mainColor - } - - Label - { - id: printCoreTypeLabel - text: printCoreConfiguration.hotendID - renderType: Text.NativeRendering - elide: Text.ElideRight - width: parent.width - font: UM.Theme.getFont("default") - color: mainColor } } diff --git a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml b/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml deleted file mode 100644 index eb6800cb36..0000000000 --- a/resources/qml/Menus/ConfigurationMenu/QuickConfigurationSelector.qml +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright (c) 2018 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.7 -import QtQuick.Controls 2.0 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Layouts 1.3 - -import QtQuick.Controls 1.1 as OldControls - -import UM 1.2 as UM -import Cura 1.0 as Cura - - -Cura.ExpandableComponent -{ - id: base - - Cura.ExtrudersModel - { - id: extrudersModel - } - - UM.I18nCatalog - { - id: catalog - name: "cura" - } - - iconSource: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") - headerItem: Item - { - // Horizontal list that shows the extruders - ListView - { - id: extrudersList - - orientation: ListView.Horizontal - anchors.fill: parent - model: extrudersModel - - delegate: Item - { - height: parent.height - width: Math.round(ListView.view.width / extrudersModel.rowCount()) - - // Extruder icon. Shows extruder index and has the same color as the active material. - Cura.ExtruderIcon - { - id: extruderIcon - materialColor: model.color - extruderEnabled: model.enabled - anchors.verticalCenter: parent.verticalCenter - } - - // Label for the brand of the material - Label - { - id: brandNameLabel - - text: model.material_brand - elide: Text.ElideRight - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - - anchors - { - left: extruderIcon.right - leftMargin: UM.Theme.getSize("default_margin").width - right: parent.right - rightMargin: UM.Theme.getSize("default_margin").width - } - } - - // Label that shows the name of the material - Label - { - text: model.material - elide: Text.ElideRight - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - - anchors - { - left: extruderIcon.right - leftMargin: UM.Theme.getSize("default_margin").width - right: parent.right - rightMargin: UM.Theme.getSize("default_margin").width - top: brandNameLabel.bottom - } - } - } - } - } - - popupItem: Item - { - width: base.width - 2 * UM.Theme.getSize("default_margin").width - height: 200 - - TabBar - { - id: tabBar - onCurrentIndexChanged: Cura.ExtruderManager.setActiveExtruderIndex(currentIndex) - width: parent.width - height: 50 - Repeater - { - model: extrudersModel - - delegate: TabButton - { - width: ListView.view != null ? Math.round(ListView.view.width / extrudersModel.rowCount()): 0 - height: parent.height - contentItem: Item - { - Cura.ExtruderIcon - { - anchors.horizontalCenter: parent.horizontalCenter - materialColor: model.color - extruderEnabled: model.enabled - width: parent.height - height: parent.height - } - } - } - } - } - - Item - { - id: tabControl - width: parent.width - anchors.top: tabBar.bottom - anchors.bottom: parent.bottom - property var model: extrudersModel.items[tabBar.currentIndex] - property real textWidth: Math.round(width * 0.3) - property real controlWidth: width - textWidth - Column - { - spacing: UM.Theme.getSize("default_margin").height - Row - { - height: UM.Theme.getSize("print_setup_item").height - - Label - { - text: catalog.i18nc("@label", "Enabled") - verticalAlignment: Text.AlignVCenter - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - height: parent.height - width: tabControl.textWidth - renderType: Text.NativeRendering - } - - OldControls.CheckBox - { - checked: tabControl.model != null ? Cura.MachineManager.getExtruder(tabControl.model.index).isEnabled: false - onClicked: Cura.MachineManager.setExtruderEnabled(tabControl.model.index, checked) - height: UM.Theme.getSize("setting_control").height - style: UM.Theme.styles.checkbox - } - } - - Row - { - height: UM.Theme.getSize("print_setup_item").height - Label - { - text: catalog.i18nc("@label", "Material") - verticalAlignment: Text.AlignVCenter - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - height: parent.height - width: tabControl.textWidth - renderType: Text.NativeRendering - } - - OldControls.ToolButton - { - id: materialSelection - - property var activeExtruder: Cura.MachineManager.activeStack - property var hasActiveExtruder: activeExtruder != null - property var currentRootMaterialName: hasActiveExtruder ? activeExtruder.material.name : "" - property var valueError: hasActiveExtruder ? Cura.ContainerManager.getContainerMetaDataEntry(activeExtruder.material.id, "compatible", "") != "True" : true - property var valueWarning: ! Cura.MachineManager.isActiveQualitySupported - - text: currentRootMaterialName - tooltip: currentRootMaterialName - visible: Cura.MachineManager.hasMaterials - - enabled: Cura.ExtruderManager.activeExtruderIndex > -1 - - height: UM.Theme.getSize("setting_control").height - width: tabControl.controlWidth - - style: UM.Theme.styles.sidebar_header_button - activeFocusOnPress: true - menu: Cura.MaterialMenu - { - extruderIndex: Cura.ExtruderManager.activeExtruderIndex - } - - } - } - - Row - { - height: UM.Theme.getSize("print_setup_item").height - - Label - { - text: Cura.MachineManager.activeDefinitionVariantsName - verticalAlignment: Text.AlignVCenter - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - height: parent.height - width: tabControl.textWidth - renderType: Text.NativeRendering - } - - OldControls.ToolButton - { - id: variantSelection - text: Cura.MachineManager.activeVariantName - tooltip: Cura.MachineManager.activeVariantName; - visible: Cura.MachineManager.hasVariants - - height: UM.Theme.getSize("setting_control").height - width: tabControl.controlWidth - style: UM.Theme.styles.sidebar_header_button - activeFocusOnPress: true; - - menu: Cura.NozzleMenu { extruderIndex: Cura.ExtruderManager.activeExtruderIndex } - } - } - } - - } - } -} diff --git a/resources/qml/Menus/ConfigurationMenu/SyncButton.qml b/resources/qml/Menus/ConfigurationMenu/SyncButton.qml deleted file mode 100644 index 558ae1e477..0000000000 --- a/resources/qml/Menus/ConfigurationMenu/SyncButton.qml +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) 2018 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.7 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 - -import UM 1.2 as UM -import Cura 1.0 as Cura - -Button -{ - id: base - property var outputDevice: null - property var matched: updateOnSync() - text: matched == true ? catalog.i18nc("@label:extruder label", "Yes") : catalog.i18nc("@label:extruder label", "No") - width: parent.width - height: parent.height - - function updateOnSync() - { - if (outputDevice != undefined) - { - for (var index in outputDevice.uniqueConfigurations) - { - var configuration = outputDevice.uniqueConfigurations[index] - if (Cura.MachineManager.matchesConfiguration(configuration)) - { - base.matched = true; - return; - } - } - } - base.matched = false; - } - - style: ButtonStyle - { - background: Rectangle - { - color: - { - if(control.pressed) - { - return UM.Theme.getColor("machine_selector_active"); - } - else if(control.hovered) - { - return UM.Theme.getColor("machine_selector_hover"); - } - else - { - return UM.Theme.getColor("machine_selector_bar"); - } - } - Behavior on color { ColorAnimation { duration: 50; } } - - UM.RecolorImage - { - id: downArrow - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: UM.Theme.getSize("default_margin").width - width: UM.Theme.getSize("standard_arrow").width - height: UM.Theme.getSize("standard_arrow").height - sourceSize.width: width - sourceSize.height: height - color: UM.Theme.getColor("text_emphasis") - source: UM.Theme.getIcon("arrow_bottom") - } - UM.RecolorImage - { - id: sidebarComboBoxLabel - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("default_margin").width - anchors.verticalCenter: parent.verticalCenter; - - width: UM.Theme.getSize("printer_sync_icon").width - height: UM.Theme.getSize("printer_sync_icon").height - - color: control.matched ? UM.Theme.getColor("printer_config_matched") : UM.Theme.getColor("printer_config_mismatch") - source: UM.Theme.getIcon("tab_status_connected") - sourceSize.width: width - sourceSize.height: height - } - } - label: Label {} - } - - Connections - { - target: outputDevice - onUniqueConfigurationsChanged: updateOnSync() - } - - Connections - { - target: Cura.MachineManager - onCurrentConfigurationChanged: updateOnSync() - onOutputDevicesChanged: updateOnSync() - } -} \ No newline at end of file diff --git a/resources/qml/Menus/ProfileMenu.qml b/resources/qml/Menus/ProfileMenu.qml index fd46d2ef72..bf950aa409 100644 --- a/resources/qml/Menus/ProfileMenu.qml +++ b/resources/qml/Menus/ProfileMenu.qml @@ -37,7 +37,7 @@ Menu MenuSeparator { id: customSeparator - visible: Cura.CustomQualityProfilesDropDownMenuModel.rowCount > 0 + visible: Cura.CustomQualityProfilesDropDownMenuModel.count > 0 } Instantiator @@ -48,7 +48,7 @@ Menu Connections { target: Cura.CustomQualityProfilesDropDownMenuModel - onModelReset: customSeparator.visible = Cura.CustomQualityProfilesDropDownMenuModel.rowCount() > 0 + onModelReset: customSeparator.visible = Cura.CustomQualityProfilesDropDownMenuModel.count > 0 } MenuItem @@ -62,12 +62,12 @@ Menu onObjectAdded: { - customSeparator.visible = model.rowCount() > 0; + customSeparator.visible = model.count > 0; menu.insertItem(index, object); } onObjectRemoved: { - customSeparator.visible = model.rowCount() > 0; + customSeparator.visible = model.count > 0; menu.removeItem(object); } } diff --git a/resources/qml/ObjectsList.qml b/resources/qml/ObjectsList.qml index 8c8eaa16ae..8f45b3744f 100644 --- a/resources/qml/ObjectsList.qml +++ b/resources/qml/ObjectsList.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 @@ -55,7 +55,6 @@ Rectangle { width: control.width height: control.height - sourceSize.width: width sourceSize.height: width color: UM.Theme.getColor("setting_control_text") source: collapsed ? UM.Theme.getIcon("arrow_left") : UM.Theme.getIcon("arrow_bottom") diff --git a/resources/qml/Preferences/MachinesPage.qml b/resources/qml/Preferences/MachinesPage.qml index 4dc5465dc6..bc75b9bc72 100644 --- a/resources/qml/Preferences/MachinesPage.qml +++ b/resources/qml/Preferences/MachinesPage.qml @@ -21,8 +21,10 @@ UM.ManagementPage function activeMachineIndex() { - for(var i = 0; i < model.rowCount(); i++) { - if (model.getItem(i).id == Cura.MachineManager.activeMachineId) { + for(var i = 0; i < model.count; i++) + { + if (model.getItem(i).id == Cura.MachineManager.activeMachineId) + { return i; } } @@ -47,7 +49,7 @@ UM.ManagementPage { text: catalog.i18nc("@action:button", "Remove"); iconName: "list-remove"; - enabled: base.currentItem != null && model.rowCount() > 1 + enabled: base.currentItem != null && model.count > 1 onClicked: confirmDialog.open(); }, Button diff --git a/resources/qml/Preferences/Materials/MaterialsBrandSection.qml b/resources/qml/Preferences/Materials/MaterialsBrandSection.qml index c8f391dfb0..a3a0e4708f 100644 --- a/resources/qml/Preferences/Materials/MaterialsBrandSection.qml +++ b/resources/qml/Preferences/Materials/MaterialsBrandSection.qml @@ -55,7 +55,8 @@ Rectangle text: "" implicitWidth: UM.Theme.getSize("favorites_button").width implicitHeight: UM.Theme.getSize("favorites_button").height - UM.RecolorImage { + UM.RecolorImage + { anchors { verticalCenter: parent.verticalCenter @@ -63,8 +64,6 @@ Rectangle } width: UM.Theme.getSize("standard_arrow").width height: UM.Theme.getSize("standard_arrow").height - sourceSize.width: width - sourceSize.height: height color: "black" source: brand_section.expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") } diff --git a/resources/qml/Preferences/Materials/MaterialsList.qml b/resources/qml/Preferences/Materials/MaterialsList.qml index 00bead9650..61f92db84c 100644 --- a/resources/qml/Preferences/Materials/MaterialsList.qml +++ b/resources/qml/Preferences/Materials/MaterialsList.qml @@ -57,7 +57,7 @@ Item var currentItemId = base.currentItem == null ? "" : base.currentItem.root_material_id search_root_id = currentItemId } - for (var material_idx = 0; material_idx < genericMaterialsModel.rowCount(); material_idx++) + for (var material_idx = 0; material_idx < genericMaterialsModel.count; material_idx++) { var material = genericMaterialsModel.getItem(material_idx) if (material.root_material_id == search_root_id) @@ -72,15 +72,15 @@ Item return true } } - for (var brand_idx = 0; brand_idx < materialsModel.rowCount(); brand_idx++) + for (var brand_idx = 0; brand_idx < materialsModel.count; brand_idx++) { var brand = materialsModel.getItem(brand_idx) var types_model = brand.material_types - for (var type_idx = 0; type_idx < types_model.rowCount(); type_idx++) + for (var type_idx = 0; type_idx < types_model.count; type_idx++) { var type = types_model.getItem(type_idx) var colors_model = type.colors - for (var material_idx = 0; material_idx < colors_model.rowCount(); material_idx++) + for (var material_idx = 0; material_idx < colors_model.count; material_idx++) { var material = colors_model.getItem(material_idx) if (material.root_material_id == search_root_id) diff --git a/resources/qml/Preferences/Materials/MaterialsSlot.qml b/resources/qml/Preferences/Materials/MaterialsSlot.qml index a5af17f47a..a706aaf2b9 100644 --- a/resources/qml/Preferences/Materials/MaterialsSlot.qml +++ b/resources/qml/Preferences/Materials/MaterialsSlot.qml @@ -95,8 +95,6 @@ Rectangle } width: UM.Theme.getSize("favorites_button_icon").width height: UM.Theme.getSize("favorites_button_icon").height - sourceSize.width: width - sourceSize.height: height color: { if (favorite_button.hovered) diff --git a/resources/qml/Preferences/Materials/MaterialsTypeSection.qml b/resources/qml/Preferences/Materials/MaterialsTypeSection.qml index f62fc4ee16..f98c19e0b3 100644 --- a/resources/qml/Preferences/Materials/MaterialsTypeSection.qml +++ b/resources/qml/Preferences/Materials/MaterialsTypeSection.qml @@ -74,8 +74,6 @@ Rectangle } width: UM.Theme.getSize("standard_arrow").width height: UM.Theme.getSize("standard_arrow").height - sourceSize.width: width - sourceSize.height: height color: "black" source: material_type_section.expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") } diff --git a/resources/qml/Preferences/ProfilesPage.qml b/resources/qml/Preferences/ProfilesPage.qml index ba0c2848a5..d7ffbb3152 100644 --- a/resources/qml/Preferences/ProfilesPage.qml +++ b/resources/qml/Preferences/ProfilesPage.qml @@ -188,21 +188,27 @@ Item Connections { target: qualitiesModel - onItemsChanged: { + onItemsChanged: + { var toSelectItemName = base.currentItem == null ? "" : base.currentItem.name; - if (newQualityNameToSelect != "") { + if (newQualityNameToSelect != "") + { toSelectItemName = newQualityNameToSelect; } var newIdx = -1; // Default to nothing if nothing can be found - if (toSelectItemName != "") { + if (toSelectItemName != "") + { // Select the required quality name if given - for (var idx = 0; idx < qualitiesModel.rowCount(); ++idx) { + for (var idx = 0; idx < qualitiesModel.count; ++idx) + { var item = qualitiesModel.getItem(idx); - if (item.name == toSelectItemName) { + if (item.name == toSelectItemName) + { // Switch to the newly created profile if needed newIdx = idx; - if (base.toActivateNewQuality) { + if (base.toActivateNewQuality) + { // Activate this custom quality if required Cura.MachineManager.setQualityChangesGroup(item.quality_changes_group); } @@ -382,9 +388,11 @@ Item var selectedItemName = Cura.MachineManager.activeQualityOrQualityChangesName; // Select the required quality name if given - for (var idx = 0; idx < qualitiesModel.rowCount(); idx++) { + for (var idx = 0; idx < qualitiesModel.count; idx++) + { var item = qualitiesModel.getItem(idx); - if (item.name == selectedItemName) { + if (item.name == selectedItemName) + { currentIndex = idx; break; } diff --git a/resources/qml/Preferences/SettingVisibilityPage.qml b/resources/qml/Preferences/SettingVisibilityPage.qml index 2edbeee960..3f7571a170 100644 --- a/resources/qml/Preferences/SettingVisibilityPage.qml +++ b/resources/qml/Preferences/SettingVisibilityPage.qml @@ -50,7 +50,7 @@ UM.PreferencesPage { return Qt.Unchecked } - else if(definitionsModel.visibleCount == definitionsModel.rowCount(null)) + else if(definitionsModel.visibleCount == definitionsModel.count) { return Qt.Checked } diff --git a/resources/qml/PrinterOutput/ExtruderBox.qml b/resources/qml/PrinterOutput/ExtruderBox.qml index f5a1bd75c4..247bb3a27d 100644 --- a/resources/qml/PrinterOutput/ExtruderBox.qml +++ b/resources/qml/PrinterOutput/ExtruderBox.qml @@ -47,7 +47,7 @@ Item { id: extruderTargetTemperature text: Math.round(extruderModel.targetHotendTemperature) + "°C" - font: UM.Theme.getFont("small") + font: UM.Theme.getFont("default_bold") color: UM.Theme.getColor("text_inactive") anchors.right: parent.right anchors.rightMargin: UM.Theme.getSize("default_margin").width diff --git a/resources/qml/PrinterOutput/HeatedBedBox.qml b/resources/qml/PrinterOutput/HeatedBedBox.qml index 8c99814e02..33cf5cd1e2 100644 --- a/resources/qml/PrinterOutput/HeatedBedBox.qml +++ b/resources/qml/PrinterOutput/HeatedBedBox.qml @@ -35,7 +35,7 @@ Item { id: bedTargetTemperature text: printerModel != null ? printerModel.targetBedTemperature + "°C" : "" - font: UM.Theme.getFont("small") + font: UM.Theme.getFont("default_bold") color: UM.Theme.getColor("text_inactive") anchors.right: parent.right anchors.rightMargin: UM.Theme.getSize("default_margin").width diff --git a/resources/qml/PrinterOutput/OutputDeviceHeader.qml b/resources/qml/PrinterOutput/OutputDeviceHeader.qml index e6328546ef..16280eab5f 100644 --- a/resources/qml/PrinterOutput/OutputDeviceHeader.qml +++ b/resources/qml/PrinterOutput/OutputDeviceHeader.qml @@ -43,7 +43,7 @@ Item { id: outputDeviceAddressLabel text: (outputDevice != null && outputDevice.address != null) ? outputDevice.address : "" - font: UM.Theme.getFont("small") + font: UM.Theme.getFont("default_bold") color: UM.Theme.getColor("text_inactive") anchors.top: outputDeviceNameLabel.bottom anchors.left: parent.left @@ -54,7 +54,7 @@ Item { text: outputDevice != null ? "" : catalog.i18nc("@info:status", "The printer is not connected.") color: outputDevice != null && outputDevice.acceptsCommands ? UM.Theme.getColor("setting_control_text") : UM.Theme.getColor("setting_control_disabled_text") - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") wrapMode: Text.WordWrap anchors.left: parent.left anchors.leftMargin: UM.Theme.getSize("default_margin").width diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index 15cd773c90..95abfd6644 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -25,29 +25,52 @@ Cura.ExpandableComponent name: "cura" } - headerItem: Cura.IconLabel + headerItem: Item { - text: isNetworkPrinter ? Cura.MachineManager.activeMachineNetworkGroupName : Cura.MachineManager.activeMachineName - source: - { - if (isNetworkPrinter) - { - if (machineSelector.outputDevice != null && machineSelector.outputDevice.clusterSize > 1) - { - return UM.Theme.getIcon("printer_group") - } - return UM.Theme.getIcon("printer_single") - } - return "" - } - font: UM.Theme.getFont("medium") - color: UM.Theme.getColor("text") - iconSize: UM.Theme.getSize("machine_selector_icon").width + implicitHeight: icon.height UM.RecolorImage { id: icon + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + + source: + { + if (isNetworkPrinter) + { + if (machineSelector.outputDevice != null && machineSelector.outputDevice.clusterSize > 1) + { + return UM.Theme.getIcon("printer_group") + } + return UM.Theme.getIcon("printer_single") + } + return "" + } + width: UM.Theme.getSize("machine_selector_icon").width + height: width + + color: UM.Theme.getColor("machine_selector_printer_icon") + visible: source != "" + } + + Label + { + id: label + anchors.left: icon.visible ? icon.right : parent.left + anchors.right: parent.right + anchors.leftMargin: UM.Theme.getSize("thin_margin").width + anchors.verticalCenter: icon.verticalCenter + text: isNetworkPrinter ? Cura.MachineManager.activeMachineNetworkGroupName : Cura.MachineManager.activeMachineName + elide: Text.ElideRight + color: UM.Theme.getColor("text") + font: UM.Theme.getFont("medium") + renderType: Text.NativeRendering + } + + UM.RecolorImage + { anchors { bottom: parent.bottom @@ -59,9 +82,6 @@ Cura.ExpandableComponent width: UM.Theme.getSize("printer_status_icon").width height: UM.Theme.getSize("printer_status_icon").height - sourceSize.width: width - sourceSize.height: height - color: UM.Theme.getColor("primary") visible: isNetworkPrinter && isPrinterConnected diff --git a/resources/qml/PrinterSelector/MachineSelectorList.qml b/resources/qml/PrinterSelector/MachineSelectorList.qml index 445940ab50..d831f4eb5c 100644 --- a/resources/qml/PrinterSelector/MachineSelectorList.qml +++ b/resources/qml/PrinterSelector/MachineSelectorList.qml @@ -13,7 +13,7 @@ Column Label { - text: catalog.i18nc("@label", "Network connected printers") + text: catalog.i18nc("@label", "Connected printers") visible: networkedPrintersModel.items.length > 0 leftPadding: UM.Theme.getSize("default_margin").width height: visible ? contentHeight + 2 * UM.Theme.getSize("default_margin").height : 0 diff --git a/resources/qml/PrinterSelector/PrinterTypeLabel.qml b/resources/qml/PrinterTypeLabel.qml similarity index 95% rename from resources/qml/PrinterSelector/PrinterTypeLabel.qml rename to resources/qml/PrinterTypeLabel.qml index cd9f3b9743..7feae32e16 100644 --- a/resources/qml/PrinterSelector/PrinterTypeLabel.qml +++ b/resources/qml/PrinterTypeLabel.qml @@ -28,7 +28,7 @@ Item anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter renderType: Text.NativeRendering - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") } } \ No newline at end of file diff --git a/resources/qml/Settings/SettingCategory.qml b/resources/qml/Settings/SettingCategory.qml index aafe36c546..196b2d6b97 100644 --- a/resources/qml/Settings/SettingCategory.qml +++ b/resources/qml/Settings/SettingCategory.qml @@ -129,23 +129,26 @@ Button anchors.rightMargin: UM.Theme.getSize("default_margin").width width: UM.Theme.getSize("standard_arrow").width height: UM.Theme.getSize("standard_arrow").height - sourceSize.width: width sourceSize.height: width color: { if (!base.enabled) { return UM.Theme.getColor("setting_category_disabled_text") - } else if ((base.hovered || base.activeFocus) && base.checkable && base.checked) + } + else if ((base.hovered || base.activeFocus) && base.checkable && base.checked) { return UM.Theme.getColor("setting_category_active_hover_text") - } else if (base.pressed || (base.checkable && base.checked)) + } + else if (base.pressed || (base.checkable && base.checked)) { return UM.Theme.getColor("setting_category_active_text") - } else if (base.hovered || base.activeFocus) + } + else if (base.hovered || base.activeFocus) { return UM.Theme.getColor("setting_category_hover_text") - } else + } + else { return UM.Theme.getColor("setting_category_text") } diff --git a/resources/qml/Settings/SettingCheckBox.qml b/resources/qml/Settings/SettingCheckBox.qml index d37754d27c..fb2d5a2f4d 100644 --- a/resources/qml/Settings/SettingCheckBox.qml +++ b/resources/qml/Settings/SettingCheckBox.qml @@ -115,12 +115,12 @@ SettingItem return UM.Theme.getColor("setting_control_border") } - UM.RecolorImage { + UM.RecolorImage + { anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter width: Math.round(parent.width / 2.5) height: Math.round(parent.height / 2.5) - sourceSize.width: width sourceSize.height: width color: !enabled ? UM.Theme.getColor("setting_control_disabled_text") : UM.Theme.getColor("setting_control_text"); source: UM.Theme.getIcon("check") diff --git a/resources/qml/Settings/SettingOptionalExtruder.qml b/resources/qml/Settings/SettingOptionalExtruder.qml index a3c1422b30..5f0d8327f8 100644 --- a/resources/qml/Settings/SettingOptionalExtruder.qml +++ b/resources/qml/Settings/SettingOptionalExtruder.qml @@ -1,5 +1,5 @@ -// Copyright (c) 2016 Ultimaker B.V. -// Uranium is released under the terms of the LGPLv3 or higher. +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.7 import QtQuick.Controls 2.0 @@ -31,12 +31,15 @@ SettingItem { forceActiveFocus(); propertyProvider.setPropertyValue("value", model.getItem(index).index); - } else + } + else { if (propertyProvider.properties.value == -1) { - control.currentIndex = model.rowCount() - 1; // we know the last item is "Not overriden" - } else { + control.currentIndex = model.count - 1; // we know the last item is "Not overriden" + } + else + { control.currentIndex = propertyProvider.properties.value; // revert to the old value } } diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index ef1f123953..bb624bcbde 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -1,5 +1,5 @@ -// Copyright (c) 2017 Ultimaker B.V. -// Uranium is released under the terms of the LGPLv3 or higher. +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.7 import QtQuick.Controls 1.1 @@ -129,13 +129,14 @@ Item } style: ButtonStyle { - background: Item { - UM.RecolorImage { + background: Item + { + 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") diff --git a/resources/qml/SidebarSimple.qml b/resources/qml/SidebarSimple.qml index 5e723a3d70..fb4d52979d 100644 --- a/resources/qml/SidebarSimple.qml +++ b/resources/qml/SidebarSimple.qml @@ -106,7 +106,7 @@ Item var availableMin = -1 var availableMax = -1 - for (var i = 0; i < Cura.QualityProfilesDropDownMenuModel.rowCount(); i++) + for (var i = 0; i < Cura.QualityProfilesDropDownMenuModel.count; i++) { var qualityItem = Cura.QualityProfilesDropDownMenuModel.getItem(i) @@ -183,7 +183,7 @@ Item qualityModel.existingQualityProfile = 0 // check, the ticks count cannot be less than zero - qualityModel.totalTicks = Math.max(0, Cura.QualityProfilesDropDownMenuModel.rowCount() - 1) + qualityModel.totalTicks = Math.max(0, Cura.QualityProfilesDropDownMenuModel.count - 1) } } @@ -731,7 +731,6 @@ Item { anchors.fill: parent anchors.margins: 2 * screenScaleFactor - sourceSize.width: width sourceSize.height: width source: UM.Theme.getIcon(model.icon) color: UM.Theme.getColor("quality_slider_unavailable") @@ -1156,7 +1155,7 @@ Item function populateExtruderModel() { extruderModel.clear(); - for(var extruderNumber = 0; extruderNumber < extruders.rowCount() ; extruderNumber++) + for(var extruderNumber = 0; extruderNumber < extruders.count; extruderNumber++) { extruderModel.append({ text: extruders.getItem(extruderNumber).name, diff --git a/resources/qml/Toolbar.qml b/resources/qml/Toolbar.qml index 5fbddea9ac..1e335472d4 100644 --- a/resources/qml/Toolbar.qml +++ b/resources/qml/Toolbar.qml @@ -62,7 +62,7 @@ Item enabled: model.enabled && UM.Selection.hasSelection && UM.Controller.toolsEnabled isTopElement: toolsModel.getItem(0).id == model.id - isBottomElement: toolsModel.getItem(toolsModel.rowCount() - 1).id == model.id + isBottomElement: toolsModel.getItem(toolsModel.count - 1).id == model.id toolItem: UM.RecolorImage { diff --git a/resources/qml/ViewsSelector.qml b/resources/qml/ViewsSelector.qml index e9fdd57177..1e42a0b3ba 100644 --- a/resources/qml/ViewsSelector.qml +++ b/resources/qml/ViewsSelector.qml @@ -19,7 +19,7 @@ Cura.ExpandableComponent property var activeView: { - for (var i = 0; i < viewModel.rowCount(); i++) + for (var i = 0; i < viewModel.count; i++) { if (viewModel.items[i].active) { @@ -74,6 +74,8 @@ Cura.ExpandableComponent { id: viewSelectorPopup width: viewSelector.width - 2 * viewSelector.popupPadding + leftPadding: UM.Theme.getSize("default_lining").width + rightPadding: UM.Theme.getSize("default_lining").width // For some reason the height/width of the column gets set to 0 if this is not set... Component.onCompleted: @@ -91,7 +93,7 @@ Cura.ExpandableComponent { id: viewsSelectorButton text: model.name - width: parent.width + width: parent.width - viewSelectorPopup.leftPadding - viewSelectorPopup.rightPadding height: UM.Theme.getSize("action_button").height leftPadding: UM.Theme.getSize("default_margin").width rightPadding: UM.Theme.getSize("default_margin").width diff --git a/resources/qml/qmldir b/resources/qml/qmldir index 2475f398f8..7e57119bc6 100644 --- a/resources/qml/qmldir +++ b/resources/qml/qmldir @@ -1,7 +1,6 @@ module Cura MachineSelector 1.0 MachineSelector.qml -QuickConfigurationSelector 1.0 QuickConfigurationSelector.qml CustomConfigurationSelector 1.0 CustomConfigurationSelector.qml PrintSetupSelector 1.0 PrintSetupSelector.qml ActionButton 1.0 ActionButton.qml diff --git a/resources/themes/cura-dark/theme.json b/resources/themes/cura-dark/theme.json index 34b944b25b..d9ef74ebb9 100644 --- a/resources/themes/cura-dark/theme.json +++ b/resources/themes/cura-dark/theme.json @@ -15,7 +15,7 @@ "primary_hover": [48, 182, 231, 255], "primary_text": [255, 255, 255, 204], "border": [127, 127, 127, 255], - "secondary": [241, 242, 242, 255], + "secondary": [95, 95, 95, 255], "main_window_header_button_text_inactive": [128, 128, 128, 255], "main_window_header_button_text_hovered": [255, 255, 255, 255], @@ -196,14 +196,6 @@ "layerview_support_interface": [64, 192, 255, 255], "layerview_nozzle": [181, 166, 66, 120], - "configuration_item": [0, 0, 0, 0], - "configuration_item_active": [12, 169, 227, 179], - "configuration_item_text": [255, 255, 255, 255], - "configuration_item_text_active": [255, 255, 255, 255], - "configuration_item_border": [255, 255, 255, 255], - "configuration_item_border_active": [12, 169, 227, 179], - "configuration_item_border_hover": [12, 169, 227, 179], - "material_compatibility_warning": [255, 255, 255, 255], "quality_slider_unavailable": [179, 179, 179, 255], diff --git a/resources/themes/cura-light/images/header_pattern.svg b/resources/themes/cura-light/images/header_pattern.svg index 2a9de2f3e9..eff5f01cfa 100644 --- a/resources/themes/cura-light/images/header_pattern.svg +++ b/resources/themes/cura-light/images/header_pattern.svg @@ -1 +1,1901 @@ -Pattern \ No newline at end of file + + + + Desktop HD + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/themes/cura-light/images/logo_about.svg b/resources/themes/cura-light/images/logo_about.svg new file mode 100644 index 0000000000..34301fd6c9 --- /dev/null +++ b/resources/themes/cura-light/images/logo_about.svg @@ -0,0 +1,172 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index f2ad2b6f4a..30cf42859a 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -73,7 +73,6 @@ QtObject anchors.rightMargin: Theme.getSize("default_margin").width width: Theme.getSize("standard_arrow").width height: Theme.getSize("standard_arrow").height - sourceSize.width: width sourceSize.height: width color: control.enabled ? Theme.getColor("setting_category_text") : Theme.getColor("setting_category_disabled_text") source: Theme.getIcon("arrow_bottom") @@ -146,7 +145,7 @@ QtObject text: control.text anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter - font: UM.Theme.getFont("medium_bold") + font: UM.Theme.getFont("medium") color: { if (control.checked) @@ -257,7 +256,6 @@ QtObject anchors.bottomMargin: Theme.getSize("button").height - Math.round(Theme.getSize("button_icon").height / 4) width: Theme.getSize("standard_arrow").width height: Theme.getSize("standard_arrow").height - sourceSize.width: width sourceSize.height: width visible: control.menu != null; color: @@ -529,7 +527,7 @@ QtObject implicitWidth: Theme.getSize("checkbox").width implicitHeight: Theme.getSize("checkbox").height - color: (control.hovered || control._hovered) ? Theme.getColor("checkbox_hover") : Theme.getColor("checkbox") + color: (control.hovered || control._hovered) ? Theme.getColor("checkbox_hover") : (control.enabled ? Theme.getColor("checkbox") : Theme.getColor("checkbox_disabled")) Behavior on color { ColorAnimation { duration: 50; } } radius: control.exclusiveGroup ? Math.round(Theme.getSize("checkbox").width / 2) : 0 @@ -543,7 +541,6 @@ QtObject anchors.horizontalCenter: parent.horizontalCenter width: Math.round(parent.width / 2.5) height: Math.round(parent.height / 2.5) - sourceSize.width: width sourceSize.height: width color: Theme.getColor("checkbox_mark") source: control.exclusiveGroup ? Theme.getIcon("dot") : Theme.getIcon("check") @@ -585,7 +582,6 @@ QtObject anchors.horizontalCenter: parent.horizontalCenter width: Math.round(parent.width / 2.5) height: Math.round(parent.height / 2.5) - sourceSize.width: width sourceSize.height: width color: Theme.getColor("checkbox_mark") source: @@ -836,7 +832,6 @@ QtObject anchors.horizontalCenter: parent.horizontalCenter width: Math.floor(control.width / 2) height: Math.floor(control.height / 2) - sourceSize.width: width sourceSize.height: width color: { diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index dfad5cfd17..2d7e92be4d 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -41,12 +41,12 @@ "family": "Noto Sans" }, "small": { - "size": 1.0, - "weight": 63, + "size": 0.85, + "weight": 50, "family": "Noto Sans" }, "very_small": { - "size": 1.0, + "size": 0.7, "weight": 50, "family": "Noto Sans" }, @@ -64,12 +64,6 @@ "size": 1.15, "weight": 50, "family": "Noto Sans" - }, - "extruder_icon": - { - "size": 0.7, - "weight": 50, - "family": "Noto Sans" } }, @@ -99,14 +93,14 @@ "secondary_button_hover": [228, 228, 228, 255], "secondary_button_text": [30, 102, 215, 255], - "main_window_header_background": [10, 8, 80, 255], + "main_window_header_background": [8, 7, 63, 255], "main_window_header_background_gradient": [25, 23, 91, 255], - "main_window_header_button_text_active": [10, 8, 80, 255], + "main_window_header_button_text_active": [8, 7, 63, 255], "main_window_header_button_text_inactive": [255, 255, 255, 255], "main_window_header_button_text_hovered": [255, 255, 255, 255], "main_window_header_button_background_active": [255, 255, 255, 255], "main_window_header_button_background_inactive": [255, 255, 255, 0], - "main_window_header_button_background_hovered": [255, 255, 255, 102], + "main_window_header_button_background_hovered": [117, 114, 159, 255], "account_widget_outline_active": [70, 66, 126, 255], @@ -114,12 +108,13 @@ "machine_selector_active": [68, 72, 75, 255], "machine_selector_hover": [68, 72, 75, 255], "machine_selector_text_active": [255, 255, 255, 255], + "machine_selector_printer_icon": [8, 7, 63, 255], "action_panel_secondary": [27, 95, 202, 255], "toolbar_background": [255, 255, 255, 255], - "printer_type_label_background": [171, 171, 191, 255], + "printer_type_label_background": [228, 228, 242, 255], "text": [0, 0, 0, 255], "text_detail": [174, 174, 174, 128], @@ -133,9 +128,9 @@ "text_scene_hover": [70, 84, 113, 255], "error": [255, 140, 0, 255], - "warning": [255, 190, 35, 255], + "warning": [245, 166, 35, 255], - "toolbar_button_text": [10, 8, 80, 255], + "toolbar_button_text": [8, 7, 63, 255], "toolbar_button_hover": [232, 242, 252, 255], "toolbar_button_active": [232, 242, 252, 255], "toolbar_button_active_hover": [232, 242, 252, 255], @@ -150,9 +145,9 @@ "button_text_active_hover": [255, 255, 255, 255], "small_button": [0, 0, 0, 0], - "small_button_hover": [10, 8, 80, 255], - "small_button_active": [10, 8, 80, 255], - "small_button_active_hover": [10, 8, 80, 255], + "small_button_hover": [8, 7, 63, 255], + "small_button_active": [8, 7, 63, 255], + "small_button_active_hover": [8, 7, 63, 255], "small_button_text": [171, 171, 191, 255], "small_button_text_hover": [255, 255, 255, 255], "small_button_text_active": [255, 255, 255, 255], @@ -228,8 +223,8 @@ "progressbar_control": [50, 130, 255, 255], "slider_groove": [223, 223, 223, 255], - "slider_groove_fill": [10, 8, 80, 255], - "slider_handle": [10, 8, 80, 255], + "slider_groove_fill": [8, 7, 63, 255], + "slider_handle": [8, 7, 63, 255], "slider_handle_active": [50, 130, 255, 255], "slider_text_background": [255, 255, 255, 255], @@ -241,6 +236,7 @@ "checkbox_border": [64, 69, 72, 255], "checkbox_border_hover": [50, 130, 255, 255], "checkbox_mark": [119, 122, 124, 255], + "checkbox_disabled": [223, 223, 223, 255], "checkbox_text": [27, 27, 27, 255], "tooltip": [68, 192, 255, 255], @@ -310,14 +306,6 @@ "layerview_support_interface": [64, 192, 255, 255], "layerview_nozzle": [181, 166, 66, 50], - "configuration_item": [255, 255, 255, 0], - "configuration_item_active": [12, 169, 227, 32], - "configuration_item_text": [0, 0, 0, 255], - "configuration_item_text_active": [0, 0, 0, 255], - "configuration_item_border": [127, 127, 127, 255], - "configuration_item_border_active": [12, 169, 227, 32], - "configuration_item_border_hover": [50, 130, 255, 255], - "tab_status_connected": [50, 130, 255, 255], "tab_status_disconnected": [200, 200, 200, 255], @@ -377,7 +365,6 @@ "action_panel_widget": [25.0, 0.0], "action_panel_information_widget": [20.0, 0.0], - "action_panel_button": [15.0, 3.0], "machine_selector_widget": [20.0, 4.0], "machine_selector_widget_content": [25.0, 32.0], @@ -423,6 +410,9 @@ "button_icon": [2.5, 2.5], "button_lining": [0, 0], + "action_button": [15.0, 3.0], + "action_button_radius": [0.15, 0.15], + "small_button": [2, 2], "small_button_icon": [1.5, 1.5], @@ -511,9 +501,6 @@ "avatar_image": [6.8, 6.8], - "action_button": [15.0, 3.0], - "action_button_radius": [0.15, 0.15], - "monitor_config_override_box": [1.0, 14.0], "monitor_extruder_circle": [2.75, 2.75], "monitor_text_line": [1.16, 1.16], From 677edd51eea2ba87392851b2a5d01402e133e459 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 7 Dec 2018 10:55:20 +0100 Subject: [PATCH 0731/1240] Remove unnecessary visibles CURA-5941 Because the parent Rows already have it set. --- resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml | 3 --- 1 file changed, 3 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index eb8c81f228..ec9c5d0e38 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -151,7 +151,6 @@ Item color: UM.Theme.getColor("text") height: parent.height width: selectors.textWidth - visible: extrudersModel.count > 1 renderType: Text.NativeRendering } @@ -189,7 +188,6 @@ Item color: UM.Theme.getColor("text") height: parent.height width: selectors.textWidth - visible: materialSelection.visible renderType: Text.NativeRendering } @@ -228,7 +226,6 @@ Item color: UM.Theme.getColor("text") height: parent.height width: selectors.textWidth - visible: variantSelection.visible renderType: Text.NativeRendering } From da052fbe81eb8fc5c03049e867d0d8417146aff9 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 7 Dec 2018 11:21:25 +0100 Subject: [PATCH 0732/1240] Not show a tooltip in the output device selector when the popup shows up --- resources/qml/ActionPanel/OutputDevicesActionButton.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/ActionPanel/OutputDevicesActionButton.qml b/resources/qml/ActionPanel/OutputDevicesActionButton.qml index 95750e6d11..9a6c97bcff 100644 --- a/resources/qml/ActionPanel/OutputDevicesActionButton.qml +++ b/resources/qml/ActionPanel/OutputDevicesActionButton.qml @@ -55,7 +55,7 @@ Item leftPadding: UM.Theme.getSize("narrow_margin").width //Need more space than usual here for wide text. rightPadding: UM.Theme.getSize("narrow_margin").width - tooltip: catalog.i18nc("@info:tooltip", "Select the active output device") + tooltip: popup.opened ? "" : catalog.i18nc("@info:tooltip", "Select the active output device") iconSource: popup.opened ? UM.Theme.getIcon("arrow_top") : UM.Theme.getIcon("arrow_bottom") color: UM.Theme.getColor("action_panel_secondary") visible: (devicesModel.deviceCount > 1) From 898fd25ddb456a3dcfba6900e2ff03d5843fb5f9 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 7 Dec 2018 11:43:41 +0100 Subject: [PATCH 0733/1240] Remove the component IconLabel since we have a similar one in IconWithText Contributes to CURA-5941. --- .../qml/ActionPanel/OutputProcessWidget.qml | 5 +- .../qml/ActionPanel/SliceProcessWidget.qml | 3 +- resources/qml/IconLabel.qml | 50 ------------------- resources/qml/IconWithText.qml | 6 ++- .../PrintSetupSelectorHeader.qml | 1 - resources/qml/qmldir | 1 - 6 files changed, 7 insertions(+), 59 deletions(-) delete mode 100644 resources/qml/IconLabel.qml diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml index 1d1a1e44e1..3f53abf28f 100644 --- a/resources/qml/ActionPanel/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -44,7 +44,7 @@ Column rightMargin: UM.Theme.getSize("thin_margin").height } - Cura.IconLabel + Cura.IconWithText { id: estimatedTime width: parent.width @@ -54,7 +54,7 @@ Column font: UM.Theme.getFont("default_bold") } - Cura.IconLabel + Cura.IconWithText { id: estimatedCosts width: parent.width @@ -84,7 +84,6 @@ Column return totalWeights + "g · " + totalLengths.toFixed(2) + "m" } source: UM.Theme.getIcon("spool") - font: UM.Theme.getFont("default") } } diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index 8f6608e15c..18caeafb40 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -52,7 +52,7 @@ Column renderType: Text.NativeRendering } - Cura.IconLabel + Cura.IconWithText { id: unableToSliceMessage width: parent.width @@ -61,7 +61,6 @@ Column text: catalog.i18nc("@label:PrintjobStatus", "Unable to Slice") source: UM.Theme.getIcon("warning") color: UM.Theme.getColor("warning") - font: UM.Theme.getFont("default") } // Progress bar, only visible when the backend is in the process of slice the printjob diff --git a/resources/qml/IconLabel.qml b/resources/qml/IconLabel.qml deleted file mode 100644 index ed41fba499..0000000000 --- a/resources/qml/IconLabel.qml +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2018 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.7 -import QtQuick.Controls 2.3 - -import UM 1.1 as UM - -// This item will show a label with a squared icon in the left -Item -{ - id: container - - property alias text: label.text - property alias source: icon.source - property alias color: label.color - property alias font: label.font - property alias iconSize: icon.width - - implicitHeight: icon.height - - UM.RecolorImage - { - id: icon - - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - - source: "" - width: UM.Theme.getSize("section_icon").width - height: width - - color: label.color - visible: source != "" - } - - Label - { - id: label - anchors.left: icon.visible ? icon.right : parent.left - anchors.right: parent.right - anchors.leftMargin: UM.Theme.getSize("thin_margin").width - anchors.verticalCenter: icon.verticalCenter - text: "Empty label" - elide: Text.ElideRight - color: UM.Theme.getColor("text") - font: UM.Theme.getFont("default") - renderType: Text.NativeRendering - } -} \ No newline at end of file diff --git a/resources/qml/IconWithText.qml b/resources/qml/IconWithText.qml index 22599b3aed..5530740040 100644 --- a/resources/qml/IconWithText.qml +++ b/resources/qml/IconWithText.qml @@ -13,9 +13,11 @@ import Cura 1.0 as Cura // It sets the icon size + half of the content as its minium width (in which case it will elide the text) Item { - property alias iconColor: icon.color property alias source: icon.source + property alias iconSize: icon.width + property alias color: label.color property alias text: label.text + property alias font: label.font property real margin: UM.Theme.getSize("narrow_margin").width @@ -37,7 +39,7 @@ Item width: UM.Theme.getSize("section_icon").width height: UM.Theme.getSize("section_icon").height - color: "black" + color: label.color anchors { diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml index d4057289f6..518f3d49eb 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml @@ -43,7 +43,6 @@ RowLayout source: UM.Theme.getIcon("category_support") text: supportEnabled.properties.value == "True" ? enabledText : disabledText - UM.SettingPropertyProvider { id: supportEnabled diff --git a/resources/qml/qmldir b/resources/qml/qmldir index c19b982318..1dc21150ce 100644 --- a/resources/qml/qmldir +++ b/resources/qml/qmldir @@ -7,7 +7,6 @@ ActionButton 1.0 ActionButton.qml MaterialMenu 1.0 MaterialMenu.qml NozzleMenu 1.0 NozzleMenu.qml ActionPanelWidget 1.0 ActionPanelWidget.qml -IconLabel 1.0 IconLabel.qml IconWithText 1.0 IconWithText.qml OutputDevicesActionButton 1.0 OutputDevicesActionButton.qml ExpandableComponent 1.0 ExpandableComponent.qml From 4f82a2759ad78a00b60c5008e78877830c38f2d7 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Fri, 7 Dec 2018 12:04:02 +0100 Subject: [PATCH 0734/1240] STAR-322: Improving configuration models interface --- .../NetworkedPrinterOutputDevice.py | 11 +- .../src/Cloud/CloudOutputDevice.py | 360 ++++++++++-------- .../src/Cloud/Models/CloudClusterPrintJob.py | 24 +- .../src/Cloud/Models/CloudClusterPrinter.py | 9 +- .../CloudClusterPrinterConfiguration.py | 19 +- plugins/UM3NetworkPrinting/src/Cloud/Utils.py | 27 ++ .../src/ClusterUM3OutputDevice.py | 11 +- 7 files changed, 278 insertions(+), 183 deletions(-) diff --git a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py index 300ed5194d..b0c8b54a67 100644 --- a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py +++ b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py @@ -4,6 +4,7 @@ from UM.FileHandler.FileHandler import FileHandler #For typing. from UM.Logger import Logger from UM.Scene.SceneNode import SceneNode #For typing. +from cura.API import Account from cura.CuraApplication import CuraApplication from cura.PrinterOutputDevice import PrinterOutputDevice, ConnectionState @@ -162,9 +163,15 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): part.setBody(data) return part - ## Convenience function to get the username from the OS. - # The code was copied from the getpass module, as we try to use as little dependencies as possible. + ## Convenience function to get the username, either from the cloud or from the OS. def _getUserName(self) -> str: + # check first if we are logged in with the Ultimaker Account + account = CuraApplication.getInstance().getCuraAPI().account # type: Account + if account and account.isLoggedIn: + return account.userName + + # Otherwise get the username from the US + # The code below was copied from the getpass module, as we try to use as little dependencies as possible. for name in ("LOGNAME", "USER", "LNAME", "USERNAME"): user = os.environ.get(name) if user: diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 9f5857dff6..3bc16cbfb0 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -16,7 +16,6 @@ from UM.Qt.Duration import Duration, DurationFormat from UM.Scene.SceneNode import SceneNode from cura.CuraApplication import CuraApplication from cura.PrinterOutput.NetworkedPrinterOutputDevice import AuthState, NetworkedPrinterOutputDevice -from cura.PrinterOutput.PrinterOutputController import PrinterOutputController from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputController import CloudOutputController from ..MeshFormatHandler import MeshFormatHandler @@ -28,7 +27,7 @@ from .Models.CloudPrintResponse import CloudPrintResponse from .Models.CloudJobResponse import CloudJobResponse from .Models.CloudClusterPrinter import CloudClusterPrinter from .Models.CloudClusterPrintJob import CloudClusterPrintJob -from .Utils import findChanges +from .Utils import findChanges, formatDateCompleted, formatTimeCompleted ## Class that contains all the translations for this module. @@ -55,6 +54,12 @@ class T: UPLOAD_SUCCESS_TITLE = _I18N_CATALOG.i18nc("@info:title", "Data Sent") UPLOAD_SUCCESS_TEXT = _I18N_CATALOG.i18nc("@info:status", "Print job was successfully sent to the printer.") + JOB_COMPLETED_TITLE = _I18N_CATALOG.i18nc("@info:status", "Print finished") + JOB_COMPLETED_PRINTER = _I18N_CATALOG.i18nc("@info:status", + "Printer '{printer_name}' has finished printing '{job_name}'.") + + JOB_COMPLETED_NO_PRINTER = _I18N_CATALOG.i18nc("@info:status", "The print job '{job_name}' was finished.") + ## The cloud output device is a network output device that works remotely but has limited functionality. # Currently it only supports viewing the printer and print job status and adding a new job to the queue. @@ -65,7 +70,7 @@ class T: class CloudOutputDevice(NetworkedPrinterOutputDevice): # The interval with which the remote clusters are checked - CHECK_CLUSTER_INTERVAL = 2.0 # seconds + CHECK_CLUSTER_INTERVAL = 4.0 # seconds # Signal triggered when the print jobs in the queue were changed. printJobsChanged = pyqtSignal() @@ -109,6 +114,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # We only allow a single upload at a time. self._sending_job = False + # TODO: handle progress messages in another class. self._progress_message = None # type: Optional[Message] ## Gets the host name of this device @@ -128,7 +134,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): return network_key.startswith(self._host_name) ## Set all the interface elements and texts for this output device. - def _setInterfaceElements(self): + def _setInterfaceElements(self) -> None: self.setPriority(2) # make sure we end up below the local networking and above 'save to file' self.setName(self._id) self.setShortDescription(T.PRINT_VIA_CLOUD_BUTTON) @@ -157,13 +163,192 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # TODO: Remove extension from the file name, since we are using content types now request = CloudJobUploadRequest( - job_name = file_name + "." + mesh_format.file_extension, + job_name = file_name, ## + "." + mesh_format.file_extension, file_size = len(mesh_bytes), content_type = mesh_format.mime_type, ) self._api.requestUpload(request, lambda response: self._onPrintJobCreated(mesh_bytes, response)) - ## Get remote printers. + ## Called when the connection to the cluster changes. + def connect(self) -> None: + super().connect() + + ## Called when the network data should be updated. + def _update(self) -> None: + super()._update() + if self._last_response_time and time() - self._last_response_time < self.CHECK_CLUSTER_INTERVAL: + return # avoid calling the cloud too often + + if self._account.isLoggedIn: + self.setAuthenticationState(AuthState.Authenticated) + self._api.getClusterStatus(self._device_id, self._onStatusCallFinished) + else: + self.setAuthenticationState(AuthState.NotAuthenticated) + + ## Method called when HTTP request to status endpoint is finished. + # Contains both printers and print jobs statuses in a single response. + def _onStatusCallFinished(self, status: CloudClusterStatus) -> None: + # Update all data from the cluster. + self._updatePrinters(status.printers) + self._updatePrintJobs(status.print_jobs) + + ## Updates the local list of printers with the list received from the cloud. + # \param jobs: The printers received from the cloud. + def _updatePrinters(self, printers: List[CloudClusterPrinter]) -> None: + previous = {p.key: p for p in self._printers} # type: Dict[str, PrinterOutputModel] + received = {p.uuid: p for p in printers} # type: Dict[str, CloudClusterPrinter] + + removed_printers, added_printers, updated_printers = findChanges(previous, received) + + for removed_printer in removed_printers: + if self._active_printer == removed_printer: + self.setActivePrinter(None) + self._printers.remove(removed_printer) + + for added_printer in added_printers: + self._printers.append(added_printer.createOutputModel(CloudOutputController(self))) + + for model, printer in updated_printers: + printer.updateOutputModel(model) + + # Always have an active printer + if not self._active_printer: + self.setActivePrinter(self._printers[0]) + + if removed_printers or added_printers or updated_printers: + self._clusterPrintersChanged.emit() + + ## Updates the local list of print jobs with the list received from the cloud. + # \param jobs: The print jobs received from the cloud. + def _updatePrintJobs(self, jobs: List[CloudClusterPrintJob]) -> None: + received = {j.uuid: j for j in jobs} # type: Dict[str, CloudClusterPrintJob] + previous = {j.key: j for j in self._print_jobs} # type: Dict[str, UM3PrintJobOutputModel] + + removed_jobs, added_jobs, updated_jobs = findChanges(previous, received) + + # TODO: we see that not all data in the UI is correctly updated when the queue and active jobs change. + # TODO: we need to fix this here somehow by updating the correct output models. + # TODO: the configuration drop down in the slice window is not populated because we are missing some data. + # TODO: to fix this we need to implement more data as shown in ClusterUM3OutputDevice._createPrintJobModel + + for removed_job in removed_jobs: + self._print_jobs.remove(removed_job) + + for added_job in added_jobs: + self._addPrintJob(added_job) + + for model, job in updated_jobs: + job.updateOutputModel(model) + if job.printer_uuid: + self._updateAssignedPrinter(model, job.printer_uuid) + + # We only have to update when jobs are added or removed + # updated jobs push their changes via their output model + if added_jobs or removed_jobs or updated_jobs: + self.printJobsChanged.emit() + + ## Registers a new print job received via the cloud API. + # \param job: The print job received. + def _addPrintJob(self, job: CloudClusterPrintJob) -> None: + model = job.createOutputModel(CloudOutputController(self)) + model.stateChanged.connect(self._onPrintJobStateChanged) + if job.printer_uuid: + self._updateAssignedPrinter(model, job.printer_uuid) + self._print_jobs.append(model) + + ## Handles the event of a change in a print job state + def _onPrintJobStateChanged(self) -> None: + username = self._account.userName + finished_jobs = [job for job in self._print_jobs if job.state == "wait_cleanup"] + + newly_finished_jobs = [job for job in finished_jobs if job not in self._finished_jobs and job.owner == username] + for job in newly_finished_jobs: + if job.assignedPrinter: + job_completed_text = T.JOB_COMPLETED_PRINTER.format(printer_name=job.assignedPrinter.name, + job_name=job.name) + else: + job_completed_text = T.JOB_COMPLETED_NO_PRINTER.format(job_name=job.name) + job_completed_message = Message(text=job_completed_text, title = T.JOB_COMPLETED_TITLE) + job_completed_message.show() + + # Ensure UI gets updated + self.printJobsChanged.emit() + + ## Updates the printer assignment for the given print job model. + def _updateAssignedPrinter(self, model: UM3PrintJobOutputModel, printer_uuid: str) -> None: + printer = next((p for p in self._printers if printer_uuid == p.key), None) + + if not printer: + return Logger.log("w", "Missing printer %s for job %s in %s", model.assignedPrinter, model.key, + [p.key for p in self._printers]) + + printer.updateActivePrintJob(model) + model.updateAssignedPrinter(printer) + + ## Uploads the mesh when the print job was registered with the cloud API. + # \param mesh: The bytes to upload. + # \param job_response: The response received from the cloud API. + def _onPrintJobCreated(self, mesh: bytes, job_response: CloudJobResponse) -> None: + self._api.uploadMesh(job_response, mesh, self._onPrintJobUploaded, self._updateUploadProgress, + lambda _: self._onUploadError(T.UPLOAD_ERROR)) + + ## Requests the print to be sent to the printer when we finished uploading the mesh. + # \param job_id: The ID of the job. + def _onPrintJobUploaded(self, job_id: str) -> None: + self._api.requestPrint(self._device_id, job_id, self._onUploadSuccess) + + ## Updates the progress of the mesh upload. + # \param progress: The amount of percentage points uploaded until now (0-100). + def _updateUploadProgress(self, progress: int) -> None: + if not self._progress_message: + self._progress_message = Message( + text = T.SENDING_DATA_TEXT, + title = T.SENDING_DATA_TITLE, + progress = -1, + lifetime = 0, + dismissable = False, + use_inactivity_timer = False + ) + self._progress_message.setProgress(progress) + self._progress_message.show() + + ## Hides the upload progress bar + def _resetUploadProgress(self) -> None: + if self._progress_message: + self._progress_message.hide() + self._progress_message = None + + ## Displays the given message if uploading the mesh has failed + # \param message: The message to display. + def _onUploadError(self, message: str = None) -> None: + self._resetUploadProgress() + if message: + message = Message( + text = message, + title = T.ERROR, + lifetime = 10, + dismissable = True + ) + message.show() + self._sending_job = False # the upload has finished so we're not sending a job anymore + self.writeError.emit() + + ## Shows a message when the upload has succeeded + # \param response: The response from the cloud API. + def _onUploadSuccess(self, response: CloudPrintResponse) -> None: + Logger.log("i", "The cluster will be printing this print job with the ID %s", response.cluster_job_id) + self._resetUploadProgress() + message = Message( + text = T.UPLOAD_SUCCESS_TEXT, + title = T.UPLOAD_SUCCESS_TITLE, + lifetime = 5, + dismissable = True, + ) + message.show() + self._sending_job = False # the upload has finished so we're not sending a job anymore + self.writeFinished.emit() + + ## Gets the remote printers. @pyqtProperty("QVariantList", notify = _clusterPrintersChanged) def printers(self) -> List[PrinterOutputModel]: return self._printers @@ -209,170 +394,11 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): @pyqtSlot(int, result = str) def getTimeCompleted(self, time_remaining: int) -> str: # TODO: this really shouldn't be in this class - 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) + return formatTimeCompleted(time_remaining) @pyqtSlot(int, result = str) def getDateCompleted(self, time_remaining: int) -> str: - # TODO: this really shouldn't be in this class - current_time = time() - completed = datetime.fromtimestamp(current_time + time_remaining) - today = datetime.fromtimestamp(current_time) - # If finishing date is more than 7 days out, using "Mon Dec 3 at HH:MM" format - if completed.toordinal() > today.toordinal() + 7: - return completed.strftime("%a %b ") + "{day}".format(day = completed.day) - # If finishing date is within the next week, use "Monday at HH:MM" format - elif completed.toordinal() > today.toordinal() + 1: - return completed.strftime("%a") - # If finishing tomorrow, use "tomorrow at HH:MM" format - elif completed.toordinal() > today.toordinal(): - return "tomorrow" - # If finishing today, use "today at HH:MM" format - else: - return "today" - - ## Called when the connection to the cluster changes. - def connect(self) -> None: - super().connect() - - ## Called when the network data should be updated. - def _update(self) -> None: - super()._update() - if self._last_response_time and time() - self._last_response_time < self.CHECK_CLUSTER_INTERVAL: - return # avoid calling the cloud too often - - if self._account.isLoggedIn: - self.setAuthenticationState(AuthState.Authenticated) - self._api.getClusterStatus(self._device_id, self._onStatusCallFinished) - else: - self.setAuthenticationState(AuthState.NotAuthenticated) - - ## Method called when HTTP request to status endpoint is finished. - # Contains both printers and print jobs statuses in a single response. - def _onStatusCallFinished(self, status: CloudClusterStatus) -> None: - # Update all data from the cluster. - self._updatePrinters(status.printers) - self._updatePrintJobs(status.print_jobs) - - def _updatePrinters(self, printers: List[CloudClusterPrinter]) -> None: - previous = {p.key: p for p in self._printers} # type: Dict[str, PrinterOutputModel] - received = {p.uuid: p for p in printers} # type: Dict[str, CloudClusterPrinter] - - removed_printers, added_printers, updated_printers = findChanges(previous, received) - - for removed_printer in removed_printers: - if self._active_printer == removed_printer: - self.setActivePrinter(None) - self._printers.remove(removed_printer) - - for added_printer in added_printers: - self._printers.append(added_printer.createOutputModel(CloudOutputController(self))) - - for model, printer in updated_printers: - printer.updateOutputModel(model) - - # Always have an active printer - if not self._active_printer: - self.setActivePrinter(self._printers[0]) - - if removed_printers or added_printers or updated_printers: - self._clusterPrintersChanged.emit() - - def _updatePrintJobs(self, jobs: List[CloudClusterPrintJob]) -> None: - received = {j.uuid: j for j in jobs} # type: Dict[str, CloudClusterPrintJob] - previous = {j.key: j for j in self._print_jobs} # type: Dict[str, UM3PrintJobOutputModel] - - removed_jobs, added_jobs, updated_jobs = findChanges(previous, received) - - # TODO: we see that not all data in the UI is correctly updated when the queue and active jobs change. - # TODO: we need to fix this here somehow by updating the correct output models. - # TODO: also the configuration drop down in the slice window is not populated because we are missing some data. - # TODO: to fix this we need to implement more data as shown in ClusterUM3OutputDevice._createPrintJobModel - - for removed_job in removed_jobs: - self._print_jobs.remove(removed_job) - - for added_job in added_jobs: - self._addPrintJob(added_job) - - for model, job in updated_jobs: - job.updateOutputModel(model) - self._updatePrintJobDetails(model) - - # We only have to update when jobs are added or removed - # updated jobs push their changes via their output model - if added_jobs or removed_jobs or updated_jobs: - self.printJobsChanged.emit() - - def _addPrintJob(self, job: CloudClusterPrintJob) -> None: - print_job = job.createOutputModel(CloudOutputController(self)) - self._updatePrintJobDetails(print_job) - self._print_jobs.append(print_job) - - def _updatePrintJobDetails(self, print_job: UM3PrintJobOutputModel): - printer = None - try: - printer = next(p for p in self._printers if print_job.assignedPrinter == p.key) - except StopIteration: - Logger.log("w", "Missing printer %s for job %s in %s", print_job.assignedPrinter, print_job.key, - [p.key for p in self._printers]) - - if printer: - printer.updateActivePrintJob(print_job) - print_job.updateAssignedPrinter(printer) - - def _onPrintJobCreated(self, mesh: bytes, job_response: CloudJobResponse) -> None: - self._api.uploadMesh(job_response, mesh, self._onPrintJobUploaded, self._updateUploadProgress, - lambda _: self._onUploadError(T.UPLOAD_ERROR)) - - def _onPrintJobUploaded(self, job_id: str) -> None: - self._api.requestPrint(self._device_id, job_id, self._onUploadSuccess) - - def _updateUploadProgress(self, progress: int): - if not self._progress_message: - self._progress_message = Message( - text = T.SENDING_DATA_TEXT, - title = T.SENDING_DATA_TITLE, - progress = -1, - lifetime = 0, - dismissable = False, - use_inactivity_timer = False - ) - self._progress_message.setProgress(progress) - self._progress_message.show() - - def _resetUploadProgress(self): - if self._progress_message: - self._progress_message.hide() - self._progress_message = None - - def _onUploadError(self, message: str = None): - self._resetUploadProgress() - if message: - message = Message( - text = message, - title = T.ERROR, - lifetime = 10, - dismissable = True - ) - message.show() - self._sending_job = False # the upload has finished so we're not sending a job anymore - self.writeError.emit() - - # Shows a message when the upload has succeeded - def _onUploadSuccess(self, response: CloudPrintResponse): - Logger.log("i", "The cluster will be printing this print job with the ID %s", response.cluster_job_id) - self._resetUploadProgress() - message = Message( - text = T.UPLOAD_SUCCESS_TEXT, - title = T.UPLOAD_SUCCESS_TITLE, - lifetime = 5, - dismissable = True, - ) - message.show() - self._sending_job = False # the upload has finished so we're not sending a job anymore - self.writeFinished.emit() + return formatDateCompleted(time_remaining) ## TODO: The following methods are required by the monitor page QML, but are not actually available using cloud. # TODO: We fake the methods here to not break the monitor page. diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py index c9255b8da8..22c66ddfab 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py @@ -1,7 +1,8 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import List, Optional +from typing import List +from cura.PrinterOutput.ConfigurationModel import ConfigurationModel from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputController import CloudOutputController from .CloudClusterPrinterConfiguration import CloudClusterPrinterConfiguration from .CloudClusterPrintJobConstraint import CloudClusterPrintJobConstraint @@ -31,20 +32,33 @@ class CloudClusterPrintJob(BaseModel): self.time_total = None # type: str self.uuid = None # type: str super().__init__(**kwargs) - self.printers = [CloudClusterPrinterConfiguration(**c) if isinstance(c, dict) else c - for c in self.configuration] - self.print_jobs = [CloudClusterPrintJobConstraint(**p) if isinstance(p, dict) else p - for p in self.constraints] + self.configuration = [CloudClusterPrinterConfiguration(**c) if isinstance(c, dict) else c + for c in self.configuration] + self.constraints = [CloudClusterPrintJobConstraint(**p) if isinstance(p, dict) else p + for p in self.constraints] ## Creates an UM3 print job output model based on this cloud cluster print job. # \param printer: The output model of the printer def createOutputModel(self, controller: CloudOutputController) -> UM3PrintJobOutputModel: model = UM3PrintJobOutputModel(controller, self.uuid, self.name) + self.updateOutputModel(model) + return model + ## Creates a new configuration model + def _createConfigurationModel(self) -> ConfigurationModel: + extruders = [extruder.createConfigurationModel() for extruder in self.configuration or ()] + configuration = ConfigurationModel() + configuration.setExtruderConfigurations(extruders) + return configuration + ## Updates an UM3 print job output model based on this cloud cluster print job. # \param model: The model to update. def updateOutputModel(self, model: UM3PrintJobOutputModel) -> None: + # TODO: Add `compatible_machine_families` to the cloud, than add model.setCompatibleMachineFamilies() + # TODO: Add `impediments_to_printing` to the cloud, see ClusterUM3OutputDevice._updatePrintJob + # TODO: Use model.updateConfigurationChanges, see ClusterUM3OutputDevice#_createConfigurationChanges + model.updateConfiguration(self._createConfigurationModel()) model.updateTimeTotal(self.time_total) model.updateTimeElapsed(self.time_elapsed) model.updateOwner(self.owner) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinter.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinter.py index dd65dffa26..78aa8e3a31 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinter.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinter.py @@ -2,6 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. from typing import List +from cura.PrinterOutput.ConfigurationModel import ConfigurationModel from cura.PrinterOutput.PrinterOutputController import PrinterOutputController from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from .CloudClusterPrinterConfiguration import CloudClusterPrinterConfiguration @@ -40,5 +41,9 @@ class CloudClusterPrinter(BaseModel): model.updateType(self.machine_variant) model.updateState(self.status if self.enabled else "disabled") - for configuration, extruder in zip(self.configuration, model.extruders): - configuration.updateOutputModel(extruder) + for configuration, extruder_output, extruder_config in \ + zip(self.configuration, model.extruders, model.printerConfiguration.extruderConfigurations): + configuration.updateOutputModel(extruder_output) + configuration.updateConfigurationModel(extruder_config) + + pass diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfiguration.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfiguration.py index be92549015..d60395f6ab 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfiguration.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfiguration.py @@ -1,5 +1,9 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from typing import List, Optional + +from cura.PrinterOutput.ConfigurationModel import ConfigurationModel +from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationModel from cura.PrinterOutput.ExtruderOutputModel import ExtruderOutputModel from .CloudClusterPrinterConfigurationMaterial import CloudClusterPrinterConfigurationMaterial from ...Models import BaseModel @@ -8,7 +12,7 @@ from ...Models import BaseModel ## Class representing a cloud cluster printer configuration class CloudClusterPrinterConfiguration(BaseModel): def __init__(self, **kwargs) -> None: - self.extruder_index = None # type: str + self.extruder_index = None # type: int self.material = None # type: CloudClusterPrinterConfigurationMaterial self.nozzle_diameter = None # type: str self.print_core_id = None # type: str @@ -25,3 +29,16 @@ class CloudClusterPrinterConfiguration(BaseModel): if model.activeMaterial is None or model.activeMaterial.guid != self.material.guid: material = self.material.createOutputModel() model.updateActiveMaterial(material) + + ## Creates a configuration model + def createConfigurationModel(self) -> ExtruderConfigurationModel: + model = ExtruderConfigurationModel(position = self.extruder_index) + self.updateConfigurationModel(model) + return model + + ## Creates a configuration model + def updateConfigurationModel(self, model: ExtruderConfigurationModel) -> ExtruderConfigurationModel: + model.setHotendID(self.print_core_id) + if self.material: + model.setMaterial(self.material.createOutputModel()) + return model diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Utils.py b/plugins/UM3NetworkPrinting/src/Cloud/Utils.py index 58eaf5edb9..eb96e49dad 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Utils.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Utils.py @@ -1,5 +1,8 @@ +from datetime import datetime, timedelta from typing import TypeVar, Dict, Tuple, List +from UM import i18nCatalog + T = TypeVar("T") U = TypeVar("U") @@ -24,3 +27,27 @@ def findChanges(previous: Dict[str, T], received: Dict[str, U]) -> Tuple[List[T] updated = [(previous[updated_id], received[updated_id]) for updated_id in updated_ids] return removed, added, updated + + +def formatTimeCompleted(time_remaining: int) -> str: + completed = datetime.now() + timedelta(seconds=time_remaining) + return "{hour:02d}:{minute:02d}".format(hour = completed.hour, minute = completed.minute) + + +def formatDateCompleted(time_remaining: int) -> str: + remaining = timedelta(seconds=time_remaining) + completed = datetime.now() + remaining + i18n = i18nCatalog("cura") + + # If finishing date is more than 7 days out, using "Mon Dec 3 at HH:MM" format + if remaining.days >= 7: + return completed.strftime("%a %b ") + "{day}".format(day = completed.day) + # If finishing date is within the next week, use "Monday at HH:MM" format + elif remaining.days >= 2: + return completed.strftime("%a") + # If finishing tomorrow, use "tomorrow at HH:MM" format + elif remaining.days >= 1: + return i18n.i18nc("@info:status", "tomorrow") + # If finishing today, use "today at HH:MM" format + else: + return i18n.i18nc("@info:status", "today") diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index a5ee3bc650..93a53373dc 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -25,6 +25,7 @@ from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationM from cura.PrinterOutput.NetworkedPrinterOutputDevice import AuthState, NetworkedPrinterOutputDevice from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel +from plugins.UM3NetworkPrinting.src.Cloud.Utils import formatTimeCompleted, formatDateCompleted from .ClusterUM3PrinterOutputController import ClusterUM3PrinterOutputController from .ConfigurationChangeModel import ConfigurationChangeModel @@ -337,14 +338,12 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): return self._printers @pyqtSlot(int, result = str) - def formatDuration(self, seconds: int) -> str: - return Duration(seconds).getDisplayString(DurationFormat.Format.Short) + def getTimeCompleted(self, time_remaining: int) -> str: + return formatTimeCompleted(time_remaining) @pyqtSlot(int, result = str) - 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) + def getDateCompleted(self, time_remaining: int) -> str: + return formatDateCompleted(time_remaining) @pyqtSlot(int, result = str) def getDateCompleted(self, time_remaining: int) -> str: From 1d33fe081fd30e257d9b0f5443288ad069b5f73c Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 7 Dec 2018 12:07:41 +0100 Subject: [PATCH 0735/1240] Clean up the code Remove all the references to the sidebar and use the term print_setup instead. Contributes to CURA-5941. --- .../PostProcessingPlugin.qml | 2 +- .../resources/qml/UM3InfoComponents.qml | 4 +- resources/qml/Cura.qml | 2 +- .../ConfigurationMenu/CustomConfiguration.qml | 4 +- resources/qml/MonitorButton.qml | 4 +- resources/qml/MonitorSidebar.qml | 2 +- resources/qml/ObjectsList.qml | 4 +- resources/qml/PrintMonitor.qml | 2 +- .../Custom/GlobalProfileSelector.qml | 2 +- ...debarTooltip.qml => PrintSetupTooltip.qml} | 0 resources/qml/SidebarContents.qml | 43 ------------------- resources/themes/cura-light/styles.qml | 6 +-- 12 files changed, 16 insertions(+), 59 deletions(-) rename resources/qml/{SidebarTooltip.qml => PrintSetupTooltip.qml} (100%) delete mode 100644 resources/qml/SidebarContents.qml diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml index 3fa10c23b9..b962f4d53b 100644 --- a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml +++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml @@ -412,7 +412,7 @@ UM.Dialog } } - Cura.SidebarTooltip + Cura.PrintSetupTooltip { id: tooltip } diff --git a/plugins/UM3NetworkPrinting/resources/qml/UM3InfoComponents.qml b/plugins/UM3NetworkPrinting/resources/qml/UM3InfoComponents.qml index 105143c851..643c8164a7 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/UM3InfoComponents.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/UM3InfoComponents.qml @@ -29,7 +29,7 @@ Item { Button { height: UM.Theme.getSize("save_button_save_to_button").height; onClicked: Cura.MachineManager.printerOutputDevices[0].requestAuthentication(); - style: UM.Theme.styles.sidebar_action_button; + style: UM.Theme.styles.print_setup_action_button; text: catalog.i18nc("@action:button", "Request Access"); tooltip: catalog.i18nc("@info:tooltip", "Send access request to the printer"); visible: printerConnected && !printerAcceptsCommands && !authenticationRequested; @@ -38,7 +38,7 @@ Item { Button { height: UM.Theme.getSize("save_button_save_to_button").height; onClicked: connectActionDialog.show(); - style: UM.Theme.styles.sidebar_action_button; + style: UM.Theme.styles.print_setup_action_button; text: catalog.i18nc("@action:button", "Connect"); tooltip: catalog.i18nc("@info:tooltip", "Connect to a printer"); visible: !printerConnected; diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 4e609ccbed..2df79d63c9 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -309,7 +309,7 @@ UM.MainWindow } } - SidebarTooltip + PrintSetupTooltip { id: tooltip } diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index ec9c5d0e38..78f6864c97 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -204,7 +204,7 @@ Item height: UM.Theme.getSize("setting_control").height width: selectors.controlWidth - style: UM.Theme.styles.sidebar_header_button + style: UM.Theme.styles.print_setup_header_button activeFocusOnPress: true menu: Cura.MaterialMenu { @@ -237,7 +237,7 @@ Item height: UM.Theme.getSize("setting_control").height width: selectors.controlWidth - style: UM.Theme.styles.sidebar_header_button + style: UM.Theme.styles.print_setup_header_button activeFocusOnPress: true; menu: Cura.NozzleMenu { extruderIndex: Cura.ExtruderManager.activeExtruderIndex } diff --git a/resources/qml/MonitorButton.qml b/resources/qml/MonitorButton.qml index eef76bcb09..fd7d2287c4 100644 --- a/resources/qml/MonitorButton.qml +++ b/resources/qml/MonitorButton.qml @@ -309,7 +309,7 @@ Item } } - style: UM.Theme.styles.sidebar_action_button + style: UM.Theme.styles.print_setup_action_button } Button @@ -325,7 +325,7 @@ Item text: catalog.i18nc("@label", "Abort Print") onClicked: confirmationDialog.visible = true - style: UM.Theme.styles.sidebar_action_button + style: UM.Theme.styles.print_setup_action_button } MessageDialog diff --git a/resources/qml/MonitorSidebar.qml b/resources/qml/MonitorSidebar.qml index 50416e34ab..669bdbfb8f 100644 --- a/resources/qml/MonitorSidebar.qml +++ b/resources/qml/MonitorSidebar.qml @@ -173,7 +173,7 @@ Rectangle anchors.bottom: parent.bottom } - SidebarTooltip + PrintSetupTooltip { id: tooltip } diff --git a/resources/qml/ObjectsList.qml b/resources/qml/ObjectsList.qml index 8f45b3744f..fd5175fce2 100644 --- a/resources/qml/ObjectsList.qml +++ b/resources/qml/ObjectsList.qml @@ -224,7 +224,7 @@ Rectangle { id: arrangeAllBuildPlatesButton; text: catalog.i18nc("@action:button","Arrange to all build plates"); - style: UM.Theme.styles.sidebar_action_button + style: UM.Theme.styles.print_setup_action_button height: UM.Theme.getSize("objects_menu_button").height; tooltip: ''; anchors @@ -244,7 +244,7 @@ Rectangle { id: arrangeBuildPlateButton; text: catalog.i18nc("@action:button","Arrange current build plate"); - style: UM.Theme.styles.sidebar_action_button + style: UM.Theme.styles.print_setup_action_button height: UM.Theme.getSize("objects_menu_button").height; tooltip: ''; anchors diff --git a/resources/qml/PrintMonitor.qml b/resources/qml/PrintMonitor.qml index 3cc161cbe7..4ed8daa55c 100644 --- a/resources/qml/PrintMonitor.qml +++ b/resources/qml/PrintMonitor.qml @@ -49,7 +49,7 @@ Rectangle property var activePrinter: connectedDevice != null ? connectedDevice.activePrinter : null property var activePrintJob: activePrinter != null ? activePrinter.activePrintJob: null - SidebarTooltip + PrintSetupTooltip { id: tooltip } diff --git a/resources/qml/PrintSetupSelector/Custom/GlobalProfileSelector.qml b/resources/qml/PrintSetupSelector/Custom/GlobalProfileSelector.qml index 11b2d3608b..8baaf9a7ae 100644 --- a/resources/qml/PrintSetupSelector/Custom/GlobalProfileSelector.qml +++ b/resources/qml/PrintSetupSelector/Custom/GlobalProfileSelector.qml @@ -43,7 +43,7 @@ Item right: parent.right } tooltip: Cura.MachineManager.activeQualityOrQualityChangesName - style: UM.Theme.styles.sidebar_header_button + style: UM.Theme.styles.print_setup_header_button activeFocusOnPress: true menu: Cura.ProfileMenu { } diff --git a/resources/qml/SidebarTooltip.qml b/resources/qml/PrintSetupTooltip.qml similarity index 100% rename from resources/qml/SidebarTooltip.qml rename to resources/qml/PrintSetupTooltip.qml diff --git a/resources/qml/SidebarContents.qml b/resources/qml/SidebarContents.qml deleted file mode 100644 index 0b19bfe3c1..0000000000 --- a/resources/qml/SidebarContents.qml +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2016 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.7 -import QtQuick.Controls 1.1 -import QtQuick.Controls.Styles 1.1 -import QtQuick.Layouts 1.1 - -import UM 1.2 as UM -import Cura 1.0 as Cura - -StackView -{ - id: sidebarContents - - delegate: StackViewDelegate - { - function transitionFinished(properties) - { - properties.exitItem.opacity = 1 - } - - pushTransition: StackViewTransition - { - PropertyAnimation - { - target: enterItem - property: "opacity" - from: 0 - to: 1 - duration: 100 - } - PropertyAnimation - { - target: exitItem - property: "opacity" - from: 1 - to: 0 - duration: 100 - } - } - } -} \ No newline at end of file diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index 18fce7f319..bcc754f4ca 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -9,7 +9,7 @@ import UM 1.1 as UM QtObject { - property Component sidebar_header_button: Component + property Component print_setup_header_button: Component { ButtonStyle { @@ -80,7 +80,7 @@ QtObject } Label { - id: sidebarComboBoxLabel + id: printSetupComboBoxLabel color: control.enabled ? Theme.getColor("setting_control_text") : Theme.getColor("setting_control_disabled_text") text: control.text; elide: Text.ElideRight; @@ -644,7 +644,7 @@ QtObject } } - property Component sidebar_action_button: Component + property Component print_setup_action_button: Component { ButtonStyle { From 2b8358fda855b2dd722f85dc7dcf525ecfb97461 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Fri, 7 Dec 2018 12:39:37 +0100 Subject: [PATCH 0736/1240] STAR-322: Improvements to date calculation and signalling --- .../src/Cloud/CloudOutputDevice.py | 10 +++------- plugins/UM3NetworkPrinting/src/Cloud/Utils.py | 17 +++++++++-------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 3bc16cbfb0..108fb0040c 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -1,10 +1,9 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import os -from datetime import datetime from time import time -from typing import Dict, List, Optional, Set +from typing import Dict, List, Optional from PyQt5.QtCore import QObject, QUrl, pyqtProperty, pyqtSignal, pyqtSlot @@ -215,8 +214,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): if not self._active_printer: self.setActivePrinter(self._printers[0]) - if removed_printers or added_printers or updated_printers: - self._clusterPrintersChanged.emit() + self.printersChanged.emit() ## Updates the local list of print jobs with the list received from the cloud. # \param jobs: The print jobs received from the cloud. @@ -388,12 +386,10 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): @pyqtSlot(int, result = str) def formatDuration(self, seconds: int) -> str: - # TODO: this really shouldn't be in this class return Duration(seconds).getDisplayString(DurationFormat.Format.Short) @pyqtSlot(int, result = str) def getTimeCompleted(self, time_remaining: int) -> str: - # TODO: this really shouldn't be in this class return formatTimeCompleted(time_remaining) @pyqtSlot(int, result = str) @@ -413,7 +409,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): @pyqtProperty(bool, notify = printJobsChanged) def receivedPrintJobs(self) -> bool: - return True + return bool(self._print_jobs) @pyqtSlot() def openPrintJobControlPanel(self) -> None: diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Utils.py b/plugins/UM3NetworkPrinting/src/Cloud/Utils.py index eb96e49dad..5136e0e7db 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Utils.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Utils.py @@ -29,24 +29,25 @@ def findChanges(previous: Dict[str, T], received: Dict[str, U]) -> Tuple[List[T] return removed, added, updated -def formatTimeCompleted(time_remaining: int) -> str: - completed = datetime.now() + timedelta(seconds=time_remaining) +def formatTimeCompleted(seconds_remaining: int) -> str: + completed = datetime.now() + timedelta(seconds=seconds_remaining) return "{hour:02d}:{minute:02d}".format(hour = completed.hour, minute = completed.minute) -def formatDateCompleted(time_remaining: int) -> str: - remaining = timedelta(seconds=time_remaining) - completed = datetime.now() + remaining +def formatDateCompleted(seconds_remaining: int) -> str: + now = datetime.now() + completed = now + timedelta(seconds=seconds_remaining) + days = (completed.date() - now.date()).days i18n = i18nCatalog("cura") # If finishing date is more than 7 days out, using "Mon Dec 3 at HH:MM" format - if remaining.days >= 7: + if days >= 7: return completed.strftime("%a %b ") + "{day}".format(day = completed.day) # If finishing date is within the next week, use "Monday at HH:MM" format - elif remaining.days >= 2: + elif days >= 2: return completed.strftime("%a") # If finishing tomorrow, use "tomorrow at HH:MM" format - elif remaining.days >= 1: + elif days >= 1: return i18n.i18nc("@info:status", "tomorrow") # If finishing today, use "today at HH:MM" format else: From dfe55b8f23b90fcdac812fc97a62e22bfcf6eea9 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 7 Dec 2018 12:59:51 +0100 Subject: [PATCH 0737/1240] Fix height of expandable component adapting to contents Since the children don't adjust their height based on if they are visible (which would cause a binding loop) we just need to adjust the height of the total menu based on which children are visible. Easy enough. Contributes to issue CURA-5876. --- .../Menus/ConfigurationMenu/ConfigurationMenu.qml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index 9bef2ca0be..5780649423 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -139,7 +139,19 @@ Cura.ExpandableComponent Item { width: parent.width - height: childrenRect.height + height: + { + var height = 0; + if(autoConfiguration.visible) + { + height += autoConfiguration.height; + } + if(customConfiguration.visible) + { + height += customConfiguration.height; + } + return height; + } AutoConfiguration { id: autoConfiguration From 6a696a57f25e0ac41091320a9abb64a3ccce37c6 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Fri, 7 Dec 2018 13:01:49 +0100 Subject: [PATCH 0738/1240] Removed unused property --- resources/qml/JobSpecs.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index 8b06ab06db..c7f82b8876 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -80,7 +80,6 @@ Item height: UM.Theme.getSize("jobspecs_line").height width: Math.max(__contentWidth + UM.Theme.getSize("default_margin").width, 50) maximumLength: 120 - property int unremovableSpacing: 5 text: PrintInformation.jobName horizontalAlignment: TextInput.AlignLeft From 5b963de2ea64058e5a1ff04b24ef27a1e56e1873 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Fri, 7 Dec 2018 13:19:45 +0100 Subject: [PATCH 0739/1240] STAR-322: Checking if response changed before updating cluster --- .../src/Cloud/CloudOutputDevice.py | 21 ++++++++++++------- .../src/Cloud/Models/BaseCloudModel.py | 17 +++++++++++++++ .../src/Cloud/Models/CloudCluster.py | 4 ++-- .../src/Cloud/Models/CloudClusterPrintJob.py | 4 ++-- .../Models/CloudClusterPrintJobConstraint.py | 4 ++-- .../src/Cloud/Models/CloudClusterPrinter.py | 4 ++-- .../CloudClusterPrinterConfiguration.py | 4 ++-- ...loudClusterPrinterConfigurationMaterial.py | 4 ++-- .../src/Cloud/Models/CloudClusterStatus.py | 10 +++++++-- .../src/Cloud/Models/CloudErrorObject.py | 4 ++-- .../src/Cloud/Models/CloudJobResponse.py | 4 ++-- .../src/Cloud/Models/CloudJobUploadRequest.py | 4 ++-- .../src/Cloud/Models/CloudPrintResponse.py | 4 ++-- 13 files changed, 58 insertions(+), 30 deletions(-) create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Models/BaseCloudModel.py diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 108fb0040c..321c40bc74 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -1,6 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import os +from datetime import datetime from time import time from typing import Dict, List, Optional @@ -116,6 +117,10 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # TODO: handle progress messages in another class. self._progress_message = None # type: Optional[Message] + # Keep server string of the last generated time to avoid updating models more than once for the same response + self._received_printers = None # type: Optional[List[CloudClusterPrinter]] + self._received_print_jobs = None # type: Optional[List[CloudClusterPrintJob]] + ## Gets the host name of this device @property def host_name(self) -> str: @@ -188,8 +193,13 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # Contains both printers and print jobs statuses in a single response. def _onStatusCallFinished(self, status: CloudClusterStatus) -> None: # Update all data from the cluster. - self._updatePrinters(status.printers) - self._updatePrintJobs(status.print_jobs) + if self._received_printers != status.printers: + self._received_printers = status.printers + self._updatePrinters(status.printers) + + if status.print_jobs != self._received_print_jobs: + self._received_print_jobs = status.print_jobs + self._updatePrintJobs(status.print_jobs) ## Updates the local list of printers with the list received from the cloud. # \param jobs: The printers received from the cloud. @@ -214,7 +224,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): if not self._active_printer: self.setActivePrinter(self._printers[0]) - self.printersChanged.emit() + self.printersChanged.emit() # TODO: Make this more efficient by not updating every request ## Updates the local list of print jobs with the list received from the cloud. # \param jobs: The print jobs received from the cloud. @@ -224,11 +234,6 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): removed_jobs, added_jobs, updated_jobs = findChanges(previous, received) - # TODO: we see that not all data in the UI is correctly updated when the queue and active jobs change. - # TODO: we need to fix this here somehow by updating the correct output models. - # TODO: the configuration drop down in the slice window is not populated because we are missing some data. - # TODO: to fix this we need to implement more data as shown in ClusterUM3OutputDevice._createPrintJobModel - for removed_job in removed_jobs: self._print_jobs.remove(removed_job) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/BaseCloudModel.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/BaseCloudModel.py new file mode 100644 index 0000000000..1176c4374a --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/BaseCloudModel.py @@ -0,0 +1,17 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from datetime import datetime, timezone + +from ...Models import BaseModel + + +class BaseCloudModel(BaseModel): + def __eq__(self, other): + return type(self) == type(other) and self.__dict__ == other.__dict__ + + def __ne__(self, other): + return type(self) != type(other) or self.__dict__ != other.__dict__ + + @staticmethod + def parseDate(date_str: str) -> datetime: + return datetime.strptime(date_str, "%Y-%m-%dT%H:%M:%S.%fZ").replace(tzinfo=timezone.utc) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudCluster.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudCluster.py index 28e95a097a..e6e2af1466 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudCluster.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudCluster.py @@ -1,10 +1,10 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from ...Models import BaseModel +from .BaseCloudModel import BaseCloudModel ## Class representing a cloud connected cluster. -class CloudCluster(BaseModel): +class CloudCluster(BaseCloudModel): def __init__(self, **kwargs): self.cluster_id = None # type: str self.host_guid = None # type: str diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py index 22c66ddfab..15d256e7d5 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py @@ -6,14 +6,14 @@ from cura.PrinterOutput.ConfigurationModel import ConfigurationModel from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputController import CloudOutputController from .CloudClusterPrinterConfiguration import CloudClusterPrinterConfiguration from .CloudClusterPrintJobConstraint import CloudClusterPrintJobConstraint -from ...Models import BaseModel +from .BaseCloudModel import BaseCloudModel ## Class representing a print job from plugins.UM3NetworkPrinting.src.UM3PrintJobOutputModel import UM3PrintJobOutputModel -class CloudClusterPrintJob(BaseModel): +class CloudClusterPrintJob(BaseCloudModel): def __init__(self, **kwargs) -> None: self.assigned_to = None # type: str self.configuration = [] # type: List[CloudClusterPrinterConfiguration] diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobConstraint.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobConstraint.py index 884ff8f0c2..f13e3098fc 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobConstraint.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobConstraint.py @@ -1,10 +1,10 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from ...Models import BaseModel +from .BaseCloudModel import BaseCloudModel ## Class representing a cloud cluster print job constraint -class CloudClusterPrintJobConstraint(BaseModel): +class CloudClusterPrintJobConstraint(BaseCloudModel): def __init__(self, **kwargs) -> None: self.require_printer_name = None # type: str super().__init__(**kwargs) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinter.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinter.py index 78aa8e3a31..9057743621 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinter.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinter.py @@ -6,11 +6,11 @@ from cura.PrinterOutput.ConfigurationModel import ConfigurationModel from cura.PrinterOutput.PrinterOutputController import PrinterOutputController from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from .CloudClusterPrinterConfiguration import CloudClusterPrinterConfiguration -from ...Models import BaseModel +from .BaseCloudModel import BaseCloudModel ## Class representing a cluster printer -class CloudClusterPrinter(BaseModel): +class CloudClusterPrinter(BaseCloudModel): def __init__(self, **kwargs) -> None: self.configuration = [] # type: List[CloudClusterPrinterConfiguration] self.enabled = None # type: str diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfiguration.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfiguration.py index d60395f6ab..aa382136d0 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfiguration.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfiguration.py @@ -6,11 +6,11 @@ from cura.PrinterOutput.ConfigurationModel import ConfigurationModel from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationModel from cura.PrinterOutput.ExtruderOutputModel import ExtruderOutputModel from .CloudClusterPrinterConfigurationMaterial import CloudClusterPrinterConfigurationMaterial -from ...Models import BaseModel +from .BaseCloudModel import BaseCloudModel ## Class representing a cloud cluster printer configuration -class CloudClusterPrinterConfiguration(BaseModel): +class CloudClusterPrinterConfiguration(BaseCloudModel): def __init__(self, **kwargs) -> None: self.extruder_index = None # type: int self.material = None # type: CloudClusterPrinterConfigurationMaterial diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfigurationMaterial.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfigurationMaterial.py index 8023784925..e5f52ac630 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfigurationMaterial.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfigurationMaterial.py @@ -1,11 +1,11 @@ from UM.Logger import Logger from cura.CuraApplication import CuraApplication from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel -from ...Models import BaseModel +from .BaseCloudModel import BaseCloudModel ## Class representing a cloud cluster printer configuration -class CloudClusterPrinterConfigurationMaterial(BaseModel): +class CloudClusterPrinterConfigurationMaterial(BaseCloudModel): def __init__(self, **kwargs) -> None: self.guid = None # type: str self.brand = None # type: str diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterStatus.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterStatus.py index a44b665973..77ed979dbc 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterStatus.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterStatus.py @@ -1,15 +1,17 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from datetime import datetime from typing import List from .CloudClusterPrinter import CloudClusterPrinter from .CloudClusterPrintJob import CloudClusterPrintJob -from ...Models import BaseModel +from .BaseCloudModel import BaseCloudModel # Model that represents the status of the cluster for the cloud -class CloudClusterStatus(BaseModel): +class CloudClusterStatus(BaseCloudModel): def __init__(self, **kwargs) -> None: + self.generated_time = None # type: datetime # a list of the printers self.printers = [] # type: List[CloudClusterPrinter] # a list of the print jobs @@ -20,3 +22,7 @@ class CloudClusterStatus(BaseModel): # converting any dictionaries into models self.printers = [CloudClusterPrinter(**p) if isinstance(p, dict) else p for p in self.printers] self.print_jobs = [CloudClusterPrintJob(**j) if isinstance(j, dict) else j for j in self.print_jobs] + + # converting generated time into datetime + if isinstance(self.generated_time, str): + self.generated_time = self.parseDate(self.generated_time) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudErrorObject.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudErrorObject.py index 813ef957e4..9696cbcb7a 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudErrorObject.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudErrorObject.py @@ -2,11 +2,11 @@ # Cura is released under the terms of the LGPLv3 or higher. from typing import Dict -from ...Models import BaseModel +from .BaseCloudModel import BaseCloudModel ## Class representing errors generated by the cloud servers, according to the json-api standard. -class CloudErrorObject(BaseModel): +class CloudErrorObject(BaseCloudModel): def __init__(self, **kwargs) -> None: self.id = None # type: str self.code = None # type: str diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudJobResponse.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudJobResponse.py index 0b611dd2d3..e3161449a5 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudJobResponse.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudJobResponse.py @@ -1,10 +1,10 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from ...Models import BaseModel +from .BaseCloudModel import BaseCloudModel # Model that represents the response received from the cloud after requesting to upload a print job -class CloudJobResponse(BaseModel): +class CloudJobResponse(BaseCloudModel): def __init__(self, **kwargs) -> None: self.download_url = None # type: str self.job_id = None # type: str diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudJobUploadRequest.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudJobUploadRequest.py index 3e038b343e..07a781e2d6 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudJobUploadRequest.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudJobUploadRequest.py @@ -1,10 +1,10 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from ...Models import BaseModel +from .BaseCloudModel import BaseCloudModel # Model that represents the request to upload a print job to the cloud -class CloudJobUploadRequest(BaseModel): +class CloudJobUploadRequest(BaseCloudModel): def __init__(self, **kwargs) -> None: self.file_size = None # type: int self.job_name = None # type: str diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudPrintResponse.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudPrintResponse.py index ff05ad5b19..3e9ad584dc 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudPrintResponse.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudPrintResponse.py @@ -1,10 +1,10 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from ...Models import BaseModel +from .BaseCloudModel import BaseCloudModel # Model that represents the responses received from the cloud after requesting a job to be printed. -class CloudPrintResponse(BaseModel): +class CloudPrintResponse(BaseCloudModel): def __init__(self, **kwargs) -> None: self.cluster_job_id = None # type: str self.job_id = None # type: str From 26be65e405f42ea806c10501a8c09793fe58a896 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 7 Dec 2018 13:29:16 +0100 Subject: [PATCH 0740/1240] Fix some other merge conflicts Contributes to CURA-6008. --- resources/qml/ExpandableComponent.qml | 3 +- resources/qml/ExpandablePopup.qml | 2 +- resources/qml/JobSpecs.qml | 1 - resources/themes/cura-light/theme.json | 81 ++++++++++++++------------ 4 files changed, 47 insertions(+), 40 deletions(-) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 3f30f8f386..3a03740251 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -109,10 +109,9 @@ Item } source: UM.Theme.getIcon("pencil") visible: source != "" && base.enabled -// color: UM.Theme.getColor("small_button_text") width: UM.Theme.getSize("standard_arrow").width height: UM.Theme.getSize("standard_arrow").height - color: UM.Theme.getColor("text") + color: UM.Theme.getColor("small_button_text") } MouseArea diff --git a/resources/qml/ExpandablePopup.qml b/resources/qml/ExpandablePopup.qml index a1e2fb0fea..c15310f803 100644 --- a/resources/qml/ExpandablePopup.qml +++ b/resources/qml/ExpandablePopup.qml @@ -131,7 +131,7 @@ Item visible: source != "" && base.enabled width: UM.Theme.getSize("standard_arrow").width height: UM.Theme.getSize("standard_arrow").height - color: UM.Theme.getColor("text") + color: UM.Theme.getColor("small_button_text") } MouseArea diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index 8b06ab06db..c7f82b8876 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -80,7 +80,6 @@ Item height: UM.Theme.getSize("jobspecs_line").height width: Math.max(__contentWidth + UM.Theme.getSize("default_margin").width, 50) maximumLength: 120 - property int unremovableSpacing: 5 text: PrintInformation.jobName horizontalAlignment: TextInput.AlignLeft diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index f96a785f11..16d13b9652 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -35,7 +35,7 @@ "family": "Noto Sans" }, "default_italic": { - "size": 1.15, + "size": 1.0, "weight": 50, "italic": true, "family": "Noto Sans" @@ -80,7 +80,7 @@ "primary_hover": [48, 182, 231, 255], "primary_text": [255, 255, 255, 255], "border": [127, 127, 127, 255], - "secondary": [240, 240, 240, 255], + "secondary": [245, 245, 245, 255], "secondary_shadow": [216, 216, 216, 255], "primary_button": [38, 113, 231, 255], @@ -178,34 +178,34 @@ "action_button_disabled_shadow": [228, 228, 228, 255], "scrollbar_background": [255, 255, 255, 255], - "scrollbar_handle": [31, 36, 39, 255], - "scrollbar_handle_hover": [12, 159, 227, 255], - "scrollbar_handle_down": [12, 159, 227, 255], + "scrollbar_handle": [10, 8, 80, 255], + "scrollbar_handle_hover": [50, 130, 255, 255], + "scrollbar_handle_down": [50, 130, 255, 255], "setting_category": [245, 245, 245, 255], "setting_category_disabled": [255, 255, 255, 255], - "setting_category_hover": [245, 245, 245, 255], + "setting_category_hover": [232, 242, 252, 255], "setting_category_active": [245, 245, 245, 255], - "setting_category_active_hover": [245, 245, 245, 255], - "setting_category_text": [31, 36, 39, 255], + "setting_category_active_hover": [232, 242, 252, 255], + "setting_category_text": [35, 35, 35, 255], "setting_category_disabled_text": [24, 41, 77, 101], - "setting_category_hover_text": [31, 36, 39, 255], - "setting_category_active_text": [31, 36, 39, 255], - "setting_category_active_hover_text": [31, 36, 39, 255], + "setting_category_hover_text": [35, 35, 35, 255], + "setting_category_active_text": [35, 35, 35, 255], + "setting_category_active_hover_text": [35, 35, 35, 255], "setting_category_border": [245, 245, 245, 255], "setting_category_disabled_border": [245, 245, 245, 255], - "setting_category_hover_border": [12, 159, 227, 255], - "setting_category_active_border": [245, 245, 245, 255], - "setting_category_active_hover_border": [12, 159, 227, 255], + "setting_category_hover_border": [50, 130, 255, 255], + "setting_category_active_border": [50, 130, 255, 255], + "setting_category_active_hover_border": [50, 130, 255, 255], "setting_control": [255, 255, 255, 255], "setting_control_selected": [31, 36, 39, 255], "setting_control_highlight": [255, 255, 255, 255], - "setting_control_border": [127, 127, 127, 255], + "setting_control_border": [199, 199, 199, 255], "setting_control_border_highlight": [50, 130, 255, 255], - "setting_control_text": [27, 27, 27, 255], - "setting_control_depth_line": [127, 127, 127, 255], - "setting_control_button": [127, 127, 127, 255], + "setting_control_text": [35, 35, 35, 255], + "setting_control_depth_line": [199, 199, 199, 255], + "setting_control_button": [199, 199, 199, 255], "setting_control_button_hover": [70, 84, 113, 255], "setting_control_disabled": [245, 245, 245, 255], "setting_control_disabled_text": [127, 127, 127, 255], @@ -216,6 +216,7 @@ "setting_validation_warning_background": [255, 145, 62, 255], "setting_validation_warning": [127, 127, 127, 255], "setting_validation_ok": [255, 255, 255, 255], + "setting_filter_field" : [153, 153, 153, 255], "material_compatibility_warning": [0, 0, 0, 255], @@ -233,11 +234,11 @@ "checkbox": [255, 255, 255, 255], "checkbox_hover": [255, 255, 255, 255], - "checkbox_border": [64, 69, 72, 255], + "checkbox_border": [199, 199, 199, 255], "checkbox_border_hover": [50, 130, 255, 255], - "checkbox_mark": [119, 122, 124, 255], + "checkbox_mark": [50, 130, 255, 255], "checkbox_disabled": [223, 223, 223, 255], - "checkbox_text": [27, 27, 27, 255], + "checkbox_text": [35, 35, 35, 255], "tooltip": [68, 192, 255, 255], "tooltip_text": [255, 255, 255, 255], @@ -293,16 +294,16 @@ "xray_error": [255, 0, 0, 255], "layerview_ghost": [32, 32, 32, 96], - "layerview_none": [255, 255, 255, 255], - "layerview_inset_0": [255, 0, 0, 255], - "layerview_inset_x": [0, 255, 0, 255], - "layerview_skin": [255, 255, 0, 255], - "layerview_support": [0, 255, 255, 255], - "layerview_skirt": [0, 255, 255, 255], - "layerview_infill": [255, 192, 0, 255], - "layerview_support_infill": [0, 255, 255, 255], - "layerview_move_combing": [0, 0, 255, 255], - "layerview_move_retraction": [128, 128, 255, 255], + "layerview_none": [255, 255, 255, 255], + "layerview_inset_0": [255, 0, 0, 255], + "layerview_inset_x": [0, 255, 0, 255], + "layerview_skin": [255, 255, 0, 255], + "layerview_support": [0, 255, 255, 255], + "layerview_skirt": [0, 255, 255, 255], + "layerview_infill": [255, 192, 0, 255], + "layerview_support_infill": [0, 255, 255, 255], + "layerview_move_combing": [0, 0, 255, 255], + "layerview_move_retraction": [128, 128, 255, 255], "layerview_support_interface": [64, 192, 255, 255], "layerview_nozzle": [181, 166, 66, 50], @@ -356,10 +357,16 @@ "account_button": [12, 3], - "print_setup_widget": [30.0, 42.0], + "print_setup_widget": [38.0, 30.0], "print_setup_mode_toggle": [0.0, 2.0], "print_setup_item": [0.0, 2.0], "print_setup_extruder_box": [0.0, 6.0], + "print_setup_widget_header": [0.0, 3.0], + "print_setup_slider_groove": [0.16, 0.16], + "print_setup_slider_handle": [1.0, 1.0], + "print_setup_slider_tickmarks": [0.32, 0.32], + "print_setup_big_item": [28, 2.5], + "print_setup_icon": [1.2, 1.2], "configuration_selector_mode_tabs": [0.0, 3.0], @@ -391,12 +398,13 @@ "extruder_icon": [2.33, 2.33], - "section": [0.0, 2.2], + "section": [0.0, 2], "section_icon": [1.6, 1.6], "section_icon_column": [2.8, 0.0], "setting": [25.0, 1.8], - "setting_control": [10.0, 2.0], + "setting_control": [11.0, 2.0], + "setting_control_radius": [0.15, 0.15], "setting_control_depth_margin": [1.4, 0.0], "setting_preferences_button_margin": [4, 0.0], "setting_control_margin": [0.0, 0.0], @@ -404,7 +412,7 @@ "setting_text_maxwidth": [40.0, 0.0], "standard_list_lineheight": [1.5, 1.5], - "standard_arrow": [0.8, 0.8], + "standard_arrow": [1.0, 1.0], "button": [4, 4], "button_icon": [2.5, 2.5], @@ -444,7 +452,8 @@ "layerview_row": [11.0, 1.5], "layerview_row_spacing": [0.0, 0.5], - "checkbox": [2.0, 2.0], + "checkbox": [1.5, 1.5], + "checkbox_radius": [0.08, 0.08], "tooltip": [20.0, 10.0], "tooltip_margins": [1.0, 1.0], From e74258c26bd1917d87e5fd4bbb1f77719eb947f8 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 7 Dec 2018 13:39:42 +0100 Subject: [PATCH 0741/1240] Don't show materials if printer has no materials But if the printer does have other configurations to change, do show a placeholder text to indicate that the configuration can be selected here. This also simplifies a bit of code where it would need to call an updateEnabled() function, since it turns out that these properties in Cura.MachineManager have proper signals (contrary to what was previously used, the metadata entry stuff). Contributes to issue UCRA-5876. --- .../ConfigurationMenu/ConfigurationMenu.qml | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index c282975cd3..33a317b42b 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -34,9 +34,11 @@ Cura.ExpandablePopup Custom } + enabled: Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariants || Cura.MachineManager.hasVariantBuildplates; //Only let it drop down if there is any configuration that you could change. + headerItem: Item { - // Horizontal list that shows the extruders + // Horizontal list that shows the extruders and their materials ListView { id: extrudersList @@ -44,7 +46,7 @@ Cura.ExpandablePopup orientation: ListView.Horizontal anchors.fill: parent model: extrudersModel - visible: base.enabled + visible: Cura.MachineManager.hasMaterials delegate: Item { @@ -101,24 +103,26 @@ Cura.ExpandablePopup } } } - } - //Disable the menu if there are no materials, variants or build plates to change. - function updateEnabled() - { - var active_definition_id = Cura.MachineManager.activeMachine.definition.id; - var has_materials = Cura.ContainerManager.getContainerMetaDataEntry(active_definition_id, "has_materials"); - var has_variants = Cura.ContainerManager.getContainerMetaDataEntry(active_definition_id, "has_variants"); - var has_buildplates = Cura.ContainerManager.getContainerMetaDataEntry(active_definition_id, "has_variant_buildplates"); - base.enabled = has_materials || has_variants || has_buildplates; //Only let it drop down if there is any configuration that you could change. - } + //Placeholder text if there is a configuration to select but no materials (so we can't show the materials per extruder). + Label + { + text: catalog.i18nc("@label", "Select configuration") + elide: Text.ElideRight + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + renderType: Text.NativeRendering - Connections - { - target: Cura.MachineManager - onGlobalContainerChanged: base.updateEnabled(); + visible: !Cura.MachineManager.hasMaterials && (Cura.MachineManager.hasVariants || Cura.MachineManager.hasVariantBuildplates) + + anchors + { + left: parent.left + leftMargin: UM.Theme.getSize("default_margin").width + verticalCenter: parent.verticalCenter + } + } } - Component.onCompleted: updateEnabled(); contentItem: Column { From 15f81da95b40e1cb27a73b9f13562bc6f525f296 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 7 Dec 2018 13:47:58 +0100 Subject: [PATCH 0742/1240] Do not show the layer height in the header panel when the profile is not supported --- cura/Settings/MachineManager.py | 4 ++++ .../PrintSetupSelectorHeader.qml | 14 +++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index a472cc7f09..c375ce01d1 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -1536,6 +1536,10 @@ class MachineManager(QObject): name = self._current_quality_group.name return name + @pyqtProperty(bool, notify = activeQualityGroupChanged) + def hasNotSupportedQuality(self) -> bool: + return self._current_quality_group is None and self._current_quality_changes_group is None + def _updateUponMaterialMetadataChange(self) -> None: if self._global_container_stack is None: return diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml index 518f3d49eb..d4287045b8 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml @@ -13,7 +13,19 @@ RowLayout Cura.IconWithText { source: UM.Theme.getIcon("category_layer_height") - text: Cura.MachineManager.activeStack ? Cura.MachineManager.activeQualityOrQualityChangesName + " " + layerHeight.properties.value + "mm" : "" + text: + { + if (Cura.MachineManager.activeStack) + { + var text = Cura.MachineManager.activeQualityOrQualityChangesName + if (!Cura.MachineManager.hasNotSupportedQuality) + { + text += " " + layerHeight.properties.value + "mm" + } + return text + } + return "" + } UM.SettingPropertyProvider { From 43a06fdc3d85e618534297d967c560cdc9783c08 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 7 Dec 2018 13:49:17 +0100 Subject: [PATCH 0743/1240] Add more margin above printer type labels A bit of Gestalt. Contributes to issue CURA-5876. --- .../qml/Menus/ConfigurationMenu/ConfigurationListView.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml index e7936b69d2..53969a0370 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml @@ -64,11 +64,12 @@ Column section.criteria: ViewSection.FullString section.delegate: Item { - height: printerTypeLabel.height + UM.Theme.getSize("default_margin").height + height: printerTypeLabel.height + UM.Theme.getSize("default_margin").height * 2 //Causes a default margin above the label and a default margin below the label. Cura.PrinterTypeLabel { id: printerTypeLabel text: Cura.MachineManager.getAbbreviatedMachineName(section) + anchors.verticalCenter: parent.verticalCenter //One default margin above and one below. } } From c44b74454f927714f891c254024e95679982ef20 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 7 Dec 2018 14:22:39 +0100 Subject: [PATCH 0744/1240] Revert "No longer make BuildVolume set max bounds." This reverts commit 1467e703ae121c3f2fa02ee7258bdeba63f80c5c. This variable was actually used... In Uranium, by the scale tiny models feature. --- cura/BuildVolume.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index 1589f16afc..f837f5cef7 100755 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -479,6 +479,8 @@ class BuildVolume(SceneNode): maximum = Vector(max_w - bed_adhesion_size - 1, max_h - self._raft_thickness - self._extra_z_clearance, max_d - disallowed_area_size + bed_adhesion_size - 1) ) + self._application.getController().getScene()._maximum_bounds = scale_to_max_bounds + self.updateNodeBoundaryCheck() def getBoundingBox(self) -> AxisAlignedBox: From 8e2c130416ecdf1adf21bc8d5388588442f6f6c7 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 7 Dec 2018 14:31:06 +0100 Subject: [PATCH 0745/1240] Use default lining style as separator for buildplate Normal colour, normal thickness. Contributes to issue CURA-5876. --- resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 6ac1e6a2ad..05cac16e29 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -72,8 +72,8 @@ Button right: parent.right rightMargin: parent.padding } - height: visible ? Math.round(UM.Theme.getSize("thick_lining").height / 2) : 0 - color: UM.Theme.getColor("text") + height: visible ? Math.round(UM.Theme.getSize("default_lining").height / 2) : 0 + color: UM.Theme.getColor("lining") } Item From c663681e66a42b63b1e831a1dbf967e6043f563b Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 7 Dec 2018 14:39:43 +0100 Subject: [PATCH 0746/1240] Simplify buildplate icon Removed all unnecessary crap added by Sketch. Contributes to issue CURA-5876. --- .../themes/cura-light/icons/buildplate.svg | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/resources/themes/cura-light/icons/buildplate.svg b/resources/themes/cura-light/icons/buildplate.svg index 9e61296958..d844b91a28 100644 --- a/resources/themes/cura-light/icons/buildplate.svg +++ b/resources/themes/cura-light/icons/buildplate.svg @@ -1,17 +1,9 @@ - - - icn_buildplate - Created with Sketch. - - - - - - - - - - + + + + + + \ No newline at end of file From 78df27369f1cc423fc3e369d22da6115fba43560 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 7 Dec 2018 14:41:30 +0100 Subject: [PATCH 0747/1240] Fill buildplate icon This is according to our newest designs. Contributes to issue CURA-5876. --- resources/themes/cura-light/icons/buildplate.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/themes/cura-light/icons/buildplate.svg b/resources/themes/cura-light/icons/buildplate.svg index d844b91a28..7505c8204e 100644 --- a/resources/themes/cura-light/icons/buildplate.svg +++ b/resources/themes/cura-light/icons/buildplate.svg @@ -4,6 +4,6 @@ - + \ No newline at end of file From ffcac04f9e1bb595bc30a33ba0d9aeb327b7c45d Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 7 Dec 2018 15:21:53 +0100 Subject: [PATCH 0748/1240] Add printer type selector if connected to cluster If there are 2 or more printer types in your connected cluster, you can switch the printer type here. Contributes to issue CURA-5876. --- .../ConfigurationMenu/CustomConfiguration.qml | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index 78f6864c97..8429e2c093 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -36,10 +36,57 @@ Item } } + //Printer type selector. + Item + { + id: printerTypeSelectorRow + visible: + { + return Cura.MachineManager.printerOutputDevices.length >= 1 //If connected... + && Cura.MachineManager.printerOutputDevices[0].connectedPrintersTypeCount != null //...and we have configuration information... + && Cura.MachineManager.printerOutputDevices[0].connectedPrintersTypeCount.length > 1; //...and there is more than one type of printer in the configuration list. + } + height: visible ? childrenRect.height : 0 + + anchors + { + left: parent.left + leftMargin: UM.Theme.getSize("default_margin").width + right: parent.right + rightMargin: UM.Theme.getSize("default_margin").width + top: header.bottom + topMargin: visible ? UM.Theme.getSize("default_margin").height : 0 + } + + Label + { + text: catalog.i18nc("@label", "Printer") + width: Math.round(parent.width * 0.3) - UM.Theme.getSize("default_margin").width + height: contentHeight + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + anchors.verticalCenter: printerTypeSelector.verticalCenter + anchors.left: parent.left + } + + OldControls.ToolButton + { + id: printerTypeSelector + text: Cura.MachineManager.activeMachineDefinitionName + tooltip: Cura.MachineManager.activeMachineDefinitionName + height: UM.Theme.getSize("setting_control").height + width: Math.round(parent.width * 0.7) + UM.Theme.getSize("default_margin").width + anchors.right: parent.right + style: UM.Theme.styles.print_setup_header_button + + menu: Cura.PrinterTypeMenu { } + } + } + UM.TabRow { id: tabBar - anchors.top: header.bottom + anchors.top: printerTypeSelectorRow.bottom anchors.topMargin: UM.Theme.getSize("default_margin").height visible: extrudersModel.count > 1 From 6a7bbe5bdb46c3f6db301097d30e472f3b45702e Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 7 Dec 2018 15:31:12 +0100 Subject: [PATCH 0749/1240] Fix hiding materials if material is empty Because the loaded material profile has the name 'Empty' then. Contributes to issue CURA-5876. --- .../qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml index 885f02d740..a344e31d4f 100644 --- a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml @@ -20,7 +20,7 @@ Row { materialColor: printCoreConfiguration.material.color anchors.verticalCenter: parent.verticalCenter - extruderEnabled: printCoreConfiguration.material.name !== "" && printCoreConfiguration.hotendID !== "" + extruderEnabled: printCoreConfiguration.material.brand !== "" && printCoreConfiguration.hotendID !== "" } Column @@ -36,7 +36,7 @@ Row } Label { - text: printCoreConfiguration.material.name ? printCoreConfiguration.material.name : " " //Use space so that the height is still correct. + text: printCoreConfiguration.material.brand ? printCoreConfiguration.material.name : " " //Use space so that the height is still correct. renderType: Text.NativeRendering elide: Text.ElideRight font: UM.Theme.getFont("default") From f4220da550ff4f5c65787b54f413415ac4dce73e Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 7 Dec 2018 15:31:33 +0100 Subject: [PATCH 0750/1240] Adds rough first version of rating stars It's not fully polished just yet CURA-6013 --- cura/API/Account.py | 2 +- .../Toolbox/resources/qml/RatingWidget.qml | 101 ++++++++++++++++++ .../qml/ToolboxDownloadsGridTile.qml | 10 ++ plugins/Toolbox/src/PackagesModel.py | 8 +- plugins/Toolbox/src/Toolbox.py | 53 ++++++--- .../themes/cura-light/icons/star_empty.svg | 11 ++ .../themes/cura-light/icons/star_filled.svg | 11 ++ resources/themes/cura-light/theme.json | 1 + 8 files changed, 180 insertions(+), 17 deletions(-) create mode 100644 plugins/Toolbox/resources/qml/RatingWidget.qml create mode 100644 resources/themes/cura-light/icons/star_empty.svg create mode 100644 resources/themes/cura-light/icons/star_filled.svg diff --git a/cura/API/Account.py b/cura/API/Account.py index 397e220478..be77a6307b 100644 --- a/cura/API/Account.py +++ b/cura/API/Account.py @@ -44,7 +44,7 @@ class Account(QObject): OAUTH_SERVER_URL= self._oauth_root, CALLBACK_PORT=self._callback_port, CALLBACK_URL="http://localhost:{}/callback".format(self._callback_port), - CLIENT_ID="um---------------ultimaker_cura_drive_plugin", + CLIENT_ID="um----------------------------ultimaker_cura", CLIENT_SCOPES="account.user.read drive.backup.read drive.backup.write packages.download packages.rating.read packages.rating.write", AUTH_DATA_PREFERENCE_KEY="general/ultimaker_auth_data", AUTH_SUCCESS_REDIRECT="{}/app/auth-success".format(self._oauth_root), diff --git a/plugins/Toolbox/resources/qml/RatingWidget.qml b/plugins/Toolbox/resources/qml/RatingWidget.qml new file mode 100644 index 0000000000..424f6c91c4 --- /dev/null +++ b/plugins/Toolbox/resources/qml/RatingWidget.qml @@ -0,0 +1,101 @@ +import QtQuick 2.2 +import QtQuick.Controls 2.0 +import UM 1.0 as UM + +Item +{ + id: ratingWidget + + property real rating: 0 + property int indexHovered: -1 + property string packageId: "" + property int numRatings: 0 + property int userRating: 0 + width: contentRow.width + height: contentRow.height + MouseArea + { + id: mouseArea + anchors.fill: parent + hoverEnabled: ratingWidget.enabled + acceptedButtons: Qt.NoButton + onExited: + { + ratingWidget.indexHovered = -1 + } + + Row + { + id: contentRow + height: childrenRect.height + Repeater + { + model: 5 // We need to get 5 stars + Button + { + id: control + hoverEnabled: true + onHoveredChanged: + { + if(hovered) + { + indexHovered = index + } + } + + property bool isStarFilled: + { + // If the entire widget is hovered, override the actual rating. + if(ratingWidget.indexHovered >= 0) + { + return indexHovered >= index + } + + if(ratingWidget.userRating > 0) + { + return userRating >= index +1 + } + + return rating >= index + 1 + } + + contentItem: Item {} + height: UM.Theme.getSize("rating_star").height + width: UM.Theme.getSize("rating_star").width + background: UM.RecolorImage + { + source: UM.Theme.getIcon(control.isStarFilled ? "star_filled" : "star_empty") + + // Unfilled stars should always have the default color. Only filled stars should change on hover + color: + { + if(!enabled) + { + return "#5a5a5a" + } + if((ratingWidget.indexHovered >= 0 || ratingWidget.userRating > 0) && isStarFilled) + { + return UM.Theme.getColor("primary") + } + return "#5a5a5a" + } + } + onClicked: + { + if(userRating == 0) + { + //User didn't vote yet, locally fake it + numRatings += 1 + } + userRating = index + 1 // Fake local data + toolbox.ratePackage(ratingWidget.packageId, index + 1) + } + } + } + Label + { + text: "(" + numRatings + ")" + } + } + } +} \ No newline at end of file diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml index cee3f0fd20..a72411ef4b 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml @@ -84,6 +84,16 @@ Item color: UM.Theme.getColor("text_medium") font: UM.Theme.getFont("default") } + + RatingWidget + { + visible: model.type == "plugin" + packageId: model.id + rating: model.average_rating != undefined ? model.average_rating : 0 + numRatings: model.num_ratings != undefined ? model.num_ratings : 0 + userRating: model.user_rating + enabled: installedPackages != 0 + } } } MouseArea diff --git a/plugins/Toolbox/src/PackagesModel.py b/plugins/Toolbox/src/PackagesModel.py index bcc02955a2..b3c388bc7c 100644 --- a/plugins/Toolbox/src/PackagesModel.py +++ b/plugins/Toolbox/src/PackagesModel.py @@ -41,6 +41,9 @@ class PackagesModel(ListModel): self.addRoleName(Qt.UserRole + 20, "links") self.addRoleName(Qt.UserRole + 21, "website") self.addRoleName(Qt.UserRole + 22, "login_required") + self.addRoleName(Qt.UserRole + 23, "average_rating") + self.addRoleName(Qt.UserRole + 24, "num_ratings") + self.addRoleName(Qt.UserRole + 25, "user_rating") # List of filters for queries. The result is the union of the each list of results. self._filter = {} # type: Dict[str, str] @@ -101,7 +104,10 @@ class PackagesModel(ListModel): "tags": package["tags"] if "tags" in package else [], "links": links_dict, "website": package["website"] if "website" in package else None, - "login_required": "login-required" in package.get("tags", []) + "login_required": "login-required" in package.get("tags", []), + "average_rating": package.get("rating", {}).get("average", 0), + "num_ratings": package.get("rating", {}).get("count", 0), + "user_rating": package.get("rating", {}).get("user", 0) }) # Filter on all the key-word arguments. diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index ab975548ce..7e35f5d1f4 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -22,7 +22,8 @@ from cura.CuraApplication import CuraApplication from .AuthorsModel import AuthorsModel from .PackagesModel import PackagesModel - +from cura.CuraVersion import CuraVersion +from cura.API import CuraAPI if TYPE_CHECKING: from cura.Settings.GlobalStack import GlobalStack @@ -50,17 +51,10 @@ class Toolbox(QObject, Extension): self._download_progress = 0 # type: float self._is_downloading = False # type: bool self._network_manager = None # type: Optional[QNetworkAccessManager] - self._request_header = [ - b"User-Agent", - str.encode( - "%s/%s (%s %s)" % ( - self._application.getApplicationName(), - self._application.getVersion(), - platform.system(), - platform.machine(), - ) - ) - ] + self._request_headers = [] # type: List[Tuple(bytes, bytes)] + self._updateRequestHeader() + + self._request_urls = {} # type: Dict[str, QUrl] self._to_update = [] # type: List[str] # Package_ids that are waiting to be updated self._old_plugin_ids = set() # type: Set[str] @@ -115,6 +109,7 @@ class Toolbox(QObject, Extension): self._restart_dialog_message = "" # type: str self._application.initializationFinished.connect(self._onAppInitialized) + self._application.getCuraAPI().account.loginStateChanged.connect(self._updateRequestHeader) # Signals: # -------------------------------------------------------------------------- @@ -134,12 +129,38 @@ class Toolbox(QObject, Extension): showLicenseDialog = pyqtSignal() uninstallVariablesChanged = pyqtSignal() + def _updateRequestHeader(self): + self._request_headers = [ + (b"User-Agent", + str.encode( + "%s/%s (%s %s)" % ( + self._application.getApplicationName(), + self._application.getVersion(), + platform.system(), + platform.machine(), + ) + )) + ] + access_token = self._application.getCuraAPI().account.accessToken + if access_token: + self._request_headers.append((b"Authorization", "Bearer {}".format(access_token).encode())) + def _resetUninstallVariables(self) -> None: self._package_id_to_uninstall = None # type: Optional[str] self._package_name_to_uninstall = "" self._package_used_materials = [] # type: List[Tuple[GlobalStack, str, str]] self._package_used_qualities = [] # type: List[Tuple[GlobalStack, str, str]] + @pyqtSlot(str, int) + def ratePackage(self, package_id: str, rating: int) -> None: + url = QUrl("{base_url}/packages/{package_id}/ratings".format(base_url=self._api_url, package_id = package_id)) + + self._rate_request = QNetworkRequest(url) + for header_name, header_value in self._request_headers: + cast(QNetworkRequest, self._rate_request).setRawHeader(header_name, header_value) + data = "{\"data\": {\"cura_version\": \"%s\", \"rating\": %i}}" % (Version(CuraVersion), rating) + self._rate_reply = cast(QNetworkAccessManager, self._network_manager).put(self._rate_request, data.encode()) + @pyqtSlot(result = str) def getLicenseDialogPluginName(self) -> str: return self._license_dialog_plugin_name @@ -563,7 +584,8 @@ class Toolbox(QObject, Extension): def _makeRequestByType(self, request_type: str) -> None: Logger.log("i", "Requesting %s metadata from server.", request_type) request = QNetworkRequest(self._request_urls[request_type]) - request.setRawHeader(*self._request_header) + for header_name, header_value in self._request_headers: + request.setRawHeader(header_name, header_value) if self._network_manager: self._network_manager.get(request) @@ -578,7 +600,8 @@ class Toolbox(QObject, Extension): if hasattr(QNetworkRequest, "RedirectPolicyAttribute"): # Patch for Qt 5.9+ cast(QNetworkRequest, self._download_request).setAttribute(QNetworkRequest.RedirectPolicyAttribute, True) - cast(QNetworkRequest, self._download_request).setRawHeader(*self._request_header) + for header_name, header_value in self._request_headers: + cast(QNetworkRequest, self._download_request).setRawHeader(header_name, header_value) self._download_reply = cast(QNetworkAccessManager, self._network_manager).get(self._download_request) self.setDownloadProgress(0) self.setIsDownloading(True) @@ -660,7 +683,7 @@ class Toolbox(QObject, Extension): else: self.setViewPage("errored") self.resetDownload() - else: + elif reply.operation() == QNetworkAccessManager.PutOperation: # Ignore any operation that is not a get operation pass diff --git a/resources/themes/cura-light/icons/star_empty.svg b/resources/themes/cura-light/icons/star_empty.svg new file mode 100644 index 0000000000..39b5791e91 --- /dev/null +++ b/resources/themes/cura-light/icons/star_empty.svg @@ -0,0 +1,11 @@ + + + + Star Copy 8 + Created with Sketch. + + + + + + \ No newline at end of file diff --git a/resources/themes/cura-light/icons/star_filled.svg b/resources/themes/cura-light/icons/star_filled.svg new file mode 100644 index 0000000000..d4e161f6c6 --- /dev/null +++ b/resources/themes/cura-light/icons/star_filled.svg @@ -0,0 +1,11 @@ + + + + Star Copy 7 + Created with Sketch. + + + + + + \ No newline at end of file diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 2d7e92be4d..8d8f5dd718 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -394,6 +394,7 @@ "section": [0.0, 2.2], "section_icon": [1.6, 1.6], "section_icon_column": [2.8, 0.0], + "rating_star": [1.0, 1.0], "setting": [25.0, 1.8], "setting_control": [10.0, 2.0], From 8b25e6946adb3ea763fc25f22c3226e557d4c8bd Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 7 Dec 2018 15:33:44 +0100 Subject: [PATCH 0751/1240] Prevent attempting to rate when not logged in CURA-6013 --- plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml index a72411ef4b..283d9bc0aa 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml @@ -6,6 +6,7 @@ import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import QtQuick.Layouts 1.3 import UM 1.1 as UM +import Cura 1.1 as Cura Item { @@ -92,7 +93,7 @@ Item rating: model.average_rating != undefined ? model.average_rating : 0 numRatings: model.num_ratings != undefined ? model.num_ratings : 0 userRating: model.user_rating - enabled: installedPackages != 0 + enabled: installedPackages != 0 && Cura.API.account.isLoggedIn } } } From b565b111c873dd3804f2ce50193c5836850c7fe9 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 7 Dec 2018 15:38:08 +0100 Subject: [PATCH 0752/1240] Adjust disabled extruder colour This also introduces a more global 'disabled' colour as defined by the style guide. I hope that we can gradually transition to this one, but we'll have to adjust some colours here and there. Contributes to issue CURA-5876. --- resources/qml/ExtruderIcon.qml | 2 +- resources/themes/cura-dark/theme.json | 1 + resources/themes/cura-light/theme.json | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/qml/ExtruderIcon.qml b/resources/qml/ExtruderIcon.qml index 49ad73a32e..bb0b347b7e 100644 --- a/resources/qml/ExtruderIcon.qml +++ b/resources/qml/ExtruderIcon.qml @@ -23,7 +23,7 @@ Item anchors.fill: parent source: UM.Theme.getIcon("extruder_button") - color: extruderEnabled ? materialColor: "gray" + color: extruderEnabled ? materialColor: UM.Theme.getColor("disabled") } Rectangle diff --git a/resources/themes/cura-dark/theme.json b/resources/themes/cura-dark/theme.json index d9ef74ebb9..8540abf61a 100644 --- a/resources/themes/cura-dark/theme.json +++ b/resources/themes/cura-dark/theme.json @@ -35,6 +35,7 @@ "text_scene_hover": [255, 255, 255, 204], "error": [212, 31, 53, 255], + "disabled": [32, 32, 32, 255], "button": [39, 44, 48, 255], "button_hover": [39, 44, 48, 255], diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 16d13b9652..d4fb59b7a1 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -129,6 +129,7 @@ "error": [255, 140, 0, 255], "warning": [245, 166, 35, 255], + "disabled": [229, 229, 229, 255], "toolbar_button_text": [8, 7, 63, 255], "toolbar_button_hover": [232, 242, 252, 255], From fed9e2b623fcc2085a8343c479946cc4edc43c2c Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 7 Dec 2018 15:42:46 +0100 Subject: [PATCH 0753/1240] Move the click area of the download tile to just the icon CURA-6013 --- .../qml/ToolboxDownloadsGridTile.qml | 81 +++++++++---------- 1 file changed, 37 insertions(+), 44 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml index 283d9bc0aa..472f273817 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml @@ -58,13 +58,49 @@ Item color: (installedPackages == packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border") source: "../images/installed_check.svg" } + + MouseArea + { + anchors.fill: thumbnail + hoverEnabled: true + onEntered: thumbnail.border.color = UM.Theme.getColor("primary") + onExited: thumbnail.border.color = UM.Theme.getColor("lining") + onClicked: + { + base.selection = model + switch(toolbox.viewCategory) + { + case "material": + + // If model has a type, it must be a package + if (model.type !== undefined) + { + toolbox.viewPage = "detail" + toolbox.filterModelByProp("packages", "id", model.id) + } + else + { + toolbox.viewPage = "author" + toolbox.setFilters("packages", { + "author_id": model.id, + "type": "material" + }) + } + break + default: + toolbox.viewPage = "detail" + toolbox.filterModelByProp("packages", "id", model.id) + break + } + } + } } Column { width: parent.width - thumbnail.width - parent.spacing spacing: Math.floor(UM.Theme.getSize("narrow_margin").width) anchors.top: parent.top - anchors.topMargin: UM.Theme.getSize("default_margin").height + //anchors.topMargin: UM.Theme.getSize("default_margin").height Label { id: name @@ -97,47 +133,4 @@ Item } } } - MouseArea - { - anchors.fill: parent - hoverEnabled: true - onEntered: - { - thumbnail.border.color = UM.Theme.getColor("primary") - highlight.opacity = 0.1 - } - onExited: - { - thumbnail.border.color = UM.Theme.getColor("lining") - highlight.opacity = 0.0 - } - onClicked: - { - base.selection = model - switch(toolbox.viewCategory) - { - case "material": - - // If model has a type, it must be a package - if (model.type !== undefined) - { - toolbox.viewPage = "detail" - toolbox.filterModelByProp("packages", "id", model.id) - } - else - { - toolbox.viewPage = "author" - toolbox.setFilters("packages", { - "author_id": model.id, - "type": "material" - }) - } - break - default: - toolbox.viewPage = "detail" - toolbox.filterModelByProp("packages", "id", model.id) - break - } - } - } } From 4742db6fec2a82aa5e8d5bc95c764d0ce9d69e0b Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 7 Dec 2018 15:57:52 +0100 Subject: [PATCH 0754/1240] Cleanup gridTile CURA-6013 --- .../qml/ToolboxDownloadsGridTile.qml | 209 +++++++++--------- 1 file changed, 104 insertions(+), 105 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml index 472f273817..7ced23aee9 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml @@ -13,124 +13,123 @@ Item id: toolboxDownloadsGridTile property int packageCount: (toolbox.viewCategory == "material" && model.type === undefined) ? toolbox.getTotalNumberOfMaterialPackagesByAuthor(model.id) : 1 property int installedPackages: (toolbox.viewCategory == "material" && model.type === undefined) ? toolbox.getNumberOfInstalledPackagesByAuthor(model.id) : (toolbox.isInstalled(model.id) ? 1 : 0) - height: childrenRect.height + height: UM.Theme.getSize("toolbox_thumbnail_small").height Layout.alignment: Qt.AlignTop | Qt.AlignLeft + Rectangle { - id: highlight - anchors.fill: parent - opacity: 0.0 - color: UM.Theme.getColor("primary") - } - Row - { - width: parent.width - height: childrenRect.height - spacing: Math.floor(UM.Theme.getSize("narrow_margin").width) - Rectangle + id: thumbnail + width: UM.Theme.getSize("toolbox_thumbnail_small").width + height: UM.Theme.getSize("toolbox_thumbnail_small").height + color: "white" + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") + + Image { - id: thumbnail - width: UM.Theme.getSize("toolbox_thumbnail_small").width - height: UM.Theme.getSize("toolbox_thumbnail_small").height - color: "white" - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("lining") - Image - { - anchors.centerIn: parent - width: UM.Theme.getSize("toolbox_thumbnail_small").width - UM.Theme.getSize("wide_margin").width - height: UM.Theme.getSize("toolbox_thumbnail_small").height - UM.Theme.getSize("wide_margin").width - fillMode: Image.PreserveAspectFit - source: model.icon_url || "../images/logobot.svg" - mipmap: true - } - UM.RecolorImage - { - width: (parent.width * 0.4) | 0 - height: (parent.height * 0.4) | 0 - anchors - { - bottom: parent.bottom - right: parent.right - } - sourceSize.height: height - visible: installedPackages != 0 - color: (installedPackages == packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border") - source: "../images/installed_check.svg" - } + anchors.centerIn: parent + width: UM.Theme.getSize("toolbox_thumbnail_small").width - UM.Theme.getSize("wide_margin").width + height: UM.Theme.getSize("toolbox_thumbnail_small").height - UM.Theme.getSize("wide_margin").width + fillMode: Image.PreserveAspectFit + source: model.icon_url || "../images/logobot.svg" + mipmap: true - MouseArea - { - anchors.fill: thumbnail - hoverEnabled: true - onEntered: thumbnail.border.color = UM.Theme.getColor("primary") - onExited: thumbnail.border.color = UM.Theme.getColor("lining") - onClicked: - { - base.selection = model - switch(toolbox.viewCategory) - { - case "material": - // If model has a type, it must be a package - if (model.type !== undefined) - { - toolbox.viewPage = "detail" - toolbox.filterModelByProp("packages", "id", model.id) - } - else - { - toolbox.viewPage = "author" - toolbox.setFilters("packages", { - "author_id": model.id, - "type": "material" - }) - } - break - default: + } + UM.RecolorImage + { + width: (parent.width * 0.4) | 0 + height: (parent.height * 0.4) | 0 + anchors + { + bottom: parent.bottom + right: parent.right + } + sourceSize.height: height + visible: installedPackages != 0 + color: (installedPackages == packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border") + source: "../images/installed_check.svg" + } + + MouseArea + { + anchors.fill: thumbnail + hoverEnabled: true + onEntered: thumbnail.border.color = UM.Theme.getColor("primary") + onExited: thumbnail.border.color = UM.Theme.getColor("lining") + onClicked: + { + base.selection = model + switch(toolbox.viewCategory) + { + case "material": + + // If model has a type, it must be a package + if (model.type !== undefined) + { toolbox.viewPage = "detail" toolbox.filterModelByProp("packages", "id", model.id) - break - } + } + else + { + toolbox.viewPage = "author" + toolbox.setFilters("packages", { + "author_id": model.id, + "type": "material" + }) + } + break + default: + toolbox.viewPage = "detail" + toolbox.filterModelByProp("packages", "id", model.id) + break } } } - Column + } + Item + { + anchors { - width: parent.width - thumbnail.width - parent.spacing - spacing: Math.floor(UM.Theme.getSize("narrow_margin").width) - anchors.top: parent.top - //anchors.topMargin: UM.Theme.getSize("default_margin").height - Label - { - id: name - text: model.name - width: parent.width - wrapMode: Text.WordWrap - color: UM.Theme.getColor("text") - font: UM.Theme.getFont("default_bold") - } - Label - { - id: info - text: model.description - maximumLineCount: 2 - elide: Text.ElideRight - width: parent.width - wrapMode: Text.WordWrap - color: UM.Theme.getColor("text_medium") - font: UM.Theme.getFont("default") - } + left: thumbnail.right + leftMargin: Math.floor(UM.Theme.getSize("narrow_margin").width) + right: parent.right + top: parent.top + bottom: parent.bottom + } - RatingWidget - { - visible: model.type == "plugin" - packageId: model.id - rating: model.average_rating != undefined ? model.average_rating : 0 - numRatings: model.num_ratings != undefined ? model.num_ratings : 0 - userRating: model.user_rating - enabled: installedPackages != 0 && Cura.API.account.isLoggedIn - } + Label + { + id: name + text: model.name + width: parent.width + elide: Text.ElideRight + color: UM.Theme.getColor("text") + font: UM.Theme.getFont("default_bold") + } + Label + { + id: info + text: model.description + elide: Text.ElideRight + width: parent.width + wrapMode: Text.WordWrap + color: UM.Theme.getColor("text_medium") + font: UM.Theme.getFont("default") + anchors.top: name.bottom + anchors.bottom: rating.top + verticalAlignment: Text.AlignVCenter + } + RatingWidget + { + id: rating + visible: model.type == "plugin" + packageId: model.id + rating: model.average_rating != undefined ? model.average_rating : 0 + numRatings: model.num_ratings != undefined ? model.num_ratings : 0 + userRating: model.user_rating + enabled: installedPackages != 0 && Cura.API.account.isLoggedIn + anchors.bottom: parent.bottom } } } From 50099ab7530da3d307426c4b069ef621fd9654b6 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 7 Dec 2018 16:02:21 +0100 Subject: [PATCH 0755/1240] Make a few missed items themeable as well CURA-6013 --- plugins/Toolbox/resources/qml/ToolboxDetailPage.qml | 2 +- plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml index 9e2e178b71..4e0bf67544 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml @@ -37,7 +37,7 @@ Item leftMargin: UM.Theme.getSize("wide_margin").width topMargin: UM.Theme.getSize("wide_margin").height } - color: "white" //Always a white background for image (regardless of theme). + color: UM.Theme.getColor("main_background") Image { anchors.fill: parent diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml index 7ced23aee9..70edee7449 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml @@ -21,7 +21,7 @@ Item id: thumbnail width: UM.Theme.getSize("toolbox_thumbnail_small").width height: UM.Theme.getSize("toolbox_thumbnail_small").height - color: "white" + color: UM.Theme.getColor("main_background") border.width: UM.Theme.getSize("default_lining").width border.color: UM.Theme.getColor("lining") From 07801b5394ae654a7fbd40c7f7a82b92baaedc1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Fri, 7 Dec 2018 16:21:08 +0100 Subject: [PATCH 0756/1240] First test for CloudDevices --- .../tests/Cloud/TestCloudApiClient.py | 136 ++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py new file mode 100644 index 0000000000..328bb053b7 --- /dev/null +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py @@ -0,0 +1,136 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +import json +from typing import Dict, Tuple +from unittest import TestCase, mock +from unittest.mock import patch, MagicMock + +from PyQt5.QtCore import QByteArray +from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply + +from UM.Application import Application +from UM.Signal import Signal +from cura.CuraApplication import CuraApplication +from plugins.UM3NetworkPrinting.src.Cloud.CloudApiClient import CloudApiClient +from plugins.UM3NetworkPrinting.src.Cloud.Models import CloudCluster, CloudErrorObject + +# This mock application must extend from Application and not QtApplication otherwise some QObjects are created and +# a segfault is raised. +class FixtureApplication(Application): + def __init__(self): + super().__init__(name = "test", version = "1.0", api_version = "5.0.0") + super().initialize() + Signal._signalQueue = self + + def functionEvent(self, event): + event.call() + + def parseCommandLine(self): + pass + + def processEvents(self): + pass + + def getRenderer(self): + return MagicMock() + +class ManagerMock: + finished = Signal() + authenticationRequired = Signal() + + def __init__(self, reply): + self.reply = reply + + def get(self, request): + self.reply.url.return_value = request.url() + + return self.reply + +class ManagerMock2: + finished = Signal() + authenticationRequired = Signal() + + def get(self, request): + reply_mock = MagicMock() + reply_mock.url = request.url + reply_mock.operation.return_value = QNetworkAccessManager.GetOperation + return reply_mock + + @staticmethod + def createReply(method: str, url: str, status_code: int, response: dict): + reply_mock = MagicMock() + reply_mock.url().toString.return_value = url + reply_mock.operation.return_value = { + "GET": QNetworkAccessManager.GetOperation, + "POST": QNetworkAccessManager.PostOperation, + "PUT": QNetworkAccessManager.PutOperation, + "DELETE": QNetworkAccessManager.DeleteOperation, + "HEAD": QNetworkAccessManager.HeadOperation, + }[method] + reply_mock.attribute.return_value = status_code + reply_mock.readAll.return_value = json.dumps(response).encode() + return reply_mock + + +class TestCloudApiClient(TestCase): + + app = CuraApplication.getInstance() or CuraApplication + + def _errorHandler(self, errors: [CloudErrorObject]): + pass + + @patch("cura.NetworkClient.QNetworkAccessManager") + @patch("cura.API.Account") + def test_GetClusters(self, account_mock, manager_mock): + reply_mock = MagicMock() + reply_mock.operation.return_value = 2 + reply_mock.attribute.return_value = 200 + reply_mock.readAll.return_value = b'{"data": [{"cluster_id": "RIZ6cZbWA_Ua7RZVJhrdVfVpf0z-MqaSHQE4v8aRTtYq", "host_guid": "e90ae0ac-1257-4403-91ee-a44c9b7e8050", "host_name": "ultimakersystem-ccbdd30044ec", "host_version": "5.1.2.20180807", "is_online": false, "status": "inactive"}, {"cluster_id": "R0YcLJwar1ugh0ikEZsZs8NWKV6vJP_LdYsXgXqAcaNC", "host_guid": "e90ae0ac-1257-4403-91ee-a44c9b7e8050", "host_name": "ultimakersystem-ccbdd30044ec", "host_version": "5.1.2.20180807", "is_online": true, "status": "active"}]}' + manager_mock.return_value = ManagerMock(reply_mock) + account_mock.isLoggedIn.return_value = True + + result = [] + + def _callback(clusters): + result.extend(clusters) + + with mock.patch.object(Application, "getInstance", new = lambda: FixtureApplication()): + api = CloudApiClient(account_mock, self._errorHandler) + api.getClusters(_callback) + + manager_mock.return_value.finished.emit(reply_mock) + + self.assertEqual(2, len(result)) + + @patch("cura.NetworkClient.QNetworkAccessManager") + @patch("cura.API.Account") + def test_GetClusters2(self, account_mock, manager_mock): + manager = ManagerMock2() + manager_mock.return_value = manager + account_mock.isLoggedIn.return_value = True + + result = [] + + # with mock.patch.object(Application, "getInstance", new = lambda: FixtureApplication()): + api = CloudApiClient(account_mock, self._errorHandler) + api.getClusters(lambda clusters: result.extend(clusters)) + + manager.finished.emit(ManagerMock2.createReply( + "GET", "https://api-staging.ultimaker.com/connect/v1/clusters", + 200, { + "data": [{ + "cluster_id": "RIZ6cZbWA_Ua7RZVJhrdVfVpf0z-MqaSHQE4v8aRTtYq", + "host_guid": "e90ae0ac-1257-4403-91ee-a44c9b7e8050", + "host_name": "ultimakersystem-ccbdd30044ec", "host_version": "5.1.2.20180807", + "is_online": False, "status": "inactive" + }, { + "cluster_id": "R0YcLJwar1ugh0ikEZsZs8NWKV6vJP_LdYsXgXqAcaNC", + "host_guid": "e90ae0ac-1257-4403-91ee-a44c9b7e8050", + "host_name": "ultimakersystem-ccbdd30044ec", "host_version": "5.1.2.20180807", + "is_online": True, "status": "active" + }] + } + )) + + self.assertEqual(2, len(result)) From 63c5b779595cb1488c44094eb431694430161625 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Fri, 7 Dec 2018 16:43:53 +0100 Subject: [PATCH 0757/1240] STAR-322: Fixing finished jobs --- .../src/Cloud/CloudOutputDevice.py | 54 +++++++++---------- .../src/Cloud/CloudOutputDeviceManager.py | 9 ++-- 2 files changed, 30 insertions(+), 33 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 321c40bc74..2800bc1c8c 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -1,10 +1,9 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import os -from datetime import datetime from time import time -from typing import Dict, List, Optional +from typing import Dict, List, Optional, Set from PyQt5.QtCore import QObject, QUrl, pyqtProperty, pyqtSignal, pyqtSlot @@ -92,7 +91,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self._host_name = host_name self._setInterfaceElements() - + self._device_id = device_id self._account = CuraApplication.getInstance().getCuraAPI().account @@ -111,7 +110,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # Properties to populate later on with received cloud data. self._print_jobs = [] # type: List[UM3PrintJobOutputModel] self._number_of_extruders = 2 # All networked printers are dual-extrusion Ultimaker machines. - + # We only allow a single upload at a time. self._sending_job = False # TODO: handle progress messages in another class. @@ -121,6 +120,9 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self._received_printers = None # type: Optional[List[CloudClusterPrinter]] self._received_print_jobs = None # type: Optional[List[CloudClusterPrintJob]] + # A set of the user's job IDs that have finished + self._finished_jobs = set() # type: Set[str] + ## Gets the host name of this device @property def host_name(self) -> str: @@ -144,16 +146,16 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self.setShortDescription(T.PRINT_VIA_CLOUD_BUTTON) self.setDescription(T.PRINT_VIA_CLOUD_TOOLTIP) self.setConnectionText(T.CONNECTED_VIA_CLOUD) - + ## Called when Cura requests an output device to receive a (G-code) file. def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mime_types: bool = False, file_handler: Optional[FileHandler] = None, **kwargs: str) -> None: - + # Show an error message if we're already sending a job. if self._sending_job: self._onUploadError(T.BLOCKED_UPLOADING) return - + # Indicate we have started sending a job. self._sending_job = True self.writeStarted.emit(self) @@ -165,9 +167,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): mesh_bytes = mesh_format.getBytes(nodes) - # TODO: Remove extension from the file name, since we are using content types now request = CloudJobUploadRequest( - job_name = file_name, ## + "." + mesh_format.file_extension, + job_name = file_name, file_size = len(mesh_bytes), content_type = mesh_format.mime_type, ) @@ -261,18 +262,15 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Handles the event of a change in a print job state def _onPrintJobStateChanged(self) -> None: - username = self._account.userName - finished_jobs = [job for job in self._print_jobs if job.state == "wait_cleanup"] - - newly_finished_jobs = [job for job in finished_jobs if job not in self._finished_jobs and job.owner == username] - for job in newly_finished_jobs: - if job.assignedPrinter: - job_completed_text = T.JOB_COMPLETED_PRINTER.format(printer_name=job.assignedPrinter.name, - job_name=job.name) - else: - job_completed_text = T.JOB_COMPLETED_NO_PRINTER.format(job_name=job.name) - job_completed_message = Message(text=job_completed_text, title = T.JOB_COMPLETED_TITLE) - job_completed_message.show() + user_name = self._getUserName() + for job in self._print_jobs: + if job.state == "wait_cleanup" and job.key not in self._finished_jobs and job.owner == user_name: + self._finished_jobs.add(job.key) + Message( + title = T.JOB_COMPLETED_TITLE, + text = (T.JOB_COMPLETED_PRINTER.format(printer_name=job.assignedPrinter.name, job_name=job.name) + if job.assignedPrinter else T.JOB_COMPLETED_NO_PRINTER.format(job_name=job.name)), + ).show() # Ensure UI gets updated self.printJobsChanged.emit() @@ -326,13 +324,12 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def _onUploadError(self, message: str = None) -> None: self._resetUploadProgress() if message: - message = Message( + Message( text = message, title = T.ERROR, lifetime = 10, dismissable = True - ) - message.show() + ).show() self._sending_job = False # the upload has finished so we're not sending a job anymore self.writeError.emit() @@ -341,18 +338,17 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def _onUploadSuccess(self, response: CloudPrintResponse) -> None: Logger.log("i", "The cluster will be printing this print job with the ID %s", response.cluster_job_id) self._resetUploadProgress() - message = Message( + Message( text = T.UPLOAD_SUCCESS_TEXT, title = T.UPLOAD_SUCCESS_TITLE, lifetime = 5, dismissable = True, - ) - message.show() + ).show() self._sending_job = False # the upload has finished so we're not sending a job anymore self.writeFinished.emit() ## Gets the remote printers. - @pyqtProperty("QVariantList", notify = _clusterPrintersChanged) + @pyqtProperty("QVariantList", notify=_clusterPrintersChanged) def printers(self) -> List[PrinterOutputModel]: return self._printers @@ -374,7 +370,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Get remote print jobs. @pyqtProperty("QVariantList", notify = printJobsChanged) - def printJobs(self)-> List[UM3PrintJobOutputModel]: + def printJobs(self) -> List[UM3PrintJobOutputModel]: return self._print_jobs ## Get remote print jobs that are still in the print queue. diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index bc410a4a1d..f06bbb305e 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -7,6 +7,7 @@ from PyQt5.QtCore import QTimer from UM import i18nCatalog from UM.Logger import Logger from UM.Message import Message +from cura.API import Account from cura.CuraApplication import CuraApplication from cura.Settings.GlobalStack import GlobalStack from .CloudApiClient import CloudApiClient @@ -40,7 +41,7 @@ class CloudOutputDeviceManager: application = CuraApplication.getInstance() self._output_device_manager = application.getOutputDeviceManager() - self._account = application.getCuraAPI().account + self._account = application.getCuraAPI().account # type: Account self._account.loginStateChanged.connect(self._onLoginStateChanged) self._api = CloudApiClient(self._account, self._onApiError) @@ -54,11 +55,11 @@ class CloudOutputDeviceManager: self._update_timer.timeout.connect(self._getRemoteClusters) # Make sure the timer is started in case we missed the loginChanged signal - self._onLoginStateChanged() + self._onLoginStateChanged(self._account.isLoggedIn) # Called when the uses logs in or out - def _onLoginStateChanged(self) -> None: - if self._account.isLoggedIn: + def _onLoginStateChanged(self, is_logged_in: bool) -> None: + if is_logged_in: if not self._update_timer.isActive(): self._update_timer.start() else: From e92bd01fb28d460facdd5f0e7883267a25379aeb Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 7 Dec 2018 17:28:42 +0100 Subject: [PATCH 0758/1240] Removed the rating from the download grid It felt a bit weird to already have it in the grid layout CURA-6013 --- .../qml/ToolboxDownloadsGridTile.qml | 112 +++++++++++------- plugins/Toolbox/src/PackagesModel.py | 2 +- 2 files changed, 67 insertions(+), 47 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml index 70edee7449..677e532827 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml @@ -16,6 +16,42 @@ Item height: UM.Theme.getSize("toolbox_thumbnail_small").height Layout.alignment: Qt.AlignTop | Qt.AlignLeft + MouseArea + { + anchors.fill: parent + hoverEnabled: true + onEntered: thumbnail.border.color = UM.Theme.getColor("primary") + onExited: thumbnail.border.color = UM.Theme.getColor("lining") + onClicked: + { + base.selection = model + switch(toolbox.viewCategory) + { + case "material": + + // If model has a type, it must be a package + if (model.type !== undefined) + { + toolbox.viewPage = "detail" + toolbox.filterModelByProp("packages", "id", model.id) + } + else + { + toolbox.viewPage = "author" + toolbox.setFilters("packages", { + "author_id": model.id, + "type": "material" + }) + } + break + default: + toolbox.viewPage = "detail" + toolbox.filterModelByProp("packages", "id", model.id) + break + } + } + } + Rectangle { id: thumbnail @@ -33,8 +69,6 @@ Item fillMode: Image.PreserveAspectFit source: model.icon_url || "../images/logobot.svg" mipmap: true - - } UM.RecolorImage { @@ -50,42 +84,6 @@ Item color: (installedPackages == packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border") source: "../images/installed_check.svg" } - - MouseArea - { - anchors.fill: thumbnail - hoverEnabled: true - onEntered: thumbnail.border.color = UM.Theme.getColor("primary") - onExited: thumbnail.border.color = UM.Theme.getColor("lining") - onClicked: - { - base.selection = model - switch(toolbox.viewCategory) - { - case "material": - - // If model has a type, it must be a package - if (model.type !== undefined) - { - toolbox.viewPage = "detail" - toolbox.filterModelByProp("packages", "id", model.id) - } - else - { - toolbox.viewPage = "author" - toolbox.setFilters("packages", { - "author_id": model.id, - "type": "material" - }) - } - break - default: - toolbox.viewPage = "detail" - toolbox.filterModelByProp("packages", "id", model.id) - break - } - } - } } Item { @@ -119,17 +117,39 @@ Item anchors.top: name.bottom anchors.bottom: rating.top verticalAlignment: Text.AlignVCenter + maximumLineCount: 2 } - RatingWidget + Row { id: rating - visible: model.type == "plugin" - packageId: model.id - rating: model.average_rating != undefined ? model.average_rating : 0 - numRatings: model.num_ratings != undefined ? model.num_ratings : 0 - userRating: model.user_rating - enabled: installedPackages != 0 && Cura.API.account.isLoggedIn - anchors.bottom: parent.bottom + height: UM.Theme.getSize("rating_star").height + visible: model.average_rating > 0 //Has a rating at all. + spacing: UM.Theme.getSize("thick_lining").width + anchors + { + bottom: parent.bottom + left: parent.left + right: parent.right + } + + UM.RecolorImage + { + id: starIcon + source: UM.Theme.getIcon("star_filled") + color: "#5a5a5a" + height: UM.Theme.getSize("rating_star").height + width: UM.Theme.getSize("rating_star").width + } + + Label + { + text: model.average_rating.toFixed(1) + " (" + model.num_ratings + " " + catalog.i18nc("@label", "ratings") + ")" + verticalAlignment: Text.AlignVCenter + height: starIcon.height + anchors.verticalCenter: starIcon.verticalCenter + color: starIcon.color + font: UM.Theme.getFont("small") + } } } } diff --git a/plugins/Toolbox/src/PackagesModel.py b/plugins/Toolbox/src/PackagesModel.py index b3c388bc7c..3f5be3bc37 100644 --- a/plugins/Toolbox/src/PackagesModel.py +++ b/plugins/Toolbox/src/PackagesModel.py @@ -105,7 +105,7 @@ class PackagesModel(ListModel): "links": links_dict, "website": package["website"] if "website" in package else None, "login_required": "login-required" in package.get("tags", []), - "average_rating": package.get("rating", {}).get("average", 0), + "average_rating": float(package.get("rating", {}).get("average", 0)), "num_ratings": package.get("rating", {}).get("count", 0), "user_rating": package.get("rating", {}).get("user", 0) }) From 9dc8450db0aa3ba21b969f787a29a6fe775058f2 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 7 Dec 2018 17:37:10 +0100 Subject: [PATCH 0759/1240] Fix typo --- .../Recommended/RecommendedQualityProfileSelector.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml index 4963f10792..15d40f545a 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml @@ -350,7 +350,7 @@ Item enabled: !Cura.SimpleModeSettingsManager.isProfileUserCreated onEntered: { - var tooltipContent = catalog.i18nc("@tooltip", "This quality profile is not available for you current material and nozzle configuration. Please change these to enable this quality profile") + var tooltipContent = catalog.i18nc("@tooltip", "This quality profile is not available for your current material and nozzle configuration. Please change these to enable this quality profile") base.showTooltip(qualityRow, Qt.point(-UM.Theme.getSize("thick_margin").width, customisedSettings.height), tooltipContent) } onExited: base.hideTooltip() From e0b159e2ad78fe8110a5e959526b8c706ad45eb7 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Fri, 7 Dec 2018 17:46:35 +0100 Subject: [PATCH 0760/1240] STAR-322: Creating a network manager mock --- .../tests/Cloud/NetworkManagerMock.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py b/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py new file mode 100644 index 0000000000..b14ee760d9 --- /dev/null +++ b/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py @@ -0,0 +1,42 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +import json +from unittest.mock import MagicMock + +from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply, QNetworkRequest + +from UM.Signal import Signal + + +class NetworkManagerMock: + finished = Signal() + authenticationRequired = Signal() + + _OPERATIONS = { + "GET": QNetworkAccessManager.GetOperation, + "POST": QNetworkAccessManager.PostOperation, + "PUT": QNetworkAccessManager.PutOperation, + "DELETE": QNetworkAccessManager.DeleteOperation, + "HEAD": QNetworkAccessManager.HeadOperation, + } + + def __getattr__(self, item): + operation = self._OPERATIONS.get(item.upper()) + if operation: + return lambda request, *_: self._fakeReply(operation, request) + return super().__getattribute__(item) + + def _fakeReply(self, operation: QNetworkAccessManager.Operation, request: QNetworkRequest) -> QNetworkReply: + reply_mock = MagicMock() + reply_mock.url = request.url + reply_mock.operation.return_value = operation + return reply_mock + + def respond(self, method: str, url: str, status_code: int, response: dict) -> QNetworkReply: + reply_mock = MagicMock() + reply_mock.url().toString.return_value = url + reply_mock.operation.return_value = self._OPERATIONS[method] + reply_mock.attribute.return_value = status_code + reply_mock.readAll.return_value = json.dumps(response).encode() + self.finished.emit(reply_mock) + return reply_mock From 45f51c358866a09810dacfc9508c4c06cbc18ec4 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Fri, 7 Dec 2018 18:09:20 +0100 Subject: [PATCH 0761/1240] STAR-322: First test cloud output device manager --- .../tests/Cloud/NetworkManagerMock.py | 32 +++++++----- .../Cloud/TestCloudOutputDeviceManager.py | 51 +++++++++++++++++++ .../tests/Cloud/__init__.py | 2 + 3 files changed, 71 insertions(+), 14 deletions(-) create mode 100644 plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py create mode 100644 plugins/UM3NetworkPrinting/tests/Cloud/__init__.py diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py b/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py index b14ee760d9..945c526814 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py @@ -1,9 +1,10 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import json +from typing import Dict, Tuple from unittest.mock import MagicMock -from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply, QNetworkRequest +from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply from UM.Signal import Signal @@ -20,23 +21,26 @@ class NetworkManagerMock: "HEAD": QNetworkAccessManager.HeadOperation, } - def __getattr__(self, item): - operation = self._OPERATIONS.get(item.upper()) + def __init__(self): + self.replies = {} # type: Dict[Tuple[str, str], QNetworkReply] + + def __getattr__(self, method): + operation = self._OPERATIONS.get(method.upper()) if operation: - return lambda request, *_: self._fakeReply(operation, request) - return super().__getattribute__(item) + return lambda request, *_: self.replies[method.upper(), request.url().toString()] + return super().__getattribute__(method) - def _fakeReply(self, operation: QNetworkAccessManager.Operation, request: QNetworkRequest) -> QNetworkReply: - reply_mock = MagicMock() - reply_mock.url = request.url - reply_mock.operation.return_value = operation - return reply_mock - - def respond(self, method: str, url: str, status_code: int, response: dict) -> QNetworkReply: + def prepareResponse(self, method: str, url: str, status_code: int, response: dict) -> None: reply_mock = MagicMock() reply_mock.url().toString.return_value = url reply_mock.operation.return_value = self._OPERATIONS[method] reply_mock.attribute.return_value = status_code reply_mock.readAll.return_value = json.dumps(response).encode() - self.finished.emit(reply_mock) - return reply_mock + self.replies[method, url] = reply_mock + + def flushReplies(self): + for reply in self.replies.values(): + self.finished.emit(reply) + + def reset(self): + self.replies.clear() diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py new file mode 100644 index 0000000000..cf86f713b2 --- /dev/null +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py @@ -0,0 +1,51 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from unittest import TestCase +from unittest.mock import patch + +from cura.CuraApplication import CuraApplication +from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputDevice import CloudOutputDevice +from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputDeviceManager import CloudOutputDeviceManager +from plugins.UM3NetworkPrinting.tests.Cloud.NetworkManagerMock import NetworkManagerMock + + +@patch("cura.NetworkClient.QNetworkAccessManager") +class TestCloudOutputDeviceManager(TestCase): + app = CuraApplication.getInstance() or CuraApplication() + + def setUp(self): + super().setUp() + self.app.initialize() + + self.network = NetworkManagerMock() + self.network.prepareResponse( + "GET", "https://api-staging.ultimaker.com/connect/v1/clusters", + 200, { + "data": [{ + "cluster_id": "RIZ6cZbWA_Ua7RZVJhrdVfVpf0z-MqaSHQE4v8aRTtYq", + "host_guid": "e90ae0ac-1257-4403-91ee-a44c9b7e8050", + "host_name": "ultimakersystem-ccbdd30044ec", "host_version": "5.1.2.20180807", + "is_online": False, "status": "inactive" + }, { + "cluster_id": "R0YcLJwar1ugh0ikEZsZs8NWKV6vJP_LdYsXgXqAcaNC", + "host_guid": "e90ae0ac-1257-4403-91ee-a44c9b7e8050", + "host_name": "ultimakersystem-ccbdd30044ec", "host_version": "5.1.2.20180807", + "is_online": True, "status": "active" + }] + } + ) + + def tearDown(self): + super().tearDown() + + def test_device(self, network_mock): + network_mock.return_value = self.network + + manager = CloudOutputDeviceManager() + manager._account.loginStateChanged.emit(True) + manager._update_timer.timeout.emit() + + self.network.flushReplies() + + devices = self.app.getOutputDeviceManager().getOutputDevices() + self.assertEqual([CloudOutputDevice], list(map(type, devices))) diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/__init__.py b/plugins/UM3NetworkPrinting/tests/Cloud/__init__.py new file mode 100644 index 0000000000..f3f6970c54 --- /dev/null +++ b/plugins/UM3NetworkPrinting/tests/Cloud/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. From 2bf641efcfef28bbdae869ab3bfb6d8d4f70d8de Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 10 Dec 2018 09:09:58 +0100 Subject: [PATCH 0762/1240] Add a Cura Tooltip to show in some buttons Contributes to CURA-6004. --- resources/qml/ActionButton.qml | 8 ++-- .../ActionPanel/OutputDevicesActionButton.qml | 1 - .../qml/ActionPanel/SliceProcessWidget.qml | 1 + resources/qml/PrintSetupTooltip.qml | 5 +-- resources/qml/ToolTip.qml | 43 +++++++++++++++++++ resources/qml/ToolbarButton.qml | 7 +++ resources/qml/qmldir | 3 +- resources/themes/cura-light/theme.json | 2 +- 8 files changed, 59 insertions(+), 11 deletions(-) create mode 100644 resources/qml/ToolTip.qml diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index 7177120f35..aa4d3f21c0 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -16,7 +16,7 @@ Button property alias iconSource: buttonIconLeft.source property alias textFont: buttonText.font property alias cornerRadius: backgroundRect.radius - property alias tooltip: tooltip.text + property alias tooltip: tooltip.tooltipText property alias cornerSide: backgroundRect.cornerSide property color color: UM.Theme.getColor("primary") @@ -109,11 +109,9 @@ Button z: backgroundRect.z - 1 } - ToolTip + Cura.ToolTip { id: tooltip - text: "" - delay: 500 - visible: text != "" && button.hovered + show: button.hovered } } \ No newline at end of file diff --git a/resources/qml/ActionPanel/OutputDevicesActionButton.qml b/resources/qml/ActionPanel/OutputDevicesActionButton.qml index 9a6c97bcff..2858bc267c 100644 --- a/resources/qml/ActionPanel/OutputDevicesActionButton.qml +++ b/resources/qml/ActionPanel/OutputDevicesActionButton.qml @@ -55,7 +55,6 @@ Item leftPadding: UM.Theme.getSize("narrow_margin").width //Need more space than usual here for wide text. rightPadding: UM.Theme.getSize("narrow_margin").width - tooltip: popup.opened ? "" : catalog.i18nc("@info:tooltip", "Select the active output device") iconSource: popup.opened ? UM.Theme.getIcon("arrow_top") : UM.Theme.getIcon("arrow_bottom") color: UM.Theme.getColor("action_panel_secondary") visible: (devicesModel.deviceCount > 1) diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index 18caeafb40..2d377abcd8 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -109,6 +109,7 @@ Column fixedWidthMode: true anchors.fill: parent text: catalog.i18nc("@button", "Slice") + tooltip: "Start slicing process" enabled: widget.backendState != UM.Backend.Error visible: widget.backendState == UM.Backend.NotStarted || widget.backendState == UM.Backend.Error onClicked: sliceOrStopSlicing() diff --git a/resources/qml/PrintSetupTooltip.qml b/resources/qml/PrintSetupTooltip.qml index 4fa4ef9dd7..693b703813 100644 --- a/resources/qml/PrintSetupTooltip.qml +++ b/resources/qml/PrintSetupTooltip.qml @@ -2,9 +2,7 @@ // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.7 -import QtQuick.Controls 1.1 -import QtQuick.Controls.Styles 1.1 -import QtQuick.Layouts 1.1 +import QtQuick.Controls 2.3 import UM 1.0 as UM @@ -57,5 +55,6 @@ UM.PointingRectangle { textFormat: Text.RichText font: UM.Theme.getFont("default"); color: UM.Theme.getColor("tooltip_text"); + renderType: Text.NativeRendering } } diff --git a/resources/qml/ToolTip.qml b/resources/qml/ToolTip.qml new file mode 100644 index 0000000000..ed71d3983b --- /dev/null +++ b/resources/qml/ToolTip.qml @@ -0,0 +1,43 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 2.3 + +import UM 1.0 as UM + +ToolTip +{ + // This property indicates when the tooltip has to show, for instance when a button is hovered + property bool show: false + + property alias tooltipText: tooltip.text + property var targetPoint: Qt.point(0, 0) + + id: tooltip + text: "" + delay: 500 + visible: text != "" && show + font: UM.Theme.getFont("default") + + background: UM.PointingRectangle + { + id: backgroundRect + color: UM.Theme.getColor("tooltip") + + target: Qt.point(targetPoint.x - tooltip.x, targetPoint.y - tooltip.y) + + arrowSize: UM.Theme.getSize("default_arrow").width + } + + contentItem: Label + { + id: label + text: tooltip.text + font: tooltip.font + wrapMode: Text.Wrap + textFormat: Text.RichText + color: UM.Theme.getColor("tooltip_text") + renderType: Text.NativeRendering + } +} \ No newline at end of file diff --git a/resources/qml/ToolbarButton.qml b/resources/qml/ToolbarButton.qml index adff73fb7c..307d49302c 100644 --- a/resources/qml/ToolbarButton.qml +++ b/resources/qml/ToolbarButton.qml @@ -96,4 +96,11 @@ Button height: UM.Theme.getSize("button_icon").height } } + + Cura.ToolTip + { + id: tooltip + tooltipText: base.text + show: base.hovered + } } diff --git a/resources/qml/qmldir b/resources/qml/qmldir index 1dc21150ce..80e0f8be46 100644 --- a/resources/qml/qmldir +++ b/resources/qml/qmldir @@ -14,4 +14,5 @@ PrinterTypeLabel 1.0 PrinterTypeLabel.qml ViewsSelector 1.0 ViewsSelector.qml ToolbarButton 1.0 ToolbarButton.qml SettingView 1.0 SettingView.qml -ProfileMenu 1.0 ProfileMenu.qml \ No newline at end of file +ProfileMenu 1.0 ProfileMenu.qml +ToolTip 1.0 ToolTip.qml \ No newline at end of file diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 16d13b9652..86201db809 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -240,7 +240,7 @@ "checkbox_disabled": [223, 223, 223, 255], "checkbox_text": [35, 35, 35, 255], - "tooltip": [68, 192, 255, 255], + "tooltip": [19, 19, 19, 255], "tooltip_text": [255, 255, 255, 255], "tool_button_border": [255, 255, 255, 0], From c495ade2d363d9f19e3bb6a8444073e1c86fe3fc Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Mon, 10 Dec 2018 09:42:26 +0100 Subject: [PATCH 0763/1240] STAR-322: Documenting the network manager mock --- .../tests/Cloud/NetworkManagerMock.py | 46 +++++++++++++++++-- .../Cloud/TestCloudOutputDeviceManager.py | 21 ++------- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py b/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py index 945c526814..25107971c0 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py @@ -9,10 +9,15 @@ from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply from UM.Signal import Signal +## This class can be used to mock the QNetworkManager class and test the code using it. +# After patching the QNetworkManager class, requests are prepared before they can be executed. +# Any requests not prepared beforehand will cause KeyErrors. class NetworkManagerMock: + # signals used in the network manager. finished = Signal() authenticationRequired = Signal() + # an enumeration of the supported operations and their code for the network access manager. _OPERATIONS = { "GET": QNetworkAccessManager.GetOperation, "POST": QNetworkAccessManager.PostOperation, @@ -22,15 +27,29 @@ class NetworkManagerMock: } def __init__(self): + # a dict with the prepared replies, using the format {(http_method, url): reply} self.replies = {} # type: Dict[Tuple[str, str], QNetworkReply] - def __getattr__(self, method): + ## Mock implementation of the get, post, put, delete and head methods from the network manager. + # Since the methods are very simple and the same it didn't make sense to repeat the code. + # \param method: The method being called. + # \return The mocked function, if the method name is known. Defaults to the standard getattr function. + def __getattr__(self, method: str): operation = self._OPERATIONS.get(method.upper()) if operation: + # this mock implementation will simply return the reply from the prepared ones. + # it raises a KeyError if requests are done without being prepared. return lambda request, *_: self.replies[method.upper(), request.url().toString()] - return super().__getattribute__(method) - def prepareResponse(self, method: str, url: str, status_code: int, response: dict) -> None: + # the attribute is not one of the implemented methods, default to the standard implementation. + return getattr(super(), method) + + ## Prepares a server reply for the given parameters. + # \param method: The HTTP method. + # \param url: The URL being requested. + # \param status_code: The HTTP status code for the response. + # \param response: A dictionary with the response from the server (this is converted to JSON). + def prepareReply(self, method: str, url: str, status_code: int, response: dict) -> None: reply_mock = MagicMock() reply_mock.url().toString.return_value = url reply_mock.operation.return_value = self._OPERATIONS[method] @@ -38,9 +57,30 @@ class NetworkManagerMock: reply_mock.readAll.return_value = json.dumps(response).encode() self.replies[method, url] = reply_mock + ## Prepares a reply for the API call to get clusters. + def prepareGetClusters(self) -> None: + self.prepareReply( + "GET", "https://api-staging.ultimaker.com/connect/v1/clusters", + 200, { + "data": [{ + "cluster_id": "RIZ6cZbWA_Ua7RZVJhrdVfVpf0z-MqaSHQE4v8aRTtYq", + "host_guid": "e90ae0ac-1257-4403-91ee-a44c9b7e8050", + "host_name": "ultimakersystem-ccbdd30044ec", "host_version": "5.1.2.20180807", + "is_online": False, "status": "inactive" + }, { + "cluster_id": "R0YcLJwar1ugh0ikEZsZs8NWKV6vJP_LdYsXgXqAcaNC", + "host_guid": "e90ae0ac-1257-4403-91ee-a44c9b7e8050", + "host_name": "ultimakersystem-ccbdd30044ec", "host_version": "5.1.2.20180807", + "is_online": True, "status": "active" + }] + } + ) + + ## Emits the signal that the reply is ready to all prepared replies. def flushReplies(self): for reply in self.replies.values(): self.finished.emit(reply) + ## Deletes all prepared replies def reset(self): self.replies.clear() diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py index cf86f713b2..b22308ac1e 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py @@ -18,22 +18,7 @@ class TestCloudOutputDeviceManager(TestCase): self.app.initialize() self.network = NetworkManagerMock() - self.network.prepareResponse( - "GET", "https://api-staging.ultimaker.com/connect/v1/clusters", - 200, { - "data": [{ - "cluster_id": "RIZ6cZbWA_Ua7RZVJhrdVfVpf0z-MqaSHQE4v8aRTtYq", - "host_guid": "e90ae0ac-1257-4403-91ee-a44c9b7e8050", - "host_name": "ultimakersystem-ccbdd30044ec", "host_version": "5.1.2.20180807", - "is_online": False, "status": "inactive" - }, { - "cluster_id": "R0YcLJwar1ugh0ikEZsZs8NWKV6vJP_LdYsXgXqAcaNC", - "host_guid": "e90ae0ac-1257-4403-91ee-a44c9b7e8050", - "host_name": "ultimakersystem-ccbdd30044ec", "host_version": "5.1.2.20180807", - "is_online": True, "status": "active" - }] - } - ) + self.network.prepareGetClusters() def tearDown(self): super().tearDown() @@ -48,4 +33,6 @@ class TestCloudOutputDeviceManager(TestCase): self.network.flushReplies() devices = self.app.getOutputDeviceManager().getOutputDevices() - self.assertEqual([CloudOutputDevice], list(map(type, devices))) + self.assertEqual([CloudOutputDevice], [type(d) for d in devices]) + self.assertEqual(["R0YcLJwar1ugh0ikEZsZs8NWKV6vJP_LdYsXgXqAcaNC"], [d.key for d in devices]) + self.assertEqual(["ultimakersystem-ccbdd30044ec"], [d.host_name for d in devices]) From 9e7a52e28bd783876ae8255fc3648f4dbb7fcc4e Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 10 Dec 2018 09:43:50 +0100 Subject: [PATCH 0764/1240] Update the background color for the expandable component when using the close button. Contributes to CURA-5941. --- resources/qml/ExpandableComponent.qml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 3a03740251..4170a0942e 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -71,7 +71,15 @@ Item function toggleContent() { - content.visible = !content.visible + content.visible = !expanded + } + + // Add this binding since the background color is not updated otherwise + Binding + { + target: background + property: "color" + value: expanded ? headerActiveColor : headerBackgroundColor } implicitHeight: 100 * screenScaleFactor @@ -82,7 +90,7 @@ Item id: background property real padding: UM.Theme.getSize("default_margin").width - color: headerBackgroundColor + color: expanded ? headerActiveColor : headerBackgroundColor anchors.fill: parent Loader From 04f3601c2711426ac5b363ad51e03d468492506f Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 10 Dec 2018 09:54:26 +0100 Subject: [PATCH 0765/1240] Ensure that local votes are displayed right away So even before an update is received, ensure that the data is updated CURA-6013 --- .../Toolbox/resources/qml/RatingWidget.qml | 29 +++++++++++++------ .../resources/qml/ToolboxDetailPage.qml | 18 ++++++++++++ 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/plugins/Toolbox/resources/qml/RatingWidget.qml b/plugins/Toolbox/resources/qml/RatingWidget.qml index 424f6c91c4..b7d07835e8 100644 --- a/plugins/Toolbox/resources/qml/RatingWidget.qml +++ b/plugins/Toolbox/resources/qml/RatingWidget.qml @@ -9,8 +9,16 @@ Item property real rating: 0 property int indexHovered: -1 property string packageId: "" + property int numRatings: 0 + + // If the widget was used to vote, but the vote isn't sent to remote yet, we do want to fake some things. + property int _numRatings: _localRating != 0 ? numRatings + 1 : numRatings + property int _localRating: 0 + onVisibleChanged: _localRating = 0 // Reset the _localRating + property int userRating: 0 + width: contentRow.width height: contentRow.height MouseArea @@ -50,7 +58,10 @@ Item { return indexHovered >= index } - + if(ratingWidget._localRating > 0) + { + return _localRating >= index +1 + } if(ratingWidget.userRating > 0) { return userRating >= index +1 @@ -73,7 +84,7 @@ Item { return "#5a5a5a" } - if((ratingWidget.indexHovered >= 0 || ratingWidget.userRating > 0) && isStarFilled) + if((ratingWidget.indexHovered >= 0 || ratingWidget.userRating > 0 || ratingWidget._localRating > 0) && isStarFilled) { return UM.Theme.getColor("primary") } @@ -82,19 +93,19 @@ Item } onClicked: { - if(userRating == 0) - { - //User didn't vote yet, locally fake it - numRatings += 1 - } - userRating = index + 1 // Fake local data + // Ensure that the local rating is updated (even though it's not on the server just yet) + _localRating = index + 1 toolbox.ratePackage(ratingWidget.packageId, index + 1) } } } Label { - text: "(" + numRatings + ")" + text: "(" + _numRatings + ")" + verticalAlignment: Text.AlignVCenter + height: parent.height + color: "#5a5a5a" + font: UM.Theme.getFont("small") } } } diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml index 4e0bf67544..dd3f0a6c84 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml @@ -6,6 +6,8 @@ import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import UM 1.1 as UM +import Cura 1.1 as Cura + Item { id: page @@ -103,6 +105,12 @@ Item font: UM.Theme.getFont("default") color: UM.Theme.getColor("text_medium") } + Label + { + text: catalog.i18nc("@label", "Rating") + ":" + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text_medium") + } } Column { @@ -160,6 +168,16 @@ Item font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") } + RatingWidget + { + id: rating + visible: details.type == "plugin" + packageId: details.id + rating: details.average_rating + numRatings: details.num_ratings + userRating: details.user_rating + enabled: toolbox.isInstalled(details.id) && Cura.API.account.isLoggedIn + } } Rectangle { From 8cfb9350bc2931224d4be597c3650c0b808e4e2c Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 10 Dec 2018 10:36:55 +0100 Subject: [PATCH 0766/1240] Add a mouse area to gather the scroll events in the settings panel Contributes to CURA-5941. --- resources/qml/Settings/SettingView.qml | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 3f84296307..9904770281 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -168,8 +168,10 @@ Item style: ButtonStyle { - background: Item { - UM.RecolorImage { + background: Item + { + UM.RecolorImage + { anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter width: UM.Theme.getSize("standard_arrow").width @@ -180,8 +182,9 @@ Item source: UM.Theme.getIcon("menu") } } - label: Label{} + label: Label {} } + menu: SettingVisibilityPresetsMenu { onShowAllSettings: @@ -192,6 +195,14 @@ Item } } + // Mouse area that gathers the scroll events to not propagate it to the main view. + MouseArea + { + anchors.fill: scrollView + acceptedButtons: Qt.AllButtons + onWheel: wheel.accepted = true + } + ScrollView { id: scrollView From 098adc45ff6464f14e6ba8c2be2deec2df17bb75 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 10 Dec 2018 10:40:54 +0100 Subject: [PATCH 0767/1240] Move the rating logic outside of the rating widget That way it's easier to re-use the component if that's ever required. CURA-6013 --- plugins/Toolbox/resources/qml/RatingWidget.qml | 7 +++++-- .../Toolbox/resources/qml/ToolboxDetailPage.qml | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/plugins/Toolbox/resources/qml/RatingWidget.qml b/plugins/Toolbox/resources/qml/RatingWidget.qml index b7d07835e8..f1499b61a7 100644 --- a/plugins/Toolbox/resources/qml/RatingWidget.qml +++ b/plugins/Toolbox/resources/qml/RatingWidget.qml @@ -19,6 +19,8 @@ Item property int userRating: 0 + signal rated(int rating) + width: contentRow.width height: contentRow.height MouseArea @@ -94,8 +96,9 @@ Item onClicked: { // Ensure that the local rating is updated (even though it's not on the server just yet) - _localRating = index + 1 - toolbox.ratePackage(ratingWidget.packageId, index + 1) + //_localRating = index + 1 + rated(index + 1) + } } } diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml index dd3f0a6c84..a503a9d519 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml @@ -177,6 +177,21 @@ Item numRatings: details.num_ratings userRating: details.user_rating enabled: toolbox.isInstalled(details.id) && Cura.API.account.isLoggedIn + + onRated: + { + toolbox.ratePackage(details.id, rating) + var index = toolbox.packagesModel.find("id", details.id) + if(index != -1) + { + // Found the package + toolbox.packagesModel.setProperty(index, "user_rating", rating) + toolbox.packagesModel.setProperty(index, "num_ratings", details.num_ratings + 1) + + // Hack; This is because the current selection is an outdated copy, so we need to re-copy it. + base.selection = toolbox.packagesModel.getItem(index) + } + } } } Rectangle From 75c2e25a012c6ae44e81be2c0747f9658a8ce930 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Mon, 10 Dec 2018 10:43:22 +0100 Subject: [PATCH 0768/1240] STAR-322: Testing more of the device manager --- .../tests/Cloud/Fixtures/clusters.json | 17 ++++++ .../tests/Cloud/NetworkManagerMock.py | 49 +++++++++------- .../Cloud/TestCloudOutputDeviceManager.py | 58 ++++++++++++++----- 3 files changed, 88 insertions(+), 36 deletions(-) create mode 100644 plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/clusters.json diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/clusters.json b/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/clusters.json new file mode 100644 index 0000000000..79a4c30113 --- /dev/null +++ b/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/clusters.json @@ -0,0 +1,17 @@ +{ + "data": [{ + "cluster_id": "RIZ6cZbWA_Ua7RZVJhrdVfVpf0z-MqaSHQE4v8aRTtYq", + "host_guid": "e90ae0ac-1257-4403-91ee-a44c9b7e8050", + "host_name": "ultimakersystem-ccbdd30044ec", + "host_version": "5.0.0.20170101", + "is_online": true, + "status": "active" + }, { + "cluster_id": "NWKV6vJP_LdYsXgXqAcaNCR0YcLJwar1ugh0ikEZsZs8", + "host_guid": "e0ace90a-91ee-1257-4403-e8050a44c9b7", + "host_name": "ultimakersystem-ccbdd30044ec", + "host_version": "5.1.2.20180807", + "is_online": true, + "status": "active" + }] +} diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py b/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py index 25107971c0..fc84569fa4 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py @@ -1,11 +1,13 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import json -from typing import Dict, Tuple +import os +from typing import Dict, Tuple, Optional from unittest.mock import MagicMock from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply +from UM.Logger import Logger from UM.Signal import Signal @@ -26,6 +28,7 @@ class NetworkManagerMock: "HEAD": QNetworkAccessManager.HeadOperation, } + ## Initializes the network manager mock. def __init__(self): # a dict with the prepared replies, using the format {(http_method, url): reply} self.replies = {} # type: Dict[Tuple[str, str], QNetworkReply] @@ -48,33 +51,37 @@ class NetworkManagerMock: # \param method: The HTTP method. # \param url: The URL being requested. # \param status_code: The HTTP status code for the response. - # \param response: A dictionary with the response from the server (this is converted to JSON). - def prepareReply(self, method: str, url: str, status_code: int, response: dict) -> None: + # \param response: The response body from the server (generally json-encoded). + def prepareReply(self, method: str, url: str, status_code: int, response: bytes) -> None: reply_mock = MagicMock() reply_mock.url().toString.return_value = url reply_mock.operation.return_value = self._OPERATIONS[method] reply_mock.attribute.return_value = status_code - reply_mock.readAll.return_value = json.dumps(response).encode() + reply_mock.readAll.return_value = response self.replies[method, url] = reply_mock + Logger.log("i", "Prepared mock {}-response to {} {}", status_code, method, url) ## Prepares a reply for the API call to get clusters. - def prepareGetClusters(self) -> None: - self.prepareReply( - "GET", "https://api-staging.ultimaker.com/connect/v1/clusters", - 200, { - "data": [{ - "cluster_id": "RIZ6cZbWA_Ua7RZVJhrdVfVpf0z-MqaSHQE4v8aRTtYq", - "host_guid": "e90ae0ac-1257-4403-91ee-a44c9b7e8050", - "host_name": "ultimakersystem-ccbdd30044ec", "host_version": "5.1.2.20180807", - "is_online": False, "status": "inactive" - }, { - "cluster_id": "R0YcLJwar1ugh0ikEZsZs8NWKV6vJP_LdYsXgXqAcaNC", - "host_guid": "e90ae0ac-1257-4403-91ee-a44c9b7e8050", - "host_name": "ultimakersystem-ccbdd30044ec", "host_version": "5.1.2.20180807", - "is_online": True, "status": "active" - }] - } - ) + # \param data: The data the server should return. If not given, a default response will be used. + # \return The data in the response. + def prepareGetClusters(self, data: Optional[dict] = None) -> dict: + data, response = self._getResponseData("clusters", data) + self.prepareReply("GET", "https://api-staging.ultimaker.com/connect/v1/clusters", 200, response) + return data + + ## Gets the data that should be in the server's response in both dictionary and JSON-encoded bytes format. + # \param fixture_name: The name of the fixture. + # \param data: The data that should be returned (optional) + # \return The server's response in both dictionary and JSON-encoded bytes format. + @staticmethod + def _getResponseData(fixture_name: str, data: Optional[dict] = None) -> Tuple[dict, bytes]: + if data is None: + with open("{}/Fixtures/{}.json".format(os.path.dirname(__file__), fixture_name), "rb") as f: + response = f.read() + data = json.loads(response.decode()) + else: + response = json.dumps(data).encode() + return data, response ## Emits the signal that the reply is ready to all prepared replies. def flushReplies(self): diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py index b22308ac1e..a043288e59 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py @@ -11,28 +11,56 @@ from plugins.UM3NetworkPrinting.tests.Cloud.NetworkManagerMock import NetworkMan @patch("cura.NetworkClient.QNetworkAccessManager") class TestCloudOutputDeviceManager(TestCase): - app = CuraApplication.getInstance() or CuraApplication() def setUp(self): super().setUp() - self.app.initialize() + self.app = CuraApplication.getInstance() + if not self.app: + self.app = CuraApplication() + self.app.initialize() self.network = NetworkManagerMock() - self.network.prepareGetClusters() + self.manager = CloudOutputDeviceManager() + self.clusters_response = self.network.prepareGetClusters() + ## In the tear down method we check whether the state of the output device manager is what we expect based on the + # mocked API response. def tearDown(self): super().tearDown() - - def test_device(self, network_mock): - network_mock.return_value = self.network - - manager = CloudOutputDeviceManager() - manager._account.loginStateChanged.emit(True) - manager._update_timer.timeout.emit() - + # let the network send replies self.network.flushReplies() - + # get the created devices devices = self.app.getOutputDeviceManager().getOutputDevices() - self.assertEqual([CloudOutputDevice], [type(d) for d in devices]) - self.assertEqual(["R0YcLJwar1ugh0ikEZsZs8NWKV6vJP_LdYsXgXqAcaNC"], [d.key for d in devices]) - self.assertEqual(["ultimakersystem-ccbdd30044ec"], [d.host_name for d in devices]) + # get the server data + clusters = self.clusters_response["data"] + self.assertEqual([CloudOutputDevice] * len(clusters), [type(d) for d in devices]) + self.assertEqual({cluster["cluster_id"] for cluster in clusters}, {device.key for device in devices}) + self.assertEqual({cluster["host_name"] for cluster in clusters}, {device.host_name for device in devices}) + + ## Runs the initial request to retrieve the clusters. + def _loadData(self, network_mock): + network_mock.return_value = self.network + self.manager._account.loginStateChanged.emit(True) + self.manager._update_timer.timeout.emit() + + def test_device_is_created(self, network_mock): + # just create the cluster, it is checked at tearDown + self._loadData(network_mock) + + def test_device_is_updated(self, network_mock): + self._loadData(network_mock) + + # update the cluster from member variable, which is checked at tearDown + self.clusters_response["data"][0]["host_name"] = "New host name" + self.network.prepareGetClusters(self.clusters_response) + + self.manager._update_timer.timeout.emit() + + def test_device_is_removed(self, network_mock): + self._loadData(network_mock) + + # delete the cluster from member variable, which is checked at tearDown + del self.clusters_response["data"][1] + self.network.prepareGetClusters(self.clusters_response) + + self.manager._update_timer.timeout.emit() From 99f0e9613144ceebb30159cc8fc73c6bff25f0e3 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 10 Dec 2018 11:01:55 +0100 Subject: [PATCH 0769/1240] Ensure that the local model is updated correctly on local vote CURA-6013 --- .../Toolbox/resources/qml/RatingWidget.qml | 22 +++----------- .../resources/qml/ToolboxDetailPage.qml | 29 +++++++++++++++---- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/plugins/Toolbox/resources/qml/RatingWidget.qml b/plugins/Toolbox/resources/qml/RatingWidget.qml index f1499b61a7..fbe782b2e2 100644 --- a/plugins/Toolbox/resources/qml/RatingWidget.qml +++ b/plugins/Toolbox/resources/qml/RatingWidget.qml @@ -12,11 +12,6 @@ Item property int numRatings: 0 - // If the widget was used to vote, but the vote isn't sent to remote yet, we do want to fake some things. - property int _numRatings: _localRating != 0 ? numRatings + 1 : numRatings - property int _localRating: 0 - onVisibleChanged: _localRating = 0 // Reset the _localRating - property int userRating: 0 signal rated(int rating) @@ -60,10 +55,7 @@ Item { return indexHovered >= index } - if(ratingWidget._localRating > 0) - { - return _localRating >= index +1 - } + if(ratingWidget.userRating > 0) { return userRating >= index +1 @@ -86,25 +78,19 @@ Item { return "#5a5a5a" } - if((ratingWidget.indexHovered >= 0 || ratingWidget.userRating > 0 || ratingWidget._localRating > 0) && isStarFilled) + if((ratingWidget.indexHovered >= 0 || ratingWidget.userRating > 0) && isStarFilled) { return UM.Theme.getColor("primary") } return "#5a5a5a" } } - onClicked: - { - // Ensure that the local rating is updated (even though it's not on the server just yet) - //_localRating = index + 1 - rated(index + 1) - - } + onClicked: rated(index + 1) // Notify anyone who cares about this. } } Label { - text: "(" + _numRatings + ")" + text: "(" + numRatings + ")" verticalAlignment: Text.AlignVCenter height: parent.height color: "#5a5a5a" diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml index a503a9d519..51bb218293 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml @@ -181,15 +181,34 @@ Item onRated: { toolbox.ratePackage(details.id, rating) - var index = toolbox.packagesModel.find("id", details.id) + // HACK: This is a far from optimal solution, but without major refactoring, this is the best we can + // do. Since a rework of this is scheduled, it shouldn't live that long... + var index = toolbox.pluginsAvailableModel.find("id", details.id) if(index != -1) { - // Found the package - toolbox.packagesModel.setProperty(index, "user_rating", rating) - toolbox.packagesModel.setProperty(index, "num_ratings", details.num_ratings + 1) + if(details.user_rating == 0) // User never rated before. + { + toolbox.pluginsAvailableModel.setProperty(index, "num_ratings", details.num_ratings + 1) + } + + toolbox.pluginsAvailableModel.setProperty(index, "user_rating", rating) + // Hack; This is because the current selection is an outdated copy, so we need to re-copy it. - base.selection = toolbox.packagesModel.getItem(index) + base.selection = toolbox.pluginsAvailableModel.getItem(index) + return + } + index = toolbox.pluginsShowcaseModel.find("id", details.id) + if(index != -1) + { + if(details.user_rating == 0) // User never rated before. + { + toolbox.pluginsShowcaseModel.setProperty(index, "user_rating", rating) + } + toolbox.pluginsShowcaseModel.setProperty(index, "num_ratings", details.num_ratings + 1) + + // Hack; This is because the current selection is an outdated copy, so we need to re-copy it. + base.selection = toolbox.pluginsShowcaseModel.getItem(index) } } } From 82322d857554b3685394dbee40485c59a973cf02 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 10 Dec 2018 11:12:25 +0100 Subject: [PATCH 0770/1240] Show that a downloaded plugin has a user rating or not CURA-6013 --- plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml index 677e532827..f34d982cfb 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml @@ -136,14 +136,16 @@ Item { id: starIcon source: UM.Theme.getIcon("star_filled") - color: "#5a5a5a" + color: model.user_rating == 0 ? "#5a5a5a" : UM.Theme.getColor("primary") height: UM.Theme.getSize("rating_star").height width: UM.Theme.getSize("rating_star").width } Label { - text: model.average_rating.toFixed(1) + " (" + model.num_ratings + " " + catalog.i18nc("@label", "ratings") + ")" + // If the user voted, show that value. Otherwsie show the average rating. + property real ratingtoUse: model.user_rating == 0 ? model.average_rating: model.user_rating + text: ratingtoUse.toFixed(1) + " (" + model.num_ratings + " " + catalog.i18nc("@label", "ratings") + ")" verticalAlignment: Text.AlignVCenter height: starIcon.height anchors.verticalCenter: starIcon.verticalCenter From 12522c929372c06ec55731ec381fed3f84bd2f8d Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 10 Dec 2018 11:19:44 +0100 Subject: [PATCH 0771/1240] Change hover colors of the small buttons. Contributes to CURA-5941 --- .../PrintSetupSelectorContents.qml | 4 ++-- resources/qml/Settings/SettingView.qml | 2 +- resources/qml/ViewOrientationButton.qml | 1 - resources/themes/cura-light/theme.json | 14 +++++++------- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml index 47d25edd54..7cd03ff74a 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml @@ -74,6 +74,7 @@ Item id: closeButton width: UM.Theme.getSize("message_close").width height: UM.Theme.getSize("message_close").height + hoverEnabled: true anchors { @@ -86,8 +87,7 @@ Item { anchors.fill: parent sourceSize.width: width - sourceSize.height: width - color: UM.Theme.getColor("message_text") + color: closeButton.hovered ? UM.Theme.getColor("small_button_text_hover") : UM.Theme.getColor("small_button_text") source: UM.Theme.getIcon("cross1") } diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 9904770281..c1b4b28d1d 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -178,7 +178,7 @@ Item height: UM.Theme.getSize("standard_arrow").height sourceSize.width: width sourceSize.height: height - color: control.enabled ? UM.Theme.getColor("setting_category_text") : UM.Theme.getColor("setting_category_disabled_text") + color: control.hovered ? UM.Theme.getColor("small_button_text_hover") : UM.Theme.getColor("small_button_text") source: UM.Theme.getIcon("menu") } } diff --git a/resources/qml/ViewOrientationButton.qml b/resources/qml/ViewOrientationButton.qml index 682fd71b4e..5371f8549b 100644 --- a/resources/qml/ViewOrientationButton.qml +++ b/resources/qml/ViewOrientationButton.qml @@ -9,7 +9,6 @@ UM.SimpleButton { width: UM.Theme.getSize("small_button").width height: UM.Theme.getSize("small_button").height - hoverBackgroundColor: UM.Theme.getColor("small_button_hover") hoverColor: UM.Theme.getColor("small_button_text_hover") color: UM.Theme.getColor("small_button_text") iconMargin: 0.5 * UM.Theme.getSize("wide_lining").width diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index d4fb59b7a1..0ef6c24bfb 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -80,7 +80,7 @@ "primary_hover": [48, 182, 231, 255], "primary_text": [255, 255, 255, 255], "border": [127, 127, 127, 255], - "secondary": [245, 245, 245, 255], + "secondary": [240, 240, 240, 255], "secondary_shadow": [216, 216, 216, 255], "primary_button": [38, 113, 231, 255], @@ -150,7 +150,7 @@ "small_button_active": [10, 8, 80, 255], "small_button_active_hover": [10, 8, 80, 255], "small_button_text": [102, 102, 102, 255], - "small_button_text_hover": [255, 255, 255, 255], + "small_button_text_hover": [8, 7, 63, 255], "small_button_text_active": [255, 255, 255, 255], "small_button_text_active_hover": [255, 255, 255, 255], @@ -183,18 +183,18 @@ "scrollbar_handle_hover": [50, 130, 255, 255], "scrollbar_handle_down": [50, 130, 255, 255], - "setting_category": [245, 245, 245, 255], + "setting_category": [240, 240, 240, 255], "setting_category_disabled": [255, 255, 255, 255], "setting_category_hover": [232, 242, 252, 255], - "setting_category_active": [245, 245, 245, 255], + "setting_category_active": [240, 240, 240, 255], "setting_category_active_hover": [232, 242, 252, 255], "setting_category_text": [35, 35, 35, 255], "setting_category_disabled_text": [24, 41, 77, 101], "setting_category_hover_text": [35, 35, 35, 255], "setting_category_active_text": [35, 35, 35, 255], "setting_category_active_hover_text": [35, 35, 35, 255], - "setting_category_border": [245, 245, 245, 255], - "setting_category_disabled_border": [245, 245, 245, 255], + "setting_category_border": [240, 240, 240, 255], + "setting_category_disabled_border": [240, 240, 240, 255], "setting_category_hover_border": [50, 130, 255, 255], "setting_category_active_border": [50, 130, 255, 255], "setting_category_active_hover_border": [50, 130, 255, 255], @@ -274,7 +274,7 @@ "z_axis": [0, 255, 0, 255], "all_axis": [255, 255, 255, 255], - "viewport_background": [245, 245, 245, 255], + "viewport_background": [250, 250, 250, 255], "volume_outline": [50, 130, 255, 255], "buildplate": [244, 244, 244, 255], "buildplate_grid": [129, 131, 134, 255], From 134f97d5f15064703d9c1b59c7bb8162e7ee5e77 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Mon, 10 Dec 2018 11:30:02 +0100 Subject: [PATCH 0772/1240] STAR-322: Finishing the output device manager tests --- .../src/Cloud/CloudOutputDeviceManager.py | 19 +++----- .../tests/Cloud/Fixtures/clusters.json | 2 +- .../Cloud/TestCloudOutputDeviceManager.py | 45 ++++++++++++++++++- 3 files changed, 51 insertions(+), 15 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index f06bbb305e..961f8d696d 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -22,7 +22,6 @@ from .Utils import findChanges # # API spec is available on https://api.ultimaker.com/docs/connect/spec/. # - class CloudOutputDeviceManager: META_CLUSTER_ID = "um_cloud_cluster_id" @@ -32,9 +31,7 @@ class CloudOutputDeviceManager: # The translation catalog for this device. I18N_CATALOG = i18nCatalog("cura") - def __init__(self): - super().__init__() - + def __init__(self) -> None: # Persistent dict containing the remote clusters for the authenticated user. self._remote_clusters = {} # type: Dict[str, CloudOutputDevice] @@ -86,6 +83,7 @@ class CloudOutputDeviceManager: for removed_cluster in removed_devices: if removed_cluster.isConnected(): removed_cluster.disconnect() + removed_cluster.close() self._output_device_manager.removeOutputDevice(removed_cluster.key) del self._remote_clusters[removed_cluster.key] @@ -124,20 +122,17 @@ class CloudOutputDeviceManager: return device = next((c for c in self._remote_clusters.values() if c.matchesNetworkKey(local_network_key)), None) - if not device: - return - - active_machine.setMetaDataEntry(self.META_CLUSTER_ID, device.key) - return device.connect() + if device: + active_machine.setMetaDataEntry(self.META_CLUSTER_ID, device.key) + device.connect() ## Handles an API error received from the cloud. # \param errors: The errors received def _onApiError(self, errors: List[CloudErrorObject]) -> None: message = ". ".join(e.title for e in errors) # TODO: translate errors - message = Message( + Message( text = message, title = self.I18N_CATALOG.i18nc("@info:title", "Error"), lifetime = 10, dismissable = True - ) - message.show() + ).show() diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/clusters.json b/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/clusters.json index 79a4c30113..5200e3b971 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/clusters.json +++ b/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/clusters.json @@ -9,7 +9,7 @@ }, { "cluster_id": "NWKV6vJP_LdYsXgXqAcaNCR0YcLJwar1ugh0ikEZsZs8", "host_guid": "e0ace90a-91ee-1257-4403-e8050a44c9b7", - "host_name": "ultimakersystem-ccbdd30044ec", + "host_name": "ultimakersystem-30044ecccbdd", "host_version": "5.1.2.20180807", "is_online": true, "status": "active" diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py index a043288e59..80ce54aeee 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py @@ -30,13 +30,18 @@ class TestCloudOutputDeviceManager(TestCase): # let the network send replies self.network.flushReplies() # get the created devices - devices = self.app.getOutputDeviceManager().getOutputDevices() + device_manager = self.app.getOutputDeviceManager() + devices = device_manager.getOutputDevices() # get the server data - clusters = self.clusters_response["data"] + clusters = self.clusters_response.get("data", []) self.assertEqual([CloudOutputDevice] * len(clusters), [type(d) for d in devices]) self.assertEqual({cluster["cluster_id"] for cluster in clusters}, {device.key for device in devices}) self.assertEqual({cluster["host_name"] for cluster in clusters}, {device.host_name for device in devices}) + for device in clusters: + device_manager.getOutputDevice(device["cluster_id"]).close() + device_manager.removeOutputDevice(device["cluster_id"]) + ## Runs the initial request to retrieve the clusters. def _loadData(self, network_mock): network_mock.return_value = self.network @@ -64,3 +69,39 @@ class TestCloudOutputDeviceManager(TestCase): self.network.prepareGetClusters(self.clusters_response) self.manager._update_timer.timeout.emit() + + @patch("cura.CuraApplication.CuraApplication.getGlobalContainerStack") + def test_device_connects_by_cluster_id(self, global_container_stack_mock, network_mock): + active_machine_mock = global_container_stack_mock.return_value + cluster1, cluster2 = self.clusters_response["data"] + cluster_id = cluster1["cluster_id"] + active_machine_mock.getMetaDataEntry.side_effect = {"um_cloud_cluster_id": cluster_id}.get + + self._loadData(network_mock) + self.network.flushReplies() + + self.assertTrue(self.app.getOutputDeviceManager().getOutputDevice(cluster1["cluster_id"]).isConnected()) + self.assertFalse(self.app.getOutputDeviceManager().getOutputDevice(cluster2["cluster_id"]).isConnected()) + + @patch("cura.CuraApplication.CuraApplication.getGlobalContainerStack") + def test_device_connects_by_network_key(self, global_container_stack_mock, network_mock): + active_machine_mock = global_container_stack_mock.return_value + + cluster1, cluster2 = self.clusters_response["data"] + network_key = cluster2["host_name"] + ".ultimaker.local" + active_machine_mock.getMetaDataEntry.side_effect = {"um_network_key": network_key}.get + + self._loadData(network_mock) + self.network.flushReplies() + + self.assertFalse(self.app.getOutputDeviceManager().getOutputDevice(cluster1["cluster_id"]).isConnected()) + self.assertTrue(self.app.getOutputDeviceManager().getOutputDevice(cluster2["cluster_id"]).isConnected()) + + active_machine_mock.setMetaDataEntry.assert_called_once_with("um_cloud_cluster_id", cluster2["cluster_id"]) + + @patch("UM.Message.Message.show") + def test_api_error(self, message_mock, network_mock): + self.clusters_response = {"errors": [{"id": "notFound"}]} + self.network.prepareGetClusters(self.clusters_response) + self._loadData(network_mock) + message_mock.assert_called_once_with() From 021c44862752905999fdd0aaf9fac3130cb3e5b2 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Mon, 10 Dec 2018 11:31:18 +0100 Subject: [PATCH 0773/1240] Remove depreciated monitor tab stuff Contributes to CL-1152 --- .../resources/qml/PrintCoreConfiguration.qml | 121 ----- .../resources/qml/PrintJobInfoBlock.qml | 505 ------------------ .../resources/qml/PrintJobPreview.qml | 75 --- .../resources/qml/PrintJobTitle.qml | 59 -- .../resources/qml/PrinterCard.qml | 241 --------- .../resources/qml/PrinterCardDetails.qml | 75 --- .../resources/qml/PrinterCardProgressBar.qml | 108 ---- .../resources/qml/PrinterFamilyPill.qml | 32 -- .../resources/qml/PrinterInfoBlock.qml | 83 --- 9 files changed, 1299 deletions(-) delete mode 100644 plugins/UM3NetworkPrinting/resources/qml/PrintCoreConfiguration.qml delete mode 100644 plugins/UM3NetworkPrinting/resources/qml/PrintJobInfoBlock.qml delete mode 100644 plugins/UM3NetworkPrinting/resources/qml/PrintJobPreview.qml delete mode 100644 plugins/UM3NetworkPrinting/resources/qml/PrintJobTitle.qml delete mode 100644 plugins/UM3NetworkPrinting/resources/qml/PrinterCard.qml delete mode 100644 plugins/UM3NetworkPrinting/resources/qml/PrinterCardDetails.qml delete mode 100644 plugins/UM3NetworkPrinting/resources/qml/PrinterCardProgressBar.qml delete mode 100644 plugins/UM3NetworkPrinting/resources/qml/PrinterFamilyPill.qml delete mode 100644 plugins/UM3NetworkPrinting/resources/qml/PrinterInfoBlock.qml diff --git a/plugins/UM3NetworkPrinting/resources/qml/PrintCoreConfiguration.qml b/plugins/UM3NetworkPrinting/resources/qml/PrintCoreConfiguration.qml deleted file mode 100644 index 7bcd9ce6e4..0000000000 --- a/plugins/UM3NetworkPrinting/resources/qml/PrintCoreConfiguration.qml +++ /dev/null @@ -1,121 +0,0 @@ -// 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.4 -import QtQuick.Controls.Styles 1.4 -import UM 1.2 as UM - -Item { - id: extruderInfo; - property var printCoreConfiguration: null; - height: childrenRect.height; - width: Math.round(parent.width / 2); - - // Extruder circle - Item { - id: extruderCircle; - height: UM.Theme.getSize("monitor_extruder_circle").height; - width: UM.Theme.getSize("monitor_extruder_circle").width; - - // Loading skeleton - Rectangle { - anchors.fill: parent; - color: UM.Theme.getColor("monitor_skeleton_fill"); - radius: Math.round(width / 2); - visible: !printCoreConfiguration; - } - - // Actual content - Rectangle { - anchors.fill: parent; - border.width: UM.Theme.getSize("monitor_thick_lining").width; - border.color: UM.Theme.getColor("monitor_lining_heavy"); - color: "transparent"; - opacity: { - if (printCoreConfiguration == null || printCoreConfiguration.activeMaterial == null || printCoreConfiguration.hotendID == null) { - return 0.5; - } - return 1; - } - radius: Math.round(width / 2); - visible: printCoreConfiguration; - - Label { - anchors.centerIn: parent; - color: UM.Theme.getColor("text"); - font: UM.Theme.getFont("default_bold"); - text: printCoreConfiguration ? printCoreConfiguration.position + 1 : 0; - } - } - } - - // Print core and material labels - Item { - id: materialLabel - anchors { - left: extruderCircle.right; - leftMargin: UM.Theme.getSize("default_margin").width; - right: parent.right; - top: parent.top; - } - height: UM.Theme.getSize("monitor_text_line").height; - - // Loading skeleton - Rectangle { - anchors.fill: parent; - color: UM.Theme.getColor("monitor_skeleton_fill"); - visible: !extruderInfo.printCoreConfiguration; - } - - // Actual content - Label { - anchors.fill: parent; - elide: Text.ElideRight; - color: UM.Theme.getColor("text"); - font: UM.Theme.getFont("default"); - text: { - if (printCoreConfiguration && printCoreConfiguration.activeMaterial != undefined) { - return printCoreConfiguration.activeMaterial.name; - } - return ""; - } - visible: extruderInfo.printCoreConfiguration; - } - } - - Item { - id: printCoreLabel; - anchors { - left: extruderCircle.right; - leftMargin: UM.Theme.getSize("default_margin").width; - right: parent.right; - top: materialLabel.bottom; - topMargin: Math.floor(UM.Theme.getSize("default_margin").height/4); - } - height: UM.Theme.getSize("monitor_text_line").height; - - // Loading skeleton - Rectangle { - color: UM.Theme.getColor("monitor_skeleton_fill"); - height: parent.height; - visible: !extruderInfo.printCoreConfiguration; - width: Math.round(parent.width / 3); - } - - // Actual content - Label { - color: UM.Theme.getColor("text"); - elide: Text.ElideRight; - font: UM.Theme.getFont("default"); - opacity: 0.6; - text: { - if (printCoreConfiguration != undefined && printCoreConfiguration.hotendID != undefined) { - return printCoreConfiguration.hotendID; - } - return ""; - } - visible: extruderInfo.printCoreConfiguration; - } - } -} diff --git a/plugins/UM3NetworkPrinting/resources/qml/PrintJobInfoBlock.qml b/plugins/UM3NetworkPrinting/resources/qml/PrintJobInfoBlock.qml deleted file mode 100644 index a611cb4ff6..0000000000 --- a/plugins/UM3NetworkPrinting/resources/qml/PrintJobInfoBlock.qml +++ /dev/null @@ -1,505 +0,0 @@ -// Copyright (c) 2018 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.2 -import QtQuick.Dialogs 1.1 -import QtQuick.Controls 2.0 -import QtQuick.Controls.Styles 1.4 -import QtGraphicalEffects 1.0 -import QtQuick.Layouts 1.1 -import QtQuick.Dialogs 1.1 -import UM 1.3 as UM - -Item { - id: root; - property var shadowRadius: UM.Theme.getSize("monitor_shadow_radius").width; - property var shadowOffset: 2 * screenScaleFactor; - property var debug: false; - property var printJob: null; - width: parent.width; // Bubbles downward - height: childrenRect.height + shadowRadius * 2; // Bubbles upward - - UM.I18nCatalog { - id: catalog; - name: "cura"; - } - - // The actual card (white block) - Rectangle { - // 5px margin, but shifted 2px vertically because of the shadow - anchors { - bottomMargin: root.shadowRadius + root.shadowOffset; - leftMargin: root.shadowRadius; - rightMargin: root.shadowRadius; - topMargin: root.shadowRadius - root.shadowOffset; - } - color: UM.Theme.getColor("monitor_card_background"); - height: childrenRect.height; - layer.enabled: true - layer.effect: DropShadow { - radius: root.shadowRadius - verticalOffset: 2 * screenScaleFactor - color: "#3F000000" // 25% shadow - } - width: parent.width - shadowRadius * 2; - - Column { - height: childrenRect.height; - width: parent.width; - - // Main content - Item { - id: mainContent; - height: 200 * screenScaleFactor; // TODO: Theme! - width: parent.width; - - // Left content - Item { - anchors { - bottom: parent.bottom; - left: parent.left; - margins: UM.Theme.getSize("wide_margin").width; - right: parent.horizontalCenter; - top: parent.top; - } - - Item { - id: printJobName; - width: parent.width; - height: UM.Theme.getSize("monitor_text_line").height; - - Rectangle { - color: UM.Theme.getColor("monitor_skeleton_fill"); - height: parent.height; - visible: !printJob; - width: Math.round(parent.width / 3); - } - Label { - anchors.fill: parent; - color: UM.Theme.getColor("text"); - elide: Text.ElideRight; - font: UM.Theme.getFont("default_bold"); - text: printJob && printJob.name ? printJob.name : ""; // Supress QML warnings - visible: printJob; - } - } - - Item { - id: printJobOwnerName; - anchors { - top: printJobName.bottom; - topMargin: Math.floor(UM.Theme.getSize("default_margin").height / 2); - } - height: UM.Theme.getSize("monitor_text_line").height; - width: parent.width; - - Rectangle { - color: UM.Theme.getColor("monitor_skeleton_fill"); - height: parent.height; - visible: !printJob; - width: Math.round(parent.width / 2); - } - Label { - anchors.fill: parent; - color: UM.Theme.getColor("text"); - elide: Text.ElideRight; - font: UM.Theme.getFont("default"); - text: printJob ? printJob.owner : ""; // Supress QML warnings - visible: printJob; - } - } - - Item { - id: printJobPreview; - property var useUltibot: false; - anchors { - bottom: parent.bottom; - horizontalCenter: parent.horizontalCenter; - top: printJobOwnerName.bottom; - topMargin: UM.Theme.getSize("default_margin").height; - } - width: height; - - // Skeleton - Rectangle { - anchors.fill: parent; - color: UM.Theme.getColor("monitor_skeleton_fill"); - radius: UM.Theme.getSize("default_margin").width; - visible: !printJob; - } - - // Actual content - Image { - id: previewImage; - anchors.fill: parent; - opacity: printJob && printJob.state == "error" ? 0.5 : 1.0; - source: printJob ? printJob.previewImageUrl : ""; - visible: printJob; - } - - UM.RecolorImage { - id: ultiBotImage; - - anchors.centerIn: printJobPreview; - color: UM.Theme.getColor("monitor_placeholder_image"); - height: printJobPreview.height; - source: "../svg/ultibot.svg"; - sourceSize { - height: height; - width: width; - } - /* Since print jobs ALWAYS have an image url, we have to check if that image URL errors or - not in order to determine if we show the placeholder (ultibot) image instead. */ - visible: printJob && previewImage.status == Image.Error; - width: printJobPreview.width; - } - - UM.RecolorImage { - id: statusImage; - anchors.centerIn: printJobPreview; - color: UM.Theme.getColor("monitor_image_overlay"); - height: 0.5 * printJobPreview.height; - source: printJob && printJob.state == "error" ? "../svg/aborted-icon.svg" : ""; - sourceSize { - height: height; - width: width; - } - visible: source != ""; - width: 0.5 * printJobPreview.width; - } - } - - Label { - id: totalTimeLabel; - anchors { - bottom: parent.bottom; - right: parent.right; - } - color: UM.Theme.getColor("text"); - elide: Text.ElideRight; - font: UM.Theme.getFont("default"); - text: printJob ? OutputDevice.formatDuration(printJob.timeTotal) : ""; - } - } - - // Divider - Rectangle { - anchors { - horizontalCenter: parent.horizontalCenter; - verticalCenter: parent.verticalCenter; - } - color: !printJob ? UM.Theme.getColor("monitor_skeleton_fill") : UM.Theme.getColor("monitor_lining_light"); - height: parent.height - 2 * UM.Theme.getSize("default_margin").height; - width: UM.Theme.getSize("default_lining").width; - } - - // Right content - Item { - anchors { - bottom: parent.bottom; - left: parent.horizontalCenter; - margins: UM.Theme.getSize("wide_margin").width; - right: parent.right; - top: parent.top; - } - - Item { - id: targetPrinterLabel; - height: UM.Theme.getSize("monitor_text_line").height; - width: parent.width; - - Rectangle { - visible: !printJob; - color: UM.Theme.getColor("monitor_skeleton_fill"); - anchors.fill: parent; - } - - Label { - color: UM.Theme.getColor("text"); - elide: Text.ElideRight; - font: UM.Theme.getFont("default_bold"); - text: { - if (printJob !== null) { - if (printJob.assignedPrinter == null) { - if (printJob.state == "error") { - return catalog.i18nc("@label", "Waiting for: Unavailable printer"); - } - return catalog.i18nc("@label", "Waiting for: First available"); - } else { - return catalog.i18nc("@label", "Waiting for: ") + printJob.assignedPrinter.name; - } - } - return ""; - } - visible: printJob; - } - } - - PrinterInfoBlock { - anchors.bottom: parent.bottom; - printer: root.printJon && root.printJob.assignedPrinter; - printJob: root.printJob; - } - } - - PrintJobContextMenu { - id: contextButton; - anchors { - right: mainContent.right; - rightMargin: UM.Theme.getSize("default_margin").width * 3 + root.shadowRadius; - top: mainContent.top; - topMargin: UM.Theme.getSize("default_margin").height; - } - printJob: root.printJob; - visible: root.printJob; - } - } - - Item { - id: configChangesBox; - height: childrenRect.height; - visible: printJob && printJob.configurationChanges.length !== 0; - width: parent.width; - - // Config change toggle - Rectangle { - id: configChangeToggle; - color: { - if (configChangeToggleArea.containsMouse) { - return UM.Theme.getColor("viewport_background"); // TODO: Theme! - } else { - return "transparent"; - } - } - width: parent.width; - height: UM.Theme.getSize("default_margin").height * 4; // TODO: Theme! - anchors { - left: parent.left; - right: parent.right; - top: parent.top; - } - - Rectangle { - color: !printJob ? UM.Theme.getColor("monitor_skeleton_fill") : UM.Theme.getColor("monitor_lining_light"); - height: UM.Theme.getSize("default_lining").height; - width: parent.width; - } - - UM.RecolorImage { - anchors { - right: configChangeToggleLabel.left; - rightMargin: UM.Theme.getSize("default_margin").width; - verticalCenter: parent.verticalCenter; - } - color: UM.Theme.getColor("text"); - height: 23 * screenScaleFactor; // TODO: Theme! - source: "../svg/warning-icon.svg"; - sourceSize { - height: height; - width: width; - } - width: 23 * screenScaleFactor; // TODO: Theme! - } - - Label { - id: configChangeToggleLabel; - anchors { - horizontalCenter: parent.horizontalCenter; - verticalCenter: parent.verticalCenter; - } - color: UM.Theme.getColor("text"); - font: UM.Theme.getFont("default"); - text: catalog.i18nc("@label", "Configuration change"); - } - - UM.RecolorImage { - anchors { - left: configChangeToggleLabel.right; - leftMargin: UM.Theme.getSize("default_margin").width; - verticalCenter: parent.verticalCenter; - } - color: UM.Theme.getColor("text"); - height: 15 * screenScaleFactor; // TODO: Theme! - source: { - if (configChangeDetails.visible) { - return UM.Theme.getIcon("arrow_top"); - } else { - return UM.Theme.getIcon("arrow_bottom"); - } - } - sourceSize { - width: width; - height: height; - } - width: 15 * screenScaleFactor; // TODO: Theme! - } - - MouseArea { - id: configChangeToggleArea; - anchors.fill: parent; - hoverEnabled: true; - onClicked: { - configChangeDetails.visible = !configChangeDetails.visible; - } - } - } - - // Config change details - Item { - id: configChangeDetails; - anchors.top: configChangeToggle.bottom; - Behavior on height { NumberAnimation { duration: 100 } } - // In case of really massive multi-line configuration changes - height: visible ? Math.max(UM.Theme.getSize("monitor_config_override_box").height, childrenRect.height) : 0; - visible: false; - width: parent.width; - - Item { - anchors { - bottomMargin: UM.Theme.getSize("wide_margin").height; - fill: parent; - leftMargin: UM.Theme.getSize("wide_margin").height * 4; - rightMargin: UM.Theme.getSize("wide_margin").height * 4; - topMargin: UM.Theme.getSize("wide_margin").height; - } - clip: true; - - Label { - anchors.fill: parent; - elide: Text.ElideRight; - color: UM.Theme.getColor("text"); - font: UM.Theme.getFont("default"); - text: { - if (!printJob || printJob.configurationChanges.length === 0) { - return ""; - } - var topLine; - if (materialsAreKnown(printJob)) { - topLine = catalog.i18nc("@label", "The assigned printer, %1, requires the following configuration change(s):").arg(printJob.assignedPrinter.name); - } else { - topLine = catalog.i18nc("@label", "The printer %1 is assigned, but the job contains an unknown material configuration.").arg(printJob.assignedPrinter.name); - } - var result = "

    " + topLine +"

    "; - for (var i = 0; i < printJob.configurationChanges.length; i++) { - var change = printJob.configurationChanges[i]; - var text; - switch (change.typeOfChange) { - case "material_change": - text = catalog.i18nc("@label", "Change material %1 from %2 to %3.").arg(change.index + 1).arg(change.originName).arg(change.targetName); - break; - case "material_insert": - text = catalog.i18nc("@label", "Load %3 as material %1 (This cannot be overridden).").arg(change.index + 1).arg(change.targetName); - break; - case "print_core_change": - text = catalog.i18nc("@label", "Change print core %1 from %2 to %3.").arg(change.index + 1).arg(change.originName).arg(change.targetName); - break; - case "buildplate_change": - text = catalog.i18nc("@label", "Change build plate to %1 (This cannot be overridden).").arg(formatBuildPlateType(change.target_name)); - break; - default: - text = ""; - } - result += "

    " + text + "

    "; - } - return result; - } - wrapMode: Text.WordWrap; - } - - Button { - anchors { - bottom: parent.bottom; - left: parent.left; - } - background: Rectangle { - border { - color: UM.Theme.getColor("monitor_lining_heavy"); - width: UM.Theme.getSize("default_lining").width; - } - color: parent.hovered ? UM.Theme.getColor("monitor_card_background_inactive") : UM.Theme.getColor("monitor_card_background"); - implicitHeight: UM.Theme.getSize("default_margin").height * 3; - implicitWidth: UM.Theme.getSize("default_margin").height * 8; - } - contentItem: Label { - color: UM.Theme.getColor("text"); - font: UM.Theme.getFont("medium"); - horizontalAlignment: Text.AlignHCenter; - text: parent.text; - verticalAlignment: Text.AlignVCenter; - } - onClicked: { - overrideConfirmationDialog.visible = true; - } - text: catalog.i18nc("@label", "Override"); - visible: { - if (printJob && printJob.configurationChanges) { - var length = printJob.configurationChanges.length; - for (var i = 0; i < length; i++) { - var typeOfChange = printJob.configurationChanges[i].typeOfChange; - if (typeOfChange === "material_insert" || typeOfChange === "buildplate_change") { - return false; - } - } - } - return true; - } - } - } - } - - MessageDialog { - id: overrideConfirmationDialog; - Component.onCompleted: visible = false; - icon: StandardIcon.Warning; - onYes: OutputDevice.forceSendJob(printJob.key); - standardButtons: StandardButton.Yes | StandardButton.No; - text: { - if (!printJob) { - return ""; - } - var printJobName = formatPrintJobName(printJob.name); - var confirmText = catalog.i18nc("@label", "Starting a print job with an incompatible configuration could damage your 3D printer. Are you sure you want to override the configuration and print %1?").arg(printJobName); - return confirmText; - } - title: catalog.i18nc("@window:title", "Override configuration configuration and start print"); - } - } - } - } - // Utils - function formatPrintJobName(name) { - var extensions = [ ".gz", ".gcode", ".ufp" ]; - for (var i = 0; i < extensions.length; i++) { - var extension = extensions[i]; - if (name.slice(-extension.length) === extension) { - name = name.substring(0, name.length - extension.length); - } - } - return name; - } - function materialsAreKnown(job) { - var conf0 = job.configuration[0]; - if (conf0 && !conf0.material.material) { - return false; - } - var conf1 = job.configuration[1]; - if (conf1 && !conf1.material.material) { - return false; - } - return true; - } - function formatBuildPlateType(buildPlateType) { - var translationText = ""; - switch (buildPlateType) { - case "glass": - translationText = catalog.i18nc("@label", "Glass"); - break; - case "aluminum": - translationText = catalog.i18nc("@label", "Aluminum"); - break; - default: - translationText = null; - } - return translationText; - } -} diff --git a/plugins/UM3NetworkPrinting/resources/qml/PrintJobPreview.qml b/plugins/UM3NetworkPrinting/resources/qml/PrintJobPreview.qml deleted file mode 100644 index b1a73255f4..0000000000 --- a/plugins/UM3NetworkPrinting/resources/qml/PrintJobPreview.qml +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2018 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.3 -import QtQuick.Dialogs 1.1 -import QtQuick.Controls 2.0 -import QtQuick.Controls.Styles 1.3 -import QtGraphicalEffects 1.0 -import QtQuick.Controls 1.4 as LegacyControls -import UM 1.3 as UM - -// Includes print job name, owner, and preview - -Item { - property var job: null; - property var useUltibot: false; - height: 100 * screenScaleFactor; - width: height; - - // Skeleton - Rectangle { - anchors.fill: parent; - color: UM.Theme.getColor("monitor_skeleton_fill"); - radius: UM.Theme.getSize("default_margin").width; - visible: !job; - } - - // Actual content - Image { - id: previewImage; - visible: job; - source: job ? job.previewImageUrl : ""; - opacity: { - if (job == null) { - return 1.0; - } - var states = ["wait_cleanup", "wait_user_action", "error", "paused"]; - if (states.indexOf(job.state) !== -1) { - return 0.5; - } - return 1.0; - } - anchors.fill: parent; - } - - UM.RecolorImage { - id: ultibotImage; - anchors.centerIn: parent; - color: UM.Theme.getColor("monitor_placeholder_image"); // TODO: Theme! - height: parent.height; - source: "../svg/ultibot.svg"; - sourceSize { - height: height; - width: width; - } - /* Since print jobs ALWAYS have an image url, we have to check if that image URL errors or - not in order to determine if we show the placeholder (ultibot) image instead. */ - visible: job && previewImage.status == Image.Error; - width: parent.width; - } - - UM.RecolorImage { - id: statusImage; - anchors.centerIn: parent; - color: "black"; // TODO: Theme! - height: Math.round(0.5 * parent.height); - source: job && job.state == "error" ? "../svg/aborted-icon.svg" : ""; - sourceSize { - height: height; - width: width; - } - visible: source != ""; - width: Math.round(0.5 * parent.width); - } -} \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/resources/qml/PrintJobTitle.qml b/plugins/UM3NetworkPrinting/resources/qml/PrintJobTitle.qml deleted file mode 100644 index f9f7b5ae87..0000000000 --- a/plugins/UM3NetworkPrinting/resources/qml/PrintJobTitle.qml +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) 2018 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.3 -import QtQuick.Controls 2.0 -import UM 1.3 as UM - -Column { - property var job: null; - height: childrenRect.height; - spacing: Math.floor( UM.Theme.getSize("default_margin").height / 2); // TODO: Use explicit theme size - width: parent.width; - - Item { - id: jobName; - height: UM.Theme.getSize("monitor_text_line").height; - width: parent.width; - - // Skeleton loading - Rectangle { - color: UM.Theme.getColor("monitor_skeleton_fill"); - height: parent.height; - visible: !job; - width: Math.round(parent.width / 3); - } - - Label { - anchors.fill: parent; - color: UM.Theme.getColor("text"); - elide: Text.ElideRight; - font: UM.Theme.getFont("default_bold"); - text: job && job.name ? job.name : ""; - visible: job; - } - } - - Item { - id: ownerName; - height: UM.Theme.getSize("monitor_text_line").height; - width: parent.width; - - // Skeleton loading - Rectangle { - color: UM.Theme.getColor("monitor_skeleton_fill"); - height: parent.height; - visible: !job; - width: Math.round(parent.width / 2); - } - - Label { - anchors.fill: parent; - color: UM.Theme.getColor("text") - elide: Text.ElideRight; - font: UM.Theme.getFont("default"); - text: job ? job.owner : ""; - visible: job; - } - } -} \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/resources/qml/PrinterCard.qml b/plugins/UM3NetworkPrinting/resources/qml/PrinterCard.qml deleted file mode 100644 index 24beaf70fe..0000000000 --- a/plugins/UM3NetworkPrinting/resources/qml/PrinterCard.qml +++ /dev/null @@ -1,241 +0,0 @@ -// Copyright (c) 2018 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.3 -import QtQuick.Dialogs 1.1 -import QtQuick.Controls 2.0 -import QtQuick.Controls.Styles 1.3 -import QtGraphicalEffects 1.0 -import UM 1.3 as UM - -Item { - id: root; - property var shadowRadius: UM.Theme.getSize("monitor_shadow_radius").width; - property var shadowOffset: UM.Theme.getSize("monitor_shadow_offset").width; - property var printer: null; - property var collapsed: true; - height: childrenRect.height + shadowRadius * 2; // Bubbles upward - width: parent.width; // Bubbles downward - - // The actual card (white block) - Rectangle { - // 5px margin, but shifted 2px vertically because of the shadow - anchors { - bottomMargin: root.shadowRadius + root.shadowOffset; - leftMargin: root.shadowRadius; - rightMargin: root.shadowRadius; - topMargin: root.shadowRadius - root.shadowOffset; - } - color: { - if (!printer) { - return UM.Theme.getColor("monitor_card_background_inactive"); - } - if (printer.state == "disabled") { - return UM.Theme.getColor("monitor_card_background_inactive"); - } else { - return UM.Theme.getColor("monitor_card_background"); - } - } - height: childrenRect.height; - layer.effect: DropShadow { - radius: root.shadowRadius; - verticalOffset: root.shadowOffset; - color: "#3F000000"; // 25% shadow - } - layer.enabled: true - width: parent.width - 2 * shadowRadius; - - Column { - id: cardContents; - height: childrenRect.height; - width: parent.width; - - // Main card - Item { - id: mainCard; - anchors { - left: parent.left; - leftMargin: UM.Theme.getSize("default_margin").width; - right: parent.right; - rightMargin: UM.Theme.getSize("default_margin").width; - } - height: 60 * screenScaleFactor + 2 * UM.Theme.getSize("default_margin").height; - width: parent.width; - - // Machine icon - Item { - id: machineIcon; - anchors.verticalCenter: parent.verticalCenter; - height: parent.height - 2 * UM.Theme.getSize("default_margin").width; - width: height; - - // Skeleton - Rectangle { - anchors.fill: parent; - color: UM.Theme.getColor("monitor_skeleton_fill_dark"); - radius: UM.Theme.getSize("default_margin").width; - visible: !printer; - } - - // Content - UM.RecolorImage { - anchors.centerIn: parent; - color: { - if (printer && printer.activePrintJob != undefined) { - return UM.Theme.getColor("monitor_printer_icon"); - } - return UM.Theme.getColor("monitor_printer_icon_inactive"); - } - height: sourceSize.height; - source: { - if (!printer) { - return ""; - } - switch(printer.type) { - case "Ultimaker 3": - return "../svg/UM3-icon.svg"; - case "Ultimaker 3 Extended": - return "../svg/UM3x-icon.svg"; - case "Ultimaker S5": - return "../svg/UMs5-icon.svg"; - } - } - visible: printer; - width: sourceSize.width; - } - } - - // Printer info - Item { - id: printerInfo; - anchors { - left: machineIcon.right; - leftMargin: UM.Theme.getSize("wide_margin").width; - right: collapseIcon.left; - verticalCenter: machineIcon.verticalCenter; - } - height: childrenRect.height; - - // Machine name - Item { - id: machineNameLabel; - height: UM.Theme.getSize("monitor_text_line").height; - width: { - var percent = printer ? 0.75 : 0.3; - return Math.round(parent.width * percent); - } - - // Skeleton - Rectangle { - anchors.fill: parent; - color: UM.Theme.getColor("monitor_skeleton_fill_dark"); - visible: !printer; - } - - // Actual content - Label { - anchors.fill: parent; - color: UM.Theme.getColor("text"); - elide: Text.ElideRight; - font: UM.Theme.getFont("default_bold"); - text: printer ? printer.name : ""; - visible: printer; - width: parent.width; - } - } - - // Job name - Item { - id: activeJobLabel; - anchors { - top: machineNameLabel.bottom; - topMargin: Math.round(UM.Theme.getSize("default_margin").height / 2); - } - height: UM.Theme.getSize("monitor_text_line").height; - width: Math.round(parent.width * 0.75); - - // Skeleton - Rectangle { - anchors.fill: parent; - color: UM.Theme.getColor("monitor_skeleton_fill_dark"); - visible: !printer; - } - - // Actual content - Label { - anchors.fill: parent; - color: UM.Theme.getColor("monitor_text_inactive"); - elide: Text.ElideRight; - font: UM.Theme.getFont("default"); - text: { - if (!printer) { - return ""; - } - if (printer.state == "disabled") { - return catalog.i18nc("@label", "Not available"); - } else if (printer.state == "unreachable") { - return catalog.i18nc("@label", "Unreachable"); - } - if (printer.activePrintJob != null && printer.activePrintJob.name) { - return printer.activePrintJob.name; - } - return catalog.i18nc("@label", "Available"); - } - visible: printer; - } - } - } - - // Collapse icon - UM.RecolorImage { - id: collapseIcon; - anchors { - right: parent.right; - rightMargin: UM.Theme.getSize("default_margin").width; - verticalCenter: parent.verticalCenter; - } - color: UM.Theme.getColor("text"); - height: 15 * screenScaleFactor; // TODO: Theme! - source: root.collapsed ? UM.Theme.getIcon("arrow_left") : UM.Theme.getIcon("arrow_bottom"); - sourceSize { - height: height; - width: width; - } - visible: printer; - width: 15 * screenScaleFactor; // TODO: Theme! - } - - MouseArea { - anchors.fill: parent; - enabled: printer; - onClicked: { - if (model && root.collapsed) { - printerList.currentIndex = model.index; - } else { - printerList.currentIndex = -1; - } - } - } - - Connections { - target: printerList; - onCurrentIndexChanged: { - root.collapsed = printerList.currentIndex != model.index; - } - } - } - // Detailed card - PrinterCardDetails { - collapsed: root.collapsed; - printer: root.printer; - visible: root.printer; - } - - // Progress bar - PrinterCardProgressBar { - visible: printer && printer.activePrintJob != null; - width: parent.width; - } - } - } -} diff --git a/plugins/UM3NetworkPrinting/resources/qml/PrinterCardDetails.qml b/plugins/UM3NetworkPrinting/resources/qml/PrinterCardDetails.qml deleted file mode 100644 index 31da388b00..0000000000 --- a/plugins/UM3NetworkPrinting/resources/qml/PrinterCardDetails.qml +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2018 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.3 -import QtQuick.Dialogs 1.1 -import QtQuick.Controls 2.0 -import QtQuick.Controls.Styles 1.3 -import QtGraphicalEffects 1.0 -import QtQuick.Controls 1.4 as LegacyControls -import UM 1.3 as UM - -Item { - id: root; - property var printer: null; - property var printJob: printer ? printer.activePrintJob : null; - property var collapsed: true; - Behavior on height { NumberAnimation { duration: 100 } } - Behavior on opacity { NumberAnimation { duration: 100 } } - height: collapsed ? 0 : childrenRect.height; - opacity: collapsed ? 0 : 1; - width: parent.width; - - Column { - id: contentColumn; - anchors { - left: parent.left; - leftMargin: UM.Theme.getSize("default_margin").width; - right: parent.right; - rightMargin: UM.Theme.getSize("default_margin").width; - } - height: childrenRect.height + UM.Theme.getSize("default_margin").height; - spacing: UM.Theme.getSize("default_margin").height; - width: parent.width; - - HorizontalLine {} - - PrinterInfoBlock { - printer: root.printer; - printJob: root.printer ? root.printer.activePrintJob : null; - } - - HorizontalLine {} - - Row { - height: childrenRect.height; - visible: root.printJob; - width: parent.width; - - PrintJobTitle { - job: root.printer ? root.printer.activePrintJob : null; - } - PrintJobContextMenu { - id: contextButton; - anchors { - right: parent.right; - rightMargin: UM.Theme.getSize("wide_margin").width; - } - printJob: root.printer ? root.printer.activePrintJob : null; - visible: printJob; - } - } - - PrintJobPreview { - anchors.horizontalCenter: parent.horizontalCenter; - job: root.printer && root.printer.activePrintJob ? root.printer.activePrintJob : null; - visible: root.printJob; - } - - CameraButton { - id: showCameraButton; - iconSource: "../svg/camera-icon.svg"; - visible: root.printer; - } - } -} diff --git a/plugins/UM3NetworkPrinting/resources/qml/PrinterCardProgressBar.qml b/plugins/UM3NetworkPrinting/resources/qml/PrinterCardProgressBar.qml deleted file mode 100644 index e86c959b8c..0000000000 --- a/plugins/UM3NetworkPrinting/resources/qml/PrinterCardProgressBar.qml +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (c) 2018 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.3 -import QtQuick.Controls.Styles 1.3 -import QtQuick.Controls 1.4 -import UM 1.3 as UM - -ProgressBar { - property var progress: { - if (!printer || printer.activePrintJob == null) { - return 0; - } - var result = printer.activePrintJob.timeElapsed / printer.activePrintJob.timeTotal; - if (result > 1.0) { - result = 1.0; - } - return result; - } - style: ProgressBarStyle { - property var remainingTime: { - if (!printer || printer.activePrintJob == null) { - return 0; - } - /* Sometimes total minus elapsed is less than 0. Use Math.max() to prevent remaining - time from ever being less than 0. Negative durations cause strange behavior such - as displaying "-1h -1m". */ - return Math.max(printer.activePrintJob.timeTotal - printer.activePrintJob.timeElapsed, 0); - } - property var progressText: { - if (printer === null ) { - return ""; - } - switch (printer.activePrintJob.state) { - case "wait_cleanup": - if (printer.activePrintJob.timeTotal > printer.activePrintJob.timeElapsed) { - return catalog.i18nc("@label:status", "Aborted"); - } - return catalog.i18nc("@label:status", "Finished"); - case "pre_print": - case "sent_to_printer": - return catalog.i18nc("@label:status", "Preparing"); - case "aborted": - return catalog.i18nc("@label:status", "Aborted"); - case "wait_user_action": - return catalog.i18nc("@label:status", "Aborted"); - case "pausing": - return catalog.i18nc("@label:status", "Pausing"); - case "paused": - return OutputDevice.formatDuration( remainingTime ); - case "resuming": - return catalog.i18nc("@label:status", "Resuming"); - case "queued": - return catalog.i18nc("@label:status", "Action required"); - default: - return OutputDevice.formatDuration( remainingTime ); - } - } - background: Rectangle { - color: UM.Theme.getColor("monitor_progress_background"); - implicitHeight: visible ? 24 : 0; - implicitWidth: 100; - } - progress: Rectangle { - id: progressItem; - color: { - if (! printer || !printer.activePrintJob) { - return "black"; - } - var state = printer.activePrintJob.state - var inactiveStates = [ - "pausing", - "paused", - "resuming", - "wait_cleanup" - ]; - if (inactiveStates.indexOf(state) > -1 && remainingTime > 0) { - return UM.Theme.getColor("monitor_progress_fill_inactive"); - } else { - return UM.Theme.getColor("monitor_progress_fill"); - } - } - - Label { - id: progressLabel; - anchors { - left: parent.left; - leftMargin: getTextOffset(); - } - text: progressText; - anchors.verticalCenter: parent.verticalCenter; - color: progressItem.width + progressLabel.width < control.width ? UM.Theme.getColor("text") : UM.Theme.getColor("monitor_progress_fill_text"); - width: contentWidth; - font: UM.Theme.getFont("default"); - } - - function getTextOffset() { - if (progressItem.width + progressLabel.width + 16 < control.width) { - return progressItem.width + UM.Theme.getSize("default_margin").width; - } else { - return progressItem.width - progressLabel.width - UM.Theme.getSize("default_margin").width; - } - } - } - } - value: progress; - width: parent.width; -} \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/resources/qml/PrinterFamilyPill.qml b/plugins/UM3NetworkPrinting/resources/qml/PrinterFamilyPill.qml deleted file mode 100644 index 0a88b053a8..0000000000 --- a/plugins/UM3NetworkPrinting/resources/qml/PrinterFamilyPill.qml +++ /dev/null @@ -1,32 +0,0 @@ -// 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.4 -import UM 1.2 as UM - -Item { - property alias text: familyNameLabel.text; - property var padding: 3 * screenScaleFactor; // TODO: Theme! - implicitHeight: familyNameLabel.contentHeight + 2 * padding; // Apply the padding to top and bottom. - implicitWidth: Math.max(48 * screenScaleFactor, familyNameLabel.contentWidth + implicitHeight); // The extra height is added to ensure the radius doesn't cut something off. - - Rectangle { - id: background; - anchors { - horizontalCenter: parent.horizontalCenter; - right: parent.right; - } - color: familyNameLabel.text.length < 1 ? UM.Theme.getColor("monitor_skeleton_fill") : UM.Theme.getColor("monitor_pill_background"); - height: parent.height; - radius: 0.5 * height; - width: parent.width; - } - - Label { - id: familyNameLabel; - anchors.centerIn: parent; - color: UM.Theme.getColor("text"); - text: ""; - } -} \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/resources/qml/PrinterInfoBlock.qml b/plugins/UM3NetworkPrinting/resources/qml/PrinterInfoBlock.qml deleted file mode 100644 index 92a8f1dcb3..0000000000 --- a/plugins/UM3NetworkPrinting/resources/qml/PrinterInfoBlock.qml +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) 2018 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.3 -import QtQuick.Dialogs 1.1 -import QtQuick.Controls 2.0 -import QtQuick.Controls.Styles 1.3 -import QtGraphicalEffects 1.0 -import QtQuick.Controls 1.4 as LegacyControls -import UM 1.3 as UM - -// Includes printer type pill and extuder configurations - -Item { - id: root; - property var printer: null; - property var printJob: null; - width: parent.width; - height: childrenRect.height; - - // Printer family pills - Row { - id: printerFamilyPills; - anchors { - left: parent.left; - right: parent.right; - } - height: childrenRect.height; - spacing: Math.round(0.5 * UM.Theme.getSize("default_margin").width); - width: parent.width; - - Repeater { - id: compatiblePills; - delegate: PrinterFamilyPill { - text: modelData; - } - model: printJob ? printJob.compatibleMachineFamilies : []; - visible: printJob; - - } - - PrinterFamilyPill { - text: printer ? printer.type : ""; - visible: !compatiblePills.visible && printer; - } - } - - // Extruder info - Row { - id: extrudersInfo; - anchors { - left: parent.left; - right: parent.right; - rightMargin: UM.Theme.getSize("default_margin").width; - top: printerFamilyPills.bottom; - topMargin: UM.Theme.getSize("default_margin").height; - } - height: childrenRect.height; - spacing: UM.Theme.getSize("default_margin").width; - width: parent.width; - - PrintCoreConfiguration { - width: Math.round(parent.width / 2) * screenScaleFactor; - printCoreConfiguration: getExtruderConfig(0); - } - - PrintCoreConfiguration { - width: Math.round(parent.width / 2) * screenScaleFactor; - printCoreConfiguration: getExtruderConfig(1); - } - } - - function getExtruderConfig( i ) { - if (root.printJob) { - // Use more-specific print job if possible - return root.printJob.configuration.extruderConfigurations[i]; - } - if (root.printer) { - return root.printer.printerConfiguration.extruderConfigurations[i]; - } - return null; - } -} \ No newline at end of file From 0d55d023a193c651a28e69cae667d5daa0ae2548 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 10 Dec 2018 11:36:03 +0100 Subject: [PATCH 0774/1240] Align the message colors with the ones in the designs. Contributes to CURA-6018. --- resources/themes/cura-light/theme.json | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 0ef6c24bfb..738f3e5c3f 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -248,11 +248,13 @@ "message_background": [255, 255, 255, 255], "message_shadow": [0, 0, 0, 120], - "message_border": [127, 127, 127, 255], + "message_border": [192, 193, 194, 255], "message_text": [0, 0, 0, 255], - "message_button": [50, 130, 255, 255], - "message_button_hover": [50, 130, 255, 255], - "message_button_active": [50, 130, 255, 255], + "message_close": [102, 102, 102, 255], + "message_close_hover": [8, 7, 63, 255], + "message_button": [38, 113, 231, 255], + "message_button_hover": [81, 145, 247, 255], + "message_button_active": [38, 113, 231, 255], "message_button_text": [255, 255, 255, 255], "message_button_text_hover": [255, 255, 255, 255], "message_button_text_active": [255, 255, 255, 255], @@ -478,6 +480,8 @@ "message_shadow": [0, 0], "message_margin": [0, 1.0], "message_inner_margin": [1.5, 1.5], + "message_radius": [0.25, 0.25], + "message_button_radius": [0.15, 0.15], "infill_button_margin": [0.5, 0.5], From ad7dcf6fc0700ebfdb424644fbeb8569a2f9f446 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Mon, 10 Dec 2018 11:36:12 +0100 Subject: [PATCH 0775/1240] Delete ClusterControlItem.qml Contributes to CL-1152 --- .../resources/qml/ClusterControlItem.qml | 110 ------------------ 1 file changed, 110 deletions(-) delete mode 100644 plugins/UM3NetworkPrinting/resources/qml/ClusterControlItem.qml diff --git a/plugins/UM3NetworkPrinting/resources/qml/ClusterControlItem.qml b/plugins/UM3NetworkPrinting/resources/qml/ClusterControlItem.qml deleted file mode 100644 index 94e75a6de0..0000000000 --- a/plugins/UM3NetworkPrinting/resources/qml/ClusterControlItem.qml +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) 2018 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.3 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.3 -import UM 1.3 as UM -import Cura 1.0 as Cura - -Component { - Rectangle { - id: base; - property var shadowRadius: UM.Theme.getSize("monitor_shadow_radius").width; - property var cornerRadius: UM.Theme.getSize("monitor_corner_radius").width; - anchors.fill: parent; - color: UM.Theme.getColor("main_background"); - visible: OutputDevice != null; - - UM.I18nCatalog { - id: catalog; - name: "cura"; - } - - Label { - id: printingLabel; - anchors { - left: parent.left; - leftMargin: 4 * UM.Theme.getSize("default_margin").width; - margins: 2 * UM.Theme.getSize("default_margin").width; - right: parent.right; - top: parent.top; - } - color: UM.Theme.getColor("text"); - elide: Text.ElideRight; - font: UM.Theme.getFont("large"); - text: catalog.i18nc("@label", "Printing"); - } - - Label { - id: managePrintersLabel; - anchors { - bottom: printingLabel.bottom; - right: printerScrollView.right; - rightMargin: 4 * UM.Theme.getSize("default_margin").width; - } - color: UM.Theme.getColor("primary"); // "Cura Blue" - font: UM.Theme.getFont("default"); - linkColor: UM.Theme.getColor("primary"); // "Cura Blue" - text: catalog.i18nc("@label link to connect manager", "Manage printers"); - } - - MouseArea { - anchors.fill: managePrintersLabel; - hoverEnabled: true; - onClicked: Cura.MachineManager.printerOutputDevices[0].openPrinterControlPanel(); - onEntered: managePrintersLabel.font.underline = true; - onExited: managePrintersLabel.font.underline = false; - } - - // Skeleton loading - Column { - id: skeletonLoader; - anchors { - left: parent.left; - leftMargin: UM.Theme.getSize("wide_margin").width; - right: parent.right; - rightMargin: UM.Theme.getSize("wide_margin").width; - top: printingLabel.bottom; - topMargin: UM.Theme.getSize("default_margin").height; - } - spacing: UM.Theme.getSize("default_margin").height - 10; - visible: printerList.count === 0; - - PrinterCard { - printer: null; - } - PrinterCard { - printer: null; - } - } - - // Actual content - ScrollView { - id: printerScrollView; - anchors { - bottom: parent.bottom; - left: parent.left; - right: parent.right; - top: printingLabel.bottom; - topMargin: UM.Theme.getSize("default_margin").height; - } - style: UM.Theme.styles.scrollview; - - ListView { - id: printerList; - property var currentIndex: -1; - anchors { - fill: parent; - leftMargin: UM.Theme.getSize("wide_margin").width; - rightMargin: UM.Theme.getSize("wide_margin").width; - } - delegate: PrinterCard { - printer: modelData; - } - model: OutputDevice.printers; - spacing: UM.Theme.getSize("default_margin").height - 10; - } - } - } -} From 8a856f13d26c44668cb086729b22534b438d9a2b Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Mon, 10 Dec 2018 11:36:51 +0100 Subject: [PATCH 0776/1240] Re-add config changes in monitor tab Contributes to CL-1152 --- .../resources/qml/ClusterMonitorItem.qml | 2 +- .../resources/qml/DiscoverUM3Action.qml | 2 +- .../resources/qml/HorizontalLine.qml | 12 -- .../qml/MonitorConfigOverrideDialog.qml | 131 ++++++++++++++++++ .../resources/qml/MonitorPrintJobCard.qml | 1 + .../resources/qml/MonitorPrintJobPreview.qml | 7 +- .../qml/MonitorPrintJobProgressBar.qml | 2 + .../resources/qml/MonitorPrinterCard.qml | 75 +++++++++- .../src/ClusterUM3OutputDevice.py | 10 -- 9 files changed, 210 insertions(+), 32 deletions(-) delete mode 100644 plugins/UM3NetworkPrinting/resources/qml/HorizontalLine.qml create mode 100644 plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml diff --git a/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml b/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml index adf5ea5e1c..31b3f4d4f5 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml @@ -130,7 +130,7 @@ Component verticalCenter: externalLinkIcon.verticalCenter } color: UM.Theme.getColor("primary") - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("default") // 12pt, regular linkColor: UM.Theme.getColor("primary") text: catalog.i18nc("@label link to connect manager", "Manage queue in Cura Connect") } diff --git a/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml b/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml index bb710127fc..e5f668c70d 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml @@ -57,7 +57,7 @@ Cura.MachineAction spacing: UM.Theme.getSize("default_margin").height SystemPalette { id: palette } - UM.I18nCatalog { id: catalog; name: "cura" } + UM.I18nCatalog { id: catalog; name:"cura" } Label { id: pageTitle diff --git a/plugins/UM3NetworkPrinting/resources/qml/HorizontalLine.qml b/plugins/UM3NetworkPrinting/resources/qml/HorizontalLine.qml deleted file mode 100644 index aeb92697ad..0000000000 --- a/plugins/UM3NetworkPrinting/resources/qml/HorizontalLine.qml +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2018 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.3 -import QtQuick.Controls 2.0 -import UM 1.3 as UM - -Rectangle { - color: UM.Theme.getColor("monitor_lining_light"); // TODO: Maybe theme separately? Maybe not. - height: UM.Theme.getSize("default_lining").height; - width: parent.width; -} \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml new file mode 100644 index 0000000000..aa62afa083 --- /dev/null +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml @@ -0,0 +1,131 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.3 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.3 +import QtQuick.Dialogs 1.2 +import UM 1.3 as UM + +UM.Dialog +{ + id: overrideConfirmationDialog + + property var printer: null + + minimumWidth: screenScaleFactor * 640; + minimumHeight: screenScaleFactor * 320; + width: minimumWidth + height: minimumHeight + title: catalog.i18nc("@title:window", "Configuration Changes") + rightButtons: + [ + Button + { + id: overrideButton + anchors.margins: UM.Theme.getSize("default_margin").width + text: catalog.i18nc("@action:button", "Override") + onClicked: + { + OutputDevice.forceSendJob(printer.activePrintJob.key) + overrideConfirmationDialog.close() + } + }, + Button + { + id: cancelButton + anchors.margins: UM.Theme.getSize("default_margin").width + text: catalog.i18nc("@action:button", "Cancel") + onClicked: + { + overrideConfirmationDialog.reject() + } + } + ] + + Label + { + anchors + { + fill: parent + leftMargin: 60 + rightMargin: 60 + topMargin: 18 + bottomMargin: 56 + } + wrapMode: Text.WordWrap + text: + { + var topLine + if (materialsAreKnown(printer.activePrintJob)) + { + topLine = catalog.i18nc("@label", "The assigned printer, %1, requires the following configuration change(s):").arg(printer.name) + } + else + { + topLine = catalog.i18nc("@label", "The printer %1 is assigned, but the job contains an unknown material configuration.").arg(printer.name) + } + var result = "

    " + topLine +"

    " + for (var i = 0; i < printer.activePrintJob.configurationChanges.length; i++) + { + var change = printer.activePrintJob.configurationChanges[i] + var text + switch (change.typeOfChange) + { + case "material_change": + text = catalog.i18nc("@label", "Change material %1 from %2 to %3.").arg(change.index + 1).arg(change.originName).arg(change.targetName) + break + case "material_insert": + text = catalog.i18nc("@label", "Load %3 as material %1 (This cannot be overridden).").arg(change.index + 1).arg(change.targetName) + break + case "print_core_change": + text = catalog.i18nc("@label", "Change print core %1 from %2 to %3.").arg(change.index + 1).arg(change.originName).arg(change.targetName) + break + case "buildplate_change": + text = catalog.i18nc("@label", "Change build plate to %1 (This cannot be overridden).").arg(formatBuildPlateType(change.target_name)) + break + default: + text = "unknown" + } + result += "

    " + text + "

    " + } + return result + } + } + // Utils + function formatPrintJobName(name) { + var extensions = [ ".gz", ".gcode", ".ufp" ]; + for (var i = 0; i < extensions.length; i++) { + var extension = extensions[i]; + if (name.slice(-extension.length) === extension) { + name = name.substring(0, name.length - extension.length); + } + } + return name; + } + function materialsAreKnown(job) { + var conf0 = job.configuration[0]; + if (conf0 && !conf0.material.material) { + return false; + } + var conf1 = job.configuration[1]; + if (conf1 && !conf1.material.material) { + return false; + } + return true; + } + function formatBuildPlateType(buildPlateType) { + var translationText = ""; + switch (buildPlateType) { + case "glass": + translationText = catalog.i18nc("@label", "Glass"); + break; + case "aluminum": + translationText = catalog.i18nc("@label", "Aluminum"); + break; + default: + translationText = null; + } + return translationText; + } +} \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml index 8231870c21..5eaeff2e84 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml @@ -26,6 +26,7 @@ Item ExpandableCard { + borderColor: printJob.configurationChanges.length !== 0 ? "#f5a623" : "#EAEAEC" // TODO: Theme! headerItem: Row { height: 48 * screenScaleFactor // TODO: Theme! diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml index ec26bbe568..5acd350abb 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml @@ -23,7 +23,7 @@ Item anchors.fill: parent opacity: { - if (printJob && (printJob.state == "error" || !printJob.isActive)) + if (printJob && (printJob.state == "error" || printJob.configurationChanges.length > 0 || !printJob.isActive)) { return 0.5 } @@ -60,6 +60,10 @@ Item height: 0.5 * printJobPreview.height source: { + if (printJob.configurationChanges.length > 0) + { + return "../svg/warning-icon.svg" + } switch(printJob.state) { case "error": @@ -75,6 +79,7 @@ Item default: return "" } + return "" } sourceSize { diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml index 88418516ed..e646172a6c 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml @@ -88,6 +88,8 @@ Item return catalog.i18nc("@label:status", "Aborted") } return catalog.i18nc("@label:status", "Finished") + case "finished": + return catalog.i18nc("@label:status", "Finished") case "sent_to_printer": return catalog.i18nc("@label:status", "Preparing...") case "pre_print": diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml index 567fff8489..1676c51edf 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml @@ -3,6 +3,7 @@ import QtQuick 2.3 import QtQuick.Controls 2.0 +import QtQuick.Dialogs 1.1 import UM 1.3 as UM /** @@ -66,7 +67,7 @@ Item { verticalCenter: parent.verticalCenter } - width: 216 * screenScaleFactor // TODO: Theme! + width: 180 * screenScaleFactor // TODO: Theme! height: printerNameLabel.height + printerFamilyPill.height + 6 * screenScaleFactor // TODO: Theme! Label @@ -150,7 +151,7 @@ Item } border { - color: "#EAEAEC" // TODO: Theme! + color: printer.activePrintJob && printer.activePrintJob.configurationChanges.length > 0 ? "#f5a623" : "#EAEAEC" // TODO: Theme! width: borderSize // TODO: Remove once themed } color: "white" // TODO: Theme! @@ -185,14 +186,15 @@ Item } if (printer && printer.state == "unreachable") { - return catalog.i18nc("@label:status", "Unreachable") + return catalog.i18nc("@label:status", "Unavailable") } - if (printer && printer.state == "idle") + if (printer && !printer.activePrintJob && printer.state == "idle") { return catalog.i18nc("@label:status", "Idle") } return "" } + visible: text !== "" } Item @@ -218,7 +220,7 @@ Item { verticalCenter: parent.verticalCenter } - width: 216 * screenScaleFactor // TODO: Theme! + width: 180 * screenScaleFactor // TODO: Theme! height: printerNameLabel.height + printerFamilyPill.height + 6 * screenScaleFactor // TODO: Theme! visible: printer.activePrintJob @@ -247,7 +249,7 @@ Item } color: printer.activePrintJob && printer.activePrintJob.isActive ? "#53657d" : "#babac1" // TODO: Theme! elide: Text.ElideRight - font: UM.Theme.getFont("very_small") // 12pt, regular + font: UM.Theme.getFont("default") // 12pt, regular text: printer.activePrintJob ? printer.activePrintJob.owner : "Anonymous" // TODO: I18N width: parent.width @@ -264,8 +266,67 @@ Item verticalCenter: parent.verticalCenter } printJob: printer.activePrintJob - visible: printer.activePrintJob + visible: printer.activePrintJob && printer.activePrintJob.configurationChanges.length === 0 + } + + Label + { + anchors + { + verticalCenter: parent.verticalCenter + } + font: UM.Theme.getFont("default") + text: "Requires configuration changes" + visible: printer.activePrintJob && printer.activePrintJob.configurationChanges.length > 0 + + // FIXED-LINE-HEIGHT: + height: 18 * screenScaleFactor // TODO: Theme! + verticalAlignment: Text.AlignVCenter } } + + Button + { + id: detailsButton + anchors + { + verticalCenter: parent.verticalCenter + right: parent.right + rightMargin: 18 * screenScaleFactor // TODO: Theme! + } + background: Rectangle + { + color: "#d8d8d8" // TODO: Theme! + radius: 2 * screenScaleFactor // Todo: Theme! + Rectangle + { + anchors.fill: parent + anchors.bottomMargin: 2 * screenScaleFactor // TODO: Theme! + color: detailsButton.hovered ? "#e4e4e4" : "#f0f0f0" // TODO: Theme! + radius: 2 * screenScaleFactor // Todo: Theme! + } + } + contentItem: Label + { + anchors.fill: parent + anchors.bottomMargin: 2 * screenScaleFactor // TODO: Theme! + color: "#1e66d7" // TODO: Theme! + font: UM.Theme.getFont("medium") // 14pt, regular + text: "Details" // TODO: I18NC! + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + height: 18 * screenScaleFactor // TODO: Theme! + } + implicitHeight: 32 * screenScaleFactor // TODO: Theme! + implicitWidth: 96 * screenScaleFactor // TODO: Theme! + visible: printer.activePrintJob && printer.activePrintJob.configurationChanges.length > 0 + onClicked: overrideConfirmationDialog.open() + } + } + + MonitorConfigOverrideDialog + { + id: overrideConfirmationDialog + printer: base.printer } } \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index e31229680c..292011929d 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -608,16 +608,6 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): def _createMaterialOutputModel(self, material_data: Dict[str, Any]) -> "MaterialOutputModel": material_manager = CuraApplication.getInstance().getMaterialManager() material_group_list = material_manager.getMaterialGroupListByGUID(material_data["guid"]) - # This can happen if the connected machine has no material in one or more extruders (if GUID is empty), or the - # material is unknown to Cura, so we should return an "empty" or "unknown" material model. - if material_group_list is None: - material_name = "Empty" if len(material_data["guid"]) == 0 else "Unknown" - return MaterialOutputModel(guid = material_data["guid"], - type = material_data.get("type", ""), - color = material_data.get("color", ""), - brand = material_data.get("brand", ""), - name = material_data.get("name", material_name) - ) # Sort the material groups by "is_read_only = True" first, and then the name alphabetically. read_only_material_group_list = list(filter(lambda x: x.is_read_only, material_group_list)) From dff364ee30870822158de7beaeb1faf7722f3d37 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 10 Dec 2018 12:03:29 +0100 Subject: [PATCH 0777/1240] Change the panel to have also rounded rectangle in the top part Contributes to CURA-5941. --- resources/qml/ExpandableComponent.qml | 2 +- .../qml/PrintSetupSelector/PrintSetupSelectorContents.qml | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 4170a0942e..04793653fe 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -162,7 +162,7 @@ Item background: Cura.RoundedRectangle { - cornerSide: Cura.RoundedRectangle.Direction.Down + cornerSide: Cura.RoundedRectangle.Direction.All color: contentBackgroundColor border.width: UM.Theme.getSize("default_lining").width border.color: UM.Theme.getColor("lining") diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml index 7cd03ff74a..313bf0830c 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml @@ -38,11 +38,13 @@ Item // Header of the popup - Rectangle + Cura.RoundedRectangle { id: header height: UM.Theme.getSize("print_setup_widget_header").height color: UM.Theme.getColor("secondary") + cornerSide: Cura.RoundedRectangle.Direction.Up + radius: UM.Theme.getSize("default_radius").width anchors { From df4e5c40df403f76dfede9b74d358a075ff951c0 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Mon, 10 Dec 2018 12:19:52 +0100 Subject: [PATCH 0778/1240] Rename ClusterMonitorItem Contributes to CL-1152 --- .../resources/qml/{ClusterMonitorItem.qml => MonitorStage.qml} | 1 + plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) rename plugins/UM3NetworkPrinting/resources/qml/{ClusterMonitorItem.qml => MonitorStage.qml} (99%) diff --git a/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml similarity index 99% rename from plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml rename to plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml index 31b3f4d4f5..4d59e0eb6b 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/ClusterMonitorItem.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml @@ -8,6 +8,7 @@ import UM 1.3 as UM import Cura 1.0 as Cura import QtGraphicalEffects 1.0 +// Root component for the monitor tab (stage) Component { Item diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index 292011929d..08592df603 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -64,7 +64,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): self._print_jobs = [] # type: List[UM3PrintJobOutputModel] self._received_print_jobs = False # type: bool - self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../resources/qml/ClusterMonitorItem.qml") + self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../resources/qml/MonitorStage.qml") # See comments about this hack with the clusterPrintersChanged signal self.printersChanged.connect(self.clusterPrintersChanged) From 70b9a44ae42de5ae7a796285338f7abad673f984 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 10 Dec 2018 13:04:02 +0100 Subject: [PATCH 0779/1240] Removed import of CuraVersion CURA-6013 --- plugins/Toolbox/src/Toolbox.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 7e35f5d1f4..b88e1aa973 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -13,7 +13,6 @@ from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkRepl from UM.Logger import Logger from UM.PluginRegistry import PluginRegistry from UM.Extension import Extension -from UM.Qt.ListModel import ListModel from UM.i18n import i18nCatalog from UM.Version import Version @@ -22,8 +21,7 @@ from cura.CuraApplication import CuraApplication from .AuthorsModel import AuthorsModel from .PackagesModel import PackagesModel -from cura.CuraVersion import CuraVersion -from cura.API import CuraAPI + if TYPE_CHECKING: from cura.Settings.GlobalStack import GlobalStack @@ -158,7 +156,7 @@ class Toolbox(QObject, Extension): self._rate_request = QNetworkRequest(url) for header_name, header_value in self._request_headers: cast(QNetworkRequest, self._rate_request).setRawHeader(header_name, header_value) - data = "{\"data\": {\"cura_version\": \"%s\", \"rating\": %i}}" % (Version(CuraVersion), rating) + data = "{\"data\": {\"cura_version\": \"%s\", \"rating\": %i}}" % (Version(self._application.getVersion()), rating) self._rate_reply = cast(QNetworkAccessManager, self._network_manager).put(self._rate_request, data.encode()) @pyqtSlot(result = str) From f432d7c858862bfbc1e671c79718f57aee3a7c07 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Mon, 10 Dec 2018 13:11:42 +0100 Subject: [PATCH 0780/1240] STAR-322: Using a test setup to run tests with Cura app --- .../tests/Cloud/NetworkManagerMock.py | 4 +- .../tests/Cloud/TestCloudApiClient.py | 5 +-- .../Cloud/TestCloudOutputDeviceManager.py | 27 ++++++++------ .../tests/TestSendMaterialJob.py | 2 +- plugins/UM3NetworkPrinting/tests/conftest.py | 37 +++++++++++++++++++ 5 files changed, 58 insertions(+), 17 deletions(-) create mode 100644 plugins/UM3NetworkPrinting/tests/conftest.py diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py b/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py index fc84569fa4..e8e4fc8de7 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py @@ -66,7 +66,8 @@ class NetworkManagerMock: # \return The data in the response. def prepareGetClusters(self, data: Optional[dict] = None) -> dict: data, response = self._getResponseData("clusters", data) - self.prepareReply("GET", "https://api-staging.ultimaker.com/connect/v1/clusters", 200, response) + status_code = 200 if "data" in data else int(data["errors"][0]["http_status"]) + self.prepareReply("GET", "https://api-staging.ultimaker.com/connect/v1/clusters", status_code, response) return data ## Gets the data that should be in the server's response in both dictionary and JSON-encoded bytes format. @@ -87,6 +88,7 @@ class NetworkManagerMock: def flushReplies(self): for reply in self.replies.values(): self.finished.emit(reply) + self.reset() ## Deletes all prepared replies def reset(self): diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py index 328bb053b7..91f367f9ad 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py @@ -95,9 +95,8 @@ class TestCloudApiClient(TestCase): def _callback(clusters): result.extend(clusters) - with mock.patch.object(Application, "getInstance", new = lambda: FixtureApplication()): - api = CloudApiClient(account_mock, self._errorHandler) - api.getClusters(_callback) + api = CloudApiClient(account_mock, self._errorHandler) + api.getClusters(_callback) manager_mock.return_value.finished.emit(reply_mock) diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py index 80ce54aeee..799e715f0d 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py @@ -4,9 +4,9 @@ from unittest import TestCase from unittest.mock import patch from cura.CuraApplication import CuraApplication -from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputDevice import CloudOutputDevice -from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputDeviceManager import CloudOutputDeviceManager -from plugins.UM3NetworkPrinting.tests.Cloud.NetworkManagerMock import NetworkManagerMock +from src.Cloud.CloudOutputDevice import CloudOutputDevice +from src.Cloud.CloudOutputDeviceManager import CloudOutputDeviceManager +from .NetworkManagerMock import NetworkManagerMock @patch("cura.NetworkClient.QNetworkAccessManager") @@ -15,18 +15,19 @@ class TestCloudOutputDeviceManager(TestCase): def setUp(self): super().setUp() self.app = CuraApplication.getInstance() - if not self.app: - self.app = CuraApplication() - self.app.initialize() - self.network = NetworkManagerMock() self.manager = CloudOutputDeviceManager() self.clusters_response = self.network.prepareGetClusters() - ## In the tear down method we check whether the state of the output device manager is what we expect based on the - # mocked API response. def tearDown(self): - super().tearDown() + try: + self._beforeTearDown() + finally: + super().tearDown() + + ## Before tear down method we check whether the state of the output device manager is what we expect based on the + # mocked API response. + def _beforeTearDown(self): # let the network send replies self.network.flushReplies() # get the created devices @@ -82,6 +83,7 @@ class TestCloudOutputDeviceManager(TestCase): self.assertTrue(self.app.getOutputDeviceManager().getOutputDevice(cluster1["cluster_id"]).isConnected()) self.assertFalse(self.app.getOutputDeviceManager().getOutputDevice(cluster2["cluster_id"]).isConnected()) + self.assertEquals([], active_machine_mock.setMetaDataEntry.mock_calls) @patch("cura.CuraApplication.CuraApplication.getGlobalContainerStack") def test_device_connects_by_network_key(self, global_container_stack_mock, network_mock): @@ -97,11 +99,12 @@ class TestCloudOutputDeviceManager(TestCase): self.assertFalse(self.app.getOutputDeviceManager().getOutputDevice(cluster1["cluster_id"]).isConnected()) self.assertTrue(self.app.getOutputDeviceManager().getOutputDevice(cluster2["cluster_id"]).isConnected()) - active_machine_mock.setMetaDataEntry.assert_called_once_with("um_cloud_cluster_id", cluster2["cluster_id"]) + active_machine_mock.setMetaDataEntry.assert_called_with("um_cloud_cluster_id", cluster2["cluster_id"]) @patch("UM.Message.Message.show") def test_api_error(self, message_mock, network_mock): - self.clusters_response = {"errors": [{"id": "notFound"}]} + self.clusters_response = {"errors": [{"id": "notFound", "title": "Not found!", "http_status": "404"}]} self.network.prepareGetClusters(self.clusters_response) self._loadData(network_mock) + self.network.flushReplies() message_mock.assert_called_once_with() diff --git a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py index e3ec9faeaf..7db5ebdedf 100644 --- a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py @@ -10,7 +10,7 @@ from PyQt5.QtCore import QByteArray from UM.MimeTypeDatabase import MimeType from UM.Application import Application -from ..src.SendMaterialJob import SendMaterialJob +from src.SendMaterialJob import SendMaterialJob @patch("builtins.open", lambda _, __: io.StringIO("")) diff --git a/plugins/UM3NetworkPrinting/tests/conftest.py b/plugins/UM3NetworkPrinting/tests/conftest.py new file mode 100644 index 0000000000..6f245f8f2f --- /dev/null +++ b/plugins/UM3NetworkPrinting/tests/conftest.py @@ -0,0 +1,37 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Uranium is released under the terms of the LGPLv3 or higher. + +import pytest +import Arcus #Prevents error: "PyCapsule_GetPointer called with incorrect name" with conflicting SIP configurations between Arcus and PyQt: Import Arcus first! +from UM.Qt.QtApplication import QtApplication # QT application import is required, even though it isn't used. +from UM.Application import Application +from UM.Signal import Signal + +from cura.CuraApplication import CuraApplication + + +# This mock application must extend from Application and not QtApplication otherwise some QObjects are created and +# a segfault is raised. +class FixtureApplication(CuraApplication): + def __init__(self): + super().__init__() + super().initialize() + Signal._signalQueue = self + + def functionEvent(self, event): + event.call() + + def parseCommandLine(self): + pass + + def processEvents(self): + pass + + +@pytest.fixture(autouse=True) +def application(): + # Since we need to use it more that once, we create the application the first time and use its instance the second + application = FixtureApplication.getInstance() + if application is None: + application = FixtureApplication() + return application From 1487af167b616a190e320e6d05c94fad3a3ff95b Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 10 Dec 2018 13:20:36 +0100 Subject: [PATCH 0781/1240] Disable async loading for ToolboxDetailTile CURA-6006 --- plugins/Toolbox/resources/qml/ToolboxDetailList.qml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailList.qml b/plugins/Toolbox/resources/qml/ToolboxDetailList.qml index 1700a58ebe..4e44ea7d0b 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailList.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailList.qml @@ -32,7 +32,11 @@ Item model: toolbox.packagesModel delegate: Loader { - asynchronous: true + // FIXME: When using asynchronous loading, on Mac and Windows, the tile may fail to load complete, + // leaving an empty space below the title part. We turn it off for now to make it work on Mac and + // Windows. + // Can be related to this QT bug: https://bugreports.qt.io/browse/QTBUG-50992 + asynchronous: false source: "ToolboxDetailTile.qml" } } From b745755a7d1a0b7b482add455cb20535bc265247 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 10 Dec 2018 13:22:28 +0100 Subject: [PATCH 0782/1240] Remove TODO in ToolboxProgressButton CURA-6006 This file has been refactored. --- plugins/Toolbox/resources/qml/ToolboxProgressButton.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml b/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml index 3ca18a52ed..933e3a5900 100644 --- a/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml +++ b/plugins/Toolbox/resources/qml/ToolboxProgressButton.qml @@ -7,7 +7,7 @@ import QtQuick.Controls.Styles 1.4 import UM 1.1 as UM import Cura 1.0 as Cura -// TODO; This is in quite some need for refactoring. + Item { id: base From 9d1701aacbef8273f3225f26c158ceb2d6b6b8f3 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 10 Dec 2018 13:29:58 +0100 Subject: [PATCH 0783/1240] Removed hardcoded color CURA-6013 --- plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml index 8a2fdc8bc8..ceaadc110d 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml @@ -20,7 +20,7 @@ Rectangle Rectangle { id: thumbnail - color: "white" + color: UM.Theme.getColor("main_background") width: UM.Theme.getSize("toolbox_thumbnail_large").width height: UM.Theme.getSize("toolbox_thumbnail_large").height anchors From af37f51cf8aa9acd121d02d4b891d72444556350 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 10 Dec 2018 13:36:06 +0100 Subject: [PATCH 0784/1240] Make the expandable content header to be a common header for every expandable component. Contributes to CURA-5941. --- .../SimulationViewMenuComponent.qml | 1 + resources/qml/ExpandableComponent.qml | 68 +++++++++++----- resources/qml/ExpandablePopup.qml | 3 + .../PrintSetupSelector/PrintSetupSelector.qml | 10 +-- .../PrintSetupSelectorContents.qml | 77 +------------------ .../PrintSetupSelectorHeader.qml | 3 + resources/themes/cura-light/theme.json | 3 +- 7 files changed, 64 insertions(+), 101 deletions(-) diff --git a/plugins/SimulationView/SimulationViewMenuComponent.qml b/plugins/SimulationView/SimulationViewMenuComponent.qml index 76875a035d..58b2bfe520 100644 --- a/plugins/SimulationView/SimulationViewMenuComponent.qml +++ b/plugins/SimulationView/SimulationViewMenuComponent.qml @@ -16,6 +16,7 @@ Cura.ExpandableComponent id: base width: UM.Theme.getSize("layerview_menu_size").width + contentHeaderTitle: catalog.i18nc("@label", "Color scheme") Connections { diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 04793653fe..3c898caeb8 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -1,3 +1,6 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + import QtQuick 2.7 import QtQuick.Controls 2.3 @@ -40,6 +43,9 @@ Item // How much spacing is needed around the contentItem property alias contentPadding: content.padding + // Adds a title to the content item + property alias contentHeaderTitle: contentHeader.headerTitle + // How much spacing is needed for the contentItem by Y coordinate property var contentSpacingY: UM.Theme.getSize("narrow_margin").width @@ -55,7 +61,7 @@ Item property alias iconSize: collapseButton.height // Is the "drawer" open? - readonly property alias expanded: content.visible + readonly property alias expanded: contentContainer.visible // What should the radius of the header be. This is also influenced by the headerCornerSide property alias headerRadius: background.radius @@ -71,7 +77,7 @@ Item function toggleContent() { - content.visible = !expanded + contentContainer.visible = !expanded } // Add this binding since the background color is not updated otherwise @@ -147,10 +153,13 @@ Item z: background.z - 1 } - Control + Cura.RoundedRectangle { - id: content + id: contentContainer + visible: false + width: childrenRect.width + height: childrenRect.height // Ensure that the content is located directly below the headerItem y: background.height + base.shadowOffset + base.contentSpacingY @@ -158,25 +167,42 @@ Item // Make the content aligned with the rest, using the property contentAlignment to decide whether is right or left. // In case of right alignment, the 3x padding is due to left, right and padding between the button & text. x: contentAlignment == ExpandableComponent.ContentAlignment.AlignRight ? -width + collapseButton.width + headerItemLoader.width + 3 * background.padding : 0 - padding: UM.Theme.getSize("default_margin").width - background: Cura.RoundedRectangle + cornerSide: Cura.RoundedRectangle.Direction.All + color: contentBackgroundColor + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") + radius: UM.Theme.getSize("default_radius").width + + ExpandableComponentHeader { - cornerSide: Cura.RoundedRectangle.Direction.All - color: contentBackgroundColor - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("lining") - radius: UM.Theme.getSize("default_radius").width + id: contentHeader + headerTitle: "" + anchors + { + top: parent.top + right: parent.right + left: parent.left + } + } - contentItem: Item {} - - onContentItemChanged: + Control { - // Since we want the size of the content to be set by the size of the content, - // we need to do it like this. - content.width = contentItem.width + 2 * content.padding - content.height = contentItem.height + 2 * content.padding + id: content + + anchors.top: contentHeader.bottom + padding: UM.Theme.getSize("default_margin").width + + contentItem: Item {} + + onContentItemChanged: + { + // Since we want the size of the content to be set by the size of the content, + // we need to do it like this. + content.width = contentItem.width + 2 * content.padding + content.height = contentItem.height + 2 * content.padding + } } } @@ -187,6 +213,10 @@ Item // Since it could be that the content is dynamically populated, we should also take these changes into account. target: content.contentItem onWidthChanged: content.width = content.contentItem.width + 2 * content.padding - onHeightChanged: content.height = content.contentItem.height + 2 * content.padding + onHeightChanged: + { + content.height = content.contentItem.height + 2 * content.padding + contentContainer.height = contentHeader.height + content.height + } } } diff --git a/resources/qml/ExpandablePopup.qml b/resources/qml/ExpandablePopup.qml index c15310f803..475f7f9f59 100644 --- a/resources/qml/ExpandablePopup.qml +++ b/resources/qml/ExpandablePopup.qml @@ -1,3 +1,6 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + import QtQuick 2.7 import QtQuick.Controls 2.3 diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector/PrintSetupSelector.qml index 19c8067683..01886a5ea5 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelector.qml @@ -11,10 +11,11 @@ Cura.ExpandableComponent { id: printSetupSelector - property string enabledText: catalog.i18nc("@label:Should be short", "On") - property string disabledText: catalog.i18nc("@label:Should be short", "Off") + property bool preSlicedData: PrintInformation.preSliced contentPadding: UM.Theme.getSize("default_lining").width + contentHeaderTitle: catalog.i18nc("@label", "Print settings") + enabled: !preSlicedData UM.I18nCatalog { @@ -22,10 +23,7 @@ Cura.ExpandableComponent name: "cura" } - headerItem: PrintSetupSelectorHeader - { - anchors.fill: parent - } + headerItem: PrintSetupSelectorHeader {} Cura.ExtrudersModel { diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml index 313bf0830c..6c678f7ce5 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml @@ -12,7 +12,7 @@ import "Custom" Item { - id: popup + id: content width: UM.Theme.getSize("print_setup_widget").width - 2 * UM.Theme.getSize("default_margin").width height: childrenRect.height @@ -36,79 +36,6 @@ Item } onCurrentModeIndexChanged: UM.Preferences.setValue("cura/active_mode", currentModeIndex) - - // Header of the popup - Cura.RoundedRectangle - { - id: header - height: UM.Theme.getSize("print_setup_widget_header").height - color: UM.Theme.getColor("secondary") - cornerSide: Cura.RoundedRectangle.Direction.Up - radius: UM.Theme.getSize("default_radius").width - - anchors - { - top: parent.top - right: parent.right - left: parent.left - } - - Label - { - id: headerLabel - text: catalog.i18nc("@label", "Print settings") - font: UM.Theme.getFont("default") - renderType: Text.NativeRendering - verticalAlignment: Text.AlignVCenter - color: UM.Theme.getColor("text") - height: parent.height - - anchors - { - topMargin: UM.Theme.getSize("default_margin").height - left: parent.left - leftMargin: UM.Theme.getSize("default_margin").height - } - } - - Button - { - id: closeButton - width: UM.Theme.getSize("message_close").width - height: UM.Theme.getSize("message_close").height - hoverEnabled: true - - anchors - { - right: parent.right - rightMargin: UM.Theme.getSize("default_margin").width - verticalCenter: parent.verticalCenter - } - - contentItem: UM.RecolorImage - { - anchors.fill: parent - sourceSize.width: width - color: closeButton.hovered ? UM.Theme.getColor("small_button_text_hover") : UM.Theme.getColor("small_button_text") - source: UM.Theme.getIcon("cross1") - } - - background: Item {} - - onClicked: toggleContent() // Will hide the popup item - } - } - - Rectangle - { - id: topSeparator - - anchors.bottom: header.bottom - width: parent.width - height: UM.Theme.getSize("default_lining").height - color: UM.Theme.getColor("lining") - } - Item { id: contents @@ -118,7 +45,7 @@ Item anchors { - top: header.bottom + top: parent.top left: parent.left right: parent.right } diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml index d4287045b8..94da5bdd6f 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml @@ -10,6 +10,9 @@ import Cura 1.0 as Cura RowLayout { + property string enabledText: catalog.i18nc("@label:Should be short", "On") + property string disabledText: catalog.i18nc("@label:Should be short", "Off") + Cura.IconWithText { source: UM.Theme.getIcon("category_layer_height") diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 0ef6c24bfb..54b41fe348 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -362,13 +362,14 @@ "print_setup_mode_toggle": [0.0, 2.0], "print_setup_item": [0.0, 2.0], "print_setup_extruder_box": [0.0, 6.0], - "print_setup_widget_header": [0.0, 3.0], "print_setup_slider_groove": [0.16, 0.16], "print_setup_slider_handle": [1.0, 1.0], "print_setup_slider_tickmarks": [0.32, 0.32], "print_setup_big_item": [28, 2.5], "print_setup_icon": [1.2, 1.2], + "expandable_component_content_header": [0.0, 3.0], + "configuration_selector_mode_tabs": [0.0, 3.0], "action_panel_widget": [25.0, 0.0], From 154c6c1ff226a820e7737dc09952043701f42069 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 10 Dec 2018 13:42:33 +0100 Subject: [PATCH 0785/1240] Change the color of the setting control button to be the same as for the small buttons Contributes to CURA-5941. --- resources/themes/cura-light/theme.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 54b41fe348..5bad1cf7ae 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -205,9 +205,8 @@ "setting_control_border": [199, 199, 199, 255], "setting_control_border_highlight": [50, 130, 255, 255], "setting_control_text": [35, 35, 35, 255], - "setting_control_depth_line": [199, 199, 199, 255], - "setting_control_button": [199, 199, 199, 255], - "setting_control_button_hover": [70, 84, 113, 255], + "setting_control_button": [102, 102, 102, 255], + "setting_control_button_hover": [8, 7, 63, 255], "setting_control_disabled": [245, 245, 245, 255], "setting_control_disabled_text": [127, 127, 127, 255], "setting_control_disabled_border": [127, 127, 127, 255], From 90f822f6835471890a753dff0b5e4614d2d6e06b Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 10 Dec 2018 13:44:50 +0100 Subject: [PATCH 0786/1240] Add the component header that I miss in a previous commit Contributes to CURA-5941. --- resources/qml/ExpandableComponentHeader.qml | 68 +++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 resources/qml/ExpandableComponentHeader.qml diff --git a/resources/qml/ExpandableComponentHeader.qml b/resources/qml/ExpandableComponentHeader.qml new file mode 100644 index 0000000000..b1fd49cd1b --- /dev/null +++ b/resources/qml/ExpandableComponentHeader.qml @@ -0,0 +1,68 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 2.3 + +import UM 1.2 as UM +import Cura 1.0 as Cura + +// Header of the popup +Cura.RoundedRectangle +{ + id: header + + property alias headerTitle: headerLabel.text + + height: UM.Theme.getSize("expandable_component_content_header").height + color: UM.Theme.getColor("secondary") + cornerSide: Cura.RoundedRectangle.Direction.Up + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") + radius: UM.Theme.getSize("default_radius").width + + Label + { + id: headerLabel + text: "Title" + font: UM.Theme.getFont("default") + renderType: Text.NativeRendering + verticalAlignment: Text.AlignVCenter + color: UM.Theme.getColor("small_button_text") + height: parent.height + + anchors + { + topMargin: UM.Theme.getSize("default_margin").height + left: parent.left + leftMargin: UM.Theme.getSize("default_margin").height + } + } + + Button + { + id: closeButton + width: UM.Theme.getSize("message_close").width + height: UM.Theme.getSize("message_close").height + hoverEnabled: true + + anchors + { + right: parent.right + rightMargin: UM.Theme.getSize("default_margin").width + verticalCenter: parent.verticalCenter + } + + contentItem: UM.RecolorImage + { + anchors.fill: parent + sourceSize.width: width + color: closeButton.hovered ? UM.Theme.getColor("small_button_text_hover") : UM.Theme.getColor("small_button_text") + source: UM.Theme.getIcon("cross1") + } + + background: Item {} + + onClicked: toggleContent() // Will hide the popup item + } +} \ No newline at end of file From e5124532f83ffb062ec6fba9a88fd0b258ce0136 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Mon, 10 Dec 2018 14:03:43 +0100 Subject: [PATCH 0787/1240] getClusterStatusResponse as fixture --- .../Fixtures/getClusterStatusResponse.json | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/getClusterStatusResponse.json diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/getClusterStatusResponse.json b/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/getClusterStatusResponse.json new file mode 100644 index 0000000000..711e429a72 --- /dev/null +++ b/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/getClusterStatusResponse.json @@ -0,0 +1,97 @@ +{ + "data": [ + { + "generated_time": "2018-12-10T08:23:55.110Z", + "printers": [ + { + "configuration": [ + { + "extruder_index": 0, + "material": { + "material": "empty" + }, + "print_core_id": "AA 0.4" + }, + { + "extruder_index": 1, + "material": { + "material": "empty" + }, + "print_core_id": "AA 0.4" + } + ], + "enabled": true, + "firmware_version": "5.1.2.20180807", + "friendly_name": "Master-Luke", + "ip_address": "10.183.1.140", + "machine_variant": "Ultimaker 3", + "status": "maintenance", + "unique_name": "ultimakersystem-ccbdd30044ec", + "uuid": "b3a47ea3-1eeb-4323-9626-6f9c3c888f9e" + }, + { + "configuration": [ + { + "extruder_index": 0, + "material": { + "brand": "Generic", + "color": "Generic", + "guid": "506c9f0d-e3aa-4bd4-b2d2-23e2425b1aa9", + "material": "PLA" + }, + "print_core_id": "AA 0.4" + }, + { + "extruder_index": 1, + "material": { + "brand": "Ultimaker", + "color": "Red", + "guid": "9cfe5bf1-bdc5-4beb-871a-52c70777842d", + "material": "PLA" + }, + "print_core_id": "AA 0.4" + } + ], + "enabled": true, + "firmware_version": "4.3.3.20180529", + "friendly_name": "UM-Marijn", + "ip_address": "10.183.1.166", + "machine_variant": "Ultimaker 3", + "status": "idle", + "unique_name": "ultimakersystem-ccbdd30058ab", + "uuid": "6e62c40a-4601-4b0e-9fec-c7c02c59c30a" + } + ], + "print_jobs": [ + { + "assigned_to": "6e62c40a-4601-4b0e-9fec-c7c02c59c30a", + "configuration": [ + { + "extruder_index": 0, + "material": { + "brand": "Ultimaker", + "color": "Black", + "guid": "3ee70a86-77d8-4b87-8005-e4a1bc57d2ce", + "material": "PLA" + }, + "print_core_id": "AA 0.4" + } + ], + "constraints": {}, + "created_at": "2018-12-10T08:28:04.108Z", + "force": false, + "last_seen": 500165.109491861, + "machine_variant": "Ultimaker 3", + "name": "UM3_dragon", + "network_error_count": 0, + "owner": "Daniel Testing", + "started": false, + "status": "queued", + "time_elapsed": 0, + "time_total": 14145, + "uuid": "d1c8bd52-5e9f-486a-8c25-a123cc8c7702" + } + ] + } + ] +} From 3f97f83c473dd015353e7ca82003042cd2d11308 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 10 Dec 2018 14:17:56 +0100 Subject: [PATCH 0788/1240] Add a disabled state to the expandable components In this case, when the component is disabled, there is an optional message that will show. It works for instance when loading a gcode and the print setup panel has to be disabled. Contributes to CURA-5941. --- resources/qml/ExpandableComponent.qml | 70 +++++++++----- resources/qml/ExpandablePopup.qml | 96 ++++++++++++------- .../PrintSetupSelector/PrintSetupSelector.qml | 1 + 3 files changed, 110 insertions(+), 57 deletions(-) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 3c898caeb8..55271d99c3 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -37,6 +37,9 @@ Item property alias enabled: mouseArea.enabled + // Text to show when this component is disabled + property alias disabledText: disabledLabel.text + // Defines the alignment of the content with respect of the headerItem, by default to the right property int contentAlignment: ExpandableComponent.ContentAlignment.AlignRight @@ -85,7 +88,7 @@ Item { target: background property: "color" - value: expanded ? headerActiveColor : headerBackgroundColor + value: enabled ? (expanded ? headerActiveColor : headerBackgroundColor) : UM.Theme.getColor("disabled") } implicitHeight: 100 * screenScaleFactor @@ -96,36 +99,55 @@ Item id: background property real padding: UM.Theme.getSize("default_margin").width - color: expanded ? headerActiveColor : headerBackgroundColor + color: base.enabled ? (base.expanded ? headerActiveColor : headerBackgroundColor) : UM.Theme.getColor("disabled") anchors.fill: parent - Loader + Label { - id: headerItemLoader - anchors - { - left: parent.left - right: collapseButton.visible ? collapseButton.left : parent.right - top: parent.top - bottom: parent.bottom - margins: background.padding - } + id: disabledLabel + visible: !base.enabled + leftPadding: background.padding + text: "This component is disabled" + font: UM.Theme.getFont("default") + renderType: Text.NativeRendering + verticalAlignment: Text.AlignVCenter + color: UM.Theme.getColor("text") + height: parent.height } - UM.RecolorImage + Item { - id: collapseButton - anchors + anchors.fill: parent + visible: base.enabled + + Loader { - right: parent.right - verticalCenter: parent.verticalCenter - margins: background.padding + id: headerItemLoader + anchors + { + left: parent.left + right: collapseButton.visible ? collapseButton.left : parent.right + top: parent.top + bottom: parent.bottom + margins: background.padding + } + } + + UM.RecolorImage + { + id: collapseButton + anchors + { + right: parent.right + verticalCenter: parent.verticalCenter + margins: background.padding + } + source: UM.Theme.getIcon("pencil") + visible: source != "" + width: UM.Theme.getSize("standard_arrow").width + height: UM.Theme.getSize("standard_arrow").height + color: UM.Theme.getColor("small_button_text") } - source: UM.Theme.getIcon("pencil") - visible: source != "" && base.enabled - width: UM.Theme.getSize("standard_arrow").width - height: UM.Theme.getSize("standard_arrow").height - color: UM.Theme.getColor("small_button_text") } MouseArea @@ -135,7 +157,7 @@ Item onClicked: toggleContent() hoverEnabled: true onEntered: background.color = headerHoverColor - onExited: background.color = expanded ? headerActiveColor : headerBackgroundColor + onExited: background.color = base.enabled ? (base.expanded ? headerActiveColor : headerBackgroundColor) : UM.Theme.getColor("disabled") } } diff --git a/resources/qml/ExpandablePopup.qml b/resources/qml/ExpandablePopup.qml index 475f7f9f59..75f718abf5 100644 --- a/resources/qml/ExpandablePopup.qml +++ b/resources/qml/ExpandablePopup.qml @@ -37,6 +37,9 @@ Item property alias enabled: mouseArea.enabled + // Text to show when this component is disabled + property alias disabledText: disabledLabel.text + // Defines the alignment of the content with respect of the headerItem, by default to the right property int contentAlignment: ExpandablePopup.ContentAlignment.AlignRight @@ -86,6 +89,14 @@ Item } } + // Add this binding since the background color is not updated otherwise + Binding + { + target: background + property: "color" + value: enabled ? headerBackgroundColor : UM.Theme.getColor("disabled") + } + implicitHeight: 100 * screenScaleFactor implicitWidth: 400 * screenScaleFactor @@ -94,47 +105,66 @@ Item id: background property real padding: UM.Theme.getSize("default_margin").width - color: headerBackgroundColor + color: base.enabled ? headerBackgroundColor : UM.Theme.getColor("disabled") anchors.fill: parent - Loader + Label { - id: headerItemLoader - anchors - { - left: parent.left - right: collapseButton.visible ? collapseButton.left : parent.right - top: parent.top - bottom: parent.bottom - margins: background.padding - } + id: disabledLabel + visible: !base.enabled + leftPadding: background.padding + text: "This component is disabled" + font: UM.Theme.getFont("default") + renderType: Text.NativeRendering + verticalAlignment: Text.AlignVCenter + color: UM.Theme.getColor("text") + height: parent.height } - // A highlight that is shown when the content is expanded - Rectangle + Item { - id: expandedHighlight - width: parent.width - height: UM.Theme.getSize("thick_lining").height - color: UM.Theme.getColor("primary") - visible: expanded - anchors.bottom: parent.bottom - } + anchors.fill: parent + visible: base.enabled - UM.RecolorImage - { - id: collapseButton - anchors + Loader { - right: parent.right - verticalCenter: parent.verticalCenter - margins: background.padding + id: headerItemLoader + anchors + { + left: parent.left + right: collapseButton.visible ? collapseButton.left : parent.right + top: parent.top + bottom: parent.bottom + margins: background.padding + } + } + + // A highlight that is shown when the content is expanded + Rectangle + { + id: expandedHighlight + width: parent.width + height: UM.Theme.getSize("thick_lining").height + color: UM.Theme.getColor("primary") + visible: expanded + anchors.bottom: parent.bottom + } + + UM.RecolorImage + { + id: collapseButton + anchors + { + right: parent.right + verticalCenter: parent.verticalCenter + margins: background.padding + } + source: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") + visible: source != "" + width: UM.Theme.getSize("standard_arrow").width + height: UM.Theme.getSize("standard_arrow").height + color: UM.Theme.getColor("small_button_text") } - source: expanded ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") - visible: source != "" && base.enabled - width: UM.Theme.getSize("standard_arrow").width - height: UM.Theme.getSize("standard_arrow").height - color: UM.Theme.getColor("small_button_text") } MouseArea @@ -144,7 +174,7 @@ Item onClicked: toggleContent() hoverEnabled: true onEntered: background.color = headerHoverColor - onExited: background.color = headerBackgroundColor + onExited: background.color = base.enabled ? headerBackgroundColor : UM.Theme.getColor("disabled") } } diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector/PrintSetupSelector.qml index 01886a5ea5..fe642bd3c1 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelector.qml @@ -16,6 +16,7 @@ Cura.ExpandableComponent contentPadding: UM.Theme.getSize("default_lining").width contentHeaderTitle: catalog.i18nc("@label", "Print settings") enabled: !preSlicedData + disabledText: catalog.i18nc("@label shown when we load a Gcode file", "Print setup disabled. G code file can not be modified." UM.I18nCatalog { From c62cb84c75c158a21ea703fd853fb5f68f3d2789 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Mon, 10 Dec 2018 14:18:10 +0100 Subject: [PATCH 0789/1240] Added CuraDirve plugin to Cura build CURA-6005 --- .gitignore | 1 - plugins/CuraDrive/__init__.py | 14 ++ plugins/CuraDrive/plugin.json | 8 + plugins/CuraDrive/src/DriveApiService.py | 185 ++++++++++++++++ plugins/CuraDrive/src/DrivePluginExtension.py | 200 ++++++++++++++++++ plugins/CuraDrive/src/Settings.py | 36 ++++ plugins/CuraDrive/src/UploadBackupJob.py | 39 ++++ plugins/CuraDrive/src/__init__.py | 0 .../CuraDrive/src/models/BackupListModel.py | 38 ++++ plugins/CuraDrive/src/models/__init__.py | 0 .../src/qml/components/ActionButton.qml | 67 ++++++ .../src/qml/components/ActionCheckBox.qml | 49 +++++ .../src/qml/components/ActionToolTip.qml | 29 +++ .../src/qml/components/BackupList.qml | 31 +++ .../src/qml/components/BackupListFooter.qml | 42 ++++ .../src/qml/components/BackupListItem.qml | 112 ++++++++++ .../qml/components/BackupListItemDetails.qml | 61 ++++++ .../components/BackupListItemDetailsRow.qml | 52 +++++ .../CuraDrive/src/qml/components/Divider.qml | 11 + plugins/CuraDrive/src/qml/components/Icon.qml | 56 +++++ .../src/qml/components/RightSideScrollBar.qml | 13 ++ .../src/qml/images/avatar_default.png | Bin 0 -> 3115 bytes .../CuraDrive/src/qml/images/background.svg | 12 ++ plugins/CuraDrive/src/qml/images/backup.svg | 3 + plugins/CuraDrive/src/qml/images/cura.svg | 7 + .../CuraDrive/src/qml/images/cura_logo.jpg | Bin 0 -> 19308 bytes .../CuraDrive/src/qml/images/cura_logo.png | Bin 0 -> 13258 bytes plugins/CuraDrive/src/qml/images/delete.svg | 7 + plugins/CuraDrive/src/qml/images/folder.svg | 7 + plugins/CuraDrive/src/qml/images/home.svg | 3 + plugins/CuraDrive/src/qml/images/icon.png | Bin 0 -> 21924 bytes plugins/CuraDrive/src/qml/images/info.svg | 4 + .../src/qml/images/inverted_circle.png | Bin 0 -> 1608 bytes plugins/CuraDrive/src/qml/images/loading.gif | Bin 0 -> 6762 bytes plugins/CuraDrive/src/qml/images/material.svg | 7 + plugins/CuraDrive/src/qml/images/plugin.svg | 7 + .../src/qml/images/preview_banner.png | Bin 0 -> 8324 bytes plugins/CuraDrive/src/qml/images/printer.svg | 14 ++ plugins/CuraDrive/src/qml/images/profile.svg | 3 + plugins/CuraDrive/src/qml/images/restore.svg | 7 + plugins/CuraDrive/src/qml/main.qml | 42 ++++ .../CuraDrive/src/qml/pages/BackupsPage.qml | 73 +++++++ .../CuraDrive/src/qml/pages/WelcomePage.qml | 48 +++++ 43 files changed, 1287 insertions(+), 1 deletion(-) create mode 100644 plugins/CuraDrive/__init__.py create mode 100644 plugins/CuraDrive/plugin.json create mode 100644 plugins/CuraDrive/src/DriveApiService.py create mode 100644 plugins/CuraDrive/src/DrivePluginExtension.py create mode 100644 plugins/CuraDrive/src/Settings.py create mode 100644 plugins/CuraDrive/src/UploadBackupJob.py create mode 100644 plugins/CuraDrive/src/__init__.py create mode 100644 plugins/CuraDrive/src/models/BackupListModel.py create mode 100644 plugins/CuraDrive/src/models/__init__.py create mode 100644 plugins/CuraDrive/src/qml/components/ActionButton.qml create mode 100644 plugins/CuraDrive/src/qml/components/ActionCheckBox.qml create mode 100644 plugins/CuraDrive/src/qml/components/ActionToolTip.qml create mode 100644 plugins/CuraDrive/src/qml/components/BackupList.qml create mode 100644 plugins/CuraDrive/src/qml/components/BackupListFooter.qml create mode 100644 plugins/CuraDrive/src/qml/components/BackupListItem.qml create mode 100644 plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml create mode 100644 plugins/CuraDrive/src/qml/components/BackupListItemDetailsRow.qml create mode 100644 plugins/CuraDrive/src/qml/components/Divider.qml create mode 100644 plugins/CuraDrive/src/qml/components/Icon.qml create mode 100644 plugins/CuraDrive/src/qml/components/RightSideScrollBar.qml create mode 100644 plugins/CuraDrive/src/qml/images/avatar_default.png create mode 100644 plugins/CuraDrive/src/qml/images/background.svg create mode 100644 plugins/CuraDrive/src/qml/images/backup.svg create mode 100644 plugins/CuraDrive/src/qml/images/cura.svg create mode 100644 plugins/CuraDrive/src/qml/images/cura_logo.jpg create mode 100644 plugins/CuraDrive/src/qml/images/cura_logo.png create mode 100644 plugins/CuraDrive/src/qml/images/delete.svg create mode 100644 plugins/CuraDrive/src/qml/images/folder.svg create mode 100644 plugins/CuraDrive/src/qml/images/home.svg create mode 100644 plugins/CuraDrive/src/qml/images/icon.png create mode 100644 plugins/CuraDrive/src/qml/images/info.svg create mode 100644 plugins/CuraDrive/src/qml/images/inverted_circle.png create mode 100644 plugins/CuraDrive/src/qml/images/loading.gif create mode 100644 plugins/CuraDrive/src/qml/images/material.svg create mode 100644 plugins/CuraDrive/src/qml/images/plugin.svg create mode 100644 plugins/CuraDrive/src/qml/images/preview_banner.png create mode 100644 plugins/CuraDrive/src/qml/images/printer.svg create mode 100644 plugins/CuraDrive/src/qml/images/profile.svg create mode 100644 plugins/CuraDrive/src/qml/images/restore.svg create mode 100644 plugins/CuraDrive/src/qml/main.qml create mode 100644 plugins/CuraDrive/src/qml/pages/BackupsPage.qml create mode 100644 plugins/CuraDrive/src/qml/pages/WelcomePage.qml diff --git a/.gitignore b/.gitignore index 0a66b6eb33..60b59e6829 100644 --- a/.gitignore +++ b/.gitignore @@ -42,7 +42,6 @@ plugins/cura-siemensnx-plugin plugins/CuraBlenderPlugin plugins/CuraCloudPlugin plugins/CuraDrivePlugin -plugins/CuraDrive plugins/CuraLiveScriptingPlugin plugins/CuraOpenSCADPlugin plugins/CuraPrintProfileCreator diff --git a/plugins/CuraDrive/__init__.py b/plugins/CuraDrive/__init__.py new file mode 100644 index 0000000000..6612a5d614 --- /dev/null +++ b/plugins/CuraDrive/__init__.py @@ -0,0 +1,14 @@ +# Copyright (c) 2017 Ultimaker B.V. +import os + +is_testing = os.getenv('ENV_NAME', "development") == "testing" + +# Only load the whole plugin when not running tests as __init__.py is automatically loaded by PyTest +if not is_testing: + from .src.DrivePluginExtension import DrivePluginExtension + + def getMetaData(): + return {} + + def register(app): + return {"extension": DrivePluginExtension(app)} diff --git a/plugins/CuraDrive/plugin.json b/plugins/CuraDrive/plugin.json new file mode 100644 index 0000000000..134cd31a77 --- /dev/null +++ b/plugins/CuraDrive/plugin.json @@ -0,0 +1,8 @@ +{ + "name": "Cura Backups", + "author": "Ultimaker B.V.", + "description": "Backup and restore your configuration.", + "version": "1.2.1", + "api": 5, + "i18n-catalog": "cura_drive" +} \ No newline at end of file diff --git a/plugins/CuraDrive/src/DriveApiService.py b/plugins/CuraDrive/src/DriveApiService.py new file mode 100644 index 0000000000..a677466838 --- /dev/null +++ b/plugins/CuraDrive/src/DriveApiService.py @@ -0,0 +1,185 @@ +# Copyright (c) 2017 Ultimaker B.V. +import base64 +import hashlib +from datetime import datetime +from tempfile import NamedTemporaryFile +from typing import Optional, List, Dict + +import requests + +from UM.Logger import Logger +from UM.Message import Message +from UM.Signal import Signal + +from .UploadBackupJob import UploadBackupJob +from .Settings import Settings + + +class DriveApiService: + """ + The DriveApiService is responsible for interacting with the CuraDrive API and Cura's backup handling. + """ + + GET_BACKUPS_URL = "{}/backups".format(Settings.DRIVE_API_URL) + PUT_BACKUP_URL = "{}/backups".format(Settings.DRIVE_API_URL) + DELETE_BACKUP_URL = "{}/backups".format(Settings.DRIVE_API_URL) + + # Emit signal when restoring backup started or finished. + onRestoringStateChanged = Signal() + + # Emit signal when creating backup started or finished. + onCreatingStateChanged = Signal() + + def __init__(self, cura_api) -> None: + """Create a new instance of the Drive API service and set the cura_api object.""" + self._cura_api = cura_api + + def getBackups(self) -> List[Dict[str, any]]: + """Get all backups from the API.""" + access_token = self._cura_api.account.accessToken + if not access_token: + Logger.log("w", "Could not get access token.") + return [] + + backup_list_request = requests.get(self.GET_BACKUPS_URL, headers={ + "Authorization": "Bearer {}".format(access_token) + }) + if backup_list_request.status_code > 299: + Logger.log("w", "Could not get backups list from remote: %s", backup_list_request.text) + Message(Settings.translatable_messages["get_backups_error"], title = Settings.MESSAGE_TITLE, + lifetime = 10).show() + return [] + return backup_list_request.json()["data"] + + def createBackup(self) -> None: + """Create a backup and upload it to CuraDrive cloud storage.""" + self.onCreatingStateChanged.emit(is_creating=True) + + # Create the backup. + backup_zip_file, backup_meta_data = self._cura_api.backups.createBackup() + if not backup_zip_file or not backup_meta_data: + self.onCreatingStateChanged.emit(is_creating=False, error_message="Could not create backup.") + return + + # Create an upload entry for the backup. + timestamp = datetime.now().isoformat() + backup_meta_data["description"] = "{}.backup.{}.cura.zip".format(timestamp, backup_meta_data["cura_release"]) + backup_upload_url = self._requestBackupUpload(backup_meta_data, len(backup_zip_file)) + if not backup_upload_url: + self.onCreatingStateChanged.emit(is_creating=False, error_message="Could not upload backup.") + return + + # Upload the backup to storage. + upload_backup_job = UploadBackupJob(backup_upload_url, backup_zip_file) + upload_backup_job.finished.connect(self._onUploadFinished) + upload_backup_job.start() + + def _onUploadFinished(self, job: "UploadBackupJob") -> None: + """ + Callback handler for the upload job. + :param job: The executed job. + """ + if job.backup_upload_error_message != "": + # If the job contains an error message we pass it along so the UI can display it. + self.onCreatingStateChanged.emit(is_creating=False, error_message=job.backup_upload_error_message) + else: + self.onCreatingStateChanged.emit(is_creating=False) + + def restoreBackup(self, backup: Dict[str, any]) -> None: + """ + Restore a previously exported backup from cloud storage. + :param backup: A dict containing an entry from the API list response. + """ + self.onRestoringStateChanged.emit(is_restoring=True) + download_url = backup.get("download_url") + if not download_url: + # If there is no download URL, we can't restore the backup. + return self._emitRestoreError() + + download_package = requests.get(download_url, stream=True) + if download_package.status_code != 200: + # Something went wrong when attempting to download the backup. + Logger.log("w", "Could not download backup from url %s: %s", download_url, download_package.text) + return self._emitRestoreError() + + # We store the file in a temporary path fist to ensure integrity. + temporary_backup_file = NamedTemporaryFile(delete=False) + with open(temporary_backup_file.name, "wb") as write_backup: + for chunk in download_package: + write_backup.write(chunk) + + if not self._verifyMd5Hash(temporary_backup_file.name, backup.get("md5_hash")): + # Don't restore the backup if the MD5 hashes do not match. + # This can happen if the download was interrupted. + Logger.log("w", "Remote and local MD5 hashes do not match, not restoring backup.") + return self._emitRestoreError() + + # Tell Cura to place the backup back in the user data folder. + with open(temporary_backup_file.name, "rb") as read_backup: + self._cura_api.backups.restoreBackup(read_backup.read(), backup.get("data")) + self.onRestoringStateChanged.emit(is_restoring=False) + + def _emitRestoreError(self, error_message: str = Settings.translatable_messages["backup_restore_error_message"]): + """Helper method for emitting a signal when restoring failed.""" + self.onRestoringStateChanged.emit( + is_restoring=False, + error_message=error_message + ) + + @staticmethod + def _verifyMd5Hash(file_path: str, known_hash: str) -> bool: + """ + Verify the MD5 hash of a file. + :param file_path: Full path to the file. + :param known_hash: The known MD5 hash of the file. + :return: Success or not. + """ + with open(file_path, "rb") as read_backup: + local_md5_hash = base64.b64encode(hashlib.md5(read_backup.read()).digest(), altchars=b"_-").decode("utf-8") + return known_hash == local_md5_hash + + def deleteBackup(self, backup_id: str) -> bool: + """ + Delete a backup from the server by ID. + :param backup_id: The ID of the backup to delete. + :return: Success bool. + """ + access_token = self._cura_api.account.accessToken + if not access_token: + Logger.log("w", "Could not get access token.") + return False + + delete_backup = requests.delete("{}/{}".format(self.DELETE_BACKUP_URL, backup_id), headers = { + "Authorization": "Bearer {}".format(access_token) + }) + if delete_backup.status_code > 299: + Logger.log("w", "Could not delete backup: %s", delete_backup.text) + return False + return True + + def _requestBackupUpload(self, backup_metadata: Dict[str, any], backup_size: int) -> Optional[str]: + """ + Request a backup upload slot from the API. + :param backup_metadata: A dict containing some meta data about the backup. + :param backup_size: The size of the backup file in bytes. + :return: The upload URL for the actual backup file if successful, otherwise None. + """ + access_token = self._cura_api.account.accessToken + if not access_token: + Logger.log("w", "Could not get access token.") + return None + + backup_upload_request = requests.put(self.PUT_BACKUP_URL, json={ + "data": { + "backup_size": backup_size, + "metadata": backup_metadata + } + }, headers={ + "Authorization": "Bearer {}".format(access_token) + }) + + if backup_upload_request.status_code > 299: + Logger.log("w", "Could not request backup upload: %s", backup_upload_request.text) + return None + + return backup_upload_request.json()["data"]["upload_url"] diff --git a/plugins/CuraDrive/src/DrivePluginExtension.py b/plugins/CuraDrive/src/DrivePluginExtension.py new file mode 100644 index 0000000000..556fb187df --- /dev/null +++ b/plugins/CuraDrive/src/DrivePluginExtension.py @@ -0,0 +1,200 @@ +# Copyright (c) 2017 Ultimaker B.V. +import os +from datetime import datetime +from typing import Optional + +from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal + +from UM.Extension import Extension +from UM.Message import Message + +from .Settings import Settings +from .DriveApiService import DriveApiService +from .models.BackupListModel import BackupListModel + + +class DrivePluginExtension(QObject, Extension): + """ + The DivePluginExtension provides functionality to backup and restore your Cura configuration to Ultimaker's cloud. + """ + + # Signal emitted when the list of backups changed. + backupsChanged = pyqtSignal() + + # Signal emitted when restoring has started. Needed to prevent parallel restoring. + restoringStateChanged = pyqtSignal() + + # Signal emitted when creating has started. Needed to prevent parallel creation of backups. + creatingStateChanged = pyqtSignal() + + # Signal emitted when preferences changed (like auto-backup). + preferencesChanged = pyqtSignal() + + DATE_FORMAT = "%d/%m/%Y %H:%M:%S" + + def __init__(self, application): + super(DrivePluginExtension, self).__init__() + + # Re-usable instance of application. + self._application = application + + # Local data caching for the UI. + self._drive_window = None # type: Optional[QObject] + self._backups_list_model = BackupListModel() + self._is_restoring_backup = False + self._is_creating_backup = False + + # Initialize services. + self._preferences = self._application.getPreferences() + self._cura_api = self._application.getCuraAPI() + self._drive_api_service = DriveApiService(self._cura_api) + + # Attach signals. + self._cura_api.account.loginStateChanged.connect(self._onLoginStateChanged) + self._drive_api_service.onRestoringStateChanged.connect(self._onRestoringStateChanged) + self._drive_api_service.onCreatingStateChanged.connect(self._onCreatingStateChanged) + + # Register preferences. + self._preferences.addPreference(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY, False) + self._preferences.addPreference(Settings.AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY, datetime.now() + .strftime(self.DATE_FORMAT)) + + # Register menu items. + self._updateMenuItems() + + # Make auto-backup on boot if required. + self._application.engineCreatedSignal.connect(self._autoBackup) + + def showDriveWindow(self) -> None: + """Show the Drive UI popup window.""" + if not self._drive_window: + self._drive_window = self.createDriveWindow() + self.refreshBackups() + self._drive_window.show() + + def createDriveWindow(self) -> Optional["QObject"]: + """ + Create an instance of the Drive UI popup window. + :return: The popup window object. + """ + path = os.path.join(os.path.dirname(__file__), "qml", "main.qml") + return self._application.createQmlComponent(path, {"CuraDrive": self}) + + def _updateMenuItems(self) -> None: + """Update the menu items.""" + self.addMenuItem(Settings.translatable_messages["extension_menu_entry"], self.showDriveWindow) + + def _autoBackup(self) -> None: + """Automatically make a backup on boot if enabled.""" + if self._preferences.getValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY) and self._lastBackupTooLongAgo(): + self.createBackup() + + def _lastBackupTooLongAgo(self) -> bool: + """Check if the last backup was longer than 1 day ago.""" + current_date = datetime.now() + last_backup_date = self._getLastBackupDate() + date_diff = current_date - last_backup_date + return date_diff.days > 1 + + def _getLastBackupDate(self) -> "datetime": + """Get the last backup date as datetime object.""" + last_backup_date = self._preferences.getValue(Settings.AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY) + return datetime.strptime(last_backup_date, self.DATE_FORMAT) + + def _storeBackupDate(self) -> None: + """Store the current date as last backup date.""" + backup_date = datetime.now().strftime(self.DATE_FORMAT) + self._preferences.setValue(Settings.AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY, backup_date) + + def _onLoginStateChanged(self, logged_in: bool = False) -> None: + """Callback handler for changes in the login state.""" + if logged_in: + self.refreshBackups() + + def _onRestoringStateChanged(self, is_restoring: bool = False, error_message: str = None) -> None: + """Callback handler for changes in the restoring state.""" + self._is_restoring_backup = is_restoring + self.restoringStateChanged.emit() + if error_message: + Message(error_message, title = Settings.MESSAGE_TITLE, lifetime = 5).show() + + def _onCreatingStateChanged(self, is_creating: bool = False, error_message: str = None) -> None: + """Callback handler for changes in the creation state.""" + self._is_creating_backup = is_creating + self.creatingStateChanged.emit() + if error_message: + Message(error_message, title = Settings.MESSAGE_TITLE, lifetime = 5).show() + else: + self._storeBackupDate() + if not is_creating: + # We've finished creating a new backup, to the list has to be updated. + self.refreshBackups() + + @pyqtSlot(bool, name = "toggleAutoBackup") + def toggleAutoBackup(self, enabled: bool) -> None: + """Enable or disable the auto-backup feature.""" + self._preferences.setValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY, enabled) + self.preferencesChanged.emit() + + @pyqtProperty(bool, notify = preferencesChanged) + def autoBackupEnabled(self) -> bool: + """Check if auto-backup is enabled or not.""" + return bool(self._preferences.getValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY)) + + @pyqtProperty(QObject, notify = backupsChanged) + def backups(self) -> BackupListModel: + """ + Get a list of the backups. + :return: The backups as Qt List Model. + """ + return self._backups_list_model + + @pyqtSlot(name = "refreshBackups") + def refreshBackups(self) -> None: + """ + Forcefully refresh the backups list. + """ + self._backups_list_model.loadBackups(self._drive_api_service.getBackups()) + self.backupsChanged.emit() + + @pyqtProperty(bool, notify = restoringStateChanged) + def isRestoringBackup(self) -> bool: + """ + Get the current restoring state. + :return: Boolean if we are restoring or not. + """ + return self._is_restoring_backup + + @pyqtProperty(bool, notify = creatingStateChanged) + def isCreatingBackup(self) -> bool: + """ + Get the current creating state. + :return: Boolean if we are creating or not. + """ + return self._is_creating_backup + + @pyqtSlot(str, name = "restoreBackup") + def restoreBackup(self, backup_id: str) -> None: + """ + Download and restore a backup by ID. + :param backup_id: The ID of the backup. + """ + index = self._backups_list_model.find("backup_id", backup_id) + backup = self._backups_list_model.getItem(index) + self._drive_api_service.restoreBackup(backup) + + @pyqtSlot(name = "createBackup") + def createBackup(self) -> None: + """ + Create a new backup. + """ + self._drive_api_service.createBackup() + + @pyqtSlot(str, name = "deleteBackup") + def deleteBackup(self, backup_id: str) -> None: + """ + Delete a backup by ID. + :param backup_id: The ID of the backup. + """ + self._drive_api_service.deleteBackup(backup_id) + self.refreshBackups() diff --git a/plugins/CuraDrive/src/Settings.py b/plugins/CuraDrive/src/Settings.py new file mode 100644 index 0000000000..277a976cc7 --- /dev/null +++ b/plugins/CuraDrive/src/Settings.py @@ -0,0 +1,36 @@ +# Copyright (c) 2018 Ultimaker B.V. +from UM import i18nCatalog + + +class Settings: + """ + Keeps the application settings. + """ + UM_CLOUD_API_ROOT = "https://api.ultimaker.com" + DRIVE_API_VERSION = 1 + DRIVE_API_URL = "{}/cura-drive/v{}".format(UM_CLOUD_API_ROOT, str(DRIVE_API_VERSION)) + + AUTO_BACKUP_ENABLED_PREFERENCE_KEY = "cura_drive/auto_backup_enabled" + AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY = "cura_drive/auto_backup_date" + + I18N_CATALOG_ID = "cura_drive" + I18N_CATALOG = i18nCatalog(I18N_CATALOG_ID) + + MESSAGE_TITLE = I18N_CATALOG.i18nc("@info:title", "Backups"), + + # Translatable messages for the entire plugin. + translatable_messages = { + + # Menu items. + "extension_menu_entry": I18N_CATALOG.i18nc("@item:inmenu", "Manage backups"), + + # Notification messages. + "backup_failed": I18N_CATALOG.i18nc("@info:backup_status", "There was an error while creating your backup."), + "uploading_backup": I18N_CATALOG.i18nc("@info:backup_status", "Uploading your backup..."), + "uploading_backup_success": I18N_CATALOG.i18nc("@info:backup_status", "Your backup has finished uploading."), + "uploading_backup_error": I18N_CATALOG.i18nc("@info:backup_status", + "There was an error while uploading your backup."), + "get_backups_error": I18N_CATALOG.i18nc("@info:backup_status", "There was an error listing your backups."), + "backup_restore_error_message": I18N_CATALOG.i18nc("@info:backup_status", + "There was an error trying to restore your backup.") + } diff --git a/plugins/CuraDrive/src/UploadBackupJob.py b/plugins/CuraDrive/src/UploadBackupJob.py new file mode 100644 index 0000000000..039e6d1a09 --- /dev/null +++ b/plugins/CuraDrive/src/UploadBackupJob.py @@ -0,0 +1,39 @@ +# Copyright (c) 2018 Ultimaker B.V. +import requests + +from UM.Job import Job +from UM.Logger import Logger +from UM.Message import Message + +from .Settings import Settings + + +class UploadBackupJob(Job): + """ + This job is responsible for uploading the backup file to cloud storage. + As it can take longer than some other tasks, we schedule this using a Cura Job. + """ + + def __init__(self, signed_upload_url: str, backup_zip: bytes): + super().__init__() + self._signed_upload_url = signed_upload_url + self._backup_zip = backup_zip + self._upload_success = False + self.backup_upload_error_message = "" + + def run(self): + Message(Settings.translatable_messages["uploading_backup"], title = Settings.MESSAGE_TITLE, + lifetime = 10).show() + + backup_upload = requests.put(self._signed_upload_url, data = self._backup_zip) + if backup_upload.status_code not in (200, 201): + self.backup_upload_error_message = backup_upload.text + Logger.log("w", "Could not upload backup file: %s", backup_upload.text) + Message(Settings.translatable_messages["uploading_backup_error"], title = Settings.MESSAGE_TITLE, + lifetime = 10).show() + else: + self._upload_success = True + Message(Settings.translatable_messages["uploading_backup_success"], title = Settings.MESSAGE_TITLE, + lifetime = 10).show() + + self.finished.emit(self) diff --git a/plugins/CuraDrive/src/__init__.py b/plugins/CuraDrive/src/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/CuraDrive/src/models/BackupListModel.py b/plugins/CuraDrive/src/models/BackupListModel.py new file mode 100644 index 0000000000..9567b3d255 --- /dev/null +++ b/plugins/CuraDrive/src/models/BackupListModel.py @@ -0,0 +1,38 @@ +# Copyright (c) 2018 Ultimaker B.V. +from typing import List, Dict + +from UM.Qt.ListModel import ListModel + +from PyQt5.QtCore import Qt + + +class BackupListModel(ListModel): + """ + The BackupListModel transforms the backups data that came from the server so it can be served to the Qt UI. + """ + + def __init__(self, parent=None): + super().__init__(parent) + self.addRoleName(Qt.UserRole + 1, "backup_id") + self.addRoleName(Qt.UserRole + 2, "download_url") + self.addRoleName(Qt.UserRole + 3, "generated_time") + self.addRoleName(Qt.UserRole + 4, "md5_hash") + self.addRoleName(Qt.UserRole + 5, "data") + + def loadBackups(self, data: List[Dict[str, any]]) -> None: + """ + Populate the model with server data. + :param data: + """ + items = [] + for backup in data: + # We do this loop because we only want to append these specific fields. + # Without this, ListModel will break. + items.append({ + "backup_id": backup["backup_id"], + "download_url": backup["download_url"], + "generated_time": backup["generated_time"], + "md5_hash": backup["md5_hash"], + "data": backup["metadata"] + }) + self.setItems(items) diff --git a/plugins/CuraDrive/src/models/__init__.py b/plugins/CuraDrive/src/models/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/CuraDrive/src/qml/components/ActionButton.qml b/plugins/CuraDrive/src/qml/components/ActionButton.qml new file mode 100644 index 0000000000..843079ed88 --- /dev/null +++ b/plugins/CuraDrive/src/qml/components/ActionButton.qml @@ -0,0 +1,67 @@ +// Copyright (c) 2018 Ultimaker B.V. +import QtQuick 2.7 +import QtQuick.Controls 2.1 +import QtQuick.Layouts 1.3 + +import UM 1.1 as UM + +Button +{ + id: button + property alias cursorShape: mouseArea.cursorShape + property var iconSource: "" + property var busy: false + property var color: UM.Theme.getColor("primary") + property var hoverColor: UM.Theme.getColor("primary_hover") + property var disabledColor: color + property var textColor: UM.Theme.getColor("button_text") + property var textHoverColor: UM.Theme.getColor("button_text_hover") + property var textDisabledColor: textColor + property var textFont: UM.Theme.getFont("action_button") + + contentItem: RowLayout + { + Icon + { + id: buttonIcon + iconSource: button.iconSource + width: 16 * screenScaleFactor + color: button.hovered ? button.textHoverColor : button.textColor + visible: button.iconSource != "" && !loader.visible + } + + Icon + { + id: loader + iconSource: "../images/loading.gif" + width: 16 * screenScaleFactor + color: button.hovered ? button.textHoverColor : button.textColor + visible: button.busy + animated: true + } + + Label + { + id: buttonText + text: button.text + color: button.enabled ? (button.hovered ? button.textHoverColor : button.textColor): button.textDisabledColor + font: button.textFont + visible: button.text != "" + renderType: Text.NativeRendering + } + } + + background: Rectangle + { + color: button.enabled ? (button.hovered ? button.hoverColor : button.color) : button.disabledColor + } + + MouseArea + { + id: mouseArea + anchors.fill: parent + onPressed: mouse.accepted = false + hoverEnabled: true + cursorShape: button.enabled ? (hovered ? Qt.PointingHandCursor : Qt.ArrowCursor) : Qt.ForbiddenCursor + } +} diff --git a/plugins/CuraDrive/src/qml/components/ActionCheckBox.qml b/plugins/CuraDrive/src/qml/components/ActionCheckBox.qml new file mode 100644 index 0000000000..71f5e6035d --- /dev/null +++ b/plugins/CuraDrive/src/qml/components/ActionCheckBox.qml @@ -0,0 +1,49 @@ +// Copyright (c) 2018 Ultimaker B.V. +import QtQuick 2.7 +import QtQuick.Controls 2.1 +import QtQuick.Layouts 1.3 + +import UM 1.3 as UM + +CheckBox +{ + id: checkbox + hoverEnabled: true + + property var label: "" + + indicator: Rectangle { + implicitWidth: 30 * screenScaleFactor + implicitHeight: 30 * screenScaleFactor + x: 0 + y: Math.round(parent.height / 2 - height / 2) + color: UM.Theme.getColor("sidebar") + border.color: UM.Theme.getColor("text") + + Rectangle { + width: 14 * screenScaleFactor + height: 14 * screenScaleFactor + x: 8 * screenScaleFactor + y: 8 * screenScaleFactor + color: UM.Theme.getColor("primary") + visible: checkbox.checked + } + } + + contentItem: Label { + anchors + { + left: checkbox.indicator.right + leftMargin: 5 * screenScaleFactor + } + text: catalog.i18nc("@checkbox:description", "Auto Backup") + color: UM.Theme.getColor("text") + renderType: Text.NativeRendering + verticalAlignment: Text.AlignVCenter + } + + ActionToolTip + { + text: checkbox.label + } +} diff --git a/plugins/CuraDrive/src/qml/components/ActionToolTip.qml b/plugins/CuraDrive/src/qml/components/ActionToolTip.qml new file mode 100644 index 0000000000..93b92bc2df --- /dev/null +++ b/plugins/CuraDrive/src/qml/components/ActionToolTip.qml @@ -0,0 +1,29 @@ +// Copyright (c) 2018 Ultimaker B.V. +import QtQuick 2.7 +import QtQuick.Controls 2.1 +import QtQuick.Layouts 1.3 + +import UM 1.1 as UM + +ToolTip +{ + id: tooltip + visible: parent.hovered + opacity: 0.9 + delay: 500 + + background: Rectangle + { + color: UM.Theme.getColor("sidebar") + border.color: UM.Theme.getColor("primary") + border.width: 1 * screenScaleFactor + } + + contentItem: Label + { + text: tooltip.text + color: UM.Theme.getColor("text") + font: UM.Theme.getFont("very_small") + renderType: Text.NativeRendering + } +} diff --git a/plugins/CuraDrive/src/qml/components/BackupList.qml b/plugins/CuraDrive/src/qml/components/BackupList.qml new file mode 100644 index 0000000000..231f25afc8 --- /dev/null +++ b/plugins/CuraDrive/src/qml/components/BackupList.qml @@ -0,0 +1,31 @@ +// Copyright (c) 2018 Ultimaker B.V. +import QtQuick 2.7 +import QtQuick.Controls 2.1 +import QtQuick.Layouts 1.3 + +import UM 1.1 as UM + +ListView +{ + id: backupList + width: parent.width + clip: true + delegate: Item + { + width: parent.width + height: childrenRect.height + + BackupListItem + { + id: backupListItem + width: parent.width + } + + Divider + { + width: parent.width + anchors.top: backupListItem.bottom + } + } + ScrollBar.vertical: RightSideScrollBar {} +} diff --git a/plugins/CuraDrive/src/qml/components/BackupListFooter.qml b/plugins/CuraDrive/src/qml/components/BackupListFooter.qml new file mode 100644 index 0000000000..80f47d6cba --- /dev/null +++ b/plugins/CuraDrive/src/qml/components/BackupListFooter.qml @@ -0,0 +1,42 @@ +// Copyright (c) 2018 Ultimaker B.V. +import QtQuick 2.7 +import QtQuick.Controls 2.1 +import QtQuick.Layouts 1.3 + +import UM 1.3 as UM + +import "../components" + +RowLayout +{ + id: backupListFooter + width: parent.width + property bool showInfoButton: false + + ActionButton + { + id: infoButton + text: catalog.i18nc("@button", "Want more?") + iconSource: "../images/info.svg" + onClicked: Qt.openUrlExternally("https://goo.gl/forms/QACEP8pP3RV60QYG2") + visible: backupListFooter.showInfoButton + } + + ActionButton + { + id: createBackupButton + text: catalog.i18nc("@button", "Backup Now") + iconSource: "../images/backup.svg" + enabled: !CuraDrive.isCreatingBackup && !CuraDrive.isRestoringBackup + onClicked: CuraDrive.createBackup() + busy: CuraDrive.isCreatingBackup + } + + ActionCheckBox + { + id: autoBackupEnabled + checked: CuraDrive.autoBackupEnabled + onClicked: CuraDrive.toggleAutoBackup(autoBackupEnabled.checked) + label: catalog.i18nc("@checkbox:description", "Automatically create a backup each day that Cura is started.") + } +} diff --git a/plugins/CuraDrive/src/qml/components/BackupListItem.qml b/plugins/CuraDrive/src/qml/components/BackupListItem.qml new file mode 100644 index 0000000000..abe9a1acf9 --- /dev/null +++ b/plugins/CuraDrive/src/qml/components/BackupListItem.qml @@ -0,0 +1,112 @@ +// Copyright (c) 2018 Ultimaker B.V. +import QtQuick 2.7 +import QtQuick.Controls 2.1 +import QtQuick.Layouts 1.3 +import QtQuick.Dialogs 1.1 + +import UM 1.1 as UM + +Item +{ + id: backupListItem + width: parent.width + height: showDetails ? dataRow.height + backupDetails.height : dataRow.height + property bool showDetails: false + + // Backup details toggle animation. + Behavior on height + { + PropertyAnimation + { + duration: 70 + } + } + + RowLayout + { + id: dataRow + spacing: UM.Theme.getSize("default_margin").width * 2 + width: parent.width + height: 50 * screenScaleFactor + + ActionButton + { + color: "transparent" + hoverColor: "transparent" + textColor: UM.Theme.getColor("text") + textHoverColor: UM.Theme.getColor("primary") + iconSource: "../images/info.svg" + onClicked: backupListItem.showDetails = !backupListItem.showDetails + } + + Label + { + text: new Date(model["generated_time"]).toLocaleString(UM.Preferences.getValue("general/language")) + color: UM.Theme.getColor("text") + elide: Text.ElideRight + Layout.minimumWidth: 100 * screenScaleFactor + Layout.maximumWidth: 500 * screenScaleFactor + Layout.fillWidth: true + renderType: Text.NativeRendering + } + + Label + { + text: model["data"]["description"] + color: UM.Theme.getColor("text") + elide: Text.ElideRight + Layout.minimumWidth: 100 * screenScaleFactor + Layout.maximumWidth: 500 * screenScaleFactor + Layout.fillWidth: true + renderType: Text.NativeRendering + } + + ActionButton + { + text: catalog.i18nc("@button", "Restore") + color: "transparent" + hoverColor: "transparent" + textColor: UM.Theme.getColor("text") + textHoverColor: UM.Theme.getColor("text_link") + enabled: !CuraDrive.isCreatingBackup && !CuraDrive.isRestoringBackup + onClicked: confirmRestoreDialog.visible = true + } + + ActionButton + { + color: "transparent" + hoverColor: "transparent" + textColor: UM.Theme.getColor("setting_validation_error") + textHoverColor: UM.Theme.getColor("setting_validation_error") + iconSource: "../images/delete.svg" + onClicked: confirmDeleteDialog.visible = true + } + } + + BackupListItemDetails + { + id: backupDetails + backupDetailsData: model + width: parent.width + visible: parent.showDetails + anchors.top: dataRow.bottom + } + + MessageDialog + { + id: confirmDeleteDialog + title: catalog.i18nc("@dialog:title", "Delete Backup") + text: catalog.i18nc("@dialog:info", "Are you sure you want to delete this backup? This cannot be undone.") + standardButtons: StandardButton.Yes | StandardButton.No + onYes: CuraDrive.deleteBackup(model["backup_id"]) + } + + MessageDialog + { + id: confirmRestoreDialog + title: catalog.i18nc("@dialog:title", "Restore Backup") + text: catalog.i18nc("@dialog:info", "You will need to restart Cura before your backup is restored. Do you want to close Cura now?") + standardButtons: StandardButton.Yes | StandardButton.No + onYes: CuraDrive.restoreBackup(model["backup_id"]) + } +} diff --git a/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml b/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml new file mode 100644 index 0000000000..74d4c5ab57 --- /dev/null +++ b/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml @@ -0,0 +1,61 @@ +// Copyright (c) 2018 Ultimaker B.V. +import QtQuick 2.7 +import QtQuick.Controls 2.1 +import QtQuick.Layouts 1.3 + +import UM 1.1 as UM + +ColumnLayout +{ + id: backupDetails + width: parent.width + spacing: 10 * screenScaleFactor + property var backupDetailsData + + // Cura version + BackupListItemDetailsRow + { + iconSource: "../images/cura.svg" + label: catalog.i18nc("@backuplist:label", "Cura Version") + value: backupDetailsData["data"]["cura_release"] + } + + // Machine count. + BackupListItemDetailsRow + { + iconSource: "../images/printer.svg" + label: catalog.i18nc("@backuplist:label", "Machines") + value: backupDetailsData["data"]["machine_count"] + } + + // Meterial count. + BackupListItemDetailsRow + { + iconSource: "../images/material.svg" + label: catalog.i18nc("@backuplist:label", "Materials") + value: backupDetailsData["data"]["material_count"] + } + + // Meterial count. + BackupListItemDetailsRow + { + iconSource: "../images/profile.svg" + label: catalog.i18nc("@backuplist:label", "Profiles") + value: backupDetailsData["data"]["profile_count"] + } + + // Meterial count. + BackupListItemDetailsRow + { + iconSource: "../images/plugin.svg" + label: catalog.i18nc("@backuplist:label", "Plugins") + value: backupDetailsData["data"]["plugin_count"] + } + + // Spacer. + Item + { + width: parent.width + height: 10 * screenScaleFactor + } +} diff --git a/plugins/CuraDrive/src/qml/components/BackupListItemDetailsRow.qml b/plugins/CuraDrive/src/qml/components/BackupListItemDetailsRow.qml new file mode 100644 index 0000000000..dad1674fe7 --- /dev/null +++ b/plugins/CuraDrive/src/qml/components/BackupListItemDetailsRow.qml @@ -0,0 +1,52 @@ +// Copyright (c) 2018 Ultimaker B.V. +import QtQuick 2.7 +import QtQuick.Controls 2.1 +import QtQuick.Layouts 1.3 + +import UM 1.3 as UM + +RowLayout +{ + id: detailsRow + width: parent.width + height: 40 * screenScaleFactor + + property var iconSource + property var label + property var value + + // Spacing. + Item + { + width: 40 * screenScaleFactor + } + + Icon + { + width: 18 * screenScaleFactor + iconSource: detailsRow.iconSource + color: UM.Theme.getColor("text") + } + + Label + { + text: detailsRow.label + color: UM.Theme.getColor("text") + elide: Text.ElideRight + Layout.minimumWidth: 50 * screenScaleFactor + Layout.maximumWidth: 100 * screenScaleFactor + Layout.fillWidth: true + renderType: Text.NativeRendering + } + + Label + { + text: detailsRow.value + color: UM.Theme.getColor("text") + elide: Text.ElideRight + Layout.minimumWidth: 50 * screenScaleFactor + Layout.maximumWidth: 100 * screenScaleFactor + Layout.fillWidth: true + renderType: Text.NativeRendering + } +} diff --git a/plugins/CuraDrive/src/qml/components/Divider.qml b/plugins/CuraDrive/src/qml/components/Divider.qml new file mode 100644 index 0000000000..bba2f2f29c --- /dev/null +++ b/plugins/CuraDrive/src/qml/components/Divider.qml @@ -0,0 +1,11 @@ +// Copyright (c) 2018 Ultimaker B.V. +import QtQuick 2.7 + +import UM 1.3 as UM + +Rectangle +{ + id: divider + color: UM.Theme.getColor("lining") + height: UM.Theme.getSize("default_lining").height +} diff --git a/plugins/CuraDrive/src/qml/components/Icon.qml b/plugins/CuraDrive/src/qml/components/Icon.qml new file mode 100644 index 0000000000..3cb822bf82 --- /dev/null +++ b/plugins/CuraDrive/src/qml/components/Icon.qml @@ -0,0 +1,56 @@ +// Copyright (c) 2018 Ultimaker B.V. +import QtQuick 2.7 +import QtQuick.Controls 2.1 +import QtGraphicalEffects 1.0 + +Item +{ + id: icon + width: parent.height + height: width + property var color: "transparent" + property var iconSource + property bool animated: false + + Image + { + id: iconImage + width: parent.height + height: width + smooth: true + source: icon.iconSource + sourceSize.width: width + sourceSize.height: height + antialiasing: true + visible: !icon.animated + } + + AnimatedImage + { + id: animatedIconImage + width: parent.height + height: width + smooth: true + antialiasing: true + source: "../images/loading.gif" + visible: icon.animated + } + + ColorOverlay + { + anchors.fill: iconImage + source: iconImage + color: icon.color + antialiasing: true + visible: !icon.animated + } + + ColorOverlay + { + anchors.fill: animatedIconImage + source: animatedIconImage + color: icon.color + antialiasing: true + visible: icon.animated + } +} diff --git a/plugins/CuraDrive/src/qml/components/RightSideScrollBar.qml b/plugins/CuraDrive/src/qml/components/RightSideScrollBar.qml new file mode 100644 index 0000000000..5ac5df15ff --- /dev/null +++ b/plugins/CuraDrive/src/qml/components/RightSideScrollBar.qml @@ -0,0 +1,13 @@ +// Copyright (c) 2018 Ultimaker B.V. +import QtQuick 2.7 +import QtQuick.Controls 2.1 +import QtQuick.Layouts 1.3 + +ScrollBar +{ + active: true + size: parent.height + anchors.top: parent.top + anchors.right: parent.right + anchors.bottom: parent.bottom +} diff --git a/plugins/CuraDrive/src/qml/images/avatar_default.png b/plugins/CuraDrive/src/qml/images/avatar_default.png new file mode 100644 index 0000000000000000000000000000000000000000..0c306680f721dba0de73fb7be90fe5c548608692 GIT binary patch literal 3115 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 + + + Polygon 18 + Created with Sketch. + + + + + + + \ No newline at end of file diff --git a/plugins/CuraDrive/src/qml/images/backup.svg b/plugins/CuraDrive/src/qml/images/backup.svg new file mode 100644 index 0000000000..51f6be4cba --- /dev/null +++ b/plugins/CuraDrive/src/qml/images/backup.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/CuraDrive/src/qml/images/cura.svg b/plugins/CuraDrive/src/qml/images/cura.svg new file mode 100644 index 0000000000..6b1b6c0c79 --- /dev/null +++ b/plugins/CuraDrive/src/qml/images/cura.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/plugins/CuraDrive/src/qml/images/cura_logo.jpg b/plugins/CuraDrive/src/qml/images/cura_logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..621c03f035cddb3c9337099c25bccf19a5866968 GIT binary patch literal 19308 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 V{_(E9+wT8(SKsZ~f4r-A{|^$iZYBT# literal 0 HcmV?d00001 diff --git a/plugins/CuraDrive/src/qml/images/cura_logo.png b/plugins/CuraDrive/src/qml/images/cura_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..f846f2a0f0934f4843c3279ed741c149c77b2e1e GIT binary patch literal 13258 zcmeHu;4V*)BSRu^XaTvbLN?uz4uJ)2XzHpY!LRVSFdoD6lJwuy+WyZdCve~ygc*u(2ZXX z6n8BJ=~tEGlzXpU@kS`gN`3M{In2g&`K-Ng?48+YOUK6%Q!&j{8UO%f(B`2OjGe6E z=pFl$ws^S=Htqi&Eu+nt`f5zkAyuM|<9+lUC7ObBbhAJ)l>)Q49oq{+g+>xugU({Y z!PhW}n#xpMueQi{{k(Xgq^qIfb|t>U@SfRMr>zd5Z{lC^?1wlPJ#bg}3RdVQbU~*? z`TxiNdl#7g_x2?FIG|)>n5~LZ=;-U{r$9xWcyi_~xxLz49$qf=d`tG6X zIStGm9v<#dC-s0$b#qRMgsHFQp^FeRDg%|8>pO3^vnue#Z%ltH62Zo)abYu)lLUvz zhB9bh6s{bH^OfNC;bPHuwsUnckKTeN_+-^P3q;Rl_BXG`4v(a2lIvSvy%OYFLvt*Z39TCgr_a$F=Mp8M>*tj%kWC~v)94VFN5 z1qXZl?GEiRi;f`o`qcv{-e76%eFdNVsQd3-SZTWP)QI7xn+|JN!Q5xxAATLlA*Y)u z8pe!KMjd{a`|_;R{4@b4QtZ`tcX#VkMd;L<4<)IL{^KurzKG;6p8r3E0*#`BRc1%Z4{vHItk9179tO)8Rr)A_% z^@S+;h%bJ(TWOfDQ?c0pN|yDfn~1yLxnevG5wggjcb=tILHvFOzr@qF%`oJMOSJ+{ zvx|e^`kBeW*=VvqpXlg0-=GhY0MM;vt$z~ATSwB$WVr;veV2t$H*iB= z9Qz*R+wd#7o~OW+AvB*R9cy`8kkDbTmzQzWzP^*cskOBg%~EfKP~j8Ris_&aPFLcN zUi#uzboWne?!(MqzJEh8^?ZVidSPC0n3@71p>*8%Z)6$;fFKy%0QQAvykQl%ipl86D zp6LP5w;D0G`6p$=CEh&yyO5|8W$0;Ch)sve$3xGmuBW@dNk;N!XYs*xus)kGap#&jjLnRa>E$AcC6{{L0hB2p z(d5UzFVqZCgKNN3ck+4~>(E_#s08-L_qzM?y77I))HmP8By}+g-%woX6(1QP(8wUT z8_$T)hmPi*wF8sEf`U=ZECL~5A8}a?!?V`I{7*d4ZwX7Lx}uJP?o@<9_<=V#dT;p< zkU)+;E_S^HTK+lpxLcUa<4?$|o6Zp!vVcu&0D#Dwt;O`Ed(S5i;&3`TIyU^WPfJ>o zMJ{QDzv6}BI!lIRtO*usuLGIrp1YIl0XL^Co4C=jrpf69aHA(>N7tQyTpu5(GS~d5%UqzuqpMu?ZR; zn5YT+op(z-ohE~l$Y}oqN`(%5@MOS)F7%$_xUvh1wzo$U0txB7mObYxX2K4(z=BQ| zLm$e;sDrk?iu{re_7*`Ura)P?pJy5L-*KN1(;Z=9a~CixFsrkfDi}Z}`u3eqNJx

    {tidI^DPfL*th*F*+_ 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 literal 0 HcmV?d00001 diff --git a/plugins/CuraDrive/src/qml/images/delete.svg b/plugins/CuraDrive/src/qml/images/delete.svg new file mode 100644 index 0000000000..2f6190ad43 --- /dev/null +++ b/plugins/CuraDrive/src/qml/images/delete.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/plugins/CuraDrive/src/qml/images/folder.svg b/plugins/CuraDrive/src/qml/images/folder.svg new file mode 100644 index 0000000000..f66f83a888 --- /dev/null +++ b/plugins/CuraDrive/src/qml/images/folder.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/plugins/CuraDrive/src/qml/images/home.svg b/plugins/CuraDrive/src/qml/images/home.svg new file mode 100644 index 0000000000..9d0e4d802c --- /dev/null +++ b/plugins/CuraDrive/src/qml/images/home.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/CuraDrive/src/qml/images/icon.png b/plugins/CuraDrive/src/qml/images/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3f75491786dcc0939175ffbab5e791e27eaefc2c GIT binary patch literal 21924 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�>#6>;@A&7;Ub9*u@xRReL>D;wp-rSVGg8b(Ftu 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 d{(EOoFjyv&`}RG)N<`qZ!PU# + + + \ No newline at end of file diff --git a/plugins/CuraDrive/src/qml/images/inverted_circle.png b/plugins/CuraDrive/src/qml/images/inverted_circle.png new file mode 100644 index 0000000000000000000000000000000000000000..3612b37d4d38cd7b1218be42c943932a79f7b465 GIT binary patch literal 1608 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 F;r|kGDY5_n literal 0 HcmV?d00001 diff --git a/plugins/CuraDrive/src/qml/images/material.svg b/plugins/CuraDrive/src/qml/images/material.svg new file mode 100644 index 0000000000..eac724e471 --- /dev/null +++ b/plugins/CuraDrive/src/qml/images/material.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/plugins/CuraDrive/src/qml/images/plugin.svg b/plugins/CuraDrive/src/qml/images/plugin.svg new file mode 100644 index 0000000000..674eb99a54 --- /dev/null +++ b/plugins/CuraDrive/src/qml/images/plugin.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/plugins/CuraDrive/src/qml/images/preview_banner.png b/plugins/CuraDrive/src/qml/images/preview_banner.png new file mode 100644 index 0000000000000000000000000000000000000000..414019531beed2e503e05cc394fc7e6de246830a GIT binary patch literal 8324 zcmY+pc|4Tg`#*kW%rM3_LUwME>|3_Va@&vJ_^F zvXre5vMXEmC59~D+w1qo@BNv7=AQfUyq?!}u5)eY%snd$6E?g69st00+|=+i04U@U z1#pbW*SQ;c?t8y(o;J}3#ce{<06;p&4fSjSP;(hfDYo738+#yD*4t4J`8av5@x`5z zQ$A(Gt@byhojdN)tLDSG)e;x7rte9wDJ-8n`CMW-t3*m7@E*4R@uxh!3*5mBa+(bF z-0o5ghtJLo^{qb-o?QL7p1->t;xnIK)~>Vretlu~?bn~Tv-MquA&2FO3t6?6g-*SU zw&Z#Bqi%oh6co;|1s!aQM*B)x+4)+-Wc`OeVE{dPtKJrQ!win2AaWf$k;ur&X@tSE zVu%CWgbqj>BI_9%p0`H@gC=tnitRfCTENhdn~}$d@&5o!QU3oIz`gi?058s>!azbA z8ZD5`gi(!&ky4cz5d}A_VK`#-Kb?`1*l-l=g~we^5JB`F$m$@&KL1e)e|PIGW#r*B zx{klgKpfyAP+n5J@20E*XtJ!F=td9a$Zi1|g=NVfasxzLeE5 z@H>|X zC1x`ba9&SQb^y&FQO7QiPhlcTa1lCO<&f3K#AKZyT?UW_7E_STv2q9^7^|K8RRrGx z3910ymb4gxAuP)CSghJW4#4yVKv}*F$OO0m&%1}WqzKwT!j?!B?GH$2zy;v$*6VP` zFo8!(Ff}@`+5&~C=BD}jW5*cwtn{gPSn`rcRD9<1=rwu?4L`!*dIcnVEnq0cS<_LP zpbI1p5{YFdY>qIQap^|z6%_z2BfG64hJ?VUb4H0mAZC_GjA~&*)~c*=N`eI(I0li$ zR!~Spv>0(WGZ35DahIF!?78%?sF4Z^D%%qm-4j1)l6dZa#owNi5d2^9i`>mj|0~XG zbQJurxKX0W|B4?z#d@OqaaZ@>@BG%2j|0B82Jn4JdIs%Y;RwmhJ?O2VK(z@Th*7$Z zPiZ>uuGWOYWDgAgCHby)3^1!+Nnz$fq1_K(3A$j}bBJi$6C28&M+;l-ilTMu^bZ*{ z%6q@?FJE)Wbnf)N!+v?6%KLBGyx>4!17zhyw=G6vp6u)Wa|Rj&1T_^j>J_bol^rK_ zu(Ug$c#q>`#`MZVS%&LqzetLwiQ@L}XY4=w`t3Sgca;sMNL(F~BN<{o#sutl4iNa0 z+kl$Cn7KK^xY=ece5)bT5k-QI2uw1bhPE@ok4E#pNuuLuSY=@FbhD1~Yf_SsT9O~X zjViAW(~0jRCUN+S$OagnfJQ17W&i!ub8QQe@2Y02VK^avEkZUS&JxVrd^K_tx;&cP z_|dI336;snoe&in}Ylb;5!~JrdTJy_4;Bi0!3!tAOpM z;{pc^mppHdl}`hN*IYQ%N1EdCZQ8Hv7q=&pLoCiOc@EzC{5B(}URTpcb?)$0pK0$J zU4W_yl2k08MZ@nb2$*y{MPj9qDK1yrWd49|NTldaO+;$Nyj;30Tf~Z=@WjjqpF=35 z{+Kn|_AwHBArzrhcI$il@Hgvg)Zm_&x>dsZ(!WT!r%flQIK)Icy7igryVd>{4wxES zywsQ)0%97J^${1E>F(ZJ_a?8+jY6f7Vq{uz{dSK?ms(u~5%y_)6lSCYAio$C+AGqf zc0;tkGQ%PW(!@xvu-1t;^d#Ttu}%v*L22Cqn3g|+A+sAO;$=ZGOf{aO@}3K`COS7z z>c9OWuFKvF`(K|=gy3_b(usHkM7AxLdRpP=yd;-c+iW51r zEe5TVyvo^imbDu-A2!)V?JU=YG@duu56vsyUhVK6?sW+GC8`l9WiEQQn16Nc@3%Kv z0*i@BBsCztMLBvyPPj`od&ClmGj=A*zqGg%ih@oE5{bAEi>1a#y(gN&<+u0m(3-b~ z&$~r;?5e=o9#nbL(cOjuzYl+^S{W>?BX?J)Kc~|ElxJCU!mdP{s^LU$5VOIiKT@O} z{V!<6Zj#m@c6;@0K|$@`bd3-G9nZpk8gi(4znxduDpuI~gMndb(ra53fV-41q-R?C z3z2O%!fI>-bSb+VPHeZd?EZPEvA@V4=wA0I{-fwZd74z*uYX;8+zaMOwjQ->M4@sp zGJ?B^GB*2NuubCdvbSt4~IO{HR-as07H1s>`9T08LUs4Ii`leqN4aV#d=hC zE-`NP{nNQyxu5f!Q0LMJ?fP6fh5Etp0@3d{n@NlS{GKXZ$Xv;@b7>%$OnX&nw$!u# z)qy-_sY}Z>STH-AxerH|-}=M(|66dX##|EO;#B)#_Usgm!T4K%xHiM1eO;L9SpqP{pk3)U>l(7vfEc9gTHn1oWpG#(P?(Cv}&fy9Yhz zB|If?qsPu(7UqyWGrG;S-2)*RR8EAIYFaYc0Op$}Bd(GsbYsG$y^yhev^%0+pE_nA zBgr1&@dD}-W0^z)z1MvI<;{s8dz?_Sb0qu3G)<;x`1CE+f*th=BXeqcdEEs@5I$|I zbqu}dZf~IKGZ;tBf)pBPn2GH^1w+*|49-3F8Pu=tOOA6bLx++b3KP7jx>< zv*^@zMd<`a-Hi$VnLlqMsAX;qJOdBEa=Y=cUlTfM*^*{hHdcy9T&gRq#gcz>)fDa)W)7>H_LIbi-afa51keEZD1 zYcH7xx({I~&INqidJk(BhTI*-N6y&{JRNQd#p$k0NfsI98GdalPiIkChTmvK>EY5_ zoo+gNhko=JO3t{iQx}TOL2~Qb2-aAqHtVU#9S`zN&MFH$rRXdqa$kwmWZrxW+2>n* ztprSVV%v15aWKbA_>oHG8pS?&t9g1uo(&o^pZ(B0n+R z{ZSK|vNQQ$jhR)QH7bx$=$jMoGI0O(wYeAF)kXFlwB4kfB=G9M`J%)Y&!RwyXVNDf z3-)19&qQ6>B0}u@=UaDi$3zDw{b#0AC%@ce0;s~W>Fqgjv+VWJ3ub|6$fwP@ewrc4 z$RHG)mg>Rb|?MRT{=52jjFaxXA|ll+RtxRy(nvB?lOgp4!)o29k4p3>fzDNTd1Rkw#IhG8GHXA3j7RIczbU(Je!2Z1Em&dN}Fk z@y*PGRhEQP3shF;=n>097G&n6%w!aHG_zhh8O9tAAL#V=| z)ATq2nA`6@SrU3c+}Y{%<%!&Fdq22Z)fmnbn&`%SjAiMUI6;jhr4rv441oJz`9?23 zblKI#mT%*4w&T5fEne;BKP~DE`Fby3Xw+j+k>WjezR6>)MAkl}Xww@eJ0#xPW&?&- z6e9?i-6=rk-6AQ5KiTN ze)(mZi%c>1oz4eNs}o5ogrfK<8+uOR!rgz4riU;)x1*$a-BOG$zrP|+8SEh!A9O!A zQ$&QxCEhynfsflqTZ+U?#fJmqD|jwAbY2=BUN}SV)}X+y8%tbM32**J1~Q!HN?7#t z;$6fqAA$4)7{l6>Hk%KRlMI}D7L2eg z-AEVhr>asixTe~n+FF{8t1cm7#j?MKwBs?Cbh^FXYc}NKF-zmCew&hs5A>PjboMzM zttEgwBW>Xx0U~_6ESx*^_Vw2UtEHwj#RyP5t(cA)8iuHakZNpx8aem z|6;SF>OSAkh4;EtVfN*rtn*5-WB~JZpFHP=Lys*Q{AD*S`)rUP$AyIHok#~rDO@|@ zj<5T-*Utuut_Le_Z;dOsth4#=g9tVnnSIdZcbd9S(2Ni{h^dH>()dt+{;2yMa{^sG zSg>HdN&7pS=8v+0-uLbPr-S1=$-k}c_nkuq>6o!fmN??W@1Fvwa6Ost=by_q&*$eW z%D*>?48cjthQ%DO1sE^!zj0ge^b_)g5@Wc=>~xPl^AmL5+PXYhu`6OWoU8r|Sj8iu znsc}RN5rUC@>q&8cAqoXqRdv-*XdwZMWp=x+vHU@v8Rte%wvwhJEPQqV&s_C^Nv(v zvc#kO=NqnpgCusZfF~7{cZUo`t+;$Th17t??J=FCJ&0VHK;rFhiGuVwQOnWfCs~ps z?uvkFo*^M*CWcYaBF~DG9}n-?Io@Gz5SNBVvX6n0?30hyUg6LtmU*vUO0vr5`-@Yy z9o_OblOJ{LG7$348168FS)1cywNK!Y17G`H7=@j-@AyVtP?11RGjjmg$Ugc_;bJG9 zbz|(5)@p#U&yuReeq+e_Y!UbwzJkit>qrzN0itc3>efR7x@Sq^Ii&YPEN{les~VY~ zo%qN4UGTLhMS{k~t*7*yANyoof=(s8tITAI0@doY5PuDfKQHl71gkmO?)q!cC!9okH}*$3U+&lls>jBLl>-=%NBH*I>2vG zni$QQ# zavq%-X!F?+i(9=GY*|;s2aV*UtRv%v7w9?dg@k?P@-M`xXq`}4h#A&kDj4{fqg?rZB3U`1kC;E(dCU?z!Q zTiad7RUd3`9k(y{GJDbAQGCoY3NyTQn%Ajbgp|HtPTKIoN~Y5&C0&RGbX$LEf#ytRC7{HE1Nd2=WiBU*w>lX6~c zq3qSK@~a6~-rrKWZxx05`u+EL!I{UW=;O_0+ee5msNGc>o?$GQwv?lpJ;W;W$(PMa z=L=t+x);o&sIb}0BY0(E;Ya6z;$@Sw^qjf#eagQsKXUPJBz=;CU6tdjTh0aQu|ZbG zrqj)gSJ7YkOQlrYx<>TvhC*g4y5+1=G$`CV(GwQgHAjS(Ezdyuyf1XE$!ye2i$2Y- zu4zPe(p6+`Vw-4SZW1o*M&csx+~efy`eNk5McKo9*B|TGCu*+@i1M1Sm|YRP%#~+! z)+y2@NZ7_IBC8B<4c0zf3@&*QRi~6SEwdFiSedal-q2Vm*O`Dqc|RE8LiT(1 zIp@kGv5ls?zLWHwCT>^j!7A^%U??+k6P8x?VF9dN5-!0P!io8`l2|6>>^o2ah#z$} zGzF9~CwW6JsUin~(P}J6f8X4b7y}V(N9`K+8uyx`D*ETlBT5aYD(fGatqbX&OZ=YJ zZVblWt}F0UW&MCdvf_bemDa^PNhSYfs}JjRW}Zhut$*G=Tr8|i*qyqRiT2R_aBn~5 zhf_$?n)k<4YWhEiyd%WP3;i+I{c}A^30FZmW6P3-ff{LC>tq0DJKjbNX)49FPq!>- zkaO5bvWfb)t?5g4zh%5WzIHMnsjIU02WsHsKzPWQOfW${@TTv1^>j&XiMR4vMTtj) zH_uJ3^w(_UESlM6-W@;pIAEc2leXaCiB8t))Ef>ecu7d!*?r`~1;DDK_AzAvA~9Lh zE0PjdC-%bEG-E$*E2@SoV`K7+%FG?jJ^}#MH!cpvo8VALM}7A43@*LagkkWFyBTr| z&~%+U%&q-XpeMp*^N{xAEC~k@vKI^Q{=lPn+D+HIHfhn4os!N(!`yw7%;?J)n=0Vw zfn`-=Ef9>7-n#Mn&c1Z&9QwQ*@+jd*H`fGC`TTyd z4=Q8{p}$~5+NWnEb*Or6q-RaOU62rcAg;RW*|P+#cKzJvU9q7tYR*_zy%CdH=a!#F zy;R#}$9Ms=(3;?GG|6+Q-}G$*K#TSM@wriBSw`jMnt?s%SL+4^PBc1CO-Wa(%g!mSi622+6U%?3o)fuO zoB80^;X9yHnlme&O@mHd>yELu**g7GSRA#hwpBCv=Bj204ef$kSOl_m`+U+`>oG4 zh(y?oCa=`)TnvtlbU95Jo?4Q-w4&>R_C?l2A75Pv*v zh73Y=5r0(FE)W1N{i`pj5X~G(zEdymr9qChE^;2h`i~Fc?>@N@z zma31^)pfhuKdvV9VE!gjPU=opcIlCDVv7sW8b>6uZmh3!2$^$5KQ+N-tOumIRb&<2 z*&1Q~HC^o1V8?o@yrGiBD^@ihSr)-HqpVGNi5N_7bco4uK*D#?ecfVnsdL`j_xO*{ z0iX3t#kGY#{hm&@bTxKtqooJ5>0iZDr%Dc4?p->`3eQhX%hP!VuKEfsZd3fNf+=C) z-|zhlONj<&n0|Pv8rq+Rp^hGnOl_7Wg|-%fZ~^s!FV_CvAvq76hi0{UO$O(WdR0I6 z_Gf-Q&=a=v#qV}r>C;gf%bK7O(!}o@vno)y{MXjLwY$t`E%=__MRCZA{LuA)_X}+B zFT_i7Ly$=71aH#q&O6oseoKxiK7leI&>vcAI4=w9CQtI@`HU8*U!4|R6gMR_Ldil} zQA283Wl{s%Kh8Lmmc>0EH>Pb@G5XFAjW2Ao1PlO6`&gxv2zcDugl<}C`;c1+pmhH< z8y>hqwLWjOqQ3cA_Os6J%Z8rEEtvFHN6&!V*U<_AWpVR?AGhxLjElKlPTmptq|F+o zyG)si;78Kyo1;pme=G{kySPd0G}(_FSM4U-L#}a|3%+4#Hl@Z}7OqOl$+CpSd-7B) zdPI&}XIiAHJh6E0kghD1iDBVmU&3x9c{cXeeP>=s4CD8Uw6XiW)PHUzW8h%#@eJF)K}Y5a48Ex0u5j27&JWuW-^pvX8rw|cYr9!c0IJ0L z7|G)eF(lc_pE?8I$r_m~l-pYrE!x~H4?TxKMsx5 znte_!^1CuyPqeZ54F>N^f@IaAN|y8Vd=0aLxGwhkJQ73drXy#V$xEUhuH~6(*BZa` zaP`Z@244RW=h6PR#+`R}NCqWKw|le~bf!GkD#dQC%t{^&xOVAfRu`1bxg;lE%XqZ} z8C9H*pc-4HU2$k$-NED5bcU?~{?B!~DhVdFOXH5i?N4^62dwBkp^poF#1xhO+{)?vrl&B^=9hE zXKQC=!{phavibN4f1h^!Jd~ju|G;N66oVS7#PN)1a>ZzOk{9LRfSIfO*17&yE9-*C zRo*oHtcR9NkJW8x-SC+f^kv<5+HzktGHpoBmBzV{QpBM8@a`t+yl~BGtL?a#?vli< zmC?A>`P?tYwF){5`@CE#myd+au}m1TGok>s+Q=sw!hgb`He!>Hpk&T%47dkHm@LJJ zlFR~ktF+c#0{5MsZ2ChR9tlLdB<~{da+Jt?ldn>0$b@Fr%+)nEtP#m=0WuE7ye16;3tQ`=Z3;-tlCC>J{-dWgkAf zjmf1idOsiwINNby|NM`c_adgN-s@3%0Q%NoFcu)P1+5(_wNKVX9Wx56vw+(suqSML z8@8hh1g+MNR}7gCygpPZdx|gr zevhWK{o3jARfNk8_|X6^m(OT@br_JY;m6l(j1Uic35?p`-~kxb-rRY007t~e9FW8U zQfed51N`(1QE!DL!BfFp(I z;=Bmc!NUyTp!A}+9rBF5$pEUp{0->EBhqLfa|5%}lz~1H4M4(2p?D?{GW-y!0<2AJ zy@_Vn8+fEZC07Y^TrY&#k=MNwnAw)s=pzvbx251Vy?ktbIa9RQVF-Q=k_=T=>dBiqX{ExStFtYPf-~WuybtuG=i*!pa8dxfTtI{Y3J;@@XENY!dV$^rejnw|-g0XA2$0WlBMZY~eb?~+ E2O#6GiU0rr literal 0 HcmV?d00001 diff --git a/plugins/CuraDrive/src/qml/images/printer.svg b/plugins/CuraDrive/src/qml/images/printer.svg new file mode 100644 index 0000000000..f7dc83987d --- /dev/null +++ b/plugins/CuraDrive/src/qml/images/printer.svg @@ -0,0 +1,14 @@ + + + + icn_singlePrinter + Created with Sketch. + + + + + + + + + \ No newline at end of file diff --git a/plugins/CuraDrive/src/qml/images/profile.svg b/plugins/CuraDrive/src/qml/images/profile.svg new file mode 100644 index 0000000000..ec2130f3d6 --- /dev/null +++ b/plugins/CuraDrive/src/qml/images/profile.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/plugins/CuraDrive/src/qml/images/restore.svg b/plugins/CuraDrive/src/qml/images/restore.svg new file mode 100644 index 0000000000..803215eada --- /dev/null +++ b/plugins/CuraDrive/src/qml/images/restore.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/plugins/CuraDrive/src/qml/main.qml b/plugins/CuraDrive/src/qml/main.qml new file mode 100644 index 0000000000..4a2219cf1f --- /dev/null +++ b/plugins/CuraDrive/src/qml/main.qml @@ -0,0 +1,42 @@ +// Copyright (c) 2018 Ultimaker B.V. +import QtQuick 2.7 +import QtQuick.Controls 2.1 +import QtQuick.Window 2.2 + +import UM 1.3 as UM +import Cura 1.1 as Cura + +import "components" +import "pages" + +Window +{ + id: curaDriveDialog + minimumWidth: Math.round(UM.Theme.getSize("modal_window_minimum").width) + minimumHeight: Math.round(UM.Theme.getSize("modal_window_minimum").height) + maximumWidth: minimumWidth * 1.2 + maximumHeight: minimumHeight * 1.2 + width: minimumWidth + height: minimumHeight + color: UM.Theme.getColor("sidebar") + title: catalog.i18nc("@title:window", "Cura Backups") + + // Globally available. + UM.I18nCatalog + { + id: catalog + name: "cura_drive" + } + + WelcomePage + { + id: welcomePage + visible: !Cura.API.account.isLoggedIn + } + + BackupsPage + { + id: backupsPage + visible: Cura.API.account.isLoggedIn + } +} diff --git a/plugins/CuraDrive/src/qml/pages/BackupsPage.qml b/plugins/CuraDrive/src/qml/pages/BackupsPage.qml new file mode 100644 index 0000000000..88ce766383 --- /dev/null +++ b/plugins/CuraDrive/src/qml/pages/BackupsPage.qml @@ -0,0 +1,73 @@ +// Copyright (c) 2018 Ultimaker B.V. +import QtQuick 2.7 +import QtQuick.Controls 2.1 +import QtQuick.Layouts 1.3 + +import UM 1.3 as UM +import Cura 1.1 as Cura + +import "../components" + +Item +{ + id: backupsPage + anchors.fill: parent + anchors.margins: UM.Theme.getSize("default_margin").width * 3 + + ColumnLayout + { + spacing: UM.Theme.getSize("default_margin").height * 2 + width: parent.width + anchors.fill: parent + + Label + { + id: backupTitle + text: catalog.i18nc("@title", "My Backups") + font: UM.Theme.getFont("large") + color: UM.Theme.getColor("text") + Layout.fillWidth: true + renderType: Text.NativeRendering + } + + Label + { + text: catalog.i18nc("@empty_state", + "You don't have any backups currently. Use the 'Backup Now' button to create one.") + width: parent.width + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + wrapMode: Label.WordWrap + visible: backupList.count == 0 + Layout.fillWidth: true + Layout.fillHeight: true + renderType: Text.NativeRendering + } + + BackupList + { + id: backupList + model: CuraDrive.backups + Layout.fillWidth: true + Layout.fillHeight: true + } + + Label + { + text: catalog.i18nc("@backup_limit_info", + "During the preview phase, you'll be limited to 5 visible backups. Remove a backup to see older ones.") + width: parent.width + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + wrapMode: Label.WordWrap + visible: backupList.count > 4 + renderType: Text.NativeRendering + } + + BackupListFooter + { + id: backupListFooter + showInfoButton: backupList.count > 4 + } + } +} diff --git a/plugins/CuraDrive/src/qml/pages/WelcomePage.qml b/plugins/CuraDrive/src/qml/pages/WelcomePage.qml new file mode 100644 index 0000000000..882656dc4a --- /dev/null +++ b/plugins/CuraDrive/src/qml/pages/WelcomePage.qml @@ -0,0 +1,48 @@ +// Copyright (c) 2018 Ultimaker B.V. +import QtQuick 2.7 +import QtQuick.Controls 2.1 +import QtQuick.Window 2.2 + +import UM 1.3 as UM +import Cura 1.1 as Cura + +import "../components" + +Column +{ + id: welcomePage + spacing: UM.Theme.getSize("wide_margin").height + width: parent.width + topPadding: 150 * screenScaleFactor + + Image + { + id: profileImage + fillMode: Image.PreserveAspectFit + source: "../images/icon.png" + anchors.horizontalCenter: parent.horizontalCenter + width: Math.round(parent.width / 4) + } + + Label + { + id: welcomeTextLabel + text: catalog.i18nc("@description", "Backup and synchronize your Cura settings.") + width: Math.round(parent.width / 2) + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + anchors.horizontalCenter: parent.horizontalCenter + wrapMode: Label.WordWrap + renderType: Text.NativeRendering + } + + ActionButton + { + id: loginButton + onClicked: Cura.API.account.login() + text: catalog.i18nc("@button", "Sign In") + anchors.horizontalCenter: parent.horizontalCenter + } +} From 437ba3848db12db4f024911c1bc6b761134f0bb0 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 10 Dec 2018 14:26:20 +0100 Subject: [PATCH 0790/1240] Fix typo. Missing parenthesis Contributes to CURA-5941. --- resources/qml/PrintSetupSelector/PrintSetupSelector.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector/PrintSetupSelector.qml index fe642bd3c1..599eac957e 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelector.qml @@ -16,7 +16,7 @@ Cura.ExpandableComponent contentPadding: UM.Theme.getSize("default_lining").width contentHeaderTitle: catalog.i18nc("@label", "Print settings") enabled: !preSlicedData - disabledText: catalog.i18nc("@label shown when we load a Gcode file", "Print setup disabled. G code file can not be modified." + disabledText: catalog.i18nc("@label shown when we load a Gcode file", "Print setup disabled. G code file can not be modified.") UM.I18nCatalog { From 315bcf50c746ee6a25aed8ae95c6245d898b7525 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Mon, 10 Dec 2018 14:31:49 +0100 Subject: [PATCH 0791/1240] Get some personal space up in this personal space Contributes to CL-1152 --- .../resources/qml/MonitorConfigOverrideDialog.qml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml index aa62afa083..14bdd07313 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml @@ -48,10 +48,7 @@ UM.Dialog anchors { fill: parent - leftMargin: 60 - rightMargin: 60 - topMargin: 18 - bottomMargin: 56 + margins: 60 * screenScaleFactor // TODO: Theme! } wrapMode: Text.WordWrap text: From 861deaa9f74f4e3bc32db5f6c8e0628406c5e2e1 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 10 Dec 2018 14:20:19 +0100 Subject: [PATCH 0792/1240] Add renderType native for toolbox QML Labels CURA-6006 --- plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml | 8 +++++++- plugins/Toolbox/resources/qml/ToolboxBackColumn.qml | 3 ++- .../resources/qml/ToolboxCompatibilityChart.qml | 8 +++++++- .../qml/ToolboxConfirmUninstallResetDialog.qml | 3 ++- plugins/Toolbox/resources/qml/ToolboxDetailPage.qml | 11 ++++++++++- plugins/Toolbox/resources/qml/ToolboxDetailTile.qml | 4 +++- .../resources/qml/ToolboxDetailTileActions.qml | 4 +++- .../Toolbox/resources/qml/ToolboxDownloadsGrid.qml | 3 ++- .../resources/qml/ToolboxDownloadsGridTile.qml | 4 +++- .../resources/qml/ToolboxDownloadsShowcase.qml | 3 ++- .../resources/qml/ToolboxDownloadsShowcaseTile.qml | 3 ++- plugins/Toolbox/resources/qml/ToolboxErrorPage.qml | 3 ++- plugins/Toolbox/resources/qml/ToolboxFooter.qml | 5 +++-- .../Toolbox/resources/qml/ToolboxInstalledPage.qml | 4 +++- .../Toolbox/resources/qml/ToolboxInstalledTile.qml | 6 +++++- .../resources/qml/ToolboxInstalledTileActions.qml | 5 ++++- .../Toolbox/resources/qml/ToolboxLicenseDialog.qml | 3 ++- plugins/Toolbox/resources/qml/ToolboxLoadingPage.qml | 3 ++- plugins/Toolbox/resources/qml/ToolboxTabButton.qml | 5 +++-- 19 files changed, 67 insertions(+), 21 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml b/plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml index 9c1df0c49e..7b026566c3 100644 --- a/plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml @@ -1,7 +1,7 @@ // Copyright (c) 2018 Ultimaker B.V. // Toolbox is released under the terms of the LGPLv3 or higher. -import QtQuick 2.3 +import QtQuick 2.10 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import UM 1.1 as UM @@ -59,6 +59,7 @@ Item wrapMode: Text.WordWrap width: parent.width height: UM.Theme.getSize("toolbox_property_label").height + renderType: Text.NativeRendering } Label { @@ -70,6 +71,7 @@ Item left: title.left topMargin: UM.Theme.getSize("default_margin").height } + renderType: Text.NativeRendering } Column { @@ -88,12 +90,14 @@ Item text: catalog.i18nc("@label", "Website") + ":" font: UM.Theme.getFont("default") color: UM.Theme.getColor("text_medium") + renderType: Text.NativeRendering } Label { text: catalog.i18nc("@label", "Email") + ":" font: UM.Theme.getFont("default") color: UM.Theme.getColor("text_medium") + renderType: Text.NativeRendering } } Column @@ -122,6 +126,7 @@ Item color: UM.Theme.getColor("text") linkColor: UM.Theme.getColor("text_link") onLinkActivated: Qt.openUrlExternally(link) + renderType: Text.NativeRendering } Label @@ -138,6 +143,7 @@ Item color: UM.Theme.getColor("text") linkColor: UM.Theme.getColor("text_link") onLinkActivated: Qt.openUrlExternally(link) + renderType: Text.NativeRendering } } Rectangle diff --git a/plugins/Toolbox/resources/qml/ToolboxBackColumn.qml b/plugins/Toolbox/resources/qml/ToolboxBackColumn.qml index 8524b7d1e5..edb1967fee 100644 --- a/plugins/Toolbox/resources/qml/ToolboxBackColumn.qml +++ b/plugins/Toolbox/resources/qml/ToolboxBackColumn.qml @@ -1,7 +1,7 @@ // Copyright (c) 2018 Ultimaker B.V. // Toolbox is released under the terms of the LGPLv3 or higher. -import QtQuick 2.2 +import QtQuick 2.10 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import UM 1.1 as UM @@ -64,6 +64,7 @@ Item font: UM.Theme.getFont("default_bold") horizontalAlignment: Text.AlignRight width: control.width + renderType: Text.NativeRendering } } } diff --git a/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml b/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml index d4c0ae14eb..db4e8c628f 100644 --- a/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml +++ b/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml @@ -1,7 +1,7 @@ // Copyright (c) 2018 Ultimaker B.V. // Toolbox is released under the terms of the LGPLv3 or higher. -import QtQuick 2.7 +import QtQuick 2.10 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import UM 1.1 as UM @@ -67,6 +67,7 @@ Item wrapMode: Text.WordWrap color: UM.Theme.getColor("text_medium") font: UM.Theme.getFont("medium") + renderType: Text.NativeRendering } TableView @@ -99,6 +100,7 @@ Item text: styleData.value || "" color: UM.Theme.getColor("text") font: UM.Theme.getFont("default_bold") + renderType: Text.NativeRendering } Rectangle { @@ -118,6 +120,7 @@ Item text: styleData.value || "" color: UM.Theme.getColor("text_medium") font: UM.Theme.getFont("default") + renderType: Text.NativeRendering } } itemDelegate: Item @@ -130,6 +133,7 @@ Item text: styleData.value || "" color: UM.Theme.getColor("text_medium") font: UM.Theme.getFont("default") + renderType: Text.NativeRendering } } @@ -144,6 +148,7 @@ Item elide: Text.ElideRight color: UM.Theme.getColor("text_medium") font: UM.Theme.getFont("default") + renderType: Text.NativeRendering } } @@ -232,5 +237,6 @@ Item color: UM.Theme.getColor("text") linkColor: UM.Theme.getColor("text_link") onLinkActivated: Qt.openUrlExternally(link) + renderType: Text.NativeRendering } } diff --git a/plugins/Toolbox/resources/qml/ToolboxConfirmUninstallResetDialog.qml b/plugins/Toolbox/resources/qml/ToolboxConfirmUninstallResetDialog.qml index 2c5d08aa72..e238132680 100644 --- a/plugins/Toolbox/resources/qml/ToolboxConfirmUninstallResetDialog.qml +++ b/plugins/Toolbox/resources/qml/ToolboxConfirmUninstallResetDialog.qml @@ -1,7 +1,7 @@ // Copyright (c) 2018 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.2 +import QtQuick 2.10 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.1 import QtQuick.Layouts 1.1 @@ -66,6 +66,7 @@ UM.Dialog anchors.right: parent.right font: UM.Theme.getFont("default") wrapMode: Text.WordWrap + renderType: Text.NativeRendering } // Buttons diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml index 9e2e178b71..7983be8aef 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml @@ -1,7 +1,7 @@ // Copyright (c) 2018 Ultimaker B.V. // Toolbox is released under the terms of the LGPLv3 or higher. -import QtQuick 2.3 +import QtQuick 2.10 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import UM 1.1 as UM @@ -65,6 +65,7 @@ Item wrapMode: Text.WordWrap width: parent.width height: UM.Theme.getSize("toolbox_property_label").height + renderType: Text.NativeRendering } Column @@ -84,24 +85,28 @@ Item text: catalog.i18nc("@label", "Version") + ":" font: UM.Theme.getFont("default") color: UM.Theme.getColor("text_medium") + renderType: Text.NativeRendering } Label { text: catalog.i18nc("@label", "Last updated") + ":" font: UM.Theme.getFont("default") color: UM.Theme.getColor("text_medium") + renderType: Text.NativeRendering } Label { text: catalog.i18nc("@label", "Author") + ":" font: UM.Theme.getFont("default") color: UM.Theme.getColor("text_medium") + renderType: Text.NativeRendering } Label { text: catalog.i18nc("@label", "Downloads") + ":" font: UM.Theme.getFont("default") color: UM.Theme.getColor("text_medium") + renderType: Text.NativeRendering } } Column @@ -121,6 +126,7 @@ Item text: details === null ? "" : (details.version || catalog.i18nc("@label", "Unknown")) font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") + renderType: Text.NativeRendering } Label { @@ -135,6 +141,7 @@ Item } font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") + renderType: Text.NativeRendering } Label { @@ -153,12 +160,14 @@ Item color: UM.Theme.getColor("text") linkColor: UM.Theme.getColor("text_link") onLinkActivated: Qt.openUrlExternally(link) + renderType: Text.NativeRendering } Label { text: details === null ? "" : (details.download_count || catalog.i18nc("@label", "Unknown")) font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") + renderType: Text.NativeRendering } } Rectangle diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailTile.qml b/plugins/Toolbox/resources/qml/ToolboxDetailTile.qml index 1d701543ce..43f97baf3f 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailTile.qml @@ -1,7 +1,7 @@ // Copyright (c) 2018 Ultimaker B.V. // Toolbox is released under the terms of the LGPLv3 or higher. -import QtQuick 2.7 +import QtQuick 2.10 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import UM 1.1 as UM @@ -31,6 +31,7 @@ Item wrapMode: Text.WordWrap color: UM.Theme.getColor("text") font: UM.Theme.getFont("medium_bold") + renderType: Text.NativeRendering } Label { @@ -42,6 +43,7 @@ Item wrapMode: Text.WordWrap color: UM.Theme.getColor("text") font: UM.Theme.getFont("default") + renderType: Text.NativeRendering } } diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml b/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml index 848acfbf4f..7160dafa2d 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml @@ -1,7 +1,7 @@ // Copyright (c) 2018 Ultimaker B.V. // Toolbox is released under the terms of the LGPLv3 or higher. -import QtQuick 2.7 +import QtQuick 2.10 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import UM 1.1 as UM @@ -57,6 +57,8 @@ Column linkColor: UM.Theme.getColor("text_link") visible: loginRequired width: installButton.width + renderType: Text.NativeRendering + MouseArea { anchors.fill: parent diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsGrid.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsGrid.qml index 3e2643938b..8e15882ae1 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsGrid.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsGrid.qml @@ -1,7 +1,7 @@ // Copyright (c) 2018 Ultimaker B.V. // Toolbox is released under the terms of the LGPLv3 or higher. -import QtQuick 2.7 +import QtQuick 2.10 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import QtQuick.Layouts 1.3 @@ -23,6 +23,7 @@ Column width: parent.width color: UM.Theme.getColor("text_medium") font: UM.Theme.getFont("medium") + renderType: Text.NativeRendering } Grid { diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml index cee3f0fd20..357e9e9a72 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml @@ -1,7 +1,7 @@ // Copyright (c) 2018 Ultimaker B.V. // Toolbox is released under the terms of the LGPLv3 or higher. -import QtQuick 2.3 +import QtQuick 2.10 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import QtQuick.Layouts 1.3 @@ -72,6 +72,7 @@ Item wrapMode: Text.WordWrap color: UM.Theme.getColor("text") font: UM.Theme.getFont("default_bold") + renderType: Text.NativeRendering } Label { @@ -83,6 +84,7 @@ Item wrapMode: Text.WordWrap color: UM.Theme.getColor("text_medium") font: UM.Theme.getFont("default") + renderType: Text.NativeRendering } } } diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcase.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcase.qml index 9851128076..820b74554a 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcase.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcase.qml @@ -1,7 +1,7 @@ // Copyright (c) 2018 Ultimaker B.V. // Toolbox is released under the terms of the LGPLv3 or higher. -import QtQuick 2.7 +import QtQuick 2.10 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import UM 1.1 as UM @@ -24,6 +24,7 @@ Rectangle width: parent.width color: UM.Theme.getColor("text_medium") font: UM.Theme.getFont("medium") + renderType: Text.NativeRendering } Grid { diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml index 8a2fdc8bc8..d1130cf63f 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml @@ -1,7 +1,7 @@ // Copyright (c) 2018 Ultimaker B.V. // Cura is released under the terms of the LGPLv3 or higher. -import QtQuick 2.7 +import QtQuick 2.10 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import QtGraphicalEffects 1.0 @@ -79,6 +79,7 @@ Rectangle wrapMode: Text.WordWrap color: UM.Theme.getColor("button_text") font: UM.Theme.getFont("medium_bold") + renderType: Text.NativeRendering } } MouseArea diff --git a/plugins/Toolbox/resources/qml/ToolboxErrorPage.qml b/plugins/Toolbox/resources/qml/ToolboxErrorPage.qml index 600ae2b39f..e57e63dbb9 100644 --- a/plugins/Toolbox/resources/qml/ToolboxErrorPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxErrorPage.qml @@ -1,7 +1,7 @@ // Copyright (c) 2018 Ultimaker B.V. // Toolbox is released under the terms of the LGPLv3 or higher. -import QtQuick 2.7 +import QtQuick 2.10 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 @@ -18,5 +18,6 @@ Rectangle { centerIn: parent } + renderType: Text.NativeRendering } } diff --git a/plugins/Toolbox/resources/qml/ToolboxFooter.qml b/plugins/Toolbox/resources/qml/ToolboxFooter.qml index 5c2a6577ad..2d42ca7269 100644 --- a/plugins/Toolbox/resources/qml/ToolboxFooter.qml +++ b/plugins/Toolbox/resources/qml/ToolboxFooter.qml @@ -1,7 +1,7 @@ // Copyright (c) 2018 Ultimaker B.V. // Toolbox is released under the terms of the LGPLv3 or higher. -import QtQuick 2.2 +import QtQuick 2.10 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import UM 1.1 as UM @@ -26,7 +26,7 @@ Item right: restartButton.right rightMargin: UM.Theme.getSize("default_margin").width } - + renderType: Text.NativeRendering } Button { @@ -56,6 +56,7 @@ Item text: control.text verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter + renderType: Text.NativeRendering } } } diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml index 3d5cd1c8d4..e1d01db59a 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml @@ -1,7 +1,7 @@ // Copyright (c) 2018 Ultimaker B.V. // Toolbox is released under the terms of the LGPLv3 or higher. -import QtQuick 2.7 +import QtQuick 2.10 import QtQuick.Dialogs 1.1 import QtQuick.Window 2.2 import QtQuick.Controls 1.4 @@ -38,6 +38,7 @@ ScrollView text: catalog.i18nc("@title:tab", "Plugins") color: UM.Theme.getColor("text_medium") font: UM.Theme.getFont("medium") + renderType: Text.NativeRendering } Rectangle { @@ -68,6 +69,7 @@ ScrollView text: catalog.i18nc("@title:tab", "Materials") color: UM.Theme.getColor("text_medium") font: UM.Theme.getFont("medium") + renderType: Text.NativeRendering } Rectangle diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml index b16564fdd2..593e024309 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml @@ -1,7 +1,7 @@ // Copyright (c) 2018 Ultimaker B.V. // Toolbox is released under the terms of the LGPLv3 or higher. -import QtQuick 2.7 +import QtQuick 2.10 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import UM 1.1 as UM @@ -51,6 +51,7 @@ Item wrapMode: Text.WordWrap font: UM.Theme.getFont("default_bold") color: pluginInfo.color + renderType: Text.NativeRendering } Label { @@ -60,6 +61,7 @@ Item width: parent.width wrapMode: Text.WordWrap color: pluginInfo.color + renderType: Text.NativeRendering } } Column @@ -88,6 +90,7 @@ Item onLinkActivated: Qt.openUrlExternally("mailto:" + model.author_email + "?Subject=Cura: " + model.name + " Plugin") color: model.enabled ? UM.Theme.getColor("text") : UM.Theme.getColor("lining") linkColor: UM.Theme.getColor("text_link") + renderType: Text.NativeRendering } Label @@ -98,6 +101,7 @@ Item color: UM.Theme.getColor("text") verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignLeft + renderType: Text.NativeRendering } } ToolboxInstalledTileActions diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml index 39528f6437..61af84fbe5 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledTileActions.qml @@ -1,7 +1,7 @@ // Copyright (c) 2018 Ultimaker B.V. // Toolbox is released under the terms of the LGPLv3 or higher. -import QtQuick 2.7 +import QtQuick 2.10 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 import UM 1.1 as UM @@ -24,6 +24,7 @@ Column font: UM.Theme.getFont("default") wrapMode: Text.WordWrap width: parent.width + renderType: Text.NativeRendering } ToolboxProgressButton @@ -55,6 +56,8 @@ Column linkColor: UM.Theme.getColor("text_link") visible: loginRequired width: updateButton.width + renderType: Text.NativeRendering + MouseArea { anchors.fill: parent diff --git a/plugins/Toolbox/resources/qml/ToolboxLicenseDialog.qml b/plugins/Toolbox/resources/qml/ToolboxLicenseDialog.qml index b8baf7bc83..40b22c268d 100644 --- a/plugins/Toolbox/resources/qml/ToolboxLicenseDialog.qml +++ b/plugins/Toolbox/resources/qml/ToolboxLicenseDialog.qml @@ -1,7 +1,7 @@ // Copyright (c) 2018 Ultimaker B.V. // Toolbox is released under the terms of the LGPLv3 or higher. -import QtQuick 2.2 +import QtQuick 2.10 import QtQuick.Dialogs 1.1 import QtQuick.Window 2.2 import QtQuick.Controls 1.4 @@ -32,6 +32,7 @@ UM.Dialog anchors.right: parent.right text: licenseDialog.pluginName + catalog.i18nc("@label", "This plugin contains a license.\nYou need to accept this license to install this plugin.\nDo you agree with the terms below?") wrapMode: Text.Wrap + renderType: Text.NativeRendering } TextArea { diff --git a/plugins/Toolbox/resources/qml/ToolboxLoadingPage.qml b/plugins/Toolbox/resources/qml/ToolboxLoadingPage.qml index 1ba271dcab..025239bd43 100644 --- a/plugins/Toolbox/resources/qml/ToolboxLoadingPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxLoadingPage.qml @@ -1,7 +1,7 @@ // Copyright (c) 2018 Ultimaker B.V. // Toolbox is released under the terms of the LGPLv3 or higher. -import QtQuick 2.7 +import QtQuick 2.10 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 @@ -18,5 +18,6 @@ Rectangle { centerIn: parent } + renderType: Text.NativeRendering } } diff --git a/plugins/Toolbox/resources/qml/ToolboxTabButton.qml b/plugins/Toolbox/resources/qml/ToolboxTabButton.qml index fa4f75d6fe..5e1aeaa636 100644 --- a/plugins/Toolbox/resources/qml/ToolboxTabButton.qml +++ b/plugins/Toolbox/resources/qml/ToolboxTabButton.qml @@ -1,8 +1,8 @@ // Copyright (c) 2018 Ultimaker B.V. // Toolbox is released under the terms of the LGPLv3 or higher. -import QtQuick 2.2 -import QtQuick.Controls 2.0 +import QtQuick 2.10 +import QtQuick.Controls 2.3 import UM 1.1 as UM Button @@ -46,5 +46,6 @@ Button font: control.enabled ? (control.active ? UM.Theme.getFont("medium_bold") : UM.Theme.getFont("medium")) : UM.Theme.getFont("default_italic") verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter + renderType: Text.NativeRendering } } \ No newline at end of file From 69744282e6208ef959035c7237a9685d9d85b819 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 10 Dec 2018 14:37:44 +0100 Subject: [PATCH 0793/1240] Fix rounding issue in toolbox QML widget size CURA-6006 --- plugins/Toolbox/resources/qml/Toolbox.qml | 4 ++-- plugins/Toolbox/resources/qml/ToolboxDownloadsGrid.qml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/Toolbox/resources/qml/Toolbox.qml b/plugins/Toolbox/resources/qml/Toolbox.qml index 7cc5a730f2..9ede2a6bda 100644 --- a/plugins/Toolbox/resources/qml/Toolbox.qml +++ b/plugins/Toolbox/resources/qml/Toolbox.qml @@ -14,8 +14,8 @@ Window modality: Qt.ApplicationModal flags: Qt.Dialog | Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowCloseButtonHint - width: 720 * screenScaleFactor - height: 640 * screenScaleFactor + width: Math.floor(720 * screenScaleFactor) + height: Math.floor(640 * screenScaleFactor) minimumWidth: width maximumWidth: minimumWidth minimumHeight: height diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsGrid.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsGrid.qml index 8e15882ae1..85f0ff8be4 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsGrid.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsGrid.qml @@ -38,7 +38,7 @@ Column delegate: Loader { asynchronous: true - width: (grid.width - (grid.columns - 1) * grid.columnSpacing) / grid.columns + width: Math.round((grid.width - (grid.columns - 1) * grid.columnSpacing) / grid.columns) height: UM.Theme.getSize("toolbox_thumbnail_small").height source: "ToolboxDownloadsGridTile.qml" } From df0ae20ddedf42040dee95243a5a236a0a65afea Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Mon, 10 Dec 2018 14:39:15 +0100 Subject: [PATCH 0794/1240] Let's just get stepped up in here and get some personal space Contributes to CL-1152 --- .../resources/qml/MonitorConfigOverrideDialog.qml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml index 14bdd07313..a283824bcf 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml @@ -48,7 +48,8 @@ UM.Dialog anchors { fill: parent - margins: 60 * screenScaleFactor // TODO: Theme! + margins: 36 * screenScaleFactor // TODO: Theme! + bottomMargin: 56 * screenScaleFactor // TODO: Theme! } wrapMode: Text.WordWrap text: @@ -62,7 +63,7 @@ UM.Dialog { topLine = catalog.i18nc("@label", "The printer %1 is assigned, but the job contains an unknown material configuration.").arg(printer.name) } - var result = "

    " + topLine +"

    " + var result = "

    " + topLine +"

    \n\n" for (var i = 0; i < printer.activePrintJob.configurationChanges.length; i++) { var change = printer.activePrintJob.configurationChanges[i] From 590e8f5eb19cff433d1d5e1926dd4bcb89f4ea76 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 10 Dec 2018 14:41:15 +0100 Subject: [PATCH 0795/1240] Wrap the disabled text in case it's too long. Contributes to CURA-5941. --- resources/qml/ExpandableComponent.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 55271d99c3..74adec5bc6 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -106,13 +106,15 @@ Item { id: disabledLabel visible: !base.enabled + anchors.fill: parent leftPadding: background.padding + rightPadding: background.padding text: "This component is disabled" font: UM.Theme.getFont("default") renderType: Text.NativeRendering verticalAlignment: Text.AlignVCenter color: UM.Theme.getColor("text") - height: parent.height + wrapMode: Text.WordWrap } Item From d482924ea299eed06939564483fd00789e9b9da6 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Mon, 10 Dec 2018 14:43:02 +0100 Subject: [PATCH 0796/1240] STAR-322: First tests for cloud output device --- .../src/Cloud/CloudApiClient.py | 7 +- .../src/Cloud/CloudOutputDevice.py | 7 +- .../CloudClusterPrinterConfiguration.py | 3 - .../Fixtures/getClusterStatusResponse.json | 184 +++++++++--------- .../tests/Cloud/NetworkManagerMock.py | 30 +-- .../tests/Cloud/TestCloudOutputDevice.py | 73 +++++++ .../Cloud/TestCloudOutputDeviceManager.py | 14 +- plugins/UM3NetworkPrinting/tests/conftest.py | 11 +- 8 files changed, 191 insertions(+), 138 deletions(-) create mode 100644 plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py index b4c8774140..b3abc74ff4 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py @@ -36,6 +36,11 @@ class CloudApiClient(NetworkClient): self._account = account self._on_error = on_error + ## Gets the account used for the API. + @property + def account(self) -> Account: + return self._account + ## Retrieves all the clusters for the user that is currently logged in. # \param on_finished: The function to be called after the result is parsed. def getClusters(self, on_finished: Callable[[List[CloudCluster]], any]) -> None: @@ -46,7 +51,7 @@ class CloudApiClient(NetworkClient): # \param cluster_id: The ID of the cluster. # \param on_finished: The function to be called after the result is parsed. def getClusterStatus(self, cluster_id: str, on_finished: Callable[[CloudClusterStatus], any]) -> None: - url = "{}/cluster/{}/status".format(self.CLUSTER_API_ROOT, cluster_id) + url = "{}/clusters/{}/status".format(self.CLUSTER_API_ROOT, cluster_id) self.get(url, on_finished=self._wrapCallback(on_finished, CloudClusterStatus)) ## Requests the cloud to register the upload of a print job mesh. diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 2800bc1c8c..a137e5261f 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -13,7 +13,6 @@ from UM.Logger import Logger from UM.Message import Message from UM.Qt.Duration import Duration, DurationFormat from UM.Scene.SceneNode import SceneNode -from cura.CuraApplication import CuraApplication from cura.PrinterOutput.NetworkedPrinterOutputDevice import AuthState, NetworkedPrinterOutputDevice from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputController import CloudOutputController @@ -93,7 +92,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self._setInterfaceElements() self._device_id = device_id - self._account = CuraApplication.getInstance().getCuraAPI().account + self._account = api_client.account # We use the Cura Connect monitor tab to get most functionality right away. self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), @@ -174,10 +173,6 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ) self._api.requestUpload(request, lambda response: self._onPrintJobCreated(mesh_bytes, response)) - ## Called when the connection to the cluster changes. - def connect(self) -> None: - super().connect() - ## Called when the network data should be updated. def _update(self) -> None: super()._update() diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfiguration.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfiguration.py index aa382136d0..c14a7f85c3 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfiguration.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfiguration.py @@ -1,8 +1,5 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import List, Optional - -from cura.PrinterOutput.ConfigurationModel import ConfigurationModel from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationModel from cura.PrinterOutput.ExtruderOutputModel import ExtruderOutputModel from .CloudClusterPrinterConfigurationMaterial import CloudClusterPrinterConfigurationMaterial diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/getClusterStatusResponse.json b/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/getClusterStatusResponse.json index 711e429a72..4f9f47fc75 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/getClusterStatusResponse.json +++ b/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/getClusterStatusResponse.json @@ -1,97 +1,95 @@ { - "data": [ - { - "generated_time": "2018-12-10T08:23:55.110Z", - "printers": [ - { - "configuration": [ - { - "extruder_index": 0, - "material": { - "material": "empty" - }, - "print_core_id": "AA 0.4" + "data": { + "generated_time": "2018-12-10T08:23:55.110Z", + "printers": [ + { + "configuration": [ + { + "extruder_index": 0, + "material": { + "material": "empty" }, - { - "extruder_index": 1, - "material": { - "material": "empty" - }, - "print_core_id": "AA 0.4" - } - ], - "enabled": true, - "firmware_version": "5.1.2.20180807", - "friendly_name": "Master-Luke", - "ip_address": "10.183.1.140", - "machine_variant": "Ultimaker 3", - "status": "maintenance", - "unique_name": "ultimakersystem-ccbdd30044ec", - "uuid": "b3a47ea3-1eeb-4323-9626-6f9c3c888f9e" - }, - { - "configuration": [ - { - "extruder_index": 0, - "material": { - "brand": "Generic", - "color": "Generic", - "guid": "506c9f0d-e3aa-4bd4-b2d2-23e2425b1aa9", - "material": "PLA" - }, - "print_core_id": "AA 0.4" + "print_core_id": "AA 0.4" + }, + { + "extruder_index": 1, + "material": { + "material": "empty" }, - { - "extruder_index": 1, - "material": { - "brand": "Ultimaker", - "color": "Red", - "guid": "9cfe5bf1-bdc5-4beb-871a-52c70777842d", - "material": "PLA" - }, - "print_core_id": "AA 0.4" - } - ], - "enabled": true, - "firmware_version": "4.3.3.20180529", - "friendly_name": "UM-Marijn", - "ip_address": "10.183.1.166", - "machine_variant": "Ultimaker 3", - "status": "idle", - "unique_name": "ultimakersystem-ccbdd30058ab", - "uuid": "6e62c40a-4601-4b0e-9fec-c7c02c59c30a" - } - ], - "print_jobs": [ - { - "assigned_to": "6e62c40a-4601-4b0e-9fec-c7c02c59c30a", - "configuration": [ - { - "extruder_index": 0, - "material": { - "brand": "Ultimaker", - "color": "Black", - "guid": "3ee70a86-77d8-4b87-8005-e4a1bc57d2ce", - "material": "PLA" - }, - "print_core_id": "AA 0.4" - } - ], - "constraints": {}, - "created_at": "2018-12-10T08:28:04.108Z", - "force": false, - "last_seen": 500165.109491861, - "machine_variant": "Ultimaker 3", - "name": "UM3_dragon", - "network_error_count": 0, - "owner": "Daniel Testing", - "started": false, - "status": "queued", - "time_elapsed": 0, - "time_total": 14145, - "uuid": "d1c8bd52-5e9f-486a-8c25-a123cc8c7702" - } - ] - } - ] + "print_core_id": "AA 0.4" + } + ], + "enabled": true, + "firmware_version": "5.1.2.20180807", + "friendly_name": "Master-Luke", + "ip_address": "10.183.1.140", + "machine_variant": "Ultimaker 3", + "status": "maintenance", + "unique_name": "ultimakersystem-ccbdd30044ec", + "uuid": "b3a47ea3-1eeb-4323-9626-6f9c3c888f9e" + }, + { + "configuration": [ + { + "extruder_index": 0, + "material": { + "brand": "Generic", + "color": "Generic", + "guid": "506c9f0d-e3aa-4bd4-b2d2-23e2425b1aa9", + "material": "PLA" + }, + "print_core_id": "AA 0.4" + }, + { + "extruder_index": 1, + "material": { + "brand": "Ultimaker", + "color": "Red", + "guid": "9cfe5bf1-bdc5-4beb-871a-52c70777842d", + "material": "PLA" + }, + "print_core_id": "AA 0.4" + } + ], + "enabled": true, + "firmware_version": "4.3.3.20180529", + "friendly_name": "UM-Marijn", + "ip_address": "10.183.1.166", + "machine_variant": "Ultimaker 3", + "status": "idle", + "unique_name": "ultimakersystem-ccbdd30058ab", + "uuid": "6e62c40a-4601-4b0e-9fec-c7c02c59c30a" + } + ], + "print_jobs": [ + { + "assigned_to": "6e62c40a-4601-4b0e-9fec-c7c02c59c30a", + "configuration": [ + { + "extruder_index": 0, + "material": { + "brand": "Ultimaker", + "color": "Black", + "guid": "3ee70a86-77d8-4b87-8005-e4a1bc57d2ce", + "material": "PLA" + }, + "print_core_id": "AA 0.4" + } + ], + "constraints": {}, + "created_at": "2018-12-10T08:28:04.108Z", + "force": false, + "last_seen": 500165.109491861, + "machine_variant": "Ultimaker 3", + "name": "UM3_dragon", + "network_error_count": 0, + "owner": "Daniel Testing", + "started": false, + "status": "queued", + "time_elapsed": 0, + "time_total": 14145, + "uuid": "d1c8bd52-5e9f-486a-8c25-a123cc8c7702" + } + ] + } } diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py b/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py index e8e4fc8de7..c7dc1bac35 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py @@ -1,8 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import json -import os -from typing import Dict, Tuple, Optional +from typing import Dict, Tuple, Union from unittest.mock import MagicMock from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply @@ -52,38 +51,15 @@ class NetworkManagerMock: # \param url: The URL being requested. # \param status_code: The HTTP status code for the response. # \param response: The response body from the server (generally json-encoded). - def prepareReply(self, method: str, url: str, status_code: int, response: bytes) -> None: + def prepareReply(self, method: str, url: str, status_code: int, response: Union[bytes, dict]) -> None: reply_mock = MagicMock() reply_mock.url().toString.return_value = url reply_mock.operation.return_value = self._OPERATIONS[method] reply_mock.attribute.return_value = status_code - reply_mock.readAll.return_value = response + reply_mock.readAll.return_value = response if isinstance(response, bytes) else json.dumps(response).encode() self.replies[method, url] = reply_mock Logger.log("i", "Prepared mock {}-response to {} {}", status_code, method, url) - ## Prepares a reply for the API call to get clusters. - # \param data: The data the server should return. If not given, a default response will be used. - # \return The data in the response. - def prepareGetClusters(self, data: Optional[dict] = None) -> dict: - data, response = self._getResponseData("clusters", data) - status_code = 200 if "data" in data else int(data["errors"][0]["http_status"]) - self.prepareReply("GET", "https://api-staging.ultimaker.com/connect/v1/clusters", status_code, response) - return data - - ## Gets the data that should be in the server's response in both dictionary and JSON-encoded bytes format. - # \param fixture_name: The name of the fixture. - # \param data: The data that should be returned (optional) - # \return The server's response in both dictionary and JSON-encoded bytes format. - @staticmethod - def _getResponseData(fixture_name: str, data: Optional[dict] = None) -> Tuple[dict, bytes]: - if data is None: - with open("{}/Fixtures/{}.json".format(os.path.dirname(__file__), fixture_name), "rb") as f: - response = f.read() - data = json.loads(response.decode()) - else: - response = json.dumps(data).encode() - return data, response - ## Emits the signal that the reply is ready to all prepared replies. def flushReplies(self): for reply in self.replies.values(): diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py new file mode 100644 index 0000000000..4ed2767288 --- /dev/null +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py @@ -0,0 +1,73 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +import json +import os +from unittest import TestCase +from unittest.mock import patch, MagicMock + +from cura.CuraApplication import CuraApplication +from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel +from src.Cloud.CloudApiClient import CloudApiClient +from src.Cloud.CloudOutputController import CloudOutputController +from src.Cloud.CloudOutputDevice import CloudOutputDevice +from .NetworkManagerMock import NetworkManagerMock + + +@patch("cura.NetworkClient.QNetworkAccessManager") +class TestCloudOutputDevice(TestCase): + CLUSTER_ID = "RIZ6cZbWA_Ua7RZVJhrdVfVpf0z-MqaSHQE4v8aRTtYq" + HOST_NAME = "ultimakersystem-ccbdd30044ec" + URL = "https://api-staging.ultimaker.com/connect/v1/clusters/{}/status".format(CLUSTER_ID) + with open("{}/Fixtures/getClusterStatusResponse.json".format(os.path.dirname(__file__)), "rb") as f: + DEFAULT_RESPONSE = f.read() + + def setUp(self): + super().setUp() + self.app = CuraApplication.getInstance() + self.network = NetworkManagerMock() + self.account = MagicMock(isLoggedIn=True, accessToken="TestAccessToken") + self.onError = MagicMock() + self.device = CloudOutputDevice(CloudApiClient(self.account, self.onError), self.CLUSTER_ID, self.HOST_NAME) + self.cluster_status = json.loads(self.DEFAULT_RESPONSE.decode()) + self.network.prepareReply("GET", self.URL, 200, self.DEFAULT_RESPONSE) + + def tearDown(self): + try: + self._beforeTearDown() + finally: + super().tearDown() + + ## Before tear down method we check whether the state of the output device manager is what we expect based on the + # mocked API response. + def _beforeTearDown(self): + # let the network send replies + self.network.flushReplies() + # TODO + + def test_status(self, network_mock): + network_mock.return_value = self.network + self.device._update() + self.network.flushReplies() + + self.assertEqual([PrinterOutputModel, PrinterOutputModel], [type(printer) for printer in self.device.printers]) + + controller_fields = { + "_output_device": self.device, + "can_abort": False, + "can_control_manually": False, + "can_pause": False, + "can_pre_heat_bed": False, + "can_pre_heat_hotends": False, + "can_send_raw_gcode": False, + "can_update_firmware": False, + } + + self.assertEqual({printer["uuid"] for printer in self.cluster_status["data"]["printers"]}, + {printer.key for printer in self.device.printers}) + self.assertEqual([controller_fields, controller_fields], + [printer.getController().__dict__ for printer in self.device.printers]) + + self.assertEqual({job["uuid"] for job in self.cluster_status["data"]["print_jobs"]}, + {job.key for job in self.device.printJobs}) + self.assertEqual(["Daniel Testing"], [job.owner for job in self.device.printJobs]) + self.assertEqual(["UM3_dragon"], [job.name for job in self.device.printJobs]) diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py index 799e715f0d..9e980a8681 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py @@ -1,5 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +import json +import os from unittest import TestCase from unittest.mock import patch @@ -11,13 +13,17 @@ from .NetworkManagerMock import NetworkManagerMock @patch("cura.NetworkClient.QNetworkAccessManager") class TestCloudOutputDeviceManager(TestCase): + URL = "https://api-staging.ultimaker.com/connect/v1/clusters" + with open("{}/Fixtures/clusters.json".format(os.path.dirname(__file__)), "rb") as f: + DEFAULT_RESPONSE = f.read() def setUp(self): super().setUp() self.app = CuraApplication.getInstance() self.network = NetworkManagerMock() self.manager = CloudOutputDeviceManager() - self.clusters_response = self.network.prepareGetClusters() + self.clusters_response = json.loads(self.DEFAULT_RESPONSE.decode()) + self.network.prepareReply("GET", self.URL, 200, self.DEFAULT_RESPONSE) def tearDown(self): try: @@ -58,7 +64,7 @@ class TestCloudOutputDeviceManager(TestCase): # update the cluster from member variable, which is checked at tearDown self.clusters_response["data"][0]["host_name"] = "New host name" - self.network.prepareGetClusters(self.clusters_response) + self.network.prepareReply("GET", self.URL, 200, self.clusters_response) self.manager._update_timer.timeout.emit() @@ -67,7 +73,7 @@ class TestCloudOutputDeviceManager(TestCase): # delete the cluster from member variable, which is checked at tearDown del self.clusters_response["data"][1] - self.network.prepareGetClusters(self.clusters_response) + self.network.prepareReply("GET", self.URL, 200, self.clusters_response) self.manager._update_timer.timeout.emit() @@ -104,7 +110,7 @@ class TestCloudOutputDeviceManager(TestCase): @patch("UM.Message.Message.show") def test_api_error(self, message_mock, network_mock): self.clusters_response = {"errors": [{"id": "notFound", "title": "Not found!", "http_status": "404"}]} - self.network.prepareGetClusters(self.clusters_response) + self.network.prepareReply("GET", self.URL, 200, self.clusters_response) self._loadData(network_mock) self.network.flushReplies() message_mock.assert_called_once_with() diff --git a/plugins/UM3NetworkPrinting/tests/conftest.py b/plugins/UM3NetworkPrinting/tests/conftest.py index 6f245f8f2f..ce49bd3cb7 100644 --- a/plugins/UM3NetworkPrinting/tests/conftest.py +++ b/plugins/UM3NetworkPrinting/tests/conftest.py @@ -1,13 +1,11 @@ # Copyright (c) 2018 Ultimaker B.V. -# Uranium is released under the terms of the LGPLv3 or higher. +# Cura is released under the terms of the LGPLv3 or higher. import pytest -import Arcus #Prevents error: "PyCapsule_GetPointer called with incorrect name" with conflicting SIP configurations between Arcus and PyQt: Import Arcus first! -from UM.Qt.QtApplication import QtApplication # QT application import is required, even though it isn't used. -from UM.Application import Application from UM.Signal import Signal from cura.CuraApplication import CuraApplication +from cura.Machines.MaterialManager import MaterialManager # This mock application must extend from Application and not QtApplication otherwise some QObjects are created and @@ -18,6 +16,11 @@ class FixtureApplication(CuraApplication): super().initialize() Signal._signalQueue = self + self.getPreferences().addPreference("cura/favorite_materials", "") + + self._material_manager = MaterialManager(self._container_registry, parent = self) + self._material_manager.initialize() + def functionEvent(self, event): event.call() From aa376e60da409e87f49150900570cb957dbfd610 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 10 Dec 2018 14:45:11 +0100 Subject: [PATCH 0797/1240] Make the disabled text translatable Contributes to CURA-5941. --- resources/qml/ExpandableComponent.qml | 2 +- resources/qml/ExpandableComponentHeader.qml | 2 +- resources/qml/ExpandablePopup.qml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 74adec5bc6..c2b9d715d5 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -109,7 +109,7 @@ Item anchors.fill: parent leftPadding: background.padding rightPadding: background.padding - text: "This component is disabled" + text: catalog.i18nc("@label default disabled text", "This component is disabled") font: UM.Theme.getFont("default") renderType: Text.NativeRendering verticalAlignment: Text.AlignVCenter diff --git a/resources/qml/ExpandableComponentHeader.qml b/resources/qml/ExpandableComponentHeader.qml index b1fd49cd1b..09ea262c82 100644 --- a/resources/qml/ExpandableComponentHeader.qml +++ b/resources/qml/ExpandableComponentHeader.qml @@ -24,7 +24,7 @@ Cura.RoundedRectangle Label { id: headerLabel - text: "Title" + text: "" font: UM.Theme.getFont("default") renderType: Text.NativeRendering verticalAlignment: Text.AlignVCenter diff --git a/resources/qml/ExpandablePopup.qml b/resources/qml/ExpandablePopup.qml index 75f718abf5..2d34b134e0 100644 --- a/resources/qml/ExpandablePopup.qml +++ b/resources/qml/ExpandablePopup.qml @@ -113,7 +113,7 @@ Item id: disabledLabel visible: !base.enabled leftPadding: background.padding - text: "This component is disabled" + text: catalog.i18nc("@label default disabled text", "This component is disabled") font: UM.Theme.getFont("default") renderType: Text.NativeRendering verticalAlignment: Text.AlignVCenter From 2db5d2b23128be682e97ff34f5bbcf67aef1ccca Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Mon, 10 Dec 2018 14:54:45 +0100 Subject: [PATCH 0798/1240] Add support for translation plurals Contributes to CL-1152 --- .../resources/qml/MonitorConfigOverrideDialog.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml index a283824bcf..127078d460 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml @@ -57,7 +57,7 @@ UM.Dialog var topLine if (materialsAreKnown(printer.activePrintJob)) { - topLine = catalog.i18nc("@label", "The assigned printer, %1, requires the following configuration change(s):").arg(printer.name) + topLine = catalog.i18ncp("@label", "The assigned printer, %1, requires the following configuration change:", "The assigned printer, %1, requires the following configuration changes:").arg(printer.name) } else { From 32f2b7ec1f293c4dde0b6575aa469e3d1c40aa8c Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Mon, 10 Dec 2018 15:05:01 +0100 Subject: [PATCH 0799/1240] Fix some warnings Contributes to CL-1152 --- .../resources/qml/MonitorConfigOverrideDialog.qml | 4 ++++ .../resources/qml/MonitorPrintJobPreview.qml | 4 ++++ .../resources/qml/MonitorPrintJobProgressBar.qml | 2 +- .../UM3NetworkPrinting/resources/qml/PrintJobContextMenu.qml | 2 +- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml index 127078d460..cf8326a504 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml @@ -54,6 +54,10 @@ UM.Dialog wrapMode: Text.WordWrap text: { + if (!printer.activePrintJob) + { + return "" + } var topLine if (materialsAreKnown(printer.activePrintJob)) { diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml index 5acd350abb..2f17db0c65 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml @@ -60,6 +60,10 @@ Item height: 0.5 * printJobPreview.height source: { + if (!printJob) + { + return "" + } if (printJob.configurationChanges.length > 0) { return "../svg/warning-icon.svg" diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml index e646172a6c..cfb7aba84d 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml @@ -55,7 +55,7 @@ Item left: progressBar.right leftMargin: 18 * screenScaleFactor // TODO: Theme! } - text: Math.round(printJob.progress * 100) + "%" + text: printJob ? Math.round(printJob.progress * 100) + "%" : "0%" color: printJob && printJob.isActive ? "#374355" : "#babac1" // TODO: Theme! width: contentWidth font: UM.Theme.getFont("medium") // 14pt, regular diff --git a/plugins/UM3NetworkPrinting/resources/qml/PrintJobContextMenu.qml b/plugins/UM3NetworkPrinting/resources/qml/PrintJobContextMenu.qml index 02a8e7ae69..1edbf9f6a2 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/PrintJobContextMenu.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/PrintJobContextMenu.qml @@ -182,7 +182,7 @@ Item { abortConfirmationDialog.visible = true; popup.close(); } - text: printJob.state == "aborting" ? catalog.i18nc("@label", "Aborting...") : catalog.i18nc("@label", "Abort"); + text: printJob && printJob.state == "aborting" ? catalog.i18nc("@label", "Aborting...") : catalog.i18nc("@label", "Abort"); visible: { if (!printJob) { return false; From 028d993babdff046b9404a84271b07b00e426fef Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Mon, 10 Dec 2018 15:27:51 +0100 Subject: [PATCH 0800/1240] Format .gcode.gz files too Contributes to CL-1152 --- .../resources/qml/MonitorConfigOverrideDialog.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml index cf8326a504..df3c99e0a1 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml @@ -96,7 +96,7 @@ UM.Dialog } // Utils function formatPrintJobName(name) { - var extensions = [ ".gz", ".gcode", ".ufp" ]; + var extensions = [ ".gz", ".gcode", ".gcode.gz", ".ufp" ]; for (var i = 0; i < extensions.length; i++) { var extension = extensions[i]; if (name.slice(-extension.length) === extension) { From d495ec18bb7ab42de07bfb3617c00e7598a20e9e Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 10 Dec 2018 15:28:20 +0100 Subject: [PATCH 0801/1240] Filter a bit more intelligently on when to check for errors CURA-6016 --- cura/Machines/MachineErrorChecker.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/cura/Machines/MachineErrorChecker.py b/cura/Machines/MachineErrorChecker.py index 06f064315b..fb11123af6 100644 --- a/cura/Machines/MachineErrorChecker.py +++ b/cura/Machines/MachineErrorChecker.py @@ -64,21 +64,21 @@ class MachineErrorChecker(QObject): def _onMachineChanged(self) -> None: if self._global_stack: - self._global_stack.propertyChanged.disconnect(self.startErrorCheck) + self._global_stack.propertyChanged.disconnect(self.startErrorCheckPropertyChanged) self._global_stack.containersChanged.disconnect(self.startErrorCheck) for extruder in self._global_stack.extruders.values(): - extruder.propertyChanged.disconnect(self.startErrorCheck) + extruder.propertyChanged.disconnect(self.startErrorCheckPropertyChanged) extruder.containersChanged.disconnect(self.startErrorCheck) self._global_stack = self._machine_manager.activeMachine if self._global_stack: - self._global_stack.propertyChanged.connect(self.startErrorCheck) + self._global_stack.propertyChanged.connect(self.startErrorCheckPropertyChanged) self._global_stack.containersChanged.connect(self.startErrorCheck) for extruder in self._global_stack.extruders.values(): - extruder.propertyChanged.connect(self.startErrorCheck) + extruder.propertyChanged.connect(self.startErrorCheckPropertyChanged) extruder.containersChanged.connect(self.startErrorCheck) hasErrorUpdated = pyqtSignal() @@ -93,6 +93,13 @@ class MachineErrorChecker(QObject): def needToWaitForResult(self) -> bool: return self._need_to_check or self._check_in_progress + # Start the error check for property changed + # this is seperate from the startErrorCheck because it ignores a number property types + def startErrorCheckPropertyChanged(self, key, property_name): + if property_name != "value": + return + self.startErrorCheck() + # Starts the error check timer to schedule a new error check. def startErrorCheck(self, *args) -> None: if not self._check_in_progress: From 04af8fbd5018d7bfdf488ddd98b1d4ccabab1bfb Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Mon, 10 Dec 2018 15:33:16 +0100 Subject: [PATCH 0802/1240] Update MonitorConfigOverrideDialog.qml Contributes to CL-1152 --- .../resources/qml/MonitorConfigOverrideDialog.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml index df3c99e0a1..7f7dc24350 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml @@ -96,7 +96,7 @@ UM.Dialog } // Utils function formatPrintJobName(name) { - var extensions = [ ".gz", ".gcode", ".gcode.gz", ".ufp" ]; + var extensions = [ ".gcode.gz", ".gz", ".gcode", ".ufp" ]; for (var i = 0; i < extensions.length; i++) { var extension = extensions[i]; if (name.slice(-extension.length) === extension) { From 1cd0d26db420af72d92c5b2538caa93a44495dcb Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 10 Dec 2018 15:34:48 +0100 Subject: [PATCH 0803/1240] Change margins Instead of using multiplications and divisions, just use the corresponding margins. Contributes to CURA-5876. --- resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml | 2 +- .../qml/Menus/ConfigurationMenu/ConfigurationListView.qml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 05cac16e29..728a0cbe9a 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -29,7 +29,7 @@ Button id: contentColumn width: parent.width padding: UM.Theme.getSize("wide_margin").width - spacing: Math.round(UM.Theme.getSize("default_margin").height / 2) + spacing: UM.Theme.getSize("narrow_margin").height Row { diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml index 53969a0370..3cc0754284 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml @@ -12,7 +12,7 @@ Column id: base property var outputDevice: null height: childrenRect.height + 2 * padding - spacing: Math.round(UM.Theme.getSize("default_margin").height / 2) + spacing: UM.Theme.getSize("narrow_margin").height function forceModelUpdate() { @@ -55,7 +55,7 @@ Column ListView { id: configurationList - spacing: Math.round(UM.Theme.getSize("default_margin").height / 2) + spacing: UM.Theme.getSize("narrow_margin").height width: container.width - ((height > container.maximumHeight) ? container.ScrollBar.vertical.background.width : 0) //Make room for scroll bar if there is any. contentHeight: childrenRect.height height: childrenRect.height @@ -64,7 +64,7 @@ Column section.criteria: ViewSection.FullString section.delegate: Item { - height: printerTypeLabel.height + UM.Theme.getSize("default_margin").height * 2 //Causes a default margin above the label and a default margin below the label. + height: printerTypeLabel.height + UM.Theme.getSize("wide_margin").height //Causes a default margin above the label and a default margin below the label. Cura.PrinterTypeLabel { id: printerTypeLabel From 11d8831d7a9a15e3e916d5d9762bfe1f755042e5 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 10 Dec 2018 15:40:18 +0100 Subject: [PATCH 0804/1240] Use the capitalized version of the buildplate name --- cura/PrinterOutput/ConfigurationModel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/PrinterOutput/ConfigurationModel.py b/cura/PrinterOutput/ConfigurationModel.py index 89e609c913..6f55aa3b1f 100644 --- a/cura/PrinterOutput/ConfigurationModel.py +++ b/cura/PrinterOutput/ConfigurationModel.py @@ -44,7 +44,7 @@ class ConfigurationModel(QObject): @pyqtProperty(str, fset = setBuildplateConfiguration, notify = configurationChanged) def buildplateConfiguration(self) -> str: - return self._buildplate_configuration + return self._buildplate_configuration.capitalize() ## This method is intended to indicate whether the configuration is valid or not. # The method checks if the mandatory fields are or not set From 0e19fa731f8bc391c3b17ab33db0197410633ef0 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Mon, 10 Dec 2018 15:47:45 +0100 Subject: [PATCH 0805/1240] Codestyle Contributes to CL-1152 --- .../qml/MonitorConfigOverrideDialog.qml | 51 +++++++++++-------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml index 7f7dc24350..1b9a03ea99 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml @@ -95,39 +95,46 @@ UM.Dialog } } // Utils - function formatPrintJobName(name) { - var extensions = [ ".gcode.gz", ".gz", ".gcode", ".ufp" ]; - for (var i = 0; i < extensions.length; i++) { - var extension = extensions[i]; - if (name.slice(-extension.length) === extension) { - name = name.substring(0, name.length - extension.length); + function formatPrintJobName(name) + { + var extensions = [ ".gcode.gz", ".gz", ".gcode", ".ufp" ] + for (var i = 0; i < extensions.length; i++) + { + var extension = extensions[i] + if (name.slice(-extension.length) === extension) + { + name = name.substring(0, name.length - extension.length) } } return name; } - function materialsAreKnown(job) { - var conf0 = job.configuration[0]; - if (conf0 && !conf0.material.material) { - return false; + function materialsAreKnown(job) + { + var conf0 = job.configuration[0] + if (conf0 && !conf0.material.material) + { + return false } - var conf1 = job.configuration[1]; - if (conf1 && !conf1.material.material) { - return false; + var conf1 = job.configuration[1] + if (conf1 && !conf1.material.material) + { + return false } - return true; + return true } - function formatBuildPlateType(buildPlateType) { - var translationText = ""; + function formatBuildPlateType(buildPlateType) + { + var translationText = "" switch (buildPlateType) { case "glass": - translationText = catalog.i18nc("@label", "Glass"); - break; + translationText = catalog.i18nc("@label", "Glass") + break case "aluminum": - translationText = catalog.i18nc("@label", "Aluminum"); - break; + translationText = catalog.i18nc("@label", "Aluminum") + break default: - translationText = null; + translationText = null } - return translationText; + return translationText } } \ No newline at end of file From 4fc4aecf17e33da689463c0d899f36a564fe2f83 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 10 Dec 2018 15:48:59 +0100 Subject: [PATCH 0806/1240] Avoid the overlapping of text when the name is too long Normally I happens in other languages other than English. --- resources/qml/ViewsSelector.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/qml/ViewsSelector.qml b/resources/qml/ViewsSelector.qml index ed0b694aed..f2906f9d4c 100644 --- a/resources/qml/ViewsSelector.qml +++ b/resources/qml/ViewsSelector.qml @@ -60,6 +60,7 @@ Cura.ExpandablePopup { left: title.right leftMargin: UM.Theme.getSize("default_margin").width + right: parent.right } height: parent.height elide: Text.ElideRight From bfd236dae417815c3f1308465f4587cf270ea449 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Mon, 10 Dec 2018 16:11:43 +0100 Subject: [PATCH 0807/1240] STAR-322: Testing cloud printing --- .../src/Cloud/CloudApiClient.py | 2 +- .../src/Cloud/CloudOutputDevice.py | 2 +- .../tests/Cloud/Fixtures/__init__.py | 12 +++ .../{clusters.json => getClusters.json} | 0 .../Cloud/Fixtures/postJobPrintResponse.json | 7 ++ .../Cloud/Fixtures/putJobUploadResponse.json | 10 ++ .../tests/Cloud/NetworkManagerMock.py | 33 +++++-- .../tests/Cloud/TestCloudOutputDevice.py | 91 +++++++++++++++---- .../Cloud/TestCloudOutputDeviceManager.py | 9 +- 9 files changed, 129 insertions(+), 37 deletions(-) create mode 100644 plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/__init__.py rename plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/{clusters.json => getClusters.json} (100%) create mode 100644 plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/postJobPrintResponse.json create mode 100644 plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/putJobUploadResponse.json diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py index b3abc74ff4..448aa4d2e7 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py @@ -92,7 +92,7 @@ class CloudApiClient(NetworkClient): # \param job_id: The ID of the print job. # \param on_finished: The function to be called after the result is parsed. def requestPrint(self, cluster_id: str, job_id: str, on_finished: Callable[[CloudPrintResponse], any]) -> None: - url = "{}/cluster/{}/print/{}".format(self.CLUSTER_API_ROOT, cluster_id, job_id) + url = "{}/clusters/{}/print/{}".format(self.CLUSTER_API_ROOT, cluster_id, job_id) self.post(url, data = "", on_finished=self._wrapCallback(on_finished, CloudPrintResponse)) ## We override _createEmptyRequest in order to add the user credentials. diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index a137e5261f..17acbe2e3f 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -217,7 +217,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): printer.updateOutputModel(model) # Always have an active printer - if not self._active_printer: + if self._printers and not self._active_printer: self.setActivePrinter(self._printers[0]) self.printersChanged.emit() # TODO: Make this more efficient by not updating every request diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/__init__.py b/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/__init__.py new file mode 100644 index 0000000000..777afc92c2 --- /dev/null +++ b/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/__init__.py @@ -0,0 +1,12 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +import json +import os + + +def readFixture(fixture_name: str) -> bytes: + with open("{}/{}.json".format(os.path.dirname(__file__), fixture_name), "rb") as f: + return f.read() + +def parseFixture(fixture_name: str) -> dict: + return json.loads(readFixture(fixture_name).decode()) diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/clusters.json b/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/getClusters.json similarity index 100% rename from plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/clusters.json rename to plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/getClusters.json diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/postJobPrintResponse.json b/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/postJobPrintResponse.json new file mode 100644 index 0000000000..8b9574359f --- /dev/null +++ b/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/postJobPrintResponse.json @@ -0,0 +1,7 @@ +{ + "data": { + "cluster_job_id": "9a59d8e9-91d3-4ff6-b4cb-9db91c4094dd", + "job_id": "ABCDefGHIjKlMNOpQrSTUvYxWZ0-1234567890abcDE=", + "status": "queued" + } +} diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/putJobUploadResponse.json b/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/putJobUploadResponse.json new file mode 100644 index 0000000000..0474862720 --- /dev/null +++ b/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/putJobUploadResponse.json @@ -0,0 +1,10 @@ +{ + "data": { + "content_type": "text/plain", + "download_url": "https://api.ultimaker.com/print-job-download", + "job_id": "ABCDefGHIjKlMNOpQrSTUvYxWZ0-1234567890abcDE=", + "job_name": "Ultimaker Robot v3.0", + "status": "queued", + "upload_url": "https://api.ultimaker.com/print-job-upload" + } +} diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py b/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py index c7dc1bac35..5a76672b83 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py @@ -1,10 +1,10 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import json -from typing import Dict, Tuple, Union +from typing import Dict, Tuple, Union, Optional from unittest.mock import MagicMock -from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply +from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply, QNetworkRequest from UM.Logger import Logger from UM.Signal import Signal @@ -25,23 +25,30 @@ class NetworkManagerMock: "PUT": QNetworkAccessManager.PutOperation, "DELETE": QNetworkAccessManager.DeleteOperation, "HEAD": QNetworkAccessManager.HeadOperation, - } + } # type: Dict[str, int] ## Initializes the network manager mock. - def __init__(self): + def __init__(self) -> None: # a dict with the prepared replies, using the format {(http_method, url): reply} self.replies = {} # type: Dict[Tuple[str, str], QNetworkReply] + self.request_bodies = {} # type: Dict[Tuple[str, str], bytes] ## Mock implementation of the get, post, put, delete and head methods from the network manager. # Since the methods are very simple and the same it didn't make sense to repeat the code. # \param method: The method being called. # \return The mocked function, if the method name is known. Defaults to the standard getattr function. - def __getattr__(self, method: str): + def __getattr__(self, method: str) -> any: + ## This mock implementation will simply return the reply from the prepared ones. + # it raises a KeyError if requests are done without being prepared. + def doRequest(request: QNetworkRequest, body: Optional[bytes] = None, *_): + key = method.upper(), request.url().toString() + if body: + self.request_bodies[key] = body + return self.replies[key] + operation = self._OPERATIONS.get(method.upper()) if operation: - # this mock implementation will simply return the reply from the prepared ones. - # it raises a KeyError if requests are done without being prepared. - return lambda request, *_: self.replies[method.upper(), request.url().toString()] + return doRequest # the attribute is not one of the implemented methods, default to the standard implementation. return getattr(super(), method) @@ -60,12 +67,18 @@ class NetworkManagerMock: self.replies[method, url] = reply_mock Logger.log("i", "Prepared mock {}-response to {} {}", status_code, method, url) + ## Gets the request that was sent to the network manager for the given method and URL. + # \param method: The HTTP method. + # \param url: The URL. + def getRequestBody(self, method: str, url: str) -> Optional[bytes]: + return self.request_bodies.get((method.upper(), url)) + ## Emits the signal that the reply is ready to all prepared replies. - def flushReplies(self): + def flushReplies(self) -> None: for reply in self.replies.values(): self.finished.emit(reply) self.reset() ## Deletes all prepared replies - def reset(self): + def reset(self) -> None: self.replies.clear() diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py index 4ed2767288..6eca5d250d 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py @@ -1,25 +1,28 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import json -import os from unittest import TestCase from unittest.mock import patch, MagicMock +from UM.Scene.SceneNode import SceneNode from cura.CuraApplication import CuraApplication from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from src.Cloud.CloudApiClient import CloudApiClient -from src.Cloud.CloudOutputController import CloudOutputController from src.Cloud.CloudOutputDevice import CloudOutputDevice +from tests.Cloud.Fixtures import readFixture, parseFixture from .NetworkManagerMock import NetworkManagerMock @patch("cura.NetworkClient.QNetworkAccessManager") class TestCloudOutputDevice(TestCase): CLUSTER_ID = "RIZ6cZbWA_Ua7RZVJhrdVfVpf0z-MqaSHQE4v8aRTtYq" + JOB_ID = "ABCDefGHIjKlMNOpQrSTUvYxWZ0-1234567890abcDE=" HOST_NAME = "ultimakersystem-ccbdd30044ec" - URL = "https://api-staging.ultimaker.com/connect/v1/clusters/{}/status".format(CLUSTER_ID) - with open("{}/Fixtures/getClusterStatusResponse.json".format(os.path.dirname(__file__)), "rb") as f: - DEFAULT_RESPONSE = f.read() + + BASE_URL = "https://api-staging.ultimaker.com" + STATUS_URL = "{}/connect/v1/clusters/{}/status".format(BASE_URL, CLUSTER_ID) + PRINT_URL = "{}/connect/v1/clusters/{}/print/{}".format(BASE_URL, CLUSTER_ID, JOB_ID) + REQUEST_UPLOAD_URL = "{}/cura/v1/jobs/upload".format(BASE_URL) def setUp(self): super().setUp() @@ -28,21 +31,12 @@ class TestCloudOutputDevice(TestCase): self.account = MagicMock(isLoggedIn=True, accessToken="TestAccessToken") self.onError = MagicMock() self.device = CloudOutputDevice(CloudApiClient(self.account, self.onError), self.CLUSTER_ID, self.HOST_NAME) - self.cluster_status = json.loads(self.DEFAULT_RESPONSE.decode()) - self.network.prepareReply("GET", self.URL, 200, self.DEFAULT_RESPONSE) + self.cluster_status = parseFixture("getClusterStatusResponse") + self.network.prepareReply("GET", self.STATUS_URL, 200, readFixture("getClusterStatusResponse")) def tearDown(self): - try: - self._beforeTearDown() - finally: - super().tearDown() - - ## Before tear down method we check whether the state of the output device manager is what we expect based on the - # mocked API response. - def _beforeTearDown(self): - # let the network send replies + super().tearDown() self.network.flushReplies() - # TODO def test_status(self, network_mock): network_mock.return_value = self.network @@ -67,7 +61,66 @@ class TestCloudOutputDevice(TestCase): self.assertEqual([controller_fields, controller_fields], [printer.getController().__dict__ for printer in self.device.printers]) + self.assertEqual(["UM3PrintJobOutputModel"], [type(printer).__name__ for printer in self.device.printJobs]) self.assertEqual({job["uuid"] for job in self.cluster_status["data"]["print_jobs"]}, {job.key for job in self.device.printJobs}) - self.assertEqual(["Daniel Testing"], [job.owner for job in self.device.printJobs]) - self.assertEqual(["UM3_dragon"], [job.name for job in self.device.printJobs]) + self.assertEqual({job["owner"] for job in self.cluster_status["data"]["print_jobs"]}, + {job.owner for job in self.device.printJobs}) + self.assertEqual({job["name"] for job in self.cluster_status["data"]["print_jobs"]}, + {job.name for job in self.device.printJobs}) + + def test_remove_print_job(self, network_mock): + network_mock.return_value = self.network + self.device._update() + self.network.flushReplies() + self.assertEqual(1, len(self.device.printJobs)) + + self.cluster_status["data"]["print_jobs"].clear() + self.network.prepareReply("GET", self.STATUS_URL, 200, self.cluster_status) + self.device._update() + self.network.flushReplies() + self.assertEqual([], self.device.printJobs) + + def test_remove_printers(self, network_mock): + network_mock.return_value = self.network + self.device._update() + self.network.flushReplies() + self.assertEqual(2, len(self.device.printers)) + + self.cluster_status["data"]["printers"].clear() + self.network.prepareReply("GET", self.STATUS_URL, 200, self.cluster_status) + self.device._update() + self.network.flushReplies() + self.assertEqual([], self.device.printers) + + @patch("cura.CuraApplication.CuraApplication.getGlobalContainerStack") + def test_print_to_cloud(self, global_container_stack_mock, network_mock): + active_machine_mock = global_container_stack_mock.return_value + active_machine_mock.getMetaDataEntry.side_effect = {"file_formats": "application/gzip"}.get + + request_upload_response = parseFixture("putJobUploadResponse") + request_print_response = parseFixture("postJobPrintResponse") + self.network.prepareReply("PUT", self.REQUEST_UPLOAD_URL, 201, request_upload_response) + self.network.prepareReply("PUT", request_upload_response["data"]["upload_url"], 201, b"{}") + self.network.prepareReply("POST", self.PRINT_URL, 200, request_print_response) + + network_mock.return_value = self.network + file_handler = MagicMock() + file_handler.getSupportedFileTypesWrite.return_value = [{ + "extension": "gcode.gz", + "mime_type": "application/gzip", + "mode": 2, + }] + file_handler.getWriterByMimeType.return_value.write.side_effect = \ + lambda stream, nodes: stream.write(str(nodes).encode()) + + scene_nodes = [SceneNode()] + self.device.requestWrite(scene_nodes, file_handler=file_handler, file_name="FileName") + + self.network.flushReplies() + self.assertEqual({"data": {"content_type": "application/gzip", "file_size": 57, "job_name": "FileName"}}, + json.loads(self.network.getRequestBody("PUT", self.REQUEST_UPLOAD_URL).decode())) + self.assertEqual(str(scene_nodes).encode(), + self.network.getRequestBody("PUT", request_upload_response["data"]["upload_url"])) + + self.assertIsNone(self.network.getRequestBody("POST", self.PRINT_URL)) diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py index 9e980a8681..420d71d0fe 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py @@ -1,29 +1,26 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -import json -import os from unittest import TestCase from unittest.mock import patch from cura.CuraApplication import CuraApplication from src.Cloud.CloudOutputDevice import CloudOutputDevice from src.Cloud.CloudOutputDeviceManager import CloudOutputDeviceManager +from tests.Cloud.Fixtures import parseFixture, readFixture from .NetworkManagerMock import NetworkManagerMock @patch("cura.NetworkClient.QNetworkAccessManager") class TestCloudOutputDeviceManager(TestCase): URL = "https://api-staging.ultimaker.com/connect/v1/clusters" - with open("{}/Fixtures/clusters.json".format(os.path.dirname(__file__)), "rb") as f: - DEFAULT_RESPONSE = f.read() def setUp(self): super().setUp() self.app = CuraApplication.getInstance() self.network = NetworkManagerMock() self.manager = CloudOutputDeviceManager() - self.clusters_response = json.loads(self.DEFAULT_RESPONSE.decode()) - self.network.prepareReply("GET", self.URL, 200, self.DEFAULT_RESPONSE) + self.clusters_response = parseFixture("getClusters") + self.network.prepareReply("GET", self.URL, 200, readFixture("getClusters")) def tearDown(self): try: From 1436301d780572e976ed0c62edbebee42a404729 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 10 Dec 2018 16:20:00 +0100 Subject: [PATCH 0808/1240] Ensure setActiveExtruderIndex only gets called once when switching machines CURA-6016 --- cura/Settings/ExtruderManager.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index b0bcf3b100..8fa0172305 100755 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -83,8 +83,9 @@ class ExtruderManager(QObject): # \param index The index of the new active extruder. @pyqtSlot(int) def setActiveExtruderIndex(self, index: int) -> None: - self._active_extruder_index = index - self.activeExtruderChanged.emit() + if self._active_extruder_index != index: + self._active_extruder_index = index + self.activeExtruderChanged.emit() @pyqtProperty(int, notify = activeExtruderChanged) def activeExtruderIndex(self) -> int: @@ -344,6 +345,7 @@ class ExtruderManager(QObject): if extruders_changed: self.extrudersChanged.emit(global_stack_id) self.setActiveExtruderIndex(0) + self.activeExtruderChanged.emit() # After 3.4, all single-extrusion machines have their own extruder definition files instead of reusing # "fdmextruder". We need to check a machine here so its extruder definition is correct according to this. From 3132b1f689ad6f83479db5bd19a071e6d47bf74e Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 10 Dec 2018 16:56:44 +0100 Subject: [PATCH 0809/1240] Update the extruder Model a whole lot less CURA-6016 --- cura/Settings/ExtrudersModel.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cura/Settings/ExtrudersModel.py b/cura/Settings/ExtrudersModel.py index 5f10ac99d4..e19617c8ef 100644 --- a/cura/Settings/ExtrudersModel.py +++ b/cura/Settings/ExtrudersModel.py @@ -224,6 +224,6 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): "definition": "" } items.append(item) - - self.setItems(items) - self.modelChanged.emit() + if self._items != items: + self.setItems(items) + self.modelChanged.emit() From 716fedc7820c6a406d3ba431d99284e264cc5541 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 10 Dec 2018 17:06:51 +0100 Subject: [PATCH 0810/1240] Use no text by default for the disabled state of the panels Contributes to CURA-5941. --- resources/qml/ExpandableComponent.qml | 2 +- resources/qml/ExpandablePopup.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index c2b9d715d5..2b07aa7d37 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -109,7 +109,7 @@ Item anchors.fill: parent leftPadding: background.padding rightPadding: background.padding - text: catalog.i18nc("@label default disabled text", "This component is disabled") + text: "" font: UM.Theme.getFont("default") renderType: Text.NativeRendering verticalAlignment: Text.AlignVCenter diff --git a/resources/qml/ExpandablePopup.qml b/resources/qml/ExpandablePopup.qml index 2d34b134e0..4bf1684f18 100644 --- a/resources/qml/ExpandablePopup.qml +++ b/resources/qml/ExpandablePopup.qml @@ -113,7 +113,7 @@ Item id: disabledLabel visible: !base.enabled leftPadding: background.padding - text: catalog.i18nc("@label default disabled text", "This component is disabled") + text: "" font: UM.Theme.getFont("default") renderType: Text.NativeRendering verticalAlignment: Text.AlignVCenter From e7ba4455e06617d6af7ee7b84e41c8eb189f8b80 Mon Sep 17 00:00:00 2001 From: Arseniy Pavlenko Date: Mon, 10 Dec 2018 19:42:23 +0200 Subject: [PATCH 0811/1240] default is 2650, not 2620, probably typo --- resources/definitions/tevo_tarantula.def.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/definitions/tevo_tarantula.def.json b/resources/definitions/tevo_tarantula.def.json index 570ae24a3d..ec4ae667d5 100644 --- a/resources/definitions/tevo_tarantula.def.json +++ b/resources/definitions/tevo_tarantula.def.json @@ -42,7 +42,7 @@ "machine_max_feedrate_x": { "default_value": 255 }, "machine_max_feedrate_y": { "default_value": 225 }, "machine_max_feedrate_z": { "default_value": 3 }, - "machine_max_acceleration_x": { "default_value": 2620 }, + "machine_max_acceleration_x": { "default_value": 2650 }, "machine_max_acceleration_y": { "default_value": 2650 }, "acceleration_print": { "default_value": 2650 }, "machine_start_gcode": { "default_value": "G21 ;metric values\nG90 ;absolute positioning\nM82 ;set extruder to absolute mode\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z15.0 F9000 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E3 ;extrude 3mm of feed stock\nG92 E0 ;zero the extruded length again\nG1 F9000\n;Put printing message on LCD screen\nM117 Printing..." }, From 0357003e6991652c7b07762c2cca0a218b3cc5f6 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Mon, 10 Dec 2018 21:47:06 +0100 Subject: [PATCH 0812/1240] STAR-322: Testing encode independent --- .../tests/Cloud/TestCloudOutputDevice.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py index 6eca5d250d..287f2dda98 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py @@ -115,12 +115,15 @@ class TestCloudOutputDevice(TestCase): lambda stream, nodes: stream.write(str(nodes).encode()) scene_nodes = [SceneNode()] + expected_mesh = str(scene_nodes).encode() self.device.requestWrite(scene_nodes, file_handler=file_handler, file_name="FileName") self.network.flushReplies() - self.assertEqual({"data": {"content_type": "application/gzip", "file_size": 57, "job_name": "FileName"}}, - json.loads(self.network.getRequestBody("PUT", self.REQUEST_UPLOAD_URL).decode())) - self.assertEqual(str(scene_nodes).encode(), + self.assertEqual( + {"data": {"content_type": "application/gzip", "file_size": len(expected_mesh), "job_name": "FileName"}}, + json.loads(self.network.getRequestBody("PUT", self.REQUEST_UPLOAD_URL).decode()) + ) + self.assertEqual(expected_mesh, self.network.getRequestBody("PUT", request_upload_response["data"]["upload_url"])) self.assertIsNone(self.network.getRequestBody("POST", self.PRINT_URL)) From 909f36d28ede29c0c9fc5f69db7f8941fc5a8429 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 11 Dec 2018 09:24:22 +0100 Subject: [PATCH 0813/1240] Let the settingsMenu use the extruders of the active machine instead of the extruderModel The extruder model gets updated way to much (for all material changes) but we only need the number and names of the extruders, since the other menu's do this by themselves --- cura/Settings/GlobalStack.py | 16 +++++++++++++--- resources/qml/Menus/SettingsMenu.qml | 5 +++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/cura/Settings/GlobalStack.py b/cura/Settings/GlobalStack.py index da1ec61254..44ceee9511 100755 --- a/cura/Settings/GlobalStack.py +++ b/cura/Settings/GlobalStack.py @@ -3,8 +3,8 @@ from collections import defaultdict import threading -from typing import Any, Dict, Optional, Set, TYPE_CHECKING -from PyQt5.QtCore import pyqtProperty, pyqtSlot +from typing import Any, Dict, Optional, Set, TYPE_CHECKING, List +from PyQt5.QtCore import pyqtProperty, pyqtSlot, pyqtSignal from UM.Decorators import override from UM.MimeTypeDatabase import MimeType, MimeTypeDatabase @@ -42,13 +42,23 @@ class GlobalStack(CuraContainerStack): # Per thread we have our own resolving_settings, or strange things sometimes occur. self._resolving_settings = defaultdict(set) #type: Dict[str, Set[str]] # keys are thread names + extrudersChanged = pyqtSignal() + ## Get the list of extruders of this stack. # # \return The extruders registered with this stack. - @pyqtProperty("QVariantMap") + @pyqtProperty("QVariantMap", notify = extrudersChanged) def extruders(self) -> Dict[str, "ExtruderStack"]: return self._extruders + @pyqtProperty("QVariantList", notify = extrudersChanged) + def extruderList(self) -> List["ExtruderStack"]: + result_tuple_list = sorted(list(self.extruders.items()), key=lambda x: int(x[0])) + result_list = [item[1] for item in result_tuple_list] + + machine_extruder_count = self.getProperty("machine_extruder_count", "value") + return result_list[:machine_extruder_count] + @classmethod def getLoadingPriority(cls) -> int: return 2 diff --git a/resources/qml/Menus/SettingsMenu.qml b/resources/qml/Menus/SettingsMenu.qml index 79f8c5b7bf..4ea3a4d71a 100644 --- a/resources/qml/Menus/SettingsMenu.qml +++ b/resources/qml/Menus/SettingsMenu.qml @@ -16,10 +16,11 @@ Menu Instantiator { - model: Cura.ExtrudersModel { simpleNames: true } + model: Cura.MachineManager.activeMachine.extruderList + Menu { - title: model.name + title: modelData.name NozzleMenu { title: Cura.MachineManager.activeDefinitionVariantsName; visible: Cura.MachineManager.hasVariants; extruderIndex: index } MaterialMenu { title: catalog.i18nc("@title:menu", "&Material"); visible: Cura.MachineManager.hasMaterials; extruderIndex: index } From 99cfef6cdc33d222178ee6f7418d40b5c1727525 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Tue, 11 Dec 2018 09:25:26 +0100 Subject: [PATCH 0814/1240] Use the NetworkManagerMock, added tests for the other functions --- .../Cloud/Fixtures/requestPrintResponse.json | 7 + .../tests/Cloud/TestCloudApiClient.py | 212 +++++++++--------- 2 files changed, 109 insertions(+), 110 deletions(-) create mode 100644 plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/requestPrintResponse.json diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/requestPrintResponse.json b/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/requestPrintResponse.json new file mode 100644 index 0000000000..e69589784d --- /dev/null +++ b/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/requestPrintResponse.json @@ -0,0 +1,7 @@ +{ + "data": { + "cluster_job_id": "", + "job_id": "db34b096-c4d5-46f3-bea7-da6a19905e6c", + "status": "queued" + } +} diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py index 91f367f9ad..07c7733ac1 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py @@ -2,134 +2,126 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import json -from typing import Dict, Tuple -from unittest import TestCase, mock +import os +from unittest import TestCase from unittest.mock import patch, MagicMock -from PyQt5.QtCore import QByteArray -from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply - -from UM.Application import Application -from UM.Signal import Signal from cura.CuraApplication import CuraApplication -from plugins.UM3NetworkPrinting.src.Cloud.CloudApiClient import CloudApiClient -from plugins.UM3NetworkPrinting.src.Cloud.Models import CloudCluster, CloudErrorObject - -# This mock application must extend from Application and not QtApplication otherwise some QObjects are created and -# a segfault is raised. -class FixtureApplication(Application): - def __init__(self): - super().__init__(name = "test", version = "1.0", api_version = "5.0.0") - super().initialize() - Signal._signalQueue = self - - def functionEvent(self, event): - event.call() - - def parseCommandLine(self): - pass - - def processEvents(self): - pass - - def getRenderer(self): - return MagicMock() - -class ManagerMock: - finished = Signal() - authenticationRequired = Signal() - - def __init__(self, reply): - self.reply = reply - - def get(self, request): - self.reply.url.return_value = request.url() - - return self.reply - -class ManagerMock2: - finished = Signal() - authenticationRequired = Signal() - - def get(self, request): - reply_mock = MagicMock() - reply_mock.url = request.url - reply_mock.operation.return_value = QNetworkAccessManager.GetOperation - return reply_mock - - @staticmethod - def createReply(method: str, url: str, status_code: int, response: dict): - reply_mock = MagicMock() - reply_mock.url().toString.return_value = url - reply_mock.operation.return_value = { - "GET": QNetworkAccessManager.GetOperation, - "POST": QNetworkAccessManager.PostOperation, - "PUT": QNetworkAccessManager.PutOperation, - "DELETE": QNetworkAccessManager.DeleteOperation, - "HEAD": QNetworkAccessManager.HeadOperation, - }[method] - reply_mock.attribute.return_value = status_code - reply_mock.readAll.return_value = json.dumps(response).encode() - return reply_mock +from src.Cloud.CloudApiClient import CloudApiClient +from src.Cloud.CloudOutputDeviceManager import CloudOutputDeviceManager +from src.Cloud.Models.CloudJobResponse import CloudJobResponse +from src.Cloud.Models.CloudJobUploadRequest import CloudJobUploadRequest +from .NetworkManagerMock import NetworkManagerMock +@patch("cura.NetworkClient.QNetworkAccessManager") class TestCloudApiClient(TestCase): - - app = CuraApplication.getInstance() or CuraApplication - - def _errorHandler(self, errors: [CloudErrorObject]): + def _errorHandler(self): pass - @patch("cura.NetworkClient.QNetworkAccessManager") - @patch("cura.API.Account") - def test_GetClusters(self, account_mock, manager_mock): - reply_mock = MagicMock() - reply_mock.operation.return_value = 2 - reply_mock.attribute.return_value = 200 - reply_mock.readAll.return_value = b'{"data": [{"cluster_id": "RIZ6cZbWA_Ua7RZVJhrdVfVpf0z-MqaSHQE4v8aRTtYq", "host_guid": "e90ae0ac-1257-4403-91ee-a44c9b7e8050", "host_name": "ultimakersystem-ccbdd30044ec", "host_version": "5.1.2.20180807", "is_online": false, "status": "inactive"}, {"cluster_id": "R0YcLJwar1ugh0ikEZsZs8NWKV6vJP_LdYsXgXqAcaNC", "host_guid": "e90ae0ac-1257-4403-91ee-a44c9b7e8050", "host_name": "ultimakersystem-ccbdd30044ec", "host_version": "5.1.2.20180807", "is_online": true, "status": "active"}]}' - manager_mock.return_value = ManagerMock(reply_mock) - account_mock.isLoggedIn.return_value = True + def setUp(self): + super().setUp() + self.account = MagicMock() + self.account.isLoggedIn.return_value = True + + self.app = CuraApplication.getInstance() + self.network = NetworkManagerMock() + self.manager = CloudOutputDeviceManager() + self.api = CloudApiClient(self.account, self._errorHandler) + + def test_GetClusters(self, network_mock): + network_mock.return_value = self.network result = [] - def _callback(clusters): - result.extend(clusters) + with open("{}/Fixtures/getClusters.json".format(os.path.dirname(__file__)), "rb") as f: + response = f.read() - api = CloudApiClient(account_mock, self._errorHandler) - api.getClusters(_callback) + self.network.prepareReply("GET", "https://api-staging.ultimaker.com/connect/v1/clusters", 200, response) + # the callback is a function that adds the result of the call to getClusters to the result list + self.api.getClusters(lambda clusters: result.extend(clusters)) - manager_mock.return_value.finished.emit(reply_mock) + self.network.flushReplies() self.assertEqual(2, len(result)) - @patch("cura.NetworkClient.QNetworkAccessManager") - @patch("cura.API.Account") - def test_GetClusters2(self, account_mock, manager_mock): - manager = ManagerMock2() - manager_mock.return_value = manager - account_mock.isLoggedIn.return_value = True + def test_getClusterStatus(self, network_mock): + network_mock.return_value = self.network result = [] - # with mock.patch.object(Application, "getInstance", new = lambda: FixtureApplication()): - api = CloudApiClient(account_mock, self._errorHandler) - api.getClusters(lambda clusters: result.extend(clusters)) + with open("{}/Fixtures/getClusterStatusResponse.json".format(os.path.dirname(__file__)), "rb") as f: + response = f.read() - manager.finished.emit(ManagerMock2.createReply( - "GET", "https://api-staging.ultimaker.com/connect/v1/clusters", - 200, { - "data": [{ - "cluster_id": "RIZ6cZbWA_Ua7RZVJhrdVfVpf0z-MqaSHQE4v8aRTtYq", - "host_guid": "e90ae0ac-1257-4403-91ee-a44c9b7e8050", - "host_name": "ultimakersystem-ccbdd30044ec", "host_version": "5.1.2.20180807", - "is_online": False, "status": "inactive" - }, { - "cluster_id": "R0YcLJwar1ugh0ikEZsZs8NWKV6vJP_LdYsXgXqAcaNC", - "host_guid": "e90ae0ac-1257-4403-91ee-a44c9b7e8050", - "host_name": "ultimakersystem-ccbdd30044ec", "host_version": "5.1.2.20180807", - "is_online": True, "status": "active" - }] - } - )) + self.network.prepareReply("GET", + "https://api-staging.ultimaker.com/connect/v1/clusters/R0YcLJwar1ugh0ikEZsZs8NWKV6vJP_LdYsXgXqAcaNC/status", + 200, response + ) + self.api.getClusterStatus("R0YcLJwar1ugh0ikEZsZs8NWKV6vJP_LdYsXgXqAcaNC", lambda status: result.append(status)) - self.assertEqual(2, len(result)) + self.network.flushReplies() + + self.assertEqual(len(result), 1) + status = result[0] + + self.assertEqual(len(status.printers), 2) + self.assertEqual(len(status.print_jobs), 1) + + def test_requestUpload(self, network_mock): + network_mock.return_value = self.network + results = [] + + with open("{}/Fixtures/requestUploadResponse.json".format(os.path.dirname(__file__)), "rb") as f: + response = f.read() + + self.network.prepareReply("PUT", "https://api-staging.ultimaker.com/cura/v1/jobs/upload", 200, response) + self.api.requestUpload(CloudJobUploadRequest(job_name = "job name", file_size = 143234, content_type = "text/plain"), + lambda r: results.append(r)) + self.network.flushReplies() + + self.assertEqual(results[0].content_type, "text/plain") + self.assertEqual(results[0].status, "uploading") + + def test_uploadMesh(self, network_mock): + network_mock.return_value = self.network + results = [] + progress = MagicMock() + + with open("{}/Fixtures/requestUploadResponse.json".format(os.path.dirname(__file__)), "rb") as f: + thedata = json.loads(f.read().decode("ascii")) + data = thedata["data"] + upload_response = CloudJobResponse(**data) + + self.network.prepareReply("PUT", upload_response.upload_url, 200, + '{ data : "" }') # Network client doesn't look into the reply + + self.api.uploadMesh(upload_response, b'', lambda job_id: results.append(job_id), + progress.advance, progress.error) + + self.network.flushReplies() + + self.assertEqual(len(results), 1) + self.assertEqual(results[0], upload_response.job_id) + + def test_requestPrint(self, network_mock): + network_mock.return_value = self.network + results = [] + + cluster_id = "NWKV6vJP_LdYsXgXqAcaNCR0YcLJwar1ugh0ikEZsZs8" + job_id = "db34b096-c4d5-46f3-bea7-da6a19905e6c" + + with open("{}/Fixtures/requestPrintResponse.json".format(os.path.dirname(__file__)), "rb") as f: + response = f.read() + + self.network.prepareReply("POST", + "https://api-staging.ultimaker.com/connect/v1/clusters/{}/print/{}" + .format(cluster_id, job_id), + 200, response) + + self.api.requestPrint(cluster_id, job_id, lambda r: results.append(r)) + + self.network.flushReplies() + + self.assertEqual(len(results), 1) + self.assertEqual(results[0].job_id, "db34b096-c4d5-46f3-bea7-da6a19905e6c") + self.assertEqual(results[0].status, "queued") From 3b54cb4b241187562341d7435c391dbefe0949b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marijn=20De=C3=A9?= Date: Tue, 11 Dec 2018 09:27:40 +0100 Subject: [PATCH 0815/1240] Use the NetworkManagerMock, added tests for the other functions --- .../tests/Cloud/Fixtures/requestUploadResponse.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/requestUploadResponse.json diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/requestUploadResponse.json b/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/requestUploadResponse.json new file mode 100644 index 0000000000..6168e5205f --- /dev/null +++ b/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/requestUploadResponse.json @@ -0,0 +1,11 @@ +{ + "data": { + "content_type": "text/plain", + "generated_time": "2018-12-10T09:33:00.009Z", + "job_id": "j9KUn4D6FRRRmdtbCo4OGAwUf6Ml3p3oU-Zv7RNRv92T", + "job_name": "job name", + "status": "uploading", + "status_description": "The print job has been created. Please upload the file.", + "upload_url": "https://www.googleapis.com/upload/storage/v1/b/ultimaker-storage-1/o?uploadType=resumable&upload_id=AEnB2Uqhg1H7BXQVeLJEWw6AheqMicydZVLuH9bnkh6Oge0e6i5X76MW3NZHWRmUTmjzulAF42mkczcC7rsAuPg1Nn8JeFpnNA" + } +} From bb1950525a5e67ae9ee0e7163d720e0d14b72f16 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 11 Dec 2018 09:42:34 +0100 Subject: [PATCH 0816/1240] Limit the amount of times the buildplate rebuild is done CURA-6016 --- cura/BuildVolume.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index f837f5cef7..aa1f170707 100755 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -83,7 +83,14 @@ class BuildVolume(SceneNode): " with printed models."), title = catalog.i18nc("@info:title", "Build Volume")) self._global_container_stack = None + + self._stack_change_timer = QTimer() + self._stack_change_timer.setInterval(100) + self._stack_change_timer.setSingleShot(True) + self._stack_change_timer.timeout.connect(self._onStackChangeTimerFinished) + self._application.globalContainerStackChanged.connect(self._onStackChanged) + self._onStackChanged() self._engine_ready = False @@ -105,6 +112,8 @@ class BuildVolume(SceneNode): self._setting_change_timer.setSingleShot(True) self._setting_change_timer.timeout.connect(self._onSettingChangeTimerFinished) + + # Must be after setting _build_volume_message, apparently that is used in getMachineManager. # activeQualityChanged is always emitted after setActiveVariant, setActiveMaterial and setActiveQuality. # Therefore this works. @@ -526,8 +535,11 @@ class BuildVolume(SceneNode): if extra_z != self._extra_z_clearance: self._extra_z_clearance = extra_z - ## Update the build volume visualization def _onStackChanged(self): + self._stack_change_timer.start() + + ## Update the build volume visualization + def _onStackChangeTimerFinished(self): if self._global_container_stack: self._global_container_stack.propertyChanged.disconnect(self._onSettingPropertyChanged) extruders = ExtruderManager.getInstance().getActiveExtruderStacks() From 77703e1fb8f2a0c60a56126b9ccc90d5aba8f8a3 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 11 Dec 2018 09:58:23 +0100 Subject: [PATCH 0817/1240] Catch an exception from numpy that happens when loading some models --- cura/Scene/ConvexHullDecorator.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cura/Scene/ConvexHullDecorator.py b/cura/Scene/ConvexHullDecorator.py index 0c03ae615b..661106dec7 100644 --- a/cura/Scene/ConvexHullDecorator.py +++ b/cura/Scene/ConvexHullDecorator.py @@ -187,7 +187,10 @@ class ConvexHullDecorator(SceneNodeDecorator): for child in self._node.getChildren(): child_hull = child.callDecoration("_compute2DConvexHull") if child_hull: - points = numpy.append(points, child_hull.getPoints(), axis = 0) + try: + points = numpy.append(points, child_hull.getPoints(), axis = 0) + except ValueError: + pass if points.size < 3: return None From badf2962bc90c910fc10b09ecb309b10604d5090 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 11 Dec 2018 10:34:00 +0100 Subject: [PATCH 0818/1240] Change CuraDrive version to 1.2.0 CURA-6005 Because the previously version is 1.1.1 --- plugins/CuraDrive/plugin.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/CuraDrive/plugin.json b/plugins/CuraDrive/plugin.json index 134cd31a77..44bf955f41 100644 --- a/plugins/CuraDrive/plugin.json +++ b/plugins/CuraDrive/plugin.json @@ -2,7 +2,7 @@ "name": "Cura Backups", "author": "Ultimaker B.V.", "description": "Backup and restore your configuration.", - "version": "1.2.1", + "version": "1.2.0", "api": 5, "i18n-catalog": "cura_drive" -} \ No newline at end of file +} From 9618e8d4e096aaaeaf6e510a00b7436bd4cc852f Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 11 Dec 2018 10:34:49 +0100 Subject: [PATCH 0819/1240] Add CuraDrive 1.2.0 to bundled_packages CURA-6005 --- resources/bundled_packages/cura.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/resources/bundled_packages/cura.json b/resources/bundled_packages/cura.json index d8a7df2478..33c6304fc5 100644 --- a/resources/bundled_packages/cura.json +++ b/resources/bundled_packages/cura.json @@ -50,6 +50,23 @@ } } }, + "CuraDrive": { + "package_info": { + "package_id": "CuraDrive", + "package_type": "plugin", + "display_name": "Cura Backups", + "description": "Backup and restore your configuration.", + "package_version": "1.2.0", + "sdk_version": 5, + "website": "https://ultimaker.com", + "author": { + "author_id": "UltimakerPackages", + "display_name": "Ultimaker B.V.", + "email": "plugins@ultimaker.com", + "website": "https://ultimaker.com" + } + } + }, "CuraEngineBackend": { "package_info": { "package_id": "CuraEngineBackend", From 4dab33d41babdb0d961d3d1bcb54479b5e8c3691 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 11 Dec 2018 10:44:30 +0100 Subject: [PATCH 0820/1240] Remove the fancy snowflakes pattern --- resources/qml/Cura.qml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 3e019cdcd5..a4faa27b67 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -123,17 +123,6 @@ UM.MainWindow } } } - - // This is the new fancy pattern - Image - { - id: backgroundPattern - anchors.fill: parent - fillMode: Image.Tile - source: UM.Theme.getImage("header_pattern") - horizontalAlignment: Image.AlignLeft - verticalAlignment: Image.AlignTop - } } MainWindowHeader From 0f357e1078b6a30a5b3ddda88bbc9fcd736f0bb4 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Tue, 11 Dec 2018 10:51:05 +0100 Subject: [PATCH 0821/1240] Replace fix for 'None' materials This was fixed in 4.0 and accidentally got reverted when merging another branch in. Contributes to CL-1160 --- .../UM3NetworkPrinting/src/ClusterUM3OutputDevice.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index 08592df603..60474156a8 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -609,6 +609,17 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): material_manager = CuraApplication.getInstance().getMaterialManager() material_group_list = material_manager.getMaterialGroupListByGUID(material_data["guid"]) + # This can happen if the connected machine has no material in one or more extruders (if GUID is empty), or the + # material is unknown to Cura, so we should return an "empty" or "unknown" material model. + if material_group_list is None: + material_name = "Empty" if len(material_data["guid"]) == 0 else "Unknown" + return MaterialOutputModel(guid = material_data["guid"], + type = material_data.get("type", ""), + color = material_data.get("color", ""), + brand = material_data.get("brand", ""), + name = material_data.get("name", material_name) + ) + # Sort the material groups by "is_read_only = True" first, and then the name alphabetically. read_only_material_group_list = list(filter(lambda x: x.is_read_only, material_group_list)) non_read_only_material_group_list = list(filter(lambda x: not x.is_read_only, material_group_list)) From f67ac8d7c4f937a323f8b1a520abfaecad5b438a Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 11 Dec 2018 10:59:17 +0100 Subject: [PATCH 0822/1240] Update far less agressively for the material models CURA-6016 --- cura/Machines/Models/BaseMaterialsModel.py | 14 ++++++++++---- cura/Machines/Models/FavoriteMaterialsModel.py | 5 +---- cura/Machines/Models/GenericMaterialsModel.py | 3 --- cura/Machines/Models/MaterialBrandsModel.py | 4 ---- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/cura/Machines/Models/BaseMaterialsModel.py b/cura/Machines/Models/BaseMaterialsModel.py index ef2e760330..3cd92bb6b4 100644 --- a/cura/Machines/Models/BaseMaterialsModel.py +++ b/cura/Machines/Models/BaseMaterialsModel.py @@ -1,5 +1,6 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from typing import Optional, Dict, Set from PyQt5.QtCore import Qt, pyqtSignal, pyqtProperty from UM.Qt.ListModel import ListModel @@ -9,6 +10,9 @@ from UM.Qt.ListModel import ListModel # Those 2 models are used by the material drop down menu to show generic materials and branded materials separately. # The extruder position defined here is being used to bound a menu to the correct extruder. This is used in the top # bar menu "Settings" -> "Extruder nr" -> "Material" -> this menu +from cura.Machines.MaterialNode import MaterialNode + + class BaseMaterialsModel(ListModel): extruderPositionChanged = pyqtSignal() @@ -54,8 +58,8 @@ class BaseMaterialsModel(ListModel): self._extruder_position = 0 self._extruder_stack = None - self._available_materials = None - self._favorite_ids = None + self._available_materials = None # type: Optional[Dict[str, MaterialNode]] + self._favorite_ids = set() # type: Set(str) def _updateExtruderStack(self): global_stack = self._machine_manager.activeMachine @@ -102,8 +106,10 @@ class BaseMaterialsModel(ListModel): return False extruder_stack = global_stack.extruders[extruder_position] - - self._available_materials = self._material_manager.getAvailableMaterialsForMachineExtruder(global_stack, extruder_stack) + available_materials = self._material_manager.getAvailableMaterialsForMachineExtruder(global_stack, extruder_stack) + if available_materials == self._available_materials: + return False + self._available_materials = available_materials if self._available_materials is None: return False diff --git a/cura/Machines/Models/FavoriteMaterialsModel.py b/cura/Machines/Models/FavoriteMaterialsModel.py index 18fe310c44..cc273e55ce 100644 --- a/cura/Machines/Models/FavoriteMaterialsModel.py +++ b/cura/Machines/Models/FavoriteMaterialsModel.py @@ -4,17 +4,14 @@ from UM.Logger import Logger from cura.Machines.Models.BaseMaterialsModel import BaseMaterialsModel -class FavoriteMaterialsModel(BaseMaterialsModel): +class FavoriteMaterialsModel(BaseMaterialsModel): def __init__(self, parent = None): super().__init__(parent) self._update() def _update(self): - - # Perform standard check and reset if the check fails if not self._canUpdate(): - self.setItems([]) return # Get updated list of favorites diff --git a/cura/Machines/Models/GenericMaterialsModel.py b/cura/Machines/Models/GenericMaterialsModel.py index c276b865bf..8f41dd6a70 100644 --- a/cura/Machines/Models/GenericMaterialsModel.py +++ b/cura/Machines/Models/GenericMaterialsModel.py @@ -11,10 +11,7 @@ class GenericMaterialsModel(BaseMaterialsModel): self._update() def _update(self): - - # Perform standard check and reset if the check fails if not self._canUpdate(): - self.setItems([]) return # Get updated list of favorites diff --git a/cura/Machines/Models/MaterialBrandsModel.py b/cura/Machines/Models/MaterialBrandsModel.py index 458e4d9b47..ac82cf6670 100644 --- a/cura/Machines/Models/MaterialBrandsModel.py +++ b/cura/Machines/Models/MaterialBrandsModel.py @@ -28,12 +28,8 @@ class MaterialBrandsModel(BaseMaterialsModel): self._update() def _update(self): - - # Perform standard check and reset if the check fails if not self._canUpdate(): - self.setItems([]) return - # Get updated list of favorites self._favorite_ids = self._material_manager.getFavorites() From 4b6f4af44eeef9b0d39c3161d7e577d66d57388c Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 11 Dec 2018 11:10:45 +0100 Subject: [PATCH 0823/1240] Close the expandable panel when it becomes disabled It happens when it's open and the user loads a GCode. Contributes to CURA-5941 --- resources/qml/ExpandableComponent.qml | 18 +++++++++++++++++- resources/qml/ExpandablePopup.qml | 15 ++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index 2b07aa7d37..afe15bcb1d 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -88,7 +88,23 @@ Item { target: background property: "color" - value: enabled ? (expanded ? headerActiveColor : headerBackgroundColor) : UM.Theme.getColor("disabled") + value: + { + return base.enabled ? (expanded ? headerActiveColor : headerBackgroundColor) : UM.Theme.getColor("disabled") + } + } + + // The panel needs to close when it becomes disabled + Connections + { + target: base + onEnabledChanged: + { + if (!base.enabled && expanded) + { + toggleContent() + } + } } implicitHeight: 100 * screenScaleFactor diff --git a/resources/qml/ExpandablePopup.qml b/resources/qml/ExpandablePopup.qml index 4bf1684f18..2d2665373e 100644 --- a/resources/qml/ExpandablePopup.qml +++ b/resources/qml/ExpandablePopup.qml @@ -94,7 +94,20 @@ Item { target: background property: "color" - value: enabled ? headerBackgroundColor : UM.Theme.getColor("disabled") + value: base.enabled ? headerBackgroundColor : UM.Theme.getColor("disabled") + } + + // The panel needs to close when it becomes disabled + Connections + { + target: base + onEnabledChanged: + { + if (!base.enabled && expanded) + { + toggleContent() + } + } } implicitHeight: 100 * screenScaleFactor From 76688015645f6e3cce3fd66d7d6539ef870200a0 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Tue, 11 Dec 2018 11:12:32 +0100 Subject: [PATCH 0824/1240] STAR-322: Adding documentation and fixing model types --- .../src/Cloud/CloudApiClient.py | 25 +++--- .../src/Cloud/CloudOutputDevice.py | 26 +++--- .../src/Cloud/CloudOutputDeviceManager.py | 6 +- .../src/Cloud/Models/BaseCloudModel.py | 48 ++++++++-- .../src/Cloud/Models/CloudCluster.py | 21 ----- .../src/Cloud/Models/CloudClusterPrintJob.py | 65 -------------- .../Models/CloudClusterPrintJobConstraint.py | 12 ++- .../Models/CloudClusterPrintJobStatus.py | 87 +++++++++++++++++++ .../src/Cloud/Models/CloudClusterPrinter.py | 49 ----------- .../CloudClusterPrinterConfiguration.py | 23 +++-- ...loudClusterPrinterConfigurationMaterial.py | 19 ++-- .../Cloud/Models/CloudClusterPrinterStatus.py | 60 +++++++++++++ .../src/Cloud/Models/CloudClusterResponse.py | 32 +++++++ .../src/Cloud/Models/CloudClusterStatus.py | 34 ++++---- .../src/Cloud/Models/CloudErrorObject.py | 29 +++++-- .../src/Cloud/Models/CloudJobResponse.py | 16 ---- .../src/Cloud/Models/CloudJobUploadRequest.py | 12 --- .../src/Cloud/Models/CloudPrintJobResponse.py | 33 +++++++ .../Models/CloudPrintJobUploadRequest.py | 17 ++++ .../src/Cloud/Models/CloudPrintResponse.py | 19 +++- .../Cloud/Fixtures/postJobPrintResponse.json | 3 +- .../Cloud/Fixtures/putJobUploadResponse.json | 3 +- .../Cloud/Fixtures/requestPrintResponse.json | 7 -- .../Cloud/Fixtures/requestUploadResponse.json | 11 --- .../tests/Cloud/TestCloudApiClient.py | 32 ++++--- .../Cloud/TestCloudOutputDeviceManager.py | 4 +- 26 files changed, 411 insertions(+), 282 deletions(-) delete mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Models/CloudCluster.py delete mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobStatus.py delete mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinter.py create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterStatus.py create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterResponse.py delete mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Models/CloudJobResponse.py delete mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Models/CloudJobUploadRequest.py create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Models/CloudPrintJobResponse.py create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Models/CloudPrintJobUploadRequest.py delete mode 100644 plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/requestPrintResponse.json delete mode 100644 plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/requestUploadResponse.json diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py index 448aa4d2e7..9cc70587a3 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py @@ -10,12 +10,12 @@ from UM.Logger import Logger from cura.API import Account from cura.NetworkClient import NetworkClient from ..Models import BaseModel -from .Models.CloudCluster import CloudCluster +from .Models.CloudClusterResponse import CloudClusterResponse from .Models.CloudErrorObject import CloudErrorObject from .Models.CloudClusterStatus import CloudClusterStatus -from .Models.CloudJobUploadRequest import CloudJobUploadRequest +from .Models.CloudPrintJobUploadRequest import CloudPrintJobUploadRequest from .Models.CloudPrintResponse import CloudPrintResponse -from .Models.CloudJobResponse import CloudJobResponse +from .Models.CloudPrintJobResponse import CloudPrintJobResponse ## The cloud API client is responsible for handling the requests and responses from the cloud. @@ -43,9 +43,9 @@ class CloudApiClient(NetworkClient): ## Retrieves all the clusters for the user that is currently logged in. # \param on_finished: The function to be called after the result is parsed. - def getClusters(self, on_finished: Callable[[List[CloudCluster]], any]) -> None: + def getClusters(self, on_finished: Callable[[List[CloudClusterResponse]], any]) -> None: url = "{}/clusters".format(self.CLUSTER_API_ROOT) - self.get(url, on_finished=self._wrapCallback(on_finished, CloudCluster)) + self.get(url, on_finished=self._wrapCallback(on_finished, CloudClusterResponse)) ## Retrieves the status of the given cluster. # \param cluster_id: The ID of the cluster. @@ -57,10 +57,11 @@ class CloudApiClient(NetworkClient): ## Requests the cloud to register the upload of a print job mesh. # \param request: The request object. # \param on_finished: The function to be called after the result is parsed. - def requestUpload(self, request: CloudJobUploadRequest, on_finished: Callable[[CloudJobResponse], any]) -> None: + def requestUpload(self, request: CloudPrintJobUploadRequest, on_finished: Callable[[CloudPrintJobResponse], any] + ) -> None: url = "{}/jobs/upload".format(self.CURA_API_ROOT) - body = json.dumps({"data": request.__dict__}) - self.put(url, body, on_finished=self._wrapCallback(on_finished, CloudJobResponse)) + body = json.dumps({"data": request.toDict()}) + self.put(url, body, on_finished=self._wrapCallback(on_finished, CloudPrintJobResponse)) ## Requests the cloud to register the upload of a print job mesh. # \param upload_response: The object received after requesting an upload with `self.requestUpload`. @@ -68,7 +69,7 @@ class CloudApiClient(NetworkClient): # \param on_finished: The function to be called after the result is parsed. It receives the print job ID. # \param on_progress: A function to be called during upload progress. It receives a percentage (0-100). # \param on_error: A function to be called if the upload fails. It receives a dict with the error. - def uploadMesh(self, upload_response: CloudJobResponse, mesh: bytes, on_finished: Callable[[str], any], + def uploadMesh(self, upload_response: CloudPrintJobResponse, mesh: bytes, on_finished: Callable[[str], any], on_progress: Callable[[int], any], on_error: Callable[[dict], any]): def progressCallback(bytes_sent: int, bytes_total: int) -> None: @@ -126,13 +127,13 @@ class CloudApiClient(NetworkClient): ## Parses the given models and calls the correct callback depending on the result. # \param response: The response from the server, after being converted to a dict. # \param on_finished: The callback in case the response is successful. - # \param model: The type of the model to convert the response to. It may either be a single record or a list. + # \param model_class: The type of the model to convert the response to. It may either be a single record or a list. def _parseModels(self, response: Dict[str, any], on_finished: Callable[[Union[Model, List[Model]]], any], - model: Type[Model]) -> None: + model_class: Type[Model]) -> None: if "data" in response: data = response["data"] - result = [model(**c) for c in data] if isinstance(data, list) else model(**data) + result = [model_class(**c) for c in data] if isinstance(data, list) else model_class(**data) on_finished(result) elif "errors" in response: self._on_error([CloudErrorObject(**error) for error in response["errors"]]) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 17acbe2e3f..15eeed108d 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -20,11 +20,11 @@ from ..MeshFormatHandler import MeshFormatHandler from ..UM3PrintJobOutputModel import UM3PrintJobOutputModel from .CloudApiClient import CloudApiClient from .Models.CloudClusterStatus import CloudClusterStatus -from .Models.CloudJobUploadRequest import CloudJobUploadRequest +from .Models.CloudPrintJobUploadRequest import CloudPrintJobUploadRequest from .Models.CloudPrintResponse import CloudPrintResponse -from .Models.CloudJobResponse import CloudJobResponse -from .Models.CloudClusterPrinter import CloudClusterPrinter -from .Models.CloudClusterPrintJob import CloudClusterPrintJob +from .Models.CloudPrintJobResponse import CloudPrintJobResponse +from .Models.CloudClusterPrinterStatus import CloudClusterPrinterStatus +from .Models.CloudClusterPrintJobStatus import CloudClusterPrintJobStatus from .Utils import findChanges, formatDateCompleted, formatTimeCompleted @@ -116,8 +116,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self._progress_message = None # type: Optional[Message] # Keep server string of the last generated time to avoid updating models more than once for the same response - self._received_printers = None # type: Optional[List[CloudClusterPrinter]] - self._received_print_jobs = None # type: Optional[List[CloudClusterPrintJob]] + self._received_printers = None # type: Optional[List[CloudClusterPrinterStatus]] + self._received_print_jobs = None # type: Optional[List[CloudClusterPrintJobStatus]] # A set of the user's job IDs that have finished self._finished_jobs = set() # type: Set[str] @@ -166,7 +166,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): mesh_bytes = mesh_format.getBytes(nodes) - request = CloudJobUploadRequest( + request = CloudPrintJobUploadRequest( job_name = file_name, file_size = len(mesh_bytes), content_type = mesh_format.mime_type, @@ -199,9 +199,9 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Updates the local list of printers with the list received from the cloud. # \param jobs: The printers received from the cloud. - def _updatePrinters(self, printers: List[CloudClusterPrinter]) -> None: + def _updatePrinters(self, printers: List[CloudClusterPrinterStatus]) -> None: previous = {p.key: p for p in self._printers} # type: Dict[str, PrinterOutputModel] - received = {p.uuid: p for p in printers} # type: Dict[str, CloudClusterPrinter] + received = {p.uuid: p for p in printers} # type: Dict[str, CloudClusterPrinterStatus] removed_printers, added_printers, updated_printers = findChanges(previous, received) @@ -224,8 +224,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Updates the local list of print jobs with the list received from the cloud. # \param jobs: The print jobs received from the cloud. - def _updatePrintJobs(self, jobs: List[CloudClusterPrintJob]) -> None: - received = {j.uuid: j for j in jobs} # type: Dict[str, CloudClusterPrintJob] + def _updatePrintJobs(self, jobs: List[CloudClusterPrintJobStatus]) -> None: + received = {j.uuid: j for j in jobs} # type: Dict[str, CloudClusterPrintJobStatus] previous = {j.key: j for j in self._print_jobs} # type: Dict[str, UM3PrintJobOutputModel] removed_jobs, added_jobs, updated_jobs = findChanges(previous, received) @@ -248,7 +248,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Registers a new print job received via the cloud API. # \param job: The print job received. - def _addPrintJob(self, job: CloudClusterPrintJob) -> None: + def _addPrintJob(self, job: CloudClusterPrintJobStatus) -> None: model = job.createOutputModel(CloudOutputController(self)) model.stateChanged.connect(self._onPrintJobStateChanged) if job.printer_uuid: @@ -284,7 +284,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Uploads the mesh when the print job was registered with the cloud API. # \param mesh: The bytes to upload. # \param job_response: The response received from the cloud API. - def _onPrintJobCreated(self, mesh: bytes, job_response: CloudJobResponse) -> None: + def _onPrintJobCreated(self, mesh: bytes, job_response: CloudPrintJobResponse) -> None: self._api.uploadMesh(job_response, mesh, self._onPrintJobUploaded, self._updateUploadProgress, lambda _: self._onUploadError(T.UPLOAD_ERROR)) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 961f8d696d..c9b30d7c79 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -12,7 +12,7 @@ from cura.CuraApplication import CuraApplication from cura.Settings.GlobalStack import GlobalStack from .CloudApiClient import CloudApiClient from .CloudOutputDevice import CloudOutputDevice -from .Models.CloudCluster import CloudCluster +from .Models.CloudClusterResponse import CloudClusterResponse from .Models.CloudErrorObject import CloudErrorObject from .Utils import findChanges @@ -72,8 +72,8 @@ class CloudOutputDeviceManager: self._api.getClusters(self._onGetRemoteClustersFinished) ## Callback for when the request for getting the clusters. is finished. - def _onGetRemoteClustersFinished(self, clusters: List[CloudCluster]) -> None: - online_clusters = {c.cluster_id: c for c in clusters if c.is_online} # type: Dict[str, CloudCluster] + def _onGetRemoteClustersFinished(self, clusters: List[CloudClusterResponse]) -> None: + online_clusters = {c.cluster_id: c for c in clusters if c.is_online} # type: Dict[str, CloudClusterResponse] removed_devices, added_clusters, updates = findChanges(self._remote_clusters, online_clusters) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/BaseCloudModel.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/BaseCloudModel.py index 1176c4374a..3a0e93e836 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/BaseCloudModel.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/BaseCloudModel.py @@ -1,17 +1,55 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. from datetime import datetime, timezone +from typing import Dict, Union, TypeVar, Type, List from ...Models import BaseModel +## Base class for the models used in the interface with the Ultimaker cloud APIs. class BaseCloudModel(BaseModel): + ## Checks whether the two models are equal. + # \param other: The other model. + # \return True if they are equal, False if they are different. def __eq__(self, other): - return type(self) == type(other) and self.__dict__ == other.__dict__ + return type(self) == type(other) and self.toDict() == other.toDict() - def __ne__(self, other): - return type(self) != type(other) or self.__dict__ != other.__dict__ + ## Checks whether the two models are different. + # \param other: The other model. + # \return True if they are different, False if they are the same. + def __ne__(self, other) -> bool: + return type(self) != type(other) or self.toDict() != other.toDict() + ## Converts the model into a serializable dictionary + def toDict(self) -> Dict[str, any]: + return self.__dict__ + + # Type variable used in the parse methods below, which should be a subclass of BaseModel. + T = TypeVar("T", bound=BaseModel) + + ## Parses a single model. + # \param model_class: The model class. + # \param values: The value of the model, which is usually a dictionary, but may also be already parsed. + # \return An instance of the model_class given. @staticmethod - def parseDate(date_str: str) -> datetime: - return datetime.strptime(date_str, "%Y-%m-%dT%H:%M:%S.%fZ").replace(tzinfo=timezone.utc) + def parseModel(model_class: Type[T], values: Union[T, Dict[str, any]]) -> T: + if isinstance(values, dict): + return model_class(**values) + return values + + ## Parses a list of models. + # \param model_class: The model class. + # \param values: The value of the list. Each value is usually a dictionary, but may also be already parsed. + # \return A list of instances of the model_class given. + @classmethod + def parseModels(cls, model_class: Type[T], values: List[Union[T, Dict[str, any]]]) -> List[T]: + return [cls.parseModel(model_class, value) for value in values] + + ## Parses the given date string. + # \param date: The date to parse. + # \return The parsed date. + @staticmethod + def parseDate(date: Union[str, datetime]) -> datetime: + if isinstance(date, datetime): + return date + return datetime.strptime(date, "%Y-%m-%dT%H:%M:%S.%fZ").replace(tzinfo=timezone.utc) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudCluster.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudCluster.py deleted file mode 100644 index e6e2af1466..0000000000 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudCluster.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2018 Ultimaker B.V. -# Cura is released under the terms of the LGPLv3 or higher. -from .BaseCloudModel import BaseCloudModel - - -## Class representing a cloud connected cluster. -class CloudCluster(BaseCloudModel): - def __init__(self, **kwargs): - self.cluster_id = None # type: str - self.host_guid = None # type: str - self.host_name = None # type: str - self.host_version = None # type: str - self.status = None # type: str - self.is_online = False # type: bool - super().__init__(**kwargs) - - # Validates the model, raising an exception if the model is invalid. - def validate(self) -> None: - super().validate() - if not self.cluster_id: - raise ValueError("cluster_id is required on CloudCluster") diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py deleted file mode 100644 index 15d256e7d5..0000000000 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJob.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) 2018 Ultimaker B.V. -# Cura is released under the terms of the LGPLv3 or higher. -from typing import List - -from cura.PrinterOutput.ConfigurationModel import ConfigurationModel -from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputController import CloudOutputController -from .CloudClusterPrinterConfiguration import CloudClusterPrinterConfiguration -from .CloudClusterPrintJobConstraint import CloudClusterPrintJobConstraint -from .BaseCloudModel import BaseCloudModel - - -## Class representing a print job -from plugins.UM3NetworkPrinting.src.UM3PrintJobOutputModel import UM3PrintJobOutputModel - - -class CloudClusterPrintJob(BaseCloudModel): - def __init__(self, **kwargs) -> None: - self.assigned_to = None # type: str - self.configuration = [] # type: List[CloudClusterPrinterConfiguration] - self.constraints = [] # type: List[CloudClusterPrintJobConstraint] - self.created_at = None # type: str - self.force = None # type: str - self.last_seen = None # type: str - self.machine_variant = None # type: str - self.name = None # type: str - self.network_error_count = None # type: int - self.owner = None # type: str - self.printer_uuid = None # type: str - self.started = None # type: str - self.status = None # type: str - self.time_elapsed = None # type: str - self.time_total = None # type: str - self.uuid = None # type: str - super().__init__(**kwargs) - self.configuration = [CloudClusterPrinterConfiguration(**c) if isinstance(c, dict) else c - for c in self.configuration] - self.constraints = [CloudClusterPrintJobConstraint(**p) if isinstance(p, dict) else p - for p in self.constraints] - - ## Creates an UM3 print job output model based on this cloud cluster print job. - # \param printer: The output model of the printer - def createOutputModel(self, controller: CloudOutputController) -> UM3PrintJobOutputModel: - model = UM3PrintJobOutputModel(controller, self.uuid, self.name) - self.updateOutputModel(model) - - return model - - ## Creates a new configuration model - def _createConfigurationModel(self) -> ConfigurationModel: - extruders = [extruder.createConfigurationModel() for extruder in self.configuration or ()] - configuration = ConfigurationModel() - configuration.setExtruderConfigurations(extruders) - return configuration - - ## Updates an UM3 print job output model based on this cloud cluster print job. - # \param model: The model to update. - def updateOutputModel(self, model: UM3PrintJobOutputModel) -> None: - # TODO: Add `compatible_machine_families` to the cloud, than add model.setCompatibleMachineFamilies() - # TODO: Add `impediments_to_printing` to the cloud, see ClusterUM3OutputDevice._updatePrintJob - # TODO: Use model.updateConfigurationChanges, see ClusterUM3OutputDevice#_createConfigurationChanges - model.updateConfiguration(self._createConfigurationModel()) - model.updateTimeTotal(self.time_total) - model.updateTimeElapsed(self.time_elapsed) - model.updateOwner(self.owner) - model.updateState(self.status) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobConstraint.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobConstraint.py index f13e3098fc..8236ec06b9 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobConstraint.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobConstraint.py @@ -1,10 +1,16 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from typing import Optional + from .BaseCloudModel import BaseCloudModel ## Class representing a cloud cluster print job constraint -class CloudClusterPrintJobConstraint(BaseCloudModel): - def __init__(self, **kwargs) -> None: - self.require_printer_name = None # type: str +# Spec: https://api-staging.ultimaker.com/connect/v1/spec +class CloudClusterPrintJobConstraints(BaseCloudModel): + ## Creates a new print job constraint. + # \param require_printer_name: Unique name of the printer that this job should be printed on. + # Should be one of the unique_name field values in the cluster, e.g. 'ultimakersystem-ccbdd30044ec' + def __init__(self, require_printer_name: Optional[str] = None, **kwargs) -> None: + self.require_printer_name = require_printer_name super().__init__(**kwargs) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobStatus.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobStatus.py new file mode 100644 index 0000000000..24ef9078d6 --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobStatus.py @@ -0,0 +1,87 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from typing import List, Optional, Union, Dict + +from cura.PrinterOutput.ConfigurationModel import ConfigurationModel +from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputController import CloudOutputController +from .CloudClusterPrinterConfiguration import CloudClusterPrinterConfiguration +from .CloudClusterPrintJobConstraint import CloudClusterPrintJobConstraints +from .BaseCloudModel import BaseCloudModel + + +## Class representing a print job +from plugins.UM3NetworkPrinting.src.UM3PrintJobOutputModel import UM3PrintJobOutputModel + + +## Model for the status of a single print job in a cluster. +# Spec: https://api-staging.ultimaker.com/connect/v1/spec +class CloudClusterPrintJobStatus(BaseCloudModel): + ## Creates a new cloud print job status model. + # \param assigned_to: The name of the printer this job is assigned to while being queued. + # \param configuration: The required print core configurations of this print job. + # \param constraints: Print job constraints object. + # \param created_at: The timestamp when the job was created in Cura Connect. + # \param force: Allow this job to be printed despite of mismatching configurations. + # \param last_seen: The number of seconds since this job was checked. + # \param machine_variant: The machine type that this job should be printed on.Coincides with the machine_type field + # of the printer object. + # \param name: The name of the print job. Usually the name of the .gcode file. + # \param network_error_count: The number of errors encountered when requesting data for this print job. + # \param owner: The name of the user who added the print job to Cura Connect. + # \param printer_uuid: UUID of the printer that the job is currently printing on or assigned to. + # \param started: Whether the job has started printing or not. + # \param status: The status of the print job. + # \param time_elapsed: The remaining printing time in seconds. + # \param time_total: The total printing time in seconds. + # \param uuid: UUID of this print job. Should be used for identification purposes. + def __init__(self, created_at: str, force: bool, machine_variant: str, name: str, started: bool, status: str, + time_total: int, uuid: str, + configuration: List[Union[Dict[str, any], CloudClusterPrinterConfiguration]], + constraints: List[Union[Dict[str, any], CloudClusterPrintJobConstraints]], + last_seen: Optional[float] = None, network_error_count: Optional[int] = None, + owner: Optional[str] = None, printer_uuid: Optional[str] = None, time_elapsed: Optional[int] = None, + assigned_to: Optional[str] = None, **kwargs) -> None: + self.assigned_to = assigned_to # type: str + self.configuration = self.parseModels(CloudClusterPrinterConfiguration, configuration) + self.constraints = self.parseModels(CloudClusterPrintJobConstraints, constraints) + self.created_at = created_at + self.force = force + self.last_seen = last_seen + self.machine_variant = machine_variant + self.name = name + self.network_error_count = network_error_count + self.owner = owner + self.printer_uuid = printer_uuid + self.started = started + self.status = status + self.time_elapsed = time_elapsed + self.time_total = time_total + self.uuid = uuid + super().__init__(**kwargs) + + ## Creates an UM3 print job output model based on this cloud cluster print job. + # \param printer: The output model of the printer + def createOutputModel(self, controller: CloudOutputController) -> UM3PrintJobOutputModel: + model = UM3PrintJobOutputModel(controller, self.uuid, self.name) + self.updateOutputModel(model) + + return model + + ## Creates a new configuration model + def _createConfigurationModel(self) -> ConfigurationModel: + extruders = [extruder.createConfigurationModel() for extruder in self.configuration or ()] + configuration = ConfigurationModel() + configuration.setExtruderConfigurations(extruders) + return configuration + + ## Updates an UM3 print job output model based on this cloud cluster print job. + # \param model: The model to update. + def updateOutputModel(self, model: UM3PrintJobOutputModel) -> None: + # TODO: Add `compatible_machine_families` to the cloud, than add model.setCompatibleMachineFamilies() + # TODO: Add `impediments_to_printing` to the cloud, see ClusterUM3OutputDevice._updatePrintJob + # TODO: Use model.updateConfigurationChanges, see ClusterUM3OutputDevice#_createConfigurationChanges + model.updateConfiguration(self._createConfigurationModel()) + model.updateTimeTotal(self.time_total) + model.updateTimeElapsed(self.time_elapsed) + model.updateOwner(self.owner) + model.updateState(self.status) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinter.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinter.py deleted file mode 100644 index 9057743621..0000000000 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinter.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) 2018 Ultimaker B.V. -# Cura is released under the terms of the LGPLv3 or higher. -from typing import List - -from cura.PrinterOutput.ConfigurationModel import ConfigurationModel -from cura.PrinterOutput.PrinterOutputController import PrinterOutputController -from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel -from .CloudClusterPrinterConfiguration import CloudClusterPrinterConfiguration -from .BaseCloudModel import BaseCloudModel - - -## Class representing a cluster printer -class CloudClusterPrinter(BaseCloudModel): - def __init__(self, **kwargs) -> None: - self.configuration = [] # type: List[CloudClusterPrinterConfiguration] - self.enabled = None # type: str - self.firmware_version = None # type: str - self.friendly_name = None # type: str - self.ip_address = None # type: str - self.machine_variant = None # type: str - self.status = None # type: str - self.unique_name = None # type: str - self.uuid = None # type: str - super().__init__(**kwargs) - - self.configuration = [CloudClusterPrinterConfiguration(**c) - if isinstance(c, dict) else c for c in self.configuration] - - ## Creates a new output model. - # \param controller - The controller of the model. - def createOutputModel(self, controller: PrinterOutputController) -> PrinterOutputModel: - model = PrinterOutputModel(controller, len(self.configuration), firmware_version = self.firmware_version) - self.updateOutputModel(model) - return model - - ## Updates the given output model. - # \param model - The output model to update. - def updateOutputModel(self, model: PrinterOutputModel) -> None: - model.updateKey(self.uuid) - model.updateName(self.friendly_name) - model.updateType(self.machine_variant) - model.updateState(self.status if self.enabled else "disabled") - - for configuration, extruder_output, extruder_config in \ - zip(self.configuration, model.extruders, model.printerConfiguration.extruderConfigurations): - configuration.updateOutputModel(extruder_output) - configuration.updateConfigurationModel(extruder_config) - - pass diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfiguration.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfiguration.py index c14a7f85c3..a6319ed6bb 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfiguration.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfiguration.py @@ -1,5 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from typing import Union, Dict, Optional + from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationModel from cura.PrinterOutput.ExtruderOutputModel import ExtruderOutputModel from .CloudClusterPrinterConfigurationMaterial import CloudClusterPrinterConfigurationMaterial @@ -7,17 +9,22 @@ from .BaseCloudModel import BaseCloudModel ## Class representing a cloud cluster printer configuration +# Spec: https://api-staging.ultimaker.com/connect/v1/spec class CloudClusterPrinterConfiguration(BaseCloudModel): - def __init__(self, **kwargs) -> None: - self.extruder_index = None # type: int - self.material = None # type: CloudClusterPrinterConfigurationMaterial - self.nozzle_diameter = None # type: str - self.print_core_id = None # type: str + ## Creates a new cloud cluster printer configuration object + # \param extruder_index: The position of the extruder on the machine as list index. Numbered from left to right. + # \param material: The material of a configuration object in a cluster printer. May be in a dict or an object. + # \param nozzle_diameter: The diameter of the print core at this position in millimeters, e.g. '0.4'. + # \param print_core_id: The type of print core inserted at this position, e.g. 'AA 0.4'. + def __init__(self, extruder_index: int, + material: Union[None, Dict[str, any], CloudClusterPrinterConfigurationMaterial], + nozzle_diameter: Optional[str] = None, print_core_id: Optional[str] = None, **kwargs) -> None: + self.extruder_index = extruder_index + self.material = self.parseModel(CloudClusterPrinterConfigurationMaterial, material) + self.nozzle_diameter = nozzle_diameter + self.print_core_id = print_core_id super().__init__(**kwargs) - if isinstance(self.material, dict): - self.material = CloudClusterPrinterConfigurationMaterial(**self.material) - ## Updates the given output model. # \param model - The output model to update. def updateOutputModel(self, model: ExtruderOutputModel) -> None: diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfigurationMaterial.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfigurationMaterial.py index e5f52ac630..652cbdabda 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfigurationMaterial.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfigurationMaterial.py @@ -1,3 +1,5 @@ +from typing import Optional + from UM.Logger import Logger from cura.CuraApplication import CuraApplication from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel @@ -5,12 +7,19 @@ from .BaseCloudModel import BaseCloudModel ## Class representing a cloud cluster printer configuration +# Spec: https://api-staging.ultimaker.com/connect/v1/spec class CloudClusterPrinterConfigurationMaterial(BaseCloudModel): - def __init__(self, **kwargs) -> None: - self.guid = None # type: str - self.brand = None # type: str - self.color = None # type: str - self.material = None # type: str + ## Creates a new material configuration model. + # \param brand: The brand of material in this print core, e.g. 'Ultimaker'. + # \param color: The color of material in this print core, e.g. 'Blue'. + # \param guid: he GUID of the material in this print core, e.g. '506c9f0d-e3aa-4bd4-b2d2-23e2425b1aa9'. + # \param material: The type of material in this print core, e.g. 'PLA'. + def __init__(self, brand: Optional[str] = None, color: Optional[str] = None, guid: Optional[str] = None, + material: Optional[str] = None, **kwargs) -> None: + self.guid = guid + self.brand = brand + self.color = color + self.material = material super().__init__(**kwargs) ## Creates a material output model based on this cloud printer material. diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterStatus.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterStatus.py new file mode 100644 index 0000000000..b25f21fde2 --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterStatus.py @@ -0,0 +1,60 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from typing import List, Union, Dict, Optional + +from cura.PrinterOutput.PrinterOutputController import PrinterOutputController +from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel +from .CloudClusterPrinterConfiguration import CloudClusterPrinterConfiguration +from .BaseCloudModel import BaseCloudModel + + +## Class representing a cluster printer +# Spec: https://api-staging.ultimaker.com/connect/v1/spec +class CloudClusterPrinterStatus(BaseCloudModel): + ## Creates a new cluster printer status + # \param enabled: A printer can be disabled if it should not receive new jobs. By default every printer is enabled. + # \param firmware_version: Firmware version installed on the printer. Can differ for each printer in a cluster. + # \param friendly_name: Human readable name of the printer. Can be used for identification purposes. + # \param ip_address: The IP address of the printer in the local network. + # \param machine_variant: The type of printer. Can be 'Ultimaker 3' or 'Ultimaker 3ext'. + # \param status: The status of the printer. + # \param unique_name: The unique name of the printer in the network. + # \param uuid: The unique ID of the printer, also known as GUID. + # \param configuration: The active print core configurations of this printer. + # \param reserved_by: A printer can be claimed by a specific print job. + def __init__(self, enabled: bool, firmware_version: str, friendly_name: str, ip_address: str, machine_variant: str, + status: str, unique_name: str, uuid: str, + configuration: List[Union[Dict[str, any], CloudClusterPrinterConfiguration]], + reserved_by: Optional[str] = None, **kwargs) -> None: + + self.configuration = self.parseModels(CloudClusterPrinterConfiguration, configuration) + self.enabled = enabled + self.firmware_version = firmware_version + self.friendly_name = friendly_name + self.ip_address = ip_address + self.machine_variant = machine_variant + self.status = status + self.unique_name = unique_name + self.uuid = uuid + self.reserved_by = reserved_by + super().__init__(**kwargs) + + ## Creates a new output model. + # \param controller - The controller of the model. + def createOutputModel(self, controller: PrinterOutputController) -> PrinterOutputModel: + model = PrinterOutputModel(controller, len(self.configuration), firmware_version = self.firmware_version) + self.updateOutputModel(model) + return model + + ## Updates the given output model. + # \param model - The output model to update. + def updateOutputModel(self, model: PrinterOutputModel) -> None: + model.updateKey(self.uuid) + model.updateName(self.friendly_name) + model.updateType(self.machine_variant) + model.updateState(self.status if self.enabled else "disabled") + + for configuration, extruder_output, extruder_config in \ + zip(self.configuration, model.extruders, model.printerConfiguration.extruderConfigurations): + configuration.updateOutputModel(extruder_output) + configuration.updateConfigurationModel(extruder_config) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterResponse.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterResponse.py new file mode 100644 index 0000000000..a3eda54a76 --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterResponse.py @@ -0,0 +1,32 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from typing import Optional + +from .BaseCloudModel import BaseCloudModel + + +## Class representing a cloud connected cluster. +# Spec: https://api-staging.ultimaker.com/connect/v1/spec +class CloudClusterResponse(BaseCloudModel): + ## Creates a new cluster response object. + # \param cluster_id: The secret unique ID, e.g. 'kBEeZWEifXbrXviO8mRYLx45P8k5lHVGs43XKvRniPg='. + # \param host_guid: The unique identifier of the print cluster host, e.g. 'e90ae0ac-1257-4403-91ee-a44c9b7e8050'. + # \param host_name: The name of the printer as configured during the Wi-Fi setup. Used as identifier for end users. + # \param is_online: Whether this cluster is currently connected to the cloud. + # \param status: The status of the cluster authentication (active or inactive). + # \param host_version: The firmware version of the cluster host. This is where the Stardust client is running on. + def __init__(self, cluster_id: str, host_guid: str, host_name: str, is_online: bool, status: str, + host_version: Optional[str] = None, **kwargs): + self.cluster_id = cluster_id + self.host_guid = host_guid + self.host_name = host_name + self.status = status + self.is_online = is_online + self.host_version = host_version + super().__init__(**kwargs) + + # Validates the model, raising an exception if the model is invalid. + def validate(self) -> None: + super().validate() + if not self.cluster_id: + raise ValueError("cluster_id is required on CloudCluster") diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterStatus.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterStatus.py index 77ed979dbc..2cebb1b592 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterStatus.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterStatus.py @@ -1,28 +1,26 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. from datetime import datetime -from typing import List +from typing import List, Dict, Union -from .CloudClusterPrinter import CloudClusterPrinter -from .CloudClusterPrintJob import CloudClusterPrintJob +from .CloudClusterPrinterStatus import CloudClusterPrinterStatus +from .CloudClusterPrintJobStatus import CloudClusterPrintJobStatus from .BaseCloudModel import BaseCloudModel # Model that represents the status of the cluster for the cloud +# Spec: https://api-staging.ultimaker.com/connect/v1/spec class CloudClusterStatus(BaseCloudModel): - def __init__(self, **kwargs) -> None: - self.generated_time = None # type: datetime - # a list of the printers - self.printers = [] # type: List[CloudClusterPrinter] - # a list of the print jobs - self.print_jobs = [] # type: List[CloudClusterPrintJob] - + ## Creates a new cluster status model object. + # \param printers: The latest status of each printer in the cluster. + # \param print_jobs: The latest status of each print job in the cluster. + # \param generated_time: The datetime when the object was generated on the server-side. + def __init__(self, + printers: List[Union[CloudClusterPrinterStatus, Dict[str, any]]], + print_jobs: List[Union[CloudClusterPrintJobStatus, Dict[str, any]]], + generated_time: Union[str, datetime], + **kwargs) -> None: + self.generated_time = self.parseDate(generated_time) + self.printers = self.parseModels(CloudClusterPrinterStatus, printers) + self.print_jobs = self.parseModels(CloudClusterPrintJobStatus, print_jobs) super().__init__(**kwargs) - - # converting any dictionaries into models - self.printers = [CloudClusterPrinter(**p) if isinstance(p, dict) else p for p in self.printers] - self.print_jobs = [CloudClusterPrintJob(**j) if isinstance(j, dict) else j for j in self.print_jobs] - - # converting generated time into datetime - if isinstance(self.generated_time, str): - self.generated_time = self.parseDate(self.generated_time) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudErrorObject.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudErrorObject.py index 9696cbcb7a..c02a21d4da 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudErrorObject.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudErrorObject.py @@ -1,17 +1,28 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import Dict +from typing import Dict, Optional from .BaseCloudModel import BaseCloudModel -## Class representing errors generated by the cloud servers, according to the json-api standard. +## Class representing errors generated by the cloud servers, according to the JSON-API standard. +# Spec: https://api-staging.ultimaker.com/connect/v1/spec class CloudErrorObject(BaseCloudModel): - def __init__(self, **kwargs) -> None: - self.id = None # type: str - self.code = None # type: str - self.http_status = None # type: str - self.title = None # type: str - self.detail = None # type: str - self.meta = None # type: Dict[str, any] + ## Creates a new error object. + # \param id: Unique identifier for this particular occurrence of the problem. + # \param title: A short, human-readable summary of the problem that SHOULD NOT change from occurrence to occurrence + # of the problem, except for purposes of localization. + # \param code: An application-specific error code, expressed as a string value. + # \param detail: A human-readable explanation specific to this occurrence of the problem. Like title, this field's + # value can be localized. + # \param http_status: The HTTP status code applicable to this problem, converted to string. + # \param meta: Non-standard meta-information about the error, depending on the error code. + def __init__(self, id: str, code: str, title: str, http_status: str, detail: Optional[str] = None, + meta: Optional[Dict[str, any]] = None, **kwargs) -> None: + self.id = id + self.code = code + self.http_status = http_status + self.title = title + self.detail = detail + self.meta = meta super().__init__(**kwargs) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudJobResponse.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudJobResponse.py deleted file mode 100644 index e3161449a5..0000000000 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudJobResponse.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) 2018 Ultimaker B.V. -# Cura is released under the terms of the LGPLv3 or higher. -from .BaseCloudModel import BaseCloudModel - - -# Model that represents the response received from the cloud after requesting to upload a print job -class CloudJobResponse(BaseCloudModel): - def __init__(self, **kwargs) -> None: - self.download_url = None # type: str - self.job_id = None # type: str - self.job_name = None # type: str - self.slicing_details = None # type: str - self.status = None # type: str - self.upload_url = None # type: str - self.content_type = None # type: str - super().__init__(**kwargs) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudJobUploadRequest.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudJobUploadRequest.py deleted file mode 100644 index 07a781e2d6..0000000000 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudJobUploadRequest.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2018 Ultimaker B.V. -# Cura is released under the terms of the LGPLv3 or higher. -from .BaseCloudModel import BaseCloudModel - - -# Model that represents the request to upload a print job to the cloud -class CloudJobUploadRequest(BaseCloudModel): - def __init__(self, **kwargs) -> None: - self.file_size = None # type: int - self.job_name = None # type: str - self.content_type = None # type: str - super().__init__(**kwargs) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudPrintJobResponse.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudPrintJobResponse.py new file mode 100644 index 0000000000..79196ee38c --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudPrintJobResponse.py @@ -0,0 +1,33 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from typing import Optional + +from .BaseCloudModel import BaseCloudModel + + +# Model that represents the response received from the cloud after requesting to upload a print job +# Spec: https://api-staging.ultimaker.com/cura/v1/spec +class CloudPrintJobResponse(BaseCloudModel): + ## Creates a new print job response model. + # \param job_id: The job unique ID, e.g. 'kBEeZWEifXbrXviO8mRYLx45P8k5lHVGs43XKvRniPg='. + # \param status: The status of the print job. + # \param status_description: Contains more details about the status, e.g. the cause of failures. + # \param download_url: A signed URL to download the resulting status. Only available when the job is finished. + # \param job_name: The name of the print job. + # \param slicing_details: Model for slice information. + # \param upload_url: The one-time use URL where the toolpath must be uploaded to (only if status is uploading). + # \param content_type: The content type of the print job (e.g. text/plain or application/gzip) + # \param generated_time: The datetime when the object was generated on the server-side. + def __init__(self, job_id: str, status: str, download_url: Optional[str] = None, job_name: Optional[str] = None, + upload_url: Optional[str] = None, content_type: Optional[str] = None, + status_description: Optional[str] = None, slicing_details: Optional[dict] = None, **kwargs) -> None: + self.job_id = job_id + self.status = status + self.download_url = download_url + self.job_name = job_name + self.upload_url = upload_url + self.content_type = content_type + self.status_description = status_description + # TODO: Implement slicing details + self.slicing_details = slicing_details + super().__init__(**kwargs) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudPrintJobUploadRequest.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudPrintJobUploadRequest.py new file mode 100644 index 0000000000..e59c571558 --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudPrintJobUploadRequest.py @@ -0,0 +1,17 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from .BaseCloudModel import BaseCloudModel + + +# Model that represents the request to upload a print job to the cloud +# Spec: https://api-staging.ultimaker.com/cura/v1/spec +class CloudPrintJobUploadRequest(BaseCloudModel): + ## Creates a new print job upload request. + # \param job_name: The name of the print job. + # \param file_size: The size of the file in bytes. + # \param content_type: The content type of the print job (e.g. text/plain or application/gzip) + def __init__(self, job_name: str, file_size: int, content_type: str, **kwargs) -> None: + self.job_name = job_name + self.file_size = file_size + self.content_type = content_type + super().__init__(**kwargs) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudPrintResponse.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudPrintResponse.py index 3e9ad584dc..919d1b3c3a 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudPrintResponse.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudPrintResponse.py @@ -1,12 +1,23 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from datetime import datetime +from typing import Optional, Union + from .BaseCloudModel import BaseCloudModel # Model that represents the responses received from the cloud after requesting a job to be printed. +# Spec: https://api-staging.ultimaker.com/connect/v1/spec class CloudPrintResponse(BaseCloudModel): - def __init__(self, **kwargs) -> None: - self.cluster_job_id = None # type: str - self.job_id = None # type: str - self.status = None # type: str + ## Creates a new print response object. + # \param job_id: The unique ID of a print job inside of the cluster. This ID is generated by Cura Connect. + # \param status: The status of the print request (queued or failed). + # \param generated_time: The datetime when the object was generated on the server-side. + # \param cluster_job_id: The unique ID of a print job inside of the cluster. This ID is generated by Cura Connect. + def __init__(self, job_id: str, status: str, generated_time: Union[str, datetime], + cluster_job_id: Optional[str] = None, **kwargs) -> None: + self.job_id = job_id + self.status = status + self.cluster_job_id = cluster_job_id + self.generated_time = self.parseDate(generated_time) super().__init__(**kwargs) diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/postJobPrintResponse.json b/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/postJobPrintResponse.json index 8b9574359f..caedcd8732 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/postJobPrintResponse.json +++ b/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/postJobPrintResponse.json @@ -2,6 +2,7 @@ "data": { "cluster_job_id": "9a59d8e9-91d3-4ff6-b4cb-9db91c4094dd", "job_id": "ABCDefGHIjKlMNOpQrSTUvYxWZ0-1234567890abcDE=", - "status": "queued" + "status": "queued", + "generated_time": "2018-12-10T08:23:55.110Z" } } diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/putJobUploadResponse.json b/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/putJobUploadResponse.json index 0474862720..1304f3a9f6 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/putJobUploadResponse.json +++ b/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/putJobUploadResponse.json @@ -1,10 +1,9 @@ { "data": { "content_type": "text/plain", - "download_url": "https://api.ultimaker.com/print-job-download", "job_id": "ABCDefGHIjKlMNOpQrSTUvYxWZ0-1234567890abcDE=", "job_name": "Ultimaker Robot v3.0", - "status": "queued", + "status": "uploading", "upload_url": "https://api.ultimaker.com/print-job-upload" } } diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/requestPrintResponse.json b/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/requestPrintResponse.json deleted file mode 100644 index e69589784d..0000000000 --- a/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/requestPrintResponse.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "data": { - "cluster_job_id": "", - "job_id": "db34b096-c4d5-46f3-bea7-da6a19905e6c", - "status": "queued" - } -} diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/requestUploadResponse.json b/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/requestUploadResponse.json deleted file mode 100644 index 6168e5205f..0000000000 --- a/plugins/UM3NetworkPrinting/tests/Cloud/Fixtures/requestUploadResponse.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "data": { - "content_type": "text/plain", - "generated_time": "2018-12-10T09:33:00.009Z", - "job_id": "j9KUn4D6FRRRmdtbCo4OGAwUf6Ml3p3oU-Zv7RNRv92T", - "job_name": "job name", - "status": "uploading", - "status_description": "The print job has been created. Please upload the file.", - "upload_url": "https://www.googleapis.com/upload/storage/v1/b/ultimaker-storage-1/o?uploadType=resumable&upload_id=AEnB2Uqhg1H7BXQVeLJEWw6AheqMicydZVLuH9bnkh6Oge0e6i5X76MW3NZHWRmUTmjzulAF42mkczcC7rsAuPg1Nn8JeFpnNA" - } -} diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py index 07c7733ac1..84f0254b55 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py @@ -1,7 +1,6 @@ # Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -import json import os from unittest import TestCase from unittest.mock import patch, MagicMock @@ -9,8 +8,9 @@ from unittest.mock import patch, MagicMock from cura.CuraApplication import CuraApplication from src.Cloud.CloudApiClient import CloudApiClient from src.Cloud.CloudOutputDeviceManager import CloudOutputDeviceManager -from src.Cloud.Models.CloudJobResponse import CloudJobResponse -from src.Cloud.Models.CloudJobUploadRequest import CloudJobUploadRequest +from src.Cloud.Models.CloudPrintJobResponse import CloudPrintJobResponse +from src.Cloud.Models.CloudPrintJobUploadRequest import CloudPrintJobUploadRequest +from tests.Cloud.Fixtures import readFixture, parseFixture from .NetworkManagerMock import NetworkManagerMock @@ -71,12 +71,11 @@ class TestCloudApiClient(TestCase): network_mock.return_value = self.network results = [] - with open("{}/Fixtures/requestUploadResponse.json".format(os.path.dirname(__file__)), "rb") as f: - response = f.read() + response = readFixture("putJobUploadResponse") self.network.prepareReply("PUT", "https://api-staging.ultimaker.com/cura/v1/jobs/upload", 200, response) - self.api.requestUpload(CloudJobUploadRequest(job_name = "job name", file_size = 143234, content_type = "text/plain"), - lambda r: results.append(r)) + request = CloudPrintJobUploadRequest(job_name = "job name", file_size = 143234, content_type = "text/plain") + self.api.requestUpload(request, lambda r: results.append(r)) self.network.flushReplies() self.assertEqual(results[0].content_type, "text/plain") @@ -87,13 +86,11 @@ class TestCloudApiClient(TestCase): results = [] progress = MagicMock() - with open("{}/Fixtures/requestUploadResponse.json".format(os.path.dirname(__file__)), "rb") as f: - thedata = json.loads(f.read().decode("ascii")) - data = thedata["data"] - upload_response = CloudJobResponse(**data) + data = parseFixture("putJobUploadResponse")["data"] + upload_response = CloudPrintJobResponse(**data) self.network.prepareReply("PUT", upload_response.upload_url, 200, - '{ data : "" }') # Network client doesn't look into the reply + b'{ data : "" }') # Network client doesn't look into the reply self.api.uploadMesh(upload_response, b'', lambda job_id: results.append(job_id), progress.advance, progress.error) @@ -107,11 +104,11 @@ class TestCloudApiClient(TestCase): network_mock.return_value = self.network results = [] - cluster_id = "NWKV6vJP_LdYsXgXqAcaNCR0YcLJwar1ugh0ikEZsZs8" - job_id = "db34b096-c4d5-46f3-bea7-da6a19905e6c" + response = readFixture("postJobPrintResponse") - with open("{}/Fixtures/requestPrintResponse.json".format(os.path.dirname(__file__)), "rb") as f: - response = f.read() + cluster_id = "NWKV6vJP_LdYsXgXqAcaNCR0YcLJwar1ugh0ikEZsZs8" + cluster_job_id = "9a59d8e9-91d3-4ff6-b4cb-9db91c4094dd" + job_id = "ABCDefGHIjKlMNOpQrSTUvYxWZ0-1234567890abcDE=" self.network.prepareReply("POST", "https://api-staging.ultimaker.com/connect/v1/clusters/{}/print/{}" @@ -123,5 +120,6 @@ class TestCloudApiClient(TestCase): self.network.flushReplies() self.assertEqual(len(results), 1) - self.assertEqual(results[0].job_id, "db34b096-c4d5-46f3-bea7-da6a19905e6c") + self.assertEqual(results[0].job_id, job_id) + self.assertEqual(results[0].cluster_job_id, cluster_job_id) self.assertEqual(results[0].status, "queued") diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py index 420d71d0fe..b6bcde6e55 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py @@ -106,7 +106,9 @@ class TestCloudOutputDeviceManager(TestCase): @patch("UM.Message.Message.show") def test_api_error(self, message_mock, network_mock): - self.clusters_response = {"errors": [{"id": "notFound", "title": "Not found!", "http_status": "404"}]} + self.clusters_response = { + "errors": [{"id": "notFound", "title": "Not found!", "http_status": "404", "code": "notFound"}] + } self.network.prepareReply("GET", self.URL, 200, self.clusters_response) self._loadData(network_mock) self.network.flushReplies() From 816c6bd4ec56204b4654cd23ba71de188180e03d Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 11 Dec 2018 11:12:36 +0100 Subject: [PATCH 0825/1240] Fixes after merging UI changes --- plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 17acbe2e3f..a77a25c375 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -96,9 +96,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # We use the Cura Connect monitor tab to get most functionality right away. self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), - "../../resources/qml/ClusterMonitorItem.qml") - self._control_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), - "../../resources/qml/ClusterControlItem.qml") + "../../resources/qml/MonitorStage.qml") # Trigger the printersChanged signal when the private signal is triggered. self.printersChanged.connect(self._clusterPrintersChanged) From 2b2e8ebb31ffaacdcd6cac3851585de94af556c4 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 11 Dec 2018 11:37:01 +0100 Subject: [PATCH 0826/1240] Add some safety checks when checking for the guid of the material Don't crash if the guid doesn't exist. Contributes to CL-1160. --- .../UM3NetworkPrinting/src/ClusterUM3OutputDevice.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index 60474156a8..d39cf6e41c 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -607,13 +607,18 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): def _createMaterialOutputModel(self, material_data: Dict[str, Any]) -> "MaterialOutputModel": material_manager = CuraApplication.getInstance().getMaterialManager() - material_group_list = material_manager.getMaterialGroupListByGUID(material_data["guid"]) + material_group_list = None + + # Avoid crashing if there is no "guid" field in the metadata + material_guid = material_data.get("guid") + if material_guid: + material_group_list = material_manager.getMaterialGroupListByGUID(material_guid) # This can happen if the connected machine has no material in one or more extruders (if GUID is empty), or the # material is unknown to Cura, so we should return an "empty" or "unknown" material model. if material_group_list is None: - material_name = "Empty" if len(material_data["guid"]) == 0 else "Unknown" - return MaterialOutputModel(guid = material_data["guid"], + material_name = "Empty" if len(material_data.get("guid", "") == 0 else "Unknown" + return MaterialOutputModel(guid = material_data.get("guid", ""), type = material_data.get("type", ""), color = material_data.get("color", ""), brand = material_data.get("brand", ""), From 1c373b720541691f4bd8d9e20a93b5596de04c32 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Tue, 11 Dec 2018 11:51:13 +0100 Subject: [PATCH 0827/1240] Improve config override text Contributes to CL-1152 --- .../resources/qml/MonitorConfigOverrideDialog.qml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml index 1b9a03ea99..6a32310dd5 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml @@ -61,7 +61,7 @@ UM.Dialog var topLine if (materialsAreKnown(printer.activePrintJob)) { - topLine = catalog.i18ncp("@label", "The assigned printer, %1, requires the following configuration change:", "The assigned printer, %1, requires the following configuration changes:").arg(printer.name) + topLine = catalog.i18ncp("@label", "The assigned printer, %1, requires the following configuration change:", "The assigned printer, %1, requires the following configuration changes:", printer.activePrintJob.configurationChanges.length).arg(printer.name) } else { @@ -89,8 +89,10 @@ UM.Dialog default: text = "unknown" } - result += "

    " + text + "

    " + result += "

    " + text + "

    \n\n" } + var bottomLine = catalog.i18nc("@label", "Override will use the specified settings with the existing printer configuration. This may result in a failed print.") + result += "

    " + bottomLine + "

    \n\n" return result } } From 70b0d16fa4827f74632e8b57e3c01db0ed3aabdf Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 11 Dec 2018 11:53:26 +0100 Subject: [PATCH 0828/1240] Fix typo Missing parenthesis. Contributes to CL-1160. --- plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index d39cf6e41c..275087447e 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -617,7 +617,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): # This can happen if the connected machine has no material in one or more extruders (if GUID is empty), or the # material is unknown to Cura, so we should return an "empty" or "unknown" material model. if material_group_list is None: - material_name = "Empty" if len(material_data.get("guid", "") == 0 else "Unknown" + material_name = "Empty" if len(material_data.get("guid", "")) == 0 else "Unknown" return MaterialOutputModel(guid = material_data.get("guid", ""), type = material_data.get("type", ""), color = material_data.get("color", ""), From 76b9fa79986d2622eca7a7316ba65a05aaaa95bd Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 11 Dec 2018 11:53:12 +0100 Subject: [PATCH 0829/1240] Use "cura" as i18n catalog for CuraDrive CURA-6005 --- plugins/CuraDrive/plugin.json | 2 +- plugins/CuraDrive/src/Settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/CuraDrive/plugin.json b/plugins/CuraDrive/plugin.json index 44bf955f41..6cf1fa273c 100644 --- a/plugins/CuraDrive/plugin.json +++ b/plugins/CuraDrive/plugin.json @@ -4,5 +4,5 @@ "description": "Backup and restore your configuration.", "version": "1.2.0", "api": 5, - "i18n-catalog": "cura_drive" + "i18n-catalog": "cura" } diff --git a/plugins/CuraDrive/src/Settings.py b/plugins/CuraDrive/src/Settings.py index 277a976cc7..f10a7d3bf7 100644 --- a/plugins/CuraDrive/src/Settings.py +++ b/plugins/CuraDrive/src/Settings.py @@ -13,7 +13,7 @@ class Settings: AUTO_BACKUP_ENABLED_PREFERENCE_KEY = "cura_drive/auto_backup_enabled" AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY = "cura_drive/auto_backup_date" - I18N_CATALOG_ID = "cura_drive" + I18N_CATALOG_ID = "cura" I18N_CATALOG = i18nCatalog(I18N_CATALOG_ID) MESSAGE_TITLE = I18N_CATALOG.i18nc("@info:title", "Backups"), From d54fc4182b6355123a6ef3c51c51e974fcc188bb Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Tue, 11 Dec 2018 11:56:36 +0100 Subject: [PATCH 0830/1240] STAR-322: Fixing style errors --- .../src/Cloud/CloudApiClient.py | 2 +- .../src/Cloud/Models/BaseCloudModel.py | 8 +++--- .../Models/CloudClusterPrintJobStatus.py | 8 +++--- .../CloudClusterPrinterConfiguration.py | 19 ++++++++----- .../Cloud/Models/CloudClusterPrinterStatus.py | 4 +-- .../src/Cloud/Models/CloudClusterResponse.py | 2 +- .../src/Cloud/Models/CloudClusterStatus.py | 6 ++-- .../src/Cloud/Models/CloudErrorObject.py | 4 +-- .../src/ClusterUM3OutputDevice.py | 28 +++---------------- .../src/MeshFormatHandler.py | 26 +++++++++++------ .../UM3NetworkPrinting/src/SendMaterialJob.py | 8 ++++-- .../src/UM3PrintJobOutputModel.py | 9 +++--- .../tests/Cloud/Models/__init__.py | 2 ++ .../tests/Cloud/NetworkManagerMock.py | 7 +++-- .../tests/Cloud/TestCloudApiClient.py | 8 +++--- 15 files changed, 69 insertions(+), 72 deletions(-) create mode 100644 plugins/UM3NetworkPrinting/tests/Cloud/Models/__init__.py diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py index 9cc70587a3..474d76d85a 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py @@ -31,7 +31,7 @@ class CloudApiClient(NetworkClient): ## Initializes a new cloud API client. # \param account: The user's account object # \param on_error: The callback to be called whenever we receive errors from the server. - def __init__(self, account: Account, on_error: Callable[[List[CloudErrorObject]], None]): + def __init__(self, account: Account, on_error: Callable[[List[CloudErrorObject]], None]) -> None: super().__init__() self._account = account self._on_error = on_error diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/BaseCloudModel.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/BaseCloudModel.py index 3a0e93e836..18a8cb5cba 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/BaseCloudModel.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/BaseCloudModel.py @@ -1,7 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. from datetime import datetime, timezone -from typing import Dict, Union, TypeVar, Type, List +from typing import Dict, Union, TypeVar, Type, List, Any from ...Models import BaseModel @@ -21,7 +21,7 @@ class BaseCloudModel(BaseModel): return type(self) != type(other) or self.toDict() != other.toDict() ## Converts the model into a serializable dictionary - def toDict(self) -> Dict[str, any]: + def toDict(self) -> Dict[str, Any]: return self.__dict__ # Type variable used in the parse methods below, which should be a subclass of BaseModel. @@ -32,7 +32,7 @@ class BaseCloudModel(BaseModel): # \param values: The value of the model, which is usually a dictionary, but may also be already parsed. # \return An instance of the model_class given. @staticmethod - def parseModel(model_class: Type[T], values: Union[T, Dict[str, any]]) -> T: + def parseModel(model_class: Type[T], values: Union[T, Dict[str, Any]]) -> T: if isinstance(values, dict): return model_class(**values) return values @@ -42,7 +42,7 @@ class BaseCloudModel(BaseModel): # \param values: The value of the list. Each value is usually a dictionary, but may also be already parsed. # \return A list of instances of the model_class given. @classmethod - def parseModels(cls, model_class: Type[T], values: List[Union[T, Dict[str, any]]]) -> List[T]: + def parseModels(cls, model_class: Type[T], values: List[Union[T, Dict[str, Any]]]) -> List[T]: return [cls.parseModel(model_class, value) for value in values] ## Parses the given date string. diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobStatus.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobStatus.py index 24ef9078d6..f451665a4f 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobStatus.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobStatus.py @@ -1,6 +1,6 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import List, Optional, Union, Dict +from typing import List, Optional, Union, Dict, Any from cura.PrinterOutput.ConfigurationModel import ConfigurationModel from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputController import CloudOutputController @@ -36,12 +36,12 @@ class CloudClusterPrintJobStatus(BaseCloudModel): # \param uuid: UUID of this print job. Should be used for identification purposes. def __init__(self, created_at: str, force: bool, machine_variant: str, name: str, started: bool, status: str, time_total: int, uuid: str, - configuration: List[Union[Dict[str, any], CloudClusterPrinterConfiguration]], - constraints: List[Union[Dict[str, any], CloudClusterPrintJobConstraints]], + configuration: List[Union[Dict[str, Any], CloudClusterPrinterConfiguration]], + constraints: List[Union[Dict[str, Any], CloudClusterPrintJobConstraints]], last_seen: Optional[float] = None, network_error_count: Optional[int] = None, owner: Optional[str] = None, printer_uuid: Optional[str] = None, time_elapsed: Optional[int] = None, assigned_to: Optional[str] = None, **kwargs) -> None: - self.assigned_to = assigned_to # type: str + self.assigned_to = assigned_to self.configuration = self.parseModels(CloudClusterPrinterConfiguration, configuration) self.constraints = self.parseModels(CloudClusterPrintJobConstraints, constraints) self.created_at = created_at diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfiguration.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfiguration.py index a6319ed6bb..3e06d0e2e7 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfiguration.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfiguration.py @@ -1,6 +1,6 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import Union, Dict, Optional +from typing import Union, Dict, Optional, Any from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationModel from cura.PrinterOutput.ExtruderOutputModel import ExtruderOutputModel @@ -17,10 +17,10 @@ class CloudClusterPrinterConfiguration(BaseCloudModel): # \param nozzle_diameter: The diameter of the print core at this position in millimeters, e.g. '0.4'. # \param print_core_id: The type of print core inserted at this position, e.g. 'AA 0.4'. def __init__(self, extruder_index: int, - material: Union[None, Dict[str, any], CloudClusterPrinterConfigurationMaterial], + material: Union[None, Dict[str, Any], CloudClusterPrinterConfigurationMaterial], nozzle_diameter: Optional[str] = None, print_core_id: Optional[str] = None, **kwargs) -> None: self.extruder_index = extruder_index - self.material = self.parseModel(CloudClusterPrinterConfigurationMaterial, material) + self.material = self.parseModel(CloudClusterPrinterConfigurationMaterial, material) if material else None self.nozzle_diameter = nozzle_diameter self.print_core_id = print_core_id super().__init__(**kwargs) @@ -28,11 +28,16 @@ class CloudClusterPrinterConfiguration(BaseCloudModel): ## Updates the given output model. # \param model - The output model to update. def updateOutputModel(self, model: ExtruderOutputModel) -> None: - model.updateHotendID(self.print_core_id) + if self.print_core_id is not None: + model.updateHotendID(self.print_core_id) - if model.activeMaterial is None or model.activeMaterial.guid != self.material.guid: - material = self.material.createOutputModel() - model.updateActiveMaterial(material) + if self.material: + active_material = model.activeMaterial + if active_material is None or active_material.guid != self.material.guid: + material = self.material.createOutputModel() + model.updateActiveMaterial(material) + else: + model.updateActiveMaterial(None) ## Creates a configuration model def createConfigurationModel(self) -> ExtruderConfigurationModel: diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterStatus.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterStatus.py index b25f21fde2..cd3b6bbdca 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterStatus.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterStatus.py @@ -1,6 +1,6 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import List, Union, Dict, Optional +from typing import List, Union, Dict, Optional, Any from cura.PrinterOutput.PrinterOutputController import PrinterOutputController from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel @@ -24,7 +24,7 @@ class CloudClusterPrinterStatus(BaseCloudModel): # \param reserved_by: A printer can be claimed by a specific print job. def __init__(self, enabled: bool, firmware_version: str, friendly_name: str, ip_address: str, machine_variant: str, status: str, unique_name: str, uuid: str, - configuration: List[Union[Dict[str, any], CloudClusterPrinterConfiguration]], + configuration: List[Union[Dict[str, Any], CloudClusterPrinterConfiguration]], reserved_by: Optional[str] = None, **kwargs) -> None: self.configuration = self.parseModels(CloudClusterPrinterConfiguration, configuration) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterResponse.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterResponse.py index a3eda54a76..9c0853e7c9 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterResponse.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterResponse.py @@ -16,7 +16,7 @@ class CloudClusterResponse(BaseCloudModel): # \param status: The status of the cluster authentication (active or inactive). # \param host_version: The firmware version of the cluster host. This is where the Stardust client is running on. def __init__(self, cluster_id: str, host_guid: str, host_name: str, is_online: bool, status: str, - host_version: Optional[str] = None, **kwargs): + host_version: Optional[str] = None, **kwargs) -> None: self.cluster_id = cluster_id self.host_guid = host_guid self.host_name = host_name diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterStatus.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterStatus.py index 2cebb1b592..b0250c2ebb 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterStatus.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterStatus.py @@ -1,7 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. from datetime import datetime -from typing import List, Dict, Union +from typing import List, Dict, Union, Any from .CloudClusterPrinterStatus import CloudClusterPrinterStatus from .CloudClusterPrintJobStatus import CloudClusterPrintJobStatus @@ -16,8 +16,8 @@ class CloudClusterStatus(BaseCloudModel): # \param print_jobs: The latest status of each print job in the cluster. # \param generated_time: The datetime when the object was generated on the server-side. def __init__(self, - printers: List[Union[CloudClusterPrinterStatus, Dict[str, any]]], - print_jobs: List[Union[CloudClusterPrintJobStatus, Dict[str, any]]], + printers: List[Union[CloudClusterPrinterStatus, Dict[str, Any]]], + print_jobs: List[Union[CloudClusterPrintJobStatus, Dict[str, Any]]], generated_time: Union[str, datetime], **kwargs) -> None: self.generated_time = self.parseDate(generated_time) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudErrorObject.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudErrorObject.py index c02a21d4da..28b4d916a1 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudErrorObject.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudErrorObject.py @@ -1,6 +1,6 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import Dict, Optional +from typing import Dict, Optional, Any from .BaseCloudModel import BaseCloudModel @@ -18,7 +18,7 @@ class CloudErrorObject(BaseCloudModel): # \param http_status: The HTTP status code applicable to this problem, converted to string. # \param meta: Non-standard meta-information about the error, depending on the error code. def __init__(self, id: str, code: str, title: str, http_status: str, detail: Optional[str] = None, - meta: Optional[Dict[str, any]] = None, **kwargs) -> None: + meta: Optional[Dict[str, Any]] = None, **kwargs) -> None: self.id = id self.code = code self.http_status = http_status diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index 93a53373dc..394c3d8552 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -56,7 +56,8 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): self._number_of_extruders = 2 - self._dummy_lambdas = ("", {}, io.BytesIO()) #type: Tuple[str, Dict, Union[io.StringIO, io.BytesIO]] + self._dummy_lambdas = ("", {}, io.BytesIO() + ) # type: Tuple[str, Dict[str, Union[str, int, bool]], Union[io.StringIO, io.BytesIO]] self._print_jobs = [] # type: List[UM3PrintJobOutputModel] self._received_print_jobs = False # type: bool @@ -254,7 +255,8 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): # Treat upload progress as response. Uploading can take more than 10 seconds, so if we don't, we can get # timeout responses if this happens. self._last_response_time = time() - if self._progress_message and new_progress > self._progress_message.getProgress(): + old_progress = self._progress_message.getProgress() + if self._progress_message and (old_progress is None or new_progress > old_progress): self._progress_message.show() # Ensure that the message is visible. self._progress_message.setProgress(bytes_sent / bytes_total * 100) @@ -345,28 +347,6 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): def getDateCompleted(self, time_remaining: int) -> str: return formatDateCompleted(time_remaining) - @pyqtSlot(int, result = str) - def getDateCompleted(self, time_remaining: int) -> str: - current_time = time() - completed = datetime.fromtimestamp(current_time + time_remaining) - today = datetime.fromtimestamp(current_time) - - # If finishing date is more than 7 days out, using "Mon Dec 3 at HH:MM" format - if completed.toordinal() > today.toordinal() + 7: - return completed.strftime("%a %b ") + "{day}".format(day=completed.day) - - # If finishing date is within the next week, use "Monday at HH:MM" format - elif completed.toordinal() > today.toordinal() + 1: - return completed.strftime("%a") - - # If finishing tomorrow, use "tomorrow at HH:MM" format - elif completed.toordinal() > today.toordinal(): - return "tomorrow" - - # If finishing today, use "today at HH:MM" format - else: - return "today" - @pyqtSlot(str) def sendJobToTop(self, print_job_uuid: str) -> None: # This function is part of the output device (and not of the printjob output model) as this type of operation diff --git a/plugins/UM3NetworkPrinting/src/MeshFormatHandler.py b/plugins/UM3NetworkPrinting/src/MeshFormatHandler.py index d64861ea91..72da3c4e6b 100644 --- a/plugins/UM3NetworkPrinting/src/MeshFormatHandler.py +++ b/plugins/UM3NetworkPrinting/src/MeshFormatHandler.py @@ -1,7 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import io -from typing import Optional, Dict, Union, List +from typing import Optional, Dict, Union, List, cast from UM.FileHandler.FileHandler import FileHandler from UM.FileHandler.FileWriter import FileWriter @@ -26,7 +26,7 @@ class MeshFormatHandler: def __init__(self, file_handler: Optional[FileHandler], firmware_version: str) -> None: self._file_handler = file_handler or CuraApplication.getInstance().getMeshFileHandler() self._preferred_format = self._getPreferredFormat(firmware_version) - self._writer = self._getWriter(self._preferred_format["mime_type"]) if self._preferred_format else None + self._writer = self._getWriter(self.mime_type) if self._preferred_format else None @property def is_valid(self) -> bool: @@ -47,32 +47,40 @@ class MeshFormatHandler: @property def mime_type(self) -> str: - return self._preferred_format["mime_type"] + return cast(str, self._preferred_format["mime_type"]) ## Gets the file mode (FileWriter.OutputMode.TextMode or FileWriter.OutputMode.BinaryMode) @property def file_mode(self) -> int: - return self._preferred_format["mode"] + return cast(int, self._preferred_format["mode"]) ## Gets the file extension @property def file_extension(self) -> str: - return self._preferred_format["extension"] + return cast(str, self._preferred_format["extension"]) ## Creates the right kind of stream based on the preferred format. def createStream(self) -> Union[io.BytesIO, io.StringIO]: - return io.StringIO() if self.file_mode == FileWriter.OutputMode.TextMode else io.BytesIO() + if self.file_mode == FileWriter.OutputMode.TextMode: + return io.StringIO() + else: + return io.BytesIO() ## Writes the mesh and returns its value. def getBytes(self, nodes: List[SceneNode]) -> bytes: + if self.writer is None: + raise ValueError("There is no writer for the mesh format handler.") stream = self.createStream() self.writer.write(stream, nodes) - return stream.getvalue() + value = stream.getvalue() + if isinstance(value, str): + value = value.encode() + return value ## Chooses the preferred file format for the given file handler. # \param firmware_version: The version of the firmware. # \return A dict with the file format details. - def _getPreferredFormat(self, firmware_version: str) -> Optional[Dict[str, Union[str, int, bool]]]: + def _getPreferredFormat(self, firmware_version: str) -> Dict[str, Union[str, int, bool]]: # Formats supported by this application (file types that we can actually write). application = CuraApplication.getInstance() @@ -82,7 +90,7 @@ class MeshFormatHandler: # Create a list from the supported file formats string. if not global_stack: Logger.log("e", "Missing global stack!") - return + return {} machine_file_formats = global_stack.getMetaDataEntry("file_formats").split(";") machine_file_formats = [file_type.strip() for file_type in machine_file_formats] diff --git a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py index f536fad49a..cbcfe73c71 100644 --- a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py @@ -3,7 +3,7 @@ import json import os import urllib.parse -from typing import Dict, TYPE_CHECKING, Set +from typing import Dict, TYPE_CHECKING, Set, Optional from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest @@ -151,7 +151,7 @@ class SendMaterialJob(Job): # \return a dictionary of ClusterMaterial objects by GUID # \throw KeyError Raised when on of the materials does not include a valid guid @classmethod - def _parseReply(cls, reply: QNetworkReply) -> Dict[str, ClusterMaterial]: + def _parseReply(cls, reply: QNetworkReply) -> Optional[Dict[str, ClusterMaterial]]: try: remote_materials = json.loads(reply.readAll().data().decode("utf-8")) return {material["guid"]: ClusterMaterial(**material) for material in remote_materials} @@ -163,6 +163,7 @@ class SendMaterialJob(Job): Logger.log("e", "Request material storage on printer: Printer's answer had an incorrect value.") except TypeError: Logger.log("e", "Request material storage on printer: Printer's answer was missing a required value.") + return None ## Retrieves a list of local materials # @@ -184,7 +185,8 @@ class SendMaterialJob(Job): local_material = LocalMaterial(**material) if local_material.GUID not in result or \ - local_material.version > result.get(local_material.GUID).version: + local_material.GUID not in result or \ + local_material.version > result[local_material.GUID].version: result[local_material.GUID] = local_material except KeyError: diff --git a/plugins/UM3NetworkPrinting/src/UM3PrintJobOutputModel.py b/plugins/UM3NetworkPrinting/src/UM3PrintJobOutputModel.py index 2ac3e6ba4f..4f44ca4af8 100644 --- a/plugins/UM3NetworkPrinting/src/UM3PrintJobOutputModel.py +++ b/plugins/UM3NetworkPrinting/src/UM3PrintJobOutputModel.py @@ -1,13 +1,12 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject, pyqtSlot -from typing import Optional, TYPE_CHECKING, List -from PyQt5.QtCore import QUrl -from PyQt5.QtGui import QImage +from typing import List + +from PyQt5.QtCore import pyqtProperty, pyqtSignal from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel - +from cura.PrinterOutput.PrinterOutputController import PrinterOutputController from .ConfigurationChangeModel import ConfigurationChangeModel diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/Models/__init__.py b/plugins/UM3NetworkPrinting/tests/Cloud/Models/__init__.py new file mode 100644 index 0000000000..f3f6970c54 --- /dev/null +++ b/plugins/UM3NetworkPrinting/tests/Cloud/Models/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py b/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py index 5a76672b83..94cc239c0a 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py @@ -14,9 +14,6 @@ from UM.Signal import Signal # After patching the QNetworkManager class, requests are prepared before they can be executed. # Any requests not prepared beforehand will cause KeyErrors. class NetworkManagerMock: - # signals used in the network manager. - finished = Signal() - authenticationRequired = Signal() # an enumeration of the supported operations and their code for the network access manager. _OPERATIONS = { @@ -33,6 +30,10 @@ class NetworkManagerMock: self.replies = {} # type: Dict[Tuple[str, str], QNetworkReply] self.request_bodies = {} # type: Dict[Tuple[str, str], bytes] + # signals used in the network manager. + self.finished = Signal() + self.authenticationRequired = Signal() + ## Mock implementation of the get, post, put, delete and head methods from the network manager. # Since the methods are very simple and the same it didn't make sense to repeat the code. # \param method: The method being called. diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py index 84f0254b55..d673554640 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py @@ -2,22 +2,23 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import os +from typing import List from unittest import TestCase from unittest.mock import patch, MagicMock from cura.CuraApplication import CuraApplication from src.Cloud.CloudApiClient import CloudApiClient -from src.Cloud.CloudOutputDeviceManager import CloudOutputDeviceManager from src.Cloud.Models.CloudPrintJobResponse import CloudPrintJobResponse from src.Cloud.Models.CloudPrintJobUploadRequest import CloudPrintJobUploadRequest +from src.Cloud.Models.CloudErrorObject import CloudErrorObject from tests.Cloud.Fixtures import readFixture, parseFixture from .NetworkManagerMock import NetworkManagerMock @patch("cura.NetworkClient.QNetworkAccessManager") class TestCloudApiClient(TestCase): - def _errorHandler(self): - pass + def _errorHandler(self, errors: List[CloudErrorObject]): + raise Exception("Received unexpected error: {}".format(errors)) def setUp(self): super().setUp() @@ -26,7 +27,6 @@ class TestCloudApiClient(TestCase): self.app = CuraApplication.getInstance() self.network = NetworkManagerMock() - self.manager = CloudOutputDeviceManager() self.api = CloudApiClient(self.account, self._errorHandler) def test_GetClusters(self, network_mock): From 4949f39c34e6ffee888fb9eed49181879c02b9d1 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 11 Dec 2018 12:10:32 +0100 Subject: [PATCH 0831/1240] Add translatable strings to empty and unknown material Contributes to CL-1160. --- .../UM3NetworkPrinting/src/ClusterUM3OutputDevice.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index 275087447e..ef890fc4ed 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -617,7 +617,8 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): # This can happen if the connected machine has no material in one or more extruders (if GUID is empty), or the # material is unknown to Cura, so we should return an "empty" or "unknown" material model. if material_group_list is None: - material_name = "Empty" if len(material_data.get("guid", "")) == 0 else "Unknown" + material_name = i18n_catalog.i18nc("@label:material", "Empty") if len(material_data.get("guid", "")) == 0 \ + else i18n_catalog.i18nc("@label:material", "Unknown") return MaterialOutputModel(guid = material_data.get("guid", ""), type = material_data.get("type", ""), color = material_data.get("color", ""), @@ -649,9 +650,10 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): color = material_data["color"] brand = material_data["brand"] material_type = material_data["material"] - name = "Empty" if material_data["material"] == "empty" else "Unknown" - return MaterialOutputModel(guid=material_data["guid"], type=material_type, - brand=brand, color=color, name=name) + name = i18n_catalog.i18nc("@label:material", "Empty") if material_data["material"] == "empty" \ + else i18n_catalog.i18nc("@label:material", "Unknown") + return MaterialOutputModel(guid = material_data["guid"], type = material_type, + brand = brand, color = color, name = name) def _updatePrinter(self, printer: PrinterOutputModel, data: Dict[str, Any]) -> None: # For some unknown reason the cluster wants UUID for everything, except for sending a job directly to a printer. From 8e4ad23da104ead8ee5ce3658e7132d3d8e9eaec Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 11 Dec 2018 12:12:32 +0100 Subject: [PATCH 0832/1240] Add CuraConstants CURA-6005 Put constant values into a separate file CuraConstants.py --- cura/CuraConstants.py | 54 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 cura/CuraConstants.py diff --git a/cura/CuraConstants.py b/cura/CuraConstants.py new file mode 100644 index 0000000000..331937a0c2 --- /dev/null +++ b/cura/CuraConstants.py @@ -0,0 +1,54 @@ +# +# This file contains all constant values in Cura +# + +# ------------- +# Cura Versions +# ------------- +DEFAULT_CURA_DISPLAY_NAME = "Ultimaker Cura" +DEFAULT_CURA_VERSION = "master" +DEFAULT_CURA_BUILD_TYPE = "" +DEFAULT_CURA_DEBUG_MODE = False +DEFAULT_CURA_SDK_VERSION = "5.0.0" + +try: + from cura.CuraVersion import CuraAppDisplayName # type: ignore +except ImportError: + CuraAppDisplayName = DEFAULT_CURA_DISPLAY_NAME + +try: + from cura.CuraVersion import CuraVersion # type: ignore +except ImportError: + CuraVersion = DEFAULT_CURA_VERSION # [CodeStyle: Reflecting imported value] + +try: + from cura.CuraVersion import CuraBuildType # type: ignore +except ImportError: + CuraBuildType = DEFAULT_CURA_BUILD_TYPE + +try: + from cura.CuraVersion import CuraDebugMode # type: ignore +except ImportError: + CuraDebugMode = DEFAULT_CURA_DEBUG_MODE + +try: + from cura.CuraVersion import CuraSDKVersion # type: ignore +except ImportError: + CuraSDKVersion = DEFAULT_CURA_SDK_VERSION + + +# --------- +# Cloud API +# --------- +DEFAULT_CLOUD_API_ROOT = "https://api.ultimaker.com" # type: str +DEFAULT_CLOUD_API_VERSION = 1 # type: int + +try: + from cura.CuraVersion import CuraCloudAPIRoot # type: ignore +except ImportError: + CuraCloudAPIRoot = DEFAULT_CLOUD_API_ROOT + +try: + from cura.CuraVersion import CuraCloudAPIVersion # type: ignore +except ImportError: + CuraCloudAPIVersion = DEFAULT_CLOUD_API_VERSION From 2275e5c71f2ddd2181bdb54bb7c775165f14f1a6 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 11 Dec 2018 12:13:34 +0100 Subject: [PATCH 0833/1240] Refactor code to use constants in CuraConstants CURA-6005 --- cura/CuraApplication.py | 23 +++++++----------- plugins/CuraDrive/src/Settings.py | 9 +++---- plugins/Toolbox/src/Toolbox.py | 40 ++++--------------------------- 3 files changed, 17 insertions(+), 55 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index dfdb50515f..95b94c01c7 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -115,6 +115,8 @@ from cura.ObjectsModel import ObjectsModel from cura.PrinterOutput.NetworkMJPGImage import NetworkMJPGImage +from cura import CuraConstants + from UM.FlameProfiler import pyqtSlot from UM.Decorators import override @@ -127,15 +129,6 @@ if TYPE_CHECKING: numpy.seterr(all = "ignore") -try: - from cura.CuraVersion import CuraAppDisplayName, CuraVersion, CuraBuildType, CuraDebugMode, CuraSDKVersion # type: ignore -except ImportError: - CuraAppDisplayName = "Ultimaker Cura" - CuraVersion = "master" # [CodeStyle: Reflecting imported value] - CuraBuildType = "" - CuraDebugMode = False - CuraSDKVersion = "5.0.0" - class CuraApplication(QtApplication): # SettingVersion represents the set of settings available in the machine/extruder definitions. @@ -162,11 +155,11 @@ class CuraApplication(QtApplication): def __init__(self, *args, **kwargs): super().__init__(name = "cura", - app_display_name = CuraAppDisplayName, - version = CuraVersion, - api_version = CuraSDKVersion, - buildtype = CuraBuildType, - is_debug_mode = CuraDebugMode, + app_display_name = CuraConstants.CuraAppDisplayName, + version = CuraConstants.CuraVersion, + api_version = CuraConstants.CuraSDKVersion, + buildtype = CuraConstants.CuraBuildType, + is_debug_mode = CuraConstants.CuraDebugMode, tray_icon_name = "cura-icon-32.png", **kwargs) @@ -937,7 +930,7 @@ class CuraApplication(QtApplication): engine.rootContext().setContextProperty("CuraApplication", self) engine.rootContext().setContextProperty("PrintInformation", self._print_information) engine.rootContext().setContextProperty("CuraActions", self._cura_actions) - engine.rootContext().setContextProperty("CuraSDKVersion", CuraSDKVersion) + engine.rootContext().setContextProperty("CuraSDKVersion", CuraConstants.CuraSDKVersion) qmlRegisterUncreatableType(CuraApplication, "Cura", 1, 0, "ResourceTypes", "Just an Enum type") diff --git a/plugins/CuraDrive/src/Settings.py b/plugins/CuraDrive/src/Settings.py index f10a7d3bf7..c0df66b950 100644 --- a/plugins/CuraDrive/src/Settings.py +++ b/plugins/CuraDrive/src/Settings.py @@ -1,21 +1,22 @@ # Copyright (c) 2018 Ultimaker B.V. from UM import i18nCatalog +from cura import CuraConstants + class Settings: """ Keeps the application settings. """ - UM_CLOUD_API_ROOT = "https://api.ultimaker.com" DRIVE_API_VERSION = 1 - DRIVE_API_URL = "{}/cura-drive/v{}".format(UM_CLOUD_API_ROOT, str(DRIVE_API_VERSION)) - + DRIVE_API_URL = "{}/cura-drive/v{}".format(CuraConstants.CuraCloudAPIRoot, str(DRIVE_API_VERSION)) + AUTO_BACKUP_ENABLED_PREFERENCE_KEY = "cura_drive/auto_backup_enabled" AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY = "cura_drive/auto_backup_date" I18N_CATALOG_ID = "cura" I18N_CATALOG = i18nCatalog(I18N_CATALOG_ID) - + MESSAGE_TITLE = I18N_CATALOG.i18nc("@info:title", "Backups"), # Translatable messages for the entire plugin. diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 562a964f01..667b28c018 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -18,6 +18,7 @@ from UM.i18n import i18nCatalog from UM.Version import Version import cura +from cura import CuraConstants from cura.CuraApplication import CuraApplication from .AuthorsModel import AuthorsModel @@ -39,9 +40,9 @@ class Toolbox(QObject, Extension): self._application = application # type: CuraApplication - self._sdk_version = None # type: Optional[Union[str, int]] - self._cloud_api_version = None # type: Optional[int] - self._cloud_api_root = None # type: Optional[str] + self._sdk_version = CuraConstants.CuraSDKVersion # type: Union[str, int] + self._cloud_api_version = CuraConstants.CuraCloudAPIVersion # type: int + self._cloud_api_root = CuraConstants.CuraCloudAPIRoot # type: str self._api_url = None # type: Optional[str] # Network: @@ -168,9 +169,6 @@ class Toolbox(QObject, Extension): def _onAppInitialized(self) -> None: self._plugin_registry = self._application.getPluginRegistry() self._package_manager = self._application.getPackageManager() - self._sdk_version = self._getSDKVersion() - self._cloud_api_version = self._getCloudAPIVersion() - self._cloud_api_root = self._getCloudAPIRoot() self._api_url = "{cloud_api_root}/cura-packages/v{cloud_api_version}/cura/v{sdk_version}".format( cloud_api_root = self._cloud_api_root, cloud_api_version = self._cloud_api_version, @@ -186,36 +184,6 @@ class Toolbox(QObject, Extension): "materials_generic": QUrl("{base_url}/packages?package_type=material&tags=generic".format(base_url = self._api_url)) } - # Get the API root for the packages API depending on Cura version settings. - def _getCloudAPIRoot(self) -> str: - if not hasattr(cura, "CuraVersion"): - return self.DEFAULT_CLOUD_API_ROOT - if not hasattr(cura.CuraVersion, "CuraCloudAPIRoot"): # type: ignore - return self.DEFAULT_CLOUD_API_ROOT - if not cura.CuraVersion.CuraCloudAPIRoot: # type: ignore - return self.DEFAULT_CLOUD_API_ROOT - return cura.CuraVersion.CuraCloudAPIRoot # type: ignore - - # Get the cloud API version from CuraVersion - def _getCloudAPIVersion(self) -> int: - if not hasattr(cura, "CuraVersion"): - return self.DEFAULT_CLOUD_API_VERSION - if not hasattr(cura.CuraVersion, "CuraCloudAPIVersion"): # type: ignore - return self.DEFAULT_CLOUD_API_VERSION - if not cura.CuraVersion.CuraCloudAPIVersion: # type: ignore - return self.DEFAULT_CLOUD_API_VERSION - return cura.CuraVersion.CuraCloudAPIVersion # type: ignore - - # Get the packages version depending on Cura version settings. - def _getSDKVersion(self) -> Union[int, str]: - if not hasattr(cura, "CuraVersion"): - return self._application.getAPIVersion().getMajor() - if not hasattr(cura.CuraVersion, "CuraSDKVersion"): # type: ignore - return self._application.getAPIVersion().getMajor() - if not cura.CuraVersion.CuraSDKVersion: # type: ignore - return self._application.getAPIVersion().getMajor() - return cura.CuraVersion.CuraSDKVersion # type: ignore - @pyqtSlot() def browsePackages(self) -> None: # Create the network manager: From a766effce8917337624b3d030400797fa73c2adb Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 11 Dec 2018 12:43:43 +0100 Subject: [PATCH 0834/1240] Fix typing for optional callback for on_progress --- cura/NetworkClient.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/NetworkClient.py b/cura/NetworkClient.py index 5294813fb7..6abf4f3d47 100644 --- a/cura/NetworkClient.py +++ b/cura/NetworkClient.py @@ -203,7 +203,7 @@ class NetworkClient: ## Does a POST request with form data to the given URL. def postForm(self, url: str, header_data: str, body_data: bytes, on_finished: Optional[Callable[[QNetworkReply], None]], - on_progress: Callable = None) -> None: + on_progress: Optional[Callable[[int, int], None]] = None) -> None: post_part = QHttpPart() post_part.setHeader(QNetworkRequest.ContentDispositionHeader, header_data) post_part.setBody(body_data) @@ -212,7 +212,7 @@ class NetworkClient: ## Does a POST request with form parts to the given URL. def postFormWithParts(self, target: str, parts: List[QHttpPart], on_finished: Optional[Callable[[QNetworkReply], None]], - on_progress: Callable = None) -> Optional[QNetworkReply]: + on_progress: Optional[Callable[[int, int], None]] = None) -> Optional[QNetworkReply]: self._validateManager() request = self._createEmptyRequest(target, content_type = None) From e52339a4248a5cf3a1aa423528e3c654defeb2e2 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 11 Dec 2018 12:44:37 +0100 Subject: [PATCH 0835/1240] Fix typing for output_device param in cloud output controller --- plugins/UM3NetworkPrinting/src/Cloud/CloudOutputController.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputController.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputController.py index d31d2bf486..b2a8d8649b 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputController.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputController.py @@ -1,10 +1,11 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from UM.OutputDevice.OutputDevice import OutputDevice from cura.PrinterOutput.PrinterOutputController import PrinterOutputController class CloudOutputController(PrinterOutputController): - def __init__(self, output_device): + def __init__(self, output_device: OutputDevice): super().__init__(output_device) # The cloud connection only supports fetching the printer and queue status and adding a job to the queue. From 7b7c687db7792634a178a2a6b001d6566b40f4c6 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 11 Dec 2018 12:46:05 +0100 Subject: [PATCH 0836/1240] Remove dismissable = False from messages as it's the default --- plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index e68eb47839..5e0d34e902 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -320,8 +320,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): Message( text = message, title = T.ERROR, - lifetime = 10, - dismissable = True + lifetime = 10 ).show() self._sending_job = False # the upload has finished so we're not sending a job anymore self.writeError.emit() @@ -334,8 +333,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): Message( text = T.UPLOAD_SUCCESS_TEXT, title = T.UPLOAD_SUCCESS_TITLE, - lifetime = 5, - dismissable = True, + lifetime = 5 ).show() self._sending_job = False # the upload has finished so we're not sending a job anymore self.writeFinished.emit() From 6c70543d11bdc37ab658e53686882bcd5a6e4452 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 11 Dec 2018 12:46:11 +0100 Subject: [PATCH 0837/1240] Only set the containerID when the dialog is visible This prevents unneeded updates CURA-6016 --- resources/qml/Dialogs/WorkspaceSummaryDialog.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/qml/Dialogs/WorkspaceSummaryDialog.qml b/resources/qml/Dialogs/WorkspaceSummaryDialog.qml index 1b3a7aac55..35630bd19b 100644 --- a/resources/qml/Dialogs/WorkspaceSummaryDialog.qml +++ b/resources/qml/Dialogs/WorkspaceSummaryDialog.qml @@ -11,6 +11,7 @@ import Cura 1.0 as Cura UM.Dialog { + id: base title: catalog.i18nc("@title:window", "Save Project") minimumWidth: 500 * screenScaleFactor @@ -49,7 +50,7 @@ UM.Dialog UM.SettingDefinitionsModel { id: definitionsModel - containerId: Cura.MachineManager.activeDefinitionId + containerId: base.visible ? Cura.MachineManager.activeDefinitionId: "" showAll: true exclude: ["command_line_settings"] showAncestors: true From e4939cf0051d4f679e8156df26b46e5a22f8b26d Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 11 Dec 2018 12:55:49 +0100 Subject: [PATCH 0838/1240] Remove outdated cloud icon, will be done in another PR --- .../cura-light/icons/printer_cloud_connected.svg | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 resources/themes/cura-light/icons/printer_cloud_connected.svg diff --git a/resources/themes/cura-light/icons/printer_cloud_connected.svg b/resources/themes/cura-light/icons/printer_cloud_connected.svg deleted file mode 100644 index 59ca67e93e..0000000000 --- a/resources/themes/cura-light/icons/printer_cloud_connected.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - noun_Cloud_377836 - Created with Sketch. - - - - - - - - \ No newline at end of file From 75ff03f3c8e55c2d2a651da0f36b3d0ee6c0fb8f Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 11 Dec 2018 13:13:20 +0100 Subject: [PATCH 0839/1240] Use setState instead of emitting the backend state CURA-6016 --- .../CuraEngineBackend/CuraEngineBackend.py | 38 +++++++++---------- plugins/CuraEngineBackend/StartSliceJob.py | 2 +- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 58bc74f3f1..7ede6b6736 100755 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -203,7 +203,7 @@ class CuraEngineBackend(QObject, Backend): @pyqtSlot() def stopSlicing(self) -> None: - self.backendStateChange.emit(BackendState.NotStarted) + self.setState(BackendState.NotStarted) if self._slicing: # We were already slicing. Stop the old job. self._terminate() self._createSocket() @@ -322,7 +322,7 @@ class CuraEngineBackend(QObject, Backend): self._start_slice_job = None if job.isCancelled() or job.getError() or job.getResult() == StartJobResult.Error: - self.backendStateChange.emit(BackendState.Error) + self.setState(BackendState.Error) self.backendError.emit(job) return @@ -331,10 +331,10 @@ class CuraEngineBackend(QObject, Backend): self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice with the current material as it is incompatible with the selected machine or configuration."), title = catalog.i18nc("@info:title", "Unable to slice")) self._error_message.show() - self.backendStateChange.emit(BackendState.Error) + self.setState(BackendState.Error) self.backendError.emit(job) else: - self.backendStateChange.emit(BackendState.NotStarted) + self.setState(BackendState.NotStarted) return if job.getResult() == StartJobResult.SettingError: @@ -362,10 +362,10 @@ class CuraEngineBackend(QObject, Backend): self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice with the current settings. The following settings have errors: {0}").format(", ".join(error_labels)), title = catalog.i18nc("@info:title", "Unable to slice")) self._error_message.show() - self.backendStateChange.emit(BackendState.Error) + self.setState(BackendState.Error) self.backendError.emit(job) else: - self.backendStateChange.emit(BackendState.NotStarted) + self.setState(BackendState.NotStarted) return elif job.getResult() == StartJobResult.ObjectSettingError: @@ -386,7 +386,7 @@ class CuraEngineBackend(QObject, Backend): self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice due to some per-model settings. The following settings have errors on one or more models: {error_labels}").format(error_labels = ", ".join(errors.values())), title = catalog.i18nc("@info:title", "Unable to slice")) self._error_message.show() - self.backendStateChange.emit(BackendState.Error) + self.setState(BackendState.Error) self.backendError.emit(job) return @@ -395,16 +395,16 @@ class CuraEngineBackend(QObject, Backend): self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice because the prime tower or prime position(s) are invalid."), title = catalog.i18nc("@info:title", "Unable to slice")) self._error_message.show() - self.backendStateChange.emit(BackendState.Error) + self.setState(BackendState.Error) self.backendError.emit(job) else: - self.backendStateChange.emit(BackendState.NotStarted) + self.setState(BackendState.NotStarted) if job.getResult() == StartJobResult.ObjectsWithDisabledExtruder: self._error_message = Message(catalog.i18nc("@info:status", "Unable to slice because there are objects associated with disabled Extruder %s." % job.getMessage()), title = catalog.i18nc("@info:title", "Unable to slice")) self._error_message.show() - self.backendStateChange.emit(BackendState.Error) + self.setState(BackendState.Error) self.backendError.emit(job) return @@ -413,10 +413,10 @@ class CuraEngineBackend(QObject, Backend): self._error_message = Message(catalog.i18nc("@info:status", "Nothing to slice because none of the models fit the build volume. Please scale or rotate models to fit."), title = catalog.i18nc("@info:title", "Unable to slice")) self._error_message.show() - self.backendStateChange.emit(BackendState.Error) + self.setState(BackendState.Error) self.backendError.emit(job) else: - self.backendStateChange.emit(BackendState.NotStarted) + self.setState(BackendState.NotStarted) self._invokeSlice() return @@ -424,7 +424,7 @@ class CuraEngineBackend(QObject, Backend): self._socket.sendMessage(job.getSliceMessage()) # Notify the user that it's now up to the backend to do it's job - self.backendStateChange.emit(BackendState.Processing) + self.setState(BackendState.Processing) if self._slice_start_time: Logger.log("d", "Sending slice message took %s seconds", time() - self._slice_start_time ) @@ -442,7 +442,7 @@ class CuraEngineBackend(QObject, Backend): for node in DepthFirstIterator(self._scene.getRoot()): #type: ignore #Ignore type error because iter() should get called automatically by Python syntax. if node.callDecoration("isBlockSlicing"): enable_timer = False - self.backendStateChange.emit(BackendState.Disabled) + self.setState(BackendState.Disabled) self._is_disabled = True gcode_list = node.callDecoration("getGCodeList") if gcode_list is not None: @@ -451,7 +451,7 @@ class CuraEngineBackend(QObject, Backend): if self._use_timer == enable_timer: return self._use_timer if enable_timer: - self.backendStateChange.emit(BackendState.NotStarted) + self.setState(BackendState.NotStarted) self.enableTimer() return True else: @@ -518,7 +518,7 @@ class CuraEngineBackend(QObject, Backend): self._build_plates_to_be_sliced.append(build_plate_number) self.printDurationMessage.emit(source_build_plate_number, {}, []) self.processingProgress.emit(0.0) - self.backendStateChange.emit(BackendState.NotStarted) + self.setState(BackendState.NotStarted) # if not self._use_timer: # With manually having to slice, we want to clear the old invalid layer data. self._clearLayerData(build_plate_changed) @@ -567,7 +567,7 @@ class CuraEngineBackend(QObject, Backend): self.stopSlicing() self.markSliceAll() self.processingProgress.emit(0.0) - self.backendStateChange.emit(BackendState.NotStarted) + self.setState(BackendState.NotStarted) if not self._use_timer: # With manually having to slice, we want to clear the old invalid layer data. self._clearLayerData() @@ -613,7 +613,7 @@ class CuraEngineBackend(QObject, Backend): # \param message The protobuf message containing the slicing progress. def _onProgressMessage(self, message: Arcus.PythonMessage) -> None: self.processingProgress.emit(message.amount) - self.backendStateChange.emit(BackendState.Processing) + self.setState(BackendState.Processing) def _invokeSlice(self) -> None: if self._use_timer: @@ -632,7 +632,7 @@ class CuraEngineBackend(QObject, Backend): # # \param message The protobuf message signalling that slicing is finished. def _onSlicingFinishedMessage(self, message: Arcus.PythonMessage) -> None: - self.backendStateChange.emit(BackendState.Done) + self.setState(BackendState.Done) self.processingProgress.emit(1.0) gcode_list = self._scene.gcode_dict[self._start_slice_job_build_plate] #type: ignore #Because we generate this attribute dynamically. diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py index 9679360ad5..d3882a1209 100644 --- a/plugins/CuraEngineBackend/StartSliceJob.py +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -323,7 +323,7 @@ class StartSliceJob(Job): value = stack.getProperty(key, "value") result[key] = value Job.yieldThread() - + result["print_bed_temperature"] = result["material_bed_temperature"] # Renamed settings. result["print_temperature"] = result["material_print_temperature"] result["time"] = time.strftime("%H:%M:%S") #Some extra settings. From 4baf0d1bdb8c9aeab709cde37f400ae7db304543 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 11 Dec 2018 13:49:20 +0100 Subject: [PATCH 0840/1240] Always show average rating CURA-6013 --- plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml index f34d982cfb..f217d901b8 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml @@ -143,9 +143,7 @@ Item Label { - // If the user voted, show that value. Otherwsie show the average rating. - property real ratingtoUse: model.user_rating == 0 ? model.average_rating: model.user_rating - text: ratingtoUse.toFixed(1) + " (" + model.num_ratings + " " + catalog.i18nc("@label", "ratings") + ")" + text: model.average_rating.toFixed(1) + " (" + model.num_ratings + " " + catalog.i18nc("@label", "ratings") + ")" verticalAlignment: Text.AlignVCenter height: starIcon.height anchors.verticalCenter: starIcon.verticalCenter From 30057e2fcd4c1b6370178d24db903d84e83b0619 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 11 Dec 2018 13:49:45 +0100 Subject: [PATCH 0841/1240] Use user_rating instead of user In the end it was implemented as user_rating (and not as user) CURA-6013 --- plugins/Toolbox/src/PackagesModel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Toolbox/src/PackagesModel.py b/plugins/Toolbox/src/PackagesModel.py index 3f5be3bc37..d94fdf6bb7 100644 --- a/plugins/Toolbox/src/PackagesModel.py +++ b/plugins/Toolbox/src/PackagesModel.py @@ -107,7 +107,7 @@ class PackagesModel(ListModel): "login_required": "login-required" in package.get("tags", []), "average_rating": float(package.get("rating", {}).get("average", 0)), "num_ratings": package.get("rating", {}).get("count", 0), - "user_rating": package.get("rating", {}).get("user", 0) + "user_rating": package.get("rating", {}).get("user_rating", 0) }) # Filter on all the key-word arguments. From faa3f42acc6f88a63b26443b6edd500061689e93 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 11 Dec 2018 13:53:05 +0100 Subject: [PATCH 0842/1240] Modify the header in the simulation view menu component This is needed to align with the designs. --- .../SimulationViewMenuComponent.qml | 40 ++++++++++++++----- resources/qml/ViewsSelector.qml | 2 +- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/plugins/SimulationView/SimulationViewMenuComponent.qml b/plugins/SimulationView/SimulationViewMenuComponent.qml index 58b2bfe520..ed615bf4f6 100644 --- a/plugins/SimulationView/SimulationViewMenuComponent.qml +++ b/plugins/SimulationView/SimulationViewMenuComponent.qml @@ -35,14 +35,36 @@ Cura.ExpandableComponent } } - headerItem: Label + headerItem: Item { - id: layerViewTypesLabel - text: catalog.i18nc("@label", "Color scheme") - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("setting_control_text") - height: base.height - verticalAlignment: Text.AlignVCenter + Label + { + id: colorSchemeLabel + text: catalog.i18nc("@label", "Color scheme") + verticalAlignment: Text.AlignVCenter + height: parent.height + elide: Text.ElideRight + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text_medium") + renderType: Text.NativeRendering + } + + Label + { + text: layerTypeCombobox.currentText + verticalAlignment: Text.AlignVCenter + anchors + { + left: colorSchemeLabel.right + leftMargin: UM.Theme.getSize("default_margin").width + right: parent.right + } + height: parent.height + elide: Text.ElideRight + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + renderType: Text.NativeRendering + } } contentItem: Column @@ -125,7 +147,7 @@ Cura.ExpandableComponent Label { id: compatibilityModeLabel - text: catalog.i18nc("@label","Compatibility Mode") + text: catalog.i18nc("@label", "Compatibility Mode") font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") visible: UM.SimulationView.compatibilityMode @@ -136,7 +158,7 @@ Cura.ExpandableComponent Item // Spacer { - height: Math.round(UM.Theme.getSize("default_margin").width / 2) + height: UM.Theme.getSize("narrow_margin").width width: width } diff --git a/resources/qml/ViewsSelector.qml b/resources/qml/ViewsSelector.qml index f2906f9d4c..0e2598a0d8 100644 --- a/resources/qml/ViewsSelector.qml +++ b/resources/qml/ViewsSelector.qml @@ -43,7 +43,7 @@ Cura.ExpandablePopup Label { id: title - text: catalog.i18nc("@button", "View types") + text: catalog.i18nc("@label", "View types") verticalAlignment: Text.AlignVCenter height: parent.height elide: Text.ElideRight From db56aa028ca24e92ce8366063066f4fef466b6a6 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 11 Dec 2018 13:59:36 +0100 Subject: [PATCH 0843/1240] Change the swatch from a circle to the extruder icon --- plugins/SimulationView/SimulationViewMenuComponent.qml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/plugins/SimulationView/SimulationViewMenuComponent.qml b/plugins/SimulationView/SimulationViewMenuComponent.qml index ed615bf4f6..c2e9b0d6fa 100644 --- a/plugins/SimulationView/SimulationViewMenuComponent.qml +++ b/plugins/SimulationView/SimulationViewMenuComponent.qml @@ -183,17 +183,16 @@ Cura.ExpandableComponent style: UM.Theme.styles.checkbox - Rectangle + + UM.RecolorImage { + id: swatch anchors.verticalCenter: parent.verticalCenter anchors.right: extrudersModelCheckBox.right width: UM.Theme.getSize("layerview_legend_size").width height: UM.Theme.getSize("layerview_legend_size").height + source: UM.Theme.getIcon("extruder_button") color: model.color - radius: Math.round(width / 2) - border.width: UM.Theme.getSize("default_lining").width - border.color: UM.Theme.getColor("lining") - visible: !viewSettings.show_legend && !viewSettings.show_gradient } Label From 7a5701b00121e3a481e6f7dbd496c3250a299018 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 11 Dec 2018 14:12:34 +0100 Subject: [PATCH 0844/1240] Made smallrating widget re-usable Also added it to the details page CURA-6013 --- .../resources/qml/SmallRatingWidget.qml | 33 +++++ .../resources/qml/ToolboxDetailPage.qml | 114 +++++++++--------- .../qml/ToolboxDownloadsGridTile.qml | 24 +--- 3 files changed, 94 insertions(+), 77 deletions(-) create mode 100644 plugins/Toolbox/resources/qml/SmallRatingWidget.qml diff --git a/plugins/Toolbox/resources/qml/SmallRatingWidget.qml b/plugins/Toolbox/resources/qml/SmallRatingWidget.qml new file mode 100644 index 0000000000..f69e9349cf --- /dev/null +++ b/plugins/Toolbox/resources/qml/SmallRatingWidget.qml @@ -0,0 +1,33 @@ +import QtQuick 2.3 +import QtQuick.Controls 1.4 +import UM 1.1 as UM +import Cura 1.1 as Cura + + + +Row +{ + id: rating + height: UM.Theme.getSize("rating_star").height + visible: model.average_rating > 0 //Has a rating at all. + spacing: UM.Theme.getSize("thick_lining").width + + UM.RecolorImage + { + id: starIcon + source: UM.Theme.getIcon("star_filled") + color: model.user_rating == 0 ? "#5a5a5a" : UM.Theme.getColor("primary") + height: UM.Theme.getSize("rating_star").height + width: UM.Theme.getSize("rating_star").width + } + + Label + { + text: model.average_rating.toFixed(1) + " (" + model.num_ratings + " " + catalog.i18nc("@label", "ratings") + ")" + verticalAlignment: Text.AlignVCenter + height: starIcon.height + anchors.verticalCenter: starIcon.verticalCenter + color: starIcon.color + font: UM.Theme.getFont("small") + } +} \ No newline at end of file diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml index 51bb218293..4adbaa2435 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml @@ -57,16 +57,22 @@ Item top: thumbnail.top left: thumbnail.right leftMargin: UM.Theme.getSize("default_margin").width - right: parent.right - rightMargin: UM.Theme.getSize("wide_margin").width bottomMargin: UM.Theme.getSize("default_margin").height } text: details === null ? "" : (details.name || "") font: UM.Theme.getFont("large") color: UM.Theme.getColor("text") wrapMode: Text.WordWrap - width: parent.width - height: UM.Theme.getSize("toolbox_property_label").height + width: properties.width + values.width + height: contentHeight + } + + SmallRatingWidget + { + anchors.left: title.right + anchors.leftMargin: UM.Theme.getSize("default_margin").width + anchors.verticalCenter: title.verticalCenter + property var model: details } Column @@ -82,6 +88,12 @@ Item width: childrenRect.width height: childrenRect.height Label + { + text: catalog.i18nc("@label", "Rating") + ":" + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text_medium") + } + Label { text: catalog.i18nc("@label", "Version") + ":" font: UM.Theme.getFont("default") @@ -105,12 +117,6 @@ Item font: UM.Theme.getFont("default") color: UM.Theme.getColor("text_medium") } - Label - { - text: catalog.i18nc("@label", "Rating") + ":" - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text_medium") - } } Column { @@ -124,50 +130,6 @@ Item } spacing: Math.floor(UM.Theme.getSize("narrow_margin").height) height: childrenRect.height - Label - { - text: details === null ? "" : (details.version || catalog.i18nc("@label", "Unknown")) - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - } - Label - { - text: - { - if (details === null) - { - return "" - } - var date = new Date(details.last_updated) - return date.toLocaleString(UM.Preferences.getValue("general/language")) - } - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - } - Label - { - text: - { - if (details === null) - { - return "" - } - else - { - return "" + details.author_name + "" - } - } - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - linkColor: UM.Theme.getColor("text_link") - onLinkActivated: Qt.openUrlExternally(link) - } - Label - { - text: details === null ? "" : (details.download_count || catalog.i18nc("@label", "Unknown")) - font: UM.Theme.getFont("default") - color: UM.Theme.getColor("text") - } RatingWidget { id: rating @@ -212,6 +174,50 @@ Item } } } + Label + { + text: details === null ? "" : (details.version || catalog.i18nc("@label", "Unknown")) + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + } + Label + { + text: + { + if (details === null) + { + return "" + } + var date = new Date(details.last_updated) + return date.toLocaleString(UM.Preferences.getValue("general/language")) + } + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + } + Label + { + text: + { + if (details === null) + { + return "" + } + else + { + return "" + details.author_name + "" + } + } + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + linkColor: UM.Theme.getColor("text_link") + onLinkActivated: Qt.openUrlExternally(link) + } + Label + { + text: details === null ? "" : (details.download_count || catalog.i18nc("@label", "Unknown")) + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + } } Rectangle { diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml index f217d901b8..8cd8f2ffef 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml @@ -119,37 +119,15 @@ Item verticalAlignment: Text.AlignVCenter maximumLineCount: 2 } - Row + SmallRatingWidget { id: rating - height: UM.Theme.getSize("rating_star").height - visible: model.average_rating > 0 //Has a rating at all. - spacing: UM.Theme.getSize("thick_lining").width anchors { bottom: parent.bottom left: parent.left right: parent.right } - - UM.RecolorImage - { - id: starIcon - source: UM.Theme.getIcon("star_filled") - color: model.user_rating == 0 ? "#5a5a5a" : UM.Theme.getColor("primary") - height: UM.Theme.getSize("rating_star").height - width: UM.Theme.getSize("rating_star").width - } - - Label - { - text: model.average_rating.toFixed(1) + " (" + model.num_ratings + " " + catalog.i18nc("@label", "ratings") + ")" - verticalAlignment: Text.AlignVCenter - height: starIcon.height - anchors.verticalCenter: starIcon.verticalCenter - color: starIcon.color - font: UM.Theme.getFont("small") - } } } } From a5d8e6ceb8da1e5df1d0a5bc72dd962457f1b216 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Tue, 11 Dec 2018 14:28:00 +0100 Subject: [PATCH 0845/1240] STAR-322: Removing the print job after it was done --- .../src/Cloud/CloudApiClient.py | 24 +++++++++---------- .../src/Cloud/CloudOutputDevice.py | 3 +++ .../src/ClusterUM3OutputDevice.py | 24 +++++++++---------- 3 files changed, 27 insertions(+), 24 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py index 474d76d85a..b08bac6670 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py @@ -2,7 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. import json from json import JSONDecodeError -from typing import Callable, List, Type, TypeVar, Union, Optional, Tuple, Dict +from typing import Callable, List, Type, TypeVar, Union, Optional, Tuple, Dict, Any from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply @@ -19,7 +19,7 @@ from .Models.CloudPrintJobResponse import CloudPrintJobResponse ## The cloud API client is responsible for handling the requests and responses from the cloud. -# Each method should only handle models instead of exposing any HTTP details. +# Each method should only handle models instead of exposing Any HTTP details. class CloudApiClient(NetworkClient): # The cloud URL to use for this remote cluster. @@ -43,21 +43,21 @@ class CloudApiClient(NetworkClient): ## Retrieves all the clusters for the user that is currently logged in. # \param on_finished: The function to be called after the result is parsed. - def getClusters(self, on_finished: Callable[[List[CloudClusterResponse]], any]) -> None: + def getClusters(self, on_finished: Callable[[List[CloudClusterResponse]], Any]) -> None: url = "{}/clusters".format(self.CLUSTER_API_ROOT) self.get(url, on_finished=self._wrapCallback(on_finished, CloudClusterResponse)) ## Retrieves the status of the given cluster. # \param cluster_id: The ID of the cluster. # \param on_finished: The function to be called after the result is parsed. - def getClusterStatus(self, cluster_id: str, on_finished: Callable[[CloudClusterStatus], any]) -> None: + def getClusterStatus(self, cluster_id: str, on_finished: Callable[[CloudClusterStatus], Any]) -> None: url = "{}/clusters/{}/status".format(self.CLUSTER_API_ROOT, cluster_id) self.get(url, on_finished=self._wrapCallback(on_finished, CloudClusterStatus)) ## Requests the cloud to register the upload of a print job mesh. # \param request: The request object. # \param on_finished: The function to be called after the result is parsed. - def requestUpload(self, request: CloudPrintJobUploadRequest, on_finished: Callable[[CloudPrintJobResponse], any] + def requestUpload(self, request: CloudPrintJobUploadRequest, on_finished: Callable[[CloudPrintJobResponse], Any] ) -> None: url = "{}/jobs/upload".format(self.CURA_API_ROOT) body = json.dumps({"data": request.toDict()}) @@ -69,8 +69,8 @@ class CloudApiClient(NetworkClient): # \param on_finished: The function to be called after the result is parsed. It receives the print job ID. # \param on_progress: A function to be called during upload progress. It receives a percentage (0-100). # \param on_error: A function to be called if the upload fails. It receives a dict with the error. - def uploadMesh(self, upload_response: CloudPrintJobResponse, mesh: bytes, on_finished: Callable[[str], any], - on_progress: Callable[[int], any], on_error: Callable[[dict], any]): + def uploadMesh(self, upload_response: CloudPrintJobResponse, mesh: bytes, on_finished: Callable[[str], Any], + on_progress: Callable[[int], Any], on_error: Callable[[dict], Any]): def progressCallback(bytes_sent: int, bytes_total: int) -> None: if bytes_total: @@ -92,7 +92,7 @@ class CloudApiClient(NetworkClient): # \param cluster_id: The ID of the cluster. # \param job_id: The ID of the print job. # \param on_finished: The function to be called after the result is parsed. - def requestPrint(self, cluster_id: str, job_id: str, on_finished: Callable[[CloudPrintResponse], any]) -> None: + def requestPrint(self, cluster_id: str, job_id: str, on_finished: Callable[[CloudPrintResponse], Any]) -> None: url = "{}/clusters/{}/print/{}".format(self.CLUSTER_API_ROOT, cluster_id, job_id) self.post(url, data = "", on_finished=self._wrapCallback(on_finished, CloudPrintResponse)) @@ -110,7 +110,7 @@ class CloudApiClient(NetworkClient): # \param reply: The reply from the server. # \return A tuple with a status code and a dictionary. @staticmethod - def _parseReply(reply: QNetworkReply) -> Tuple[int, Dict[str, any]]: + def _parseReply(reply: QNetworkReply) -> Tuple[int, Dict[str, Any]]: status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) try: response = bytes(reply.readAll()).decode() @@ -128,8 +128,8 @@ class CloudApiClient(NetworkClient): # \param response: The response from the server, after being converted to a dict. # \param on_finished: The callback in case the response is successful. # \param model_class: The type of the model to convert the response to. It may either be a single record or a list. - def _parseModels(self, response: Dict[str, any], - on_finished: Callable[[Union[Model, List[Model]]], any], + def _parseModels(self, response: Dict[str, Any], + on_finished: Callable[[Union[Model, List[Model]]], Any], model_class: Type[Model]) -> None: if "data" in response: data = response["data"] @@ -145,7 +145,7 @@ class CloudApiClient(NetworkClient): # \param model: The type of the model to convert the response to. It may either be a single record or a list. # \return: A function that can be passed to the def _wrapCallback(self, - on_finished: Callable[[Union[Model, List[Model]]], any], + on_finished: Callable[[Union[Model, List[Model]]], Any], model: Type[Model], ) -> Callable[[QNetworkReply], None]: def parse(reply: QNetworkReply) -> None: diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index e68eb47839..bf80e0f84c 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -229,6 +229,9 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): removed_jobs, added_jobs, updated_jobs = findChanges(previous, received) for removed_job in removed_jobs: + if removed_job.assignedPrinter: + removed_job.assignedPrinter.updateActivePrintJob(None) + removed_job.stateChanged.disconnect(self._onPrintJobStateChanged) self._print_jobs.remove(removed_job) for added_job in added_jobs: diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index 5e43b602cc..96fee0d96d 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -56,8 +56,9 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): self._number_of_extruders = 2 - self._dummy_lambdas = ("", {}, io.BytesIO() - ) # type: Tuple[str, Dict[str, Union[str, int, bool]], Union[io.StringIO, io.BytesIO]] + self._dummy_lambdas = ( + "", {}, io.BytesIO() + ) # type: Tuple[Optional[str], Dict[str, Union[str, int, bool]], Union[io.StringIO, io.BytesIO]] self._print_jobs = [] # type: List[UM3PrintJobOutputModel] self._received_print_jobs = False # type: bool @@ -165,7 +166,8 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): self._sending_gcode = True - target_printer = yield #Potentially wait on the user to select a target printer. + # Potentially wait on the user to select a target printer. + target_printer = yield # type: Optional[str] # Using buffering greatly reduces the write time for many lines of gcode @@ -179,13 +181,12 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): use_inactivity_timer = False) self._write_job_progress_message.show() - self._dummy_lambdas = (target_printer, mesh_format.preferred_format, stream) - job.finished.connect(self._sendPrintJobWaitOnWriteJobFinished) - - job.start() - - yield True # Return that we had success! - yield # To prevent having to catch the StopIteration exception. + if mesh_format.preferred_format is not None: + self._dummy_lambdas = (target_printer, mesh_format.preferred_format, stream) + job.finished.connect(self._sendPrintJobWaitOnWriteJobFinished) + job.start() + yield True # Return that we had success! + yield # To prevent having to catch the StopIteration exception. def _sendPrintJobWaitOnWriteJobFinished(self, job: WriteFileJob) -> None: if self._write_job_progress_message: @@ -255,8 +256,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): # Treat upload progress as response. Uploading can take more than 10 seconds, so if we don't, we can get # timeout responses if this happens. self._last_response_time = time() - old_progress = self._progress_message.getProgress() - if self._progress_message and (old_progress is None or new_progress > old_progress): + if self._progress_message is not None and new_progress > self._progress_message.getProgress(): self._progress_message.show() # Ensure that the message is visible. self._progress_message.setProgress(bytes_sent / bytes_total * 100) From 742fc34e75305cbefdb339ef6fa08f239c538f61 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 11 Dec 2018 14:31:12 +0100 Subject: [PATCH 0846/1240] Change labels in the layer view legend --- plugins/SimulationView/SimulationViewMenuComponent.qml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/SimulationView/SimulationViewMenuComponent.qml b/plugins/SimulationView/SimulationViewMenuComponent.qml index c2e9b0d6fa..95d7d20006 100644 --- a/plugins/SimulationView/SimulationViewMenuComponent.qml +++ b/plugins/SimulationView/SimulationViewMenuComponent.qml @@ -222,25 +222,25 @@ Cura.ExpandableComponent Component.onCompleted: { typesLegendModel.append({ - label: catalog.i18nc("@label", "Show Travels"), + label: catalog.i18nc("@label", "Travels"), initialValue: viewSettings.show_travel_moves, preference: "layerview/show_travel_moves", colorId: "layerview_move_combing" }); typesLegendModel.append({ - label: catalog.i18nc("@label", "Show Helpers"), + label: catalog.i18nc("@label", "Helpers"), initialValue: viewSettings.show_helpers, preference: "layerview/show_helpers", colorId: "layerview_support" }); typesLegendModel.append({ - label: catalog.i18nc("@label", "Show Shell"), + label: catalog.i18nc("@label", "Shell"), initialValue: viewSettings.show_skin, preference: "layerview/show_skin", colorId: "layerview_inset_0" }); typesLegendModel.append({ - label: catalog.i18nc("@label", "Show Infill"), + label: catalog.i18nc("@label", "Infill"), initialValue: viewSettings.show_infill, preference: "layerview/show_infill", colorId: "layerview_infill" From fcaa23ed0ae2c168ff405ce7d9a66acf1ae19351 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 11 Dec 2018 15:16:21 +0100 Subject: [PATCH 0847/1240] Added smallRating widget to featured plugins CURA-6013 --- .../resources/qml/SmallRatingWidget.qml | 4 +- .../qml/ToolboxDownloadsShowcaseTile.qml | 103 +++++++----------- 2 files changed, 43 insertions(+), 64 deletions(-) diff --git a/plugins/Toolbox/resources/qml/SmallRatingWidget.qml b/plugins/Toolbox/resources/qml/SmallRatingWidget.qml index f69e9349cf..0b93131cfd 100644 --- a/plugins/Toolbox/resources/qml/SmallRatingWidget.qml +++ b/plugins/Toolbox/resources/qml/SmallRatingWidget.qml @@ -11,7 +11,7 @@ Row height: UM.Theme.getSize("rating_star").height visible: model.average_rating > 0 //Has a rating at all. spacing: UM.Theme.getSize("thick_lining").width - + width: starIcon.width + spacing + numRatingsLabel.width UM.RecolorImage { id: starIcon @@ -23,9 +23,11 @@ Row Label { + id: numRatingsLabel text: model.average_rating.toFixed(1) + " (" + model.num_ratings + " " + catalog.i18nc("@label", "ratings") + ")" verticalAlignment: Text.AlignVCenter height: starIcon.height + width: contentWidth anchors.verticalCenter: starIcon.verticalCenter color: starIcon.color font: UM.Theme.getFont("small") diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml index ceaadc110d..3e09654173 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml @@ -13,90 +13,67 @@ Rectangle property int installedPackages: toolbox.viewCategory == "material" ? toolbox.getNumberOfInstalledPackagesByAuthor(model.id) : (toolbox.isInstalled(model.id) ? 1 : 0) id: tileBase width: UM.Theme.getSize("toolbox_thumbnail_large").width + (2 * UM.Theme.getSize("default_lining").width) - height: thumbnail.height + packageNameBackground.height + (2 * UM.Theme.getSize("default_lining").width) + height: thumbnail.height + packageName.height + rating.height + UM.Theme.getSize("default_margin").width border.width: UM.Theme.getSize("default_lining").width border.color: UM.Theme.getColor("lining") - color: "transparent" - Rectangle + color: UM.Theme.getColor("main_background") + Image { id: thumbnail - color: UM.Theme.getColor("main_background") - width: UM.Theme.getSize("toolbox_thumbnail_large").width - height: UM.Theme.getSize("toolbox_thumbnail_large").height + height: UM.Theme.getSize("toolbox_thumbnail_large").height - 4 * UM.Theme.getSize("default_margin").height + width: UM.Theme.getSize("toolbox_thumbnail_large").height - 4 * UM.Theme.getSize("default_margin").height + fillMode: Image.PreserveAspectFit + source: model.icon_url || "../images/logobot.svg" + mipmap: true anchors { top: parent.top + topMargin: UM.Theme.getSize("default_margin").height horizontalCenter: parent.horizontalCenter - topMargin: UM.Theme.getSize("default_lining").width - } - Image - { - anchors.centerIn: parent - width: UM.Theme.getSize("toolbox_thumbnail_large").width - 2 * UM.Theme.getSize("default_margin").width - height: UM.Theme.getSize("toolbox_thumbnail_large").height - 2 * UM.Theme.getSize("default_margin").height - fillMode: Image.PreserveAspectFit - source: model.icon_url || "../images/logobot.svg" - mipmap: true - } - UM.RecolorImage - { - width: (parent.width * 0.3) | 0 - height: (parent.height * 0.3) | 0 - anchors - { - bottom: parent.bottom - right: parent.right - bottomMargin: UM.Theme.getSize("default_lining").width - } - visible: installedPackages != 0 - color: (installedPackages == packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border") - source: "../images/installed_check.svg" } } - Rectangle + Label { - id: packageNameBackground - color: UM.Theme.getColor("primary") + id: packageName + text: model.name anchors { - top: thumbnail.bottom horizontalCenter: parent.horizontalCenter + top: thumbnail.bottom } + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter height: UM.Theme.getSize("toolbox_heading_label").height width: parent.width - Label - { - id: packageName - text: model.name - anchors - { - horizontalCenter: parent.horizontalCenter - } - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - height: UM.Theme.getSize("toolbox_heading_label").height - width: parent.width - wrapMode: Text.WordWrap - color: UM.Theme.getColor("button_text") - font: UM.Theme.getFont("medium_bold") - } + wrapMode: Text.WordWrap + font: UM.Theme.getFont("medium_bold") } + UM.RecolorImage + { + width: (parent.width * 0.20) | 0 + height: (parent.height * 0.20) | 0 + anchors + { + bottom: parent.bottom + right: parent.right + bottomMargin: UM.Theme.getSize("default_lining").width + } + visible: installedPackages != 0 + color: (installedPackages == packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border") + source: "../images/installed_check.svg" + } + + SmallRatingWidget + { + id: rating + anchors.bottom: parent.bottom + anchors.bottomMargin: UM.Theme.getSize("narrow_margin").height + anchors.horizontalCenter: parent.horizontalCenter + } + MouseArea { anchors.fill: parent - hoverEnabled: true - onEntered: - { - packageName.color = UM.Theme.getColor("button_text_hover") - packageNameBackground.color = UM.Theme.getColor("primary_hover") - tileBase.border.color = UM.Theme.getColor("primary_hover") - } - onExited: - { - packageName.color = UM.Theme.getColor("button_text") - packageNameBackground.color = UM.Theme.getColor("primary") - tileBase.border.color = UM.Theme.getColor("lining") - } onClicked: { base.selection = model From c804610fa6ab8cad1b52b34666200c2026ef3732 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 11 Dec 2018 15:19:40 +0100 Subject: [PATCH 0848/1240] Make the stage menu in the preview using 85 percent of the total width in order to prevent the print setup panel to be in the middle --- plugins/PreviewStage/PreviewMenu.qml | 92 +++++++++++-------- .../SimulationViewMenuComponent.qml | 1 - resources/themes/cura-light/theme.json | 4 +- 3 files changed, 54 insertions(+), 43 deletions(-) diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index 1543536160..4e1fbac837 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -2,6 +2,7 @@ // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.7 +import QtQuick.Layouts 1.1 import QtQuick.Controls 2.3 import UM 1.3 as UM @@ -19,56 +20,67 @@ Item name: "cura" } - - Row + // Item to ensure that all of the buttons are nicely centered. + Item { - id: stageMenuRow - anchors.centerIn: parent + anchors.horizontalCenter: parent.horizontalCenter + width: stageMenuRow.width height: parent.height - Cura.ViewsSelector + RowLayout { - id: viewsSelector + id: stageMenuRow + width: Math.round(0.85 * previewMenu.width) height: parent.height - width: UM.Theme.getSize("views_selector").width - headerCornerSide: Cura.RoundedRectangle.Direction.Left - } + spacing: 0 - // Separator line - Rectangle - { - height: parent.height - // If there is no viewPanel, we only need a single spacer, so hide this one. - visible: viewPanel.source != "" - width: visible ? UM.Theme.getSize("default_lining").width : 0 + Cura.ViewsSelector + { + id: viewsSelector + headerCornerSide: Cura.RoundedRectangle.Direction.Left + Layout.minimumWidth: UM.Theme.getSize("views_selector").width + Layout.maximumWidth: UM.Theme.getSize("views_selector").width + Layout.fillWidth: true + Layout.fillHeight: true + } - color: UM.Theme.getColor("lining") - } + // Separator line + Rectangle + { + height: parent.height + // If there is no viewPanel, we only need a single spacer, so hide this one. + visible: viewPanel.source != "" + width: UM.Theme.getSize("default_lining").width - Loader - { - id: viewPanel - height: parent.height - width: childrenRect.width - source: UM.Controller.activeView != null && UM.Controller.activeView.stageMenuComponent != null ? UM.Controller.activeView.stageMenuComponent : "" - } + color: UM.Theme.getColor("lining") + } - // Separator line - Rectangle - { - height: parent.height - width: UM.Theme.getSize("default_lining").width - color: UM.Theme.getColor("lining") - } + Loader + { + id: viewPanel + Layout.fillHeight: true + Layout.fillWidth: true + Layout.preferredWidth: stageMenuRow.width - viewsSelector.width - printSetupSelectorItem.width - 2 * UM.Theme.getSize("default_lining").width + source: UM.Controller.activeView != null && UM.Controller.activeView.stageMenuComponent != null ? UM.Controller.activeView.stageMenuComponent : "" + } - Item - { - id: printSetupSelectorItem - // This is a work around to prevent the printSetupSelector from having to be re-loaded every time - // a stage switch is done. - children: [printSetupSelector] - height: childrenRect.height - width: childrenRect.width + // Separator line + Rectangle + { + height: parent.height + width: UM.Theme.getSize("default_lining").width + color: UM.Theme.getColor("lining") + } + + Item + { + id: printSetupSelectorItem + // This is a work around to prevent the printSetupSelector from having to be re-loaded every time + // a stage switch is done. + children: [printSetupSelector] + height: childrenRect.height + width: childrenRect.width + } } } } diff --git a/plugins/SimulationView/SimulationViewMenuComponent.qml b/plugins/SimulationView/SimulationViewMenuComponent.qml index 95d7d20006..9f43252126 100644 --- a/plugins/SimulationView/SimulationViewMenuComponent.qml +++ b/plugins/SimulationView/SimulationViewMenuComponent.qml @@ -15,7 +15,6 @@ Cura.ExpandableComponent { id: base - width: UM.Theme.getSize("layerview_menu_size").width contentHeaderTitle: catalog.i18nc("@label", "Color scheme") Connections diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 3dc216ad70..0c17a34e9a 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -380,7 +380,7 @@ "machine_selector_widget_content": [25.0, 32.0], "machine_selector_icon": [2.66, 2.66], - "views_selector": [16.0, 4.5], + "views_selector": [23.0, 4.0], "printer_type_label": [3.5, 1.5], @@ -450,7 +450,7 @@ "slider_handle": [1.5, 1.5], "slider_layerview_size": [1.0, 26.0], - "layerview_menu_size": [15, 20], + "layerview_menu_size": [16.0, 4.0], "layerview_legend_size": [1.0, 1.0], "layerview_row": [11.0, 1.5], "layerview_row_spacing": [0.0, 0.5], From 64dc73aed3f6d043c5ab7c81c54f54495b951abe Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 11 Dec 2018 15:47:57 +0100 Subject: [PATCH 0849/1240] remove incorrect __init__.py --- __init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 __init__.py diff --git a/__init__.py b/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 From daeb86bdb1ec55400b86edc14ec76c66cba4dfbd Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Tue, 11 Dec 2018 15:55:03 +0100 Subject: [PATCH 0850/1240] Use short printer family tags Contributes to CL-1173 --- .../resources/qml/MonitorPrintJobCard.qml | 1 + .../resources/qml/MonitorPrinterPill.qml | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml index 5eaeff2e84..d8c5d1ec28 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml @@ -97,6 +97,7 @@ Item return "" } visible: printJob + width: 120 * screenScaleFactor // TODO: Theme! // FIXED-LINE-HEIGHT: height: 18 * screenScaleFactor // TODO: Theme! diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterPill.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterPill.qml index cd78f1b11f..94d9c7e7d0 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterPill.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterPill.qml @@ -12,7 +12,19 @@ import UM 1.2 as UM Item { // The printer name - property alias text: printerNameLabel.text; + property var text: "" + property var tagText: { + switch(text) { + case "Ultimaker 3": + return "UM 3" + case "Ultimaker 3 Extended": + return "UM 3 EXT" + case "Ultimaker S5": + return "UM S5" + default: + return "" + } + } implicitHeight: 18 * screenScaleFactor // TODO: Theme! implicitWidth: printerNameLabel.contentWidth + 12 // TODO: Theme! @@ -28,7 +40,7 @@ Item id: printerNameLabel anchors.centerIn: parent color: "#535369" // TODO: Theme! - text: "" + text: tagText font.pointSize: 10 } } \ No newline at end of file From 0467756ed6ef4d4ae194cccede5465dd78a401c9 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Tue, 11 Dec 2018 16:01:52 +0100 Subject: [PATCH 0851/1240] STAR-322: Adding return type to init method --- plugins/UM3NetworkPrinting/src/Cloud/CloudOutputController.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputController.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputController.py index b2a8d8649b..c139be0c38 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputController.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputController.py @@ -5,7 +5,7 @@ from cura.PrinterOutput.PrinterOutputController import PrinterOutputController class CloudOutputController(PrinterOutputController): - def __init__(self, output_device: OutputDevice): + def __init__(self, output_device: OutputDevice) -> None: super().__init__(output_device) # The cloud connection only supports fetching the printer and queue status and adding a job to the queue. From b993a7b0d92c8d1d357d639716aab280bb8f0da0 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 11 Dec 2018 16:05:24 +0100 Subject: [PATCH 0852/1240] Change the header color and font in the configuration popup Also fix some alignments with the printer type selector. Contributes to CURA-5876. --- resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml | 5 ++--- .../qml/Menus/ConfigurationMenu/CustomConfiguration.qml | 6 ++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml index 68c56c7c4b..2e8be05fef 100644 --- a/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml @@ -16,8 +16,8 @@ Item { id: header text: catalog.i18nc("@header", "Configurations") - font: UM.Theme.getFont("large") - color: UM.Theme.getColor("text") + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("small_button_text") height: contentHeight renderType: Text.NativeRendering @@ -31,7 +31,6 @@ Item ConfigurationListView { anchors.top: header.bottom - anchors.topMargin: UM.Theme.getSize("default_margin").width width: parent.width outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index 8429e2c093..cbe4263e33 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -23,8 +23,8 @@ Item { id: header text: catalog.i18nc("@header", "Custom") - font: UM.Theme.getFont("large") - color: UM.Theme.getColor("text") + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("small_button_text") height: contentHeight renderType: Text.NativeRendering @@ -51,9 +51,7 @@ Item anchors { left: parent.left - leftMargin: UM.Theme.getSize("default_margin").width right: parent.right - rightMargin: UM.Theme.getSize("default_margin").width top: header.bottom topMargin: visible ? UM.Theme.getSize("default_margin").height : 0 } From f99c788eb63bef4035ce7694c3a94b77277e40ad Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 11 Dec 2018 16:35:59 +0100 Subject: [PATCH 0853/1240] Change some colors for the arrows in some setting selectors Contributes to CURA-5876. --- resources/qml/ActionButton.qml | 2 +- .../ConfigurationMenu/ConfigurationItem.qml | 4 ++-- .../ConfigurationMenu/CustomConfiguration.qml | 12 +++++----- resources/qml/PrinterOutput/ExtruderBox.qml | 2 +- resources/qml/PrinterOutput/HeatedBedBox.qml | 2 +- .../qml/PrinterOutput/MonitorSection.qml | 2 +- .../PrinterSelector/MachineSelectorButton.qml | 2 +- resources/qml/Settings/SettingCategory.qml | 23 ++----------------- resources/qml/Settings/SettingComboBox.qml | 2 +- resources/qml/Settings/SettingExtruder.qml | 2 +- .../qml/Settings/SettingOptionalExtruder.qml | 2 +- resources/qml/ViewsSelector.qml | 2 +- resources/themes/cura-light/styles.qml | 10 ++++---- resources/themes/cura-light/theme.json | 15 ------------ 14 files changed, 24 insertions(+), 58 deletions(-) diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index 7177120f35..c3d39e8251 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -62,7 +62,7 @@ Button id: buttonText text: button.text color: button.enabled ? (button.hovered ? button.textHoverColor : button.textColor): button.textDisabledColor - font: UM.Theme.getFont("action_button") + font: UM.Theme.getFont("medium") visible: text != "" renderType: Text.NativeRendering anchors.verticalCenter: parent.verticalCenter diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 728a0cbe9a..9dae075b48 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -20,8 +20,8 @@ Button { height: childrenRect.height color: parent.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button") - border.color: (parent.checked || parent.hovered) ? UM.Theme.getColor("primary") : UM.Theme.getColor("lining") - border.width: parent.checked ? UM.Theme.getSize("thick_lining").width : UM.Theme.getSize("default_lining").width + border.color: parent.checked ? UM.Theme.getColor("primary") : UM.Theme.getColor("lining") + border.width: UM.Theme.getSize("default_lining").width radius: UM.Theme.getSize("default_radius").width Column diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index cbe4263e33..7181f841d1 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -60,7 +60,7 @@ Item { text: catalog.i18nc("@label", "Printer") width: Math.round(parent.width * 0.3) - UM.Theme.getSize("default_margin").width - height: contentHeight +// height: contentHeight font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") anchors.verticalCenter: printerTypeSelector.verticalCenter @@ -72,7 +72,7 @@ Item id: printerTypeSelector text: Cura.MachineManager.activeMachineDefinitionName tooltip: Cura.MachineManager.activeMachineDefinitionName - height: UM.Theme.getSize("setting_control").height + height: UM.Theme.getSize("print_setup_big_item").height width: Math.round(parent.width * 0.7) + UM.Theme.getSize("default_margin").width anchors.right: parent.right style: UM.Theme.styles.print_setup_header_button @@ -222,7 +222,7 @@ Item Row { - height: UM.Theme.getSize("print_setup_item").height + height: UM.Theme.getSize("print_setup_big_item").height visible: Cura.MachineManager.hasMaterials Label @@ -246,7 +246,7 @@ Item text: Cura.MachineManager.activeStack != null ? Cura.MachineManager.activeStack.material.name : "" tooltip: text - height: UM.Theme.getSize("setting_control").height + height: UM.Theme.getSize("print_setup_big_item").height width: selectors.controlWidth style: UM.Theme.styles.print_setup_header_button @@ -260,7 +260,7 @@ Item Row { - height: UM.Theme.getSize("print_setup_item").height + height: UM.Theme.getSize("print_setup_big_item").height visible: Cura.MachineManager.hasVariants Label @@ -280,7 +280,7 @@ Item text: Cura.MachineManager.activeVariantName tooltip: Cura.MachineManager.activeVariantName - height: UM.Theme.getSize("setting_control").height + height: UM.Theme.getSize("print_setup_big_item").height width: selectors.controlWidth style: UM.Theme.styles.print_setup_header_button activeFocusOnPress: true; diff --git a/resources/qml/PrinterOutput/ExtruderBox.qml b/resources/qml/PrinterOutput/ExtruderBox.qml index 247bb3a27d..9ba78f778f 100644 --- a/resources/qml/PrinterOutput/ExtruderBox.qml +++ b/resources/qml/PrinterOutput/ExtruderBox.qml @@ -326,7 +326,7 @@ Item return UM.Theme.getColor("action_button_text"); } } - font: UM.Theme.getFont("action_button") + font: UM.Theme.getFont("medium") text: { if(extruderModel == null) diff --git a/resources/qml/PrinterOutput/HeatedBedBox.qml b/resources/qml/PrinterOutput/HeatedBedBox.qml index 33cf5cd1e2..ac541f707c 100644 --- a/resources/qml/PrinterOutput/HeatedBedBox.qml +++ b/resources/qml/PrinterOutput/HeatedBedBox.qml @@ -320,7 +320,7 @@ Item return UM.Theme.getColor("action_button_text"); } } - font: UM.Theme.getFont("action_button") + font: UM.Theme.getFont("medium") text: { if(printerModel == null) diff --git a/resources/qml/PrinterOutput/MonitorSection.qml b/resources/qml/PrinterOutput/MonitorSection.qml index 7ef89dabf7..1d9df777b6 100644 --- a/resources/qml/PrinterOutput/MonitorSection.qml +++ b/resources/qml/PrinterOutput/MonitorSection.qml @@ -27,7 +27,7 @@ Item anchors.left: parent.left anchors.leftMargin: UM.Theme.getSize("default_margin").width text: label - font: UM.Theme.getFont("setting_category") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("setting_category_text") } } diff --git a/resources/qml/PrinterSelector/MachineSelectorButton.qml b/resources/qml/PrinterSelector/MachineSelectorButton.qml index b88af35f82..39e63d27c3 100644 --- a/resources/qml/PrinterSelector/MachineSelectorButton.qml +++ b/resources/qml/PrinterSelector/MachineSelectorButton.qml @@ -42,7 +42,7 @@ Button } text: machineSelectorButton.text color: UM.Theme.getColor("text") - font: UM.Theme.getFont("action_button") + font: UM.Theme.getFont("medium") visible: text != "" renderType: Text.NativeRendering verticalAlignment: Text.AlignVCenter diff --git a/resources/qml/Settings/SettingCategory.qml b/resources/qml/Settings/SettingCategory.qml index 5676bcedf9..da731bcd55 100644 --- a/resources/qml/Settings/SettingCategory.qml +++ b/resources/qml/Settings/SettingCategory.qml @@ -73,7 +73,7 @@ Button text: definition.label textFormat: Text.PlainText renderType: Text.NativeRendering - font: UM.Theme.getFont("setting_category") + font: UM.Theme.getFont("default") color: { if (!base.enabled) @@ -106,26 +106,7 @@ Button width: UM.Theme.getSize("standard_arrow").width height: UM.Theme.getSize("standard_arrow").height sourceSize.height: width - color: - { - if (!base.enabled) - { - return UM.Theme.getColor("setting_category_disabled_text") - } - else if ((base.hovered || base.activeFocus) && base.checkable && base.checked) - { - return UM.Theme.getColor("setting_category_active_hover_text") - } - else if (base.pressed || (base.checkable && base.checked)) - { - return UM.Theme.getColor("setting_category_active_text") - } - else if (base.hovered || base.activeFocus) - { - return UM.Theme.getColor("setting_category_hover_text") - } - return UM.Theme.getColor("setting_category_text") - } + color: UM.Theme.getColor("setting_control_button") source: base.checked ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_left") } } diff --git a/resources/qml/Settings/SettingComboBox.qml b/resources/qml/Settings/SettingComboBox.qml index 13d2a0eb8f..a287e0c3ce 100644 --- a/resources/qml/Settings/SettingComboBox.qml +++ b/resources/qml/Settings/SettingComboBox.qml @@ -63,7 +63,7 @@ SettingItem sourceSize.width: width + 5 * screenScaleFactor sourceSize.height: width + 5 * screenScaleFactor - color: UM.Theme.getColor("setting_control_text") + color: UM.Theme.getColor("setting_control_button") } contentItem: Label diff --git a/resources/qml/Settings/SettingExtruder.qml b/resources/qml/Settings/SettingExtruder.qml index e1fedd9274..a6c1beb3e5 100644 --- a/resources/qml/Settings/SettingExtruder.qml +++ b/resources/qml/Settings/SettingExtruder.qml @@ -105,7 +105,7 @@ SettingItem sourceSize.width: width + 5 * screenScaleFactor sourceSize.height: width + 5 * screenScaleFactor - color: UM.Theme.getColor("setting_control_text"); + color: UM.Theme.getColor("setting_control_button"); } background: Rectangle diff --git a/resources/qml/Settings/SettingOptionalExtruder.qml b/resources/qml/Settings/SettingOptionalExtruder.qml index 200a3f64f1..aa843e2719 100644 --- a/resources/qml/Settings/SettingOptionalExtruder.qml +++ b/resources/qml/Settings/SettingOptionalExtruder.qml @@ -102,7 +102,7 @@ SettingItem sourceSize.width: width + 5 * screenScaleFactor sourceSize.height: width + 5 * screenScaleFactor - color: UM.Theme.getColor("setting_control_text"); + color: UM.Theme.getColor("setting_control_button"); } background: Rectangle diff --git a/resources/qml/ViewsSelector.qml b/resources/qml/ViewsSelector.qml index f2906f9d4c..58749c09f2 100644 --- a/resources/qml/ViewsSelector.qml +++ b/resources/qml/ViewsSelector.qml @@ -105,7 +105,7 @@ Cura.ExpandablePopup id: buttonText text: viewsSelectorButton.text color: UM.Theme.getColor("text") - font: UM.Theme.getFont("action_button") + font: UM.Theme.getFont("medium") renderType: Text.NativeRendering verticalAlignment: Text.AlignVCenter elide: Text.ElideRight diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index bcc754f4ca..42b63e84f7 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -75,7 +75,7 @@ QtObject width: Theme.getSize("standard_arrow").width height: Theme.getSize("standard_arrow").height sourceSize.height: width - color: control.enabled ? Theme.getColor("setting_category_text") : Theme.getColor("setting_category_disabled_text") + color: control.enabled ? Theme.getColor("setting_control_button") : Theme.getColor("setting_category_disabled_text") source: Theme.getIcon("arrow_bottom") } Label @@ -208,7 +208,7 @@ QtObject anchors.verticalCenter: parent.verticalCenter; text: control.text; - font: Theme.getFont("button_tooltip"); + font: Theme.getFont("default"); color: Theme.getColor("tooltip_text"); } } @@ -446,7 +446,7 @@ QtObject sourceSize.width: width + 5 * screenScaleFactor sourceSize.height: width + 5 * screenScaleFactor - color: Theme.getColor("setting_control_text"); + color: Theme.getColor("setting_control_button"); } } } @@ -513,7 +513,7 @@ QtObject sourceSize.width: width + 5 * screenScaleFactor sourceSize.height: width + 5 * screenScaleFactor - color: UM.Theme.getColor("setting_control_text") + color: UM.Theme.getColor("setting_control_button") } } } @@ -716,7 +716,7 @@ QtObject return UM.Theme.getColor("action_button_text"); } } - font: UM.Theme.getFont("action_button") + font: UM.Theme.getFont("medium") text: control.text } } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 3dc216ad70..f812d4245f 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -49,21 +49,6 @@ "size": 0.7, "weight": 50, "family": "Noto Sans" - }, - "button_tooltip": { - "size": 1.0, - "weight": 50, - "family": "Noto Sans" - }, - "setting_category": { - "size": 1.15, - "weight": 63, - "family": "Noto Sans" - }, - "action_button": { - "size": 1.15, - "weight": 50, - "family": "Noto Sans" } }, From 1ababf38ad1cd50946ebca15a72e971e949223a7 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Tue, 11 Dec 2018 16:42:44 +0100 Subject: [PATCH 0854/1240] Use full printer name if no tag is known Contributes to CL-1173 --- plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterPill.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterPill.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterPill.qml index 94d9c7e7d0..80a089cc2a 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterPill.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterPill.qml @@ -22,7 +22,7 @@ Item case "Ultimaker S5": return "UM S5" default: - return "" + return text } } From ed8292c47243c0329c3b2bed04a49acc15b79852 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 11 Dec 2018 17:00:02 +0100 Subject: [PATCH 0855/1240] Adjust size and margins of the icon in the action button Contributes to CURA-5876. --- resources/qml/ActionButton.qml | 5 +++-- .../qml/Menus/ConfigurationMenu/CustomConfiguration.qml | 2 +- resources/themes/cura-light/theme.json | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index c3d39e8251..3a9552cd9c 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -43,12 +43,13 @@ Button contentItem: Row { + spacing: UM.Theme.getSize("narrow_margin").width //Left side icon. Only displayed if !isIconOnRightSide. UM.RecolorImage { id: buttonIconLeft source: "" - height: buttonText.height + height: UM.Theme.getSize("action_button_icon").height width: visible ? height : 0 sourceSize.width: width sourceSize.height: height @@ -76,7 +77,7 @@ Button { id: buttonIconRight source: buttonIconLeft.source - height: buttonText.height + height: UM.Theme.getSize("action_button_icon").height width: visible ? height : 0 sourceSize.width: width sourceSize.height: height diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index 7181f841d1..4d6d80c1b4 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -60,7 +60,7 @@ Item { text: catalog.i18nc("@label", "Printer") width: Math.round(parent.width * 0.3) - UM.Theme.getSize("default_margin").width -// height: contentHeight + height: contentHeight font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") anchors.verticalCenter: printerTypeSelector.verticalCenter diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index f812d4245f..b4d0ab7092 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -407,6 +407,7 @@ "button_lining": [0, 0], "action_button": [15.0, 3.0], + "action_button_icon": [1.0, 1.0], "action_button_radius": [0.15, 0.15], "small_button": [2, 2], From b413b4cdb69fd3f6c46f93c3797c92bd9abc53b8 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 11 Dec 2018 17:21:14 +0100 Subject: [PATCH 0856/1240] Correct a typo in typing. [CURA-6016] --- cura/Machines/Models/BaseMaterialsModel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/Machines/Models/BaseMaterialsModel.py b/cura/Machines/Models/BaseMaterialsModel.py index 3cd92bb6b4..629e5c2b48 100644 --- a/cura/Machines/Models/BaseMaterialsModel.py +++ b/cura/Machines/Models/BaseMaterialsModel.py @@ -59,7 +59,7 @@ class BaseMaterialsModel(ListModel): self._extruder_stack = None self._available_materials = None # type: Optional[Dict[str, MaterialNode]] - self._favorite_ids = set() # type: Set(str) + self._favorite_ids = set() # type: Set[str] def _updateExtruderStack(self): global_stack = self._machine_manager.activeMachine From 0b6841e5d9ba0af192866c1dbe0cec21c14debe1 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 12 Dec 2018 09:19:43 +0100 Subject: [PATCH 0857/1240] Revert "Use the capitalized version of the buildplate name" This reverts commit 11d8831d7a9a15e3e916d5d9762bfe1f755042e5. Contributes to CURA-6021. --- cura/PrinterOutput/ConfigurationModel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/PrinterOutput/ConfigurationModel.py b/cura/PrinterOutput/ConfigurationModel.py index 6f55aa3b1f..89e609c913 100644 --- a/cura/PrinterOutput/ConfigurationModel.py +++ b/cura/PrinterOutput/ConfigurationModel.py @@ -44,7 +44,7 @@ class ConfigurationModel(QObject): @pyqtProperty(str, fset = setBuildplateConfiguration, notify = configurationChanged) def buildplateConfiguration(self) -> str: - return self._buildplate_configuration.capitalize() + return self._buildplate_configuration ## This method is intended to indicate whether the configuration is valid or not. # The method checks if the mandatory fields are or not set From 4d57aa8ea42096dde3ef1db3f370e672ebd89b2b Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 12 Dec 2018 09:28:15 +0100 Subject: [PATCH 0858/1240] Simplify the rating widget CURA-6013 --- plugins/Toolbox/resources/qml/RatingWidget.qml | 10 ---------- plugins/Toolbox/resources/qml/ToolboxDetailPage.qml | 2 -- 2 files changed, 12 deletions(-) diff --git a/plugins/Toolbox/resources/qml/RatingWidget.qml b/plugins/Toolbox/resources/qml/RatingWidget.qml index fbe782b2e2..355b99c0c4 100644 --- a/plugins/Toolbox/resources/qml/RatingWidget.qml +++ b/plugins/Toolbox/resources/qml/RatingWidget.qml @@ -10,8 +10,6 @@ Item property int indexHovered: -1 property string packageId: "" - property int numRatings: 0 - property int userRating: 0 signal rated(int rating) @@ -88,14 +86,6 @@ Item onClicked: rated(index + 1) // Notify anyone who cares about this. } } - Label - { - text: "(" + numRatings + ")" - verticalAlignment: Text.AlignVCenter - height: parent.height - color: "#5a5a5a" - font: UM.Theme.getFont("small") - } } } } \ No newline at end of file diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml index 4adbaa2435..52af1d6ddf 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml @@ -135,8 +135,6 @@ Item id: rating visible: details.type == "plugin" packageId: details.id - rating: details.average_rating - numRatings: details.num_ratings userRating: details.user_rating enabled: toolbox.isInstalled(details.id) && Cura.API.account.isLoggedIn From a6dbba709088c93b110abd874b82785eb612f4f7 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 12 Dec 2018 09:41:35 +0100 Subject: [PATCH 0859/1240] Minor UI tweaks CURA-6013 --- plugins/Toolbox/resources/qml/ToolboxDetailPage.qml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml index 52af1d6ddf..1d977883f9 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml @@ -26,7 +26,7 @@ Item right: parent.right rightMargin: UM.Theme.getSize("wide_margin").width } - height: UM.Theme.getSize("toolbox_detail_header").height + height: childrenRect.height + 3 * UM.Theme.getSize("default_margin").width Rectangle { id: thumbnail @@ -62,8 +62,7 @@ Item text: details === null ? "" : (details.name || "") font: UM.Theme.getFont("large") color: UM.Theme.getColor("text") - wrapMode: Text.WordWrap - width: properties.width + values.width + width: contentWidth height: contentHeight } From 8c07a6e89b97c6a545720e481a606f4f2a7e29f7 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 12 Dec 2018 10:35:36 +0100 Subject: [PATCH 0860/1240] Fix typing issues CURA-6005 --- plugins/CuraDrive/src/DriveApiService.py | 8 ++++---- plugins/CuraDrive/src/DrivePluginExtension.py | 3 ++- plugins/CuraDrive/src/UploadBackupJob.py | 4 ++-- plugins/CuraDrive/src/models/BackupListModel.py | 6 +++--- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/plugins/CuraDrive/src/DriveApiService.py b/plugins/CuraDrive/src/DriveApiService.py index a677466838..6963e595b5 100644 --- a/plugins/CuraDrive/src/DriveApiService.py +++ b/plugins/CuraDrive/src/DriveApiService.py @@ -3,7 +3,7 @@ import base64 import hashlib from datetime import datetime from tempfile import NamedTemporaryFile -from typing import Optional, List, Dict +from typing import Any, Optional, List, Dict import requests @@ -34,7 +34,7 @@ class DriveApiService: """Create a new instance of the Drive API service and set the cura_api object.""" self._cura_api = cura_api - def getBackups(self) -> List[Dict[str, any]]: + def getBackups(self) -> List[Dict[str, Any]]: """Get all backups from the API.""" access_token = self._cura_api.account.accessToken if not access_token: @@ -85,7 +85,7 @@ class DriveApiService: else: self.onCreatingStateChanged.emit(is_creating=False) - def restoreBackup(self, backup: Dict[str, any]) -> None: + def restoreBackup(self, backup: Dict[str, Any]) -> None: """ Restore a previously exported backup from cloud storage. :param backup: A dict containing an entry from the API list response. @@ -157,7 +157,7 @@ class DriveApiService: return False return True - def _requestBackupUpload(self, backup_metadata: Dict[str, any], backup_size: int) -> Optional[str]: + def _requestBackupUpload(self, backup_metadata: Dict[str, Any], backup_size: int) -> Optional[str]: """ Request a backup upload slot from the API. :param backup_metadata: A dict containing some meta data about the backup. diff --git a/plugins/CuraDrive/src/DrivePluginExtension.py b/plugins/CuraDrive/src/DrivePluginExtension.py index 556fb187df..7e1472b988 100644 --- a/plugins/CuraDrive/src/DrivePluginExtension.py +++ b/plugins/CuraDrive/src/DrivePluginExtension.py @@ -70,7 +70,8 @@ class DrivePluginExtension(QObject, Extension): if not self._drive_window: self._drive_window = self.createDriveWindow() self.refreshBackups() - self._drive_window.show() + if self._drive_window: + self._drive_window.show() def createDriveWindow(self) -> Optional["QObject"]: """ diff --git a/plugins/CuraDrive/src/UploadBackupJob.py b/plugins/CuraDrive/src/UploadBackupJob.py index 039e6d1a09..bcecce554a 100644 --- a/plugins/CuraDrive/src/UploadBackupJob.py +++ b/plugins/CuraDrive/src/UploadBackupJob.py @@ -14,14 +14,14 @@ class UploadBackupJob(Job): As it can take longer than some other tasks, we schedule this using a Cura Job. """ - def __init__(self, signed_upload_url: str, backup_zip: bytes): + def __init__(self, signed_upload_url: str, backup_zip: bytes) -> None: super().__init__() self._signed_upload_url = signed_upload_url self._backup_zip = backup_zip self._upload_success = False self.backup_upload_error_message = "" - def run(self): + def run(self) -> None: Message(Settings.translatable_messages["uploading_backup"], title = Settings.MESSAGE_TITLE, lifetime = 10).show() diff --git a/plugins/CuraDrive/src/models/BackupListModel.py b/plugins/CuraDrive/src/models/BackupListModel.py index 9567b3d255..93b0c4c48c 100644 --- a/plugins/CuraDrive/src/models/BackupListModel.py +++ b/plugins/CuraDrive/src/models/BackupListModel.py @@ -1,5 +1,5 @@ # Copyright (c) 2018 Ultimaker B.V. -from typing import List, Dict +from typing import Any, List, Dict from UM.Qt.ListModel import ListModel @@ -11,7 +11,7 @@ class BackupListModel(ListModel): The BackupListModel transforms the backups data that came from the server so it can be served to the Qt UI. """ - def __init__(self, parent=None): + def __init__(self, parent = None) -> None: super().__init__(parent) self.addRoleName(Qt.UserRole + 1, "backup_id") self.addRoleName(Qt.UserRole + 2, "download_url") @@ -19,7 +19,7 @@ class BackupListModel(ListModel): self.addRoleName(Qt.UserRole + 4, "md5_hash") self.addRoleName(Qt.UserRole + 5, "data") - def loadBackups(self, data: List[Dict[str, any]]) -> None: + def loadBackups(self, data: List[Dict[str, Any]]) -> None: """ Populate the model with server data. :param data: From 95f70a75d8629663dd42b2fdac148407cc993ed9 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 12 Dec 2018 10:39:56 +0100 Subject: [PATCH 0861/1240] Ensure that package name has margins in showcase tile CURA-6013 --- plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml index 3e09654173..ca0226b39d 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml @@ -44,7 +44,7 @@ Rectangle verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter height: UM.Theme.getSize("toolbox_heading_label").height - width: parent.width + width: parent.width - UM.Theme.getSize("default_margin").width wrapMode: Text.WordWrap font: UM.Theme.getFont("medium_bold") } From 31331e36d2fa6468973f1f1d26ae7df3d8c0d6a9 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 12 Dec 2018 09:19:43 +0100 Subject: [PATCH 0862/1240] Revert "Use the capitalized version of the buildplate name" This reverts commit 11d8831d7a9a15e3e916d5d9762bfe1f755042e5. Contributes to CURA-6021. --- cura/PrinterOutput/ConfigurationModel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/PrinterOutput/ConfigurationModel.py b/cura/PrinterOutput/ConfigurationModel.py index 6f55aa3b1f..89e609c913 100644 --- a/cura/PrinterOutput/ConfigurationModel.py +++ b/cura/PrinterOutput/ConfigurationModel.py @@ -44,7 +44,7 @@ class ConfigurationModel(QObject): @pyqtProperty(str, fset = setBuildplateConfiguration, notify = configurationChanged) def buildplateConfiguration(self) -> str: - return self._buildplate_configuration.capitalize() + return self._buildplate_configuration ## This method is intended to indicate whether the configuration is valid or not. # The method checks if the mandatory fields are or not set From f302a76d3aedf38e051e692f59246d406a1295d9 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 12 Dec 2018 10:47:15 +0100 Subject: [PATCH 0863/1240] Fix typing issue in Toolbox CURA-6006 --- plugins/Toolbox/src/Toolbox.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index ab975548ce..d957b7aae1 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -670,7 +670,8 @@ class Toolbox(QObject, Extension): self.setDownloadProgress(new_progress) if bytes_sent == bytes_total: self.setIsDownloading(False) - cast(QNetworkReply, self._download_reply).downloadProgress.disconnect(self._onDownloadProgress) + self._download_reply = cast(QNetworkReply, self._download_reply) + self._download_reply.downloadProgress.disconnect(self._onDownloadProgress) # Check if the download was sucessfull if self._download_reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200: From a6663ea0e8cf80cfc6bcd4d71db593009b83f514 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 12 Dec 2018 10:51:11 +0100 Subject: [PATCH 0864/1240] Fix typing issues CURA-6005 --- cura/CuraConstants.py | 2 +- plugins/CuraDrive/src/DriveApiService.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/CuraConstants.py b/cura/CuraConstants.py index 331937a0c2..c573d550c5 100644 --- a/cura/CuraConstants.py +++ b/cura/CuraConstants.py @@ -41,7 +41,7 @@ except ImportError: # Cloud API # --------- DEFAULT_CLOUD_API_ROOT = "https://api.ultimaker.com" # type: str -DEFAULT_CLOUD_API_VERSION = 1 # type: int +DEFAULT_CLOUD_API_VERSION = "1" # type: str try: from cura.CuraVersion import CuraCloudAPIRoot # type: ignore diff --git a/plugins/CuraDrive/src/DriveApiService.py b/plugins/CuraDrive/src/DriveApiService.py index 6963e595b5..98199c91cf 100644 --- a/plugins/CuraDrive/src/DriveApiService.py +++ b/plugins/CuraDrive/src/DriveApiService.py @@ -108,7 +108,7 @@ class DriveApiService: for chunk in download_package: write_backup.write(chunk) - if not self._verifyMd5Hash(temporary_backup_file.name, backup.get("md5_hash")): + if not self._verifyMd5Hash(temporary_backup_file.name, backup.get("md5_hash", "")): # Don't restore the backup if the MD5 hashes do not match. # This can happen if the download was interrupted. Logger.log("w", "Remote and local MD5 hashes do not match, not restoring backup.") From 5fd0f2b5f69e8f0597b4550a828a88da16eb8621 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 12 Dec 2018 11:09:58 +0100 Subject: [PATCH 0865/1240] Add tooltip to rating if the rating is disabled This makes it clearer what the user needs to do in order to rate (install package or login) CURA-6013 --- plugins/Toolbox/resources/qml/RatingWidget.qml | 16 ++++++++++------ .../Toolbox/resources/qml/ToolboxDetailPage.qml | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/plugins/Toolbox/resources/qml/RatingWidget.qml b/plugins/Toolbox/resources/qml/RatingWidget.qml index 355b99c0c4..9d9eb8bca8 100644 --- a/plugins/Toolbox/resources/qml/RatingWidget.qml +++ b/plugins/Toolbox/resources/qml/RatingWidget.qml @@ -1,7 +1,7 @@ -import QtQuick 2.2 -import QtQuick.Controls 2.0 +import QtQuick 2.7 +import QtQuick.Controls 2.1 import UM 1.0 as UM - +import Cura 1.1 as Cura Item { id: ratingWidget @@ -11,6 +11,7 @@ Item property string packageId: "" property int userRating: 0 + property bool canRate: false signal rated(int rating) @@ -20,7 +21,7 @@ Item { id: mouseArea anchors.fill: parent - hoverEnabled: ratingWidget.enabled + hoverEnabled: ratingWidget.canRate acceptedButtons: Qt.NoButton onExited: { @@ -40,12 +41,15 @@ Item hoverEnabled: true onHoveredChanged: { - if(hovered) + if(hovered && ratingWidget.canRate) { indexHovered = index } } + ToolTip.visible: control.hovered && !ratingWidget.canRate + ToolTip.text: !Cura.API.account.isLoggedIn ? catalog.i18nc("@label", "You need to login first before you can rate"): catalog.i18nc("@label", "You need to install the package before you can rate") + property bool isStarFilled: { // If the entire widget is hovered, override the actual rating. @@ -72,7 +76,7 @@ Item // Unfilled stars should always have the default color. Only filled stars should change on hover color: { - if(!enabled) + if(!ratingWidget.canRate) { return "#5a5a5a" } diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml index 1d977883f9..d0608be4de 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml @@ -135,7 +135,7 @@ Item visible: details.type == "plugin" packageId: details.id userRating: details.user_rating - enabled: toolbox.isInstalled(details.id) && Cura.API.account.isLoggedIn + canRate: toolbox.isInstalled(details.id) && Cura.API.account.isLoggedIn onRated: { From a6a16a682dd6995c7b84e597710765c3e21cd0a1 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 12 Dec 2018 11:17:44 +0100 Subject: [PATCH 0866/1240] Fix some alignments Also modify a bit the code in the ConfigurationItem, trying to get rid of a binding loop, but I couldn't (so weird) Contributes to CURA-5876. --- .../ConfigurationMenu/AutoConfiguration.qml | 1 + .../ConfigurationMenu/ConfigurationItem.qml | 167 +++++++++--------- .../ConfigurationListView.qml | 3 +- .../ConfigurationMenu/ConfigurationMenu.qml | 25 +-- resources/themes/cura-light/theme.json | 1 + 5 files changed, 98 insertions(+), 99 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml index 2e8be05fef..a3ed5040b7 100644 --- a/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml @@ -31,6 +31,7 @@ Item ConfigurationListView { anchors.top: header.bottom + anchors.topMargin: UM.Theme.getSize("default_margin").width width: parent.width outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 9dae075b48..a73cd3b46c 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -14,120 +14,115 @@ Button property var configuration: null hoverEnabled: true - height: background.height - background: Rectangle { - height: childrenRect.height color: parent.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button") border.color: parent.checked ? UM.Theme.getColor("primary") : UM.Theme.getColor("lining") border.width: UM.Theme.getSize("default_lining").width radius: UM.Theme.getSize("default_radius").width + } - Column + contentItem: Column + { + id: contentColumn + width: parent.width + padding: UM.Theme.getSize("default_margin").width + spacing: UM.Theme.getSize("narrow_margin").height + + Row { - id: contentColumn - width: parent.width - padding: UM.Theme.getSize("wide_margin").width - spacing: UM.Theme.getSize("narrow_margin").height + id: extruderRow - Row + anchors { - id: extruderRow - - anchors - { - left: parent.left - leftMargin: parent.padding - right: parent.right - rightMargin: parent.padding - } - height: childrenRect.height - - spacing: UM.Theme.getSize("default_margin").width - - Repeater - { - id: repeater - height: childrenRect.height - model: configuration.extruderConfigurations - delegate: PrintCoreConfiguration - { - width: Math.round(parent.width / 2) - printCoreConfiguration: modelData - } - } + left: parent.left + leftMargin: 2 * parent.padding + right: parent.right + rightMargin: 2 * parent.padding } - //Buildplate row separator - Rectangle + spacing: UM.Theme.getSize("default_margin").width + + Repeater { - id: separator - - visible: buildplateInformation.visible - anchors + id: repeater + model: configuration.extruderConfigurations + delegate: PrintCoreConfiguration { - left: parent.left - leftMargin: parent.padding - right: parent.right - rightMargin: parent.padding - } - height: visible ? Math.round(UM.Theme.getSize("default_lining").height / 2) : 0 - color: UM.Theme.getColor("lining") - } - - Item - { - id: buildplateInformation - - anchors - { - left: parent.left - leftMargin: parent.padding - right: parent.right - rightMargin: parent.padding - } - height: childrenRect.height - visible: configuration.buildplateConfiguration != "" - - UM.RecolorImage - { - id: buildplateIcon - anchors.left: parent.left - width: UM.Theme.getSize("main_window_header_button_icon").width - height: UM.Theme.getSize("main_window_header_button_icon").height - source: UM.Theme.getIcon("buildplate") - color: UM.Theme.getColor("text") - } - - Label - { - id: buildplateLabel - anchors.left: buildplateIcon.right - anchors.verticalCenter: buildplateIcon.verticalCenter - anchors.leftMargin: Math.round(UM.Theme.getSize("default_margin").height / 2) - text: configuration.buildplateConfiguration - renderType: Text.NativeRendering - color: UM.Theme.getColor("text") + width: Math.round(parent.width / 2) + printCoreConfiguration: modelData } } } - Connections + //Buildplate row separator + Rectangle { - target: Cura.MachineManager - onCurrentConfigurationChanged: + id: separator + + visible: buildplateInformation.visible + anchors { - configurationItem.checked = Cura.MachineManager.matchesConfiguration(configuration) + left: parent.left + leftMargin: 2 * parent.padding + right: parent.right + rightMargin: 2 * parent.padding } + height: visible ? Math.round(UM.Theme.getSize("default_lining").height / 2) : 0 + color: UM.Theme.getColor("lining") } - Component.onCompleted: + Item + { + id: buildplateInformation + + anchors + { + left: parent.left + leftMargin: 2 * parent.padding + right: parent.right + rightMargin: 2 * parent.padding + } + height: childrenRect.height + visible: configuration.buildplateConfiguration != "" + + UM.RecolorImage + { + id: buildplateIcon + anchors.left: parent.left + width: UM.Theme.getSize("main_window_header_button_icon").width + height: UM.Theme.getSize("main_window_header_button_icon").height + source: UM.Theme.getIcon("buildplate") + color: UM.Theme.getColor("text") + } + + Label + { + id: buildplateLabel + anchors.left: buildplateIcon.right + anchors.verticalCenter: buildplateIcon.verticalCenter + anchors.leftMargin: Math.round(UM.Theme.getSize("default_margin").height / 2) + text: configuration.buildplateConfiguration + renderType: Text.NativeRendering + color: UM.Theme.getColor("text") + } + } + } + + Connections + { + target: Cura.MachineManager + onCurrentConfigurationChanged: { configurationItem.checked = Cura.MachineManager.matchesConfiguration(configuration) } } + Component.onCompleted: + { + configurationItem.checked = Cura.MachineManager.matchesConfiguration(configuration) + } + onClicked: { Cura.MachineManager.applyRemoteConfiguration(configuration) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml index 3cc0754284..d7ffa0d8ff 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml @@ -11,7 +11,7 @@ Column { id: base property var outputDevice: null - height: childrenRect.height + 2 * padding + height: childrenRect.height + padding spacing: UM.Theme.getSize("narrow_margin").height function forceModelUpdate() @@ -57,7 +57,6 @@ Column id: configurationList spacing: UM.Theme.getSize("narrow_margin").height width: container.width - ((height > container.maximumHeight) ? container.ScrollBar.vertical.background.width : 0) //Make room for scroll bar if there is any. - contentHeight: childrenRect.height height: childrenRect.height section.property: "modelData.printerType" diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index 33a317b42b..2165f001f9 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -34,6 +34,8 @@ Cura.ExpandablePopup Custom } + contentPadding: UM.Theme.getSize("default_lining").width + contentAlignment: Cura.ExpandablePopup.ContentAlignment.AlignLeft enabled: Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariants || Cura.MachineManager.hasVariantBuildplates; //Only let it drop down if there is any configuration that you could change. headerItem: Item @@ -127,8 +129,9 @@ Cura.ExpandablePopup contentItem: Column { id: popupItem - width: base.width - 2 * UM.Theme.getSize("default_margin").width + width: UM.Theme.getSize("configuration_selector").width height: implicitHeight //Required because ExpandableComponent will try to use this to determine the size of the background of the pop-up. + padding: UM.Theme.getSize("default_margin").height spacing: UM.Theme.getSize("default_margin").height property bool is_connected: false //If current machine is connected to a printer. Only evaluated upon making popup visible. @@ -141,19 +144,19 @@ Cura.ExpandablePopup Item { - width: parent.width + width: parent.width - 2 * parent.padding height: { - var height = 0; - if(autoConfiguration.visible) + var height = 0 + if (autoConfiguration.visible) { - height += autoConfiguration.height; + height += autoConfiguration.height } - if(customConfiguration.visible) + if (customConfiguration.visible) { - height += customConfiguration.height; + height += customConfiguration.height } - return height; + return height } AutoConfiguration { @@ -172,9 +175,9 @@ Cura.ExpandablePopup { id: separator visible: buttonBar.visible - x: -contentPadding + x: -parent.padding - width: base.width + width: parent.width height: UM.Theme.getSize("default_lining").height color: UM.Theme.getColor("lining") @@ -186,7 +189,7 @@ Cura.ExpandablePopup id: buttonBar visible: popupItem.is_connected //Switching only makes sense if the "auto" part is possible. - width: parent.width + width: parent.width - 2 * parent.padding height: childrenRect.height Cura.SecondaryButton diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index b4d0ab7092..201703c6be 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -356,6 +356,7 @@ "expandable_component_content_header": [0.0, 3.0], + "configuration_selector": [38.0, 4.0], "configuration_selector_mode_tabs": [0.0, 3.0], "action_panel_widget": [25.0, 0.0], From b1244b6bde57b158dc0629dcd66f32644582487e Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 12 Dec 2018 11:22:35 +0100 Subject: [PATCH 0867/1240] Remove file MonitorSidebar It's not used anymore. Contributes to CURA-5876. --- resources/qml/MonitorSidebar.qml | 212 ------------------------------- 1 file changed, 212 deletions(-) delete mode 100644 resources/qml/MonitorSidebar.qml diff --git a/resources/qml/MonitorSidebar.qml b/resources/qml/MonitorSidebar.qml deleted file mode 100644 index 669bdbfb8f..0000000000 --- a/resources/qml/MonitorSidebar.qml +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright (c) 2018 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.10 -import QtQuick.Controls 2.0 -import QtQuick.Layouts 1.3 - -import UM 1.2 as UM -import Cura 1.0 as Cura - -import "Menus" -import "Menus/ConfigurationMenu" - - -Rectangle -{ - id: base - - property int currentModeIndex - property bool hideSettings: PrintInformation.preSliced - 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.printerConnected - property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands - property var connectedPrinter: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null - - property variant printDuration: PrintInformation.currentPrintTime - property variant printMaterialLengths: PrintInformation.materialLengths - property variant printMaterialWeights: PrintInformation.materialWeights - property variant printMaterialCosts: PrintInformation.materialCosts - property variant printMaterialNames: PrintInformation.materialNames - - color: UM.Theme.getColor("main_background") - UM.I18nCatalog { id: catalog; name: "cura"} - - Timer { - id: tooltipDelayTimer - interval: 500 - repeat: false - property var item - property string text - - onTriggered: - { - base.showTooltip(base, {x: 0, y: item.y}, text); - } - } - - function showTooltip(item, position, text) - { - tooltip.text = text; - position = item.mapToItem(base, position.x - UM.Theme.getSize("default_arrow").width, position.y); - tooltip.show(position); - } - - function hideTooltip() - { - tooltip.hide(); - } - - function strPadLeft(string, pad, length) { - return (new Array(length + 1).join(pad) + string).slice(-length); - } - - function getPrettyTime(time) - { - var hours = Math.floor(time / 3600) - time -= hours * 3600 - var minutes = Math.floor(time / 60); - time -= minutes * 60 - var seconds = Math.floor(time); - - var finalTime = strPadLeft(hours, "0", 2) + ":" + strPadLeft(minutes, "0", 2) + ":" + strPadLeft(seconds, "0", 2); - return finalTime; - } - - MouseArea - { - anchors.fill: parent - acceptedButtons: Qt.AllButtons - - onWheel: - { - wheel.accepted = true; - } - } - - MachineSelector - { - id: machineSelection - width: base.width - configSelection.width - separator.width - height: UM.Theme.getSize("stage_menu").height - anchors.top: base.top - anchors.left: parent.left - } - - Rectangle - { - id: separator - visible: configSelection.visible - width: visible ? Math.round(UM.Theme.getSize("thick_lining").height / 2) : 0 - height: UM.Theme.getSize("stage_menu").height - color: UM.Theme.getColor("thick_lining") - anchors.left: machineSelection.right - } - - CustomConfigurationSelector - { - id: configSelection - visible: isNetworkPrinter && printerConnected - width: visible ? Math.round(base.width * 0.15) : 0 - height: UM.Theme.getSize("stage_menu").height - anchors.top: base.top - anchors.right: parent.right - } - - Loader - { - id: controlItem - anchors.bottom: footerSeparator.top - anchors.top: machineSelection.bottom - anchors.left: base.left - anchors.right: base.right - sourceComponent: - { - if(connectedPrinter != null) - { - if(connectedPrinter.controlItem != null) - { - return connectedPrinter.controlItem - } - } - return null - } - } - - Loader - { - anchors.bottom: footerSeparator.top - anchors.top: machineSelection.bottom - anchors.left: base.left - anchors.right: base.right - source: - { - if(controlItem.sourceComponent == null) - { - return "PrintMonitor.qml" - } - else - { - return "" - } - } - } - - Rectangle - { - id: footerSeparator - width: parent.width - height: UM.Theme.getSize("wide_lining").height - color: UM.Theme.getColor("wide_lining") - anchors.bottom: monitorButton.top - anchors.bottomMargin: UM.Theme.getSize("thick_margin").height - } - - // MonitorButton is actually the bottom footer panel. - MonitorButton - { - id: monitorButton - implicitWidth: base.width - anchors.bottom: parent.bottom - } - - PrintSetupTooltip - { - id: tooltip - } - - UM.SettingPropertyProvider - { - id: machineExtruderCount - - containerStack: Cura.MachineManager.activeMachine - key: "machine_extruder_count" - watchedProperties: [ "value" ] - storeIndex: 0 - } - - UM.SettingPropertyProvider - { - id: machineHeatedBed - - containerStack: Cura.MachineManager.activeMachine - key: "machine_heated_bed" - watchedProperties: [ "value" ] - storeIndex: 0 - } - - // Make the ConfigurationSelector react when the global container changes, otherwise if Cura is not connected to the printer, - // switching printers make no reaction - Connections - { - target: Cura.MachineManager - onGlobalContainerChanged: - { - base.isNetworkPrinter = Cura.MachineManager.activeMachineNetworkKey != "" - base.printerConnected = Cura.MachineManager.printerOutputDevices.length != 0 - } - } -} From dbf91fca7f42e34111134d5e794267c1f917ee09 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 12 Dec 2018 11:40:51 +0100 Subject: [PATCH 0868/1240] Re-added hover on showcase tiles CURA-6013 --- .../qml/ToolboxDownloadsShowcaseTile.qml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml index ca0226b39d..73d3acc76f 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml @@ -51,12 +51,11 @@ Rectangle UM.RecolorImage { width: (parent.width * 0.20) | 0 - height: (parent.height * 0.20) | 0 + height: width anchors { - bottom: parent.bottom + bottom: bottomBorder.top right: parent.right - bottomMargin: UM.Theme.getSize("default_lining").width } visible: installedPackages != 0 color: (installedPackages == packageCount) ? UM.Theme.getColor("primary") : UM.Theme.getColor("border") @@ -70,10 +69,21 @@ Rectangle anchors.bottomMargin: UM.Theme.getSize("narrow_margin").height anchors.horizontalCenter: parent.horizontalCenter } + Rectangle + { + id: bottomBorder + color: UM.Theme.getColor("primary") + anchors.bottom: parent.bottom + width: parent.width + height: UM.Theme.getSize("toolbox_header_highlight").height + } MouseArea { anchors.fill: parent + hoverEnabled: true + onEntered: tileBase.border.color = UM.Theme.getColor("primary") + onExited: tileBase.border.color = UM.Theme.getColor("lining") onClicked: { base.selection = model From 901c19e270e3c3258e734b14c1f2433135f99695 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 12 Dec 2018 11:52:04 +0100 Subject: [PATCH 0869/1240] Prevent QML warning CURA-6013 --- plugins/Toolbox/resources/qml/SmallRatingWidget.qml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/Toolbox/resources/qml/SmallRatingWidget.qml b/plugins/Toolbox/resources/qml/SmallRatingWidget.qml index 0b93131cfd..439a7baec0 100644 --- a/plugins/Toolbox/resources/qml/SmallRatingWidget.qml +++ b/plugins/Toolbox/resources/qml/SmallRatingWidget.qml @@ -3,8 +3,6 @@ import QtQuick.Controls 1.4 import UM 1.1 as UM import Cura 1.1 as Cura - - Row { id: rating @@ -24,7 +22,7 @@ Row Label { id: numRatingsLabel - text: model.average_rating.toFixed(1) + " (" + model.num_ratings + " " + catalog.i18nc("@label", "ratings") + ")" + text: model.average_rating != undefined ? model.average_rating.toFixed(1) + " (" + model.num_ratings + " " + catalog.i18nc("@label", "ratings") + ")": "" verticalAlignment: Text.AlignVCenter height: starIcon.height width: contentWidth From 4ba448077e59791e655a497196ef65aaaae5e3c1 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 12 Dec 2018 13:33:46 +0100 Subject: [PATCH 0870/1240] Add an empty state, when there are no configurations, showing a label indicating that the list is empty Contributes to CURA-5876. --- .../ConfigurationListView.qml | 18 ++++++++++++++---- .../ConfigurationMenu/ConfigurationMenu.qml | 12 ++++++------ resources/themes/cura-light/theme.json | 2 +- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml index d7ffa0d8ff..7943bba81d 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml @@ -7,16 +7,15 @@ import QtQuick.Controls 2.3 import UM 1.2 as UM import Cura 1.0 as Cura -Column +Item { id: base property var outputDevice: null - height: childrenRect.height + padding - spacing: UM.Theme.getSize("narrow_margin").height + height: childrenRect.height function forceModelUpdate() { - // FIXME For now the model should be removed and then created again, otherwise changes in the printer don't automatically update the UI + // FIXME For now the model has to be removed and then created again, otherwise changes in the printer don't automatically update the UI configurationList.model = [] if (outputDevice) { @@ -24,6 +23,17 @@ Column } } + // This component will appear when there is no configurations (e.g. when loosing connection) + Label + { + width: parent.width + visible: configurationList.model.length == 0 + text: "Configuration list empty. Probably because of lost connection" // TODO change this to a proper component + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + renderType: Text.NativeRendering + } + ScrollView { id: container diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index 2165f001f9..e04c04f83b 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -35,7 +35,6 @@ Cura.ExpandablePopup } contentPadding: UM.Theme.getSize("default_lining").width - contentAlignment: Cura.ExpandablePopup.ContentAlignment.AlignLeft enabled: Cura.MachineManager.hasMaterials || Cura.MachineManager.hasVariants || Cura.MachineManager.hasVariantBuildplates; //Only let it drop down if there is any configuration that you could change. headerItem: Item @@ -130,18 +129,19 @@ Cura.ExpandablePopup { id: popupItem width: UM.Theme.getSize("configuration_selector").width - height: implicitHeight //Required because ExpandableComponent will try to use this to determine the size of the background of the pop-up. + height: implicitHeight // Required because ExpandableComponent will try to use this to determine the size of the background of the pop-up. padding: UM.Theme.getSize("default_margin").height spacing: UM.Theme.getSize("default_margin").height - property bool is_connected: false //If current machine is connected to a printer. Only evaluated upon making popup visible. + property bool is_connected: false // If current machine is connected to a printer. Only evaluated upon making popup visible. + property int configuration_method: ConfigurationMenu.ConfigurationMethod.Custom // Type of configuration being used. Only evaluated upon making popup visible. + onVisibleChanged: { - is_connected = Cura.MachineManager.activeMachineNetworkKey !== "" && Cura.MachineManager.printerConnected //Re-evaluate. + is_connected = Cura.MachineManager.activeMachineNetworkKey !== "" && Cura.MachineManager.printerConnected // Re-evaluate. + configuration_method = is_connected ? ConfigurationMenu.ConfigurationMethod.Auto : ConfigurationMenu.ConfigurationMethod.Custom // Auto if connected to a printer at start-up, or Custom if not. } - property int configuration_method: is_connected ? ConfigurationMenu.ConfigurationMethod.Auto : ConfigurationMenu.ConfigurationMethod.Custom //Auto if connected to a printer at start-up, or Custom if not. - Item { width: parent.width - 2 * parent.padding diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 201703c6be..413d547d5d 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -356,7 +356,7 @@ "expandable_component_content_header": [0.0, 3.0], - "configuration_selector": [38.0, 4.0], + "configuration_selector": [35.0, 4.0], "configuration_selector_mode_tabs": [0.0, 3.0], "action_panel_widget": [25.0, 0.0], From 1d3da3244f593a978693e9bb8030bc01177032bb Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 12 Dec 2018 14:09:15 +0100 Subject: [PATCH 0871/1240] Remember the previous selected method in the configuration The current behavior now is to open the configuration panel in the previous state, in case it was manually selected. That means that after selecting a printer, the manual state is reset so it will open the auto configuration if it is a connected printer. It will open the custom state in case it's not connected or the printer has no connection. Contributes to CURA-5876. --- .../ConfigurationMenu/ConfigurationMenu.qml | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index e04c04f83b..6aea5b9009 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -135,11 +135,15 @@ Cura.ExpandablePopup property bool is_connected: false // If current machine is connected to a printer. Only evaluated upon making popup visible. property int configuration_method: ConfigurationMenu.ConfigurationMethod.Custom // Type of configuration being used. Only evaluated upon making popup visible. + property int manual_selected_method: -1 // It stores the configuration method selected by the user. By default the selected method is onVisibleChanged: { is_connected = Cura.MachineManager.activeMachineNetworkKey !== "" && Cura.MachineManager.printerConnected // Re-evaluate. - configuration_method = is_connected ? ConfigurationMenu.ConfigurationMethod.Auto : ConfigurationMenu.ConfigurationMethod.Custom // Auto if connected to a printer at start-up, or Custom if not. + + // If the printer is not connected, we switch always to the custom mode. If is connected instead, the auto mode + // or the previous state is selected + configuration_method = is_connected ? (manual_selected_method == -1 ? ConfigurationMenu.ConfigurationMethod.Auto : manual_selected_method) : ConfigurationMenu.ConfigurationMethod.Custom } Item @@ -158,6 +162,7 @@ Cura.ExpandablePopup } return height } + AutoConfiguration { id: autoConfiguration @@ -203,7 +208,11 @@ Cura.ExpandablePopup iconSource: UM.Theme.getIcon("arrow_right") isIconOnRightSide: true - onClicked: popupItem.configuration_method = ConfigurationMenu.ConfigurationMethod.Custom + onClicked: + { + popupItem.configuration_method = ConfigurationMenu.ConfigurationMethod.Custom + popupItem.manual_selected_method = popupItem.configuration_method + } } Cura.SecondaryButton @@ -214,8 +223,18 @@ Cura.ExpandablePopup iconSource: UM.Theme.getIcon("arrow_left") - onClicked: popupItem.configuration_method = ConfigurationMenu.ConfigurationMethod.Auto + onClicked: + { + popupItem.configuration_method = ConfigurationMenu.ConfigurationMethod.Auto + popupItem.manual_selected_method = popupItem.configuration_method + } } } } + + Connections + { + target: Cura.MachineManager + onGlobalContainerChanged: popupItem.manual_selected_method = -1 // When switching printers, reset the value of the manual selected method + } } From 17173aba0637b1a2ea22692ff3935801bb62c1a2 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 12 Dec 2018 17:04:17 +0100 Subject: [PATCH 0872/1240] Add a component to show when no configurations are available because lack of connection. Contributes to CURA-5876. --- .../qml/ActionPanel/OutputProcessWidget.qml | 2 - .../qml/ActionPanel/SliceProcessWidget.qml | 2 +- resources/qml/IconWithText.qml | 3 +- .../ConfigurationListView.qml | 37 ++++++++++++--- .../qml/PrinterSelector/MachineSelector.qml | 47 +++++-------------- resources/themes/cura-light/icons/warning.svg | 13 +++-- resources/themes/cura-light/theme.json | 2 +- 7 files changed, 56 insertions(+), 50 deletions(-) diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml index 3f53abf28f..5ac777e2ad 100644 --- a/resources/qml/ActionPanel/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -119,8 +119,6 @@ Column } height: UM.Theme.getSize("action_button").height - leftPadding: UM.Theme.getSize("default_margin").width - rightPadding: UM.Theme.getSize("default_margin").width text: catalog.i18nc("@button", "Preview") onClicked: UM.Controller.setActiveStage("PreviewStage") diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index 18caeafb40..3756d0d452 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -60,7 +60,7 @@ Column text: catalog.i18nc("@label:PrintjobStatus", "Unable to Slice") source: UM.Theme.getIcon("warning") - color: UM.Theme.getColor("warning") + iconColor: UM.Theme.getColor("warning") } // Progress bar, only visible when the backend is in the process of slice the printjob diff --git a/resources/qml/IconWithText.qml b/resources/qml/IconWithText.qml index 5530740040..9fd527b27e 100644 --- a/resources/qml/IconWithText.qml +++ b/resources/qml/IconWithText.qml @@ -15,6 +15,7 @@ Item { property alias source: icon.source property alias iconSize: icon.width + property alias iconColor: icon.color property alias color: label.color property alias text: label.text property alias font: label.font @@ -37,7 +38,7 @@ Item { id: icon width: UM.Theme.getSize("section_icon").width - height: UM.Theme.getSize("section_icon").height + height: width color: label.color diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml index 7943bba81d..3ddbb49fe8 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml @@ -23,15 +23,40 @@ Item } } - // This component will appear when there is no configurations (e.g. when loosing connection) - Label + // This component will appear when there is no configurations (e.g. when losing connection) + Item { width: parent.width visible: configurationList.model.length == 0 - text: "Configuration list empty. Probably because of lost connection" // TODO change this to a proper component - horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap - renderType: Text.NativeRendering + height: label.height + 2 * UM.Theme.getSize("default_margin").height + anchors.top: parent.top + anchors.topMargin: UM.Theme.getSize("default_margin").height + + UM.RecolorImage + { + id: icon + + anchors.left: parent.left + anchors.verticalCenter: label.verticalCenter + + source: UM.Theme.getIcon("warning") + color: UM.Theme.getColor("warning") + width: UM.Theme.getSize("section_icon").width + height: width + } + + Label + { + id: label + anchors.left: icon.right + anchors.right: parent.right + anchors.leftMargin: UM.Theme.getSize("default_margin").width + text: catalog.i18nc("@label", "The configurations are not available because the printer is disconnected.") + color: UM.Theme.getColor("text") + font: UM.Theme.getFont("default") + renderType: Text.NativeRendering + wrapMode: Text.WordWrap + } } ScrollView diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index 7cda4f1d2e..db9c38b9f1 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -24,49 +24,24 @@ Cura.ExpandablePopup name: "cura" } - headerItem: Item + headerItem: Cura.IconWithText { - implicitHeight: icon.height - - UM.RecolorImage + text: isNetworkPrinter ? Cura.MachineManager.activeMachineNetworkGroupName : Cura.MachineManager.activeMachineName + source: { - id: icon - - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - - source: + if (isNetworkPrinter) { - if (isNetworkPrinter) + if (machineSelector.outputDevice != null && machineSelector.outputDevice.clusterSize > 1) { - if (machineSelector.outputDevice != null && machineSelector.outputDevice.clusterSize > 1) - { - return UM.Theme.getIcon("printer_group") - } - return UM.Theme.getIcon("printer_single") + return UM.Theme.getIcon("printer_group") } - return "" + return UM.Theme.getIcon("printer_single") } - width: UM.Theme.getSize("machine_selector_icon").width - height: width - - color: UM.Theme.getColor("machine_selector_printer_icon") - visible: source != "" - } - - Label - { - id: label - anchors.left: icon.visible ? icon.right : parent.left - anchors.right: parent.right - anchors.leftMargin: UM.Theme.getSize("thin_margin").width - anchors.verticalCenter: icon.verticalCenter - text: isNetworkPrinter ? Cura.MachineManager.activeMachineNetworkGroupName : Cura.MachineManager.activeMachineName - elide: Text.ElideRight - color: UM.Theme.getColor("text") - font: UM.Theme.getFont("medium") - renderType: Text.NativeRendering + return "" } + font: UM.Theme.getFont("medium") + iconColor: UM.Theme.getColor("machine_selector_printer_icon") + iconSize: UM.Theme.getSize("machine_selector_icon").width UM.RecolorImage { diff --git a/resources/themes/cura-light/icons/warning.svg b/resources/themes/cura-light/icons/warning.svg index ae8a7a6430..14b7d797d0 100644 --- a/resources/themes/cura-light/icons/warning.svg +++ b/resources/themes/cura-light/icons/warning.svg @@ -1,4 +1,11 @@ - - - + + + + Icon/warning-s + Created with Sketch. + + + + + \ No newline at end of file diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 413d547d5d..1a9dec5deb 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -101,7 +101,7 @@ "printer_type_label_background": [228, 228, 242, 255], - "text": [0, 0, 0, 255], + "text": [25, 25, 25, 255], "text_detail": [174, 174, 174, 128], "text_link": [50, 130, 255, 255], "text_inactive": [174, 174, 174, 255], From a1d7fa893da138f241fd1cc74895ec8e5d928eb6 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 12 Dec 2018 17:26:13 +0100 Subject: [PATCH 0873/1240] Change the style for the toolbuttons that appear in the popup panel of some tools Contributes to CURA-6024 --- resources/themes/cura-light/styles.qml | 94 ++++++-------------------- resources/themes/cura-light/theme.json | 2 - 2 files changed, 20 insertions(+), 76 deletions(-) diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index bcc754f4ca..c940052668 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -177,8 +177,8 @@ QtObject { background: Item { - implicitWidth: Theme.getSize("button").width; - implicitHeight: Theme.getSize("button").height; + implicitWidth: Theme.getSize("button").width + implicitHeight: Theme.getSize("button").height UM.PointingRectangle { @@ -205,20 +205,20 @@ QtObject id: button_tip anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter; + anchors.verticalCenter: parent.verticalCenter text: control.text; - font: Theme.getFont("button_tooltip"); - color: Theme.getColor("tooltip_text"); + font: Theme.getFont("button_tooltip") + color: Theme.getColor("tooltip_text") } } Rectangle { - id: buttonFace; + id: buttonFace - anchors.fill: parent; - property bool down: control.pressed || (control.checkable && control.checked); + anchors.fill: parent + property bool down: control.pressed || (control.checkable && control.checked) color: { @@ -228,58 +228,22 @@ QtObject } else if(control.checkable && control.checked && control.hovered) { - return Theme.getColor("button_active_hover"); + return Theme.getColor("toolbar_button_active_hover") } else if(control.pressed || (control.checkable && control.checked)) { - return Theme.getColor("button_active"); + return Theme.getColor("toolbar_button_active") } else if(control.hovered) { - return Theme.getColor("button_hover"); - } - else - { - return Theme.getColor("button"); + return Theme.getColor("toolbar_button_hover") } + return Theme.getColor("toolbar_background") } Behavior on color { ColorAnimation { duration: 50; } } - border.width: (control.hasOwnProperty("needBorder") && control.needBorder) ? 2 * screenScaleFactor : 0 - border.color: Theme.getColor("tool_button_border") - - UM.RecolorImage - { - id: tool_button_arrow - anchors.right: parent.right; - anchors.rightMargin: Theme.getSize("button").width - Math.round(Theme.getSize("button_icon").width / 4) - anchors.bottom: parent.bottom; - anchors.bottomMargin: Theme.getSize("button").height - Math.round(Theme.getSize("button_icon").height / 4) - width: Theme.getSize("standard_arrow").width - height: Theme.getSize("standard_arrow").height - sourceSize.height: width - visible: control.menu != null; - color: - { - if(control.checkable && control.checked && control.hovered) - { - return Theme.getColor("button_text_active_hover"); - } - else if(control.pressed || (control.checkable && control.checked)) - { - return Theme.getColor("button_text_active"); - } - else if(control.hovered) - { - return Theme.getColor("button_text_hover"); - } - else - { - return Theme.getColor("button_text"); - } - } - source: Theme.getIcon("arrow_bottom") - } + border.width: (control.hasOwnProperty("needBorder") && control.needBorder) ? Theme.getSize("default_lining").width : 0 + border.color: Theme.getColor("lining") } } @@ -287,30 +251,12 @@ QtObject { UM.RecolorImage { - anchors.centerIn: parent; - opacity: !control.enabled ? 0.2 : 1.0 - source: control.iconSource; - width: Theme.getSize("button_icon").width; - height: Theme.getSize("button_icon").height; - color: - { - if(control.checkable && control.checked && control.hovered) - { - return Theme.getColor("button_text_active_hover"); - } - else if(control.pressed || (control.checkable && control.checked)) - { - return Theme.getColor("button_text_active"); - } - else if(control.hovered) - { - return Theme.getColor("button_text_hover"); - } - else - { - return Theme.getColor("button_text"); - } - } + anchors.centerIn: parent + opacity: control.enabled ? 1.0 : 0.2 + source: control.iconSource + width: Theme.getSize("button_icon").width + height: Theme.getSize("button_icon").height + color: Theme.getColor("toolbar_button_text") sourceSize: Theme.getSize("button_icon") } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 3dc216ad70..59596e9a3f 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -243,8 +243,6 @@ "tooltip": [68, 192, 255, 255], "tooltip_text": [255, 255, 255, 255], - "tool_button_border": [255, 255, 255, 0], - "message_background": [255, 255, 255, 255], "message_shadow": [0, 0, 0, 120], "message_border": [192, 193, 194, 255], From fed779d0d2cfd6a81fa5747a0a10debbcdb8c8ec Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Wed, 12 Dec 2018 17:31:08 +0100 Subject: [PATCH 0874/1240] STAR-322: Implementing multi-part upload (doesnt always work) --- .../src/Cloud/CloudApiClient.py | 23 +---- .../src/Cloud/CloudOutputDevice.py | 56 +++-------- .../src/Cloud/CloudProgressMessage.py | 37 ++++++++ .../src/Cloud/ResumableUpload.py | 94 +++++++++++++++++++ .../tests/Cloud/NetworkManagerMock.py | 3 +- .../tests/Cloud/TestCloudApiClient.py | 15 +-- 6 files changed, 161 insertions(+), 67 deletions(-) create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/CloudProgressMessage.py create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/ResumableUpload.py diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py index b08bac6670..2637f17010 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py @@ -9,6 +9,7 @@ from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply from UM.Logger import Logger from cura.API import Account from cura.NetworkClient import NetworkClient +from .ResumableUpload import ResumableUpload from ..Models import BaseModel from .Models.CloudClusterResponse import CloudClusterResponse from .Models.CloudErrorObject import CloudErrorObject @@ -69,24 +70,10 @@ class CloudApiClient(NetworkClient): # \param on_finished: The function to be called after the result is parsed. It receives the print job ID. # \param on_progress: A function to be called during upload progress. It receives a percentage (0-100). # \param on_error: A function to be called if the upload fails. It receives a dict with the error. - def uploadMesh(self, upload_response: CloudPrintJobResponse, mesh: bytes, on_finished: Callable[[str], Any], - on_progress: Callable[[int], Any], on_error: Callable[[dict], Any]): - - def progressCallback(bytes_sent: int, bytes_total: int) -> None: - if bytes_total: - on_progress(int((bytes_sent / bytes_total) * 100)) - - def finishedCallback(reply: QNetworkReply): - status_code, response = self._parseReply(reply) - if status_code < 300: - on_finished(upload_response.job_id) - else: - Logger.log("e", "Received unexpected response %s uploading mesh: %s", status_code, response) - on_error(response) - - # TODO: Multipart upload - self.put(upload_response.upload_url, data = mesh, content_type = upload_response.content_type, - on_finished = finishedCallback, on_progress = progressCallback) + def uploadMesh(self, upload_response: CloudPrintJobResponse, mesh: bytes, on_finished: Callable[[], Any], + on_progress: Callable[[int], Any], on_error: Callable[[], Any]): + ResumableUpload(upload_response.upload_url, upload_response.content_type, mesh, on_finished, + on_progress, on_error).start() # Requests a cluster to print the given print job. # \param cluster_id: The ID of the cluster. diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index c4ab752163..e75989a6a8 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -18,6 +18,7 @@ from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputController import CloudOutputController from ..MeshFormatHandler import MeshFormatHandler from ..UM3PrintJobOutputModel import UM3PrintJobOutputModel +from .CloudProgressMessage import CloudProgressMessage from .CloudApiClient import CloudApiClient from .Models.CloudClusterStatus import CloudClusterStatus from .Models.CloudPrintJobUploadRequest import CloudPrintJobUploadRequest @@ -43,9 +44,6 @@ class T: COULD_NOT_EXPORT = _I18N_CATALOG.i18nc("@info:status", "Could not export print job.") - SENDING_DATA_TEXT = _I18N_CATALOG.i18nc("@info:status", "Sending data to remote cluster") - SENDING_DATA_TITLE = _I18N_CATALOG.i18nc("@info:status", "Sending data to remote cluster") - ERROR = _I18N_CATALOG.i18nc("@info:title", "Error") UPLOAD_ERROR = _I18N_CATALOG.i18nc("@info:text", "Could not upload the data to the printer.") @@ -68,7 +66,7 @@ class T: class CloudOutputDevice(NetworkedPrinterOutputDevice): # The interval with which the remote clusters are checked - CHECK_CLUSTER_INTERVAL = 4.0 # seconds + CHECK_CLUSTER_INTERVAL = 5.0 # seconds # Signal triggered when the print jobs in the queue were changed. printJobsChanged = pyqtSignal() @@ -109,9 +107,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self._number_of_extruders = 2 # All networked printers are dual-extrusion Ultimaker machines. # We only allow a single upload at a time. - self._sending_job = False - # TODO: handle progress messages in another class. - self._progress_message = None # type: Optional[Message] + self._progress = CloudProgressMessage() # Keep server string of the last generated time to avoid updating models more than once for the same response self._received_printers = None # type: Optional[List[CloudClusterPrinterStatus]] @@ -149,7 +145,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): file_handler: Optional[FileHandler] = None, **kwargs: str) -> None: # Show an error message if we're already sending a job. - if self._sending_job: + if self._progress.visible: self._onUploadError(T.BLOCKED_UPLOADING) return @@ -286,53 +282,31 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # \param mesh: The bytes to upload. # \param job_response: The response received from the cloud API. def _onPrintJobCreated(self, mesh: bytes, job_response: CloudPrintJobResponse) -> None: - self._api.uploadMesh(job_response, mesh, self._onPrintJobUploaded, self._updateUploadProgress, - lambda _: self._onUploadError(T.UPLOAD_ERROR)) + self._progress.show() + self._api.uploadMesh(job_response, mesh, lambda: self._onPrintJobUploaded(job_response.job_id), + self._progress.update, self._onUploadError) ## Requests the print to be sent to the printer when we finished uploading the mesh. # \param job_id: The ID of the job. def _onPrintJobUploaded(self, job_id: str) -> None: self._api.requestPrint(self._device_id, job_id, self._onUploadSuccess) - ## Updates the progress of the mesh upload. - # \param progress: The amount of percentage points uploaded until now (0-100). - def _updateUploadProgress(self, progress: int) -> None: - if not self._progress_message: - self._progress_message = Message( - text = T.SENDING_DATA_TEXT, - title = T.SENDING_DATA_TITLE, - progress = -1, - lifetime = 0, - dismissable = False, - use_inactivity_timer = False - ) - self._progress_message.setProgress(progress) - self._progress_message.show() - - ## Hides the upload progress bar - def _resetUploadProgress(self) -> None: - if self._progress_message: - self._progress_message.hide() - self._progress_message = None - ## Displays the given message if uploading the mesh has failed # \param message: The message to display. - def _onUploadError(self, message: str = None) -> None: - self._resetUploadProgress() - if message: - Message( - text = message, - title = T.ERROR, - lifetime = 10 - ).show() - self._sending_job = False # the upload has finished so we're not sending a job anymore + def _onUploadError(self, message = None) -> None: + self._progress.hide() + Message( + text = message or T.UPLOAD_ERROR, + title = T.ERROR, + lifetime = 10 + ).show() self.writeError.emit() ## Shows a message when the upload has succeeded # \param response: The response from the cloud API. def _onUploadSuccess(self, response: CloudPrintResponse) -> None: Logger.log("i", "The cluster will be printing this print job with the ID %s", response.cluster_job_id) - self._resetUploadProgress() + self._progress.hide() Message( text = T.UPLOAD_SUCCESS_TEXT, title = T.UPLOAD_SUCCESS_TITLE, diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudProgressMessage.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudProgressMessage.py new file mode 100644 index 0000000000..e3e0cefc0c --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudProgressMessage.py @@ -0,0 +1,37 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from UM import i18nCatalog +from UM.Message import Message + + +## Class that contains all the translations for this module. +class T: + _I18N_CATALOG = i18nCatalog("cura") + + SENDING_DATA_TEXT = _I18N_CATALOG.i18nc("@info:status", "Sending data to remote cluster") + SENDING_DATA_TITLE = _I18N_CATALOG.i18nc("@info:status", "Sending data to remote cluster") + + +class CloudProgressMessage(Message): + def __init__(self): + super().__init__( + text = T.SENDING_DATA_TEXT, + title = T.SENDING_DATA_TITLE, + progress = -1, + lifetime = 0, + dismissable = False, + use_inactivity_timer = False + ) + + def show(self): + self.setProgress(0) + super().show() + + def update(self, percentage: int) -> None: + if not self._visible: + super().show() + self.setProgress(percentage) + + @property + def visible(self) -> bool: + return self._visible diff --git a/plugins/UM3NetworkPrinting/src/Cloud/ResumableUpload.py b/plugins/UM3NetworkPrinting/src/Cloud/ResumableUpload.py new file mode 100644 index 0000000000..52b8e5c2d7 --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/ResumableUpload.py @@ -0,0 +1,94 @@ +# Copyright (c) 2018 Ultimaker B.V. +# !/usr/bin/env python +# -*- coding: utf-8 -*- +from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply +from typing import Optional, Callable, Any, Tuple + +from UM.Logger import Logger +from cura.NetworkClient import NetworkClient + + +class ResumableUpload(NetworkClient): + MAX_RETRIES = 10 + BYTES_PER_REQUEST = 256 * 1024 + RETRY_HTTP_CODES = {500, 502, 503, 504} + + ## Creates a resumable upload + # \param url: The URL to which we shall upload. + # \param content_length: The total content length of the file, in bytes. + # \param http_method: The HTTP method to be used, e.g. "POST" or "PUT". + # \param timeout: The timeout for each chunk upload. Important: If None, no timeout is applied at all. + def __init__(self, url: str, content_type: str, data: bytes, + on_finished: Callable[[], Any], on_progress: Callable[[int], Any], on_error: Callable[[], Any]): + super().__init__() + self._url = url + self._content_type = content_type + self._data = data + + self._on_finished = on_finished + self._on_progress = on_progress + self._on_error = on_error + + self._sent_bytes = 0 + self._retries = 0 + self._finished = False + + ## We override _createEmptyRequest in order to add the user credentials. + # \param url: The URL to request + # \param content_type: The type of the body contents. + def _createEmptyRequest(self, path: str, content_type: Optional[str] = "application/json") -> QNetworkRequest: + request = super()._createEmptyRequest(path, content_type = self._content_type) + + first_byte, last_byte = self._chunkRange() + content_range = "bytes {}-{}/{}".format(first_byte, last_byte - 1, len(self._data)) + request.setRawHeader(b"Content-Range", content_range.encode()) + Logger.log("i", "Uploading %s to %s", content_range, self._url) + + return request + + def _chunkRange(self) -> Tuple[int, int]: + last_byte = min(len(self._data), self._sent_bytes + self.BYTES_PER_REQUEST) + return self._sent_bytes, last_byte + + def start(self) -> None: + self._uploadChunk() + + def _uploadChunk(self) -> None: + if self._finished: + raise ValueError("The upload is already finished") + + first_byte, last_byte = self._chunkRange() + Logger.log("i", "PUT %s - %s", first_byte, last_byte) + self.put(self._url, data = self._data[first_byte:last_byte], content_type = self._content_type, + on_finished = self.finishedCallback, on_progress = self.progressCallback) + + def progressCallback(self, bytes_sent: int, bytes_total: int) -> None: + if bytes_total: + self._on_progress(int((self._sent_bytes + bytes_sent) / len(self._data) * 100)) + + def finishedCallback(self, reply: QNetworkReply) -> None: + status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) + + if self._retries < self.MAX_RETRIES and status_code in self.RETRY_HTTP_CODES: + self._retries += 1 + Logger.log("i", "Retrying %s/%s request %s", tries, self.MAX_RETRIES, request.url) + self._uploadChunk() + return + + body = bytes(reply.readAll()).decode() + Logger.log("w", "status_code: %s, Headers: %s, body: %s", status_code, + [bytes(header).decode() for header in reply.rawHeaderList()], body) + + if status_code > 308: + self._finished = True + Logger.log("e", "Received error while uploading: %s", body) + self._on_error() + return + + first_byte, last_byte = self._chunkRange() + self._sent_bytes += last_byte - first_byte + self._finished = self._sent_bytes >= len(self._data) + if self._finished: + self._on_finished() + else: + self._uploadChunk() diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py b/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py index 94cc239c0a..60627cbe7c 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py @@ -76,7 +76,8 @@ class NetworkManagerMock: ## Emits the signal that the reply is ready to all prepared replies. def flushReplies(self) -> None: - for reply in self.replies.values(): + for key, reply in self.replies.items(): + Logger.log("i", "Flushing reply to {} {}", *key) self.finished.emit(reply) self.reset() diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py index d673554640..e377627465 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py @@ -89,16 +89,17 @@ class TestCloudApiClient(TestCase): data = parseFixture("putJobUploadResponse")["data"] upload_response = CloudPrintJobResponse(**data) - self.network.prepareReply("PUT", upload_response.upload_url, 200, - b'{ data : "" }') # Network client doesn't look into the reply + # Network client doesn't look into the reply + self.network.prepareReply("PUT", upload_response.upload_url, 200, b'{}') - self.api.uploadMesh(upload_response, b'', lambda job_id: results.append(job_id), - progress.advance, progress.error) + mesh = ("1234" * 100000).encode() + self.api.uploadMesh(upload_response, mesh, lambda: results.append("sent"), progress.advance, progress.error) - self.network.flushReplies() + for _ in range(10): + self.network.flushReplies() + self.network.prepareReply("PUT", upload_response.upload_url, 200, b'{}') - self.assertEqual(len(results), 1) - self.assertEqual(results[0], upload_response.job_id) + self.assertEqual(["sent"], results) def test_requestPrint(self, network_mock): network_mock.return_value = self.network From 77ede1ae6bbd6c1a53b6682640fb754f186eee42 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 12 Dec 2018 17:50:21 +0100 Subject: [PATCH 0875/1240] Unify the fonts to only have 8 instead of 13. Contributes to CURA-6025. --- .../PostProcessingPlugin.qml | 4 +- .../resources/qml/ToolboxAuthorPage.qml | 2 +- .../resources/qml/ToolboxDetailPage.qml | 2 +- .../resources/qml/DiscoverUM3Action.qml | 2 +- .../resources/qml/MonitorPrinterCard.qml | 6 +-- .../resources/qml/MonitorStage.qml | 2 +- resources/qml/Account/AccountDetails.qml | 2 +- resources/qml/ActionButton.qml | 2 +- resources/qml/Dialogs/AboutDialog.qml | 2 +- resources/qml/ExtruderIcon.qml | 2 +- .../ConfigurationMenu/AutoConfiguration.qml | 2 +- .../ConfigurationMenu/CustomConfiguration.qml | 2 +- resources/qml/MonitorButton.qml | 4 +- resources/qml/Preferences/MachinesPage.qml | 2 +- .../Materials/MaterialsDetailsPanel.qml | 2 +- resources/qml/Preferences/ProfilesPage.qml | 2 +- resources/qml/PrinterOutput/ExtruderBox.qml | 4 +- resources/qml/PrinterOutput/HeatedBedBox.qml | 4 +- .../qml/PrinterOutput/MonitorSection.qml | 2 +- .../qml/PrinterOutput/OutputDeviceHeader.qml | 2 +- .../PrinterSelector/MachineSelectorButton.qml | 2 +- resources/qml/Settings/SettingCategory.qml | 2 +- resources/qml/ViewsSelector.qml | 2 +- resources/themes/cura-light/styles.qml | 6 +-- resources/themes/cura-light/theme.json | 38 +++++-------------- 25 files changed, 41 insertions(+), 61 deletions(-) diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml index b962f4d53b..580bb8c878 100644 --- a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml +++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml @@ -61,7 +61,7 @@ UM.Dialog anchors.leftMargin: base.textMargin anchors.right: parent.right anchors.rightMargin: base.textMargin - font: UM.Theme.getFont("large") + font: UM.Theme.getFont("large_bold") elide: Text.ElideRight } ListView @@ -276,7 +276,7 @@ UM.Dialog anchors.rightMargin: base.textMargin elide: Text.ElideRight height: 20 * screenScaleFactor - font: UM.Theme.getFont("large") + font: UM.Theme.getFont("large_bold") color: UM.Theme.getColor("text") } diff --git a/plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml b/plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml index 7b026566c3..b653f1a73b 100644 --- a/plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml @@ -55,7 +55,7 @@ Item bottomMargin: UM.Theme.getSize("default_margin").height } text: details.name || "" - font: UM.Theme.getFont("large") + font: UM.Theme.getFont("large_bold") wrapMode: Text.WordWrap width: parent.width height: UM.Theme.getSize("toolbox_property_label").height diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml index 7983be8aef..fa91fa4884 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml @@ -60,7 +60,7 @@ Item bottomMargin: UM.Theme.getSize("default_margin").height } text: details === null ? "" : (details.name || "") - font: UM.Theme.getFont("large") + font: UM.Theme.getFont("large_bold") color: UM.Theme.getColor("text") wrapMode: Text.WordWrap width: parent.width diff --git a/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml b/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml index e5f668c70d..65fe859772 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml @@ -223,7 +223,7 @@ Cura.MachineAction width: parent.width wrapMode: Text.WordWrap text: base.selectedDevice ? base.selectedDevice.name : "" - font: UM.Theme.getFont("large") + font: UM.Theme.getFont("large_bold") elide: Text.ElideRight renderType: Text.NativeRendering } diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml index 1676c51edf..67a8af727a 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml @@ -76,7 +76,7 @@ Item text: printer && printer.name ? printer.name : "" color: "#414054" // TODO: Theme! elide: Text.ElideRight - font: UM.Theme.getFont("large") // 16pt, bold + font: UM.Theme.getFont("large_bold") // 16pt, bold width: parent.width // FIXED-LINE-HEIGHT: @@ -178,7 +178,7 @@ Item verticalCenter: parent.verticalCenter } color: "#414054" // TODO: Theme! - font: UM.Theme.getFont("large") // 16pt, bold + font: UM.Theme.getFont("large_bold") // 16pt, bold text: { if (printer && printer.state == "disabled") { @@ -229,7 +229,7 @@ Item id: printerJobNameLabel color: printer.activePrintJob && printer.activePrintJob.isActive ? "#414054" : "#babac1" // TODO: Theme! elide: Text.ElideRight - font: UM.Theme.getFont("large") // 16pt, bold + font: UM.Theme.getFont("large_bold") // 16pt, bold text: base.printer.activePrintJob ? base.printer.activePrintJob.name : "Untitled" // TODO: I18N width: parent.width diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml index 4d59e0eb6b..0008295301 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml @@ -97,7 +97,7 @@ Component top: parent.top } color: UM.Theme.getColor("text") - font: UM.Theme.getFont("large_nonbold") + font: UM.Theme.getFont("large") text: catalog.i18nc("@label", "Queued") } diff --git a/resources/qml/Account/AccountDetails.qml b/resources/qml/Account/AccountDetails.qml index bfb23930c6..45f822e41f 100644 --- a/resources/qml/Account/AccountDetails.qml +++ b/resources/qml/Account/AccountDetails.qml @@ -44,7 +44,7 @@ Column horizontalAlignment: Text.AlignHCenter renderType: Text.NativeRendering text: loggedIn ? profile["username"] : catalog.i18nc("@label", "Please log in or create an account to\nenjoy all features of Ultimaker Cura.") - font: loggedIn ? UM.Theme.getFont("large") : UM.Theme.getFont("default") + font: loggedIn ? UM.Theme.getFont("large_bold") : UM.Theme.getFont("default") color: UM.Theme.getColor("text") } diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index 7177120f35..c3d39e8251 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -62,7 +62,7 @@ Button id: buttonText text: button.text color: button.enabled ? (button.hovered ? button.textHoverColor : button.textColor): button.textDisabledColor - font: UM.Theme.getFont("action_button") + font: UM.Theme.getFont("medium") visible: text != "" renderType: Text.NativeRendering anchors.verticalCenter: parent.verticalCenter diff --git a/resources/qml/Dialogs/AboutDialog.qml b/resources/qml/Dialogs/AboutDialog.qml index add84614e0..ac115a0e5f 100644 --- a/resources/qml/Dialogs/AboutDialog.qml +++ b/resources/qml/Dialogs/AboutDialog.qml @@ -51,7 +51,7 @@ UM.Dialog id: version text: catalog.i18nc("@label","version: %1").arg(UM.Application.version) - font: UM.Theme.getFont("large") + font: UM.Theme.getFont("large_bold") color: UM.Theme.getColor("text") anchors.right : logo.right anchors.top: logo.bottom diff --git a/resources/qml/ExtruderIcon.qml b/resources/qml/ExtruderIcon.qml index bb0b347b7e..0ab9df308e 100644 --- a/resources/qml/ExtruderIcon.qml +++ b/resources/qml/ExtruderIcon.qml @@ -48,7 +48,7 @@ Item id: extruderNumberText anchors.centerIn: parent text: index + 1 - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("small") width: contentWidth height: contentHeight visible: extruderEnabled diff --git a/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml index 68c56c7c4b..af229e4cc0 100644 --- a/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml @@ -16,7 +16,7 @@ Item { id: header text: catalog.i18nc("@header", "Configurations") - font: UM.Theme.getFont("large") + font: UM.Theme.getFont("large_bold") color: UM.Theme.getColor("text") height: contentHeight renderType: Text.NativeRendering diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index 8429e2c093..8f38edfe5b 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -23,7 +23,7 @@ Item { id: header text: catalog.i18nc("@header", "Custom") - font: UM.Theme.getFont("large") + font: UM.Theme.getFont("large_bold") color: UM.Theme.getColor("text") height: contentHeight renderType: Text.NativeRendering diff --git a/resources/qml/MonitorButton.qml b/resources/qml/MonitorButton.qml index fd7d2287c4..99640b1059 100644 --- a/resources/qml/MonitorButton.qml +++ b/resources/qml/MonitorButton.qml @@ -168,7 +168,7 @@ Item anchors.leftMargin: UM.Theme.getSize("thick_margin").width color: base.statusColor - font: UM.Theme.getFont("large") + font: UM.Theme.getFont("large_bold") text: statusText } @@ -179,7 +179,7 @@ Item anchors.right: progressBar.right color: base.statusColor - font: UM.Theme.getFont("large") + font: UM.Theme.getFont("large_bold") text: Math.round(progress) + "%" visible: showProgress } diff --git a/resources/qml/Preferences/MachinesPage.qml b/resources/qml/Preferences/MachinesPage.qml index bc75b9bc72..de65579cd3 100644 --- a/resources/qml/Preferences/MachinesPage.qml +++ b/resources/qml/Preferences/MachinesPage.qml @@ -70,7 +70,7 @@ UM.ManagementPage { id: machineName text: base.currentItem && base.currentItem.name ? base.currentItem.name : "" - font: UM.Theme.getFont("large") + font: UM.Theme.getFont("large_bold") width: parent.width elide: Text.ElideRight } diff --git a/resources/qml/Preferences/Materials/MaterialsDetailsPanel.qml b/resources/qml/Preferences/Materials/MaterialsDetailsPanel.qml index 92970f40e2..eb4a63250f 100644 --- a/resources/qml/Preferences/Materials/MaterialsDetailsPanel.qml +++ b/resources/qml/Preferences/Materials/MaterialsDetailsPanel.qml @@ -65,7 +65,7 @@ Item Label { text: materialProperties.name - font: UM.Theme.getFont("large") + font: UM.Theme.getFont("large_bold") } } diff --git a/resources/qml/Preferences/ProfilesPage.qml b/resources/qml/Preferences/ProfilesPage.qml index d7ffbb3152..70f9881b1e 100644 --- a/resources/qml/Preferences/ProfilesPage.qml +++ b/resources/qml/Preferences/ProfilesPage.qml @@ -471,7 +471,7 @@ Item Label { text: base.currentItemName - font: UM.Theme.getFont("large") + font: UM.Theme.getFont("large_bold") } } diff --git a/resources/qml/PrinterOutput/ExtruderBox.qml b/resources/qml/PrinterOutput/ExtruderBox.qml index 247bb3a27d..a19c02b0dd 100644 --- a/resources/qml/PrinterOutput/ExtruderBox.qml +++ b/resources/qml/PrinterOutput/ExtruderBox.qml @@ -80,7 +80,7 @@ Item id: extruderCurrentTemperature text: Math.round(extruderModel.hotendTemperature) + "°C" color: UM.Theme.getColor("text") - font: UM.Theme.getFont("large") + font: UM.Theme.getFont("large_bold") anchors.right: extruderTargetTemperature.left anchors.top: parent.top anchors.margins: UM.Theme.getSize("default_margin").width @@ -326,7 +326,7 @@ Item return UM.Theme.getColor("action_button_text"); } } - font: UM.Theme.getFont("action_button") + font: UM.Theme.getFont("medium") text: { if(extruderModel == null) diff --git a/resources/qml/PrinterOutput/HeatedBedBox.qml b/resources/qml/PrinterOutput/HeatedBedBox.qml index 33cf5cd1e2..77421c8aad 100644 --- a/resources/qml/PrinterOutput/HeatedBedBox.qml +++ b/resources/qml/PrinterOutput/HeatedBedBox.qml @@ -67,7 +67,7 @@ Item { id: bedCurrentTemperature text: printerModel != null ? printerModel.bedTemperature + "°C" : "" - font: UM.Theme.getFont("large") + font: UM.Theme.getFont("large_bold") color: UM.Theme.getColor("text") anchors.right: bedTargetTemperature.left anchors.top: parent.top @@ -320,7 +320,7 @@ Item return UM.Theme.getColor("action_button_text"); } } - font: UM.Theme.getFont("action_button") + font: UM.Theme.getFont("medium") text: { if(printerModel == null) diff --git a/resources/qml/PrinterOutput/MonitorSection.qml b/resources/qml/PrinterOutput/MonitorSection.qml index 7ef89dabf7..63b4b385e5 100644 --- a/resources/qml/PrinterOutput/MonitorSection.qml +++ b/resources/qml/PrinterOutput/MonitorSection.qml @@ -27,7 +27,7 @@ Item anchors.left: parent.left anchors.leftMargin: UM.Theme.getSize("default_margin").width text: label - font: UM.Theme.getFont("setting_category") + font: UM.Theme.getFont("medium_bold") color: UM.Theme.getColor("setting_category_text") } } diff --git a/resources/qml/PrinterOutput/OutputDeviceHeader.qml b/resources/qml/PrinterOutput/OutputDeviceHeader.qml index 16280eab5f..47f855266b 100644 --- a/resources/qml/PrinterOutput/OutputDeviceHeader.qml +++ b/resources/qml/PrinterOutput/OutputDeviceHeader.qml @@ -31,7 +31,7 @@ Item Label { id: outputDeviceNameLabel - font: UM.Theme.getFont("large") + font: UM.Theme.getFont("large_bold") color: UM.Theme.getColor("text") anchors.left: parent.left anchors.top: parent.top diff --git a/resources/qml/PrinterSelector/MachineSelectorButton.qml b/resources/qml/PrinterSelector/MachineSelectorButton.qml index b88af35f82..39e63d27c3 100644 --- a/resources/qml/PrinterSelector/MachineSelectorButton.qml +++ b/resources/qml/PrinterSelector/MachineSelectorButton.qml @@ -42,7 +42,7 @@ Button } text: machineSelectorButton.text color: UM.Theme.getColor("text") - font: UM.Theme.getFont("action_button") + font: UM.Theme.getFont("medium") visible: text != "" renderType: Text.NativeRendering verticalAlignment: Text.AlignVCenter diff --git a/resources/qml/Settings/SettingCategory.qml b/resources/qml/Settings/SettingCategory.qml index 5676bcedf9..93627dcb52 100644 --- a/resources/qml/Settings/SettingCategory.qml +++ b/resources/qml/Settings/SettingCategory.qml @@ -73,7 +73,7 @@ Button text: definition.label textFormat: Text.PlainText renderType: Text.NativeRendering - font: UM.Theme.getFont("setting_category") + font: UM.Theme.getFont("medium_bold") color: { if (!base.enabled) diff --git a/resources/qml/ViewsSelector.qml b/resources/qml/ViewsSelector.qml index f2906f9d4c..58749c09f2 100644 --- a/resources/qml/ViewsSelector.qml +++ b/resources/qml/ViewsSelector.qml @@ -105,7 +105,7 @@ Cura.ExpandablePopup id: buttonText text: viewsSelectorButton.text color: UM.Theme.getColor("text") - font: UM.Theme.getFont("action_button") + font: UM.Theme.getFont("medium") renderType: Text.NativeRendering verticalAlignment: Text.AlignVCenter elide: Text.ElideRight diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index bcc754f4ca..86f4100687 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -208,8 +208,8 @@ QtObject anchors.verticalCenter: parent.verticalCenter; text: control.text; - font: Theme.getFont("button_tooltip"); - color: Theme.getColor("tooltip_text"); + font: Theme.getFont("default") + color: Theme.getColor("tooltip_text") } } @@ -716,7 +716,7 @@ QtObject return UM.Theme.getColor("action_button_text"); } } - font: UM.Theme.getFont("action_button") + font: UM.Theme.getFont("medium") text: control.text } } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 3dc216ad70..9cfae3b311 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -6,63 +6,43 @@ "fonts": { "large": { "size": 1.35, - "weight": 63, + "weight": 40, "family": "Noto Sans" }, - "large_nonbold": { + "large_bold": { "size": 1.35, - "weight": 50, + "weight": 60, "family": "Noto Sans" }, "medium": { "size": 1.16, - "weight": 50, + "weight": 40, "family": "Noto Sans" }, "medium_bold": { "size": 1.16, - "weight": 63, + "weight": 60, "family": "Noto Sans" }, "default": { "size": 1.0, - "weight": 50, + "weight": 40, "family": "Noto Sans" }, "default_bold": { "size": 1.0, - "weight": 63, + "weight": 60, "family": "Noto Sans" }, "default_italic": { "size": 1.0, - "weight": 50, + "weight": 40, "italic": true, "family": "Noto Sans" }, "small": { - "size": 0.85, - "weight": 50, - "family": "Noto Sans" - }, - "very_small": { "size": 0.7, - "weight": 50, - "family": "Noto Sans" - }, - "button_tooltip": { - "size": 1.0, - "weight": 50, - "family": "Noto Sans" - }, - "setting_category": { - "size": 1.15, - "weight": 63, - "family": "Noto Sans" - }, - "action_button": { - "size": 1.15, - "weight": 50, + "weight": 40, "family": "Noto Sans" } }, From e7fe7571aafdb89f24af2a687ca13e067b07d425 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 13 Dec 2018 09:02:14 +0100 Subject: [PATCH 0876/1240] Change the behaviour of the output device selector Now the behavior is the following: - The active output device is the one that shows up in the button (same as before) - The list of the output devices don't show the active device - When clicking in one item of the list, it becomes the active output and automatically it performs the action. Contributes to CURA-6026. --- .../qml/ActionPanel/OutputDevicesActionButton.qml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/resources/qml/ActionPanel/OutputDevicesActionButton.qml b/resources/qml/ActionPanel/OutputDevicesActionButton.qml index 9a6c97bcff..b56f50b9a9 100644 --- a/resources/qml/ActionPanel/OutputDevicesActionButton.qml +++ b/resources/qml/ActionPanel/OutputDevicesActionButton.qml @@ -12,6 +12,12 @@ Item { id: widget + function requestWriteToDevice() + { + UM.OutputDeviceManager.requestWriteToDevice(UM.OutputDeviceManager.activeDevice, PrintInformation.jobName, + { "filter_by_machine": true, "preferred_mimetypes": Cura.MachineManager.activeMachine.preferred_output_file_formats }); + } + Cura.PrimaryButton { id: saveToButton @@ -32,9 +38,8 @@ Item onClicked: { - forceActiveFocus(); - UM.OutputDeviceManager.requestWriteToDevice(UM.OutputDeviceManager.activeDevice, PrintInformation.jobName, - { "filter_by_machine": true, "preferred_mimetypes": Cura.MachineManager.activeMachine.preferred_output_file_formats }); + forceActiveFocus() + widget.requestWriteToDevice() } } @@ -81,6 +86,7 @@ Item delegate: Cura.ActionButton { text: model.description + visible: model.id != UM.OutputDeviceManager.activeDevice // Don't show the active device in the list color: "transparent" cornerRadius: 0 hoverColor: UM.Theme.getColor("primary") @@ -88,6 +94,7 @@ Item onClicked: { UM.OutputDeviceManager.setActiveDevice(model.id) + widget.requestWriteToDevice() popup.close() } } From 121af7ad6085096bb9c8bbae4643516de342eb93 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 13 Dec 2018 09:41:07 +0100 Subject: [PATCH 0877/1240] Reuse the primary button in the toolbox for the "Quit Cura" button --- .../Toolbox/resources/qml/ToolboxFooter.qml | 36 ++++++------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxFooter.qml b/plugins/Toolbox/resources/qml/ToolboxFooter.qml index 2d42ca7269..6f46e939ff 100644 --- a/plugins/Toolbox/resources/qml/ToolboxFooter.qml +++ b/plugins/Toolbox/resources/qml/ToolboxFooter.qml @@ -2,21 +2,23 @@ // Toolbox is released under the terms of the LGPLv3 or higher. import QtQuick 2.10 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.3 + import UM 1.1 as UM +import Cura 1.0 as Cura Item { id: footer width: parent.width anchors.bottom: parent.bottom - height: visible ? Math.floor(UM.Theme.getSize("toolbox_footer").height) : 0 + height: visible ? UM.Theme.getSize("toolbox_footer").height : 0 + Label { text: catalog.i18nc("@info", "You will need to restart Cura before changes in packages have effect.") color: UM.Theme.getColor("text") - height: Math.floor(UM.Theme.getSize("toolbox_footer_button").height) + height: UM.Theme.getSize("toolbox_footer_button").height verticalAlignment: Text.AlignVCenter anchors { @@ -28,10 +30,10 @@ Item } renderType: Text.NativeRendering } - Button + + Cura.PrimaryButton { id: restartButton - text: catalog.i18nc("@info:button", "Quit Cura") anchors { top: parent.top @@ -39,27 +41,11 @@ Item right: parent.right rightMargin: UM.Theme.getSize("wide_margin").width } - iconName: "dialog-restart" + height: UM.Theme.getSize("toolbox_footer_button").height + text: catalog.i18nc("@info:button", "Quit Cura") onClicked: toolbox.restart() - style: ButtonStyle - { - background: Rectangle - { - implicitWidth: UM.Theme.getSize("toolbox_footer_button").width - implicitHeight: Math.floor(UM.Theme.getSize("toolbox_footer_button").height) - color: control.hovered ? UM.Theme.getColor("primary_hover") : UM.Theme.getColor("primary") - } - label: Label - { - color: UM.Theme.getColor("button_text") - font: UM.Theme.getFont("default_bold") - text: control.text - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - renderType: Text.NativeRendering - } - } } + ToolboxShadow { visible: footer.visible From f72b58386b39d1140ffdc489127eb396604afb77 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Thu, 13 Dec 2018 11:46:11 +0100 Subject: [PATCH 0878/1240] Also use CuraConstants for account API root --- cura/API/Account.py | 10 ++++++---- cura/CuraConstants.py | 6 ++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/cura/API/Account.py b/cura/API/Account.py index 397e220478..7b4bc32e99 100644 --- a/cura/API/Account.py +++ b/cura/API/Account.py @@ -6,6 +6,7 @@ from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, pyqtProperty from UM.i18n import i18nCatalog from UM.Message import Message +from cura import CuraConstants from cura.OAuth2.AuthorizationService import AuthorizationService from cura.OAuth2.Models import OAuth2Settings @@ -37,15 +38,16 @@ class Account(QObject): self._logged_in = False self._callback_port = 32118 - self._oauth_root = "https://account.ultimaker.com" - self._cloud_api_root = "https://api.ultimaker.com" + self._oauth_root = CuraConstants.CuraCloudAccountAPIRoot self._oauth_settings = OAuth2Settings( OAUTH_SERVER_URL= self._oauth_root, CALLBACK_PORT=self._callback_port, CALLBACK_URL="http://localhost:{}/callback".format(self._callback_port), - CLIENT_ID="um---------------ultimaker_cura_drive_plugin", - CLIENT_SCOPES="account.user.read drive.backup.read drive.backup.write packages.download packages.rating.read packages.rating.write", + CLIENT_ID="um----------------------------ultimaker_cura", + CLIENT_SCOPES="account.user.read drive.backup.read drive.backup.write packages.download " + "packages.rating.read packages.rating.write connect.cluster.read connect.cluster.write " + "cura.printjob.read cura.printjob.write cura.mesh.read cura.mesh.write", AUTH_DATA_PREFERENCE_KEY="general/ultimaker_auth_data", AUTH_SUCCESS_REDIRECT="{}/app/auth-success".format(self._oauth_root), AUTH_FAILED_REDIRECT="{}/app/auth-error".format(self._oauth_root) diff --git a/cura/CuraConstants.py b/cura/CuraConstants.py index c573d550c5..7ca8ea865b 100644 --- a/cura/CuraConstants.py +++ b/cura/CuraConstants.py @@ -42,6 +42,7 @@ except ImportError: # --------- DEFAULT_CLOUD_API_ROOT = "https://api.ultimaker.com" # type: str DEFAULT_CLOUD_API_VERSION = "1" # type: str +DEFAULT_CLOUD_ACCOUNT_API_ROOT = "https://account.ultimaker.com" # type: str try: from cura.CuraVersion import CuraCloudAPIRoot # type: ignore @@ -52,3 +53,8 @@ try: from cura.CuraVersion import CuraCloudAPIVersion # type: ignore except ImportError: CuraCloudAPIVersion = DEFAULT_CLOUD_API_VERSION + +try: + from cura.CuraVersion import CuraCloudAccountAPIRoot # type: ignore +except ImportError: + CuraCloudAccountAPIRoot = DEFAULT_CLOUD_ACCOUNT_API_ROOT From 819f8531a21f68ebf47f18c347043f30117dbf35 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Thu, 13 Dec 2018 11:54:10 +0100 Subject: [PATCH 0879/1240] Use CuraConstants for Cloud printing API root --- plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py index b08bac6670..2dd0c84442 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py @@ -7,6 +7,7 @@ from typing import Callable, List, Type, TypeVar, Union, Optional, Tuple, Dict, from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply from UM.Logger import Logger +from cura import CuraConstants from cura.API import Account from cura.NetworkClient import NetworkClient from ..Models import BaseModel @@ -23,8 +24,7 @@ from .Models.CloudPrintJobResponse import CloudPrintJobResponse class CloudApiClient(NetworkClient): # The cloud URL to use for this remote cluster. - # TODO: Make sure that this URL goes to the live api before release - ROOT_PATH = "https://api-staging.ultimaker.com" + ROOT_PATH = CuraConstants.CuraCloudAPIRoot CLUSTER_API_ROOT = "{}/connect/v1".format(ROOT_PATH) CURA_API_ROOT = "{}/cura/v1".format(ROOT_PATH) From 0a6c1a7c14e2d67ec8879ec2dc24e2acbade0c1d Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 13 Dec 2018 13:12:57 +0100 Subject: [PATCH 0880/1240] Fix CLIENT_ID (again) It got messed up in a merge again. Ugh. --- cura/API/Account.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/API/Account.py b/cura/API/Account.py index 397e220478..be77a6307b 100644 --- a/cura/API/Account.py +++ b/cura/API/Account.py @@ -44,7 +44,7 @@ class Account(QObject): OAUTH_SERVER_URL= self._oauth_root, CALLBACK_PORT=self._callback_port, CALLBACK_URL="http://localhost:{}/callback".format(self._callback_port), - CLIENT_ID="um---------------ultimaker_cura_drive_plugin", + CLIENT_ID="um----------------------------ultimaker_cura", CLIENT_SCOPES="account.user.read drive.backup.read drive.backup.write packages.download packages.rating.read packages.rating.write", AUTH_DATA_PREFERENCE_KEY="general/ultimaker_auth_data", AUTH_SUCCESS_REDIRECT="{}/app/auth-success".format(self._oauth_root), From 2cf80b457820595cee714f012f3e9b46ef1c6348 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 12 Dec 2018 09:57:25 +0100 Subject: [PATCH 0881/1240] Remove unused simpleNames flag CURA-6015 --- cura/Settings/ExtrudersModel.py | 17 ----------------- .../resources/qml/UM3InfoComponents.qml | 4 +--- resources/qml/PrintMonitor.qml | 1 - 3 files changed, 1 insertion(+), 21 deletions(-) diff --git a/cura/Settings/ExtrudersModel.py b/cura/Settings/ExtrudersModel.py index e19617c8ef..84d40cea6e 100644 --- a/cura/Settings/ExtrudersModel.py +++ b/cura/Settings/ExtrudersModel.py @@ -78,8 +78,6 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): self._update_extruder_timer.setSingleShot(True) self._update_extruder_timer.timeout.connect(self.__updateExtruders) - self._simple_names = False - self._active_machine_extruders = [] # type: Iterable[ExtruderStack] self._add_optional_extruder = False @@ -101,21 +99,6 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): def addOptionalExtruder(self): return self._add_optional_extruder - ## Set the simpleNames property. - def setSimpleNames(self, simple_names): - if simple_names != self._simple_names: - self._simple_names = simple_names - self.simpleNamesChanged.emit() - self._updateExtruders() - - ## Emitted when the simpleNames property changes. - simpleNamesChanged = pyqtSignal() - - ## Whether or not the model should show all definitions regardless of visibility. - @pyqtProperty(bool, fset = setSimpleNames, notify = simpleNamesChanged) - def simpleNames(self): - return self._simple_names - ## Links to the stack-changed signal of the new extruders when an extruder # is swapped out or added in the current machine. # diff --git a/plugins/UM3NetworkPrinting/resources/qml/UM3InfoComponents.qml b/plugins/UM3NetworkPrinting/resources/qml/UM3InfoComponents.qml index 643c8164a7..0ee8880c36 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/UM3InfoComponents.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/UM3InfoComponents.qml @@ -83,9 +83,7 @@ Item { Column { Repeater { - model: Cura.ExtrudersModel { - simpleNames: true; - } + model: Cura.ExtrudersModel { } Label { text: model.name; diff --git a/resources/qml/PrintMonitor.qml b/resources/qml/PrintMonitor.qml index 4ed8daa55c..316dcad653 100644 --- a/resources/qml/PrintMonitor.qml +++ b/resources/qml/PrintMonitor.qml @@ -63,7 +63,6 @@ Rectangle Cura.ExtrudersModel { id: extrudersModel - simpleNames: true } OutputDeviceHeader From 620790ae3de03113f23eafc0e1374fad7d222eb7 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 12 Dec 2018 11:08:33 +0100 Subject: [PATCH 0882/1240] Reduce the creations of ExtrudersModels CURA-6015 --- cura/CuraApplication.py | 15 +++++++++++++++ .../MachineSettingsAction.qml | 2 +- .../SimulationViewMenuComponent.qml | 2 +- plugins/SolidView/SolidView.py | 6 ++++-- .../resources/qml/UM3InfoComponents.qml | 2 +- .../Menus/ConfigurationMenu/ConfigurationMenu.qml | 5 +---- resources/qml/Menus/ContextMenu.qml | 2 +- resources/qml/Preferences/ProfilesPage.qml | 2 +- resources/qml/PrintMonitor.qml | 5 +---- .../Custom/CustomPrintSetup.qml | 5 +---- .../qml/PrintSetupSelector/PrintSetupSelector.qml | 5 +---- .../Recommended/RecommendedSupportSelector.qml | 5 +++-- resources/qml/Settings/SettingExtruder.qml | 9 +++++++-- .../qml/Settings/SettingOptionalExtruder.qml | 8 +++++--- resources/qml/Toolbar.qml | 5 +---- 15 files changed, 44 insertions(+), 34 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 7e11fd4d59..55e37617d5 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -205,6 +205,8 @@ class CuraApplication(QtApplication): self._container_manager = None self._object_manager = None + self._extruders_model = None + self._extruders_model_with_optional = None self._build_plate_model = None self._multi_build_plate_model = None self._setting_visibility_presets_model = None @@ -862,6 +864,19 @@ class CuraApplication(QtApplication): self._object_manager = ObjectsModel.createObjectsModel() return self._object_manager + @pyqtSlot(result = QObject) + def getExtrudersModel(self, *args) -> "ExtrudersModel": + if self._extruders_model is None: + self._extruders_model = ExtrudersModel(self) + return self._extruders_model + + @pyqtSlot(result = QObject) + def getExtrudersModelWithOptional(self, *args) -> "ExtrudersModel": + if self._extruders_model_with_optional is None: + self._extruders_model_with_optional = ExtrudersModel(self) + self._extruders_model_with_optional.setAddOptionalExtruder(True) + return self._extruders_model_with_optional + @pyqtSlot(result = QObject) def getMultiBuildPlateModel(self, *args) -> MultiBuildPlateModel: if self._multi_build_plate_model is None: diff --git a/plugins/MachineSettingsAction/MachineSettingsAction.qml b/plugins/MachineSettingsAction/MachineSettingsAction.qml index c88a721a84..d8efe6f8b8 100644 --- a/plugins/MachineSettingsAction/MachineSettingsAction.qml +++ b/plugins/MachineSettingsAction/MachineSettingsAction.qml @@ -13,7 +13,7 @@ import Cura 1.0 as Cura Cura.MachineAction { id: base - property var extrudersModel: Cura.ExtrudersModel{} + property var extrudersModel: CuraApplication.getExtrudersModel() property int extruderTabsCount: 0 property var activeMachineId: Cura.MachineManager.activeMachine != null ? Cura.MachineManager.activeMachine.id : "" diff --git a/plugins/SimulationView/SimulationViewMenuComponent.qml b/plugins/SimulationView/SimulationViewMenuComponent.qml index 9f43252126..eec254c0dd 100644 --- a/plugins/SimulationView/SimulationViewMenuComponent.qml +++ b/plugins/SimulationView/SimulationViewMenuComponent.qml @@ -163,7 +163,7 @@ Cura.ExpandableComponent Repeater { - model: Cura.ExtrudersModel{} + model: CuraApplication.getExtrudersModel() CheckBox { diff --git a/plugins/SolidView/SolidView.py b/plugins/SolidView/SolidView.py index b9ad5c8829..797d6dabec 100644 --- a/plugins/SolidView/SolidView.py +++ b/plugins/SolidView/SolidView.py @@ -12,7 +12,6 @@ from UM.Math.Color import Color from UM.View.GL.OpenGL import OpenGL from cura.Settings.ExtruderManager import ExtruderManager -from cura.Settings.ExtrudersModel import ExtrudersModel import math @@ -29,13 +28,16 @@ class SolidView(View): self._non_printing_shader = None self._support_mesh_shader = None - self._extruders_model = ExtrudersModel() + self._extruders_model = None self._theme = None def beginRendering(self): scene = self.getController().getScene() renderer = self.getRenderer() + if not self._extruders_model: + self._extruders_model = Application.getInstance().getExtrudersModel() + if not self._theme: self._theme = Application.getInstance().getTheme() diff --git a/plugins/UM3NetworkPrinting/resources/qml/UM3InfoComponents.qml b/plugins/UM3NetworkPrinting/resources/qml/UM3InfoComponents.qml index 0ee8880c36..42e3b7d160 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/UM3InfoComponents.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/UM3InfoComponents.qml @@ -83,7 +83,7 @@ Item { Column { Repeater { - model: Cura.ExtrudersModel { } + model: CuraApplication.getExtrudersModel() Label { text: model.name; diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index 33a317b42b..d95dd9ebfa 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -17,10 +17,7 @@ Cura.ExpandablePopup { id: base - Cura.ExtrudersModel - { - id: extrudersModel - } + property var extrudersModel: CuraApplication.getExtrudersModel() UM.I18nCatalog { diff --git a/resources/qml/Menus/ContextMenu.qml b/resources/qml/Menus/ContextMenu.qml index 1ea402d815..cb10d50ce8 100644 --- a/resources/qml/Menus/ContextMenu.qml +++ b/resources/qml/Menus/ContextMenu.qml @@ -27,7 +27,7 @@ Menu MenuItem { id: extruderHeader; text: catalog.i18ncp("@label", "Print Selected Model With:", "Print Selected Models With:", UM.Selection.selectionCount); enabled: false; visible: base.shouldShowExtruders } Instantiator { - model: Cura.ExtrudersModel { id: extrudersModel } + model: CuraApplication.getExtrudersModel() MenuItem { text: "%1: %2 - %3".arg(model.name).arg(model.material).arg(model.variant) visible: base.shouldShowExtruders diff --git a/resources/qml/Preferences/ProfilesPage.qml b/resources/qml/Preferences/ProfilesPage.qml index d7ffbb3152..7fb17b7aa1 100644 --- a/resources/qml/Preferences/ProfilesPage.qml +++ b/resources/qml/Preferences/ProfilesPage.qml @@ -16,7 +16,7 @@ Item property QtObject qualityManager: CuraApplication.getQualityManager() property var resetEnabled: false // Keep PreferencesDialog happy - property var extrudersModel: Cura.ExtrudersModel {} + property var extrudersModel: CuraApplication.getExtrudersModel() UM.I18nCatalog { id: catalog; name: "cura"; } diff --git a/resources/qml/PrintMonitor.qml b/resources/qml/PrintMonitor.qml index 316dcad653..6d8edf0deb 100644 --- a/resources/qml/PrintMonitor.qml +++ b/resources/qml/PrintMonitor.qml @@ -60,10 +60,7 @@ Rectangle anchors.fill: parent - Cura.ExtrudersModel - { - id: extrudersModel - } + property var extrudersModel: CuraApplication.getExtrudersModel() OutputDeviceHeader { diff --git a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml index b28c9ceb46..27de8df835 100644 --- a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml +++ b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml @@ -16,10 +16,7 @@ Item property real padding: UM.Theme.getSize("default_margin").width property bool multipleExtruders: extrudersModel.count > 1 - Cura.ExtrudersModel - { - id: extrudersModel - } + property var extrudersModel: CuraApplication.getExtrudersModel() // Profile selector row GlobalProfileSelector diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector/PrintSetupSelector.qml index 599eac957e..2d4d7f6cf1 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelector.qml @@ -26,10 +26,7 @@ Cura.ExpandableComponent headerItem: PrintSetupSelectorHeader {} - Cura.ExtrudersModel - { - id: extrudersModel - } + property var extrudersModel: CuraApplication.getExtrudersModel() contentItem: PrintSetupSelectorContents {} } \ No newline at end of file diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml index 57e0c8ce6b..87fb664713 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml @@ -156,9 +156,10 @@ Item } //: Model used to populate the extrudelModel - Cura.ExtrudersModel + property var extruders: CuraApplication.getExtrudersModel() + Connections { - id: extruders + target: extruders onModelChanged: populateExtruderModel() } diff --git a/resources/qml/Settings/SettingExtruder.qml b/resources/qml/Settings/SettingExtruder.qml index e1fedd9274..024eb17639 100644 --- a/resources/qml/Settings/SettingExtruder.qml +++ b/resources/qml/Settings/SettingExtruder.qml @@ -17,11 +17,16 @@ SettingItem id: control anchors.fill: parent - model: Cura.ExtrudersModel + property var extrudersModel: CuraApplication.getExtrudersModel() + + model: extrudersModel + + Connections { + target: extrudersModel onModelChanged: { - control.color = getItem(control.currentIndex).color + control.color = extrudersModel.getItem(control.currentIndex).color } } diff --git a/resources/qml/Settings/SettingOptionalExtruder.qml b/resources/qml/Settings/SettingOptionalExtruder.qml index 200a3f64f1..d9ec1f07c4 100644 --- a/resources/qml/Settings/SettingOptionalExtruder.qml +++ b/resources/qml/Settings/SettingOptionalExtruder.qml @@ -17,10 +17,12 @@ SettingItem id: control anchors.fill: parent - model: Cura.ExtrudersModel + model: CuraApplication.getExtrudersModelWithOptional() + + Connections { - onModelChanged: control.color = getItem(control.currentIndex).color - addOptionalExtruder: true + target: model + onModelChanged: control.color = model.getItem(control.currentIndex).color } textRole: "name" diff --git a/resources/qml/Toolbar.qml b/resources/qml/Toolbar.qml index 1e335472d4..1df516a315 100644 --- a/resources/qml/Toolbar.qml +++ b/resources/qml/Toolbar.qml @@ -144,10 +144,7 @@ Item } } - Cura.ExtrudersModel - { - id: extrudersModel - } + property var extrudersModel: CuraApplication.getExtrudersModel() UM.PointingRectangle { From 935f7a2512e14e361903155acbfc7198fa881512 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 12 Dec 2018 11:09:22 +0100 Subject: [PATCH 0883/1240] Remove unused imports CURA-6015 --- cura/Settings/ExtrudersModel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/Settings/ExtrudersModel.py b/cura/Settings/ExtrudersModel.py index 84d40cea6e..b7fa659554 100644 --- a/cura/Settings/ExtrudersModel.py +++ b/cura/Settings/ExtrudersModel.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 Qt, pyqtSignal, pyqtSlot, pyqtProperty, QTimer +from PyQt5.QtCore import Qt, pyqtSignal, pyqtProperty, QTimer from typing import Iterable from UM.i18n import i18nCatalog From df0b1c6c7735757e73317881fdc1f3b461cdabbd Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 12 Dec 2018 11:10:22 +0100 Subject: [PATCH 0884/1240] Fix ExtruderManager creation in MachineManager CURA-6015 --- cura/Settings/MachineManager.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index c375ce01d1..e26b82e487 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -88,12 +88,14 @@ class MachineManager(QObject): self._onGlobalContainerChanged() - ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged) + extruder_manager = self._application.getExtruderManager() + + extruder_manager.activeExtruderChanged.connect(self._onActiveExtruderStackChanged) self._onActiveExtruderStackChanged() - ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeMaterialChanged) - ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeVariantChanged) - ExtruderManager.getInstance().activeExtruderChanged.connect(self.activeQualityChanged) + extruder_manager.activeExtruderChanged.connect(self.activeMaterialChanged) + extruder_manager.activeExtruderChanged.connect(self.activeVariantChanged) + extruder_manager.activeExtruderChanged.connect(self.activeQualityChanged) self.globalContainerChanged.connect(self.activeStackChanged) self.globalValueChanged.connect(self.activeStackValueChanged) From d879cab91ae2030a2aeb7a32c772dce7cfaf28e8 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 13 Dec 2018 13:49:06 +0100 Subject: [PATCH 0885/1240] Add all fields for optional extruder in ExtruderModel CURA-6015 --- cura/Settings/ExtrudersModel.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cura/Settings/ExtrudersModel.py b/cura/Settings/ExtrudersModel.py index b7fa659554..076cebf60d 100644 --- a/cura/Settings/ExtrudersModel.py +++ b/cura/Settings/ExtrudersModel.py @@ -204,7 +204,12 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel): "enabled": True, "color": "#ffffff", "index": -1, - "definition": "" + "definition": "", + "material": "", + "variant": "", + "stack": None, + "material_brand": "", + "color_name": "", } items.append(item) if self._items != items: From 8021c8e44900aa89ae4254b09e17be2179165b18 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 13 Dec 2018 13:49:45 +0100 Subject: [PATCH 0886/1240] Fix errors in SettingOptionalExtruder.qml CURA-6015 --- resources/qml/Settings/SettingOptionalExtruder.qml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/resources/qml/Settings/SettingOptionalExtruder.qml b/resources/qml/Settings/SettingOptionalExtruder.qml index d9ec1f07c4..aabf808d83 100644 --- a/resources/qml/Settings/SettingOptionalExtruder.qml +++ b/resources/qml/Settings/SettingOptionalExtruder.qml @@ -12,17 +12,24 @@ SettingItem id: base property var focusItem: control + // Somehow if we directory set control.model to CuraApplication.getExtrudersModelWithOptional() + // and in the Connections.onModelChanged use control.model as a reference, it will complain about + // non-existing properties such as "onModelChanged" and "getItem". I guess if we access the model + // via "control.model", it gives back a generic/abstract model instance. To avoid this, we add + // this extra property to keep the ExtrudersModel and use this in the rest of the code. + property var extrudersWithOptionalModel: CuraApplication.getExtrudersModelWithOptional() + contents: ComboBox { id: control anchors.fill: parent - model: CuraApplication.getExtrudersModelWithOptional() + model: base.extrudersWithOptionalModel Connections { - target: model - onModelChanged: control.color = model.getItem(control.currentIndex).color + target: base.extrudersWithOptionalModel + onModelChanged: control.color = base.extrudersWithOptionalModel.getItem(control.currentIndex).color } textRole: "name" From a1a9b058f5e874f0ab41871afc94a7ac712badd0 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 13 Dec 2018 14:28:32 +0100 Subject: [PATCH 0887/1240] Let header listen to activeStageId instead of the model CURA-6028 --- resources/qml/MainWindow/MainWindowHeader.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/qml/MainWindow/MainWindowHeader.qml b/resources/qml/MainWindow/MainWindowHeader.qml index ae1c13d9c3..6cb7370ec2 100644 --- a/resources/qml/MainWindow/MainWindowHeader.qml +++ b/resources/qml/MainWindow/MainWindowHeader.qml @@ -54,7 +54,8 @@ Item { text: model.name.toUpperCase() checkable: true - checked: model.active + checked: model.id == UM.Controller.activeStage.stageId + anchors.verticalCenter: parent.verticalCenter exclusiveGroup: mainWindowHeaderMenuGroup style: UM.Theme.styles.main_window_header_tab From c4700b27522dafa6d2164ea08b2215e1f738e013 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 13 Dec 2018 14:30:00 +0100 Subject: [PATCH 0888/1240] Also update the model if the data changed CURA-6028 --- .../Recommended/RecommendedQualityProfileSelector.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml index 15d40f545a..349c6dbb57 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml @@ -39,6 +39,7 @@ Item { target: Cura.QualityProfilesDropDownMenuModel onItemsChanged: qualityModel.update() + onDataChanged: qualityModel.update() } Connections { From 17f0c12858ae2a3cf6466e8e5bc900338d22f444 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 13 Dec 2018 14:31:34 +0100 Subject: [PATCH 0889/1240] Make the middle component to have zero width if there is no component to fill the gap. Contributes to CURA-6020. --- plugins/PreviewStage/PreviewMenu.qml | 95 +++++++++++++--------------- 1 file changed, 44 insertions(+), 51 deletions(-) diff --git a/plugins/PreviewStage/PreviewMenu.qml b/plugins/PreviewStage/PreviewMenu.qml index 4e1fbac837..62f814aac9 100644 --- a/plugins/PreviewStage/PreviewMenu.qml +++ b/plugins/PreviewStage/PreviewMenu.qml @@ -20,67 +20,60 @@ Item name: "cura" } - // Item to ensure that all of the buttons are nicely centered. - Item + Row { - anchors.horizontalCenter: parent.horizontalCenter - width: stageMenuRow.width + id: stageMenuRow + anchors.centerIn: parent height: parent.height + width: childrenRect.width - RowLayout + // We want this row to have a preferred with equals to the 85% of the parent + property int preferredWidth: Math.round(0.85 * previewMenu.width) + + Cura.ViewsSelector { - id: stageMenuRow - width: Math.round(0.85 * previewMenu.width) + id: viewsSelector height: parent.height - spacing: 0 + width: UM.Theme.getSize("views_selector").width + headerCornerSide: Cura.RoundedRectangle.Direction.Left + } - Cura.ViewsSelector - { - id: viewsSelector - headerCornerSide: Cura.RoundedRectangle.Direction.Left - Layout.minimumWidth: UM.Theme.getSize("views_selector").width - Layout.maximumWidth: UM.Theme.getSize("views_selector").width - Layout.fillWidth: true - Layout.fillHeight: true - } + // Separator line + Rectangle + { + height: parent.height + // If there is no viewPanel, we only need a single spacer, so hide this one. + visible: viewPanel.source != "" + width: visible ? UM.Theme.getSize("default_lining").width : 0 - // Separator line - Rectangle - { - height: parent.height - // If there is no viewPanel, we only need a single spacer, so hide this one. - visible: viewPanel.source != "" - width: UM.Theme.getSize("default_lining").width + color: UM.Theme.getColor("lining") + } - color: UM.Theme.getColor("lining") - } + // This component will grow freely up to complete the preferredWidth of the row. + Loader + { + id: viewPanel + height: parent.height + width: source != "" ? (stageMenuRow.preferredWidth - viewsSelector.width - printSetupSelectorItem.width - 2 * UM.Theme.getSize("default_lining").width) : 0 + source: UM.Controller.activeView != null && UM.Controller.activeView.stageMenuComponent != null ? UM.Controller.activeView.stageMenuComponent : "" + } - Loader - { - id: viewPanel - Layout.fillHeight: true - Layout.fillWidth: true - Layout.preferredWidth: stageMenuRow.width - viewsSelector.width - printSetupSelectorItem.width - 2 * UM.Theme.getSize("default_lining").width - source: UM.Controller.activeView != null && UM.Controller.activeView.stageMenuComponent != null ? UM.Controller.activeView.stageMenuComponent : "" - } + // Separator line + Rectangle + { + height: parent.height + width: UM.Theme.getSize("default_lining").width + color: UM.Theme.getColor("lining") + } - // Separator line - Rectangle - { - height: parent.height - width: UM.Theme.getSize("default_lining").width - color: UM.Theme.getColor("lining") - } - - Item - { - id: printSetupSelectorItem - // This is a work around to prevent the printSetupSelector from having to be re-loaded every time - // a stage switch is done. - children: [printSetupSelector] - height: childrenRect.height - width: childrenRect.width - } + Item + { + id: printSetupSelectorItem + // This is a work around to prevent the printSetupSelector from having to be re-loaded every time + // a stage switch is done. + children: [printSetupSelector] + height: childrenRect.height + width: childrenRect.width } } } From 8091b2810c2feb04af81c8deef362688ed26eea4 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 13 Dec 2018 14:36:58 +0100 Subject: [PATCH 0890/1240] Apply suggestions from code review Change some margins for the corresponding absolute values instead of adding formulas. Contributes to CURA-5876. Co-Authored-By: diegopradogesto --- .../ConfigurationMenu/ConfigurationItem.qml | 16 ++++++++-------- .../ConfigurationMenu/ConfigurationListView.qml | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index a73cd3b46c..862e1475a9 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -36,9 +36,9 @@ Button anchors { left: parent.left - leftMargin: 2 * parent.padding + leftMargin: UM.Theme.getSize("wide_margin").width right: parent.right - rightMargin: 2 * parent.padding + rightMargin: UM.Theme.getSize("wide_margin").width } spacing: UM.Theme.getSize("default_margin").width @@ -64,9 +64,9 @@ Button anchors { left: parent.left - leftMargin: 2 * parent.padding + leftMargin: UM.Theme.getSize("wide_margin").width right: parent.right - rightMargin: 2 * parent.padding + rightMargin: UM.Theme.getSize("wide_margin").width } height: visible ? Math.round(UM.Theme.getSize("default_lining").height / 2) : 0 color: UM.Theme.getColor("lining") @@ -79,9 +79,9 @@ Button anchors { left: parent.left - leftMargin: 2 * parent.padding + leftMargin: UM.Theme.getSize("wide_margin").width right: parent.right - rightMargin: 2 * parent.padding + rightMargin: UM.Theme.getSize("wide_margin").width } height: childrenRect.height visible: configuration.buildplateConfiguration != "" @@ -101,7 +101,7 @@ Button id: buildplateLabel anchors.left: buildplateIcon.right anchors.verticalCenter: buildplateIcon.verticalCenter - anchors.leftMargin: Math.round(UM.Theme.getSize("default_margin").height / 2) + anchors.leftMargin: UM.Theme.getSize("narrow_margin").height text: configuration.buildplateConfiguration renderType: Text.NativeRendering color: UM.Theme.getColor("text") @@ -127,4 +127,4 @@ Button { Cura.MachineManager.applyRemoteConfiguration(configuration) } -} \ No newline at end of file +} diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml index 3ddbb49fe8..684e575bfd 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml @@ -28,7 +28,7 @@ Item { width: parent.width visible: configurationList.model.length == 0 - height: label.height + 2 * UM.Theme.getSize("default_margin").height + height: label.height + UM.Theme.getSize("wide_margin").height anchors.top: parent.top anchors.topMargin: UM.Theme.getSize("default_margin").height @@ -134,4 +134,4 @@ Item forceModelUpdate() } } -} \ No newline at end of file +} From b48eedfb805fd8ed6aa3accb4658622dcd57ef43 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 13 Dec 2018 15:15:22 +0100 Subject: [PATCH 0891/1240] Modify color for the progress bar in the messages. --- resources/themes/cura-light/theme.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 1a7d377fa7..6d76004739 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -255,8 +255,8 @@ "message_button_text": [255, 255, 255, 255], "message_button_text_hover": [255, 255, 255, 255], "message_button_text_active": [255, 255, 255, 255], - "message_progressbar_background": [200, 200, 200, 255], - "message_progressbar_control": [77, 182, 226, 255], + "message_progressbar_background": [245, 245, 245, 255], + "message_progressbar_control": [50, 130, 255, 255], "tool_panel_background": [255, 255, 255, 255], From 3225512851f61f0ac9f18ae6322f8eeb2c3bfd79 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 13 Dec 2018 15:48:03 +0100 Subject: [PATCH 0892/1240] Fix typing CURA-6013 --- plugins/Toolbox/src/Toolbox.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 715c105bad..05669e55d8 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -49,7 +49,7 @@ class Toolbox(QObject, Extension): self._download_progress = 0 # type: float self._is_downloading = False # type: bool self._network_manager = None # type: Optional[QNetworkAccessManager] - self._request_headers = [] # type: List[Tuple(bytes, bytes)] + self._request_headers = [] # type: List[Tuple[bytes, bytes]] self._updateRequestHeader() From 1b3cb323344cd6c75ec160ca936a1aff379ad64f Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 13 Dec 2018 15:59:15 +0100 Subject: [PATCH 0893/1240] Fix the simulation view not being selected as default CURA-6028 --- resources/qml/ViewsSelector.qml | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/resources/qml/ViewsSelector.qml b/resources/qml/ViewsSelector.qml index 0e2598a0d8..d91412cad0 100644 --- a/resources/qml/ViewsSelector.qml +++ b/resources/qml/ViewsSelector.qml @@ -14,24 +14,31 @@ Cura.ExpandablePopup contentPadding: UM.Theme.getSize("default_lining").width contentAlignment: Cura.ExpandablePopup.ContentAlignment.AlignLeft - property var viewModel: UM.ViewModel { } - - property var activeView: + property var viewModel: UM.ViewModel { - for (var i = 0; i < viewModel.count; i++) + onDataChanged: updateActiveView() + } + + + property var activeView: null + + function updateActiveView() + { + for (var index in viewModel.items) { - if (viewModel.items[i].active) + + if (viewModel.items[index].active) { - return viewModel.items[i] + activeView = viewModel.items[index] + return } } - return null + activeView = null } Component.onCompleted: { - // Nothing was active, so just return the first one (the list is sorted by priority, so the most - // important one should be returned) + updateActiveView() if (activeView == null) { UM.Controller.setActiveView(viewModel.getItem(0).id) From 973970a89505fea4cb3e0f4d003c8360b2b2353e Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Wed, 12 Dec 2018 09:53:26 +0100 Subject: [PATCH 0894/1240] Every print ouput device should define its connection type(usb, network, cluster and etc) CURA-6011 --- .../NetworkedPrinterOutputDevice.py | 6 ++--- cura/PrinterOutputDevice.py | 19 ++++++++++++++- .../resources/qml/DiscoverUM3Action.qml | 2 +- .../src/ClusterUM3OutputDevice.py | 3 ++- .../src/DiscoverUM3Action.py | 23 ++++++++++++------- .../src/LegacyUM3OutputDevice.py | 3 ++- plugins/USBPrinting/USBPrinterOutputDevice.py | 4 ++-- .../PrinterSelector/MachineSelectorList.qml | 2 +- 8 files changed, 44 insertions(+), 18 deletions(-) diff --git a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py index 9a3be936a2..c288596f0c 100644 --- a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py +++ b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py @@ -6,7 +6,7 @@ from UM.Logger import Logger from UM.Scene.SceneNode import SceneNode #For typing. from cura.CuraApplication import CuraApplication -from cura.PrinterOutputDevice import PrinterOutputDevice, ConnectionState +from cura.PrinterOutputDevice import PrinterOutputDevice, ConnectionState, ConnectionType from PyQt5.QtNetwork import QHttpMultiPart, QHttpPart, QNetworkRequest, QNetworkAccessManager, QNetworkReply, QAuthenticator from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QUrl, QCoreApplication @@ -28,8 +28,8 @@ class AuthState(IntEnum): class NetworkedPrinterOutputDevice(PrinterOutputDevice): authenticationStateChanged = pyqtSignal() - def __init__(self, device_id, address: str, properties: Dict[bytes, bytes], parent: QObject = None) -> None: - super().__init__(device_id = device_id, parent = parent) + def __init__(self, device_id, address: str, properties: Dict[bytes, bytes], connection_type: ConnectionType = ConnectionType.networkConnection, parent: QObject = None) -> None: + super().__init__(device_id = device_id, connection_type = connection_type, parent = parent) self._manager = None # type: Optional[QNetworkAccessManager] self._last_manager_create_time = None # type: Optional[float] self._recreate_network_manager_time = 30 diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 99c48189cc..3a72fba4e3 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -34,6 +34,12 @@ class ConnectionState(IntEnum): busy = 3 error = 4 +class ConnectionType(IntEnum): + none = 0 + usbConnection = 1 + networkConnection = 2 + clusterConnection = 3 + cloudConnection = 4 ## Printer output device adds extra interface options on top of output device. # @@ -62,7 +68,7 @@ class PrinterOutputDevice(QObject, OutputDevice): # Signal to indicate that the configuration of one of the printers has changed. uniqueConfigurationsChanged = pyqtSignal() - def __init__(self, device_id: str, parent: QObject = None) -> None: + def __init__(self, device_id: str, connection_type: ConnectionType, parent: QObject = None) -> None: super().__init__(device_id = device_id, parent = parent) # type: ignore # MyPy complains with the multiple inheritance self._printers = [] # type: List[PrinterOutputModel] @@ -84,6 +90,7 @@ class PrinterOutputDevice(QObject, OutputDevice): self._update_timer.timeout.connect(self._update) self._connection_state = ConnectionState.closed #type: ConnectionState + self._connection_type = connection_type self._firmware_updater = None #type: Optional[FirmwareUpdater] self._firmware_name = None #type: Optional[str] @@ -117,6 +124,16 @@ class PrinterOutputDevice(QObject, OutputDevice): self._connection_state = connection_state self.connectionStateChanged.emit(self._id) + def checkConnectionType(self, connection_type: ConnectionType) -> bool: + return connection_type == self._connection_type + + def getConnectionType(self) -> ConnectionType: + return self._connection_type + + def setConnectionType(self, new_connection_type: ConnectionType) -> None: + if self._connection_type != new_connection_type: + self._connection_type = new_connection_type + @pyqtProperty(str, notify = connectionStateChanged) def connectionState(self) -> ConnectionState: return self._connection_state diff --git a/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml b/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml index e5f668c70d..2c9ab2df03 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml @@ -28,7 +28,7 @@ Cura.MachineAction // Check if there is another instance with the same key if (!manager.existsKey(printerKey)) { - manager.setKey(printerKey) + manager.setKey(base.selectedDevice) manager.setGroupName(printerName) // TODO To change when the groups have a name completed() } diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index ef890fc4ed..206654ff88 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -22,6 +22,7 @@ from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationM from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutputDevice, AuthState from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel +from cura.PrinterOutputDevice import ConnectionType from .ClusterUM3PrinterOutputController import ClusterUM3PrinterOutputController from .SendMaterialJob import SendMaterialJob @@ -54,7 +55,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): clusterPrintersChanged = pyqtSignal() def __init__(self, device_id, address, properties, parent = None) -> None: - super().__init__(device_id = device_id, address = address, properties=properties, parent = parent) + super().__init__(device_id = device_id, address = address, properties=properties, connection_type = ConnectionType.clusterConnection, parent = parent) self._api_prefix = "/cluster-api/v1/" self._number_of_extruders = 2 diff --git a/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py b/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py index be83e04585..9cd21de036 100644 --- a/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py +++ b/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py @@ -3,7 +3,7 @@ import os.path import time -from typing import cast, Optional +from typing import Optional from PyQt5.QtCore import pyqtSignal, pyqtProperty, pyqtSlot, QObject @@ -13,6 +13,7 @@ from UM.i18n import i18nCatalog from cura.CuraApplication import CuraApplication from cura.MachineAction import MachineAction +from cura.PrinterOutputDevice import PrinterOutputDevice from .UM3OutputDevicePlugin import UM3OutputDevicePlugin @@ -116,22 +117,28 @@ class DiscoverUM3Action(MachineAction): # Ensure that the connection states are refreshed. self._network_plugin.reCheckConnections() - @pyqtSlot(str) - def setKey(self, key: str) -> None: - Logger.log("d", "Attempting to set the network key of the active machine to %s", key) + @pyqtSlot(QObject) + def setKey(self, printer_device: Optional[PrinterOutputDevice]) -> None: + Logger.log("d", "Attempting to set the network key of the active machine to %s", printer_device.key) global_container_stack = CuraApplication.getInstance().getGlobalContainerStack() if global_container_stack: meta_data = global_container_stack.getMetaData() if "um_network_key" in meta_data: previous_network_key= meta_data["um_network_key"] - global_container_stack.setMetaDataEntry("um_network_key", key) + global_container_stack.setMetaDataEntry("um_network_key", printer_device.key) # Delete old authentication data. - Logger.log("d", "Removing old authentication id %s for device %s", global_container_stack.getMetaDataEntry("network_authentication_id", None), key) + Logger.log("d", "Removing old authentication id %s for device %s", global_container_stack.getMetaDataEntry("network_authentication_id", None), printer_device.key) global_container_stack.removeMetaDataEntry("network_authentication_id") global_container_stack.removeMetaDataEntry("network_authentication_key") - CuraApplication.getInstance().getMachineManager().replaceContainersMetadata(key = "um_network_key", value = previous_network_key, new_value = key) + CuraApplication.getInstance().getMachineManager().replaceContainersMetadata(key = "um_network_key", value = previous_network_key, new_value = printer_device.key) + + if "um_connection_type" in meta_data: + previous_connection_type = meta_data["um_connection_type"] + global_container_stack.setMetaDataEntry("um_connection_type", printer_device.getConnectionType().value) + CuraApplication.getInstance().getMachineManager().replaceContainersMetadata(key = "um_connection_type", value = previous_connection_type, new_value = printer_device.getConnectionType().value) else: - global_container_stack.setMetaDataEntry("um_network_key", key) + global_container_stack.setMetaDataEntry("um_network_key", printer_device.key) + global_container_stack.setMetaDataEntry("um_connection_type", printer_device.getConnectionType().value) if self._network_plugin: # Ensure that the connection states are refreshed. diff --git a/plugins/UM3NetworkPrinting/src/LegacyUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/LegacyUM3OutputDevice.py index 18af22e72f..0ae9e8a004 100644 --- a/plugins/UM3NetworkPrinting/src/LegacyUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/LegacyUM3OutputDevice.py @@ -7,6 +7,7 @@ from cura.PrinterOutput.NetworkedPrinterOutputDevice import NetworkedPrinterOutp from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel +from cura.PrinterOutputDevice import ConnectionType from cura.Settings.ContainerManager import ContainerManager from cura.Settings.ExtruderManager import ExtruderManager @@ -43,7 +44,7 @@ i18n_catalog = i18nCatalog("cura") # 5. As a final step, we verify the authentication, as this forces the QT manager to setup the authenticator. class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice): def __init__(self, device_id, address: str, properties, parent = None) -> None: - super().__init__(device_id = device_id, address = address, properties = properties, parent = parent) + super().__init__(device_id = device_id, address = address, properties = properties, connection_type = ConnectionType.networkConnection, parent = parent) self._api_prefix = "/api/v1/" self._number_of_extruders = 2 diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index 1d42e70366..a8cb240b03 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -7,7 +7,7 @@ from UM.i18n import i18nCatalog from UM.Qt.Duration import DurationFormat from cura.CuraApplication import CuraApplication -from cura.PrinterOutputDevice import PrinterOutputDevice, ConnectionState +from cura.PrinterOutputDevice import PrinterOutputDevice, ConnectionState, ConnectionType from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from cura.PrinterOutput.PrintJobOutputModel import PrintJobOutputModel from cura.PrinterOutput.GenericOutputController import GenericOutputController @@ -29,7 +29,7 @@ catalog = i18nCatalog("cura") class USBPrinterOutputDevice(PrinterOutputDevice): def __init__(self, serial_port: str, baud_rate: Optional[int] = None) -> None: - super().__init__(serial_port) + super().__init__(serial_port, connection_type=ConnectionType.usbConnection) self.setName(catalog.i18nc("@item:inmenu", "USB printing")) self.setShortDescription(catalog.i18nc("@action:button Preceded by 'Ready to'.", "Print via USB")) self.setDescription(catalog.i18nc("@info:tooltip", "Print via USB")) diff --git a/resources/qml/PrinterSelector/MachineSelectorList.qml b/resources/qml/PrinterSelector/MachineSelectorList.qml index 54d766a6e0..3324b9948b 100644 --- a/resources/qml/PrinterSelector/MachineSelectorList.qml +++ b/resources/qml/PrinterSelector/MachineSelectorList.qml @@ -32,7 +32,7 @@ Column id: networkedPrintersModel filter: { - "type": "machine", "um_network_key": "*", "hidden": "False" + "type": "machine", "um_network_key": "*", "hidden": "False", "um_connection_type": "[2,3,4]" } } From d48e89e22432ae9146c404a745d785af9491da37 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 13 Dec 2018 16:32:48 +0100 Subject: [PATCH 0895/1240] Prevent the parent of printSetupSelector from being set to null If it's parent gets set to null it will break the focus, which we need for the binding updates. CURA-5941 --- resources/qml/Cura.qml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index a4faa27b67..cf6f36a492 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -278,6 +278,14 @@ UM.MainWindow height: UM.Theme.getSize("stage_menu").height source: UM.Controller.activeStage != null ? UM.Controller.activeStage.stageMenuComponent : "" + + // HACK: This is to ensure that the parent never gets set to null, as this wreaks havoc on the focus. + function onParentDestroyed() + { + printSetupSelector.parent = stageMenu + printSetupSelector.visible = false + } + // The printSetupSelector is defined here so that the setting list doesn't need to get re-instantiated // Every time the stage is changed. property var printSetupSelector: Cura.PrintSetupSelector @@ -285,6 +293,11 @@ UM.MainWindow width: UM.Theme.getSize("print_setup_widget").width height: UM.Theme.getSize("stage_menu").height headerCornerSide: RoundedRectangle.Direction.Right + onParentChanged: + { + visible = parent != stageMenu + parent.Component.destruction.connect(stageMenu.onParentDestroyed) + } } } From 3f3cd5e33443ebe1f01671f5c08e3397c85fd962 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 13 Dec 2018 16:32:27 +0100 Subject: [PATCH 0896/1240] Remove broken load of PrepareSidebar.qml This file no longer exists. Loading it gives a warning. --- cura/Stages/CuraStage.py | 4 ---- plugins/PrepareStage/PrepareStage.py | 6 +----- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/cura/Stages/CuraStage.py b/cura/Stages/CuraStage.py index e8537fb6b9..844b0d0768 100644 --- a/cura/Stages/CuraStage.py +++ b/cura/Stages/CuraStage.py @@ -24,10 +24,6 @@ class CuraStage(Stage): def mainComponent(self) -> QUrl: return self.getDisplayComponent("main") - @pyqtProperty(QUrl, constant = True) - def sidebarComponent(self) -> QUrl: - return self.getDisplayComponent("sidebar") - @pyqtProperty(QUrl, constant = True) def stageMenuComponent(self) -> QUrl: return self.getDisplayComponent("menu") \ No newline at end of file diff --git a/plugins/PrepareStage/PrepareStage.py b/plugins/PrepareStage/PrepareStage.py index b22f3385b8..b0f862dc48 100644 --- a/plugins/PrepareStage/PrepareStage.py +++ b/plugins/PrepareStage/PrepareStage.py @@ -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 os.path from UM.Application import Application @@ -15,9 +15,5 @@ class PrepareStage(CuraStage): Application.getInstance().engineCreatedSignal.connect(self._engineCreated) def _engineCreated(self): - sidebar_component_path = os.path.join(Resources.getPath(Application.getInstance().ResourceTypes.QmlFiles), - "PrepareSidebar.qml") - menu_component_path = os.path.join(PluginRegistry.getInstance().getPluginPath("PrepareStage"), "PrepareMenu.qml") self.addDisplayComponent("menu", menu_component_path) - self.addDisplayComponent("sidebar", sidebar_component_path) From 012ee0c02a66ef4e1bbbc5dde6aee8d8fec9988a Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 13 Dec 2018 16:44:49 +0100 Subject: [PATCH 0897/1240] Use the MouseArea trick to assure that the binding still works for the checked property of the button. Contributes to CURA-6028. --- resources/qml/MainWindow/MainWindowHeader.qml | 8 +++++++- resources/qml/ViewsSelector.qml | 3 --- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/resources/qml/MainWindow/MainWindowHeader.qml b/resources/qml/MainWindow/MainWindowHeader.qml index 6cb7370ec2..793df42da0 100644 --- a/resources/qml/MainWindow/MainWindowHeader.qml +++ b/resources/qml/MainWindow/MainWindowHeader.qml @@ -60,11 +60,17 @@ Item exclusiveGroup: mainWindowHeaderMenuGroup style: UM.Theme.styles.main_window_header_tab height: UM.Theme.getSize("main_window_header_button").height - onClicked: UM.Controller.setActiveStage(model.id) iconSource: model.stage.iconSource property color overlayColor: "transparent" property string overlayIconSource: "" + + // This is a trick to assure the activeStage is correctly changed. It doesn't work propertly if done in the onClicked (see CURA-6028) + MouseArea + { + anchors.fill: parent + onClicked: UM.Controller.setActiveStage(model.id) + } } } diff --git a/resources/qml/ViewsSelector.qml b/resources/qml/ViewsSelector.qml index d91412cad0..06d2e662b5 100644 --- a/resources/qml/ViewsSelector.qml +++ b/resources/qml/ViewsSelector.qml @@ -19,14 +19,12 @@ Cura.ExpandablePopup onDataChanged: updateActiveView() } - property var activeView: null function updateActiveView() { for (var index in viewModel.items) { - if (viewModel.items[index].active) { activeView = viewModel.items[index] @@ -38,7 +36,6 @@ Cura.ExpandablePopup Component.onCompleted: { - updateActiveView() if (activeView == null) { UM.Controller.setActiveView(viewModel.getItem(0).id) From 3d4da94b36593505a4e86f41e46999f9660337f1 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 13 Dec 2018 16:49:27 +0100 Subject: [PATCH 0898/1240] Remove obsolete description detail about old Cura versions This setting had a detail in the description about how the setting used to behave in old Cura versions. Let's remove it since it is not relevant any more. Fixes #4536. --- resources/definitions/fdmprinter.def.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index c015ab8ccb..f39e267354 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -3385,7 +3385,7 @@ "retraction_combing": { "label": "Combing Mode", - "description": "Combing keeps the nozzle within already printed areas when traveling. This results in slightly longer travel moves but reduces the need for retractions. If combing is off, the material will retract and the nozzle moves in a straight line to the next point. It is also possible to avoid combing over top/bottom skin areas and also to only comb within the infill. Note that the 'Within Infill' option behaves exactly like the 'Not in Skin' option in earlier Cura releases.", + "description": "Combing keeps the nozzle within already printed areas when traveling. This results in slightly longer travel moves but reduces the need for retractions. If combing is off, the material will retract and the nozzle moves in a straight line to the next point. It is also possible to avoid combing over top/bottom skin areas or to only comb within the infill.", "type": "enum", "options": { From 473160a102c902bd4161d3b7d4027414a71c144a Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 13 Dec 2018 17:02:13 +0100 Subject: [PATCH 0899/1240] Add a check for null to avoid warnings in the logs. --- resources/qml/MainWindow/MainWindowHeader.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/MainWindow/MainWindowHeader.qml b/resources/qml/MainWindow/MainWindowHeader.qml index 793df42da0..8f6957d9cd 100644 --- a/resources/qml/MainWindow/MainWindowHeader.qml +++ b/resources/qml/MainWindow/MainWindowHeader.qml @@ -54,7 +54,7 @@ Item { text: model.name.toUpperCase() checkable: true - checked: model.id == UM.Controller.activeStage.stageId + checked: UM.Controller.activeStage != null ? model.id == UM.Controller.activeStage.stageId : false anchors.verticalCenter: parent.verticalCenter exclusiveGroup: mainWindowHeaderMenuGroup From 856c1dcb20d42a624bfc367ca135042d7404953f Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 13 Dec 2018 17:21:07 +0100 Subject: [PATCH 0900/1240] Also disconnect the signal for the old parent of printSetupSelector --- resources/qml/Cura.qml | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index cf6f36a492..ff5a603fda 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -278,29 +278,33 @@ UM.MainWindow height: UM.Theme.getSize("stage_menu").height source: UM.Controller.activeStage != null ? UM.Controller.activeStage.stageMenuComponent : "" - // HACK: This is to ensure that the parent never gets set to null, as this wreaks havoc on the focus. function onParentDestroyed() { printSetupSelector.parent = stageMenu printSetupSelector.visible = false } + property item oldParent: null // The printSetupSelector is defined here so that the setting list doesn't need to get re-instantiated // Every time the stage is changed. property var printSetupSelector: Cura.PrintSetupSelector { - width: UM.Theme.getSize("print_setup_widget").width - height: UM.Theme.getSize("stage_menu").height - headerCornerSide: RoundedRectangle.Direction.Right - onParentChanged: - { - visible = parent != stageMenu - parent.Component.destruction.connect(stageMenu.onParentDestroyed) - } + width: UM.Theme.getSize("print_setup_widget").width + height: UM.Theme.getSize("stage_menu").height + headerCornerSide: RoundedRectangle.Direction.Right + onParentChanged: + { + if(stageMenu.oldParent !=null) + { + stageMenu.oldParent.Component.destruction.disconnect(stageMenu.onParentDestroyed) + } + stageMenu.oldParent = parent + visible = parent != stageMenu + parent.Component.destruction.connect(stageMenu.onParentDestroyed) + } } } - UM.MessageStack { anchors From 8dc2a41fb7af876a2f4c041fd68482f131b4385c Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Thu, 13 Dec 2018 17:27:16 +0100 Subject: [PATCH 0901/1240] Typo, wrong item type, "item" CURA-5941 --- resources/qml/Cura.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index ff5a603fda..2b6f989e0b 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -284,7 +284,7 @@ UM.MainWindow printSetupSelector.parent = stageMenu printSetupSelector.visible = false } - property item oldParent: null + property Item oldParent: null // The printSetupSelector is defined here so that the setting list doesn't need to get re-instantiated // Every time the stage is changed. From 6bf39a32a941dfc4ef96a3f250527c04d9060541 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 14 Dec 2018 09:56:06 +0100 Subject: [PATCH 0902/1240] Rename Enum names to camal cases CURA-6011 --- .../NetworkedPrinterOutputDevice.py | 10 +++---- cura/PrinterOutputDevice.py | 26 ++++++++++--------- .../src/ClusterUM3OutputDevice.py | 2 +- .../src/LegacyUM3OutputDevice.py | 2 +- plugins/USBPrinting/USBPrinterOutputDevice.py | 8 +++--- .../USBPrinterOutputDeviceManager.py | 2 +- 6 files changed, 26 insertions(+), 24 deletions(-) diff --git a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py index c288596f0c..14f1364601 100644 --- a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py +++ b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py @@ -28,7 +28,7 @@ class AuthState(IntEnum): class NetworkedPrinterOutputDevice(PrinterOutputDevice): authenticationStateChanged = pyqtSignal() - def __init__(self, device_id, address: str, properties: Dict[bytes, bytes], connection_type: ConnectionType = ConnectionType.networkConnection, parent: QObject = None) -> None: + def __init__(self, device_id, address: str, properties: Dict[bytes, bytes], connection_type: ConnectionType = ConnectionType.NetworkConnection, parent: QObject = None) -> None: super().__init__(device_id = device_id, connection_type = connection_type, parent = parent) self._manager = None # type: Optional[QNetworkAccessManager] self._last_manager_create_time = None # type: Optional[float] @@ -125,7 +125,7 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): if self._connection_state_before_timeout is None: self._connection_state_before_timeout = self._connection_state - self.setConnectionState(ConnectionState.closed) + self.setConnectionState(ConnectionState.Closed) # We need to check if the manager needs to be re-created. If we don't, we get some issues when OSX goes to # sleep. @@ -133,7 +133,7 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): if self._last_manager_create_time is None or time() - self._last_manager_create_time > self._recreate_network_manager_time: self._createNetworkManager() assert(self._manager is not None) - elif self._connection_state == ConnectionState.closed: + elif self._connection_state == ConnectionState.Closed: # Go out of timeout. if self._connection_state_before_timeout is not None: # sanity check, but it should never be None here self.setConnectionState(self._connection_state_before_timeout) @@ -285,8 +285,8 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): self._last_response_time = time() - if self._connection_state == ConnectionState.connecting: - self.setConnectionState(ConnectionState.connected) + if self._connection_state == ConnectionState.Connecting: + self.setConnectionState(ConnectionState.Connected) callback_key = reply.url().toString() + str(reply.operation()) try: diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 3a72fba4e3..51be3c0465 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -28,18 +28,20 @@ i18n_catalog = i18nCatalog("cura") ## The current processing state of the backend. class ConnectionState(IntEnum): - closed = 0 - connecting = 1 - connected = 2 - busy = 3 + Closed = 0 + Connecting = 1 + Connected = 2 + Busy = 3 error = 4 + class ConnectionType(IntEnum): none = 0 - usbConnection = 1 - networkConnection = 2 - clusterConnection = 3 - cloudConnection = 4 + UsbConnection = 1 + NetworkConnection = 2 + ClusterConnection = 3 + CloudConnection = 4 + ## Printer output device adds extra interface options on top of output device. # @@ -89,7 +91,7 @@ class PrinterOutputDevice(QObject, OutputDevice): self._update_timer.setSingleShot(False) self._update_timer.timeout.connect(self._update) - self._connection_state = ConnectionState.closed #type: ConnectionState + self._connection_state = ConnectionState.Closed #type: ConnectionState self._connection_type = connection_type self._firmware_updater = None #type: Optional[FirmwareUpdater] @@ -117,7 +119,7 @@ class PrinterOutputDevice(QObject, OutputDevice): callback(QMessageBox.Yes) def isConnected(self) -> bool: - return self._connection_state != ConnectionState.closed and self._connection_state != ConnectionState.error + return self._connection_state != ConnectionState.Closed and self._connection_state != ConnectionState.error def setConnectionState(self, connection_state: ConnectionState) -> None: if self._connection_state != connection_state: @@ -191,13 +193,13 @@ class PrinterOutputDevice(QObject, OutputDevice): ## Attempt to establish connection def connect(self) -> None: - self.setConnectionState(ConnectionState.connecting) + self.setConnectionState(ConnectionState.Connecting) self._update_timer.start() ## Attempt to close the connection def close(self) -> None: self._update_timer.stop() - self.setConnectionState(ConnectionState.closed) + self.setConnectionState(ConnectionState.Closed) ## Ensure that close gets called when object is destroyed def __del__(self) -> None: diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index 206654ff88..d85cbeb27b 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -55,7 +55,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): clusterPrintersChanged = pyqtSignal() def __init__(self, device_id, address, properties, parent = None) -> None: - super().__init__(device_id = device_id, address = address, properties=properties, connection_type = ConnectionType.clusterConnection, parent = parent) + super().__init__(device_id = device_id, address = address, properties=properties, connection_type = ConnectionType.ClusterConnection, parent = parent) self._api_prefix = "/cluster-api/v1/" self._number_of_extruders = 2 diff --git a/plugins/UM3NetworkPrinting/src/LegacyUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/LegacyUM3OutputDevice.py index 0ae9e8a004..3ce0460d6b 100644 --- a/plugins/UM3NetworkPrinting/src/LegacyUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/LegacyUM3OutputDevice.py @@ -44,7 +44,7 @@ i18n_catalog = i18nCatalog("cura") # 5. As a final step, we verify the authentication, as this forces the QT manager to setup the authenticator. class LegacyUM3OutputDevice(NetworkedPrinterOutputDevice): def __init__(self, device_id, address: str, properties, parent = None) -> None: - super().__init__(device_id = device_id, address = address, properties = properties, connection_type = ConnectionType.networkConnection, parent = parent) + super().__init__(device_id = device_id, address = address, properties = properties, connection_type = ConnectionType.NetworkConnection, parent = parent) self._api_prefix = "/api/v1/" self._number_of_extruders = 2 diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index a8cb240b03..08cf29baf4 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -29,7 +29,7 @@ catalog = i18nCatalog("cura") class USBPrinterOutputDevice(PrinterOutputDevice): def __init__(self, serial_port: str, baud_rate: Optional[int] = None) -> None: - super().__init__(serial_port, connection_type=ConnectionType.usbConnection) + super().__init__(serial_port, connection_type=ConnectionType.UsbConnection) self.setName(catalog.i18nc("@item:inmenu", "USB printing")) self.setShortDescription(catalog.i18nc("@action:button Preceded by 'Ready to'.", "Print via USB")) self.setDescription(catalog.i18nc("@info:tooltip", "Print via USB")) @@ -179,7 +179,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): return CuraApplication.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerStackChanged) self._onGlobalContainerStackChanged() - self.setConnectionState(ConnectionState.connected) + self.setConnectionState(ConnectionState.Connected) self._update_thread.start() def _onGlobalContainerStackChanged(self): @@ -208,7 +208,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): self._sendCommand(command) def _sendCommand(self, command: Union[str, bytes]): - if self._serial is None or self._connection_state != ConnectionState.connected: + if self._serial is None or self._connection_state != ConnectionState.Connected: return new_command = cast(bytes, command) if type(command) is bytes else cast(str, command).encode() # type: bytes @@ -222,7 +222,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): self._command_received.set() def _update(self): - while self._connection_state == ConnectionState.connected and self._serial is not None: + while self._connection_state == ConnectionState.Connected and self._serial is not None: try: line = self._serial.readline() except: diff --git a/plugins/USBPrinting/USBPrinterOutputDeviceManager.py b/plugins/USBPrinting/USBPrinterOutputDeviceManager.py index bd207d9d96..d4c0d1828e 100644 --- a/plugins/USBPrinting/USBPrinterOutputDeviceManager.py +++ b/plugins/USBPrinting/USBPrinterOutputDeviceManager.py @@ -66,7 +66,7 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin): return changed_device = self._usb_output_devices[serial_port] - if changed_device.connectionState == ConnectionState.connected: + if changed_device.connectionState == ConnectionState.Connected: self.getOutputDeviceManager().addOutputDevice(changed_device) else: self.getOutputDeviceManager().removeOutputDevice(serial_port) From 0aa49270ebb53521b3364ce5ab795b51d3e3adba Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 14 Dec 2018 09:57:40 +0100 Subject: [PATCH 0903/1240] Remove unused function checkConnectionType() CURA-6011 --- cura/PrinterOutputDevice.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 51be3c0465..dce470f442 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -126,9 +126,6 @@ class PrinterOutputDevice(QObject, OutputDevice): self._connection_state = connection_state self.connectionStateChanged.emit(self._id) - def checkConnectionType(self, connection_type: ConnectionType) -> bool: - return connection_type == self._connection_type - def getConnectionType(self) -> ConnectionType: return self._connection_type From 644944bc4171a98b9abd8ee54493d967d6732779 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 14 Dec 2018 10:00:18 +0100 Subject: [PATCH 0904/1240] Remove unused function setConnectionType() CURA-6011 --- cura/PrinterOutputDevice.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index dce470f442..0b2f10c570 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -129,10 +129,6 @@ class PrinterOutputDevice(QObject, OutputDevice): def getConnectionType(self) -> ConnectionType: return self._connection_type - def setConnectionType(self, new_connection_type: ConnectionType) -> None: - if self._connection_type != new_connection_type: - self._connection_type = new_connection_type - @pyqtProperty(str, notify = connectionStateChanged) def connectionState(self) -> ConnectionState: return self._connection_state From 3e1993d87679c0d29f3df5cc1a14962b32ccb18a Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 14 Dec 2018 10:10:54 +0100 Subject: [PATCH 0905/1240] Rename Enum name to camal cases CURA-6011 --- cura/PrinterOutputDevice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 0b2f10c570..f6d15f5a4a 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -32,7 +32,7 @@ class ConnectionState(IntEnum): Connecting = 1 Connected = 2 Busy = 3 - error = 4 + Error = 4 class ConnectionType(IntEnum): From bb1bf14a0182710ebb5c08fcbc8248e8d67c711e Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 14 Dec 2018 10:46:31 +0100 Subject: [PATCH 0906/1240] Prevent rating when user is not logged in CURA-6013 --- plugins/Toolbox/resources/qml/RatingWidget.qml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/plugins/Toolbox/resources/qml/RatingWidget.qml b/plugins/Toolbox/resources/qml/RatingWidget.qml index 9d9eb8bca8..2623d13c0e 100644 --- a/plugins/Toolbox/resources/qml/RatingWidget.qml +++ b/plugins/Toolbox/resources/qml/RatingWidget.qml @@ -25,7 +25,10 @@ Item acceptedButtons: Qt.NoButton onExited: { - ratingWidget.indexHovered = -1 + if(ratingWidget.canRate) + { + ratingWidget.indexHovered = -1 + } } Row @@ -87,7 +90,13 @@ Item return "#5a5a5a" } } - onClicked: rated(index + 1) // Notify anyone who cares about this. + onClicked: + { + if(ratingWidget.canRate) + { + rated(index + 1) // Notify anyone who cares about this. + } + } } } } From 8bb8ae8652d1abb7f153aa8bcd309dd183b2b262 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 14 Dec 2018 10:48:23 +0100 Subject: [PATCH 0907/1240] Rename ConnectionType.none to ConnectionType.Unknown CURA-6011 Cannot use None --- cura/PrinterOutputDevice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index f6d15f5a4a..b664e30d0b 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -36,7 +36,7 @@ class ConnectionState(IntEnum): class ConnectionType(IntEnum): - none = 0 + Unknown = 0 UsbConnection = 1 NetworkConnection = 2 ClusterConnection = 3 From 99cee1dfe766b16b5a9b0cedf3c439ca7ded39b5 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 14 Dec 2018 10:54:52 +0100 Subject: [PATCH 0908/1240] Use double-quotes for custom type hinting in functions CURA-6011 --- cura/PrinterOutputDevice.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index b664e30d0b..2db03a4e87 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -70,7 +70,7 @@ class PrinterOutputDevice(QObject, OutputDevice): # Signal to indicate that the configuration of one of the printers has changed. uniqueConfigurationsChanged = pyqtSignal() - def __init__(self, device_id: str, connection_type: ConnectionType, parent: QObject = None) -> None: + def __init__(self, device_id: str, connection_type: "ConnectionType", parent: QObject = None) -> None: super().__init__(device_id = device_id, parent = parent) # type: ignore # MyPy complains with the multiple inheritance self._printers = [] # type: List[PrinterOutputModel] @@ -119,18 +119,18 @@ class PrinterOutputDevice(QObject, OutputDevice): callback(QMessageBox.Yes) def isConnected(self) -> bool: - return self._connection_state != ConnectionState.Closed and self._connection_state != ConnectionState.error + return self._connection_state != ConnectionState.Closed and self._connection_state != ConnectionState.Error - def setConnectionState(self, connection_state: ConnectionState) -> None: + def setConnectionState(self, connection_state: "ConnectionState") -> None: if self._connection_state != connection_state: self._connection_state = connection_state self.connectionStateChanged.emit(self._id) - def getConnectionType(self) -> ConnectionType: + def getConnectionType(self) -> "ConnectionType": return self._connection_type @pyqtProperty(str, notify = connectionStateChanged) - def connectionState(self) -> ConnectionState: + def connectionState(self) -> "ConnectionState": return self._connection_state def _update(self) -> None: From aba3b6dd8ee66ea05273e8f0ec74ae7a9c06611b Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 14 Dec 2018 10:55:34 +0100 Subject: [PATCH 0909/1240] Rename setKey() and add docs CURA-6011 --- .../resources/qml/DiscoverUM3Action.qml | 2 +- plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml b/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml index 2c9ab2df03..80b5c2f99e 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/DiscoverUM3Action.qml @@ -28,7 +28,7 @@ Cura.MachineAction // Check if there is another instance with the same key if (!manager.existsKey(printerKey)) { - manager.setKey(base.selectedDevice) + manager.associateActiveMachineWithPrinterDevice(base.selectedDevice) manager.setGroupName(printerName) // TODO To change when the groups have a name completed() } diff --git a/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py b/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py index 9cd21de036..2e59810317 100644 --- a/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py +++ b/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py @@ -3,7 +3,7 @@ import os.path import time -from typing import Optional +from typing import Optional, TYPE_CHECKING from PyQt5.QtCore import pyqtSignal, pyqtProperty, pyqtSlot, QObject @@ -13,10 +13,12 @@ from UM.i18n import i18nCatalog from cura.CuraApplication import CuraApplication from cura.MachineAction import MachineAction -from cura.PrinterOutputDevice import PrinterOutputDevice from .UM3OutputDevicePlugin import UM3OutputDevicePlugin +if TYPE_CHECKING: + from cura.PrinterOutputDevice import PrinterOutputDevice + catalog = i18nCatalog("cura") @@ -117,8 +119,10 @@ class DiscoverUM3Action(MachineAction): # Ensure that the connection states are refreshed. self._network_plugin.reCheckConnections() + # Associates the currently active machine with the given printer device. The network connection information will be + # stored into the metadata of the currently active machine. @pyqtSlot(QObject) - def setKey(self, printer_device: Optional[PrinterOutputDevice]) -> None: + def associateActiveMachineWithPrinterDevice(self, printer_device: Optional["PrinterOutputDevice"]) -> None: Logger.log("d", "Attempting to set the network key of the active machine to %s", printer_device.key) global_container_stack = CuraApplication.getInstance().getGlobalContainerStack() if global_container_stack: From 450f301c8c1211684db866eee9e1d8573a4d8665 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 14 Dec 2018 10:56:13 +0100 Subject: [PATCH 0910/1240] Fix binding loop CURA-6013 --- plugins/Toolbox/resources/qml/ToolboxDetailPage.qml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml index 0e183c2860..95c3a20e1e 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml @@ -57,7 +57,6 @@ Item top: thumbnail.top left: thumbnail.right leftMargin: UM.Theme.getSize("default_margin").width - bottomMargin: UM.Theme.getSize("default_margin").height } text: details === null ? "" : (details.name || "") font: UM.Theme.getFont("large") @@ -226,13 +225,6 @@ Item renderType: Text.NativeRendering } } - Rectangle - { - color: UM.Theme.getColor("lining") - width: parent.width - height: UM.Theme.getSize("default_lining").height - anchors.bottom: parent.bottom - } } ToolboxDetailList { From 7616f9c97db92a7b213bd45232bb419e51581436 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 14 Dec 2018 10:59:59 +0100 Subject: [PATCH 0911/1240] Make hardcoded color themeable CURA-6013 --- plugins/Toolbox/resources/qml/RatingWidget.qml | 4 ++-- plugins/Toolbox/resources/qml/SmallRatingWidget.qml | 2 +- resources/themes/cura-light/theme.json | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/Toolbox/resources/qml/RatingWidget.qml b/plugins/Toolbox/resources/qml/RatingWidget.qml index 2623d13c0e..3dcae6d602 100644 --- a/plugins/Toolbox/resources/qml/RatingWidget.qml +++ b/plugins/Toolbox/resources/qml/RatingWidget.qml @@ -81,13 +81,13 @@ Item { if(!ratingWidget.canRate) { - return "#5a5a5a" + return UM.Theme.getColor("rating_star") } if((ratingWidget.indexHovered >= 0 || ratingWidget.userRating > 0) && isStarFilled) { return UM.Theme.getColor("primary") } - return "#5a5a5a" + return UM.Theme.getColor("rating_star") } } onClicked: diff --git a/plugins/Toolbox/resources/qml/SmallRatingWidget.qml b/plugins/Toolbox/resources/qml/SmallRatingWidget.qml index 439a7baec0..bab219d294 100644 --- a/plugins/Toolbox/resources/qml/SmallRatingWidget.qml +++ b/plugins/Toolbox/resources/qml/SmallRatingWidget.qml @@ -14,7 +14,7 @@ Row { id: starIcon source: UM.Theme.getIcon("star_filled") - color: model.user_rating == 0 ? "#5a5a5a" : UM.Theme.getColor("primary") + color: model.user_rating == 0 ? UM.Theme.getColor("rating_star") : UM.Theme.getColor("primary") height: UM.Theme.getSize("rating_star").height width: UM.Theme.getSize("rating_star").width } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 969f5666a6..16f885c0b5 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -160,6 +160,8 @@ "extruder_button_material_border": [255, 255, 255, 255], + "rating_star": [90, 90, 90, 255], + "sync_button_text": [120, 120, 120, 255], "sync_button_text_hovered": [0, 0, 0, 255], From edd4f6e1faa7062299d832ceac2e412a3aec23b6 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 14 Dec 2018 11:02:10 +0100 Subject: [PATCH 0912/1240] Fix some minor QML warnings CURA-6013 --- plugins/Toolbox/resources/qml/ToolboxDetailPage.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml index 95c3a20e1e..92b9fb1198 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml @@ -138,8 +138,8 @@ Item { id: rating visible: details.type == "plugin" - packageId: details.id - userRating: details.user_rating + packageId: details.id != undefined ? details.id: "" + userRating: details.user_rating != undefined ? details.user_rating: 0 canRate: toolbox.isInstalled(details.id) && Cura.API.account.isLoggedIn onRated: From 5e4e52e6fc8fc912e9c43492f418cb77a6c12655 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 14 Dec 2018 11:09:48 +0100 Subject: [PATCH 0913/1240] Fix blue square being shown in selector if no icon is set CURA-5876 --- resources/qml/PrinterSelector/MachineSelector.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index db9c38b9f1..fb8f0a58e1 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -41,7 +41,7 @@ Cura.ExpandablePopup } font: UM.Theme.getFont("medium") iconColor: UM.Theme.getColor("machine_selector_printer_icon") - iconSize: UM.Theme.getSize("machine_selector_icon").width + iconSize: source != "" ? UM.Theme.getSize("machine_selector_icon").width: 0 UM.RecolorImage { From 4252b956039f4875e63735944ee8d1f36d5c1cb8 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 14 Dec 2018 11:28:18 +0100 Subject: [PATCH 0914/1240] Make ConnectionType Enum type accessible to QML CURA-6011 --- cura/CuraApplication.py | 3 +++ cura/PrinterOutputDevice.py | 8 +++++++- resources/qml/PrinterSelector/MachineSelectorList.qml | 9 ++++++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 7e11fd4d59..a50d7d55c8 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -113,6 +113,7 @@ from cura.Settings.CuraFormulaFunctions import CuraFormulaFunctions from cura.ObjectsModel import ObjectsModel +from cura.PrinterOutputDevice import PrinterOutputDevice from cura.PrinterOutput.NetworkMJPGImage import NetworkMJPGImage from UM.FlameProfiler import pyqtSlot @@ -975,6 +976,8 @@ class CuraApplication(QtApplication): qmlRegisterSingletonType(ContainerManager, "Cura", 1, 0, "ContainerManager", ContainerManager.getInstance) qmlRegisterType(SidebarCustomMenuItemsModel, "Cura", 1, 0, "SidebarCustomMenuItemsModel") + qmlRegisterType(PrinterOutputDevice, "Cura", 1, 0, "PrinterOutputDevice") + from cura.API import CuraAPI qmlRegisterSingletonType(CuraAPI, "Cura", 1, 1, "API", self.getCuraAPI) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 2db03a4e87..298c690b01 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -4,7 +4,7 @@ from UM.Decorators import deprecated from UM.i18n import i18nCatalog from UM.OutputDevice.OutputDevice import OutputDevice -from PyQt5.QtCore import pyqtProperty, pyqtSignal, QObject, QTimer, QUrl +from PyQt5.QtCore import pyqtProperty, pyqtSignal, QObject, QTimer, QUrl, Q_ENUMS from PyQt5.QtWidgets import QMessageBox from UM.Logger import Logger @@ -54,6 +54,12 @@ class ConnectionType(IntEnum): # For all other uses it should be used in the same way as a "regular" OutputDevice. @signalemitter class PrinterOutputDevice(QObject, OutputDevice): + + # Put ConnectionType here with Q_ENUMS() so it can be registered as a QML type and accessible via QML, and there is + # no need to remember what those Enum integer values mean. + ConnectionType = ConnectionType + Q_ENUMS(ConnectionType) + printersChanged = pyqtSignal() connectionStateChanged = pyqtSignal(str) acceptsCommandsChanged = pyqtSignal() diff --git a/resources/qml/PrinterSelector/MachineSelectorList.qml b/resources/qml/PrinterSelector/MachineSelectorList.qml index 3324b9948b..bc3fe105a2 100644 --- a/resources/qml/PrinterSelector/MachineSelectorList.qml +++ b/resources/qml/PrinterSelector/MachineSelectorList.qml @@ -30,9 +30,16 @@ Column model: UM.ContainerStacksModel { id: networkedPrintersModel + property var umConnectionTypes: [Cura.PrinterOutputDevice.NetworkConnection, + Cura.PrinterOutputDevice.ClusterConnection, + Cura.PrinterOutputDevice.CloudConnection + ] filter: { - "type": "machine", "um_network_key": "*", "hidden": "False", "um_connection_type": "[2,3,4]" + "type": "machine", + "um_network_key": "*", + "hidden": "False", + "um_connection_type": "[" + umConnectionTypes.join(",") + "]" } } From 15cd2a926975403ad0e225ffa0b2e63beadcab1b Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 14 Dec 2018 11:36:40 +0100 Subject: [PATCH 0915/1240] Changed textColor of recommended infill slider text to be themed. This is required to make the dark theme work correctly --- .../Recommended/RecommendedInfillDensitySelector.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml index 2971415948..0da53cc1c1 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml @@ -144,6 +144,7 @@ Item anchors.horizontalCenter: parent.horizontalCenter y: UM.Theme.getSize("thin_margin").height renderType: Text.NativeRendering + color: UM.Theme.getColor("quality_slider_available") } } } From a18203b2864358dfcde1206bf51250e523e6af91 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 14 Dec 2018 11:37:30 +0100 Subject: [PATCH 0916/1240] Fix typing CURA-6011 --- cura/PrinterOutputDevice.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 298c690b01..ed65d5d700 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -57,7 +57,6 @@ class PrinterOutputDevice(QObject, OutputDevice): # Put ConnectionType here with Q_ENUMS() so it can be registered as a QML type and accessible via QML, and there is # no need to remember what those Enum integer values mean. - ConnectionType = ConnectionType Q_ENUMS(ConnectionType) printersChanged = pyqtSignal() From 6a3ac9955162885c81859354d366d82cad40e42f Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 14 Dec 2018 11:59:22 +0100 Subject: [PATCH 0917/1240] Ensure that all icons use the same color from theme. Also added some fixes for the dark theme --- plugins/PrepareStage/PrepareMenu.qml | 2 +- resources/qml/ActionPanel/PrintInformationWidget.qml | 2 +- resources/qml/IconWithText.qml | 4 ++-- resources/qml/Toolbar.qml | 2 +- resources/themes/cura-dark/theme.json | 6 ++++++ resources/themes/cura-light/theme.json | 2 ++ 6 files changed, 13 insertions(+), 5 deletions(-) diff --git a/plugins/PrepareStage/PrepareMenu.qml b/plugins/PrepareStage/PrepareMenu.qml index b7980bc30b..b62d65254d 100644 --- a/plugins/PrepareStage/PrepareMenu.qml +++ b/plugins/PrepareStage/PrepareMenu.qml @@ -100,7 +100,7 @@ Item source: UM.Theme.getIcon("load") width: UM.Theme.getSize("button_icon").width height: UM.Theme.getSize("button_icon").height - color: UM.Theme.getColor("toolbar_button_text") + color: UM.Theme.getColor("icon") sourceSize.height: height } diff --git a/resources/qml/ActionPanel/PrintInformationWidget.qml b/resources/qml/ActionPanel/PrintInformationWidget.qml index 554273a818..0826e2c715 100644 --- a/resources/qml/ActionPanel/PrintInformationWidget.qml +++ b/resources/qml/ActionPanel/PrintInformationWidget.qml @@ -15,7 +15,7 @@ UM.RecolorImage width: UM.Theme.getSize("section_icon").width height: UM.Theme.getSize("section_icon").height - color: popup.opened ? UM.Theme.getColor("primary") : UM.Theme.getColor("text_medium") + color: UM.Theme.getColor("icon") MouseArea { diff --git a/resources/qml/IconWithText.qml b/resources/qml/IconWithText.qml index 5530740040..f9220380f2 100644 --- a/resources/qml/IconWithText.qml +++ b/resources/qml/IconWithText.qml @@ -18,7 +18,7 @@ Item property alias color: label.color property alias text: label.text property alias font: label.font - + property alias iconColor: icon.color property real margin: UM.Theme.getSize("narrow_margin").width // These properties can be used in combination with layouts. @@ -39,7 +39,7 @@ Item width: UM.Theme.getSize("section_icon").width height: UM.Theme.getSize("section_icon").height - color: label.color + color: UM.Theme.getColor("icon") anchors { diff --git a/resources/qml/Toolbar.qml b/resources/qml/Toolbar.qml index 1e335472d4..c3c4c1cd6d 100644 --- a/resources/qml/Toolbar.qml +++ b/resources/qml/Toolbar.qml @@ -67,7 +67,7 @@ Item toolItem: UM.RecolorImage { source: UM.Theme.getIcon(model.icon) != "" ? UM.Theme.getIcon(model.icon) : "file:///" + model.location + "/" + model.icon - color: UM.Theme.getColor("toolbar_button_text") + color: UM.Theme.getColor("icon") sourceSize: UM.Theme.getSize("button_icon") } diff --git a/resources/themes/cura-dark/theme.json b/resources/themes/cura-dark/theme.json index 8540abf61a..2fa96c8897 100644 --- a/resources/themes/cura-dark/theme.json +++ b/resources/themes/cura-dark/theme.json @@ -17,6 +17,12 @@ "border": [127, 127, 127, 255], "secondary": [95, 95, 95, 255], + "icon": [204, 204, 204, 255], + "toolbar_background": [39, 44, 48, 255], + "toolbar_button_active": [95, 95, 95, 255], + "toolbar_button_hover": [95, 95, 95, 255], + "toolbar_button_active_hover": [95, 95, 95, 255], + "main_window_header_button_text_inactive": [128, 128, 128, 255], "main_window_header_button_text_hovered": [255, 255, 255, 255], diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 6d76004739..b64190dd23 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -83,6 +83,8 @@ "secondary": [240, 240, 240, 255], "secondary_shadow": [216, 216, 216, 255], + "icon": [8, 7, 63, 255], + "primary_button": [38, 113, 231, 255], "primary_button_shadow": [27, 95, 202, 255], "primary_button_hover": [81, 145, 247, 255], From c3aca8907cdfe83439b863912dcf3291142980dc Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 14 Dec 2018 12:02:52 +0100 Subject: [PATCH 0918/1240] Fix extruder number not being visible in dark theme --- resources/qml/ExtruderIcon.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/qml/ExtruderIcon.qml b/resources/qml/ExtruderIcon.qml index bb0b347b7e..c44b6e0fa3 100644 --- a/resources/qml/ExtruderIcon.qml +++ b/resources/qml/ExtruderIcon.qml @@ -49,6 +49,7 @@ Item anchors.centerIn: parent text: index + 1 font: UM.Theme.getFont("very_small") + color: UM.Theme.getColor("text") width: contentWidth height: contentHeight visible: extruderEnabled From 4dc8edb99625de2c4b1efbe96eb3170e034b795b Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Fri, 14 Dec 2018 12:48:40 +0100 Subject: [PATCH 0919/1240] STAR-322: Fixing the multipart upload --- cura/NetworkClient.py | 82 +++++++------------ cura/OAuth2/AuthorizationService.py | 2 + .../src/Cloud/CloudOutputDevice.py | 12 +-- .../src/Cloud/CloudOutputDeviceManager.py | 34 ++++++-- .../src/Cloud/ResumableUpload.py | 53 ++++++++---- .../src/UM3OutputDevicePlugin.py | 2 + 6 files changed, 105 insertions(+), 80 deletions(-) diff --git a/cura/NetworkClient.py b/cura/NetworkClient.py index 6abf4f3d47..b455d03db0 100644 --- a/cura/NetworkClient.py +++ b/cura/NetworkClient.py @@ -1,7 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. from time import time -from typing import Optional, Dict, Callable, List, Union +from typing import Optional, Dict, Callable, List, Union, Tuple from PyQt5.QtCore import QUrl from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply, QHttpMultiPart, QNetworkRequest, QHttpPart, \ @@ -16,50 +16,63 @@ from UM.Logger import Logger class NetworkClient: def __init__(self) -> None: - + # Network manager instance to use for this client. self._manager = None # type: Optional[QNetworkAccessManager] - + # Timings. self._last_manager_create_time = None # type: Optional[float] self._last_response_time = None # type: Optional[float] self._last_request_time = None # type: Optional[float] - + # The user agent of Cura. application = Application.getInstance() self._user_agent = "%s/%s " % (application.getApplicationName(), application.getVersion()) # Uses to store callback methods for finished network requests. # This allows us to register network calls with a callback directly instead of having to dissect the reply. - self._on_finished_callbacks = {} # type: Dict[str, Callable[[QNetworkReply], None]] + # The key is created out of a tuple (operation, url) + self._on_finished_callbacks = {} # type: Dict[Tuple[int, str], Callable[[QNetworkReply], None]] # QHttpMultiPart objects need to be kept alive and not garbage collected during the # HTTP which uses them. We hold references to these QHttpMultiPart objects here. self._kept_alive_multiparts = {} # type: Dict[QNetworkReply, QHttpMultiPart] - ## Creates a network manager with all the required properties and event bindings. - def _createNetworkManager(self) -> None: + ## Creates a network manager if needed, with all the required properties and event bindings. + def start(self) -> None: if self._manager: - self._manager.finished.disconnect(self.__handleOnFinished) - self._manager.authenticationRequired.disconnect(self._onAuthenticationRequired) + return self._manager = QNetworkAccessManager() - self._manager.finished.connect(self.__handleOnFinished) + self._manager.finished.connect(self._handleOnFinished) self._last_manager_create_time = time() self._manager.authenticationRequired.connect(self._onAuthenticationRequired) + ## Destroys the network manager and event bindings. + def stop(self) -> None: + if not self._manager: + return + self._manager.finished.disconnect(self._handleOnFinished) + self._manager.authenticationRequired.disconnect(self._onAuthenticationRequired) + self._manager = None + ## Create a new empty network request. # Automatically adds the required HTTP headers. # \param url: The URL to request # \param content_type: The type of the body contents. def _createEmptyRequest(self, url: str, content_type: Optional[str] = "application/json") -> QNetworkRequest: + if not self._manager: + self.start() # make sure the manager is created request = QNetworkRequest(QUrl(url)) if content_type: request.setHeader(QNetworkRequest.ContentTypeHeader, content_type) request.setHeader(QNetworkRequest.UserAgentHeader, self._user_agent) + self._last_request_time = time() return request ## Executes the correct callback method when a network request finishes. - def __handleOnFinished(self, reply: QNetworkReply) -> None: + def _handleOnFinished(self, reply: QNetworkReply) -> None: + + Logger.log("i", "On finished %s %s", reply.attribute(QNetworkRequest.HttpStatusCodeAttribute), reply.url().toString()) # Due to garbage collection, we need to cache certain bits of post operations. # As we don't want to keep them around forever, delete them if we get a reply. @@ -76,7 +89,7 @@ class NetworkClient: # Find the right callback and execute it. # It always takes the full reply as single parameter. - callback_key = reply.url().toString() + str(reply.operation()) + callback_key = reply.operation(), reply.url().toString() if callback_key in self._on_finished_callbacks: self._on_finished_callbacks[callback_key](reply) else: @@ -87,12 +100,6 @@ class NetworkClient: if reply in self._kept_alive_multiparts: del self._kept_alive_multiparts[reply] - ## Makes sure the network manager is created. - def _validateManager(self) -> None: - if self._manager is None: - self._createNetworkManager() - assert self._manager is not None - ## Callback for when the network manager detects that authentication is required but was not given. @staticmethod def _onAuthenticationRequired(reply: QNetworkReply, authenticator: QAuthenticator) -> None: @@ -102,7 +109,7 @@ class NetworkClient: def _registerOnFinishedCallback(self, reply: QNetworkReply, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: if on_finished is not None: - self._on_finished_callbacks[reply.url().toString() + str(reply.operation())] = on_finished + self._on_finished_callbacks[reply.operation(), reply.url().toString()] = on_finished ## Add a part to a Multi-Part form. @staticmethod @@ -133,33 +140,23 @@ class NetworkClient: def put(self, url: str, data: Union[str, bytes], content_type: Optional[str] = None, on_finished: Optional[Callable[[QNetworkReply], None]] = None, on_progress: Optional[Callable[[int, int], None]] = None) -> None: - self._validateManager() - request = self._createEmptyRequest(url, content_type = content_type) - self._last_request_time = time() - - if not self._manager: - return Logger.log("e", "No network manager was created to execute the PUT call with.") body = data if isinstance(data, bytes) else data.encode() # type: bytes reply = self._manager.put(request, body) self._registerOnFinishedCallback(reply, on_finished) if on_progress is not None: + # TODO: Do we need to disconnect() as well? reply.uploadProgress.connect(on_progress) + reply.finished.connect(lambda r: Logger.log("i", "On finished %s %s", url, r)) + reply.error.connect(lambda r: Logger.log("i", "On error %s %s", url, r)) ## Sends a delete request to the given path. # url: The path after the API prefix. # on_finished: The function to be call when the response is received. def delete(self, url: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: - self._validateManager() - request = self._createEmptyRequest(url) - self._last_request_time = time() - - if not self._manager: - return Logger.log("e", "No network manager was created to execute the DELETE call with.") - reply = self._manager.deleteResource(request) self._registerOnFinishedCallback(reply, on_finished) @@ -167,14 +164,7 @@ class NetworkClient: # \param url: The path after the API prefix. # \param on_finished: The function to be call when the response is received. def get(self, url: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: - self._validateManager() - request = self._createEmptyRequest(url) - self._last_request_time = time() - - if not self._manager: - return Logger.log("e", "No network manager was created to execute the GET call with.") - reply = self._manager.get(request) self._registerOnFinishedCallback(reply, on_finished) @@ -186,13 +176,7 @@ class NetworkClient: def post(self, url: str, data: Union[str, bytes], on_finished: Optional[Callable[[QNetworkReply], None]], on_progress: Optional[Callable[[int, int], None]] = None) -> None: - self._validateManager() - request = self._createEmptyRequest(url) - self._last_request_time = time() - - if not self._manager: - return Logger.log("e", "Could not find manager.") body = data if isinstance(data, bytes) else data.encode() # type: bytes reply = self._manager.post(request, body) @@ -213,20 +197,12 @@ class NetworkClient: def postFormWithParts(self, target: str, parts: List[QHttpPart], on_finished: Optional[Callable[[QNetworkReply], None]], on_progress: Optional[Callable[[int, int], None]] = None) -> Optional[QNetworkReply]: - self._validateManager() - request = self._createEmptyRequest(target, content_type = None) multi_post_part = QHttpMultiPart(QHttpMultiPart.FormDataType) for part in parts: multi_post_part.append(part) - self._last_request_time = time() - - if not self._manager: - Logger.log("e", "No network manager was created to execute the POST call with.") - return None - reply = self._manager.post(request, multi_post_part) self._kept_alive_multiparts[reply] = multi_post_part diff --git a/cura/OAuth2/AuthorizationService.py b/cura/OAuth2/AuthorizationService.py index 4355891139..21dbbe8248 100644 --- a/cura/OAuth2/AuthorizationService.py +++ b/cura/OAuth2/AuthorizationService.py @@ -54,6 +54,8 @@ class AuthorizationService: self._user_profile = self._parseJWT() if not self._user_profile: # If there is still no user profile from the JWT, we have to log in again. + Logger.log("w", "The user profile could not be loaded. The user must log in again!") + self.deleteAuthData() return None return self._user_profile diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index e75989a6a8..83b5bed16b 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -66,7 +66,7 @@ class T: class CloudOutputDevice(NetworkedPrinterOutputDevice): # The interval with which the remote clusters are checked - CHECK_CLUSTER_INTERVAL = 5.0 # seconds + CHECK_CLUSTER_INTERVAL = 50.0 # seconds # Signal triggered when the print jobs in the queue were changed. printJobsChanged = pyqtSignal() @@ -150,7 +150,6 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): return # Indicate we have started sending a job. - self._sending_job = True self.writeStarted.emit(self) mesh_format = MeshFormatHandler(file_handler, self.firmwareVersion) @@ -173,6 +172,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): if self._last_response_time and time() - self._last_response_time < self.CHECK_CLUSTER_INTERVAL: return # avoid calling the cloud too often + Logger.log("i", "Requesting update for %s after %s", self._device_id, + self._last_response_time and time() - self._last_response_time) if self._account.isLoggedIn: self.setAuthenticationState(AuthState.Authenticated) self._api.getClusterStatus(self._device_id, self._onStatusCallFinished) @@ -183,6 +184,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # Contains both printers and print jobs statuses in a single response. def _onStatusCallFinished(self, status: CloudClusterStatus) -> None: # Update all data from the cluster. + self._last_response_time = time() if self._received_printers != status.printers: self._received_printers = status.printers self._updatePrinters(status.printers) @@ -289,7 +291,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Requests the print to be sent to the printer when we finished uploading the mesh. # \param job_id: The ID of the job. def _onPrintJobUploaded(self, job_id: str) -> None: - self._api.requestPrint(self._device_id, job_id, self._onUploadSuccess) + self._progress.update(100) + self._api.requestPrint(self._device_id, job_id, self._onPrintRequested) ## Displays the given message if uploading the mesh has failed # \param message: The message to display. @@ -304,7 +307,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Shows a message when the upload has succeeded # \param response: The response from the cloud API. - def _onUploadSuccess(self, response: CloudPrintResponse) -> None: + def _onPrintRequested(self, response: CloudPrintResponse) -> None: Logger.log("i", "The cluster will be printing this print job with the ID %s", response.cluster_job_id) self._progress.hide() Message( @@ -312,7 +315,6 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): title = T.UPLOAD_SUCCESS_TITLE, lifetime = 5 ).show() - self._sending_job = False # the upload has finished so we're not sending a job anymore self.writeFinished.emit() ## Gets the remote printers. diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index c9b30d7c79..68b5f99bba 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -26,7 +26,7 @@ class CloudOutputDeviceManager: META_CLUSTER_ID = "um_cloud_cluster_id" # The interval with which the remote clusters are checked - CHECK_CLUSTER_INTERVAL = 5.0 # seconds + CHECK_CLUSTER_INTERVAL = 50.0 # seconds # The translation catalog for this device. I18N_CATALOG = i18nCatalog("cura") @@ -39,26 +39,22 @@ class CloudOutputDeviceManager: self._output_device_manager = application.getOutputDeviceManager() self._account = application.getCuraAPI().account # type: Account - self._account.loginStateChanged.connect(self._onLoginStateChanged) self._api = CloudApiClient(self._account, self._onApiError) - # When switching machines we check if we have to activate a remote cluster. - application.globalContainerStackChanged.connect(self._connectToActiveMachine) - # create a timer to update the remote cluster list self._update_timer = QTimer(application) self._update_timer.setInterval(int(self.CHECK_CLUSTER_INTERVAL * 1000)) self._update_timer.setSingleShot(False) - self._update_timer.timeout.connect(self._getRemoteClusters) - # Make sure the timer is started in case we missed the loginChanged signal - self._onLoginStateChanged(self._account.isLoggedIn) + self._running = False # Called when the uses logs in or out def _onLoginStateChanged(self, is_logged_in: bool) -> None: + Logger.log("i", "Log in state changed to %s", is_logged_in) if is_logged_in: if not self._update_timer.isActive(): self._update_timer.start() + self._getRemoteClusters() else: if self._update_timer.isActive(): self._update_timer.stop() @@ -136,3 +132,25 @@ class CloudOutputDeviceManager: lifetime = 10, dismissable = True ).show() + + def start(self): + if self._running: + return + application = CuraApplication.getInstance() + self._account.loginStateChanged.connect(self._onLoginStateChanged) + # When switching machines we check if we have to activate a remote cluster. + application.globalContainerStackChanged.connect(self._connectToActiveMachine) + self._update_timer.timeout.connect(self._getRemoteClusters) + self._api.start() + self._onLoginStateChanged(is_logged_in = self._account.isLoggedIn) + + def stop(self): + if not self._running: + return + application = CuraApplication.getInstance() + self._account.loginStateChanged.disconnect(self._onLoginStateChanged) + # When switching machines we check if we have to activate a remote cluster. + application.globalContainerStackChanged.disconnect(self._connectToActiveMachine) + self._update_timer.timeout.disconnect(self._getRemoteClusters) + self._api.stop() + self._onLoginStateChanged(is_logged_in = False) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/ResumableUpload.py b/plugins/UM3NetworkPrinting/src/Cloud/ResumableUpload.py index 52b8e5c2d7..e2052c33c8 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/ResumableUpload.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/ResumableUpload.py @@ -51,44 +51,69 @@ class ResumableUpload(NetworkClient): return self._sent_bytes, last_byte def start(self) -> None: + super().start() + if self._finished: + self._sent_bytes = 0 + self._retries = 0 + self._finished = False self._uploadChunk() + def stop(self): + super().stop() + Logger.log("i", "Stopped uploading") + self._finished = True + def _uploadChunk(self) -> None: if self._finished: raise ValueError("The upload is already finished") first_byte, last_byte = self._chunkRange() - Logger.log("i", "PUT %s - %s", first_byte, last_byte) - self.put(self._url, data = self._data[first_byte:last_byte], content_type = self._content_type, - on_finished = self.finishedCallback, on_progress = self.progressCallback) + # self.put(self._url, data = self._data[first_byte:last_byte], content_type = self._content_type, + # on_finished = self.finishedCallback, on_progress = self._progressCallback) + request = self._createEmptyRequest(self._url, content_type=self._content_type) - def progressCallback(self, bytes_sent: int, bytes_total: int) -> None: + reply = self._manager.put(request, self._data[first_byte:last_byte]) + reply.finished.connect(lambda: self._finishedCallback(reply)) + reply.uploadProgress.connect(self._progressCallback) + reply.error.connect(self._errorCallback) + if reply.isFinished(): + self._finishedCallback(reply) + + def _progressCallback(self, bytes_sent: int, bytes_total: int) -> None: + Logger.log("i", "Progress callback %s / %s", bytes_sent, bytes_total) if bytes_total: self._on_progress(int((self._sent_bytes + bytes_sent) / len(self._data) * 100)) - def finishedCallback(self, reply: QNetworkReply) -> None: + def _errorCallback(self, reply: QNetworkReply) -> None: + body = bytes(reply.readAll()).decode() + Logger.log("e", "Received error while uploading: %s", body) + self.stop() + self._on_error() + + def _finishedCallback(self, reply: QNetworkReply) -> None: + Logger.log("i", "Finished callback %s %s", + reply.attribute(QNetworkRequest.HttpStatusCodeAttribute), reply.url().toString()) + status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) if self._retries < self.MAX_RETRIES and status_code in self.RETRY_HTTP_CODES: self._retries += 1 - Logger.log("i", "Retrying %s/%s request %s", tries, self.MAX_RETRIES, request.url) + Logger.log("i", "Retrying %s/%s request %s", self._retries, self.MAX_RETRIES, reply.url().toString()) self._uploadChunk() return + if status_code > 308: + self._errorCallback(reply) + return + body = bytes(reply.readAll()).decode() Logger.log("w", "status_code: %s, Headers: %s, body: %s", status_code, [bytes(header).decode() for header in reply.rawHeaderList()], body) - if status_code > 308: - self._finished = True - Logger.log("e", "Received error while uploading: %s", body) - self._on_error() - return - first_byte, last_byte = self._chunkRange() self._sent_bytes += last_byte - first_byte - self._finished = self._sent_bytes >= len(self._data) - if self._finished: + if self._sent_bytes >= len(self._data): + self.stop() self._on_finished() else: self._uploadChunk() diff --git a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py index 6a80ae046e..52fbf31e3c 100644 --- a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py @@ -86,6 +86,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): ## Start looking for devices on network. def start(self): self.startDiscovery() + self._cloud_output_device_manager.start() def startDiscovery(self): self.stop() @@ -142,6 +143,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): if self._zero_conf is not None: Logger.log("d", "zeroconf close...") self._zero_conf.close() + self._cloud_output_device_manager.stop() def removeManualDevice(self, key, address = None): if key in self._discovered_devices: From a02bccf74d587793796c1737de455e74856cdff8 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 14 Dec 2018 13:00:03 +0100 Subject: [PATCH 0920/1240] Fix NozzleModel to work with new ListModel data update CURA-6015 ListModels should not modify items directly. All ListModels should use setItems() and the insertions/removals/modifications will be done in setItems() itself. --- cura/Machines/Models/NozzleModel.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/cura/Machines/Models/NozzleModel.py b/cura/Machines/Models/NozzleModel.py index 9d97106d6b..785ff5b9b9 100644 --- a/cura/Machines/Models/NozzleModel.py +++ b/cura/Machines/Models/NozzleModel.py @@ -33,8 +33,6 @@ class NozzleModel(ListModel): def _update(self): Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__)) - self.items.clear() - global_stack = self._machine_manager.activeMachine if global_stack is None: self.setItems([]) From 8a4a1c9d49ab684a8bfdadfd87166fb494463277 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 14 Dec 2018 13:18:00 +0100 Subject: [PATCH 0921/1240] Don't let print info take space if invisible This way there is a little bit more space for the text 'No time estimation available', which was previously abbreviated to 'No time estimation availa...'. --- resources/qml/ActionPanel/OutputProcessWidget.qml | 2 +- resources/qml/ActionPanel/PrintInformationWidget.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml index 3f53abf28f..ad5a708898 100644 --- a/resources/qml/ActionPanel/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -41,7 +41,7 @@ Column { left: parent.left right: printInformationPanel.left - rightMargin: UM.Theme.getSize("thin_margin").height + rightMargin: printInformationPanel.visible ? UM.Theme.getSize("thin_margin").width : 0 } Cura.IconWithText diff --git a/resources/qml/ActionPanel/PrintInformationWidget.qml b/resources/qml/ActionPanel/PrintInformationWidget.qml index 0826e2c715..2e108b05d7 100644 --- a/resources/qml/ActionPanel/PrintInformationWidget.qml +++ b/resources/qml/ActionPanel/PrintInformationWidget.qml @@ -12,7 +12,7 @@ UM.RecolorImage id: widget source: UM.Theme.getIcon("info") - width: UM.Theme.getSize("section_icon").width + width: visible ? UM.Theme.getSize("section_icon").width : 0 height: UM.Theme.getSize("section_icon").height color: UM.Theme.getColor("icon") From a010823a4e39902d0bfadb807edeb180e649b108 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 14 Dec 2018 13:20:43 +0100 Subject: [PATCH 0922/1240] Fix warning when active stage is not yet instantiated Edit: Originally this was more or less the same fix as what Diego just did at the same time, which caused a merge conflict, but I think my solution is more elegant than the ternary operator that was originally there so I'm keeping mine. --- resources/qml/MainWindow/MainWindowHeader.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/MainWindow/MainWindowHeader.qml b/resources/qml/MainWindow/MainWindowHeader.qml index 8f6957d9cd..eecf2a1e73 100644 --- a/resources/qml/MainWindow/MainWindowHeader.qml +++ b/resources/qml/MainWindow/MainWindowHeader.qml @@ -54,7 +54,7 @@ Item { text: model.name.toUpperCase() checkable: true - checked: UM.Controller.activeStage != null ? model.id == UM.Controller.activeStage.stageId : false + checked: UM.Controller.activeStage !== null && model.id == UM.Controller.activeStage.stageId anchors.verticalCenter: parent.verticalCenter exclusiveGroup: mainWindowHeaderMenuGroup From d379f9477508ad1c248ba308d551e5f3bb97909b Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 14 Dec 2018 13:23:28 +0100 Subject: [PATCH 0923/1240] Make the dark theme a bit more readable --- resources/qml/ExtruderIcon.qml | 2 +- .../qml/PrintSetupSelector/Custom/CustomPrintSetup.qml | 2 ++ resources/themes/cura-dark/theme.json | 10 +++++----- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/resources/qml/ExtruderIcon.qml b/resources/qml/ExtruderIcon.qml index c44b6e0fa3..fcc49c9040 100644 --- a/resources/qml/ExtruderIcon.qml +++ b/resources/qml/ExtruderIcon.qml @@ -49,7 +49,7 @@ Item anchors.centerIn: parent text: index + 1 font: UM.Theme.getFont("very_small") - color: UM.Theme.getColor("text") + color: UM.Theme.getColor("text") width: contentWidth height: contentHeight visible: extruderEnabled diff --git a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml index b28c9ceb46..26ca4deec6 100644 --- a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml +++ b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml @@ -113,9 +113,11 @@ Item } z: tabBar.z - 1 // Don't show the border when only one extruder + border.color: tabBar.visible ? UM.Theme.getColor("lining") : "transparent" border.width: UM.Theme.getSize("default_lining").width + color: UM.Theme.getColor("main_background") Cura.SettingView { anchors diff --git a/resources/themes/cura-dark/theme.json b/resources/themes/cura-dark/theme.json index 2fa96c8897..cade342488 100644 --- a/resources/themes/cura-dark/theme.json +++ b/resources/themes/cura-dark/theme.json @@ -99,11 +99,11 @@ "scrollbar_handle_hover": [255, 255, 255, 255], "scrollbar_handle_down": [255, 255, 255, 255], - "setting_category": [39, 44, 48, 255], - "setting_category_disabled": [39, 44, 48, 255], - "setting_category_hover": [39, 44, 48, 255], - "setting_category_active": [39, 44, 48, 255], - "setting_category_active_hover": [39, 44, 48, 255], + "setting_category": [75, 80, 83, 255], + "setting_category_disabled": [75, 80, 83, 255], + "setting_category_hover": [75, 80, 83, 255], + "setting_category_active": [75, 80, 83, 255], + "setting_category_active_hover": [75, 80, 83, 255], "setting_category_text": [255, 255, 255, 152], "setting_category_disabled_text": [255, 255, 255, 101], "setting_category_hover_text": [255, 255, 255, 204], From 475b008310376d0e26fe7649aef870baa0a366e1 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 14 Dec 2018 13:27:26 +0100 Subject: [PATCH 0924/1240] Reduce minimum window size slightly This way it just fits on the screen of a 13 inch MacBook Air. --- resources/themes/cura-light/theme.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index b64190dd23..965fe63179 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -349,7 +349,7 @@ }, "sizes": { - "window_minimum_size": [106, 66], + "window_minimum_size": [100, 60], "main_window_header": [0.0, 4.0], "main_window_header_button": [8, 2.35], From 2f08854097dcf3895e2d320752c8afa6a67296f1 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Fri, 14 Dec 2018 14:50:15 +0100 Subject: [PATCH 0925/1240] STAR-322: Using QNetworkReply.finished signal instead of QNetworkAccessManager.finished --- cura/NetworkClient.py | 73 ++++++------------- .../src/Cloud/CloudApiClient.py | 32 +++++--- .../src/Cloud/CloudOutputDevice.py | 2 - .../src/Cloud/CloudOutputDeviceManager.py | 11 ++- .../src/Cloud/ResumableUpload.py | 54 +++++++------- .../tests/Cloud/NetworkManagerMock.py | 23 +++++- .../tests/Cloud/TestCloudApiClient.py | 55 ++++++-------- .../tests/Cloud/TestCloudOutputDevice.py | 21 +++--- .../Cloud/TestCloudOutputDeviceManager.py | 45 ++++++------ 9 files changed, 152 insertions(+), 164 deletions(-) diff --git a/cura/NetworkClient.py b/cura/NetworkClient.py index b455d03db0..4c43e58c4f 100644 --- a/cura/NetworkClient.py +++ b/cura/NetworkClient.py @@ -1,9 +1,9 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. from time import time -from typing import Optional, Dict, Callable, List, Union, Tuple +from typing import Optional, Dict, Callable, List, Union -from PyQt5.QtCore import QUrl +from PyQt5.QtCore import QUrl, QObject from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply, QHttpMultiPart, QNetworkRequest, QHttpPart, \ QAuthenticator @@ -13,9 +13,10 @@ from UM.Logger import Logger ## Abstraction of QNetworkAccessManager for easier networking in Cura. # This was originally part of NetworkedPrinterOutputDevice but was moved out for re-use in other classes. -class NetworkClient: +class NetworkClient(QObject): def __init__(self) -> None: + super().__init__() # Network manager instance to use for this client. self._manager = None # type: Optional[QNetworkAccessManager] @@ -29,11 +30,6 @@ class NetworkClient: application = Application.getInstance() self._user_agent = "%s/%s " % (application.getApplicationName(), application.getVersion()) - # Uses to store callback methods for finished network requests. - # This allows us to register network calls with a callback directly instead of having to dissect the reply. - # The key is created out of a tuple (operation, url) - self._on_finished_callbacks = {} # type: Dict[Tuple[int, str], Callable[[QNetworkReply], None]] - # QHttpMultiPart objects need to be kept alive and not garbage collected during the # HTTP which uses them. We hold references to these QHttpMultiPart objects here. self._kept_alive_multiparts = {} # type: Dict[QNetworkReply, QHttpMultiPart] @@ -43,7 +39,6 @@ class NetworkClient: if self._manager: return self._manager = QNetworkAccessManager() - self._manager.finished.connect(self._handleOnFinished) self._last_manager_create_time = time() self._manager.authenticationRequired.connect(self._onAuthenticationRequired) @@ -51,7 +46,6 @@ class NetworkClient: def stop(self) -> None: if not self._manager: return - self._manager.finished.disconnect(self._handleOnFinished) self._manager.authenticationRequired.disconnect(self._onAuthenticationRequired) self._manager = None @@ -69,32 +63,6 @@ class NetworkClient: self._last_request_time = time() return request - ## Executes the correct callback method when a network request finishes. - def _handleOnFinished(self, reply: QNetworkReply) -> None: - - Logger.log("i", "On finished %s %s", reply.attribute(QNetworkRequest.HttpStatusCodeAttribute), reply.url().toString()) - - # Due to garbage collection, we need to cache certain bits of post operations. - # As we don't want to keep them around forever, delete them if we get a reply. - if reply.operation() == QNetworkAccessManager.PostOperation: - self._clearCachedMultiPart(reply) - - # No status code means it never even reached remote. - if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) is None: - return - - # Not used by this class itself, but children might need it for better network handling. - # An example of this is the _update method in the NetworkedPrinterOutputDevice. - self._last_response_time = time() - - # Find the right callback and execute it. - # It always takes the full reply as single parameter. - callback_key = reply.operation(), reply.url().toString() - if callback_key in self._on_finished_callbacks: - self._on_finished_callbacks[callback_key](reply) - else: - Logger.log("w", "Received reply to URL %s but no callbacks are registered", reply.url()) - ## Removes all cached Multi-Part items. def _clearCachedMultiPart(self, reply: QNetworkReply) -> None: if reply in self._kept_alive_multiparts: @@ -105,12 +73,6 @@ class NetworkClient: def _onAuthenticationRequired(reply: QNetworkReply, authenticator: QAuthenticator) -> None: Logger.log("w", "Request to {} required authentication but was not given".format(reply.url().toString())) - ## Register a method to be executed when the associated network request finishes. - def _registerOnFinishedCallback(self, reply: QNetworkReply, - on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: - if on_finished is not None: - self._on_finished_callbacks[reply.operation(), reply.url().toString()] = on_finished - ## Add a part to a Multi-Part form. @staticmethod def _createFormPart(content_header: str, data: bytes, content_type: Optional[str] = None) -> QHttpPart: @@ -144,13 +106,10 @@ class NetworkClient: body = data if isinstance(data, bytes) else data.encode() # type: bytes reply = self._manager.put(request, body) - self._registerOnFinishedCallback(reply, on_finished) - + callback = self._createCallback(reply, on_finished) + reply.finished.connect(callback) if on_progress is not None: - # TODO: Do we need to disconnect() as well? reply.uploadProgress.connect(on_progress) - reply.finished.connect(lambda r: Logger.log("i", "On finished %s %s", url, r)) - reply.error.connect(lambda r: Logger.log("i", "On error %s %s", url, r)) ## Sends a delete request to the given path. # url: The path after the API prefix. @@ -158,7 +117,8 @@ class NetworkClient: def delete(self, url: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: request = self._createEmptyRequest(url) reply = self._manager.deleteResource(request) - self._registerOnFinishedCallback(reply, on_finished) + callback = self._createCallback(reply, on_finished) + reply.finished.connect(callback) ## Sends a get request to the given path. # \param url: The path after the API prefix. @@ -166,7 +126,8 @@ class NetworkClient: def get(self, url: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: request = self._createEmptyRequest(url) reply = self._manager.get(request) - self._registerOnFinishedCallback(reply, on_finished) + callback = self._createCallback(reply, on_finished) + reply.finished.connect(callback) ## Sends a post request to the given path. # \param url: The path after the API prefix. @@ -180,9 +141,10 @@ class NetworkClient: body = data if isinstance(data, bytes) else data.encode() # type: bytes reply = self._manager.post(request, body) + callback = self._createCallback(reply, on_finished) + reply.finished.connect(callback) if on_progress is not None: reply.uploadProgress.connect(on_progress) - self._registerOnFinishedCallback(reply, on_finished) ## Does a POST request with form data to the given URL. def postForm(self, url: str, header_data: str, body_data: bytes, @@ -205,10 +167,19 @@ class NetworkClient: reply = self._manager.post(request, multi_post_part) + def callback(): + on_finished(reply) + self._clearCachedMultiPart(reply) + + reply.finished.connect(callback) + self._kept_alive_multiparts[reply] = multi_post_part if on_progress is not None: reply.uploadProgress.connect(on_progress) - self._registerOnFinishedCallback(reply, on_finished) return reply + + @staticmethod + def _createCallback(reply: QNetworkReply, on_finished: Optional[Callable[[QNetworkReply], None]] = None): + return lambda: on_finished(reply) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py index 2637f17010..7c3c08e044 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py @@ -4,11 +4,11 @@ import json from json import JSONDecodeError from typing import Callable, List, Type, TypeVar, Union, Optional, Tuple, Dict, Any -from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply +from PyQt5.QtCore import QObject, QUrl +from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply, QNetworkAccessManager from UM.Logger import Logger from cura.API import Account -from cura.NetworkClient import NetworkClient from .ResumableUpload import ResumableUpload from ..Models import BaseModel from .Models.CloudClusterResponse import CloudClusterResponse @@ -21,7 +21,7 @@ from .Models.CloudPrintJobResponse import CloudPrintJobResponse ## The cloud API client is responsible for handling the requests and responses from the cloud. # Each method should only handle models instead of exposing Any HTTP details. -class CloudApiClient(NetworkClient): +class CloudApiClient: # The cloud URL to use for this remote cluster. # TODO: Make sure that this URL goes to the live api before release @@ -34,6 +34,7 @@ class CloudApiClient(NetworkClient): # \param on_error: The callback to be called whenever we receive errors from the server. def __init__(self, account: Account, on_error: Callable[[List[CloudErrorObject]], None]) -> None: super().__init__() + self._manager = QNetworkAccessManager() self._account = account self._on_error = on_error @@ -46,14 +47,18 @@ class CloudApiClient(NetworkClient): # \param on_finished: The function to be called after the result is parsed. def getClusters(self, on_finished: Callable[[List[CloudClusterResponse]], Any]) -> None: url = "{}/clusters".format(self.CLUSTER_API_ROOT) - self.get(url, on_finished=self._wrapCallback(on_finished, CloudClusterResponse)) + reply = self._manager.get(self._createEmptyRequest(url)) + callback = self._wrapCallback(reply, on_finished, CloudClusterResponse) + reply.finished.connect(callback) ## Retrieves the status of the given cluster. # \param cluster_id: The ID of the cluster. # \param on_finished: The function to be called after the result is parsed. def getClusterStatus(self, cluster_id: str, on_finished: Callable[[CloudClusterStatus], Any]) -> None: url = "{}/clusters/{}/status".format(self.CLUSTER_API_ROOT, cluster_id) - self.get(url, on_finished=self._wrapCallback(on_finished, CloudClusterStatus)) + reply = self._manager.get(self._createEmptyRequest(url)) + callback = self._wrapCallback(reply, on_finished, CloudClusterStatus) + reply.finished.connect(callback) ## Requests the cloud to register the upload of a print job mesh. # \param request: The request object. @@ -62,7 +67,9 @@ class CloudApiClient(NetworkClient): ) -> None: url = "{}/jobs/upload".format(self.CURA_API_ROOT) body = json.dumps({"data": request.toDict()}) - self.put(url, body, on_finished=self._wrapCallback(on_finished, CloudPrintJobResponse)) + reply = self._manager.put(self._createEmptyRequest(url), body.encode()) + callback = self._wrapCallback(reply, on_finished, CloudPrintJobResponse) + reply.finished.connect(callback) ## Requests the cloud to register the upload of a print job mesh. # \param upload_response: The object received after requesting an upload with `self.requestUpload`. @@ -72,7 +79,7 @@ class CloudApiClient(NetworkClient): # \param on_error: A function to be called if the upload fails. It receives a dict with the error. def uploadMesh(self, upload_response: CloudPrintJobResponse, mesh: bytes, on_finished: Callable[[], Any], on_progress: Callable[[int], Any], on_error: Callable[[], Any]): - ResumableUpload(upload_response.upload_url, upload_response.content_type, mesh, on_finished, + ResumableUpload(self._manager, upload_response.upload_url, upload_response.content_type, mesh, on_finished, on_progress, on_error).start() # Requests a cluster to print the given print job. @@ -81,13 +88,17 @@ class CloudApiClient(NetworkClient): # \param on_finished: The function to be called after the result is parsed. def requestPrint(self, cluster_id: str, job_id: str, on_finished: Callable[[CloudPrintResponse], Any]) -> None: url = "{}/clusters/{}/print/{}".format(self.CLUSTER_API_ROOT, cluster_id, job_id) - self.post(url, data = "", on_finished=self._wrapCallback(on_finished, CloudPrintResponse)) + reply = self._manager.post(self._createEmptyRequest(url), b"") + callback = self._wrapCallback(reply, on_finished, CloudPrintResponse) + reply.finished.connect(callback) ## We override _createEmptyRequest in order to add the user credentials. # \param url: The URL to request # \param content_type: The type of the body contents. def _createEmptyRequest(self, path: str, content_type: Optional[str] = "application/json") -> QNetworkRequest: - request = super()._createEmptyRequest(path, content_type) + request = QNetworkRequest(QUrl(path)) + if content_type: + request.setHeader(QNetworkRequest.ContentTypeHeader, content_type) if self._account.isLoggedIn: request.setRawHeader(b"Authorization", "Bearer {}".format(self._account.accessToken).encode()) Logger.log("i", "Created request for URL %s. Logged in = %s", path, self._account.isLoggedIn) @@ -132,10 +143,11 @@ class CloudApiClient(NetworkClient): # \param model: The type of the model to convert the response to. It may either be a single record or a list. # \return: A function that can be passed to the def _wrapCallback(self, + reply: QNetworkReply, on_finished: Callable[[Union[Model, List[Model]]], Any], model: Type[Model], ) -> Callable[[QNetworkReply], None]: - def parse(reply: QNetworkReply) -> None: + def parse() -> None: status_code, response = self._parseReply(reply) return self._parseModels(response, on_finished, model) return parse diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 83b5bed16b..09677d5e48 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -172,8 +172,6 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): if self._last_response_time and time() - self._last_response_time < self.CHECK_CLUSTER_INTERVAL: return # avoid calling the cloud too often - Logger.log("i", "Requesting update for %s after %s", self._device_id, - self._last_response_time and time() - self._last_response_time) if self._account.isLoggedIn: self.setAuthenticationState(AuthState.Authenticated) self._api.getClusterStatus(self._device_id, self._onStatusCallFinished) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 68b5f99bba..af80907f01 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -125,13 +125,14 @@ class CloudOutputDeviceManager: ## Handles an API error received from the cloud. # \param errors: The errors received def _onApiError(self, errors: List[CloudErrorObject]) -> None: - message = ". ".join(e.title for e in errors) # TODO: translate errors - Message( - text = message, + text = ". ".join(e.title for e in errors) # TODO: translate errors + message = Message( + text = text, title = self.I18N_CATALOG.i18nc("@info:title", "Error"), lifetime = 10, dismissable = True - ).show() + ) + message.show() def start(self): if self._running: @@ -141,7 +142,6 @@ class CloudOutputDeviceManager: # When switching machines we check if we have to activate a remote cluster. application.globalContainerStackChanged.connect(self._connectToActiveMachine) self._update_timer.timeout.connect(self._getRemoteClusters) - self._api.start() self._onLoginStateChanged(is_logged_in = self._account.isLoggedIn) def stop(self): @@ -152,5 +152,4 @@ class CloudOutputDeviceManager: # When switching machines we check if we have to activate a remote cluster. application.globalContainerStackChanged.disconnect(self._connectToActiveMachine) self._update_timer.timeout.disconnect(self._getRemoteClusters) - self._api.stop() self._onLoginStateChanged(is_logged_in = False) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/ResumableUpload.py b/plugins/UM3NetworkPrinting/src/Cloud/ResumableUpload.py index e2052c33c8..5e3bc9545e 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/ResumableUpload.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/ResumableUpload.py @@ -1,14 +1,14 @@ # Copyright (c) 2018 Ultimaker B.V. # !/usr/bin/env python # -*- coding: utf-8 -*- -from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply +from PyQt5.QtCore import QUrl +from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply, QNetworkAccessManager from typing import Optional, Callable, Any, Tuple from UM.Logger import Logger -from cura.NetworkClient import NetworkClient -class ResumableUpload(NetworkClient): +class ResumableUpload: MAX_RETRIES = 10 BYTES_PER_REQUEST = 256 * 1024 RETRY_HTTP_CODES = {500, 502, 503, 504} @@ -18,9 +18,9 @@ class ResumableUpload(NetworkClient): # \param content_length: The total content length of the file, in bytes. # \param http_method: The HTTP method to be used, e.g. "POST" or "PUT". # \param timeout: The timeout for each chunk upload. Important: If None, no timeout is applied at all. - def __init__(self, url: str, content_type: str, data: bytes, + def __init__(self, manager: QNetworkAccessManager, url: str, content_type: str, data: bytes, on_finished: Callable[[], Any], on_progress: Callable[[int], Any], on_error: Callable[[], Any]): - super().__init__() + self._manager = manager self._url = url self._content_type = content_type self._data = data @@ -32,13 +32,15 @@ class ResumableUpload(NetworkClient): self._sent_bytes = 0 self._retries = 0 self._finished = False + self._reply = None # type: Optional[QNetworkReply] - ## We override _createEmptyRequest in order to add the user credentials. + ## We override _createRequest in order to add the user credentials. # \param url: The URL to request # \param content_type: The type of the body contents. - def _createEmptyRequest(self, path: str, content_type: Optional[str] = "application/json") -> QNetworkRequest: - request = super()._createEmptyRequest(path, content_type = self._content_type) - + def _createRequest(self) -> QNetworkRequest: + request = QNetworkRequest(QUrl(self._url)) + request.setHeader(QNetworkRequest.ContentTypeHeader, self._content_type) + first_byte, last_byte = self._chunkRange() content_range = "bytes {}-{}/{}".format(first_byte, last_byte - 1, len(self._data)) request.setRawHeader(b"Content-Range", content_range.encode()) @@ -51,7 +53,6 @@ class ResumableUpload(NetworkClient): return self._sent_bytes, last_byte def start(self) -> None: - super().start() if self._finished: self._sent_bytes = 0 self._retries = 0 @@ -59,7 +60,6 @@ class ResumableUpload(NetworkClient): self._uploadChunk() def stop(self): - super().stop() Logger.log("i", "Stopped uploading") self._finished = True @@ -68,47 +68,43 @@ class ResumableUpload(NetworkClient): raise ValueError("The upload is already finished") first_byte, last_byte = self._chunkRange() - # self.put(self._url, data = self._data[first_byte:last_byte], content_type = self._content_type, - # on_finished = self.finishedCallback, on_progress = self._progressCallback) - request = self._createEmptyRequest(self._url, content_type=self._content_type) + request = self._createRequest() - reply = self._manager.put(request, self._data[first_byte:last_byte]) - reply.finished.connect(lambda: self._finishedCallback(reply)) - reply.uploadProgress.connect(self._progressCallback) - reply.error.connect(self._errorCallback) - if reply.isFinished(): - self._finishedCallback(reply) + self._reply = self._manager.put(request, self._data[first_byte:last_byte]) + self._reply.finished.connect(self._finishedCallback) + self._reply.uploadProgress.connect(self._progressCallback) + self._reply.error.connect(self._errorCallback) def _progressCallback(self, bytes_sent: int, bytes_total: int) -> None: Logger.log("i", "Progress callback %s / %s", bytes_sent, bytes_total) if bytes_total: self._on_progress(int((self._sent_bytes + bytes_sent) / len(self._data) * 100)) - def _errorCallback(self, reply: QNetworkReply) -> None: - body = bytes(reply.readAll()).decode() + def _errorCallback(self) -> None: + body = bytes(self._reply.readAll()).decode() Logger.log("e", "Received error while uploading: %s", body) self.stop() self._on_error() - def _finishedCallback(self, reply: QNetworkReply) -> None: + def _finishedCallback(self) -> None: Logger.log("i", "Finished callback %s %s", - reply.attribute(QNetworkRequest.HttpStatusCodeAttribute), reply.url().toString()) + self._reply.attribute(QNetworkRequest.HttpStatusCodeAttribute), self._reply.url().toString()) - status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) + status_code = self._reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) if self._retries < self.MAX_RETRIES and status_code in self.RETRY_HTTP_CODES: self._retries += 1 - Logger.log("i", "Retrying %s/%s request %s", self._retries, self.MAX_RETRIES, reply.url().toString()) + Logger.log("i", "Retrying %s/%s request %s", self._retries, self.MAX_RETRIES, self._reply.url().toString()) self._uploadChunk() return if status_code > 308: - self._errorCallback(reply) + self._errorCallback() return - body = bytes(reply.readAll()).decode() + body = bytes(self._reply.readAll()).decode() Logger.log("w", "status_code: %s, Headers: %s, body: %s", status_code, - [bytes(header).decode() for header in reply.rawHeaderList()], body) + [bytes(header).decode() for header in self._reply.rawHeaderList()], body) first_byte, last_byte = self._chunkRange() self._sent_bytes += last_byte - first_byte diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py b/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py index 60627cbe7c..59b79fdfa6 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py @@ -4,12 +4,27 @@ import json from typing import Dict, Tuple, Union, Optional from unittest.mock import MagicMock -from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply, QNetworkRequest +from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest from UM.Logger import Logger from UM.Signal import Signal +class FakeSignal: + def __init__(self): + self._callbacks = [] + + def connect(self, callback): + self._callbacks.append(callback) + + def disconnect(self, callback): + self._callbacks.remove(callback) + + def emit(self, *args, **kwargs): + for callback in self._callbacks: + callback(*args, **kwargs) + + ## This class can be used to mock the QNetworkManager class and test the code using it. # After patching the QNetworkManager class, requests are prepared before they can be executed. # Any requests not prepared beforehand will cause KeyErrors. @@ -27,7 +42,7 @@ class NetworkManagerMock: ## Initializes the network manager mock. def __init__(self) -> None: # a dict with the prepared replies, using the format {(http_method, url): reply} - self.replies = {} # type: Dict[Tuple[str, str], QNetworkReply] + self.replies = {} # type: Dict[Tuple[str, str], MagicMock] self.request_bodies = {} # type: Dict[Tuple[str, str], bytes] # signals used in the network manager. @@ -64,6 +79,8 @@ class NetworkManagerMock: reply_mock.url().toString.return_value = url reply_mock.operation.return_value = self._OPERATIONS[method] reply_mock.attribute.return_value = status_code + reply_mock.finished = FakeSignal() + reply_mock.isFinished.return_value = False reply_mock.readAll.return_value = response if isinstance(response, bytes) else json.dumps(response).encode() self.replies[method, url] = reply_mock Logger.log("i", "Prepared mock {}-response to {} {}", status_code, method, url) @@ -78,6 +95,8 @@ class NetworkManagerMock: def flushReplies(self) -> None: for key, reply in self.replies.items(): Logger.log("i", "Flushing reply to {} {}", *key) + reply.isFinished.return_value = True + reply.finished.emit() self.finished.emit(reply) self.reset() diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py index e377627465..d4044726a3 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py @@ -8,6 +8,8 @@ from unittest.mock import patch, MagicMock from cura.CuraApplication import CuraApplication from src.Cloud.CloudApiClient import CloudApiClient +from src.Cloud.Models.CloudClusterResponse import CloudClusterResponse +from src.Cloud.Models.CloudClusterStatus import CloudClusterStatus from src.Cloud.Models.CloudPrintJobResponse import CloudPrintJobResponse from src.Cloud.Models.CloudPrintJobUploadRequest import CloudPrintJobUploadRequest from src.Cloud.Models.CloudErrorObject import CloudErrorObject @@ -15,7 +17,6 @@ from tests.Cloud.Fixtures import readFixture, parseFixture from .NetworkManagerMock import NetworkManagerMock -@patch("cura.NetworkClient.QNetworkAccessManager") class TestCloudApiClient(TestCase): def _errorHandler(self, errors: List[CloudErrorObject]): raise Exception("Received unexpected error: {}".format(errors)) @@ -27,15 +28,14 @@ class TestCloudApiClient(TestCase): self.app = CuraApplication.getInstance() self.network = NetworkManagerMock() - self.api = CloudApiClient(self.account, self._errorHandler) - - def test_GetClusters(self, network_mock): - network_mock.return_value = self.network + with patch("src.Cloud.CloudApiClient.QNetworkAccessManager", return_value = self.network): + self.api = CloudApiClient(self.account, self._errorHandler) + def test_getClusters(self): result = [] - with open("{}/Fixtures/getClusters.json".format(os.path.dirname(__file__)), "rb") as f: - response = f.read() + response = readFixture("getClusters") + data = parseFixture("getClusters")["data"] self.network.prepareReply("GET", "https://api-staging.ultimaker.com/connect/v1/clusters", 200, response) # the callback is a function that adds the result of the call to getClusters to the result list @@ -43,32 +43,26 @@ class TestCloudApiClient(TestCase): self.network.flushReplies() - self.assertEqual(2, len(result)) - - def test_getClusterStatus(self, network_mock): - network_mock.return_value = self.network + self.assertEqual([CloudClusterResponse(**data[0]), CloudClusterResponse(**data[1])], result) + def test_getClusterStatus(self): result = [] - with open("{}/Fixtures/getClusterStatusResponse.json".format(os.path.dirname(__file__)), "rb") as f: - response = f.read() + response = readFixture("getClusterStatusResponse") + data = parseFixture("getClusterStatusResponse")["data"] self.network.prepareReply("GET", "https://api-staging.ultimaker.com/connect/v1/clusters/R0YcLJwar1ugh0ikEZsZs8NWKV6vJP_LdYsXgXqAcaNC/status", 200, response ) - self.api.getClusterStatus("R0YcLJwar1ugh0ikEZsZs8NWKV6vJP_LdYsXgXqAcaNC", lambda status: result.append(status)) + self.api.getClusterStatus("R0YcLJwar1ugh0ikEZsZs8NWKV6vJP_LdYsXgXqAcaNC", lambda s: result.append(s)) self.network.flushReplies() - self.assertEqual(len(result), 1) - status = result[0] + self.assertEqual([CloudClusterStatus(**data)], result) - self.assertEqual(len(status.printers), 2) - self.assertEqual(len(status.print_jobs), 1) - - def test_requestUpload(self, network_mock): - network_mock.return_value = self.network + def test_requestUpload(self): + results = [] response = readFixture("putJobUploadResponse") @@ -78,11 +72,11 @@ class TestCloudApiClient(TestCase): self.api.requestUpload(request, lambda r: results.append(r)) self.network.flushReplies() - self.assertEqual(results[0].content_type, "text/plain") - self.assertEqual(results[0].status, "uploading") + self.assertEqual(["text/plain"], [r.content_type for r in results]) + self.assertEqual(["uploading"], [r.status for r in results]) - def test_uploadMesh(self, network_mock): - network_mock.return_value = self.network + def test_uploadMesh(self): + results = [] progress = MagicMock() @@ -101,8 +95,8 @@ class TestCloudApiClient(TestCase): self.assertEqual(["sent"], results) - def test_requestPrint(self, network_mock): - network_mock.return_value = self.network + def test_requestPrint(self): + results = [] response = readFixture("postJobPrintResponse") @@ -120,7 +114,6 @@ class TestCloudApiClient(TestCase): self.network.flushReplies() - self.assertEqual(len(results), 1) - self.assertEqual(results[0].job_id, job_id) - self.assertEqual(results[0].cluster_job_id, cluster_job_id) - self.assertEqual(results[0].status, "queued") + self.assertEqual([job_id], [r.job_id for r in results]) + self.assertEqual([cluster_job_id], [r.cluster_job_id for r in results]) + self.assertEqual(["queued"], [r.status for r in results]) diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py index 287f2dda98..c391dc75dd 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py @@ -13,7 +13,6 @@ from tests.Cloud.Fixtures import readFixture, parseFixture from .NetworkManagerMock import NetworkManagerMock -@patch("cura.NetworkClient.QNetworkAccessManager") class TestCloudOutputDevice(TestCase): CLUSTER_ID = "RIZ6cZbWA_Ua7RZVJhrdVfVpf0z-MqaSHQE4v8aRTtYq" JOB_ID = "ABCDefGHIjKlMNOpQrSTUvYxWZ0-1234567890abcDE=" @@ -30,7 +29,9 @@ class TestCloudOutputDevice(TestCase): self.network = NetworkManagerMock() self.account = MagicMock(isLoggedIn=True, accessToken="TestAccessToken") self.onError = MagicMock() - self.device = CloudOutputDevice(CloudApiClient(self.account, self.onError), self.CLUSTER_ID, self.HOST_NAME) + with patch("src.Cloud.CloudApiClient.QNetworkAccessManager", return_value = self.network): + self._api = CloudApiClient(self.account, self.onError) + self.device = CloudOutputDevice(self._api, self.CLUSTER_ID, self.HOST_NAME) self.cluster_status = parseFixture("getClusterStatusResponse") self.network.prepareReply("GET", self.STATUS_URL, 200, readFixture("getClusterStatusResponse")) @@ -38,8 +39,7 @@ class TestCloudOutputDevice(TestCase): super().tearDown() self.network.flushReplies() - def test_status(self, network_mock): - network_mock.return_value = self.network + def test_status(self): self.device._update() self.network.flushReplies() @@ -69,32 +69,34 @@ class TestCloudOutputDevice(TestCase): self.assertEqual({job["name"] for job in self.cluster_status["data"]["print_jobs"]}, {job.name for job in self.device.printJobs}) - def test_remove_print_job(self, network_mock): - network_mock.return_value = self.network + def test_remove_print_job(self): self.device._update() self.network.flushReplies() self.assertEqual(1, len(self.device.printJobs)) self.cluster_status["data"]["print_jobs"].clear() self.network.prepareReply("GET", self.STATUS_URL, 200, self.cluster_status) + + self.device._last_response_time = None self.device._update() self.network.flushReplies() self.assertEqual([], self.device.printJobs) - def test_remove_printers(self, network_mock): - network_mock.return_value = self.network + def test_remove_printers(self): self.device._update() self.network.flushReplies() self.assertEqual(2, len(self.device.printers)) self.cluster_status["data"]["printers"].clear() self.network.prepareReply("GET", self.STATUS_URL, 200, self.cluster_status) + + self.device._last_response_time = None self.device._update() self.network.flushReplies() self.assertEqual([], self.device.printers) @patch("cura.CuraApplication.CuraApplication.getGlobalContainerStack") - def test_print_to_cloud(self, global_container_stack_mock, network_mock): + def test_print_to_cloud(self, global_container_stack_mock): active_machine_mock = global_container_stack_mock.return_value active_machine_mock.getMetaDataEntry.side_effect = {"file_formats": "application/gzip"}.get @@ -104,7 +106,6 @@ class TestCloudOutputDevice(TestCase): self.network.prepareReply("PUT", request_upload_response["data"]["upload_url"], 201, b"{}") self.network.prepareReply("POST", self.PRINT_URL, 200, request_print_response) - network_mock.return_value = self.network file_handler = MagicMock() file_handler.getSupportedFileTypesWrite.return_value = [{ "extension": "gcode.gz", diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py index b6bcde6e55..80dd2c7990 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py @@ -10,7 +10,6 @@ from tests.Cloud.Fixtures import parseFixture, readFixture from .NetworkManagerMock import NetworkManagerMock -@patch("cura.NetworkClient.QNetworkAccessManager") class TestCloudOutputDeviceManager(TestCase): URL = "https://api-staging.ultimaker.com/connect/v1/clusters" @@ -18,13 +17,15 @@ class TestCloudOutputDeviceManager(TestCase): super().setUp() self.app = CuraApplication.getInstance() self.network = NetworkManagerMock() - self.manager = CloudOutputDeviceManager() + with patch("src.Cloud.CloudApiClient.QNetworkAccessManager", return_value = self.network): + self.manager = CloudOutputDeviceManager() self.clusters_response = parseFixture("getClusters") self.network.prepareReply("GET", self.URL, 200, readFixture("getClusters")) def tearDown(self): try: self._beforeTearDown() + self.manager.stop() finally: super().tearDown() @@ -47,17 +48,17 @@ class TestCloudOutputDeviceManager(TestCase): device_manager.removeOutputDevice(device["cluster_id"]) ## Runs the initial request to retrieve the clusters. - def _loadData(self, network_mock): - network_mock.return_value = self.network - self.manager._account.loginStateChanged.emit(True) - self.manager._update_timer.timeout.emit() + def _loadData(self): + self.manager.start() + self.manager._onLoginStateChanged(is_logged_in = True) + self.network.flushReplies() - def test_device_is_created(self, network_mock): + def test_device_is_created(self): # just create the cluster, it is checked at tearDown - self._loadData(network_mock) + self._loadData() - def test_device_is_updated(self, network_mock): - self._loadData(network_mock) + def test_device_is_updated(self): + self._loadData() # update the cluster from member variable, which is checked at tearDown self.clusters_response["data"][0]["host_name"] = "New host name" @@ -65,8 +66,8 @@ class TestCloudOutputDeviceManager(TestCase): self.manager._update_timer.timeout.emit() - def test_device_is_removed(self, network_mock): - self._loadData(network_mock) + def test_device_is_removed(self): + self._loadData() # delete the cluster from member variable, which is checked at tearDown del self.clusters_response["data"][1] @@ -75,41 +76,39 @@ class TestCloudOutputDeviceManager(TestCase): self.manager._update_timer.timeout.emit() @patch("cura.CuraApplication.CuraApplication.getGlobalContainerStack") - def test_device_connects_by_cluster_id(self, global_container_stack_mock, network_mock): + def test_device_connects_by_cluster_id(self, global_container_stack_mock): active_machine_mock = global_container_stack_mock.return_value cluster1, cluster2 = self.clusters_response["data"] cluster_id = cluster1["cluster_id"] active_machine_mock.getMetaDataEntry.side_effect = {"um_cloud_cluster_id": cluster_id}.get - self._loadData(network_mock) - self.network.flushReplies() + self._loadData() self.assertTrue(self.app.getOutputDeviceManager().getOutputDevice(cluster1["cluster_id"]).isConnected()) self.assertFalse(self.app.getOutputDeviceManager().getOutputDevice(cluster2["cluster_id"]).isConnected()) self.assertEquals([], active_machine_mock.setMetaDataEntry.mock_calls) @patch("cura.CuraApplication.CuraApplication.getGlobalContainerStack") - def test_device_connects_by_network_key(self, global_container_stack_mock, network_mock): + def test_device_connects_by_network_key(self, global_container_stack_mock): active_machine_mock = global_container_stack_mock.return_value cluster1, cluster2 = self.clusters_response["data"] network_key = cluster2["host_name"] + ".ultimaker.local" active_machine_mock.getMetaDataEntry.side_effect = {"um_network_key": network_key}.get - self._loadData(network_mock) - self.network.flushReplies() + self._loadData() self.assertFalse(self.app.getOutputDeviceManager().getOutputDevice(cluster1["cluster_id"]).isConnected()) self.assertTrue(self.app.getOutputDeviceManager().getOutputDevice(cluster2["cluster_id"]).isConnected()) active_machine_mock.setMetaDataEntry.assert_called_with("um_cloud_cluster_id", cluster2["cluster_id"]) - @patch("UM.Message.Message.show") - def test_api_error(self, message_mock, network_mock): + @patch("src.Cloud.CloudOutputDeviceManager.Message") + def test_api_error(self, message_mock): self.clusters_response = { "errors": [{"id": "notFound", "title": "Not found!", "http_status": "404", "code": "notFound"}] } self.network.prepareReply("GET", self.URL, 200, self.clusters_response) - self._loadData(network_mock) - self.network.flushReplies() - message_mock.assert_called_once_with() + self._loadData() + message_mock.assert_called_once_with(text='Not found!', title='Error', lifetime=10, dismissable=True) + message_mock.return_value.show.assert_called_once_with() From 7fc5742b7f328b0a46b1708526eaf8bf3f5aba33 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 14 Dec 2018 15:01:40 +0100 Subject: [PATCH 0926/1240] Add monitor carousel Contributes to CL-1151 --- .../resources/qml/MonitorCarousel.qml | 231 ++++++++++++++++++ .../resources/qml/MonitorPrinterCard.qml | 2 +- .../resources/qml/MonitorStage.qml | 21 +- 3 files changed, 235 insertions(+), 19 deletions(-) create mode 100644 plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml new file mode 100644 index 0000000000..7d56b2f294 --- /dev/null +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml @@ -0,0 +1,231 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.3 +import QtQuick.Controls 2.0 +import QtGraphicalEffects 1.0 +import UM 1.3 as UM + +Item +{ + id: base + + property var currentIndex: 0 + property var tileWidth: 834 * screenScaleFactor // TODO: Theme! + property var tileHeight: 216 * screenScaleFactor // TODO: Theme! + property var tileSpacing: 60 * screenScaleFactor // TODO: Theme! + property var maxOffset: (OutputDevice.printers.length - 1) * (tileWidth + tileSpacing) + + height: centerSection.height + width: maximumWidth + + Item + { + id: leftHint + anchors + { + right: leftButton.left + rightMargin: 12 * screenScaleFactor + left: parent.left + } + height: parent.height + z: 10 + LinearGradient + { + anchors.fill: parent + start: Qt.point(0, 0) + end: Qt.point(leftHint.width, 0) + gradient: Gradient + { + GradientStop + { + position: 0.0 + color: "#fff6f6f6" + } + GradientStop + { + position: 1.0 + color: "#00f6f6f6" + } + } + } + } + + Button + { + id: leftButton + anchors + { + verticalCenter: parent.verticalCenter + right: centerSection.left + rightMargin: 12 * screenScaleFactor + } + width: 36 * screenScaleFactor // TODO: Theme! + height: 72 * screenScaleFactor // TODO: Theme! + visible: currentIndex > 0 + z: 10 + onClicked: navigateTo(currentIndex - 1) + background: Rectangle + { + color: "#ffffff" // TODO: Theme! + border.width: 1 * screenScaleFactor // TODO: Theme! + border.color: "#cccccc" // TODO: Theme! + radius: 2 * screenScaleFactor // TODO: Theme! + } + contentItem: Item + { + anchors.fill: parent + UM.RecolorImage + { + anchors.centerIn: parent + width: 18 + height: 18 + sourceSize.width: 18 + sourceSize.height: 18 + color: "#666666" // TODO: Theme! + source: UM.Theme.getIcon("arrow_left") + } + } + } + + Item + { + id: centerSection + anchors + { + verticalCenter: parent.verticalCenter + horizontalCenter: parent.horizontalCenter + } + width: tileWidth + height: tiles.height + z: 1 + + Row + { + id: tiles + height: childrenRect.height + width: 5 * tileWidth + 4 * tileSpacing + x: 0 + z: 0 + Behavior on x { NumberAnimation { duration: 100 } } + spacing: 60 * screenScaleFactor // TODO: Theme! + + Repeater + { + model: OutputDevice.printers + MonitorPrinterCard + { + printer: modelData + } + } + } + } + + Button + { + id: rightButton + anchors + { + verticalCenter: parent.verticalCenter + left: centerSection.right + leftMargin: 12 * screenScaleFactor + } + width: 36 * screenScaleFactor // TODO: Theme! + height: 72 * screenScaleFactor // TODO: Theme! + z: 10 + visible: currentIndex < OutputDevice.printers.length - 1 + onClicked: navigateTo(currentIndex + 1) + background: Rectangle + { + color: "#ffffff" // TODO: Theme! + border.width: 1 * screenScaleFactor // TODO: Theme! + border.color: "#cccccc" // TODO: Theme! + radius: 2 * screenScaleFactor // TODO: Theme! + } + contentItem: Item + { + anchors.fill: parent + UM.RecolorImage + { + anchors.centerIn: parent + width: 18 + height: 18 + sourceSize.width: 18 + sourceSize.height: 18 + color: "#666666" // TODO: Theme! + source: UM.Theme.getIcon("arrow_right") + } + } + } + + Item + { + id: rightHint + anchors + { + left: rightButton.right + leftMargin: 12 * screenScaleFactor + right: parent.right + } + height: centerSection.height + z: 10 + + LinearGradient + { + anchors.fill: parent + start: Qt.point(0, 0) + end: Qt.point(rightHint.width, 0) + gradient: Gradient + { + GradientStop + { + position: 0.0 + color: "#00f6f6f6" + } + GradientStop + { + position: 1.0 + color: "#fff6f6f6" + } + } + } + } + + Item + { + id: navigationDots + anchors + { + horizontalCenter: centerSection.horizontalCenter + top: centerSection.bottom + topMargin: 36 * screenScaleFactor // TODO: Theme! + } + Row + { + spacing: 8 * screenScaleFactor // TODO: Theme! + Repeater + { + model: OutputDevice.printers + Button + { + background: Rectangle + { + color: model.index == currentIndex ? "#777777" : "#d8d8d8" // TODO: Theme! + radius: Math.floor(width / 2) + width: 12 * screenScaleFactor // TODO: Theme! + height: width + } + onClicked: navigateTo(model.index) + } + } + } + } + + function navigateTo( i ) { + if (i >= 0 && i < OutputDevice.printers.length) + { + tiles.x = -1 * i * (tileWidth + tileSpacing) + currentIndex = i + } + } +} \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml index 1676c51edf..cfeb77cc89 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml @@ -26,7 +26,7 @@ Item property var borderSize: 1 * screenScaleFactor // TODO: Theme, and remove from here width: 834 * screenScaleFactor // TODO: Theme! - height: 216 * screenScaleFactor // TODO: Theme! + height: childrenRect.height // Printer portion Rectangle diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml index 4d59e0eb6b..f77cfee1ef 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml @@ -48,32 +48,17 @@ Component } } - ScrollView + Item { id: printers anchors { - left: queue.left - right: queue.right top: parent.top topMargin: 48 * screenScaleFactor // TODO: Theme! } + width: parent.width height: 264 * screenScaleFactor // TODO: Theme! - - Row - { - spacing: 60 * screenScaleFactor // TODO: Theme! - - Repeater - { - model: OutputDevice.printers - - MonitorPrinterCard - { - printer: modelData - } - } - } + MonitorCarousel {} } Item From 3a74511d231d573e7ed910fb8d51080265740ac4 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 14 Dec 2018 15:01:56 +0100 Subject: [PATCH 0927/1240] Remove padding from views selector This padding made it break the height of the item for some reason. I guess that is a Qt bug. In any case, the padding shouldn't be there either since the lining is on the inside of the child buttons so the padding only causes a gap to appear on the left and right sides, which looks weird. Contributes to issue CURA-6029. --- resources/qml/ViewsSelector.qml | 2 -- 1 file changed, 2 deletions(-) diff --git a/resources/qml/ViewsSelector.qml b/resources/qml/ViewsSelector.qml index 06d2e662b5..acde7d1f71 100644 --- a/resources/qml/ViewsSelector.qml +++ b/resources/qml/ViewsSelector.qml @@ -78,8 +78,6 @@ Cura.ExpandablePopup { id: viewSelectorPopup width: viewSelector.width - 2 * viewSelector.contentPadding - leftPadding: UM.Theme.getSize("default_lining").width - rightPadding: UM.Theme.getSize("default_lining").width // For some reason the height/width of the column gets set to 0 if this is not set... Component.onCompleted: From 5d0da580b9a65c76b9bf21c4d7bcf4470133c1dd Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 14 Dec 2018 15:04:22 +0100 Subject: [PATCH 0928/1240] Fix border colors --- plugins/UM3NetworkPrinting/resources/qml/ExpandableCard.qml | 2 +- .../UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml | 2 +- .../UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/ExpandableCard.qml b/plugins/UM3NetworkPrinting/resources/qml/ExpandableCard.qml index 0877a15f00..7778c4e1a2 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/ExpandableCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/ExpandableCard.qml @@ -16,7 +16,7 @@ Item property bool expanded: false property var borderWidth: 1 - property color borderColor: "#EAEAEC" + property color borderColor: "#CCCCCC" property color headerBackgroundColor: "white" property color headerHoverColor: "#f5f5f5" property color drawerBackgroundColor: "white" diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml index d8c5d1ec28..f431ef1c52 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml @@ -26,7 +26,7 @@ Item ExpandableCard { - borderColor: printJob.configurationChanges.length !== 0 ? "#f5a623" : "#EAEAEC" // TODO: Theme! + borderColor: printJob.configurationChanges.length !== 0 ? "#f5a623" : "#CCCCCC" // TODO: Theme! headerItem: Row { height: 48 * screenScaleFactor // TODO: Theme! diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml index cfeb77cc89..038f0535f0 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml @@ -34,7 +34,7 @@ Item id: printerInfo border { - color: "#EAEAEC" // TODO: Theme! + color: "#CCCCCC" // TODO: Theme! width: borderSize // TODO: Remove once themed } color: "white" // TODO: Theme! @@ -151,7 +151,7 @@ Item } border { - color: printer.activePrintJob && printer.activePrintJob.configurationChanges.length > 0 ? "#f5a623" : "#EAEAEC" // TODO: Theme! + color: printer.activePrintJob && printer.activePrintJob.configurationChanges.length > 0 ? "#f5a623" : "#CCCCCC" // TODO: Theme! width: borderSize // TODO: Remove once themed } color: "white" // TODO: Theme! From b2ea597543f58db9e447167f38389e58425237f9 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 14 Dec 2018 15:10:34 +0100 Subject: [PATCH 0929/1240] Fix button color Contributes to CL-1151 --- plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml index 7d56b2f294..49d35f794f 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml @@ -82,7 +82,7 @@ Item height: 18 sourceSize.width: 18 sourceSize.height: 18 - color: "#666666" // TODO: Theme! + color: "#152950" // TODO: Theme! source: UM.Theme.getIcon("arrow_left") } } @@ -152,7 +152,7 @@ Item height: 18 sourceSize.width: 18 sourceSize.height: 18 - color: "#666666" // TODO: Theme! + color: "#152950" // TODO: Theme! source: UM.Theme.getIcon("arrow_right") } } From ae695b77e6ad8e9ea882c2b20e6ec4922fa6603a Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Fri, 14 Dec 2018 15:23:56 +0100 Subject: [PATCH 0930/1240] Add native rendering for QML Label CURA-6013 --- plugins/Toolbox/resources/qml/SmallRatingWidget.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/Toolbox/resources/qml/SmallRatingWidget.qml b/plugins/Toolbox/resources/qml/SmallRatingWidget.qml index bab219d294..686058f4e8 100644 --- a/plugins/Toolbox/resources/qml/SmallRatingWidget.qml +++ b/plugins/Toolbox/resources/qml/SmallRatingWidget.qml @@ -29,5 +29,6 @@ Row anchors.verticalCenter: starIcon.verticalCenter color: starIcon.color font: UM.Theme.getFont("small") + renderType: Text.NativeRendering } } \ No newline at end of file From 2f6a274c0e2b78064fb9ae8a3c6f7766f8623e1a Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 14 Dec 2018 15:27:55 +0100 Subject: [PATCH 0931/1240] Small style improvements Contributes to CL-1151 --- .../resources/qml/ExpandableCard.qml | 2 +- .../resources/qml/MonitorCarousel.qml | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/ExpandableCard.qml b/plugins/UM3NetworkPrinting/resources/qml/ExpandableCard.qml index 7778c4e1a2..f86135ae62 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/ExpandableCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/ExpandableCard.qml @@ -18,7 +18,7 @@ Item property var borderWidth: 1 property color borderColor: "#CCCCCC" property color headerBackgroundColor: "white" - property color headerHoverColor: "#f5f5f5" + property color headerHoverColor: "#e8f2fc" property color drawerBackgroundColor: "white" property alias headerItem: header.children property alias drawerItem: drawer.children diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml index 49d35f794f..cd7c6f177f 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml @@ -63,11 +63,12 @@ Item width: 36 * screenScaleFactor // TODO: Theme! height: 72 * screenScaleFactor // TODO: Theme! visible: currentIndex > 0 + hoverEnabled: true z: 10 onClicked: navigateTo(currentIndex - 1) background: Rectangle { - color: "#ffffff" // TODO: Theme! + color: leftButton.hovered ? "#e8f2fc" : "#ffffff" // TODO: Theme! border.width: 1 * screenScaleFactor // TODO: Theme! border.color: "#cccccc" // TODO: Theme! radius: 2 * screenScaleFactor // TODO: Theme! @@ -79,9 +80,9 @@ Item { anchors.centerIn: parent width: 18 - height: 18 - sourceSize.width: 18 - sourceSize.height: 18 + height: width + sourceSize.width: width + sourceSize.height: width color: "#152950" // TODO: Theme! source: UM.Theme.getIcon("arrow_left") } @@ -135,9 +136,10 @@ Item z: 10 visible: currentIndex < OutputDevice.printers.length - 1 onClicked: navigateTo(currentIndex + 1) + hoverEnabled: true background: Rectangle { - color: "#ffffff" // TODO: Theme! + color: rightButton.hovered ? "#e8f2fc" : "#ffffff" // TODO: Theme! border.width: 1 * screenScaleFactor // TODO: Theme! border.color: "#cccccc" // TODO: Theme! radius: 2 * screenScaleFactor // TODO: Theme! @@ -149,9 +151,9 @@ Item { anchors.centerIn: parent width: 18 - height: 18 - sourceSize.width: 18 - sourceSize.height: 18 + height: width + sourceSize.width: width + sourceSize.height: width color: "#152950" // TODO: Theme! source: UM.Theme.getIcon("arrow_right") } From 3bd5d141ab74b9e41f85510add3168604c13f5a8 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 14 Dec 2018 15:39:17 +0100 Subject: [PATCH 0932/1240] Add easing and make animation less snappy and awesome Contributes to CL-1151 --- .../UM3NetworkPrinting/resources/qml/MonitorCarousel.qml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml index cd7c6f177f..988008dc31 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml @@ -108,7 +108,14 @@ Item width: 5 * tileWidth + 4 * tileSpacing x: 0 z: 0 - Behavior on x { NumberAnimation { duration: 100 } } + Behavior on x + { + NumberAnimation + { + duration: 200 + easing.type: Easing.InOutCubic + } + } spacing: 60 * screenScaleFactor // TODO: Theme! Repeater From 30168103b726fb2d49d7d4cc1d563b9a302757a6 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 14 Dec 2018 15:47:36 +0100 Subject: [PATCH 0933/1240] Update MonitorStage.qml --- plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml index f77cfee1ef..22badfc8ac 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml @@ -15,9 +15,6 @@ Component { id: monitorFrame - property var emphasisColor: UM.Theme.getColor("setting_control_border_highlight") - property var cornerRadius: UM.Theme.getSize("monitor_corner_radius").width - height: maximumHeight onVisibleChanged: { @@ -39,11 +36,11 @@ Component gradient: Gradient { GradientStop { position: 0.0 - color: "#f6f6f6" + color: "#f6f6f6" // TODO: Theme! } GradientStop { position: 1.0 - color: "#ffffff" + color: "#ffffff" // TODO: Theme! } } } From e815d5da8f73a939cd4a1e028fea44c65f987cc8 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Fri, 14 Dec 2018 16:02:28 +0100 Subject: [PATCH 0934/1240] STAR-322: Avoiding lambdas and direct callbacks to avoid gc --- cura/NetworkClient.py | 4 +- .../src/Cloud/CloudApiClient.py | 11 +++-- .../src/Cloud/CloudOutputDevice.py | 46 +++++++++++++++---- .../{ResumableUpload.py => MeshUploader.py} | 18 +++++--- .../tests/Cloud/TestCloudOutputDevice.py | 4 ++ 5 files changed, 59 insertions(+), 24 deletions(-) rename plugins/UM3NetworkPrinting/src/Cloud/{ResumableUpload.py => MeshUploader.py} (91%) diff --git a/cura/NetworkClient.py b/cura/NetworkClient.py index 4c43e58c4f..878158542a 100644 --- a/cura/NetworkClient.py +++ b/cura/NetworkClient.py @@ -3,7 +3,7 @@ from time import time from typing import Optional, Dict, Callable, List, Union -from PyQt5.QtCore import QUrl, QObject +from PyQt5.QtCore import QUrl from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply, QHttpMultiPart, QNetworkRequest, QHttpPart, \ QAuthenticator @@ -13,7 +13,7 @@ from UM.Logger import Logger ## Abstraction of QNetworkAccessManager for easier networking in Cura. # This was originally part of NetworkedPrinterOutputDevice but was moved out for re-use in other classes. -class NetworkClient(QObject): +class NetworkClient: def __init__(self) -> None: super().__init__() diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py index 7c3c08e044..8cdedd1229 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py @@ -4,12 +4,12 @@ import json from json import JSONDecodeError from typing import Callable, List, Type, TypeVar, Union, Optional, Tuple, Dict, Any -from PyQt5.QtCore import QObject, QUrl +from PyQt5.QtCore import QUrl from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply, QNetworkAccessManager from UM.Logger import Logger from cura.API import Account -from .ResumableUpload import ResumableUpload +from .MeshUploader import MeshUploader from ..Models import BaseModel from .Models.CloudClusterResponse import CloudClusterResponse from .Models.CloudErrorObject import CloudErrorObject @@ -37,6 +37,7 @@ class CloudApiClient: self._manager = QNetworkAccessManager() self._account = account self._on_error = on_error + self._upload = None # type: Optional[MeshUploader] ## Gets the account used for the API. @property @@ -77,10 +78,10 @@ class CloudApiClient: # \param on_finished: The function to be called after the result is parsed. It receives the print job ID. # \param on_progress: A function to be called during upload progress. It receives a percentage (0-100). # \param on_error: A function to be called if the upload fails. It receives a dict with the error. - def uploadMesh(self, upload_response: CloudPrintJobResponse, mesh: bytes, on_finished: Callable[[], Any], + def uploadMesh(self, print_job: CloudPrintJobResponse, mesh: bytes, on_finished: Callable[[], Any], on_progress: Callable[[int], Any], on_error: Callable[[], Any]): - ResumableUpload(self._manager, upload_response.upload_url, upload_response.content_type, mesh, on_finished, - on_progress, on_error).start() + self._upload = MeshUploader(self._manager, print_job, mesh, on_finished, on_progress, on_error) + self._upload.start() # Requests a cluster to print the given print job. # \param cluster_id: The ID of the cluster. diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 09677d5e48..88c2f8da1d 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -8,11 +8,13 @@ from typing import Dict, List, Optional, Set from PyQt5.QtCore import QObject, QUrl, pyqtProperty, pyqtSignal, pyqtSlot from UM import i18nCatalog +from UM.Backend.Backend import BackendState from UM.FileHandler.FileHandler import FileHandler from UM.Logger import Logger from UM.Message import Message from UM.Qt.Duration import Duration, DurationFormat from UM.Scene.SceneNode import SceneNode +from cura.CuraApplication import CuraApplication from cura.PrinterOutput.NetworkedPrinterOutputDevice import AuthState, NetworkedPrinterOutputDevice from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputController import CloudOutputController @@ -92,6 +94,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self._device_id = device_id self._account = api_client.account + CuraApplication.getInstance().getBackend().backendStateChange.connect(self._onBackendStateChange) + # We use the Cura Connect monitor tab to get most functionality right away. self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../resources/qml/MonitorStage.qml") @@ -116,6 +120,17 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # A set of the user's job IDs that have finished self._finished_jobs = set() # type: Set[str] + # Reference to the uploaded print job + self._mesh = None # type: Optional[bytes] + self._uploaded_print_job = None # type: Optional[CloudPrintJobResponse] + + def disconnect(self) -> None: + CuraApplication.getInstance().getBackend().backendStateChange.disconnect(self._onBackendStateChange) + + def _onBackendStateChange(self, _: BackendState) -> None: + self._mesh = None + self._uploaded_print_job = None + ## Gets the host name of this device @property def host_name(self) -> str: @@ -146,7 +161,16 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # Show an error message if we're already sending a job. if self._progress.visible: - self._onUploadError(T.BLOCKED_UPLOADING) + Message( + text = T.BLOCKED_UPLOADING, + title = T.ERROR, + lifetime = 10, + ).show() + return + + if self._uploaded_print_job: + # the mesh didn't change, let's not upload it again + self._api.requestPrint(self._device_id, self._uploaded_print_job.job_id, self._onPrintRequested) return # Indicate we have started sending a job. @@ -157,14 +181,15 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): Logger.log("e", "Missing file or mesh writer!") return self._onUploadError(T.COULD_NOT_EXPORT) - mesh_bytes = mesh_format.getBytes(nodes) + mesh = mesh_format.getBytes(nodes) + self._mesh = mesh request = CloudPrintJobUploadRequest( job_name = file_name, - file_size = len(mesh_bytes), + file_size = len(mesh), content_type = mesh_format.mime_type, ) - self._api.requestUpload(request, lambda response: self._onPrintJobCreated(mesh_bytes, response)) + self._api.requestUpload(request, self._onPrintJobCreated) ## Called when the network data should be updated. def _update(self) -> None: @@ -281,21 +306,22 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Uploads the mesh when the print job was registered with the cloud API. # \param mesh: The bytes to upload. # \param job_response: The response received from the cloud API. - def _onPrintJobCreated(self, mesh: bytes, job_response: CloudPrintJobResponse) -> None: + def _onPrintJobCreated(self, job_response: CloudPrintJobResponse) -> None: self._progress.show() - self._api.uploadMesh(job_response, mesh, lambda: self._onPrintJobUploaded(job_response.job_id), - self._progress.update, self._onUploadError) + self._uploaded_print_job = job_response + self._api.uploadMesh(job_response, self._mesh, self._onPrintJobUploaded, self._progress.update, + self._onUploadError) ## Requests the print to be sent to the printer when we finished uploading the mesh. - # \param job_id: The ID of the job. - def _onPrintJobUploaded(self, job_id: str) -> None: + def _onPrintJobUploaded(self) -> None: self._progress.update(100) - self._api.requestPrint(self._device_id, job_id, self._onPrintRequested) + self._api.requestPrint(self._device_id, self._uploaded_print_job.job_id, self._onPrintRequested) ## Displays the given message if uploading the mesh has failed # \param message: The message to display. def _onUploadError(self, message = None) -> None: self._progress.hide() + self._uploaded_print_job = None Message( text = message or T.UPLOAD_ERROR, title = T.ERROR, diff --git a/plugins/UM3NetworkPrinting/src/Cloud/ResumableUpload.py b/plugins/UM3NetworkPrinting/src/Cloud/MeshUploader.py similarity index 91% rename from plugins/UM3NetworkPrinting/src/Cloud/ResumableUpload.py rename to plugins/UM3NetworkPrinting/src/Cloud/MeshUploader.py index 5e3bc9545e..4f0d6f2e81 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/ResumableUpload.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/MeshUploader.py @@ -6,9 +6,10 @@ from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply, QNetworkAccessManage from typing import Optional, Callable, Any, Tuple from UM.Logger import Logger +from src.Cloud.Models.CloudPrintJobResponse import CloudPrintJobResponse -class ResumableUpload: +class MeshUploader: MAX_RETRIES = 10 BYTES_PER_REQUEST = 256 * 1024 RETRY_HTTP_CODES = {500, 502, 503, 504} @@ -18,11 +19,10 @@ class ResumableUpload: # \param content_length: The total content length of the file, in bytes. # \param http_method: The HTTP method to be used, e.g. "POST" or "PUT". # \param timeout: The timeout for each chunk upload. Important: If None, no timeout is applied at all. - def __init__(self, manager: QNetworkAccessManager, url: str, content_type: str, data: bytes, + def __init__(self, manager: QNetworkAccessManager, print_job: CloudPrintJobResponse, data: bytes, on_finished: Callable[[], Any], on_progress: Callable[[int], Any], on_error: Callable[[], Any]): self._manager = manager - self._url = url - self._content_type = content_type + self._print_job = print_job self._data = data self._on_finished = on_finished @@ -34,17 +34,21 @@ class ResumableUpload: self._finished = False self._reply = None # type: Optional[QNetworkReply] + @property + def printJob(self): + return self._print_job + ## We override _createRequest in order to add the user credentials. # \param url: The URL to request # \param content_type: The type of the body contents. def _createRequest(self) -> QNetworkRequest: - request = QNetworkRequest(QUrl(self._url)) - request.setHeader(QNetworkRequest.ContentTypeHeader, self._content_type) + request = QNetworkRequest(QUrl(self._print_job.upload_url)) + request.setHeader(QNetworkRequest.ContentTypeHeader, self._print_job.content_type) first_byte, last_byte = self._chunkRange() content_range = "bytes {}-{}/{}".format(first_byte, last_byte - 1, len(self._data)) request.setRawHeader(b"Content-Range", content_range.encode()) - Logger.log("i", "Uploading %s to %s", content_range, self._url) + Logger.log("i", "Uploading %s to %s", content_range, self._print_job.upload_url) return request diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py index c391dc75dd..d31f59f85a 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py @@ -5,6 +5,7 @@ from unittest import TestCase from unittest.mock import patch, MagicMock from UM.Scene.SceneNode import SceneNode +from UM.Signal import Signal from cura.CuraApplication import CuraApplication from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from src.Cloud.CloudApiClient import CloudApiClient @@ -26,6 +27,9 @@ class TestCloudOutputDevice(TestCase): def setUp(self): super().setUp() self.app = CuraApplication.getInstance() + self.backend = MagicMock(backendStateChange = Signal()) + self.app.setBackend(self.backend) + self.network = NetworkManagerMock() self.account = MagicMock(isLoggedIn=True, accessToken="TestAccessToken") self.onError = MagicMock() From 3766effa81dc111d70c0d7ff4563936ec92fc5c4 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 14 Dec 2018 16:04:31 +0100 Subject: [PATCH 0935/1240] Quick fix to prevent monitor stage from overlapping header --- plugins/MonitorStage/MonitorMain.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/MonitorStage/MonitorMain.qml b/plugins/MonitorStage/MonitorMain.qml index 1f287fc0fa..1f52ceea51 100644 --- a/plugins/MonitorStage/MonitorMain.qml +++ b/plugins/MonitorStage/MonitorMain.qml @@ -16,6 +16,7 @@ Item color: UM.Theme.getColor("viewport_overlay") anchors.fill: parent + anchors.topMargin: Math.round(stageMenu.height / 2) MouseArea { anchors.fill: parent @@ -29,8 +30,7 @@ Item id: monitorViewComponent anchors.fill: parent - - height: parent.height + anchors.topMargin: Math.round(stageMenu.height / 2) property real maximumWidth: parent.width property real maximumHeight: parent.height From 2789a0fdc43a2eb4c32f2c434ac23b22a83eef1f Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 14 Dec 2018 16:13:35 +0100 Subject: [PATCH 0936/1240] Revert "Quick fix to prevent monitor stage from overlapping header" This reverts commit 3766effa81dc111d70c0d7ff4563936ec92fc5c4. --- plugins/MonitorStage/MonitorMain.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/MonitorStage/MonitorMain.qml b/plugins/MonitorStage/MonitorMain.qml index 1f52ceea51..1f287fc0fa 100644 --- a/plugins/MonitorStage/MonitorMain.qml +++ b/plugins/MonitorStage/MonitorMain.qml @@ -16,7 +16,6 @@ Item color: UM.Theme.getColor("viewport_overlay") anchors.fill: parent - anchors.topMargin: Math.round(stageMenu.height / 2) MouseArea { anchors.fill: parent @@ -30,7 +29,8 @@ Item id: monitorViewComponent anchors.fill: parent - anchors.topMargin: Math.round(stageMenu.height / 2) + + height: parent.height property real maximumWidth: parent.width property real maximumHeight: parent.height From ffccbcea2fbe91bef2becc71a90b5d09bf840203 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 14 Dec 2018 16:13:49 +0100 Subject: [PATCH 0937/1240] Anchor to bottom of header background --- resources/qml/Cura.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 2b6f989e0b..573d75e5fa 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -252,7 +252,7 @@ UM.MainWindow anchors { // Align to the top of the stageMenu since the stageMenu may not exist - top: parent.top + top: headerBackground.top left: parent.left right: parent.right bottom: parent.bottom From 9146a775a4c3ae4b83cd7f62d2bf0d945e9caab1 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Fri, 14 Dec 2018 16:17:05 +0100 Subject: [PATCH 0938/1240] After resetting the custom settings the quality slider did not update selected value CURA-6028 --- cura/Settings/SimpleModeSettingsManager.py | 11 ++++++----- .../RecommendedQualityProfileSelector.qml | 12 +++++++++++- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/cura/Settings/SimpleModeSettingsManager.py b/cura/Settings/SimpleModeSettingsManager.py index fce43243bd..210a5794d4 100644 --- a/cura/Settings/SimpleModeSettingsManager.py +++ b/cura/Settings/SimpleModeSettingsManager.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, pyqtSignal, pyqtProperty +from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty, pyqtSlot from UM.Application import Application @@ -16,12 +16,12 @@ class SimpleModeSettingsManager(QObject): self._is_profile_user_created = False # True when profile was custom created by user self._machine_manager.activeStackValueChanged.connect(self._updateIsProfileCustomized) - self._machine_manager.activeQualityGroupChanged.connect(self._updateIsProfileUserCreated) - self._machine_manager.activeQualityChangesGroupChanged.connect(self._updateIsProfileUserCreated) + self._machine_manager.activeQualityGroupChanged.connect(self.updateIsProfileUserCreated) + self._machine_manager.activeQualityChangesGroupChanged.connect(self.updateIsProfileUserCreated) # update on create as the activeQualityChanged signal is emitted before this manager is created when Cura starts self._updateIsProfileCustomized() - self._updateIsProfileUserCreated() + self.updateIsProfileUserCreated() isProfileCustomizedChanged = pyqtSignal() isProfileUserCreatedChanged = pyqtSignal() @@ -61,7 +61,8 @@ class SimpleModeSettingsManager(QObject): def isProfileUserCreated(self): return self._is_profile_user_created - def _updateIsProfileUserCreated(self): + @pyqtSlot() + def updateIsProfileUserCreated(self) -> None: quality_changes_keys = set() if not self._machine_manager.activeMachine: diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml index 349c6dbb57..e6b3f1b9eb 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml @@ -39,7 +39,17 @@ Item { target: Cura.QualityProfilesDropDownMenuModel onItemsChanged: qualityModel.update() - onDataChanged: qualityModel.update() + onDataChanged: + { + // If a custom profile is selected and then a user decides to change any of setting the slider should show + // the reset button. After clicking the reset button the QualityProfilesDropDownMenuModel(ListModel) is + // updated before the property isProfileCustomized is called to update. + if (Cura.SimpleModeSettingsManager.isProfileCustomized) + { + Cura.SimpleModeSettingsManager.updateIsProfileUserCreated() + } + qualityModel.update() + } } Connections { From 74af11d609f7e927f14de92af619a06121cc35ed Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 14 Dec 2018 16:20:08 +0100 Subject: [PATCH 0939/1240] Anchor loader to stage menu vertical center --- resources/qml/Cura.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 573d75e5fa..8ab943b93b 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -252,7 +252,7 @@ UM.MainWindow anchors { // Align to the top of the stageMenu since the stageMenu may not exist - top: headerBackground.top + top: stageMenu.source ? stageMenu.verticalCenter : parent.top left: parent.left right: parent.right bottom: parent.bottom From ef6f666c3e07b0b9c5fd4ab85023d2a9d29433c2 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 14 Dec 2018 16:41:07 +0100 Subject: [PATCH 0940/1240] Remove duplicate alias definition Yay. Merges. --- resources/qml/IconWithText.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/qml/IconWithText.qml b/resources/qml/IconWithText.qml index dd522031c1..24b6dc7fe2 100644 --- a/resources/qml/IconWithText.qml +++ b/resources/qml/IconWithText.qml @@ -19,7 +19,6 @@ Item property alias color: label.color property alias text: label.text property alias font: label.font - property alias iconColor: icon.color property real margin: UM.Theme.getSize("narrow_margin").width // These properties can be used in combination with layouts. From e877b47e22cb95b2534a7121db73914d4a321d93 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 14 Dec 2018 16:55:48 +0100 Subject: [PATCH 0941/1240] Disable printer cards which are not in focus Contributes to CL-1151 --- .../resources/qml/CameraButton.qml | 18 +++++++++++++----- .../resources/qml/MonitorCarousel.qml | 1 + .../resources/qml/MonitorPrinterCard.qml | 9 ++++++++- .../resources/qml/PrintJobContextMenu.qml | 5 +++-- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/CameraButton.qml b/plugins/UM3NetworkPrinting/resources/qml/CameraButton.qml index 618dbed81c..6f054f9c19 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/CameraButton.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/CameraButton.qml @@ -8,11 +8,13 @@ import UM 1.3 as UM import Cura 1.0 as Cura Rectangle { + id: base property var iconSource: null; color: "#0a0850" // TODO: Theme! height: width; radius: Math.round(0.5 * width); width: 24 * screenScaleFactor; + property var enabled: true UM.RecolorImage { id: icon; @@ -29,12 +31,18 @@ Rectangle { MouseArea { id: clickArea; anchors.fill: parent; - hoverEnabled: true; + hoverEnabled: base.enabled onClicked: { - if (OutputDevice.activeCameraUrl != "") { - OutputDevice.setActiveCameraUrl(""); - } else { - OutputDevice.setActiveCameraUrl(modelData.cameraUrl); + if (base.enabled) + { + if (OutputDevice.activeCameraUrl != "") + { + OutputDevice.setActiveCameraUrl("") + } + else + { + OutputDevice.setActiveCameraUrl(modelData.cameraUrl) + } } } } diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml index 988008dc31..952ec1e162 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml @@ -124,6 +124,7 @@ Item MonitorPrinterCard { printer: modelData + enabled: model.index == currentIndex } } } diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml index 038f0535f0..2ca37a7e13 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml @@ -25,6 +25,11 @@ Item property var borderSize: 1 * screenScaleFactor // TODO: Theme, and remove from here + // If the printer card's controls are enabled. This is used by the carousel + // to prevent opening the context menu or camera while the printer card is not + // "in focus" + property var enabled: true + width: 834 * screenScaleFactor // TODO: Theme! height: childrenRect.height @@ -124,6 +129,7 @@ Item printJob: printer.activePrintJob width: 36 * screenScaleFactor // TODO: Theme! height: 36 * screenScaleFactor // TODO: Theme! + enabled: base.enabled } CameraButton { @@ -136,6 +142,7 @@ Item bottomMargin: 20 * screenScaleFactor // TODO: Theme! } iconSource: "../svg/icons/camera.svg" + enabled: base.enabled } } @@ -320,7 +327,7 @@ Item implicitHeight: 32 * screenScaleFactor // TODO: Theme! implicitWidth: 96 * screenScaleFactor // TODO: Theme! visible: printer.activePrintJob && printer.activePrintJob.configurationChanges.length > 0 - onClicked: overrideConfirmationDialog.open() + onClicked: base.enabled ? overrideConfirmationDialog.open() : {} } } diff --git a/plugins/UM3NetworkPrinting/resources/qml/PrintJobContextMenu.qml b/plugins/UM3NetworkPrinting/resources/qml/PrintJobContextMenu.qml index 1edbf9f6a2..5c5c892dad 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/PrintJobContextMenu.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/PrintJobContextMenu.qml @@ -13,6 +13,7 @@ Item { property var printJob: null; property var started: isStarted(printJob); property var assigned: isAssigned(printJob); + property var enabled: true Button { id: button; @@ -31,8 +32,8 @@ Item { verticalAlignment: Text.AlignVCenter; } height: width; - hoverEnabled: true; - onClicked: parent.switchPopupState(); + hoverEnabled: base.enabled + onClicked: base.enabled ? parent.switchPopupState() : {} text: "\u22EE"; //Unicode; Three stacked points. visible: { if (!printJob) { From 331cd730f11c4b5a486fe7a5e1474c54b0d054fd Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 14 Dec 2018 16:56:20 +0100 Subject: [PATCH 0942/1240] Improve visual hint that side cards are not in focus Contributes to CL-1151 --- plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml index 952ec1e162..ea1449ac8d 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml @@ -45,7 +45,7 @@ Item GradientStop { position: 1.0 - color: "#00f6f6f6" + color: "#66f6f6f6" } } } @@ -190,7 +190,7 @@ Item GradientStop { position: 0.0 - color: "#00f6f6f6" + color: "#66f6f6f6" } GradientStop { From 226d05246877a95c46c0799160cbd0015bff714c Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 14 Dec 2018 17:10:58 +0100 Subject: [PATCH 0943/1240] Move the machines from machinelist into their own model CURA-6011 --- cura/CuraApplication.py | 2 + cura/PrintersModel.py | 65 ++++++++++++++++ .../src/UM3OutputDevicePlugin.py | 1 + .../qml/PrinterSelector/MachineSelector.qml | 6 ++ .../PrinterSelector/MachineSelectorList.qml | 76 ++++++------------- 5 files changed, 97 insertions(+), 53 deletions(-) create mode 100644 cura/PrintersModel.py diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index a50d7d55c8..c773dae998 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -51,6 +51,7 @@ from cura.Arranging.ArrangeObjectsJob import ArrangeObjectsJob from cura.Arranging.ArrangeObjectsAllBuildPlatesJob import ArrangeObjectsAllBuildPlatesJob from cura.Arranging.ShapeArray import ShapeArray from cura.MultiplyObjectsJob import MultiplyObjectsJob +from cura.PrintersModel import PrintersModel from cura.Scene.ConvexHullDecorator import ConvexHullDecorator from cura.Operations.SetParentOperation import SetParentOperation from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator @@ -955,6 +956,7 @@ class CuraApplication(QtApplication): qmlRegisterType(MultiBuildPlateModel, "Cura", 1, 0, "MultiBuildPlateModel") qmlRegisterType(InstanceContainer, "Cura", 1, 0, "InstanceContainer") qmlRegisterType(ExtrudersModel, "Cura", 1, 0, "ExtrudersModel") + qmlRegisterType(PrintersModel, "Cura", 1, 0, "PrintersModel") qmlRegisterType(FavoriteMaterialsModel, "Cura", 1, 0, "FavoriteMaterialsModel") qmlRegisterType(GenericMaterialsModel, "Cura", 1, 0, "GenericMaterialsModel") diff --git a/cura/PrintersModel.py b/cura/PrintersModel.py new file mode 100644 index 0000000000..83471a2e2a --- /dev/null +++ b/cura/PrintersModel.py @@ -0,0 +1,65 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from UM.Qt.ListModel import ListModel + +from PyQt5.QtCore import pyqtProperty, Qt, pyqtSignal + +from UM.Settings.ContainerRegistry import ContainerRegistry +from UM.Settings.ContainerStack import ContainerStack + +from cura.PrinterOutputDevice import ConnectionType + +from cura.Settings.GlobalStack import GlobalStack + +class PrintersModel(ListModel): + NameRole = Qt.UserRole + 1 + IdRole = Qt.UserRole + 2 + HasRemoteConnectionRole = Qt.UserRole + 3 + ConnectionTypeRole = Qt.UserRole + 4 + MetaDataRole = Qt.UserRole + 5 + + def __init__(self, parent = None): + super().__init__(parent) + self.addRoleName(self.NameRole, "name") + self.addRoleName(self.IdRole, "id") + self.addRoleName(self.HasRemoteConnectionRole, "hasRemoteConnection") + self.addRoleName(self.ConnectionTypeRole, "connectionType") + self.addRoleName(self.MetaDataRole, "metadata") + self._container_stacks = [] + + # Listen to changes + ContainerRegistry.getInstance().containerAdded.connect(self._onContainerChanged) + ContainerRegistry.getInstance().containerMetaDataChanged.connect(self._onContainerChanged) + ContainerRegistry.getInstance().containerRemoved.connect(self._onContainerChanged) + self._filter_dict = {} + self._update() + + ## Handler for container added/removed events from registry + def _onContainerChanged(self, container): + # We only need to update when the added / removed container GlobalStack + if isinstance(container, GlobalStack): + self._update() + + ## Handler for container name change events. + def _onContainerNameChanged(self): + self._update() + + def _update(self) -> None: + items = [] + for container in self._container_stacks: + container.nameChanged.disconnect(self._onContainerNameChanged) + + container_stacks = ContainerRegistry.getInstance().findContainerStacks(type = "machine") + + for container_stack in container_stacks: + connection_type = container_stack.getMetaDataEntry("connection_type") + has_remote_connection = connection_type in [str(ConnectionType.NetworkConnection), str(ConnectionType.CloudConnection), str(ConnectionType.ClusterConnection)] + + # TODO: Remove reference to connect group name. + items.append({"name": container_stack.getMetaDataEntry("connect_group_name", container_stack.getName()), + "id": container_stack.getId(), + "hasRemoteConnection": has_remote_connection, + "connectionType": connection_type}) + items.sort(key=lambda i: not i["hasRemoteConnection"]) + self.setItems(items) \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py index b96c508d70..43290c8e44 100644 --- a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py @@ -283,6 +283,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): global_container_stack = Application.getInstance().getGlobalContainerStack() if global_container_stack and device.getId() == global_container_stack.getMetaDataEntry("um_network_key"): + global_container_stack.setMetaDataEntry("connection_type", str(device.getConnectionType())) device.connect() device.connectionStateChanged.connect(self._onDeviceConnectionStateChanged) diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index 7cda4f1d2e..6e120e89c7 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -123,6 +123,12 @@ Cura.ExpandablePopup scroll.height = Math.min(height, maximumHeight) popup.height = scroll.height + buttonRow.height } + Component.onCompleted: + { + scroll.height = Math.min(height, maximumHeight) + popup.height = scroll.height + buttonRow.height + } + } } diff --git a/resources/qml/PrinterSelector/MachineSelectorList.qml b/resources/qml/PrinterSelector/MachineSelectorList.qml index bc3fe105a2..b157f9a4f6 100644 --- a/resources/qml/PrinterSelector/MachineSelectorList.qml +++ b/resources/qml/PrinterSelector/MachineSelectorList.qml @@ -7,46 +7,48 @@ import QtQuick.Controls 2.3 import UM 1.2 as UM import Cura 1.0 as Cura -Column +ListView { - id: machineSelectorList + id: listView + height: childrenRect.height + width: 200 + model: Cura.PrintersModel {} + section.property: "hasRemoteConnection" - Label + section.delegate: Label { - text: catalog.i18nc("@label", "Connected printers") - visible: networkedPrintersModel.items.length > 0 + text: section == "true" ? catalog.i18nc("@label", "Connected printers") : catalog.i18nc("@label", "Preset printers") + width: parent.width leftPadding: UM.Theme.getSize("default_margin").width - height: visible ? contentHeight + 2 * UM.Theme.getSize("default_margin").height : 0 renderType: Text.NativeRendering font: UM.Theme.getFont("medium") color: UM.Theme.getColor("text_medium") verticalAlignment: Text.AlignVCenter } + delegate: MachineSelectorButton + { + text: model.name + width: listView.width + } +} + /* + + + Repeater { id: networkedPrinters - model: UM.ContainerStacksModel + model: Cura.PrintersModel { id: networkedPrintersModel - property var umConnectionTypes: [Cura.PrinterOutputDevice.NetworkConnection, - Cura.PrinterOutputDevice.ClusterConnection, - Cura.PrinterOutputDevice.CloudConnection - ] - filter: - { - "type": "machine", - "um_network_key": "*", - "hidden": "False", - "um_connection_type": "[" + umConnectionTypes.join(",") + "]" - } } delegate: MachineSelectorButton { - text: model.metadata["connect_group_name"] - checked: Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["connect_group_name"] + text: model.name //model.metadata["connect_group_name"] + //checked: Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["connect_group_name"] outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null Connections @@ -55,37 +57,5 @@ Column onActiveMachineNetworkGroupNameChanged: checked = Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["connect_group_name"] } } - } + }*/ - Label - { - text: catalog.i18nc("@label", "Preset printers") - visible: virtualPrintersModel.items.length > 0 - leftPadding: UM.Theme.getSize("default_margin").width - height: visible ? contentHeight + 2 * UM.Theme.getSize("default_margin").height : 0 - renderType: Text.NativeRendering - font: UM.Theme.getFont("medium") - color: UM.Theme.getColor("text_medium") - verticalAlignment: Text.AlignVCenter - } - - Repeater - { - id: virtualPrinters - - model: UM.ContainerStacksModel - { - id: virtualPrintersModel - filter: - { - "type": "machine", "um_network_key": null - } - } - - delegate: MachineSelectorButton - { - text: model.name - checked: Cura.MachineManager.activeMachineId == model.id - } - } -} \ No newline at end of file From b8a4d8e80d39c81dbb2670e3236709a3ad4df4ba Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 14 Dec 2018 17:14:56 +0100 Subject: [PATCH 0944/1240] Remove the cluster connection type CURA-6011 --- cura/PrinterOutputDevice.py | 3 +-- cura/PrintersModel.py | 2 +- plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index ed65d5d700..b33993f150 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -39,8 +39,7 @@ class ConnectionType(IntEnum): Unknown = 0 UsbConnection = 1 NetworkConnection = 2 - ClusterConnection = 3 - CloudConnection = 4 + CloudConnection = 3 ## Printer output device adds extra interface options on top of output device. diff --git a/cura/PrintersModel.py b/cura/PrintersModel.py index 83471a2e2a..26b65409c5 100644 --- a/cura/PrintersModel.py +++ b/cura/PrintersModel.py @@ -54,7 +54,7 @@ class PrintersModel(ListModel): for container_stack in container_stacks: connection_type = container_stack.getMetaDataEntry("connection_type") - has_remote_connection = connection_type in [str(ConnectionType.NetworkConnection), str(ConnectionType.CloudConnection), str(ConnectionType.ClusterConnection)] + has_remote_connection = connection_type in [str(ConnectionType.NetworkConnection), str(ConnectionType.CloudConnection)] # TODO: Remove reference to connect group name. items.append({"name": container_stack.getMetaDataEntry("connect_group_name", container_stack.getName()), diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index d85cbeb27b..bebccc54e3 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -55,7 +55,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): clusterPrintersChanged = pyqtSignal() def __init__(self, device_id, address, properties, parent = None) -> None: - super().__init__(device_id = device_id, address = address, properties=properties, connection_type = ConnectionType.ClusterConnection, parent = parent) + super().__init__(device_id = device_id, address = address, properties=properties, connection_type = ConnectionType.NetworkConnection, parent = parent) self._api_prefix = "/cluster-api/v1/" self._number_of_extruders = 2 From c235f339ae9e59b2c54a9054028a456658c306f1 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 14 Dec 2018 17:29:02 +0100 Subject: [PATCH 0945/1240] Increment API version to 6 All plug-ins now have to re-check whether they are still compatible with the current version of Cura. Contributes to issue CURA-6019. --- cura/CuraApplication.py | 2 +- plugins/3MFReader/plugin.json | 2 +- plugins/3MFWriter/plugin.json | 2 +- plugins/ChangeLogPlugin/plugin.json | 2 +- plugins/CuraEngineBackend/plugin.json | 2 +- plugins/CuraProfileReader/plugin.json | 2 +- plugins/CuraProfileWriter/plugin.json | 2 +- plugins/FirmwareUpdateChecker/plugin.json | 2 +- plugins/FirmwareUpdater/plugin.json | 2 +- plugins/GCodeGzReader/plugin.json | 2 +- plugins/GCodeGzWriter/plugin.json | 2 +- plugins/GCodeProfileReader/plugin.json | 2 +- plugins/GCodeReader/plugin.json | 4 ++-- plugins/GCodeWriter/plugin.json | 2 +- plugins/ImageReader/plugin.json | 2 +- plugins/LegacyProfileReader/plugin.json | 2 +- plugins/MachineSettingsAction/plugin.json | 2 +- plugins/ModelChecker/plugin.json | 2 +- plugins/MonitorStage/plugin.json | 2 +- plugins/PerObjectSettingsTool/plugin.json | 2 +- plugins/PostProcessingPlugin/plugin.json | 2 +- plugins/PrepareStage/plugin.json | 2 +- plugins/PreviewStage/plugin.json | 2 +- plugins/RemovableDriveOutputDevice/plugin.json | 2 +- plugins/SimulationView/plugin.json | 2 +- plugins/SliceInfoPlugin/plugin.json | 2 +- plugins/SolidView/plugin.json | 2 +- plugins/SupportEraser/plugin.json | 2 +- plugins/Toolbox/plugin.json | 2 +- plugins/UFPWriter/plugin.json | 2 +- plugins/UM3NetworkPrinting/plugin.json | 2 +- plugins/USBPrinting/plugin.json | 2 +- plugins/UltimakerMachineActions/plugin.json | 2 +- plugins/UserAgreement/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade21to22/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade22to24/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade25to26/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade26to27/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade27to30/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade30to31/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade34to35/plugin.json | 2 +- plugins/X3DReader/plugin.json | 2 +- plugins/XRayView/plugin.json | 2 +- plugins/XmlMaterialProfile/plugin.json | 2 +- 46 files changed, 47 insertions(+), 47 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 55e37617d5..726197de10 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -134,7 +134,7 @@ except ImportError: CuraVersion = "master" # [CodeStyle: Reflecting imported value] CuraBuildType = "" CuraDebugMode = False - CuraSDKVersion = "5.0.0" + CuraSDKVersion = "6.0.0" class CuraApplication(QtApplication): diff --git a/plugins/3MFReader/plugin.json b/plugins/3MFReader/plugin.json index 5e41975752..d7160e166a 100644 --- a/plugins/3MFReader/plugin.json +++ b/plugins/3MFReader/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides support for reading 3MF files.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/3MFWriter/plugin.json b/plugins/3MFWriter/plugin.json index 9ec4fb0c20..865c3bf026 100644 --- a/plugins/3MFWriter/plugin.json +++ b/plugins/3MFWriter/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides support for writing 3MF files.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/ChangeLogPlugin/plugin.json b/plugins/ChangeLogPlugin/plugin.json index e09a08564a..04d5ce7c51 100644 --- a/plugins/ChangeLogPlugin/plugin.json +++ b/plugins/ChangeLogPlugin/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Shows changes since latest checked version.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/CuraEngineBackend/plugin.json b/plugins/CuraEngineBackend/plugin.json index 111698d8d1..25db9abefd 100644 --- a/plugins/CuraEngineBackend/plugin.json +++ b/plugins/CuraEngineBackend/plugin.json @@ -2,7 +2,7 @@ "name": "CuraEngine Backend", "author": "Ultimaker B.V.", "description": "Provides the link to the CuraEngine slicing backend.", - "api": 5, + "api": "6.0", "version": "1.0.0", "i18n-catalog": "cura" } diff --git a/plugins/CuraProfileReader/plugin.json b/plugins/CuraProfileReader/plugin.json index 66a2a6a56b..adbf376b72 100644 --- a/plugins/CuraProfileReader/plugin.json +++ b/plugins/CuraProfileReader/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides support for importing Cura profiles.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/CuraProfileWriter/plugin.json b/plugins/CuraProfileWriter/plugin.json index 16c8c34152..d576f517de 100644 --- a/plugins/CuraProfileWriter/plugin.json +++ b/plugins/CuraProfileWriter/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides support for exporting Cura profiles.", - "api": 5, + "api": "6.0", "i18n-catalog":"cura" } diff --git a/plugins/FirmwareUpdateChecker/plugin.json b/plugins/FirmwareUpdateChecker/plugin.json index cbbd41e420..4b77a53a62 100644 --- a/plugins/FirmwareUpdateChecker/plugin.json +++ b/plugins/FirmwareUpdateChecker/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Checks for firmware updates.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/FirmwareUpdater/plugin.json b/plugins/FirmwareUpdater/plugin.json index 3e09eab2b5..4098759630 100644 --- a/plugins/FirmwareUpdater/plugin.json +++ b/plugins/FirmwareUpdater/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides a machine actions for updating firmware.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/GCodeGzReader/plugin.json b/plugins/GCodeGzReader/plugin.json index 3bd6a4097d..cea1d0f55c 100644 --- a/plugins/GCodeGzReader/plugin.json +++ b/plugins/GCodeGzReader/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Reads g-code from a compressed archive.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/GCodeGzWriter/plugin.json b/plugins/GCodeGzWriter/plugin.json index 4c6497317b..d725b2649d 100644 --- a/plugins/GCodeGzWriter/plugin.json +++ b/plugins/GCodeGzWriter/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Writes g-code to a compressed archive.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/GCodeProfileReader/plugin.json b/plugins/GCodeProfileReader/plugin.json index 9677628c85..12d8a276da 100644 --- a/plugins/GCodeProfileReader/plugin.json +++ b/plugins/GCodeProfileReader/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides support for importing profiles from g-code files.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/GCodeReader/plugin.json b/plugins/GCodeReader/plugin.json index 75b4d0cd4f..351d6cbc31 100644 --- a/plugins/GCodeReader/plugin.json +++ b/plugins/GCodeReader/plugin.json @@ -1,8 +1,8 @@ { "name": "G-code Reader", - "author": "Victor Larchenko", + "author": "Victor Larchenko, Ultimaker", "version": "1.0.0", "description": "Allows loading and displaying G-code files.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/GCodeWriter/plugin.json b/plugins/GCodeWriter/plugin.json index 3bbbab8b95..78b63a582f 100644 --- a/plugins/GCodeWriter/plugin.json +++ b/plugins/GCodeWriter/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Writes g-code to a file.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/ImageReader/plugin.json b/plugins/ImageReader/plugin.json index 08195863e8..001da0822b 100644 --- a/plugins/ImageReader/plugin.json +++ b/plugins/ImageReader/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Enables ability to generate printable geometry from 2D image files.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/LegacyProfileReader/plugin.json b/plugins/LegacyProfileReader/plugin.json index 179f5444e0..2cd1e9ca2c 100644 --- a/plugins/LegacyProfileReader/plugin.json +++ b/plugins/LegacyProfileReader/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides support for importing profiles from legacy Cura versions.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/MachineSettingsAction/plugin.json b/plugins/MachineSettingsAction/plugin.json index 571658e40a..4587729a9a 100644 --- a/plugins/MachineSettingsAction/plugin.json +++ b/plugins/MachineSettingsAction/plugin.json @@ -3,6 +3,6 @@ "author": "fieldOfView", "version": "1.0.0", "description": "Provides a way to change machine settings (such as build volume, nozzle size, etc.).", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/ModelChecker/plugin.json b/plugins/ModelChecker/plugin.json index 3753c0cc88..a210c3bf9f 100644 --- a/plugins/ModelChecker/plugin.json +++ b/plugins/ModelChecker/plugin.json @@ -2,7 +2,7 @@ "name": "Model Checker", "author": "Ultimaker B.V.", "version": "0.1", - "api": 5, + "api": "6.0", "description": "Checks models and print configuration for possible printing issues and give suggestions.", "i18n-catalog": "cura" } diff --git a/plugins/MonitorStage/plugin.json b/plugins/MonitorStage/plugin.json index 88b53840e0..71fc10c5ce 100644 --- a/plugins/MonitorStage/plugin.json +++ b/plugins/MonitorStage/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides a monitor stage in Cura.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } \ No newline at end of file diff --git a/plugins/PerObjectSettingsTool/plugin.json b/plugins/PerObjectSettingsTool/plugin.json index 15fde63387..e5bdc5d3da 100644 --- a/plugins/PerObjectSettingsTool/plugin.json +++ b/plugins/PerObjectSettingsTool/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides the Per Model Settings.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/PostProcessingPlugin/plugin.json b/plugins/PostProcessingPlugin/plugin.json index fea061e93b..ad60312af0 100644 --- a/plugins/PostProcessingPlugin/plugin.json +++ b/plugins/PostProcessingPlugin/plugin.json @@ -2,7 +2,7 @@ "name": "Post Processing", "author": "Ultimaker", "version": "2.2", - "api": 5, + "api": "6.0", "description": "Extension that allows for user created scripts for post processing", "catalog": "cura" } \ No newline at end of file diff --git a/plugins/PrepareStage/plugin.json b/plugins/PrepareStage/plugin.json index f0464313c7..e948b96f6f 100644 --- a/plugins/PrepareStage/plugin.json +++ b/plugins/PrepareStage/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides a prepare stage in Cura.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } \ No newline at end of file diff --git a/plugins/PreviewStage/plugin.json b/plugins/PreviewStage/plugin.json index 9349da2b0e..c9c13fe341 100644 --- a/plugins/PreviewStage/plugin.json +++ b/plugins/PreviewStage/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides a preview stage in Cura.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } \ No newline at end of file diff --git a/plugins/RemovableDriveOutputDevice/plugin.json b/plugins/RemovableDriveOutputDevice/plugin.json index 36bb9ae186..48cd60596c 100644 --- a/plugins/RemovableDriveOutputDevice/plugin.json +++ b/plugins/RemovableDriveOutputDevice/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "description": "Provides removable drive hotplugging and writing support.", "version": "1.0.0", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/SimulationView/plugin.json b/plugins/SimulationView/plugin.json index 93df98068f..dcfbeb305e 100644 --- a/plugins/SimulationView/plugin.json +++ b/plugins/SimulationView/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides the Simulation view.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/SliceInfoPlugin/plugin.json b/plugins/SliceInfoPlugin/plugin.json index 939e5ff235..c2d78e8d78 100644 --- a/plugins/SliceInfoPlugin/plugin.json +++ b/plugins/SliceInfoPlugin/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Submits anonymous slice info. Can be disabled through preferences.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/SolidView/plugin.json b/plugins/SolidView/plugin.json index e70ec224dd..c66e41a7aa 100644 --- a/plugins/SolidView/plugin.json +++ b/plugins/SolidView/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides a normal solid mesh view.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } \ No newline at end of file diff --git a/plugins/SupportEraser/plugin.json b/plugins/SupportEraser/plugin.json index 7af35e0fb5..dc624ecd77 100644 --- a/plugins/SupportEraser/plugin.json +++ b/plugins/SupportEraser/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Creates an eraser mesh to block the printing of support in certain places", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/Toolbox/plugin.json b/plugins/Toolbox/plugin.json index 2557185524..91e9561327 100644 --- a/plugins/Toolbox/plugin.json +++ b/plugins/Toolbox/plugin.json @@ -2,6 +2,6 @@ "name": "Toolbox", "author": "Ultimaker B.V.", "version": "1.0.0", - "api": 5, + "api": "6.0", "description": "Find, manage and install new Cura packages." } diff --git a/plugins/UFPWriter/plugin.json b/plugins/UFPWriter/plugin.json index ab590353e0..b4e05d9346 100644 --- a/plugins/UFPWriter/plugin.json +++ b/plugins/UFPWriter/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides support for writing Ultimaker Format Packages.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/plugin.json b/plugins/UM3NetworkPrinting/plugin.json index d415338374..def02562db 100644 --- a/plugins/UM3NetworkPrinting/plugin.json +++ b/plugins/UM3NetworkPrinting/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "description": "Manages network connections to Ultimaker 3 printers.", "version": "1.0.0", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/USBPrinting/plugin.json b/plugins/USBPrinting/plugin.json index 5d3cba8415..3a58abe7e7 100644 --- a/plugins/USBPrinting/plugin.json +++ b/plugins/USBPrinting/plugin.json @@ -2,7 +2,7 @@ "name": "USB printing", "author": "Ultimaker B.V.", "version": "1.0.1", - "api": 5, + "api": "6.0", "description": "Accepts G-Code and sends them to a printer. Plugin can also update firmware.", "i18n-catalog": "cura" } diff --git a/plugins/UltimakerMachineActions/plugin.json b/plugins/UltimakerMachineActions/plugin.json index b60c7df88e..fc1eb242fe 100644 --- a/plugins/UltimakerMachineActions/plugin.json +++ b/plugins/UltimakerMachineActions/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides machine actions for Ultimaker machines (such as bed leveling wizard, selecting upgrades, etc.).", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/UserAgreement/plugin.json b/plugins/UserAgreement/plugin.json index 50a2aa0441..7781e383d6 100644 --- a/plugins/UserAgreement/plugin.json +++ b/plugins/UserAgreement/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Ask the user once if he/she agrees with our license.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/plugin.json b/plugins/VersionUpgrade/VersionUpgrade21to22/plugin.json index 463fcdc941..e732c43813 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 2.1 to Cura 2.2.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade22to24/plugin.json b/plugins/VersionUpgrade/VersionUpgrade22to24/plugin.json index e7a0b1c559..2b344ce5d3 100644 --- a/plugins/VersionUpgrade/VersionUpgrade22to24/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade22to24/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 2.2 to Cura 2.4.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade25to26/plugin.json b/plugins/VersionUpgrade/VersionUpgrade25to26/plugin.json index 3029539887..8691ebfc67 100644 --- a/plugins/VersionUpgrade/VersionUpgrade25to26/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade25to26/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 2.5 to Cura 2.6.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade26to27/plugin.json b/plugins/VersionUpgrade/VersionUpgrade26to27/plugin.json index 225da67235..c7124eada6 100644 --- a/plugins/VersionUpgrade/VersionUpgrade26to27/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade26to27/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 2.6 to Cura 2.7.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade27to30/plugin.json b/plugins/VersionUpgrade/VersionUpgrade27to30/plugin.json index 9a139851ec..ce8f4a6d44 100644 --- a/plugins/VersionUpgrade/VersionUpgrade27to30/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade27to30/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 2.7 to Cura 3.0.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade30to31/plugin.json b/plugins/VersionUpgrade/VersionUpgrade30to31/plugin.json index cf42b3f6cd..f94f3dfe30 100644 --- a/plugins/VersionUpgrade/VersionUpgrade30to31/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade30to31/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 3.0 to Cura 3.1.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json b/plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json index f9cc968dae..0bbc237436 100644 --- a/plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 3.2 to Cura 3.3.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json b/plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json index f5ba7235d1..5f83695245 100644 --- a/plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 3.3 to Cura 3.4.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade34to35/plugin.json b/plugins/VersionUpgrade/VersionUpgrade34to35/plugin.json index b73001b683..f95cb7be87 100644 --- a/plugins/VersionUpgrade/VersionUpgrade34to35/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade34to35/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 3.4 to Cura 3.5.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/X3DReader/plugin.json b/plugins/X3DReader/plugin.json index 9ee09e43df..4052ac6605 100644 --- a/plugins/X3DReader/plugin.json +++ b/plugins/X3DReader/plugin.json @@ -3,6 +3,6 @@ "author": "Seva Alekseyev", "version": "0.5.0", "description": "Provides support for reading X3D files.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/XRayView/plugin.json b/plugins/XRayView/plugin.json index 576dec4656..4d8e42bae3 100644 --- a/plugins/XRayView/plugin.json +++ b/plugins/XRayView/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides the X-Ray view.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/XmlMaterialProfile/plugin.json b/plugins/XmlMaterialProfile/plugin.json index 4b2901c375..d172d49f87 100644 --- a/plugins/XmlMaterialProfile/plugin.json +++ b/plugins/XmlMaterialProfile/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Provides capabilities to read and write XML-based material profiles.", - "api": 5, + "api": "6.0", "i18n-catalog": "cura" } From 36ccef420926b0bc38b94c701e5e92405747071d Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 17 Dec 2018 09:08:32 +0100 Subject: [PATCH 0946/1240] Remove duplicate iconColor property in the IconWithText component Probably it was a mistake in a merge conflict --- resources/qml/IconWithText.qml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/resources/qml/IconWithText.qml b/resources/qml/IconWithText.qml index dd522031c1..97983dd7ce 100644 --- a/resources/qml/IconWithText.qml +++ b/resources/qml/IconWithText.qml @@ -19,7 +19,6 @@ Item property alias color: label.color property alias text: label.text property alias font: label.font - property alias iconColor: icon.color property real margin: UM.Theme.getSize("narrow_margin").width // These properties can be used in combination with layouts. @@ -40,7 +39,7 @@ Item width: UM.Theme.getSize("section_icon").width height: width - color: UM.Theme.getColor("icon") + color: label.color anchors { From 328f32615369f0a5e0bd2c4729fb5fe9dfc7c4ae Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 17 Dec 2018 09:15:47 +0100 Subject: [PATCH 0947/1240] Use a specific color for the icons --- resources/qml/IconWithText.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/IconWithText.qml b/resources/qml/IconWithText.qml index 97983dd7ce..24b6dc7fe2 100644 --- a/resources/qml/IconWithText.qml +++ b/resources/qml/IconWithText.qml @@ -39,7 +39,7 @@ Item width: UM.Theme.getSize("section_icon").width height: width - color: label.color + color: UM.Theme.getColor("icon") anchors { From 77deabf6d4d2f6ceee9bb536c53b8516acb08f7c Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Mon, 17 Dec 2018 09:43:24 +0100 Subject: [PATCH 0948/1240] Code style --- .../resources/qml/MonitorStage.qml | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml index 4d59e0eb6b..150bc49254 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml @@ -34,14 +34,18 @@ Component name: "cura" } - LinearGradient { + LinearGradient + { anchors.fill: parent - gradient: Gradient { - GradientStop { + gradient: Gradient + { + GradientStop + { position: 0.0 color: "#f6f6f6" } - GradientStop { + GradientStop + { position: 1.0 color: "#ffffff" } @@ -81,7 +85,8 @@ Component id: queue width: Math.min(834 * screenScaleFactor, maximumWidth) - anchors { + anchors + { bottom: parent.bottom horizontalCenter: parent.horizontalCenter top: printers.bottom @@ -210,7 +215,8 @@ Component ScrollView { id: queuedPrintJobs - anchors { + anchors + { bottom: parent.bottom horizontalCenter: parent.horizontalCenter top: printJobQueueHeadings.bottom @@ -239,7 +245,8 @@ Component } } - PrinterVideoStream { + PrinterVideoStream + { anchors.fill: parent cameraUrl: OutputDevice.activeCameraUrl visible: OutputDevice.activeCameraUrl != "" From 8d6a281070e1fb222324edaa2a99230c347aa540 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 17 Dec 2018 09:58:38 +0100 Subject: [PATCH 0949/1240] Increment SDK version in bundled packages file And increment the version number of every package. Contributes to issue CURA-6019. --- resources/bundled_packages/cura.json | 364 +++++++++++++-------------- 1 file changed, 182 insertions(+), 182 deletions(-) diff --git a/resources/bundled_packages/cura.json b/resources/bundled_packages/cura.json index 384b7d0412..c32b94af3f 100644 --- a/resources/bundled_packages/cura.json +++ b/resources/bundled_packages/cura.json @@ -5,8 +5,8 @@ "package_type": "plugin", "display_name": "3MF Reader", "description": "Provides support for reading 3MF files.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -22,8 +22,8 @@ "package_type": "plugin", "display_name": "3MF Writer", "description": "Provides support for writing 3MF files.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -39,8 +39,8 @@ "package_type": "plugin", "display_name": "Change Log", "description": "Shows changes since latest checked version.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -56,8 +56,8 @@ "package_type": "plugin", "display_name": "CuraEngine Backend", "description": "Provides the link to the CuraEngine slicing backend.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -73,8 +73,8 @@ "package_type": "plugin", "display_name": "Cura Profile Reader", "description": "Provides support for importing Cura profiles.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -90,8 +90,8 @@ "package_type": "plugin", "display_name": "Cura Profile Writer", "description": "Provides support for exporting Cura profiles.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -107,8 +107,8 @@ "package_type": "plugin", "display_name": "Firmware Update Checker", "description": "Checks for firmware updates.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -124,8 +124,8 @@ "package_type": "plugin", "display_name": "Firmware Updater", "description": "Provides a machine actions for updating firmware.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -141,8 +141,8 @@ "package_type": "plugin", "display_name": "Compressed G-code Reader", "description": "Reads g-code from a compressed archive.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -158,8 +158,8 @@ "package_type": "plugin", "display_name": "Compressed G-code Writer", "description": "Writes g-code to a compressed archive.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -175,8 +175,8 @@ "package_type": "plugin", "display_name": "G-Code Profile Reader", "description": "Provides support for importing profiles from g-code files.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -192,8 +192,8 @@ "package_type": "plugin", "display_name": "G-Code Reader", "description": "Allows loading and displaying G-code files.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "VictorLarchenko", @@ -209,8 +209,8 @@ "package_type": "plugin", "display_name": "G-Code Writer", "description": "Writes g-code to a file.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -226,8 +226,8 @@ "package_type": "plugin", "display_name": "Image Reader", "description": "Enables ability to generate printable geometry from 2D image files.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -243,8 +243,8 @@ "package_type": "plugin", "display_name": "Legacy Cura Profile Reader", "description": "Provides support for importing profiles from legacy Cura versions.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -260,8 +260,8 @@ "package_type": "plugin", "display_name": "Machine Settings Action", "description": "Provides a way to change machine settings (such as build volume, nozzle size, etc.).", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "fieldOfView", @@ -277,8 +277,8 @@ "package_type": "plugin", "display_name": "Model Checker", "description": "Checks models and print configuration for possible printing issues and give suggestions.", - "package_version": "0.1.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -294,8 +294,8 @@ "package_type": "plugin", "display_name": "Monitor Stage", "description": "Provides a monitor stage in Cura.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -311,8 +311,8 @@ "package_type": "plugin", "display_name": "Per-Object Settings Tool", "description": "Provides the per-model settings.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -328,8 +328,8 @@ "package_type": "plugin", "display_name": "Post Processing", "description": "Extension that allows for user created scripts for post processing.", - "package_version": "2.2.0", - "sdk_version": 5, + "package_version": "2.2.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -345,8 +345,8 @@ "package_type": "plugin", "display_name": "Prepare Stage", "description": "Provides a prepare stage in Cura.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -362,8 +362,8 @@ "package_type": "plugin", "display_name": "Preview Stage", "description": "Provides a preview stage in Cura.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -379,8 +379,8 @@ "package_type": "plugin", "display_name": "Removable Drive Output Device", "description": "Provides removable drive hotplugging and writing support.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -396,8 +396,8 @@ "package_type": "plugin", "display_name": "Simulation View", "description": "Provides the Simulation view.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -413,8 +413,8 @@ "package_type": "plugin", "display_name": "Slice Info", "description": "Submits anonymous slice info. Can be disabled through preferences.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -430,8 +430,8 @@ "package_type": "plugin", "display_name": "Solid View", "description": "Provides a normal solid mesh view.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -447,8 +447,8 @@ "package_type": "plugin", "display_name": "Support Eraser Tool", "description": "Creates an eraser mesh to block the printing of support in certain places.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -464,8 +464,8 @@ "package_type": "plugin", "display_name": "Toolbox", "description": "Find, manage and install new Cura packages.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -481,8 +481,8 @@ "package_type": "plugin", "display_name": "UFP Writer", "description": "Provides support for writing Ultimaker Format Packages.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -498,8 +498,8 @@ "package_type": "plugin", "display_name": "Ultimaker Machine Actions", "description": "Provides machine actions for Ultimaker machines (such as bed leveling wizard, selecting upgrades, etc.).", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -515,8 +515,8 @@ "package_type": "plugin", "display_name": "UM3 Network Printing", "description": "Manages network connections to Ultimaker 3 printers.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -532,8 +532,8 @@ "package_type": "plugin", "display_name": "USB Printing", "description": "Accepts G-Code and sends them to a printer. Plugin can also update firmware.", - "package_version": "1.0.1", - "sdk_version": 5, + "package_version": "1.0.2", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -549,8 +549,8 @@ "package_type": "plugin", "display_name": "User Agreement", "description": "Ask the user once if he/she agrees with our license.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -566,8 +566,8 @@ "package_type": "plugin", "display_name": "Version Upgrade 2.1 to 2.2", "description": "Upgrades configurations from Cura 2.1 to Cura 2.2.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -583,8 +583,8 @@ "package_type": "plugin", "display_name": "Version Upgrade 2.2 to 2.4", "description": "Upgrades configurations from Cura 2.2 to Cura 2.4.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -600,8 +600,8 @@ "package_type": "plugin", "display_name": "Version Upgrade 2.5 to 2.6", "description": "Upgrades configurations from Cura 2.5 to Cura 2.6.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -617,8 +617,8 @@ "package_type": "plugin", "display_name": "Version Upgrade 2.6 to 2.7", "description": "Upgrades configurations from Cura 2.6 to Cura 2.7.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -634,8 +634,8 @@ "package_type": "plugin", "display_name": "Version Upgrade 2.7 to 3.0", "description": "Upgrades configurations from Cura 2.7 to Cura 3.0.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -651,8 +651,8 @@ "package_type": "plugin", "display_name": "Version Upgrade 3.0 to 3.1", "description": "Upgrades configurations from Cura 3.0 to Cura 3.1.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -668,8 +668,8 @@ "package_type": "plugin", "display_name": "Version Upgrade 3.2 to 3.3", "description": "Upgrades configurations from Cura 3.2 to Cura 3.3.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -685,8 +685,8 @@ "package_type": "plugin", "display_name": "Version Upgrade 3.3 to 3.4", "description": "Upgrades configurations from Cura 3.3 to Cura 3.4.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -702,8 +702,8 @@ "package_type": "plugin", "display_name": "Version Upgrade 3.4 to 3.5", "description": "Upgrades configurations from Cura 3.4 to Cura 3.5.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -719,8 +719,8 @@ "package_type": "plugin", "display_name": "X3D Reader", "description": "Provides support for reading X3D files.", - "package_version": "0.5.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "SevaAlekseyev", @@ -736,8 +736,8 @@ "package_type": "plugin", "display_name": "XML Material Profiles", "description": "Provides capabilities to read and write XML-based material profiles.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -753,8 +753,8 @@ "package_type": "plugin", "display_name": "X-Ray View", "description": "Provides the X-Ray view.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", @@ -770,8 +770,8 @@ "package_type": "material", "display_name": "Generic ABS", "description": "The generic ABS profile which other profiles can be based upon.", - "package_version": "1.2.0", - "sdk_version": 5, + "package_version": "1.2.1", + "sdk_version": "6.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -788,8 +788,8 @@ "package_type": "material", "display_name": "Generic BAM", "description": "The generic BAM profile which other profiles can be based upon.", - "package_version": "1.2.0", - "sdk_version": 5, + "package_version": "1.2.1", + "sdk_version": "6.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -806,8 +806,8 @@ "package_type": "material", "display_name": "Generic CFF CPE", "description": "The generic CFF CPE profile which other profiles can be based upon.", - "package_version": "1.1.0", - "sdk_version": 5, + "package_version": "1.1.1", + "sdk_version": "6.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -824,8 +824,8 @@ "package_type": "material", "display_name": "Generic CFF PA", "description": "The generic CFF PA profile which other profiles can be based upon.", - "package_version": "1.1.0", - "sdk_version": 5, + "package_version": "1.1.1", + "sdk_version": "6.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -842,8 +842,8 @@ "package_type": "material", "display_name": "Generic CPE", "description": "The generic CPE profile which other profiles can be based upon.", - "package_version": "1.2.0", - "sdk_version": 5, + "package_version": "1.2.1", + "sdk_version": "6.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -860,8 +860,8 @@ "package_type": "material", "display_name": "Generic CPE+", "description": "The generic CPE+ profile which other profiles can be based upon.", - "package_version": "1.2.0", - "sdk_version": 5, + "package_version": "1.2.1", + "sdk_version": "6.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -878,8 +878,8 @@ "package_type": "material", "display_name": "Generic GFF CPE", "description": "The generic GFF CPE profile which other profiles can be based upon.", - "package_version": "1.1.0", - "sdk_version": 5, + "package_version": "1.1.1", + "sdk_version": "6.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -896,8 +896,8 @@ "package_type": "material", "display_name": "Generic GFF PA", "description": "The generic GFF PA profile which other profiles can be based upon.", - "package_version": "1.1.0", - "sdk_version": 5, + "package_version": "1.1.1", + "sdk_version": "6.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -914,8 +914,8 @@ "package_type": "material", "display_name": "Generic HIPS", "description": "The generic HIPS profile which other profiles can be based upon.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -932,8 +932,8 @@ "package_type": "material", "display_name": "Generic Nylon", "description": "The generic Nylon profile which other profiles can be based upon.", - "package_version": "1.2.0", - "sdk_version": 5, + "package_version": "1.2.1", + "sdk_version": "6.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -950,8 +950,8 @@ "package_type": "material", "display_name": "Generic PC", "description": "The generic PC profile which other profiles can be based upon.", - "package_version": "1.2.0", - "sdk_version": 5, + "package_version": "1.2.1", + "sdk_version": "6.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -968,8 +968,8 @@ "package_type": "material", "display_name": "Generic PETG", "description": "The generic PETG profile which other profiles can be based upon.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -986,8 +986,8 @@ "package_type": "material", "display_name": "Generic PLA", "description": "The generic PLA profile which other profiles can be based upon.", - "package_version": "1.2.0", - "sdk_version": 5, + "package_version": "1.2.1", + "sdk_version": "6.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1004,8 +1004,8 @@ "package_type": "material", "display_name": "Generic PP", "description": "The generic PP profile which other profiles can be based upon.", - "package_version": "1.2.0", - "sdk_version": 5, + "package_version": "1.2.1", + "sdk_version": "6.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1022,8 +1022,8 @@ "package_type": "material", "display_name": "Generic PVA", "description": "The generic PVA profile which other profiles can be based upon.", - "package_version": "1.2.0", - "sdk_version": 5, + "package_version": "1.2.1", + "sdk_version": "6.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1040,8 +1040,8 @@ "package_type": "material", "display_name": "Generic Tough PLA", "description": "The generic Tough PLA profile which other profiles can be based upon.", - "package_version": "1.0.1", - "sdk_version": 5, + "package_version": "1.0.2", + "sdk_version": "6.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1058,8 +1058,8 @@ "package_type": "material", "display_name": "Generic TPU", "description": "The generic TPU profile which other profiles can be based upon.", - "package_version": "1.2.0", - "sdk_version": 5, + "package_version": "1.2.1", + "sdk_version": "6.0", "website": "https://github.com/Ultimaker/fdm_materials", "author": { "author_id": "Generic", @@ -1076,8 +1076,8 @@ "package_type": "material", "display_name": "Dagoma Chromatik PLA", "description": "Filament testé et approuvé pour les imprimantes 3D Dagoma. Chromatik est l'idéal pour débuter et suivre les tutoriels premiers pas. Il vous offre qualité et résistance pour chacune de vos impressions.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://dagoma.fr/boutique/filaments.html", "author": { "author_id": "Dagoma", @@ -1093,8 +1093,8 @@ "package_type": "material", "display_name": "FABtotum ABS", "description": "This material is easy to be extruded but it is not the simplest to use. It is one of the most used in 3D printing to get very well finished objects. It is not sustainable and its smoke can be dangerous if inhaled. The reason to prefer this filament to PLA is mainly because of its precision and mechanical specs. ABS (for plastic) stands for Acrylonitrile Butadiene Styrene and it is a thermoplastic which is widely used in everyday objects. It can be printed with any FFF 3D printer which can get to high temperatures as it must be extruded in a range between 220° and 245°, so it’s compatible with all versions of the FABtotum Personal fabricator.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://store.fabtotum.com/eu/products/filaments.html?filament_type=40", "author": { "author_id": "FABtotum", @@ -1110,8 +1110,8 @@ "package_type": "material", "display_name": "FABtotum Nylon", "description": "When 3D printing started this material was not listed among the extrudable filaments. It is flexible as well as resistant to tractions. It is well known for its uses in textile but also in industries which require a strong and flexible material. There are different kinds of Nylon: 3D printing mostly uses Nylon 6 and Nylon 6.6, which are the most common. It requires higher temperatures to be printed, so a 3D printer must be able to reach them (around 240°C): the FABtotum, of course, can.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://store.fabtotum.com/eu/products/filaments.html?filament_type=53", "author": { "author_id": "FABtotum", @@ -1127,8 +1127,8 @@ "package_type": "material", "display_name": "FABtotum PLA", "description": "It is the most common filament used for 3D printing. It is studied to be bio-degradable as it comes from corn starch’s sugar mainly. It is completely made of renewable sources and has no footprint on polluting. PLA stands for PolyLactic Acid and it is a thermoplastic that today is still considered the easiest material to be 3D printed. It can be extruded at lower temperatures: the standard range of FABtotum’s one is between 185° and 195°.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://store.fabtotum.com/eu/products/filaments.html?filament_type=39", "author": { "author_id": "FABtotum", @@ -1144,8 +1144,8 @@ "package_type": "material", "display_name": "FABtotum TPU Shore 98A", "description": "", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://store.fabtotum.com/eu/products/filaments.html?filament_type=66", "author": { "author_id": "FABtotum", @@ -1161,8 +1161,8 @@ "package_type": "material", "display_name": "Fiberlogy HD PLA", "description": "With our HD PLA you have many more options. You can use this material in two ways. Choose the one you like best. You can use it as a normal PLA and get prints characterized by a very good adhesion between the layers and high precision. You can also make your prints acquire similar properties to that of ABS – better impact resistance and high temperature resistance. All you need is an oven. Yes, an oven! By annealing our HD PLA in an oven, in accordance with the manual, you will avoid all the inconveniences of printing with ABS, such as unpleasant odour or hazardous fumes.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "http://fiberlogy.com/en/fiberlogy-filaments/filament-hd-pla/", "author": { "author_id": "Fiberlogy", @@ -1178,8 +1178,8 @@ "package_type": "material", "display_name": "Filo3D PLA", "description": "Fast, safe and reliable printing. PLA is ideal for the fast and reliable printing of parts and prototypes with a great surface quality.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://dagoma.fr", "author": { "author_id": "Dagoma", @@ -1195,8 +1195,8 @@ "package_type": "material", "display_name": "IMADE3D JellyBOX PETG", "description": "", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "http://shop.imade3d.com/filament.html", "author": { "author_id": "IMADE3D", @@ -1212,8 +1212,8 @@ "package_type": "material", "display_name": "IMADE3D JellyBOX PLA", "description": "", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "http://shop.imade3d.com/filament.html", "author": { "author_id": "IMADE3D", @@ -1229,8 +1229,8 @@ "package_type": "material", "display_name": "Octofiber PLA", "description": "PLA material from Octofiber.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://nl.octofiber.com/3d-printing-filament/pla.html", "author": { "author_id": "Octofiber", @@ -1246,8 +1246,8 @@ "package_type": "material", "display_name": "PolyFlex™ PLA", "description": "PolyFlex™ is a highly flexible yet easy to print 3D printing material. Featuring good elasticity and a large strain-to- failure, PolyFlex™ opens up a completely new realm of applications.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "http://www.polymaker.com/shop/polyflex/", "author": { "author_id": "Polymaker", @@ -1263,8 +1263,8 @@ "package_type": "material", "display_name": "PolyMax™ PLA", "description": "PolyMax™ PLA is a 3D printing material with excellent mechanical properties and printing quality. PolyMax™ PLA has an impact resistance of up to nine times that of regular PLA, and better overall mechanical properties than ABS.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "http://www.polymaker.com/shop/polymax/", "author": { "author_id": "Polymaker", @@ -1280,8 +1280,8 @@ "package_type": "material", "display_name": "PolyPlus™ PLA True Colour", "description": "PolyPlus™ PLA is a premium PLA designed for all desktop FDM/FFF 3D printers. It is produced with our patented Jam-Free™ technology that ensures consistent extrusion and prevents jams.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "http://www.polymaker.com/shop/polyplus-true-colour/", "author": { "author_id": "Polymaker", @@ -1297,8 +1297,8 @@ "package_type": "material", "display_name": "PolyWood™ PLA", "description": "PolyWood™ is a wood mimic printing material that contains no actual wood ensuring a clean Jam-Free™ printing experience.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "http://www.polymaker.com/shop/polywood/", "author": { "author_id": "Polymaker", @@ -1314,8 +1314,8 @@ "package_type": "material", "display_name": "Ultimaker ABS", "description": "Example package for material and quality profiles for Ultimaker materials.", - "package_version": "1.2.0", - "sdk_version": 5, + "package_version": "1.2.1", + "sdk_version": "6.0", "website": "https://ultimaker.com/products/materials/abs", "author": { "author_id": "UltimakerPackages", @@ -1333,8 +1333,8 @@ "package_type": "material", "display_name": "Ultimaker Breakaway", "description": "Example package for material and quality profiles for Ultimaker materials.", - "package_version": "1.2.0", - "sdk_version": 5, + "package_version": "1.2.1", + "sdk_version": "6.0", "website": "https://ultimaker.com/products/materials/breakaway", "author": { "author_id": "UltimakerPackages", @@ -1352,8 +1352,8 @@ "package_type": "material", "display_name": "Ultimaker CPE", "description": "Example package for material and quality profiles for Ultimaker materials.", - "package_version": "1.2.0", - "sdk_version": 5, + "package_version": "1.2.1", + "sdk_version": "6.0", "website": "https://ultimaker.com/products/materials/abs", "author": { "author_id": "UltimakerPackages", @@ -1371,8 +1371,8 @@ "package_type": "material", "display_name": "Ultimaker CPE+", "description": "Example package for material and quality profiles for Ultimaker materials.", - "package_version": "1.2.0", - "sdk_version": 5, + "package_version": "1.2.1", + "sdk_version": "6.0", "website": "https://ultimaker.com/products/materials/cpe", "author": { "author_id": "UltimakerPackages", @@ -1390,8 +1390,8 @@ "package_type": "material", "display_name": "Ultimaker Nylon", "description": "Example package for material and quality profiles for Ultimaker materials.", - "package_version": "1.2.0", - "sdk_version": 5, + "package_version": "1.2.1", + "sdk_version": "6.0", "website": "https://ultimaker.com/products/materials/abs", "author": { "author_id": "UltimakerPackages", @@ -1409,8 +1409,8 @@ "package_type": "material", "display_name": "Ultimaker PC", "description": "Example package for material and quality profiles for Ultimaker materials.", - "package_version": "1.2.0", - "sdk_version": 5, + "package_version": "1.2.1", + "sdk_version": "6.0", "website": "https://ultimaker.com/products/materials/pc", "author": { "author_id": "UltimakerPackages", @@ -1428,8 +1428,8 @@ "package_type": "material", "display_name": "Ultimaker PLA", "description": "Example package for material and quality profiles for Ultimaker materials.", - "package_version": "1.2.0", - "sdk_version": 5, + "package_version": "1.2.1", + "sdk_version": "6.0", "website": "https://ultimaker.com/products/materials/abs", "author": { "author_id": "UltimakerPackages", @@ -1447,8 +1447,8 @@ "package_type": "material", "display_name": "Ultimaker PP", "description": "Example package for material and quality profiles for Ultimaker materials.", - "package_version": "1.2.0", - "sdk_version": 5, + "package_version": "1.2.1", + "sdk_version": "6.0", "website": "https://ultimaker.com/products/materials/pp", "author": { "author_id": "UltimakerPackages", @@ -1466,8 +1466,8 @@ "package_type": "material", "display_name": "Ultimaker PVA", "description": "Example package for material and quality profiles for Ultimaker materials.", - "package_version": "1.2.0", - "sdk_version": 5, + "package_version": "1.2.1", + "sdk_version": "6.0", "website": "https://ultimaker.com/products/materials/abs", "author": { "author_id": "UltimakerPackages", @@ -1485,8 +1485,8 @@ "package_type": "material", "display_name": "Ultimaker TPU 95A", "description": "Example package for material and quality profiles for Ultimaker materials.", - "package_version": "1.2.0", - "sdk_version": 5, + "package_version": "1.2.1", + "sdk_version": "6.0", "website": "https://ultimaker.com/products/materials/tpu-95a", "author": { "author_id": "UltimakerPackages", @@ -1504,8 +1504,8 @@ "package_type": "material", "display_name": "Ultimaker Tough PLA", "description": "Example package for material and quality profiles for Ultimaker materials.", - "package_version": "1.0.2", - "sdk_version": 5, + "package_version": "1.0.3", + "sdk_version": "6.0", "website": "https://ultimaker.com/products/materials/tough-pla", "author": { "author_id": "UltimakerPackages", @@ -1523,8 +1523,8 @@ "package_type": "material", "display_name": "Vertex Delta ABS", "description": "ABS material and quality files for the Delta Vertex K8800.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://vertex3dprinter.eu", "author": { "author_id": "Velleman", @@ -1540,8 +1540,8 @@ "package_type": "material", "display_name": "Vertex Delta PET", "description": "ABS material and quality files for the Delta Vertex K8800.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://vertex3dprinter.eu", "author": { "author_id": "Velleman", @@ -1557,8 +1557,8 @@ "package_type": "material", "display_name": "Vertex Delta PLA", "description": "ABS material and quality files for the Delta Vertex K8800.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://vertex3dprinter.eu", "author": { "author_id": "Velleman", @@ -1574,8 +1574,8 @@ "package_type": "material", "display_name": "Vertex Delta TPU", "description": "ABS material and quality files for the Delta Vertex K8800.", - "package_version": "1.0.0", - "sdk_version": 5, + "package_version": "1.0.1", + "sdk_version": "6.0", "website": "https://vertex3dprinter.eu", "author": { "author_id": "Velleman", From ff1a0e30f6f9dbebad72e90079df1ee23c42c437 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Mon, 17 Dec 2018 10:42:41 +0100 Subject: [PATCH 0950/1240] Code style & comments --- plugins/MonitorStage/MonitorMain.qml | 2 +- plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/MonitorStage/MonitorMain.qml b/plugins/MonitorStage/MonitorMain.qml index 1f287fc0fa..8f113735ee 100644 --- a/plugins/MonitorStage/MonitorMain.qml +++ b/plugins/MonitorStage/MonitorMain.qml @@ -35,6 +35,6 @@ Item property real maximumWidth: parent.width property real maximumHeight: parent.height - sourceComponent: Cura.MachineManager.printerOutputDevices.length > 0 ? Cura.MachineManager.printerOutputDevices[0].monitorItem: null + sourceComponent: Cura.MachineManager.printerOutputDevices.length > 0 ? Cura.MachineManager.printerOutputDevices[0].monitorItem : null } } diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml index 150bc49254..98ca715108 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml @@ -42,12 +42,12 @@ Component GradientStop { position: 0.0 - color: "#f6f6f6" + color: "#f6f6f6" // TODO: Theme! } GradientStop { position: 1.0 - color: "#ffffff" + color: "#ffffff" // TODO: Theme! } } } From cbd8e72bd5061547573bad6c847110268b5ec64a Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 17 Dec 2018 10:42:52 +0100 Subject: [PATCH 0951/1240] Add fixed width to the preview button Doing that, the elide will work. Also increase the size of the panel a bit so it will fit better in other languages. Adding also the tooltip in case the text is too long in other languages. --- resources/qml/ActionPanel/OutputProcessWidget.qml | 2 ++ resources/themes/cura-light/theme.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml index b6aa9289f5..eb6dc5b417 100644 --- a/resources/qml/ActionPanel/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -120,6 +120,8 @@ Column height: UM.Theme.getSize("action_button").height text: catalog.i18nc("@button", "Preview") + tooltip: text + fixedWidthMode: true onClicked: UM.Controller.setActiveStage("PreviewStage") visible: UM.Controller.activeStage != null && UM.Controller.activeStage.stageId != "PreviewStage" diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 2ff014bd31..ea6c92b479 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -361,7 +361,7 @@ "configuration_selector": [35.0, 4.0], "configuration_selector_mode_tabs": [0.0, 3.0], - "action_panel_widget": [25.0, 0.0], + "action_panel_widget": [26.0, 0.0], "action_panel_information_widget": [20.0, 0.0], "machine_selector_widget": [20.0, 4.0], From 938287095f3c2705b97df881feba30924733f291 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 17 Dec 2018 10:47:14 +0100 Subject: [PATCH 0952/1240] Use connection type instead of um_network_key to see if a printer has a network connection CURA-6011 --- cura/Settings/MachineManager.py | 9 ++++++++- .../qml/Menus/ConfigurationMenu/ConfigurationMenu.qml | 2 +- resources/qml/MonitorSidebar.qml | 4 ++-- resources/qml/PrinterSelector/MachineSelector.qml | 2 +- resources/qml/PrinterSelector/MachineSelectorList.qml | 1 + 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index c375ce01d1..342f53aac6 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -23,7 +23,7 @@ from UM.Settings.SettingFunction import SettingFunction from UM.Signal import postponeSignals, CompressTechnique from cura.Machines.QualityManager import getMachineDefinitionIDForQualitySearch -from cura.PrinterOutputDevice import PrinterOutputDevice +from cura.PrinterOutputDevice import PrinterOutputDevice, ConnectionType from cura.PrinterOutput.ConfigurationModel import ConfigurationModel from cura.PrinterOutput.ExtruderConfigurationModel import ExtruderConfigurationModel from cura.PrinterOutput.MaterialOutputModel import MaterialOutputModel @@ -521,6 +521,13 @@ class MachineManager(QObject): def printerConnected(self): return bool(self._printer_output_devices) + @pyqtProperty(bool, notify=printerConnectedStatusChanged) + def activeMachineHasRemoteConnection(self) -> bool: + if self._global_container_stack: + connection_type = self._global_container_stack.getMetaDataEntry("connection_type") + return connection_type in [ConnectionType.NetworkConnection, ConnectionType.CloudConnection] + return False + @pyqtProperty(str, notify = printerConnectedStatusChanged) def activeMachineNetworkKey(self) -> str: if self._global_container_stack: diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index 33a317b42b..8e9c276c0d 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -134,7 +134,7 @@ Cura.ExpandablePopup property bool is_connected: false //If current machine is connected to a printer. Only evaluated upon making popup visible. onVisibleChanged: { - is_connected = Cura.MachineManager.activeMachineNetworkKey !== "" && Cura.MachineManager.printerConnected //Re-evaluate. + is_connected = Cura.MachineManager.activeMachineHasRemoteConnection && Cura.MachineManager.printerConnected //Re-evaluate. } property int configuration_method: is_connected ? ConfigurationMenu.ConfigurationMethod.Auto : ConfigurationMenu.ConfigurationMethod.Custom //Auto if connected to a printer at start-up, or Custom if not. diff --git a/resources/qml/MonitorSidebar.qml b/resources/qml/MonitorSidebar.qml index 669bdbfb8f..70ee60377d 100644 --- a/resources/qml/MonitorSidebar.qml +++ b/resources/qml/MonitorSidebar.qml @@ -21,7 +21,7 @@ Rectangle property bool hideView: Cura.MachineManager.activeMachineName == "" // Is there an output device for this printer? - property bool isNetworkPrinter: Cura.MachineManager.activeMachineNetworkKey != "" + property bool isNetworkPrinter: Cura.MachineManager.activeMachineHasRemoteConnection property bool printerConnected: Cura.MachineManager.printerConnected property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands property var connectedPrinter: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null @@ -205,7 +205,7 @@ Rectangle target: Cura.MachineManager onGlobalContainerChanged: { - base.isNetworkPrinter = Cura.MachineManager.activeMachineNetworkKey != "" + base.isNetworkPrinter = Cura.MachineManager.activeMachineHasRemoteConnection base.printerConnected = Cura.MachineManager.printerOutputDevices.length != 0 } } diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index 6e120e89c7..8a7b87f409 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -11,7 +11,7 @@ Cura.ExpandablePopup { id: machineSelector - property bool isNetworkPrinter: Cura.MachineManager.activeMachineNetworkKey != "" + property bool isNetworkPrinter: Cura.MachineManager.activeMachineHasRemoteConnection property bool isPrinterConnected: Cura.MachineManager.printerConnected property var outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null diff --git a/resources/qml/PrinterSelector/MachineSelectorList.qml b/resources/qml/PrinterSelector/MachineSelectorList.qml index b157f9a4f6..e0dc6f5e44 100644 --- a/resources/qml/PrinterSelector/MachineSelectorList.qml +++ b/resources/qml/PrinterSelector/MachineSelectorList.qml @@ -30,6 +30,7 @@ ListView { text: model.name width: listView.width + outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null } } /* From ed4d339deec699098ac70971edbd44d3d56a269d Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 17 Dec 2018 11:00:49 +0100 Subject: [PATCH 0953/1240] Change the margin in the view orientation buttons to aling the pixels correctly --- resources/qml/ViewOrientationButton.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/ViewOrientationButton.qml b/resources/qml/ViewOrientationButton.qml index 5371f8549b..5d72de9a8d 100644 --- a/resources/qml/ViewOrientationButton.qml +++ b/resources/qml/ViewOrientationButton.qml @@ -11,5 +11,5 @@ UM.SimpleButton height: UM.Theme.getSize("small_button").height hoverColor: UM.Theme.getColor("small_button_text_hover") color: UM.Theme.getColor("small_button_text") - iconMargin: 0.5 * UM.Theme.getSize("wide_lining").width + iconMargin: UM.Theme.getSize("thick_lining").width } \ No newline at end of file From 9f4b7bd70362a3f7c77ebda0ee77d52ee68ca945 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Mon, 17 Dec 2018 11:28:16 +0100 Subject: [PATCH 0954/1240] STAR-322: Improving logging and cluster connection --- cura/OAuth2/AuthorizationService.py | 3 +- .../src/Cloud/CloudApiClient.py | 29 ++++++++------ .../src/Cloud/CloudOutputDevice.py | 40 +++++++++++-------- .../src/Cloud/CloudOutputDeviceManager.py | 11 +++-- .../src/ClusterUM3OutputDevice.py | 2 +- .../src/ClusterUM3PrinterOutputController.py | 1 - 6 files changed, 51 insertions(+), 35 deletions(-) diff --git a/cura/OAuth2/AuthorizationService.py b/cura/OAuth2/AuthorizationService.py index 21dbbe8248..0c3074f3d5 100644 --- a/cura/OAuth2/AuthorizationService.py +++ b/cura/OAuth2/AuthorizationService.py @@ -52,7 +52,8 @@ class AuthorizationService: if not self._user_profile: # If no user profile was stored locally, we try to get it from JWT. self._user_profile = self._parseJWT() - if not self._user_profile: + + if not self._user_profile and self._auth_data: # If there is still no user profile from the JWT, we have to log in again. Logger.log("w", "The user profile could not be loaded. The user must log in again!") self.deleteAuthData() diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py index b54c9e97d6..5fd14efc9c 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py @@ -2,6 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. import json from json import JSONDecodeError +from time import time from typing import Callable, List, Type, TypeVar, Union, Optional, Tuple, Dict, Any from PyQt5.QtCore import QUrl @@ -38,6 +39,8 @@ class CloudApiClient: self._account = account self._on_error = on_error self._upload = None # type: Optional[MeshUploader] + # in order to avoid garbage collection we keep the callbacks in this list. + self._anti_gc_callbacks = [] # type: List[Callable[[QNetworkReply], None]] ## Gets the account used for the API. @property @@ -49,8 +52,7 @@ class CloudApiClient: def getClusters(self, on_finished: Callable[[List[CloudClusterResponse]], Any]) -> None: url = "{}/clusters".format(self.CLUSTER_API_ROOT) reply = self._manager.get(self._createEmptyRequest(url)) - callback = self._wrapCallback(reply, on_finished, CloudClusterResponse) - reply.finished.connect(callback) + self._addCallbacks(reply, on_finished, CloudClusterResponse) ## Retrieves the status of the given cluster. # \param cluster_id: The ID of the cluster. @@ -58,8 +60,7 @@ class CloudApiClient: def getClusterStatus(self, cluster_id: str, on_finished: Callable[[CloudClusterStatus], Any]) -> None: url = "{}/clusters/{}/status".format(self.CLUSTER_API_ROOT, cluster_id) reply = self._manager.get(self._createEmptyRequest(url)) - callback = self._wrapCallback(reply, on_finished, CloudClusterStatus) - reply.finished.connect(callback) + self._addCallbacks(reply, on_finished, CloudClusterStatus) ## Requests the cloud to register the upload of a print job mesh. # \param request: The request object. @@ -69,8 +70,7 @@ class CloudApiClient: url = "{}/jobs/upload".format(self.CURA_API_ROOT) body = json.dumps({"data": request.toDict()}) reply = self._manager.put(self._createEmptyRequest(url), body.encode()) - callback = self._wrapCallback(reply, on_finished, CloudPrintJobResponse) - reply.finished.connect(callback) + self._addCallbacks(reply, on_finished, CloudPrintJobResponse) ## Requests the cloud to register the upload of a print job mesh. # \param upload_response: The object received after requesting an upload with `self.requestUpload`. @@ -90,8 +90,7 @@ class CloudApiClient: def requestPrint(self, cluster_id: str, job_id: str, on_finished: Callable[[CloudPrintResponse], Any]) -> None: url = "{}/clusters/{}/print/{}".format(self.CLUSTER_API_ROOT, cluster_id, job_id) reply = self._manager.post(self._createEmptyRequest(url), b"") - callback = self._wrapCallback(reply, on_finished, CloudPrintResponse) - reply.finished.connect(callback) + self._addCallbacks(reply, on_finished, CloudPrintResponse) ## We override _createEmptyRequest in order to add the user credentials. # \param url: The URL to request @@ -116,9 +115,10 @@ class CloudApiClient: Logger.log("i", "Received a reply %s from %s with %s", status_code, reply.url().toString(), response) return status_code, json.loads(response) except (UnicodeDecodeError, JSONDecodeError, ValueError) as err: - error = {"code": type(err).__name__, "title": str(err), "http_code": str(status_code)} + error = CloudErrorObject(code=type(err).__name__, title=str(err), http_code=str(status_code), + id=str(time()), http_status="500") Logger.logException("e", "Could not parse the stardust response: %s", error) - return status_code, {"errors": [error]} + return status_code, {"errors": [error.toDict()]} ## The generic type variable used to document the methods below. Model = TypeVar("Model", bound=BaseModel) @@ -143,12 +143,15 @@ class CloudApiClient: # \param on_finished: The callback in case the response is successful. # \param model: The type of the model to convert the response to. It may either be a single record or a list. # \return: A function that can be passed to the - def _wrapCallback(self, + def _addCallbacks(self, reply: QNetworkReply, on_finished: Callable[[Union[Model, List[Model]]], Any], model: Type[Model], - ) -> Callable[[QNetworkReply], None]: + ) -> None: def parse() -> None: status_code, response = self._parseReply(reply) + self._anti_gc_callbacks.remove(parse) return self._parseModels(response, on_finished, model) - return parse + + self._anti_gc_callbacks.append(parse) + reply.finished.connect(parse) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 88c2f8da1d..3890e7cee2 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -18,6 +18,7 @@ from cura.CuraApplication import CuraApplication from cura.PrinterOutput.NetworkedPrinterOutputDevice import AuthState, NetworkedPrinterOutputDevice from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputController import CloudOutputController +from src.Cloud.Models.CloudClusterResponse import CloudClusterResponse from ..MeshFormatHandler import MeshFormatHandler from ..UM3PrintJobOutputModel import UM3PrintJobOutputModel from .CloudProgressMessage import CloudProgressMessage @@ -84,18 +85,15 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # \param api_client: The client that will run the API calls # \param device_id: The ID of the device (i.e. the cluster_id for the cloud API) # \param parent: The optional parent of this output device. - def __init__(self, api_client: CloudApiClient, device_id: str, host_name: str, parent: QObject = None) -> None: - super().__init__(device_id = device_id, address = "", properties = {}, parent = parent) + def __init__(self, api_client: CloudApiClient, cluster: CloudClusterResponse, parent: QObject = None) -> None: + super().__init__(device_id = cluster.cluster_id, address = "", properties = {}, parent = parent) self._api = api_client - self._host_name = host_name + self._cluster = cluster self._setInterfaceElements() - self._device_id = device_id self._account = api_client.account - CuraApplication.getInstance().getBackend().backendStateChange.connect(self._onBackendStateChange) - # We use the Cura Connect monitor tab to get most functionality right away. self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../resources/qml/MonitorStage.qml") @@ -124,7 +122,14 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self._mesh = None # type: Optional[bytes] self._uploaded_print_job = None # type: Optional[CloudPrintJobResponse] + def connect(self) -> None: + super().connect() + Logger.log("i", "Connected to cluster %s", self.key) + CuraApplication.getInstance().getBackend().backendStateChange.connect(self._onBackendStateChange) + def disconnect(self) -> None: + super().disconnect() + Logger.log("i", "Disconnected to cluster %s", self.key) CuraApplication.getInstance().getBackend().backendStateChange.disconnect(self._onBackendStateChange) def _onBackendStateChange(self, _: BackendState) -> None: @@ -133,19 +138,19 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Gets the host name of this device @property - def host_name(self) -> str: - return self._host_name + def clusterData(self) -> CloudClusterResponse: + return self._cluster ## Updates the host name of the output device - @host_name.setter - def host_name(self, value: str) -> None: - self._host_name = value + @clusterData.setter + def clusterData(self, value: CloudClusterResponse) -> None: + self._cluster = value ## Checks whether the given network key is found in the cloud's host name def matchesNetworkKey(self, network_key: str) -> bool: # A network key looks like "ultimakersystem-aabbccdd0011._ultimaker._tcp.local." # the host name should then be "ultimakersystem-aabbccdd0011" - return network_key.startswith(self._host_name) + return network_key.startswith(self.clusterData.host_name) ## Set all the interface elements and texts for this output device. def _setInterfaceElements(self) -> None: @@ -170,7 +175,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): if self._uploaded_print_job: # the mesh didn't change, let's not upload it again - self._api.requestPrint(self._device_id, self._uploaded_print_job.job_id, self._onPrintRequested) + self._api.requestPrint(self.key, self._uploaded_print_job.job_id, self._onPrintRequested) return # Indicate we have started sending a job. @@ -194,12 +199,15 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Called when the network data should be updated. def _update(self) -> None: super()._update() - if self._last_response_time and time() - self._last_response_time < self.CHECK_CLUSTER_INTERVAL: + if self._last_request_time and time() - self._last_request_time < self.CHECK_CLUSTER_INTERVAL: + Logger.log("i", "Not updating: %s - %s < %s", time(), self._last_request_time, self.CHECK_CLUSTER_INTERVAL) return # avoid calling the cloud too often + Logger.log("i", "Updating: %s - %s >= %s", time(), self._last_request_time, self.CHECK_CLUSTER_INTERVAL) if self._account.isLoggedIn: self.setAuthenticationState(AuthState.Authenticated) - self._api.getClusterStatus(self._device_id, self._onStatusCallFinished) + self._last_request_time = time() + self._api.getClusterStatus(self.key, self._onStatusCallFinished) else: self.setAuthenticationState(AuthState.NotAuthenticated) @@ -315,7 +323,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Requests the print to be sent to the printer when we finished uploading the mesh. def _onPrintJobUploaded(self) -> None: self._progress.update(100) - self._api.requestPrint(self._device_id, self._uploaded_print_job.job_id, self._onPrintRequested) + self._api.requestPrint(self.key, self._uploaded_print_job.job_id, self._onPrintRequested) ## Displays the given message if uploading the mesh has failed # \param message: The message to display. diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index af80907f01..29c60fd14a 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -73,7 +73,8 @@ class CloudOutputDeviceManager: removed_devices, added_clusters, updates = findChanges(self._remote_clusters, online_clusters) - Logger.log("i", "Parsed remote clusters to %s", online_clusters) + Logger.log("i", "Parsed remote clusters to %s", [cluster.toDict() for cluster in online_clusters.values()]) + Logger.log("i", "Removed: %s, added: %s, updates: %s", len(removed_devices), len(added_clusters), len(updates)) # Remove output devices that are gone for removed_cluster in removed_devices: @@ -86,12 +87,12 @@ class CloudOutputDeviceManager: # Add an output device for each new remote cluster. # We only add when is_online as we don't want the option in the drop down if the cluster is not online. for added_cluster in added_clusters: - device = CloudOutputDevice(self._api, added_cluster.cluster_id, added_cluster.host_name) + device = CloudOutputDevice(self._api, added_cluster) self._output_device_manager.addOutputDevice(device) self._remote_clusters[added_cluster.cluster_id] = device for device, cluster in updates: - device.host_name = cluster.host_name + device.clusterData = cluster self._connectToActiveMachine() @@ -99,6 +100,7 @@ class CloudOutputDeviceManager: def _connectToActiveMachine(self) -> None: active_machine = CuraApplication.getInstance().getGlobalContainerStack() if not active_machine: + Logger.log("i", "no active machine") return # Check if the stored cluster_id for the active machine is in our list of remote clusters. @@ -107,6 +109,7 @@ class CloudOutputDeviceManager: device = self._remote_clusters[stored_cluster_id] if not device.isConnected(): device.connect() + Logger.log("i", "Device connected by metadata %s", stored_cluster_id) else: self._connectByNetworkKey(active_machine) @@ -122,6 +125,8 @@ class CloudOutputDeviceManager: active_machine.setMetaDataEntry(self.META_CLUSTER_ID, device.key) device.connect() + Logger.log("i", "Found cluster %s with network key %s", device, local_network_key) + ## Handles an API error received from the cloud. # \param errors: The errors received def _onApiError(self, errors: List[CloudErrorObject]) -> None: diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index 96fee0d96d..1896ffac9c 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -390,10 +390,10 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): ## Called when the connection to the cluster changes. def connect(self) -> None: - pass # TODO: uncomment this once cloud implementation works for testing # super().connect() # self.sendMaterialProfiles() + pass def _onGetPreviewImageFinished(self, reply: QNetworkReply) -> None: reply_url = reply.url().toString() diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3PrinterOutputController.py b/plugins/UM3NetworkPrinting/src/ClusterUM3PrinterOutputController.py index fcced0b883..fc6798386a 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3PrinterOutputController.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3PrinterOutputController.py @@ -18,4 +18,3 @@ class ClusterUM3PrinterOutputController(PrinterOutputController): def setJobState(self, job: "PrintJobOutputModel", state: str): data = "{\"action\": \"%s\"}" % state self._output_device.put("print_jobs/%s/action" % job.key, data, on_finished=None) - From 6178f9ddf62e193865558f7ac8e4e2975c57a316 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Mon, 17 Dec 2018 11:49:09 +0100 Subject: [PATCH 0955/1240] Improve code style and line lengths --- .../resources/qml/UM3InfoComponents.qml | 40 +++++++++++++++++-- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/UM3InfoComponents.qml b/plugins/UM3NetworkPrinting/resources/qml/UM3InfoComponents.qml index 42e3b7d160..320201e165 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/UM3InfoComponents.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/UM3InfoComponents.qml @@ -13,8 +13,40 @@ Item { property string activeQualityDefinitionId: Cura.MachineManager.activeQualityDefinitionId; property bool isUM3: activeQualityDefinitionId == "ultimaker3" || activeQualityDefinitionId.match("ultimaker_") != null; property bool printerConnected: Cura.MachineManager.printerConnected; - property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands; - property bool authenticationRequested: printerConnected && (Cura.MachineManager.printerOutputDevices[0].authenticationState == 2 || Cura.MachineManager.printerOutputDevices[0].authenticationState == 5); // AuthState.AuthenticationRequested or AuthenticationReceived. + property bool printerAcceptsCommands: + { + if (printerConnected && Cura.MachineManager.printerOutputDevices[0]) + { + return Cura.MachineManager.printerOutputDevices[0].acceptsCommands + } + return false + } + property bool authenticationRequested: + { + if (printerConnected && Cura.MachineManager.printerOutputDevices[0]) + { + var device = Cura.MachineManager.printerOutputDevices[0] + // AuthState.AuthenticationRequested or AuthState.AuthenticationReceived + return device.authenticationState == 2 || device.authenticationState == 5 + } + return false + } + property var materialNames: + { + if (printerConnected && Cura.MachineManager.printerOutputDevices[0]) + { + return Cura.MachineManager.printerOutputDevices[0].materialNames + } + return null + } + property var hotendIds: + { + if (printerConnected && Cura.MachineManager.printerOutputDevices[0]) + { + return Cura.MachineManager.printerOutputDevices[0].hotendIds + } + return null + } UM.I18nCatalog { id: catalog; @@ -94,7 +126,7 @@ Item { Column { Repeater { id: nozzleColumn; - model: printerConnected ? Cura.MachineManager.printerOutputDevices[0].hotendIds : null; + model: hotendIds Label { text: nozzleColumn.model[index]; @@ -105,7 +137,7 @@ Item { Column { Repeater { id: materialColumn; - model: printerConnected ? Cura.MachineManager.printerOutputDevices[0].materialNames : null; + model: materialNames Label { text: materialColumn.model[index]; From 9eb743bcb8b1619769f0c6679bb3378b9c813231 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Mon, 17 Dec 2018 12:01:10 +0100 Subject: [PATCH 0956/1240] STAR-322: Making mypy happy --- cura/NetworkClient.py | 57 +++++++++++-------- cura/Settings/SimpleModeSettingsManager.py | 5 +- .../src/Cloud/CloudApiClient.py | 16 ++++-- .../src/Cloud/CloudOutputDevice.py | 13 +++-- .../src/Cloud/MeshUploader.py | 19 ++++--- 5 files changed, 64 insertions(+), 46 deletions(-) diff --git a/cura/NetworkClient.py b/cura/NetworkClient.py index 878158542a..fabaaed95e 100644 --- a/cura/NetworkClient.py +++ b/cura/NetworkClient.py @@ -1,7 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. from time import time -from typing import Optional, Dict, Callable, List, Union +from typing import Optional, Dict, Callable, List, Union, cast from PyQt5.QtCore import QUrl from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply, QHttpMultiPart, QNetworkRequest, QHttpPart, \ @@ -19,7 +19,7 @@ class NetworkClient: super().__init__() # Network manager instance to use for this client. - self._manager = None # type: Optional[QNetworkAccessManager] + self.__manager = None # type: Optional[QNetworkAccessManager] # Timings. self._last_manager_create_time = None # type: Optional[float] @@ -34,20 +34,26 @@ class NetworkClient: # HTTP which uses them. We hold references to these QHttpMultiPart objects here. self._kept_alive_multiparts = {} # type: Dict[QNetworkReply, QHttpMultiPart] + # in order to avoid garbage collection we keep the callbacks in this list. + self._anti_gc_callbacks = [] # type: List[Callable[[], None]] + ## Creates a network manager if needed, with all the required properties and event bindings. def start(self) -> None: - if self._manager: - return - self._manager = QNetworkAccessManager() - self._last_manager_create_time = time() - self._manager.authenticationRequired.connect(self._onAuthenticationRequired) + if not self.__manager: + self.__manager = QNetworkAccessManager() + self._last_manager_create_time = time() + self.__manager.authenticationRequired.connect(self._onAuthenticationRequired) ## Destroys the network manager and event bindings. def stop(self) -> None: - if not self._manager: - return - self._manager.authenticationRequired.disconnect(self._onAuthenticationRequired) - self._manager = None + if self.__manager: + self.__manager.authenticationRequired.disconnect(self._onAuthenticationRequired) + self.__manager = None + + @property + def _manager(self) -> QNetworkAccessManager: + self.start() + return cast(QNetworkAccessManager, self.__manager) ## Create a new empty network request. # Automatically adds the required HTTP headers. @@ -94,13 +100,13 @@ class NetworkClient: return self._createFormPart(content_header, data, content_type) ## Sends a put request to the given path. - # url: The path after the API prefix. - # data: The data to be sent in the body - # content_type: The content type of the body data. - # on_finished: The function to call when the response is received. - # on_progress: The function to call when the progress changes. Parameters are bytes_sent / bytes_total. - def put(self, url: str, data: Union[str, bytes], content_type: Optional[str] = None, - on_finished: Optional[Callable[[QNetworkReply], None]] = None, + # \param url: The path after the API prefix. + # \param data: The data to be sent in the body + # \param content_type: The content type of the body data. + # \param on_finished: The function to call when the response is received. + # \param on_progress: The function to call when the progress changes. Parameters are bytes_sent / bytes_total. + def put(self, url: str, data: Union[str, bytes], content_type: str, + on_finished: Callable[[QNetworkReply], None], on_progress: Optional[Callable[[int, int], None]] = None) -> None: request = self._createEmptyRequest(url, content_type = content_type) @@ -114,7 +120,7 @@ class NetworkClient: ## Sends a delete request to the given path. # url: The path after the API prefix. # on_finished: The function to be call when the response is received. - def delete(self, url: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: + def delete(self, url: str, on_finished: Callable[[QNetworkReply], None]) -> None: request = self._createEmptyRequest(url) reply = self._manager.deleteResource(request) callback = self._createCallback(reply, on_finished) @@ -123,7 +129,7 @@ class NetworkClient: ## Sends a get request to the given path. # \param url: The path after the API prefix. # \param on_finished: The function to be call when the response is received. - def get(self, url: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: + def get(self, url: str, on_finished: Callable[[QNetworkReply], None]) -> None: request = self._createEmptyRequest(url) reply = self._manager.get(request) callback = self._createCallback(reply, on_finished) @@ -135,7 +141,7 @@ class NetworkClient: # \param on_finished: The function to call when the response is received. # \param on_progress: The function to call when the progress changes. Parameters are bytes_sent / bytes_total. def post(self, url: str, data: Union[str, bytes], - on_finished: Optional[Callable[[QNetworkReply], None]], + on_finished: Callable[[QNetworkReply], None], on_progress: Optional[Callable[[int, int], None]] = None) -> None: request = self._createEmptyRequest(url) @@ -180,6 +186,9 @@ class NetworkClient: return reply - @staticmethod - def _createCallback(reply: QNetworkReply, on_finished: Optional[Callable[[QNetworkReply], None]] = None): - return lambda: on_finished(reply) + def _createCallback(self, reply: QNetworkReply, on_finished: Callable[[QNetworkReply], None]) -> Callable[[], None]: + def callback(): + on_finished(reply) + self._anti_gc_callbacks.remove(callback) + self._anti_gc_callbacks.append(callback) + return callback diff --git a/cura/Settings/SimpleModeSettingsManager.py b/cura/Settings/SimpleModeSettingsManager.py index 210a5794d4..b22aea15ea 100644 --- a/cura/Settings/SimpleModeSettingsManager.py +++ b/cura/Settings/SimpleModeSettingsManager.py @@ -1,5 +1,6 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from typing import Set from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty, pyqtSlot @@ -63,10 +64,10 @@ class SimpleModeSettingsManager(QObject): @pyqtSlot() def updateIsProfileUserCreated(self) -> None: - quality_changes_keys = set() + quality_changes_keys = set() # type: Set[str] if not self._machine_manager.activeMachine: - return False + return global_stack = self._machine_manager.activeMachine diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py index 5fd14efc9c..d58def4545 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py @@ -3,7 +3,7 @@ import json from json import JSONDecodeError from time import time -from typing import Callable, List, Type, TypeVar, Union, Optional, Tuple, Dict, Any +from typing import Callable, List, Type, TypeVar, Union, Optional, Tuple, Dict, Any, cast from PyQt5.QtCore import QUrl from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply, QNetworkAccessManager @@ -40,7 +40,7 @@ class CloudApiClient: self._on_error = on_error self._upload = None # type: Optional[MeshUploader] # in order to avoid garbage collection we keep the callbacks in this list. - self._anti_gc_callbacks = [] # type: List[Callable[[QNetworkReply], None]] + self._anti_gc_callbacks = [] # type: List[Callable[[], None]] ## Gets the account used for the API. @property @@ -128,12 +128,16 @@ class CloudApiClient: # \param on_finished: The callback in case the response is successful. # \param model_class: The type of the model to convert the response to. It may either be a single record or a list. def _parseModels(self, response: Dict[str, Any], - on_finished: Callable[[Union[Model, List[Model]]], Any], + on_finished: Union[Callable[[Model], Any], Callable[[List[Model]], Any]], model_class: Type[Model]) -> None: if "data" in response: data = response["data"] - result = [model_class(**c) for c in data] if isinstance(data, list) else model_class(**data) - on_finished(result) + if isinstance(data, list): + results = [model_class(**c) for c in data] # type: List[CloudApiClient.Model] + cast(Callable[[List[CloudApiClient.Model]], Any], on_finished)(results) + else: + result = model_class(**data) # type: CloudApiClient.Model + cast(Callable[[CloudApiClient.Model], Any], on_finished)(result) elif "errors" in response: self._on_error([CloudErrorObject(**error) for error in response["errors"]]) else: @@ -145,7 +149,7 @@ class CloudApiClient: # \return: A function that can be passed to the def _addCallbacks(self, reply: QNetworkReply, - on_finished: Callable[[Union[Model, List[Model]]], Any], + on_finished: Union[Callable[[Model], Any], Callable[[List[Model]], Any]], model: Type[Model], ) -> None: def parse() -> None: diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 3890e7cee2..54f0dc20b6 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -3,7 +3,7 @@ import os from time import time -from typing import Dict, List, Optional, Set +from typing import Dict, List, Optional, Set, cast from PyQt5.QtCore import QObject, QUrl, pyqtProperty, pyqtSignal, pyqtSlot @@ -161,7 +161,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self.setConnectionText(T.CONNECTED_VIA_CLOUD) ## Called when Cura requests an output device to receive a (G-code) file. - def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mime_types: bool = False, + def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False, file_handler: Optional[FileHandler] = None, **kwargs: str) -> None: # Show an error message if we're already sending a job. @@ -190,7 +190,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self._mesh = mesh request = CloudPrintJobUploadRequest( - job_name = file_name, + job_name = file_name or mesh_format.file_extension, file_size = len(mesh), content_type = mesh_format.mime_type, ) @@ -317,13 +317,14 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def _onPrintJobCreated(self, job_response: CloudPrintJobResponse) -> None: self._progress.show() self._uploaded_print_job = job_response - self._api.uploadMesh(job_response, self._mesh, self._onPrintJobUploaded, self._progress.update, - self._onUploadError) + mesh = cast(bytes, self._mesh) + self._api.uploadMesh(job_response, mesh, self._onPrintJobUploaded, self._progress.update, self._onUploadError) ## Requests the print to be sent to the printer when we finished uploading the mesh. def _onPrintJobUploaded(self) -> None: self._progress.update(100) - self._api.requestPrint(self.key, self._uploaded_print_job.job_id, self._onPrintRequested) + print_job = cast(CloudPrintJobResponse, self._uploaded_print_job) + self._api.requestPrint(self.key, print_job.job_id, self._onPrintRequested) ## Displays the given message if uploading the mesh has failed # \param message: The message to display. diff --git a/plugins/UM3NetworkPrinting/src/Cloud/MeshUploader.py b/plugins/UM3NetworkPrinting/src/Cloud/MeshUploader.py index 4f0d6f2e81..f0360b83e3 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/MeshUploader.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/MeshUploader.py @@ -3,7 +3,7 @@ # -*- coding: utf-8 -*- from PyQt5.QtCore import QUrl from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply, QNetworkAccessManager -from typing import Optional, Callable, Any, Tuple +from typing import Optional, Callable, Any, Tuple, cast from UM.Logger import Logger from src.Cloud.Models.CloudPrintJobResponse import CloudPrintJobResponse @@ -20,7 +20,8 @@ class MeshUploader: # \param http_method: The HTTP method to be used, e.g. "POST" or "PUT". # \param timeout: The timeout for each chunk upload. Important: If None, no timeout is applied at all. def __init__(self, manager: QNetworkAccessManager, print_job: CloudPrintJobResponse, data: bytes, - on_finished: Callable[[], Any], on_progress: Callable[[int], Any], on_error: Callable[[], Any]): + on_finished: Callable[[], Any], on_progress: Callable[[int], Any], on_error: Callable[[], Any] + ) -> None: self._manager = manager self._print_job = print_job self._data = data @@ -85,20 +86,22 @@ class MeshUploader: self._on_progress(int((self._sent_bytes + bytes_sent) / len(self._data) * 100)) def _errorCallback(self) -> None: - body = bytes(self._reply.readAll()).decode() + reply = cast(QNetworkReply, self._reply) + body = bytes(reply.readAll()).decode() Logger.log("e", "Received error while uploading: %s", body) self.stop() self._on_error() def _finishedCallback(self) -> None: + reply = cast(QNetworkReply, self._reply) Logger.log("i", "Finished callback %s %s", - self._reply.attribute(QNetworkRequest.HttpStatusCodeAttribute), self._reply.url().toString()) + reply.attribute(QNetworkRequest.HttpStatusCodeAttribute), reply.url().toString()) - status_code = self._reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) + status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) if self._retries < self.MAX_RETRIES and status_code in self.RETRY_HTTP_CODES: self._retries += 1 - Logger.log("i", "Retrying %s/%s request %s", self._retries, self.MAX_RETRIES, self._reply.url().toString()) + Logger.log("i", "Retrying %s/%s request %s", self._retries, self.MAX_RETRIES, reply.url().toString()) self._uploadChunk() return @@ -106,9 +109,9 @@ class MeshUploader: self._errorCallback() return - body = bytes(self._reply.readAll()).decode() + body = bytes(reply.readAll()).decode() Logger.log("w", "status_code: %s, Headers: %s, body: %s", status_code, - [bytes(header).decode() for header in self._reply.rawHeaderList()], body) + [bytes(header).decode() for header in reply.rawHeaderList()], body) first_byte, last_byte = self._chunkRange() self._sent_bytes += last_byte - first_byte From 87c1392173ce05785f9e4c981bb1cae86dcbacce Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Mon, 17 Dec 2018 12:05:25 +0100 Subject: [PATCH 0957/1240] STAR-322: Fixing tests --- .../tests/Cloud/TestCloudOutputDevice.py | 10 +++++++--- .../tests/Cloud/TestCloudOutputDeviceManager.py | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py index d31f59f85a..c42a208ece 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py @@ -10,6 +10,7 @@ from cura.CuraApplication import CuraApplication from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from src.Cloud.CloudApiClient import CloudApiClient from src.Cloud.CloudOutputDevice import CloudOutputDevice +from src.Cloud.Models.CloudClusterResponse import CloudClusterResponse from tests.Cloud.Fixtures import readFixture, parseFixture from .NetworkManagerMock import NetworkManagerMock @@ -18,6 +19,7 @@ class TestCloudOutputDevice(TestCase): CLUSTER_ID = "RIZ6cZbWA_Ua7RZVJhrdVfVpf0z-MqaSHQE4v8aRTtYq" JOB_ID = "ABCDefGHIjKlMNOpQrSTUvYxWZ0-1234567890abcDE=" HOST_NAME = "ultimakersystem-ccbdd30044ec" + HOST_GUID = "e90ae0ac-1257-4403-91ee-a44c9b7e8050" BASE_URL = "https://api-staging.ultimaker.com" STATUS_URL = "{}/connect/v1/clusters/{}/status".format(BASE_URL, CLUSTER_ID) @@ -29,13 +31,15 @@ class TestCloudOutputDevice(TestCase): self.app = CuraApplication.getInstance() self.backend = MagicMock(backendStateChange = Signal()) self.app.setBackend(self.backend) + self.cluster = CloudClusterResponse(self.CLUSTER_ID, self.HOST_GUID, self.HOST_NAME, is_online=True, + status="active") self.network = NetworkManagerMock() self.account = MagicMock(isLoggedIn=True, accessToken="TestAccessToken") self.onError = MagicMock() with patch("src.Cloud.CloudApiClient.QNetworkAccessManager", return_value = self.network): self._api = CloudApiClient(self.account, self.onError) - self.device = CloudOutputDevice(self._api, self.CLUSTER_ID, self.HOST_NAME) + self.device = CloudOutputDevice(self._api, self.cluster) self.cluster_status = parseFixture("getClusterStatusResponse") self.network.prepareReply("GET", self.STATUS_URL, 200, readFixture("getClusterStatusResponse")) @@ -81,7 +85,7 @@ class TestCloudOutputDevice(TestCase): self.cluster_status["data"]["print_jobs"].clear() self.network.prepareReply("GET", self.STATUS_URL, 200, self.cluster_status) - self.device._last_response_time = None + self.device._last_request_time = None self.device._update() self.network.flushReplies() self.assertEqual([], self.device.printJobs) @@ -94,7 +98,7 @@ class TestCloudOutputDevice(TestCase): self.cluster_status["data"]["printers"].clear() self.network.prepareReply("GET", self.STATUS_URL, 200, self.cluster_status) - self.device._last_response_time = None + self.device._last_request_time = None self.device._update() self.network.flushReplies() self.assertEqual([], self.device.printers) diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py index 80dd2c7990..f98e954274 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py @@ -41,7 +41,7 @@ class TestCloudOutputDeviceManager(TestCase): clusters = self.clusters_response.get("data", []) self.assertEqual([CloudOutputDevice] * len(clusters), [type(d) for d in devices]) self.assertEqual({cluster["cluster_id"] for cluster in clusters}, {device.key for device in devices}) - self.assertEqual({cluster["host_name"] for cluster in clusters}, {device.host_name for device in devices}) + self.assertEqual(clusters, [device.clusterData.toDict() for device in devices]) for device in clusters: device_manager.getOutputDevice(device["cluster_id"]).close() From ab83af3a0353bc308a5d417ebbaa7d0bc24b446a Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 17 Dec 2018 12:20:30 +0100 Subject: [PATCH 0958/1240] Fix code-style --- cura/Settings/SimpleModeSettingsManager.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cura/Settings/SimpleModeSettingsManager.py b/cura/Settings/SimpleModeSettingsManager.py index 210a5794d4..b22aea15ea 100644 --- a/cura/Settings/SimpleModeSettingsManager.py +++ b/cura/Settings/SimpleModeSettingsManager.py @@ -1,5 +1,6 @@ # Copyright (c) 2017 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from typing import Set from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty, pyqtSlot @@ -63,10 +64,10 @@ class SimpleModeSettingsManager(QObject): @pyqtSlot() def updateIsProfileUserCreated(self) -> None: - quality_changes_keys = set() + quality_changes_keys = set() # type: Set[str] if not self._machine_manager.activeMachine: - return False + return global_stack = self._machine_manager.activeMachine From 6992fd299159dd7775a3406c5c798f5e00bbda62 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 17 Dec 2018 13:03:17 +0100 Subject: [PATCH 0959/1240] Update plugin versions to match package versions CURA-6019 --- plugins/3MFReader/plugin.json | 2 +- plugins/3MFWriter/plugin.json | 2 +- plugins/ChangeLogPlugin/plugin.json | 2 +- plugins/CuraEngineBackend/plugin.json | 2 +- plugins/CuraProfileReader/plugin.json | 2 +- plugins/CuraProfileWriter/plugin.json | 2 +- plugins/FirmwareUpdateChecker/plugin.json | 2 +- plugins/FirmwareUpdater/plugin.json | 2 +- plugins/GCodeGzReader/plugin.json | 2 +- plugins/GCodeGzWriter/plugin.json | 2 +- plugins/GCodeProfileReader/plugin.json | 2 +- plugins/GCodeReader/plugin.json | 2 +- plugins/GCodeWriter/plugin.json | 2 +- plugins/ImageReader/plugin.json | 2 +- plugins/LegacyProfileReader/plugin.json | 2 +- plugins/MachineSettingsAction/plugin.json | 2 +- plugins/ModelChecker/plugin.json | 2 +- plugins/MonitorStage/plugin.json | 2 +- plugins/PerObjectSettingsTool/plugin.json | 2 +- plugins/PostProcessingPlugin/plugin.json | 2 +- plugins/PrepareStage/plugin.json | 2 +- plugins/PreviewStage/plugin.json | 2 +- plugins/RemovableDriveOutputDevice/plugin.json | 2 +- plugins/SimulationView/plugin.json | 2 +- plugins/SliceInfoPlugin/plugin.json | 2 +- plugins/SolidView/plugin.json | 2 +- plugins/SupportEraser/plugin.json | 2 +- plugins/Toolbox/plugin.json | 2 +- plugins/UFPWriter/plugin.json | 2 +- plugins/UM3NetworkPrinting/plugin.json | 2 +- plugins/USBPrinting/plugin.json | 2 +- plugins/UltimakerMachineActions/plugin.json | 2 +- plugins/UserAgreement/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade21to22/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade22to24/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade25to26/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade26to27/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade27to30/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade30to31/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json | 2 +- plugins/VersionUpgrade/VersionUpgrade34to35/plugin.json | 2 +- plugins/X3DReader/plugin.json | 2 +- plugins/XRayView/plugin.json | 2 +- plugins/XmlMaterialProfile/plugin.json | 2 +- 45 files changed, 45 insertions(+), 45 deletions(-) diff --git a/plugins/3MFReader/plugin.json b/plugins/3MFReader/plugin.json index d7160e166a..5af21a7033 100644 --- a/plugins/3MFReader/plugin.json +++ b/plugins/3MFReader/plugin.json @@ -1,7 +1,7 @@ { "name": "3MF Reader", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Provides support for reading 3MF files.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/3MFWriter/plugin.json b/plugins/3MFWriter/plugin.json index 865c3bf026..3820ebd2e7 100644 --- a/plugins/3MFWriter/plugin.json +++ b/plugins/3MFWriter/plugin.json @@ -1,7 +1,7 @@ { "name": "3MF Writer", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Provides support for writing 3MF files.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/ChangeLogPlugin/plugin.json b/plugins/ChangeLogPlugin/plugin.json index 04d5ce7c51..92041d1543 100644 --- a/plugins/ChangeLogPlugin/plugin.json +++ b/plugins/ChangeLogPlugin/plugin.json @@ -1,7 +1,7 @@ { "name": "Changelog", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Shows changes since latest checked version.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/CuraEngineBackend/plugin.json b/plugins/CuraEngineBackend/plugin.json index 25db9abefd..28f0e294e7 100644 --- a/plugins/CuraEngineBackend/plugin.json +++ b/plugins/CuraEngineBackend/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "description": "Provides the link to the CuraEngine slicing backend.", "api": "6.0", - "version": "1.0.0", + "version": "1.0.1", "i18n-catalog": "cura" } diff --git a/plugins/CuraProfileReader/plugin.json b/plugins/CuraProfileReader/plugin.json index adbf376b72..169fb43360 100644 --- a/plugins/CuraProfileReader/plugin.json +++ b/plugins/CuraProfileReader/plugin.json @@ -1,7 +1,7 @@ { "name": "Cura Profile Reader", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Provides support for importing Cura profiles.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/CuraProfileWriter/plugin.json b/plugins/CuraProfileWriter/plugin.json index d576f517de..9627c754d7 100644 --- a/plugins/CuraProfileWriter/plugin.json +++ b/plugins/CuraProfileWriter/plugin.json @@ -1,7 +1,7 @@ { "name": "Cura Profile Writer", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Provides support for exporting Cura profiles.", "api": "6.0", "i18n-catalog":"cura" diff --git a/plugins/FirmwareUpdateChecker/plugin.json b/plugins/FirmwareUpdateChecker/plugin.json index 4b77a53a62..6c55d77fd8 100644 --- a/plugins/FirmwareUpdateChecker/plugin.json +++ b/plugins/FirmwareUpdateChecker/plugin.json @@ -1,7 +1,7 @@ { "name": "Firmware Update Checker", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Checks for firmware updates.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/FirmwareUpdater/plugin.json b/plugins/FirmwareUpdater/plugin.json index 4098759630..c1034e5e42 100644 --- a/plugins/FirmwareUpdater/plugin.json +++ b/plugins/FirmwareUpdater/plugin.json @@ -1,7 +1,7 @@ { "name": "Firmware Updater", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Provides a machine actions for updating firmware.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/GCodeGzReader/plugin.json b/plugins/GCodeGzReader/plugin.json index cea1d0f55c..d4f281682f 100644 --- a/plugins/GCodeGzReader/plugin.json +++ b/plugins/GCodeGzReader/plugin.json @@ -1,7 +1,7 @@ { "name": "Compressed G-code Reader", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Reads g-code from a compressed archive.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/GCodeGzWriter/plugin.json b/plugins/GCodeGzWriter/plugin.json index d725b2649d..b0e6f8d605 100644 --- a/plugins/GCodeGzWriter/plugin.json +++ b/plugins/GCodeGzWriter/plugin.json @@ -1,7 +1,7 @@ { "name": "Compressed G-code Writer", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Writes g-code to a compressed archive.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/GCodeProfileReader/plugin.json b/plugins/GCodeProfileReader/plugin.json index 12d8a276da..af1c2d1827 100644 --- a/plugins/GCodeProfileReader/plugin.json +++ b/plugins/GCodeProfileReader/plugin.json @@ -1,7 +1,7 @@ { "name": "G-code Profile Reader", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Provides support for importing profiles from g-code files.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/GCodeReader/plugin.json b/plugins/GCodeReader/plugin.json index 351d6cbc31..bbc94fa917 100644 --- a/plugins/GCodeReader/plugin.json +++ b/plugins/GCodeReader/plugin.json @@ -1,7 +1,7 @@ { "name": "G-code Reader", "author": "Victor Larchenko, Ultimaker", - "version": "1.0.0", + "version": "1.0.1", "description": "Allows loading and displaying G-code files.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/GCodeWriter/plugin.json b/plugins/GCodeWriter/plugin.json index 78b63a582f..f3a95ddb78 100644 --- a/plugins/GCodeWriter/plugin.json +++ b/plugins/GCodeWriter/plugin.json @@ -1,7 +1,7 @@ { "name": "G-code Writer", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Writes g-code to a file.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/ImageReader/plugin.json b/plugins/ImageReader/plugin.json index 001da0822b..d966537d99 100644 --- a/plugins/ImageReader/plugin.json +++ b/plugins/ImageReader/plugin.json @@ -1,7 +1,7 @@ { "name": "Image Reader", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Enables ability to generate printable geometry from 2D image files.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/LegacyProfileReader/plugin.json b/plugins/LegacyProfileReader/plugin.json index 2cd1e9ca2c..2f5264ad37 100644 --- a/plugins/LegacyProfileReader/plugin.json +++ b/plugins/LegacyProfileReader/plugin.json @@ -1,7 +1,7 @@ { "name": "Legacy Cura Profile Reader", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Provides support for importing profiles from legacy Cura versions.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/MachineSettingsAction/plugin.json b/plugins/MachineSettingsAction/plugin.json index 4587729a9a..d734c1adf5 100644 --- a/plugins/MachineSettingsAction/plugin.json +++ b/plugins/MachineSettingsAction/plugin.json @@ -1,7 +1,7 @@ { "name": "Machine Settings action", "author": "fieldOfView", - "version": "1.0.0", + "version": "1.0.1", "description": "Provides a way to change machine settings (such as build volume, nozzle size, etc.).", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/ModelChecker/plugin.json b/plugins/ModelChecker/plugin.json index a210c3bf9f..59be5bbf0a 100644 --- a/plugins/ModelChecker/plugin.json +++ b/plugins/ModelChecker/plugin.json @@ -1,7 +1,7 @@ { "name": "Model Checker", "author": "Ultimaker B.V.", - "version": "0.1", + "version": "1.0.1", "api": "6.0", "description": "Checks models and print configuration for possible printing issues and give suggestions.", "i18n-catalog": "cura" diff --git a/plugins/MonitorStage/plugin.json b/plugins/MonitorStage/plugin.json index 71fc10c5ce..95e4b86f36 100644 --- a/plugins/MonitorStage/plugin.json +++ b/plugins/MonitorStage/plugin.json @@ -1,7 +1,7 @@ { "name": "Monitor Stage", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Provides a monitor stage in Cura.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/PerObjectSettingsTool/plugin.json b/plugins/PerObjectSettingsTool/plugin.json index e5bdc5d3da..f272abf06a 100644 --- a/plugins/PerObjectSettingsTool/plugin.json +++ b/plugins/PerObjectSettingsTool/plugin.json @@ -1,7 +1,7 @@ { "name": "Per Model Settings Tool", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Provides the Per Model Settings.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/PostProcessingPlugin/plugin.json b/plugins/PostProcessingPlugin/plugin.json index ad60312af0..1e73133c53 100644 --- a/plugins/PostProcessingPlugin/plugin.json +++ b/plugins/PostProcessingPlugin/plugin.json @@ -1,7 +1,7 @@ { "name": "Post Processing", "author": "Ultimaker", - "version": "2.2", + "version": "2.2.1", "api": "6.0", "description": "Extension that allows for user created scripts for post processing", "catalog": "cura" diff --git a/plugins/PrepareStage/plugin.json b/plugins/PrepareStage/plugin.json index e948b96f6f..dc5c68ce16 100644 --- a/plugins/PrepareStage/plugin.json +++ b/plugins/PrepareStage/plugin.json @@ -1,7 +1,7 @@ { "name": "Prepare Stage", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Provides a prepare stage in Cura.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/PreviewStage/plugin.json b/plugins/PreviewStage/plugin.json index c9c13fe341..e1e4288bae 100644 --- a/plugins/PreviewStage/plugin.json +++ b/plugins/PreviewStage/plugin.json @@ -1,7 +1,7 @@ { "name": "Preview Stage", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Provides a preview stage in Cura.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/RemovableDriveOutputDevice/plugin.json b/plugins/RemovableDriveOutputDevice/plugin.json index 48cd60596c..5523d6b1c1 100644 --- a/plugins/RemovableDriveOutputDevice/plugin.json +++ b/plugins/RemovableDriveOutputDevice/plugin.json @@ -2,7 +2,7 @@ "name": "Removable Drive Output Device Plugin", "author": "Ultimaker B.V.", "description": "Provides removable drive hotplugging and writing support.", - "version": "1.0.0", + "version": "1.0.1", "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/SimulationView/plugin.json b/plugins/SimulationView/plugin.json index dcfbeb305e..3ccf91b9e6 100644 --- a/plugins/SimulationView/plugin.json +++ b/plugins/SimulationView/plugin.json @@ -1,7 +1,7 @@ { "name": "Simulation View", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Provides the Simulation view.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/SliceInfoPlugin/plugin.json b/plugins/SliceInfoPlugin/plugin.json index c2d78e8d78..8ff0e194fb 100644 --- a/plugins/SliceInfoPlugin/plugin.json +++ b/plugins/SliceInfoPlugin/plugin.json @@ -1,7 +1,7 @@ { "name": "Slice info", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Submits anonymous slice info. Can be disabled through preferences.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/SolidView/plugin.json b/plugins/SolidView/plugin.json index c66e41a7aa..b3f62221c5 100644 --- a/plugins/SolidView/plugin.json +++ b/plugins/SolidView/plugin.json @@ -1,7 +1,7 @@ { "name": "Solid View", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Provides a normal solid mesh view.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/SupportEraser/plugin.json b/plugins/SupportEraser/plugin.json index dc624ecd77..fa6d6d230e 100644 --- a/plugins/SupportEraser/plugin.json +++ b/plugins/SupportEraser/plugin.json @@ -1,7 +1,7 @@ { "name": "Support Eraser", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Creates an eraser mesh to block the printing of support in certain places", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/Toolbox/plugin.json b/plugins/Toolbox/plugin.json index 91e9561327..61dc0429f5 100644 --- a/plugins/Toolbox/plugin.json +++ b/plugins/Toolbox/plugin.json @@ -1,7 +1,7 @@ { "name": "Toolbox", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "api": "6.0", "description": "Find, manage and install new Cura packages." } diff --git a/plugins/UFPWriter/plugin.json b/plugins/UFPWriter/plugin.json index b4e05d9346..288d6acf77 100644 --- a/plugins/UFPWriter/plugin.json +++ b/plugins/UFPWriter/plugin.json @@ -1,7 +1,7 @@ { "name": "UFP Writer", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Provides support for writing Ultimaker Format Packages.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/UM3NetworkPrinting/plugin.json b/plugins/UM3NetworkPrinting/plugin.json index def02562db..088b4dae6a 100644 --- a/plugins/UM3NetworkPrinting/plugin.json +++ b/plugins/UM3NetworkPrinting/plugin.json @@ -2,7 +2,7 @@ "name": "UM3 Network Connection", "author": "Ultimaker B.V.", "description": "Manages network connections to Ultimaker 3 printers.", - "version": "1.0.0", + "version": "1.0.1", "api": "6.0", "i18n-catalog": "cura" } diff --git a/plugins/USBPrinting/plugin.json b/plugins/USBPrinting/plugin.json index 3a58abe7e7..45971d858b 100644 --- a/plugins/USBPrinting/plugin.json +++ b/plugins/USBPrinting/plugin.json @@ -1,7 +1,7 @@ { "name": "USB printing", "author": "Ultimaker B.V.", - "version": "1.0.1", + "version": "1.0.2", "api": "6.0", "description": "Accepts G-Code and sends them to a printer. Plugin can also update firmware.", "i18n-catalog": "cura" diff --git a/plugins/UltimakerMachineActions/plugin.json b/plugins/UltimakerMachineActions/plugin.json index fc1eb242fe..3e3e0af9b0 100644 --- a/plugins/UltimakerMachineActions/plugin.json +++ b/plugins/UltimakerMachineActions/plugin.json @@ -1,7 +1,7 @@ { "name": "Ultimaker machine actions", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Provides machine actions for Ultimaker machines (such as bed leveling wizard, selecting upgrades, etc.).", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/UserAgreement/plugin.json b/plugins/UserAgreement/plugin.json index 7781e383d6..b172d1f9a2 100644 --- a/plugins/UserAgreement/plugin.json +++ b/plugins/UserAgreement/plugin.json @@ -1,7 +1,7 @@ { "name": "UserAgreement", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Ask the user once if he/she agrees with our license.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/plugin.json b/plugins/VersionUpgrade/VersionUpgrade21to22/plugin.json index e732c43813..cad94c2eb5 100644 --- a/plugins/VersionUpgrade/VersionUpgrade21to22/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade21to22/plugin.json @@ -1,7 +1,7 @@ { "name": "Version Upgrade 2.1 to 2.2", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Upgrades configurations from Cura 2.1 to Cura 2.2.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/VersionUpgrade/VersionUpgrade22to24/plugin.json b/plugins/VersionUpgrade/VersionUpgrade22to24/plugin.json index 2b344ce5d3..7da1e7a56d 100644 --- a/plugins/VersionUpgrade/VersionUpgrade22to24/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade22to24/plugin.json @@ -1,7 +1,7 @@ { "name": "Version Upgrade 2.2 to 2.4", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Upgrades configurations from Cura 2.2 to Cura 2.4.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/VersionUpgrade/VersionUpgrade25to26/plugin.json b/plugins/VersionUpgrade/VersionUpgrade25to26/plugin.json index 8691ebfc67..e1f0a47685 100644 --- a/plugins/VersionUpgrade/VersionUpgrade25to26/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade25to26/plugin.json @@ -1,7 +1,7 @@ { "name": "Version Upgrade 2.5 to 2.6", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Upgrades configurations from Cura 2.5 to Cura 2.6.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/VersionUpgrade/VersionUpgrade26to27/plugin.json b/plugins/VersionUpgrade/VersionUpgrade26to27/plugin.json index c7124eada6..6cdbd64cbb 100644 --- a/plugins/VersionUpgrade/VersionUpgrade26to27/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade26to27/plugin.json @@ -1,7 +1,7 @@ { "name": "Version Upgrade 2.6 to 2.7", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Upgrades configurations from Cura 2.6 to Cura 2.7.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/VersionUpgrade/VersionUpgrade27to30/plugin.json b/plugins/VersionUpgrade/VersionUpgrade27to30/plugin.json index ce8f4a6d44..885d741a8c 100644 --- a/plugins/VersionUpgrade/VersionUpgrade27to30/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade27to30/plugin.json @@ -1,7 +1,7 @@ { "name": "Version Upgrade 2.7 to 3.0", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Upgrades configurations from Cura 2.7 to Cura 3.0.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/VersionUpgrade/VersionUpgrade30to31/plugin.json b/plugins/VersionUpgrade/VersionUpgrade30to31/plugin.json index f94f3dfe30..d5f22649c1 100644 --- a/plugins/VersionUpgrade/VersionUpgrade30to31/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade30to31/plugin.json @@ -1,7 +1,7 @@ { "name": "Version Upgrade 3.0 to 3.1", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Upgrades configurations from Cura 3.0 to Cura 3.1.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json b/plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json index 0bbc237436..eb489169e0 100644 --- a/plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json @@ -1,7 +1,7 @@ { "name": "Version Upgrade 3.2 to 3.3", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Upgrades configurations from Cura 3.2 to Cura 3.3.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json b/plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json index 5f83695245..9649010643 100644 --- a/plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json @@ -1,7 +1,7 @@ { "name": "Version Upgrade 3.3 to 3.4", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Upgrades configurations from Cura 3.3 to Cura 3.4.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/VersionUpgrade/VersionUpgrade34to35/plugin.json b/plugins/VersionUpgrade/VersionUpgrade34to35/plugin.json index f95cb7be87..02635ec606 100644 --- a/plugins/VersionUpgrade/VersionUpgrade34to35/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade34to35/plugin.json @@ -1,7 +1,7 @@ { "name": "Version Upgrade 3.4 to 3.5", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Upgrades configurations from Cura 3.4 to Cura 3.5.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/X3DReader/plugin.json b/plugins/X3DReader/plugin.json index 4052ac6605..1fc14104ed 100644 --- a/plugins/X3DReader/plugin.json +++ b/plugins/X3DReader/plugin.json @@ -1,7 +1,7 @@ { "name": "X3D Reader", "author": "Seva Alekseyev", - "version": "0.5.0", + "version": "1.0.1", "description": "Provides support for reading X3D files.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/XRayView/plugin.json b/plugins/XRayView/plugin.json index 4d8e42bae3..71cc165b6c 100644 --- a/plugins/XRayView/plugin.json +++ b/plugins/XRayView/plugin.json @@ -1,7 +1,7 @@ { "name": "X-Ray View", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Provides the X-Ray view.", "api": "6.0", "i18n-catalog": "cura" diff --git a/plugins/XmlMaterialProfile/plugin.json b/plugins/XmlMaterialProfile/plugin.json index d172d49f87..bb1db82fa4 100644 --- a/plugins/XmlMaterialProfile/plugin.json +++ b/plugins/XmlMaterialProfile/plugin.json @@ -1,7 +1,7 @@ { "name": "Material Profiles", "author": "Ultimaker B.V.", - "version": "1.0.0", + "version": "1.0.1", "description": "Provides capabilities to read and write XML-based material profiles.", "api": "6.0", "i18n-catalog": "cura" From b7904d6e056b1edbac2ac8b75755704d4ad47f75 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 17 Dec 2018 13:04:23 +0100 Subject: [PATCH 0960/1240] Unify the CuraDrive plugin with the rest of the items in Cura For instance, the buttons were converted to either primary buttons or secondary buttons. A new CheckBox component was created in Cura in order to reuse it in the future. Contributes to CURA-6005. --- .../src/qml/components/ActionButton.qml | 67 ------------------- .../src/qml/components/ActionCheckBox.qml | 49 -------------- .../src/qml/components/ActionToolTip.qml | 4 +- .../src/qml/components/BackupListFooter.qml | 10 +-- .../src/qml/components/BackupListItem.qml | 15 +++-- plugins/CuraDrive/src/qml/main.qml | 6 +- .../CuraDrive/src/qml/pages/WelcomePage.qml | 9 ++- resources/qml/ActionButton.qml | 15 +++++ resources/qml/CheckBox.qml | 61 +++++++++++++++++ resources/qml/Cura.qml | 11 +++ resources/qml/qmldir | 3 +- resources/themes/cura-light/styles.qml | 2 +- 12 files changed, 115 insertions(+), 137 deletions(-) delete mode 100644 plugins/CuraDrive/src/qml/components/ActionButton.qml delete mode 100644 plugins/CuraDrive/src/qml/components/ActionCheckBox.qml create mode 100644 resources/qml/CheckBox.qml diff --git a/plugins/CuraDrive/src/qml/components/ActionButton.qml b/plugins/CuraDrive/src/qml/components/ActionButton.qml deleted file mode 100644 index 843079ed88..0000000000 --- a/plugins/CuraDrive/src/qml/components/ActionButton.qml +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) 2018 Ultimaker B.V. -import QtQuick 2.7 -import QtQuick.Controls 2.1 -import QtQuick.Layouts 1.3 - -import UM 1.1 as UM - -Button -{ - id: button - property alias cursorShape: mouseArea.cursorShape - property var iconSource: "" - property var busy: false - property var color: UM.Theme.getColor("primary") - property var hoverColor: UM.Theme.getColor("primary_hover") - property var disabledColor: color - property var textColor: UM.Theme.getColor("button_text") - property var textHoverColor: UM.Theme.getColor("button_text_hover") - property var textDisabledColor: textColor - property var textFont: UM.Theme.getFont("action_button") - - contentItem: RowLayout - { - Icon - { - id: buttonIcon - iconSource: button.iconSource - width: 16 * screenScaleFactor - color: button.hovered ? button.textHoverColor : button.textColor - visible: button.iconSource != "" && !loader.visible - } - - Icon - { - id: loader - iconSource: "../images/loading.gif" - width: 16 * screenScaleFactor - color: button.hovered ? button.textHoverColor : button.textColor - visible: button.busy - animated: true - } - - Label - { - id: buttonText - text: button.text - color: button.enabled ? (button.hovered ? button.textHoverColor : button.textColor): button.textDisabledColor - font: button.textFont - visible: button.text != "" - renderType: Text.NativeRendering - } - } - - background: Rectangle - { - color: button.enabled ? (button.hovered ? button.hoverColor : button.color) : button.disabledColor - } - - MouseArea - { - id: mouseArea - anchors.fill: parent - onPressed: mouse.accepted = false - hoverEnabled: true - cursorShape: button.enabled ? (hovered ? Qt.PointingHandCursor : Qt.ArrowCursor) : Qt.ForbiddenCursor - } -} diff --git a/plugins/CuraDrive/src/qml/components/ActionCheckBox.qml b/plugins/CuraDrive/src/qml/components/ActionCheckBox.qml deleted file mode 100644 index 71f5e6035d..0000000000 --- a/plugins/CuraDrive/src/qml/components/ActionCheckBox.qml +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2018 Ultimaker B.V. -import QtQuick 2.7 -import QtQuick.Controls 2.1 -import QtQuick.Layouts 1.3 - -import UM 1.3 as UM - -CheckBox -{ - id: checkbox - hoverEnabled: true - - property var label: "" - - indicator: Rectangle { - implicitWidth: 30 * screenScaleFactor - implicitHeight: 30 * screenScaleFactor - x: 0 - y: Math.round(parent.height / 2 - height / 2) - color: UM.Theme.getColor("sidebar") - border.color: UM.Theme.getColor("text") - - Rectangle { - width: 14 * screenScaleFactor - height: 14 * screenScaleFactor - x: 8 * screenScaleFactor - y: 8 * screenScaleFactor - color: UM.Theme.getColor("primary") - visible: checkbox.checked - } - } - - contentItem: Label { - anchors - { - left: checkbox.indicator.right - leftMargin: 5 * screenScaleFactor - } - text: catalog.i18nc("@checkbox:description", "Auto Backup") - color: UM.Theme.getColor("text") - renderType: Text.NativeRendering - verticalAlignment: Text.AlignVCenter - } - - ActionToolTip - { - text: checkbox.label - } -} diff --git a/plugins/CuraDrive/src/qml/components/ActionToolTip.qml b/plugins/CuraDrive/src/qml/components/ActionToolTip.qml index 93b92bc2df..7401221328 100644 --- a/plugins/CuraDrive/src/qml/components/ActionToolTip.qml +++ b/plugins/CuraDrive/src/qml/components/ActionToolTip.qml @@ -14,7 +14,7 @@ ToolTip background: Rectangle { - color: UM.Theme.getColor("sidebar") + color: UM.Theme.getColor("main_background") border.color: UM.Theme.getColor("primary") border.width: 1 * screenScaleFactor } @@ -23,7 +23,7 @@ ToolTip { text: tooltip.text color: UM.Theme.getColor("text") - font: UM.Theme.getFont("very_small") + font: UM.Theme.getFont("default") renderType: Text.NativeRendering } } diff --git a/plugins/CuraDrive/src/qml/components/BackupListFooter.qml b/plugins/CuraDrive/src/qml/components/BackupListFooter.qml index 80f47d6cba..1e7fc16801 100644 --- a/plugins/CuraDrive/src/qml/components/BackupListFooter.qml +++ b/plugins/CuraDrive/src/qml/components/BackupListFooter.qml @@ -4,6 +4,7 @@ import QtQuick.Controls 2.1 import QtQuick.Layouts 1.3 import UM 1.3 as UM +import Cura 1.0 as Cura import "../components" @@ -13,7 +14,7 @@ RowLayout width: parent.width property bool showInfoButton: false - ActionButton + Cura.PrimaryButton { id: infoButton text: catalog.i18nc("@button", "Want more?") @@ -22,7 +23,7 @@ RowLayout visible: backupListFooter.showInfoButton } - ActionButton + Cura.PrimaryButton { id: createBackupButton text: catalog.i18nc("@button", "Backup Now") @@ -32,11 +33,12 @@ RowLayout busy: CuraDrive.isCreatingBackup } - ActionCheckBox + Cura.CheckBox { id: autoBackupEnabled checked: CuraDrive.autoBackupEnabled onClicked: CuraDrive.toggleAutoBackup(autoBackupEnabled.checked) - label: catalog.i18nc("@checkbox:description", "Automatically create a backup each day that Cura is started.") + text: catalog.i18nc("@checkbox:description", "Auto Backup") + tooltip: catalog.i18nc("@checkbox:description", "Automatically create a backup each day that Cura is started.") } } diff --git a/plugins/CuraDrive/src/qml/components/BackupListItem.qml b/plugins/CuraDrive/src/qml/components/BackupListItem.qml index abe9a1acf9..ad1ce5f9df 100644 --- a/plugins/CuraDrive/src/qml/components/BackupListItem.qml +++ b/plugins/CuraDrive/src/qml/components/BackupListItem.qml @@ -5,6 +5,7 @@ import QtQuick.Layouts 1.3 import QtQuick.Dialogs 1.1 import UM 1.1 as UM +import Cura 1.0 as Cura Item { @@ -29,7 +30,7 @@ Item width: parent.width height: 50 * screenScaleFactor - ActionButton + Cura.ActionButton { color: "transparent" hoverColor: "transparent" @@ -61,18 +62,18 @@ Item renderType: Text.NativeRendering } - ActionButton + Cura.SecondaryButton { text: catalog.i18nc("@button", "Restore") - color: "transparent" - hoverColor: "transparent" - textColor: UM.Theme.getColor("text") - textHoverColor: UM.Theme.getColor("text_link") +// color: "transparent" +// hoverColor: "transparent" +// textColor: UM.Theme.getColor("text") +// textHoverColor: UM.Theme.getColor("text_link") enabled: !CuraDrive.isCreatingBackup && !CuraDrive.isRestoringBackup onClicked: confirmRestoreDialog.visible = true } - ActionButton + Cura.ActionButton { color: "transparent" hoverColor: "transparent" diff --git a/plugins/CuraDrive/src/qml/main.qml b/plugins/CuraDrive/src/qml/main.qml index 4a2219cf1f..359f8dd725 100644 --- a/plugins/CuraDrive/src/qml/main.qml +++ b/plugins/CuraDrive/src/qml/main.qml @@ -14,11 +14,11 @@ Window id: curaDriveDialog minimumWidth: Math.round(UM.Theme.getSize("modal_window_minimum").width) minimumHeight: Math.round(UM.Theme.getSize("modal_window_minimum").height) - maximumWidth: minimumWidth * 1.2 - maximumHeight: minimumHeight * 1.2 + maximumWidth: Math.round(minimumWidth * 1.2) + maximumHeight: Math.round(minimumHeight * 1.2) width: minimumWidth height: minimumHeight - color: UM.Theme.getColor("sidebar") + color: UM.Theme.getColor("main_background") title: catalog.i18nc("@title:window", "Cura Backups") // Globally available. diff --git a/plugins/CuraDrive/src/qml/pages/WelcomePage.qml b/plugins/CuraDrive/src/qml/pages/WelcomePage.qml index 882656dc4a..b7f96d162a 100644 --- a/plugins/CuraDrive/src/qml/pages/WelcomePage.qml +++ b/plugins/CuraDrive/src/qml/pages/WelcomePage.qml @@ -38,11 +38,14 @@ Column renderType: Text.NativeRendering } - ActionButton + Cura.PrimaryButton { id: loginButton - onClicked: Cura.API.account.login() - text: catalog.i18nc("@button", "Sign In") + width: UM.Theme.getSize("account_button").width + height: UM.Theme.getSize("account_button").height anchors.horizontalCenter: parent.horizontalCenter + text: catalog.i18nc("@button", "Sign in") + onClicked: Cura.API.account.login() + fixedWidthMode: true } } diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index 3a9552cd9c..573ead2910 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -4,6 +4,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.1 import QtGraphicalEffects 1.0 // For the dropshadow + import UM 1.1 as UM import Cura 1.0 as Cura @@ -30,6 +31,7 @@ Button property color outlineDisabledColor: outlineColor property alias shadowColor: shadow.color property alias shadowEnabled: shadow.visible + property alias busy: busyIndicator.visible // This property is used to indicate whether the button has a fixed width or the width would depend on the contents // Be careful when using fixedWidthMode, the translated texts can be too long that they won't fit. In any case, @@ -117,4 +119,17 @@ Button delay: 500 visible: text != "" && button.hovered } + + BusyIndicator { + id: busyIndicator + + anchors { + centerIn: parent + } + + width: height + height: parent.height + + visible: false + } } \ No newline at end of file diff --git a/resources/qml/CheckBox.qml b/resources/qml/CheckBox.qml new file mode 100644 index 0000000000..7a79182a4b --- /dev/null +++ b/resources/qml/CheckBox.qml @@ -0,0 +1,61 @@ +// Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 2.1 + +import UM 1.3 as UM + +CheckBox +{ + id: checkbox + hoverEnabled: true + + property alias tooltip: tooltip.text + + indicator: Rectangle { + implicitWidth: UM.Theme.getSize("checkbox").width + implicitHeight: UM.Theme.getSize("checkbox").height + x: 0 + y: Math.round(parent.height / 2 - height / 2) + color: UM.Theme.getColor("main_background") + radius: UM.Theme.getSize("checkbox_radius").width + border.width: UM.Theme.getSize("default_lining").width + border.color: checkbox.hovered ? UM.Theme.getColor("checkbox_border_hover") : UM.Theme.getColor("checkbox_border") + + UM.RecolorImage + { + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + width: Math.round(parent.width / 2.5) + height: Math.round(parent.height / 2.5) + sourceSize.height: width + color: UM.Theme.getColor("checkbox_mark") + source: UM.Theme.getIcon("check") + opacity: checkbox.checked + Behavior on opacity { NumberAnimation { duration: 100; } } + } + } + + contentItem: Label { + anchors + { + left: checkbox.indicator.right + leftMargin: UM.Theme.getSize("narrow_margin").width + } + text: checkbox.text + color: UM.Theme.getColor("checkbox_text") + font: UM.Theme.getFont("default") + renderType: Text.NativeRendering + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + } + + ToolTip + { + id: tooltip + text: "" + delay: 500 + visible: text != "" && checkbox.hovered + } +} diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 8ab943b93b..71b97fa6f4 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -123,6 +123,17 @@ UM.MainWindow } } } + + // This is a placehoder for adding a pattern in the header + Image + { + id: backgroundPattern + anchors.fill: parent + fillMode: Image.Tile + source: UM.Theme.getImage("header_pattern") + horizontalAlignment: Image.AlignLeft + verticalAlignment: Image.AlignTop + } } MainWindowHeader diff --git a/resources/qml/qmldir b/resources/qml/qmldir index 1dc21150ce..4e28e12f62 100644 --- a/resources/qml/qmldir +++ b/resources/qml/qmldir @@ -14,4 +14,5 @@ PrinterTypeLabel 1.0 PrinterTypeLabel.qml ViewsSelector 1.0 ViewsSelector.qml ToolbarButton 1.0 ToolbarButton.qml SettingView 1.0 SettingView.qml -ProfileMenu 1.0 ProfileMenu.qml \ No newline at end of file +ProfileMenu 1.0 ProfileMenu.qml +CheckBox 1.0 CheckBox.qml \ No newline at end of file diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index 89729fc08c..95f811c3aa 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -478,7 +478,7 @@ QtObject color: (control.hovered || control._hovered) ? Theme.getColor("checkbox_hover") : (control.enabled ? Theme.getColor("checkbox") : Theme.getColor("checkbox_disabled")) Behavior on color { ColorAnimation { duration: 50; } } - radius: control.exclusiveGroup ? Math.round(Theme.getSize("checkbox").width / 2) : UM.Theme.getSize("checkbox_radius").width + radius: control.exclusiveGroup ? Math.round(Theme.getSize("checkbox").width / 2) : Theme.getSize("checkbox_radius").width border.width: Theme.getSize("default_lining").width border.color: (control.hovered || control._hovered) ? Theme.getColor("checkbox_border_hover") : Theme.getColor("checkbox_border") From ee74b9f89f04f8bfaaf71ab852a2abea8b9be250 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 17 Dec 2018 13:09:01 +0100 Subject: [PATCH 0961/1240] Once the connectiontype is recovered, it's converted to a string So we need to check if that's the case. CURA-6011 --- cura/Settings/MachineManager.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 342f53aac6..db43207cc9 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -525,10 +525,9 @@ class MachineManager(QObject): def activeMachineHasRemoteConnection(self) -> bool: if self._global_container_stack: connection_type = self._global_container_stack.getMetaDataEntry("connection_type") - return connection_type in [ConnectionType.NetworkConnection, ConnectionType.CloudConnection] + return connection_type in [str(ConnectionType.NetworkConnection), str(ConnectionType.CloudConnection)] return False - @pyqtProperty(str, notify = printerConnectedStatusChanged) def activeMachineNetworkKey(self) -> str: if self._global_container_stack: return self._global_container_stack.getMetaDataEntry("um_network_key", "") @@ -756,7 +755,7 @@ class MachineManager(QObject): self.setActiveMachine(other_machine_stacks[0]["id"]) metadata = CuraContainerRegistry.getInstance().findContainerStacksMetadata(id = machine_id)[0] - network_key = metadata["um_network_key"] if "um_network_key" in metadata else None + network_key = metadata.get("um_network_key", None) ExtruderManager.getInstance().removeMachineExtruders(machine_id) containers = CuraContainerRegistry.getInstance().findInstanceContainersMetadata(type = "user", machine = machine_id) for container in containers: From 34f56b046aeb562c8d8e8d52a1dd89d35474c176 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Mon, 17 Dec 2018 13:13:44 +0100 Subject: [PATCH 0962/1240] STAR-322: Review comments --- cura/NetworkClient.py | 194 ------------------ .../src/Cloud/CloudOutputDevice.py | 2 +- 2 files changed, 1 insertion(+), 195 deletions(-) delete mode 100644 cura/NetworkClient.py diff --git a/cura/NetworkClient.py b/cura/NetworkClient.py deleted file mode 100644 index fabaaed95e..0000000000 --- a/cura/NetworkClient.py +++ /dev/null @@ -1,194 +0,0 @@ -# Copyright (c) 2018 Ultimaker B.V. -# Cura is released under the terms of the LGPLv3 or higher. -from time import time -from typing import Optional, Dict, Callable, List, Union, cast - -from PyQt5.QtCore import QUrl -from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkReply, QHttpMultiPart, QNetworkRequest, QHttpPart, \ - QAuthenticator - -from UM.Application import Application -from UM.Logger import Logger - - -## Abstraction of QNetworkAccessManager for easier networking in Cura. -# This was originally part of NetworkedPrinterOutputDevice but was moved out for re-use in other classes. -class NetworkClient: - - def __init__(self) -> None: - super().__init__() - - # Network manager instance to use for this client. - self.__manager = None # type: Optional[QNetworkAccessManager] - - # Timings. - self._last_manager_create_time = None # type: Optional[float] - self._last_response_time = None # type: Optional[float] - self._last_request_time = None # type: Optional[float] - - # The user agent of Cura. - application = Application.getInstance() - self._user_agent = "%s/%s " % (application.getApplicationName(), application.getVersion()) - - # QHttpMultiPart objects need to be kept alive and not garbage collected during the - # HTTP which uses them. We hold references to these QHttpMultiPart objects here. - self._kept_alive_multiparts = {} # type: Dict[QNetworkReply, QHttpMultiPart] - - # in order to avoid garbage collection we keep the callbacks in this list. - self._anti_gc_callbacks = [] # type: List[Callable[[], None]] - - ## Creates a network manager if needed, with all the required properties and event bindings. - def start(self) -> None: - if not self.__manager: - self.__manager = QNetworkAccessManager() - self._last_manager_create_time = time() - self.__manager.authenticationRequired.connect(self._onAuthenticationRequired) - - ## Destroys the network manager and event bindings. - def stop(self) -> None: - if self.__manager: - self.__manager.authenticationRequired.disconnect(self._onAuthenticationRequired) - self.__manager = None - - @property - def _manager(self) -> QNetworkAccessManager: - self.start() - return cast(QNetworkAccessManager, self.__manager) - - ## Create a new empty network request. - # Automatically adds the required HTTP headers. - # \param url: The URL to request - # \param content_type: The type of the body contents. - def _createEmptyRequest(self, url: str, content_type: Optional[str] = "application/json") -> QNetworkRequest: - if not self._manager: - self.start() # make sure the manager is created - request = QNetworkRequest(QUrl(url)) - if content_type: - request.setHeader(QNetworkRequest.ContentTypeHeader, content_type) - request.setHeader(QNetworkRequest.UserAgentHeader, self._user_agent) - self._last_request_time = time() - return request - - ## Removes all cached Multi-Part items. - def _clearCachedMultiPart(self, reply: QNetworkReply) -> None: - if reply in self._kept_alive_multiparts: - del self._kept_alive_multiparts[reply] - - ## Callback for when the network manager detects that authentication is required but was not given. - @staticmethod - def _onAuthenticationRequired(reply: QNetworkReply, authenticator: QAuthenticator) -> None: - Logger.log("w", "Request to {} required authentication but was not given".format(reply.url().toString())) - - ## Add a part to a Multi-Part form. - @staticmethod - def _createFormPart(content_header: str, data: bytes, content_type: Optional[str] = None) -> QHttpPart: - part = QHttpPart() - - if not content_header.startswith("form-data;"): - content_header = "form_data; " + content_header - - part.setHeader(QNetworkRequest.ContentDispositionHeader, content_header) - - if content_type is not None: - part.setHeader(QNetworkRequest.ContentTypeHeader, content_type) - - part.setBody(data) - return part - - ## Public version of _createFormPart. Both are needed for backward compatibility with 3rd party plugins. - def createFormPart(self, content_header: str, data: bytes, content_type: Optional[str] = None) -> QHttpPart: - return self._createFormPart(content_header, data, content_type) - - ## Sends a put request to the given path. - # \param url: The path after the API prefix. - # \param data: The data to be sent in the body - # \param content_type: The content type of the body data. - # \param on_finished: The function to call when the response is received. - # \param on_progress: The function to call when the progress changes. Parameters are bytes_sent / bytes_total. - def put(self, url: str, data: Union[str, bytes], content_type: str, - on_finished: Callable[[QNetworkReply], None], - on_progress: Optional[Callable[[int, int], None]] = None) -> None: - request = self._createEmptyRequest(url, content_type = content_type) - - body = data if isinstance(data, bytes) else data.encode() # type: bytes - reply = self._manager.put(request, body) - callback = self._createCallback(reply, on_finished) - reply.finished.connect(callback) - if on_progress is not None: - reply.uploadProgress.connect(on_progress) - - ## Sends a delete request to the given path. - # url: The path after the API prefix. - # on_finished: The function to be call when the response is received. - def delete(self, url: str, on_finished: Callable[[QNetworkReply], None]) -> None: - request = self._createEmptyRequest(url) - reply = self._manager.deleteResource(request) - callback = self._createCallback(reply, on_finished) - reply.finished.connect(callback) - - ## Sends a get request to the given path. - # \param url: The path after the API prefix. - # \param on_finished: The function to be call when the response is received. - def get(self, url: str, on_finished: Callable[[QNetworkReply], None]) -> None: - request = self._createEmptyRequest(url) - reply = self._manager.get(request) - callback = self._createCallback(reply, on_finished) - reply.finished.connect(callback) - - ## Sends a post request to the given path. - # \param url: The path after the API prefix. - # \param data: The data to be sent in the body - # \param on_finished: The function to call when the response is received. - # \param on_progress: The function to call when the progress changes. Parameters are bytes_sent / bytes_total. - def post(self, url: str, data: Union[str, bytes], - on_finished: Callable[[QNetworkReply], None], - on_progress: Optional[Callable[[int, int], None]] = None) -> None: - request = self._createEmptyRequest(url) - - body = data if isinstance(data, bytes) else data.encode() # type: bytes - reply = self._manager.post(request, body) - callback = self._createCallback(reply, on_finished) - reply.finished.connect(callback) - if on_progress is not None: - reply.uploadProgress.connect(on_progress) - - ## Does a POST request with form data to the given URL. - def postForm(self, url: str, header_data: str, body_data: bytes, - on_finished: Optional[Callable[[QNetworkReply], None]], - on_progress: Optional[Callable[[int, int], None]] = None) -> None: - post_part = QHttpPart() - post_part.setHeader(QNetworkRequest.ContentDispositionHeader, header_data) - post_part.setBody(body_data) - self.postFormWithParts(url, [post_part], on_finished, on_progress) - - ## Does a POST request with form parts to the given URL. - def postFormWithParts(self, target: str, parts: List[QHttpPart], - on_finished: Optional[Callable[[QNetworkReply], None]], - on_progress: Optional[Callable[[int, int], None]] = None) -> Optional[QNetworkReply]: - request = self._createEmptyRequest(target, content_type = None) - multi_post_part = QHttpMultiPart(QHttpMultiPart.FormDataType) - - for part in parts: - multi_post_part.append(part) - - reply = self._manager.post(request, multi_post_part) - - def callback(): - on_finished(reply) - self._clearCachedMultiPart(reply) - - reply.finished.connect(callback) - - self._kept_alive_multiparts[reply] = multi_post_part - - if on_progress is not None: - reply.uploadProgress.connect(on_progress) - - return reply - - def _createCallback(self, reply: QNetworkReply, on_finished: Callable[[QNetworkReply], None]) -> Callable[[], None]: - def callback(): - on_finished(reply) - self._anti_gc_callbacks.remove(callback) - self._anti_gc_callbacks.append(callback) - return callback diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 54f0dc20b6..f29e29c40b 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -18,11 +18,11 @@ from cura.CuraApplication import CuraApplication from cura.PrinterOutput.NetworkedPrinterOutputDevice import AuthState, NetworkedPrinterOutputDevice from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputController import CloudOutputController -from src.Cloud.Models.CloudClusterResponse import CloudClusterResponse from ..MeshFormatHandler import MeshFormatHandler from ..UM3PrintJobOutputModel import UM3PrintJobOutputModel from .CloudProgressMessage import CloudProgressMessage from .CloudApiClient import CloudApiClient +from .Models.CloudClusterResponse import CloudClusterResponse from .Models.CloudClusterStatus import CloudClusterStatus from .Models.CloudPrintJobUploadRequest import CloudPrintJobUploadRequest from .Models.CloudPrintResponse import CloudPrintResponse From a1cb09f73fe4403090c4857c35910ba92e74e411 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Mon, 17 Dec 2018 13:24:07 +0100 Subject: [PATCH 0963/1240] STAR-322: Sorting clusters --- plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py | 2 ++ .../UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py | 2 ++ .../tests/Cloud/TestCloudOutputDeviceManager.py | 5 ++++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py index d4044726a3..c7d58cea78 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py @@ -18,6 +18,8 @@ from .NetworkManagerMock import NetworkManagerMock class TestCloudApiClient(TestCase): + maxDiff = None + def _errorHandler(self, errors: List[CloudErrorObject]): raise Exception("Received unexpected error: {}".format(errors)) diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py index c42a208ece..fded79e15b 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py @@ -16,6 +16,8 @@ from .NetworkManagerMock import NetworkManagerMock class TestCloudOutputDevice(TestCase): + maxDiff = None + CLUSTER_ID = "RIZ6cZbWA_Ua7RZVJhrdVfVpf0z-MqaSHQE4v8aRTtYq" JOB_ID = "ABCDefGHIjKlMNOpQrSTUvYxWZ0-1234567890abcDE=" HOST_NAME = "ultimakersystem-ccbdd30044ec" diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py index f98e954274..f62d92e9db 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py @@ -11,6 +11,8 @@ from .NetworkManagerMock import NetworkManagerMock class TestCloudOutputDeviceManager(TestCase): + maxDiff = None + URL = "https://api-staging.ultimaker.com/connect/v1/clusters" def setUp(self): @@ -41,7 +43,8 @@ class TestCloudOutputDeviceManager(TestCase): clusters = self.clusters_response.get("data", []) self.assertEqual([CloudOutputDevice] * len(clusters), [type(d) for d in devices]) self.assertEqual({cluster["cluster_id"] for cluster in clusters}, {device.key for device in devices}) - self.assertEqual(clusters, [device.clusterData.toDict() for device in devices]) + self.assertEqual(clusters, sorted((device.clusterData.toDict() for device in devices), + key=lambda device_dict: device_dict["host_version"])) for device in clusters: device_manager.getOutputDevice(device["cluster_id"]).close() From b6f90f1ab2caa1be9a1974f680970997dd8e72b3 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Mon, 17 Dec 2018 13:29:20 +0100 Subject: [PATCH 0964/1240] STAR-322: Using cura constants --- .../UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py | 9 +++++---- .../tests/Cloud/TestCloudOutputDevice.py | 8 ++++---- .../tests/Cloud/TestCloudOutputDeviceManager.py | 3 ++- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py index c7d58cea78..a09894deb0 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py @@ -7,6 +7,7 @@ from unittest import TestCase from unittest.mock import patch, MagicMock from cura.CuraApplication import CuraApplication +from cura.CuraConstants import CuraCloudAPIRoot from src.Cloud.CloudApiClient import CloudApiClient from src.Cloud.Models.CloudClusterResponse import CloudClusterResponse from src.Cloud.Models.CloudClusterStatus import CloudClusterStatus @@ -39,7 +40,7 @@ class TestCloudApiClient(TestCase): response = readFixture("getClusters") data = parseFixture("getClusters")["data"] - self.network.prepareReply("GET", "https://api-staging.ultimaker.com/connect/v1/clusters", 200, response) + self.network.prepareReply("GET", CuraCloudAPIRoot + "/connect/v1/clusters", 200, response) # the callback is a function that adds the result of the call to getClusters to the result list self.api.getClusters(lambda clusters: result.extend(clusters)) @@ -54,7 +55,7 @@ class TestCloudApiClient(TestCase): data = parseFixture("getClusterStatusResponse")["data"] self.network.prepareReply("GET", - "https://api-staging.ultimaker.com/connect/v1/clusters/R0YcLJwar1ugh0ikEZsZs8NWKV6vJP_LdYsXgXqAcaNC/status", + CuraCloudAPIRoot + "/connect/v1/clusters/R0YcLJwar1ugh0ikEZsZs8NWKV6vJP_LdYsXgXqAcaNC/status", 200, response ) self.api.getClusterStatus("R0YcLJwar1ugh0ikEZsZs8NWKV6vJP_LdYsXgXqAcaNC", lambda s: result.append(s)) @@ -69,7 +70,7 @@ class TestCloudApiClient(TestCase): response = readFixture("putJobUploadResponse") - self.network.prepareReply("PUT", "https://api-staging.ultimaker.com/cura/v1/jobs/upload", 200, response) + self.network.prepareReply("PUT", CuraCloudAPIRoot + "/cura/v1/jobs/upload", 200, response) request = CloudPrintJobUploadRequest(job_name = "job name", file_size = 143234, content_type = "text/plain") self.api.requestUpload(request, lambda r: results.append(r)) self.network.flushReplies() @@ -108,7 +109,7 @@ class TestCloudApiClient(TestCase): job_id = "ABCDefGHIjKlMNOpQrSTUvYxWZ0-1234567890abcDE=" self.network.prepareReply("POST", - "https://api-staging.ultimaker.com/connect/v1/clusters/{}/print/{}" + CuraCloudAPIRoot + "/connect/v1/clusters/{}/print/{}" .format(cluster_id, job_id), 200, response) diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py index fded79e15b..90a1b2fa96 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py @@ -7,6 +7,7 @@ from unittest.mock import patch, MagicMock from UM.Scene.SceneNode import SceneNode from UM.Signal import Signal from cura.CuraApplication import CuraApplication +from cura.CuraConstants import CuraCloudAPIRoot from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from src.Cloud.CloudApiClient import CloudApiClient from src.Cloud.CloudOutputDevice import CloudOutputDevice @@ -23,10 +24,9 @@ class TestCloudOutputDevice(TestCase): HOST_NAME = "ultimakersystem-ccbdd30044ec" HOST_GUID = "e90ae0ac-1257-4403-91ee-a44c9b7e8050" - BASE_URL = "https://api-staging.ultimaker.com" - STATUS_URL = "{}/connect/v1/clusters/{}/status".format(BASE_URL, CLUSTER_ID) - PRINT_URL = "{}/connect/v1/clusters/{}/print/{}".format(BASE_URL, CLUSTER_ID, JOB_ID) - REQUEST_UPLOAD_URL = "{}/cura/v1/jobs/upload".format(BASE_URL) + STATUS_URL = "{}/connect/v1/clusters/{}/status".format(CuraCloudAPIRoot, CLUSTER_ID) + PRINT_URL = "{}/connect/v1/clusters/{}/print/{}".format(CuraCloudAPIRoot, CLUSTER_ID, JOB_ID) + REQUEST_UPLOAD_URL = "{}/cura/v1/jobs/upload".format(CuraCloudAPIRoot) def setUp(self): super().setUp() diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py index f62d92e9db..01b1b18ff1 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py @@ -4,6 +4,7 @@ from unittest import TestCase from unittest.mock import patch from cura.CuraApplication import CuraApplication +from cura.CuraConstants import CuraCloudAPIRoot from src.Cloud.CloudOutputDevice import CloudOutputDevice from src.Cloud.CloudOutputDeviceManager import CloudOutputDeviceManager from tests.Cloud.Fixtures import parseFixture, readFixture @@ -13,7 +14,7 @@ from .NetworkManagerMock import NetworkManagerMock class TestCloudOutputDeviceManager(TestCase): maxDiff = None - URL = "https://api-staging.ultimaker.com/connect/v1/clusters" + URL = CuraCloudAPIRoot + "/connect/v1/clusters" def setUp(self): super().setUp() From aad7540366e6b7b71619574f77961499493fd4f8 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 17 Dec 2018 13:31:38 +0100 Subject: [PATCH 0965/1240] Fix situation where multiple connect configurations would cause issues CURA-6011 --- cura/PrintersModel.py | 6 +++- cura/Settings/MachineManager.py | 11 ++++--- .../PrinterSelector/MachineSelectorList.qml | 32 +++++-------------- 3 files changed, 19 insertions(+), 30 deletions(-) diff --git a/cura/PrintersModel.py b/cura/PrintersModel.py index 26b65409c5..0c58d9bf8c 100644 --- a/cura/PrintersModel.py +++ b/cura/PrintersModel.py @@ -56,10 +56,14 @@ class PrintersModel(ListModel): connection_type = container_stack.getMetaDataEntry("connection_type") has_remote_connection = connection_type in [str(ConnectionType.NetworkConnection), str(ConnectionType.CloudConnection)] + if container_stack.getMetaDataEntry("hidden", False) in ["True", True]: + continue + # TODO: Remove reference to connect group name. items.append({"name": container_stack.getMetaDataEntry("connect_group_name", container_stack.getName()), "id": container_stack.getId(), "hasRemoteConnection": has_remote_connection, - "connectionType": connection_type}) + "connectionType": connection_type, + "metadata": container_stack.getMetaData().copy()}) items.sort(key=lambda i: not i["hasRemoteConnection"]) self.setItems(items) \ No newline at end of file diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index db43207cc9..c51c26f1b9 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -1321,17 +1321,18 @@ class MachineManager(QObject): # Get the definition id corresponding to this machine name machine_definition_id = CuraContainerRegistry.getInstance().findDefinitionContainers(name = machine_name)[0].getId() # Try to find a machine with the same network key - new_machine = self.getMachine(machine_definition_id, metadata_filter = {"um_network_key": self.activeMachineNetworkKey}) + new_machine = self.getMachine(machine_definition_id, metadata_filter = {"um_network_key": self.activeMachineNetworkKey()}) # If there is no machine, then create a new one and set it to the non-hidden instance if not new_machine: new_machine = CuraStackBuilder.createMachine(machine_definition_id + "_sync", machine_definition_id) if not new_machine: return - new_machine.setMetaDataEntry("um_network_key", self.activeMachineNetworkKey) + new_machine.setMetaDataEntry("um_network_key", self.activeMachineNetworkKey()) new_machine.setMetaDataEntry("connect_group_name", self.activeMachineNetworkGroupName) new_machine.setMetaDataEntry("hidden", False) + new_machine.setMetaDataEntry("connection_type", self._global_container_stack.getMetaDataEntry("connection_type")) else: - Logger.log("i", "Found a %s with the key %s. Let's use it!", machine_name, self.activeMachineNetworkKey) + Logger.log("i", "Found a %s with the key %s. Let's use it!", machine_name, self.activeMachineNetworkKey()) new_machine.setMetaDataEntry("hidden", False) # Set the current printer instance to hidden (the metadata entry must exist) @@ -1391,10 +1392,10 @@ class MachineManager(QObject): # 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) -> None: - if self._global_container_stack and device_id == self.activeMachineNetworkKey: + 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} + metadata_filter = {"um_network_key": self.activeMachineNetworkKey()} containers = CuraContainerRegistry.getInstance().findContainerStacks(type = "machine", **metadata_filter) for container in containers: container.setMetaDataEntry("connect_group_name", group_name) diff --git a/resources/qml/PrinterSelector/MachineSelectorList.qml b/resources/qml/PrinterSelector/MachineSelectorList.qml index e0dc6f5e44..52cea0ea80 100644 --- a/resources/qml/PrinterSelector/MachineSelectorList.qml +++ b/resources/qml/PrinterSelector/MachineSelectorList.qml @@ -31,32 +31,16 @@ ListView text: model.name width: listView.width outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null - } -} - /* - - - Repeater - { - id: networkedPrinters - - model: Cura.PrintersModel + checked: { - id: networkedPrintersModel - } - - delegate: MachineSelectorButton - { - text: model.name //model.metadata["connect_group_name"] - //checked: Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["connect_group_name"] - outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null - - Connections + // If the machine has a remote connection + var result = Cura.MachineManager.activeMachineId == model.id + if (Cura.MachineManager.activeMachineHasRemoteConnection) { - target: Cura.MachineManager - onActiveMachineNetworkGroupNameChanged: checked = Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["connect_group_name"] + result |= Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["connect_group_name"] } + return result } - }*/ - + } +} \ No newline at end of file From 02825a062fd4e2413938fb8eee3969d7c3cbdc06 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 17 Dec 2018 13:38:41 +0100 Subject: [PATCH 0966/1240] Change the remove and the info buttons Contributes to CURA-6005. --- .../src/qml/components/ActionToolTip.qml | 29 ------------------ .../src/qml/components/BackupList.qml | 2 +- .../src/qml/components/BackupListFooter.qml | 4 +-- .../src/qml/components/BackupListItem.qml | 30 ++++++++----------- plugins/CuraDrive/src/qml/images/delete.svg | 7 ----- plugins/CuraDrive/src/qml/images/info.svg | 4 --- resources/qml/ActionButton.qml | 2 +- 7 files changed, 17 insertions(+), 61 deletions(-) delete mode 100644 plugins/CuraDrive/src/qml/components/ActionToolTip.qml delete mode 100644 plugins/CuraDrive/src/qml/images/delete.svg delete mode 100644 plugins/CuraDrive/src/qml/images/info.svg diff --git a/plugins/CuraDrive/src/qml/components/ActionToolTip.qml b/plugins/CuraDrive/src/qml/components/ActionToolTip.qml deleted file mode 100644 index 7401221328..0000000000 --- a/plugins/CuraDrive/src/qml/components/ActionToolTip.qml +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2018 Ultimaker B.V. -import QtQuick 2.7 -import QtQuick.Controls 2.1 -import QtQuick.Layouts 1.3 - -import UM 1.1 as UM - -ToolTip -{ - id: tooltip - visible: parent.hovered - opacity: 0.9 - delay: 500 - - background: Rectangle - { - color: UM.Theme.getColor("main_background") - border.color: UM.Theme.getColor("primary") - border.width: 1 * screenScaleFactor - } - - contentItem: Label - { - text: tooltip.text - color: UM.Theme.getColor("text") - font: UM.Theme.getFont("default") - renderType: Text.NativeRendering - } -} diff --git a/plugins/CuraDrive/src/qml/components/BackupList.qml b/plugins/CuraDrive/src/qml/components/BackupList.qml index 231f25afc8..a19d1f0ae7 100644 --- a/plugins/CuraDrive/src/qml/components/BackupList.qml +++ b/plugins/CuraDrive/src/qml/components/BackupList.qml @@ -18,7 +18,7 @@ ListView BackupListItem { id: backupListItem - width: parent.width + width: parent.width - UM.Theme.getSize("default_margin").width // Add a margin, otherwise the scrollbar is be on top of the right most component } Divider diff --git a/plugins/CuraDrive/src/qml/components/BackupListFooter.qml b/plugins/CuraDrive/src/qml/components/BackupListFooter.qml index 1e7fc16801..72dc3df044 100644 --- a/plugins/CuraDrive/src/qml/components/BackupListFooter.qml +++ b/plugins/CuraDrive/src/qml/components/BackupListFooter.qml @@ -18,7 +18,7 @@ RowLayout { id: infoButton text: catalog.i18nc("@button", "Want more?") - iconSource: "../images/info.svg" + iconSource: UM.Theme.getIcon("info") onClicked: Qt.openUrlExternally("https://goo.gl/forms/QACEP8pP3RV60QYG2") visible: backupListFooter.showInfoButton } @@ -28,7 +28,7 @@ RowLayout id: createBackupButton text: catalog.i18nc("@button", "Backup Now") iconSource: "../images/backup.svg" - enabled: !CuraDrive.isCreatingBackup && !CuraDrive.isRestoringBackup + enabled: !CuraDrive.isCreatingBackup && !CuraDrive.isRestoringBackup && !backupListFooter.showInfoButton onClicked: CuraDrive.createBackup() busy: CuraDrive.isCreatingBackup } diff --git a/plugins/CuraDrive/src/qml/components/BackupListItem.qml b/plugins/CuraDrive/src/qml/components/BackupListItem.qml index ad1ce5f9df..a84caeb6ab 100644 --- a/plugins/CuraDrive/src/qml/components/BackupListItem.qml +++ b/plugins/CuraDrive/src/qml/components/BackupListItem.qml @@ -26,17 +26,17 @@ Item RowLayout { id: dataRow - spacing: UM.Theme.getSize("default_margin").width * 2 + spacing: UM.Theme.getSize("wide_margin").width width: parent.width height: 50 * screenScaleFactor - Cura.ActionButton + UM.SimpleButton { - color: "transparent" - hoverColor: "transparent" - textColor: UM.Theme.getColor("text") - textHoverColor: UM.Theme.getColor("primary") - iconSource: "../images/info.svg" + width: UM.Theme.getSize("section_icon").width + height: UM.Theme.getSize("section_icon").height + color: UM.Theme.getColor("small_button_text") + hoverColor: UM.Theme.getColor("small_button_text_hover") + iconSource: UM.Theme.getIcon("info") onClicked: backupListItem.showDetails = !backupListItem.showDetails } @@ -65,21 +65,17 @@ Item Cura.SecondaryButton { text: catalog.i18nc("@button", "Restore") -// color: "transparent" -// hoverColor: "transparent" -// textColor: UM.Theme.getColor("text") -// textHoverColor: UM.Theme.getColor("text_link") enabled: !CuraDrive.isCreatingBackup && !CuraDrive.isRestoringBackup onClicked: confirmRestoreDialog.visible = true } - Cura.ActionButton + UM.SimpleButton { - color: "transparent" - hoverColor: "transparent" - textColor: UM.Theme.getColor("setting_validation_error") - textHoverColor: UM.Theme.getColor("setting_validation_error") - iconSource: "../images/delete.svg" + width: UM.Theme.getSize("message_close").width + height: UM.Theme.getSize("message_close").height + color: UM.Theme.getColor("small_button_text") + hoverColor: UM.Theme.getColor("small_button_text_hover") + iconSource: UM.Theme.getIcon("cross1") onClicked: confirmDeleteDialog.visible = true } } diff --git a/plugins/CuraDrive/src/qml/images/delete.svg b/plugins/CuraDrive/src/qml/images/delete.svg deleted file mode 100644 index 2f6190ad43..0000000000 --- a/plugins/CuraDrive/src/qml/images/delete.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/plugins/CuraDrive/src/qml/images/info.svg b/plugins/CuraDrive/src/qml/images/info.svg deleted file mode 100644 index 36154d6729..0000000000 --- a/plugins/CuraDrive/src/qml/images/info.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index 573ead2910..2448b9a551 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -55,7 +55,7 @@ Button width: visible ? height : 0 sourceSize.width: width sourceSize.height: height - color: button.hovered ? button.textHoverColor : button.textColor + color: button.enabled ? (button.hovered ? button.textHoverColor : button.textColor) : button.textDisabledColor visible: source != "" && !button.isIconOnRightSide anchors.verticalCenter: parent.verticalCenter } From 3b367938de9600a94829bfaa8ac29e069cb49552 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Mon, 17 Dec 2018 13:53:31 +0100 Subject: [PATCH 0967/1240] STAR-322: Removing TestSendMaterialJob temporarily --- .../tests/Cloud/TestCloudApiClient.py | 6 +- .../tests/TestSendMaterialJob.py | 190 ------------------ plugins/UM3NetworkPrinting/tests/__init__.py | 2 + run_mypy.py | 1 + 4 files changed, 5 insertions(+), 194 deletions(-) delete mode 100644 plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py create mode 100644 plugins/UM3NetworkPrinting/tests/__init__.py diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py index a09894deb0..49d2b1b34b 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py @@ -54,10 +54,8 @@ class TestCloudApiClient(TestCase): response = readFixture("getClusterStatusResponse") data = parseFixture("getClusterStatusResponse")["data"] - self.network.prepareReply("GET", - CuraCloudAPIRoot + "/connect/v1/clusters/R0YcLJwar1ugh0ikEZsZs8NWKV6vJP_LdYsXgXqAcaNC/status", - 200, response - ) + url = CuraCloudAPIRoot + "/connect/v1/clusters/R0YcLJwar1ugh0ikEZsZs8NWKV6vJP_LdYsXgXqAcaNC/status" + self.network.prepareReply("GET", url, 200, response) self.api.getClusterStatus("R0YcLJwar1ugh0ikEZsZs8NWKV6vJP_LdYsXgXqAcaNC", lambda s: result.append(s)) self.network.flushReplies() diff --git a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py deleted file mode 100644 index 7db5ebdedf..0000000000 --- a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py +++ /dev/null @@ -1,190 +0,0 @@ -# Copyright (c) 2018 Ultimaker B.V. -# Copyright (c) 2018 Ultimaker B.V. -# Cura is released under the terms of the LGPLv3 or higher. -import io -import json -from unittest import TestCase, mock -from unittest.mock import patch, call - -from PyQt5.QtCore import QByteArray - -from UM.MimeTypeDatabase import MimeType -from UM.Application import Application -from src.SendMaterialJob import SendMaterialJob - - -@patch("builtins.open", lambda _, __: io.StringIO("")) -@patch("UM.MimeTypeDatabase.MimeTypeDatabase.getMimeTypeForFile", - lambda _: MimeType(name = "application/x-ultimaker-material-profile", comment = "Ultimaker Material Profile", - suffixes = ["xml.fdm_material"])) -@patch("UM.Resources.Resources.getAllResourcesOfType", lambda _: ["/materials/generic_pla_white.xml.fdm_material"]) -@patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") -@patch("PyQt5.QtNetwork.QNetworkReply") -class TestSendMaterialJob(TestCase): - _LOCAL_MATERIAL_WHITE = {"type": "material", "status": "unknown", "id": "generic_pla_white", - "base_file": "generic_pla_white", "setting_version": "5", "name": "White PLA", - "brand": "Generic", "material": "PLA", "color_name": "White", - "GUID": "badb0ee7-87c8-4f3f-9398-938587b67dce", "version": "1", "color_code": "#ffffff", - "description": "Test PLA White", "adhesion_info": "Use glue.", "approximate_diameter": "3", - "properties": {"density": "1.00", "diameter": "2.85", "weight": "750"}, - "definition": "fdmprinter", "compatible": True} - - _LOCAL_MATERIAL_BLACK = {"type": "material", "status": "unknown", "id": "generic_pla_black", - "base_file": "generic_pla_black", "setting_version": "5", "name": "Yellow CPE", - "brand": "Ultimaker", "material": "CPE", "color_name": "Black", - "GUID": "5fbb362a-41f9-4818-bb43-15ea6df34aa4", "version": "1", "color_code": "#000000", - "description": "Test PLA Black", "adhesion_info": "Use glue.", "approximate_diameter": "3", - "properties": {"density": "1.01", "diameter": "2.85", "weight": "750"}, - "definition": "fdmprinter", "compatible": True} - - _REMOTE_MATERIAL_WHITE = { - "guid": "badb0ee7-87c8-4f3f-9398-938587b67dce", - "material": "PLA", - "brand": "Generic", - "version": 1, - "color": "White", - "density": 1.00 - } - - _REMOTE_MATERIAL_BLACK = { - "guid": "5fbb362a-41f9-4818-bb43-15ea6df34aa4", - "material": "PLA", - "brand": "Generic", - "version": 2, - "color": "Black", - "density": 1.00 - } - - def test_run(self, device_mock, reply_mock): - job = SendMaterialJob(device_mock) - job.run() - - # We expect the materials endpoint to be called when the job runs. - device_mock.get.assert_called_with("materials/", on_finished = job._onGetRemoteMaterials) - - def test__onGetRemoteMaterials_withFailedRequest(self, reply_mock, device_mock): - reply_mock.attribute.return_value = 404 - job = SendMaterialJob(device_mock) - job._onGetRemoteMaterials(reply_mock) - - # We expect the device not to be called for any follow up. - self.assertEqual(0, device_mock.createFormPart.call_count) - - def test__onGetRemoteMaterials_withWrongEncoding(self, reply_mock, device_mock): - reply_mock.attribute.return_value = 200 - reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("cp500")) - job = SendMaterialJob(device_mock) - job._onGetRemoteMaterials(reply_mock) - - # Given that the parsing fails we do no expect the device to be called for any follow up. - self.assertEqual(0, device_mock.createFormPart.call_count) - - def test__onGetRemoteMaterials_withBadJsonAnswer(self, reply_mock, device_mock): - reply_mock.attribute.return_value = 200 - reply_mock.readAll.return_value = QByteArray(b"Six sick hicks nick six slick bricks with picks and sticks.") - job = SendMaterialJob(device_mock) - job._onGetRemoteMaterials(reply_mock) - - # Given that the parsing fails we do no expect the device to be called for any follow up. - self.assertEqual(0, device_mock.createFormPart.call_count) - - def test__onGetRemoteMaterials_withMissingGuidInRemoteMaterial(self, reply_mock, device_mock): - reply_mock.attribute.return_value = 200 - remote_material_without_guid = self._REMOTE_MATERIAL_WHITE.copy() - del remote_material_without_guid["guid"] - reply_mock.readAll.return_value = QByteArray(json.dumps([remote_material_without_guid]).encode("ascii")) - job = SendMaterialJob(device_mock) - job._onGetRemoteMaterials(reply_mock) - - # Given that parsing fails we do not expect the device to be called for any follow up. - self.assertEqual(0, device_mock.createFormPart.call_count) - - @patch("cura.Settings.CuraContainerRegistry") - @patch("UM.Application") - def test__onGetRemoteMaterials_withInvalidVersionInLocalMaterial(self, application_mock, container_registry_mock, - reply_mock, device_mock): - reply_mock.attribute.return_value = 200 - reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii")) - - localMaterialWhiteWithInvalidVersion = self._LOCAL_MATERIAL_WHITE.copy() - localMaterialWhiteWithInvalidVersion["version"] = "one" - container_registry_mock.findContainersMetadata.return_value = [localMaterialWhiteWithInvalidVersion] - - application_mock.getContainerRegistry.return_value = container_registry_mock - - with mock.patch.object(Application, "getInstance", new = lambda: application_mock): - job = SendMaterialJob(device_mock) - job._onGetRemoteMaterials(reply_mock) - - self.assertEqual(0, device_mock.createFormPart.call_count) - - @patch("cura.Settings.CuraContainerRegistry") - @patch("UM.Application") - def test__onGetRemoteMaterials_withNoUpdate(self, application_mock, container_registry_mock, reply_mock, - device_mock): - application_mock.getContainerRegistry.return_value = container_registry_mock - - device_mock.createFormPart.return_value = "_xXx_" - - container_registry_mock.findContainersMetadata.return_value = [self._LOCAL_MATERIAL_WHITE] - - reply_mock.attribute.return_value = 200 - reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii")) - - with mock.patch.object(Application, "getInstance", new = lambda: application_mock): - job = SendMaterialJob(device_mock) - job._onGetRemoteMaterials(reply_mock) - - self.assertEqual(0, device_mock.createFormPart.call_count) - self.assertEqual(0, device_mock.postFormWithParts.call_count) - - @patch("cura.Settings.CuraContainerRegistry") - @patch("UM.Application") - def test__onGetRemoteMaterials_withUpdatedMaterial(self, application_mock, container_registry_mock, reply_mock, - device_mock): - application_mock.getContainerRegistry.return_value = container_registry_mock - - device_mock.createFormPart.return_value = "_xXx_" - - localMaterialWhiteWithHigherVersion = self._LOCAL_MATERIAL_WHITE.copy() - localMaterialWhiteWithHigherVersion["version"] = "2" - container_registry_mock.findContainersMetadata.return_value = [localMaterialWhiteWithHigherVersion] - - reply_mock.attribute.return_value = 200 - reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii")) - - with mock.patch.object(Application, "getInstance", new = lambda: application_mock): - job = SendMaterialJob(device_mock) - job._onGetRemoteMaterials(reply_mock) - - self.assertEqual(1, device_mock.createFormPart.call_count) - self.assertEqual(1, device_mock.postFormWithParts.call_count) - self.assertEquals( - [call.createFormPart("name=\"file\"; filename=\"generic_pla_white.xml.fdm_material\"", ""), - call.postFormWithParts(target = "materials/", parts = ["_xXx_"], on_finished = job.sendingFinished)], - device_mock.method_calls) - - @patch("cura.Settings.CuraContainerRegistry") - @patch("UM.Application") - def test__onGetRemoteMaterials_withNewMaterial(self, application_mock, container_registry_mock, reply_mock, - device_mock): - application_mock.getContainerRegistry.return_value = container_registry_mock - - device_mock.createFormPart.return_value = "_xXx_" - - container_registry_mock.findContainersMetadata.return_value = [self._LOCAL_MATERIAL_WHITE, - self._LOCAL_MATERIAL_BLACK] - - reply_mock.attribute.return_value = 200 - reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_BLACK]).encode("ascii")) - - with mock.patch.object(Application, "getInstance", new = lambda: application_mock): - job = SendMaterialJob(device_mock) - job._onGetRemoteMaterials(reply_mock) - - self.assertEqual(1, device_mock.createFormPart.call_count) - self.assertEqual(1, device_mock.postFormWithParts.call_count) - self.assertEquals( - [call.createFormPart("name=\"file\"; filename=\"generic_pla_white.xml.fdm_material\"", ""), - call.postFormWithParts(target = "materials/", parts = ["_xXx_"], on_finished = job.sendingFinished)], - device_mock.method_calls) diff --git a/plugins/UM3NetworkPrinting/tests/__init__.py b/plugins/UM3NetworkPrinting/tests/__init__.py new file mode 100644 index 0000000000..f3f6970c54 --- /dev/null +++ b/plugins/UM3NetworkPrinting/tests/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. diff --git a/run_mypy.py b/run_mypy.py index 27f07cd281..2073f0e9a7 100644 --- a/run_mypy.py +++ b/run_mypy.py @@ -29,6 +29,7 @@ def where(exe_name: str, search_path: str = os.getenv("PATH")) -> str: def findModules(path): + return ["UM3NetworkPrinting"] result = [] for entry in os.scandir(path): if entry.is_dir() and os.path.exists(os.path.join(path, entry.name, "__init__.py")): From cb6e4ff2d1a0f26ed8849c45cd97b0f15d8dab36 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Mon, 17 Dec 2018 14:05:28 +0100 Subject: [PATCH 0968/1240] Organize MonitorStage.qml better --- .../resources/qml/MonitorQueue.qml | 167 ++++++++++++++++++ .../resources/qml/MonitorStage.qml | 159 +---------------- 2 files changed, 169 insertions(+), 157 deletions(-) create mode 100644 plugins/UM3NetworkPrinting/resources/qml/MonitorQueue.qml diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorQueue.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorQueue.qml new file mode 100644 index 0000000000..884dbabc2f --- /dev/null +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorQueue.qml @@ -0,0 +1,167 @@ +// 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.4 +import QtQuick.Controls.Styles 1.4 +import UM 1.3 as UM +import Cura 1.0 as Cura + +/** + * This component contains the print job queue, extracted from the primary + * MonitorStage.qml file not for reusability but simply to keep it lean and more + * readable. + */ +Item +{ + Label + { + id: queuedLabel + anchors + { + left: queuedPrintJobs.left + top: parent.top + } + color: UM.Theme.getColor("text") + font: UM.Theme.getFont("large_nonbold") + text: catalog.i18nc("@label", "Queued") + } + + Item + { + id: manageQueueLabel + anchors + { + right: queuedPrintJobs.right + verticalCenter: queuedLabel.verticalCenter + } + height: 18 * screenScaleFactor // TODO: Theme! + width: childrenRect.width + + UM.RecolorImage + { + id: externalLinkIcon + anchors.verticalCenter: manageQueueLabel.verticalCenter + color: UM.Theme.getColor("primary") + source: "../svg/icons/external_link.svg" + width: 16 * screenScaleFactor // TODO: Theme! (Y U NO USE 18 LIKE ALL OTHER ICONS?!) + height: 16 * screenScaleFactor // TODO: Theme! (Y U NO USE 18 LIKE ALL OTHER ICONS?!) + } + Label + { + id: manageQueueText + anchors + { + left: externalLinkIcon.right + leftMargin: 6 * screenScaleFactor // TODO: Theme! + verticalCenter: externalLinkIcon.verticalCenter + } + color: UM.Theme.getColor("primary") + font: UM.Theme.getFont("default") // 12pt, regular + linkColor: UM.Theme.getColor("primary") + text: catalog.i18nc("@label link to connect manager", "Manage queue in Cura Connect") + } + } + + MouseArea + { + anchors.fill: manageQueueLabel + hoverEnabled: true + onClicked: Cura.MachineManager.printerOutputDevices[0].openPrintJobControlPanel() + onEntered: + { + manageQueueText.font.underline = true + } + onExited: + { + manageQueueText.font.underline = false + } + } + + Row + { + id: printJobQueueHeadings + anchors + { + left: queuedPrintJobs.left + leftMargin: 6 * screenScaleFactor // TODO: Theme! + top: queuedLabel.bottom + topMargin: 24 * screenScaleFactor // TODO: Theme! + } + spacing: 18 * screenScaleFactor // TODO: Theme! + + Label + { + text: catalog.i18nc("@label", "Print jobs") + color: "#666666" + elide: Text.ElideRight + font: UM.Theme.getFont("medium") // 14pt, regular + anchors.verticalCenter: parent.verticalCenter + width: 284 * screenScaleFactor // TODO: Theme! (Should match column size) + + // FIXED-LINE-HEIGHT: + height: 18 * screenScaleFactor // TODO: Theme! + verticalAlignment: Text.AlignVCenter + } + + Label + { + text: catalog.i18nc("@label", "Total print time") + color: "#666666" + elide: Text.ElideRight + font: UM.Theme.getFont("medium") // 14pt, regular + anchors.verticalCenter: parent.verticalCenter + width: 216 * screenScaleFactor // TODO: Theme! (Should match column size) + + // FIXED-LINE-HEIGHT: + height: 18 * screenScaleFactor // TODO: Theme! + verticalAlignment: Text.AlignVCenter + } + + Label + { + text: catalog.i18nc("@label", "Waiting for") + color: "#666666" + elide: Text.ElideRight + font: UM.Theme.getFont("medium") // 14pt, regular + anchors.verticalCenter: parent.verticalCenter + width: 216 * screenScaleFactor // TODO: Theme! (Should match column size) + + // FIXED-LINE-HEIGHT: + height: 18 * screenScaleFactor // TODO: Theme! + verticalAlignment: Text.AlignVCenter + } + } + + ScrollView + { + id: queuedPrintJobs + anchors + { + bottom: parent.bottom + horizontalCenter: parent.horizontalCenter + top: printJobQueueHeadings.bottom + topMargin: 12 * screenScaleFactor // TODO: Theme! + } + style: UM.Theme.styles.scrollview + visible: OutputDevice.receivedPrintJobs + width: parent.width + + ListView + { + id: printJobList + anchors.fill: parent + delegate: MonitorPrintJobCard + { + anchors + { + left: parent.left + right: parent.right + } + printJob: modelData + } + model: OutputDevice.queuedPrintJobs + spacing: 6 // TODO: Theme! + } + } +} \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml index 98ca715108..0333e1447c 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml @@ -8,16 +8,13 @@ import UM 1.3 as UM import Cura 1.0 as Cura import QtGraphicalEffects 1.0 -// Root component for the monitor tab (stage) +// This is the root component for the monitor stage. Component { Item { id: monitorFrame - property var emphasisColor: UM.Theme.getColor("setting_control_border_highlight") - property var cornerRadius: UM.Theme.getSize("monitor_corner_radius").width - height: maximumHeight onVisibleChanged: { @@ -80,11 +77,10 @@ Component } } - Item + MonitorQueue { id: queue width: Math.min(834 * screenScaleFactor, maximumWidth) - anchors { bottom: parent.bottom @@ -92,157 +88,6 @@ Component top: printers.bottom topMargin: 48 * screenScaleFactor // TODO: Theme! } - - Label - { - id: queuedLabel - anchors - { - left: queuedPrintJobs.left - top: parent.top - } - color: UM.Theme.getColor("text") - font: UM.Theme.getFont("large_nonbold") - text: catalog.i18nc("@label", "Queued") - } - - Item - { - id: manageQueueLabel - anchors - { - right: queuedPrintJobs.right - verticalCenter: queuedLabel.verticalCenter - } - height: 18 * screenScaleFactor // TODO: Theme! - width: childrenRect.width - - UM.RecolorImage - { - id: externalLinkIcon - anchors.verticalCenter: manageQueueLabel.verticalCenter - color: UM.Theme.getColor("primary") - source: "../svg/icons/external_link.svg" - width: 16 * screenScaleFactor // TODO: Theme! (Y U NO USE 18 LIKE ALL OTHER ICONS?!) - height: 16 * screenScaleFactor // TODO: Theme! (Y U NO USE 18 LIKE ALL OTHER ICONS?!) - } - Label - { - id: manageQueueText - anchors - { - left: externalLinkIcon.right - leftMargin: 6 * screenScaleFactor // TODO: Theme! - verticalCenter: externalLinkIcon.verticalCenter - } - color: UM.Theme.getColor("primary") - font: UM.Theme.getFont("default") // 12pt, regular - linkColor: UM.Theme.getColor("primary") - text: catalog.i18nc("@label link to connect manager", "Manage queue in Cura Connect") - } - } - - MouseArea - { - anchors.fill: manageQueueLabel - hoverEnabled: true - onClicked: Cura.MachineManager.printerOutputDevices[0].openPrintJobControlPanel() - onEntered: - { - manageQueueText.font.underline = true - } - onExited: - { - manageQueueText.font.underline = false - } - } - - Row - { - id: printJobQueueHeadings - anchors - { - left: queuedPrintJobs.left - leftMargin: 6 * screenScaleFactor // TODO: Theme! - top: queuedLabel.bottom - topMargin: 24 * screenScaleFactor // TODO: Theme! - } - spacing: 18 * screenScaleFactor // TODO: Theme! - - Label - { - text: catalog.i18nc("@label", "Print jobs") - color: "#666666" - elide: Text.ElideRight - font: UM.Theme.getFont("medium") // 14pt, regular - anchors.verticalCenter: parent.verticalCenter - width: 284 * screenScaleFactor // TODO: Theme! (Should match column size) - - // FIXED-LINE-HEIGHT: - height: 18 * screenScaleFactor // TODO: Theme! - verticalAlignment: Text.AlignVCenter - } - - Label - { - text: catalog.i18nc("@label", "Total print time") - color: "#666666" - elide: Text.ElideRight - font: UM.Theme.getFont("medium") // 14pt, regular - anchors.verticalCenter: parent.verticalCenter - width: 216 * screenScaleFactor // TODO: Theme! (Should match column size) - - // FIXED-LINE-HEIGHT: - height: 18 * screenScaleFactor // TODO: Theme! - verticalAlignment: Text.AlignVCenter - } - - Label - { - text: catalog.i18nc("@label", "Waiting for") - color: "#666666" - elide: Text.ElideRight - font: UM.Theme.getFont("medium") // 14pt, regular - anchors.verticalCenter: parent.verticalCenter - width: 216 * screenScaleFactor // TODO: Theme! (Should match column size) - - // FIXED-LINE-HEIGHT: - height: 18 * screenScaleFactor // TODO: Theme! - verticalAlignment: Text.AlignVCenter - } - } - - ScrollView - { - id: queuedPrintJobs - anchors - { - bottom: parent.bottom - horizontalCenter: parent.horizontalCenter - top: printJobQueueHeadings.bottom - topMargin: 12 * screenScaleFactor // TODO: Theme! - } - style: UM.Theme.styles.scrollview - visible: OutputDevice.receivedPrintJobs - width: parent.width - - ListView - { - id: printJobList - anchors.fill: parent - delegate: MonitorPrintJobCard - { - anchors - { - left: parent.left - right: parent.right - } - printJob: modelData - } - model: OutputDevice.queuedPrintJobs - spacing: 6 - } - } } PrinterVideoStream From 75d7d493499b4594166c8a7adf9d4be719065ff4 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Mon, 17 Dec 2018 14:41:28 +0100 Subject: [PATCH 0969/1240] STAR-322: Mocking the CuraApp --- .../src/Cloud/CloudApiClient.py | 10 +- .../tests/Cloud/TestCloudApiClient.py | 2 - .../tests/Cloud/TestCloudOutputDevice.py | 18 +- .../Cloud/TestCloudOutputDeviceManager.py | 51 +++-- .../tests/TestSendMaterialJob.py | 190 ++++++++++++++++++ plugins/UM3NetworkPrinting/tests/conftest.py | 40 ---- 6 files changed, 238 insertions(+), 73 deletions(-) create mode 100644 plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py delete mode 100644 plugins/UM3NetworkPrinting/tests/conftest.py diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py index d58def4545..c6fb02753f 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py @@ -52,7 +52,7 @@ class CloudApiClient: def getClusters(self, on_finished: Callable[[List[CloudClusterResponse]], Any]) -> None: url = "{}/clusters".format(self.CLUSTER_API_ROOT) reply = self._manager.get(self._createEmptyRequest(url)) - self._addCallbacks(reply, on_finished, CloudClusterResponse) + self._addCallback(reply, on_finished, CloudClusterResponse) ## Retrieves the status of the given cluster. # \param cluster_id: The ID of the cluster. @@ -60,7 +60,7 @@ class CloudApiClient: def getClusterStatus(self, cluster_id: str, on_finished: Callable[[CloudClusterStatus], Any]) -> None: url = "{}/clusters/{}/status".format(self.CLUSTER_API_ROOT, cluster_id) reply = self._manager.get(self._createEmptyRequest(url)) - self._addCallbacks(reply, on_finished, CloudClusterStatus) + self._addCallback(reply, on_finished, CloudClusterStatus) ## Requests the cloud to register the upload of a print job mesh. # \param request: The request object. @@ -70,7 +70,7 @@ class CloudApiClient: url = "{}/jobs/upload".format(self.CURA_API_ROOT) body = json.dumps({"data": request.toDict()}) reply = self._manager.put(self._createEmptyRequest(url), body.encode()) - self._addCallbacks(reply, on_finished, CloudPrintJobResponse) + self._addCallback(reply, on_finished, CloudPrintJobResponse) ## Requests the cloud to register the upload of a print job mesh. # \param upload_response: The object received after requesting an upload with `self.requestUpload`. @@ -90,7 +90,7 @@ class CloudApiClient: def requestPrint(self, cluster_id: str, job_id: str, on_finished: Callable[[CloudPrintResponse], Any]) -> None: url = "{}/clusters/{}/print/{}".format(self.CLUSTER_API_ROOT, cluster_id, job_id) reply = self._manager.post(self._createEmptyRequest(url), b"") - self._addCallbacks(reply, on_finished, CloudPrintResponse) + self._addCallback(reply, on_finished, CloudPrintResponse) ## We override _createEmptyRequest in order to add the user credentials. # \param url: The URL to request @@ -147,7 +147,7 @@ class CloudApiClient: # \param on_finished: The callback in case the response is successful. # \param model: The type of the model to convert the response to. It may either be a single record or a list. # \return: A function that can be passed to the - def _addCallbacks(self, + def _addCallback(self, reply: QNetworkReply, on_finished: Union[Callable[[Model], Any], Callable[[List[Model]], Any]], model: Type[Model], diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py index 49d2b1b34b..0c0c8cffdf 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py @@ -6,7 +6,6 @@ from typing import List from unittest import TestCase from unittest.mock import patch, MagicMock -from cura.CuraApplication import CuraApplication from cura.CuraConstants import CuraCloudAPIRoot from src.Cloud.CloudApiClient import CloudApiClient from src.Cloud.Models.CloudClusterResponse import CloudClusterResponse @@ -29,7 +28,6 @@ class TestCloudApiClient(TestCase): self.account = MagicMock() self.account.isLoggedIn.return_value = True - self.app = CuraApplication.getInstance() self.network = NetworkManagerMock() with patch("src.Cloud.CloudApiClient.QNetworkAccessManager", return_value = self.network): self.api = CloudApiClient(self.account, self._errorHandler) diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py index 90a1b2fa96..34e04689c2 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py @@ -30,9 +30,13 @@ class TestCloudOutputDevice(TestCase): def setUp(self): super().setUp() - self.app = CuraApplication.getInstance() - self.backend = MagicMock(backendStateChange = Signal()) - self.app.setBackend(self.backend) + self.app = MagicMock() + + self.patches = [patch("UM.Qt.QtApplication.QtApplication.getInstance", return_value=self.app), + patch("UM.Application.Application.getInstance", return_value=self.app)] + for patched_method in self.patches: + patched_method.start() + self.cluster = CloudClusterResponse(self.CLUSTER_ID, self.HOST_GUID, self.HOST_NAME, is_online=True, status="active") @@ -41,6 +45,7 @@ class TestCloudOutputDevice(TestCase): self.onError = MagicMock() with patch("src.Cloud.CloudApiClient.QNetworkAccessManager", return_value = self.network): self._api = CloudApiClient(self.account, self.onError) + self.device = CloudOutputDevice(self._api, self.cluster) self.cluster_status = parseFixture("getClusterStatusResponse") self.network.prepareReply("GET", self.STATUS_URL, 200, readFixture("getClusterStatusResponse")) @@ -48,6 +53,8 @@ class TestCloudOutputDevice(TestCase): def tearDown(self): super().tearDown() self.network.flushReplies() + for patched_method in self.patches: + patched_method.stop() def test_status(self): self.device._update() @@ -105,9 +112,8 @@ class TestCloudOutputDevice(TestCase): self.network.flushReplies() self.assertEqual([], self.device.printers) - @patch("cura.CuraApplication.CuraApplication.getGlobalContainerStack") - def test_print_to_cloud(self, global_container_stack_mock): - active_machine_mock = global_container_stack_mock.return_value + def test_print_to_cloud(self): + active_machine_mock = self.app.getGlobalContainerStack.return_value active_machine_mock.getMetaDataEntry.side_effect = {"file_formats": "application/gzip"}.get request_upload_response = parseFixture("putJobUploadResponse") diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py index 01b1b18ff1..96137a3edb 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py @@ -1,14 +1,14 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. from unittest import TestCase -from unittest.mock import patch +from unittest.mock import patch, MagicMock -from cura.CuraApplication import CuraApplication +from UM.OutputDevice.OutputDeviceManager import OutputDeviceManager from cura.CuraConstants import CuraCloudAPIRoot from src.Cloud.CloudOutputDevice import CloudOutputDevice from src.Cloud.CloudOutputDeviceManager import CloudOutputDeviceManager from tests.Cloud.Fixtures import parseFixture, readFixture -from .NetworkManagerMock import NetworkManagerMock +from .NetworkManagerMock import NetworkManagerMock, FakeSignal class TestCloudOutputDeviceManager(TestCase): @@ -18,9 +18,19 @@ class TestCloudOutputDeviceManager(TestCase): def setUp(self): super().setUp() - self.app = CuraApplication.getInstance() + self.app = MagicMock() + self.device_manager = OutputDeviceManager() + self.app.getOutputDeviceManager.return_value = self.device_manager + + self.patches = [patch("UM.Qt.QtApplication.QtApplication.getInstance", return_value=self.app), + patch("UM.Application.Application.getInstance", return_value=self.app)] + for patched_method in self.patches: + patched_method.start() + self.network = NetworkManagerMock() - with patch("src.Cloud.CloudApiClient.QNetworkAccessManager", return_value = self.network): + self.timer = MagicMock(timeout = FakeSignal()) + with patch("src.Cloud.CloudApiClient.QNetworkAccessManager", return_value = self.network), \ + patch("src.Cloud.CloudOutputDeviceManager.QTimer", return_value = self.timer): self.manager = CloudOutputDeviceManager() self.clusters_response = parseFixture("getClusters") self.network.prepareReply("GET", self.URL, 200, readFixture("getClusters")) @@ -28,7 +38,11 @@ class TestCloudOutputDeviceManager(TestCase): def tearDown(self): try: self._beforeTearDown() + + self.network.flushReplies() self.manager.stop() + for patched_method in self.patches: + patched_method.stop() finally: super().tearDown() @@ -38,8 +52,7 @@ class TestCloudOutputDeviceManager(TestCase): # let the network send replies self.network.flushReplies() # get the created devices - device_manager = self.app.getOutputDeviceManager() - devices = device_manager.getOutputDevices() + devices = self.device_manager.getOutputDevices() # get the server data clusters = self.clusters_response.get("data", []) self.assertEqual([CloudOutputDevice] * len(clusters), [type(d) for d in devices]) @@ -48,13 +61,12 @@ class TestCloudOutputDeviceManager(TestCase): key=lambda device_dict: device_dict["host_version"])) for device in clusters: - device_manager.getOutputDevice(device["cluster_id"]).close() - device_manager.removeOutputDevice(device["cluster_id"]) + self.device_manager.getOutputDevice(device["cluster_id"]).close() + self.device_manager.removeOutputDevice(device["cluster_id"]) ## Runs the initial request to retrieve the clusters. def _loadData(self): self.manager.start() - self.manager._onLoginStateChanged(is_logged_in = True) self.network.flushReplies() def test_device_is_created(self): @@ -79,22 +91,20 @@ class TestCloudOutputDeviceManager(TestCase): self.manager._update_timer.timeout.emit() - @patch("cura.CuraApplication.CuraApplication.getGlobalContainerStack") - def test_device_connects_by_cluster_id(self, global_container_stack_mock): - active_machine_mock = global_container_stack_mock.return_value + def test_device_connects_by_cluster_id(self): + active_machine_mock = self.app.getGlobalContainerStack.return_value cluster1, cluster2 = self.clusters_response["data"] cluster_id = cluster1["cluster_id"] active_machine_mock.getMetaDataEntry.side_effect = {"um_cloud_cluster_id": cluster_id}.get self._loadData() - self.assertTrue(self.app.getOutputDeviceManager().getOutputDevice(cluster1["cluster_id"]).isConnected()) - self.assertFalse(self.app.getOutputDeviceManager().getOutputDevice(cluster2["cluster_id"]).isConnected()) + self.assertTrue(self.device_manager.getOutputDevice(cluster1["cluster_id"]).isConnected()) + self.assertFalse(self.device_manager.getOutputDevice(cluster2["cluster_id"]).isConnected()) self.assertEquals([], active_machine_mock.setMetaDataEntry.mock_calls) - @patch("cura.CuraApplication.CuraApplication.getGlobalContainerStack") - def test_device_connects_by_network_key(self, global_container_stack_mock): - active_machine_mock = global_container_stack_mock.return_value + def test_device_connects_by_network_key(self): + active_machine_mock = self.app.getGlobalContainerStack.return_value cluster1, cluster2 = self.clusters_response["data"] network_key = cluster2["host_name"] + ".ultimaker.local" @@ -102,8 +112,9 @@ class TestCloudOutputDeviceManager(TestCase): self._loadData() - self.assertFalse(self.app.getOutputDeviceManager().getOutputDevice(cluster1["cluster_id"]).isConnected()) - self.assertTrue(self.app.getOutputDeviceManager().getOutputDevice(cluster2["cluster_id"]).isConnected()) + self.assertEqual([False, True], + [self.device_manager.getOutputDevice(cluster["cluster_id"]).isConnected() + for cluster in (cluster1, cluster2)]) active_machine_mock.setMetaDataEntry.assert_called_with("um_cloud_cluster_id", cluster2["cluster_id"]) diff --git a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py new file mode 100644 index 0000000000..b669eb192a --- /dev/null +++ b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py @@ -0,0 +1,190 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +import io +import json +from unittest import TestCase, mock +from unittest.mock import patch, call + +from PyQt5.QtCore import QByteArray + +from UM.MimeTypeDatabase import MimeType +from UM.Application import Application +from plugins.UM3NetworkPrinting.src.SendMaterialJob import SendMaterialJob + + +@patch("builtins.open", lambda _, __: io.StringIO("")) +@patch("UM.MimeTypeDatabase.MimeTypeDatabase.getMimeTypeForFile", + lambda _: MimeType(name = "application/x-ultimaker-material-profile", comment = "Ultimaker Material Profile", + suffixes = ["xml.fdm_material"])) +@patch("UM.Resources.Resources.getAllResourcesOfType", lambda _: ["/materials/generic_pla_white.xml.fdm_material"]) +@patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") +@patch("PyQt5.QtNetwork.QNetworkReply") +class TestSendMaterialJob(TestCase): + _LOCAL_MATERIAL_WHITE = {"type": "material", "status": "unknown", "id": "generic_pla_white", + "base_file": "generic_pla_white", "setting_version": "5", "name": "White PLA", + "brand": "Generic", "material": "PLA", "color_name": "White", + "GUID": "badb0ee7-87c8-4f3f-9398-938587b67dce", "version": "1", "color_code": "#ffffff", + "description": "Test PLA White", "adhesion_info": "Use glue.", "approximate_diameter": "3", + "properties": {"density": "1.00", "diameter": "2.85", "weight": "750"}, + "definition": "fdmprinter", "compatible": True} + + _LOCAL_MATERIAL_BLACK = {"type": "material", "status": "unknown", "id": "generic_pla_black", + "base_file": "generic_pla_black", "setting_version": "5", "name": "Yellow CPE", + "brand": "Ultimaker", "material": "CPE", "color_name": "Black", + "GUID": "5fbb362a-41f9-4818-bb43-15ea6df34aa4", "version": "1", "color_code": "#000000", + "description": "Test PLA Black", "adhesion_info": "Use glue.", "approximate_diameter": "3", + "properties": {"density": "1.01", "diameter": "2.85", "weight": "750"}, + "definition": "fdmprinter", "compatible": True} + + _REMOTE_MATERIAL_WHITE = { + "guid": "badb0ee7-87c8-4f3f-9398-938587b67dce", + "material": "PLA", + "brand": "Generic", + "version": 1, + "color": "White", + "density": 1.00 + } + + _REMOTE_MATERIAL_BLACK = { + "guid": "5fbb362a-41f9-4818-bb43-15ea6df34aa4", + "material": "PLA", + "brand": "Generic", + "version": 2, + "color": "Black", + "density": 1.00 + } + + def test_run(self, device_mock, reply_mock): + job = SendMaterialJob(device_mock) + job.run() + + # We expect the materials endpoint to be called when the job runs. + device_mock.get.assert_called_with("materials/", on_finished = job._onGetRemoteMaterials) + + def test__onGetRemoteMaterials_withFailedRequest(self, reply_mock, device_mock): + reply_mock.attribute.return_value = 404 + job = SendMaterialJob(device_mock) + job._onGetRemoteMaterials(reply_mock) + + # We expect the device not to be called for any follow up. + self.assertEqual(0, device_mock.createFormPart.call_count) + + def test__onGetRemoteMaterials_withWrongEncoding(self, reply_mock, device_mock): + reply_mock.attribute.return_value = 200 + reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("cp500")) + job = SendMaterialJob(device_mock) + job._onGetRemoteMaterials(reply_mock) + + # Given that the parsing fails we do no expect the device to be called for any follow up. + self.assertEqual(0, device_mock.createFormPart.call_count) + + def test__onGetRemoteMaterials_withBadJsonAnswer(self, reply_mock, device_mock): + reply_mock.attribute.return_value = 200 + reply_mock.readAll.return_value = QByteArray(b"Six sick hicks nick six slick bricks with picks and sticks.") + job = SendMaterialJob(device_mock) + job._onGetRemoteMaterials(reply_mock) + + # Given that the parsing fails we do no expect the device to be called for any follow up. + self.assertEqual(0, device_mock.createFormPart.call_count) + + def test__onGetRemoteMaterials_withMissingGuidInRemoteMaterial(self, reply_mock, device_mock): + reply_mock.attribute.return_value = 200 + remote_material_without_guid = self._REMOTE_MATERIAL_WHITE.copy() + del remote_material_without_guid["guid"] + reply_mock.readAll.return_value = QByteArray(json.dumps([remote_material_without_guid]).encode("ascii")) + job = SendMaterialJob(device_mock) + job._onGetRemoteMaterials(reply_mock) + + # Given that parsing fails we do not expect the device to be called for any follow up. + self.assertEqual(0, device_mock.createFormPart.call_count) + + @patch("cura.Settings.CuraContainerRegistry") + @patch("UM.Application") + def test__onGetRemoteMaterials_withInvalidVersionInLocalMaterial(self, application_mock, container_registry_mock, + reply_mock, device_mock): + reply_mock.attribute.return_value = 200 + reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii")) + + localMaterialWhiteWithInvalidVersion = self._LOCAL_MATERIAL_WHITE.copy() + localMaterialWhiteWithInvalidVersion["version"] = "one" + container_registry_mock.findContainersMetadata.return_value = [localMaterialWhiteWithInvalidVersion] + + application_mock.getContainerRegistry.return_value = container_registry_mock + + with mock.patch.object(Application, "getInstance", new = lambda: application_mock): + job = SendMaterialJob(device_mock) + job._onGetRemoteMaterials(reply_mock) + + self.assertEqual(0, device_mock.createFormPart.call_count) + + @patch("cura.Settings.CuraContainerRegistry") + @patch("UM.Application") + def test__onGetRemoteMaterials_withNoUpdate(self, application_mock, container_registry_mock, reply_mock, + device_mock): + application_mock.getContainerRegistry.return_value = container_registry_mock + + device_mock.createFormPart.return_value = "_xXx_" + + container_registry_mock.findContainersMetadata.return_value = [self._LOCAL_MATERIAL_WHITE] + + reply_mock.attribute.return_value = 200 + reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii")) + + with mock.patch.object(Application, "getInstance", new = lambda: application_mock): + job = SendMaterialJob(device_mock) + job._onGetRemoteMaterials(reply_mock) + + self.assertEqual(0, device_mock.createFormPart.call_count) + self.assertEqual(0, device_mock.postFormWithParts.call_count) + + @patch("cura.Settings.CuraContainerRegistry") + @patch("UM.Application") + def test__onGetRemoteMaterials_withUpdatedMaterial(self, application_mock, container_registry_mock, reply_mock, + device_mock): + application_mock.getContainerRegistry.return_value = container_registry_mock + + device_mock.createFormPart.return_value = "_xXx_" + + localMaterialWhiteWithHigherVersion = self._LOCAL_MATERIAL_WHITE.copy() + localMaterialWhiteWithHigherVersion["version"] = "2" + container_registry_mock.findContainersMetadata.return_value = [localMaterialWhiteWithHigherVersion] + + reply_mock.attribute.return_value = 200 + reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii")) + + with mock.patch.object(Application, "getInstance", new = lambda: application_mock): + job = SendMaterialJob(device_mock) + job._onGetRemoteMaterials(reply_mock) + + self.assertEqual(1, device_mock.createFormPart.call_count) + self.assertEqual(1, device_mock.postFormWithParts.call_count) + self.assertEquals( + [call.createFormPart("name=\"file\"; filename=\"generic_pla_white.xml.fdm_material\"", ""), + call.postFormWithParts(target = "materials/", parts = ["_xXx_"], on_finished = job.sendingFinished)], + device_mock.method_calls) + + @patch("cura.Settings.CuraContainerRegistry") + @patch("UM.Application") + def test__onGetRemoteMaterials_withNewMaterial(self, application_mock, container_registry_mock, reply_mock, + device_mock): + application_mock.getContainerRegistry.return_value = container_registry_mock + + device_mock.createFormPart.return_value = "_xXx_" + + container_registry_mock.findContainersMetadata.return_value = [self._LOCAL_MATERIAL_WHITE, + self._LOCAL_MATERIAL_BLACK] + + reply_mock.attribute.return_value = 200 + reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_BLACK]).encode("ascii")) + + with mock.patch.object(Application, "getInstance", new = lambda: application_mock): + job = SendMaterialJob(device_mock) + job._onGetRemoteMaterials(reply_mock) + + self.assertEqual(1, device_mock.createFormPart.call_count) + self.assertEqual(1, device_mock.postFormWithParts.call_count) + self.assertEquals( + [call.createFormPart("name=\"file\"; filename=\"generic_pla_white.xml.fdm_material\"", ""), + call.postFormWithParts(target = "materials/", parts = ["_xXx_"], on_finished = job.sendingFinished)], + device_mock.method_calls) diff --git a/plugins/UM3NetworkPrinting/tests/conftest.py b/plugins/UM3NetworkPrinting/tests/conftest.py deleted file mode 100644 index ce49bd3cb7..0000000000 --- a/plugins/UM3NetworkPrinting/tests/conftest.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright (c) 2018 Ultimaker B.V. -# Cura is released under the terms of the LGPLv3 or higher. - -import pytest -from UM.Signal import Signal - -from cura.CuraApplication import CuraApplication -from cura.Machines.MaterialManager import MaterialManager - - -# This mock application must extend from Application and not QtApplication otherwise some QObjects are created and -# a segfault is raised. -class FixtureApplication(CuraApplication): - def __init__(self): - super().__init__() - super().initialize() - Signal._signalQueue = self - - self.getPreferences().addPreference("cura/favorite_materials", "") - - self._material_manager = MaterialManager(self._container_registry, parent = self) - self._material_manager.initialize() - - def functionEvent(self, event): - event.call() - - def parseCommandLine(self): - pass - - def processEvents(self): - pass - - -@pytest.fixture(autouse=True) -def application(): - # Since we need to use it more that once, we create the application the first time and use its instance the second - application = FixtureApplication.getInstance() - if application is None: - application = FixtureApplication() - return application From 42ae9faeb4df483be003b6f45c7f91605402d54f Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Mon, 17 Dec 2018 14:42:16 +0100 Subject: [PATCH 0970/1240] STAR-322: Reverting change to run mypy --- run_mypy.py | 1 - 1 file changed, 1 deletion(-) diff --git a/run_mypy.py b/run_mypy.py index 2073f0e9a7..27f07cd281 100644 --- a/run_mypy.py +++ b/run_mypy.py @@ -29,7 +29,6 @@ def where(exe_name: str, search_path: str = os.getenv("PATH")) -> str: def findModules(path): - return ["UM3NetworkPrinting"] result = [] for entry in os.scandir(path): if entry.is_dir() and os.path.exists(os.path.join(path, entry.name, "__init__.py")): From da974d98686356eee9a39335334841d02fe68555 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Mon, 17 Dec 2018 14:51:57 +0100 Subject: [PATCH 0971/1240] STAR-322: Improving documentation --- .../src/Cloud/CloudApiClient.py | 32 +++++++++++-------- .../tests/Cloud/NetworkManagerMock.py | 4 +-- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py index c6fb02753f..29a9f48c3f 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py @@ -72,12 +72,12 @@ class CloudApiClient: reply = self._manager.put(self._createEmptyRequest(url), body.encode()) self._addCallback(reply, on_finished, CloudPrintJobResponse) - ## Requests the cloud to register the upload of a print job mesh. - # \param upload_response: The object received after requesting an upload with `self.requestUpload`. + ## Uploads a print job mesh to the cloud. + # \param print_job: The object received after requesting an upload with `self.requestUpload`. # \param mesh: The mesh data to be uploaded. - # \param on_finished: The function to be called after the result is parsed. It receives the print job ID. + # \param on_finished: The function to be called after the upload is successful. # \param on_progress: A function to be called during upload progress. It receives a percentage (0-100). - # \param on_error: A function to be called if the upload fails. It receives a dict with the error. + # \param on_error: A function to be called if the upload fails. def uploadMesh(self, print_job: CloudPrintJobResponse, mesh: bytes, on_finished: Callable[[], Any], on_progress: Callable[[int], Any], on_error: Callable[[], Any]): self._upload = MeshUploader(self._manager, print_job, mesh, on_finished, on_progress, on_error) @@ -134,24 +134,28 @@ class CloudApiClient: data = response["data"] if isinstance(data, list): results = [model_class(**c) for c in data] # type: List[CloudApiClient.Model] - cast(Callable[[List[CloudApiClient.Model]], Any], on_finished)(results) + on_finished_list = cast(Callable[[List[CloudApiClient.Model]], Any], on_finished) + on_finished_list(results) else: result = model_class(**data) # type: CloudApiClient.Model - cast(Callable[[CloudApiClient.Model], Any], on_finished)(result) + on_finished_item = cast(Callable[[CloudApiClient.Model], Any], on_finished) + on_finished_item(result) elif "errors" in response: self._on_error([CloudErrorObject(**error) for error in response["errors"]]) else: Logger.log("e", "Cannot find data or errors in the cloud response: %s", response) - ## Wraps a callback function so that it includes the parsing of the response into the correct model. - # \param on_finished: The callback in case the response is successful. - # \param model: The type of the model to convert the response to. It may either be a single record or a list. - # \return: A function that can be passed to the + ## Creates a callback function so that it includes the parsing of the response into the correct model. + # The callback is added to the 'finished' signal of the reply. + # \param reply: The reply that should be listened to. + # \param on_finished: The callback in case the response is successful. Depending on the endpoint it will be either + # a list or a single item. + # \param model: The type of the model to convert the response to. def _addCallback(self, - reply: QNetworkReply, - on_finished: Union[Callable[[Model], Any], Callable[[List[Model]], Any]], - model: Type[Model], - ) -> None: + reply: QNetworkReply, + on_finished: Union[Callable[[Model], Any], Callable[[List[Model]], Any]], + model: Type[Model], + ) -> None: def parse() -> None: status_code, response = self._parseReply(reply) self._anti_gc_callbacks.remove(parse) diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py b/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py index 59b79fdfa6..5b5d89ca54 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py @@ -1,7 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import json -from typing import Dict, Tuple, Union, Optional +from typing import Dict, Tuple, Union, Optional, Any from unittest.mock import MagicMock from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest @@ -53,7 +53,7 @@ class NetworkManagerMock: # Since the methods are very simple and the same it didn't make sense to repeat the code. # \param method: The method being called. # \return The mocked function, if the method name is known. Defaults to the standard getattr function. - def __getattr__(self, method: str) -> any: + def __getattr__(self, method: str) -> Any: ## This mock implementation will simply return the reply from the prepared ones. # it raises a KeyError if requests are done without being prepared. def doRequest(request: QNetworkRequest, body: Optional[bytes] = None, *_): From d28ac5e1205f8b5d3083807c2975925c78052e9b Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Mon, 17 Dec 2018 14:57:57 +0100 Subject: [PATCH 0972/1240] STAR-322: Improving documentation --- .../src/Cloud/CloudOutputDevice.py | 19 +++++++++---------- .../src/Cloud/CloudOutputDeviceManager.py | 2 ++ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index f29e29c40b..fed8cb040a 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -83,7 +83,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Creates a new cloud output device # \param api_client: The client that will run the API calls - # \param device_id: The ID of the device (i.e. the cluster_id for the cloud API) + # \param cluster: The device response received from the cloud API. # \param parent: The optional parent of this output device. def __init__(self, api_client: CloudApiClient, cluster: CloudClusterResponse, parent: QObject = None) -> None: super().__init__(device_id = cluster.cluster_id, address = "", properties = {}, parent = parent) @@ -118,30 +118,33 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # A set of the user's job IDs that have finished self._finished_jobs = set() # type: Set[str] - # Reference to the uploaded print job + # Reference to the uploaded print job / mesh self._mesh = None # type: Optional[bytes] self._uploaded_print_job = None # type: Optional[CloudPrintJobResponse] + ## Connects this device. def connect(self) -> None: super().connect() Logger.log("i", "Connected to cluster %s", self.key) CuraApplication.getInstance().getBackend().backendStateChange.connect(self._onBackendStateChange) + ## Disconnects the device def disconnect(self) -> None: super().disconnect() Logger.log("i", "Disconnected to cluster %s", self.key) CuraApplication.getInstance().getBackend().backendStateChange.disconnect(self._onBackendStateChange) + ## Resets the print job that was uploaded to force a new upload, runs whenever the user re-slices. def _onBackendStateChange(self, _: BackendState) -> None: self._mesh = None self._uploaded_print_job = None - ## Gets the host name of this device + ## Gets the cluster response from which this device was created. @property def clusterData(self) -> CloudClusterResponse: return self._cluster - ## Updates the host name of the output device + ## Updates the cluster data from the cloud. @clusterData.setter def clusterData(self, value: CloudClusterResponse) -> None: self._cluster = value @@ -166,11 +169,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # Show an error message if we're already sending a job. if self._progress.visible: - Message( - text = T.BLOCKED_UPLOADING, - title = T.ERROR, - lifetime = 10, - ).show() + message = Message(text = T.BLOCKED_UPLOADING, title = T.ERROR, lifetime = 10) + message.show() return if self._uploaded_print_job: @@ -312,7 +312,6 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): model.updateAssignedPrinter(printer) ## Uploads the mesh when the print job was registered with the cloud API. - # \param mesh: The bytes to upload. # \param job_response: The response received from the cloud API. def _onPrintJobCreated(self, job_response: CloudPrintJobResponse) -> None: self._progress.show() diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 29c60fd14a..07051f15fd 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -139,6 +139,7 @@ class CloudOutputDeviceManager: ) message.show() + ## Starts running the cloud output device manager, thus periodically requesting cloud data. def start(self): if self._running: return @@ -149,6 +150,7 @@ class CloudOutputDeviceManager: self._update_timer.timeout.connect(self._getRemoteClusters) self._onLoginStateChanged(is_logged_in = self._account.isLoggedIn) + ## Stops running the cloud output device manager. def stop(self): if not self._running: return From c5f438819a60c547b9d416696a6fc4665b539aa3 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Mon, 17 Dec 2018 15:11:01 +0100 Subject: [PATCH 0973/1240] STAR-322: Improving documentation --- .../src/Cloud/CloudProgressMessage.py | 5 ++ .../src/Cloud/MeshUploader.py | 54 ++++++++++++++----- 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudProgressMessage.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudProgressMessage.py index e3e0cefc0c..aefe59cc85 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudProgressMessage.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudProgressMessage.py @@ -12,6 +12,7 @@ class T: SENDING_DATA_TITLE = _I18N_CATALOG.i18nc("@info:status", "Sending data to remote cluster") +## Class responsible for showing a progress message while a mesh is being uploaded to the cloud. class CloudProgressMessage(Message): def __init__(self): super().__init__( @@ -23,15 +24,19 @@ class CloudProgressMessage(Message): use_inactivity_timer = False ) + ## Shows the progress message. def show(self): self.setProgress(0) super().show() + ## Updates the percentage of the uploaded. + # \param percentage: The percentage amount (0-100). def update(self, percentage: int) -> None: if not self._visible: super().show() self.setProgress(percentage) + ## Returns a boolean indicating whether the message is currently visible. @property def visible(self) -> bool: return self._visible diff --git a/plugins/UM3NetworkPrinting/src/Cloud/MeshUploader.py b/plugins/UM3NetworkPrinting/src/Cloud/MeshUploader.py index f0360b83e3..9d9662a82a 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/MeshUploader.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/MeshUploader.py @@ -9,16 +9,25 @@ from UM.Logger import Logger from src.Cloud.Models.CloudPrintJobResponse import CloudPrintJobResponse +## Class responsible for uploading meshes to the cloud in separate requests. class MeshUploader: + + # The maximum amount of times to retry if the server returns one of the RETRY_HTTP_CODES MAX_RETRIES = 10 - BYTES_PER_REQUEST = 256 * 1024 + + # The HTTP codes that should trigger a retry. RETRY_HTTP_CODES = {500, 502, 503, 504} - ## Creates a resumable upload - # \param url: The URL to which we shall upload. - # \param content_length: The total content length of the file, in bytes. - # \param http_method: The HTTP method to be used, e.g. "POST" or "PUT". - # \param timeout: The timeout for each chunk upload. Important: If None, no timeout is applied at all. + # The amount of bytes to send per request + BYTES_PER_REQUEST = 256 * 1024 + + ## Creates a mesh upload object. + # \param manager: The network access manager that will handle the HTTP requests. + # \param print_job: The print job response that was returned by the cloud after registering the upload. + # \param data: The mesh bytes to be uploaded. + # \param on_finished: The method to be called when done. + # \param on_progress: The method to be called when the progress changes (receives a percentage 0-100). + # \param on_error: The method to be called when an error occurs. def __init__(self, manager: QNetworkAccessManager, print_job: CloudPrintJobResponse, data: bytes, on_finished: Callable[[], Any], on_progress: Callable[[int], Any], on_error: Callable[[], Any] ) -> None: @@ -35,13 +44,12 @@ class MeshUploader: self._finished = False self._reply = None # type: Optional[QNetworkReply] + ## Returns the print job for which this object was created. @property def printJob(self): return self._print_job - ## We override _createRequest in order to add the user credentials. - # \param url: The URL to request - # \param content_type: The type of the body contents. + ## Creates a network request to the print job upload URL, adding the needed content range header. def _createRequest(self) -> QNetworkRequest: request = QNetworkRequest(QUrl(self._print_job.upload_url)) request.setHeader(QNetworkRequest.ContentTypeHeader, self._print_job.content_type) @@ -53,21 +61,27 @@ class MeshUploader: return request + ## Determines the bytes that should be uploaded next. + # \return: A tuple with the first and the last byte to upload. def _chunkRange(self) -> Tuple[int, int]: last_byte = min(len(self._data), self._sent_bytes + self.BYTES_PER_REQUEST) return self._sent_bytes, last_byte + ## Starts uploading the mesh. def start(self) -> None: if self._finished: + # reset state. self._sent_bytes = 0 self._retries = 0 self._finished = False self._uploadChunk() + ## Stops uploading the mesh, marking it as finished. def stop(self): Logger.log("i", "Stopped uploading") self._finished = True + ## Uploads a chunk of the mesh to the cloud. def _uploadChunk(self) -> None: if self._finished: raise ValueError("The upload is already finished") @@ -75,16 +89,22 @@ class MeshUploader: first_byte, last_byte = self._chunkRange() request = self._createRequest() + # now send the reply and subscribe to the results self._reply = self._manager.put(request, self._data[first_byte:last_byte]) self._reply.finished.connect(self._finishedCallback) self._reply.uploadProgress.connect(self._progressCallback) self._reply.error.connect(self._errorCallback) + ## Handles an update to the upload progress + # \param bytes_sent: The amount of bytes sent in the current request. + # \param bytes_total: The amount of bytes to send in the current request. def _progressCallback(self, bytes_sent: int, bytes_total: int) -> None: Logger.log("i", "Progress callback %s / %s", bytes_sent, bytes_total) if bytes_total: - self._on_progress(int((self._sent_bytes + bytes_sent) / len(self._data) * 100)) + total_sent = self._sent_bytes + bytes_sent + self._on_progress(int(total_sent / len(self._data) * 100)) + ## Handles an error uploading. def _errorCallback(self) -> None: reply = cast(QNetworkReply, self._reply) body = bytes(reply.readAll()).decode() @@ -92,27 +112,33 @@ class MeshUploader: self.stop() self._on_error() + ## Checks whether a chunk of data was uploaded successfully, starting the next chunk if needed. def _finishedCallback(self) -> None: reply = cast(QNetworkReply, self._reply) Logger.log("i", "Finished callback %s %s", reply.attribute(QNetworkRequest.HttpStatusCodeAttribute), reply.url().toString()) - status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) + status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) # type: int + # check if we should retry the last chunk if self._retries < self.MAX_RETRIES and status_code in self.RETRY_HTTP_CODES: self._retries += 1 Logger.log("i", "Retrying %s/%s request %s", self._retries, self.MAX_RETRIES, reply.url().toString()) self._uploadChunk() return + # Http codes that are not to be retried are assumed to be errors. if status_code > 308: self._errorCallback() return - body = bytes(reply.readAll()).decode() - Logger.log("w", "status_code: %s, Headers: %s, body: %s", status_code, - [bytes(header).decode() for header in reply.rawHeaderList()], body) + Logger.log("d", "status_code: %s, Headers: %s, body: %s", status_code, + [bytes(header).decode() for header in reply.rawHeaderList()], bytes(reply.readAll()).decode()) + self._chunkUploaded() + ## Handles a chunk of data being uploaded, starting the next chunk if needed. + def _chunkUploaded(self) -> None: + # We got a successful response. Let's start the next chunk or report the upload is finished. first_byte, last_byte = self._chunkRange() self._sent_bytes += last_byte - first_byte if self._sent_bytes >= len(self._data): From 53da111617d9363784e3363659653496ebd4c734 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 17 Dec 2018 15:21:48 +0100 Subject: [PATCH 0974/1240] Change "Rating" to "Your rating" --- plugins/Toolbox/resources/qml/ToolboxDetailPage.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml index 92b9fb1198..b9b36cd878 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailPage.qml @@ -88,7 +88,7 @@ Item height: childrenRect.height Label { - text: catalog.i18nc("@label", "Rating") + ":" + text: catalog.i18nc("@label", "Your rating") + ":" font: UM.Theme.getFont("default") color: UM.Theme.getColor("text_medium") renderType: Text.NativeRendering From 135219365423e588303c5bf30e6e94c851bfd4cd Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 17 Dec 2018 15:26:10 +0100 Subject: [PATCH 0975/1240] Ensure that the sourceSize is set correctly --- plugins/Toolbox/resources/qml/RatingWidget.qml | 2 ++ plugins/Toolbox/resources/qml/SmallRatingWidget.qml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/plugins/Toolbox/resources/qml/RatingWidget.qml b/plugins/Toolbox/resources/qml/RatingWidget.qml index 3dcae6d602..441cf238f7 100644 --- a/plugins/Toolbox/resources/qml/RatingWidget.qml +++ b/plugins/Toolbox/resources/qml/RatingWidget.qml @@ -75,6 +75,8 @@ Item background: UM.RecolorImage { source: UM.Theme.getIcon(control.isStarFilled ? "star_filled" : "star_empty") + sourceSize.width: width + sourceSize.height: height // Unfilled stars should always have the default color. Only filled stars should change on hover color: diff --git a/plugins/Toolbox/resources/qml/SmallRatingWidget.qml b/plugins/Toolbox/resources/qml/SmallRatingWidget.qml index 686058f4e8..4950ea9242 100644 --- a/plugins/Toolbox/resources/qml/SmallRatingWidget.qml +++ b/plugins/Toolbox/resources/qml/SmallRatingWidget.qml @@ -17,6 +17,8 @@ Row color: model.user_rating == 0 ? UM.Theme.getColor("rating_star") : UM.Theme.getColor("primary") height: UM.Theme.getSize("rating_star").height width: UM.Theme.getSize("rating_star").width + sourceSize.height: height + sourceSize.width: width } Label From 4d135c13bb8dc5f6971d07eb6682962433826b55 Mon Sep 17 00:00:00 2001 From: alekseisasin Date: Mon, 17 Dec 2018 16:46:02 +0100 Subject: [PATCH 0976/1240] Cura used wrong firmware version for UMO+. The firmware was from UMO. The reason of this issue because it used a heated firmware from his parent(UMO) printer. To avoid this case we need to define externally the firmware_hbk_file property in umo+ defintion file CURA-5994 --- resources/definitions/ultimaker_original_plus.def.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/definitions/ultimaker_original_plus.def.json b/resources/definitions/ultimaker_original_plus.def.json index 5ad7ae66e8..bdb8a3d788 100644 --- a/resources/definitions/ultimaker_original_plus.def.json +++ b/resources/definitions/ultimaker_original_plus.def.json @@ -16,7 +16,8 @@ { "0": "ultimaker_original_plus_extruder_0" }, - "firmware_file": "MarlinUltimaker-UMOP-{baudrate}.hex" + "firmware_file": "MarlinUltimaker-UMOP-{baudrate}.hex", + "firmware_hbk_file": "MarlinUltimaker-UMOP-{baudrate}.hex" }, "overrides": { From 5c697ab5bed61ad4169db31ac676cdd2f056d9c8 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Mon, 17 Dec 2018 16:58:51 +0100 Subject: [PATCH 0977/1240] Make hints clickable Contributes to CL-1151 --- .../resources/qml/MonitorCarousel.qml | 46 +++++++++++-------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml index ea1449ac8d..476e3c2aa1 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml @@ -25,7 +25,7 @@ Item anchors { right: leftButton.left - rightMargin: 12 * screenScaleFactor + rightMargin: 12 * screenScaleFactor // TODO: Theme! left: parent.left } height: parent.height @@ -40,15 +40,20 @@ Item GradientStop { position: 0.0 - color: "#fff6f6f6" + color: "#fff6f6f6" // TODO: Theme! } GradientStop { position: 1.0 - color: "#66f6f6f6" + color: "#66f6f6f6" // TODO: Theme! } } } + MouseArea + { + anchors.fill: parent + onClicked: navigateTo(currentIndex - 1) + } } Button @@ -58,7 +63,7 @@ Item { verticalCenter: parent.verticalCenter right: centerSection.left - rightMargin: 12 * screenScaleFactor + rightMargin: 12 * screenScaleFactor // TODO: Theme! } width: 36 * screenScaleFactor // TODO: Theme! height: 72 * screenScaleFactor // TODO: Theme! @@ -79,10 +84,10 @@ Item UM.RecolorImage { anchors.centerIn: parent - width: 18 - height: width - sourceSize.width: width - sourceSize.height: width + width: 18 // TODO: Theme! + height: width // TODO: Theme! + sourceSize.width: width // TODO: Theme! + sourceSize.height: width // TODO: Theme! color: "#152950" // TODO: Theme! source: UM.Theme.getIcon("arrow_left") } @@ -105,7 +110,7 @@ Item { id: tiles height: childrenRect.height - width: 5 * tileWidth + 4 * tileSpacing + width: 5 * tileWidth + 4 * tileSpacing // TODO: Theme! x: 0 z: 0 Behavior on x @@ -137,7 +142,7 @@ Item { verticalCenter: parent.verticalCenter left: centerSection.right - leftMargin: 12 * screenScaleFactor + leftMargin: 12 * screenScaleFactor // TODO: Theme! } width: 36 * screenScaleFactor // TODO: Theme! height: 72 * screenScaleFactor // TODO: Theme! @@ -158,10 +163,10 @@ Item UM.RecolorImage { anchors.centerIn: parent - width: 18 - height: width - sourceSize.width: width - sourceSize.height: width + width: 18 // TODO: Theme! + height: width // TODO: Theme! + sourceSize.width: width // TODO: Theme! + sourceSize.height: width // TODO: Theme! color: "#152950" // TODO: Theme! source: UM.Theme.getIcon("arrow_right") } @@ -174,7 +179,7 @@ Item anchors { left: rightButton.right - leftMargin: 12 * screenScaleFactor + leftMargin: 12 * screenScaleFactor // TODO: Theme! right: parent.right } height: centerSection.height @@ -190,15 +195,20 @@ Item GradientStop { position: 0.0 - color: "#66f6f6f6" + color: "#66f6f6f6" // TODO: Theme! } GradientStop { position: 1.0 - color: "#fff6f6f6" + color: "#fff6f6f6" // TODO: Theme! } } } + MouseArea + { + anchors.fill: parent + onClicked: navigateTo(currentIndex + 1) + } } Item @@ -223,7 +233,7 @@ Item color: model.index == currentIndex ? "#777777" : "#d8d8d8" // TODO: Theme! radius: Math.floor(width / 2) width: 12 * screenScaleFactor // TODO: Theme! - height: width + height: width // TODO: Theme! } onClicked: navigateTo(model.index) } From d6212d5a398d19bf72d9a718b316270629dcef52 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 17 Dec 2018 17:29:24 +0100 Subject: [PATCH 0978/1240] Add elide in the showcase title and reduce the height a bit so there are only 2 lines for the title. --- plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml | 1 + resources/themes/cura-light/theme.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml index bb4f34163d..c8c1e56c82 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml @@ -47,6 +47,7 @@ Rectangle height: UM.Theme.getSize("toolbox_heading_label").height width: parent.width - UM.Theme.getSize("default_margin").width wrapMode: Text.WordWrap + elide: Text.ElideRight font: UM.Theme.getFont("medium_bold") } UM.RecolorImage diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index ea6c92b479..83a51e5431 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -495,7 +495,7 @@ "toolbox_back_button": [4.0, 2.0], "toolbox_installed_tile": [1.0, 8.0], "toolbox_property_label": [1.0, 2.0], - "toolbox_heading_label": [1.0, 4.0], + "toolbox_heading_label": [1.0, 3.8], "toolbox_header": [1.0, 4.0], "toolbox_header_highlight": [0.25, 0.25], "toolbox_progress_bar": [8.0, 0.5], From 0e294481ff9e2ffa4e2dbe24dda5b527a74a0fcc Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 17 Dec 2018 17:32:11 +0100 Subject: [PATCH 0979/1240] Improve readability of printer_type_label for dark theme --- resources/themes/cura-dark/theme.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/themes/cura-dark/theme.json b/resources/themes/cura-dark/theme.json index cade342488..537fccbc5c 100644 --- a/resources/themes/cura-dark/theme.json +++ b/resources/themes/cura-dark/theme.json @@ -40,6 +40,8 @@ "text_scene": [255, 255, 255, 162], "text_scene_hover": [255, 255, 255, 204], + "printer_type_label_background": [95, 95, 95, 255], + "error": [212, 31, 53, 255], "disabled": [32, 32, 32, 255], From 3f7c9227340a57f1d031910a34ff6e23221c9fb8 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 17 Dec 2018 17:37:12 +0100 Subject: [PATCH 0980/1240] Add missing type for g-code reader --- plugins/GCodeReader/FlavorParser.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/GCodeReader/FlavorParser.py b/plugins/GCodeReader/FlavorParser.py index 6fe2cb5260..baf21d47ce 100644 --- a/plugins/GCodeReader/FlavorParser.py +++ b/plugins/GCodeReader/FlavorParser.py @@ -364,6 +364,8 @@ class FlavorParser: self._layer_type = LayerPolygon.SupportType elif type == "FILL": self._layer_type = LayerPolygon.InfillType + elif type == "SUPPORT-INTERFACE": + self._layer_type = LayerPolygon.SupportInterfaceType else: Logger.log("w", "Encountered a unknown type (%s) while parsing g-code.", type) From 7d6e698f431c24126d0521b408bd2dafb79788ec Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Mon, 17 Dec 2018 17:37:21 +0100 Subject: [PATCH 0981/1240] Fix (USB) print monitor in dark theme --- plugins/USBPrinting/MonitorItem.qml | 2 ++ resources/qml/PrintMonitor.qml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/USBPrinting/MonitorItem.qml b/plugins/USBPrinting/MonitorItem.qml index 8041698ef0..c86353f814 100644 --- a/plugins/USBPrinting/MonitorItem.qml +++ b/plugins/USBPrinting/MonitorItem.qml @@ -13,6 +13,8 @@ Component { Rectangle { + color: UM.Theme.getColor("main_background") + anchors.right: parent.right width: parent.width * 0.3 anchors.top: parent.top diff --git a/resources/qml/PrintMonitor.qml b/resources/qml/PrintMonitor.qml index 6d8edf0deb..d44acf0adb 100644 --- a/resources/qml/PrintMonitor.qml +++ b/resources/qml/PrintMonitor.qml @@ -12,7 +12,7 @@ import Cura 1.0 as Cura import "PrinterOutput" -Rectangle +Item { id: base UM.I18nCatalog { id: catalog; name: "cura"} From 74bec96ce810a569b02161dd0b14f7032194341f Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 17 Dec 2018 17:41:02 +0100 Subject: [PATCH 0982/1240] Fix a relative import --- plugins/UM3NetworkPrinting/src/Cloud/MeshUploader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/MeshUploader.py b/plugins/UM3NetworkPrinting/src/Cloud/MeshUploader.py index 9d9662a82a..cb721b872e 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/MeshUploader.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/MeshUploader.py @@ -6,7 +6,7 @@ from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply, QNetworkAccessManage from typing import Optional, Callable, Any, Tuple, cast from UM.Logger import Logger -from src.Cloud.Models.CloudPrintJobResponse import CloudPrintJobResponse +from .Models.CloudPrintJobResponse import CloudPrintJobResponse ## Class responsible for uploading meshes to the cloud in separate requests. From d395c38436712979639747c202741f788dbd388d Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 17 Dec 2018 17:47:33 +0100 Subject: [PATCH 0983/1240] Removed collapse / expand all I don't think it ever worked. --- resources/qml/Settings/SettingView.qml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index c1b4b28d1d..972cbcdbb1 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -558,17 +558,6 @@ Item onTriggered: Cura.Actions.configureSettingVisibility.trigger(contextMenu); } - MenuSeparator {} - MenuItem - { - text: catalog.i18nc("@action:inmenu", "Collapse All") - onTriggered: definitionsModel.collapseAll() - } - MenuItem - { - text: catalog.i18nc("@action:inmenu", "Expand All") - onTriggered: definitionsModel.expandRecursive() - } } UM.SettingPropertyProvider From 62d20ac773b617e1ed0fe0a43ce05913ca59b789 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 17 Dec 2018 17:55:40 +0100 Subject: [PATCH 0984/1240] Change the base color of the tooltips to align with the designs Contributes to CURA-6004. --- resources/themes/cura-light/theme.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 83a51e5431..2b9ce3d218 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -229,7 +229,7 @@ "checkbox_disabled": [223, 223, 223, 255], "checkbox_text": [35, 35, 35, 255], - "tooltip": [68, 192, 255, 255], + "tooltip": [25, 25, 25, 255], "tooltip_text": [255, 255, 255, 255], "message_background": [255, 255, 255, 255], From 6017c2b4d2cd52110856e3cb835638a40458a6b6 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 18 Dec 2018 08:55:55 +0100 Subject: [PATCH 0985/1240] Replace isProfileUserCreated with hasCustomQuality CURA-6028 --- cura/Settings/MachineManager.py | 4 +++ cura/Settings/SimpleModeSettingsManager.py | 32 ------------------- .../RecommendedQualityProfileSelector.qml | 23 ++++--------- 3 files changed, 10 insertions(+), 49 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index e26b82e487..5e0bbea1ee 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -1529,6 +1529,10 @@ class MachineManager(QObject): def activeQualityChangesGroup(self) -> Optional["QualityChangesGroup"]: return self._current_quality_changes_group + @pyqtProperty(bool, notify = activeQualityChangesGroupChanged) + def hasCustomQuality(self) -> bool: + return self._current_quality_changes_group is not None + @pyqtProperty(str, notify = activeQualityGroupChanged) def activeQualityOrQualityChangesName(self) -> str: name = empty_quality_container.getName() diff --git a/cura/Settings/SimpleModeSettingsManager.py b/cura/Settings/SimpleModeSettingsManager.py index b22aea15ea..b1896a9205 100644 --- a/cura/Settings/SimpleModeSettingsManager.py +++ b/cura/Settings/SimpleModeSettingsManager.py @@ -17,15 +17,11 @@ class SimpleModeSettingsManager(QObject): self._is_profile_user_created = False # True when profile was custom created by user self._machine_manager.activeStackValueChanged.connect(self._updateIsProfileCustomized) - self._machine_manager.activeQualityGroupChanged.connect(self.updateIsProfileUserCreated) - self._machine_manager.activeQualityChangesGroupChanged.connect(self.updateIsProfileUserCreated) # update on create as the activeQualityChanged signal is emitted before this manager is created when Cura starts self._updateIsProfileCustomized() - self.updateIsProfileUserCreated() isProfileCustomizedChanged = pyqtSignal() - isProfileUserCreatedChanged = pyqtSignal() @pyqtProperty(bool, notify = isProfileCustomizedChanged) def isProfileCustomized(self): @@ -58,34 +54,6 @@ class SimpleModeSettingsManager(QObject): self._is_profile_customized = has_customized_user_settings self.isProfileCustomizedChanged.emit() - @pyqtProperty(bool, notify = isProfileUserCreatedChanged) - def isProfileUserCreated(self): - return self._is_profile_user_created - - @pyqtSlot() - def updateIsProfileUserCreated(self) -> None: - quality_changes_keys = set() # type: Set[str] - - if not self._machine_manager.activeMachine: - return - - global_stack = self._machine_manager.activeMachine - - # check quality changes settings in the global stack - quality_changes_keys.update(global_stack.qualityChanges.getAllKeys()) - - # check quality changes settings in the extruder stacks - if global_stack.extruders: - for extruder_stack in global_stack.extruders.values(): - quality_changes_keys.update(extruder_stack.qualityChanges.getAllKeys()) - - # check if the qualityChanges container is not empty (meaning it is a user created profile) - has_quality_changes = len(quality_changes_keys) > 0 - - if has_quality_changes != self._is_profile_user_created: - self._is_profile_user_created = has_quality_changes - self.isProfileUserCreatedChanged.emit() - # These are the settings included in the Simple ("Recommended") Mode, so only when the other settings have been # changed, we consider it as a user customized profile in the Simple ("Recommended") Mode. __ignored_custom_setting_keys = ["support_enable", diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml index e6b3f1b9eb..1e71134404 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml @@ -39,17 +39,6 @@ Item { target: Cura.QualityProfilesDropDownMenuModel onItemsChanged: qualityModel.update() - onDataChanged: - { - // If a custom profile is selected and then a user decides to change any of setting the slider should show - // the reset button. After clicking the reset button the QualityProfilesDropDownMenuModel(ListModel) is - // updated before the property isProfileCustomized is called to update. - if (Cura.SimpleModeSettingsManager.isProfileCustomized) - { - Cura.SimpleModeSettingsManager.updateIsProfileUserCreated() - } - qualityModel.update() - } } Connections { @@ -97,7 +86,7 @@ Item if (Cura.MachineManager.activeQualityType == qualityItem.quality_type) { // set to -1 when switching to user created profile so all ticks are clickable - if (Cura.SimpleModeSettingsManager.isProfileUserCreated) + if (Cura.MachineManager.hasCustomQuality) { qualityModel.qualitySliderActiveIndex = -1 } @@ -192,7 +181,7 @@ Item { id: customisedSettings - visible: Cura.SimpleModeSettingsManager.isProfileCustomized || Cura.SimpleModeSettingsManager.isProfileUserCreated + visible: Cura.SimpleModeSettingsManager.isProfileCustomized || Cura.MachineManager.hasCustomQuality height: visible ? UM.Theme.getSize("print_setup_icon").height : 0 width: height anchors @@ -358,7 +347,7 @@ Item { anchors.fill: parent hoverEnabled: true - enabled: !Cura.SimpleModeSettingsManager.isProfileUserCreated + enabled: !Cura.MachineManager.hasCustomQuality onEntered: { var tooltipContent = catalog.i18nc("@tooltip", "This quality profile is not available for your current material and nozzle configuration. Please change these to enable this quality profile") @@ -417,7 +406,7 @@ Item implicitWidth: UM.Theme.getSize("print_setup_slider_handle").width implicitHeight: implicitWidth radius: Math.round(implicitWidth / 2) - visible: !Cura.SimpleModeSettingsManager.isProfileCustomized && !Cura.SimpleModeSettingsManager.isProfileUserCreated && qualityModel.existingQualityProfile + visible: !Cura.SimpleModeSettingsManager.isProfileCustomized && !Cura.MachineManager.hasCustomQuality && qualityModel.existingQualityProfile } } @@ -441,7 +430,7 @@ Item anchors.fill: parent hoverEnabled: true acceptedButtons: Qt.NoButton - enabled: !Cura.SimpleModeSettingsManager.isProfileUserCreated + enabled: !Cura.MachineManager.hasCustomQuality } } @@ -451,7 +440,7 @@ Item { anchors.fill: parent hoverEnabled: true - visible: Cura.SimpleModeSettingsManager.isProfileUserCreated + visible: Cura.MachineManager.hasCustomQuality onEntered: { From 9f52a52ea3ee9e497c8b7dc220a9077ad7c64c59 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 18 Dec 2018 09:32:11 +0100 Subject: [PATCH 0986/1240] Fix ExtruderModel reference in machine settings dialog --- .../MachineSettingsAction.qml | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/plugins/MachineSettingsAction/MachineSettingsAction.qml b/plugins/MachineSettingsAction/MachineSettingsAction.qml index d8efe6f8b8..a9900070a8 100644 --- a/plugins/MachineSettingsAction/MachineSettingsAction.qml +++ b/plugins/MachineSettingsAction/MachineSettingsAction.qml @@ -14,20 +14,9 @@ Cura.MachineAction { id: base property var extrudersModel: CuraApplication.getExtrudersModel() - property int extruderTabsCount: 0 property var activeMachineId: Cura.MachineManager.activeMachine != null ? Cura.MachineManager.activeMachine.id : "" - Connections - { - target: base.extrudersModel - onModelChanged: - { - var extruderCount = base.extrudersModel.count; - base.extruderTabsCount = extruderCount; - } - } - Connections { target: dialog ? dialog : null @@ -357,11 +346,11 @@ Cura.MachineAction Repeater { id: extruderTabsRepeater - model: base.extruderTabsCount + model: base.extrudersModel Tab { - title: base.extrudersModel.getItem(index).name + title: model.name anchors.margins: UM.Theme.getSize("default_margin").width Column From 84a7f2e5a2d1ec96f74ffb15b9b08f053af44198 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 18 Dec 2018 09:40:08 +0100 Subject: [PATCH 0987/1240] Fix review comments CURA-6011 --- cura/PrintersModel.py | 2 +- cura/Settings/MachineManager.py | 4 ++-- plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py | 10 +++++----- .../UM3NetworkPrinting/src/UM3OutputDevicePlugin.py | 2 +- plugins/USBPrinting/USBPrinterOutputDevice.py | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cura/PrintersModel.py b/cura/PrintersModel.py index 0c58d9bf8c..8b5d2f6cc9 100644 --- a/cura/PrintersModel.py +++ b/cura/PrintersModel.py @@ -54,7 +54,7 @@ class PrintersModel(ListModel): for container_stack in container_stacks: connection_type = container_stack.getMetaDataEntry("connection_type") - has_remote_connection = connection_type in [str(ConnectionType.NetworkConnection), str(ConnectionType.CloudConnection)] + has_remote_connection = connection_type in [ConnectionType.NetworkConnection.value, ConnectionType.CloudConnection.value] if container_stack.getMetaDataEntry("hidden", False) in ["True", True]: continue diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index c51c26f1b9..e1ae0d761c 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -521,11 +521,11 @@ class MachineManager(QObject): def printerConnected(self): return bool(self._printer_output_devices) - @pyqtProperty(bool, notify=printerConnectedStatusChanged) + @pyqtProperty(bool, notify = printerConnectedStatusChanged) def activeMachineHasRemoteConnection(self) -> bool: if self._global_container_stack: connection_type = self._global_container_stack.getMetaDataEntry("connection_type") - return connection_type in [str(ConnectionType.NetworkConnection), str(ConnectionType.CloudConnection)] + return connection_type in [ConnectionType.NetworkConnection.value, ConnectionType.CloudConnection.value] return False def activeMachineNetworkKey(self) -> str: diff --git a/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py b/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py index 2e59810317..6ce99e4891 100644 --- a/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py +++ b/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py @@ -136,13 +136,13 @@ class DiscoverUM3Action(MachineAction): global_container_stack.removeMetaDataEntry("network_authentication_key") CuraApplication.getInstance().getMachineManager().replaceContainersMetadata(key = "um_network_key", value = previous_network_key, new_value = printer_device.key) - if "um_connection_type" in meta_data: - previous_connection_type = meta_data["um_connection_type"] - global_container_stack.setMetaDataEntry("um_connection_type", printer_device.getConnectionType().value) - CuraApplication.getInstance().getMachineManager().replaceContainersMetadata(key = "um_connection_type", value = previous_connection_type, new_value = printer_device.getConnectionType().value) + if "connection_type" in meta_data: + previous_connection_type = meta_data["connection_type"] + global_container_stack.setMetaDataEntry("connection_type", printer_device.getConnectionType().value) + CuraApplication.getInstance().getMachineManager().replaceContainersMetadata(key = "connection_type", value = previous_connection_type, new_value = printer_device.getConnectionType().value) else: global_container_stack.setMetaDataEntry("um_network_key", printer_device.key) - global_container_stack.setMetaDataEntry("um_connection_type", printer_device.getConnectionType().value) + global_container_stack.setMetaDataEntry("connection_type", printer_device.getConnectionType().value) if self._network_plugin: # Ensure that the connection states are refreshed. diff --git a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py index 43290c8e44..80212fcf00 100644 --- a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py @@ -283,7 +283,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): global_container_stack = Application.getInstance().getGlobalContainerStack() if global_container_stack and device.getId() == global_container_stack.getMetaDataEntry("um_network_key"): - global_container_stack.setMetaDataEntry("connection_type", str(device.getConnectionType())) + global_container_stack.setMetaDataEntry("connection_type", device.getConnectionType().value) device.connect() device.connectionStateChanged.connect(self._onDeviceConnectionStateChanged) diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index 08cf29baf4..19a703e605 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -29,7 +29,7 @@ catalog = i18nCatalog("cura") class USBPrinterOutputDevice(PrinterOutputDevice): def __init__(self, serial_port: str, baud_rate: Optional[int] = None) -> None: - super().__init__(serial_port, connection_type=ConnectionType.UsbConnection) + super().__init__(serial_port, connection_type = ConnectionType.UsbConnection) self.setName(catalog.i18nc("@item:inmenu", "USB printing")) self.setShortDescription(catalog.i18nc("@action:button Preceded by 'Ready to'.", "Print via USB")) self.setDescription(catalog.i18nc("@info:tooltip", "Print via USB")) From aa0c8c75ee40373b1b2faac4055b72b06164d3ee Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 18 Dec 2018 09:45:26 +0100 Subject: [PATCH 0988/1240] Add a sane default to connection type CURA-6011 --- cura/PrinterOutputDevice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index b33993f150..3102d73bb0 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -74,7 +74,7 @@ class PrinterOutputDevice(QObject, OutputDevice): # Signal to indicate that the configuration of one of the printers has changed. uniqueConfigurationsChanged = pyqtSignal() - def __init__(self, device_id: str, connection_type: "ConnectionType", parent: QObject = None) -> None: + def __init__(self, device_id: str, connection_type: "ConnectionType" = ConnectionType.Unknown, parent: QObject = None) -> None: super().__init__(device_id = device_id, parent = parent) # type: ignore # MyPy complains with the multiple inheritance self._printers = [] # type: List[PrinterOutputModel] From be9d35d45f63d5a3c6896abcfca1a74165ec0f1b Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 18 Dec 2018 09:47:51 +0100 Subject: [PATCH 0989/1240] Fix some minor layout issues CURA-6011 --- resources/qml/PrinterSelector/MachineSelectorList.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/PrinterSelector/MachineSelectorList.qml b/resources/qml/PrinterSelector/MachineSelectorList.qml index 52cea0ea80..ea8068fa95 100644 --- a/resources/qml/PrinterSelector/MachineSelectorList.qml +++ b/resources/qml/PrinterSelector/MachineSelectorList.qml @@ -11,7 +11,6 @@ ListView { id: listView height: childrenRect.height - width: 200 model: Cura.PrintersModel {} section.property: "hasRemoteConnection" @@ -19,6 +18,7 @@ ListView { text: section == "true" ? catalog.i18nc("@label", "Connected printers") : catalog.i18nc("@label", "Preset printers") width: parent.width + height: visible ? contentHeight + 2 * UM.Theme.getSize("default_margin").height : 0 leftPadding: UM.Theme.getSize("default_margin").width renderType: Text.NativeRendering font: UM.Theme.getFont("medium") From cc462ef3a609be1df9ee0fb9d94ce4a541db2183 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 18 Dec 2018 10:29:44 +0100 Subject: [PATCH 0990/1240] Add a placeholder for a pattern in the header, if the theme wants to add one. --- resources/qml/Cura.qml | 11 + .../cura-light/images/header_pattern.svg | 1901 ----------------- 2 files changed, 11 insertions(+), 1901 deletions(-) delete mode 100644 resources/themes/cura-light/images/header_pattern.svg diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 8ab943b93b..8a34c7e219 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -123,6 +123,17 @@ UM.MainWindow } } } + + // This is a placehoder for adding a pattern in the header + Image + { + id: backgroundPattern + anchors.fill: parent + fillMode: Image.Tile + source: UM.Theme.getImage("header_pattern") + horizontalAlignment: Image.AlignLeft + verticalAlignment: Image.AlignTop + } } MainWindowHeader diff --git a/resources/themes/cura-light/images/header_pattern.svg b/resources/themes/cura-light/images/header_pattern.svg deleted file mode 100644 index eff5f01cfa..0000000000 --- a/resources/themes/cura-light/images/header_pattern.svg +++ /dev/null @@ -1,1901 +0,0 @@ - - - - Desktop HD - Created with Sketch. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From dff9a3dfb24301d27e6ef53365bd4786d9d926a9 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 18 Dec 2018 10:52:11 +0100 Subject: [PATCH 0991/1240] Fix None problem in project loading --- plugins/3MFReader/ThreeMFWorkspaceReader.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 9ee2ef0dd4..55296979b5 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -794,7 +794,8 @@ class ThreeMFWorkspaceReader(WorkspaceReader): # Clear all existing containers quality_changes_info.global_info.container.clear() for container_info in quality_changes_info.extruder_info_dict.values(): - container_info.container.clear() + if container_info.container: + container_info.container.clear() # Loop over everything and override the existing containers global_info = quality_changes_info.global_info From 9b6e52a5be95471b96baa4fe53f674c719f37b00 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 18 Dec 2018 13:08:18 +0100 Subject: [PATCH 0992/1240] Make monitor item background themable --- plugins/USBPrinting/MonitorItem.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/USBPrinting/MonitorItem.qml b/plugins/USBPrinting/MonitorItem.qml index 8041698ef0..14b1402e50 100644 --- a/plugins/USBPrinting/MonitorItem.qml +++ b/plugins/USBPrinting/MonitorItem.qml @@ -17,6 +17,7 @@ Component width: parent.width * 0.3 anchors.top: parent.top anchors.bottom: parent.bottom + color: UM.Theme.getColor("main_background") Cura.PrintMonitor { From 627c647fbcca9555807770fa640fa69b47c9877c Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Tue, 18 Dec 2018 13:17:25 +0100 Subject: [PATCH 0993/1240] Center navigation dots Contributes to CL-1151 --- .../resources/qml/MonitorCarousel.qml | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml index 476e3c2aa1..a3e2517b45 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml @@ -211,7 +211,7 @@ Item } } - Item + Row { id: navigationDots anchors @@ -220,23 +220,20 @@ Item top: centerSection.bottom topMargin: 36 * screenScaleFactor // TODO: Theme! } - Row + spacing: 8 * screenScaleFactor // TODO: Theme! + Repeater { - spacing: 8 * screenScaleFactor // TODO: Theme! - Repeater + model: OutputDevice.printers + Button { - model: OutputDevice.printers - Button + background: Rectangle { - background: Rectangle - { - color: model.index == currentIndex ? "#777777" : "#d8d8d8" // TODO: Theme! - radius: Math.floor(width / 2) - width: 12 * screenScaleFactor // TODO: Theme! - height: width // TODO: Theme! - } - onClicked: navigateTo(model.index) + color: model.index == currentIndex ? "#777777" : "#d8d8d8" // TODO: Theme! + radius: Math.floor(width / 2) + width: 12 * screenScaleFactor // TODO: Theme! + height: width // TODO: Theme! } + onClicked: navigateTo(model.index) } } } From e885b527370a84d0399962b436e3da472d8ac85a Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 18 Dec 2018 13:42:23 +0100 Subject: [PATCH 0994/1240] Add time prime tower into Cura.proto --- plugins/CuraEngineBackend/Cura.proto | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/CuraEngineBackend/Cura.proto b/plugins/CuraEngineBackend/Cura.proto index 292330576b..c4f934bc00 100644 --- a/plugins/CuraEngineBackend/Cura.proto +++ b/plugins/CuraEngineBackend/Cura.proto @@ -29,7 +29,7 @@ message Object bytes normals = 3; //An array of 3 floats. bytes indices = 4; //An array of ints. repeated Setting settings = 5; // Setting override per object, overruling the global settings. - string name = 6; + string name = 6; //Mesh name } message Progress @@ -108,8 +108,9 @@ message PrintTimeMaterialEstimates { // The print time for each feature and mate float time_travel = 9; float time_retract = 10; float time_support_interface = 11; + float time_prime_tower = 12; - repeated MaterialEstimates materialEstimates = 12; // materialEstimates data + repeated MaterialEstimates materialEstimates = 13; // materialEstimates data } message MaterialEstimates { From 279812e4ff356170463dbf0e693e86689a063945 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 18 Dec 2018 13:55:12 +0100 Subject: [PATCH 0995/1240] Round width of buttons They were getting rendered a bit weird if your pixel scale was not an integer (for me it's 1.25). --- resources/qml/Preferences/MachinesPage.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/Preferences/MachinesPage.qml b/resources/qml/Preferences/MachinesPage.qml index bc75b9bc72..4acefdb493 100644 --- a/resources/qml/Preferences/MachinesPage.qml +++ b/resources/qml/Preferences/MachinesPage.qml @@ -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 QtQuick 2.7 @@ -91,7 +91,7 @@ UM.ManagementPage Item { - width: childrenRect.width + 2 * screenScaleFactor + width: Math.round(childrenRect.width + 2 * screenScaleFactor) height: childrenRect.height Button { From 1ac5403c21a6b55f372f19329328ef618ecf03fb Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Tue, 18 Dec 2018 15:52:26 +0100 Subject: [PATCH 0996/1240] Remove extruder tabs from tabView CURA-6036 --- .../MachineSettingsAction/MachineSettingsAction.qml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/plugins/MachineSettingsAction/MachineSettingsAction.qml b/plugins/MachineSettingsAction/MachineSettingsAction.qml index a9900070a8..60dd31dcae 100644 --- a/plugins/MachineSettingsAction/MachineSettingsAction.qml +++ b/plugins/MachineSettingsAction/MachineSettingsAction.qml @@ -348,6 +348,17 @@ Cura.MachineAction id: extruderTabsRepeater model: base.extrudersModel + + onItemAdded: + { + settingsTabs.addTab(index + 1, item) + } + + onItemRemoved: + { + settingsTabs.removeTab(index + 1) + } + Tab { title: model.name From 1b11164340d93ee6f955a6b710dd2dbe3865489a Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 18 Dec 2018 16:09:19 +0100 Subject: [PATCH 0997/1240] Remove unused import and add documentation --- cura/Machines/Models/FavoriteMaterialsModel.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cura/Machines/Models/FavoriteMaterialsModel.py b/cura/Machines/Models/FavoriteMaterialsModel.py index cc273e55ce..98a2a01597 100644 --- a/cura/Machines/Models/FavoriteMaterialsModel.py +++ b/cura/Machines/Models/FavoriteMaterialsModel.py @@ -1,10 +1,9 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from UM.Logger import Logger from cura.Machines.Models.BaseMaterialsModel import BaseMaterialsModel - +## Model that shows the list of favorite materials. class FavoriteMaterialsModel(BaseMaterialsModel): def __init__(self, parent = None): super().__init__(parent) From da69c16e0962231b2d8ae14cc5684d5788f57bf8 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 18 Dec 2018 16:14:13 +0100 Subject: [PATCH 0998/1240] Split the constants in to files, indicating which constants each file stores. Contributes to CURA-6005. --- cura/API/Account.py | 4 +- ...uraConstants.py => ApplicationMetadata.py} | 96 +++++++------------ cura/CuraApplication.py | 14 +-- cura/UltimakerCloudAuthentication.py | 24 +++++ plugins/CuraDrive/src/Settings.py | 4 +- plugins/Toolbox/src/Toolbox.py | 10 +- 6 files changed, 76 insertions(+), 76 deletions(-) rename cura/{CuraConstants.py => ApplicationMetadata.py} (50%) create mode 100644 cura/UltimakerCloudAuthentication.py diff --git a/cura/API/Account.py b/cura/API/Account.py index 7b4bc32e99..47f67af8ce 100644 --- a/cura/API/Account.py +++ b/cura/API/Account.py @@ -6,7 +6,7 @@ from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, pyqtProperty from UM.i18n import i18nCatalog from UM.Message import Message -from cura import CuraConstants +from cura import UltimakerCloudAuthentication from cura.OAuth2.AuthorizationService import AuthorizationService from cura.OAuth2.Models import OAuth2Settings @@ -38,7 +38,7 @@ class Account(QObject): self._logged_in = False self._callback_port = 32118 - self._oauth_root = CuraConstants.CuraCloudAccountAPIRoot + self._oauth_root = UltimakerCloudAuthentication.CuraCloudAccountAPIRoot self._oauth_settings = OAuth2Settings( OAUTH_SERVER_URL= self._oauth_root, diff --git a/cura/CuraConstants.py b/cura/ApplicationMetadata.py similarity index 50% rename from cura/CuraConstants.py rename to cura/ApplicationMetadata.py index 7ca8ea865b..e2ac4453eb 100644 --- a/cura/CuraConstants.py +++ b/cura/ApplicationMetadata.py @@ -1,60 +1,36 @@ -# -# This file contains all constant values in Cura -# - -# ------------- -# Cura Versions -# ------------- -DEFAULT_CURA_DISPLAY_NAME = "Ultimaker Cura" -DEFAULT_CURA_VERSION = "master" -DEFAULT_CURA_BUILD_TYPE = "" -DEFAULT_CURA_DEBUG_MODE = False -DEFAULT_CURA_SDK_VERSION = "5.0.0" - -try: - from cura.CuraVersion import CuraAppDisplayName # type: ignore -except ImportError: - CuraAppDisplayName = DEFAULT_CURA_DISPLAY_NAME - -try: - from cura.CuraVersion import CuraVersion # type: ignore -except ImportError: - CuraVersion = DEFAULT_CURA_VERSION # [CodeStyle: Reflecting imported value] - -try: - from cura.CuraVersion import CuraBuildType # type: ignore -except ImportError: - CuraBuildType = DEFAULT_CURA_BUILD_TYPE - -try: - from cura.CuraVersion import CuraDebugMode # type: ignore -except ImportError: - CuraDebugMode = DEFAULT_CURA_DEBUG_MODE - -try: - from cura.CuraVersion import CuraSDKVersion # type: ignore -except ImportError: - CuraSDKVersion = DEFAULT_CURA_SDK_VERSION - - -# --------- -# Cloud API -# --------- -DEFAULT_CLOUD_API_ROOT = "https://api.ultimaker.com" # type: str -DEFAULT_CLOUD_API_VERSION = "1" # type: str -DEFAULT_CLOUD_ACCOUNT_API_ROOT = "https://account.ultimaker.com" # type: str - -try: - from cura.CuraVersion import CuraCloudAPIRoot # type: ignore -except ImportError: - CuraCloudAPIRoot = DEFAULT_CLOUD_API_ROOT - -try: - from cura.CuraVersion import CuraCloudAPIVersion # type: ignore -except ImportError: - CuraCloudAPIVersion = DEFAULT_CLOUD_API_VERSION - -try: - from cura.CuraVersion import CuraCloudAccountAPIRoot # type: ignore -except ImportError: - CuraCloudAccountAPIRoot = DEFAULT_CLOUD_ACCOUNT_API_ROOT +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +# --------- +# Genearl constants used in Cura +# --------- +DEFAULT_CURA_DISPLAY_NAME = "Ultimaker Cura" +DEFAULT_CURA_VERSION = "master" +DEFAULT_CURA_BUILD_TYPE = "" +DEFAULT_CURA_DEBUG_MODE = False +DEFAULT_CURA_SDK_VERSION = "6.0.0" + +try: + from cura.CuraVersion import CuraAppDisplayName # type: ignore +except ImportError: + CuraAppDisplayName = DEFAULT_CURA_DISPLAY_NAME + +try: + from cura.CuraVersion import CuraVersion # type: ignore +except ImportError: + CuraVersion = DEFAULT_CURA_VERSION # [CodeStyle: Reflecting imported value] + +try: + from cura.CuraVersion import CuraBuildType # type: ignore +except ImportError: + CuraBuildType = DEFAULT_CURA_BUILD_TYPE + +try: + from cura.CuraVersion import CuraDebugMode # type: ignore +except ImportError: + CuraDebugMode = DEFAULT_CURA_DEBUG_MODE + +try: + from cura.CuraVersion import CuraSDKVersion # type: ignore +except ImportError: + CuraSDKVersion = DEFAULT_CURA_SDK_VERSION diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index a7845708e4..748c5c2fc4 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -115,7 +115,7 @@ from cura.ObjectsModel import ObjectsModel from cura.PrinterOutput.NetworkMJPGImage import NetworkMJPGImage -from cura import CuraConstants +from cura import ApplicationMetadata from UM.FlameProfiler import pyqtSlot from UM.Decorators import override @@ -164,11 +164,11 @@ class CuraApplication(QtApplication): def __init__(self, *args, **kwargs): super().__init__(name = "cura", - app_display_name = CuraConstants.CuraAppDisplayName, - version = CuraConstants.CuraVersion, - api_version = CuraConstants.CuraSDKVersion, - buildtype = CuraConstants.CuraBuildType, - is_debug_mode = CuraConstants.CuraDebugMode, + app_display_name = ApplicationMetadata.CuraAppDisplayName, + version = ApplicationMetadata.CuraVersion, + api_version = ApplicationMetadata.CuraSDKVersion, + buildtype = ApplicationMetadata.CuraBuildType, + is_debug_mode = ApplicationMetadata.CuraDebugMode, tray_icon_name = "cura-icon-32.png", **kwargs) @@ -953,7 +953,7 @@ class CuraApplication(QtApplication): engine.rootContext().setContextProperty("CuraApplication", self) engine.rootContext().setContextProperty("PrintInformation", self._print_information) engine.rootContext().setContextProperty("CuraActions", self._cura_actions) - engine.rootContext().setContextProperty("CuraSDKVersion", CuraConstants.CuraSDKVersion) + engine.rootContext().setContextProperty("CuraSDKVersion", ApplicationMetadata.CuraSDKVersion) qmlRegisterUncreatableType(CuraApplication, "Cura", 1, 0, "ResourceTypes", "Just an Enum type") diff --git a/cura/UltimakerCloudAuthentication.py b/cura/UltimakerCloudAuthentication.py new file mode 100644 index 0000000000..7ebdfd054b --- /dev/null +++ b/cura/UltimakerCloudAuthentication.py @@ -0,0 +1,24 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +# --------- +# Constants used for the Cloud API +# --------- +DEFAULT_CLOUD_API_ROOT = "https://api.ultimaker.com" # type: str +DEFAULT_CLOUD_API_VERSION = "1" # type: str +DEFAULT_CLOUD_ACCOUNT_API_ROOT = "https://account.ultimaker.com" # type: str + +try: + from cura.CuraVersion import CuraCloudAPIRoot # type: ignore +except ImportError: + CuraCloudAPIRoot = DEFAULT_CLOUD_API_ROOT + +try: + from cura.CuraVersion import CuraCloudAPIVersion # type: ignore +except ImportError: + CuraCloudAPIVersion = DEFAULT_CLOUD_API_VERSION + +try: + from cura.CuraVersion import CuraCloudAccountAPIRoot # type: ignore +except ImportError: + CuraCloudAccountAPIRoot = DEFAULT_CLOUD_ACCOUNT_API_ROOT diff --git a/plugins/CuraDrive/src/Settings.py b/plugins/CuraDrive/src/Settings.py index c0df66b950..4ee73ad149 100644 --- a/plugins/CuraDrive/src/Settings.py +++ b/plugins/CuraDrive/src/Settings.py @@ -1,7 +1,7 @@ # Copyright (c) 2018 Ultimaker B.V. from UM import i18nCatalog -from cura import CuraConstants +from cura import UltimakerCloudAuthentication class Settings: @@ -9,7 +9,7 @@ class Settings: Keeps the application settings. """ DRIVE_API_VERSION = 1 - DRIVE_API_URL = "{}/cura-drive/v{}".format(CuraConstants.CuraCloudAPIRoot, str(DRIVE_API_VERSION)) + DRIVE_API_URL = "{}/cura-drive/v{}".format(UltimakerCloudAuthentication.CuraCloudAPIRoot, str(DRIVE_API_VERSION)) AUTO_BACKUP_ENABLED_PREFERENCE_KEY = "cura_drive/auto_backup_enabled" AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY = "cura_drive/auto_backup_date" diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 2229ab1f67..e7e849f1d2 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -16,8 +16,8 @@ from UM.Extension import Extension from UM.i18n import i18nCatalog from UM.Version import Version -import cura -from cura import CuraConstants +from cura import ApplicationMetadata +from cura import UltimakerCloudAuthentication from cura.CuraApplication import CuraApplication from .AuthorsModel import AuthorsModel @@ -39,9 +39,9 @@ class Toolbox(QObject, Extension): self._application = application # type: CuraApplication - self._sdk_version = CuraConstants.CuraSDKVersion # type: Union[str, int] - self._cloud_api_version = CuraConstants.CuraCloudAPIVersion # type: int - self._cloud_api_root = CuraConstants.CuraCloudAPIRoot # type: str + self._sdk_version = ApplicationMetadata.CuraSDKVersion # type: Union[str, int] + self._cloud_api_version = UltimakerCloudAuthentication.CuraCloudAPIVersion # type: int + self._cloud_api_root = UltimakerCloudAuthentication.CuraCloudAPIRoot # type: str self._api_url = None # type: Optional[str] # Network: From 8e78582b619e9e2511f57dd00162c800fcf4c43d Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 18 Dec 2018 16:17:37 +0100 Subject: [PATCH 0999/1240] Add correct copyright and license header to all the files in the plugin Contributes to CURA-6005. --- plugins/CuraDrive/__init__.py | 4 +++- plugins/CuraDrive/plugin.json | 2 +- plugins/CuraDrive/src/DriveApiService.py | 4 +++- plugins/CuraDrive/src/DrivePluginExtension.py | 4 +++- plugins/CuraDrive/src/Settings.py | 2 ++ plugins/CuraDrive/src/UploadBackupJob.py | 2 ++ plugins/CuraDrive/src/models/BackupListModel.py | 2 ++ plugins/CuraDrive/src/qml/components/BackupList.qml | 2 ++ plugins/CuraDrive/src/qml/components/BackupListFooter.qml | 2 ++ plugins/CuraDrive/src/qml/components/BackupListItem.qml | 2 ++ .../CuraDrive/src/qml/components/BackupListItemDetails.qml | 2 ++ .../CuraDrive/src/qml/components/BackupListItemDetailsRow.qml | 2 ++ plugins/CuraDrive/src/qml/components/Divider.qml | 2 ++ plugins/CuraDrive/src/qml/components/Icon.qml | 2 ++ plugins/CuraDrive/src/qml/components/RightSideScrollBar.qml | 2 ++ plugins/CuraDrive/src/qml/main.qml | 2 ++ plugins/CuraDrive/src/qml/pages/BackupsPage.qml | 2 ++ plugins/CuraDrive/src/qml/pages/WelcomePage.qml | 2 ++ 18 files changed, 38 insertions(+), 4 deletions(-) diff --git a/plugins/CuraDrive/__init__.py b/plugins/CuraDrive/__init__.py index 6612a5d614..4103b0cf2e 100644 --- a/plugins/CuraDrive/__init__.py +++ b/plugins/CuraDrive/__init__.py @@ -1,4 +1,6 @@ -# Copyright (c) 2017 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + import os is_testing = os.getenv('ENV_NAME', "development") == "testing" diff --git a/plugins/CuraDrive/plugin.json b/plugins/CuraDrive/plugin.json index 6cf1fa273c..d1cab39ca5 100644 --- a/plugins/CuraDrive/plugin.json +++ b/plugins/CuraDrive/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "description": "Backup and restore your configuration.", "version": "1.2.0", - "api": 5, + "api": 6, "i18n-catalog": "cura" } diff --git a/plugins/CuraDrive/src/DriveApiService.py b/plugins/CuraDrive/src/DriveApiService.py index 98199c91cf..7c3e7b7026 100644 --- a/plugins/CuraDrive/src/DriveApiService.py +++ b/plugins/CuraDrive/src/DriveApiService.py @@ -1,4 +1,6 @@ -# Copyright (c) 2017 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + import base64 import hashlib from datetime import datetime diff --git a/plugins/CuraDrive/src/DrivePluginExtension.py b/plugins/CuraDrive/src/DrivePluginExtension.py index 7e1472b988..a76c623fe8 100644 --- a/plugins/CuraDrive/src/DrivePluginExtension.py +++ b/plugins/CuraDrive/src/DrivePluginExtension.py @@ -1,4 +1,6 @@ -# Copyright (c) 2017 Ultimaker B.V. +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + import os from datetime import datetime from typing import Optional diff --git a/plugins/CuraDrive/src/Settings.py b/plugins/CuraDrive/src/Settings.py index 4ee73ad149..04ace8af95 100644 --- a/plugins/CuraDrive/src/Settings.py +++ b/plugins/CuraDrive/src/Settings.py @@ -1,4 +1,6 @@ # Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + from UM import i18nCatalog from cura import UltimakerCloudAuthentication diff --git a/plugins/CuraDrive/src/UploadBackupJob.py b/plugins/CuraDrive/src/UploadBackupJob.py index bcecce554a..ae6cb13f2e 100644 --- a/plugins/CuraDrive/src/UploadBackupJob.py +++ b/plugins/CuraDrive/src/UploadBackupJob.py @@ -1,4 +1,6 @@ # Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + import requests from UM.Job import Job diff --git a/plugins/CuraDrive/src/models/BackupListModel.py b/plugins/CuraDrive/src/models/BackupListModel.py index 93b0c4c48c..06b256b22c 100644 --- a/plugins/CuraDrive/src/models/BackupListModel.py +++ b/plugins/CuraDrive/src/models/BackupListModel.py @@ -1,4 +1,6 @@ # Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + from typing import Any, List, Dict from UM.Qt.ListModel import ListModel diff --git a/plugins/CuraDrive/src/qml/components/BackupList.qml b/plugins/CuraDrive/src/qml/components/BackupList.qml index a19d1f0ae7..af7e72b6e6 100644 --- a/plugins/CuraDrive/src/qml/components/BackupList.qml +++ b/plugins/CuraDrive/src/qml/components/BackupList.qml @@ -1,4 +1,6 @@ // Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + import QtQuick 2.7 import QtQuick.Controls 2.1 import QtQuick.Layouts 1.3 diff --git a/plugins/CuraDrive/src/qml/components/BackupListFooter.qml b/plugins/CuraDrive/src/qml/components/BackupListFooter.qml index 72dc3df044..2a6d82bc74 100644 --- a/plugins/CuraDrive/src/qml/components/BackupListFooter.qml +++ b/plugins/CuraDrive/src/qml/components/BackupListFooter.qml @@ -1,4 +1,6 @@ // Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + import QtQuick 2.7 import QtQuick.Controls 2.1 import QtQuick.Layouts 1.3 diff --git a/plugins/CuraDrive/src/qml/components/BackupListItem.qml b/plugins/CuraDrive/src/qml/components/BackupListItem.qml index a84caeb6ab..ba4f1a32a4 100644 --- a/plugins/CuraDrive/src/qml/components/BackupListItem.qml +++ b/plugins/CuraDrive/src/qml/components/BackupListItem.qml @@ -1,4 +1,6 @@ // Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + import QtQuick 2.7 import QtQuick.Controls 2.1 import QtQuick.Layouts 1.3 diff --git a/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml b/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml index 74d4c5ab57..38a2557c47 100644 --- a/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml +++ b/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml @@ -1,4 +1,6 @@ // Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + import QtQuick 2.7 import QtQuick.Controls 2.1 import QtQuick.Layouts 1.3 diff --git a/plugins/CuraDrive/src/qml/components/BackupListItemDetailsRow.qml b/plugins/CuraDrive/src/qml/components/BackupListItemDetailsRow.qml index dad1674fe7..d1c8dd33d2 100644 --- a/plugins/CuraDrive/src/qml/components/BackupListItemDetailsRow.qml +++ b/plugins/CuraDrive/src/qml/components/BackupListItemDetailsRow.qml @@ -1,4 +1,6 @@ // Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + import QtQuick 2.7 import QtQuick.Controls 2.1 import QtQuick.Layouts 1.3 diff --git a/plugins/CuraDrive/src/qml/components/Divider.qml b/plugins/CuraDrive/src/qml/components/Divider.qml index bba2f2f29c..202794fe23 100644 --- a/plugins/CuraDrive/src/qml/components/Divider.qml +++ b/plugins/CuraDrive/src/qml/components/Divider.qml @@ -1,4 +1,6 @@ // Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + import QtQuick 2.7 import UM 1.3 as UM diff --git a/plugins/CuraDrive/src/qml/components/Icon.qml b/plugins/CuraDrive/src/qml/components/Icon.qml index 3cb822bf82..8d559bc2b4 100644 --- a/plugins/CuraDrive/src/qml/components/Icon.qml +++ b/plugins/CuraDrive/src/qml/components/Icon.qml @@ -1,4 +1,6 @@ // Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + import QtQuick 2.7 import QtQuick.Controls 2.1 import QtGraphicalEffects 1.0 diff --git a/plugins/CuraDrive/src/qml/components/RightSideScrollBar.qml b/plugins/CuraDrive/src/qml/components/RightSideScrollBar.qml index 5ac5df15ff..5ef8558ee7 100644 --- a/plugins/CuraDrive/src/qml/components/RightSideScrollBar.qml +++ b/plugins/CuraDrive/src/qml/components/RightSideScrollBar.qml @@ -1,4 +1,6 @@ // Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + import QtQuick 2.7 import QtQuick.Controls 2.1 import QtQuick.Layouts 1.3 diff --git a/plugins/CuraDrive/src/qml/main.qml b/plugins/CuraDrive/src/qml/main.qml index 359f8dd725..e8a49a49e5 100644 --- a/plugins/CuraDrive/src/qml/main.qml +++ b/plugins/CuraDrive/src/qml/main.qml @@ -1,4 +1,6 @@ // Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + import QtQuick 2.7 import QtQuick.Controls 2.1 import QtQuick.Window 2.2 diff --git a/plugins/CuraDrive/src/qml/pages/BackupsPage.qml b/plugins/CuraDrive/src/qml/pages/BackupsPage.qml index 88ce766383..0a7b00d2cb 100644 --- a/plugins/CuraDrive/src/qml/pages/BackupsPage.qml +++ b/plugins/CuraDrive/src/qml/pages/BackupsPage.qml @@ -1,4 +1,6 @@ // Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + import QtQuick 2.7 import QtQuick.Controls 2.1 import QtQuick.Layouts 1.3 diff --git a/plugins/CuraDrive/src/qml/pages/WelcomePage.qml b/plugins/CuraDrive/src/qml/pages/WelcomePage.qml index b7f96d162a..19eecedf28 100644 --- a/plugins/CuraDrive/src/qml/pages/WelcomePage.qml +++ b/plugins/CuraDrive/src/qml/pages/WelcomePage.qml @@ -1,4 +1,6 @@ // Copyright (c) 2018 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + import QtQuick 2.7 import QtQuick.Controls 2.1 import QtQuick.Window 2.2 From c66257bf4d42c5649606de352cf41496bd7a90dd Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 18 Dec 2018 16:18:01 +0100 Subject: [PATCH 1000/1240] Use item instead of transparent rectangle This is faster to render. Contributes to issue CURA-6032. --- .../qml/Preferences/Materials/MaterialsBrandSection.qml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/resources/qml/Preferences/Materials/MaterialsBrandSection.qml b/resources/qml/Preferences/Materials/MaterialsBrandSection.qml index a3a0e4708f..c40693e343 100644 --- a/resources/qml/Preferences/Materials/MaterialsBrandSection.qml +++ b/resources/qml/Preferences/Materials/MaterialsBrandSection.qml @@ -1,5 +1,5 @@ // Copyright (c) 2018 Ultimaker B.V. -// Uranium is released under the terms of the LGPLv3 or higher. +// Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.7 import QtQuick.Controls 1.4 @@ -69,11 +69,7 @@ Rectangle } style: ButtonStyle { - background: Rectangle - { - anchors.fill: parent - color: "transparent" - } + background: Item { } } } } From 7cf1df74354022c5cceba84ae8ab304ad0d16b1e Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Tue, 18 Dec 2018 16:19:21 +0100 Subject: [PATCH 1001/1240] Remove back Cura.ExtrudersModel{}, because if retrieve the model from backend the tabs cannot be removed after model update. The QML bug CURA-6036 --- .../MachineSettingsAction.qml | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/plugins/MachineSettingsAction/MachineSettingsAction.qml b/plugins/MachineSettingsAction/MachineSettingsAction.qml index 60dd31dcae..ef8fda224a 100644 --- a/plugins/MachineSettingsAction/MachineSettingsAction.qml +++ b/plugins/MachineSettingsAction/MachineSettingsAction.qml @@ -13,10 +13,22 @@ import Cura 1.0 as Cura Cura.MachineAction { id: base - property var extrudersModel: CuraApplication.getExtrudersModel() + property var extrudersModel: Cura.ExtrudersModel{} // Do not retrieve the Model from a backend. Otherwise the tabs + // in tabView will not removed/updated. Probably QML bug + property int extruderTabsCount: 0 property var activeMachineId: Cura.MachineManager.activeMachine != null ? Cura.MachineManager.activeMachine.id : "" + Connections + { + target: base.extrudersModel + onModelChanged: + { + var extruderCount = base.extrudersModel.count; + base.extruderTabsCount = extruderCount; + } + } + Connections { target: dialog ? dialog : null @@ -346,22 +358,11 @@ Cura.MachineAction Repeater { id: extruderTabsRepeater - model: base.extrudersModel - - - onItemAdded: - { - settingsTabs.addTab(index + 1, item) - } - - onItemRemoved: - { - settingsTabs.removeTab(index + 1) - } + model: base.extruderTabsCount Tab { - title: model.name + title: base.extrudersModel.getItem(index).name anchors.margins: UM.Theme.getSize("default_margin").width Column From 8206fde2237930de5347a711ae6539055952888f Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Tue, 18 Dec 2018 16:24:13 +0100 Subject: [PATCH 1002/1240] Remove the non-load-only-when-testing check Contributes to CURA-6005. --- plugins/CuraDrive/__init__.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/plugins/CuraDrive/__init__.py b/plugins/CuraDrive/__init__.py index 4103b0cf2e..766d94752f 100644 --- a/plugins/CuraDrive/__init__.py +++ b/plugins/CuraDrive/__init__.py @@ -1,16 +1,10 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -import os +from .src.DrivePluginExtension import DrivePluginExtension -is_testing = os.getenv('ENV_NAME', "development") == "testing" +def getMetaData(): + return {} -# Only load the whole plugin when not running tests as __init__.py is automatically loaded by PyTest -if not is_testing: - from .src.DrivePluginExtension import DrivePluginExtension - - def getMetaData(): - return {} - - def register(app): - return {"extension": DrivePluginExtension(app)} +def register(app): + return {"extension": DrivePluginExtension(app)} From 0588c54035441d536f6afce236563e957a5b5114 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 18 Dec 2018 16:29:13 +0100 Subject: [PATCH 1003/1240] Start work on cloud icon --- cura/PrinterOutputDevice.py | 7 ++++++- .../UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py | 4 +++- resources/qml/PrinterSelector/MachineSelector.qml | 7 ++++--- .../cura-light/icons/printer_cloud_connected.svg | 10 ++++++++++ 4 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 resources/themes/cura-light/icons/printer_cloud_connected.svg diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index d16242d9c2..44c564d03a 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -56,6 +56,7 @@ class PrinterOutputDevice(QObject, OutputDevice): # Put ConnectionType here with Q_ENUMS() so it can be registered as a QML type and accessible via QML, and there is # no need to remember what those Enum integer values mean. + ConnectionType = ConnectionType Q_ENUMS(ConnectionType) printersChanged = pyqtSignal() @@ -133,7 +134,11 @@ class PrinterOutputDevice(QObject, OutputDevice): def getConnectionType(self) -> "ConnectionType": return self._connection_type - @pyqtProperty(str, notify = connectionStateChanged) + @pyqtProperty(int, constant = True) + def connectionType(self) -> "ConnectionType": + return self._connection_type + + @pyqtProperty(int, notify = connectionStateChanged) def connectionState(self) -> "ConnectionState": return self._connection_state diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index fed8cb040a..7e9608dc8b 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -17,6 +17,7 @@ from UM.Scene.SceneNode import SceneNode from cura.CuraApplication import CuraApplication from cura.PrinterOutput.NetworkedPrinterOutputDevice import AuthState, NetworkedPrinterOutputDevice from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel +from cura.PrinterOutputDevice import ConnectionType from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputController import CloudOutputController from ..MeshFormatHandler import MeshFormatHandler from ..UM3PrintJobOutputModel import UM3PrintJobOutputModel @@ -86,7 +87,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # \param cluster: The device response received from the cloud API. # \param parent: The optional parent of this output device. def __init__(self, api_client: CloudApiClient, cluster: CloudClusterResponse, parent: QObject = None) -> None: - super().__init__(device_id = cluster.cluster_id, address = "", properties = {}, parent = parent) + super().__init__(device_id = cluster.cluster_id, address = "", + connection_type = ConnectionType.CloudConnection, properties = {}, parent = parent) self._api = api_client self._cluster = cluster diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index 28e01c7ae9..3804c04025 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -11,9 +11,10 @@ Cura.ExpandablePopup { id: machineSelector - property bool isNetworkPrinter: Cura.MachineManager.activeMachineHasRemoteConnection - property bool isPrinterConnected: Cura.MachineManager.printerConnected property var outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null + property bool isNetworkPrinter: Cura.MachineManager.activeMachineHasRemoteConnection + property bool isCloudPrinter: machineSelector.outputDevice.connectionType == Cura.PrinterOutputDevice.CloudConnection + property bool isPrinterConnected: Cura.MachineManager.printerConnected contentPadding: UM.Theme.getSize("default_lining").width contentAlignment: Cura.ExpandablePopup.ContentAlignment.AlignLeft @@ -52,7 +53,7 @@ Cura.ExpandablePopup leftMargin: UM.Theme.getSize("thick_margin").width } - source: UM.Theme.getIcon("printer_connected") + source: machineSelector.isCloudPrinter ? UM.Theme.getIcon("printer_connected") : UM.Theme.getIcon("printer_connected") width: UM.Theme.getSize("printer_status_icon").width height: UM.Theme.getSize("printer_status_icon").height diff --git a/resources/themes/cura-light/icons/printer_cloud_connected.svg b/resources/themes/cura-light/icons/printer_cloud_connected.svg new file mode 100644 index 0000000000..6b2c814786 --- /dev/null +++ b/resources/themes/cura-light/icons/printer_cloud_connected.svg @@ -0,0 +1,10 @@ + + + + Artboard Copy 2 + Created with Sketch. + + + + + \ No newline at end of file From 4bf24671a21d3b17ed55548200198180eb71e52c Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 18 Dec 2018 16:33:39 +0100 Subject: [PATCH 1004/1240] Use the (not final) cloud icon when cloud connected --- resources/qml/PrinterSelector/MachineSelector.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index 3804c04025..13ebae4ac5 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -53,7 +53,7 @@ Cura.ExpandablePopup leftMargin: UM.Theme.getSize("thick_margin").width } - source: machineSelector.isCloudPrinter ? UM.Theme.getIcon("printer_connected") : UM.Theme.getIcon("printer_connected") + source: machineSelector.isCloudPrinter ? UM.Theme.getIcon("printer_cloud_connected") : UM.Theme.getIcon("printer_connected") width: UM.Theme.getSize("printer_status_icon").width height: UM.Theme.getSize("printer_status_icon").height From c0c45519a06847d49a745dd2d23537350b937710 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 18 Dec 2018 16:49:41 +0100 Subject: [PATCH 1005/1240] Change another useless rectangle into an item It's more efficient to render. Also changed some code style. Contributes to issue CURA-6032. --- .../qml/Preferences/Materials/MaterialsSlot.qml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/resources/qml/Preferences/Materials/MaterialsSlot.qml b/resources/qml/Preferences/Materials/MaterialsSlot.qml index a706aaf2b9..fb3cb9607d 100644 --- a/resources/qml/Preferences/Materials/MaterialsSlot.qml +++ b/resources/qml/Preferences/Materials/MaterialsSlot.qml @@ -1,5 +1,5 @@ // Copyright (c) 2018 Ultimaker B.V. -// Uranium is released under the terms of the LGPLv3 or higher. +// Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.7 import QtQuick.Controls 1.4 @@ -70,7 +70,8 @@ Rectangle } onClicked: { - if (materialSlot.is_favorite) { + if (materialSlot.is_favorite) + { base.materialManager.removeFavorite(material.root_material_id) materialSlot.is_favorite = false return @@ -81,13 +82,10 @@ Rectangle } style: ButtonStyle { - background: Rectangle - { - anchors.fill: parent - color: "transparent" - } + background: Item { } } - UM.RecolorImage { + UM.RecolorImage + { anchors { verticalCenter: favorite_button.verticalCenter From 6600cea632c9e3cdb925407ff4c7eecf745b40e3 Mon Sep 17 00:00:00 2001 From: susisstrolch Date: Tue, 18 Dec 2018 16:51:16 +0100 Subject: [PATCH 1006/1240] Add missing baudrate to USB connection. Extend the baudrate list by 500.000bd to also detect Vertex Delta 3D (K8800). Fixes #4952 --- plugins/USBPrinting/AutoDetectBaudJob.py | 2 +- plugins/USBPrinting/USBPrinterOutputDevice.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/USBPrinting/AutoDetectBaudJob.py b/plugins/USBPrinting/AutoDetectBaudJob.py index 78de864e57..2fa0af1795 100644 --- a/plugins/USBPrinting/AutoDetectBaudJob.py +++ b/plugins/USBPrinting/AutoDetectBaudJob.py @@ -18,7 +18,7 @@ class AutoDetectBaudJob(Job): def __init__(self, serial_port: int) -> None: super().__init__() self._serial_port = serial_port - self._all_baud_rates = [115200, 250000, 230400, 57600, 38400, 19200, 9600] + self._all_baud_rates = [115200, 250000, 500000, 230400, 57600, 38400, 19200, 9600] def run(self) -> None: Logger.log("d", "Auto detect baud rate started.") diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index 19a703e605..89903b06f4 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -49,7 +49,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): self._baud_rate = baud_rate - self._all_baud_rates = [115200, 250000, 230400, 57600, 38400, 19200, 9600] + self._all_baud_rates = [115200, 250000, 500000, 230400, 57600, 38400, 19200, 9600] # Instead of using a timer, we really need the update to be as a thread, as reading from serial can block. self._update_thread = Thread(target = self._update, daemon = True) From 66ed9ed201dd3873c98fda46508243e4902a41ce Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 18 Dec 2018 16:52:04 +0100 Subject: [PATCH 1007/1240] Remove optimisation that broke updates of models upon metadata change If the metadata changed, such as whether a material was favourite or not, then the materials models were not updating any more because the actual list of available materials was still the same. I've removed this optimisation and tested performance locally. It seems to be slightly slower (though that might be placebo or measurement error). However most of the performance boost of cura-6016 was resulting from different changes there so the interface still seems to be quite a lot faster than what it used to be. Contributes to issue CURA-6032. --- cura/Machines/Models/BaseMaterialsModel.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cura/Machines/Models/BaseMaterialsModel.py b/cura/Machines/Models/BaseMaterialsModel.py index 629e5c2b48..212e4fcf1e 100644 --- a/cura/Machines/Models/BaseMaterialsModel.py +++ b/cura/Machines/Models/BaseMaterialsModel.py @@ -106,10 +106,7 @@ class BaseMaterialsModel(ListModel): return False extruder_stack = global_stack.extruders[extruder_position] - available_materials = self._material_manager.getAvailableMaterialsForMachineExtruder(global_stack, extruder_stack) - if available_materials == self._available_materials: - return False - self._available_materials = available_materials + self._available_materials = self._material_manager.getAvailableMaterialsForMachineExtruder(global_stack, extruder_stack) if self._available_materials is None: return False From 6987d5ff782c53253c1b85b9f280ee4b9b328b59 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 19 Dec 2018 09:33:46 +0100 Subject: [PATCH 1008/1240] Add a radius to the printer type label --- resources/qml/PrinterTypeLabel.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/qml/PrinterTypeLabel.qml b/resources/qml/PrinterTypeLabel.qml index 7feae32e16..cfc9e56513 100644 --- a/resources/qml/PrinterTypeLabel.qml +++ b/resources/qml/PrinterTypeLabel.qml @@ -19,6 +19,7 @@ Item { anchors.fill: parent color: UM.Theme.getColor("printer_type_label_background") + radius: UM.Theme.getSize("checkbox_radius").width } Label From 382286509f6cc5b1af6093b7e3b003965e955958 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 19 Dec 2018 09:49:12 +0100 Subject: [PATCH 1009/1240] Change the buildplate information to use a reusable component Boy scouting. Contributes to CURA-6025. --- .../ConfigurationMenu/ConfigurationItem.qml | 22 +++++-------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 728a0cbe9a..7e1b7e5af7 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -90,25 +90,13 @@ Button height: childrenRect.height visible: configuration.buildplateConfiguration != "" - UM.RecolorImage - { - id: buildplateIcon - anchors.left: parent.left - width: UM.Theme.getSize("main_window_header_button_icon").width - height: UM.Theme.getSize("main_window_header_button_icon").height - source: UM.Theme.getIcon("buildplate") - color: UM.Theme.getColor("text") - } - - Label + // Show the type of buildplate. The first letter is capitalized + Cura.IconWithText { id: buildplateLabel - anchors.left: buildplateIcon.right - anchors.verticalCenter: buildplateIcon.verticalCenter - anchors.leftMargin: Math.round(UM.Theme.getSize("default_margin").height / 2) - text: configuration.buildplateConfiguration - renderType: Text.NativeRendering - color: UM.Theme.getColor("text") + source: UM.Theme.getIcon("buildplate") + text: configuration.buildplateConfiguration.charAt(0).toUpperCase() + configuration.buildplateConfiguration.substr(1) + anchors.left: parent.left } } } From f1c28498a67aa73f93ada591bfdcb01c7ec14e59 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 19 Dec 2018 11:00:00 +0100 Subject: [PATCH 1010/1240] Display 0 if there are no measurements for minimum values Don't display max float then. --- plugins/SimulationView/SimulationView.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/SimulationView/SimulationView.py b/plugins/SimulationView/SimulationView.py index 6a0ffc1666..ae01b7cdb0 100644 --- a/plugins/SimulationView/SimulationView.py +++ b/plugins/SimulationView/SimulationView.py @@ -342,12 +342,16 @@ class SimulationView(CuraView): return self._extruder_count def getMinFeedrate(self) -> float: + if abs(self._min_feedrate - sys.float_info.max) < 10: # Some lenience due to floating point rounding. + return 0.0 # If it's still max-float, there are no measurements. Use 0 then. return self._min_feedrate def getMaxFeedrate(self) -> float: return self._max_feedrate def getMinThickness(self) -> float: + if abs(self._min_thickness - sys.float_info.max) < 10: # Some lenience due to floating point rounding. + return 0.0 # If it's still max-float, there are no measurements. Use 0 then. return self._min_thickness def getMaxThickness(self) -> float: From 5e9fe3fe500f7dca38139672fb58cdb6a28014d2 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 19 Dec 2018 11:21:50 +0100 Subject: [PATCH 1011/1240] Fix some codestyle, make connectionType a property as it's needed in QML --- cura/PrinterOutputDevice.py | 31 +++++++++---------- .../src/DiscoverUM3Action.py | 6 ++-- .../src/UM3OutputDevicePlugin.py | 2 +- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 44c564d03a..54fbf3a7f2 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -81,28 +81,28 @@ class PrinterOutputDevice(QObject, OutputDevice): self._printers = [] # type: List[PrinterOutputModel] self._unique_configurations = [] # type: List[ConfigurationModel] - self._monitor_view_qml_path = "" #type: str - self._monitor_component = None #type: Optional[QObject] - self._monitor_item = None #type: Optional[QObject] + self._monitor_view_qml_path = "" # type: str + self._monitor_component = None # type: Optional[QObject] + self._monitor_item = None # type: Optional[QObject] - self._control_view_qml_path = "" #type: str - self._control_component = None #type: Optional[QObject] - self._control_item = None #type: Optional[QObject] + self._control_view_qml_path = "" # type: str + self._control_component = None # type: Optional[QObject] + self._control_item = None # type: Optional[QObject] - self._accepts_commands = False #type: bool + self._accepts_commands = False # type: bool - self._update_timer = QTimer() #type: QTimer + self._update_timer = QTimer() # type: QTimer self._update_timer.setInterval(2000) # TODO; Add preference for update interval self._update_timer.setSingleShot(False) self._update_timer.timeout.connect(self._update) - self._connection_state = ConnectionState.Closed #type: ConnectionState - self._connection_type = connection_type + self._connection_state = ConnectionState.Closed # type: ConnectionState + self._connection_type = connection_type # type: ConnectionType - self._firmware_updater = None #type: Optional[FirmwareUpdater] - self._firmware_name = None #type: Optional[str] - self._address = "" #type: str - self._connection_text = "" #type: str + self._firmware_updater = None # type: Optional[FirmwareUpdater] + self._firmware_name = None # type: Optional[str] + self._address = "" # type: str + self._connection_text = "" # type: str self.printersChanged.connect(self._onPrintersChanged) QtApplication.getInstance().getOutputDeviceManager().outputDevicesChanged.connect(self._updateUniqueConfigurations) @@ -131,9 +131,6 @@ class PrinterOutputDevice(QObject, OutputDevice): self._connection_state = connection_state self.connectionStateChanged.emit(self._id) - def getConnectionType(self) -> "ConnectionType": - return self._connection_type - @pyqtProperty(int, constant = True) def connectionType(self) -> "ConnectionType": return self._connection_type diff --git a/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py b/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py index 6ce99e4891..6b016c4f42 100644 --- a/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py +++ b/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py @@ -138,11 +138,11 @@ class DiscoverUM3Action(MachineAction): if "connection_type" in meta_data: previous_connection_type = meta_data["connection_type"] - global_container_stack.setMetaDataEntry("connection_type", printer_device.getConnectionType().value) - CuraApplication.getInstance().getMachineManager().replaceContainersMetadata(key = "connection_type", value = previous_connection_type, new_value = printer_device.getConnectionType().value) + global_container_stack.setMetaDataEntry("connection_type", printer_device.connectionType.value) + CuraApplication.getInstance().getMachineManager().replaceContainersMetadata(key = "connection_type", value = previous_connection_type, new_value = printer_device.connectionType.value) else: global_container_stack.setMetaDataEntry("um_network_key", printer_device.key) - global_container_stack.setMetaDataEntry("connection_type", printer_device.getConnectionType().value) + global_container_stack.setMetaDataEntry("connection_type", printer_device.connectionType.value) if self._network_plugin: # Ensure that the connection states are refreshed. diff --git a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py index 4642811f39..e7e6b35978 100644 --- a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py @@ -288,7 +288,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): global_container_stack = Application.getInstance().getGlobalContainerStack() if global_container_stack and device.getId() == global_container_stack.getMetaDataEntry("um_network_key"): - global_container_stack.setMetaDataEntry("connection_type", device.getConnectionType().value) + global_container_stack.setMetaDataEntry("connection_type", device.connectionType.value) device.connect() device.connectionStateChanged.connect(self._onDeviceConnectionStateChanged) From 142ac56d78e2bcb91eff8fffb0341be736d3461f Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Wed, 19 Dec 2018 11:26:38 +0100 Subject: [PATCH 1012/1240] Add keyboard navigation for printer carousel --- .../UM3NetworkPrinting/resources/qml/MonitorCarousel.qml | 4 ++++ .../UM3NetworkPrinting/resources/qml/MonitorStage.qml | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml index a3e2517b45..eccd93c578 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml @@ -18,6 +18,10 @@ Item height: centerSection.height width: maximumWidth + + // Enable keyboard navigation + Keys.onLeftPressed: navigateTo(currentIndex - 1) + Keys.onRightPressed: navigateTo(currentIndex + 1) Item { diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml index b80c6a3661..8b1a11cb4d 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml @@ -24,6 +24,11 @@ Component } } width: maximumWidth + + // Enable keyboard navigation. NOTE: This is done here so that we can also potentially + // forward to the queue items in the future. (Deleting selected print job, etc.) + Keys.forwardTo: carousel + Component.onCompleted: forceActiveFocus() UM.I18nCatalog { @@ -59,7 +64,9 @@ Component } width: parent.width height: 264 * screenScaleFactor // TODO: Theme! - MonitorCarousel {} + MonitorCarousel { + id: carousel + } } MonitorQueue From 1b356a321958437c2781a475800ee9bb12f67c44 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 19 Dec 2018 11:41:22 +0100 Subject: [PATCH 1013/1240] Move cloud connection check logic to Python --- cura/Settings/MachineManager.py | 9 +++++++++ resources/qml/PrinterSelector/MachineSelector.qml | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index cd8ca09447..669a5a7cf7 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -530,6 +530,15 @@ class MachineManager(QObject): return connection_type in [ConnectionType.NetworkConnection.value, ConnectionType.CloudConnection.value] return False + @pyqtProperty(bool, notify = printerConnectedStatusChanged) + def activeMachineHasCloudConnection(self) -> bool: + if not self.activeMachineHasRemoteConnection: + return False + output_device = next(iter(self.printerOutputDevices), None) # type: PrinterOutputDevice + if not output_device: + return False + return output_device.connectionType == ConnectionType.CloudConnection + def activeMachineNetworkKey(self) -> str: if self._global_container_stack: return self._global_container_stack.getMetaDataEntry("um_network_key", "") diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index 13ebae4ac5..bb8934f620 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -13,7 +13,7 @@ Cura.ExpandablePopup property var outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null property bool isNetworkPrinter: Cura.MachineManager.activeMachineHasRemoteConnection - property bool isCloudPrinter: machineSelector.outputDevice.connectionType == Cura.PrinterOutputDevice.CloudConnection + property bool isCloudPrinter: Cura.MachineManager.activeMachineHasCloudConnection property bool isPrinterConnected: Cura.MachineManager.printerConnected contentPadding: UM.Theme.getSize("default_lining").width From eb1bde05166a64ac860e4c07ca056448826bab82 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 19 Dec 2018 11:42:17 +0100 Subject: [PATCH 1014/1240] Remove Q_ENUMS for connection type, not used in QML anymore --- cura/PrinterOutputDevice.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 54fbf3a7f2..2d95b21c8a 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -54,11 +54,6 @@ class ConnectionType(IntEnum): @signalemitter class PrinterOutputDevice(QObject, OutputDevice): - # Put ConnectionType here with Q_ENUMS() so it can be registered as a QML type and accessible via QML, and there is - # no need to remember what those Enum integer values mean. - ConnectionType = ConnectionType - Q_ENUMS(ConnectionType) - printersChanged = pyqtSignal() connectionStateChanged = pyqtSignal(str) acceptsCommandsChanged = pyqtSignal() From 112950f00319b69c09f0d532b4edc556178ee212 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 19 Dec 2018 11:45:36 +0100 Subject: [PATCH 1015/1240] Remove unused import --- cura/PrinterOutputDevice.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 2d95b21c8a..723a255133 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -1,10 +1,12 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from enum import IntEnum +from typing import Callable, List, Optional, Union from UM.Decorators import deprecated from UM.i18n import i18nCatalog from UM.OutputDevice.OutputDevice import OutputDevice -from PyQt5.QtCore import pyqtProperty, pyqtSignal, QObject, QTimer, QUrl, Q_ENUMS +from PyQt5.QtCore import pyqtProperty, pyqtSignal, QObject, QTimer, QUrl from PyQt5.QtWidgets import QMessageBox from UM.Logger import Logger @@ -12,9 +14,6 @@ from UM.Signal import signalemitter from UM.Qt.QtApplication import QtApplication from UM.FlameProfiler import pyqtSlot -from enum import IntEnum # For the connection state tracking. -from typing import Callable, List, Optional, Union - MYPY = False if MYPY: from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel From 80d2a784634f74039594fdf17be92ee7263f6e2b Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 19 Dec 2018 11:47:41 +0100 Subject: [PATCH 1016/1240] Fix optional type for output device --- 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 669a5a7cf7..f47724372f 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -534,7 +534,7 @@ class MachineManager(QObject): def activeMachineHasCloudConnection(self) -> bool: if not self.activeMachineHasRemoteConnection: return False - output_device = next(iter(self.printerOutputDevices), None) # type: PrinterOutputDevice + output_device = next(iter(self.printerOutputDevices), None) # type: Optional[PrinterOutputDevice] if not output_device: return False return output_device.connectionType == ConnectionType.CloudConnection From 56d0a52faccb3cc0cd5ecd8701b342a530395ed3 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 19 Dec 2018 11:51:46 +0100 Subject: [PATCH 1017/1240] Documentation and code style --- plugins/SimulationView/SimulationViewMenuComponent.qml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/plugins/SimulationView/SimulationViewMenuComponent.qml b/plugins/SimulationView/SimulationViewMenuComponent.qml index eec254c0dd..fe32fe9eb1 100644 --- a/plugins/SimulationView/SimulationViewMenuComponent.qml +++ b/plugins/SimulationView/SimulationViewMenuComponent.qml @@ -358,7 +358,7 @@ Cura.ExpandableComponent width: parent.width height: UM.Theme.getSize("layerview_row").height - Label + Label //Minimum value. { text: { @@ -383,9 +383,10 @@ Cura.ExpandableComponent renderType: Text.NativeRendering } - Label + Label //Unit in the middle. { - text: { + text: + { if (UM.SimulationView.layerActivity && CuraApplication.platformActivity) { // Feedrate selected @@ -407,7 +408,7 @@ Cura.ExpandableComponent font: UM.Theme.getFont("default") } - Label + Label //Maximum value. { text: { if (UM.SimulationView.layerActivity && CuraApplication.platformActivity) From d2746d03c1a5786b0d1fef1d41d2abfff79939c7 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 19 Dec 2018 11:56:43 +0100 Subject: [PATCH 1018/1240] Fix type checking for DiscoverUM3Action --- .../src/DiscoverUM3Action.py | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py b/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py index 6b016c4f42..c02c18e942 100644 --- a/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py +++ b/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py @@ -124,25 +124,31 @@ class DiscoverUM3Action(MachineAction): @pyqtSlot(QObject) def associateActiveMachineWithPrinterDevice(self, printer_device: Optional["PrinterOutputDevice"]) -> None: Logger.log("d", "Attempting to set the network key of the active machine to %s", printer_device.key) - global_container_stack = CuraApplication.getInstance().getGlobalContainerStack() - if global_container_stack: - meta_data = global_container_stack.getMetaData() - if "um_network_key" in meta_data: - previous_network_key= meta_data["um_network_key"] - global_container_stack.setMetaDataEntry("um_network_key", printer_device.key) - # Delete old authentication data. - Logger.log("d", "Removing old authentication id %s for device %s", global_container_stack.getMetaDataEntry("network_authentication_id", None), printer_device.key) - global_container_stack.removeMetaDataEntry("network_authentication_id") - global_container_stack.removeMetaDataEntry("network_authentication_key") - CuraApplication.getInstance().getMachineManager().replaceContainersMetadata(key = "um_network_key", value = previous_network_key, new_value = printer_device.key) + if not printer_device: + return - if "connection_type" in meta_data: - previous_connection_type = meta_data["connection_type"] - global_container_stack.setMetaDataEntry("connection_type", printer_device.connectionType.value) - CuraApplication.getInstance().getMachineManager().replaceContainersMetadata(key = "connection_type", value = previous_connection_type, new_value = printer_device.connectionType.value) - else: - global_container_stack.setMetaDataEntry("um_network_key", printer_device.key) + global_container_stack = CuraApplication.getInstance().getGlobalContainerStack() + if not global_container_stack: + return + + meta_data = global_container_stack.getMetaData() + if "um_network_key" in meta_data: + previous_network_key = meta_data["um_network_key"] + global_container_stack.setMetaDataEntry("um_network_key", printer_device.key) + # Delete old authentication data. + Logger.log("d", "Removing old authentication id %s for device %s", + global_container_stack.getMetaDataEntry("network_authentication_id", None), printer_device.key) + global_container_stack.removeMetaDataEntry("network_authentication_id") + global_container_stack.removeMetaDataEntry("network_authentication_key") + CuraApplication.getInstance().getMachineManager().replaceContainersMetadata(key = "um_network_key", value = previous_network_key, new_value = printer_device.key) + + if "connection_type" in meta_data: + previous_connection_type = meta_data["connection_type"] global_container_stack.setMetaDataEntry("connection_type", printer_device.connectionType.value) + CuraApplication.getInstance().getMachineManager().replaceContainersMetadata(key = "connection_type", value = previous_connection_type, new_value = printer_device.connectionType.value) + else: + global_container_stack.setMetaDataEntry("um_network_key", printer_device.key) + global_container_stack.setMetaDataEntry("connection_type", printer_device.connectionType.value) if self._network_plugin: # Ensure that the connection states are refreshed. From c4a8545c45717547449c0c03c39c3cd25acb9bc2 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 19 Dec 2018 12:05:07 +0100 Subject: [PATCH 1019/1240] Add final cloud connected icon to printer selector --- .../themes/cura-light/icons/printer_cloud_connected.svg | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/resources/themes/cura-light/icons/printer_cloud_connected.svg b/resources/themes/cura-light/icons/printer_cloud_connected.svg index 6b2c814786..3bc94a05e7 100644 --- a/resources/themes/cura-light/icons/printer_cloud_connected.svg +++ b/resources/themes/cura-light/icons/printer_cloud_connected.svg @@ -1,10 +1,11 @@ - + Artboard Copy 2 Created with Sketch. - - + + + \ No newline at end of file From cf06cb5351f7ec3c08ac1bd811334822406bfb1a Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 19 Dec 2018 12:10:24 +0100 Subject: [PATCH 1020/1240] Do not call printer_device.key before checking if it exists --- plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py b/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py index c02c18e942..c88848317a 100644 --- a/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py +++ b/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py @@ -123,10 +123,11 @@ class DiscoverUM3Action(MachineAction): # stored into the metadata of the currently active machine. @pyqtSlot(QObject) def associateActiveMachineWithPrinterDevice(self, printer_device: Optional["PrinterOutputDevice"]) -> None: - Logger.log("d", "Attempting to set the network key of the active machine to %s", printer_device.key) if not printer_device: return + Logger.log("d", "Attempting to set the network key of the active machine to %s", printer_device.key) + global_container_stack = CuraApplication.getInstance().getGlobalContainerStack() if not global_container_stack: return From acabd06542ea4907d503055bba6174c19a5ab89e Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 19 Dec 2018 12:32:18 +0100 Subject: [PATCH 1021/1240] Change the large_nonbold to large in one remaining component Contributes to CURA-6025. --- plugins/UM3NetworkPrinting/resources/qml/MonitorQueue.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorQueue.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorQueue.qml index 884dbabc2f..f2a0e785b8 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorQueue.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorQueue.qml @@ -23,7 +23,7 @@ Item top: parent.top } color: UM.Theme.getColor("text") - font: UM.Theme.getFont("large_nonbold") + font: UM.Theme.getFont("large") text: catalog.i18nc("@label", "Queued") } From 773190bee18dd7e18fefacfe2aca0104a0762897 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 19 Dec 2018 12:53:41 +0100 Subject: [PATCH 1022/1240] Fix code style Add space between binary operator. Contributes to CURA-6005. --- plugins/CuraDrive/src/DriveApiService.py | 30 ++++++++++++------------ 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/plugins/CuraDrive/src/DriveApiService.py b/plugins/CuraDrive/src/DriveApiService.py index 7c3e7b7026..a542ac439e 100644 --- a/plugins/CuraDrive/src/DriveApiService.py +++ b/plugins/CuraDrive/src/DriveApiService.py @@ -43,7 +43,7 @@ class DriveApiService: Logger.log("w", "Could not get access token.") return [] - backup_list_request = requests.get(self.GET_BACKUPS_URL, headers={ + backup_list_request = requests.get(self.GET_BACKUPS_URL, headers = { "Authorization": "Bearer {}".format(access_token) }) if backup_list_request.status_code > 299: @@ -55,12 +55,12 @@ class DriveApiService: def createBackup(self) -> None: """Create a backup and upload it to CuraDrive cloud storage.""" - self.onCreatingStateChanged.emit(is_creating=True) + self.onCreatingStateChanged.emit(is_creating = True) # Create the backup. backup_zip_file, backup_meta_data = self._cura_api.backups.createBackup() if not backup_zip_file or not backup_meta_data: - self.onCreatingStateChanged.emit(is_creating=False, error_message="Could not create backup.") + self.onCreatingStateChanged.emit(is_creating = False, error_message = "Could not create backup.") return # Create an upload entry for the backup. @@ -68,7 +68,7 @@ class DriveApiService: backup_meta_data["description"] = "{}.backup.{}.cura.zip".format(timestamp, backup_meta_data["cura_release"]) backup_upload_url = self._requestBackupUpload(backup_meta_data, len(backup_zip_file)) if not backup_upload_url: - self.onCreatingStateChanged.emit(is_creating=False, error_message="Could not upload backup.") + self.onCreatingStateChanged.emit(is_creating = False, error_message = "Could not upload backup.") return # Upload the backup to storage. @@ -83,29 +83,29 @@ class DriveApiService: """ if job.backup_upload_error_message != "": # If the job contains an error message we pass it along so the UI can display it. - self.onCreatingStateChanged.emit(is_creating=False, error_message=job.backup_upload_error_message) + self.onCreatingStateChanged.emit(is_creating = False, error_message = job.backup_upload_error_message) else: - self.onCreatingStateChanged.emit(is_creating=False) + self.onCreatingStateChanged.emit(is_creating = False) def restoreBackup(self, backup: Dict[str, Any]) -> None: """ Restore a previously exported backup from cloud storage. :param backup: A dict containing an entry from the API list response. """ - self.onRestoringStateChanged.emit(is_restoring=True) + self.onRestoringStateChanged.emit(is_restoring = True) download_url = backup.get("download_url") if not download_url: # If there is no download URL, we can't restore the backup. return self._emitRestoreError() - download_package = requests.get(download_url, stream=True) + download_package = requests.get(download_url, stream = True) if download_package.status_code != 200: # Something went wrong when attempting to download the backup. Logger.log("w", "Could not download backup from url %s: %s", download_url, download_package.text) return self._emitRestoreError() # We store the file in a temporary path fist to ensure integrity. - temporary_backup_file = NamedTemporaryFile(delete=False) + temporary_backup_file = NamedTemporaryFile(delete = False) with open(temporary_backup_file.name, "wb") as write_backup: for chunk in download_package: write_backup.write(chunk) @@ -119,13 +119,13 @@ class DriveApiService: # Tell Cura to place the backup back in the user data folder. with open(temporary_backup_file.name, "rb") as read_backup: self._cura_api.backups.restoreBackup(read_backup.read(), backup.get("data")) - self.onRestoringStateChanged.emit(is_restoring=False) + self.onRestoringStateChanged.emit(is_restoring = False) def _emitRestoreError(self, error_message: str = Settings.translatable_messages["backup_restore_error_message"]): """Helper method for emitting a signal when restoring failed.""" self.onRestoringStateChanged.emit( - is_restoring=False, - error_message=error_message + is_restoring = False, + error_message = error_message ) @staticmethod @@ -137,7 +137,7 @@ class DriveApiService: :return: Success or not. """ with open(file_path, "rb") as read_backup: - local_md5_hash = base64.b64encode(hashlib.md5(read_backup.read()).digest(), altchars=b"_-").decode("utf-8") + local_md5_hash = base64.b64encode(hashlib.md5(read_backup.read()).digest(), altchars = b"_-").decode("utf-8") return known_hash == local_md5_hash def deleteBackup(self, backup_id: str) -> bool: @@ -171,12 +171,12 @@ class DriveApiService: Logger.log("w", "Could not get access token.") return None - backup_upload_request = requests.put(self.PUT_BACKUP_URL, json={ + backup_upload_request = requests.put(self.PUT_BACKUP_URL, json = { "data": { "backup_size": backup_size, "metadata": backup_metadata } - }, headers={ + }, headers = { "Authorization": "Bearer {}".format(access_token) }) From e62ce0e4ca3f7af27282e246cc103c5804f1047c Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Wed, 19 Dec 2018 13:18:53 +0100 Subject: [PATCH 1023/1240] Make printer carousel more flexible/robust Contributes to CL-1157 --- .../resources/qml/MonitorCarousel.qml | 15 ++++++++++----- .../resources/qml/MonitorStage.qml | 4 +++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml index eccd93c578..7c0d9b95b6 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml @@ -14,7 +14,12 @@ Item property var tileWidth: 834 * screenScaleFactor // TODO: Theme! property var tileHeight: 216 * screenScaleFactor // TODO: Theme! property var tileSpacing: 60 * screenScaleFactor // TODO: Theme! - property var maxOffset: (OutputDevice.printers.length - 1) * (tileWidth + tileSpacing) + + // Array/model of printers to populate the carousel with + property var printers: [] + + // Maximum distance the carousel can be shifted + property var maxOffset: (printers.length - 1) * (tileWidth + tileSpacing) height: centerSection.height width: maximumWidth @@ -129,7 +134,7 @@ Item Repeater { - model: OutputDevice.printers + model: printers MonitorPrinterCard { printer: modelData @@ -151,7 +156,7 @@ Item width: 36 * screenScaleFactor // TODO: Theme! height: 72 * screenScaleFactor // TODO: Theme! z: 10 - visible: currentIndex < OutputDevice.printers.length - 1 + visible: currentIndex < printers.length - 1 onClicked: navigateTo(currentIndex + 1) hoverEnabled: true background: Rectangle @@ -227,7 +232,7 @@ Item spacing: 8 * screenScaleFactor // TODO: Theme! Repeater { - model: OutputDevice.printers + model: printers Button { background: Rectangle @@ -243,7 +248,7 @@ Item } function navigateTo( i ) { - if (i >= 0 && i < OutputDevice.printers.length) + if (i >= 0 && i < printers.length) { tiles.x = -1 * i * (tileWidth + tileSpacing) currentIndex = i diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml index 8b1a11cb4d..d20c2247b4 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml @@ -64,8 +64,10 @@ Component } width: parent.width height: 264 * screenScaleFactor // TODO: Theme! - MonitorCarousel { + MonitorCarousel + { id: carousel + printers: OutputDevice.printers } } From 9acaf9abd772643de75a51d4a8c47878c8ee2079 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 19 Dec 2018 13:30:25 +0100 Subject: [PATCH 1024/1240] Disable dropping of files into cura when monitor stage is active. CURA-6038 --- plugins/MonitorStage/MonitorMain.qml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/MonitorStage/MonitorMain.qml b/plugins/MonitorStage/MonitorMain.qml index 8f113735ee..34cf4ad801 100644 --- a/plugins/MonitorStage/MonitorMain.qml +++ b/plugins/MonitorStage/MonitorMain.qml @@ -16,12 +16,20 @@ Item color: UM.Theme.getColor("viewport_overlay") anchors.fill: parent + + // This mouse area is to prevent mouse clicks to be passed onto the scene. MouseArea { anchors.fill: parent acceptedButtons: Qt.AllButtons onWheel: wheel.accepted = true } + + // Disable dropping files into Cura when the monitor page is active + DropArea + { + anchors.fill: parent + } } Loader From 9e867f8077e95bec3fe15567983813433e42e5ef Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 19 Dec 2018 13:53:34 +0100 Subject: [PATCH 1025/1240] Fix the codestyle and cleanup the QML a bit CURA-6043 --- .../PostProcessingPlugin.qml | 103 +++++++++++------- 1 file changed, 63 insertions(+), 40 deletions(-) diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml index b962f4d53b..5862259be5 100644 --- a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml +++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml @@ -25,7 +25,7 @@ UM.Dialog { if(!visible) //Whenever the window is closed (either via the "Close" button or the X on the window frame), we want to update it in the stack. { - manager.writeScriptsToStack(); + manager.writeScriptsToStack() } } @@ -67,12 +67,17 @@ UM.Dialog ListView { id: activeScriptsList - anchors.top: activeScriptsHeader.bottom - anchors.topMargin: base.textMargin - anchors.left: parent.left - anchors.leftMargin: UM.Theme.getSize("default_margin").width - anchors.right: parent.right - anchors.rightMargin: base.textMargin + + anchors + { + top: activeScriptsHeader.bottom + left: parent.left + right: parent.right + rightMargin: base.textMargin + topMargin: base.textMargin + leftMargin: UM.Theme.getSize("default_margin").width + } + height: childrenRect.height model: manager.scriptList delegate: Item @@ -84,8 +89,12 @@ UM.Dialog id: activeScriptButton text: manager.getScriptLabelByKey(modelData.toString()) exclusiveGroup: selectedScriptGroup + width: parent.width + height: UM.Theme.getSize("setting").height checkable: true - checked: { + + checked: + { if (manager.selectedScriptIndex == index) { base.activeScriptName = manager.getScriptLabelByKey(modelData.toString()) @@ -102,8 +111,7 @@ UM.Dialog manager.setSelectedScriptIndex(index) base.activeScriptName = manager.getScriptLabelByKey(modelData.toString()) } - width: parent.width - height: UM.Theme.getSize("setting").height + style: ButtonStyle { background: Rectangle @@ -121,6 +129,7 @@ UM.Dialog } } } + Button { id: removeButton @@ -249,8 +258,8 @@ UM.Dialog onTriggered: manager.addScriptToList(modelData.toString()) } - onObjectAdded: scriptsMenu.insertItem(index, object); - onObjectRemoved: scriptsMenu.removeItem(object); + onObjectAdded: scriptsMenu.insertItem(index, object) + onObjectRemoved: scriptsMenu.removeItem(object) } } } @@ -268,12 +277,16 @@ UM.Dialog { id: scriptSpecsHeader text: manager.selectedScriptIndex == -1 ? catalog.i18nc("@label", "Settings") : base.activeScriptName - anchors.top: parent.top - anchors.topMargin: base.textMargin - anchors.left: parent.left - anchors.leftMargin: base.textMargin - anchors.right: parent.right - anchors.rightMargin: base.textMargin + anchors + { + top: parent.top + topMargin: base.textMargin + left: parent.left + leftMargin: base.textMargin + right: parent.right + rightMargin: base.textMargin + } + elide: Text.ElideRight height: 20 * screenScaleFactor font: UM.Theme.getFont("large") @@ -283,11 +296,15 @@ UM.Dialog ScrollView { id: scrollView - anchors.top: scriptSpecsHeader.bottom - anchors.topMargin: settingsPanel.textMargin - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom + anchors + { + top: scriptSpecsHeader.bottom + topMargin: settingsPanel.textMargin + left: parent.left + right: parent.right + bottom: parent.bottom + } + visible: manager.selectedScriptDefinitionId != "" style: UM.Theme.styles.scrollview; @@ -297,7 +314,7 @@ UM.Dialog spacing: UM.Theme.getSize("default_lining").height model: UM.SettingDefinitionsModel { - id: definitionsModel; + id: definitionsModel containerId: manager.selectedScriptDefinitionId showAll: true } @@ -327,8 +344,10 @@ UM.Dialog } Behavior on height { NumberAnimation { duration: 100 } } opacity: provider.properties.enabled == "True" ? 1 : 0 + Behavior on opacity { NumberAnimation { duration: 100 } } enabled: opacity > 0 + property var definition: model property var settingDefinitionsModel: definitionsModel property var propertyProvider: provider @@ -339,7 +358,8 @@ UM.Dialog //causing nasty issues when selecting different options. So disable asynchronous loading of enum type completely. asynchronous: model.type != "enum" && model.type != "extruder" - onLoaded: { + onLoaded: + { settingLoader.item.showRevertButton = false settingLoader.item.showInheritButton = false settingLoader.item.showLinkedSettingIcon = false @@ -395,18 +415,14 @@ UM.Dialog onShowTooltip: { - tooltip.text = text; - var position = settingLoader.mapToItem(settingsPanel, settingsPanel.x, 0); - tooltip.show(position); + tooltip.text = text + var position = settingLoader.mapToItem(settingsPanel, settingsPanel.x, 0) + tooltip.show(position) tooltip.target.x = position.x + 1 } - onHideTooltip: - { - tooltip.hide(); - } + onHideTooltip: tooltip.hide() } - } } } @@ -459,6 +475,7 @@ UM.Dialog Cura.SettingUnknown { } } } + rightButtons: Button { text: catalog.i18nc("@action:button", "Close") @@ -466,7 +483,8 @@ UM.Dialog onClicked: dialog.accept() } - Button { + Button + { objectName: "postProcessingSaveAreaButton" visible: activeScriptsList.count > 0 height: UM.Theme.getSize("save_button_save_to_button").height @@ -474,8 +492,10 @@ UM.Dialog tooltip: catalog.i18nc("@info:tooltip", "Change active post-processing scripts") onClicked: dialog.show() - style: ButtonStyle { - background: Rectangle { + style: ButtonStyle + { + background: Rectangle + { id: deviceSelectionIcon border.width: UM.Theme.getSize("default_lining").width border.color: !control.enabled ? UM.Theme.getColor("action_button_disabled_border") : @@ -485,12 +505,15 @@ UM.Dialog control.pressed ? UM.Theme.getColor("action_button_active") : control.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button") Behavior on color { ColorAnimation { duration: 50; } } + anchors.left: parent.left - anchors.leftMargin: Math.round(UM.Theme.getSize("save_button_text_margin").width / 2); + anchors.leftMargin: Math.round(UM.Theme.getSize("save_button_text_margin").width / 2) + width: parent.height height: parent.height - UM.RecolorImage { + UM.RecolorImage + { anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter width: Math.round(parent.width / 2) @@ -498,11 +521,11 @@ UM.Dialog sourceSize.height: height color: !control.enabled ? UM.Theme.getColor("action_button_disabled_text") : control.pressed ? UM.Theme.getColor("action_button_active_text") : - control.hovered ? UM.Theme.getColor("action_button_hovered_text") : UM.Theme.getColor("action_button_text"); + control.hovered ? UM.Theme.getColor("action_button_hovered_text") : UM.Theme.getColor("action_button_text") source: "postprocessing.svg" } } - label: Label{ } + label: Label { } } } } \ No newline at end of file From a5134001e98934576969eb1815b228ed92f885f9 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 19 Dec 2018 13:03:59 +0100 Subject: [PATCH 1026/1240] Sort unique printer types So that they appear in a consistent order everywhere. --- cura/PrinterOutputDevice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index 3102d73bb0..aeeb0381b2 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -230,7 +230,7 @@ class PrinterOutputDevice(QObject, OutputDevice): # Returns the unique configurations of the printers within this output device @pyqtProperty("QStringList", notify = uniqueConfigurationsChanged) def uniquePrinterTypes(self) -> List[str]: - return list(set([configuration.printerType for configuration in self._unique_configurations])) + return list(sorted(set([configuration.printerType for configuration in self._unique_configurations]))) def _onPrintersChanged(self) -> None: for printer in self._printers: From c0835f3a2fa8541de9b74fe715882e5e2993076f Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 19 Dec 2018 13:04:37 +0100 Subject: [PATCH 1027/1240] Code style Just something I boyscouted while working on something else. --- resources/qml/Menus/NetworkPrinterMenu.qml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/resources/qml/Menus/NetworkPrinterMenu.qml b/resources/qml/Menus/NetworkPrinterMenu.qml index 07a22202e4..166e45f3b9 100644 --- a/resources/qml/Menus/NetworkPrinterMenu.qml +++ b/resources/qml/Menus/NetworkPrinterMenu.qml @@ -7,11 +7,14 @@ import QtQuick.Controls 1.4 import UM 1.2 as UM import Cura 1.0 as Cura -Instantiator { - model: UM.ContainerStacksModel { +Instantiator +{ + model: UM.ContainerStacksModel + { filter: {"type": "machine", "um_network_key": "*", "hidden": "False"} } - MenuItem { + MenuItem + { // TODO: Use printer_group icon when it's a cluster. Not use it for now since it doesn't look as expected // iconSource: UM.Theme.getIcon("printer_single") text: model.metadata["connect_group_name"] From f2719ef582093f857e171678052507409f8958c0 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 19 Dec 2018 14:09:07 +0100 Subject: [PATCH 1028/1240] Fix identation for postProcessing plugin settings CURA-6043 --- .../PostProcessingPlugin/PostProcessingPlugin.qml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml index 5862259be5..09fda8d454 100644 --- a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml +++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml @@ -301,6 +301,7 @@ UM.Dialog top: scriptSpecsHeader.bottom topMargin: settingsPanel.textMargin left: parent.left + leftMargin: UM.Theme.getSize("default_margin").width right: parent.right bottom: parent.bottom } @@ -318,7 +319,8 @@ UM.Dialog containerId: manager.selectedScriptDefinitionId showAll: true } - delegate:Loader + + delegate: Loader { id: settingLoader @@ -329,18 +331,17 @@ UM.Dialog { if(model.type != undefined) { - return UM.Theme.getSize("section").height; + return UM.Theme.getSize("section").height } else { - return 0; + return 0 } } else { - return 0; + return 0 } - } Behavior on height { NumberAnimation { duration: 100 } } opacity: provider.properties.enabled == "True" ? 1 : 0 @@ -363,7 +364,7 @@ UM.Dialog settingLoader.item.showRevertButton = false settingLoader.item.showInheritButton = false settingLoader.item.showLinkedSettingIcon = false - settingLoader.item.doDepthIndentation = true + settingLoader.item.doDepthIndentation = false settingLoader.item.doQualityUserSettingEmphasis = false } From beb68213f403eb6f2f3318ce2fb058d3aa83bdc5 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 19 Dec 2018 14:14:44 +0100 Subject: [PATCH 1029/1240] Remove much logging or use debug level, fix cloud icon not appearing right away --- cura/Settings/MachineManager.py | 10 ++++++---- .../UM3NetworkPrinting/src/Cloud/CloudApiClient.py | 4 ++-- .../src/Cloud/CloudOutputDevice.py | 7 ++----- .../src/Cloud/CloudOutputDeviceManager.py | 12 ++++++------ 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index f47724372f..bdb0f10082 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -178,6 +178,7 @@ class MachineManager(QObject): self._printer_output_devices.append(printer_output_device) self.outputDevicesChanged.emit() + self.printerConnectedStatusChanged.emit() @pyqtProperty(QObject, notify = currentConfigurationChanged) def currentConfiguration(self) -> ConfigurationModel: @@ -520,7 +521,7 @@ class MachineManager(QObject): return "" @pyqtProperty(bool, notify = printerConnectedStatusChanged) - def printerConnected(self): + def printerConnected(self) -> bool: return bool(self._printer_output_devices) @pyqtProperty(bool, notify = printerConnectedStatusChanged) @@ -532,9 +533,10 @@ class MachineManager(QObject): @pyqtProperty(bool, notify = printerConnectedStatusChanged) def activeMachineHasCloudConnection(self) -> bool: - if not self.activeMachineHasRemoteConnection: - return False - output_device = next(iter(self.printerOutputDevices), None) # type: Optional[PrinterOutputDevice] + # A cloud connection is only available if the active output device actually is a cloud connected device. + # We cannot simply use the connection_type metadata entry as that's always set to 'NetworkConnection' + # if there was a network connection during setup, which is always the case. + output_device = next(iter(self._printer_output_devices), None) # type: Optional[PrinterOutputDevice] if not output_device: return False return output_device.connectionType == ConnectionType.CloudConnection diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py index 29a9f48c3f..302ca86d32 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py @@ -101,7 +101,7 @@ class CloudApiClient: request.setHeader(QNetworkRequest.ContentTypeHeader, content_type) if self._account.isLoggedIn: request.setRawHeader(b"Authorization", "Bearer {}".format(self._account.accessToken).encode()) - Logger.log("i", "Created request for URL %s. Logged in = %s", path, self._account.isLoggedIn) + # Logger.log("i", "Created request for URL %s. Logged in = %s", path, self._account.isLoggedIn) return request ## Parses the given JSON network reply into a status code and a dictionary, handling unexpected errors as well. @@ -112,7 +112,7 @@ class CloudApiClient: status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) try: response = bytes(reply.readAll()).decode() - Logger.log("i", "Received a reply %s from %s with %s", status_code, reply.url().toString(), response) + # Logger.log("i", "Received a reply %s from %s with %s", status_code, reply.url().toString(), response) return status_code, json.loads(response) except (UnicodeDecodeError, JSONDecodeError, ValueError) as err: error = CloudErrorObject(code=type(err).__name__, title=str(err), http_code=str(status_code), diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 7e9608dc8b..c436418f5e 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -65,8 +65,6 @@ class T: # Currently it only supports viewing the printer and print job status and adding a new job to the queue. # As such, those methods have been implemented here. # Note that this device represents a single remote cluster, not a list of multiple clusters. -# -# TODO: figure our how the QML interface for the cluster networking should operate with this limited functionality. class CloudOutputDevice(NetworkedPrinterOutputDevice): # The interval with which the remote clusters are checked @@ -202,10 +200,9 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def _update(self) -> None: super()._update() if self._last_request_time and time() - self._last_request_time < self.CHECK_CLUSTER_INTERVAL: - Logger.log("i", "Not updating: %s - %s < %s", time(), self._last_request_time, self.CHECK_CLUSTER_INTERVAL) return # avoid calling the cloud too often - Logger.log("i", "Updating: %s - %s >= %s", time(), self._last_request_time, self.CHECK_CLUSTER_INTERVAL) + Logger.log("d", "Updating: %s - %s >= %s", time(), self._last_request_time, self.CHECK_CLUSTER_INTERVAL) if self._account.isLoggedIn: self.setAuthenticationState(AuthState.Authenticated) self._last_request_time = time() @@ -342,7 +339,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Shows a message when the upload has succeeded # \param response: The response from the cloud API. def _onPrintRequested(self, response: CloudPrintResponse) -> None: - Logger.log("i", "The cluster will be printing this print job with the ID %s", response.cluster_job_id) + Logger.log("d", "The cluster will be printing this print job with the ID %s", response.cluster_job_id) self._progress.hide() Message( text = T.UPLOAD_SUCCESS_TEXT, diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 07051f15fd..af45f06394 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -50,7 +50,7 @@ class CloudOutputDeviceManager: # Called when the uses logs in or out def _onLoginStateChanged(self, is_logged_in: bool) -> None: - Logger.log("i", "Log in state changed to %s", is_logged_in) + Logger.log("d", "Log in state changed to %s", is_logged_in) if is_logged_in: if not self._update_timer.isActive(): self._update_timer.start() @@ -64,7 +64,7 @@ class CloudOutputDeviceManager: ## Gets all remote clusters from the API. def _getRemoteClusters(self) -> None: - Logger.log("i", "Retrieving remote clusters") + Logger.log("d", "Retrieving remote clusters") self._api.getClusters(self._onGetRemoteClustersFinished) ## Callback for when the request for getting the clusters. is finished. @@ -73,8 +73,8 @@ class CloudOutputDeviceManager: removed_devices, added_clusters, updates = findChanges(self._remote_clusters, online_clusters) - Logger.log("i", "Parsed remote clusters to %s", [cluster.toDict() for cluster in online_clusters.values()]) - Logger.log("i", "Removed: %s, added: %s, updates: %s", len(removed_devices), len(added_clusters), len(updates)) + Logger.log("d", "Parsed remote clusters to %s", [cluster.toDict() for cluster in online_clusters.values()]) + Logger.log("d", "Removed: %s, added: %s, updates: %s", len(removed_devices), len(added_clusters), len(updates)) # Remove output devices that are gone for removed_cluster in removed_devices: @@ -100,7 +100,7 @@ class CloudOutputDeviceManager: def _connectToActiveMachine(self) -> None: active_machine = CuraApplication.getInstance().getGlobalContainerStack() if not active_machine: - Logger.log("i", "no active machine") + Logger.log("d", "no active machine") return # Check if the stored cluster_id for the active machine is in our list of remote clusters. @@ -109,7 +109,7 @@ class CloudOutputDeviceManager: device = self._remote_clusters[stored_cluster_id] if not device.isConnected(): device.connect() - Logger.log("i", "Device connected by metadata %s", stored_cluster_id) + Logger.log("d", "Device connected by metadata %s", stored_cluster_id) else: self._connectByNetworkKey(active_machine) From 3953c7bb3a4400f7e49fe3ee53052e317b341f62 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Wed, 19 Dec 2018 14:24:02 +0100 Subject: [PATCH 1030/1240] Added Tooltip alignment CURA-6004 --- resources/qml/ActionButton.qml | 2 ++ .../qml/ActionPanel/OutputProcessWidget.qml | 2 ++ resources/qml/ToolTip.qml | 29 +++++++++++++++++-- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index 675cbb588e..9ceade6a57 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -31,6 +31,8 @@ Button property alias shadowColor: shadow.color property alias shadowEnabled: shadow.visible + property alias toolTipContentAlignment: tooltip.contentAlignment + // This property is used to indicate whether the button has a fixed width or the width would depend on the contents // Be careful when using fixedWidthMode, the translated texts can be too long that they won't fit. In any case, // we elide the text to the right so the text will be cut off with the three dots at the end. diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml index eb6dc5b417..03fa00d504 100644 --- a/resources/qml/ActionPanel/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -123,6 +123,8 @@ Column tooltip: text fixedWidthMode: true + toolTipContentAlignment: Cura.ToolTip.ContentAlignment.AlignLeft + onClicked: UM.Controller.setActiveStage("PreviewStage") visible: UM.Controller.activeStage != null && UM.Controller.activeStage.stageId != "PreviewStage" diff --git a/resources/qml/ToolTip.qml b/resources/qml/ToolTip.qml index ed71d3983b..5e7d436d70 100644 --- a/resources/qml/ToolTip.qml +++ b/resources/qml/ToolTip.qml @@ -5,14 +5,26 @@ import QtQuick 2.7 import QtQuick.Controls 2.3 import UM 1.0 as UM +import Cura 1.0 as Cura ToolTip { + + enum ContentAlignment + { + AlignLeft, + AlignRight + } + // This property indicates when the tooltip has to show, for instance when a button is hovered property bool show: false + // Defines the alignment of the content, by default to the left + property int contentAlignment: Cura.ToolTip.ContentAlignment.AlignRight + property alias tooltipText: tooltip.text - property var targetPoint: Qt.point(0, 0) + property var targetPoint: Qt.point(parent.x, y + Math.round(height/2)) + id: tooltip text: "" @@ -20,13 +32,24 @@ ToolTip visible: text != "" && show font: UM.Theme.getFont("default") + x: + { + if (contentAlignment == Cura.ToolTip.ContentAlignment.AlignLeft) + { + return (label.width + Math.round(UM.Theme.getSize("default_arrow").width * 1.2) + padding * 2) * -1 + } + return parent.width + Math.round(UM.Theme.getSize("default_arrow").width * 1.2 + padding) + } + + y: Math.round(parent.height / 2 - label.height / 2 ) - padding + + padding: 2 + background: UM.PointingRectangle { id: backgroundRect color: UM.Theme.getColor("tooltip") - target: Qt.point(targetPoint.x - tooltip.x, targetPoint.y - tooltip.y) - arrowSize: UM.Theme.getSize("default_arrow").width } From d891d30ab8df3b528902102b12dba6de7298eacc Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Wed, 19 Dec 2018 15:51:32 +0100 Subject: [PATCH 1031/1240] The Cura logo in left top corner is not displayed properly. CURA-6044 --- resources/qml/MainWindow/MainWindowHeader.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/qml/MainWindow/MainWindowHeader.qml b/resources/qml/MainWindow/MainWindowHeader.qml index eecf2a1e73..971b275bd8 100644 --- a/resources/qml/MainWindow/MainWindowHeader.qml +++ b/resources/qml/MainWindow/MainWindowHeader.qml @@ -29,6 +29,8 @@ Item source: UM.Theme.getImage("logo") width: UM.Theme.getSize("logo").width height: UM.Theme.getSize("logo").height + + mipmap: true } Row From 9dbc6429674cf13f9f763a0a5dd76f0dae6f6446 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 19 Dec 2018 17:34:31 +0100 Subject: [PATCH 1032/1240] SendMaterials: Make sure the base-material is compared to and make case insensitive. [CURA-6035] --- plugins/UM3NetworkPrinting/src/SendMaterialJob.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py index f536fad49a..a1ddf37461 100644 --- a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py @@ -101,11 +101,10 @@ class SendMaterialJob(Job): continue file_name = os.path.basename(file_path) - material_id = urllib.parse.unquote_plus(mime_type.stripExtension(file_name)) + material_id = urllib.parse.unquote_plus(mime_type.stripExtension(file_name)).lower() if material_id not in materials_to_send: # If the material does not have to be sent we skip it. continue - self._sendMaterialFile(file_path, file_name, material_id) ## Send a single material file to the printer. @@ -182,6 +181,7 @@ class SendMaterialJob(Job): # Create a new local material local_material = LocalMaterial(**material) + local_material.id = material["base_file"].lower() # Don't compare each profile, only base materials. if local_material.GUID not in result or \ local_material.version > result.get(local_material.GUID).version: From 8bff0d17e8b2b15e67d5da8f02bda41ea0fe3576 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 20 Dec 2018 09:43:01 +0100 Subject: [PATCH 1033/1240] Prevent the backend attempting to reslice everytime a preference changed --- plugins/CuraEngineBackend/CuraEngineBackend.py | 1 + resources/qml/ActionPanel/SliceProcessWidget.qml | 10 +++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 7ede6b6736..1830a30b30 100755 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -229,6 +229,7 @@ class CuraEngineBackend(QObject, Backend): if not self._build_plates_to_be_sliced: self.processingProgress.emit(1.0) Logger.log("w", "Slice unnecessary, nothing has changed that needs reslicing.") + self.setState(BackendState.Done) return if self._process_layers_job: diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index 3756d0d452..b80c24e7b7 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -134,10 +134,14 @@ Column onPreferenceChanged: { var autoSlice = UM.Preferences.getValue("general/auto_slice") - prepareButtons.autoSlice = autoSlice - if(autoSlice) + print(prepareButtons.autoSlice, autoSlice) + if(prepareButtons.autoSlice != autoSlice) { - CuraApplication.backend.forceSlice() + prepareButtons.autoSlice = autoSlice + if(autoSlice) + { + CuraApplication.backend.forceSlice() + } } } } From a9f4b70b5cf35be3b8dec8f99178a73d12ebd571 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 20 Dec 2018 09:53:58 +0100 Subject: [PATCH 1034/1240] Emit 'number of extruders changed' signal after add machine. [CURA-6045] --- cura/Settings/MachineManager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index cd8ca09447..2185bbce9d 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -299,6 +299,7 @@ class MachineManager(QObject): self.activeMaterialChanged.emit() self.rootMaterialChanged.emit() + self.numberExtrudersEnabledChanged.emit() def _onContainersChanged(self, container: ContainerInterface) -> None: self._instance_container_timer.start() From d93e19d530e93b0fb02c79a591b49aa6b8593967 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 20 Dec 2018 10:45:03 +0100 Subject: [PATCH 1035/1240] Add getAllMaterialGroups() CURA-6035 --- cura/Machines/MaterialManager.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cura/Machines/MaterialManager.py b/cura/Machines/MaterialManager.py index aee96f3153..4d9574bf38 100644 --- a/cura/Machines/MaterialManager.py +++ b/cura/Machines/MaterialManager.py @@ -302,6 +302,10 @@ class MaterialManager(QObject): def getMaterialGroupListByGUID(self, guid: str) -> Optional[List[MaterialGroup]]: return self._guid_material_groups_map.get(guid) + # Returns a dict of all material groups organized by root_material_id. + def getAllMaterialGroups(self) -> Dict[str, "MaterialGroup"]: + return self._material_group_map + # # Return a dict with all root material IDs (k) and ContainerNodes (v) that's suitable for the given setup. # From 68372aff6038064892becda076e6c642a36e92b7 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 20 Dec 2018 10:45:38 +0100 Subject: [PATCH 1036/1240] Use MaterialManager in SendMaterialJob CURA-6035 --- .../UM3NetworkPrinting/src/SendMaterialJob.py | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py index a1ddf37461..7a0da24098 100644 --- a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py @@ -2,17 +2,14 @@ # Cura is released under the terms of the LGPLv3 or higher. import json import os -import urllib.parse from typing import Dict, TYPE_CHECKING, Set from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest -from UM.Application import Application from UM.Job import Job from UM.Logger import Logger -from UM.MimeTypeDatabase import MimeTypeDatabase -from UM.Resources import Resources from cura.CuraApplication import CuraApplication + # Absolute imports don't work in plugins from .Models import ClusterMaterial, LocalMaterial @@ -91,21 +88,22 @@ class SendMaterialJob(Job): # # \param materials_to_send A set with id's of materials that must be sent. def _sendMaterials(self, materials_to_send: Set[str]) -> None: - file_paths = Resources.getAllResourcesOfType(CuraApplication.ResourceTypes.MaterialInstanceContainer) + container_registry = CuraApplication.getInstance().getContainerRegistry() + material_manager = CuraApplication.getInstance().getMaterialManager() + material_group_dict = material_manager.getAllMaterialGroups() - # Find all local material files and send them if needed. - for file_path in file_paths: - try: - mime_type = MimeTypeDatabase.getMimeTypeForFile(file_path) - except MimeTypeDatabase.MimeTypeNotFoundError: + for root_material_id in material_group_dict: + if root_material_id not in materials_to_send: + # If the material does not have to be sent we skip it. + continue + + file_path = container_registry.getContainerFilePathById(root_material_id) + if not file_path: + Logger.log("e", "Cannot get file path for material container [%s]", root_material_id) continue file_name = os.path.basename(file_path) - material_id = urllib.parse.unquote_plus(mime_type.stripExtension(file_name)).lower() - if material_id not in materials_to_send: - # If the material does not have to be sent we skip it. - continue - self._sendMaterialFile(file_path, file_name, material_id) + self._sendMaterialFile(file_path, file_name, root_material_id) ## Send a single material file to the printer. # @@ -115,7 +113,6 @@ class SendMaterialJob(Job): # \param file_name The name of the material file. # \param material_id The ID of the material in the file. def _sendMaterialFile(self, file_path: str, file_name: str, material_id: str) -> None: - parts = [] # Add the material file. @@ -170,28 +167,31 @@ class SendMaterialJob(Job): # \return a dictionary of LocalMaterial objects by GUID def _getLocalMaterials(self) -> Dict[str, LocalMaterial]: result = {} # type: Dict[str, LocalMaterial] - container_registry = Application.getInstance().getContainerRegistry() - material_containers = container_registry.findContainersMetadata(type = "material") + material_manager = CuraApplication.getInstance().getMaterialManager() + + material_group_dict = material_manager.getAllMaterialGroups() # Find the latest version of all material containers in the registry. - for material in material_containers: + for root_material_id, material_group in material_group_dict.items(): + material_metadata = material_group.root_material_node.getMetadata() + try: # material version must be an int - material["version"] = int(material["version"]) + material_metadata["version"] = int(material_metadata["version"]) # Create a new local material - local_material = LocalMaterial(**material) - local_material.id = material["base_file"].lower() # Don't compare each profile, only base materials. + local_material = LocalMaterial(**material_metadata) + local_material.id = root_material_id if local_material.GUID not in result or \ local_material.version > result.get(local_material.GUID).version: result[local_material.GUID] = local_material except KeyError: - Logger.logException("w", "Local material {} has missing values.".format(material["id"])) + Logger.logException("w", "Local material {} has missing values.".format(material_metadata["id"])) except ValueError: - Logger.logException("w", "Local material {} has invalid values.".format(material["id"])) + Logger.logException("w", "Local material {} has invalid values.".format(material_metadata["id"])) except TypeError: - Logger.logException("w", "Local material {} has invalid values.".format(material["id"])) + Logger.logException("w", "Local material {} has invalid values.".format(material_metadata["id"])) return result From dc473dc78d63c15a62dfd5a197162f43b0780b8f Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 20 Dec 2018 11:09:02 +0100 Subject: [PATCH 1037/1240] Add missing typing for setting visibility preset model --- .../Models/SettingVisibilityPresetsModel.py | 35 +++++++++++++------ .../qml/ActionPanel/SliceProcessWidget.qml | 1 - 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/cura/Machines/Models/SettingVisibilityPresetsModel.py b/cura/Machines/Models/SettingVisibilityPresetsModel.py index 79131521f2..baa8e3ed29 100644 --- a/cura/Machines/Models/SettingVisibilityPresetsModel.py +++ b/cura/Machines/Models/SettingVisibilityPresetsModel.py @@ -6,6 +6,7 @@ from typing import Optional, List from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject from UM.Logger import Logger +from UM.Preferences import Preferences from UM.Resources import Resources from UM.i18n import i18nCatalog @@ -18,14 +19,20 @@ class SettingVisibilityPresetsModel(QObject): onItemsChanged = pyqtSignal() activePresetChanged = pyqtSignal() - def __init__(self, preferences, parent = None): + def __init__(self, preferences: Preferences, parent = None) -> None: super().__init__(parent) self._items = [] # type: List[SettingVisibilityPreset] + self._custom_preset = SettingVisibilityPreset(preset_id = "custom", name = "Custom selection", weight = -100) + self._populate() basic_item = self.getVisibilityPresetById("basic") - basic_visibile_settings = ";".join(basic_item.settings) + if basic_item is not None: + basic_visibile_settings = ";".join(basic_item.settings) + else: + Logger.log("w", "Unable to find the basic visiblity preset.") + basic_visibile_settings = "" self._preferences = preferences @@ -42,7 +49,8 @@ class SettingVisibilityPresetsModel(QObject): visible_settings = self._preferences.getValue("general/visible_settings") if not visible_settings: - self._preferences.setValue("general/visible_settings", ";".join(self._active_preset_item.settings)) + new_visible_settings = self._active_preset_item.settings if self._active_preset_item is not None else [] + self._preferences.setValue("general/visible_settings", ";".join(new_visible_settings)) else: self._onPreferencesChanged("general/visible_settings") @@ -59,9 +67,7 @@ class SettingVisibilityPresetsModel(QObject): def _populate(self) -> None: from cura.CuraApplication import CuraApplication items = [] # type: List[SettingVisibilityPreset] - - custom_preset = SettingVisibilityPreset(preset_id="custom", name ="Custom selection", weight = -100) - items.append(custom_preset) + items.append(self._custom_preset) for file_path in Resources.getAllResourcesOfType(CuraApplication.ResourceTypes.SettingVisibilityPreset): setting_visibility_preset = SettingVisibilityPreset() try: @@ -77,7 +83,7 @@ class SettingVisibilityPresetsModel(QObject): self.setItems(items) @pyqtProperty("QVariantList", notify = onItemsChanged) - def items(self): + def items(self) -> List[SettingVisibilityPreset]: return self._items def setItems(self, items: List[SettingVisibilityPreset]) -> None: @@ -87,7 +93,7 @@ class SettingVisibilityPresetsModel(QObject): @pyqtSlot(str) def setActivePreset(self, preset_id: str) -> None: - if preset_id == self._active_preset_item.presetId: + if self._active_preset_item is not None and preset_id == self._active_preset_item.presetId: Logger.log("d", "Same setting visibility preset [%s] selected, do nothing.", preset_id) return @@ -96,7 +102,7 @@ class SettingVisibilityPresetsModel(QObject): Logger.log("w", "Tried to set active preset to unknown id [%s]", preset_id) return - need_to_save_to_custom = self._active_preset_item.presetId == "custom" and preset_id != "custom" + need_to_save_to_custom = self._active_preset_item is None or (self._active_preset_item.presetId == "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") @@ -117,7 +123,9 @@ class SettingVisibilityPresetsModel(QObject): @pyqtProperty(str, notify = activePresetChanged) def activePreset(self) -> str: - return self._active_preset_item.presetId + if self._active_preset_item is not None: + return self._active_preset_item.presetId + return "" def _onPreferencesChanged(self, name: str) -> None: if name != "general/visible_settings": @@ -149,7 +157,12 @@ class SettingVisibilityPresetsModel(QObject): else: item_to_set = matching_preset_item + # If we didn't find a matching preset, fallback to custom. + if item_to_set is None: + item_to_set = self._custom_preset + if self._active_preset_item is None or self._active_preset_item.presetId != item_to_set.presetId: self._active_preset_item = item_to_set - self._preferences.setValue("cura/active_setting_visibility_preset", self._active_preset_item.presetId) + if self._active_preset_item is not None: + self._preferences.setValue("cura/active_setting_visibility_preset", self._active_preset_item.presetId) self.activePresetChanged.emit() diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index b80c24e7b7..462d685fd4 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -134,7 +134,6 @@ Column onPreferenceChanged: { var autoSlice = UM.Preferences.getValue("general/auto_slice") - print(prepareButtons.autoSlice, autoSlice) if(prepareButtons.autoSlice != autoSlice) { prepareButtons.autoSlice = autoSlice From 36efa171c68dd7b0beb5771b1f29be1680127a99 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 20 Dec 2018 11:31:01 +0100 Subject: [PATCH 1038/1240] Add a bit more logging to the authorization service --- cura/OAuth2/AuthorizationService.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cura/OAuth2/AuthorizationService.py b/cura/OAuth2/AuthorizationService.py index 4355891139..a055254891 100644 --- a/cura/OAuth2/AuthorizationService.py +++ b/cura/OAuth2/AuthorizationService.py @@ -83,9 +83,11 @@ class AuthorizationService: if not self.getUserProfile(): # We check if we can get the user profile. # If we can't get it, that means the access token (JWT) was invalid or expired. + Logger.log("w", "Unable to get the user profile.") return None if self._auth_data is None: + Logger.log("d", "No auth data to retrieve the access_token from") return None return self._auth_data.access_token From 2f92f6ef50faba4b8c3875481b60e446bc6bc83b Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Thu, 20 Dec 2018 13:45:59 +0100 Subject: [PATCH 1039/1240] Simplify checking if cloud or network printer, small fixes --- .../NetworkedPrinterOutputDevice.py | 5 +++++ cura/Settings/MachineManager.py | 16 +++++++------- .../src/Cloud/CloudOutputDevice.py | 21 +++++++------------ .../src/ClusterUM3OutputDevice.py | 6 ++---- .../qml/PrinterSelector/MachineSelector.qml | 4 ++-- 5 files changed, 24 insertions(+), 28 deletions(-) diff --git a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py index d0a0b5076a..3dcc43dd00 100644 --- a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py +++ b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py @@ -150,6 +150,11 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): request.setHeader(QNetworkRequest.UserAgentHeader, self._user_agent) return request + ## This method was only available privately before, but it was actually called from SendMaterialJob.py. + # We now have a public equivalent as well. We did not remove the private one as plugins might be using that. + def createFormPart(self, content_header: str, data: bytes, content_type: Optional[str] = None) -> QHttpPart: + return self._createFormPart(content_header, data, content_type) + def _createFormPart(self, content_header: str, data: bytes, content_type: Optional[str] = None) -> QHttpPart: part = QHttpPart() diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 8d42ee59a2..66ee7f9543 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -533,14 +533,14 @@ class MachineManager(QObject): return False @pyqtProperty(bool, notify = printerConnectedStatusChanged) - def activeMachineHasCloudConnection(self) -> bool: - # A cloud connection is only available if the active output device actually is a cloud connected device. - # We cannot simply use the connection_type metadata entry as that's always set to 'NetworkConnection' - # if there was a network connection during setup, which is always the case. - output_device = next(iter(self._printer_output_devices), None) # type: Optional[PrinterOutputDevice] - if not output_device: - return False - return output_device.connectionType == ConnectionType.CloudConnection + def activeMachineHasActiveNetworkConnection(self) -> bool: + # A network connection is only available if any output device is actually a network connected device. + return any(d.connectionType == ConnectionType.NetworkConnection for d in self._printer_output_devices) + + @pyqtProperty(bool, notify = printerConnectedStatusChanged) + def activeMachineHasActiveCloudConnection(self) -> bool: + # A cloud connection is only available if any output device actually is a cloud connected device. + return any(d.connectionType == ConnectionType.CloudConnection for d in self._printer_output_devices) def activeMachineNetworkKey(self) -> str: if self._global_container_stack: diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index c436418f5e..093aa05ea9 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -246,7 +246,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): if self._printers and not self._active_printer: self.setActivePrinter(self._printers[0]) - self.printersChanged.emit() # TODO: Make this more efficient by not updating every request + if added_printers or removed_printers or updated_printers: + self.printersChanged.emit() ## Updates the local list of print jobs with the list received from the cloud. # \param jobs: The print jobs received from the cloud. @@ -302,10 +303,10 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Updates the printer assignment for the given print job model. def _updateAssignedPrinter(self, model: UM3PrintJobOutputModel, printer_uuid: str) -> None: printer = next((p for p in self._printers if printer_uuid == p.key), None) - if not printer: - return Logger.log("w", "Missing printer %s for job %s in %s", model.assignedPrinter, model.key, - [p.key for p in self._printers]) + Logger.log("w", "Missing printer %s for job %s in %s", model.assignedPrinter, model.key, + [p.key for p in self._printers]) + return printer.updateActivePrintJob(model) model.updateAssignedPrinter(printer) @@ -329,11 +330,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def _onUploadError(self, message = None) -> None: self._progress.hide() self._uploaded_print_job = None - Message( - text = message or T.UPLOAD_ERROR, - title = T.ERROR, - lifetime = 10 - ).show() + Message(text = message or T.UPLOAD_ERROR, title = T.ERROR, lifetime = 10).show() self.writeError.emit() ## Shows a message when the upload has succeeded @@ -341,11 +338,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def _onPrintRequested(self, response: CloudPrintResponse) -> None: Logger.log("d", "The cluster will be printing this print job with the ID %s", response.cluster_job_id) self._progress.hide() - Message( - text = T.UPLOAD_SUCCESS_TEXT, - title = T.UPLOAD_SUCCESS_TITLE, - lifetime = 5 - ).show() + Message(text = T.UPLOAD_SUCCESS_TEXT, title = T.UPLOAD_SUCCESS_TITLE, lifetime = 5).show() self.writeFinished.emit() ## Gets the remote printers. diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index a9a002aed7..75fd1e0f9e 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -389,10 +389,8 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): ## Called when the connection to the cluster changes. def connect(self) -> None: - # TODO: uncomment this once cloud implementation works for testing - # super().connect() - # self.sendMaterialProfiles() - pass + super().connect() + self.sendMaterialProfiles() def _onGetPreviewImageFinished(self, reply: QNetworkReply) -> None: reply_url = reply.url().toString() diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index bb8934f620..64cf3e6005 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -12,9 +12,9 @@ Cura.ExpandablePopup id: machineSelector property var outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null - property bool isNetworkPrinter: Cura.MachineManager.activeMachineHasRemoteConnection - property bool isCloudPrinter: Cura.MachineManager.activeMachineHasCloudConnection property bool isPrinterConnected: Cura.MachineManager.printerConnected + property bool isNetworkPrinter: Cura.MachineManager.activeMachineHasActiveNetworkConnection + property bool isCloudPrinter: Cura.MachineManager.activeMachineHasActiveCloudConnection contentPadding: UM.Theme.getSize("default_lining").width contentAlignment: Cura.ExpandablePopup.ContentAlignment.AlignLeft From 9ffc03925469aaaebceebd7d7931ab377e2f5d95 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 20 Dec 2018 14:01:12 +0100 Subject: [PATCH 1040/1240] Fix logo aliasing CURA-6044 --- plugins/VersionUpgrade/VersionUpgrade32to33/__init__.py | 2 +- resources/qml/MainWindow/MainWindowHeader.qml | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/VersionUpgrade/VersionUpgrade32to33/__init__.py b/plugins/VersionUpgrade/VersionUpgrade32to33/__init__.py index 006b21bc48..b55ea5ebaf 100644 --- a/plugins/VersionUpgrade/VersionUpgrade32to33/__init__.py +++ b/plugins/VersionUpgrade/VersionUpgrade32to33/__init__.py @@ -47,7 +47,7 @@ def getMetaData() -> Dict[str, Any]: }, "user": { "get_version": upgrade.getCfgVersion, - "location": {"./user"} + "location": {"./user", "./materials/*"} }, "variant": { "get_version": upgrade.getCfgVersion, diff --git a/resources/qml/MainWindow/MainWindowHeader.qml b/resources/qml/MainWindow/MainWindowHeader.qml index 971b275bd8..e3b7e199fa 100644 --- a/resources/qml/MainWindow/MainWindowHeader.qml +++ b/resources/qml/MainWindow/MainWindowHeader.qml @@ -30,7 +30,8 @@ Item width: UM.Theme.getSize("logo").width height: UM.Theme.getSize("logo").height - mipmap: true + sourceSize.width: width + sourceSize.height: height } Row From e79f312fa0fa3e35bfc407e4c7d59a572d044686 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 20 Dec 2018 14:20:31 +0100 Subject: [PATCH 1041/1240] Fix unit tests for SendMaterialJob CURA-6035 --- .../UM3NetworkPrinting/src/SendMaterialJob.py | 10 +- .../tests/TestSendMaterialJob.py | 135 +++++++++++++----- 2 files changed, 102 insertions(+), 43 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py index 7a0da24098..ab6ebfef2e 100644 --- a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py @@ -6,9 +6,9 @@ from typing import Dict, TYPE_CHECKING, Set from PyQt5.QtNetwork import QNetworkReply, QNetworkRequest +from UM.Application import Application from UM.Job import Job from UM.Logger import Logger -from cura.CuraApplication import CuraApplication # Absolute imports don't work in plugins from .Models import ClusterMaterial, LocalMaterial @@ -34,7 +34,6 @@ class SendMaterialJob(Job): # # \param reply The reply from the printer, a json file. def _onGetRemoteMaterials(self, reply: QNetworkReply) -> None: - # Got an error from the HTTP request. If we did not receive a 200 something happened. if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) != 200: Logger.log("e", "Error fetching materials from printer: %s", reply.errorString()) @@ -49,7 +48,6 @@ class SendMaterialJob(Job): # # \param remote_materials_by_guid The remote materials by GUID. def _sendMissingMaterials(self, remote_materials_by_guid: Dict[str, ClusterMaterial]) -> None: - # Collect local materials local_materials_by_guid = self._getLocalMaterials() if len(local_materials_by_guid) == 0: @@ -88,8 +86,8 @@ class SendMaterialJob(Job): # # \param materials_to_send A set with id's of materials that must be sent. def _sendMaterials(self, materials_to_send: Set[str]) -> None: - container_registry = CuraApplication.getInstance().getContainerRegistry() - material_manager = CuraApplication.getInstance().getMaterialManager() + container_registry = Application.getInstance().getContainerRegistry() + material_manager = Application.getInstance().getMaterialManager() material_group_dict = material_manager.getAllMaterialGroups() for root_material_id in material_group_dict: @@ -167,7 +165,7 @@ class SendMaterialJob(Job): # \return a dictionary of LocalMaterial objects by GUID def _getLocalMaterials(self) -> Dict[str, LocalMaterial]: result = {} # type: Dict[str, LocalMaterial] - material_manager = CuraApplication.getInstance().getMaterialManager() + material_manager = Application.getInstance().getMaterialManager() material_group_dict = material_manager.getAllMaterialGroups() diff --git a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py index b669eb192a..4303f89c20 100644 --- a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py @@ -1,26 +1,29 @@ # Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +import copy import io import json from unittest import TestCase, mock -from unittest.mock import patch, call +from unittest.mock import patch, call, MagicMock from PyQt5.QtCore import QByteArray -from UM.MimeTypeDatabase import MimeType from UM.Application import Application + +from cura.Machines.MaterialGroup import MaterialGroup +from cura.Machines.MaterialNode import MaterialNode + from plugins.UM3NetworkPrinting.src.SendMaterialJob import SendMaterialJob +_FILES_MAP = {"generic_pla_white": "/materials/generic_pla_white.xml.fdm_material", + "generic_pla_black": "/materials/generic_pla_black.xml.fdm_material", + } + @patch("builtins.open", lambda _, __: io.StringIO("")) -@patch("UM.MimeTypeDatabase.MimeTypeDatabase.getMimeTypeForFile", - lambda _: MimeType(name = "application/x-ultimaker-material-profile", comment = "Ultimaker Material Profile", - suffixes = ["xml.fdm_material"])) -@patch("UM.Resources.Resources.getAllResourcesOfType", lambda _: ["/materials/generic_pla_white.xml.fdm_material"]) -@patch("plugins.UM3NetworkPrinting.src.ClusterUM3OutputDevice") -@patch("PyQt5.QtNetwork.QNetworkReply") class TestSendMaterialJob(TestCase): + # version 1 _LOCAL_MATERIAL_WHITE = {"type": "material", "status": "unknown", "id": "generic_pla_white", "base_file": "generic_pla_white", "setting_version": "5", "name": "White PLA", "brand": "Generic", "material": "PLA", "color_name": "White", @@ -29,6 +32,37 @@ class TestSendMaterialJob(TestCase): "properties": {"density": "1.00", "diameter": "2.85", "weight": "750"}, "definition": "fdmprinter", "compatible": True} + # version 2 + _LOCAL_MATERIAL_WHITE_NEWER = {"type": "material", "status": "unknown", "id": "generic_pla_white", + "base_file": "generic_pla_white", "setting_version": "5", "name": "White PLA", + "brand": "Generic", "material": "PLA", "color_name": "White", + "GUID": "badb0ee7-87c8-4f3f-9398-938587b67dce", "version": "2", + "color_code": "#ffffff", + "description": "Test PLA White", "adhesion_info": "Use glue.", + "approximate_diameter": "3", + "properties": {"density": "1.00", "diameter": "2.85", "weight": "750"}, + "definition": "fdmprinter", "compatible": True} + + # invalid version: "one" + _LOCAL_MATERIAL_WHITE_INVALID_VERSION = {"type": "material", "status": "unknown", "id": "generic_pla_white", + "base_file": "generic_pla_white", "setting_version": "5", "name": "White PLA", + "brand": "Generic", "material": "PLA", "color_name": "White", + "GUID": "badb0ee7-87c8-4f3f-9398-938587b67dce", "version": "one", + "color_code": "#ffffff", + "description": "Test PLA White", "adhesion_info": "Use glue.", + "approximate_diameter": "3", + "properties": {"density": "1.00", "diameter": "2.85", "weight": "750"}, + "definition": "fdmprinter", "compatible": True} + + _LOCAL_MATERIAL_WHITE_ALL_RESULT = {"generic_pla_white": MaterialGroup("generic_pla_white", + MaterialNode(_LOCAL_MATERIAL_WHITE))} + + _LOCAL_MATERIAL_WHITE_NEWER_ALL_RESULT = {"generic_pla_white": MaterialGroup("generic_pla_white", + MaterialNode(_LOCAL_MATERIAL_WHITE_NEWER))} + + _LOCAL_MATERIAL_WHITE_INVALID_VERSION_ALL_RESULT = {"generic_pla_white": MaterialGroup("generic_pla_white", + MaterialNode(_LOCAL_MATERIAL_WHITE_INVALID_VERSION))} + _LOCAL_MATERIAL_BLACK = {"type": "material", "status": "unknown", "id": "generic_pla_black", "base_file": "generic_pla_black", "setting_version": "5", "name": "Yellow CPE", "brand": "Ultimaker", "material": "CPE", "color_name": "Black", @@ -37,6 +71,9 @@ class TestSendMaterialJob(TestCase): "properties": {"density": "1.01", "diameter": "2.85", "weight": "750"}, "definition": "fdmprinter", "compatible": True} + _LOCAL_MATERIAL_BLACK_ALL_RESULT = {"generic_pla_black": MaterialGroup("generic_pla_black", + MaterialNode(_LOCAL_MATERIAL_BLACK))} + _REMOTE_MATERIAL_WHITE = { "guid": "badb0ee7-87c8-4f3f-9398-938587b67dce", "material": "PLA", @@ -55,14 +92,17 @@ class TestSendMaterialJob(TestCase): "density": 1.00 } - def test_run(self, device_mock, reply_mock): + def test_run(self): + device_mock = MagicMock() job = SendMaterialJob(device_mock) job.run() # We expect the materials endpoint to be called when the job runs. device_mock.get.assert_called_with("materials/", on_finished = job._onGetRemoteMaterials) - def test__onGetRemoteMaterials_withFailedRequest(self, reply_mock, device_mock): + def test__onGetRemoteMaterials_withFailedRequest(self): + reply_mock = MagicMock() + device_mock = MagicMock() reply_mock.attribute.return_value = 404 job = SendMaterialJob(device_mock) job._onGetRemoteMaterials(reply_mock) @@ -70,7 +110,9 @@ class TestSendMaterialJob(TestCase): # We expect the device not to be called for any follow up. self.assertEqual(0, device_mock.createFormPart.call_count) - def test__onGetRemoteMaterials_withWrongEncoding(self, reply_mock, device_mock): + def test__onGetRemoteMaterials_withWrongEncoding(self): + reply_mock = MagicMock() + device_mock = MagicMock() reply_mock.attribute.return_value = 200 reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("cp500")) job = SendMaterialJob(device_mock) @@ -79,7 +121,9 @@ class TestSendMaterialJob(TestCase): # Given that the parsing fails we do no expect the device to be called for any follow up. self.assertEqual(0, device_mock.createFormPart.call_count) - def test__onGetRemoteMaterials_withBadJsonAnswer(self, reply_mock, device_mock): + def test__onGetRemoteMaterials_withBadJsonAnswer(self): + reply_mock = MagicMock() + device_mock = MagicMock() reply_mock.attribute.return_value = 200 reply_mock.readAll.return_value = QByteArray(b"Six sick hicks nick six slick bricks with picks and sticks.") job = SendMaterialJob(device_mock) @@ -88,7 +132,9 @@ class TestSendMaterialJob(TestCase): # Given that the parsing fails we do no expect the device to be called for any follow up. self.assertEqual(0, device_mock.createFormPart.call_count) - def test__onGetRemoteMaterials_withMissingGuidInRemoteMaterial(self, reply_mock, device_mock): + def test__onGetRemoteMaterials_withMissingGuidInRemoteMaterial(self): + reply_mock = MagicMock() + device_mock = MagicMock() reply_mock.attribute.return_value = 200 remote_material_without_guid = self._REMOTE_MATERIAL_WHITE.copy() del remote_material_without_guid["guid"] @@ -99,18 +145,20 @@ class TestSendMaterialJob(TestCase): # Given that parsing fails we do not expect the device to be called for any follow up. self.assertEqual(0, device_mock.createFormPart.call_count) + @patch("cura.Machines.MaterialManager.MaterialManager") @patch("cura.Settings.CuraContainerRegistry") @patch("UM.Application") def test__onGetRemoteMaterials_withInvalidVersionInLocalMaterial(self, application_mock, container_registry_mock, - reply_mock, device_mock): + material_manager_mock): + reply_mock = MagicMock() + device_mock = MagicMock() + application_mock.getContainerRegistry.return_value = container_registry_mock + application_mock.getMaterialManager.return_value = material_manager_mock + reply_mock.attribute.return_value = 200 reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii")) - localMaterialWhiteWithInvalidVersion = self._LOCAL_MATERIAL_WHITE.copy() - localMaterialWhiteWithInvalidVersion["version"] = "one" - container_registry_mock.findContainersMetadata.return_value = [localMaterialWhiteWithInvalidVersion] - - application_mock.getContainerRegistry.return_value = container_registry_mock + material_manager_mock.getAllMaterialGroups.return_value = self._LOCAL_MATERIAL_WHITE_INVALID_VERSION_ALL_RESULT.copy() with mock.patch.object(Application, "getInstance", new = lambda: application_mock): job = SendMaterialJob(device_mock) @@ -118,15 +166,19 @@ class TestSendMaterialJob(TestCase): self.assertEqual(0, device_mock.createFormPart.call_count) + @patch("cura.Machines.MaterialManager") @patch("cura.Settings.CuraContainerRegistry") @patch("UM.Application") - def test__onGetRemoteMaterials_withNoUpdate(self, application_mock, container_registry_mock, reply_mock, - device_mock): + def test__onGetRemoteMaterials_withNoUpdate(self, application_mock, container_registry_mock, + material_manager_mock): + reply_mock = MagicMock() + device_mock = MagicMock() application_mock.getContainerRegistry.return_value = container_registry_mock + application_mock.getMaterialManager.return_value = material_manager_mock device_mock.createFormPart.return_value = "_xXx_" - container_registry_mock.findContainersMetadata.return_value = [self._LOCAL_MATERIAL_WHITE] + material_manager_mock.getAllMaterialGroups.return_value = self._LOCAL_MATERIAL_WHITE_ALL_RESULT.copy() reply_mock.attribute.return_value = 200 reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii")) @@ -138,24 +190,25 @@ class TestSendMaterialJob(TestCase): self.assertEqual(0, device_mock.createFormPart.call_count) self.assertEqual(0, device_mock.postFormWithParts.call_count) - @patch("cura.Settings.CuraContainerRegistry") - @patch("UM.Application") - def test__onGetRemoteMaterials_withUpdatedMaterial(self, application_mock, container_registry_mock, reply_mock, - device_mock): - application_mock.getContainerRegistry.return_value = container_registry_mock + @patch("UM.Application.Application.getInstance") + def test__onGetRemoteMaterials_withUpdatedMaterial(self, get_instance_mock): + reply_mock = MagicMock() + device_mock = MagicMock() + application_mock = get_instance_mock.return_value + container_registry_mock = application_mock.getContainerRegistry.return_value + material_manager_mock = application_mock.getMaterialManager.return_value + + container_registry_mock.getContainerFilePathById = lambda x: _FILES_MAP.get(x) device_mock.createFormPart.return_value = "_xXx_" - localMaterialWhiteWithHigherVersion = self._LOCAL_MATERIAL_WHITE.copy() - localMaterialWhiteWithHigherVersion["version"] = "2" - container_registry_mock.findContainersMetadata.return_value = [localMaterialWhiteWithHigherVersion] + material_manager_mock.getAllMaterialGroups.return_value = self._LOCAL_MATERIAL_WHITE_NEWER_ALL_RESULT.copy() reply_mock.attribute.return_value = 200 reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii")) - with mock.patch.object(Application, "getInstance", new = lambda: application_mock): - job = SendMaterialJob(device_mock) - job._onGetRemoteMaterials(reply_mock) + job = SendMaterialJob(device_mock) + job._onGetRemoteMaterials(reply_mock) self.assertEqual(1, device_mock.createFormPart.call_count) self.assertEqual(1, device_mock.postFormWithParts.call_count) @@ -164,16 +217,24 @@ class TestSendMaterialJob(TestCase): call.postFormWithParts(target = "materials/", parts = ["_xXx_"], on_finished = job.sendingFinished)], device_mock.method_calls) + @patch("cura.Machines.MaterialManager.MaterialManager") @patch("cura.Settings.CuraContainerRegistry") @patch("UM.Application") - def test__onGetRemoteMaterials_withNewMaterial(self, application_mock, container_registry_mock, reply_mock, - device_mock): + def test__onGetRemoteMaterials_withNewMaterial(self, application_mock, container_registry_mock, + material_manager_mock): + reply_mock = MagicMock() + device_mock = MagicMock() application_mock.getContainerRegistry.return_value = container_registry_mock + application_mock.getMaterialManager.return_value = material_manager_mock + + container_registry_mock.getContainerFilePathById = lambda x: _FILES_MAP.get(x) device_mock.createFormPart.return_value = "_xXx_" - container_registry_mock.findContainersMetadata.return_value = [self._LOCAL_MATERIAL_WHITE, - self._LOCAL_MATERIAL_BLACK] + all_results = self._LOCAL_MATERIAL_WHITE_ALL_RESULT.copy() + for key, value in self._LOCAL_MATERIAL_BLACK_ALL_RESULT.items(): + all_results[key] = value + material_manager_mock.getAllMaterialGroups.return_value = all_results reply_mock.attribute.return_value = 200 reply_mock.readAll.return_value = QByteArray(json.dumps([self._REMOTE_MATERIAL_BLACK]).encode("ascii")) From 346d8d884e3f39e8aaae740ca1dcbf26022b3e0f Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 20 Dec 2018 14:23:37 +0100 Subject: [PATCH 1042/1240] Remove unnecessary patches in TestSendMaterialJob CURA-6035 --- .../tests/TestSendMaterialJob.py | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py index 4303f89c20..6eac892af6 100644 --- a/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/tests/TestSendMaterialJob.py @@ -166,15 +166,12 @@ class TestSendMaterialJob(TestCase): self.assertEqual(0, device_mock.createFormPart.call_count) - @patch("cura.Machines.MaterialManager") - @patch("cura.Settings.CuraContainerRegistry") - @patch("UM.Application") - def test__onGetRemoteMaterials_withNoUpdate(self, application_mock, container_registry_mock, - material_manager_mock): + @patch("UM.Application.Application.getInstance") + def test__onGetRemoteMaterials_withNoUpdate(self, application_mock): reply_mock = MagicMock() device_mock = MagicMock() - application_mock.getContainerRegistry.return_value = container_registry_mock - application_mock.getMaterialManager.return_value = material_manager_mock + container_registry_mock = application_mock.getContainerRegistry.return_value + material_manager_mock = application_mock.getMaterialManager.return_value device_mock.createFormPart.return_value = "_xXx_" @@ -217,15 +214,12 @@ class TestSendMaterialJob(TestCase): call.postFormWithParts(target = "materials/", parts = ["_xXx_"], on_finished = job.sendingFinished)], device_mock.method_calls) - @patch("cura.Machines.MaterialManager.MaterialManager") - @patch("cura.Settings.CuraContainerRegistry") - @patch("UM.Application") - def test__onGetRemoteMaterials_withNewMaterial(self, application_mock, container_registry_mock, - material_manager_mock): + @patch("UM.Application.Application.getInstance") + def test__onGetRemoteMaterials_withNewMaterial(self, application_mock): reply_mock = MagicMock() device_mock = MagicMock() - application_mock.getContainerRegistry.return_value = container_registry_mock - application_mock.getMaterialManager.return_value = material_manager_mock + container_registry_mock = application_mock.getContainerRegistry.return_value + material_manager_mock = application_mock.getMaterialManager.return_value container_registry_mock.getContainerFilePathById = lambda x: _FILES_MAP.get(x) From af2061cd52ec05cd9f25f07c6f858e2b81194fc8 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Thu, 20 Dec 2018 14:26:30 +0100 Subject: [PATCH 1043/1240] Simplify some checks for connection types and group size --- cura/Settings/MachineManager.py | 13 ++++++--- .../src/Cloud/CloudOutputDeviceManager.py | 7 +++-- .../qml/PrinterSelector/MachineSelector.qml | 29 ++++++++++++------- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 66ee7f9543..b5f8420b97 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -527,10 +527,15 @@ class MachineManager(QObject): @pyqtProperty(bool, notify = printerConnectedStatusChanged) def activeMachineHasRemoteConnection(self) -> bool: - if self._global_container_stack: - connection_type = self._global_container_stack.getMetaDataEntry("connection_type") - return connection_type in [ConnectionType.NetworkConnection.value, ConnectionType.CloudConnection.value] - return False + return self.activeMachineHasActiveNetworkConnection or self.activeMachineHasActiveCloudConnection + # if self._global_container_stack: + # connection_type = self._global_container_stack.getMetaDataEntry("connection_type") + # return connection_type in [ConnectionType.NetworkConnection.value, ConnectionType.CloudConnection.value] + # return False + + @pyqtProperty(bool, notify = printerConnectedStatusChanged) + def activeMachineIsGroup(self) -> bool: + return self._printer_output_devices and self._printer_output_devices[0].clusterSize > 1 @pyqtProperty(bool, notify = printerConnectedStatusChanged) def activeMachineHasActiveNetworkConnection(self) -> bool: diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index af45f06394..c849ebfe4a 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -121,10 +121,11 @@ class CloudOutputDeviceManager: return device = next((c for c in self._remote_clusters.values() if c.matchesNetworkKey(local_network_key)), None) - if device: - active_machine.setMetaDataEntry(self.META_CLUSTER_ID, device.key) - device.connect() + if not device: + return + active_machine.setMetaDataEntry(self.META_CLUSTER_ID, device.key) + device.connect() Logger.log("i", "Found cluster %s with network key %s", device, local_network_key) ## Handles an API error received from the cloud. diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index 64cf3e6005..c39acb53e7 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -11,10 +11,9 @@ Cura.ExpandablePopup { id: machineSelector - property var outputDevice: Cura.MachineManager.printerOutputDevices.length >= 1 ? Cura.MachineManager.printerOutputDevices[0] : null - property bool isPrinterConnected: Cura.MachineManager.printerConnected property bool isNetworkPrinter: Cura.MachineManager.activeMachineHasActiveNetworkConnection property bool isCloudPrinter: Cura.MachineManager.activeMachineHasActiveCloudConnection + property bool isGroup: Cura.MachineManager.activeMachineIsGroup contentPadding: UM.Theme.getSize("default_lining").width contentAlignment: Cura.ExpandablePopup.ContentAlignment.AlignLeft @@ -30,15 +29,13 @@ Cura.ExpandablePopup text: isNetworkPrinter ? Cura.MachineManager.activeMachineNetworkGroupName : Cura.MachineManager.activeMachineName source: { - if (isNetworkPrinter) - { - if (machineSelector.outputDevice != null && machineSelector.outputDevice.clusterSize > 1) - { - return UM.Theme.getIcon("printer_group") - } + if (isGroup) { + return UM.Theme.getIcon("printer_group") + } else if (isNetworkPrinter || isCloudPrinter) { return UM.Theme.getIcon("printer_single") + } else { + return "" } - return "" } font: UM.Theme.getFont("medium") iconColor: UM.Theme.getColor("machine_selector_printer_icon") @@ -53,12 +50,22 @@ Cura.ExpandablePopup leftMargin: UM.Theme.getSize("thick_margin").width } - source: machineSelector.isCloudPrinter ? UM.Theme.getIcon("printer_cloud_connected") : UM.Theme.getIcon("printer_connected") + source: + { + if (isNetworkPrinter) { + return UM.Theme.getIcon("printer_connected") + } else if (isCloudPrinter) { + return UM.Theme.getIcon("printer_cloud_connected") + } else { + return "" + } + } + width: UM.Theme.getSize("printer_status_icon").width height: UM.Theme.getSize("printer_status_icon").height color: UM.Theme.getColor("primary") - visible: isNetworkPrinter && isPrinterConnected + visible: isNetworkPrinter || isCloudPrinter // Make a themable circle in the background so we can change it in other themes Rectangle From bbddbcde9a780660c603d080ca903af1a9949654 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Thu, 20 Dec 2018 14:29:39 +0100 Subject: [PATCH 1044/1240] cleanup --- cura/Settings/MachineManager.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index b5f8420b97..1cb9af02d1 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -528,10 +528,6 @@ class MachineManager(QObject): @pyqtProperty(bool, notify = printerConnectedStatusChanged) def activeMachineHasRemoteConnection(self) -> bool: return self.activeMachineHasActiveNetworkConnection or self.activeMachineHasActiveCloudConnection - # if self._global_container_stack: - # connection_type = self._global_container_stack.getMetaDataEntry("connection_type") - # return connection_type in [ConnectionType.NetworkConnection.value, ConnectionType.CloudConnection.value] - # return False @pyqtProperty(bool, notify = printerConnectedStatusChanged) def activeMachineIsGroup(self) -> bool: From f3a0b44d5ebfb7a69d54cf1610a50eeec0285941 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 20 Dec 2018 14:24:00 +0100 Subject: [PATCH 1045/1240] Simplify is_favorite condition No stupid ternary operators necessary. Contributes to issue CURA-6032. --- resources/qml/Preferences/Materials/MaterialsSlot.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Preferences/Materials/MaterialsSlot.qml b/resources/qml/Preferences/Materials/MaterialsSlot.qml index fb3cb9607d..7c891dc65f 100644 --- a/resources/qml/Preferences/Materials/MaterialsSlot.qml +++ b/resources/qml/Preferences/Materials/MaterialsSlot.qml @@ -15,7 +15,7 @@ Rectangle id: materialSlot property var material: null property var hovered: false - property var is_favorite: material != null ? material.is_favorite : false + property var is_favorite: material != null && material.is_favorite height: UM.Theme.getSize("favorites_row").height width: parent.width From 5bf260f5247f1367d0bf100514d596f4b8d4cfee Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 20 Dec 2018 14:25:52 +0100 Subject: [PATCH 1046/1240] Don't break binding of is_favorite upon clicking star We shouldn't override it here because doing that causes the binding to break. Just remove the favourite and let the model update itself, thus removing the star. Contributes to issue CURA-6032. --- resources/qml/Preferences/Materials/MaterialsSlot.qml | 2 -- 1 file changed, 2 deletions(-) diff --git a/resources/qml/Preferences/Materials/MaterialsSlot.qml b/resources/qml/Preferences/Materials/MaterialsSlot.qml index 7c891dc65f..2f4847103b 100644 --- a/resources/qml/Preferences/Materials/MaterialsSlot.qml +++ b/resources/qml/Preferences/Materials/MaterialsSlot.qml @@ -73,11 +73,9 @@ Rectangle if (materialSlot.is_favorite) { base.materialManager.removeFavorite(material.root_material_id) - materialSlot.is_favorite = false return } base.materialManager.addFavorite(material.root_material_id) - materialSlot.is_favorite = true return } style: ButtonStyle From 1012eb7553db8f2246c0a94c21faa1012a9d2555 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Thu, 20 Dec 2018 14:48:56 +0100 Subject: [PATCH 1047/1240] Assure bool --- 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 1cb9af02d1..c87ddf2f80 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -531,7 +531,7 @@ class MachineManager(QObject): @pyqtProperty(bool, notify = printerConnectedStatusChanged) def activeMachineIsGroup(self) -> bool: - return self._printer_output_devices and self._printer_output_devices[0].clusterSize > 1 + return bool(self._printer_output_devices) and self._printer_output_devices[0].clusterSize > 1 @pyqtProperty(bool, notify = printerConnectedStatusChanged) def activeMachineHasActiveNetworkConnection(self) -> bool: From 119d3e9974780708516f88e760b4ea3d1dd3e665 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Thu, 20 Dec 2018 15:47:48 +0100 Subject: [PATCH 1048/1240] Fix printer selector sections --- cura/PrintersModel.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cura/PrintersModel.py b/cura/PrintersModel.py index 8b5d2f6cc9..77df73505d 100644 --- a/cura/PrintersModel.py +++ b/cura/PrintersModel.py @@ -53,9 +53,8 @@ class PrintersModel(ListModel): container_stacks = ContainerRegistry.getInstance().findContainerStacks(type = "machine") for container_stack in container_stacks: - connection_type = container_stack.getMetaDataEntry("connection_type") - has_remote_connection = connection_type in [ConnectionType.NetworkConnection.value, ConnectionType.CloudConnection.value] - + connection_type = container_stack.getMetaDataEntry("connection_type", 0) + has_remote_connection = int(connection_type) in [ConnectionType.NetworkConnection.value, ConnectionType.CloudConnection.value] if container_stack.getMetaDataEntry("hidden", False) in ["True", True]: continue From 3f82cd491697588c8ffcd58cf611fa9c845439ae Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Thu, 20 Dec 2018 15:55:04 +0100 Subject: [PATCH 1049/1240] Add missing new-line --- cura/PrintersModel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/PrintersModel.py b/cura/PrintersModel.py index 77df73505d..f05697d2b5 100644 --- a/cura/PrintersModel.py +++ b/cura/PrintersModel.py @@ -65,4 +65,4 @@ class PrintersModel(ListModel): "connectionType": connection_type, "metadata": container_stack.getMetaData().copy()}) items.sort(key=lambda i: not i["hasRemoteConnection"]) - self.setItems(items) \ No newline at end of file + self.setItems(items) From 3f599bd06f817ad39aaeb9d50b6939760e5505ea Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 20 Dec 2018 16:31:57 +0100 Subject: [PATCH 1050/1240] Ensure that the tooltip text is translated CURA-6004 --- resources/qml/ActionPanel/SliceProcessWidget.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index ab5e224c90..79c0186443 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -109,7 +109,7 @@ Column fixedWidthMode: true anchors.fill: parent text: catalog.i18nc("@button", "Slice") - tooltip: "Start slicing process" + tooltip: catalog.i18nc("@label", "Start the slicing process") enabled: widget.backendState != UM.Backend.Error visible: widget.backendState == UM.Backend.NotStarted || widget.backendState == UM.Backend.Error onClicked: sliceOrStopSlicing() From 403010aa90184a7977648d0d0701791ef5d33a1b Mon Sep 17 00:00:00 2001 From: Marijn Dee Date: Thu, 20 Dec 2018 16:32:00 +0100 Subject: [PATCH 1051/1240] Mirrored the changes made to the models in Commons --- .../Cloud/Models/CloudClusterBuildPlate.py | 13 +++++ ... => CloudClusterPrintCoreConfiguration.py} | 5 +- ...CloudClusterPrintJobConfigurationChange.py | 28 +++++++++ .../Models/CloudClusterPrintJobImpediment.py | 15 +++++ .../Models/CloudClusterPrintJobStatus.py | 58 ++++++++++++++++--- .../Cloud/Models/CloudClusterPrinterStatus.py | 20 +++++-- 6 files changed, 124 insertions(+), 15 deletions(-) create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterBuildPlate.py rename plugins/UM3NetworkPrinting/src/Cloud/Models/{CloudClusterPrinterConfiguration.py => CloudClusterPrintCoreConfiguration.py} (92%) create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobConfigurationChange.py create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobImpediment.py diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterBuildPlate.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterBuildPlate.py new file mode 100644 index 0000000000..4386bbb435 --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterBuildPlate.py @@ -0,0 +1,13 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from .BaseCloudModel import BaseCloudModel + + +## Class representing a cluster printer +# Spec: https://api-staging.ultimaker.com/connect/v1/spec +class CloudClusterBuildPlate(BaseCloudModel): + ## Create a new build plate + # \param type: The type of buildplate glass or aluminium + def __init__(self, type: str = "glass", **kwargs) -> None: + self.type = type + super().__init__(**kwargs) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfiguration.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintCoreConfiguration.py similarity index 92% rename from plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfiguration.py rename to plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintCoreConfiguration.py index 3e06d0e2e7..7454401d09 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterConfiguration.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintCoreConfiguration.py @@ -10,7 +10,7 @@ from .BaseCloudModel import BaseCloudModel ## Class representing a cloud cluster printer configuration # Spec: https://api-staging.ultimaker.com/connect/v1/spec -class CloudClusterPrinterConfiguration(BaseCloudModel): +class CloudClusterPrintCoreConfiguration(BaseCloudModel): ## Creates a new cloud cluster printer configuration object # \param extruder_index: The position of the extruder on the machine as list index. Numbered from left to right. # \param material: The material of a configuration object in a cluster printer. May be in a dict or an object. @@ -18,10 +18,9 @@ class CloudClusterPrinterConfiguration(BaseCloudModel): # \param print_core_id: The type of print core inserted at this position, e.g. 'AA 0.4'. def __init__(self, extruder_index: int, material: Union[None, Dict[str, Any], CloudClusterPrinterConfigurationMaterial], - nozzle_diameter: Optional[str] = None, print_core_id: Optional[str] = None, **kwargs) -> None: + print_core_id: Optional[str] = None, **kwargs) -> None: self.extruder_index = extruder_index self.material = self.parseModel(CloudClusterPrinterConfigurationMaterial, material) if material else None - self.nozzle_diameter = nozzle_diameter self.print_core_id = print_core_id super().__init__(**kwargs) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobConfigurationChange.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobConfigurationChange.py new file mode 100644 index 0000000000..6c02972757 --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobConfigurationChange.py @@ -0,0 +1,28 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from typing import Optional + +from .BaseCloudModel import BaseCloudModel + + +## Model for the types of changes that are needed before a print job can start +# Spec: https://api-staging.ultimaker.com/connect/v1/spec +class CloudClusterPrintJobConfigurationChange(BaseCloudModel): + ## Creates a new print job constraint. + # \param type_of_change: The type of configuration change, one of: "material", "print_core_change" + # \param index: The hotend slot or extruder index to change + # \param target_id: Target material guid or hotend id + # \param origin_id: Original/current material guid or hotend id + # \param target_name: Target material name or hotend id + # \param origin_name: Original/current material name or hotend id + def __init__(self, type_of_change: Optional[str] = None, index: Optional[int] = None, + target_id: Optional[str] = None,origin_id: Optional[str] = None, + target_name: Optional[str] = None,origin_name: Optional[str] = None, + **kwargs) -> None: + self.type_of_change = type_of_change + self.index = index + self.target_id = target_id + self.origin_id = origin_id + self.target_name = target_name + self.origin_name = origin_name + super().__init__(**kwargs) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobImpediment.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobImpediment.py new file mode 100644 index 0000000000..12b67996c1 --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobImpediment.py @@ -0,0 +1,15 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from .BaseCloudModel import BaseCloudModel + + +## Class representing the reasons that prevent this job from being printed on the associated printer +# Spec: https://api-staging.ultimaker.com/connect/v1/spec +class CloudClusterPrintJobImpediment(BaseCloudModel): + ## Creates a new print job constraint. + # \param translation_key: A string indicating a reason the print cannot be printed, such as 'does_not_fit_in_build_volume' + # \param severity: A number indicating the severity of the problem, with higher being more severe + def __init__(self, translation_key: str, severity: int, **kwargs) -> None: + self.translation_key = translation_key + self.severity = severity + super().__init__(**kwargs) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobStatus.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobStatus.py index f451665a4f..4b70d191e4 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobStatus.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobStatus.py @@ -4,11 +4,14 @@ from typing import List, Optional, Union, Dict, Any from cura.PrinterOutput.ConfigurationModel import ConfigurationModel from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputController import CloudOutputController -from .CloudClusterPrinterConfiguration import CloudClusterPrinterConfiguration +from src.ConfigurationChangeModel import ConfigurationChangeModel +from .CloudClusterBuildPlate import CloudClusterBuildPlate +from .CloudClusterPrintJobConfigurationChange import CloudClusterPrintJobConfigurationChange +from .CloudClusterPrintJobImpediment import CloudClusterPrintJobImpediment +from .CloudClusterPrintCoreConfiguration import CloudClusterPrintCoreConfiguration from .CloudClusterPrintJobConstraint import CloudClusterPrintJobConstraints from .BaseCloudModel import BaseCloudModel - ## Class representing a print job from plugins.UM3NetworkPrinting.src.UM3PrintJobOutputModel import UM3PrintJobOutputModel @@ -34,15 +37,29 @@ class CloudClusterPrintJobStatus(BaseCloudModel): # \param time_elapsed: The remaining printing time in seconds. # \param time_total: The total printing time in seconds. # \param uuid: UUID of this print job. Should be used for identification purposes. + # \param deleted_at: The time when this print job was deleted. + # \param printed_on_uuid: UUID of the printer used to print this job. + # \param configuration_changes_required: List of configuration changes the printer this job is associated with + # needs to make in order to be able to print this job + # \param build_plate: The build plate (type) this job needs to be printed on. + # \param compatible_machine_families: Family names of machines suitable for this print job + # \param impediments_to_printing: A list of reasons that prevent this job from being printed on the associated + # printer def __init__(self, created_at: str, force: bool, machine_variant: str, name: str, started: bool, status: str, time_total: int, uuid: str, - configuration: List[Union[Dict[str, Any], CloudClusterPrinterConfiguration]], + configuration: List[Union[Dict[str, Any], CloudClusterPrintCoreConfiguration]], constraints: List[Union[Dict[str, Any], CloudClusterPrintJobConstraints]], last_seen: Optional[float] = None, network_error_count: Optional[int] = None, owner: Optional[str] = None, printer_uuid: Optional[str] = None, time_elapsed: Optional[int] = None, - assigned_to: Optional[str] = None, **kwargs) -> None: + assigned_to: Optional[str] = None, deleted_at: Optional[str] = None, + printed_on_uuid: Optional[str] = None, + configuration_changes_required: List[ + Union[Dict[str, Any], CloudClusterPrintJobConfigurationChange]] = None, + build_plate: Optional[str] = None, compatible_machine_families: List[str] = None, + impediments_to_printing: List[Union[Dict[str, Any], CloudClusterPrintJobImpediment]] = None, + **kwargs) -> None: self.assigned_to = assigned_to - self.configuration = self.parseModels(CloudClusterPrinterConfiguration, configuration) + self.configuration = self.parseModels(CloudClusterPrintCoreConfiguration, configuration) self.constraints = self.parseModels(CloudClusterPrintJobConstraints, constraints) self.created_at = created_at self.force = force @@ -57,6 +74,14 @@ class CloudClusterPrintJobStatus(BaseCloudModel): self.time_elapsed = time_elapsed self.time_total = time_total self.uuid = uuid + self.deleted_at = deleted_at + self.printed_on_uuid = printed_on_uuid + self.configuration_changes_required = self.parseModels(CloudClusterPrintJobConfigurationChange, + configuration_changes_required) + self.build_plate = self.parseModel(CloudClusterBuildPlate, build_plate) + self.compatible_machine_families = compatible_machine_families + self.impediments_to_printing = self.parseModels(CloudClusterPrintJobImpediment, impediments_to_printing) + super().__init__(**kwargs) ## Creates an UM3 print job output model based on this cloud cluster print job. @@ -77,11 +102,28 @@ class CloudClusterPrintJobStatus(BaseCloudModel): ## Updates an UM3 print job output model based on this cloud cluster print job. # \param model: The model to update. def updateOutputModel(self, model: UM3PrintJobOutputModel) -> None: - # TODO: Add `compatible_machine_families` to the cloud, than add model.setCompatibleMachineFamilies() - # TODO: Add `impediments_to_printing` to the cloud, see ClusterUM3OutputDevice._updatePrintJob - # TODO: Use model.updateConfigurationChanges, see ClusterUM3OutputDevice#_createConfigurationChanges model.updateConfiguration(self._createConfigurationModel()) model.updateTimeTotal(self.time_total) model.updateTimeElapsed(self.time_elapsed) model.updateOwner(self.owner) model.updateState(self.status) + model.setCompatibleMachineFamilies(self.compatible_machine_families) + model.updateTimeTotal(self.time_total) + model.updateTimeElapsed(self.time_elapsed) + model.updateOwner(self.owner) + + status_set_by_impediment = False + for impediment in self.impediments_to_printing: + if impediment.severity == "UNFIXABLE": # TODO: impediment.severity is defined as int, this will not work, is there a translation? + status_set_by_impediment = True + model.updateState("error") + break + + if not status_set_by_impediment: + model.updateState(self.status) + + model.updateConfigurationChanges([ConfigurationChangeModel(type_of_change=change.type_of_change, + index=change.index, + target_name=change.target_name, + origin_name=change.origin_name) + for change in self.configuration_changes_required]) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterStatus.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterStatus.py index cd3b6bbdca..23409a761d 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterStatus.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterStatus.py @@ -4,7 +4,8 @@ from typing import List, Union, Dict, Optional, Any from cura.PrinterOutput.PrinterOutputController import PrinterOutputController from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel -from .CloudClusterPrinterConfiguration import CloudClusterPrinterConfiguration +from .CloudClusterBuildPlate import CloudClusterBuildPlate +from .CloudClusterPrintCoreConfiguration import CloudClusterPrintCoreConfiguration from .BaseCloudModel import BaseCloudModel @@ -22,12 +23,19 @@ class CloudClusterPrinterStatus(BaseCloudModel): # \param uuid: The unique ID of the printer, also known as GUID. # \param configuration: The active print core configurations of this printer. # \param reserved_by: A printer can be claimed by a specific print job. + # \param maintenance_required: Indicates if maintenance is necessary + # \param firmware_update_status: Whether the printer's firmware is up-to-date, value is one of: "up_to_date", + # "pending_update", "update_available", "update_in_progress", "update_failed", "update_impossible" + # \param latest_available_firmware: The version of the latest firmware that is available + # \param build_plate: The build plate that is on the printer def __init__(self, enabled: bool, firmware_version: str, friendly_name: str, ip_address: str, machine_variant: str, status: str, unique_name: str, uuid: str, - configuration: List[Union[Dict[str, Any], CloudClusterPrinterConfiguration]], - reserved_by: Optional[str] = None, **kwargs) -> None: + configuration: List[Union[Dict[str, Any], CloudClusterPrintCoreConfiguration]], + reserved_by: Optional[str] = None, maintenance_required: Optional[bool] = None, + firmware_update_status: Optional[str] = None, latest_available_firmware: Optional[str] = None, + build_plate: Optional[str] = None, **kwargs) -> None: - self.configuration = self.parseModels(CloudClusterPrinterConfiguration, configuration) + self.configuration = self.parseModels(CloudClusterPrintCoreConfiguration, configuration) self.enabled = enabled self.firmware_version = firmware_version self.friendly_name = friendly_name @@ -37,6 +45,10 @@ class CloudClusterPrinterStatus(BaseCloudModel): self.unique_name = unique_name self.uuid = uuid self.reserved_by = reserved_by + self.maintenance_required = maintenance_required + self.firmware_update_status = firmware_update_status + self.latest_available_firmware = latest_available_firmware + self.build_plate = self.parseModel(CloudClusterBuildPlate, build_plate) super().__init__(**kwargs) ## Creates a new output model. From 0965d909c09bdc27acb6c04084f35a7d5fac6ed5 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 20 Dec 2018 16:41:26 +0100 Subject: [PATCH 1052/1240] Remove the show property from the tooltip This was a bit of a weird setup, so i removed it. This way the tooltip can be used in the same way as the regular tooltip (by simply setting the visibility) CURA-6004 --- resources/qml/ActionButton.qml | 2 +- resources/qml/ToolTip.qml | 11 ++++------- resources/qml/ToolbarButton.qml | 2 +- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index 9ceade6a57..6cab04e5ec 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -115,6 +115,6 @@ Button Cura.ToolTip { id: tooltip - show: button.hovered + visible: button.hovered } } \ No newline at end of file diff --git a/resources/qml/ToolTip.qml b/resources/qml/ToolTip.qml index 5e7d436d70..87586f76d8 100644 --- a/resources/qml/ToolTip.qml +++ b/resources/qml/ToolTip.qml @@ -9,28 +9,25 @@ import Cura 1.0 as Cura ToolTip { - enum ContentAlignment { AlignLeft, AlignRight } - // This property indicates when the tooltip has to show, for instance when a button is hovered - property bool show: false - // Defines the alignment of the content, by default to the left property int contentAlignment: Cura.ToolTip.ContentAlignment.AlignRight property alias tooltipText: tooltip.text property var targetPoint: Qt.point(parent.x, y + Math.round(height/2)) - id: tooltip text: "" delay: 500 - visible: text != "" && show font: UM.Theme.getFont("default") + + // If the text is not set, just set the height to 0 to prevent it from showing + height: text != "" ? label.contentHeight + 2 * UM.Theme.getSize("thin_margin").width: 0 x: { @@ -43,7 +40,7 @@ ToolTip y: Math.round(parent.height / 2 - label.height / 2 ) - padding - padding: 2 + padding: UM.Theme.getSize("thin_margin").width background: UM.PointingRectangle { diff --git a/resources/qml/ToolbarButton.qml b/resources/qml/ToolbarButton.qml index 307d49302c..b3f84bba1d 100644 --- a/resources/qml/ToolbarButton.qml +++ b/resources/qml/ToolbarButton.qml @@ -101,6 +101,6 @@ Button { id: tooltip tooltipText: base.text - show: base.hovered + visible: base.hovered } } From 9d27c29c8ccd5bc09d54dd9f0a90f474cf643c18 Mon Sep 17 00:00:00 2001 From: Marijn Dee Date: Thu, 20 Dec 2018 17:04:17 +0100 Subject: [PATCH 1053/1240] Fixed indentation and relative import, tests still fail though --- .../src/Cloud/Models/CloudClusterPrintJobStatus.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobStatus.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobStatus.py index 4b70d191e4..5b7ef7c18a 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobStatus.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobStatus.py @@ -4,7 +4,7 @@ from typing import List, Optional, Union, Dict, Any from cura.PrinterOutput.ConfigurationModel import ConfigurationModel from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputController import CloudOutputController -from src.ConfigurationChangeModel import ConfigurationChangeModel +from plugins.UM3NetworkPrinting.src.ConfigurationChangeModel import ConfigurationChangeModel from .CloudClusterBuildPlate import CloudClusterBuildPlate from .CloudClusterPrintJobConfigurationChange import CloudClusterPrintJobConfigurationChange from .CloudClusterPrintJobImpediment import CloudClusterPrintJobImpediment @@ -122,8 +122,10 @@ class CloudClusterPrintJobStatus(BaseCloudModel): if not status_set_by_impediment: model.updateState(self.status) - model.updateConfigurationChanges([ConfigurationChangeModel(type_of_change=change.type_of_change, - index=change.index, - target_name=change.target_name, - origin_name=change.origin_name) - for change in self.configuration_changes_required]) + model.updateConfigurationChanges( + [ConfigurationChangeModel( + type_of_change=change.type_of_change, + index=change.index, + target_name=change.target_name, + origin_name=change.origin_name) + for change in self.configuration_changes_required]) From 5403f5241b44684ec6b857fa86c97f8d593a7527 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Thu, 20 Dec 2018 17:21:07 +0100 Subject: [PATCH 1054/1240] Fix not registering cloud output device for every machine --- .../src/Cloud/CloudOutputDeviceManager.py | 22 +++++++++++++------ .../src/ClusterUM3OutputDevice.py | 5 +++-- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index c849ebfe4a..b1dc13e34f 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -88,7 +88,6 @@ class CloudOutputDeviceManager: # We only add when is_online as we don't want the option in the drop down if the cluster is not online. for added_cluster in added_clusters: device = CloudOutputDevice(self._api, added_cluster) - self._output_device_manager.addOutputDevice(device) self._remote_clusters[added_cluster.cluster_id] = device for device, cluster in updates: @@ -103,17 +102,20 @@ class CloudOutputDeviceManager: Logger.log("d", "no active machine") return + # Remove all output devices that we have registered. + for stored_cluster_id in self._remote_clusters: + self._output_device_manager.removeOutputDevice(stored_cluster_id) + # Check if the stored cluster_id for the active machine is in our list of remote clusters. stored_cluster_id = active_machine.getMetaDataEntry(self.META_CLUSTER_ID) if stored_cluster_id in self._remote_clusters: device = self._remote_clusters[stored_cluster_id] - if not device.isConnected(): - device.connect() - Logger.log("d", "Device connected by metadata %s", stored_cluster_id) + self._connectToOutputDevice(device) + Logger.log("d", "Device connected by metadata cluster ID %s", stored_cluster_id) else: self._connectByNetworkKey(active_machine) - ## Tries to match the + ## Tries to match the local network key to the cloud cluster host name. def _connectByNetworkKey(self, active_machine: GlobalStack) -> None: # Check if the active printer has a local network connection and match this key to the remote cluster. local_network_key = active_machine.getMetaDataEntry("um_network_key") @@ -124,9 +126,15 @@ class CloudOutputDeviceManager: if not device: return - active_machine.setMetaDataEntry(self.META_CLUSTER_ID, device.key) - device.connect() Logger.log("i", "Found cluster %s with network key %s", device, local_network_key) + active_machine.setMetaDataEntry(self.META_CLUSTER_ID, device.key) + self._connectToOutputDevice(device) + + ## Connects to an output device and makes sure it is registered in the output device manager. + def _connectToOutputDevice(self, device: CloudOutputDevice) -> None: + if not device.isConnected(): + device.connect() + self._output_device_manager.addOutputDevice(device) ## Handles an API error received from the cloud. # \param errors: The errors received diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index 75fd1e0f9e..a1931c428a 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -389,8 +389,9 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): ## Called when the connection to the cluster changes. def connect(self) -> None: - super().connect() - self.sendMaterialProfiles() + # super().connect() + # self.sendMaterialProfiles() + pass def _onGetPreviewImageFinished(self, reply: QNetworkReply) -> None: reply_url = reply.url().toString() From 7cfb29e337ae47884c0faf80be7394ea19ddb604 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Thu, 20 Dec 2018 18:12:40 +0100 Subject: [PATCH 1055/1240] If a connected printer has a selected material which is unknown for Cura then show a message for downloading it from Marketplace CURA-6033 --- .../ConfigurationMenu/ConfigurationItem.qml | 104 +++++++++++++++++- 1 file changed, 102 insertions(+), 2 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 862e1475a9..16d683adca 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -14,6 +14,23 @@ Button property var configuration: null hoverEnabled: true + property bool isValidMaterial: + { + var extruderConfigurations = configuration.extruderConfigurations + + for (var index = 0; index < extruderConfigurations.length; index++) + { + var name = extruderConfigurations[index].material.brand ? extruderConfigurations[index].material.name : "" + + if (name == "" || name == "Unknown") + { + hoverEnabled = false + return false + } + } + return true + } + background: Rectangle { color: parent.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button") @@ -40,19 +57,102 @@ Button right: parent.right rightMargin: UM.Theme.getSize("wide_margin").width } - + height: childrenRect.height spacing: UM.Theme.getSize("default_margin").width Repeater { id: repeater - model: configuration.extruderConfigurations + model: + { + if (configurationItem.isValidMaterial) + { + return configuration.extruderConfigurations + } + return [] + } + delegate: PrintCoreConfiguration { width: Math.round(parent.width / 2) printCoreConfiguration: modelData } } + + // Unknown material + Rectangle + { + id: unknownMaterial + height: unknownMaterialMessage.height + UM.Theme.getSize("thin_margin").width / 2 + width: parent.width + + anchors.top: parent.top + anchors.topMargin: + { + return UM.Theme.getSize("thin_margin").width / 2 + } + + visible: !configurationItem.isValidMaterial + + UM.RecolorImage + { + id: icon + anchors.verticalCenter: unknownMaterialMessage.verticalCenter + + source: UM.Theme.getIcon("warning") + color: UM.Theme.getColor("warning") + width: UM.Theme.getSize("section_icon").width + height: width + } + + Label + { + id: unknownMaterialMessage + text: + { + var extruderConfigurations = configuration.extruderConfigurations + var unknownMaterials = [] + for (var index = 0; index < extruderConfigurations.length; index++) + { + var name = extruderConfigurations[index].material.brand ? extruderConfigurations[index].material.name : "" + + if (name == "" || name == "Unknown") + { + unknownMaterials.push(extruderConfigurations[index].material.brand ? extruderConfigurations[index].material.brand : "Unknown Brand") + } + } + + unknownMaterials = "" + unknownMaterials + "" + var draftResult = catalog.i18nc("@label", "This configuration is not available because %1 is not recognized. Please visit %2 to download the correct material profile."); + var result = draftResult.arg(unknownMaterials).arg("" + catalog.i18nc("@label","Marketplace") + " ") + + return result + } + width: extruderRow.width + + anchors.left: icon.right + anchors.right: unknownMaterial.right + anchors.leftMargin: UM.Theme.getSize("wide_margin").height + anchors.top: unknownMaterial.top + + wrapMode: Text.WordWrap + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + verticalAlignment: Text.AlignVCenter + linkColor: UM.Theme.getColor("text_link") + + onLinkActivated: + { + Cura.Actions.browsePackages.trigger() + } + } + + MouseArea { + anchors.fill: parent + cursorShape: unknownMaterialMessage.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor + acceptedButtons: Qt.NoButton + } + } } //Buildplate row separator From 9430c421ebcf202caca4c6dda40af920678e396d Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Thu, 20 Dec 2018 18:19:59 +0100 Subject: [PATCH 1056/1240] Code refactor CURA-6033 --- resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 16d683adca..4cf0437098 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -87,10 +87,7 @@ Button width: parent.width anchors.top: parent.top - anchors.topMargin: - { - return UM.Theme.getSize("thin_margin").width / 2 - } + anchors.topMargin: UM.Theme.getSize("thin_margin").width / 2 visible: !configurationItem.isValidMaterial From 81a6531f47f3b7356146827f27a8bf430b93a94e Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Thu, 20 Dec 2018 20:37:48 +0100 Subject: [PATCH 1057/1240] Fix range slider label position in LayerSlider.qml. --- plugins/SimulationView/LayerSlider.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/SimulationView/LayerSlider.qml b/plugins/SimulationView/LayerSlider.qml index 42b8cf0ba0..88f298d1f5 100644 --- a/plugins/SimulationView/LayerSlider.qml +++ b/plugins/SimulationView/LayerSlider.qml @@ -163,9 +163,9 @@ Item id: rangleHandleLabel height: sliderRoot.handleSize + UM.Theme.getSize("default_margin").height - x: parent.x + parent.width + UM.Theme.getSize("default_margin").width + x: parent.x - width - UM.Theme.getSize("default_margin").width anchors.verticalCenter: parent.verticalCenter - target: Qt.point(sliderRoot.width + width, y + height / 2) + target: Qt.point(sliderRoot.width, y + height / 2) visible: sliderRoot.activeHandle == parent // custom properties From e51b5993d334e424fe97278d317f7dce37a76d83 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 20 Dec 2018 21:56:27 +0100 Subject: [PATCH 1058/1240] Don't create system tray icon in headless mode --- cura/CuraApplication.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 9ec2435f0b..4e02be87f5 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -434,7 +434,8 @@ class CuraApplication(QtApplication): def startSplashWindowPhase(self) -> None: super().startSplashWindowPhase() - self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png"))) + if not self.getIsHeadLess(): + self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png"))) self.setRequiredPlugins([ # Misc.: From 131211e604bed58b719f6f017d44d0fbb68d15b8 Mon Sep 17 00:00:00 2001 From: Marijn Dee Date: Fri, 21 Dec 2018 09:42:28 +0100 Subject: [PATCH 1059/1240] Fixed the unit tests --- .../src/Cloud/Models/CloudClusterPrintJobStatus.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobStatus.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobStatus.py index 5b7ef7c18a..b1672f362e 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobStatus.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobStatus.py @@ -76,11 +76,17 @@ class CloudClusterPrintJobStatus(BaseCloudModel): self.uuid = uuid self.deleted_at = deleted_at self.printed_on_uuid = printed_on_uuid - self.configuration_changes_required = self.parseModels(CloudClusterPrintJobConfigurationChange, - configuration_changes_required) + if configuration_changes_required: + self.configuration_changes_required = self.parseModels(CloudClusterPrintJobConfigurationChange, + configuration_changes_required) + else: + self.configuration_changes_required = [] self.build_plate = self.parseModel(CloudClusterBuildPlate, build_plate) self.compatible_machine_families = compatible_machine_families - self.impediments_to_printing = self.parseModels(CloudClusterPrintJobImpediment, impediments_to_printing) + if impediments_to_printing: + self.impediments_to_printing = self.parseModels(CloudClusterPrintJobImpediment, impediments_to_printing) + else: + self.impediments_to_printing = [] super().__init__(**kwargs) From 75fbdf2c94a5e0f004dbecaba71447cb491a87e0 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 21 Dec 2018 10:40:37 +0100 Subject: [PATCH 1060/1240] Expand error message for cases when extruders are disabled It is also unable to slice models that are on extruders that are disabled. --- plugins/CuraEngineBackend/CuraEngineBackend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index 1830a30b30..cf00035e3b 100755 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -411,7 +411,7 @@ class CuraEngineBackend(QObject, Backend): if job.getResult() == StartJobResult.NothingToSlice: if self._application.platformActivity: - self._error_message = Message(catalog.i18nc("@info:status", "Nothing to slice because none of the models fit the build volume. Please scale or rotate models to fit."), + self._error_message = Message(catalog.i18nc("@info:status", "Nothing to slice because none of the models fit the build volume or are assigned to a disabled extruder. Please scale or rotate models to fit, or enable an extruder."), title = catalog.i18nc("@info:title", "Unable to slice")) self._error_message.show() self.setState(BackendState.Error) From 81e356ea1b3ce7e19b0ec211f337e75038369d53 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 21 Dec 2018 11:20:09 +0100 Subject: [PATCH 1061/1240] Switch out the containersModel for the more specific printersModel --- resources/qml/Menus/LocalPrinterMenu.qml | 20 +++++++++++--------- resources/qml/Menus/NetworkPrinterMenu.qml | 14 +++++--------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/resources/qml/Menus/LocalPrinterMenu.qml b/resources/qml/Menus/LocalPrinterMenu.qml index 0bdd4f33b9..e7c5037814 100644 --- a/resources/qml/Menus/LocalPrinterMenu.qml +++ b/resources/qml/Menus/LocalPrinterMenu.qml @@ -7,16 +7,18 @@ import QtQuick.Controls 1.4 import UM 1.2 as UM import Cura 1.0 as Cura -Instantiator { - model: UM.ContainerStacksModel { - filter: {"type": "machine", "um_network_key": null} - } - MenuItem { - text: model.name; - checkable: true; +Instantiator +{ + model: Cura.PrintersModel {} + + MenuItem + { + text: model.name + checkable: true checked: Cura.MachineManager.activeMachineId == model.id - exclusiveGroup: group; - onTriggered: Cura.MachineManager.setActiveMachine(model.id); + exclusiveGroup: group + visible: !model.hasRemoteConnection + onTriggered: Cura.MachineManager.setActiveMachine(model.id) } onObjectAdded: menu.insertItem(index, object) onObjectRemoved: menu.removeItem(object) diff --git a/resources/qml/Menus/NetworkPrinterMenu.qml b/resources/qml/Menus/NetworkPrinterMenu.qml index 166e45f3b9..8c607bc5ae 100644 --- a/resources/qml/Menus/NetworkPrinterMenu.qml +++ b/resources/qml/Menus/NetworkPrinterMenu.qml @@ -9,19 +9,15 @@ import Cura 1.0 as Cura Instantiator { - model: UM.ContainerStacksModel - { - filter: {"type": "machine", "um_network_key": "*", "hidden": "False"} - } + model: Cura.PrintersModel {} MenuItem { - // TODO: Use printer_group icon when it's a cluster. Not use it for now since it doesn't look as expected -// iconSource: UM.Theme.getIcon("printer_single") text: model.metadata["connect_group_name"] - checkable: true; + checkable: true + visible: model.hasRemoteConnection checked: Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["connect_group_name"] - exclusiveGroup: group; - onTriggered: Cura.MachineManager.setActiveMachine(model.id); + exclusiveGroup: group + onTriggered: Cura.MachineManager.setActiveMachine(model.id) } onObjectAdded: menu.insertItem(index, object) onObjectRemoved: menu.removeItem(object) From a5500b028f9d5f43688822d1ba8427868baeb833 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 21 Dec 2018 11:50:30 +0100 Subject: [PATCH 1062/1240] Use HTTPS for Help links The Ultimaker website doesn't even accept anything else any more. --- cura/CuraActions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/CuraActions.py b/cura/CuraActions.py index 49f7e740a9..ebb1523f13 100644 --- a/cura/CuraActions.py +++ b/cura/CuraActions.py @@ -36,12 +36,12 @@ class CuraActions(QObject): # Starting a web browser from a signal handler connected to a menu will crash on windows. # So instead, defer the call to the next run of the event loop, since that does work. # Note that weirdly enough, only signal handlers that open a web browser fail like that. - event = CallFunctionEvent(self._openUrl, [QUrl("http://ultimaker.com/en/support/software")], {}) + event = CallFunctionEvent(self._openUrl, [QUrl("https://ultimaker.com/en/support/software")], {}) cura.CuraApplication.CuraApplication.getInstance().functionEvent(event) @pyqtSlot() def openBugReportPage(self) -> None: - event = CallFunctionEvent(self._openUrl, [QUrl("http://github.com/Ultimaker/Cura/issues")], {}) + event = CallFunctionEvent(self._openUrl, [QUrl("https://github.com/Ultimaker/Cura/issues")], {}) cura.CuraApplication.CuraApplication.getInstance().functionEvent(event) ## Reset camera position and direction to default From 7269065cca3bcd16bb4689095d2d6e54dbe80544 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 21 Dec 2018 11:54:11 +0100 Subject: [PATCH 1063/1240] Only clear the stored optimized layer data if the slice started --- plugins/CuraEngineBackend/CuraEngineBackend.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index cf00035e3b..f12a5b1222 100755 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -86,8 +86,8 @@ class CuraEngineBackend(QObject, Backend): self._layer_view_active = False #type: bool self._onActiveViewChanged() - self._stored_layer_data = [] #type: List[Arcus.PythonMessage] - self._stored_optimized_layer_data = {} #type: Dict[int, List[Arcus.PythonMessage]] # key is build plate number, then arrays are stored until they go to the ProcessSlicesLayersJob + self._stored_layer_data = [] # type: List[Arcus.PythonMessage] + self._stored_optimized_layer_data = {} # type: Dict[int, List[Arcus.PythonMessage]] # key is build plate number, then arrays are stored until they go to the ProcessSlicesLayersJob self._scene = self._application.getController().getScene() #type: Scene self._scene.sceneChanged.connect(self._onSceneChanged) @@ -246,7 +246,7 @@ class CuraEngineBackend(QObject, Backend): num_objects = self._numObjectsPerBuildPlate() self._stored_layer_data = [] - self._stored_optimized_layer_data[build_plate_to_be_sliced] = [] + if build_plate_to_be_sliced not in num_objects or num_objects[build_plate_to_be_sliced] == 0: self._scene.gcode_dict[build_plate_to_be_sliced] = [] #type: ignore #Because we created this attribute above. @@ -254,7 +254,7 @@ class CuraEngineBackend(QObject, Backend): if self._build_plates_to_be_sliced: self.slice() return - + self._stored_optimized_layer_data[build_plate_to_be_sliced] = [] if self._application.getPrintInformation() and build_plate_to_be_sliced == active_build_plate: self._application.getPrintInformation().setToZeroPrintInformation(build_plate_to_be_sliced) From 07f0433751ad9f97e4169c60b46ce57f6010d671 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 21 Dec 2018 11:58:57 +0100 Subject: [PATCH 1064/1240] Correct link to manual They changed this on the website, breaking all previous releases of Cura in the process. --- cura/CuraActions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/CuraActions.py b/cura/CuraActions.py index ebb1523f13..91e0966fed 100644 --- a/cura/CuraActions.py +++ b/cura/CuraActions.py @@ -36,7 +36,7 @@ class CuraActions(QObject): # Starting a web browser from a signal handler connected to a menu will crash on windows. # So instead, defer the call to the next run of the event loop, since that does work. # Note that weirdly enough, only signal handlers that open a web browser fail like that. - event = CallFunctionEvent(self._openUrl, [QUrl("https://ultimaker.com/en/support/software")], {}) + event = CallFunctionEvent(self._openUrl, [QUrl("https://ultimaker.com/en/resources/manuals/software")], {}) cura.CuraApplication.CuraApplication.getInstance().functionEvent(event) @pyqtSlot() From 01783e7f685cda2a24ce6a0de846ab7cca79d17d Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Fri, 21 Dec 2018 13:54:01 +0100 Subject: [PATCH 1065/1240] Check material name instead of material brand CURA-6033 --- resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 4cf0437098..9e2fed0767 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -20,7 +20,7 @@ Button for (var index = 0; index < extruderConfigurations.length; index++) { - var name = extruderConfigurations[index].material.brand ? extruderConfigurations[index].material.name : "" + var name = extruderConfigurations[index].material ? extruderConfigurations[index].material.name : "" if (name == "" || name == "Unknown") { @@ -111,7 +111,7 @@ Button var unknownMaterials = [] for (var index = 0; index < extruderConfigurations.length; index++) { - var name = extruderConfigurations[index].material.brand ? extruderConfigurations[index].material.name : "" + var name = extruderConfigurations[index].material ? extruderConfigurations[index].material.name : "" if (name == "" || name == "Unknown") { From 766fc2293e1e5e635fdf0d5b2b5fbb75071df816 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 21 Dec 2018 14:35:41 +0100 Subject: [PATCH 1066/1240] Fix visibility for machine icon in dark theme --- resources/themes/cura-dark/theme.json | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/themes/cura-dark/theme.json b/resources/themes/cura-dark/theme.json index 537fccbc5c..6b29073475 100644 --- a/resources/themes/cura-dark/theme.json +++ b/resources/themes/cura-dark/theme.json @@ -28,6 +28,7 @@ "machine_selector_bar": [39, 44, 48, 255], "machine_selector_active": [39, 44, 48, 255], + "machine_selector_printer_icon": [204, 204, 204, 255], "text": [255, 255, 255, 204], "text_detail": [255, 255, 255, 172], From c8994102da49ac08f3fcd041df12c7ffaa12eb88 Mon Sep 17 00:00:00 2001 From: Aleksei S Date: Fri, 21 Dec 2018 14:44:25 +0100 Subject: [PATCH 1067/1240] Use short for each javascript function CURA-6033 --- .../qml/Menus/ConfigurationMenu/ConfigurationItem.qml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 9e2fed0767..b23bcc4b8c 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -18,7 +18,7 @@ Button { var extruderConfigurations = configuration.extruderConfigurations - for (var index = 0; index < extruderConfigurations.length; index++) + for (var index in extruderConfigurations) { var name = extruderConfigurations[index].material ? extruderConfigurations[index].material.name : "" @@ -109,7 +109,7 @@ Button { var extruderConfigurations = configuration.extruderConfigurations var unknownMaterials = [] - for (var index = 0; index < extruderConfigurations.length; index++) + for (var index in extruderConfigurations) { var name = extruderConfigurations[index].material ? extruderConfigurations[index].material.name : "" @@ -144,7 +144,8 @@ Button } } - MouseArea { + MouseArea + { anchors.fill: parent cursorShape: unknownMaterialMessage.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor acceptedButtons: Qt.NoButton From 12a4a5e9f54ba62b6c2eb1f6df8e1a28c8441506 Mon Sep 17 00:00:00 2001 From: Marijn Dee Date: Fri, 21 Dec 2018 15:06:02 +0100 Subject: [PATCH 1068/1240] Fixing the rest of the automatic test failures --- ...CloudClusterPrintJobConfigurationChange.py | 5 ++- .../Models/CloudClusterPrintJobStatus.py | 35 +++++++++---------- .../Cloud/Models/CloudClusterPrinterStatus.py | 4 +-- 3 files changed, 21 insertions(+), 23 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobConfigurationChange.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobConfigurationChange.py index 6c02972757..9ff4154666 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobConfigurationChange.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobConfigurationChange.py @@ -15,9 +15,8 @@ class CloudClusterPrintJobConfigurationChange(BaseCloudModel): # \param origin_id: Original/current material guid or hotend id # \param target_name: Target material name or hotend id # \param origin_name: Original/current material name or hotend id - def __init__(self, type_of_change: Optional[str] = None, index: Optional[int] = None, - target_id: Optional[str] = None,origin_id: Optional[str] = None, - target_name: Optional[str] = None,origin_name: Optional[str] = None, + def __init__(self, type_of_change: str, target_id: str, origin_id: str, + index: Optional[int] = None, target_name: Optional[str] = None, origin_name: Optional[str] = None, **kwargs) -> None: self.type_of_change = type_of_change self.index = index diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobStatus.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobStatus.py index b1672f362e..5d62471710 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobStatus.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobStatus.py @@ -55,7 +55,8 @@ class CloudClusterPrintJobStatus(BaseCloudModel): printed_on_uuid: Optional[str] = None, configuration_changes_required: List[ Union[Dict[str, Any], CloudClusterPrintJobConfigurationChange]] = None, - build_plate: Optional[str] = None, compatible_machine_families: List[str] = None, + build_plate: Union[Dict[str, Any], CloudClusterBuildPlate] = None, + compatible_machine_families: List[str] = None, impediments_to_printing: List[Union[Dict[str, Any], CloudClusterPrintJobImpediment]] = None, **kwargs) -> None: self.assigned_to = assigned_to @@ -76,17 +77,14 @@ class CloudClusterPrintJobStatus(BaseCloudModel): self.uuid = uuid self.deleted_at = deleted_at self.printed_on_uuid = printed_on_uuid - if configuration_changes_required: - self.configuration_changes_required = self.parseModels(CloudClusterPrintJobConfigurationChange, - configuration_changes_required) - else: - self.configuration_changes_required = [] - self.build_plate = self.parseModel(CloudClusterBuildPlate, build_plate) - self.compatible_machine_families = compatible_machine_families - if impediments_to_printing: - self.impediments_to_printing = self.parseModels(CloudClusterPrintJobImpediment, impediments_to_printing) - else: - self.impediments_to_printing = [] + + self.configuration_changes_required = self.parseModels(CloudClusterPrintJobConfigurationChange, + configuration_changes_required) \ + if configuration_changes_required else [] + self.build_plate = self.parseModel(CloudClusterBuildPlate, build_plate) if build_plate else None + self.compatible_machine_families = compatible_machine_families if compatible_machine_families else [] + self.impediments_to_printing = self.parseModels(CloudClusterPrintJobImpediment, impediments_to_printing) \ + if impediments_to_printing else [] super().__init__(**kwargs) @@ -120,7 +118,8 @@ class CloudClusterPrintJobStatus(BaseCloudModel): status_set_by_impediment = False for impediment in self.impediments_to_printing: - if impediment.severity == "UNFIXABLE": # TODO: impediment.severity is defined as int, this will not work, is there a translation? + # TODO: impediment.severity is defined as int, this will not work, is there a translation? + if impediment.severity == "UNFIXABLE": status_set_by_impediment = True model.updateState("error") break @@ -130,8 +129,8 @@ class CloudClusterPrintJobStatus(BaseCloudModel): model.updateConfigurationChanges( [ConfigurationChangeModel( - type_of_change=change.type_of_change, - index=change.index, - target_name=change.target_name, - origin_name=change.origin_name) - for change in self.configuration_changes_required]) + type_of_change = change.type_of_change, + index = change.index if change.index else 0, + target_name = change.target_name if change.target_name else "", + origin_name = change.origin_name if change.origin_name else "") + for change in self.configuration_changes_required]) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterStatus.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterStatus.py index 23409a761d..a8165ff69c 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterStatus.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrinterStatus.py @@ -33,7 +33,7 @@ class CloudClusterPrinterStatus(BaseCloudModel): configuration: List[Union[Dict[str, Any], CloudClusterPrintCoreConfiguration]], reserved_by: Optional[str] = None, maintenance_required: Optional[bool] = None, firmware_update_status: Optional[str] = None, latest_available_firmware: Optional[str] = None, - build_plate: Optional[str] = None, **kwargs) -> None: + build_plate: Union[Dict[str, Any], CloudClusterBuildPlate] = None, **kwargs) -> None: self.configuration = self.parseModels(CloudClusterPrintCoreConfiguration, configuration) self.enabled = enabled @@ -48,7 +48,7 @@ class CloudClusterPrinterStatus(BaseCloudModel): self.maintenance_required = maintenance_required self.firmware_update_status = firmware_update_status self.latest_available_firmware = latest_available_firmware - self.build_plate = self.parseModel(CloudClusterBuildPlate, build_plate) + self.build_plate = self.parseModel(CloudClusterBuildPlate, build_plate) if build_plate else None super().__init__(**kwargs) ## Creates a new output model. From 33c97fcf4a5c7958dd3d899811dde6d52bcde114 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 21 Dec 2018 15:09:09 +0100 Subject: [PATCH 1069/1240] Updated typing --- cura/CuraApplication.py | 89 +++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 4e02be87f5..49b0026b39 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -4,7 +4,7 @@ import os import sys import time -from typing import cast, TYPE_CHECKING, Optional, Callable +from typing import cast, TYPE_CHECKING, Optional, Callable, List import numpy @@ -665,12 +665,12 @@ class CuraApplication(QtApplication): ## Handle loading of all plugin types (and the backend explicitly) # \sa PluginRegistry - def _loadPlugins(self): + def _loadPlugins(self) -> None: self._plugin_registry.addType("profile_reader", self._addProfileReader) self._plugin_registry.addType("profile_writer", self._addProfileWriter) if Platform.isLinux(): - lib_suffixes = {"", "64", "32", "x32"} #A few common ones on different distributions. + lib_suffixes = {"", "64", "32", "x32"} # A few common ones on different distributions. else: lib_suffixes = {""} for suffix in lib_suffixes: @@ -1267,62 +1267,75 @@ class CuraApplication(QtApplication): ## Arrange all objects. @pyqtSlot() - def arrangeObjectsToAllBuildPlates(self): - nodes = [] - for node in DepthFirstIterator(self.getController().getScene().getRoot()): + def arrangeObjectsToAllBuildPlates(self) -> None: + nodes_to_arrange = [] + for node in DepthFirstIterator(self.getController().getScene().getRoot()): # type: ignore if not isinstance(node, SceneNode): continue + if not node.getMeshData() and not node.callDecoration("isGroup"): continue # Node that doesnt have a mesh and is not a group. - if node.getParent() and node.getParent().callDecoration("isGroup"): - continue # Grouped nodes don't need resetting as their parent (the group) is resetted) + + parent_node = node.getParent() + if parent_node and parent_node.callDecoration("isGroup"): + continue # Grouped nodes don't need resetting as their parent (the group) is reset) + if not node.callDecoration("isSliceable") and not node.callDecoration("isGroup"): continue # i.e. node with layer data + + bounding_box = node.getBoundingBox() # Skip nodes that are too big - if node.getBoundingBox().width < self._volume.getBoundingBox().width or node.getBoundingBox().depth < self._volume.getBoundingBox().depth: - nodes.append(node) - job = ArrangeObjectsAllBuildPlatesJob(nodes) + if bounding_box is None or bounding_box.width < self._volume.getBoundingBox().width or bounding_box.depth < self._volume.getBoundingBox().depth: + nodes_to_arrange.append(node) + job = ArrangeObjectsAllBuildPlatesJob(nodes_to_arrange) job.start() self.getCuraSceneController().setActiveBuildPlate(0) # Select first build plate # Single build plate @pyqtSlot() - def arrangeAll(self): - nodes = [] + def arrangeAll(self) -> None: + nodes_to_arrange = [] active_build_plate = self.getMultiBuildPlateModel().activeBuildPlate - for node in DepthFirstIterator(self.getController().getScene().getRoot()): + for node in DepthFirstIterator(self.getController().getScene().getRoot()): # type: ignore if not isinstance(node, SceneNode): continue + if not node.getMeshData() and not node.callDecoration("isGroup"): continue # Node that doesnt have a mesh and is not a group. - if node.getParent() and node.getParent().callDecoration("isGroup"): + + parent_node = node.getParent() + if parent_node and parent_node.callDecoration("isGroup"): continue # Grouped nodes don't need resetting as their parent (the group) is resetted) + if not node.isSelectable(): continue # i.e. node with layer data + if not node.callDecoration("isSliceable") and not node.callDecoration("isGroup"): continue # i.e. node with layer data + if node.callDecoration("getBuildPlateNumber") == active_build_plate: # Skip nodes that are too big - if node.getBoundingBox().width < self._volume.getBoundingBox().width or node.getBoundingBox().depth < self._volume.getBoundingBox().depth: - nodes.append(node) - self.arrange(nodes, fixed_nodes = []) + bounding_box = node.getBoundingBox() + if bounding_box is None or bounding_box.width < self._volume.getBoundingBox().width or bounding_box.depth < self._volume.getBoundingBox().depth: + nodes_to_arrange.append(node) + self.arrange(nodes_to_arrange, fixed_nodes = []) ## Arrange a set of nodes given a set of fixed nodes # \param nodes nodes that we have to place # \param fixed_nodes nodes that are placed in the arranger before finding spots for nodes - def arrange(self, nodes, fixed_nodes): + def arrange(self, nodes: List[SceneNode], fixed_nodes: List[SceneNode]) -> None: min_offset = self.getBuildVolume().getEdgeDisallowedSize() + 2 # Allow for some rounding errors job = ArrangeObjectsJob(nodes, fixed_nodes, min_offset = max(min_offset, 8)) job.start() ## Reload all mesh data on the screen from file. @pyqtSlot() - def reloadAll(self): + def reloadAll(self) -> None: Logger.log("i", "Reloading all loaded mesh data.") nodes = [] has_merged_nodes = False - for node in DepthFirstIterator(self.getController().getScene().getRoot()): - if not isinstance(node, CuraSceneNode) or not node.getMeshData() : + for node in DepthFirstIterator(self.getController().getScene().getRoot()): # type: ignore + if not isinstance(node, CuraSceneNode) or not node.getMeshData(): if node.getName() == "MergedMesh": has_merged_nodes = True continue @@ -1336,7 +1349,7 @@ class CuraApplication(QtApplication): file_name = node.getMeshData().getFileName() if file_name: job = ReadMeshJob(file_name) - job._node = node + job._node = node # type: ignore job.finished.connect(self._reloadMeshFinished) if has_merged_nodes: job.finished.connect(self.updateOriginOfMergedMeshes) @@ -1345,20 +1358,8 @@ class CuraApplication(QtApplication): else: Logger.log("w", "Unable to reload data because we don't have a filename.") - - ## Get logging data of the backend engine - # \returns \type{string} Logging data - @pyqtSlot(result = str) - def getEngineLog(self): - log = "" - - for entry in self.getBackend().getLog(): - log += entry.decode() - - return log - @pyqtSlot("QStringList") - def setExpandedCategories(self, categories): + def setExpandedCategories(self, categories: List[str]) -> None: categories = list(set(categories)) categories.sort() joined = ";".join(categories) @@ -1369,7 +1370,7 @@ class CuraApplication(QtApplication): expandedCategoriesChanged = pyqtSignal() @pyqtProperty("QStringList", notify = expandedCategoriesChanged) - def expandedCategories(self): + def expandedCategories(self) -> List[str]: return self.getPreferences().getValue("cura/categories_expanded").split(";") @pyqtSlot() @@ -1419,13 +1420,12 @@ class CuraApplication(QtApplication): ## Updates origin position of all merged meshes - # \param jobNode \type{Job} empty object which passed which is required by JobQueue - def updateOriginOfMergedMeshes(self, jobNode): + def updateOriginOfMergedMeshes(self, _): group_nodes = [] for node in DepthFirstIterator(self.getController().getScene().getRoot()): if isinstance(node, CuraSceneNode) and node.getName() == "MergedMesh": - #checking by name might be not enough, the merged mesh should has "GroupDecorator" decorator + # Checking by name might be not enough, the merged mesh should has "GroupDecorator" decorator for decorator in node.getDecorators(): if isinstance(decorator, GroupDecorator): group_nodes.append(node) @@ -1469,7 +1469,7 @@ class CuraApplication(QtApplication): @pyqtSlot() - def groupSelected(self): + def groupSelected(self) -> None: # Create a group-node group_node = CuraSceneNode() group_decorator = GroupDecorator() @@ -1485,7 +1485,8 @@ class CuraApplication(QtApplication): # Remove nodes that are directly parented to another selected node from the selection so they remain parented selected_nodes = Selection.getAllSelectedObjects().copy() for node in selected_nodes: - if node.getParent() in selected_nodes and not node.getParent().callDecoration("isGroup"): + parent = node.getParent() + if parent is not None and node in selected_nodes and not node.callDecoration("isGroup"): Selection.remove(node) # Move selected nodes into the group-node @@ -1497,7 +1498,7 @@ class CuraApplication(QtApplication): Selection.add(group_node) @pyqtSlot() - def ungroupSelected(self): + def ungroupSelected(self) -> None: selected_objects = Selection.getAllSelectedObjects().copy() for node in selected_objects: if node.callDecoration("isGroup"): @@ -1520,7 +1521,7 @@ class CuraApplication(QtApplication): # Note: The group removes itself from the scene once all its children have left it, # see GroupDecorator._onChildrenChanged - def _createSplashScreen(self): + def _createSplashScreen(self) -> Optional[CuraSplashScreen.CuraSplashScreen]: if self._is_headless: return None return CuraSplashScreen.CuraSplashScreen() From 4ab79f963ab8e38096cb53b1c11c3c7b1db17edc Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 21 Dec 2018 15:10:14 +0100 Subject: [PATCH 1070/1240] Remove deprecated functions --- cura/CuraApplication.py | 82 ----------------------------------------- 1 file changed, 82 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 49b0026b39..4a1023f69e 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1106,88 +1106,6 @@ class CuraApplication(QtApplication): self._platform_activity = True if count > 0 else False self.activityChanged.emit() - # Remove all selected objects from the scene. - @pyqtSlot() - @deprecated("Moved to CuraActions", "2.6") - def deleteSelection(self): - if not self.getController().getToolsEnabled(): - return - removed_group_nodes = [] - op = GroupedOperation() - nodes = Selection.getAllSelectedObjects() - for node in nodes: - op.addOperation(RemoveSceneNodeOperation(node)) - group_node = node.getParent() - if group_node and group_node.callDecoration("isGroup") and group_node not in removed_group_nodes: - remaining_nodes_in_group = list(set(group_node.getChildren()) - set(nodes)) - if len(remaining_nodes_in_group) == 1: - removed_group_nodes.append(group_node) - op.addOperation(SetParentOperation(remaining_nodes_in_group[0], group_node.getParent())) - op.addOperation(RemoveSceneNodeOperation(group_node)) - op.push() - - ## Remove an object from the scene. - # Note that this only removes an object if it is selected. - @pyqtSlot("quint64") - @deprecated("Use deleteSelection instead", "2.6") - def deleteObject(self, object_id): - if not self.getController().getToolsEnabled(): - return - - node = self.getController().getScene().findObject(object_id) - - if not node and object_id != 0: # Workaround for tool handles overlapping the selected object - node = Selection.getSelectedObject(0) - - if node: - op = GroupedOperation() - op.addOperation(RemoveSceneNodeOperation(node)) - - group_node = node.getParent() - if group_node: - # Note that at this point the node has not yet been deleted - if len(group_node.getChildren()) <= 2 and group_node.callDecoration("isGroup"): - op.addOperation(SetParentOperation(group_node.getChildren()[0], group_node.getParent())) - op.addOperation(RemoveSceneNodeOperation(group_node)) - - op.push() - - ## Create a number of copies of existing object. - # \param object_id - # \param count number of copies - # \param min_offset minimum offset to other objects. - @pyqtSlot("quint64", int) - @deprecated("Use CuraActions::multiplySelection", "2.6") - def multiplyObject(self, object_id, count, min_offset = 8): - node = self.getController().getScene().findObject(object_id) - if not node: - node = Selection.getSelectedObject(0) - - while node.getParent() and node.getParent().callDecoration("isGroup"): - node = node.getParent() - - job = MultiplyObjectsJob([node], count, min_offset) - job.start() - return - - ## Center object on platform. - @pyqtSlot("quint64") - @deprecated("Use CuraActions::centerSelection", "2.6") - def centerObject(self, object_id): - node = self.getController().getScene().findObject(object_id) - if not node and object_id != 0: # Workaround for tool handles overlapping the selected object - node = Selection.getSelectedObject(0) - - if not node: - return - - if node.getParent() and node.getParent().callDecoration("isGroup"): - node = node.getParent() - - if node: - op = SetTransformOperation(node, Vector()) - op.push() - ## Select all nodes containing mesh data in the scene. @pyqtSlot() def selectAll(self): From e8ea742cf6c9cb533604fd7741a94381a33b27d7 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 21 Dec 2018 16:27:38 +0100 Subject: [PATCH 1071/1240] Retain binding with isValidMaterial on hover So that if a material becomes valid, it updates this property. Contributes to issue CURA-6033. --- resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index b23bcc4b8c..31569c58d2 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -12,7 +12,7 @@ Button id: configurationItem property var configuration: null - hoverEnabled: true + hoverEnabled: isValidMaterial property bool isValidMaterial: { @@ -24,7 +24,6 @@ Button if (name == "" || name == "Unknown") { - hoverEnabled = false return false } } From 5e9854454185fee9490b0d9aa996d36f65b7e2fd Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 21 Dec 2018 16:33:16 +0100 Subject: [PATCH 1072/1240] Don't change the model depending on isValidMaterial That would cause a circular dependency in a way. Contributes to issue CURA-6033. --- .../qml/Menus/ConfigurationMenu/ConfigurationItem.qml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 31569c58d2..6a17353459 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -62,19 +62,13 @@ Button Repeater { id: repeater - model: - { - if (configurationItem.isValidMaterial) - { - return configuration.extruderConfigurations - } - return [] - } + model: configuration.extruderConfigurations delegate: PrintCoreConfiguration { width: Math.round(parent.width / 2) printCoreConfiguration: modelData + visible: configurationItem.isValidMaterial } } From a720cca5b6f279696e398e96abe919491c12775e Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 21 Dec 2018 16:40:24 +0100 Subject: [PATCH 1073/1240] Fix background colour for dark theme The default colour for Rectangle is white. So this Rectangle, not defining a colour, became white. Instead we should use an Item, which cannot have a background colour and thus becomes transparent, making the background colour the same as the button below, which was intended. It's also slightly faster to render. Contributes to issue CURA-6033. --- resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 6a17353459..81f2183488 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -73,7 +73,7 @@ Button } // Unknown material - Rectangle + Item { id: unknownMaterial height: unknownMaterialMessage.height + UM.Theme.getSize("thin_margin").width / 2 From 2277a3d316ae2ded6c8f6df18e43de9277181e38 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Fri, 21 Dec 2018 16:43:45 +0100 Subject: [PATCH 1074/1240] Prevent syncing with invalid configurations Contributes to issue CURA-6033. --- resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 81f2183488..75a1b21186 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -216,6 +216,9 @@ Button onClicked: { - Cura.MachineManager.applyRemoteConfiguration(configuration) + if(isValidMaterial) + { + Cura.MachineManager.applyRemoteConfiguration(configuration); + } } } From 85f10e7c4f9bc48afd2fe43903e565b128e03189 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 21 Dec 2018 16:57:52 +0100 Subject: [PATCH 1075/1240] Prevent some unneeded calling of signals --- cura/Settings/ExtruderManager.py | 4 +++- cura/Settings/MachineManager.py | 1 - cura/Settings/SettingInheritanceManager.py | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index 8fa0172305..6e462031dc 100755 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -344,7 +344,9 @@ class ExtruderManager(QObject): self._fixSingleExtrusionMachineExtruderDefinition(global_stack) if extruders_changed: self.extrudersChanged.emit(global_stack_id) - self.setActiveExtruderIndex(0) + + # Set it directly instead of using setActiveExtruder, since we want to force the signal to emitted. + self._active_extruder_index = 0 self.activeExtruderChanged.emit() # After 3.4, all single-extrusion machines have their own extruder definition files instead of reusing diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 2185bbce9d..755d05d71e 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -385,7 +385,6 @@ class MachineManager(QObject): self._application.setGlobalContainerStack(global_stack) ExtruderManager.getInstance()._globalContainerStackChanged() self._initMachineState(global_stack) - self._onGlobalContainerChanged() self.__emitChangedSignals() diff --git a/cura/Settings/SettingInheritanceManager.py b/cura/Settings/SettingInheritanceManager.py index 12b541c3d8..3815d85e81 100644 --- a/cura/Settings/SettingInheritanceManager.py +++ b/cura/Settings/SettingInheritanceManager.py @@ -249,7 +249,6 @@ class SettingInheritanceManager(QObject): if self._global_container_stack: self._global_container_stack.containersChanged.connect(self._onContainersChanged) self._global_container_stack.propertyChanged.connect(self._onPropertyChanged) - self._onActiveExtruderChanged() def _onContainersChanged(self, container): self._update_timer.start() From f627560751761f25b75733412b91a14cd2357e1e Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 21 Dec 2018 17:12:07 +0100 Subject: [PATCH 1076/1240] Add timer to QualityProfilesDropdownMenuModel to prevent unneeded updates It's the same old trick we've pulled off quite often, so this should be pretty safe --- .../Models/QualityProfilesDropDownMenuModel.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/cura/Machines/Models/QualityProfilesDropDownMenuModel.py b/cura/Machines/Models/QualityProfilesDropDownMenuModel.py index 747882b041..7ccc886bfe 100644 --- a/cura/Machines/Models/QualityProfilesDropDownMenuModel.py +++ b/cura/Machines/Models/QualityProfilesDropDownMenuModel.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 Qt +from PyQt5.QtCore import Qt, QTimer from UM.Application import Application from UM.Logger import Logger @@ -39,15 +39,23 @@ class QualityProfilesDropDownMenuModel(ListModel): self._machine_manager = self._application.getMachineManager() self._quality_manager = Application.getInstance().getQualityManager() - self._application.globalContainerStackChanged.connect(self._update) - self._machine_manager.activeQualityGroupChanged.connect(self._update) - self._machine_manager.extruderChanged.connect(self._update) - self._quality_manager.qualitiesUpdated.connect(self._update) + self._application.globalContainerStackChanged.connect(self._onChange) + self._machine_manager.activeQualityGroupChanged.connect(self._onChange) + self._machine_manager.extruderChanged.connect(self._onChange) + self._quality_manager.qualitiesUpdated.connect(self._onChange) self._layer_height_unit = "" # This is cached + self._update_timer = QTimer() # type: QTimer + self._update_timer.setInterval(100) + self._update_timer.setSingleShot(True) + self._update_timer.timeout.connect(self._update) + self._update() + def _onChange(self) -> None: + self._update_timer.start() + def _update(self): Logger.log("d", "Updating {model_class_name}.".format(model_class_name = self.__class__.__name__)) From e4af883f251ee8e0e6be9db6ab9d5afa18a5652a Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 21 Dec 2018 18:19:34 +0100 Subject: [PATCH 1077/1240] Add missing signal --- cura/Settings/GlobalStack.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cura/Settings/GlobalStack.py b/cura/Settings/GlobalStack.py index 44ceee9511..8dba0f5204 100755 --- a/cura/Settings/GlobalStack.py +++ b/cura/Settings/GlobalStack.py @@ -97,6 +97,7 @@ class GlobalStack(CuraContainerStack): return self._extruders[position] = extruder + self.extrudersChanged.emit() Logger.log("i", "Extruder[%s] added to [%s] at position [%s]", extruder.id, self.id, position) ## Overridden from ContainerStack From 42058a2e8ff8d35c3adb89fbf324fbe353b3576d Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 24 Dec 2018 09:37:17 +0100 Subject: [PATCH 1078/1240] Revert "Prevent some unneeded calling of signals" This reverts commit 85f10e7c4f9bc48afd2fe43903e565b128e03189. The second time you start Cura, it won't slice due to the number of extruders being wrong. --- cura/Settings/ExtruderManager.py | 4 +--- cura/Settings/MachineManager.py | 1 + cura/Settings/SettingInheritanceManager.py | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index 6e462031dc..8fa0172305 100755 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -344,9 +344,7 @@ class ExtruderManager(QObject): self._fixSingleExtrusionMachineExtruderDefinition(global_stack) if extruders_changed: self.extrudersChanged.emit(global_stack_id) - - # Set it directly instead of using setActiveExtruder, since we want to force the signal to emitted. - self._active_extruder_index = 0 + self.setActiveExtruderIndex(0) self.activeExtruderChanged.emit() # After 3.4, all single-extrusion machines have their own extruder definition files instead of reusing diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 755d05d71e..2185bbce9d 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -385,6 +385,7 @@ class MachineManager(QObject): self._application.setGlobalContainerStack(global_stack) ExtruderManager.getInstance()._globalContainerStackChanged() self._initMachineState(global_stack) + self._onGlobalContainerChanged() self.__emitChangedSignals() diff --git a/cura/Settings/SettingInheritanceManager.py b/cura/Settings/SettingInheritanceManager.py index 3815d85e81..12b541c3d8 100644 --- a/cura/Settings/SettingInheritanceManager.py +++ b/cura/Settings/SettingInheritanceManager.py @@ -249,6 +249,7 @@ class SettingInheritanceManager(QObject): if self._global_container_stack: self._global_container_stack.containersChanged.connect(self._onContainersChanged) self._global_container_stack.propertyChanged.connect(self._onPropertyChanged) + self._onActiveExtruderChanged() def _onContainersChanged(self, container): self._update_timer.start() From c6f4cb492761f1b80e71021d322ed78852e8af5f Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 24 Dec 2018 15:31:47 +0100 Subject: [PATCH 1079/1240] Make the settings panel resizable You can now drag the bottom side of the panel to make it bigger or smaller. Contributes to issue CURA-6054. --- cura/CuraApplication.py | 2 +- .../Custom/CustomPrintSetup.qml | 5 +- .../PrintSetupSelectorContents.qml | 48 ++++++++++++++++++- 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 9ec2435f0b..905c367cd1 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -499,7 +499,7 @@ class CuraApplication(QtApplication): preferences.addPreference("cura/choice_on_profile_override", "always_ask") preferences.addPreference("cura/choice_on_open_project", "always_ask") preferences.addPreference("cura/use_multi_build_plate", False) - + preferences.addPreference("view/settings_list_height", 600) preferences.addPreference("cura/currency", "€") preferences.addPreference("cura/material_settings", "{}") diff --git a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml index 51eb14a441..98bb5c0405 100644 --- a/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml +++ b/resources/qml/PrintSetupSelector/Custom/CustomPrintSetup.qml @@ -11,7 +11,6 @@ import Cura 1.0 as Cura Item { id: customPrintSetup - height: childrenRect.height + padding property real padding: UM.Theme.getSize("default_margin").width property bool multipleExtruders: extrudersModel.count > 1 @@ -98,15 +97,15 @@ Item Rectangle { - height: UM.Theme.getSize("print_setup_widget").height anchors { top: tabBar.visible ? tabBar.bottom : globalProfileRow.bottom + topMargin: -UM.Theme.getSize("default_lining").width left: parent.left leftMargin: parent.padding right: parent.right rightMargin: parent.padding - topMargin: -UM.Theme.getSize("default_lining").width + bottom: parent.bottom } z: tabBar.z - 1 // Don't show the border when only one extruder diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml index 6c678f7ce5..52c2807b81 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml @@ -15,7 +15,7 @@ Item id: content width: UM.Theme.getSize("print_setup_widget").width - 2 * UM.Theme.getSize("default_margin").width - height: childrenRect.height + height: contents.height + buttonRow.height enum Mode { @@ -71,6 +71,15 @@ Item right: parent.right top: parent.top } + height: UM.Preferences.getValue("view/settings_list_height") - UM.Theme.getSize("default_margin").height + Connections + { + target: UM.Preferences + onPreferenceChanged: + { + customPrintSetup.height = UM.Preferences.getValue("view/settings_list_height"); + } + } visible: currentModeIndex == PrintSetupSelectorContents.Mode.Custom } } @@ -94,13 +103,14 @@ Item anchors { - top: buttonsSeparator.bottom + bottom: parent.bottom left: parent.left right: parent.right } Cura.SecondaryButton { + id: recommendedButton anchors.top: parent.top anchors.left: parent.left anchors.margins: parent.padding @@ -125,5 +135,39 @@ Item visible: currentModeIndex == PrintSetupSelectorContents.Mode.Recommended onClicked: currentModeIndex = PrintSetupSelectorContents.Mode.Custom } + + //Invisible area at the bottom with which you can resize the panel. + MouseArea + { + anchors + { + left: parent.left + right: parent.right + bottom: parent.bottom + top: recommendedButton.bottom + topMargin: UM.Theme.getSize("default_lining").height + } + cursorShape: Qt.SplitVCursor + visible: currentModeIndex == PrintSetupSelectorContents.Mode.Custom + drag + { + target: parent + axis: Drag.YAxis + } + onMouseYChanged: + { + if(drag.active) + { + // position of mouse relative to dropdown align vertical centre of mouse area to cursor + // v------------------------------v v------------v + var h = mouseY + buttonRow.y + content.y - height / 2 | 0; + if(h < 200 * screenScaleFactor) //Enforce a minimum size. + { + h = 200 * screenScaleFactor; + } + UM.Preferences.setValue("view/settings_list_height", h); + } + } + } } } \ No newline at end of file From b7a23399f5bac16e0275647b23564558ada9185e Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 24 Dec 2018 15:37:48 +0100 Subject: [PATCH 1080/1240] Move action panel in front of settings panel The settings panel can now be so long that they overlap (if the user so chooses). This causes the action panel to be hidden behind the settings. We'd prefer to show the action panel in front, always. Contributes to issue CURA-6054. --- resources/qml/ActionPanel/ActionPanelWidget.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/qml/ActionPanel/ActionPanelWidget.qml b/resources/qml/ActionPanel/ActionPanelWidget.qml index a1cd81e9e9..1d9ee95548 100644 --- a/resources/qml/ActionPanel/ActionPanelWidget.qml +++ b/resources/qml/ActionPanel/ActionPanelWidget.qml @@ -23,6 +23,7 @@ Rectangle border.width: UM.Theme.getSize("default_lining").width border.color: UM.Theme.getColor("lining") radius: UM.Theme.getSize("default_radius").width + z: 10 property bool outputAvailable: UM.Backend.state == UM.Backend.Done || UM.Backend.state == UM.Backend.Disabled From c6c09a83271d368e68afec1ce106b246b770f1f4 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 24 Dec 2018 16:16:23 +0100 Subject: [PATCH 1081/1240] Limit height of settings drop-down to window size It seems to be very hard to limit it to not overlap with the action panel. Well, going out of the window is a bigger problem so at least we can fix that. Contributes to issue CURA-6054. --- .../qml/PrintSetupSelector/PrintSetupSelectorContents.qml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml index 52c2807b81..35c5f008b6 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml @@ -165,6 +165,14 @@ Item { h = 200 * screenScaleFactor; } + + //Absolute mouse Y position in the window, to prevent it from going outside the window. + var mouse_absolute_y = mapToGlobal(mouseX, mouseY).y - UM.Preferences.getValue("general/window_top"); + if(mouse_absolute_y > base.height) + { + h -= mouse_absolute_y - base.height; + } + UM.Preferences.setValue("view/settings_list_height", h); } } From f2a32e62fc8ab19b1dad9352a3a8e9aca965e149 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 27 Dec 2018 11:06:09 +0100 Subject: [PATCH 1082/1240] Rename 'Protected profiles' to 'Default profiles' We agreed that it looks better, especially when listed right above 'Custom profiles'. --- resources/qml/Preferences/ProfilesPage.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Preferences/ProfilesPage.qml b/resources/qml/Preferences/ProfilesPage.qml index 7fb17b7aa1..d20519b361 100644 --- a/resources/qml/Preferences/ProfilesPage.qml +++ b/resources/qml/Preferences/ProfilesPage.qml @@ -408,7 +408,7 @@ Item { anchors.left: parent.left anchors.leftMargin: UM.Theme.getSize("default_lining").width - text: section == "true" ? catalog.i18nc("@label", "Protected profiles") : catalog.i18nc("@label", "Custom profiles") + text: section == "true" ? catalog.i18nc("@label", "Default profiles") : catalog.i18nc("@label", "Custom profiles") font.bold: true } } From 02f7a1476524dd39d2ac775ff1139a509a1bf856 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 27 Dec 2018 11:28:02 +0100 Subject: [PATCH 1083/1240] Fix ordering of a bunch of profiles --- resources/bundled_packages/cura.json | 4 ++-- resources/quality/abax_pri3/apri3_pla_fast.inst.cfg | 2 +- resources/quality/abax_pri5/apri5_pla_fast.inst.cfg | 2 +- resources/quality/abax_titan/atitan_pla_fast.inst.cfg | 2 +- .../anycubic_4max/abs/anycubic_4max_abs_draft.inst.cfg | 2 +- .../quality/anycubic_4max/abs/anycubic_4max_abs_high.inst.cfg | 2 +- .../anycubic_4max/abs/anycubic_4max_abs_normal.inst.cfg | 2 +- resources/quality/anycubic_4max/anycubic_4max_draft.inst.cfg | 2 +- resources/quality/anycubic_4max/anycubic_4max_high.inst.cfg | 2 +- resources/quality/anycubic_4max/anycubic_4max_normal.inst.cfg | 2 +- .../anycubic_4max/hips/anycubic_4max_hips_draft.inst.cfg | 2 +- .../anycubic_4max/hips/anycubic_4max_hips_high.inst.cfg | 2 +- .../anycubic_4max/hips/anycubic_4max_hips_normal.inst.cfg | 2 +- .../anycubic_4max/petg/anycubic_4max_petg_draft.inst.cfg | 2 +- .../anycubic_4max/petg/anycubic_4max_petg_high.inst.cfg | 2 +- .../anycubic_4max/petg/anycubic_4max_petg_normal.inst.cfg | 2 +- .../anycubic_4max/pla/anycubic_4max_pla_draft.inst.cfg | 2 +- .../quality/anycubic_4max/pla/anycubic_4max_pla_high.inst.cfg | 2 +- .../anycubic_4max/pla/anycubic_4max_pla_normal.inst.cfg | 2 +- .../quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg | 2 +- .../quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg | 2 +- .../quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg | 2 +- .../quality/cartesio/abs/cartesio_0.25_abs_normal.inst.cfg | 2 +- .../quality/cartesio/abs/cartesio_0.4_abs_normal.inst.cfg | 2 +- .../quality/cartesio/abs/cartesio_0.8_abs_normal.inst.cfg | 2 +- .../cartesio/arnitel/cartesio_0.4_arnitel2045_normal.inst.cfg | 2 +- .../quality/cartesio/hips/cartesio_0.25_hips_normal.inst.cfg | 2 +- .../quality/cartesio/hips/cartesio_0.4_hips_normal.inst.cfg | 2 +- .../quality/cartesio/hips/cartesio_0.8_hips_normal.inst.cfg | 2 +- .../cartesio/nylon/cartesio_0.25_nylon_normal.inst.cfg | 2 +- .../quality/cartesio/nylon/cartesio_0.4_nylon_normal.inst.cfg | 2 +- .../quality/cartesio/nylon/cartesio_0.8_nylon_normal.inst.cfg | 2 +- .../quality/cartesio/pc/cartesio_0.25_pc_normal.inst.cfg | 2 +- resources/quality/cartesio/pc/cartesio_0.4_pc_normal.inst.cfg | 2 +- resources/quality/cartesio/pc/cartesio_0.8_pc_normal.inst.cfg | 2 +- .../quality/cartesio/petg/cartesio_0.25_petg_normal.inst.cfg | 2 +- .../quality/cartesio/petg/cartesio_0.4_petg_normal.inst.cfg | 2 +- .../quality/cartesio/petg/cartesio_0.8_petg_normal.inst.cfg | 2 +- .../quality/cartesio/pva/cartesio_0.25_pva_normal.inst.cfg | 2 +- .../quality/cartesio/pva/cartesio_0.4_pva_normal.inst.cfg | 2 +- .../quality/cartesio/pva/cartesio_0.8_pva_normal.inst.cfg | 2 +- .../quality/deltacomb/deltacomb_abs_High_Quality.inst.cfg | 2 +- .../quality/deltacomb/deltacomb_global_High_Quality.inst.cfg | 2 +- .../quality/deltacomb/deltacomb_pla_High_Quality.inst.cfg | 2 +- .../quality/gmax15plus/gmax15plus_pla_dual_normal.inst.cfg | 2 +- .../quality/gmax15plus/gmax15plus_pla_dual_thin.inst.cfg | 2 +- resources/quality/gmax15plus/gmax15plus_pla_normal.inst.cfg | 2 +- resources/quality/gmax15plus/gmax15plus_pla_thin.inst.cfg | 2 +- .../malyan_m200/abs/malyan_m200_abs_verydraft.inst.cfg | 2 +- .../malyan_m200/malyan_m200_global_VeryDraft_Quality.inst.cfg | 2 +- .../malyan_m200/petg/malyan_m200_petg_verydraft.inst.cfg | 2 +- .../malyan_m200/pla/malyan_m200_pla_verydraft.inst.cfg | 2 +- .../abs/monoprice_select_mini_v2_abs_verydraft.inst.cfg | 2 +- ...monoprice_select_mini_v2_global_VeryDraft_Quality.inst.cfg | 2 +- .../nylon/monoprice_select_mini_v2_nylon_verydraft.inst.cfg | 2 +- .../pc/monoprice_select_mini_v2_pc_verydraft.inst.cfg | 2 +- .../petg/monoprice_select_mini_v2_petg_verydraft.inst.cfg | 2 +- .../pla/monoprice_select_mini_v2_pla_high.inst.cfg | 2 +- resources/quality/peopoly_moai/peopoly_moai_draft.inst.cfg | 2 +- resources/quality/peopoly_moai/peopoly_moai_normal.inst.cfg | 2 +- .../quality/ultimaker2_plus/um2p_cpe_0.25_normal.inst.cfg | 2 +- .../quality/ultimaker2_plus/um2p_global_High_Quality.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_pp_0.8_draft.inst.cfg | 2 +- .../quality/ultimaker2_plus/um2p_pp_0.8_verydraft.inst.cfg | 2 +- resources/quality/ultimaker3/um3_aa0.8_PC_Fast_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.8_PC_Superdraft_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.8_PC_Verydraft_Print.inst.cfg | 2 +- resources/quality/ultimaker3/um3_global_High_Quality.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.8_PC_Fast_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.8_PC_Superdraft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.8_PC_Verydraft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.8_aluminum_PC_Fast_Print.inst.cfg | 2 +- .../um_s5_aa0.8_aluminum_PC_Superdraft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_aluminum_PC_Verydraft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_cc0.6_CFFCPE_Draft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_cc0.6_CFFPA_Draft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_cc0.6_GFFCPE_Draft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_cc0.6_GFFPA_Draft_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_global_High_Quality.inst.cfg | 2 +- resources/quality/zyyx/zyyx_agile_global_fine.inst.cfg | 2 +- resources/quality/zyyx/zyyx_agile_global_normal.inst.cfg | 2 +- resources/quality/zyyx/zyyx_agile_pro_flex_fine.inst.cfg | 2 +- resources/quality/zyyx/zyyx_agile_pro_flex_normal.inst.cfg | 2 +- resources/quality/zyyx/zyyx_agile_pro_pla_fine.inst.cfg | 2 +- resources/quality/zyyx/zyyx_agile_pro_pla_normal.inst.cfg | 2 +- 85 files changed, 86 insertions(+), 86 deletions(-) diff --git a/resources/bundled_packages/cura.json b/resources/bundled_packages/cura.json index c32b94af3f..99b8cd35a0 100644 --- a/resources/bundled_packages/cura.json +++ b/resources/bundled_packages/cura.json @@ -356,7 +356,7 @@ } } }, - "PreviewStage": { + "PreviewStage": { "package_info": { "package_id": "PreviewStage", "package_type": "plugin", @@ -1409,7 +1409,7 @@ "package_type": "material", "display_name": "Ultimaker PC", "description": "Example package for material and quality profiles for Ultimaker materials.", - "package_version": "1.2.1", + "package_version": "1.2.2", "sdk_version": "6.0", "website": "https://ultimaker.com/products/materials/pc", "author": { diff --git a/resources/quality/abax_pri3/apri3_pla_fast.inst.cfg b/resources/quality/abax_pri3/apri3_pla_fast.inst.cfg index 1d406e4387..ebb506a222 100644 --- a/resources/quality/abax_pri3/apri3_pla_fast.inst.cfg +++ b/resources/quality/abax_pri3/apri3_pla_fast.inst.cfg @@ -7,7 +7,7 @@ definition = abax_pri3 setting_version = 5 type = quality quality_type = normal -weight = -1 +weight = 0 material = generic_pla [values] diff --git a/resources/quality/abax_pri5/apri5_pla_fast.inst.cfg b/resources/quality/abax_pri5/apri5_pla_fast.inst.cfg index 6d0fdd40d2..f278788c68 100644 --- a/resources/quality/abax_pri5/apri5_pla_fast.inst.cfg +++ b/resources/quality/abax_pri5/apri5_pla_fast.inst.cfg @@ -7,7 +7,7 @@ definition = abax_pri5 setting_version = 5 type = quality quality_type = normal -weight = -1 +weight = 0 material = generic_pla [values] diff --git a/resources/quality/abax_titan/atitan_pla_fast.inst.cfg b/resources/quality/abax_titan/atitan_pla_fast.inst.cfg index 71740ede84..997e77041d 100644 --- a/resources/quality/abax_titan/atitan_pla_fast.inst.cfg +++ b/resources/quality/abax_titan/atitan_pla_fast.inst.cfg @@ -7,7 +7,7 @@ definition = abax_titan setting_version = 5 type = quality quality_type = normal -weight = -1 +weight = 0 material = generic_pla [values] diff --git a/resources/quality/anycubic_4max/abs/anycubic_4max_abs_draft.inst.cfg b/resources/quality/anycubic_4max/abs/anycubic_4max_abs_draft.inst.cfg index f5baa55029..9bb11389d2 100644 --- a/resources/quality/anycubic_4max/abs/anycubic_4max_abs_draft.inst.cfg +++ b/resources/quality/anycubic_4max/abs/anycubic_4max_abs_draft.inst.cfg @@ -7,7 +7,7 @@ definition = anycubic_4max setting_version = 5 type = quality quality_type = draft -weight = 0 +weight = -2 material = generic_abs [values] diff --git a/resources/quality/anycubic_4max/abs/anycubic_4max_abs_high.inst.cfg b/resources/quality/anycubic_4max/abs/anycubic_4max_abs_high.inst.cfg index bd613c6aad..4a09e0ee7c 100644 --- a/resources/quality/anycubic_4max/abs/anycubic_4max_abs_high.inst.cfg +++ b/resources/quality/anycubic_4max/abs/anycubic_4max_abs_high.inst.cfg @@ -7,7 +7,7 @@ definition = anycubic_4max setting_version = 5 type = quality quality_type = high -weight = 2 +weight = 1 material = generic_abs [values] diff --git a/resources/quality/anycubic_4max/abs/anycubic_4max_abs_normal.inst.cfg b/resources/quality/anycubic_4max/abs/anycubic_4max_abs_normal.inst.cfg index 7cff1db4d2..f415cb379f 100644 --- a/resources/quality/anycubic_4max/abs/anycubic_4max_abs_normal.inst.cfg +++ b/resources/quality/anycubic_4max/abs/anycubic_4max_abs_normal.inst.cfg @@ -7,7 +7,7 @@ definition = anycubic_4max setting_version = 5 type = quality quality_type = normal -weight = 1 +weight = 0 material = generic_abs [values] diff --git a/resources/quality/anycubic_4max/anycubic_4max_draft.inst.cfg b/resources/quality/anycubic_4max/anycubic_4max_draft.inst.cfg index c0114e3d6c..56810934c7 100644 --- a/resources/quality/anycubic_4max/anycubic_4max_draft.inst.cfg +++ b/resources/quality/anycubic_4max/anycubic_4max_draft.inst.cfg @@ -7,7 +7,7 @@ definition = anycubic_4max setting_version = 5 type = quality quality_type = draft -weight = 0 +weight = -2 global_quality = True [values] diff --git a/resources/quality/anycubic_4max/anycubic_4max_high.inst.cfg b/resources/quality/anycubic_4max/anycubic_4max_high.inst.cfg index 4a0993412a..4f35669ae2 100644 --- a/resources/quality/anycubic_4max/anycubic_4max_high.inst.cfg +++ b/resources/quality/anycubic_4max/anycubic_4max_high.inst.cfg @@ -7,7 +7,7 @@ definition = anycubic_4max setting_version = 5 type = quality quality_type = high -weight = 2 +weight = 1 global_quality = True [values] diff --git a/resources/quality/anycubic_4max/anycubic_4max_normal.inst.cfg b/resources/quality/anycubic_4max/anycubic_4max_normal.inst.cfg index eeb1d699e4..778764f005 100644 --- a/resources/quality/anycubic_4max/anycubic_4max_normal.inst.cfg +++ b/resources/quality/anycubic_4max/anycubic_4max_normal.inst.cfg @@ -7,7 +7,7 @@ definition = anycubic_4max setting_version = 5 type = quality quality_type = normal -weight = 1 +weight = 0 global_quality = True [values] diff --git a/resources/quality/anycubic_4max/hips/anycubic_4max_hips_draft.inst.cfg b/resources/quality/anycubic_4max/hips/anycubic_4max_hips_draft.inst.cfg index 3cd0226bd4..59b727e7be 100644 --- a/resources/quality/anycubic_4max/hips/anycubic_4max_hips_draft.inst.cfg +++ b/resources/quality/anycubic_4max/hips/anycubic_4max_hips_draft.inst.cfg @@ -7,7 +7,7 @@ definition = anycubic_4max setting_version = 5 type = quality quality_type = draft -weight = 0 +weight = -2 material = generic_hips [values] diff --git a/resources/quality/anycubic_4max/hips/anycubic_4max_hips_high.inst.cfg b/resources/quality/anycubic_4max/hips/anycubic_4max_hips_high.inst.cfg index ff5c6bee2f..1ddfe9ee4b 100644 --- a/resources/quality/anycubic_4max/hips/anycubic_4max_hips_high.inst.cfg +++ b/resources/quality/anycubic_4max/hips/anycubic_4max_hips_high.inst.cfg @@ -7,7 +7,7 @@ definition = anycubic_4max setting_version = 5 type = quality quality_type = high -weight = 2 +weight = 1 material = generic_hips [values] diff --git a/resources/quality/anycubic_4max/hips/anycubic_4max_hips_normal.inst.cfg b/resources/quality/anycubic_4max/hips/anycubic_4max_hips_normal.inst.cfg index c4701ae246..829392ad1f 100644 --- a/resources/quality/anycubic_4max/hips/anycubic_4max_hips_normal.inst.cfg +++ b/resources/quality/anycubic_4max/hips/anycubic_4max_hips_normal.inst.cfg @@ -7,7 +7,7 @@ definition = anycubic_4max setting_version = 5 type = quality quality_type = normal -weight = 1 +weight = 0 material = generic_hips [values] diff --git a/resources/quality/anycubic_4max/petg/anycubic_4max_petg_draft.inst.cfg b/resources/quality/anycubic_4max/petg/anycubic_4max_petg_draft.inst.cfg index 5e0c3e204a..028fe5d8a7 100644 --- a/resources/quality/anycubic_4max/petg/anycubic_4max_petg_draft.inst.cfg +++ b/resources/quality/anycubic_4max/petg/anycubic_4max_petg_draft.inst.cfg @@ -7,7 +7,7 @@ definition = anycubic_4max setting_version = 5 type = quality quality_type = draft -weight = 0 +weight = -2 material = generic_petg [values] diff --git a/resources/quality/anycubic_4max/petg/anycubic_4max_petg_high.inst.cfg b/resources/quality/anycubic_4max/petg/anycubic_4max_petg_high.inst.cfg index 57a89c4ec2..73eb73fe16 100644 --- a/resources/quality/anycubic_4max/petg/anycubic_4max_petg_high.inst.cfg +++ b/resources/quality/anycubic_4max/petg/anycubic_4max_petg_high.inst.cfg @@ -7,7 +7,7 @@ definition = anycubic_4max setting_version = 5 type = quality quality_type = high -weight = 2 +weight = 1 material = generic_petg [values] diff --git a/resources/quality/anycubic_4max/petg/anycubic_4max_petg_normal.inst.cfg b/resources/quality/anycubic_4max/petg/anycubic_4max_petg_normal.inst.cfg index 14a4607ceb..3ed9e5e707 100644 --- a/resources/quality/anycubic_4max/petg/anycubic_4max_petg_normal.inst.cfg +++ b/resources/quality/anycubic_4max/petg/anycubic_4max_petg_normal.inst.cfg @@ -7,7 +7,7 @@ definition = anycubic_4max setting_version = 5 type = quality quality_type = normal -weight = 1 +weight = 0 material = generic_petg [values] diff --git a/resources/quality/anycubic_4max/pla/anycubic_4max_pla_draft.inst.cfg b/resources/quality/anycubic_4max/pla/anycubic_4max_pla_draft.inst.cfg index eae9e3b5ef..fef5f88f97 100644 --- a/resources/quality/anycubic_4max/pla/anycubic_4max_pla_draft.inst.cfg +++ b/resources/quality/anycubic_4max/pla/anycubic_4max_pla_draft.inst.cfg @@ -7,7 +7,7 @@ definition = anycubic_4max setting_version = 5 type = quality quality_type = draft -weight = 0 +weight = -2 material = generic_pla [values] diff --git a/resources/quality/anycubic_4max/pla/anycubic_4max_pla_high.inst.cfg b/resources/quality/anycubic_4max/pla/anycubic_4max_pla_high.inst.cfg index c856fc66a7..edfa07067b 100644 --- a/resources/quality/anycubic_4max/pla/anycubic_4max_pla_high.inst.cfg +++ b/resources/quality/anycubic_4max/pla/anycubic_4max_pla_high.inst.cfg @@ -7,7 +7,7 @@ definition = anycubic_4max setting_version = 5 type = quality quality_type = high -weight = 2 +weight = 1 material = generic_pla [values] diff --git a/resources/quality/anycubic_4max/pla/anycubic_4max_pla_normal.inst.cfg b/resources/quality/anycubic_4max/pla/anycubic_4max_pla_normal.inst.cfg index be33bfe53a..e90bac1a0b 100644 --- a/resources/quality/anycubic_4max/pla/anycubic_4max_pla_normal.inst.cfg +++ b/resources/quality/anycubic_4max/pla/anycubic_4max_pla_normal.inst.cfg @@ -7,7 +7,7 @@ definition = anycubic_4max setting_version = 5 type = quality quality_type = normal -weight = 1 +weight = 0 material = generic_pla [values] diff --git a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg index e94b9f01d1..b4f8ba78ed 100644 --- a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg +++ b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg @@ -7,7 +7,7 @@ definition = anycubic_i3_mega setting_version = 5 type = quality quality_type = draft -weight = 0 +weight = -2 global_quality = True [values] diff --git a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg index c8c4bf9a81..c727764c5d 100644 --- a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg +++ b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg @@ -7,7 +7,7 @@ definition = anycubic_i3_mega setting_version = 5 type = quality quality_type = high -weight = 2 +weight = 1 global_quality = True [values] diff --git a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg index 399c3ebc55..beb7d98b8e 100644 --- a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg +++ b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg @@ -7,7 +7,7 @@ definition = anycubic_i3_mega setting_version = 5 type = quality quality_type = normal -weight = 1 +weight = 0 global_quality = True [values] diff --git a/resources/quality/cartesio/abs/cartesio_0.25_abs_normal.inst.cfg b/resources/quality/cartesio/abs/cartesio_0.25_abs_normal.inst.cfg index f74918ef4f..f0b4581b90 100644 --- a/resources/quality/cartesio/abs/cartesio_0.25_abs_normal.inst.cfg +++ b/resources/quality/cartesio/abs/cartesio_0.25_abs_normal.inst.cfg @@ -7,7 +7,7 @@ definition = cartesio setting_version = 5 type = quality quality_type = normal -weight = 2 +weight = 0 material = generic_abs variant = 0.25mm thermoplastic extruder diff --git a/resources/quality/cartesio/abs/cartesio_0.4_abs_normal.inst.cfg b/resources/quality/cartesio/abs/cartesio_0.4_abs_normal.inst.cfg index ab72092035..5ee76e8e2b 100644 --- a/resources/quality/cartesio/abs/cartesio_0.4_abs_normal.inst.cfg +++ b/resources/quality/cartesio/abs/cartesio_0.4_abs_normal.inst.cfg @@ -7,7 +7,7 @@ definition = cartesio setting_version = 5 type = quality quality_type = normal -weight = 2 +weight = 0 material = generic_abs variant = 0.4mm thermoplastic extruder diff --git a/resources/quality/cartesio/abs/cartesio_0.8_abs_normal.inst.cfg b/resources/quality/cartesio/abs/cartesio_0.8_abs_normal.inst.cfg index 9eea2177a8..82a980538c 100644 --- a/resources/quality/cartesio/abs/cartesio_0.8_abs_normal.inst.cfg +++ b/resources/quality/cartesio/abs/cartesio_0.8_abs_normal.inst.cfg @@ -7,7 +7,7 @@ definition = cartesio setting_version = 5 type = quality quality_type = normal -weight = 2 +weight = 0 material = generic_abs variant = 0.8mm thermoplastic extruder diff --git a/resources/quality/cartesio/arnitel/cartesio_0.4_arnitel2045_normal.inst.cfg b/resources/quality/cartesio/arnitel/cartesio_0.4_arnitel2045_normal.inst.cfg index 8215eb2f50..50dc4d42a0 100644 --- a/resources/quality/cartesio/arnitel/cartesio_0.4_arnitel2045_normal.inst.cfg +++ b/resources/quality/cartesio/arnitel/cartesio_0.4_arnitel2045_normal.inst.cfg @@ -7,7 +7,7 @@ definition = cartesio setting_version = 5 type = quality quality_type = normal -weight = 2 +weight = 0 material = dsm_arnitel2045_175 variant = 0.4mm thermoplastic extruder diff --git a/resources/quality/cartesio/hips/cartesio_0.25_hips_normal.inst.cfg b/resources/quality/cartesio/hips/cartesio_0.25_hips_normal.inst.cfg index 0cf82847a0..08d1cf709e 100644 --- a/resources/quality/cartesio/hips/cartesio_0.25_hips_normal.inst.cfg +++ b/resources/quality/cartesio/hips/cartesio_0.25_hips_normal.inst.cfg @@ -7,7 +7,7 @@ definition = cartesio setting_version = 5 type = quality quality_type = normal -weight = 2 +weight = 0 material = generic_hips variant = 0.25mm thermoplastic extruder diff --git a/resources/quality/cartesio/hips/cartesio_0.4_hips_normal.inst.cfg b/resources/quality/cartesio/hips/cartesio_0.4_hips_normal.inst.cfg index 8101fb6dd8..18daa16d97 100644 --- a/resources/quality/cartesio/hips/cartesio_0.4_hips_normal.inst.cfg +++ b/resources/quality/cartesio/hips/cartesio_0.4_hips_normal.inst.cfg @@ -7,7 +7,7 @@ definition = cartesio setting_version = 5 type = quality quality_type = normal -weight = 2 +weight = 0 material = generic_hips variant = 0.4mm thermoplastic extruder diff --git a/resources/quality/cartesio/hips/cartesio_0.8_hips_normal.inst.cfg b/resources/quality/cartesio/hips/cartesio_0.8_hips_normal.inst.cfg index 0b019d555f..5afc358d51 100644 --- a/resources/quality/cartesio/hips/cartesio_0.8_hips_normal.inst.cfg +++ b/resources/quality/cartesio/hips/cartesio_0.8_hips_normal.inst.cfg @@ -7,7 +7,7 @@ definition = cartesio setting_version = 5 type = quality quality_type = normal -weight = 2 +weight = 0 material = generic_hips variant = 0.8mm thermoplastic extruder diff --git a/resources/quality/cartesio/nylon/cartesio_0.25_nylon_normal.inst.cfg b/resources/quality/cartesio/nylon/cartesio_0.25_nylon_normal.inst.cfg index e0100d37ec..b8d17bcc8f 100644 --- a/resources/quality/cartesio/nylon/cartesio_0.25_nylon_normal.inst.cfg +++ b/resources/quality/cartesio/nylon/cartesio_0.25_nylon_normal.inst.cfg @@ -7,7 +7,7 @@ definition = cartesio setting_version = 5 type = quality quality_type = normal -weight = 2 +weight = 0 material = generic_nylon variant = 0.25mm thermoplastic extruder diff --git a/resources/quality/cartesio/nylon/cartesio_0.4_nylon_normal.inst.cfg b/resources/quality/cartesio/nylon/cartesio_0.4_nylon_normal.inst.cfg index 7d899ae927..b208462ed7 100644 --- a/resources/quality/cartesio/nylon/cartesio_0.4_nylon_normal.inst.cfg +++ b/resources/quality/cartesio/nylon/cartesio_0.4_nylon_normal.inst.cfg @@ -7,7 +7,7 @@ definition = cartesio setting_version = 5 type = quality quality_type = normal -weight = 2 +weight = 0 material = generic_nylon variant = 0.4mm thermoplastic extruder diff --git a/resources/quality/cartesio/nylon/cartesio_0.8_nylon_normal.inst.cfg b/resources/quality/cartesio/nylon/cartesio_0.8_nylon_normal.inst.cfg index 95be159ff2..3c51389cc8 100644 --- a/resources/quality/cartesio/nylon/cartesio_0.8_nylon_normal.inst.cfg +++ b/resources/quality/cartesio/nylon/cartesio_0.8_nylon_normal.inst.cfg @@ -7,7 +7,7 @@ definition = cartesio setting_version = 5 type = quality quality_type = normal -weight = 2 +weight = 0 material = generic_nylon variant = 0.8mm thermoplastic extruder diff --git a/resources/quality/cartesio/pc/cartesio_0.25_pc_normal.inst.cfg b/resources/quality/cartesio/pc/cartesio_0.25_pc_normal.inst.cfg index 248517d769..cca1922fa1 100644 --- a/resources/quality/cartesio/pc/cartesio_0.25_pc_normal.inst.cfg +++ b/resources/quality/cartesio/pc/cartesio_0.25_pc_normal.inst.cfg @@ -7,7 +7,7 @@ definition = cartesio setting_version = 5 type = quality quality_type = normal -weight = 2 +weight = 0 material = generic_pc variant = 0.25mm thermoplastic extruder diff --git a/resources/quality/cartesio/pc/cartesio_0.4_pc_normal.inst.cfg b/resources/quality/cartesio/pc/cartesio_0.4_pc_normal.inst.cfg index a0b71f1f0a..6dc8225f82 100644 --- a/resources/quality/cartesio/pc/cartesio_0.4_pc_normal.inst.cfg +++ b/resources/quality/cartesio/pc/cartesio_0.4_pc_normal.inst.cfg @@ -7,7 +7,7 @@ definition = cartesio setting_version = 5 type = quality quality_type = normal -weight = 2 +weight = 0 material = generic_pc variant = 0.4mm thermoplastic extruder diff --git a/resources/quality/cartesio/pc/cartesio_0.8_pc_normal.inst.cfg b/resources/quality/cartesio/pc/cartesio_0.8_pc_normal.inst.cfg index 173ad406f3..b47f7f1ded 100644 --- a/resources/quality/cartesio/pc/cartesio_0.8_pc_normal.inst.cfg +++ b/resources/quality/cartesio/pc/cartesio_0.8_pc_normal.inst.cfg @@ -7,7 +7,7 @@ definition = cartesio setting_version = 5 type = quality quality_type = normal -weight = 2 +weight = 0 material = generic_pc variant = 0.8mm thermoplastic extruder diff --git a/resources/quality/cartesio/petg/cartesio_0.25_petg_normal.inst.cfg b/resources/quality/cartesio/petg/cartesio_0.25_petg_normal.inst.cfg index 019af9d905..d9f358360d 100644 --- a/resources/quality/cartesio/petg/cartesio_0.25_petg_normal.inst.cfg +++ b/resources/quality/cartesio/petg/cartesio_0.25_petg_normal.inst.cfg @@ -7,7 +7,7 @@ definition = cartesio setting_version = 5 type = quality quality_type = normal -weight = 2 +weight = 0 material = generic_petg variant = 0.25mm thermoplastic extruder diff --git a/resources/quality/cartesio/petg/cartesio_0.4_petg_normal.inst.cfg b/resources/quality/cartesio/petg/cartesio_0.4_petg_normal.inst.cfg index ed17ccce31..f5269d4e88 100644 --- a/resources/quality/cartesio/petg/cartesio_0.4_petg_normal.inst.cfg +++ b/resources/quality/cartesio/petg/cartesio_0.4_petg_normal.inst.cfg @@ -7,7 +7,7 @@ definition = cartesio setting_version = 5 type = quality quality_type = normal -weight = 2 +weight = 0 material = generic_petg variant = 0.4mm thermoplastic extruder diff --git a/resources/quality/cartesio/petg/cartesio_0.8_petg_normal.inst.cfg b/resources/quality/cartesio/petg/cartesio_0.8_petg_normal.inst.cfg index 08f918f59b..ef4b5428c0 100644 --- a/resources/quality/cartesio/petg/cartesio_0.8_petg_normal.inst.cfg +++ b/resources/quality/cartesio/petg/cartesio_0.8_petg_normal.inst.cfg @@ -7,7 +7,7 @@ definition = cartesio setting_version = 5 type = quality quality_type = normal -weight = 2 +weight = 0 material = generic_petg variant = 0.8mm thermoplastic extruder diff --git a/resources/quality/cartesio/pva/cartesio_0.25_pva_normal.inst.cfg b/resources/quality/cartesio/pva/cartesio_0.25_pva_normal.inst.cfg index 6525991986..940432e07e 100644 --- a/resources/quality/cartesio/pva/cartesio_0.25_pva_normal.inst.cfg +++ b/resources/quality/cartesio/pva/cartesio_0.25_pva_normal.inst.cfg @@ -7,7 +7,7 @@ definition = cartesio setting_version = 5 type = quality quality_type = normal -weight = 2 +weight = 0 material = generic_pva variant = 0.25mm thermoplastic extruder diff --git a/resources/quality/cartesio/pva/cartesio_0.4_pva_normal.inst.cfg b/resources/quality/cartesio/pva/cartesio_0.4_pva_normal.inst.cfg index e7267735e4..9a0e279e6a 100644 --- a/resources/quality/cartesio/pva/cartesio_0.4_pva_normal.inst.cfg +++ b/resources/quality/cartesio/pva/cartesio_0.4_pva_normal.inst.cfg @@ -7,7 +7,7 @@ definition = cartesio setting_version = 5 type = quality quality_type = normal -weight = 2 +weight = 0 material = generic_pva variant = 0.4mm thermoplastic extruder diff --git a/resources/quality/cartesio/pva/cartesio_0.8_pva_normal.inst.cfg b/resources/quality/cartesio/pva/cartesio_0.8_pva_normal.inst.cfg index a6c0a89fcc..1d8cd52d0d 100644 --- a/resources/quality/cartesio/pva/cartesio_0.8_pva_normal.inst.cfg +++ b/resources/quality/cartesio/pva/cartesio_0.8_pva_normal.inst.cfg @@ -7,7 +7,7 @@ definition = cartesio setting_version = 5 type = quality quality_type = normal -weight = 2 +weight = 0 material = generic_pva variant = 0.8mm thermoplastic extruder diff --git a/resources/quality/deltacomb/deltacomb_abs_High_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_abs_High_Quality.inst.cfg index c196209553..d407b66762 100644 --- a/resources/quality/deltacomb/deltacomb_abs_High_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_abs_High_Quality.inst.cfg @@ -7,7 +7,7 @@ definition = deltacomb setting_version = 5 type = quality quality_type = high -weight = 0 +weight = 1 material = generic_abs [values] diff --git a/resources/quality/deltacomb/deltacomb_global_High_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_global_High_Quality.inst.cfg index d6d853466a..b56b222eb1 100755 --- a/resources/quality/deltacomb/deltacomb_global_High_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_global_High_Quality.inst.cfg @@ -7,7 +7,7 @@ definition = deltacomb setting_version = 5 type = quality quality_type = high -weight = 0 +weight = 1 global_quality = True [values] diff --git a/resources/quality/deltacomb/deltacomb_pla_High_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_pla_High_Quality.inst.cfg index 774b8969c0..440bac36e9 100644 --- a/resources/quality/deltacomb/deltacomb_pla_High_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_pla_High_Quality.inst.cfg @@ -7,7 +7,7 @@ definition = deltacomb setting_version = 5 type = quality quality_type = high -weight = 0 +weight = 1 material = generic_pla [values] diff --git a/resources/quality/gmax15plus/gmax15plus_pla_dual_normal.inst.cfg b/resources/quality/gmax15plus/gmax15plus_pla_dual_normal.inst.cfg index 7fd2ab2296..4bd83d5950 100644 --- a/resources/quality/gmax15plus/gmax15plus_pla_dual_normal.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_pla_dual_normal.inst.cfg @@ -7,7 +7,7 @@ definition = gmax15plus_dual setting_version = 5 type = quality quality_type = normal -weight = -1 +weight = 0 [values] layer_height = 0.2 diff --git a/resources/quality/gmax15plus/gmax15plus_pla_dual_thin.inst.cfg b/resources/quality/gmax15plus/gmax15plus_pla_dual_thin.inst.cfg index decafac241..e85a70d069 100644 --- a/resources/quality/gmax15plus/gmax15plus_pla_dual_thin.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_pla_dual_thin.inst.cfg @@ -7,7 +7,7 @@ definition = gmax15plus_dual setting_version = 5 type = quality quality_type = high -weight = 0 +weight = 1 [values] layer_height = 0.16 diff --git a/resources/quality/gmax15plus/gmax15plus_pla_normal.inst.cfg b/resources/quality/gmax15plus/gmax15plus_pla_normal.inst.cfg index ddf5a4c491..afecc2ef58 100644 --- a/resources/quality/gmax15plus/gmax15plus_pla_normal.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_pla_normal.inst.cfg @@ -7,7 +7,7 @@ definition = gmax15plus setting_version = 5 type = quality quality_type = normal -weight = -1 +weight = 0 [values] layer_height = 0.2 diff --git a/resources/quality/gmax15plus/gmax15plus_pla_thin.inst.cfg b/resources/quality/gmax15plus/gmax15plus_pla_thin.inst.cfg index d119539d32..41d32cb6b9 100644 --- a/resources/quality/gmax15plus/gmax15plus_pla_thin.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_pla_thin.inst.cfg @@ -7,7 +7,7 @@ definition = gmax15plus setting_version = 5 type = quality quality_type = high -weight = 0 +weight = 1 [values] layer_height = 0.16 diff --git a/resources/quality/malyan_m200/abs/malyan_m200_abs_verydraft.inst.cfg b/resources/quality/malyan_m200/abs/malyan_m200_abs_verydraft.inst.cfg index 373908b2bd..a3d8bd4579 100644 --- a/resources/quality/malyan_m200/abs/malyan_m200_abs_verydraft.inst.cfg +++ b/resources/quality/malyan_m200/abs/malyan_m200_abs_verydraft.inst.cfg @@ -7,7 +7,7 @@ definition = malyan_m200 setting_version = 5 type = quality quality_type = verydraft -weight = -4 +weight = -3 material = generic_abs [values] diff --git a/resources/quality/malyan_m200/malyan_m200_global_VeryDraft_Quality.inst.cfg b/resources/quality/malyan_m200/malyan_m200_global_VeryDraft_Quality.inst.cfg index 42c670920f..a3013a68fb 100644 --- a/resources/quality/malyan_m200/malyan_m200_global_VeryDraft_Quality.inst.cfg +++ b/resources/quality/malyan_m200/malyan_m200_global_VeryDraft_Quality.inst.cfg @@ -7,7 +7,7 @@ definition = malyan_m200 setting_version = 5 type = quality quality_type = verydraft -weight = -4 +weight = -3 global_quality = True [values] diff --git a/resources/quality/malyan_m200/petg/malyan_m200_petg_verydraft.inst.cfg b/resources/quality/malyan_m200/petg/malyan_m200_petg_verydraft.inst.cfg index 2f33760afa..e0305105af 100644 --- a/resources/quality/malyan_m200/petg/malyan_m200_petg_verydraft.inst.cfg +++ b/resources/quality/malyan_m200/petg/malyan_m200_petg_verydraft.inst.cfg @@ -7,5 +7,5 @@ definition = malyan_m200 setting_version = 5 type = quality quality_type = verydraft -weight = -4 +weight = -3 material = generic_petg diff --git a/resources/quality/malyan_m200/pla/malyan_m200_pla_verydraft.inst.cfg b/resources/quality/malyan_m200/pla/malyan_m200_pla_verydraft.inst.cfg index 7d96a9f56d..a79d253e49 100644 --- a/resources/quality/malyan_m200/pla/malyan_m200_pla_verydraft.inst.cfg +++ b/resources/quality/malyan_m200/pla/malyan_m200_pla_verydraft.inst.cfg @@ -7,7 +7,7 @@ definition = malyan_m200 setting_version = 5 type = quality quality_type = verydraft -weight = -4 +weight = -3 material = generic_pla [values] diff --git a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_verydraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_verydraft.inst.cfg index 279f58d64f..21d1215dca 100644 --- a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_verydraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_verydraft.inst.cfg @@ -7,7 +7,7 @@ definition = monoprice_select_mini_v2 setting_version = 5 type = quality quality_type = verydraft -weight = -4 +weight = -3 material = generic_abs [values] diff --git a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_VeryDraft_Quality.inst.cfg b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_VeryDraft_Quality.inst.cfg index 79d9c8abb9..36aa5a9a37 100644 --- a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_VeryDraft_Quality.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_VeryDraft_Quality.inst.cfg @@ -7,7 +7,7 @@ definition = monoprice_select_mini_v2 setting_version = 5 type = quality quality_type = verydraft -weight = -4 +weight = -3 global_quality = True [values] diff --git a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_verydraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_verydraft.inst.cfg index 8f568d3948..40f6a9f53d 100644 --- a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_verydraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_verydraft.inst.cfg @@ -7,5 +7,5 @@ definition = monoprice_select_mini_v2 setting_version = 5 type = quality quality_type = verydraft -weight = -4 +weight = -3 material = generic_nylon diff --git a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_verydraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_verydraft.inst.cfg index 23b72a3a3b..5202565894 100644 --- a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_verydraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_verydraft.inst.cfg @@ -7,7 +7,7 @@ definition = monoprice_select_mini_v2 setting_version = 5 type = quality quality_type = verydraft -weight = -4 +weight = -3 material = generic_pc [values] diff --git a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_verydraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_verydraft.inst.cfg index 99874f0571..a9f891692a 100644 --- a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_verydraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_verydraft.inst.cfg @@ -7,5 +7,5 @@ definition = monoprice_select_mini_v2 setting_version = 5 type = quality quality_type = verydraft -weight = -4 +weight = -3 material = generic_petg diff --git a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_high.inst.cfg b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_high.inst.cfg index 9f64ce57f0..f8458ce63b 100644 --- a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_high.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_high.inst.cfg @@ -7,5 +7,5 @@ definition = monoprice_select_mini_v2 setting_version = 5 type = quality quality_type = high -weight = 0 +weight = 1 material = generic_pla diff --git a/resources/quality/peopoly_moai/peopoly_moai_draft.inst.cfg b/resources/quality/peopoly_moai/peopoly_moai_draft.inst.cfg index 2d21b1f7e0..53abc477ae 100644 --- a/resources/quality/peopoly_moai/peopoly_moai_draft.inst.cfg +++ b/resources/quality/peopoly_moai/peopoly_moai_draft.inst.cfg @@ -7,7 +7,7 @@ definition = peopoly_moai setting_version = 5 type = quality quality_type = draft -weight = 4 +weight = -2 [values] layer_height = 0.1 diff --git a/resources/quality/peopoly_moai/peopoly_moai_normal.inst.cfg b/resources/quality/peopoly_moai/peopoly_moai_normal.inst.cfg index cf67591ab2..89d0aa2980 100644 --- a/resources/quality/peopoly_moai/peopoly_moai_normal.inst.cfg +++ b/resources/quality/peopoly_moai/peopoly_moai_normal.inst.cfg @@ -7,7 +7,7 @@ definition = peopoly_moai setting_version = 5 type = quality quality_type = normal -weight = 2 +weight = 0 [values] layer_height = 0.06 diff --git a/resources/quality/ultimaker2_plus/um2p_cpe_0.25_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpe_0.25_normal.inst.cfg index eca5070eb9..8f225271d6 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpe_0.25_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpe_0.25_normal.inst.cfg @@ -7,7 +7,7 @@ definition = ultimaker2_plus setting_version = 5 type = quality quality_type = high -weight = -1 +weight = 1 material = generic_cpe variant = 0.25 mm diff --git a/resources/quality/ultimaker2_plus/um2p_global_High_Quality.inst.cfg b/resources/quality/ultimaker2_plus/um2p_global_High_Quality.inst.cfg index ade183d14a..c47efcfb7d 100644 --- a/resources/quality/ultimaker2_plus/um2p_global_High_Quality.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_global_High_Quality.inst.cfg @@ -7,7 +7,7 @@ definition = ultimaker2_plus setting_version = 5 type = quality quality_type = high -weight = 0 +weight = 1 global_quality = True [values] diff --git a/resources/quality/ultimaker2_plus/um2p_pp_0.8_draft.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pp_0.8_draft.inst.cfg index 2bba1be3d4..f0da3e678d 100644 --- a/resources/quality/ultimaker2_plus/um2p_pp_0.8_draft.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pp_0.8_draft.inst.cfg @@ -7,7 +7,7 @@ definition = ultimaker2_plus setting_version = 5 type = quality quality_type = fast -weight = -2 +weight = -1 material = generic_pp variant = 0.8 mm diff --git a/resources/quality/ultimaker2_plus/um2p_pp_0.8_verydraft.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pp_0.8_verydraft.inst.cfg index 15f7577bdb..586eb04dd8 100644 --- a/resources/quality/ultimaker2_plus/um2p_pp_0.8_verydraft.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pp_0.8_verydraft.inst.cfg @@ -7,7 +7,7 @@ definition = ultimaker2_plus setting_version = 5 type = quality quality_type = draft -weight = -3 +weight = -2 material = generic_pp variant = 0.8 mm diff --git a/resources/quality/ultimaker3/um3_aa0.8_PC_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_PC_Fast_Print.inst.cfg index c2bb6d4988..80be29ed85 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_PC_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_PC_Fast_Print.inst.cfg @@ -7,7 +7,7 @@ definition = ultimaker3 setting_version = 5 type = quality quality_type = draft -weight = 0 +weight = -2 material = generic_pc variant = AA 0.8 is_experimental = True diff --git a/resources/quality/ultimaker3/um3_aa0.8_PC_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_PC_Superdraft_Print.inst.cfg index e815b673d1..149712dde9 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_PC_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_PC_Superdraft_Print.inst.cfg @@ -7,7 +7,7 @@ definition = ultimaker3 setting_version = 5 type = quality quality_type = superdraft -weight = -2 +weight = -4 material = generic_pc variant = AA 0.8 is_experimental = True diff --git a/resources/quality/ultimaker3/um3_aa0.8_PC_Verydraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_PC_Verydraft_Print.inst.cfg index c50cee576d..7311cc43a6 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_PC_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_PC_Verydraft_Print.inst.cfg @@ -7,7 +7,7 @@ definition = ultimaker3 setting_version = 5 type = quality quality_type = verydraft -weight = -1 +weight = -3 material = generic_pc variant = AA 0.8 is_experimental = True diff --git a/resources/quality/ultimaker3/um3_global_High_Quality.inst.cfg b/resources/quality/ultimaker3/um3_global_High_Quality.inst.cfg index ce5497bd39..a1fa758163 100644 --- a/resources/quality/ultimaker3/um3_global_High_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_global_High_Quality.inst.cfg @@ -7,7 +7,7 @@ definition = ultimaker3 setting_version = 5 type = quality quality_type = high -weight = 0 +weight = 1 global_quality = True [values] diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Fast_Print.inst.cfg index c24aa9a98d..378028694b 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Fast_Print.inst.cfg @@ -7,7 +7,7 @@ definition = ultimaker_s5 setting_version = 5 type = quality quality_type = draft -weight = 0 +weight = -2 material = generic_pc variant = AA 0.8 is_experimental = True diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Superdraft_Print.inst.cfg index 5fc306b1f1..a179d6b7c9 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Superdraft_Print.inst.cfg @@ -7,7 +7,7 @@ definition = ultimaker_s5 setting_version = 5 type = quality quality_type = superdraft -weight = -2 +weight = -4 material = generic_pc variant = AA 0.8 is_experimental = True diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Verydraft_Print.inst.cfg index 996adf5be7..b474cb23d9 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Verydraft_Print.inst.cfg @@ -7,7 +7,7 @@ definition = ultimaker_s5 setting_version = 5 type = quality quality_type = verydraft -weight = -1 +weight = -3 material = generic_pc variant = AA 0.8 is_experimental = True diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Fast_Print.inst.cfg index 6fdd22c6b0..a313ac56fc 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Fast_Print.inst.cfg @@ -7,7 +7,7 @@ definition = ultimaker_s5 setting_version = 5 type = quality quality_type = draft -weight = 0 +weight = -2 material = generic_pc variant = AA 0.8 buildplate = Aluminum diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Superdraft_Print.inst.cfg index 689652dc06..f1c1762d2c 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Superdraft_Print.inst.cfg @@ -7,7 +7,7 @@ definition = ultimaker_s5 setting_version = 5 type = quality quality_type = superdraft -weight = -2 +weight = -4 material = generic_pc variant = AA 0.8 buildplate = Aluminum diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Verydraft_Print.inst.cfg index 0480ee5620..399c139412 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Verydraft_Print.inst.cfg @@ -7,7 +7,7 @@ definition = ultimaker_s5 setting_version = 5 type = quality quality_type = verydraft -weight = -1 +weight = -3 material = generic_pc variant = AA 0.8 buildplate = Aluminum diff --git a/resources/quality/ultimaker_s5/um_s5_cc0.6_CFFCPE_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_cc0.6_CFFCPE_Draft_Print.inst.cfg index 99b4b142fa..feb8891d26 100644 --- a/resources/quality/ultimaker_s5/um_s5_cc0.6_CFFCPE_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_cc0.6_CFFCPE_Draft_Print.inst.cfg @@ -7,7 +7,7 @@ definition = ultimaker_s5 setting_version = 5 type = quality quality_type = draft -weight = -3 +weight = -2 material = generic_cffcpe variant = CC 0.6 diff --git a/resources/quality/ultimaker_s5/um_s5_cc0.6_CFFPA_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_cc0.6_CFFPA_Draft_Print.inst.cfg index 80c383aa8d..980ded99e9 100644 --- a/resources/quality/ultimaker_s5/um_s5_cc0.6_CFFPA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_cc0.6_CFFPA_Draft_Print.inst.cfg @@ -7,7 +7,7 @@ definition = ultimaker_s5 setting_version = 5 type = quality quality_type = draft -weight = -3 +weight = -2 material = generic_cffpa variant = CC 0.6 diff --git a/resources/quality/ultimaker_s5/um_s5_cc0.6_GFFCPE_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_cc0.6_GFFCPE_Draft_Print.inst.cfg index c94d239c81..331c5e6d73 100644 --- a/resources/quality/ultimaker_s5/um_s5_cc0.6_GFFCPE_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_cc0.6_GFFCPE_Draft_Print.inst.cfg @@ -7,7 +7,7 @@ definition = ultimaker_s5 setting_version = 5 type = quality quality_type = draft -weight = -3 +weight = -2 material = generic_gffcpe variant = CC 0.6 diff --git a/resources/quality/ultimaker_s5/um_s5_cc0.6_GFFPA_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_cc0.6_GFFPA_Draft_Print.inst.cfg index e7d4d1955b..0e90d6df78 100644 --- a/resources/quality/ultimaker_s5/um_s5_cc0.6_GFFPA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_cc0.6_GFFPA_Draft_Print.inst.cfg @@ -7,7 +7,7 @@ definition = ultimaker_s5 setting_version = 5 type = quality quality_type = draft -weight = -3 +weight = -2 material = generic_gffpa variant = CC 0.6 diff --git a/resources/quality/ultimaker_s5/um_s5_global_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_global_High_Quality.inst.cfg index cd1c269b1d..96005a48da 100644 --- a/resources/quality/ultimaker_s5/um_s5_global_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_global_High_Quality.inst.cfg @@ -7,7 +7,7 @@ definition = ultimaker_s5 setting_version = 5 type = quality quality_type = high -weight = 0 +weight = 1 global_quality = True [values] diff --git a/resources/quality/zyyx/zyyx_agile_global_fine.inst.cfg b/resources/quality/zyyx/zyyx_agile_global_fine.inst.cfg index 58e13b22c5..b8bc7eb628 100644 --- a/resources/quality/zyyx/zyyx_agile_global_fine.inst.cfg +++ b/resources/quality/zyyx/zyyx_agile_global_fine.inst.cfg @@ -7,7 +7,7 @@ definition = zyyx_agile setting_version = 5 type = quality quality_type = fine -weight = 3 +weight = 1 global_quality = True [values] diff --git a/resources/quality/zyyx/zyyx_agile_global_normal.inst.cfg b/resources/quality/zyyx/zyyx_agile_global_normal.inst.cfg index cb4e042e7b..3f934b0114 100644 --- a/resources/quality/zyyx/zyyx_agile_global_normal.inst.cfg +++ b/resources/quality/zyyx/zyyx_agile_global_normal.inst.cfg @@ -7,7 +7,7 @@ definition = zyyx_agile setting_version = 5 type = quality quality_type = normal -weight = 2 +weight = 0 global_quality = True [values] diff --git a/resources/quality/zyyx/zyyx_agile_pro_flex_fine.inst.cfg b/resources/quality/zyyx/zyyx_agile_pro_flex_fine.inst.cfg index 6654889c10..d69bc930e9 100644 --- a/resources/quality/zyyx/zyyx_agile_pro_flex_fine.inst.cfg +++ b/resources/quality/zyyx/zyyx_agile_pro_flex_fine.inst.cfg @@ -7,7 +7,7 @@ definition = zyyx_agile setting_version = 5 type = quality quality_type = fine -weight = 3 +weight = 1 material = generic_tpu [values] diff --git a/resources/quality/zyyx/zyyx_agile_pro_flex_normal.inst.cfg b/resources/quality/zyyx/zyyx_agile_pro_flex_normal.inst.cfg index f56355100c..d717f11528 100644 --- a/resources/quality/zyyx/zyyx_agile_pro_flex_normal.inst.cfg +++ b/resources/quality/zyyx/zyyx_agile_pro_flex_normal.inst.cfg @@ -7,7 +7,7 @@ definition = zyyx_agile setting_version = 5 type = quality quality_type = normal -weight = 2 +weight = 0 material = generic_tpu [values] diff --git a/resources/quality/zyyx/zyyx_agile_pro_pla_fine.inst.cfg b/resources/quality/zyyx/zyyx_agile_pro_pla_fine.inst.cfg index 64c7d4afc8..44b722ac32 100644 --- a/resources/quality/zyyx/zyyx_agile_pro_pla_fine.inst.cfg +++ b/resources/quality/zyyx/zyyx_agile_pro_pla_fine.inst.cfg @@ -7,7 +7,7 @@ definition = zyyx_agile setting_version = 5 type = quality quality_type = fine -weight = 3 +weight = 1 material = generic_pla [values] diff --git a/resources/quality/zyyx/zyyx_agile_pro_pla_normal.inst.cfg b/resources/quality/zyyx/zyyx_agile_pro_pla_normal.inst.cfg index dbdd600ece..01424a9422 100644 --- a/resources/quality/zyyx/zyyx_agile_pro_pla_normal.inst.cfg +++ b/resources/quality/zyyx/zyyx_agile_pro_pla_normal.inst.cfg @@ -7,7 +7,7 @@ definition = zyyx_agile setting_version = 5 type = quality quality_type = normal -weight = 2 +weight = 0 material = generic_pla [values] From abe684c1bda37bb1ca7c7f82dd07b6720a5d22c1 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 27 Dec 2018 13:08:25 +0100 Subject: [PATCH 1084/1240] Use Item instead of rectangle without defined colour Item should be faster to render. Contributes to issue CURA-6056. --- resources/qml/Preferences/Materials/MaterialsBrandSection.qml | 2 +- resources/qml/Preferences/Materials/MaterialsTypeSection.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/Preferences/Materials/MaterialsBrandSection.qml b/resources/qml/Preferences/Materials/MaterialsBrandSection.qml index c40693e343..c976233805 100644 --- a/resources/qml/Preferences/Materials/MaterialsBrandSection.qml +++ b/resources/qml/Preferences/Materials/MaterialsBrandSection.qml @@ -10,7 +10,7 @@ import QtQuick.Dialogs 1.2 import UM 1.2 as UM import Cura 1.0 as Cura -Rectangle +Item { id: brand_section diff --git a/resources/qml/Preferences/Materials/MaterialsTypeSection.qml b/resources/qml/Preferences/Materials/MaterialsTypeSection.qml index f98c19e0b3..8f34217cce 100644 --- a/resources/qml/Preferences/Materials/MaterialsTypeSection.qml +++ b/resources/qml/Preferences/Materials/MaterialsTypeSection.qml @@ -10,7 +10,7 @@ import QtQuick.Dialogs 1.2 import UM 1.2 as UM import Cura 1.0 as Cura -Rectangle +Item { id: material_type_section property var materialType From 88d6e0bcaf29f14df298ce0b63224be56bbdc81a Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 27 Dec 2018 16:27:26 +0100 Subject: [PATCH 1085/1240] Correct inst.cfg version Contributes to issue CURA-5848. --- resources/variants/gmax15plus_025_e3d.inst.cfg | 2 +- resources/variants/gmax15plus_dual_025_e3d.inst.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/variants/gmax15plus_025_e3d.inst.cfg b/resources/variants/gmax15plus_025_e3d.inst.cfg index f106b13be1..8a6b37067d 100644 --- a/resources/variants/gmax15plus_025_e3d.inst.cfg +++ b/resources/variants/gmax15plus_025_e3d.inst.cfg @@ -1,6 +1,6 @@ [general] name = 0.25mm E3D (Difficult) -version = 2 +version = 4 definition = gmax15plus [metadata] diff --git a/resources/variants/gmax15plus_dual_025_e3d.inst.cfg b/resources/variants/gmax15plus_dual_025_e3d.inst.cfg index d5f6457902..750a5381b3 100644 --- a/resources/variants/gmax15plus_dual_025_e3d.inst.cfg +++ b/resources/variants/gmax15plus_dual_025_e3d.inst.cfg @@ -1,6 +1,6 @@ [general] name = 0.25mm E3D (Difficult) -version = 2 +version = 4 definition = gmax15plus_dual [metadata] From 139ab2e0bb0bed3661d70a945105a2a75a5c1341 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 27 Dec 2018 16:39:05 +0100 Subject: [PATCH 1086/1240] Increment setting_version to 6 Because Cura 4.1 is going to have a version upgrade to change the stack files. Contributes to issue CURA-5848. --- cura/CuraApplication.py | 2 +- resources/quality/abax_pri3/apri3_pla_fast.inst.cfg | 2 +- resources/quality/abax_pri3/apri3_pla_high.inst.cfg | 2 +- resources/quality/abax_pri3/apri3_pla_normal.inst.cfg | 2 +- resources/quality/abax_pri5/apri5_pla_fast.inst.cfg | 2 +- resources/quality/abax_pri5/apri5_pla_high.inst.cfg | 2 +- resources/quality/abax_pri5/apri5_pla_normal.inst.cfg | 2 +- resources/quality/abax_titan/atitan_pla_fast.inst.cfg | 2 +- resources/quality/abax_titan/atitan_pla_high.inst.cfg | 2 +- resources/quality/abax_titan/atitan_pla_normal.inst.cfg | 2 +- .../quality/anycubic_4max/abs/anycubic_4max_abs_draft.inst.cfg | 2 +- .../quality/anycubic_4max/abs/anycubic_4max_abs_high.inst.cfg | 2 +- .../quality/anycubic_4max/abs/anycubic_4max_abs_normal.inst.cfg | 2 +- resources/quality/anycubic_4max/anycubic_4max_draft.inst.cfg | 2 +- resources/quality/anycubic_4max/anycubic_4max_high.inst.cfg | 2 +- resources/quality/anycubic_4max/anycubic_4max_normal.inst.cfg | 2 +- .../anycubic_4max/hips/anycubic_4max_hips_draft.inst.cfg | 2 +- .../quality/anycubic_4max/hips/anycubic_4max_hips_high.inst.cfg | 2 +- .../anycubic_4max/hips/anycubic_4max_hips_normal.inst.cfg | 2 +- .../anycubic_4max/petg/anycubic_4max_petg_draft.inst.cfg | 2 +- .../quality/anycubic_4max/petg/anycubic_4max_petg_high.inst.cfg | 2 +- .../anycubic_4max/petg/anycubic_4max_petg_normal.inst.cfg | 2 +- .../quality/anycubic_4max/pla/anycubic_4max_pla_draft.inst.cfg | 2 +- .../quality/anycubic_4max/pla/anycubic_4max_pla_high.inst.cfg | 2 +- .../quality/anycubic_4max/pla/anycubic_4max_pla_normal.inst.cfg | 2 +- .../quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg | 2 +- .../quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg | 2 +- .../quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg | 2 +- .../quality/builder_premium/bp_BVOH_Coarse_Quality.inst.cfg | 2 +- resources/quality/builder_premium/bp_BVOH_High_Quality.inst.cfg | 2 +- .../quality/builder_premium/bp_BVOH_Normal_Quality.inst.cfg | 2 +- .../builder_premium/bp_Innoflex60_Coarse_Quality.inst.cfg | 2 +- .../quality/builder_premium/bp_Innoflex60_High_Quality.inst.cfg | 2 +- .../builder_premium/bp_Innoflex60_Normal_Quality.inst.cfg | 2 +- .../quality/builder_premium/bp_PET_Coarse_Quality.inst.cfg | 2 +- resources/quality/builder_premium/bp_PET_High_Quality.inst.cfg | 2 +- .../quality/builder_premium/bp_PET_Normal_Quality.inst.cfg | 2 +- .../quality/builder_premium/bp_PLA_Coarse_Quality.inst.cfg | 2 +- resources/quality/builder_premium/bp_PLA_High_Quality.inst.cfg | 2 +- .../quality/builder_premium/bp_PLA_Normal_Quality.inst.cfg | 2 +- .../quality/builder_premium/bp_PVA_Coarse_Quality.inst.cfg | 2 +- resources/quality/builder_premium/bp_PVA_High_Quality.inst.cfg | 2 +- .../quality/builder_premium/bp_PVA_Normal_Quality.inst.cfg | 2 +- .../quality/builder_premium/bp_global_Coarse_Quality.inst.cfg | 2 +- .../quality/builder_premium/bp_global_High_Quality.inst.cfg | 2 +- .../quality/builder_premium/bp_global_Normal_Quality.inst.cfg | 2 +- resources/quality/cartesio/abs/cartesio_0.25_abs_high.inst.cfg | 2 +- .../quality/cartesio/abs/cartesio_0.25_abs_normal.inst.cfg | 2 +- resources/quality/cartesio/abs/cartesio_0.4_abs_high.inst.cfg | 2 +- resources/quality/cartesio/abs/cartesio_0.4_abs_normal.inst.cfg | 2 +- resources/quality/cartesio/abs/cartesio_0.8_abs_coarse.inst.cfg | 2 +- .../quality/cartesio/abs/cartesio_0.8_abs_extra_coarse.inst.cfg | 2 +- resources/quality/cartesio/abs/cartesio_0.8_abs_high.inst.cfg | 2 +- resources/quality/cartesio/abs/cartesio_0.8_abs_normal.inst.cfg | 2 +- .../cartesio/arnitel/cartesio_0.4_arnitel2045_high.inst.cfg | 2 +- .../cartesio/arnitel/cartesio_0.4_arnitel2045_normal.inst.cfg | 2 +- .../quality/cartesio/cartesio_global_Coarse_Quality.inst.cfg | 2 +- .../cartesio/cartesio_global_Extra_Coarse_Quality.inst.cfg | 2 +- .../quality/cartesio/cartesio_global_High_Quality.inst.cfg | 2 +- .../quality/cartesio/cartesio_global_Normal_Quality.inst.cfg | 2 +- .../quality/cartesio/hips/cartesio_0.25_hips_high.inst.cfg | 2 +- .../quality/cartesio/hips/cartesio_0.25_hips_normal.inst.cfg | 2 +- resources/quality/cartesio/hips/cartesio_0.4_hips_high.inst.cfg | 2 +- .../quality/cartesio/hips/cartesio_0.4_hips_normal.inst.cfg | 2 +- .../quality/cartesio/hips/cartesio_0.8_hips_coarse.inst.cfg | 2 +- .../cartesio/hips/cartesio_0.8_hips_extra_coarse.inst.cfg | 2 +- resources/quality/cartesio/hips/cartesio_0.8_hips_high.inst.cfg | 2 +- .../quality/cartesio/hips/cartesio_0.8_hips_normal.inst.cfg | 2 +- .../quality/cartesio/nylon/cartesio_0.25_nylon_high.inst.cfg | 2 +- .../quality/cartesio/nylon/cartesio_0.25_nylon_normal.inst.cfg | 2 +- .../quality/cartesio/nylon/cartesio_0.4_nylon_high.inst.cfg | 2 +- .../quality/cartesio/nylon/cartesio_0.4_nylon_normal.inst.cfg | 2 +- .../quality/cartesio/nylon/cartesio_0.8_nylon_coarse.inst.cfg | 2 +- .../cartesio/nylon/cartesio_0.8_nylon_extra_coarse.inst.cfg | 2 +- .../quality/cartesio/nylon/cartesio_0.8_nylon_high.inst.cfg | 2 +- .../quality/cartesio/nylon/cartesio_0.8_nylon_normal.inst.cfg | 2 +- resources/quality/cartesio/pc/cartesio_0.25_pc_high.inst.cfg | 2 +- resources/quality/cartesio/pc/cartesio_0.25_pc_normal.inst.cfg | 2 +- resources/quality/cartesio/pc/cartesio_0.4_pc_high.inst.cfg | 2 +- resources/quality/cartesio/pc/cartesio_0.4_pc_normal.inst.cfg | 2 +- resources/quality/cartesio/pc/cartesio_0.8_pc_coarse.inst.cfg | 2 +- .../quality/cartesio/pc/cartesio_0.8_pc_extra_coarse.inst.cfg | 2 +- resources/quality/cartesio/pc/cartesio_0.8_pc_high.inst.cfg | 2 +- resources/quality/cartesio/pc/cartesio_0.8_pc_normal.inst.cfg | 2 +- .../quality/cartesio/petg/cartesio_0.25_petg_high.inst.cfg | 2 +- .../quality/cartesio/petg/cartesio_0.25_petg_normal.inst.cfg | 2 +- resources/quality/cartesio/petg/cartesio_0.4_petg_high.inst.cfg | 2 +- .../quality/cartesio/petg/cartesio_0.4_petg_normal.inst.cfg | 2 +- .../quality/cartesio/petg/cartesio_0.8_petg_coarse.inst.cfg | 2 +- .../cartesio/petg/cartesio_0.8_petg_extra_coarse.inst.cfg | 2 +- resources/quality/cartesio/petg/cartesio_0.8_petg_high.inst.cfg | 2 +- .../quality/cartesio/petg/cartesio_0.8_petg_normal.inst.cfg | 2 +- resources/quality/cartesio/pla/cartesio_0.25_pla_high.inst.cfg | 2 +- .../quality/cartesio/pla/cartesio_0.25_pla_normal.inst.cfg | 2 +- resources/quality/cartesio/pla/cartesio_0.4_pla_high.inst.cfg | 2 +- resources/quality/cartesio/pla/cartesio_0.4_pla_normal.inst.cfg | 2 +- resources/quality/cartesio/pla/cartesio_0.8_pla_coarse.inst.cfg | 2 +- .../quality/cartesio/pla/cartesio_0.8_pla_extra_coarse.inst.cfg | 2 +- resources/quality/cartesio/pla/cartesio_0.8_pla_high.inst.cfg | 2 +- resources/quality/cartesio/pla/cartesio_0.8_pla_normal.inst.cfg | 2 +- resources/quality/cartesio/pva/cartesio_0.25_pva_high.inst.cfg | 2 +- .../quality/cartesio/pva/cartesio_0.25_pva_normal.inst.cfg | 2 +- resources/quality/cartesio/pva/cartesio_0.4_pva_high.inst.cfg | 2 +- resources/quality/cartesio/pva/cartesio_0.4_pva_normal.inst.cfg | 2 +- resources/quality/cartesio/pva/cartesio_0.8_pva_coarse.inst.cfg | 2 +- .../quality/cartesio/pva/cartesio_0.8_pva_extra_coarse.inst.cfg | 2 +- resources/quality/cartesio/pva/cartesio_0.8_pva_high.inst.cfg | 2 +- resources/quality/cartesio/pva/cartesio_0.8_pva_normal.inst.cfg | 2 +- resources/quality/coarse.inst.cfg | 2 +- resources/quality/dagoma/dagoma_discoeasy200_pla_fast.inst.cfg | 2 +- resources/quality/dagoma/dagoma_discoeasy200_pla_fine.inst.cfg | 2 +- .../quality/dagoma/dagoma_discoeasy200_pla_standard.inst.cfg | 2 +- resources/quality/dagoma/dagoma_global_fast.inst.cfg | 2 +- resources/quality/dagoma/dagoma_global_fine.inst.cfg | 2 +- resources/quality/dagoma/dagoma_global_standard.inst.cfg | 2 +- resources/quality/dagoma/dagoma_magis_pla_fast.inst.cfg | 2 +- resources/quality/dagoma/dagoma_magis_pla_fine.inst.cfg | 2 +- resources/quality/dagoma/dagoma_magis_pla_standard.inst.cfg | 2 +- resources/quality/dagoma/dagoma_neva_pla_fast.inst.cfg | 2 +- resources/quality/dagoma/dagoma_neva_pla_fine.inst.cfg | 2 +- resources/quality/dagoma/dagoma_neva_pla_standard.inst.cfg | 2 +- .../quality/deltacomb/deltacomb_abs_Draft_Quality.inst.cfg | 2 +- resources/quality/deltacomb/deltacomb_abs_Fast_Quality.inst.cfg | 2 +- resources/quality/deltacomb/deltacomb_abs_High_Quality.inst.cfg | 2 +- .../quality/deltacomb/deltacomb_abs_Normal_Quality.inst.cfg | 2 +- .../quality/deltacomb/deltacomb_abs_Verydraft_Quality.inst.cfg | 2 +- .../quality/deltacomb/deltacomb_global_Draft_Quality.inst.cfg | 2 +- .../quality/deltacomb/deltacomb_global_Fast_Quality.inst.cfg | 2 +- .../quality/deltacomb/deltacomb_global_High_Quality.inst.cfg | 2 +- .../quality/deltacomb/deltacomb_global_Normal_Quality.inst.cfg | 2 +- .../deltacomb/deltacomb_global_Verydraft_Quality.inst.cfg | 2 +- .../quality/deltacomb/deltacomb_pla_Draft_Quality.inst.cfg | 2 +- resources/quality/deltacomb/deltacomb_pla_Fast_Quality.inst.cfg | 2 +- resources/quality/deltacomb/deltacomb_pla_High_Quality.inst.cfg | 2 +- .../quality/deltacomb/deltacomb_pla_Normal_Quality.inst.cfg | 2 +- .../quality/deltacomb/deltacomb_pla_Verydraft_Quality.inst.cfg | 2 +- resources/quality/draft.inst.cfg | 2 +- resources/quality/extra_coarse.inst.cfg | 2 +- resources/quality/extra_fast.inst.cfg | 2 +- resources/quality/fabtotum/fabtotum_abs_fast.inst.cfg | 2 +- resources/quality/fabtotum/fabtotum_abs_high.inst.cfg | 2 +- resources/quality/fabtotum/fabtotum_abs_normal.inst.cfg | 2 +- resources/quality/fabtotum/fabtotum_nylon_fast.inst.cfg | 2 +- resources/quality/fabtotum/fabtotum_nylon_high.inst.cfg | 2 +- resources/quality/fabtotum/fabtotum_nylon_normal.inst.cfg | 2 +- resources/quality/fabtotum/fabtotum_pla_fast.inst.cfg | 2 +- resources/quality/fabtotum/fabtotum_pla_high.inst.cfg | 2 +- resources/quality/fabtotum/fabtotum_pla_normal.inst.cfg | 2 +- resources/quality/fabtotum/fabtotum_tpu_fast.inst.cfg | 2 +- resources/quality/fabtotum/fabtotum_tpu_high.inst.cfg | 2 +- resources/quality/fabtotum/fabtotum_tpu_normal.inst.cfg | 2 +- resources/quality/fast.inst.cfg | 2 +- .../quality/gmax15plus/gmax15plus_global_dual_normal.inst.cfg | 2 +- .../quality/gmax15plus/gmax15plus_global_dual_thick.inst.cfg | 2 +- .../quality/gmax15plus/gmax15plus_global_dual_thin.inst.cfg | 2 +- .../gmax15plus/gmax15plus_global_dual_very_thick.inst.cfg | 2 +- resources/quality/gmax15plus/gmax15plus_global_normal.inst.cfg | 2 +- resources/quality/gmax15plus/gmax15plus_global_thick.inst.cfg | 2 +- resources/quality/gmax15plus/gmax15plus_global_thin.inst.cfg | 2 +- .../quality/gmax15plus/gmax15plus_global_very_thick.inst.cfg | 2 +- resources/quality/high.inst.cfg | 2 +- .../quality/imade3d_jellybox/generic_petg_0.4_coarse.inst.cfg | 2 +- .../imade3d_jellybox/generic_petg_0.4_coarse_2-fans.inst.cfg | 2 +- .../quality/imade3d_jellybox/generic_petg_0.4_medium.inst.cfg | 2 +- .../imade3d_jellybox/generic_petg_0.4_medium_2-fans.inst.cfg | 2 +- .../quality/imade3d_jellybox/generic_pla_0.4_coarse.inst.cfg | 2 +- .../imade3d_jellybox/generic_pla_0.4_coarse_2-fans.inst.cfg | 2 +- .../quality/imade3d_jellybox/generic_pla_0.4_fine.inst.cfg | 2 +- .../imade3d_jellybox/generic_pla_0.4_fine_2-fans.inst.cfg | 2 +- .../quality/imade3d_jellybox/generic_pla_0.4_medium.inst.cfg | 2 +- .../imade3d_jellybox/generic_pla_0.4_medium_2-fans.inst.cfg | 2 +- .../quality/imade3d_jellybox/generic_pla_0.4_ultrafine.inst.cfg | 2 +- .../imade3d_jellybox/generic_pla_0.4_ultrafine_2-fans.inst.cfg | 2 +- .../quality/imade3d_jellybox/imade3d_jellybox_coarse.inst.cfg | 2 +- .../quality/imade3d_jellybox/imade3d_jellybox_fine.inst.cfg | 2 +- .../quality/imade3d_jellybox/imade3d_jellybox_normal.inst.cfg | 2 +- .../imade3d_jellybox/imade3d_jellybox_ultrafine.inst.cfg | 2 +- resources/quality/kemiq_q2/kemiq_q2_beta_abs_draft.inst.cfg | 2 +- .../quality/kemiq_q2/kemiq_q2_beta_abs_extra_fine.inst.cfg | 2 +- resources/quality/kemiq_q2/kemiq_q2_beta_abs_fine.inst.cfg | 2 +- resources/quality/kemiq_q2/kemiq_q2_beta_abs_low.inst.cfg | 2 +- resources/quality/kemiq_q2/kemiq_q2_beta_abs_normal.inst.cfg | 2 +- resources/quality/kemiq_q2/kemiq_q2_beta_pla_draft.inst.cfg | 2 +- .../quality/kemiq_q2/kemiq_q2_beta_pla_extra_fine.inst.cfg | 2 +- resources/quality/kemiq_q2/kemiq_q2_beta_pla_fine.inst.cfg | 2 +- resources/quality/kemiq_q2/kemiq_q2_beta_pla_low.inst.cfg | 2 +- resources/quality/kemiq_q2/kemiq_q2_beta_pla_normal.inst.cfg | 2 +- resources/quality/kemiq_q2/kemiq_q2_gama_pla_draft.inst.cfg | 2 +- .../quality/kemiq_q2/kemiq_q2_gama_pla_extra_fine.inst.cfg | 2 +- resources/quality/kemiq_q2/kemiq_q2_gama_pla_fine.inst.cfg | 2 +- resources/quality/kemiq_q2/kemiq_q2_gama_pla_low.inst.cfg | 2 +- resources/quality/kemiq_q2/kemiq_q2_gama_pla_normal.inst.cfg | 2 +- .../quality/malyan_m200/abs/malyan_m200_abs_draft.inst.cfg | 2 +- resources/quality/malyan_m200/abs/malyan_m200_abs_fast.inst.cfg | 2 +- resources/quality/malyan_m200/abs/malyan_m200_abs_high.inst.cfg | 2 +- .../quality/malyan_m200/abs/malyan_m200_abs_normal.inst.cfg | 2 +- .../quality/malyan_m200/abs/malyan_m200_abs_superdraft.inst.cfg | 2 +- .../malyan_m200/abs/malyan_m200_abs_thickerdraft.inst.cfg | 2 +- .../quality/malyan_m200/abs/malyan_m200_abs_ultra.inst.cfg | 2 +- .../quality/malyan_m200/abs/malyan_m200_abs_verydraft.inst.cfg | 2 +- .../malyan_m200/malyan_m200_global_Draft_Quality.inst.cfg | 2 +- .../malyan_m200/malyan_m200_global_Fast_Quality.inst.cfg | 2 +- .../malyan_m200/malyan_m200_global_High_Quality.inst.cfg | 2 +- .../malyan_m200/malyan_m200_global_Normal_Quality.inst.cfg | 2 +- .../malyan_m200/malyan_m200_global_SuperDraft_Quality.inst.cfg | 2 +- .../malyan_m200_global_ThickerDraft_Quality.inst.cfg | 2 +- .../malyan_m200/malyan_m200_global_Ultra_Quality.inst.cfg | 2 +- .../malyan_m200/malyan_m200_global_VeryDraft_Quality.inst.cfg | 2 +- .../quality/malyan_m200/petg/malyan_m200_petg_draft.inst.cfg | 2 +- .../quality/malyan_m200/petg/malyan_m200_petg_fast.inst.cfg | 2 +- .../quality/malyan_m200/petg/malyan_m200_petg_high.inst.cfg | 2 +- .../quality/malyan_m200/petg/malyan_m200_petg_normal.inst.cfg | 2 +- .../malyan_m200/petg/malyan_m200_petg_superdraft.inst.cfg | 2 +- .../malyan_m200/petg/malyan_m200_petg_thickerdraft.inst.cfg | 2 +- .../quality/malyan_m200/petg/malyan_m200_petg_ultra.inst.cfg | 2 +- .../malyan_m200/petg/malyan_m200_petg_verydraft.inst.cfg | 2 +- .../quality/malyan_m200/pla/malyan_m200_pla_draft.inst.cfg | 2 +- resources/quality/malyan_m200/pla/malyan_m200_pla_fast.inst.cfg | 2 +- resources/quality/malyan_m200/pla/malyan_m200_pla_high.inst.cfg | 2 +- .../quality/malyan_m200/pla/malyan_m200_pla_normal.inst.cfg | 2 +- .../quality/malyan_m200/pla/malyan_m200_pla_superdraft.inst.cfg | 2 +- .../malyan_m200/pla/malyan_m200_pla_thickerdraft.inst.cfg | 2 +- .../quality/malyan_m200/pla/malyan_m200_pla_ultra.inst.cfg | 2 +- .../quality/malyan_m200/pla/malyan_m200_pla_verydraft.inst.cfg | 2 +- .../abs/monoprice_select_mini_v2_abs_draft.inst.cfg | 2 +- .../abs/monoprice_select_mini_v2_abs_fast.inst.cfg | 2 +- .../abs/monoprice_select_mini_v2_abs_high.inst.cfg | 2 +- .../abs/monoprice_select_mini_v2_abs_normal.inst.cfg | 2 +- .../abs/monoprice_select_mini_v2_abs_superdraft.inst.cfg | 2 +- .../abs/monoprice_select_mini_v2_abs_thickerdraft.inst.cfg | 2 +- .../abs/monoprice_select_mini_v2_abs_ultra.inst.cfg | 2 +- .../abs/monoprice_select_mini_v2_abs_verydraft.inst.cfg | 2 +- .../monoprice_select_mini_v2_global_Draft_Quality.inst.cfg | 2 +- .../monoprice_select_mini_v2_global_Fast_Quality.inst.cfg | 2 +- .../monoprice_select_mini_v2_global_High_Quality.inst.cfg | 2 +- .../monoprice_select_mini_v2_global_Normal_Quality.inst.cfg | 2 +- .../monoprice_select_mini_v2_global_SuperDraft_Quality.inst.cfg | 2 +- ...onoprice_select_mini_v2_global_ThickerDraft_Quality.inst.cfg | 2 +- .../monoprice_select_mini_v2_global_Ultra_Quality.inst.cfg | 2 +- .../monoprice_select_mini_v2_global_VeryDraft_Quality.inst.cfg | 2 +- .../nylon/monoprice_select_mini_v2_nylon_draft.inst.cfg | 2 +- .../nylon/monoprice_select_mini_v2_nylon_fast.inst.cfg | 2 +- .../nylon/monoprice_select_mini_v2_nylon_high.inst.cfg | 2 +- .../nylon/monoprice_select_mini_v2_nylon_normal.inst.cfg | 2 +- .../nylon/monoprice_select_mini_v2_nylon_superdraft.inst.cfg | 2 +- .../nylon/monoprice_select_mini_v2_nylon_thickerdraft.inst.cfg | 2 +- .../nylon/monoprice_select_mini_v2_nylon_ultra.inst.cfg | 2 +- .../nylon/monoprice_select_mini_v2_nylon_verydraft.inst.cfg | 2 +- .../pc/monoprice_select_mini_v2_pc_draft.inst.cfg | 2 +- .../pc/monoprice_select_mini_v2_pc_fast.inst.cfg | 2 +- .../pc/monoprice_select_mini_v2_pc_high.inst.cfg | 2 +- .../pc/monoprice_select_mini_v2_pc_normal.inst.cfg | 2 +- .../pc/monoprice_select_mini_v2_pc_superdraft.inst.cfg | 2 +- .../pc/monoprice_select_mini_v2_pc_thickerdraft.inst.cfg | 2 +- .../pc/monoprice_select_mini_v2_pc_ultra.inst.cfg | 2 +- .../pc/monoprice_select_mini_v2_pc_verydraft.inst.cfg | 2 +- .../petg/monoprice_select_mini_v2_petg_draft.inst.cfg | 2 +- .../petg/monoprice_select_mini_v2_petg_fast.inst.cfg | 2 +- .../petg/monoprice_select_mini_v2_petg_high.inst.cfg | 2 +- .../petg/monoprice_select_mini_v2_petg_normal.inst.cfg | 2 +- .../petg/monoprice_select_mini_v2_petg_superdraft.inst.cfg | 2 +- .../petg/monoprice_select_mini_v2_petg_thickerdraft.inst.cfg | 2 +- .../petg/monoprice_select_mini_v2_petg_ultra.inst.cfg | 2 +- .../petg/monoprice_select_mini_v2_petg_verydraft.inst.cfg | 2 +- .../pla/monoprice_select_mini_v2_pla_draft.inst.cfg | 2 +- .../pla/monoprice_select_mini_v2_pla_fast.inst.cfg | 2 +- .../pla/monoprice_select_mini_v2_pla_high.inst.cfg | 2 +- .../pla/monoprice_select_mini_v2_pla_normal.inst.cfg | 2 +- .../pla/monoprice_select_mini_v2_pla_superdraft.inst.cfg | 2 +- .../pla/monoprice_select_mini_v2_pla_thickerdraft.inst.cfg | 2 +- .../pla/monoprice_select_mini_v2_pla_ultra.inst.cfg | 2 +- .../pla/monoprice_select_mini_v2_pla_verydraft.inst.cfg | 2 +- resources/quality/normal.inst.cfg | 2 +- resources/quality/peopoly_moai/peopoly_moai_coarse.inst.cfg | 2 +- resources/quality/peopoly_moai/peopoly_moai_draft.inst.cfg | 2 +- resources/quality/peopoly_moai/peopoly_moai_extra_high.inst.cfg | 2 +- resources/quality/peopoly_moai/peopoly_moai_high.inst.cfg | 2 +- resources/quality/peopoly_moai/peopoly_moai_normal.inst.cfg | 2 +- .../quality/tevo_blackwidow/tevo_blackwidow_draft.inst.cfg | 2 +- resources/quality/tevo_blackwidow/tevo_blackwidow_high.inst.cfg | 2 +- .../quality/tevo_blackwidow/tevo_blackwidow_normal.inst.cfg | 2 +- resources/quality/tizyx_k25/tizyx_k25_normal.inst.cfg | 2 +- resources/quality/ultimaker2/um2_draft.inst.cfg | 2 +- resources/quality/ultimaker2/um2_fast.inst.cfg | 2 +- resources/quality/ultimaker2/um2_high.inst.cfg | 2 +- resources/quality/ultimaker2/um2_normal.inst.cfg | 2 +- resources/quality/ultimaker2_plus/pla_0.25_normal.inst.cfg | 2 +- resources/quality/ultimaker2_plus/pla_0.4_fast.inst.cfg | 2 +- resources/quality/ultimaker2_plus/pla_0.4_high.inst.cfg | 2 +- resources/quality/ultimaker2_plus/pla_0.4_normal.inst.cfg | 2 +- resources/quality/ultimaker2_plus/pla_0.6_normal.inst.cfg | 2 +- resources/quality/ultimaker2_plus/pla_0.8_normal.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_abs_0.25_normal.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_abs_0.4_fast.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_abs_0.4_high.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_abs_0.4_normal.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_abs_0.6_normal.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_abs_0.8_normal.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_cpe_0.25_normal.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_cpe_0.4_fast.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_cpe_0.4_high.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_cpe_0.4_normal.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_cpe_0.6_normal.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_cpe_0.8_normal.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_cpep_0.4_draft.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_cpep_0.4_normal.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_cpep_0.6_draft.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_cpep_0.6_normal.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_cpep_0.8_draft.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_cpep_0.8_normal.inst.cfg | 2 +- .../quality/ultimaker2_plus/um2p_global_Coarse_Quality.inst.cfg | 2 +- .../quality/ultimaker2_plus/um2p_global_Draft_Quality.inst.cfg | 2 +- .../ultimaker2_plus/um2p_global_Extra_Coarse_Quality.inst.cfg | 2 +- .../quality/ultimaker2_plus/um2p_global_Fast_Quality.inst.cfg | 2 +- .../quality/ultimaker2_plus/um2p_global_High_Quality.inst.cfg | 2 +- .../quality/ultimaker2_plus/um2p_global_Normal_Quality.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_nylon_0.25_high.inst.cfg | 2 +- .../quality/ultimaker2_plus/um2p_nylon_0.25_normal.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_nylon_0.4_fast.inst.cfg | 2 +- .../quality/ultimaker2_plus/um2p_nylon_0.4_normal.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_nylon_0.6_fast.inst.cfg | 2 +- .../quality/ultimaker2_plus/um2p_nylon_0.6_normal.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_nylon_0.8_draft.inst.cfg | 2 +- .../quality/ultimaker2_plus/um2p_nylon_0.8_normal.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_pc_0.25_high.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_pc_0.25_normal.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_pc_0.4_fast.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_pc_0.4_normal.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_pc_0.6_fast.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_pc_0.6_normal.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_pc_0.8_draft.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_pc_0.8_normal.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_pp_0.4_fast.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_pp_0.4_normal.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_pp_0.6_draft.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_pp_0.6_fast.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_pp_0.8_draft.inst.cfg | 2 +- .../quality/ultimaker2_plus/um2p_pp_0.8_verydraft.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_tpu_0.25_high.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_tpu_0.4_normal.inst.cfg | 2 +- resources/quality/ultimaker2_plus/um2p_tpu_0.6_fast.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.25_ABS_Normal_Quality.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.25_CPE_Normal_Quality.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.25_Nylon_Normal_Quality.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.25_PC_Normal_Quality.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.25_PLA_Normal_Quality.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.25_PP_Normal_Quality.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.25_TPLA_Normal_Quality.inst.cfg | 2 +- resources/quality/ultimaker3/um3_aa0.4_ABS_Draft_Print.inst.cfg | 2 +- resources/quality/ultimaker3/um3_aa0.4_ABS_Fast_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.4_ABS_High_Quality.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.4_ABS_Normal_Quality.inst.cfg | 2 +- resources/quality/ultimaker3/um3_aa0.4_BAM_Draft_Print.inst.cfg | 2 +- resources/quality/ultimaker3/um3_aa0.4_BAM_Fast_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.4_BAM_Normal_Quality.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.4_CPEP_Draft_Print.inst.cfg | 2 +- resources/quality/ultimaker3/um3_aa0.4_CPEP_Fast_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.4_CPEP_High_Quality.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.4_CPEP_Normal_Quality.inst.cfg | 2 +- resources/quality/ultimaker3/um3_aa0.4_CPE_Draft_Print.inst.cfg | 2 +- resources/quality/ultimaker3/um3_aa0.4_CPE_Fast_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.4_CPE_High_Quality.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.4_CPE_Normal_Quality.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.4_Nylon_Draft_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.4_Nylon_Fast_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.4_Nylon_High_Quality.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.4_Nylon_Normal_Quality.inst.cfg | 2 +- resources/quality/ultimaker3/um3_aa0.4_PC_Draft_Print.inst.cfg | 2 +- resources/quality/ultimaker3/um3_aa0.4_PC_Fast_Print.inst.cfg | 2 +- resources/quality/ultimaker3/um3_aa0.4_PC_High_Quality.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.4_PC_Normal_Quality.inst.cfg | 2 +- resources/quality/ultimaker3/um3_aa0.4_PLA_Draft_Print.inst.cfg | 2 +- resources/quality/ultimaker3/um3_aa0.4_PLA_Fast_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.4_PLA_High_Quality.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.4_PLA_Normal_Quality.inst.cfg | 2 +- resources/quality/ultimaker3/um3_aa0.4_PP_Draft_Print.inst.cfg | 2 +- resources/quality/ultimaker3/um3_aa0.4_PP_Fast_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.4_PP_Normal_Quality.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.4_TPLA_Draft_Print.inst.cfg | 2 +- resources/quality/ultimaker3/um3_aa0.4_TPLA_Fast_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.4_TPLA_Normal_Quality.inst.cfg | 2 +- resources/quality/ultimaker3/um3_aa0.4_TPU_Draft_Print.inst.cfg | 2 +- resources/quality/ultimaker3/um3_aa0.4_TPU_Fast_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.4_TPU_Normal_Quality.inst.cfg | 2 +- resources/quality/ultimaker3/um3_aa0.8_ABS_Draft_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.8_ABS_Superdraft_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.8_ABS_Verydraft_Print.inst.cfg | 2 +- resources/quality/ultimaker3/um3_aa0.8_CPEP_Fast_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.8_CPEP_Superdraft_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.8_CPEP_Verydraft_Print.inst.cfg | 2 +- resources/quality/ultimaker3/um3_aa0.8_CPE_Draft_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.8_CPE_Superdraft_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.8_CPE_Verydraft_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.8_Nylon_Draft_Print.inst.cfg | 2 +- .../ultimaker3/um3_aa0.8_Nylon_Superdraft_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.8_Nylon_Verydraft_Print.inst.cfg | 2 +- resources/quality/ultimaker3/um3_aa0.8_PC_Fast_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.8_PC_Superdraft_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.8_PC_Verydraft_Print.inst.cfg | 2 +- resources/quality/ultimaker3/um3_aa0.8_PLA_Draft_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.8_PLA_Superdraft_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.8_PLA_Verydraft_Print.inst.cfg | 2 +- resources/quality/ultimaker3/um3_aa0.8_PP_Draft_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.8_PP_Superdraft_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.8_PP_Verydraft_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.8_TPLA_Draft_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.8_TPLA_Superdraft_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.8_TPLA_Verydraft_Print.inst.cfg | 2 +- resources/quality/ultimaker3/um3_aa0.8_TPU_Draft_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.8_TPU_Superdraft_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_aa0.8_TPU_Verydraft_Print.inst.cfg | 2 +- resources/quality/ultimaker3/um3_bb0.4_PVA_Draft_Print.inst.cfg | 2 +- resources/quality/ultimaker3/um3_bb0.4_PVA_Fast_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_bb0.4_PVA_High_Quality.inst.cfg | 2 +- .../quality/ultimaker3/um3_bb0.4_PVA_Normal_Quality.inst.cfg | 2 +- resources/quality/ultimaker3/um3_bb0.8_PVA_Draft_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_bb0.8_PVA_Superdraft_Print.inst.cfg | 2 +- .../quality/ultimaker3/um3_bb0.8_PVA_Verydraft_Print.inst.cfg | 2 +- resources/quality/ultimaker3/um3_global_Draft_Quality.inst.cfg | 2 +- resources/quality/ultimaker3/um3_global_Fast_Quality.inst.cfg | 2 +- resources/quality/ultimaker3/um3_global_High_Quality.inst.cfg | 2 +- resources/quality/ultimaker3/um3_global_Normal_Quality.inst.cfg | 2 +- .../quality/ultimaker3/um3_global_Superdraft_Quality.inst.cfg | 2 +- .../quality/ultimaker3/um3_global_Verydraft_Quality.inst.cfg | 2 +- .../ultimaker_original/umo_global_Coarse_Quality.inst.cfg | 2 +- .../ultimaker_original/umo_global_Draft_Quality.inst.cfg | 2 +- .../ultimaker_original/umo_global_Extra_Coarse_Quality.inst.cfg | 2 +- .../quality/ultimaker_original/umo_global_Fast_Quality.inst.cfg | 2 +- .../quality/ultimaker_original/umo_global_High_Quality.inst.cfg | 2 +- .../ultimaker_original/umo_global_Normal_Quality.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.25_ABS_Normal_Quality.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.25_CPE_Normal_Quality.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.25_Nylon_Normal_Quality.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.25_PC_Normal_Quality.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.25_PLA_Normal_Quality.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.25_PP_Normal_Quality.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.25_TPLA_Normal_Quality.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_ABS_Draft_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_ABS_Fast_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_ABS_High_Quality.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.4_ABS_Normal_Quality.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_BAM_Draft_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_BAM_Fast_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.4_BAM_Normal_Quality.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_CPEP_Draft_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_CPEP_Fast_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_CPEP_High_Quality.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.4_CPEP_Normal_Quality.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_CPE_Draft_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_CPE_Fast_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_CPE_High_Quality.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.4_CPE_Normal_Quality.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_Nylon_Draft_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_Nylon_Fast_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.4_Nylon_High_Quality.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.4_Nylon_Normal_Quality.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_PC_Draft_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_PC_Fast_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_PC_High_Quality.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_PC_Normal_Quality.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_PLA_Draft_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_PLA_Fast_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_PLA_High_Quality.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.4_PLA_Normal_Quality.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_PP_Draft_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_PP_Fast_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_PP_Normal_Quality.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_TPLA_Draft_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_TPLA_Fast_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_TPLA_High_Quality.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.4_TPLA_Normal_Quality.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_TPU_Draft_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.4_TPU_Fast_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.4_TPU_Normal_Quality.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.4_aluminum_ABS_Draft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.4_aluminum_ABS_Fast_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.4_aluminum_ABS_High_Quality.inst.cfg | 2 +- .../um_s5_aa0.4_aluminum_ABS_Normal_Quality.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Draft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Fast_Print.inst.cfg | 2 +- .../um_s5_aa0.4_aluminum_CPEP_High_Quality.inst.cfg | 2 +- .../um_s5_aa0.4_aluminum_CPEP_Normal_Quality.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Draft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Fast_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.4_aluminum_CPE_High_Quality.inst.cfg | 2 +- .../um_s5_aa0.4_aluminum_CPE_Normal_Quality.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.4_aluminum_PC_Draft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.4_aluminum_PC_Fast_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.4_aluminum_PC_High_Quality.inst.cfg | 2 +- .../um_s5_aa0.4_aluminum_PC_Normal_Quality.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.4_aluminum_PP_Draft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.4_aluminum_PP_Fast_Print.inst.cfg | 2 +- .../um_s5_aa0.4_aluminum_PP_Normal_Quality.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.8_ABS_Draft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.8_ABS_Superdraft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.8_ABS_Verydraft_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.8_CPEP_Fast_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.8_CPEP_Superdraft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.8_CPEP_Verydraft_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.8_CPE_Draft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.8_CPE_Superdraft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.8_CPE_Verydraft_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.8_Nylon_Draft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.8_Nylon_Superdraft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.8_Nylon_Verydraft_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.8_PC_Fast_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.8_PC_Superdraft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.8_PC_Verydraft_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.8_PLA_Draft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.8_PLA_Superdraft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.8_PLA_Verydraft_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.8_PP_Draft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.8_PP_Superdraft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.8_PP_Verydraft_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.8_TPLA_Draft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.8_TPLA_Superdraft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.8_TPLA_Verydraft_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_aa0.8_TPU_Draft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.8_TPU_Superdraft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.8_TPU_Verydraft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.8_aluminum_ABS_Draft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_aluminum_ABS_Superdraft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_aluminum_ABS_Verydraft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Fast_Print.inst.cfg | 2 +- .../um_s5_aa0.8_aluminum_CPEP_Superdraft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_aluminum_CPEP_Verydraft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Draft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_aluminum_CPE_Superdraft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_aluminum_CPE_Verydraft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.8_aluminum_PC_Fast_Print.inst.cfg | 2 +- .../um_s5_aa0.8_aluminum_PC_Superdraft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_aluminum_PC_Verydraft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_aa0.8_aluminum_PP_Draft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_aluminum_PP_Superdraft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_aluminum_PP_Verydraft_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_bb0.4_PVA_Draft_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_bb0.4_PVA_Fast_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_bb0.4_PVA_High_Quality.inst.cfg | 2 +- .../ultimaker_s5/um_s5_bb0.4_PVA_Normal_Quality.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_bb0.8_PVA_Draft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_bb0.8_PVA_Superdraft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_bb0.8_PVA_Verydraft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_cc0.6_CFFCPE_Draft_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_cc0.6_CFFPA_Draft_Print.inst.cfg | 2 +- .../ultimaker_s5/um_s5_cc0.6_GFFCPE_Draft_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_cc0.6_GFFPA_Draft_Print.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_global_Draft_Quality.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_global_Fast_Quality.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_global_High_Quality.inst.cfg | 2 +- .../quality/ultimaker_s5/um_s5_global_Normal_Quality.inst.cfg | 2 +- .../ultimaker_s5/um_s5_global_Superdraft_Quality.inst.cfg | 2 +- .../ultimaker_s5/um_s5_global_Verydraft_Quality.inst.cfg | 2 +- .../vertex_delta_k8800/k8800_ABS_Extreme_Quality.inst.cfg | 2 +- .../quality/vertex_delta_k8800/k8800_ABS_High_Quality.inst.cfg | 2 +- .../vertex_delta_k8800/k8800_ABS_Normal_Quality.inst.cfg | 2 +- .../vertex_delta_k8800/k8800_Global_Extreme_Quality.inst.cfg | 2 +- .../vertex_delta_k8800/k8800_Global_High_Quality.inst.cfg | 2 +- .../vertex_delta_k8800/k8800_Global_Normal_Quality.inst.cfg | 2 +- .../vertex_delta_k8800/k8800_PET_Extreme_Quality.inst.cfg | 2 +- .../quality/vertex_delta_k8800/k8800_PET_High_Quality.inst.cfg | 2 +- .../vertex_delta_k8800/k8800_PET_Normal_Quality.inst.cfg | 2 +- .../vertex_delta_k8800/k8800_PLA_Extreme_Quality.inst.cfg | 2 +- .../quality/vertex_delta_k8800/k8800_PLA_High_Quality.inst.cfg | 2 +- .../vertex_delta_k8800/k8800_PLA_Normal_Quality.inst.cfg | 2 +- .../vertex_delta_k8800/k8800_TPU_Extreme_Quality.inst.cfg | 2 +- .../quality/vertex_delta_k8800/k8800_TPU_High_Quality.inst.cfg | 2 +- .../vertex_delta_k8800/k8800_TPU_Normal_Quality.inst.cfg | 2 +- resources/quality/zyyx/zyyx_agile_global_fast.inst.cfg | 2 +- resources/quality/zyyx/zyyx_agile_global_fine.inst.cfg | 2 +- resources/quality/zyyx/zyyx_agile_global_normal.inst.cfg | 2 +- resources/quality/zyyx/zyyx_agile_pro_flex_fast.inst.cfg | 2 +- resources/quality/zyyx/zyyx_agile_pro_flex_fine.inst.cfg | 2 +- resources/quality/zyyx/zyyx_agile_pro_flex_normal.inst.cfg | 2 +- resources/quality/zyyx/zyyx_agile_pro_pla_fast.inst.cfg | 2 +- resources/quality/zyyx/zyyx_agile_pro_pla_fine.inst.cfg | 2 +- resources/quality/zyyx/zyyx_agile_pro_pla_normal.inst.cfg | 2 +- resources/variants/cartesio_0.25.inst.cfg | 2 +- resources/variants/cartesio_0.4.inst.cfg | 2 +- resources/variants/cartesio_0.8.inst.cfg | 2 +- resources/variants/fabtotum_hyb35.inst.cfg | 2 +- resources/variants/fabtotum_lite04.inst.cfg | 2 +- resources/variants/fabtotum_lite06.inst.cfg | 2 +- resources/variants/fabtotum_pro02.inst.cfg | 2 +- resources/variants/fabtotum_pro04.inst.cfg | 2 +- resources/variants/fabtotum_pro06.inst.cfg | 2 +- resources/variants/fabtotum_pro08.inst.cfg | 2 +- resources/variants/felixtec4_0.25.inst.cfg | 2 +- resources/variants/felixtec4_0.35.inst.cfg | 2 +- resources/variants/felixtec4_0.50.inst.cfg | 2 +- resources/variants/felixtec4_0.70.inst.cfg | 2 +- resources/variants/gmax15plus_025_e3d.inst.cfg | 2 +- resources/variants/gmax15plus_04_e3d.inst.cfg | 2 +- resources/variants/gmax15plus_05_e3d.inst.cfg | 2 +- resources/variants/gmax15plus_05_jhead.inst.cfg | 2 +- resources/variants/gmax15plus_06_e3d.inst.cfg | 2 +- resources/variants/gmax15plus_08_e3d.inst.cfg | 2 +- resources/variants/gmax15plus_10_jhead.inst.cfg | 2 +- resources/variants/gmax15plus_12_e3d.inst.cfg | 2 +- resources/variants/gmax15plus_dual_025_e3d.inst.cfg | 2 +- resources/variants/gmax15plus_dual_04_e3d.inst.cfg | 2 +- resources/variants/gmax15plus_dual_05_e3d.inst.cfg | 2 +- resources/variants/gmax15plus_dual_05_jhead.inst.cfg | 2 +- resources/variants/gmax15plus_dual_06_e3d.inst.cfg | 2 +- resources/variants/gmax15plus_dual_08_e3d.inst.cfg | 2 +- resources/variants/gmax15plus_dual_10_jhead.inst.cfg | 2 +- resources/variants/imade3d_jellybox_0.4.inst.cfg | 2 +- resources/variants/imade3d_jellybox_0.4_2-fans.inst.cfg | 2 +- resources/variants/tizyx_k25_0.2.inst.cfg | 2 +- resources/variants/tizyx_k25_0.3.inst.cfg | 2 +- resources/variants/tizyx_k25_0.4.inst.cfg | 2 +- resources/variants/tizyx_k25_0.5.inst.cfg | 2 +- resources/variants/tizyx_k25_0.6.inst.cfg | 2 +- resources/variants/tizyx_k25_0.8.inst.cfg | 2 +- resources/variants/tizyx_k25_1.0.inst.cfg | 2 +- resources/variants/ultimaker2_0.25.inst.cfg | 2 +- resources/variants/ultimaker2_0.4.inst.cfg | 2 +- resources/variants/ultimaker2_0.6.inst.cfg | 2 +- resources/variants/ultimaker2_0.8.inst.cfg | 2 +- resources/variants/ultimaker2_extended_0.25.inst.cfg | 2 +- resources/variants/ultimaker2_extended_0.4.inst.cfg | 2 +- resources/variants/ultimaker2_extended_0.6.inst.cfg | 2 +- resources/variants/ultimaker2_extended_0.8.inst.cfg | 2 +- resources/variants/ultimaker2_extended_plus_0.25.inst.cfg | 2 +- resources/variants/ultimaker2_extended_plus_0.4.inst.cfg | 2 +- resources/variants/ultimaker2_extended_plus_0.6.inst.cfg | 2 +- resources/variants/ultimaker2_extended_plus_0.8.inst.cfg | 2 +- resources/variants/ultimaker2_plus_0.25.inst.cfg | 2 +- resources/variants/ultimaker2_plus_0.4.inst.cfg | 2 +- resources/variants/ultimaker2_plus_0.6.inst.cfg | 2 +- resources/variants/ultimaker2_plus_0.8.inst.cfg | 2 +- resources/variants/ultimaker3_aa0.25.inst.cfg | 2 +- resources/variants/ultimaker3_aa0.8.inst.cfg | 2 +- resources/variants/ultimaker3_aa04.inst.cfg | 2 +- resources/variants/ultimaker3_bb0.8.inst.cfg | 2 +- resources/variants/ultimaker3_bb04.inst.cfg | 2 +- resources/variants/ultimaker3_extended_aa0.25.inst.cfg | 2 +- resources/variants/ultimaker3_extended_aa0.8.inst.cfg | 2 +- resources/variants/ultimaker3_extended_aa04.inst.cfg | 2 +- resources/variants/ultimaker3_extended_bb0.8.inst.cfg | 2 +- resources/variants/ultimaker3_extended_bb04.inst.cfg | 2 +- resources/variants/ultimaker_s5_aa0.25.inst.cfg | 2 +- resources/variants/ultimaker_s5_aa0.8.inst.cfg | 2 +- resources/variants/ultimaker_s5_aa04.inst.cfg | 2 +- resources/variants/ultimaker_s5_aluminum.inst.cfg | 2 +- resources/variants/ultimaker_s5_bb0.8.inst.cfg | 2 +- resources/variants/ultimaker_s5_bb04.inst.cfg | 2 +- resources/variants/ultimaker_s5_cc06.inst.cfg | 2 +- resources/variants/ultimaker_s5_glass.inst.cfg | 2 +- 648 files changed, 648 insertions(+), 648 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 4a1023f69e..032a56ae40 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -143,7 +143,7 @@ class CuraApplication(QtApplication): # SettingVersion represents the set of settings available in the machine/extruder definitions. # You need to make sure that this version number needs to be increased if there is any non-backwards-compatible # changes of the settings. - SettingVersion = 5 + SettingVersion = 6 Created = False diff --git a/resources/quality/abax_pri3/apri3_pla_fast.inst.cfg b/resources/quality/abax_pri3/apri3_pla_fast.inst.cfg index ebb506a222..1ca2674e1f 100644 --- a/resources/quality/abax_pri3/apri3_pla_fast.inst.cfg +++ b/resources/quality/abax_pri3/apri3_pla_fast.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = abax_pri3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/abax_pri3/apri3_pla_high.inst.cfg b/resources/quality/abax_pri3/apri3_pla_high.inst.cfg index 20d2c024aa..e9e8d53a03 100644 --- a/resources/quality/abax_pri3/apri3_pla_high.inst.cfg +++ b/resources/quality/abax_pri3/apri3_pla_high.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = abax_pri3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/abax_pri3/apri3_pla_normal.inst.cfg b/resources/quality/abax_pri3/apri3_pla_normal.inst.cfg index 0a4d1f1c62..40399c9548 100644 --- a/resources/quality/abax_pri3/apri3_pla_normal.inst.cfg +++ b/resources/quality/abax_pri3/apri3_pla_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = abax_pri3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/abax_pri5/apri5_pla_fast.inst.cfg b/resources/quality/abax_pri5/apri5_pla_fast.inst.cfg index f278788c68..c6d20f70da 100644 --- a/resources/quality/abax_pri5/apri5_pla_fast.inst.cfg +++ b/resources/quality/abax_pri5/apri5_pla_fast.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = abax_pri5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/abax_pri5/apri5_pla_high.inst.cfg b/resources/quality/abax_pri5/apri5_pla_high.inst.cfg index 212c92e7d1..99bd10d654 100644 --- a/resources/quality/abax_pri5/apri5_pla_high.inst.cfg +++ b/resources/quality/abax_pri5/apri5_pla_high.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = abax_pri5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/abax_pri5/apri5_pla_normal.inst.cfg b/resources/quality/abax_pri5/apri5_pla_normal.inst.cfg index 8a1338f28c..55e0a7755a 100644 --- a/resources/quality/abax_pri5/apri5_pla_normal.inst.cfg +++ b/resources/quality/abax_pri5/apri5_pla_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = abax_pri5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/abax_titan/atitan_pla_fast.inst.cfg b/resources/quality/abax_titan/atitan_pla_fast.inst.cfg index 997e77041d..6a2a49b555 100644 --- a/resources/quality/abax_titan/atitan_pla_fast.inst.cfg +++ b/resources/quality/abax_titan/atitan_pla_fast.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = abax_titan [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/abax_titan/atitan_pla_high.inst.cfg b/resources/quality/abax_titan/atitan_pla_high.inst.cfg index 73cd31f3fd..7dd5833297 100644 --- a/resources/quality/abax_titan/atitan_pla_high.inst.cfg +++ b/resources/quality/abax_titan/atitan_pla_high.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = abax_titan [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/abax_titan/atitan_pla_normal.inst.cfg b/resources/quality/abax_titan/atitan_pla_normal.inst.cfg index c356e197ce..b6aae46c6d 100644 --- a/resources/quality/abax_titan/atitan_pla_normal.inst.cfg +++ b/resources/quality/abax_titan/atitan_pla_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = abax_titan [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/anycubic_4max/abs/anycubic_4max_abs_draft.inst.cfg b/resources/quality/anycubic_4max/abs/anycubic_4max_abs_draft.inst.cfg index 9bb11389d2..e12954b6bc 100644 --- a/resources/quality/anycubic_4max/abs/anycubic_4max_abs_draft.inst.cfg +++ b/resources/quality/anycubic_4max/abs/anycubic_4max_abs_draft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/anycubic_4max/abs/anycubic_4max_abs_high.inst.cfg b/resources/quality/anycubic_4max/abs/anycubic_4max_abs_high.inst.cfg index 4a09e0ee7c..0218b87a63 100644 --- a/resources/quality/anycubic_4max/abs/anycubic_4max_abs_high.inst.cfg +++ b/resources/quality/anycubic_4max/abs/anycubic_4max_abs_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/anycubic_4max/abs/anycubic_4max_abs_normal.inst.cfg b/resources/quality/anycubic_4max/abs/anycubic_4max_abs_normal.inst.cfg index f415cb379f..f686855364 100644 --- a/resources/quality/anycubic_4max/abs/anycubic_4max_abs_normal.inst.cfg +++ b/resources/quality/anycubic_4max/abs/anycubic_4max_abs_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/anycubic_4max/anycubic_4max_draft.inst.cfg b/resources/quality/anycubic_4max/anycubic_4max_draft.inst.cfg index 56810934c7..d9dc632c16 100644 --- a/resources/quality/anycubic_4max/anycubic_4max_draft.inst.cfg +++ b/resources/quality/anycubic_4max/anycubic_4max_draft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/anycubic_4max/anycubic_4max_high.inst.cfg b/resources/quality/anycubic_4max/anycubic_4max_high.inst.cfg index 4f35669ae2..55b5833c3f 100644 --- a/resources/quality/anycubic_4max/anycubic_4max_high.inst.cfg +++ b/resources/quality/anycubic_4max/anycubic_4max_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/anycubic_4max/anycubic_4max_normal.inst.cfg b/resources/quality/anycubic_4max/anycubic_4max_normal.inst.cfg index 778764f005..45c6f19f8b 100644 --- a/resources/quality/anycubic_4max/anycubic_4max_normal.inst.cfg +++ b/resources/quality/anycubic_4max/anycubic_4max_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/anycubic_4max/hips/anycubic_4max_hips_draft.inst.cfg b/resources/quality/anycubic_4max/hips/anycubic_4max_hips_draft.inst.cfg index 59b727e7be..97c19b7083 100644 --- a/resources/quality/anycubic_4max/hips/anycubic_4max_hips_draft.inst.cfg +++ b/resources/quality/anycubic_4max/hips/anycubic_4max_hips_draft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/anycubic_4max/hips/anycubic_4max_hips_high.inst.cfg b/resources/quality/anycubic_4max/hips/anycubic_4max_hips_high.inst.cfg index 1ddfe9ee4b..cfa727bcb8 100644 --- a/resources/quality/anycubic_4max/hips/anycubic_4max_hips_high.inst.cfg +++ b/resources/quality/anycubic_4max/hips/anycubic_4max_hips_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/anycubic_4max/hips/anycubic_4max_hips_normal.inst.cfg b/resources/quality/anycubic_4max/hips/anycubic_4max_hips_normal.inst.cfg index 829392ad1f..f4372939f5 100644 --- a/resources/quality/anycubic_4max/hips/anycubic_4max_hips_normal.inst.cfg +++ b/resources/quality/anycubic_4max/hips/anycubic_4max_hips_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/anycubic_4max/petg/anycubic_4max_petg_draft.inst.cfg b/resources/quality/anycubic_4max/petg/anycubic_4max_petg_draft.inst.cfg index 028fe5d8a7..124bd487b0 100644 --- a/resources/quality/anycubic_4max/petg/anycubic_4max_petg_draft.inst.cfg +++ b/resources/quality/anycubic_4max/petg/anycubic_4max_petg_draft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/anycubic_4max/petg/anycubic_4max_petg_high.inst.cfg b/resources/quality/anycubic_4max/petg/anycubic_4max_petg_high.inst.cfg index 73eb73fe16..eeaaac6270 100644 --- a/resources/quality/anycubic_4max/petg/anycubic_4max_petg_high.inst.cfg +++ b/resources/quality/anycubic_4max/petg/anycubic_4max_petg_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/anycubic_4max/petg/anycubic_4max_petg_normal.inst.cfg b/resources/quality/anycubic_4max/petg/anycubic_4max_petg_normal.inst.cfg index 3ed9e5e707..ec0579015a 100644 --- a/resources/quality/anycubic_4max/petg/anycubic_4max_petg_normal.inst.cfg +++ b/resources/quality/anycubic_4max/petg/anycubic_4max_petg_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/anycubic_4max/pla/anycubic_4max_pla_draft.inst.cfg b/resources/quality/anycubic_4max/pla/anycubic_4max_pla_draft.inst.cfg index fef5f88f97..b72ce43e29 100644 --- a/resources/quality/anycubic_4max/pla/anycubic_4max_pla_draft.inst.cfg +++ b/resources/quality/anycubic_4max/pla/anycubic_4max_pla_draft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/anycubic_4max/pla/anycubic_4max_pla_high.inst.cfg b/resources/quality/anycubic_4max/pla/anycubic_4max_pla_high.inst.cfg index edfa07067b..4386159a66 100644 --- a/resources/quality/anycubic_4max/pla/anycubic_4max_pla_high.inst.cfg +++ b/resources/quality/anycubic_4max/pla/anycubic_4max_pla_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/anycubic_4max/pla/anycubic_4max_pla_normal.inst.cfg b/resources/quality/anycubic_4max/pla/anycubic_4max_pla_normal.inst.cfg index e90bac1a0b..9998fff2ad 100644 --- a/resources/quality/anycubic_4max/pla/anycubic_4max_pla_normal.inst.cfg +++ b/resources/quality/anycubic_4max/pla/anycubic_4max_pla_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg index b4f8ba78ed..ba99d30de6 100644 --- a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg +++ b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = anycubic_i3_mega [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg index c727764c5d..b1092b115c 100644 --- a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg +++ b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = anycubic_i3_mega [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg index beb7d98b8e..d088d650a0 100644 --- a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg +++ b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = anycubic_i3_mega [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/builder_premium/bp_BVOH_Coarse_Quality.inst.cfg b/resources/quality/builder_premium/bp_BVOH_Coarse_Quality.inst.cfg index 10c8bc553f..491d24c919 100644 --- a/resources/quality/builder_premium/bp_BVOH_Coarse_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_BVOH_Coarse_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = -1 diff --git a/resources/quality/builder_premium/bp_BVOH_High_Quality.inst.cfg b/resources/quality/builder_premium/bp_BVOH_High_Quality.inst.cfg index b09ceb8872..3ff0f07ac1 100644 --- a/resources/quality/builder_premium/bp_BVOH_High_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_BVOH_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = High Quality definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/builder_premium/bp_BVOH_Normal_Quality.inst.cfg b/resources/quality/builder_premium/bp_BVOH_Normal_Quality.inst.cfg index c2425a6d86..cf67185706 100644 --- a/resources/quality/builder_premium/bp_BVOH_Normal_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_BVOH_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/builder_premium/bp_Innoflex60_Coarse_Quality.inst.cfg b/resources/quality/builder_premium/bp_Innoflex60_Coarse_Quality.inst.cfg index 038fd02fff..aedb45a2b1 100644 --- a/resources/quality/builder_premium/bp_Innoflex60_Coarse_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_Innoflex60_Coarse_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = -1 diff --git a/resources/quality/builder_premium/bp_Innoflex60_High_Quality.inst.cfg b/resources/quality/builder_premium/bp_Innoflex60_High_Quality.inst.cfg index e9512e30f8..08fb395a36 100644 --- a/resources/quality/builder_premium/bp_Innoflex60_High_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_Innoflex60_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = High Quality definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/builder_premium/bp_Innoflex60_Normal_Quality.inst.cfg b/resources/quality/builder_premium/bp_Innoflex60_Normal_Quality.inst.cfg index fb6ec6c89f..008de91758 100644 --- a/resources/quality/builder_premium/bp_Innoflex60_Normal_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_Innoflex60_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/builder_premium/bp_PET_Coarse_Quality.inst.cfg b/resources/quality/builder_premium/bp_PET_Coarse_Quality.inst.cfg index 572e83d4b3..0e2c26812b 100644 --- a/resources/quality/builder_premium/bp_PET_Coarse_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_PET_Coarse_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = -1 diff --git a/resources/quality/builder_premium/bp_PET_High_Quality.inst.cfg b/resources/quality/builder_premium/bp_PET_High_Quality.inst.cfg index f4a45dd43a..8217ca9156 100644 --- a/resources/quality/builder_premium/bp_PET_High_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_PET_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = High Quality definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/builder_premium/bp_PET_Normal_Quality.inst.cfg b/resources/quality/builder_premium/bp_PET_Normal_Quality.inst.cfg index d927ab0851..48b2df0cae 100644 --- a/resources/quality/builder_premium/bp_PET_Normal_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_PET_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/builder_premium/bp_PLA_Coarse_Quality.inst.cfg b/resources/quality/builder_premium/bp_PLA_Coarse_Quality.inst.cfg index fc877df05d..6834cbb2c4 100644 --- a/resources/quality/builder_premium/bp_PLA_Coarse_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_PLA_Coarse_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = -1 diff --git a/resources/quality/builder_premium/bp_PLA_High_Quality.inst.cfg b/resources/quality/builder_premium/bp_PLA_High_Quality.inst.cfg index 586133a500..9fc5426cf9 100644 --- a/resources/quality/builder_premium/bp_PLA_High_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_PLA_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = High Quality definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/builder_premium/bp_PLA_Normal_Quality.inst.cfg b/resources/quality/builder_premium/bp_PLA_Normal_Quality.inst.cfg index e84b2eaa87..161bee24df 100644 --- a/resources/quality/builder_premium/bp_PLA_Normal_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_PLA_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/builder_premium/bp_PVA_Coarse_Quality.inst.cfg b/resources/quality/builder_premium/bp_PVA_Coarse_Quality.inst.cfg index f661e58b6d..149b938bac 100644 --- a/resources/quality/builder_premium/bp_PVA_Coarse_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_PVA_Coarse_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = -1 diff --git a/resources/quality/builder_premium/bp_PVA_High_Quality.inst.cfg b/resources/quality/builder_premium/bp_PVA_High_Quality.inst.cfg index 31b9c094df..ba310a0890 100644 --- a/resources/quality/builder_premium/bp_PVA_High_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_PVA_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = High Quality definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/builder_premium/bp_PVA_Normal_Quality.inst.cfg b/resources/quality/builder_premium/bp_PVA_Normal_Quality.inst.cfg index 8c3ca85124..4ab949957f 100644 --- a/resources/quality/builder_premium/bp_PVA_Normal_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_PVA_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/builder_premium/bp_global_Coarse_Quality.inst.cfg b/resources/quality/builder_premium/bp_global_Coarse_Quality.inst.cfg index a1d80774a1..9a85c924ee 100644 --- a/resources/quality/builder_premium/bp_global_Coarse_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_global_Coarse_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = -1 diff --git a/resources/quality/builder_premium/bp_global_High_Quality.inst.cfg b/resources/quality/builder_premium/bp_global_High_Quality.inst.cfg index 465ea6090a..ca2f8c843f 100644 --- a/resources/quality/builder_premium/bp_global_High_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_global_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = High Quality definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/builder_premium/bp_global_Normal_Quality.inst.cfg b/resources/quality/builder_premium/bp_global_Normal_Quality.inst.cfg index e872561b90..3b24f9963d 100644 --- a/resources/quality/builder_premium/bp_global_Normal_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_global_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/cartesio/abs/cartesio_0.25_abs_high.inst.cfg b/resources/quality/cartesio/abs/cartesio_0.25_abs_high.inst.cfg index 5626ed58de..f19635331d 100644 --- a/resources/quality/cartesio/abs/cartesio_0.25_abs_high.inst.cfg +++ b/resources/quality/cartesio/abs/cartesio_0.25_abs_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/abs/cartesio_0.25_abs_normal.inst.cfg b/resources/quality/cartesio/abs/cartesio_0.25_abs_normal.inst.cfg index f0b4581b90..14babfa93d 100644 --- a/resources/quality/cartesio/abs/cartesio_0.25_abs_normal.inst.cfg +++ b/resources/quality/cartesio/abs/cartesio_0.25_abs_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/cartesio/abs/cartesio_0.4_abs_high.inst.cfg b/resources/quality/cartesio/abs/cartesio_0.4_abs_high.inst.cfg index d4dd85b09d..99e1290c77 100644 --- a/resources/quality/cartesio/abs/cartesio_0.4_abs_high.inst.cfg +++ b/resources/quality/cartesio/abs/cartesio_0.4_abs_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/abs/cartesio_0.4_abs_normal.inst.cfg b/resources/quality/cartesio/abs/cartesio_0.4_abs_normal.inst.cfg index 5ee76e8e2b..32c306ba71 100644 --- a/resources/quality/cartesio/abs/cartesio_0.4_abs_normal.inst.cfg +++ b/resources/quality/cartesio/abs/cartesio_0.4_abs_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/cartesio/abs/cartesio_0.8_abs_coarse.inst.cfg b/resources/quality/cartesio/abs/cartesio_0.8_abs_coarse.inst.cfg index d15efb770f..5496468fd6 100644 --- a/resources/quality/cartesio/abs/cartesio_0.8_abs_coarse.inst.cfg +++ b/resources/quality/cartesio/abs/cartesio_0.8_abs_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = 3 diff --git a/resources/quality/cartesio/abs/cartesio_0.8_abs_extra_coarse.inst.cfg b/resources/quality/cartesio/abs/cartesio_0.8_abs_extra_coarse.inst.cfg index 7467da765f..360397ddc9 100644 --- a/resources/quality/cartesio/abs/cartesio_0.8_abs_extra_coarse.inst.cfg +++ b/resources/quality/cartesio/abs/cartesio_0.8_abs_extra_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Extra Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extra coarse weight = 4 diff --git a/resources/quality/cartesio/abs/cartesio_0.8_abs_high.inst.cfg b/resources/quality/cartesio/abs/cartesio_0.8_abs_high.inst.cfg index caa6b71a4a..82e0afa853 100644 --- a/resources/quality/cartesio/abs/cartesio_0.8_abs_high.inst.cfg +++ b/resources/quality/cartesio/abs/cartesio_0.8_abs_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/abs/cartesio_0.8_abs_normal.inst.cfg b/resources/quality/cartesio/abs/cartesio_0.8_abs_normal.inst.cfg index 82a980538c..20ba9b777d 100644 --- a/resources/quality/cartesio/abs/cartesio_0.8_abs_normal.inst.cfg +++ b/resources/quality/cartesio/abs/cartesio_0.8_abs_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/cartesio/arnitel/cartesio_0.4_arnitel2045_high.inst.cfg b/resources/quality/cartesio/arnitel/cartesio_0.4_arnitel2045_high.inst.cfg index 17f2acd8d1..fa89b4c3aa 100644 --- a/resources/quality/cartesio/arnitel/cartesio_0.4_arnitel2045_high.inst.cfg +++ b/resources/quality/cartesio/arnitel/cartesio_0.4_arnitel2045_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/arnitel/cartesio_0.4_arnitel2045_normal.inst.cfg b/resources/quality/cartesio/arnitel/cartesio_0.4_arnitel2045_normal.inst.cfg index 50dc4d42a0..63abbf1cc5 100644 --- a/resources/quality/cartesio/arnitel/cartesio_0.4_arnitel2045_normal.inst.cfg +++ b/resources/quality/cartesio/arnitel/cartesio_0.4_arnitel2045_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/cartesio/cartesio_global_Coarse_Quality.inst.cfg b/resources/quality/cartesio/cartesio_global_Coarse_Quality.inst.cfg index f92fc49981..7ce1111dd8 100644 --- a/resources/quality/cartesio/cartesio_global_Coarse_Quality.inst.cfg +++ b/resources/quality/cartesio/cartesio_global_Coarse_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = -3 diff --git a/resources/quality/cartesio/cartesio_global_Extra_Coarse_Quality.inst.cfg b/resources/quality/cartesio/cartesio_global_Extra_Coarse_Quality.inst.cfg index 1681e83279..0369c74e2a 100644 --- a/resources/quality/cartesio/cartesio_global_Extra_Coarse_Quality.inst.cfg +++ b/resources/quality/cartesio/cartesio_global_Extra_Coarse_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extra coarse weight = -4 diff --git a/resources/quality/cartesio/cartesio_global_High_Quality.inst.cfg b/resources/quality/cartesio/cartesio_global_High_Quality.inst.cfg index 95d1f3b37e..dafe5ef621 100644 --- a/resources/quality/cartesio/cartesio_global_High_Quality.inst.cfg +++ b/resources/quality/cartesio/cartesio_global_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/cartesio_global_Normal_Quality.inst.cfg b/resources/quality/cartesio/cartesio_global_Normal_Quality.inst.cfg index 6bb348282e..100da3c70a 100644 --- a/resources/quality/cartesio/cartesio_global_Normal_Quality.inst.cfg +++ b/resources/quality/cartesio/cartesio_global_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/cartesio/hips/cartesio_0.25_hips_high.inst.cfg b/resources/quality/cartesio/hips/cartesio_0.25_hips_high.inst.cfg index 3e212b2446..93e04e1514 100644 --- a/resources/quality/cartesio/hips/cartesio_0.25_hips_high.inst.cfg +++ b/resources/quality/cartesio/hips/cartesio_0.25_hips_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/hips/cartesio_0.25_hips_normal.inst.cfg b/resources/quality/cartesio/hips/cartesio_0.25_hips_normal.inst.cfg index 08d1cf709e..ba3f88ce13 100644 --- a/resources/quality/cartesio/hips/cartesio_0.25_hips_normal.inst.cfg +++ b/resources/quality/cartesio/hips/cartesio_0.25_hips_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/cartesio/hips/cartesio_0.4_hips_high.inst.cfg b/resources/quality/cartesio/hips/cartesio_0.4_hips_high.inst.cfg index a9664cf9d1..91b1627650 100644 --- a/resources/quality/cartesio/hips/cartesio_0.4_hips_high.inst.cfg +++ b/resources/quality/cartesio/hips/cartesio_0.4_hips_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/hips/cartesio_0.4_hips_normal.inst.cfg b/resources/quality/cartesio/hips/cartesio_0.4_hips_normal.inst.cfg index 18daa16d97..a2f817aef2 100644 --- a/resources/quality/cartesio/hips/cartesio_0.4_hips_normal.inst.cfg +++ b/resources/quality/cartesio/hips/cartesio_0.4_hips_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/cartesio/hips/cartesio_0.8_hips_coarse.inst.cfg b/resources/quality/cartesio/hips/cartesio_0.8_hips_coarse.inst.cfg index f009383ad8..6b84c5e864 100644 --- a/resources/quality/cartesio/hips/cartesio_0.8_hips_coarse.inst.cfg +++ b/resources/quality/cartesio/hips/cartesio_0.8_hips_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = 3 diff --git a/resources/quality/cartesio/hips/cartesio_0.8_hips_extra_coarse.inst.cfg b/resources/quality/cartesio/hips/cartesio_0.8_hips_extra_coarse.inst.cfg index 1adbbb0fb9..16f299813b 100644 --- a/resources/quality/cartesio/hips/cartesio_0.8_hips_extra_coarse.inst.cfg +++ b/resources/quality/cartesio/hips/cartesio_0.8_hips_extra_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Extra Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extra coarse weight = 4 diff --git a/resources/quality/cartesio/hips/cartesio_0.8_hips_high.inst.cfg b/resources/quality/cartesio/hips/cartesio_0.8_hips_high.inst.cfg index d3e6df227f..686759bc4d 100644 --- a/resources/quality/cartesio/hips/cartesio_0.8_hips_high.inst.cfg +++ b/resources/quality/cartesio/hips/cartesio_0.8_hips_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/hips/cartesio_0.8_hips_normal.inst.cfg b/resources/quality/cartesio/hips/cartesio_0.8_hips_normal.inst.cfg index 5afc358d51..654564f291 100644 --- a/resources/quality/cartesio/hips/cartesio_0.8_hips_normal.inst.cfg +++ b/resources/quality/cartesio/hips/cartesio_0.8_hips_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/cartesio/nylon/cartesio_0.25_nylon_high.inst.cfg b/resources/quality/cartesio/nylon/cartesio_0.25_nylon_high.inst.cfg index 9eb5d5c4e9..bd2dca940f 100644 --- a/resources/quality/cartesio/nylon/cartesio_0.25_nylon_high.inst.cfg +++ b/resources/quality/cartesio/nylon/cartesio_0.25_nylon_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/nylon/cartesio_0.25_nylon_normal.inst.cfg b/resources/quality/cartesio/nylon/cartesio_0.25_nylon_normal.inst.cfg index b8d17bcc8f..55d22623bb 100644 --- a/resources/quality/cartesio/nylon/cartesio_0.25_nylon_normal.inst.cfg +++ b/resources/quality/cartesio/nylon/cartesio_0.25_nylon_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/cartesio/nylon/cartesio_0.4_nylon_high.inst.cfg b/resources/quality/cartesio/nylon/cartesio_0.4_nylon_high.inst.cfg index d4b261b99f..05e9abbe8f 100644 --- a/resources/quality/cartesio/nylon/cartesio_0.4_nylon_high.inst.cfg +++ b/resources/quality/cartesio/nylon/cartesio_0.4_nylon_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/nylon/cartesio_0.4_nylon_normal.inst.cfg b/resources/quality/cartesio/nylon/cartesio_0.4_nylon_normal.inst.cfg index b208462ed7..51327c844a 100644 --- a/resources/quality/cartesio/nylon/cartesio_0.4_nylon_normal.inst.cfg +++ b/resources/quality/cartesio/nylon/cartesio_0.4_nylon_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/cartesio/nylon/cartesio_0.8_nylon_coarse.inst.cfg b/resources/quality/cartesio/nylon/cartesio_0.8_nylon_coarse.inst.cfg index 21e6d357b0..279e2d98bf 100644 --- a/resources/quality/cartesio/nylon/cartesio_0.8_nylon_coarse.inst.cfg +++ b/resources/quality/cartesio/nylon/cartesio_0.8_nylon_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = 3 diff --git a/resources/quality/cartesio/nylon/cartesio_0.8_nylon_extra_coarse.inst.cfg b/resources/quality/cartesio/nylon/cartesio_0.8_nylon_extra_coarse.inst.cfg index 15128584e1..69f9a8caad 100644 --- a/resources/quality/cartesio/nylon/cartesio_0.8_nylon_extra_coarse.inst.cfg +++ b/resources/quality/cartesio/nylon/cartesio_0.8_nylon_extra_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Extra Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extra coarse weight = 4 diff --git a/resources/quality/cartesio/nylon/cartesio_0.8_nylon_high.inst.cfg b/resources/quality/cartesio/nylon/cartesio_0.8_nylon_high.inst.cfg index 1d78bd0f1d..ff4d152ef2 100644 --- a/resources/quality/cartesio/nylon/cartesio_0.8_nylon_high.inst.cfg +++ b/resources/quality/cartesio/nylon/cartesio_0.8_nylon_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/nylon/cartesio_0.8_nylon_normal.inst.cfg b/resources/quality/cartesio/nylon/cartesio_0.8_nylon_normal.inst.cfg index 3c51389cc8..f181244796 100644 --- a/resources/quality/cartesio/nylon/cartesio_0.8_nylon_normal.inst.cfg +++ b/resources/quality/cartesio/nylon/cartesio_0.8_nylon_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/cartesio/pc/cartesio_0.25_pc_high.inst.cfg b/resources/quality/cartesio/pc/cartesio_0.25_pc_high.inst.cfg index c45194e18b..15c7f17451 100644 --- a/resources/quality/cartesio/pc/cartesio_0.25_pc_high.inst.cfg +++ b/resources/quality/cartesio/pc/cartesio_0.25_pc_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/pc/cartesio_0.25_pc_normal.inst.cfg b/resources/quality/cartesio/pc/cartesio_0.25_pc_normal.inst.cfg index cca1922fa1..91676e65f0 100644 --- a/resources/quality/cartesio/pc/cartesio_0.25_pc_normal.inst.cfg +++ b/resources/quality/cartesio/pc/cartesio_0.25_pc_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/cartesio/pc/cartesio_0.4_pc_high.inst.cfg b/resources/quality/cartesio/pc/cartesio_0.4_pc_high.inst.cfg index 8c46693c91..73e1ff6bc5 100644 --- a/resources/quality/cartesio/pc/cartesio_0.4_pc_high.inst.cfg +++ b/resources/quality/cartesio/pc/cartesio_0.4_pc_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/pc/cartesio_0.4_pc_normal.inst.cfg b/resources/quality/cartesio/pc/cartesio_0.4_pc_normal.inst.cfg index 6dc8225f82..d217756cb4 100644 --- a/resources/quality/cartesio/pc/cartesio_0.4_pc_normal.inst.cfg +++ b/resources/quality/cartesio/pc/cartesio_0.4_pc_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/cartesio/pc/cartesio_0.8_pc_coarse.inst.cfg b/resources/quality/cartesio/pc/cartesio_0.8_pc_coarse.inst.cfg index 04f01db6ba..cba868e95e 100644 --- a/resources/quality/cartesio/pc/cartesio_0.8_pc_coarse.inst.cfg +++ b/resources/quality/cartesio/pc/cartesio_0.8_pc_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = 3 diff --git a/resources/quality/cartesio/pc/cartesio_0.8_pc_extra_coarse.inst.cfg b/resources/quality/cartesio/pc/cartesio_0.8_pc_extra_coarse.inst.cfg index 53e21ec4d0..4aff24ed02 100644 --- a/resources/quality/cartesio/pc/cartesio_0.8_pc_extra_coarse.inst.cfg +++ b/resources/quality/cartesio/pc/cartesio_0.8_pc_extra_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Extra Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extra coarse weight = 4 diff --git a/resources/quality/cartesio/pc/cartesio_0.8_pc_high.inst.cfg b/resources/quality/cartesio/pc/cartesio_0.8_pc_high.inst.cfg index 0b2b9dcb26..f00e83f63e 100644 --- a/resources/quality/cartesio/pc/cartesio_0.8_pc_high.inst.cfg +++ b/resources/quality/cartesio/pc/cartesio_0.8_pc_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/pc/cartesio_0.8_pc_normal.inst.cfg b/resources/quality/cartesio/pc/cartesio_0.8_pc_normal.inst.cfg index b47f7f1ded..2f8ed80751 100644 --- a/resources/quality/cartesio/pc/cartesio_0.8_pc_normal.inst.cfg +++ b/resources/quality/cartesio/pc/cartesio_0.8_pc_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/cartesio/petg/cartesio_0.25_petg_high.inst.cfg b/resources/quality/cartesio/petg/cartesio_0.25_petg_high.inst.cfg index 9dc7adfc88..3c8dcc7af6 100644 --- a/resources/quality/cartesio/petg/cartesio_0.25_petg_high.inst.cfg +++ b/resources/quality/cartesio/petg/cartesio_0.25_petg_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/petg/cartesio_0.25_petg_normal.inst.cfg b/resources/quality/cartesio/petg/cartesio_0.25_petg_normal.inst.cfg index d9f358360d..7aedc76af9 100644 --- a/resources/quality/cartesio/petg/cartesio_0.25_petg_normal.inst.cfg +++ b/resources/quality/cartesio/petg/cartesio_0.25_petg_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/cartesio/petg/cartesio_0.4_petg_high.inst.cfg b/resources/quality/cartesio/petg/cartesio_0.4_petg_high.inst.cfg index 93388787e6..3788a4dd51 100644 --- a/resources/quality/cartesio/petg/cartesio_0.4_petg_high.inst.cfg +++ b/resources/quality/cartesio/petg/cartesio_0.4_petg_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/petg/cartesio_0.4_petg_normal.inst.cfg b/resources/quality/cartesio/petg/cartesio_0.4_petg_normal.inst.cfg index f5269d4e88..4805ca55bb 100644 --- a/resources/quality/cartesio/petg/cartesio_0.4_petg_normal.inst.cfg +++ b/resources/quality/cartesio/petg/cartesio_0.4_petg_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/cartesio/petg/cartesio_0.8_petg_coarse.inst.cfg b/resources/quality/cartesio/petg/cartesio_0.8_petg_coarse.inst.cfg index 754c55caf5..d7d6e35a3c 100644 --- a/resources/quality/cartesio/petg/cartesio_0.8_petg_coarse.inst.cfg +++ b/resources/quality/cartesio/petg/cartesio_0.8_petg_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = 3 diff --git a/resources/quality/cartesio/petg/cartesio_0.8_petg_extra_coarse.inst.cfg b/resources/quality/cartesio/petg/cartesio_0.8_petg_extra_coarse.inst.cfg index 81b1de32a2..784bd0d6af 100644 --- a/resources/quality/cartesio/petg/cartesio_0.8_petg_extra_coarse.inst.cfg +++ b/resources/quality/cartesio/petg/cartesio_0.8_petg_extra_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Extra Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extra coarse weight = 4 diff --git a/resources/quality/cartesio/petg/cartesio_0.8_petg_high.inst.cfg b/resources/quality/cartesio/petg/cartesio_0.8_petg_high.inst.cfg index 86e93c8a32..bced328a9c 100644 --- a/resources/quality/cartesio/petg/cartesio_0.8_petg_high.inst.cfg +++ b/resources/quality/cartesio/petg/cartesio_0.8_petg_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/petg/cartesio_0.8_petg_normal.inst.cfg b/resources/quality/cartesio/petg/cartesio_0.8_petg_normal.inst.cfg index ef4b5428c0..2a9da4f164 100644 --- a/resources/quality/cartesio/petg/cartesio_0.8_petg_normal.inst.cfg +++ b/resources/quality/cartesio/petg/cartesio_0.8_petg_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/cartesio/pla/cartesio_0.25_pla_high.inst.cfg b/resources/quality/cartesio/pla/cartesio_0.25_pla_high.inst.cfg index 4841f9f368..15456a0b78 100644 --- a/resources/quality/cartesio/pla/cartesio_0.25_pla_high.inst.cfg +++ b/resources/quality/cartesio/pla/cartesio_0.25_pla_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/pla/cartesio_0.25_pla_normal.inst.cfg b/resources/quality/cartesio/pla/cartesio_0.25_pla_normal.inst.cfg index 3ba36f9436..bfa792f133 100644 --- a/resources/quality/cartesio/pla/cartesio_0.25_pla_normal.inst.cfg +++ b/resources/quality/cartesio/pla/cartesio_0.25_pla_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/cartesio/pla/cartesio_0.4_pla_high.inst.cfg b/resources/quality/cartesio/pla/cartesio_0.4_pla_high.inst.cfg index 7c785a750a..e64b67b59b 100644 --- a/resources/quality/cartesio/pla/cartesio_0.4_pla_high.inst.cfg +++ b/resources/quality/cartesio/pla/cartesio_0.4_pla_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/pla/cartesio_0.4_pla_normal.inst.cfg b/resources/quality/cartesio/pla/cartesio_0.4_pla_normal.inst.cfg index b24394daf0..704a974b63 100644 --- a/resources/quality/cartesio/pla/cartesio_0.4_pla_normal.inst.cfg +++ b/resources/quality/cartesio/pla/cartesio_0.4_pla_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/cartesio/pla/cartesio_0.8_pla_coarse.inst.cfg b/resources/quality/cartesio/pla/cartesio_0.8_pla_coarse.inst.cfg index 6443f2d526..b8ff99c256 100644 --- a/resources/quality/cartesio/pla/cartesio_0.8_pla_coarse.inst.cfg +++ b/resources/quality/cartesio/pla/cartesio_0.8_pla_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = -3 diff --git a/resources/quality/cartesio/pla/cartesio_0.8_pla_extra_coarse.inst.cfg b/resources/quality/cartesio/pla/cartesio_0.8_pla_extra_coarse.inst.cfg index 51a93f76d9..da613e6e1a 100644 --- a/resources/quality/cartesio/pla/cartesio_0.8_pla_extra_coarse.inst.cfg +++ b/resources/quality/cartesio/pla/cartesio_0.8_pla_extra_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Extra Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extra coarse weight = -4 diff --git a/resources/quality/cartesio/pla/cartesio_0.8_pla_high.inst.cfg b/resources/quality/cartesio/pla/cartesio_0.8_pla_high.inst.cfg index 2f72d9d158..05c0f72686 100644 --- a/resources/quality/cartesio/pla/cartesio_0.8_pla_high.inst.cfg +++ b/resources/quality/cartesio/pla/cartesio_0.8_pla_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/pla/cartesio_0.8_pla_normal.inst.cfg b/resources/quality/cartesio/pla/cartesio_0.8_pla_normal.inst.cfg index 431f7c0fff..8434f4abc6 100644 --- a/resources/quality/cartesio/pla/cartesio_0.8_pla_normal.inst.cfg +++ b/resources/quality/cartesio/pla/cartesio_0.8_pla_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/cartesio/pva/cartesio_0.25_pva_high.inst.cfg b/resources/quality/cartesio/pva/cartesio_0.25_pva_high.inst.cfg index fe21c17e22..7df550917f 100644 --- a/resources/quality/cartesio/pva/cartesio_0.25_pva_high.inst.cfg +++ b/resources/quality/cartesio/pva/cartesio_0.25_pva_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/pva/cartesio_0.25_pva_normal.inst.cfg b/resources/quality/cartesio/pva/cartesio_0.25_pva_normal.inst.cfg index 940432e07e..1f1d9549fb 100644 --- a/resources/quality/cartesio/pva/cartesio_0.25_pva_normal.inst.cfg +++ b/resources/quality/cartesio/pva/cartesio_0.25_pva_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/cartesio/pva/cartesio_0.4_pva_high.inst.cfg b/resources/quality/cartesio/pva/cartesio_0.4_pva_high.inst.cfg index cdf8f8cae7..faf1633ce2 100644 --- a/resources/quality/cartesio/pva/cartesio_0.4_pva_high.inst.cfg +++ b/resources/quality/cartesio/pva/cartesio_0.4_pva_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/pva/cartesio_0.4_pva_normal.inst.cfg b/resources/quality/cartesio/pva/cartesio_0.4_pva_normal.inst.cfg index 9a0e279e6a..1dc24190c6 100644 --- a/resources/quality/cartesio/pva/cartesio_0.4_pva_normal.inst.cfg +++ b/resources/quality/cartesio/pva/cartesio_0.4_pva_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/cartesio/pva/cartesio_0.8_pva_coarse.inst.cfg b/resources/quality/cartesio/pva/cartesio_0.8_pva_coarse.inst.cfg index fc35e14fc6..35b2d1690d 100644 --- a/resources/quality/cartesio/pva/cartesio_0.8_pva_coarse.inst.cfg +++ b/resources/quality/cartesio/pva/cartesio_0.8_pva_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = 3 diff --git a/resources/quality/cartesio/pva/cartesio_0.8_pva_extra_coarse.inst.cfg b/resources/quality/cartesio/pva/cartesio_0.8_pva_extra_coarse.inst.cfg index 0e1b8b1241..3d41308a5d 100644 --- a/resources/quality/cartesio/pva/cartesio_0.8_pva_extra_coarse.inst.cfg +++ b/resources/quality/cartesio/pva/cartesio_0.8_pva_extra_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Extra Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extra coarse weight = 4 diff --git a/resources/quality/cartesio/pva/cartesio_0.8_pva_high.inst.cfg b/resources/quality/cartesio/pva/cartesio_0.8_pva_high.inst.cfg index 249cf6485e..0293ee9acb 100644 --- a/resources/quality/cartesio/pva/cartesio_0.8_pva_high.inst.cfg +++ b/resources/quality/cartesio/pva/cartesio_0.8_pva_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/pva/cartesio_0.8_pva_normal.inst.cfg b/resources/quality/cartesio/pva/cartesio_0.8_pva_normal.inst.cfg index 1d8cd52d0d..ae3d51a03f 100644 --- a/resources/quality/cartesio/pva/cartesio_0.8_pva_normal.inst.cfg +++ b/resources/quality/cartesio/pva/cartesio_0.8_pva_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/coarse.inst.cfg b/resources/quality/coarse.inst.cfg index e9b8156a70..77b9004aa6 100644 --- a/resources/quality/coarse.inst.cfg +++ b/resources/quality/coarse.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = fdmprinter [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = -3 diff --git a/resources/quality/dagoma/dagoma_discoeasy200_pla_fast.inst.cfg b/resources/quality/dagoma/dagoma_discoeasy200_pla_fast.inst.cfg index a302f5b513..b12a92dc1c 100644 --- a/resources/quality/dagoma/dagoma_discoeasy200_pla_fast.inst.cfg +++ b/resources/quality/dagoma/dagoma_discoeasy200_pla_fast.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = dagoma_discoeasy200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/dagoma/dagoma_discoeasy200_pla_fine.inst.cfg b/resources/quality/dagoma/dagoma_discoeasy200_pla_fine.inst.cfg index b26eb1d910..055b3e30d6 100644 --- a/resources/quality/dagoma/dagoma_discoeasy200_pla_fine.inst.cfg +++ b/resources/quality/dagoma/dagoma_discoeasy200_pla_fine.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = dagoma_discoeasy200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/dagoma/dagoma_discoeasy200_pla_standard.inst.cfg b/resources/quality/dagoma/dagoma_discoeasy200_pla_standard.inst.cfg index 9ec56f696a..7c244b3f18 100644 --- a/resources/quality/dagoma/dagoma_discoeasy200_pla_standard.inst.cfg +++ b/resources/quality/dagoma/dagoma_discoeasy200_pla_standard.inst.cfg @@ -4,7 +4,7 @@ name = Standard definition = dagoma_discoeasy200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/dagoma/dagoma_global_fast.inst.cfg b/resources/quality/dagoma/dagoma_global_fast.inst.cfg index 28569387f2..f0d312e0a7 100644 --- a/resources/quality/dagoma/dagoma_global_fast.inst.cfg +++ b/resources/quality/dagoma/dagoma_global_fast.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = dagoma_discoeasy200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/dagoma/dagoma_global_fine.inst.cfg b/resources/quality/dagoma/dagoma_global_fine.inst.cfg index 1f7d577c1b..bf0a173ec3 100644 --- a/resources/quality/dagoma/dagoma_global_fine.inst.cfg +++ b/resources/quality/dagoma/dagoma_global_fine.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = dagoma_discoeasy200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/dagoma/dagoma_global_standard.inst.cfg b/resources/quality/dagoma/dagoma_global_standard.inst.cfg index 167062c1d7..afeb925e96 100644 --- a/resources/quality/dagoma/dagoma_global_standard.inst.cfg +++ b/resources/quality/dagoma/dagoma_global_standard.inst.cfg @@ -4,7 +4,7 @@ name = Standard definition = dagoma_discoeasy200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/dagoma/dagoma_magis_pla_fast.inst.cfg b/resources/quality/dagoma/dagoma_magis_pla_fast.inst.cfg index d87c913eb6..8387ff2401 100644 --- a/resources/quality/dagoma/dagoma_magis_pla_fast.inst.cfg +++ b/resources/quality/dagoma/dagoma_magis_pla_fast.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = dagoma_magis [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/dagoma/dagoma_magis_pla_fine.inst.cfg b/resources/quality/dagoma/dagoma_magis_pla_fine.inst.cfg index d046726e0e..60c0cae7ec 100644 --- a/resources/quality/dagoma/dagoma_magis_pla_fine.inst.cfg +++ b/resources/quality/dagoma/dagoma_magis_pla_fine.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = dagoma_magis [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/dagoma/dagoma_magis_pla_standard.inst.cfg b/resources/quality/dagoma/dagoma_magis_pla_standard.inst.cfg index 39961ea93b..b406d43cb1 100644 --- a/resources/quality/dagoma/dagoma_magis_pla_standard.inst.cfg +++ b/resources/quality/dagoma/dagoma_magis_pla_standard.inst.cfg @@ -4,7 +4,7 @@ name = Standard definition = dagoma_magis [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/dagoma/dagoma_neva_pla_fast.inst.cfg b/resources/quality/dagoma/dagoma_neva_pla_fast.inst.cfg index efdf2f7d32..c0e8d7fe94 100644 --- a/resources/quality/dagoma/dagoma_neva_pla_fast.inst.cfg +++ b/resources/quality/dagoma/dagoma_neva_pla_fast.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = dagoma_neva [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/dagoma/dagoma_neva_pla_fine.inst.cfg b/resources/quality/dagoma/dagoma_neva_pla_fine.inst.cfg index 50915db112..b6d6966c81 100644 --- a/resources/quality/dagoma/dagoma_neva_pla_fine.inst.cfg +++ b/resources/quality/dagoma/dagoma_neva_pla_fine.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = dagoma_neva [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/dagoma/dagoma_neva_pla_standard.inst.cfg b/resources/quality/dagoma/dagoma_neva_pla_standard.inst.cfg index ed67800eac..eff587c908 100644 --- a/resources/quality/dagoma/dagoma_neva_pla_standard.inst.cfg +++ b/resources/quality/dagoma/dagoma_neva_pla_standard.inst.cfg @@ -4,7 +4,7 @@ name = Standard definition = dagoma_neva [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/deltacomb/deltacomb_abs_Draft_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_abs_Draft_Quality.inst.cfg index f540400575..9316a84175 100644 --- a/resources/quality/deltacomb/deltacomb_abs_Draft_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_abs_Draft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fast (beta) definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/deltacomb/deltacomb_abs_Fast_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_abs_Fast_Quality.inst.cfg index 2214813913..a2c67ff890 100644 --- a/resources/quality/deltacomb/deltacomb_abs_Fast_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_abs_Fast_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal (beta) definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/deltacomb/deltacomb_abs_High_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_abs_High_Quality.inst.cfg index d407b66762..be428bb8f4 100644 --- a/resources/quality/deltacomb/deltacomb_abs_High_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_abs_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine (beta) definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/deltacomb/deltacomb_abs_Normal_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_abs_Normal_Quality.inst.cfg index 332e1890c6..1f4184f3ad 100644 --- a/resources/quality/deltacomb/deltacomb_abs_Normal_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_abs_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine (beta) definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/deltacomb/deltacomb_abs_Verydraft_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_abs_Verydraft_Quality.inst.cfg index 674174c0bd..37925e0e4c 100644 --- a/resources/quality/deltacomb/deltacomb_abs_Verydraft_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_abs_Verydraft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast (beta) definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/deltacomb/deltacomb_global_Draft_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_global_Draft_Quality.inst.cfg index f8887810d5..072f0435f6 100755 --- a/resources/quality/deltacomb/deltacomb_global_Draft_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_global_Draft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/deltacomb/deltacomb_global_Fast_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_global_Fast_Quality.inst.cfg index 99030a084b..eef96e35b8 100755 --- a/resources/quality/deltacomb/deltacomb_global_Fast_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_global_Fast_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/deltacomb/deltacomb_global_High_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_global_High_Quality.inst.cfg index b56b222eb1..becd10ea65 100755 --- a/resources/quality/deltacomb/deltacomb_global_High_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_global_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/deltacomb/deltacomb_global_Normal_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_global_Normal_Quality.inst.cfg index a3bafadeec..4c95c74cfa 100755 --- a/resources/quality/deltacomb/deltacomb_global_Normal_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_global_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/deltacomb/deltacomb_global_Verydraft_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_global_Verydraft_Quality.inst.cfg index 84c6e66f61..0ba6ca588f 100755 --- a/resources/quality/deltacomb/deltacomb_global_Verydraft_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_global_Verydraft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/deltacomb/deltacomb_pla_Draft_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_pla_Draft_Quality.inst.cfg index c4f884486e..e0e695570d 100644 --- a/resources/quality/deltacomb/deltacomb_pla_Draft_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_pla_Draft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/deltacomb/deltacomb_pla_Fast_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_pla_Fast_Quality.inst.cfg index 714d4e3517..3f43871a8f 100644 --- a/resources/quality/deltacomb/deltacomb_pla_Fast_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_pla_Fast_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/deltacomb/deltacomb_pla_High_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_pla_High_Quality.inst.cfg index 440bac36e9..b3924431af 100644 --- a/resources/quality/deltacomb/deltacomb_pla_High_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_pla_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/deltacomb/deltacomb_pla_Normal_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_pla_Normal_Quality.inst.cfg index 58470ed650..98ec4f46cd 100644 --- a/resources/quality/deltacomb/deltacomb_pla_Normal_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_pla_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/deltacomb/deltacomb_pla_Verydraft_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_pla_Verydraft_Quality.inst.cfg index 9c00877c24..f67a2d8a26 100644 --- a/resources/quality/deltacomb/deltacomb_pla_Verydraft_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_pla_Verydraft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/draft.inst.cfg b/resources/quality/draft.inst.cfg index ac1d9ec52f..a605b9c83c 100644 --- a/resources/quality/draft.inst.cfg +++ b/resources/quality/draft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = fdmprinter [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/extra_coarse.inst.cfg b/resources/quality/extra_coarse.inst.cfg index 2a1a203d22..bf22b7ac3c 100644 --- a/resources/quality/extra_coarse.inst.cfg +++ b/resources/quality/extra_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Extra Coarse definition = fdmprinter [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extra coarse weight = -4 diff --git a/resources/quality/extra_fast.inst.cfg b/resources/quality/extra_fast.inst.cfg index da890f1653..eb1e7b3a42 100644 --- a/resources/quality/extra_fast.inst.cfg +++ b/resources/quality/extra_fast.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = fdmprinter [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/fabtotum/fabtotum_abs_fast.inst.cfg b/resources/quality/fabtotum/fabtotum_abs_fast.inst.cfg index 95e8b93b36..036d49a94f 100644 --- a/resources/quality/fabtotum/fabtotum_abs_fast.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_abs_fast.inst.cfg @@ -4,7 +4,7 @@ definition = fabtotum name = Fast Quality [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/fabtotum/fabtotum_abs_high.inst.cfg b/resources/quality/fabtotum/fabtotum_abs_high.inst.cfg index baedf0ed2b..f3d6f33952 100644 --- a/resources/quality/fabtotum/fabtotum_abs_high.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_abs_high.inst.cfg @@ -4,7 +4,7 @@ definition = fabtotum name = High Quality [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/fabtotum/fabtotum_abs_normal.inst.cfg b/resources/quality/fabtotum/fabtotum_abs_normal.inst.cfg index 58933486ee..d01599a392 100644 --- a/resources/quality/fabtotum/fabtotum_abs_normal.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_abs_normal.inst.cfg @@ -4,7 +4,7 @@ definition = fabtotum name = Normal Quality [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/fabtotum/fabtotum_nylon_fast.inst.cfg b/resources/quality/fabtotum/fabtotum_nylon_fast.inst.cfg index 00f0737227..1f0c050126 100644 --- a/resources/quality/fabtotum/fabtotum_nylon_fast.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_nylon_fast.inst.cfg @@ -4,7 +4,7 @@ name = Fast Quality definition = fabtotum [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/fabtotum/fabtotum_nylon_high.inst.cfg b/resources/quality/fabtotum/fabtotum_nylon_high.inst.cfg index bd7f32c9ba..4e234f62f8 100644 --- a/resources/quality/fabtotum/fabtotum_nylon_high.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_nylon_high.inst.cfg @@ -4,7 +4,7 @@ name = High Quality definition = fabtotum [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/fabtotum/fabtotum_nylon_normal.inst.cfg b/resources/quality/fabtotum/fabtotum_nylon_normal.inst.cfg index 6a450e7ffe..67998dea38 100644 --- a/resources/quality/fabtotum/fabtotum_nylon_normal.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_nylon_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal Quality definition = fabtotum [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/fabtotum/fabtotum_pla_fast.inst.cfg b/resources/quality/fabtotum/fabtotum_pla_fast.inst.cfg index afac0b0884..e27477e8de 100644 --- a/resources/quality/fabtotum/fabtotum_pla_fast.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_pla_fast.inst.cfg @@ -4,7 +4,7 @@ definition = fabtotum name = Fast Quality [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/fabtotum/fabtotum_pla_high.inst.cfg b/resources/quality/fabtotum/fabtotum_pla_high.inst.cfg index 89dc6d9b33..bab6d5fc82 100644 --- a/resources/quality/fabtotum/fabtotum_pla_high.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_pla_high.inst.cfg @@ -4,7 +4,7 @@ definition = fabtotum name = High Quality [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/fabtotum/fabtotum_pla_normal.inst.cfg b/resources/quality/fabtotum/fabtotum_pla_normal.inst.cfg index e5496a13d4..7bd3bc024e 100644 --- a/resources/quality/fabtotum/fabtotum_pla_normal.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_pla_normal.inst.cfg @@ -4,7 +4,7 @@ definition = fabtotum name = Normal Quality [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/fabtotum/fabtotum_tpu_fast.inst.cfg b/resources/quality/fabtotum/fabtotum_tpu_fast.inst.cfg index 7917c92514..5668d4a5b9 100644 --- a/resources/quality/fabtotum/fabtotum_tpu_fast.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_tpu_fast.inst.cfg @@ -5,7 +5,7 @@ name = Fast Quality [metadata] type = quality -setting_version = 5 +setting_version = 6 material = generic_tpu quality_type = fast weight = -1 diff --git a/resources/quality/fabtotum/fabtotum_tpu_high.inst.cfg b/resources/quality/fabtotum/fabtotum_tpu_high.inst.cfg index 1c31967d79..6af9e88f00 100644 --- a/resources/quality/fabtotum/fabtotum_tpu_high.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_tpu_high.inst.cfg @@ -5,7 +5,7 @@ name = High Quality [metadata] type = quality -setting_version = 5 +setting_version = 6 material = generic_tpu quality_type = high weight = 1 diff --git a/resources/quality/fabtotum/fabtotum_tpu_normal.inst.cfg b/resources/quality/fabtotum/fabtotum_tpu_normal.inst.cfg index 0a3821f953..13faf5bc2f 100644 --- a/resources/quality/fabtotum/fabtotum_tpu_normal.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_tpu_normal.inst.cfg @@ -5,7 +5,7 @@ name = Normal Quality [metadata] type = quality -setting_version = 5 +setting_version = 6 material = generic_tpu quality_type = normal weight = 0 diff --git a/resources/quality/fast.inst.cfg b/resources/quality/fast.inst.cfg index 7568c42e8f..94a7961320 100644 --- a/resources/quality/fast.inst.cfg +++ b/resources/quality/fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = fdmprinter [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/gmax15plus/gmax15plus_global_dual_normal.inst.cfg b/resources/quality/gmax15plus/gmax15plus_global_dual_normal.inst.cfg index 63af72a6b1..3c9c9c0dfd 100644 --- a/resources/quality/gmax15plus/gmax15plus_global_dual_normal.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_global_dual_normal.inst.cfg @@ -4,7 +4,7 @@ name = gMax 1.5+ Dual Normal Layers definition = gmax15plus_dual [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/gmax15plus/gmax15plus_global_dual_thick.inst.cfg b/resources/quality/gmax15plus/gmax15plus_global_dual_thick.inst.cfg index 94a15bfa2b..24740362af 100644 --- a/resources/quality/gmax15plus/gmax15plus_global_dual_thick.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_global_dual_thick.inst.cfg @@ -4,7 +4,7 @@ name = gMax 1.5+ Dual Thick Layers definition = gmax15plus_dual [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = course weight = -2 diff --git a/resources/quality/gmax15plus/gmax15plus_global_dual_thin.inst.cfg b/resources/quality/gmax15plus/gmax15plus_global_dual_thin.inst.cfg index 3a69778d89..845a1c2c39 100644 --- a/resources/quality/gmax15plus/gmax15plus_global_dual_thin.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_global_dual_thin.inst.cfg @@ -4,7 +4,7 @@ name = gMax 1.5+ Dual Thin Layers definition = gmax15plus_dual [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/gmax15plus/gmax15plus_global_dual_very_thick.inst.cfg b/resources/quality/gmax15plus/gmax15plus_global_dual_very_thick.inst.cfg index a35e951c32..4dbd81255a 100644 --- a/resources/quality/gmax15plus/gmax15plus_global_dual_very_thick.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_global_dual_very_thick.inst.cfg @@ -4,7 +4,7 @@ name = gMax 1.5+ Dual Very Thick Layers definition = gmax15plus_dual [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extra_course weight = -3 diff --git a/resources/quality/gmax15plus/gmax15plus_global_normal.inst.cfg b/resources/quality/gmax15plus/gmax15plus_global_normal.inst.cfg index 5d52a8149e..8b91006184 100644 --- a/resources/quality/gmax15plus/gmax15plus_global_normal.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_global_normal.inst.cfg @@ -4,7 +4,7 @@ name = gMax 1.5+ Normal Layers definition = gmax15plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/gmax15plus/gmax15plus_global_thick.inst.cfg b/resources/quality/gmax15plus/gmax15plus_global_thick.inst.cfg index 8b01d1a166..c5ef9fdf83 100644 --- a/resources/quality/gmax15plus/gmax15plus_global_thick.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_global_thick.inst.cfg @@ -4,7 +4,7 @@ name = gMax 1.5+ Thick Layers definition = gmax15plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = course weight = -2 diff --git a/resources/quality/gmax15plus/gmax15plus_global_thin.inst.cfg b/resources/quality/gmax15plus/gmax15plus_global_thin.inst.cfg index a097e9a981..b3a2ed8d82 100644 --- a/resources/quality/gmax15plus/gmax15plus_global_thin.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_global_thin.inst.cfg @@ -4,7 +4,7 @@ name = gMax 1.5+ Thin Layers definition = gmax15plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/gmax15plus/gmax15plus_global_very_thick.inst.cfg b/resources/quality/gmax15plus/gmax15plus_global_very_thick.inst.cfg index 36f78673c2..5ebcf5e12d 100644 --- a/resources/quality/gmax15plus/gmax15plus_global_very_thick.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_global_very_thick.inst.cfg @@ -4,7 +4,7 @@ name = gMax 1.5+ Very Thick Layers definition = gmax15plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extra_course weight = -3 diff --git a/resources/quality/high.inst.cfg b/resources/quality/high.inst.cfg index 1b687bf5e4..2ef1aba9e1 100644 --- a/resources/quality/high.inst.cfg +++ b/resources/quality/high.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = fdmprinter [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/imade3d_jellybox/generic_petg_0.4_coarse.inst.cfg b/resources/quality/imade3d_jellybox/generic_petg_0.4_coarse.inst.cfg index 6a50e24678..e299e675e0 100644 --- a/resources/quality/imade3d_jellybox/generic_petg_0.4_coarse.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_petg_0.4_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/imade3d_jellybox/generic_petg_0.4_coarse_2-fans.inst.cfg b/resources/quality/imade3d_jellybox/generic_petg_0.4_coarse_2-fans.inst.cfg index 2c8a43874a..7123c6440a 100644 --- a/resources/quality/imade3d_jellybox/generic_petg_0.4_coarse_2-fans.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_petg_0.4_coarse_2-fans.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/imade3d_jellybox/generic_petg_0.4_medium.inst.cfg b/resources/quality/imade3d_jellybox/generic_petg_0.4_medium.inst.cfg index 264c95c933..297531f989 100644 --- a/resources/quality/imade3d_jellybox/generic_petg_0.4_medium.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_petg_0.4_medium.inst.cfg @@ -4,7 +4,7 @@ name = Medium definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/imade3d_jellybox/generic_petg_0.4_medium_2-fans.inst.cfg b/resources/quality/imade3d_jellybox/generic_petg_0.4_medium_2-fans.inst.cfg index dfdfc2458c..d15c4a3b2d 100644 --- a/resources/quality/imade3d_jellybox/generic_petg_0.4_medium_2-fans.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_petg_0.4_medium_2-fans.inst.cfg @@ -4,7 +4,7 @@ name = Medium definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/imade3d_jellybox/generic_pla_0.4_coarse.inst.cfg b/resources/quality/imade3d_jellybox/generic_pla_0.4_coarse.inst.cfg index 68eec0c753..9ff4e6d49e 100644 --- a/resources/quality/imade3d_jellybox/generic_pla_0.4_coarse.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_pla_0.4_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/imade3d_jellybox/generic_pla_0.4_coarse_2-fans.inst.cfg b/resources/quality/imade3d_jellybox/generic_pla_0.4_coarse_2-fans.inst.cfg index 46f81d0c1a..88635bb56d 100644 --- a/resources/quality/imade3d_jellybox/generic_pla_0.4_coarse_2-fans.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_pla_0.4_coarse_2-fans.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/imade3d_jellybox/generic_pla_0.4_fine.inst.cfg b/resources/quality/imade3d_jellybox/generic_pla_0.4_fine.inst.cfg index f614b7cc3c..89dcca901c 100644 --- a/resources/quality/imade3d_jellybox/generic_pla_0.4_fine.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_pla_0.4_fine.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/imade3d_jellybox/generic_pla_0.4_fine_2-fans.inst.cfg b/resources/quality/imade3d_jellybox/generic_pla_0.4_fine_2-fans.inst.cfg index 5525899ea7..c7fd702eeb 100644 --- a/resources/quality/imade3d_jellybox/generic_pla_0.4_fine_2-fans.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_pla_0.4_fine_2-fans.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/imade3d_jellybox/generic_pla_0.4_medium.inst.cfg b/resources/quality/imade3d_jellybox/generic_pla_0.4_medium.inst.cfg index 28b4307cea..7f030b53e2 100644 --- a/resources/quality/imade3d_jellybox/generic_pla_0.4_medium.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_pla_0.4_medium.inst.cfg @@ -4,7 +4,7 @@ name = Medium definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/imade3d_jellybox/generic_pla_0.4_medium_2-fans.inst.cfg b/resources/quality/imade3d_jellybox/generic_pla_0.4_medium_2-fans.inst.cfg index 52a8594505..2759a24a8d 100644 --- a/resources/quality/imade3d_jellybox/generic_pla_0.4_medium_2-fans.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_pla_0.4_medium_2-fans.inst.cfg @@ -4,7 +4,7 @@ name = Medium definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/imade3d_jellybox/generic_pla_0.4_ultrafine.inst.cfg b/resources/quality/imade3d_jellybox/generic_pla_0.4_ultrafine.inst.cfg index bf3d409050..b74b9434b7 100644 --- a/resources/quality/imade3d_jellybox/generic_pla_0.4_ultrafine.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_pla_0.4_ultrafine.inst.cfg @@ -4,7 +4,7 @@ name = UltraFine definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = ultrahigh weight = 2 diff --git a/resources/quality/imade3d_jellybox/generic_pla_0.4_ultrafine_2-fans.inst.cfg b/resources/quality/imade3d_jellybox/generic_pla_0.4_ultrafine_2-fans.inst.cfg index 676ea84825..21df5445ab 100644 --- a/resources/quality/imade3d_jellybox/generic_pla_0.4_ultrafine_2-fans.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_pla_0.4_ultrafine_2-fans.inst.cfg @@ -4,7 +4,7 @@ name = UltraFine definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = ultrahigh weight = 2 diff --git a/resources/quality/imade3d_jellybox/imade3d_jellybox_coarse.inst.cfg b/resources/quality/imade3d_jellybox/imade3d_jellybox_coarse.inst.cfg index 819501c727..1c6ef69145 100644 --- a/resources/quality/imade3d_jellybox/imade3d_jellybox_coarse.inst.cfg +++ b/resources/quality/imade3d_jellybox/imade3d_jellybox_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/imade3d_jellybox/imade3d_jellybox_fine.inst.cfg b/resources/quality/imade3d_jellybox/imade3d_jellybox_fine.inst.cfg index 0f312aa62d..702c5182c9 100644 --- a/resources/quality/imade3d_jellybox/imade3d_jellybox_fine.inst.cfg +++ b/resources/quality/imade3d_jellybox/imade3d_jellybox_fine.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/imade3d_jellybox/imade3d_jellybox_normal.inst.cfg b/resources/quality/imade3d_jellybox/imade3d_jellybox_normal.inst.cfg index 25d0e65d74..87256a9af7 100644 --- a/resources/quality/imade3d_jellybox/imade3d_jellybox_normal.inst.cfg +++ b/resources/quality/imade3d_jellybox/imade3d_jellybox_normal.inst.cfg @@ -4,7 +4,7 @@ name = Medium definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/imade3d_jellybox/imade3d_jellybox_ultrafine.inst.cfg b/resources/quality/imade3d_jellybox/imade3d_jellybox_ultrafine.inst.cfg index ebe5ab620c..ffa7ed4550 100644 --- a/resources/quality/imade3d_jellybox/imade3d_jellybox_ultrafine.inst.cfg +++ b/resources/quality/imade3d_jellybox/imade3d_jellybox_ultrafine.inst.cfg @@ -4,7 +4,7 @@ name = UltraFine definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = ultrahigh weight = 2 diff --git a/resources/quality/kemiq_q2/kemiq_q2_beta_abs_draft.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_beta_abs_draft.inst.cfg index edba5e79ce..e4b6411ef6 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_beta_abs_draft.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_beta_abs_draft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = kemiq_q2_beta [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = -3 diff --git a/resources/quality/kemiq_q2/kemiq_q2_beta_abs_extra_fine.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_beta_abs_extra_fine.inst.cfg index 899af00b78..faeaee854d 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_beta_abs_extra_fine.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_beta_abs_extra_fine.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = kemiq_q2_beta [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/kemiq_q2/kemiq_q2_beta_abs_fine.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_beta_abs_fine.inst.cfg index 9b7ae123df..1ee9821018 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_beta_abs_fine.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_beta_abs_fine.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = kemiq_q2_beta [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/kemiq_q2/kemiq_q2_beta_abs_low.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_beta_abs_low.inst.cfg index 96888821e3..c47239ee4a 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_beta_abs_low.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_beta_abs_low.inst.cfg @@ -4,7 +4,7 @@ name = Low definition = kemiq_q2_beta [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/kemiq_q2/kemiq_q2_beta_abs_normal.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_beta_abs_normal.inst.cfg index c8f27f6a8f..8eac1515ca 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_beta_abs_normal.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_beta_abs_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = kemiq_q2_beta [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/kemiq_q2/kemiq_q2_beta_pla_draft.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_beta_pla_draft.inst.cfg index a121d921cc..e51e3f6cd8 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_beta_pla_draft.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_beta_pla_draft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = kemiq_q2_beta [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = -3 diff --git a/resources/quality/kemiq_q2/kemiq_q2_beta_pla_extra_fine.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_beta_pla_extra_fine.inst.cfg index 211da57f0c..f3d9aa9409 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_beta_pla_extra_fine.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_beta_pla_extra_fine.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = kemiq_q2_beta [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/kemiq_q2/kemiq_q2_beta_pla_fine.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_beta_pla_fine.inst.cfg index 5879a76012..92d1a98e2b 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_beta_pla_fine.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_beta_pla_fine.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = kemiq_q2_beta [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/kemiq_q2/kemiq_q2_beta_pla_low.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_beta_pla_low.inst.cfg index 0b437327d8..3fa48b32a6 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_beta_pla_low.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_beta_pla_low.inst.cfg @@ -4,7 +4,7 @@ name = Low definition = kemiq_q2_beta [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/kemiq_q2/kemiq_q2_beta_pla_normal.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_beta_pla_normal.inst.cfg index ca5ac5bd0e..277ddc7ec5 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_beta_pla_normal.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_beta_pla_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = kemiq_q2_beta [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/kemiq_q2/kemiq_q2_gama_pla_draft.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_gama_pla_draft.inst.cfg index cd6ee5356e..e73ff9b982 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_gama_pla_draft.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_gama_pla_draft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = kemiq_q2_gama [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = -3 diff --git a/resources/quality/kemiq_q2/kemiq_q2_gama_pla_extra_fine.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_gama_pla_extra_fine.inst.cfg index 345fdc2f0d..0175eb8ffa 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_gama_pla_extra_fine.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_gama_pla_extra_fine.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = kemiq_q2_gama [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/kemiq_q2/kemiq_q2_gama_pla_fine.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_gama_pla_fine.inst.cfg index bafc8f0016..415fbef684 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_gama_pla_fine.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_gama_pla_fine.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = kemiq_q2_gama [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/kemiq_q2/kemiq_q2_gama_pla_low.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_gama_pla_low.inst.cfg index c8aa72def2..94aa963911 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_gama_pla_low.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_gama_pla_low.inst.cfg @@ -4,7 +4,7 @@ name = Low definition = kemiq_q2_gama [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/kemiq_q2/kemiq_q2_gama_pla_normal.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_gama_pla_normal.inst.cfg index 62c43104e1..cc69216ecd 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_gama_pla_normal.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_gama_pla_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = kemiq_q2_gama [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/malyan_m200/abs/malyan_m200_abs_draft.inst.cfg b/resources/quality/malyan_m200/abs/malyan_m200_abs_draft.inst.cfg index 5bb967b78a..d54f0071cd 100644 --- a/resources/quality/malyan_m200/abs/malyan_m200_abs_draft.inst.cfg +++ b/resources/quality/malyan_m200/abs/malyan_m200_abs_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/malyan_m200/abs/malyan_m200_abs_fast.inst.cfg b/resources/quality/malyan_m200/abs/malyan_m200_abs_fast.inst.cfg index 30641b863a..bdd9a1ccb8 100644 --- a/resources/quality/malyan_m200/abs/malyan_m200_abs_fast.inst.cfg +++ b/resources/quality/malyan_m200/abs/malyan_m200_abs_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/malyan_m200/abs/malyan_m200_abs_high.inst.cfg b/resources/quality/malyan_m200/abs/malyan_m200_abs_high.inst.cfg index 746b579c70..7a4722d599 100644 --- a/resources/quality/malyan_m200/abs/malyan_m200_abs_high.inst.cfg +++ b/resources/quality/malyan_m200/abs/malyan_m200_abs_high.inst.cfg @@ -4,7 +4,7 @@ name = Finer definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/malyan_m200/abs/malyan_m200_abs_normal.inst.cfg b/resources/quality/malyan_m200/abs/malyan_m200_abs_normal.inst.cfg index e4ab115bc5..4cd3f34466 100644 --- a/resources/quality/malyan_m200/abs/malyan_m200_abs_normal.inst.cfg +++ b/resources/quality/malyan_m200/abs/malyan_m200_abs_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/malyan_m200/abs/malyan_m200_abs_superdraft.inst.cfg b/resources/quality/malyan_m200/abs/malyan_m200_abs_superdraft.inst.cfg index d8a7607c5c..bd349b6d72 100644 --- a/resources/quality/malyan_m200/abs/malyan_m200_abs_superdraft.inst.cfg +++ b/resources/quality/malyan_m200/abs/malyan_m200_abs_superdraft.inst.cfg @@ -4,7 +4,7 @@ name = Lowest Quality Draft definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -5 diff --git a/resources/quality/malyan_m200/abs/malyan_m200_abs_thickerdraft.inst.cfg b/resources/quality/malyan_m200/abs/malyan_m200_abs_thickerdraft.inst.cfg index 44cad5a198..a01282748b 100644 --- a/resources/quality/malyan_m200/abs/malyan_m200_abs_thickerdraft.inst.cfg +++ b/resources/quality/malyan_m200/abs/malyan_m200_abs_thickerdraft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = thickerdraft weight = -3 diff --git a/resources/quality/malyan_m200/abs/malyan_m200_abs_ultra.inst.cfg b/resources/quality/malyan_m200/abs/malyan_m200_abs_ultra.inst.cfg index 995074f330..ac82e263a7 100644 --- a/resources/quality/malyan_m200/abs/malyan_m200_abs_ultra.inst.cfg +++ b/resources/quality/malyan_m200/abs/malyan_m200_abs_ultra.inst.cfg @@ -4,7 +4,7 @@ name = Ultra Fine definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = ultra weight = 2 diff --git a/resources/quality/malyan_m200/abs/malyan_m200_abs_verydraft.inst.cfg b/resources/quality/malyan_m200/abs/malyan_m200_abs_verydraft.inst.cfg index a3d8bd4579..5e3afcb633 100644 --- a/resources/quality/malyan_m200/abs/malyan_m200_abs_verydraft.inst.cfg +++ b/resources/quality/malyan_m200/abs/malyan_m200_abs_verydraft.inst.cfg @@ -4,7 +4,7 @@ name = Low Detail Draft definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/malyan_m200/malyan_m200_global_Draft_Quality.inst.cfg b/resources/quality/malyan_m200/malyan_m200_global_Draft_Quality.inst.cfg index e53aa86a94..aa1f30d0b3 100644 --- a/resources/quality/malyan_m200/malyan_m200_global_Draft_Quality.inst.cfg +++ b/resources/quality/malyan_m200/malyan_m200_global_Draft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/malyan_m200/malyan_m200_global_Fast_Quality.inst.cfg b/resources/quality/malyan_m200/malyan_m200_global_Fast_Quality.inst.cfg index 4379d9fdcb..8cbbdac2e4 100644 --- a/resources/quality/malyan_m200/malyan_m200_global_Fast_Quality.inst.cfg +++ b/resources/quality/malyan_m200/malyan_m200_global_Fast_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/malyan_m200/malyan_m200_global_High_Quality.inst.cfg b/resources/quality/malyan_m200/malyan_m200_global_High_Quality.inst.cfg index a9a5720e14..a56a5771d8 100644 --- a/resources/quality/malyan_m200/malyan_m200_global_High_Quality.inst.cfg +++ b/resources/quality/malyan_m200/malyan_m200_global_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Finer definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/malyan_m200/malyan_m200_global_Normal_Quality.inst.cfg b/resources/quality/malyan_m200/malyan_m200_global_Normal_Quality.inst.cfg index 96b982e86a..f24faf98f0 100644 --- a/resources/quality/malyan_m200/malyan_m200_global_Normal_Quality.inst.cfg +++ b/resources/quality/malyan_m200/malyan_m200_global_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/malyan_m200/malyan_m200_global_SuperDraft_Quality.inst.cfg b/resources/quality/malyan_m200/malyan_m200_global_SuperDraft_Quality.inst.cfg index 0550efd603..ff0d97b1cc 100644 --- a/resources/quality/malyan_m200/malyan_m200_global_SuperDraft_Quality.inst.cfg +++ b/resources/quality/malyan_m200/malyan_m200_global_SuperDraft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Lowest Quality Draft definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -5 diff --git a/resources/quality/malyan_m200/malyan_m200_global_ThickerDraft_Quality.inst.cfg b/resources/quality/malyan_m200/malyan_m200_global_ThickerDraft_Quality.inst.cfg index 9a0454fc75..623b48acff 100644 --- a/resources/quality/malyan_m200/malyan_m200_global_ThickerDraft_Quality.inst.cfg +++ b/resources/quality/malyan_m200/malyan_m200_global_ThickerDraft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = thickerdraft weight = -3 diff --git a/resources/quality/malyan_m200/malyan_m200_global_Ultra_Quality.inst.cfg b/resources/quality/malyan_m200/malyan_m200_global_Ultra_Quality.inst.cfg index 76eaa3463f..ec04f9055b 100644 --- a/resources/quality/malyan_m200/malyan_m200_global_Ultra_Quality.inst.cfg +++ b/resources/quality/malyan_m200/malyan_m200_global_Ultra_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Ultra Fine definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = ultra weight = 2 diff --git a/resources/quality/malyan_m200/malyan_m200_global_VeryDraft_Quality.inst.cfg b/resources/quality/malyan_m200/malyan_m200_global_VeryDraft_Quality.inst.cfg index a3013a68fb..85b5d0df6b 100644 --- a/resources/quality/malyan_m200/malyan_m200_global_VeryDraft_Quality.inst.cfg +++ b/resources/quality/malyan_m200/malyan_m200_global_VeryDraft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Low Detail Draft definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/malyan_m200/petg/malyan_m200_petg_draft.inst.cfg b/resources/quality/malyan_m200/petg/malyan_m200_petg_draft.inst.cfg index 96144c193f..6f36402470 100644 --- a/resources/quality/malyan_m200/petg/malyan_m200_petg_draft.inst.cfg +++ b/resources/quality/malyan_m200/petg/malyan_m200_petg_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/malyan_m200/petg/malyan_m200_petg_fast.inst.cfg b/resources/quality/malyan_m200/petg/malyan_m200_petg_fast.inst.cfg index f390034a1f..ff14d6a96e 100644 --- a/resources/quality/malyan_m200/petg/malyan_m200_petg_fast.inst.cfg +++ b/resources/quality/malyan_m200/petg/malyan_m200_petg_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/malyan_m200/petg/malyan_m200_petg_high.inst.cfg b/resources/quality/malyan_m200/petg/malyan_m200_petg_high.inst.cfg index 693c6efc08..74ab347def 100644 --- a/resources/quality/malyan_m200/petg/malyan_m200_petg_high.inst.cfg +++ b/resources/quality/malyan_m200/petg/malyan_m200_petg_high.inst.cfg @@ -4,7 +4,7 @@ name = Finer definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/malyan_m200/petg/malyan_m200_petg_normal.inst.cfg b/resources/quality/malyan_m200/petg/malyan_m200_petg_normal.inst.cfg index 09c5c2b14d..0862f96460 100644 --- a/resources/quality/malyan_m200/petg/malyan_m200_petg_normal.inst.cfg +++ b/resources/quality/malyan_m200/petg/malyan_m200_petg_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/malyan_m200/petg/malyan_m200_petg_superdraft.inst.cfg b/resources/quality/malyan_m200/petg/malyan_m200_petg_superdraft.inst.cfg index 0faec6b357..df50ebcc42 100644 --- a/resources/quality/malyan_m200/petg/malyan_m200_petg_superdraft.inst.cfg +++ b/resources/quality/malyan_m200/petg/malyan_m200_petg_superdraft.inst.cfg @@ -4,7 +4,7 @@ name = Lowest Quality Draft definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -5 diff --git a/resources/quality/malyan_m200/petg/malyan_m200_petg_thickerdraft.inst.cfg b/resources/quality/malyan_m200/petg/malyan_m200_petg_thickerdraft.inst.cfg index 988082f783..3697573e0e 100644 --- a/resources/quality/malyan_m200/petg/malyan_m200_petg_thickerdraft.inst.cfg +++ b/resources/quality/malyan_m200/petg/malyan_m200_petg_thickerdraft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = thickerdraft weight = -3 diff --git a/resources/quality/malyan_m200/petg/malyan_m200_petg_ultra.inst.cfg b/resources/quality/malyan_m200/petg/malyan_m200_petg_ultra.inst.cfg index 4eb84672e7..c9858b63c7 100644 --- a/resources/quality/malyan_m200/petg/malyan_m200_petg_ultra.inst.cfg +++ b/resources/quality/malyan_m200/petg/malyan_m200_petg_ultra.inst.cfg @@ -4,7 +4,7 @@ name = Ultra Fine definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = ultra weight = 2 diff --git a/resources/quality/malyan_m200/petg/malyan_m200_petg_verydraft.inst.cfg b/resources/quality/malyan_m200/petg/malyan_m200_petg_verydraft.inst.cfg index e0305105af..b88d57ab76 100644 --- a/resources/quality/malyan_m200/petg/malyan_m200_petg_verydraft.inst.cfg +++ b/resources/quality/malyan_m200/petg/malyan_m200_petg_verydraft.inst.cfg @@ -4,7 +4,7 @@ name = Low Detail Draft definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/malyan_m200/pla/malyan_m200_pla_draft.inst.cfg b/resources/quality/malyan_m200/pla/malyan_m200_pla_draft.inst.cfg index 9a26d1b2a1..7a3af8285a 100644 --- a/resources/quality/malyan_m200/pla/malyan_m200_pla_draft.inst.cfg +++ b/resources/quality/malyan_m200/pla/malyan_m200_pla_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/malyan_m200/pla/malyan_m200_pla_fast.inst.cfg b/resources/quality/malyan_m200/pla/malyan_m200_pla_fast.inst.cfg index ef4d002ce3..f813e062dd 100644 --- a/resources/quality/malyan_m200/pla/malyan_m200_pla_fast.inst.cfg +++ b/resources/quality/malyan_m200/pla/malyan_m200_pla_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/malyan_m200/pla/malyan_m200_pla_high.inst.cfg b/resources/quality/malyan_m200/pla/malyan_m200_pla_high.inst.cfg index 6ff347a7cc..5fe854ddab 100644 --- a/resources/quality/malyan_m200/pla/malyan_m200_pla_high.inst.cfg +++ b/resources/quality/malyan_m200/pla/malyan_m200_pla_high.inst.cfg @@ -4,7 +4,7 @@ name = Finer definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/malyan_m200/pla/malyan_m200_pla_normal.inst.cfg b/resources/quality/malyan_m200/pla/malyan_m200_pla_normal.inst.cfg index b6aa938b94..cce3508550 100644 --- a/resources/quality/malyan_m200/pla/malyan_m200_pla_normal.inst.cfg +++ b/resources/quality/malyan_m200/pla/malyan_m200_pla_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/malyan_m200/pla/malyan_m200_pla_superdraft.inst.cfg b/resources/quality/malyan_m200/pla/malyan_m200_pla_superdraft.inst.cfg index 8a4b63691f..477256a933 100644 --- a/resources/quality/malyan_m200/pla/malyan_m200_pla_superdraft.inst.cfg +++ b/resources/quality/malyan_m200/pla/malyan_m200_pla_superdraft.inst.cfg @@ -4,7 +4,7 @@ name = Lowest Quality Draft definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -5 diff --git a/resources/quality/malyan_m200/pla/malyan_m200_pla_thickerdraft.inst.cfg b/resources/quality/malyan_m200/pla/malyan_m200_pla_thickerdraft.inst.cfg index 98a8363c85..1841b91d3d 100644 --- a/resources/quality/malyan_m200/pla/malyan_m200_pla_thickerdraft.inst.cfg +++ b/resources/quality/malyan_m200/pla/malyan_m200_pla_thickerdraft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = thickerdraft weight = -3 diff --git a/resources/quality/malyan_m200/pla/malyan_m200_pla_ultra.inst.cfg b/resources/quality/malyan_m200/pla/malyan_m200_pla_ultra.inst.cfg index 5a334bd9c1..24717cda19 100644 --- a/resources/quality/malyan_m200/pla/malyan_m200_pla_ultra.inst.cfg +++ b/resources/quality/malyan_m200/pla/malyan_m200_pla_ultra.inst.cfg @@ -4,7 +4,7 @@ name = Ultra Fine definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = ultra weight = 2 diff --git a/resources/quality/malyan_m200/pla/malyan_m200_pla_verydraft.inst.cfg b/resources/quality/malyan_m200/pla/malyan_m200_pla_verydraft.inst.cfg index a79d253e49..9e638d67c5 100644 --- a/resources/quality/malyan_m200/pla/malyan_m200_pla_verydraft.inst.cfg +++ b/resources/quality/malyan_m200/pla/malyan_m200_pla_verydraft.inst.cfg @@ -4,7 +4,7 @@ name = Low Detail Draft definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_draft.inst.cfg b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_draft.inst.cfg index ef579a567e..e8959a1235 100644 --- a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_draft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_fast.inst.cfg b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_fast.inst.cfg index 33296bf677..256eea1976 100644 --- a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_fast.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_high.inst.cfg b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_high.inst.cfg index e282847f14..6d0250121f 100644 --- a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_high.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_high.inst.cfg @@ -4,7 +4,7 @@ name = Finer definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_normal.inst.cfg b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_normal.inst.cfg index 2979f574a4..88c12a38f0 100644 --- a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_normal.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_superdraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_superdraft.inst.cfg index 2d4d3c7461..2eacd312f4 100644 --- a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_superdraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_superdraft.inst.cfg @@ -4,7 +4,7 @@ name = Lowest Quality Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -5 diff --git a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_thickerdraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_thickerdraft.inst.cfg index ed43dec3ae..4b311bcfed 100644 --- a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_thickerdraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_thickerdraft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = thickerdraft weight = -3 diff --git a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_ultra.inst.cfg b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_ultra.inst.cfg index eb556fe862..e9857b76b3 100644 --- a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_ultra.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_ultra.inst.cfg @@ -4,7 +4,7 @@ name = Ultra Fine definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = thickerdraft weight = 2 diff --git a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_verydraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_verydraft.inst.cfg index 21d1215dca..aa29220d34 100644 --- a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_verydraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_verydraft.inst.cfg @@ -4,7 +4,7 @@ name = Low Detail Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Draft_Quality.inst.cfg b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Draft_Quality.inst.cfg index 1d81d7535e..add2590cce 100644 --- a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Draft_Quality.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Draft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Fast_Quality.inst.cfg b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Fast_Quality.inst.cfg index 6a5a041244..2feddbd32a 100644 --- a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Fast_Quality.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Fast_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_High_Quality.inst.cfg b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_High_Quality.inst.cfg index 65925a1ea4..263bd8544c 100644 --- a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_High_Quality.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Finer definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Normal_Quality.inst.cfg b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Normal_Quality.inst.cfg index 0ac150c117..52e2ec79f3 100644 --- a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Normal_Quality.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_SuperDraft_Quality.inst.cfg b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_SuperDraft_Quality.inst.cfg index b02910cf12..dfbd2d4d3d 100644 --- a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_SuperDraft_Quality.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_SuperDraft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Lowest Quality Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -5 diff --git a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_ThickerDraft_Quality.inst.cfg b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_ThickerDraft_Quality.inst.cfg index 46434555ad..f05617c4f0 100644 --- a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_ThickerDraft_Quality.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_ThickerDraft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = thickerdraft weight = -3 diff --git a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Ultra_Quality.inst.cfg b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Ultra_Quality.inst.cfg index dfb4942b29..e6d835d683 100644 --- a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Ultra_Quality.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Ultra_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Ultra Fine definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = ultra weight = 2 diff --git a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_VeryDraft_Quality.inst.cfg b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_VeryDraft_Quality.inst.cfg index 36aa5a9a37..f44b085be1 100644 --- a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_VeryDraft_Quality.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_VeryDraft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Low Detail Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_draft.inst.cfg b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_draft.inst.cfg index 4d7d1cd848..c7701b56d3 100644 --- a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_draft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_fast.inst.cfg b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_fast.inst.cfg index 1a18df9651..dbe3b03148 100644 --- a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_fast.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_high.inst.cfg b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_high.inst.cfg index ac0ca29caa..4612b19638 100644 --- a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_high.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_high.inst.cfg @@ -4,7 +4,7 @@ name = Finer definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_normal.inst.cfg b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_normal.inst.cfg index 32c5e33f0d..7b887afb8d 100644 --- a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_normal.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_superdraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_superdraft.inst.cfg index 26f260f861..c696c71fd5 100644 --- a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_superdraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_superdraft.inst.cfg @@ -4,7 +4,7 @@ name = Lowest Quality Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -5 diff --git a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_thickerdraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_thickerdraft.inst.cfg index b5bcc7d455..c561bcda04 100644 --- a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_thickerdraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_thickerdraft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = thickerdraft weight = -3 diff --git a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_ultra.inst.cfg b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_ultra.inst.cfg index 6fc41e34b6..e16e044708 100644 --- a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_ultra.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_ultra.inst.cfg @@ -4,7 +4,7 @@ name = Ultra Fine definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = ultra weight = 2 diff --git a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_verydraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_verydraft.inst.cfg index 40f6a9f53d..9bf829c778 100644 --- a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_verydraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_verydraft.inst.cfg @@ -4,7 +4,7 @@ name = Low Detail Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_draft.inst.cfg b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_draft.inst.cfg index 76b210eba1..6cc27cd3e8 100644 --- a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_draft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_fast.inst.cfg b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_fast.inst.cfg index 53a099f42d..95ef667be9 100644 --- a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_fast.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_high.inst.cfg b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_high.inst.cfg index 1a6193fbaf..c874fc5c42 100644 --- a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_high.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_high.inst.cfg @@ -4,7 +4,7 @@ name = Finer definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_normal.inst.cfg b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_normal.inst.cfg index 72e94d1828..62fb4046aa 100644 --- a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_normal.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_superdraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_superdraft.inst.cfg index 13914d07e7..e1c88805e7 100644 --- a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_superdraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_superdraft.inst.cfg @@ -4,7 +4,7 @@ name = Lowest Quality Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -5 diff --git a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_thickerdraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_thickerdraft.inst.cfg index d02a793b25..0cec8831bf 100644 --- a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_thickerdraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_thickerdraft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = thickerdraft weight = -3 diff --git a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_ultra.inst.cfg b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_ultra.inst.cfg index 41b3821727..6aaf8680f5 100644 --- a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_ultra.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_ultra.inst.cfg @@ -4,7 +4,7 @@ name = Ultra Fine definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = ultra weight = 2 diff --git a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_verydraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_verydraft.inst.cfg index 5202565894..7a63b896c3 100644 --- a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_verydraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_verydraft.inst.cfg @@ -4,7 +4,7 @@ name = Low Detail Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_draft.inst.cfg b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_draft.inst.cfg index c2c8889127..4e31a46fbd 100644 --- a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_draft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_fast.inst.cfg b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_fast.inst.cfg index 881311d395..e0d7d393c5 100644 --- a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_fast.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_high.inst.cfg b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_high.inst.cfg index f9f1e169cb..64451b0788 100644 --- a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_high.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_high.inst.cfg @@ -4,7 +4,7 @@ name = Finer definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_normal.inst.cfg b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_normal.inst.cfg index 026279f012..1f5b6c956c 100644 --- a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_normal.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_superdraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_superdraft.inst.cfg index 3ff71bf416..9b269ee95b 100644 --- a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_superdraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_superdraft.inst.cfg @@ -4,7 +4,7 @@ name = Lowest Quality Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -5 diff --git a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_thickerdraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_thickerdraft.inst.cfg index 5524ec59ea..e180b50a89 100644 --- a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_thickerdraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_thickerdraft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = thickerdraft weight = -3 diff --git a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_ultra.inst.cfg b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_ultra.inst.cfg index 99d1da45c7..fe77254c56 100644 --- a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_ultra.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_ultra.inst.cfg @@ -4,7 +4,7 @@ name = Ultra Fine definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = ultra weight = 2 diff --git a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_verydraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_verydraft.inst.cfg index a9f891692a..9505b2b15b 100644 --- a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_verydraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_verydraft.inst.cfg @@ -4,7 +4,7 @@ name = Low Detail Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_draft.inst.cfg b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_draft.inst.cfg index a64020a89e..8333cd10ac 100644 --- a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_draft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_fast.inst.cfg b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_fast.inst.cfg index 994d6ff0fd..ab83dacbe8 100644 --- a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_fast.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = 0 diff --git a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_high.inst.cfg b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_high.inst.cfg index f8458ce63b..13f31a614e 100644 --- a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_high.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_high.inst.cfg @@ -4,7 +4,7 @@ name = Finer definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_normal.inst.cfg b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_normal.inst.cfg index afbc03d5af..57fb63fdea 100644 --- a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_normal.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_superdraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_superdraft.inst.cfg index ec30cfe2b9..77c3a18b54 100644 --- a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_superdraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_superdraft.inst.cfg @@ -4,7 +4,7 @@ name = Lowest Quality Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -5 diff --git a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_thickerdraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_thickerdraft.inst.cfg index 916285385d..52b871217b 100644 --- a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_thickerdraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_thickerdraft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = thickerdraft weight = -3 diff --git a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_ultra.inst.cfg b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_ultra.inst.cfg index 5d627b6d35..36ef492905 100644 --- a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_ultra.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_ultra.inst.cfg @@ -4,7 +4,7 @@ name = Ultra Fine definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = ultra weight = 2 diff --git a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_verydraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_verydraft.inst.cfg index 9f22fb3692..e1d636b2b1 100644 --- a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_verydraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_verydraft.inst.cfg @@ -4,7 +4,7 @@ name = Low Detail Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality material = generic_pla weight = 0 diff --git a/resources/quality/normal.inst.cfg b/resources/quality/normal.inst.cfg index f32c87bc60..02b9ebf621 100644 --- a/resources/quality/normal.inst.cfg +++ b/resources/quality/normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = fdmprinter [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/peopoly_moai/peopoly_moai_coarse.inst.cfg b/resources/quality/peopoly_moai/peopoly_moai_coarse.inst.cfg index ac4f9ee81d..9ca6cec759 100644 --- a/resources/quality/peopoly_moai/peopoly_moai_coarse.inst.cfg +++ b/resources/quality/peopoly_moai/peopoly_moai_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = peopoly_moai [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = 3 diff --git a/resources/quality/peopoly_moai/peopoly_moai_draft.inst.cfg b/resources/quality/peopoly_moai/peopoly_moai_draft.inst.cfg index 53abc477ae..72953e5305 100644 --- a/resources/quality/peopoly_moai/peopoly_moai_draft.inst.cfg +++ b/resources/quality/peopoly_moai/peopoly_moai_draft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = peopoly_moai [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/peopoly_moai/peopoly_moai_extra_high.inst.cfg b/resources/quality/peopoly_moai/peopoly_moai_extra_high.inst.cfg index 796c2cff3c..30d41aa7f8 100644 --- a/resources/quality/peopoly_moai/peopoly_moai_extra_high.inst.cfg +++ b/resources/quality/peopoly_moai/peopoly_moai_extra_high.inst.cfg @@ -4,7 +4,7 @@ name = Extra High definition = peopoly_moai [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extra_high weight = 0 diff --git a/resources/quality/peopoly_moai/peopoly_moai_high.inst.cfg b/resources/quality/peopoly_moai/peopoly_moai_high.inst.cfg index b36163d9f1..37442ed20c 100644 --- a/resources/quality/peopoly_moai/peopoly_moai_high.inst.cfg +++ b/resources/quality/peopoly_moai/peopoly_moai_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = peopoly_moai [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/peopoly_moai/peopoly_moai_normal.inst.cfg b/resources/quality/peopoly_moai/peopoly_moai_normal.inst.cfg index 89d0aa2980..33bb5673ae 100644 --- a/resources/quality/peopoly_moai/peopoly_moai_normal.inst.cfg +++ b/resources/quality/peopoly_moai/peopoly_moai_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = peopoly_moai [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/tevo_blackwidow/tevo_blackwidow_draft.inst.cfg b/resources/quality/tevo_blackwidow/tevo_blackwidow_draft.inst.cfg index be83533e0b..13f12ef643 100644 --- a/resources/quality/tevo_blackwidow/tevo_blackwidow_draft.inst.cfg +++ b/resources/quality/tevo_blackwidow/tevo_blackwidow_draft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = tevo_blackwidow [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/tevo_blackwidow/tevo_blackwidow_high.inst.cfg b/resources/quality/tevo_blackwidow/tevo_blackwidow_high.inst.cfg index 5ca8a6e4ef..0f0ccf64c6 100644 --- a/resources/quality/tevo_blackwidow/tevo_blackwidow_high.inst.cfg +++ b/resources/quality/tevo_blackwidow/tevo_blackwidow_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = tevo_blackwidow [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/tevo_blackwidow/tevo_blackwidow_normal.inst.cfg b/resources/quality/tevo_blackwidow/tevo_blackwidow_normal.inst.cfg index f542952fab..ebd5997027 100644 --- a/resources/quality/tevo_blackwidow/tevo_blackwidow_normal.inst.cfg +++ b/resources/quality/tevo_blackwidow/tevo_blackwidow_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = tevo_blackwidow [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/tizyx_k25/tizyx_k25_normal.inst.cfg b/resources/quality/tizyx_k25/tizyx_k25_normal.inst.cfg index 8b066f139f..f259ad1b34 100644 --- a/resources/quality/tizyx_k25/tizyx_k25_normal.inst.cfg +++ b/resources/quality/tizyx_k25/tizyx_k25_normal.inst.cfg @@ -5,7 +5,7 @@ definition = tizyx_k25 [metadata] quality_type = normal -setting_version = 5 +setting_version = 6 type = quality global_quality = True diff --git a/resources/quality/ultimaker2/um2_draft.inst.cfg b/resources/quality/ultimaker2/um2_draft.inst.cfg index 8c34d2c09d..7d38ba6bf4 100644 --- a/resources/quality/ultimaker2/um2_draft.inst.cfg +++ b/resources/quality/ultimaker2/um2_draft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = ultimaker2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker2/um2_fast.inst.cfg b/resources/quality/ultimaker2/um2_fast.inst.cfg index 084ed05f92..39cd4da4ef 100644 --- a/resources/quality/ultimaker2/um2_fast.inst.cfg +++ b/resources/quality/ultimaker2/um2_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker2/um2_high.inst.cfg b/resources/quality/ultimaker2/um2_high.inst.cfg index 83bb6bb972..142440d53e 100644 --- a/resources/quality/ultimaker2/um2_high.inst.cfg +++ b/resources/quality/ultimaker2/um2_high.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker2/um2_normal.inst.cfg b/resources/quality/ultimaker2/um2_normal.inst.cfg index febee8581f..b5a87f07f3 100644 --- a/resources/quality/ultimaker2/um2_normal.inst.cfg +++ b/resources/quality/ultimaker2/um2_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/pla_0.25_normal.inst.cfg b/resources/quality/ultimaker2_plus/pla_0.25_normal.inst.cfg index ff830ae660..d27a136765 100644 --- a/resources/quality/ultimaker2_plus/pla_0.25_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/pla_0.25_normal.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker2_plus/pla_0.4_fast.inst.cfg b/resources/quality/ultimaker2_plus/pla_0.4_fast.inst.cfg index 94ea62a7ca..5bc62e5fd0 100644 --- a/resources/quality/ultimaker2_plus/pla_0.4_fast.inst.cfg +++ b/resources/quality/ultimaker2_plus/pla_0.4_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker2_plus/pla_0.4_high.inst.cfg b/resources/quality/ultimaker2_plus/pla_0.4_high.inst.cfg index 03de437ee2..2847ab8c15 100644 --- a/resources/quality/ultimaker2_plus/pla_0.4_high.inst.cfg +++ b/resources/quality/ultimaker2_plus/pla_0.4_high.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker2_plus/pla_0.4_normal.inst.cfg b/resources/quality/ultimaker2_plus/pla_0.4_normal.inst.cfg index 836d866eab..009fcf7877 100644 --- a/resources/quality/ultimaker2_plus/pla_0.4_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/pla_0.4_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/pla_0.6_normal.inst.cfg b/resources/quality/ultimaker2_plus/pla_0.6_normal.inst.cfg index de55623c0f..7c492f2ec0 100644 --- a/resources/quality/ultimaker2_plus/pla_0.6_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/pla_0.6_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/pla_0.8_normal.inst.cfg b/resources/quality/ultimaker2_plus/pla_0.8_normal.inst.cfg index c96260d52f..1b6cee0e1d 100644 --- a/resources/quality/ultimaker2_plus/pla_0.8_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/pla_0.8_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker2_plus/um2p_abs_0.25_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_abs_0.25_normal.inst.cfg index 886daf58e2..f55567f986 100644 --- a/resources/quality/ultimaker2_plus/um2p_abs_0.25_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_abs_0.25_normal.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker2_plus/um2p_abs_0.4_fast.inst.cfg b/resources/quality/ultimaker2_plus/um2p_abs_0.4_fast.inst.cfg index b727acc510..63904632dc 100644 --- a/resources/quality/ultimaker2_plus/um2p_abs_0.4_fast.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_abs_0.4_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker2_plus/um2p_abs_0.4_high.inst.cfg b/resources/quality/ultimaker2_plus/um2p_abs_0.4_high.inst.cfg index 20e217315b..f76efeb7b2 100644 --- a/resources/quality/ultimaker2_plus/um2p_abs_0.4_high.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_abs_0.4_high.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker2_plus/um2p_abs_0.4_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_abs_0.4_normal.inst.cfg index 853a87c751..fd7dfc86a7 100644 --- a/resources/quality/ultimaker2_plus/um2p_abs_0.4_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_abs_0.4_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_abs_0.6_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_abs_0.6_normal.inst.cfg index 6d3ef94b9d..aba3222412 100644 --- a/resources/quality/ultimaker2_plus/um2p_abs_0.6_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_abs_0.6_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_abs_0.8_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_abs_0.8_normal.inst.cfg index 7b39ce966a..23618e2e1d 100644 --- a/resources/quality/ultimaker2_plus/um2p_abs_0.8_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_abs_0.8_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker2_plus/um2p_cpe_0.25_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpe_0.25_normal.inst.cfg index 8f225271d6..db184abe54 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpe_0.25_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpe_0.25_normal.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker2_plus/um2p_cpe_0.4_fast.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpe_0.4_fast.inst.cfg index 1e181e23d1..2dc076b012 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpe_0.4_fast.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpe_0.4_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker2_plus/um2p_cpe_0.4_high.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpe_0.4_high.inst.cfg index 9ffb8a05bb..300d5242e2 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpe_0.4_high.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpe_0.4_high.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker2_plus/um2p_cpe_0.4_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpe_0.4_normal.inst.cfg index ee7101f2ec..17a9f1a579 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpe_0.4_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpe_0.4_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_cpe_0.6_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpe_0.6_normal.inst.cfg index d2de84eae6..6807a1a86a 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpe_0.6_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpe_0.6_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_cpe_0.8_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpe_0.8_normal.inst.cfg index 581dc0368d..7a15ed53ec 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpe_0.8_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpe_0.8_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker2_plus/um2p_cpep_0.4_draft.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpep_0.4_draft.inst.cfg index 7549c0081d..cc68478dd8 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpep_0.4_draft.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpep_0.4_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker2_plus/um2p_cpep_0.4_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpep_0.4_normal.inst.cfg index b1e5552562..0d0e0e2043 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpep_0.4_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpep_0.4_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_cpep_0.6_draft.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpep_0.6_draft.inst.cfg index 616f13f110..e08a7fc357 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpep_0.6_draft.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpep_0.6_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker2_plus/um2p_cpep_0.6_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpep_0.6_normal.inst.cfg index d28dc76af8..7c8589f09e 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpep_0.6_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpep_0.6_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_cpep_0.8_draft.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpep_0.8_draft.inst.cfg index 1c4fa746ad..1c42d28128 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpep_0.8_draft.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpep_0.8_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker2_plus/um2p_cpep_0.8_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpep_0.8_normal.inst.cfg index e40d6efc58..072081e092 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpep_0.8_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpep_0.8_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_global_Coarse_Quality.inst.cfg b/resources/quality/ultimaker2_plus/um2p_global_Coarse_Quality.inst.cfg index a10cb4030c..2a4fe17a24 100644 --- a/resources/quality/ultimaker2_plus/um2p_global_Coarse_Quality.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_global_Coarse_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Coarse Quality definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = -4 diff --git a/resources/quality/ultimaker2_plus/um2p_global_Draft_Quality.inst.cfg b/resources/quality/ultimaker2_plus/um2p_global_Draft_Quality.inst.cfg index 5645bbee0b..bdf79827b2 100644 --- a/resources/quality/ultimaker2_plus/um2p_global_Draft_Quality.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_global_Draft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Draft Quality definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker2_plus/um2p_global_Extra_Coarse_Quality.inst.cfg b/resources/quality/ultimaker2_plus/um2p_global_Extra_Coarse_Quality.inst.cfg index d9afc804ba..e5338c0e34 100644 --- a/resources/quality/ultimaker2_plus/um2p_global_Extra_Coarse_Quality.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_global_Extra_Coarse_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Coarse Quality definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extra coarse weight = -3 diff --git a/resources/quality/ultimaker2_plus/um2p_global_Fast_Quality.inst.cfg b/resources/quality/ultimaker2_plus/um2p_global_Fast_Quality.inst.cfg index 7fd6d54c87..453def0668 100644 --- a/resources/quality/ultimaker2_plus/um2p_global_Fast_Quality.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_global_Fast_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker2_plus/um2p_global_High_Quality.inst.cfg b/resources/quality/ultimaker2_plus/um2p_global_High_Quality.inst.cfg index c47efcfb7d..20b9e7ba16 100644 --- a/resources/quality/ultimaker2_plus/um2p_global_High_Quality.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_global_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker2_plus/um2p_global_Normal_Quality.inst.cfg b/resources/quality/ultimaker2_plus/um2p_global_Normal_Quality.inst.cfg index 76c7b8163c..c901687f22 100644 --- a/resources/quality/ultimaker2_plus/um2p_global_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_global_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_nylon_0.25_high.inst.cfg b/resources/quality/ultimaker2_plus/um2p_nylon_0.25_high.inst.cfg index 2cab693c74..512f9e499b 100644 --- a/resources/quality/ultimaker2_plus/um2p_nylon_0.25_high.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_nylon_0.25_high.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker2_plus/um2p_nylon_0.25_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_nylon_0.25_normal.inst.cfg index f61a29a35a..ce607f7a83 100644 --- a/resources/quality/ultimaker2_plus/um2p_nylon_0.25_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_nylon_0.25_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_nylon_0.4_fast.inst.cfg b/resources/quality/ultimaker2_plus/um2p_nylon_0.4_fast.inst.cfg index 341dc7422f..7b06d5205c 100644 --- a/resources/quality/ultimaker2_plus/um2p_nylon_0.4_fast.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_nylon_0.4_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker2_plus/um2p_nylon_0.4_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_nylon_0.4_normal.inst.cfg index 63bc156e15..b65f694e11 100644 --- a/resources/quality/ultimaker2_plus/um2p_nylon_0.4_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_nylon_0.4_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_nylon_0.6_fast.inst.cfg b/resources/quality/ultimaker2_plus/um2p_nylon_0.6_fast.inst.cfg index 8aea23fb50..6caa1d2e82 100644 --- a/resources/quality/ultimaker2_plus/um2p_nylon_0.6_fast.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_nylon_0.6_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker2_plus/um2p_nylon_0.6_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_nylon_0.6_normal.inst.cfg index 28ccd6ffcf..3dad97297f 100644 --- a/resources/quality/ultimaker2_plus/um2p_nylon_0.6_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_nylon_0.6_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_nylon_0.8_draft.inst.cfg b/resources/quality/ultimaker2_plus/um2p_nylon_0.8_draft.inst.cfg index f868313ba9..fe5749aeff 100644 --- a/resources/quality/ultimaker2_plus/um2p_nylon_0.8_draft.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_nylon_0.8_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker2_plus/um2p_nylon_0.8_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_nylon_0.8_normal.inst.cfg index c30d849553..3e16229ad1 100644 --- a/resources/quality/ultimaker2_plus/um2p_nylon_0.8_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_nylon_0.8_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_pc_0.25_high.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pc_0.25_high.inst.cfg index 08b60eeb20..0195a6170f 100644 --- a/resources/quality/ultimaker2_plus/um2p_pc_0.25_high.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pc_0.25_high.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker2_plus/um2p_pc_0.25_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pc_0.25_normal.inst.cfg index dbc36f0c25..fe15f050c7 100644 --- a/resources/quality/ultimaker2_plus/um2p_pc_0.25_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pc_0.25_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_pc_0.4_fast.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pc_0.4_fast.inst.cfg index 18f299b64d..1268e4f5a5 100644 --- a/resources/quality/ultimaker2_plus/um2p_pc_0.4_fast.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pc_0.4_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker2_plus/um2p_pc_0.4_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pc_0.4_normal.inst.cfg index 9ebb46c4d3..31a55131bd 100644 --- a/resources/quality/ultimaker2_plus/um2p_pc_0.4_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pc_0.4_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_pc_0.6_fast.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pc_0.6_fast.inst.cfg index 47f84fe790..a4b0e7865e 100644 --- a/resources/quality/ultimaker2_plus/um2p_pc_0.6_fast.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pc_0.6_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker2_plus/um2p_pc_0.6_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pc_0.6_normal.inst.cfg index 2c857435c5..9fef7be2f8 100644 --- a/resources/quality/ultimaker2_plus/um2p_pc_0.6_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pc_0.6_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_pc_0.8_draft.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pc_0.8_draft.inst.cfg index 6450d9fe56..cfbd0795ab 100644 --- a/resources/quality/ultimaker2_plus/um2p_pc_0.8_draft.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pc_0.8_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker2_plus/um2p_pc_0.8_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pc_0.8_normal.inst.cfg index 91c990712e..fd24f29c40 100644 --- a/resources/quality/ultimaker2_plus/um2p_pc_0.8_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pc_0.8_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_pp_0.4_fast.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pp_0.4_fast.inst.cfg index 4266bcd46b..8e320bef25 100644 --- a/resources/quality/ultimaker2_plus/um2p_pp_0.4_fast.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pp_0.4_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker2_plus/um2p_pp_0.4_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pp_0.4_normal.inst.cfg index b995c92922..94d7a22965 100644 --- a/resources/quality/ultimaker2_plus/um2p_pp_0.4_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pp_0.4_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_pp_0.6_draft.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pp_0.6_draft.inst.cfg index a9e4917fa2..aee8437e39 100644 --- a/resources/quality/ultimaker2_plus/um2p_pp_0.6_draft.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pp_0.6_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker2_plus/um2p_pp_0.6_fast.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pp_0.6_fast.inst.cfg index 2fec539e2f..f1e709495b 100644 --- a/resources/quality/ultimaker2_plus/um2p_pp_0.6_fast.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pp_0.6_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker2_plus/um2p_pp_0.8_draft.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pp_0.8_draft.inst.cfg index f0da3e678d..0e84d2c2e4 100644 --- a/resources/quality/ultimaker2_plus/um2p_pp_0.8_draft.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pp_0.8_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker2_plus/um2p_pp_0.8_verydraft.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pp_0.8_verydraft.inst.cfg index 586eb04dd8..905c147e41 100644 --- a/resources/quality/ultimaker2_plus/um2p_pp_0.8_verydraft.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pp_0.8_verydraft.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker2_plus/um2p_tpu_0.25_high.inst.cfg b/resources/quality/ultimaker2_plus/um2p_tpu_0.25_high.inst.cfg index e0c016abee..47bf078c2f 100644 --- a/resources/quality/ultimaker2_plus/um2p_tpu_0.25_high.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_tpu_0.25_high.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker2_plus/um2p_tpu_0.4_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_tpu_0.4_normal.inst.cfg index 127f281913..d8543f1f8a 100644 --- a/resources/quality/ultimaker2_plus/um2p_tpu_0.4_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_tpu_0.4_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_tpu_0.6_fast.inst.cfg b/resources/quality/ultimaker2_plus/um2p_tpu_0.6_fast.inst.cfg index c39ea9cec3..1983878a79 100644 --- a/resources/quality/ultimaker2_plus/um2p_tpu_0.6_fast.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_tpu_0.6_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker3/um3_aa0.25_ABS_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.25_ABS_Normal_Quality.inst.cfg index 5139a1fea8..dda9fd3891 100644 --- a/resources/quality/ultimaker3/um3_aa0.25_ABS_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.25_ABS_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.25_CPE_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.25_CPE_Normal_Quality.inst.cfg index 4e81b4f39e..922366d716 100644 --- a/resources/quality/ultimaker3/um3_aa0.25_CPE_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.25_CPE_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.25_Nylon_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.25_Nylon_Normal_Quality.inst.cfg index 04dc2ec79b..3535bc4cba 100644 --- a/resources/quality/ultimaker3/um3_aa0.25_Nylon_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.25_Nylon_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.25_PC_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.25_PC_Normal_Quality.inst.cfg index 4d585b54c1..f24c53aaee 100644 --- a/resources/quality/ultimaker3/um3_aa0.25_PC_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.25_PC_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine - Experimental definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.25_PLA_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.25_PLA_Normal_Quality.inst.cfg index 8dbca3cd05..a59c404754 100644 --- a/resources/quality/ultimaker3/um3_aa0.25_PLA_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.25_PLA_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.25_PP_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.25_PP_Normal_Quality.inst.cfg index bee345e302..6f9734511b 100644 --- a/resources/quality/ultimaker3/um3_aa0.25_PP_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.25_PP_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine - Experimental definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.25_TPLA_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.25_TPLA_Normal_Quality.inst.cfg index 768864bfef..af9bea762a 100644 --- a/resources/quality/ultimaker3/um3_aa0.25_TPLA_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.25_TPLA_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.4_ABS_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_ABS_Draft_Print.inst.cfg index 8877912a33..a6ba0a666b 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_ABS_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_ABS_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.4_ABS_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_ABS_Fast_Print.inst.cfg index 926cfd6995..4bd04e36af 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_ABS_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_ABS_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_ABS_High_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_ABS_High_Quality.inst.cfg index 4e79728945..6ef2fa3cb4 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_ABS_High_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_ABS_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_ABS_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_ABS_Normal_Quality.inst.cfg index 3bded3b97c..e0862a137c 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_ABS_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_ABS_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.4_BAM_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_BAM_Draft_Print.inst.cfg index df7f0fdf02..3de7252b86 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_BAM_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_BAM_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.4_BAM_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_BAM_Fast_Print.inst.cfg index cf330dc984..28bce4eb15 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_BAM_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_BAM_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_BAM_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_BAM_Normal_Quality.inst.cfg index 705c9c4105..0f15931e59 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_BAM_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_BAM_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.4_CPEP_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_CPEP_Draft_Print.inst.cfg index 4e94789a6b..c27fc027fb 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_CPEP_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_CPEP_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.4_CPEP_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_CPEP_Fast_Print.inst.cfg index d93915d721..6a035522f1 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_CPEP_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_CPEP_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_CPEP_High_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_CPEP_High_Quality.inst.cfg index 082152c50f..3387430d26 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_CPEP_High_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_CPEP_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_CPEP_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_CPEP_Normal_Quality.inst.cfg index 889b94e001..8d0283a20f 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_CPEP_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_CPEP_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.4_CPE_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_CPE_Draft_Print.inst.cfg index 1891a274c8..a8d5c26315 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_CPE_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_CPE_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.4_CPE_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_CPE_Fast_Print.inst.cfg index e4cfdb67fc..e4b4e2e21b 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_CPE_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_CPE_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_CPE_High_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_CPE_High_Quality.inst.cfg index cec4b950cf..3964608aec 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_CPE_High_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_CPE_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_CPE_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_CPE_Normal_Quality.inst.cfg index 892083b264..b0e7234c90 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_CPE_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_CPE_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.4_Nylon_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_Nylon_Draft_Print.inst.cfg index 2e4b8f8dcc..5bfa29fd61 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_Nylon_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_Nylon_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.4_Nylon_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_Nylon_Fast_Print.inst.cfg index 9b271c47cd..eaec1cdbdb 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_Nylon_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_Nylon_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_Nylon_High_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_Nylon_High_Quality.inst.cfg index 16c0b6febb..7a0a04b6c2 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_Nylon_High_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_Nylon_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_Nylon_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_Nylon_Normal_Quality.inst.cfg index 17661efbb8..8b18ee20df 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_Nylon_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_Nylon_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.4_PC_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PC_Draft_Print.inst.cfg index 96acf403a5..fafb839f17 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_PC_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_PC_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.4_PC_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PC_Fast_Print.inst.cfg index f9159b5fca..57527f928c 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_PC_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_PC_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_PC_High_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PC_High_Quality.inst.cfg index d175e99ad6..37e31c5863 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_PC_High_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_PC_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_PC_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PC_Normal_Quality.inst.cfg index 557a449022..0961b3ccd6 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_PC_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_PC_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.4_PLA_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PLA_Draft_Print.inst.cfg index 16626dc544..c24c2cacdf 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_PLA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_PLA_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.4_PLA_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PLA_Fast_Print.inst.cfg index 7f50a2a6f0..d20a06a94a 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_PLA_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_PLA_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_PLA_High_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PLA_High_Quality.inst.cfg index 507afc5526..bd365c56ae 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_PLA_High_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_PLA_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_PLA_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PLA_Normal_Quality.inst.cfg index 05febab06d..a291357e58 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_PLA_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_PLA_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.4_PP_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PP_Draft_Print.inst.cfg index 4efa5199cb..6dbb444454 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_PP_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_PP_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.4_PP_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PP_Fast_Print.inst.cfg index ad03df5d86..60c304794b 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_PP_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_PP_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_PP_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PP_Normal_Quality.inst.cfg index d8d51dd716..92d53d6295 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_PP_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_PP_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.4_TPLA_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_TPLA_Draft_Print.inst.cfg index 9959a39457..6d8c6690a2 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_TPLA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_TPLA_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.4_TPLA_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_TPLA_Fast_Print.inst.cfg index 5c68557e9b..c8fbe827e3 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_TPLA_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_TPLA_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_TPLA_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_TPLA_Normal_Quality.inst.cfg index 90556ea487..d1a1bc994c 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_TPLA_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_TPLA_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.4_TPU_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_TPU_Draft_Print.inst.cfg index 9b9dca3a16..13bc7c0b7e 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_TPU_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_TPU_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.4_TPU_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_TPU_Fast_Print.inst.cfg index e6233a8184..cb3692a84f 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_TPU_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_TPU_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_TPU_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_TPU_Normal_Quality.inst.cfg index e725615854..a5264ce6ec 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_TPU_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_TPU_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.8_ABS_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_ABS_Draft_Print.inst.cfg index e71ea07531..474b24a462 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_ABS_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_ABS_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.8_ABS_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_ABS_Superdraft_Print.inst.cfg index 39aa103631..e9c4f9e3e9 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_ABS_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_ABS_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker3/um3_aa0.8_ABS_Verydraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_ABS_Verydraft_Print.inst.cfg index 3a08643086..213298653a 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_ABS_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_ABS_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker3/um3_aa0.8_CPEP_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_CPEP_Fast_Print.inst.cfg index fc4acf3cb0..bb2aa2d6ef 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_CPEP_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_CPEP_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast - Experimental definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.8_CPEP_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_CPEP_Superdraft_Print.inst.cfg index 36b3ef603f..98086b2bf1 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_CPEP_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_CPEP_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint - Experimental definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker3/um3_aa0.8_CPEP_Verydraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_CPEP_Verydraft_Print.inst.cfg index 14e08cb14d..0684b9ac1e 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_CPEP_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_CPEP_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast - Experimental definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker3/um3_aa0.8_CPE_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_CPE_Draft_Print.inst.cfg index 170643275c..05294737b6 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_CPE_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_CPE_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.8_CPE_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_CPE_Superdraft_Print.inst.cfg index 5b3cb52f18..555d6da1bf 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_CPE_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_CPE_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker3/um3_aa0.8_CPE_Verydraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_CPE_Verydraft_Print.inst.cfg index fff96ba9fc..da71c679ff 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_CPE_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_CPE_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker3/um3_aa0.8_Nylon_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_Nylon_Draft_Print.inst.cfg index e9b0873716..ab11f0fe86 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_Nylon_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_Nylon_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.8_Nylon_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_Nylon_Superdraft_Print.inst.cfg index 7518acc7f0..46f5a87a1b 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_Nylon_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_Nylon_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker3/um3_aa0.8_Nylon_Verydraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_Nylon_Verydraft_Print.inst.cfg index 040632efec..12d96e9f7d 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_Nylon_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_Nylon_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker3/um3_aa0.8_PC_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_PC_Fast_Print.inst.cfg index 80be29ed85..8005618066 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_PC_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_PC_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast - Experimental definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.8_PC_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_PC_Superdraft_Print.inst.cfg index 149712dde9..601a68ef46 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_PC_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_PC_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint - Experimental definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker3/um3_aa0.8_PC_Verydraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_PC_Verydraft_Print.inst.cfg index 7311cc43a6..0d5b579846 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_PC_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_PC_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast - Experimental definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker3/um3_aa0.8_PLA_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_PLA_Draft_Print.inst.cfg index 9b861030d8..c5000b0a43 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_PLA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_PLA_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.8_PLA_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_PLA_Superdraft_Print.inst.cfg index 42a499f22c..e03663bc2b 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_PLA_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_PLA_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker3/um3_aa0.8_PLA_Verydraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_PLA_Verydraft_Print.inst.cfg index d1f3937244..06bf4cf224 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_PLA_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_PLA_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker3/um3_aa0.8_PP_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_PP_Draft_Print.inst.cfg index 19496565bc..b577ba8185 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_PP_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_PP_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.8_PP_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_PP_Superdraft_Print.inst.cfg index aeee3b4e09..c0917a9e8e 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_PP_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_PP_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker3/um3_aa0.8_PP_Verydraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_PP_Verydraft_Print.inst.cfg index fcd4fcd999..00eb39a400 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_PP_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_PP_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker3/um3_aa0.8_TPLA_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_TPLA_Draft_Print.inst.cfg index 3f679870fd..30ce94000f 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_TPLA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_TPLA_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.8_TPLA_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_TPLA_Superdraft_Print.inst.cfg index 17dbd1faf9..ea3525c7ee 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_TPLA_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_TPLA_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker3/um3_aa0.8_TPLA_Verydraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_TPLA_Verydraft_Print.inst.cfg index 624496a9ec..1027f52b38 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_TPLA_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_TPLA_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker3/um3_aa0.8_TPU_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_TPU_Draft_Print.inst.cfg index 90b5103f20..83fb56a84d 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_TPU_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_TPU_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.8_TPU_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_TPU_Superdraft_Print.inst.cfg index a9fab40d4e..36e38654db 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_TPU_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_TPU_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker3/um3_aa0.8_TPU_Verydraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_TPU_Verydraft_Print.inst.cfg index e2ced0a364..96b1406adb 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_TPU_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_TPU_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker3/um3_bb0.4_PVA_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_bb0.4_PVA_Draft_Print.inst.cfg index 7010d292b2..a41b0c0210 100644 --- a/resources/quality/ultimaker3/um3_bb0.4_PVA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_bb0.4_PVA_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_bb0.4_PVA_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_bb0.4_PVA_Fast_Print.inst.cfg index 325609362f..d7a8885bf9 100644 --- a/resources/quality/ultimaker3/um3_bb0.4_PVA_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_bb0.4_PVA_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker3/um3_bb0.4_PVA_High_Quality.inst.cfg b/resources/quality/ultimaker3/um3_bb0.4_PVA_High_Quality.inst.cfg index a0507299fb..be4eedadc8 100644 --- a/resources/quality/ultimaker3/um3_bb0.4_PVA_High_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_bb0.4_PVA_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker3/um3_bb0.4_PVA_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_bb0.4_PVA_Normal_Quality.inst.cfg index 086f811b36..ae576790a8 100644 --- a/resources/quality/ultimaker3/um3_bb0.4_PVA_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_bb0.4_PVA_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_bb0.8_PVA_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_bb0.8_PVA_Draft_Print.inst.cfg index 28556ca7bf..247e7f12c6 100644 --- a/resources/quality/ultimaker3/um3_bb0.8_PVA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_bb0.8_PVA_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_bb0.8_PVA_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_bb0.8_PVA_Superdraft_Print.inst.cfg index 9ad5499f18..8a4c0cc992 100644 --- a/resources/quality/ultimaker3/um3_bb0.8_PVA_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_bb0.8_PVA_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker3/um3_bb0.8_PVA_Verydraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_bb0.8_PVA_Verydraft_Print.inst.cfg index e616214704..c931e6ad5d 100644 --- a/resources/quality/ultimaker3/um3_bb0.8_PVA_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_bb0.8_PVA_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker3/um3_global_Draft_Quality.inst.cfg b/resources/quality/ultimaker3/um3_global_Draft_Quality.inst.cfg index a421203220..b95ddb1aa9 100644 --- a/resources/quality/ultimaker3/um3_global_Draft_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_global_Draft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_global_Fast_Quality.inst.cfg b/resources/quality/ultimaker3/um3_global_Fast_Quality.inst.cfg index 2ecf7526a2..3510a05343 100644 --- a/resources/quality/ultimaker3/um3_global_Fast_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_global_Fast_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker3/um3_global_High_Quality.inst.cfg b/resources/quality/ultimaker3/um3_global_High_Quality.inst.cfg index a1fa758163..55fe2bdfda 100644 --- a/resources/quality/ultimaker3/um3_global_High_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_global_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker3/um3_global_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_global_Normal_Quality.inst.cfg index afadda378a..cffacfd312 100644 --- a/resources/quality/ultimaker3/um3_global_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_global_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_global_Superdraft_Quality.inst.cfg b/resources/quality/ultimaker3/um3_global_Superdraft_Quality.inst.cfg index f88f5df85f..5fd966bb9f 100644 --- a/resources/quality/ultimaker3/um3_global_Superdraft_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_global_Superdraft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker3/um3_global_Verydraft_Quality.inst.cfg b/resources/quality/ultimaker3/um3_global_Verydraft_Quality.inst.cfg index df626dc724..7e65a6c4c4 100644 --- a/resources/quality/ultimaker3/um3_global_Verydraft_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_global_Verydraft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_original/umo_global_Coarse_Quality.inst.cfg b/resources/quality/ultimaker_original/umo_global_Coarse_Quality.inst.cfg index 34f3a2a901..6125d0f687 100644 --- a/resources/quality/ultimaker_original/umo_global_Coarse_Quality.inst.cfg +++ b/resources/quality/ultimaker_original/umo_global_Coarse_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Coarse Quality definition = ultimaker_original [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = -3 diff --git a/resources/quality/ultimaker_original/umo_global_Draft_Quality.inst.cfg b/resources/quality/ultimaker_original/umo_global_Draft_Quality.inst.cfg index ed8c0ddb97..ebabe4dd46 100644 --- a/resources/quality/ultimaker_original/umo_global_Draft_Quality.inst.cfg +++ b/resources/quality/ultimaker_original/umo_global_Draft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Draft Quality definition = ultimaker_original [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_original/umo_global_Extra_Coarse_Quality.inst.cfg b/resources/quality/ultimaker_original/umo_global_Extra_Coarse_Quality.inst.cfg index 1ad10ac4db..b625ec45ea 100644 --- a/resources/quality/ultimaker_original/umo_global_Extra_Coarse_Quality.inst.cfg +++ b/resources/quality/ultimaker_original/umo_global_Extra_Coarse_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Coarse Quality definition = ultimaker_original [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extra coarse weight = -4 diff --git a/resources/quality/ultimaker_original/umo_global_Fast_Quality.inst.cfg b/resources/quality/ultimaker_original/umo_global_Fast_Quality.inst.cfg index 6c83239164..e2bceeae7e 100644 --- a/resources/quality/ultimaker_original/umo_global_Fast_Quality.inst.cfg +++ b/resources/quality/ultimaker_original/umo_global_Fast_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_original [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_original/umo_global_High_Quality.inst.cfg b/resources/quality/ultimaker_original/umo_global_High_Quality.inst.cfg index 19752f07bf..883048f557 100644 --- a/resources/quality/ultimaker_original/umo_global_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_original/umo_global_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker_original [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker_original/umo_global_Normal_Quality.inst.cfg b/resources/quality/ultimaker_original/umo_global_Normal_Quality.inst.cfg index a7dedc9b88..2cec4bfbda 100644 --- a/resources/quality/ultimaker_original/umo_global_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_original/umo_global_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_original [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.25_ABS_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.25_ABS_Normal_Quality.inst.cfg index f2e05b08e8..be8e56d133 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.25_ABS_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.25_ABS_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.25_CPE_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.25_CPE_Normal_Quality.inst.cfg index c15311d104..3eaeda67d5 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.25_CPE_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.25_CPE_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.25_Nylon_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.25_Nylon_Normal_Quality.inst.cfg index 3a8ed8e773..9407fb72b0 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.25_Nylon_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.25_Nylon_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.25_PC_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.25_PC_Normal_Quality.inst.cfg index 53c319d6e6..7cde76a1e1 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.25_PC_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.25_PC_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine - Experimental definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.25_PLA_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.25_PLA_Normal_Quality.inst.cfg index a06f2158fe..ab45d82a3b 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.25_PLA_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.25_PLA_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.25_PP_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.25_PP_Normal_Quality.inst.cfg index b4d34cc392..1474cad5c1 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.25_PP_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.25_PP_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine - Experimental definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.25_TPLA_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.25_TPLA_Normal_Quality.inst.cfg index e9628225bc..ea4375b854 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.25_TPLA_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.25_TPLA_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Draft_Print.inst.cfg index b86c61b3a2..6d26c2bb97 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Fast_Print.inst.cfg index f3c099724a..e2e8bd82e5 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_High_Quality.inst.cfg index 8d016a2ee4..fc39383f04 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Normal_Quality.inst.cfg index 6ce623b66e..8ec7e6453b 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_BAM_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_BAM_Draft_Print.inst.cfg index 254afbc109..93665c36de 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_BAM_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_BAM_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_BAM_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_BAM_Fast_Print.inst.cfg index 39bedce77f..478df58f43 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_BAM_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_BAM_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_BAM_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_BAM_Normal_Quality.inst.cfg index c87d590650..e936d0f131 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_BAM_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_BAM_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Draft_Print.inst.cfg index 627302e0ab..da639d22c9 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Fast_Print.inst.cfg index cda8d85211..2c2eb99fb0 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_High_Quality.inst.cfg index 3ce76bf6be..20fbcd9430 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Normal_Quality.inst.cfg index d402b663c6..e40949fc7d 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Draft_Print.inst.cfg index 505cd952d2..7ccfe4f786 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Fast_Print.inst.cfg index cc5df0abb9..f12c90bc05 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_High_Quality.inst.cfg index c81dc0f5a7..04957025f1 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Normal_Quality.inst.cfg index 7d29f8fb7c..b0a5fb820f 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Draft_Print.inst.cfg index 991ad30a5a..1a198f3e03 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Fast_Print.inst.cfg index 695ce2c8fb..e6b882d2af 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_High_Quality.inst.cfg index e55867efe5..292ef73a4c 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Normal_Quality.inst.cfg index 41e28c51d5..2b71a01e0d 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_Draft_Print.inst.cfg index 5d03e1c980..e27a6894e3 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_Fast_Print.inst.cfg index b630ab6232..db1ecee3bc 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_High_Quality.inst.cfg index 1c080c3b47..ca80494f3d 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_Normal_Quality.inst.cfg index 79ce686da5..d2d5171fed 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Draft_Print.inst.cfg index c7a4864328..9bf58d4f39 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Fast_Print.inst.cfg index 42048fa297..017bf46bd8 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_High_Quality.inst.cfg index b7ad8bd5c4..314f7fe0bb 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Normal_Quality.inst.cfg index 911fa9a0a8..2bb0d31d76 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_PP_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_PP_Draft_Print.inst.cfg index 68558bcf93..8b8bdfd999 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_PP_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_PP_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_PP_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_PP_Fast_Print.inst.cfg index 1145d1900f..0fd70e5973 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_PP_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_PP_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_PP_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_PP_Normal_Quality.inst.cfg index c0b094f0a2..0d683a9f87 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_PP_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_PP_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_Draft_Print.inst.cfg index 280e5c4bfb..df45c190e1 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_Fast_Print.inst.cfg index 304c170b55..96b77ebca9 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_High_Quality.inst.cfg index cd5c598d4f..3a2cf495a3 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_Normal_Quality.inst.cfg index 2522c0f20f..2d93d1e980 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_TPU_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_TPU_Draft_Print.inst.cfg index 9b4ab52543..30a1e0c29f 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_TPU_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_TPU_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_TPU_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_TPU_Fast_Print.inst.cfg index 35cf66a93b..3b57900a46 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_TPU_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_TPU_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_TPU_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_TPU_Normal_Quality.inst.cfg index 4357d765df..368bfd9e48 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_TPU_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_TPU_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_Draft_Print.inst.cfg index c8d64f9dcb..a2b7aa7b87 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_Fast_Print.inst.cfg index c7fa604e89..8e82e27210 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_High_Quality.inst.cfg index 187023d3c0..7e4f17cbf1 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_Normal_Quality.inst.cfg index 81cb27f060..959d9241c7 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Draft_Print.inst.cfg index 4a55f5d24c..c9083cf2ab 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Fast_Print.inst.cfg index 730e058212..9d7663b6e1 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_High_Quality.inst.cfg index e6921e63d8..ba796da940 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Normal_Quality.inst.cfg index a4eec45e38..96ec8d9e36 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Draft_Print.inst.cfg index 4f085f10a6..bae8b09016 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Fast_Print.inst.cfg index 2580bf952d..f585c2a787 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_High_Quality.inst.cfg index d6f07c37a5..a580a27176 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Normal_Quality.inst.cfg index 6032ff3845..3db4053d2a 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_Draft_Print.inst.cfg index f05ecddc25..e86c3b363b 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_Fast_Print.inst.cfg index 6103519f1c..0da88626b6 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_High_Quality.inst.cfg index 130afb8c91..d7552ba921 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_Normal_Quality.inst.cfg index 9e1bf394d4..638ce2323e 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PP_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PP_Draft_Print.inst.cfg index 6124dff257..c0ca84281d 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PP_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PP_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PP_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PP_Fast_Print.inst.cfg index 2791e9f5d5..43b77f6492 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PP_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PP_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PP_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PP_Normal_Quality.inst.cfg index f78b4048fb..04abd0d18b 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PP_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PP_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_ABS_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_ABS_Draft_Print.inst.cfg index 911fc6e78e..fb786c7759 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_ABS_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_ABS_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_ABS_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_ABS_Superdraft_Print.inst.cfg index 0fcdac4a85..cfacaebad0 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_ABS_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_ABS_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_ABS_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_ABS_Verydraft_Print.inst.cfg index e3346bbd1d..18ce266cd6 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_ABS_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_ABS_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Fast_Print.inst.cfg index d126dddaad..a4a7a5e08c 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast - Experimental definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Superdraft_Print.inst.cfg index dd8b20652a..e4f330099b 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint - Experimental definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Verydraft_Print.inst.cfg index bc107422f1..0685b27b8a 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast - Experimental definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Draft_Print.inst.cfg index 7cb69ba3eb..a57bde98b1 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Superdraft_Print.inst.cfg index 6c323fe602..2f0851d9fb 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Verydraft_Print.inst.cfg index a0380ecc0e..6ccca75b44 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_Nylon_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_Nylon_Draft_Print.inst.cfg index 2108839d3f..5d04892443 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_Nylon_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_Nylon_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_Nylon_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_Nylon_Superdraft_Print.inst.cfg index 0702d174a0..5c31e26bd6 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_Nylon_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_Nylon_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_Nylon_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_Nylon_Verydraft_Print.inst.cfg index d02d410ed6..d04482c94c 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_Nylon_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_Nylon_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Fast_Print.inst.cfg index 378028694b..97afe62e07 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast - Experimental definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Superdraft_Print.inst.cfg index a179d6b7c9..3f296c0300 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint - Experimental definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Verydraft_Print.inst.cfg index b474cb23d9..5eec532f22 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast - Experimental definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_PLA_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_PLA_Draft_Print.inst.cfg index 7db4e96311..1410290859 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_PLA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_PLA_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_PLA_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_PLA_Superdraft_Print.inst.cfg index c59f015b5d..2c65968d77 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_PLA_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_PLA_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_PLA_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_PLA_Verydraft_Print.inst.cfg index 6fdff8bf8d..9b6e7c2ed9 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_PLA_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_PLA_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_PP_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_PP_Draft_Print.inst.cfg index fee58b367d..c5b01ab1b1 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_PP_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_PP_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_PP_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_PP_Superdraft_Print.inst.cfg index aaa810e864..5c83f24395 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_PP_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_PP_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_PP_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_PP_Verydraft_Print.inst.cfg index 5b8aa6d2e1..f182d5c6cb 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_PP_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_PP_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPLA_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPLA_Draft_Print.inst.cfg index 50dead746b..5b39508f3c 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPLA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPLA_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPLA_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPLA_Superdraft_Print.inst.cfg index 0bdb088f8c..b07a78419a 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPLA_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPLA_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPLA_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPLA_Verydraft_Print.inst.cfg index c7cb5902a2..46a61bb79f 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPLA_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPLA_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Draft_Print.inst.cfg index e8276d54c5..088414fa34 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Superdraft_Print.inst.cfg index 7da73a200d..d74309c476 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Verydraft_Print.inst.cfg index 60dbbf38e6..deb2b55dbb 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_ABS_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_ABS_Draft_Print.inst.cfg index 37dceff349..062fcfdd5b 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_ABS_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_ABS_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_ABS_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_ABS_Superdraft_Print.inst.cfg index eac339baa8..38c341af28 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_ABS_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_ABS_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_ABS_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_ABS_Verydraft_Print.inst.cfg index 590496df0f..8a38bb9aa3 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_ABS_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_ABS_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Fast_Print.inst.cfg index 50091a6fb4..d9dd09c7b6 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast - Experimental definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Superdraft_Print.inst.cfg index b9c9ef6611..bc6a04ae55 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Verydraft_Print.inst.cfg index be2cc62b08..b384bd7083 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Draft_Print.inst.cfg index 0d0ed8f8b2..28409d4181 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Superdraft_Print.inst.cfg index a163e0c735..33c5d73a59 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Verydraft_Print.inst.cfg index 2137cf740b..8db23c11c2 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Fast_Print.inst.cfg index a313ac56fc..c0af70748a 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast - Experimental definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Superdraft_Print.inst.cfg index f1c1762d2c..bba2dfa92f 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Verydraft_Print.inst.cfg index 399c139412..3987c01ecc 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PP_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PP_Draft_Print.inst.cfg index b80af1b75f..5a2c3005c9 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PP_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PP_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PP_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PP_Superdraft_Print.inst.cfg index 970e0971a9..aaa8cbfcd3 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PP_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PP_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PP_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PP_Verydraft_Print.inst.cfg index e51ba3207b..782cadce6b 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PP_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PP_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_Draft_Print.inst.cfg index 73639be0b6..34b078278c 100644 --- a/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_Fast_Print.inst.cfg index 5da25be32d..5677c5931c 100644 --- a/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_High_Quality.inst.cfg index 36634af2c8..b9e32cb7c5 100644 --- a/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_Normal_Quality.inst.cfg index f76c4c944a..081ee2261e 100644 --- a/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_bb0.8_PVA_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_bb0.8_PVA_Draft_Print.inst.cfg index e4e3ab772a..0193081db6 100644 --- a/resources/quality/ultimaker_s5/um_s5_bb0.8_PVA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_bb0.8_PVA_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_bb0.8_PVA_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_bb0.8_PVA_Superdraft_Print.inst.cfg index 5e78e51014..121b32f903 100644 --- a/resources/quality/ultimaker_s5/um_s5_bb0.8_PVA_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_bb0.8_PVA_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_bb0.8_PVA_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_bb0.8_PVA_Verydraft_Print.inst.cfg index 5af09aebcc..f0296c9279 100644 --- a/resources/quality/ultimaker_s5/um_s5_bb0.8_PVA_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_bb0.8_PVA_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_cc0.6_CFFCPE_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_cc0.6_CFFCPE_Draft_Print.inst.cfg index feb8891d26..35da5b113e 100644 --- a/resources/quality/ultimaker_s5/um_s5_cc0.6_CFFCPE_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_cc0.6_CFFCPE_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_cc0.6_CFFPA_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_cc0.6_CFFPA_Draft_Print.inst.cfg index 980ded99e9..67400daf9f 100644 --- a/resources/quality/ultimaker_s5/um_s5_cc0.6_CFFPA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_cc0.6_CFFPA_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_cc0.6_GFFCPE_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_cc0.6_GFFCPE_Draft_Print.inst.cfg index 331c5e6d73..0c4f55bddf 100644 --- a/resources/quality/ultimaker_s5/um_s5_cc0.6_GFFCPE_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_cc0.6_GFFCPE_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_cc0.6_GFFPA_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_cc0.6_GFFPA_Draft_Print.inst.cfg index 0e90d6df78..8a6f9d2ac8 100644 --- a/resources/quality/ultimaker_s5/um_s5_cc0.6_GFFPA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_cc0.6_GFFPA_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_global_Draft_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_global_Draft_Quality.inst.cfg index ed5303637b..f0121fa4d7 100644 --- a/resources/quality/ultimaker_s5/um_s5_global_Draft_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_global_Draft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_global_Fast_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_global_Fast_Quality.inst.cfg index ee9c6a8409..b9bb0beff6 100644 --- a/resources/quality/ultimaker_s5/um_s5_global_Fast_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_global_Fast_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_global_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_global_High_Quality.inst.cfg index 96005a48da..aa2420e465 100644 --- a/resources/quality/ultimaker_s5/um_s5_global_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_global_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker_s5/um_s5_global_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_global_Normal_Quality.inst.cfg index 099ba7c584..8bb8f7f282 100644 --- a/resources/quality/ultimaker_s5/um_s5_global_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_global_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_global_Superdraft_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_global_Superdraft_Quality.inst.cfg index 4c0bd40bd1..ec46668b61 100644 --- a/resources/quality/ultimaker_s5/um_s5_global_Superdraft_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_global_Superdraft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_global_Verydraft_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_global_Verydraft_Quality.inst.cfg index ec4ec910ff..dd6c7bb4b8 100644 --- a/resources/quality/ultimaker_s5/um_s5_global_Verydraft_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_global_Verydraft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/vertex_delta_k8800/k8800_ABS_Extreme_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_ABS_Extreme_Quality.inst.cfg index 70aac3f666..370cc149a2 100644 --- a/resources/quality/vertex_delta_k8800/k8800_ABS_Extreme_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_ABS_Extreme_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extreme definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extreme weight = 2 diff --git a/resources/quality/vertex_delta_k8800/k8800_ABS_High_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_ABS_High_Quality.inst.cfg index 564b330132..6c96b47169 100644 --- a/resources/quality/vertex_delta_k8800/k8800_ABS_High_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_ABS_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = High definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/vertex_delta_k8800/k8800_ABS_Normal_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_ABS_Normal_Quality.inst.cfg index e2f740a60a..19150baab9 100644 --- a/resources/quality/vertex_delta_k8800/k8800_ABS_Normal_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_ABS_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/vertex_delta_k8800/k8800_Global_Extreme_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_Global_Extreme_Quality.inst.cfg index 48e80b5512..00d626d72c 100644 --- a/resources/quality/vertex_delta_k8800/k8800_Global_Extreme_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_Global_Extreme_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extreme definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extreme weight = 2 diff --git a/resources/quality/vertex_delta_k8800/k8800_Global_High_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_Global_High_Quality.inst.cfg index 496144772c..0bd519affe 100644 --- a/resources/quality/vertex_delta_k8800/k8800_Global_High_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_Global_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = High definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/vertex_delta_k8800/k8800_Global_Normal_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_Global_Normal_Quality.inst.cfg index 75ae5f15e6..2b18891998 100644 --- a/resources/quality/vertex_delta_k8800/k8800_Global_Normal_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_Global_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/vertex_delta_k8800/k8800_PET_Extreme_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_PET_Extreme_Quality.inst.cfg index 8309106d9f..8715e573ab 100644 --- a/resources/quality/vertex_delta_k8800/k8800_PET_Extreme_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_PET_Extreme_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extreme definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extreme weight = 2 diff --git a/resources/quality/vertex_delta_k8800/k8800_PET_High_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_PET_High_Quality.inst.cfg index 6efaa3299f..0c94cc5ca4 100644 --- a/resources/quality/vertex_delta_k8800/k8800_PET_High_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_PET_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = High definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/vertex_delta_k8800/k8800_PET_Normal_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_PET_Normal_Quality.inst.cfg index bd3b0c35fb..40698f134d 100644 --- a/resources/quality/vertex_delta_k8800/k8800_PET_Normal_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_PET_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/vertex_delta_k8800/k8800_PLA_Extreme_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_PLA_Extreme_Quality.inst.cfg index d10b4c3f8d..f81c82fa9b 100644 --- a/resources/quality/vertex_delta_k8800/k8800_PLA_Extreme_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_PLA_Extreme_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extreme definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extreme weight = 2 diff --git a/resources/quality/vertex_delta_k8800/k8800_PLA_High_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_PLA_High_Quality.inst.cfg index ede77b0dfe..e4b4771b0e 100644 --- a/resources/quality/vertex_delta_k8800/k8800_PLA_High_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_PLA_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = High definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/vertex_delta_k8800/k8800_PLA_Normal_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_PLA_Normal_Quality.inst.cfg index a75cff6968..41950da7a1 100644 --- a/resources/quality/vertex_delta_k8800/k8800_PLA_Normal_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_PLA_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/vertex_delta_k8800/k8800_TPU_Extreme_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_TPU_Extreme_Quality.inst.cfg index bee6b3bf11..32077f6cc1 100644 --- a/resources/quality/vertex_delta_k8800/k8800_TPU_Extreme_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_TPU_Extreme_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extreme definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extreme weight = 2 diff --git a/resources/quality/vertex_delta_k8800/k8800_TPU_High_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_TPU_High_Quality.inst.cfg index 8b0f87cc77..a7ef7d8cc8 100644 --- a/resources/quality/vertex_delta_k8800/k8800_TPU_High_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_TPU_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = High definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/vertex_delta_k8800/k8800_TPU_Normal_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_TPU_Normal_Quality.inst.cfg index bd4e04744f..17a09f366f 100644 --- a/resources/quality/vertex_delta_k8800/k8800_TPU_Normal_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_TPU_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/zyyx/zyyx_agile_global_fast.inst.cfg b/resources/quality/zyyx/zyyx_agile_global_fast.inst.cfg index 67e350b39e..19cda650d3 100644 --- a/resources/quality/zyyx/zyyx_agile_global_fast.inst.cfg +++ b/resources/quality/zyyx/zyyx_agile_global_fast.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = zyyx_agile [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = 1 diff --git a/resources/quality/zyyx/zyyx_agile_global_fine.inst.cfg b/resources/quality/zyyx/zyyx_agile_global_fine.inst.cfg index b8bc7eb628..f2f127f483 100644 --- a/resources/quality/zyyx/zyyx_agile_global_fine.inst.cfg +++ b/resources/quality/zyyx/zyyx_agile_global_fine.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = zyyx_agile [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fine weight = 1 diff --git a/resources/quality/zyyx/zyyx_agile_global_normal.inst.cfg b/resources/quality/zyyx/zyyx_agile_global_normal.inst.cfg index 3f934b0114..d9cbdc691e 100644 --- a/resources/quality/zyyx/zyyx_agile_global_normal.inst.cfg +++ b/resources/quality/zyyx/zyyx_agile_global_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = zyyx_agile [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/zyyx/zyyx_agile_pro_flex_fast.inst.cfg b/resources/quality/zyyx/zyyx_agile_pro_flex_fast.inst.cfg index 188bdd25e5..d6f715c09f 100644 --- a/resources/quality/zyyx/zyyx_agile_pro_flex_fast.inst.cfg +++ b/resources/quality/zyyx/zyyx_agile_pro_flex_fast.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = zyyx_agile [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = 1 diff --git a/resources/quality/zyyx/zyyx_agile_pro_flex_fine.inst.cfg b/resources/quality/zyyx/zyyx_agile_pro_flex_fine.inst.cfg index d69bc930e9..a0aa1996ac 100644 --- a/resources/quality/zyyx/zyyx_agile_pro_flex_fine.inst.cfg +++ b/resources/quality/zyyx/zyyx_agile_pro_flex_fine.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = zyyx_agile [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fine weight = 1 diff --git a/resources/quality/zyyx/zyyx_agile_pro_flex_normal.inst.cfg b/resources/quality/zyyx/zyyx_agile_pro_flex_normal.inst.cfg index d717f11528..a70b1dd19a 100644 --- a/resources/quality/zyyx/zyyx_agile_pro_flex_normal.inst.cfg +++ b/resources/quality/zyyx/zyyx_agile_pro_flex_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = zyyx_agile [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/zyyx/zyyx_agile_pro_pla_fast.inst.cfg b/resources/quality/zyyx/zyyx_agile_pro_pla_fast.inst.cfg index 7ae4be06b0..5b32c3f07b 100644 --- a/resources/quality/zyyx/zyyx_agile_pro_pla_fast.inst.cfg +++ b/resources/quality/zyyx/zyyx_agile_pro_pla_fast.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = zyyx_agile [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = 1 diff --git a/resources/quality/zyyx/zyyx_agile_pro_pla_fine.inst.cfg b/resources/quality/zyyx/zyyx_agile_pro_pla_fine.inst.cfg index 44b722ac32..a3849bf5e2 100644 --- a/resources/quality/zyyx/zyyx_agile_pro_pla_fine.inst.cfg +++ b/resources/quality/zyyx/zyyx_agile_pro_pla_fine.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = zyyx_agile [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fine weight = 1 diff --git a/resources/quality/zyyx/zyyx_agile_pro_pla_normal.inst.cfg b/resources/quality/zyyx/zyyx_agile_pro_pla_normal.inst.cfg index 01424a9422..d126308660 100644 --- a/resources/quality/zyyx/zyyx_agile_pro_pla_normal.inst.cfg +++ b/resources/quality/zyyx/zyyx_agile_pro_pla_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = zyyx_agile [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/variants/cartesio_0.25.inst.cfg b/resources/variants/cartesio_0.25.inst.cfg index b3aae8a393..53048622f2 100644 --- a/resources/variants/cartesio_0.25.inst.cfg +++ b/resources/variants/cartesio_0.25.inst.cfg @@ -5,7 +5,7 @@ definition = cartesio [metadata] author = Cartesio -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/cartesio_0.4.inst.cfg b/resources/variants/cartesio_0.4.inst.cfg index 5cea5823c4..3ad6b3f3d9 100644 --- a/resources/variants/cartesio_0.4.inst.cfg +++ b/resources/variants/cartesio_0.4.inst.cfg @@ -5,7 +5,7 @@ definition = cartesio [metadata] author = Cartesio -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/cartesio_0.8.inst.cfg b/resources/variants/cartesio_0.8.inst.cfg index b4009cf9ed..f19c4d58d4 100644 --- a/resources/variants/cartesio_0.8.inst.cfg +++ b/resources/variants/cartesio_0.8.inst.cfg @@ -5,7 +5,7 @@ definition = cartesio [metadata] author = Cartesio -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/fabtotum_hyb35.inst.cfg b/resources/variants/fabtotum_hyb35.inst.cfg index d3f0077792..5ed9eca26a 100644 --- a/resources/variants/fabtotum_hyb35.inst.cfg +++ b/resources/variants/fabtotum_hyb35.inst.cfg @@ -5,7 +5,7 @@ definition = fabtotum [metadata] author = FABtotum -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/fabtotum_lite04.inst.cfg b/resources/variants/fabtotum_lite04.inst.cfg index 226c136564..7b52bf5c29 100644 --- a/resources/variants/fabtotum_lite04.inst.cfg +++ b/resources/variants/fabtotum_lite04.inst.cfg @@ -5,7 +5,7 @@ definition = fabtotum [metadata] author = FABtotum -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/fabtotum_lite06.inst.cfg b/resources/variants/fabtotum_lite06.inst.cfg index 62e3014b60..f518f60ce9 100644 --- a/resources/variants/fabtotum_lite06.inst.cfg +++ b/resources/variants/fabtotum_lite06.inst.cfg @@ -5,7 +5,7 @@ definition = fabtotum [metadata] author = FABtotum -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/fabtotum_pro02.inst.cfg b/resources/variants/fabtotum_pro02.inst.cfg index 3e4661ee2c..897bd5aea9 100644 --- a/resources/variants/fabtotum_pro02.inst.cfg +++ b/resources/variants/fabtotum_pro02.inst.cfg @@ -5,7 +5,7 @@ definition = fabtotum [metadata] author = FABtotum -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/fabtotum_pro04.inst.cfg b/resources/variants/fabtotum_pro04.inst.cfg index 3fe140f8be..d27be2f3b8 100644 --- a/resources/variants/fabtotum_pro04.inst.cfg +++ b/resources/variants/fabtotum_pro04.inst.cfg @@ -5,7 +5,7 @@ definition = fabtotum [metadata] author = FABtotum -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/fabtotum_pro06.inst.cfg b/resources/variants/fabtotum_pro06.inst.cfg index fcb5c71ef0..25b9d7c710 100644 --- a/resources/variants/fabtotum_pro06.inst.cfg +++ b/resources/variants/fabtotum_pro06.inst.cfg @@ -5,7 +5,7 @@ definition = fabtotum [metadata] author = FABtotum -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/fabtotum_pro08.inst.cfg b/resources/variants/fabtotum_pro08.inst.cfg index bef04734eb..627e26e2a2 100644 --- a/resources/variants/fabtotum_pro08.inst.cfg +++ b/resources/variants/fabtotum_pro08.inst.cfg @@ -5,7 +5,7 @@ definition = fabtotum [metadata] author = FABtotum -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/felixtec4_0.25.inst.cfg b/resources/variants/felixtec4_0.25.inst.cfg index 7d8bca94b0..3dda15171b 100644 --- a/resources/variants/felixtec4_0.25.inst.cfg +++ b/resources/variants/felixtec4_0.25.inst.cfg @@ -6,7 +6,7 @@ definition = felixtec4dual [metadata] author = kerog777 type = variant -setting_version = 5 +setting_version = 6 hardware_type = nozzle [values] diff --git a/resources/variants/felixtec4_0.35.inst.cfg b/resources/variants/felixtec4_0.35.inst.cfg index f061aa1cbc..db79c3bad4 100644 --- a/resources/variants/felixtec4_0.35.inst.cfg +++ b/resources/variants/felixtec4_0.35.inst.cfg @@ -6,7 +6,7 @@ definition = felixtec4dual [metadata] author = kerog777 type = variant -setting_version = 5 +setting_version = 6 hardware_type = nozzle [values] diff --git a/resources/variants/felixtec4_0.50.inst.cfg b/resources/variants/felixtec4_0.50.inst.cfg index 3c68c42dae..6d52881ee5 100644 --- a/resources/variants/felixtec4_0.50.inst.cfg +++ b/resources/variants/felixtec4_0.50.inst.cfg @@ -7,7 +7,7 @@ definition = felixtec4dual author = kerog777 type = variant hardware_type = nozzle -setting_version = 5 +setting_version = 6 [values] machine_nozzle_size = 0.5 diff --git a/resources/variants/felixtec4_0.70.inst.cfg b/resources/variants/felixtec4_0.70.inst.cfg index 3a52644714..4edeebbc84 100644 --- a/resources/variants/felixtec4_0.70.inst.cfg +++ b/resources/variants/felixtec4_0.70.inst.cfg @@ -7,7 +7,7 @@ definition = felixtec4dual author = kerog777 type = variant hardware_type = nozzle -setting_version = 5 +setting_version = 6 [values] machine_nozzle_size = 0.70 diff --git a/resources/variants/gmax15plus_025_e3d.inst.cfg b/resources/variants/gmax15plus_025_e3d.inst.cfg index 8a6b37067d..a085be0526 100644 --- a/resources/variants/gmax15plus_025_e3d.inst.cfg +++ b/resources/variants/gmax15plus_025_e3d.inst.cfg @@ -5,7 +5,7 @@ definition = gmax15plus [metadata] author = gcreate -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_04_e3d.inst.cfg b/resources/variants/gmax15plus_04_e3d.inst.cfg index 48fe7ada16..05473dafd2 100644 --- a/resources/variants/gmax15plus_04_e3d.inst.cfg +++ b/resources/variants/gmax15plus_04_e3d.inst.cfg @@ -5,7 +5,7 @@ definition = gmax15plus [metadata] author = gcreate -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_05_e3d.inst.cfg b/resources/variants/gmax15plus_05_e3d.inst.cfg index 0bb9517da8..f3324382ba 100644 --- a/resources/variants/gmax15plus_05_e3d.inst.cfg +++ b/resources/variants/gmax15plus_05_e3d.inst.cfg @@ -5,7 +5,7 @@ definition = gmax15plus [metadata] author = gcreate -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_05_jhead.inst.cfg b/resources/variants/gmax15plus_05_jhead.inst.cfg index 6d0b084969..d480b47367 100644 --- a/resources/variants/gmax15plus_05_jhead.inst.cfg +++ b/resources/variants/gmax15plus_05_jhead.inst.cfg @@ -5,7 +5,7 @@ definition = gmax15plus [metadata] author = gcreate -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_06_e3d.inst.cfg b/resources/variants/gmax15plus_06_e3d.inst.cfg index 3a372b20c9..732870f044 100644 --- a/resources/variants/gmax15plus_06_e3d.inst.cfg +++ b/resources/variants/gmax15plus_06_e3d.inst.cfg @@ -5,7 +5,7 @@ definition = gmax15plus [metadata] author = gcreate -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_08_e3d.inst.cfg b/resources/variants/gmax15plus_08_e3d.inst.cfg index 39eeef748e..0c4137a018 100644 --- a/resources/variants/gmax15plus_08_e3d.inst.cfg +++ b/resources/variants/gmax15plus_08_e3d.inst.cfg @@ -5,7 +5,7 @@ definition = gmax15plus [metadata] author = gcreate -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_10_jhead.inst.cfg b/resources/variants/gmax15plus_10_jhead.inst.cfg index 37d2546d2a..8ae93fc93c 100644 --- a/resources/variants/gmax15plus_10_jhead.inst.cfg +++ b/resources/variants/gmax15plus_10_jhead.inst.cfg @@ -5,7 +5,7 @@ definition = gmax15plus [metadata] author = gcreate -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_12_e3d.inst.cfg b/resources/variants/gmax15plus_12_e3d.inst.cfg index 57052dd0f8..016f48c1a3 100644 --- a/resources/variants/gmax15plus_12_e3d.inst.cfg +++ b/resources/variants/gmax15plus_12_e3d.inst.cfg @@ -5,7 +5,7 @@ definition = gmax15plus [metadata] author = gcreate -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_dual_025_e3d.inst.cfg b/resources/variants/gmax15plus_dual_025_e3d.inst.cfg index 750a5381b3..185166d3b5 100644 --- a/resources/variants/gmax15plus_dual_025_e3d.inst.cfg +++ b/resources/variants/gmax15plus_dual_025_e3d.inst.cfg @@ -5,7 +5,7 @@ definition = gmax15plus_dual [metadata] author = gcreate -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_dual_04_e3d.inst.cfg b/resources/variants/gmax15plus_dual_04_e3d.inst.cfg index 809227a62c..1c534f13e0 100644 --- a/resources/variants/gmax15plus_dual_04_e3d.inst.cfg +++ b/resources/variants/gmax15plus_dual_04_e3d.inst.cfg @@ -5,7 +5,7 @@ definition = gmax15plus_dual [metadata] author = gcreate -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_dual_05_e3d.inst.cfg b/resources/variants/gmax15plus_dual_05_e3d.inst.cfg index 05d9a88d54..f1d67509a9 100644 --- a/resources/variants/gmax15plus_dual_05_e3d.inst.cfg +++ b/resources/variants/gmax15plus_dual_05_e3d.inst.cfg @@ -5,7 +5,7 @@ definition = gmax15plus_dual [metadata] author = gcreate -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_dual_05_jhead.inst.cfg b/resources/variants/gmax15plus_dual_05_jhead.inst.cfg index 54a237e848..bc72c4ae20 100644 --- a/resources/variants/gmax15plus_dual_05_jhead.inst.cfg +++ b/resources/variants/gmax15plus_dual_05_jhead.inst.cfg @@ -5,7 +5,7 @@ definition = gmax15plus_dual [metadata] author = gcreate -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_dual_06_e3d.inst.cfg b/resources/variants/gmax15plus_dual_06_e3d.inst.cfg index 39c41be968..545cfcb238 100644 --- a/resources/variants/gmax15plus_dual_06_e3d.inst.cfg +++ b/resources/variants/gmax15plus_dual_06_e3d.inst.cfg @@ -5,7 +5,7 @@ definition = gmax15plus_dual [metadata] author = gcreate -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_dual_08_e3d.inst.cfg b/resources/variants/gmax15plus_dual_08_e3d.inst.cfg index 1f2d7b9790..1923aac7f7 100644 --- a/resources/variants/gmax15plus_dual_08_e3d.inst.cfg +++ b/resources/variants/gmax15plus_dual_08_e3d.inst.cfg @@ -5,7 +5,7 @@ definition = gmax15plus_dual [metadata] author = gcreate -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_dual_10_jhead.inst.cfg b/resources/variants/gmax15plus_dual_10_jhead.inst.cfg index ee0c8fa948..de0776ad9b 100644 --- a/resources/variants/gmax15plus_dual_10_jhead.inst.cfg +++ b/resources/variants/gmax15plus_dual_10_jhead.inst.cfg @@ -5,7 +5,7 @@ definition = gmax15plus_dual [metadata] author = gcreate -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/imade3d_jellybox_0.4.inst.cfg b/resources/variants/imade3d_jellybox_0.4.inst.cfg index 2bd0f578cf..1623e6755c 100644 --- a/resources/variants/imade3d_jellybox_0.4.inst.cfg +++ b/resources/variants/imade3d_jellybox_0.4.inst.cfg @@ -5,7 +5,7 @@ definition = imade3d_jellybox [metadata] author = IMADE3D -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/imade3d_jellybox_0.4_2-fans.inst.cfg b/resources/variants/imade3d_jellybox_0.4_2-fans.inst.cfg index 6a93cdf13d..2f1cb002b4 100644 --- a/resources/variants/imade3d_jellybox_0.4_2-fans.inst.cfg +++ b/resources/variants/imade3d_jellybox_0.4_2-fans.inst.cfg @@ -5,7 +5,7 @@ definition = imade3d_jellybox [metadata] author = IMADE3D -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/tizyx_k25_0.2.inst.cfg b/resources/variants/tizyx_k25_0.2.inst.cfg index cd9f1bcbd1..c616579911 100644 --- a/resources/variants/tizyx_k25_0.2.inst.cfg +++ b/resources/variants/tizyx_k25_0.2.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = tizyx_k25 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/tizyx_k25_0.3.inst.cfg b/resources/variants/tizyx_k25_0.3.inst.cfg index 8b34d23bf6..180d831cca 100644 --- a/resources/variants/tizyx_k25_0.3.inst.cfg +++ b/resources/variants/tizyx_k25_0.3.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = tizyx_k25 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/tizyx_k25_0.4.inst.cfg b/resources/variants/tizyx_k25_0.4.inst.cfg index c147eb0ad0..07ef3ca8ed 100644 --- a/resources/variants/tizyx_k25_0.4.inst.cfg +++ b/resources/variants/tizyx_k25_0.4.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = tizyx_k25 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/tizyx_k25_0.5.inst.cfg b/resources/variants/tizyx_k25_0.5.inst.cfg index 14102fb2c7..a09a207e7a 100644 --- a/resources/variants/tizyx_k25_0.5.inst.cfg +++ b/resources/variants/tizyx_k25_0.5.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = tizyx_k25 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/tizyx_k25_0.6.inst.cfg b/resources/variants/tizyx_k25_0.6.inst.cfg index 00f69f71f4..751cf8e794 100644 --- a/resources/variants/tizyx_k25_0.6.inst.cfg +++ b/resources/variants/tizyx_k25_0.6.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = tizyx_k25 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/tizyx_k25_0.8.inst.cfg b/resources/variants/tizyx_k25_0.8.inst.cfg index c80f5e70d2..cca0986ed5 100644 --- a/resources/variants/tizyx_k25_0.8.inst.cfg +++ b/resources/variants/tizyx_k25_0.8.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = tizyx_k25 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/tizyx_k25_1.0.inst.cfg b/resources/variants/tizyx_k25_1.0.inst.cfg index ce8593b1e8..d99948c26c 100644 --- a/resources/variants/tizyx_k25_1.0.inst.cfg +++ b/resources/variants/tizyx_k25_1.0.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = tizyx_k25 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_0.25.inst.cfg b/resources/variants/ultimaker2_0.25.inst.cfg index a58b4d9a56..004cdaf671 100644 --- a/resources/variants/ultimaker2_0.25.inst.cfg +++ b/resources/variants/ultimaker2_0.25.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_0.4.inst.cfg b/resources/variants/ultimaker2_0.4.inst.cfg index 46845d974e..607d0c4f29 100644 --- a/resources/variants/ultimaker2_0.4.inst.cfg +++ b/resources/variants/ultimaker2_0.4.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_0.6.inst.cfg b/resources/variants/ultimaker2_0.6.inst.cfg index f9ab1f1358..1ddc07817b 100644 --- a/resources/variants/ultimaker2_0.6.inst.cfg +++ b/resources/variants/ultimaker2_0.6.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_0.8.inst.cfg b/resources/variants/ultimaker2_0.8.inst.cfg index 3d9c273783..938b472c2c 100644 --- a/resources/variants/ultimaker2_0.8.inst.cfg +++ b/resources/variants/ultimaker2_0.8.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_extended_0.25.inst.cfg b/resources/variants/ultimaker2_extended_0.25.inst.cfg index f5471fc505..45546c55aa 100644 --- a/resources/variants/ultimaker2_extended_0.25.inst.cfg +++ b/resources/variants/ultimaker2_extended_0.25.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2_extended [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_extended_0.4.inst.cfg b/resources/variants/ultimaker2_extended_0.4.inst.cfg index a7d03f2408..fb3d8c1116 100644 --- a/resources/variants/ultimaker2_extended_0.4.inst.cfg +++ b/resources/variants/ultimaker2_extended_0.4.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2_extended [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_extended_0.6.inst.cfg b/resources/variants/ultimaker2_extended_0.6.inst.cfg index 25c180e07e..50f7dc04c6 100644 --- a/resources/variants/ultimaker2_extended_0.6.inst.cfg +++ b/resources/variants/ultimaker2_extended_0.6.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2_extended [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_extended_0.8.inst.cfg b/resources/variants/ultimaker2_extended_0.8.inst.cfg index c33f483da3..178737dbd5 100644 --- a/resources/variants/ultimaker2_extended_0.8.inst.cfg +++ b/resources/variants/ultimaker2_extended_0.8.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2_extended [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_extended_plus_0.25.inst.cfg b/resources/variants/ultimaker2_extended_plus_0.25.inst.cfg index c65940251c..7e67824d16 100644 --- a/resources/variants/ultimaker2_extended_plus_0.25.inst.cfg +++ b/resources/variants/ultimaker2_extended_plus_0.25.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2_extended_plus [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_extended_plus_0.4.inst.cfg b/resources/variants/ultimaker2_extended_plus_0.4.inst.cfg index 7493f2af44..1150c6127c 100644 --- a/resources/variants/ultimaker2_extended_plus_0.4.inst.cfg +++ b/resources/variants/ultimaker2_extended_plus_0.4.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2_extended_plus [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_extended_plus_0.6.inst.cfg b/resources/variants/ultimaker2_extended_plus_0.6.inst.cfg index c4a3ab6340..fbdef77918 100644 --- a/resources/variants/ultimaker2_extended_plus_0.6.inst.cfg +++ b/resources/variants/ultimaker2_extended_plus_0.6.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2_extended_plus [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_extended_plus_0.8.inst.cfg b/resources/variants/ultimaker2_extended_plus_0.8.inst.cfg index e77ec2a5c2..106537e0a7 100644 --- a/resources/variants/ultimaker2_extended_plus_0.8.inst.cfg +++ b/resources/variants/ultimaker2_extended_plus_0.8.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2_extended_plus [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_plus_0.25.inst.cfg b/resources/variants/ultimaker2_plus_0.25.inst.cfg index 7fd7f3980f..c07b80c246 100644 --- a/resources/variants/ultimaker2_plus_0.25.inst.cfg +++ b/resources/variants/ultimaker2_plus_0.25.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_plus_0.4.inst.cfg b/resources/variants/ultimaker2_plus_0.4.inst.cfg index 3b54e0cdef..623fffbeb9 100644 --- a/resources/variants/ultimaker2_plus_0.4.inst.cfg +++ b/resources/variants/ultimaker2_plus_0.4.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_plus_0.6.inst.cfg b/resources/variants/ultimaker2_plus_0.6.inst.cfg index d8fea055e5..b57fa81dfe 100644 --- a/resources/variants/ultimaker2_plus_0.6.inst.cfg +++ b/resources/variants/ultimaker2_plus_0.6.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_plus_0.8.inst.cfg b/resources/variants/ultimaker2_plus_0.8.inst.cfg index 3ae902ac2f..702ec2ef31 100644 --- a/resources/variants/ultimaker2_plus_0.8.inst.cfg +++ b/resources/variants/ultimaker2_plus_0.8.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker3_aa0.25.inst.cfg b/resources/variants/ultimaker3_aa0.25.inst.cfg index b46fdf5dfb..fc8cc3b090 100644 --- a/resources/variants/ultimaker3_aa0.25.inst.cfg +++ b/resources/variants/ultimaker3_aa0.25.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker3_aa0.8.inst.cfg b/resources/variants/ultimaker3_aa0.8.inst.cfg index 56740233dd..308bed6fcb 100644 --- a/resources/variants/ultimaker3_aa0.8.inst.cfg +++ b/resources/variants/ultimaker3_aa0.8.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker3_aa04.inst.cfg b/resources/variants/ultimaker3_aa04.inst.cfg index ce91e89d26..25230cd30b 100644 --- a/resources/variants/ultimaker3_aa04.inst.cfg +++ b/resources/variants/ultimaker3_aa04.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker3_bb0.8.inst.cfg b/resources/variants/ultimaker3_bb0.8.inst.cfg index ace0bf3a94..5ccf2816ff 100644 --- a/resources/variants/ultimaker3_bb0.8.inst.cfg +++ b/resources/variants/ultimaker3_bb0.8.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker3_bb04.inst.cfg b/resources/variants/ultimaker3_bb04.inst.cfg index d571cabc9b..d919e5aab7 100644 --- a/resources/variants/ultimaker3_bb04.inst.cfg +++ b/resources/variants/ultimaker3_bb04.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker3_extended_aa0.25.inst.cfg b/resources/variants/ultimaker3_extended_aa0.25.inst.cfg index 714b017653..ce0f20fa7e 100644 --- a/resources/variants/ultimaker3_extended_aa0.25.inst.cfg +++ b/resources/variants/ultimaker3_extended_aa0.25.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker3_extended [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker3_extended_aa0.8.inst.cfg b/resources/variants/ultimaker3_extended_aa0.8.inst.cfg index f72c96b551..f209508875 100644 --- a/resources/variants/ultimaker3_extended_aa0.8.inst.cfg +++ b/resources/variants/ultimaker3_extended_aa0.8.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker3_extended [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker3_extended_aa04.inst.cfg b/resources/variants/ultimaker3_extended_aa04.inst.cfg index f354784fc6..714d19051f 100644 --- a/resources/variants/ultimaker3_extended_aa04.inst.cfg +++ b/resources/variants/ultimaker3_extended_aa04.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker3_extended [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker3_extended_bb0.8.inst.cfg b/resources/variants/ultimaker3_extended_bb0.8.inst.cfg index fe760c93b8..528c7f70ec 100644 --- a/resources/variants/ultimaker3_extended_bb0.8.inst.cfg +++ b/resources/variants/ultimaker3_extended_bb0.8.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker3_extended [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker3_extended_bb04.inst.cfg b/resources/variants/ultimaker3_extended_bb04.inst.cfg index 742dc9896e..5ee562ee38 100644 --- a/resources/variants/ultimaker3_extended_bb04.inst.cfg +++ b/resources/variants/ultimaker3_extended_bb04.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker3_extended [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker_s5_aa0.25.inst.cfg b/resources/variants/ultimaker_s5_aa0.25.inst.cfg index 643513faad..ebdb096b6f 100644 --- a/resources/variants/ultimaker_s5_aa0.25.inst.cfg +++ b/resources/variants/ultimaker_s5_aa0.25.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker_s5_aa0.8.inst.cfg b/resources/variants/ultimaker_s5_aa0.8.inst.cfg index eca8c400d0..d8ff1c020e 100644 --- a/resources/variants/ultimaker_s5_aa0.8.inst.cfg +++ b/resources/variants/ultimaker_s5_aa0.8.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker_s5_aa04.inst.cfg b/resources/variants/ultimaker_s5_aa04.inst.cfg index b5b694d0c1..ac377e3e78 100644 --- a/resources/variants/ultimaker_s5_aa04.inst.cfg +++ b/resources/variants/ultimaker_s5_aa04.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker_s5_aluminum.inst.cfg b/resources/variants/ultimaker_s5_aluminum.inst.cfg index 1018b7e5ab..ca457bd7e7 100644 --- a/resources/variants/ultimaker_s5_aluminum.inst.cfg +++ b/resources/variants/ultimaker_s5_aluminum.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = buildplate diff --git a/resources/variants/ultimaker_s5_bb0.8.inst.cfg b/resources/variants/ultimaker_s5_bb0.8.inst.cfg index c1c5c1a10b..bb7f2d1420 100644 --- a/resources/variants/ultimaker_s5_bb0.8.inst.cfg +++ b/resources/variants/ultimaker_s5_bb0.8.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker_s5_bb04.inst.cfg b/resources/variants/ultimaker_s5_bb04.inst.cfg index b5ff8d51f6..cda1036507 100644 --- a/resources/variants/ultimaker_s5_bb04.inst.cfg +++ b/resources/variants/ultimaker_s5_bb04.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker_s5_cc06.inst.cfg b/resources/variants/ultimaker_s5_cc06.inst.cfg index 7adf7ab7a0..afbeb44462 100644 --- a/resources/variants/ultimaker_s5_cc06.inst.cfg +++ b/resources/variants/ultimaker_s5_cc06.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker_s5_glass.inst.cfg b/resources/variants/ultimaker_s5_glass.inst.cfg index d74eb3c6c9..e7431c933f 100644 --- a/resources/variants/ultimaker_s5_glass.inst.cfg +++ b/resources/variants/ultimaker_s5_glass.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = buildplate From 265a1b3fa0bf472da347328a46996df7f11a2527 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 27 Dec 2018 16:50:05 +0100 Subject: [PATCH 1087/1240] Add version upgrade plug-in for 4.0 to 4.1 This currently only updates the stack files, but all files have to be upgraded because of that stupid setting_version. Contributes to issue CURA-5848. --- .../VersionUpgrade34to35.py | 4 +- .../VersionUpgrade34to35/plugin.json | 2 +- .../VersionUpgrade40to41.py | 86 +++++++++++++++++++ .../VersionUpgrade40to41/__init__.py | 39 +++++++++ .../VersionUpgrade40to41/plugin.json | 8 ++ 5 files changed, 136 insertions(+), 3 deletions(-) create mode 100644 plugins/VersionUpgrade/VersionUpgrade40to41/VersionUpgrade40to41.py create mode 100644 plugins/VersionUpgrade/VersionUpgrade40to41/__init__.py create mode 100644 plugins/VersionUpgrade/VersionUpgrade40to41/plugin.json diff --git a/plugins/VersionUpgrade/VersionUpgrade34to35/VersionUpgrade34to35.py b/plugins/VersionUpgrade/VersionUpgrade34to35/VersionUpgrade34to35.py index d930b6e217..8e45d7cf73 100644 --- a/plugins/VersionUpgrade/VersionUpgrade34to35/VersionUpgrade34to35.py +++ b/plugins/VersionUpgrade/VersionUpgrade34to35/VersionUpgrade34to35.py @@ -63,9 +63,9 @@ _RENAMED_MATERIAL_PROFILES = { ## Upgrades configurations from the state they were in at version 3.4 to the # state they should be in at version 3.5. class VersionUpgrade34to35(VersionUpgrade): - ## Gets the version number from a CFG file in Uranium's 3.3 format. + ## Gets the version number from a CFG file in Uranium's 3.4 format. # - # Since the format may change, this is implemented for the 3.3 format only + # Since the format may change, this is implemented for the 3.4 format only # and needs to be included in the version upgrade system rather than # globally in Uranium. # diff --git a/plugins/VersionUpgrade/VersionUpgrade34to35/plugin.json b/plugins/VersionUpgrade/VersionUpgrade34to35/plugin.json index 02635ec606..71b13ee5a9 100644 --- a/plugins/VersionUpgrade/VersionUpgrade34to35/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade34to35/plugin.json @@ -1,4 +1,4 @@ - { +{ "name": "Version Upgrade 3.4 to 3.5", "author": "Ultimaker B.V.", "version": "1.0.1", diff --git a/plugins/VersionUpgrade/VersionUpgrade40to41/VersionUpgrade40to41.py b/plugins/VersionUpgrade/VersionUpgrade40to41/VersionUpgrade40to41.py new file mode 100644 index 0000000000..ac54b7c8e0 --- /dev/null +++ b/plugins/VersionUpgrade/VersionUpgrade40to41/VersionUpgrade40to41.py @@ -0,0 +1,86 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +import configparser +import io +from typing import Dict, List, Tuple + +from UM.VersionUpgrade import VersionUpgrade + +_renamed_quality_profiles = { + "gmax15plus_pla_dual_normal": "gmax15plus_global_dual_normal", + "gmax15plus_pla_dual_thick": "gmax15plus_global_dual_thick", + "gmax15plus_pla_dual_thin": "gmax15plus_global_dual_thin", + "gmax15plus_pla_dual_very_thick": "gmax15plus_global_dual_very_thick", + "gmax15plus_pla_normal": "gmax15plus_global_normal", + "gmax15plus_pla_thick": "gmax15plus_global_thick", + "gmax15plus_pla_thin": "gmax15plus_global_thin", + "gmax15plus_pla_very_thick": "gmax15plus_global_very_thick" +} # type: Dict[str, str] + +## Upgrades configurations from the state they were in at version 4.0 to the +# state they should be in at version 4.1. +class VersionUpgrade40to41(VersionUpgrade): + ## Gets the version number from a CFG file in Uranium's 4.0 format. + # + # Since the format may change, this is implemented for the 4.0 format only + # and needs to be included in the version upgrade system rather than + # globally in Uranium. + # + # \param serialised The serialised form of a CFG file. + # \return The version number stored in the CFG file. + # \raises ValueError The format of the version number in the file is + # incorrect. + # \raises KeyError The format of the file is incorrect. + def getCfgVersion(self, serialised: str) -> int: + parser = configparser.ConfigParser(interpolation = None) + parser.read_string(serialised) + format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised. + setting_version = int(parser.get("metadata", "setting_version", fallback = "0")) + return format_version * 1000000 + setting_version + + ## Upgrades instance containers to have the new version + # number. + def upgradeInstanceContainer(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]: + parser = configparser.ConfigParser(interpolation = None) + parser.read_string(serialized) + + # Update version number. + parser["general"]["version"] = "4" + parser["metadata"]["setting_version"] = "6" + + result = io.StringIO() + parser.write(result) + return [filename], [result.getvalue()] + + ## Upgrades Preferences to have the new version number. + def upgradePreferences(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]: + parser = configparser.ConfigParser(interpolation = None) + parser.read_string(serialized) + + # Update version number. + parser["general"]["version"] = "6" + if "metadata" not in parser: + parser["metadata"] = {} + parser["metadata"]["setting_version"] = "6" + + result = io.StringIO() + parser.write(result) + return [filename], [result.getvalue()] + + ## Upgrades stacks to have the new version number. + def upgradeStack(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]: + parser = configparser.ConfigParser(interpolation = None) + parser.read_string(serialized) + + # Update version number. + parser["general"]["version"] = "4" + parser["metadata"]["setting_version"] = "6" + + #Update the name of the quality profile. + if parser["containers"]["4"] in _renamed_quality_profiles: + parser["containers"]["4"] = _renamed_quality_profiles[parser["containers"]["4"]] + + result = io.StringIO() + parser.write(result) + return [filename], [result.getvalue()] \ No newline at end of file diff --git a/plugins/VersionUpgrade/VersionUpgrade40to41/__init__.py b/plugins/VersionUpgrade/VersionUpgrade40to41/__init__.py new file mode 100644 index 0000000000..757a7a51c0 --- /dev/null +++ b/plugins/VersionUpgrade/VersionUpgrade40to41/__init__.py @@ -0,0 +1,39 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. + +from typing import Any, Dict, TYPE_CHECKING + +from . import VersionUpgrade40to41 + +if TYPE_CHECKING: + from UM.Application import Application + +upgrade = VersionUpgrade40to41.VersionUpgrade40to41() + +def getMetaData() -> Dict[str, Any]: + return { + "version_upgrade": { + # From To Upgrade function + ("machine_stack", 4000005): ("machine_stack", 4000006, upgrade.upgradeStack), + ("extruder_train", 4000005): ("extruder_train", 4000006, upgrade.upgradeStack), + ("preferences", 6000005): ("preferences", 6000006, upgrade.upgradePreferences), + ("definition_changes", 4000005): ("definition_changes", 4000006, upgrade.upgradeInstanceContainer), + ("quality_changes", 4000005): ("quality_changes", 4000006, upgrade.upgradeInstanceContainer), + ("quality", 4000005): ("quality", 4000006, upgrade.upgradeInstanceContainer), + ("user", 4000005): ("user", 4000006, upgrade.upgradeInstanceContainer), + }, + "sources": { + "machine_stack": { + "get_version": upgrade.getCfgVersion, + "location": {"./machine_instances"} + }, + "extruder_train": { + "get_version": upgrade.getCfgVersion, + "location": {"./extruders"} + } + } + } + + +def register(app: "Application") -> Dict[str, Any]: + return { "version_upgrade": upgrade } diff --git a/plugins/VersionUpgrade/VersionUpgrade40to41/plugin.json b/plugins/VersionUpgrade/VersionUpgrade40to41/plugin.json new file mode 100644 index 0000000000..b1c6d75669 --- /dev/null +++ b/plugins/VersionUpgrade/VersionUpgrade40to41/plugin.json @@ -0,0 +1,8 @@ +{ + "name": "Version Upgrade 4.0 to 4.1", + "author": "Ultimaker B.V.", + "version": "1.0.1", + "description": "Upgrades configurations from Cura 4.0 to Cura 4.1.", + "api": "6.0", + "i18n-catalog": "cura" +} From 52d97dfbd21bfc0d190955734163a702cefdfe86 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 31 Dec 2018 09:32:16 +0100 Subject: [PATCH 1088/1240] Change back the behavior of the output device selector to the old way Although the new way was discussed with our UXers, we got a number of reports that it was confusing. I also accidentally started a print while using it --- resources/qml/ActionPanel/OutputDevicesActionButton.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/qml/ActionPanel/OutputDevicesActionButton.qml b/resources/qml/ActionPanel/OutputDevicesActionButton.qml index fc0f9b8303..3bfaab0fc1 100644 --- a/resources/qml/ActionPanel/OutputDevicesActionButton.qml +++ b/resources/qml/ActionPanel/OutputDevicesActionButton.qml @@ -93,7 +93,6 @@ Item onClicked: { UM.OutputDeviceManager.setActiveDevice(model.id) - widget.requestWriteToDevice() popup.close() } } From a16e40650754f45f178cdc33f0ef9076863cd55b Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 31 Dec 2018 11:01:30 +0100 Subject: [PATCH 1089/1240] Properly display the full type + brand of unknown material CURA-6033 --- .../src/ClusterUM3OutputDevice.py | 3 ++- .../ConfigurationMenu/ConfigurationItem.qml | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index bebccc54e3..5e8aaa9fa9 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -620,8 +620,9 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): if material_group_list is None: material_name = i18n_catalog.i18nc("@label:material", "Empty") if len(material_data.get("guid", "")) == 0 \ else i18n_catalog.i18nc("@label:material", "Unknown") + return MaterialOutputModel(guid = material_data.get("guid", ""), - type = material_data.get("type", ""), + type = material_data.get("material", ""), color = material_data.get("color", ""), brand = material_data.get("brand", ""), name = material_data.get("name", material_name) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 75a1b21186..51ba77d012 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -105,10 +105,22 @@ Button for (var index in extruderConfigurations) { var name = extruderConfigurations[index].material ? extruderConfigurations[index].material.name : "" - if (name == "" || name == "Unknown") { - unknownMaterials.push(extruderConfigurations[index].material.brand ? extruderConfigurations[index].material.brand : "Unknown Brand") + var materialType = extruderConfigurations[index].material.type + if (extruderConfigurations[index].material.type == "") + { + materialType = "Unknown" + } + + var brand = extruderConfigurations[index].material.brand + if (brand == "") + { + brand = "Unknown" + } + + name = materialType + " (" + brand + ")" + unknownMaterials.push(name) } } From c333899d18dd7ecb548ca1066e7eb22127c66dff Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 31 Dec 2018 11:09:10 +0100 Subject: [PATCH 1090/1240] Fix reference to renamed file --- plugins/SimulationView/SimulationView.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/SimulationView/SimulationView.py b/plugins/SimulationView/SimulationView.py index ae01b7cdb0..3b2db2efac 100644 --- a/plugins/SimulationView/SimulationView.py +++ b/plugins/SimulationView/SimulationView.py @@ -50,7 +50,7 @@ catalog = i18nCatalog("cura") ## View used to display g-code paths. class SimulationView(CuraView): - # Must match SimulationView.qml + # Must match SimulationViewMenuComponent.qml LAYER_VIEW_TYPE_MATERIAL_TYPE = 0 LAYER_VIEW_TYPE_LINE_TYPE = 1 LAYER_VIEW_TYPE_FEEDRATE = 2 From fd9b29fee2558e9e387d2cdb9f74fed5c69bbf53 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 31 Dec 2018 11:09:39 +0100 Subject: [PATCH 1091/1240] Rename printersModel to GlobalStacksModel This is a better description for the model anyway. CURA-6011 --- cura/CuraApplication.py | 4 ++-- cura/{PrintersModel.py => GlobalStacksModel.py} | 3 ++- resources/qml/Menus/LocalPrinterMenu.qml | 2 +- resources/qml/Menus/NetworkPrinterMenu.qml | 2 +- resources/qml/PrinterSelector/MachineSelectorList.qml | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) rename cura/{PrintersModel.py => GlobalStacksModel.py} (98%) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 905c367cd1..0edf6c1899 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -51,7 +51,7 @@ from cura.Arranging.ArrangeObjectsJob import ArrangeObjectsJob from cura.Arranging.ArrangeObjectsAllBuildPlatesJob import ArrangeObjectsAllBuildPlatesJob from cura.Arranging.ShapeArray import ShapeArray from cura.MultiplyObjectsJob import MultiplyObjectsJob -from cura.PrintersModel import PrintersModel +from cura.GlobalStacksModel import GlobalStacksModel from cura.Scene.ConvexHullDecorator import ConvexHullDecorator from cura.Operations.SetParentOperation import SetParentOperation from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator @@ -971,7 +971,7 @@ class CuraApplication(QtApplication): qmlRegisterType(MultiBuildPlateModel, "Cura", 1, 0, "MultiBuildPlateModel") qmlRegisterType(InstanceContainer, "Cura", 1, 0, "InstanceContainer") qmlRegisterType(ExtrudersModel, "Cura", 1, 0, "ExtrudersModel") - qmlRegisterType(PrintersModel, "Cura", 1, 0, "PrintersModel") + qmlRegisterType(GlobalStacksModel, "Cura", 1, 0, "GlobalStacksModel") qmlRegisterType(FavoriteMaterialsModel, "Cura", 1, 0, "FavoriteMaterialsModel") qmlRegisterType(GenericMaterialsModel, "Cura", 1, 0, "GenericMaterialsModel") diff --git a/cura/PrintersModel.py b/cura/GlobalStacksModel.py similarity index 98% rename from cura/PrintersModel.py rename to cura/GlobalStacksModel.py index 8b5d2f6cc9..8ae6b05291 100644 --- a/cura/PrintersModel.py +++ b/cura/GlobalStacksModel.py @@ -12,7 +12,8 @@ from cura.PrinterOutputDevice import ConnectionType from cura.Settings.GlobalStack import GlobalStack -class PrintersModel(ListModel): + +class GlobalStacksModel(ListModel): NameRole = Qt.UserRole + 1 IdRole = Qt.UserRole + 2 HasRemoteConnectionRole = Qt.UserRole + 3 diff --git a/resources/qml/Menus/LocalPrinterMenu.qml b/resources/qml/Menus/LocalPrinterMenu.qml index e7c5037814..4da1de2abf 100644 --- a/resources/qml/Menus/LocalPrinterMenu.qml +++ b/resources/qml/Menus/LocalPrinterMenu.qml @@ -9,7 +9,7 @@ import Cura 1.0 as Cura Instantiator { - model: Cura.PrintersModel {} + model: Cura.GlobalStacksModel {} MenuItem { diff --git a/resources/qml/Menus/NetworkPrinterMenu.qml b/resources/qml/Menus/NetworkPrinterMenu.qml index 8c607bc5ae..3cb0aae016 100644 --- a/resources/qml/Menus/NetworkPrinterMenu.qml +++ b/resources/qml/Menus/NetworkPrinterMenu.qml @@ -9,7 +9,7 @@ import Cura 1.0 as Cura Instantiator { - model: Cura.PrintersModel {} + model: Cura.GlobalStacksModel {} MenuItem { text: model.metadata["connect_group_name"] diff --git a/resources/qml/PrinterSelector/MachineSelectorList.qml b/resources/qml/PrinterSelector/MachineSelectorList.qml index ea8068fa95..62ecf48cc5 100644 --- a/resources/qml/PrinterSelector/MachineSelectorList.qml +++ b/resources/qml/PrinterSelector/MachineSelectorList.qml @@ -11,7 +11,7 @@ ListView { id: listView height: childrenRect.height - model: Cura.PrintersModel {} + model: Cura.GlobalStacksModel {} section.property: "hasRemoteConnection" section.delegate: Label From 1277fbabc588634a78b6544f10abdcbc16f9c8ad Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 31 Dec 2018 11:15:03 +0100 Subject: [PATCH 1092/1240] Fix connection type not always being seen correctly CURA-6011 --- cura/GlobalStacksModel.py | 3 +-- cura/Settings/MachineManager.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/cura/GlobalStacksModel.py b/cura/GlobalStacksModel.py index 8ae6b05291..b0052900cd 100644 --- a/cura/GlobalStacksModel.py +++ b/cura/GlobalStacksModel.py @@ -54,9 +54,8 @@ class GlobalStacksModel(ListModel): container_stacks = ContainerRegistry.getInstance().findContainerStacks(type = "machine") for container_stack in container_stacks: - connection_type = container_stack.getMetaDataEntry("connection_type") + connection_type = int(container_stack.getMetaDataEntry("connection_type", "-1")) has_remote_connection = connection_type in [ConnectionType.NetworkConnection.value, ConnectionType.CloudConnection.value] - if container_stack.getMetaDataEntry("hidden", False) in ["True", True]: continue diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 2185bbce9d..5b99b73b3c 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -527,7 +527,7 @@ class MachineManager(QObject): @pyqtProperty(bool, notify = printerConnectedStatusChanged) def activeMachineHasRemoteConnection(self) -> bool: if self._global_container_stack: - connection_type = self._global_container_stack.getMetaDataEntry("connection_type") + connection_type = int(self._global_container_stack.getMetaDataEntry("connection_type", "-1")) return connection_type in [ConnectionType.NetworkConnection.value, ConnectionType.CloudConnection.value] return False From 89040b6d8f9957d41b92c7b88bc78f6fb25fca2e Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 31 Dec 2018 11:18:17 +0100 Subject: [PATCH 1093/1240] Remove unneeded nameChanged signal connection CURA-6011 --- cura/GlobalStacksModel.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cura/GlobalStacksModel.py b/cura/GlobalStacksModel.py index b0052900cd..3ad110b7d0 100644 --- a/cura/GlobalStacksModel.py +++ b/cura/GlobalStacksModel.py @@ -42,14 +42,8 @@ class GlobalStacksModel(ListModel): if isinstance(container, GlobalStack): self._update() - ## Handler for container name change events. - def _onContainerNameChanged(self): - self._update() - def _update(self) -> None: items = [] - for container in self._container_stacks: - container.nameChanged.disconnect(self._onContainerNameChanged) container_stacks = ContainerRegistry.getInstance().findContainerStacks(type = "machine") From d9d1c93bd08d262a52aafe61024498ea4421f9a3 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 31 Dec 2018 11:25:23 +0100 Subject: [PATCH 1094/1240] Use "NotConnected" as default for the connection state CURA-6011 --- cura/GlobalStacksModel.py | 2 +- cura/PrinterOutputDevice.py | 4 ++-- cura/Settings/MachineManager.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cura/GlobalStacksModel.py b/cura/GlobalStacksModel.py index 3ad110b7d0..939809151d 100644 --- a/cura/GlobalStacksModel.py +++ b/cura/GlobalStacksModel.py @@ -48,7 +48,7 @@ class GlobalStacksModel(ListModel): container_stacks = ContainerRegistry.getInstance().findContainerStacks(type = "machine") for container_stack in container_stacks: - connection_type = int(container_stack.getMetaDataEntry("connection_type", "-1")) + connection_type = int(container_stack.getMetaDataEntry("connection_type", ConnectionType.NotConnected.value)) has_remote_connection = connection_type in [ConnectionType.NetworkConnection.value, ConnectionType.CloudConnection.value] if container_stack.getMetaDataEntry("hidden", False) in ["True", True]: continue diff --git a/cura/PrinterOutputDevice.py b/cura/PrinterOutputDevice.py index aeeb0381b2..99e8835c2f 100644 --- a/cura/PrinterOutputDevice.py +++ b/cura/PrinterOutputDevice.py @@ -36,7 +36,7 @@ class ConnectionState(IntEnum): class ConnectionType(IntEnum): - Unknown = 0 + NotConnected = 0 UsbConnection = 1 NetworkConnection = 2 CloudConnection = 3 @@ -74,7 +74,7 @@ class PrinterOutputDevice(QObject, OutputDevice): # Signal to indicate that the configuration of one of the printers has changed. uniqueConfigurationsChanged = pyqtSignal() - def __init__(self, device_id: str, connection_type: "ConnectionType" = ConnectionType.Unknown, parent: QObject = None) -> None: + def __init__(self, device_id: str, connection_type: "ConnectionType" = ConnectionType.NotConnected, parent: QObject = None) -> None: super().__init__(device_id = device_id, parent = parent) # type: ignore # MyPy complains with the multiple inheritance self._printers = [] # type: List[PrinterOutputModel] diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 5b99b73b3c..32b83ead28 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -527,7 +527,7 @@ class MachineManager(QObject): @pyqtProperty(bool, notify = printerConnectedStatusChanged) def activeMachineHasRemoteConnection(self) -> bool: if self._global_container_stack: - connection_type = int(self._global_container_stack.getMetaDataEntry("connection_type", "-1")) + connection_type = int(self._global_container_stack.getMetaDataEntry("connection_type", ConnectionType.NotConnected.value)) return connection_type in [ConnectionType.NetworkConnection.value, ConnectionType.CloudConnection.value] return False From b5c406dc43930f2eca4fe866e07cc96cbb237b98 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 31 Dec 2018 13:11:24 +0100 Subject: [PATCH 1095/1240] Make default font size slightly smaller In accordance with the designs. Contributes to issue CURA-6025. --- resources/themes/cura-light/theme.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 5be1de1cfb..b14fdf0b4f 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -25,17 +25,17 @@ "family": "Noto Sans" }, "default": { - "size": 1.0, + "size": 0.95, "weight": 40, "family": "Noto Sans" }, "default_bold": { - "size": 1.0, + "size": 0.95, "weight": 60, "family": "Noto Sans" }, "default_italic": { - "size": 1.0, + "size": 0.95, "weight": 40, "italic": true, "family": "Noto Sans" From 8dbebaa23c0ba8132e0245a62221cababb6bf3b4 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 31 Dec 2018 13:14:08 +0100 Subject: [PATCH 1096/1240] Ensure that the machine selector list scales properly The old implementation directly listend to the height (instead of childrenRect). This caused issues, since other things apart from adding / removing machines could influence the height. CURA-6060 --- resources/qml/PrinterSelector/MachineSelector.qml | 8 +++++--- resources/qml/PrinterSelector/MachineSelectorList.qml | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index 28e01c7ae9..9f0d3b4ac6 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -93,14 +93,16 @@ Cura.ExpandablePopup width: scroll.width - scroll.leftPadding - scroll.rightPadding property real maximumHeight: UM.Theme.getSize("machine_selector_widget_content").height - buttonRow.height - onHeightChanged: + // We use an extra property here, since we only want to to be informed about the content size changes. + onContentHeightChanged: { - scroll.height = Math.min(height, maximumHeight) + scroll.height = Math.min(contentHeight, maximumHeight) popup.height = scroll.height + buttonRow.height } + Component.onCompleted: { - scroll.height = Math.min(height, maximumHeight) + scroll.height = Math.min(contentHeight, maximumHeight) popup.height = scroll.height + buttonRow.height } diff --git a/resources/qml/PrinterSelector/MachineSelectorList.qml b/resources/qml/PrinterSelector/MachineSelectorList.qml index 62ecf48cc5..604e0f668d 100644 --- a/resources/qml/PrinterSelector/MachineSelectorList.qml +++ b/resources/qml/PrinterSelector/MachineSelectorList.qml @@ -10,9 +10,9 @@ import Cura 1.0 as Cura ListView { id: listView - height: childrenRect.height model: Cura.GlobalStacksModel {} section.property: "hasRemoteConnection" + property real contentHeight: childrenRect.height section.delegate: Label { From c6fb0d70af8877f840f6cbbc8348eaa4dc63b8de Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 31 Dec 2018 13:22:53 +0100 Subject: [PATCH 1097/1240] Use medium font size for everything in stage menu Except the material manufacturer, which should remain default. Contributes to issue CURA-6025. --- plugins/SimulationView/SimulationViewMenuComponent.qml | 4 ++-- resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml | 4 ++-- resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml | 4 ++++ resources/qml/ViewsSelector.qml | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/plugins/SimulationView/SimulationViewMenuComponent.qml b/plugins/SimulationView/SimulationViewMenuComponent.qml index fe32fe9eb1..4c952d4c43 100644 --- a/plugins/SimulationView/SimulationViewMenuComponent.qml +++ b/plugins/SimulationView/SimulationViewMenuComponent.qml @@ -43,7 +43,7 @@ Cura.ExpandableComponent verticalAlignment: Text.AlignVCenter height: parent.height elide: Text.ElideRight - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("medium") color: UM.Theme.getColor("text_medium") renderType: Text.NativeRendering } @@ -60,7 +60,7 @@ Cura.ExpandableComponent } height: parent.height elide: Text.ElideRight - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("medium") color: UM.Theme.getColor("text") renderType: Text.NativeRendering } diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index 207b65afc7..3001efac54 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -86,7 +86,7 @@ Cura.ExpandablePopup { text: model.material elide: Text.ElideRight - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("medium") color: UM.Theme.getColor("text") renderType: Text.NativeRendering @@ -107,7 +107,7 @@ Cura.ExpandablePopup { text: catalog.i18nc("@label", "Select configuration") elide: Text.ElideRight - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("medium") color: UM.Theme.getColor("text") renderType: Text.NativeRendering diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml index 94da5bdd6f..5ae9488cd3 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml @@ -29,6 +29,7 @@ RowLayout } return "" } + font: UM.Theme.getFont("medium") UM.SettingPropertyProvider { @@ -43,6 +44,7 @@ RowLayout { source: UM.Theme.getIcon("category_infill") text: Cura.MachineManager.activeStack ? parseInt(infillDensity.properties.value) + "%" : "0%" + font: UM.Theme.getFont("medium") UM.SettingPropertyProvider { @@ -57,6 +59,7 @@ RowLayout { source: UM.Theme.getIcon("category_support") text: supportEnabled.properties.value == "True" ? enabledText : disabledText + font: UM.Theme.getFont("medium") UM.SettingPropertyProvider { @@ -71,6 +74,7 @@ RowLayout { source: UM.Theme.getIcon("category_adhesion") text: platformAdhesionType.properties.value != "skirt" && platformAdhesionType.properties.value != "none" ? enabledText : disabledText + font: UM.Theme.getFont("medium") UM.SettingPropertyProvider { diff --git a/resources/qml/ViewsSelector.qml b/resources/qml/ViewsSelector.qml index 1f5a0bbc85..da9693be6e 100644 --- a/resources/qml/ViewsSelector.qml +++ b/resources/qml/ViewsSelector.qml @@ -51,7 +51,7 @@ Cura.ExpandablePopup verticalAlignment: Text.AlignVCenter height: parent.height elide: Text.ElideRight - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("medium") color: UM.Theme.getColor("text_medium") renderType: Text.NativeRendering } From d020a49b751d885ff366f648c1d1b0d0c46ee003 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 31 Dec 2018 13:26:55 +0100 Subject: [PATCH 1098/1240] Assign a font to Marketplace button Otherwise the font face may be different, and the theme has no effect on the font. Contributes to issue CURA-6025. --- resources/qml/MainWindow/MainWindowHeader.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/qml/MainWindow/MainWindowHeader.qml b/resources/qml/MainWindow/MainWindowHeader.qml index e3b7e199fa..3e296ead40 100644 --- a/resources/qml/MainWindow/MainWindowHeader.qml +++ b/resources/qml/MainWindow/MainWindowHeader.qml @@ -102,6 +102,7 @@ Item { id: label text: marketplaceButton.text + font: UM.Theme.getFont("default") color: marketplaceButton.hovered ? UM.Theme.getColor("main_window_header_background") : UM.Theme.getColor("primary_text") width: contentWidth verticalAlignment: Text.AlignVCenter From c74d6fc1e7c6fe01a4e03719861e580e5bbc4640 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 31 Dec 2018 13:32:49 +0100 Subject: [PATCH 1099/1240] Make printerTypeLabel smaller More in line with the theme. Contributes to issue CURA-6025. --- resources/qml/PrinterTypeLabel.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/PrinterTypeLabel.qml b/resources/qml/PrinterTypeLabel.qml index cfc9e56513..b445228e83 100644 --- a/resources/qml/PrinterTypeLabel.qml +++ b/resources/qml/PrinterTypeLabel.qml @@ -29,7 +29,7 @@ Item anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter renderType: Text.NativeRendering - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("small") color: UM.Theme.getColor("text") } } \ No newline at end of file From 8d8133d521d546b19bf32c2fa58a96894a7d152e Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 31 Dec 2018 13:34:57 +0100 Subject: [PATCH 1100/1240] Make synced material name larger than the rest In accordance with the designs. Contributes to issue CURA-6025. --- .../qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml index a344e31d4f..db6a97aa65 100644 --- a/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/PrintCoreConfiguration.qml @@ -39,7 +39,7 @@ Row text: printCoreConfiguration.material.brand ? printCoreConfiguration.material.name : " " //Use space so that the height is still correct. renderType: Text.NativeRendering elide: Text.ElideRight - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("medium") color: UM.Theme.getColor("text") } Label From 60a5438db6b10942357ba77d40b9d61202780886 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 31 Dec 2018 13:43:31 +0100 Subject: [PATCH 1101/1240] Make expandable component header fonts larger As per the design. Contributes to issue CURA-6025. --- resources/qml/ExpandableComponentHeader.qml | 2 +- resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml | 2 +- resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml | 2 +- resources/qml/ViewsSelector.qml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/qml/ExpandableComponentHeader.qml b/resources/qml/ExpandableComponentHeader.qml index 09ea262c82..94066340e3 100644 --- a/resources/qml/ExpandableComponentHeader.qml +++ b/resources/qml/ExpandableComponentHeader.qml @@ -25,7 +25,7 @@ Cura.RoundedRectangle { id: headerLabel text: "" - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("medium") renderType: Text.NativeRendering verticalAlignment: Text.AlignVCenter color: UM.Theme.getColor("small_button_text") diff --git a/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml index a3ed5040b7..18409dd43a 100644 --- a/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/AutoConfiguration.qml @@ -16,7 +16,7 @@ Item { id: header text: catalog.i18nc("@header", "Configurations") - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("medium") color: UM.Theme.getColor("small_button_text") height: contentHeight renderType: Text.NativeRendering diff --git a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml index 4d6d80c1b4..5cecda4e5c 100644 --- a/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml +++ b/resources/qml/Menus/ConfigurationMenu/CustomConfiguration.qml @@ -23,7 +23,7 @@ Item { id: header text: catalog.i18nc("@header", "Custom") - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("medium") color: UM.Theme.getColor("small_button_text") height: contentHeight renderType: Text.NativeRendering diff --git a/resources/qml/ViewsSelector.qml b/resources/qml/ViewsSelector.qml index da9693be6e..0e9be649db 100644 --- a/resources/qml/ViewsSelector.qml +++ b/resources/qml/ViewsSelector.qml @@ -68,7 +68,7 @@ Cura.ExpandablePopup } height: parent.height elide: Text.ElideRight - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("medium") color: UM.Theme.getColor("text") renderType: Text.NativeRendering } From 4be228a4e2e46e59a53c446224e6850518cba176 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 31 Dec 2018 13:43:47 +0100 Subject: [PATCH 1102/1240] Fix merging & grouping I made a boo-boo while adding typing. CURA-6058 --- cura/CuraApplication.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 07464c97ca..186d30fe89 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1404,7 +1404,7 @@ class CuraApplication(QtApplication): selected_nodes = Selection.getAllSelectedObjects().copy() for node in selected_nodes: parent = node.getParent() - if parent is not None and node in selected_nodes and not node.callDecoration("isGroup"): + if parent is not None and parent in selected_nodes and not parent.callDecoration("isGroup"): Selection.remove(node) # Move selected nodes into the group-node From 44f21b37ac613fc229e32fa507077b1641d2fede Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 31 Dec 2018 13:49:03 +0100 Subject: [PATCH 1103/1240] Make labels on left side of recommended mode larger Contributes to issue CURA-6025. --- .../Recommended/RecommendedAdhesionSelector.qml | 1 + .../Recommended/RecommendedInfillDensitySelector.qml | 1 + .../Recommended/RecommendedQualityProfileSelector.qml | 1 + .../Recommended/RecommendedSupportSelector.qml | 1 + 4 files changed, 4 insertions(+) diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedAdhesionSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedAdhesionSelector.qml index a5f35f333b..941199707c 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedAdhesionSelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedAdhesionSelector.qml @@ -26,6 +26,7 @@ Item anchors.left: parent.left source: UM.Theme.getIcon("category_adhesion") text: catalog.i18nc("@label", "Adhesion") + font: UM.Theme.getFont("medium") width: labelColumnWidth } diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml index 0da53cc1c1..b733a576e4 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml @@ -63,6 +63,7 @@ Item anchors.left: parent.left source: UM.Theme.getIcon("category_infill") text: catalog.i18nc("@label", "Infill") + " (%)" + font: UM.Theme.getFont("medium") width: labelColumnWidth } diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml index 1e71134404..af145410b5 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml @@ -173,6 +173,7 @@ Item id: qualityRowTitle source: UM.Theme.getIcon("category_layer_height") text: catalog.i18nc("@label", "Layer Height") + font: UM.Theme.getFont("medium") anchors.left: parent.left anchors.right: customisedSettings.left } diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml index 87fb664713..0e834ac4df 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedSupportSelector.qml @@ -27,6 +27,7 @@ Item visible: enableSupportCheckBox.visible source: UM.Theme.getIcon("category_support") text: catalog.i18nc("@label", "Support") + font: UM.Theme.getFont("medium") width: labelColumnWidth } From 4be9fbb75ec6f31d51fbed3e44086dc756b22f08 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 31 Dec 2018 13:57:49 +0100 Subject: [PATCH 1104/1240] Give tick marks a font Otherwise it uses your system's font, which is not consistent. Contributes to issue CURA-6025. --- .../Recommended/RecommendedInfillDensitySelector.qml | 1 + .../Recommended/RecommendedQualityProfileSelector.qml | 1 + 2 files changed, 2 insertions(+) diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml index b733a576e4..19f199fea6 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedInfillDensitySelector.qml @@ -141,6 +141,7 @@ Item Label { text: index + font: UM.Theme.getFont("default") visible: (index % 20) == 0 // Only show steps of 20% anchors.horizontalCenter: parent.horizontalCenter y: UM.Theme.getSize("thin_margin").height diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml index af145410b5..801e76382b 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedQualityProfileSelector.qml @@ -272,6 +272,7 @@ Item return Math.round((settingsColumnWidth / qualityModel.totalTicks) * index - (width / 2)) } } + font: UM.Theme.getFont("default") } } } From cf6cce5df0f4da01adbb8631d3349d4b8e8d2d33 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 31 Dec 2018 14:00:39 +0100 Subject: [PATCH 1105/1240] Make 'Profile' text larger As per the design. Contributes to issue CURA-6025. --- .../qml/PrintSetupSelector/Custom/GlobalProfileSelector.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/PrintSetupSelector/Custom/GlobalProfileSelector.qml b/resources/qml/PrintSetupSelector/Custom/GlobalProfileSelector.qml index 8baaf9a7ae..32c07a52a6 100644 --- a/resources/qml/PrintSetupSelector/Custom/GlobalProfileSelector.qml +++ b/resources/qml/PrintSetupSelector/Custom/GlobalProfileSelector.qml @@ -25,7 +25,7 @@ Item right: globalProfileSelection.left } text: catalog.i18nc("@label", "Profile") - font: UM.Theme.getFont("default") + font: UM.Theme.getFont("medium") color: UM.Theme.getColor("text") verticalAlignment: Text.AlignVCenter } From 66aeb2d1dd0bd8e59dbd4c988b282fbb664c48c9 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 31 Dec 2018 14:27:01 +0100 Subject: [PATCH 1106/1240] Give print time estimate a large font Contributes to issue CURA-6025. --- resources/qml/ActionPanel/OutputProcessWidget.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml index 03fa00d504..223ddacb31 100644 --- a/resources/qml/ActionPanel/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -51,7 +51,7 @@ Column text: preSlicedData ? catalog.i18nc("@label", "No time estimation available") : PrintInformation.currentPrintTime.getDisplayString(UM.DurationFormat.Long) source: UM.Theme.getIcon("clock") - font: UM.Theme.getFont("default_bold") + font: UM.Theme.getFont("large_bold") } Cura.IconWithText From c92a0f1fd43d79e24f70ff3e7f09774769743fcb Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 31 Dec 2018 15:09:15 +0100 Subject: [PATCH 1107/1240] Fix bold fonts on Linux I'm not sure how this performs on other platforms, but on mine it needs at least 63 weight for bold. This is because there is a normal font at weight 40 and a bold font at weight 85. From 63+ it rounds to the bold font with the closest weight then. Contributes to issue CURA-6025. --- resources/themes/cura-light/theme.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index b14fdf0b4f..dbd1ad34b1 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -11,7 +11,7 @@ }, "large_bold": { "size": 1.35, - "weight": 60, + "weight": 63, "family": "Noto Sans" }, "medium": { @@ -21,7 +21,7 @@ }, "medium_bold": { "size": 1.16, - "weight": 60, + "weight": 63, "family": "Noto Sans" }, "default": { @@ -31,7 +31,7 @@ }, "default_bold": { "size": 0.95, - "weight": 60, + "weight": 63, "family": "Noto Sans" }, "default_italic": { From df0351acd44386e6a50809d842c379cece3938d7 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 31 Dec 2018 15:14:23 +0100 Subject: [PATCH 1108/1240] Fix fonts of plug-in tiles Contributes to issue CURA-6025. --- plugins/Toolbox/resources/qml/SmallRatingWidget.qml | 2 +- plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml | 2 +- resources/qml/PrintSetupTooltip.qml | 3 ++- resources/qml/ToolTip.qml | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/plugins/Toolbox/resources/qml/SmallRatingWidget.qml b/plugins/Toolbox/resources/qml/SmallRatingWidget.qml index 4950ea9242..965b81dc0f 100644 --- a/plugins/Toolbox/resources/qml/SmallRatingWidget.qml +++ b/plugins/Toolbox/resources/qml/SmallRatingWidget.qml @@ -30,7 +30,7 @@ Row width: contentWidth anchors.verticalCenter: starIcon.verticalCenter color: starIcon.color - font: UM.Theme.getFont("small") + font: UM.Theme.getFont("default") renderType: Text.NativeRendering } } \ No newline at end of file diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml index 58e4f070e0..a11c6ee963 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml @@ -112,7 +112,7 @@ Item elide: Text.ElideRight width: parent.width wrapMode: Text.WordWrap - color: UM.Theme.getColor("text_medium") + color: UM.Theme.getColor("text") font: UM.Theme.getFont("default") anchors.top: name.bottom anchors.bottom: rating.top diff --git a/resources/qml/PrintSetupTooltip.qml b/resources/qml/PrintSetupTooltip.qml index 693b703813..6b1538d849 100644 --- a/resources/qml/PrintSetupTooltip.qml +++ b/resources/qml/PrintSetupTooltip.qml @@ -41,7 +41,8 @@ UM.PointingRectangle { base.opacity = 0; } - Label { + Label + { id: label; anchors { top: parent.top; diff --git a/resources/qml/ToolTip.qml b/resources/qml/ToolTip.qml index 87586f76d8..e82caf01b2 100644 --- a/resources/qml/ToolTip.qml +++ b/resources/qml/ToolTip.qml @@ -25,7 +25,7 @@ ToolTip text: "" delay: 500 font: UM.Theme.getFont("default") - + // If the text is not set, just set the height to 0 to prevent it from showing height: text != "" ? label.contentHeight + 2 * UM.Theme.getSize("thin_margin").width: 0 From 30ab1a957ff935872ba6cad4a8cf1fcc461bdc61 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 31 Dec 2018 15:18:50 +0100 Subject: [PATCH 1109/1240] Make headers of Marketplace large As per the design. Contributes to issue CURA-6025. --- plugins/Toolbox/resources/qml/ToolboxDownloadsGrid.qml | 2 +- plugins/Toolbox/resources/qml/ToolboxDownloadsShowcase.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsGrid.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsGrid.qml index 85f0ff8be4..a9fcb39b28 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsGrid.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsGrid.qml @@ -22,7 +22,7 @@ Column text: gridArea.heading width: parent.width color: UM.Theme.getColor("text_medium") - font: UM.Theme.getFont("medium") + font: UM.Theme.getFont("large") renderType: Text.NativeRendering } Grid diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcase.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcase.qml index 820b74554a..795622cf82 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcase.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcase.qml @@ -23,7 +23,7 @@ Rectangle text: catalog.i18nc("@label", "Featured") width: parent.width color: UM.Theme.getColor("text_medium") - font: UM.Theme.getFont("medium") + font: UM.Theme.getFont("large") renderType: Text.NativeRendering } Grid From 346c8209597ad3c7357320ae4cd6475d82a6ef41 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 31 Dec 2018 15:22:34 +0100 Subject: [PATCH 1110/1240] Remove hover color for Marketplace header tabs It was not desirable, as discussed with the designer. Contributes to issue CURA-6025. --- resources/themes/cura-light/theme.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index dbd1ad34b1..fa8f96b6b3 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -298,7 +298,7 @@ "printer_config_mismatch": [127, 127, 127, 255], "toolbox_header_button_text_active": [0, 0, 0, 255], - "toolbox_header_button_text_inactive": [128, 128, 128, 255], + "toolbox_header_button_text_inactive": [0, 0, 0, 255], "toolbox_header_button_text_hovered": [0, 0, 0, 255], "favorites_header_bar": [245, 245, 245, 255], From 7ef8287e52c686cab72cf674a7cbd7c3df713aa5 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 31 Dec 2018 15:33:37 +0100 Subject: [PATCH 1111/1240] Fix theming of back button in Marketplace Give it a bit more space for if the translated text is wider, and if it is wider also align it properly with the arrow. Contributes to issue CURA-6025. --- plugins/Toolbox/resources/qml/ToolboxBackColumn.qml | 9 +++++++-- plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml | 2 +- resources/themes/cura-light/theme.json | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxBackColumn.qml b/plugins/Toolbox/resources/qml/ToolboxBackColumn.qml index edb1967fee..dba9f19ccd 100644 --- a/plugins/Toolbox/resources/qml/ToolboxBackColumn.qml +++ b/plugins/Toolbox/resources/qml/ToolboxBackColumn.qml @@ -61,8 +61,13 @@ Item id: labelStyle text: control.text color: control.enabled ? (control.hovered ? UM.Theme.getColor("primary") : UM.Theme.getColor("text")) : UM.Theme.getColor("text_inactive") - font: UM.Theme.getFont("default_bold") - horizontalAlignment: Text.AlignRight + font: UM.Theme.getFont("medium_bold") + horizontalAlignment: Text.AlignLeft + anchors + { + left: parent.left + leftMargin: UM.Theme.getSize("default_margin").width + } width: control.width renderType: Text.NativeRendering } diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml index e1d01db59a..a85a69cbac 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledPage.qml @@ -37,7 +37,7 @@ ScrollView width: page.width text: catalog.i18nc("@title:tab", "Plugins") color: UM.Theme.getColor("text_medium") - font: UM.Theme.getFont("medium") + font: UM.Theme.getFont("large") renderType: Text.NativeRendering } Rectangle diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index fa8f96b6b3..e0a1c0f9bd 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -487,7 +487,7 @@ "toolbox_detail_header": [1.0, 14.0], "toolbox_detail_tile": [1.0, 8.0], "toolbox_back_column": [6.0, 1.0], - "toolbox_back_button": [4.0, 2.0], + "toolbox_back_button": [6.0, 2.0], "toolbox_installed_tile": [1.0, 8.0], "toolbox_property_label": [1.0, 2.0], "toolbox_heading_label": [1.0, 3.8], From 4177faa6eac5b65d796fe27f3e16a0c82ec1cf34 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 31 Dec 2018 15:37:31 +0100 Subject: [PATCH 1112/1240] Fix font usage in installed plug-ins Several different font sizes should be used here. Contributes to issue CURA-6025. --- plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml index 593e024309..333d4dd50a 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml @@ -49,13 +49,14 @@ Item width: parent.width height: Math.floor(UM.Theme.getSize("toolbox_property_label").height) wrapMode: Text.WordWrap - font: UM.Theme.getFont("default_bold") + font: UM.Theme.getFont("large_bold") color: pluginInfo.color renderType: Text.NativeRendering } Label { text: model.description + font: UM.Theme.getFont("default") maximumLineCount: 3 elide: Text.ElideRight width: parent.width @@ -82,6 +83,7 @@ Item return model.author_name } } + font: UM.Theme.getFont("medium") width: parent.width height: Math.floor(UM.Theme.getSize("toolbox_property_label").height) wrapMode: Text.WordWrap @@ -96,6 +98,7 @@ Item Label { text: model.version + font: UM.Theme.getFont("default") width: parent.width height: UM.Theme.getSize("toolbox_property_label").height color: UM.Theme.getColor("text") From 6092402a878c6ba4bab877b31dbf1033dc9c8a8e Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Mon, 31 Dec 2018 15:44:09 +0100 Subject: [PATCH 1113/1240] Fix 'Slicing...' message when not autoslicing Also changes the 'auto slicing' text to just 'slicing', because that's more what the user expects. Discussed with UX. Contributes to issue CURA-6025. --- resources/qml/ActionPanel/SliceProcessWidget.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index 6c3b136ca0..51c5e4cac7 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -44,9 +44,9 @@ Column { id: autoSlicingLabel width: parent.width - visible: prepareButtons.autoSlice && (widget.backendState == UM.Backend.Processing || widget.backendState == UM.Backend.NotStarted) + visible: progressBar.visible - text: catalog.i18nc("@label:PrintjobStatus", "Auto slicing...") + text: catalog.i18nc("@label:PrintjobStatus", "Slicing...") color: UM.Theme.getColor("text") font: UM.Theme.getFont("default") renderType: Text.NativeRendering From 0b0f674e4b4cf7ab470a374e3ba41c1fa989845f Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 2 Jan 2019 09:22:50 +0100 Subject: [PATCH 1114/1240] Remove unused code --- cura/Settings/MachineManager.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 32b83ead28..3af7c7fba8 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -64,8 +64,6 @@ class MachineManager(QObject): self._default_extruder_position = "0" # to be updated when extruders are switched on and off - self.machine_extruder_material_update_dict = collections.defaultdict(list) #type: Dict[str, List[Callable[[], None]]] - self._instance_container_timer = QTimer() # type: QTimer self._instance_container_timer.setInterval(250) self._instance_container_timer.setSingleShot(True) @@ -275,11 +273,6 @@ class MachineManager(QObject): extruder_stack.propertyChanged.connect(self._onPropertyChanged) extruder_stack.containersChanged.connect(self._onContainersChanged) - if self._global_container_stack.getId() in self.machine_extruder_material_update_dict: - for func in self.machine_extruder_material_update_dict[self._global_container_stack.getId()]: - self._application.callLater(func) - del self.machine_extruder_material_update_dict[self._global_container_stack.getId()] - self.activeQualityGroupChanged.emit() def _onActiveExtruderStackChanged(self) -> None: From e8febaff59dacd03ac71917b4508c68117a1ad2a Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 2 Jan 2019 09:23:20 +0100 Subject: [PATCH 1115/1240] Remove code duplication --- cura/Settings/ExtruderManager.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index 8fa0172305..a459d65ba3 100755 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -301,12 +301,7 @@ class ExtruderManager(QObject): global_stack = self._application.getGlobalContainerStack() if not global_stack: return [] - - result_tuple_list = sorted(list(global_stack.extruders.items()), key = lambda x: int(x[0])) - result_list = [item[1] for item in result_tuple_list] - - machine_extruder_count = global_stack.getProperty("machine_extruder_count", "value") - return result_list[:machine_extruder_count] + return global_stack.extruderList def _globalContainerStackChanged(self) -> None: # If the global container changed, the machine changed and might have extruders that were not registered yet From 2a4c66888e2432e932e2ffe472db021b9715df5e Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 2 Jan 2019 09:39:44 +0100 Subject: [PATCH 1116/1240] Use getNumInstances instead of counting the number of instances in machinemanager This should prevent having to initiate the cached values if we only want to know the number of settings. --- cura/Settings/MachineManager.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 3af7c7fba8..dec2524e6b 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -436,12 +436,12 @@ class MachineManager(QObject): if not self._global_container_stack: return False - if self._global_container_stack.getTop().findInstances(): + if self._global_container_stack.getTop().getNumInstances() != 0: return True stacks = ExtruderManager.getInstance().getActiveExtruderStacks() for stack in stacks: - if stack.getTop().findInstances(): + if stack.getTop().getNumInstances() != 0: return True return False @@ -451,10 +451,10 @@ class MachineManager(QObject): if not self._global_container_stack: return 0 num_user_settings = 0 - num_user_settings += len(self._global_container_stack.getTop().findInstances()) - stacks = ExtruderManager.getInstance().getActiveExtruderStacks() + num_user_settings += self._global_container_stack.getTop().getNumInstances() + stacks = self._global_container_stack.extruderList for stack in stacks: - num_user_settings += len(stack.getTop().findInstances()) + num_user_settings += stack.getTop().getNumInstances() return num_user_settings ## Delete a user setting from the global stack and all extruder stacks. From 11dfb9acdc04a047e9914db08a338c7252aecb49 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 2 Jan 2019 11:18:15 +0100 Subject: [PATCH 1117/1240] Remove PluginsPage.qml CURA-6066 --- resources/qml/Cura.qml | 3 --- 1 file changed, 3 deletions(-) diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 8a34c7e219..ca1c2e38c1 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -353,9 +353,6 @@ UM.MainWindow insertPage(4, catalog.i18nc("@title:tab", "Profiles"), Qt.resolvedUrl("Preferences/ProfilesPage.qml")); - // Remove plug-ins page because we will use the shiny new plugin browser: - removePage(5); - //Force refresh setPage(0); } From a0031853d365a3ad9d44f898be77df66899a3a9d Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 2 Jan 2019 11:32:49 +0100 Subject: [PATCH 1118/1240] Fix button color in toolpanel for dark theme --- resources/themes/cura-light/styles.qml | 2 +- resources/themes/cura-light/theme.json | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index 89729fc08c..2c4dab7c89 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -256,7 +256,7 @@ QtObject source: control.iconSource width: Theme.getSize("button_icon").width height: Theme.getSize("button_icon").height - color: Theme.getColor("toolbar_button_text") + color: Theme.getColor("icon") sourceSize: Theme.getSize("button_icon") } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 2b9ce3d218..d56869d895 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -118,7 +118,6 @@ "warning": [245, 166, 35, 255], "disabled": [229, 229, 229, 255], - "toolbar_button_text": [8, 7, 63, 255], "toolbar_button_hover": [232, 242, 252, 255], "toolbar_button_active": [232, 242, 252, 255], "toolbar_button_active_hover": [232, 242, 252, 255], From 9be1f8aa7bff7d3c8bc1b107c7656de920e86603 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 2 Jan 2019 11:33:49 +0100 Subject: [PATCH 1119/1240] Removed marketplace menu item from top menu It was a bit double since we had the marketplace button in the top bar --- resources/qml/MainWindow/ApplicationMenu.qml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/resources/qml/MainWindow/ApplicationMenu.qml b/resources/qml/MainWindow/ApplicationMenu.qml index 04c068cb54..a694b8e403 100644 --- a/resources/qml/MainWindow/ApplicationMenu.qml +++ b/resources/qml/MainWindow/ApplicationMenu.qml @@ -83,14 +83,6 @@ Item } } - Menu - { - id: plugin_menu - title: catalog.i18nc("@title:menu menubar:toplevel", "&Marketplace") - - MenuItem { action: Cura.Actions.browsePackages } - } - Menu { id: preferencesMenu From df23097a99b1b1d027b55c64332354ec5d070ae2 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 2 Jan 2019 11:36:07 +0100 Subject: [PATCH 1120/1240] Changed the label for when no remote configurations are loaded yet --- 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 684e575bfd..e57b21cb78 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml @@ -51,7 +51,7 @@ Item anchors.left: icon.right anchors.right: parent.right anchors.leftMargin: UM.Theme.getSize("default_margin").width - text: catalog.i18nc("@label", "The configurations are not available because the printer is disconnected.") + text: catalog.i18nc("@label", "Downloading the configurations from the remote printer") color: UM.Theme.getColor("text") font: UM.Theme.getFont("default") renderType: Text.NativeRendering From 9e0a423f28775c59204b62482f04e46464589a23 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 2 Jan 2019 13:30:16 +0100 Subject: [PATCH 1121/1240] Disable visibility of glass buildplate configuration in sync menu Because the build plate swapping is not implemented yet. --- resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml index 51ba77d012..5e47cafcf1 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationItem.qml @@ -187,7 +187,7 @@ Button rightMargin: UM.Theme.getSize("wide_margin").width } height: childrenRect.height - visible: configuration.buildplateConfiguration != "" + visible: configuration.buildplateConfiguration != "" && false //Buildplate is disabled as long as we have no printers that properly support buildplate swapping (so we can't test). UM.RecolorImage { From 71632fd098fd4e55a54948adaeb6c6af460d5782 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 2 Jan 2019 13:53:35 +0100 Subject: [PATCH 1122/1240] Add the postProcessing button back next to the slice button CURA-6043 --- .../qml/ActionPanel/SliceProcessWidget.qml | 45 ++++++++++++++++++- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index 6c3b136ca0..4c5ae24f25 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -107,7 +107,13 @@ Column { id: sliceButton fixedWidthMode: true - anchors.fill: parent + + height: parent.height + + anchors.right: additionalComponents.left + anchors.rightMargin: additionalComponents.width != 0 ? UM.Theme.getSize("default_margin").width : 0 + anchors.left: parent.left + text: catalog.i18nc("@button", "Slice") tooltip: catalog.i18nc("@label", "Start the slicing process") enabled: widget.backendState != UM.Backend.Error @@ -119,12 +125,47 @@ Column { id: cancelButton fixedWidthMode: true - anchors.fill: parent + height: parent.height + anchors.left: parent.left + + anchors.right: additionalComponents.left + anchors.rightMargin: additionalComponents.width != 0 ? UM.Theme.getSize("default_margin").width : 0 text: catalog.i18nc("@button", "Cancel") enabled: sliceButton.enabled visible: !sliceButton.visible onClicked: sliceOrStopSlicing() } + Item + { + id: additionalComponents + width: childrenRect.width + anchors.right: parent.right + height: parent.height + Row + { + id: additionalComponentsRow + anchors.verticalCenter: parent.verticalCenter + spacing: UM.Theme.getSize("default_margin").width + } + } + Component.onCompleted: prepareButtons.addAdditionalComponents("saveButton") + + Connections + { + target: CuraApplication + onAdditionalComponentsChanged: prepareButtons.addAdditionalComponents("saveButton") + } + + function addAdditionalComponents (areaId) + { + if(areaId == "saveButton") + { + for (var component in CuraApplication.additionalComponents["saveButton"]) + { + CuraApplication.additionalComponents["saveButton"][component].parent = additionalComponentsRow + } + } + } } From 96a6b7559eec9d6c79925cc253c754e615a5bb68 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 2 Jan 2019 14:04:56 +0100 Subject: [PATCH 1123/1240] Remember opened state of settings panel So if you restart Cura while the settings panel was open, it'll stay open when Cura is started back up. Contributes to issue CURA-6069. --- cura/CuraApplication.py | 1 + resources/qml/ExpandableComponent.qml | 2 +- resources/qml/PrintSetupSelector/PrintSetupSelector.qml | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 0edf6c1899..4d1c530237 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -500,6 +500,7 @@ class CuraApplication(QtApplication): preferences.addPreference("cura/choice_on_open_project", "always_ask") preferences.addPreference("cura/use_multi_build_plate", False) preferences.addPreference("view/settings_list_height", 600) + preferences.addPreference("view/settings_visible", False) preferences.addPreference("cura/currency", "€") preferences.addPreference("cura/material_settings", "{}") diff --git a/resources/qml/ExpandableComponent.qml b/resources/qml/ExpandableComponent.qml index afe15bcb1d..025c63d754 100644 --- a/resources/qml/ExpandableComponent.qml +++ b/resources/qml/ExpandableComponent.qml @@ -64,7 +64,7 @@ Item property alias iconSize: collapseButton.height // Is the "drawer" open? - readonly property alias expanded: contentContainer.visible + property alias expanded: contentContainer.visible // What should the radius of the header be. This is also influenced by the headerCornerSide property alias headerRadius: background.radius diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelector.qml b/resources/qml/PrintSetupSelector/PrintSetupSelector.qml index 2d4d7f6cf1..48ac07679d 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelector.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelector.qml @@ -29,4 +29,7 @@ Cura.ExpandableComponent property var extrudersModel: CuraApplication.getExtrudersModel() contentItem: PrintSetupSelectorContents {} + + onExpandedChanged: UM.Preferences.setValue("view/settings_visible", expanded) + Component.onCompleted: expanded = UM.Preferences.getValue("view/settings_visible") } \ No newline at end of file From a5385b229acf0afc5f1b1fca9cd7dee368c3054d Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Wed, 2 Jan 2019 16:37:19 +0100 Subject: [PATCH 1124/1240] Make text in printer type label bigger Just shown it to our UX designer and she thinks it's better this way. Contributes to issue CURA-6025. --- resources/qml/PrinterTypeLabel.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/PrinterTypeLabel.qml b/resources/qml/PrinterTypeLabel.qml index b445228e83..cfc9e56513 100644 --- a/resources/qml/PrinterTypeLabel.qml +++ b/resources/qml/PrinterTypeLabel.qml @@ -29,7 +29,7 @@ Item anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter renderType: Text.NativeRendering - font: UM.Theme.getFont("small") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") } } \ No newline at end of file From ee72284616bbf191cdc71c7ac9b1471dca926ba6 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 3 Jan 2019 08:32:39 +0100 Subject: [PATCH 1125/1240] Use appropriate log level CURA-6035 --- plugins/UM3NetworkPrinting/src/SendMaterialJob.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py index ab6ebfef2e..9d0d3dbbad 100644 --- a/plugins/UM3NetworkPrinting/src/SendMaterialJob.py +++ b/plugins/UM3NetworkPrinting/src/SendMaterialJob.py @@ -97,7 +97,7 @@ class SendMaterialJob(Job): file_path = container_registry.getContainerFilePathById(root_material_id) if not file_path: - Logger.log("e", "Cannot get file path for material container [%s]", root_material_id) + Logger.log("w", "Cannot get file path for material container [%s]", root_material_id) continue file_name = os.path.basename(file_path) From 3505eb321f5e7908840caeb49e8faa0a9dcdf5c7 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 3 Jan 2019 09:42:22 +0100 Subject: [PATCH 1126/1240] Also add space for the savebutton additional components with the output widget CURA-6043 --- .../qml/ActionPanel/OutputProcessWidget.qml | 56 ++++++++++++++----- .../qml/ActionPanel/SliceProcessWidget.qml | 1 + 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml index 03fa00d504..a8797670b4 100644 --- a/resources/qml/ActionPanel/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -40,8 +40,7 @@ Column anchors { left: parent.left - right: printInformationPanel.left - rightMargin: printInformationPanel.visible ? UM.Theme.getSize("thin_margin").width : 0 + right: parent.right } Cura.IconWithText @@ -52,6 +51,13 @@ Column text: preSlicedData ? catalog.i18nc("@label", "No time estimation available") : PrintInformation.currentPrintTime.getDisplayString(UM.DurationFormat.Long) source: UM.Theme.getIcon("clock") font: UM.Theme.getFont("default_bold") + PrintInformationWidget + { + id: printInformationPanel + visible: !preSlicedData + anchors.left: parent.left + anchors.leftMargin: parent.contentWidth + UM.Theme.getSize("default_margin").width + } } Cura.IconWithText @@ -84,20 +90,43 @@ Column return totalWeights + "g · " + totalLengths.toFixed(2) + "m" } source: UM.Theme.getIcon("spool") + + Item + { + id: additionalComponents + width: childrenRect.width + anchors.right: parent.right + height: parent.height + Row + { + id: additionalComponentsRow + anchors.right: parent.right + anchors.bottom: parent.bottom + spacing: UM.Theme.getSize("default_margin").width + } + } + Component.onCompleted: addAdditionalComponents("saveButton") + + Connections + { + target: CuraApplication + onAdditionalComponentsChanged: addAdditionalComponents("saveButton") + } + + function addAdditionalComponents (areaId) + { + if(areaId == "saveButton") + { + for (var component in CuraApplication.additionalComponents["saveButton"]) + { + CuraApplication.additionalComponents["saveButton"][component].parent = additionalComponentsRow + } + } + } } } - PrintInformationWidget - { - id: printInformationPanel - visible: !preSlicedData - anchors - { - right: parent.right - verticalCenter: timeAndCostsInformation.verticalCenter - } - } } Item @@ -127,9 +156,6 @@ Column onClicked: UM.Controller.setActiveStage("PreviewStage") visible: UM.Controller.activeStage != null && UM.Controller.activeStage.stageId != "PreviewStage" - - shadowEnabled: true - shadowColor: UM.Theme.getColor("action_button_disabled_shadow") } Cura.OutputDevicesActionButton diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index 4c5ae24f25..127f454902 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -135,6 +135,7 @@ Column visible: !sliceButton.visible onClicked: sliceOrStopSlicing() } + Item { id: additionalComponents From 195d39f698cda58b1bedcfcf2d75acb4c2de344d Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 3 Jan 2019 09:47:48 +0100 Subject: [PATCH 1127/1240] Update the style of the postprocessing button CURA-6043 --- .../PostProcessingPlugin.qml | 40 ++----------------- 1 file changed, 3 insertions(+), 37 deletions(-) diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml index 09fda8d454..5aa7660237 100644 --- a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml +++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml @@ -484,7 +484,7 @@ UM.Dialog onClicked: dialog.accept() } - Button + Cura.SecondaryButton { objectName: "postProcessingSaveAreaButton" visible: activeScriptsList.count > 0 @@ -492,41 +492,7 @@ UM.Dialog width: height tooltip: catalog.i18nc("@info:tooltip", "Change active post-processing scripts") onClicked: dialog.show() - - style: ButtonStyle - { - background: Rectangle - { - id: deviceSelectionIcon - border.width: UM.Theme.getSize("default_lining").width - border.color: !control.enabled ? UM.Theme.getColor("action_button_disabled_border") : - control.pressed ? UM.Theme.getColor("action_button_active_border") : - control.hovered ? UM.Theme.getColor("action_button_hovered_border") : UM.Theme.getColor("action_button_border") - color: !control.enabled ? UM.Theme.getColor("action_button_disabled") : - control.pressed ? UM.Theme.getColor("action_button_active") : - control.hovered ? UM.Theme.getColor("action_button_hovered") : UM.Theme.getColor("action_button") - Behavior on color { ColorAnimation { duration: 50; } } - - anchors.left: parent.left - anchors.leftMargin: Math.round(UM.Theme.getSize("save_button_text_margin").width / 2) - - width: parent.height - height: parent.height - - UM.RecolorImage - { - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter - width: Math.round(parent.width / 2) - height: Math.round(parent.height / 2) - sourceSize.height: height - color: !control.enabled ? UM.Theme.getColor("action_button_disabled_text") : - control.pressed ? UM.Theme.getColor("action_button_active_text") : - control.hovered ? UM.Theme.getColor("action_button_hovered_text") : UM.Theme.getColor("action_button_text") - source: "postprocessing.svg" - } - } - label: Label { } - } + iconSource: "postprocessing.svg" + fixedWidthMode: true } } \ No newline at end of file From 979fd507dee720501422e17045b71c5abc46f467 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 3 Jan 2019 10:39:43 +0100 Subject: [PATCH 1128/1240] Give the section delegates a fixed height This fixes the issue where the first printer would be drawn over the section label CURA-6011 --- resources/qml/PrinterSelector/MachineSelectorList.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/PrinterSelector/MachineSelectorList.qml b/resources/qml/PrinterSelector/MachineSelectorList.qml index 604e0f668d..b9c20d0de1 100644 --- a/resources/qml/PrinterSelector/MachineSelectorList.qml +++ b/resources/qml/PrinterSelector/MachineSelectorList.qml @@ -18,7 +18,7 @@ ListView { text: section == "true" ? catalog.i18nc("@label", "Connected printers") : catalog.i18nc("@label", "Preset printers") width: parent.width - height: visible ? contentHeight + 2 * UM.Theme.getSize("default_margin").height : 0 + height: UM.Theme.getSize("action_button").height leftPadding: UM.Theme.getSize("default_margin").width renderType: Text.NativeRendering font: UM.Theme.getFont("medium") From 707c3a26a88400bc2e0cd781053a0f5a3a3de938 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 3 Jan 2019 11:05:54 +0100 Subject: [PATCH 1129/1240] Make job specs not bold As per the design. Contributes to issue CURA-6025. --- resources/qml/JobSpecs.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index c7f82b8876..144616c22d 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -97,7 +97,7 @@ Item style: TextFieldStyle { textColor: UM.Theme.getColor("text_scene") - font: UM.Theme.getFont("default_bold") + font: UM.Theme.getFont("default") background: Rectangle { opacity: 0 @@ -115,7 +115,7 @@ Item height: UM.Theme.getSize("jobspecs_line").height verticalAlignment: Text.AlignVCenter - font: UM.Theme.getFont("default_bold") + font: UM.Theme.getFont("default") color: UM.Theme.getColor("text_scene") text: CuraApplication.getSceneBoundingBoxString } From 136317c3c761e416ddaad78f455fb5c79062ad50 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 3 Jan 2019 11:08:01 +0100 Subject: [PATCH 1130/1240] Fix code styles CURA-6005 --- resources/qml/ActionButton.qml | 7 +++---- resources/qml/CheckBox.qml | 8 +++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/resources/qml/ActionButton.qml b/resources/qml/ActionButton.qml index 2448b9a551..9e963f8d3e 100644 --- a/resources/qml/ActionButton.qml +++ b/resources/qml/ActionButton.qml @@ -120,12 +120,11 @@ Button visible: text != "" && button.hovered } - BusyIndicator { + BusyIndicator + { id: busyIndicator - anchors { - centerIn: parent - } + anchors.centerIn: parent width: height height: parent.height diff --git a/resources/qml/CheckBox.qml b/resources/qml/CheckBox.qml index 7a79182a4b..403efb4d7b 100644 --- a/resources/qml/CheckBox.qml +++ b/resources/qml/CheckBox.qml @@ -13,11 +13,12 @@ CheckBox property alias tooltip: tooltip.text - indicator: Rectangle { + indicator: Rectangle + { implicitWidth: UM.Theme.getSize("checkbox").width implicitHeight: UM.Theme.getSize("checkbox").height x: 0 - y: Math.round(parent.height / 2 - height / 2) + anchors.verticalCenter: parent.verticalCenter color: UM.Theme.getColor("main_background") radius: UM.Theme.getSize("checkbox_radius").width border.width: UM.Theme.getSize("default_lining").width @@ -37,7 +38,8 @@ CheckBox } } - contentItem: Label { + contentItem: Label + { anchors { left: checkbox.indicator.right From 56c9de44f741f919214e7d7f563876944f5b9bf4 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 3 Jan 2019 11:09:10 +0100 Subject: [PATCH 1131/1240] Update Cura Backups sdk_version to 6 in bundled json CURA-6005 --- resources/bundled_packages/cura.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/bundled_packages/cura.json b/resources/bundled_packages/cura.json index 896033fb22..2b5750ea8d 100644 --- a/resources/bundled_packages/cura.json +++ b/resources/bundled_packages/cura.json @@ -57,7 +57,7 @@ "display_name": "Cura Backups", "description": "Backup and restore your configuration.", "package_version": "1.2.0", - "sdk_version": 5, + "sdk_version": 6, "website": "https://ultimaker.com", "author": { "author_id": "UltimakerPackages", From a14c846e294acbc169fdca3c5756222ae2088c6a Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Thu, 3 Jan 2019 11:53:17 +0100 Subject: [PATCH 1132/1240] Rename CheckBox.qml to CheckBoxWithTooltip.qml CURA-6005 --- plugins/CuraDrive/src/qml/components/BackupListFooter.qml | 2 +- resources/qml/{CheckBox.qml => CheckBoxWithTooltip.qml} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename resources/qml/{CheckBox.qml => CheckBoxWithTooltip.qml} (100%) diff --git a/plugins/CuraDrive/src/qml/components/BackupListFooter.qml b/plugins/CuraDrive/src/qml/components/BackupListFooter.qml index 2a6d82bc74..a0bc212597 100644 --- a/plugins/CuraDrive/src/qml/components/BackupListFooter.qml +++ b/plugins/CuraDrive/src/qml/components/BackupListFooter.qml @@ -35,7 +35,7 @@ RowLayout busy: CuraDrive.isCreatingBackup } - Cura.CheckBox + Cura.CheckBoxWithTooltip { id: autoBackupEnabled checked: CuraDrive.autoBackupEnabled diff --git a/resources/qml/CheckBox.qml b/resources/qml/CheckBoxWithTooltip.qml similarity index 100% rename from resources/qml/CheckBox.qml rename to resources/qml/CheckBoxWithTooltip.qml From 1973397b929dd2e42a051c34fbb4533bf3007f33 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 3 Jan 2019 15:34:41 +0100 Subject: [PATCH 1133/1240] Remove confusing printer preference text CURA-6003 --- .../resources/qml/UM3InfoComponents.qml | 63 --------- .../src/DiscoverUM3Action.py | 1 - resources/qml/Preferences/MachinesPage.qml | 123 +----------------- 3 files changed, 3 insertions(+), 184 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/UM3InfoComponents.qml b/plugins/UM3NetworkPrinting/resources/qml/UM3InfoComponents.qml index 320201e165..c99ed1688e 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/UM3InfoComponents.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/UM3InfoComponents.qml @@ -90,67 +90,4 @@ Item { source: "DiscoverUM3Action.qml"; } } - - Column { - anchors.fill: parent; - objectName: "networkPrinterConnectionInfo"; - spacing: UM.Theme.getSize("default_margin").width; - visible: isUM3; - - Button { - onClicked: Cura.MachineManager.printerOutputDevices[0].requestAuthentication(); - text: catalog.i18nc("@action:button", "Request Access"); - tooltip: catalog.i18nc("@info:tooltip", "Send access request to the printer"); - visible: printerConnected && !printerAcceptsCommands && !authenticationRequested; - } - - Row { - anchors { - left: parent.left; - right: parent.right; - } - height: childrenRect.height; - spacing: UM.Theme.getSize("default_margin").width; - visible: printerConnected; - - Column { - Repeater { - model: CuraApplication.getExtrudersModel() - - Label { - text: model.name; - } - } - } - - Column { - Repeater { - id: nozzleColumn; - model: hotendIds - - Label { - text: nozzleColumn.model[index]; - } - } - } - - Column { - Repeater { - id: materialColumn; - model: materialNames - - Label { - text: materialColumn.model[index]; - } - } - } - } - - Button { - onClicked: manager.loadConfigurationFromPrinter(); - text: catalog.i18nc("@action:button", "Activate Configuration"); - tooltip: catalog.i18nc("@info:tooltip", "Load the configuration of the printer into Cura"); - visible: false; // printerConnected && !isClusterPrinter() - } - } } diff --git a/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py b/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py index 6ce99e4891..68af2bd575 100644 --- a/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py +++ b/plugins/UM3NetworkPrinting/src/DiscoverUM3Action.py @@ -193,4 +193,3 @@ class DiscoverUM3Action(MachineAction): # Create extra components CuraApplication.getInstance().addAdditionalComponent("monitorButtons", self.__additional_components_view.findChild(QObject, "networkPrinterConnectButton")) - CuraApplication.getInstance().addAdditionalComponent("machinesDetailPane", self.__additional_components_view.findChild(QObject, "networkPrinterConnectionInfo")) diff --git a/resources/qml/Preferences/MachinesPage.qml b/resources/qml/Preferences/MachinesPage.qml index d5ecb10658..f9c1a9b0a0 100644 --- a/resources/qml/Preferences/MachinesPage.qml +++ b/resources/qml/Preferences/MachinesPage.qml @@ -126,132 +126,15 @@ UM.ManagementPage } } - Grid - { - id: machineInfo - - anchors.top: machineActions.visible ? machineActions.bottom : machineActions.anchors.top - anchors.topMargin: UM.Theme.getSize("default_margin").height - anchors.left: parent.left - anchors.right: parent.right - spacing: UM.Theme.getSize("default_margin").height - rowSpacing: UM.Theme.getSize("default_lining").height - columns: 2 - - visible: base.currentItem - - property bool printerConnected: Cura.MachineManager.printerConnected - property var connectedPrinter: printerConnected ? Cura.MachineManager.printerOutputDevices[0] : null - property bool printerAcceptsCommands: printerConnected && Cura.MachineManager.printerOutputDevices[0].acceptsCommands - property var printJob: connectedPrinter != null ? connectedPrinter.activePrintJob: null - Label - { - text: catalog.i18nc("@label", "Printer type:") - visible: base.currentItem && "definition_name" in base.currentItem.metadata - } - Label - { - text: (base.currentItem && "definition_name" in base.currentItem.metadata) ? base.currentItem.metadata.definition_name : "" - } - Label - { - text: catalog.i18nc("@label", "Connection:") - visible: base.currentItem && base.currentItem.id == Cura.MachineManager.activeMachineId - } - Label - { - width: (parent.width * 0.7) | 0 - text: machineInfo.printerConnected ? machineInfo.connectedPrinter.connectionText : catalog.i18nc("@info:status", "The printer is not connected.") - visible: base.currentItem && base.currentItem.id == Cura.MachineManager.activeMachineId - wrapMode: Text.WordWrap - } - Label - { - text: catalog.i18nc("@label", "State:") - visible: base.currentItem && base.currentItem.id == Cura.MachineManager.activeMachineId && machineInfo.printerAcceptsCommands - } - Label { - width: (parent.width * 0.7) | 0 - text: - { - if(!machineInfo.printerConnected || !machineInfo.printerAcceptsCommands) { - return ""; - } - - if (machineInfo.printJob == null) - { - return catalog.i18nc("@label:MonitorStatus", "Waiting for a printjob"); - } - - switch(machineInfo.printJob.state) - { - case "printing": - return catalog.i18nc("@label:MonitorStatus", "Printing..."); - case "paused": - return catalog.i18nc("@label:MonitorStatus", "Paused"); - case "pre_print": - return catalog.i18nc("@label:MonitorStatus", "Preparing..."); - case "wait_cleanup": - return catalog.i18nc("@label:MonitorStatus", "Waiting for someone to clear the build plate"); - case "error": - return printerOutputDevice.errorText; - case "maintenance": - return catalog.i18nc("@label:MonitorStatus", "In maintenance. Please check the printer"); - case "abort": // note sure if this jobState actually occurs in the wild - return catalog.i18nc("@label:MonitorStatus", "Aborting print..."); - - } - return "" - } - visible: base.currentItem && base.currentItem.id == Cura.MachineManager.activeMachineId && machineInfo.printerAcceptsCommands - wrapMode: Text.WordWrap - } - } - - Column { - id: additionalComponentsColumn - anchors.left: parent.left - anchors.right: parent.right - anchors.top: machineInfo.visible ? machineInfo.bottom : machineInfo.anchors.top - anchors.topMargin: UM.Theme.getSize("default_margin").width - - spacing: UM.Theme.getSize("default_margin").width - visible: base.currentItem && base.currentItem.id == Cura.MachineManager.activeMachineId - - Component.onCompleted: - { - for (var component in CuraApplication.additionalComponents["machinesDetailPane"]) { - CuraApplication.additionalComponents["machinesDetailPane"][component].parent = additionalComponentsColumn - } - } - } - - Component.onCompleted: { - addAdditionalComponents("machinesDetailPane") - } - - Connections { - target: CuraApplication - onAdditionalComponentsChanged: addAdditionalComponents - } - - function addAdditionalComponents (areaId) { - if(areaId == "machinesDetailPane") { - for (var component in CuraApplication.additionalComponents["machinesDetailPane"]) { - CuraApplication.additionalComponents["machinesDetailPane"][component].parent = additionalComponentsColumn - } - } - } - UM.I18nCatalog { id: catalog; name: "cura"; } UM.ConfirmRemoveDialog { - id: confirmDialog; - object: base.currentItem && base.currentItem.name ? base.currentItem.name : ""; + id: confirmDialog + object: base.currentItem && base.currentItem.name ? base.currentItem.name : "" onYes: { - Cura.MachineManager.removeMachine(base.currentItem.id); + Cura.MachineManager.removeMachine(base.currentItem.id) if(!base.currentItem) { objectList.currentIndex = activeMachineIndex() From 6eca0ce69f906457a0760d4e8ae9c01c0c57e449 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 3 Jan 2019 11:52:03 +0100 Subject: [PATCH 1134/1240] Use native rendering for text fields Hopefully this fixes the font rendering issue we're seeing on MacOS now. --- resources/themes/cura-light/styles.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/themes/cura-light/styles.qml b/resources/themes/cura-light/styles.qml index 4361c3ae2b..b314190e24 100755 --- a/resources/themes/cura-light/styles.qml +++ b/resources/themes/cura-light/styles.qml @@ -585,6 +585,7 @@ QtObject text: control.unit ? control.unit : "" color: Theme.getColor("setting_unit"); font: Theme.getFont("default"); + renderType: Text.NativeRendering } } } From 798c1f198c48c0f3f590bca64f254c776befc0da Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 3 Jan 2019 16:02:49 +0100 Subject: [PATCH 1135/1240] Hide action panel when displaying a stage with a background This is not the worst hack ever, but it's quite a hack. Contributes to issue CURA-6054. --- plugins/MonitorStage/MonitorMain.qml | 41 +++++++++++++--------------- resources/qml/Cura.qml | 33 +++++++++++++++++++++- 2 files changed, 51 insertions(+), 23 deletions(-) diff --git a/plugins/MonitorStage/MonitorMain.qml b/plugins/MonitorStage/MonitorMain.qml index 34cf4ad801..5fda32db9e 100644 --- a/plugins/MonitorStage/MonitorMain.qml +++ b/plugins/MonitorStage/MonitorMain.qml @@ -1,4 +1,5 @@ -// 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.10 import QtQuick.Controls 1.4 @@ -7,31 +8,27 @@ import UM 1.3 as UM import Cura 1.0 as Cura -Item +// We show a nice overlay on the 3D viewer when the current output device has no monitor view +Rectangle { - // We show a nice overlay on the 3D viewer when the current output device has no monitor view - Rectangle + id: viewportOverlay + + color: UM.Theme.getColor("viewport_overlay") + anchors.fill: parent + + // This mouse area is to prevent mouse clicks to be passed onto the scene. + MouseArea { - id: viewportOverlay - - color: UM.Theme.getColor("viewport_overlay") anchors.fill: parent - - // This mouse area is to prevent mouse clicks to be passed onto the scene. - MouseArea - { - anchors.fill: parent - acceptedButtons: Qt.AllButtons - onWheel: wheel.accepted = true - } - - // Disable dropping files into Cura when the monitor page is active - DropArea - { - anchors.fill: parent - } + acceptedButtons: Qt.AllButtons + onWheel: wheel.accepted = true } + // Disable dropping files into Cura when the monitor page is active + DropArea + { + anchors.fill: parent + } Loader { id: monitorViewComponent @@ -45,4 +42,4 @@ Item sourceComponent: Cura.MachineManager.printerOutputDevices.length > 0 ? Cura.MachineManager.printerOutputDevices[0].monitorItem : null } -} +} \ No newline at end of file diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 8a34c7e219..a78295e7fa 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -252,7 +252,21 @@ UM.MainWindow anchors.bottom: parent.bottom anchors.rightMargin: UM.Theme.getSize("thick_margin").width anchors.bottomMargin: UM.Theme.getSize("thick_margin").height - visible: CuraApplication.platformActivity + + /* + Show this panel only if there is something on the build plate, and there is NOT an opaque item in front of the build plate. + This cannot be solved by Z indexing! If you want to try solving this, please increase this counter when you're done: + Number of people having tried to fix this by z-indexing: 2 + The problem arises from the following render order requirements: + - The stage menu must be rendered above the stage main. + - The stage main must be rendered above the action panel (because the monitor page must be rendered above the action panel). + - The action panel must be rendered above the expandable components drop-down. + However since the expandable components drop-downs are child elements of the stage menu, + they can't be rendered lower than elements that are lower than the stage menu. + Therefore we opted to forego the second requirement and hide the action panel instead when something obscures it (except the expandable components). + We assume that QQuickRectangles are always opaque and any other item is not. + */ + visible: CuraApplication.platformActivity && (main.item == null || !qmlTypeOf(main.item, "QQuickRectangle")) } Loader @@ -818,4 +832,21 @@ UM.MainWindow } } } + + /** + * Function to check whether a QML object has a certain type. + * Taken from StackOverflow: https://stackoverflow.com/a/28384228 and + * adapted to our code style. + * Licensed under CC BY-SA 3.0. + * \param obj The QtObject to get the name of. + * \param class_name (str) The name of the class to check against. Has to be + * the QtObject class name, not the QML entity name. + */ + function qmlTypeOf(obj, class_name) + { + //className plus "(" is the class instance without modification. + //className plus "_QML" is the class instance with user-defined properties. + var str = obj.toString(); + return str.indexOf(class_name + "(") == 0 || str.indexOf(class_name + "_QML") == 0; + } } From e15e06f29f2d18c6629d66f0f3196d0279ae1db0 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 3 Jan 2019 16:04:17 +0100 Subject: [PATCH 1136/1240] Make Z relative This way if we ever want to change the parent's Z for some other ordering requirements, the children will stay in order. --- plugins/Toolbox/resources/qml/Toolbox.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Toolbox/resources/qml/Toolbox.qml b/plugins/Toolbox/resources/qml/Toolbox.qml index 9ede2a6bda..d15d98eed7 100644 --- a/plugins/Toolbox/resources/qml/Toolbox.qml +++ b/plugins/Toolbox/resources/qml/Toolbox.qml @@ -38,7 +38,7 @@ Window { id: mainView width: parent.width - z: -1 + z: parent.z - 1 anchors { top: header.bottom From 65183ade0a53deceac8c00413e656aec79e12628 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 3 Jan 2019 16:44:08 +0100 Subject: [PATCH 1137/1240] No longer chache singleton objects CURA-6005 --- plugins/CuraDrive/__init__.py | 2 +- plugins/CuraDrive/src/DriveApiService.py | 11 ++++------- plugins/CuraDrive/src/DrivePluginExtension.py | 18 ++++++++---------- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/plugins/CuraDrive/__init__.py b/plugins/CuraDrive/__init__.py index 766d94752f..dd7ffeaac3 100644 --- a/plugins/CuraDrive/__init__.py +++ b/plugins/CuraDrive/__init__.py @@ -7,4 +7,4 @@ def getMetaData(): return {} def register(app): - return {"extension": DrivePluginExtension(app)} + return {"extension": DrivePluginExtension()} diff --git a/plugins/CuraDrive/src/DriveApiService.py b/plugins/CuraDrive/src/DriveApiService.py index a542ac439e..3b6641cd74 100644 --- a/plugins/CuraDrive/src/DriveApiService.py +++ b/plugins/CuraDrive/src/DriveApiService.py @@ -12,16 +12,14 @@ import requests from UM.Logger import Logger from UM.Message import Message from UM.Signal import Signal +from cura.CuraApplication import CuraApplication from .UploadBackupJob import UploadBackupJob from .Settings import Settings +## The DriveApiService is responsible for interacting with the CuraDrive API and Cura's backup handling. class DriveApiService: - """ - The DriveApiService is responsible for interacting with the CuraDrive API and Cura's backup handling. - """ - GET_BACKUPS_URL = "{}/backups".format(Settings.DRIVE_API_URL) PUT_BACKUP_URL = "{}/backups".format(Settings.DRIVE_API_URL) DELETE_BACKUP_URL = "{}/backups".format(Settings.DRIVE_API_URL) @@ -32,9 +30,8 @@ class DriveApiService: # Emit signal when creating backup started or finished. onCreatingStateChanged = Signal() - def __init__(self, cura_api) -> None: - """Create a new instance of the Drive API service and set the cura_api object.""" - self._cura_api = cura_api + def __init__(self) -> None: + self._cura_api = CuraApplication.getInstance().getCuraAPI() def getBackups(self) -> List[Dict[str, Any]]: """Get all backups from the API.""" diff --git a/plugins/CuraDrive/src/DrivePluginExtension.py b/plugins/CuraDrive/src/DrivePluginExtension.py index a76c623fe8..bed54140d9 100644 --- a/plugins/CuraDrive/src/DrivePluginExtension.py +++ b/plugins/CuraDrive/src/DrivePluginExtension.py @@ -7,8 +7,10 @@ from typing import Optional from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal +from UM.Application import Application from UM.Extension import Extension from UM.Message import Message +from cura.CuraApplication import CuraApplication from .Settings import Settings from .DriveApiService import DriveApiService @@ -34,11 +36,8 @@ class DrivePluginExtension(QObject, Extension): DATE_FORMAT = "%d/%m/%Y %H:%M:%S" - def __init__(self, application): + def __init__(self): super(DrivePluginExtension, self).__init__() - - # Re-usable instance of application. - self._application = application # Local data caching for the UI. self._drive_window = None # type: Optional[QObject] @@ -47,12 +46,11 @@ class DrivePluginExtension(QObject, Extension): self._is_creating_backup = False # Initialize services. - self._preferences = self._application.getPreferences() - self._cura_api = self._application.getCuraAPI() - self._drive_api_service = DriveApiService(self._cura_api) + self._preferences = CuraApplication.getInstance().getPreferences() + self._drive_api_service = DriveApiService() # Attach signals. - self._cura_api.account.loginStateChanged.connect(self._onLoginStateChanged) + CuraApplication.getInstance().getCuraAPI().account.loginStateChanged.connect(self._onLoginStateChanged) self._drive_api_service.onRestoringStateChanged.connect(self._onRestoringStateChanged) self._drive_api_service.onCreatingStateChanged.connect(self._onCreatingStateChanged) @@ -65,7 +63,7 @@ class DrivePluginExtension(QObject, Extension): self._updateMenuItems() # Make auto-backup on boot if required. - self._application.engineCreatedSignal.connect(self._autoBackup) + CuraApplication.getInstance().engineCreatedSignal.connect(self._autoBackup) def showDriveWindow(self) -> None: """Show the Drive UI popup window.""" @@ -81,7 +79,7 @@ class DrivePluginExtension(QObject, Extension): :return: The popup window object. """ path = os.path.join(os.path.dirname(__file__), "qml", "main.qml") - return self._application.createQmlComponent(path, {"CuraDrive": self}) + return CuraApplication.getInstance().createQmlComponent(path, {"CuraDrive": self}) def _updateMenuItems(self) -> None: """Update the menu items.""" From ea1712df0f120985fb62fb474a698320b4da6726 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 3 Jan 2019 16:51:11 +0100 Subject: [PATCH 1138/1240] Codestyle fixes & simplifications CURA-6005 --- plugins/CuraDrive/src/DrivePluginExtension.py | 64 ++++--------------- 1 file changed, 11 insertions(+), 53 deletions(-) diff --git a/plugins/CuraDrive/src/DrivePluginExtension.py b/plugins/CuraDrive/src/DrivePluginExtension.py index bed54140d9..cd8274f83a 100644 --- a/plugins/CuraDrive/src/DrivePluginExtension.py +++ b/plugins/CuraDrive/src/DrivePluginExtension.py @@ -16,6 +16,9 @@ from .Settings import Settings from .DriveApiService import DriveApiService from .models.BackupListModel import BackupListModel +from UM.i18n import i18nCatalog +catalog = i18nCatalog("cura") + class DrivePluginExtension(QObject, Extension): """ @@ -37,7 +40,8 @@ class DrivePluginExtension(QObject, Extension): DATE_FORMAT = "%d/%m/%Y %H:%M:%S" def __init__(self): - super(DrivePluginExtension, self).__init__() + QObject.__init__(self, None) + Extension.__init__(self) # Local data caching for the UI. self._drive_window = None # type: Optional[QObject] @@ -59,8 +63,8 @@ class DrivePluginExtension(QObject, Extension): self._preferences.addPreference(Settings.AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY, datetime.now() .strftime(self.DATE_FORMAT)) - # Register menu items. - self._updateMenuItems() + # Register the menu item + self.addMenuItem(catalog.i18nc("@item:inmenu", "Manage backups"), self.showDriveWindow) # Make auto-backup on boot if required. CuraApplication.getInstance().engineCreatedSignal.connect(self._autoBackup) @@ -68,59 +72,41 @@ class DrivePluginExtension(QObject, Extension): def showDriveWindow(self) -> None: """Show the Drive UI popup window.""" if not self._drive_window: - self._drive_window = self.createDriveWindow() + path = os.path.join(os.path.dirname(__file__), "qml", "main.qml") + self._drive_window = CuraApplication.getInstance().createQmlComponent(path, {"CuraDrive": self}) self.refreshBackups() if self._drive_window: self._drive_window.show() - def createDriveWindow(self) -> Optional["QObject"]: - """ - Create an instance of the Drive UI popup window. - :return: The popup window object. - """ - path = os.path.join(os.path.dirname(__file__), "qml", "main.qml") - return CuraApplication.getInstance().createQmlComponent(path, {"CuraDrive": self}) - - def _updateMenuItems(self) -> None: - """Update the menu items.""" - self.addMenuItem(Settings.translatable_messages["extension_menu_entry"], self.showDriveWindow) - def _autoBackup(self) -> None: - """Automatically make a backup on boot if enabled.""" - if self._preferences.getValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY) and self._lastBackupTooLongAgo(): + if self._preferences.getValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY) and self._isLastBackupTooLongAgo(): self.createBackup() - def _lastBackupTooLongAgo(self) -> bool: - """Check if the last backup was longer than 1 day ago.""" + def _isLastBackupTooLongAgo(self) -> bool: current_date = datetime.now() last_backup_date = self._getLastBackupDate() date_diff = current_date - last_backup_date return date_diff.days > 1 def _getLastBackupDate(self) -> "datetime": - """Get the last backup date as datetime object.""" last_backup_date = self._preferences.getValue(Settings.AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY) return datetime.strptime(last_backup_date, self.DATE_FORMAT) def _storeBackupDate(self) -> None: - """Store the current date as last backup date.""" backup_date = datetime.now().strftime(self.DATE_FORMAT) self._preferences.setValue(Settings.AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY, backup_date) def _onLoginStateChanged(self, logged_in: bool = False) -> None: - """Callback handler for changes in the login state.""" if logged_in: self.refreshBackups() def _onRestoringStateChanged(self, is_restoring: bool = False, error_message: str = None) -> None: - """Callback handler for changes in the restoring state.""" self._is_restoring_backup = is_restoring self.restoringStateChanged.emit() if error_message: Message(error_message, title = Settings.MESSAGE_TITLE, lifetime = 5).show() def _onCreatingStateChanged(self, is_creating: bool = False, error_message: str = None) -> None: - """Callback handler for changes in the creation state.""" self._is_creating_backup = is_creating self.creatingStateChanged.emit() if error_message: @@ -133,69 +119,41 @@ class DrivePluginExtension(QObject, Extension): @pyqtSlot(bool, name = "toggleAutoBackup") def toggleAutoBackup(self, enabled: bool) -> None: - """Enable or disable the auto-backup feature.""" self._preferences.setValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY, enabled) self.preferencesChanged.emit() @pyqtProperty(bool, notify = preferencesChanged) def autoBackupEnabled(self) -> bool: - """Check if auto-backup is enabled or not.""" return bool(self._preferences.getValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY)) @pyqtProperty(QObject, notify = backupsChanged) def backups(self) -> BackupListModel: - """ - Get a list of the backups. - :return: The backups as Qt List Model. - """ return self._backups_list_model @pyqtSlot(name = "refreshBackups") def refreshBackups(self) -> None: - """ - Forcefully refresh the backups list. - """ self._backups_list_model.loadBackups(self._drive_api_service.getBackups()) self.backupsChanged.emit() @pyqtProperty(bool, notify = restoringStateChanged) def isRestoringBackup(self) -> bool: - """ - Get the current restoring state. - :return: Boolean if we are restoring or not. - """ return self._is_restoring_backup @pyqtProperty(bool, notify = creatingStateChanged) def isCreatingBackup(self) -> bool: - """ - Get the current creating state. - :return: Boolean if we are creating or not. - """ return self._is_creating_backup @pyqtSlot(str, name = "restoreBackup") def restoreBackup(self, backup_id: str) -> None: - """ - Download and restore a backup by ID. - :param backup_id: The ID of the backup. - """ index = self._backups_list_model.find("backup_id", backup_id) backup = self._backups_list_model.getItem(index) self._drive_api_service.restoreBackup(backup) @pyqtSlot(name = "createBackup") def createBackup(self) -> None: - """ - Create a new backup. - """ self._drive_api_service.createBackup() @pyqtSlot(str, name = "deleteBackup") def deleteBackup(self, backup_id: str) -> None: - """ - Delete a backup by ID. - :param backup_id: The ID of the backup. - """ self._drive_api_service.deleteBackup(backup_id) self.refreshBackups() From 4d1e9d24f98d3c12e8f91f02d416ab1ddb8287b7 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 3 Jan 2019 16:58:51 +0100 Subject: [PATCH 1139/1240] Cleanup documentation Either fix the style to doxygen or remove it if it just repeated the functionnname. CURA-6005 --- plugins/CuraDrive/src/DriveApiService.py | 43 +++++++----------------- 1 file changed, 13 insertions(+), 30 deletions(-) diff --git a/plugins/CuraDrive/src/DriveApiService.py b/plugins/CuraDrive/src/DriveApiService.py index 3b6641cd74..39045d1317 100644 --- a/plugins/CuraDrive/src/DriveApiService.py +++ b/plugins/CuraDrive/src/DriveApiService.py @@ -34,7 +34,6 @@ class DriveApiService: self._cura_api = CuraApplication.getInstance().getCuraAPI() def getBackups(self) -> List[Dict[str, Any]]: - """Get all backups from the API.""" access_token = self._cura_api.account.accessToken if not access_token: Logger.log("w", "Could not get access token.") @@ -43,6 +42,7 @@ class DriveApiService: backup_list_request = requests.get(self.GET_BACKUPS_URL, headers = { "Authorization": "Bearer {}".format(access_token) }) + if backup_list_request.status_code > 299: Logger.log("w", "Could not get backups list from remote: %s", backup_list_request.text) Message(Settings.translatable_messages["get_backups_error"], title = Settings.MESSAGE_TITLE, @@ -51,7 +51,6 @@ class DriveApiService: return backup_list_request.json()["data"] def createBackup(self) -> None: - """Create a backup and upload it to CuraDrive cloud storage.""" self.onCreatingStateChanged.emit(is_creating = True) # Create the backup. @@ -74,10 +73,6 @@ class DriveApiService: upload_backup_job.start() def _onUploadFinished(self, job: "UploadBackupJob") -> None: - """ - Callback handler for the upload job. - :param job: The executed job. - """ if job.backup_upload_error_message != "": # If the job contains an error message we pass it along so the UI can display it. self.onCreatingStateChanged.emit(is_creating = False, error_message = job.backup_upload_error_message) @@ -85,10 +80,6 @@ class DriveApiService: self.onCreatingStateChanged.emit(is_creating = False) def restoreBackup(self, backup: Dict[str, Any]) -> None: - """ - Restore a previously exported backup from cloud storage. - :param backup: A dict containing an entry from the API list response. - """ self.onRestoringStateChanged.emit(is_restoring = True) download_url = backup.get("download_url") if not download_url: @@ -119,30 +110,22 @@ class DriveApiService: self.onRestoringStateChanged.emit(is_restoring = False) def _emitRestoreError(self, error_message: str = Settings.translatable_messages["backup_restore_error_message"]): - """Helper method for emitting a signal when restoring failed.""" self.onRestoringStateChanged.emit( is_restoring = False, error_message = error_message ) + # Verify the MD5 hash of a file. + # \param file_path Full path to the file. + # \param known_hash The known MD5 hash of the file. + # \return: Success or not. @staticmethod def _verifyMd5Hash(file_path: str, known_hash: str) -> bool: - """ - Verify the MD5 hash of a file. - :param file_path: Full path to the file. - :param known_hash: The known MD5 hash of the file. - :return: Success or not. - """ with open(file_path, "rb") as read_backup: local_md5_hash = base64.b64encode(hashlib.md5(read_backup.read()).digest(), altchars = b"_-").decode("utf-8") return known_hash == local_md5_hash def deleteBackup(self, backup_id: str) -> bool: - """ - Delete a backup from the server by ID. - :param backup_id: The ID of the backup to delete. - :return: Success bool. - """ access_token = self._cura_api.account.accessToken if not access_token: Logger.log("w", "Could not get access token.") @@ -156,13 +139,12 @@ class DriveApiService: return False return True + # Request a backup upload slot from the API. + # \param backup_metadata: A dict containing some meta data about the backup. + # \param backup_size The size of the backup file in bytes. + # \return: The upload URL for the actual backup file if successful, otherwise None. def _requestBackupUpload(self, backup_metadata: Dict[str, Any], backup_size: int) -> Optional[str]: - """ - Request a backup upload slot from the API. - :param backup_metadata: A dict containing some meta data about the backup. - :param backup_size: The size of the backup file in bytes. - :return: The upload URL for the actual backup file if successful, otherwise None. - """ + access_token = self._cura_api.account.accessToken if not access_token: Logger.log("w", "Could not get access token.") @@ -176,8 +158,9 @@ class DriveApiService: }, headers = { "Authorization": "Bearer {}".format(access_token) }) - - if backup_upload_request.status_code > 299: + + # Any status code of 300 or above indicates an error. + if backup_upload_request.status_code >= 300: Logger.log("w", "Could not request backup upload: %s", backup_upload_request.text) return None From ed9a51791b9ed83c517dc5d1ba26b489905078ec Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 3 Jan 2019 17:05:58 +0100 Subject: [PATCH 1140/1240] Handle bunch of review comments CURA-6005 --- plugins/CuraDrive/src/DriveApiService.py | 23 ++++++++++++----------- plugins/CuraDrive/src/Settings.py | 2 +- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/plugins/CuraDrive/src/DriveApiService.py b/plugins/CuraDrive/src/DriveApiService.py index 39045d1317..f74f30bcda 100644 --- a/plugins/CuraDrive/src/DriveApiService.py +++ b/plugins/CuraDrive/src/DriveApiService.py @@ -17,6 +17,9 @@ from cura.CuraApplication import CuraApplication from .UploadBackupJob import UploadBackupJob from .Settings import Settings +from UM.i18n import i18nCatalog +catalog = i18nCatalog("cura") + ## The DriveApiService is responsible for interacting with the CuraDrive API and Cura's backup handling. class DriveApiService: @@ -42,11 +45,10 @@ class DriveApiService: backup_list_request = requests.get(self.GET_BACKUPS_URL, headers = { "Authorization": "Bearer {}".format(access_token) }) - - if backup_list_request.status_code > 299: + + if backup_list_request.status_code >= 300: Logger.log("w", "Could not get backups list from remote: %s", backup_list_request.text) - Message(Settings.translatable_messages["get_backups_error"], title = Settings.MESSAGE_TITLE, - lifetime = 10).show() + Message(catalog.i18nc("@info:backup_status", "There was an error listing your backups."), title = Settings.MESSAGE_TITLE).show() return [] return backup_list_request.json()["data"] @@ -87,7 +89,7 @@ class DriveApiService: return self._emitRestoreError() download_package = requests.get(download_url, stream = True) - if download_package.status_code != 200: + if download_package.status_code >= 300: # Something went wrong when attempting to download the backup. Logger.log("w", "Could not download backup from url %s: %s", download_url, download_package.text) return self._emitRestoreError() @@ -109,11 +111,10 @@ class DriveApiService: self._cura_api.backups.restoreBackup(read_backup.read(), backup.get("data")) self.onRestoringStateChanged.emit(is_restoring = False) - def _emitRestoreError(self, error_message: str = Settings.translatable_messages["backup_restore_error_message"]): - self.onRestoringStateChanged.emit( - is_restoring = False, - error_message = error_message - ) + def _emitRestoreError(self): + self.onRestoringStateChanged.emit(is_restoring = False, + error_message = catalog.i18nc("@info:backup_status", + "There was an error trying to restore your backup.")) # Verify the MD5 hash of a file. # \param file_path Full path to the file. @@ -134,7 +135,7 @@ class DriveApiService: delete_backup = requests.delete("{}/{}".format(self.DELETE_BACKUP_URL, backup_id), headers = { "Authorization": "Bearer {}".format(access_token) }) - if delete_backup.status_code > 299: + if delete_backup.status_code >= 300: Logger.log("w", "Could not delete backup: %s", delete_backup.text) return False return True diff --git a/plugins/CuraDrive/src/Settings.py b/plugins/CuraDrive/src/Settings.py index 04ace8af95..10d1ba4397 100644 --- a/plugins/CuraDrive/src/Settings.py +++ b/plugins/CuraDrive/src/Settings.py @@ -19,7 +19,7 @@ class Settings: I18N_CATALOG_ID = "cura" I18N_CATALOG = i18nCatalog(I18N_CATALOG_ID) - MESSAGE_TITLE = I18N_CATALOG.i18nc("@info:title", "Backups"), + MESSAGE_TITLE = I18N_CATALOG.i18nc("@info:title", "Backups") # Translatable messages for the entire plugin. translatable_messages = { From cfdce25a6214c2d4e56183869c05973fe8aaaf54 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 3 Jan 2019 17:06:17 +0100 Subject: [PATCH 1141/1240] Add grip lines to bottom of resizable panel Looks all right now. Contributes to issue CURA-6054. --- .../PrintSetupSelectorContents.qml | 18 ++++++++++++++++++ .../themes/cura-light/icons/grip_lines.svg | 4 ++++ 2 files changed, 22 insertions(+) create mode 100644 resources/themes/cura-light/icons/grip_lines.svg diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml index 35c5f008b6..77b6cd9b63 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml @@ -176,6 +176,24 @@ Item UM.Preferences.setValue("view/settings_list_height", h); } } + + UM.RecolorImage + { + width: parent.width * 0.05 + sourceSize.height: height + sourceSize.width: width + anchors + { + horizontalCenter: parent.horizontalCenter + top: parent.top + topMargin: UM.Theme.getSize("thick_lining").height + bottom: parent.bottom + bottomMargin: UM.Theme.getSize("thick_lining").height + } + + source: UM.Theme.getIcon("grip_lines") + color: UM.Theme.getColor("lining") + } } } } \ No newline at end of file diff --git a/resources/themes/cura-light/icons/grip_lines.svg b/resources/themes/cura-light/icons/grip_lines.svg new file mode 100644 index 0000000000..253d1fb486 --- /dev/null +++ b/resources/themes/cura-light/icons/grip_lines.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file From e36b3e37d1b680b89f2beb2b4af4bd2cdab233ec Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 3 Jan 2019 17:13:47 +0100 Subject: [PATCH 1142/1240] Remove indirection in the translation CURA-6005 --- plugins/CuraDrive/src/Settings.py | 22 ++-------------------- plugins/CuraDrive/src/UploadBackupJob.py | 14 +++++++------- 2 files changed, 9 insertions(+), 27 deletions(-) diff --git a/plugins/CuraDrive/src/Settings.py b/plugins/CuraDrive/src/Settings.py index 10d1ba4397..fcb05b8c04 100644 --- a/plugins/CuraDrive/src/Settings.py +++ b/plugins/CuraDrive/src/Settings.py @@ -16,24 +16,6 @@ class Settings: AUTO_BACKUP_ENABLED_PREFERENCE_KEY = "cura_drive/auto_backup_enabled" AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY = "cura_drive/auto_backup_date" - I18N_CATALOG_ID = "cura" - I18N_CATALOG = i18nCatalog(I18N_CATALOG_ID) + I18N_CATALOG = i18nCatalog("cura") - MESSAGE_TITLE = I18N_CATALOG.i18nc("@info:title", "Backups") - - # Translatable messages for the entire plugin. - translatable_messages = { - - # Menu items. - "extension_menu_entry": I18N_CATALOG.i18nc("@item:inmenu", "Manage backups"), - - # Notification messages. - "backup_failed": I18N_CATALOG.i18nc("@info:backup_status", "There was an error while creating your backup."), - "uploading_backup": I18N_CATALOG.i18nc("@info:backup_status", "Uploading your backup..."), - "uploading_backup_success": I18N_CATALOG.i18nc("@info:backup_status", "Your backup has finished uploading."), - "uploading_backup_error": I18N_CATALOG.i18nc("@info:backup_status", - "There was an error while uploading your backup."), - "get_backups_error": I18N_CATALOG.i18nc("@info:backup_status", "There was an error listing your backups."), - "backup_restore_error_message": I18N_CATALOG.i18nc("@info:backup_status", - "There was an error trying to restore your backup.") - } + MESSAGE_TITLE = I18N_CATALOG.i18nc("@info:title", "Backups") \ No newline at end of file diff --git a/plugins/CuraDrive/src/UploadBackupJob.py b/plugins/CuraDrive/src/UploadBackupJob.py index ae6cb13f2e..bdcc8dd8a6 100644 --- a/plugins/CuraDrive/src/UploadBackupJob.py +++ b/plugins/CuraDrive/src/UploadBackupJob.py @@ -8,14 +8,17 @@ from UM.Logger import Logger from UM.Message import Message from .Settings import Settings +from UM.i18n import i18nCatalog +catalog = i18nCatalog("cura") class UploadBackupJob(Job): + MESSAGE_TITLE = catalog.i18nc("@info:title", "Backups") + """ This job is responsible for uploading the backup file to cloud storage. As it can take longer than some other tasks, we schedule this using a Cura Job. """ - def __init__(self, signed_upload_url: str, backup_zip: bytes) -> None: super().__init__() self._signed_upload_url = signed_upload_url @@ -24,18 +27,15 @@ class UploadBackupJob(Job): self.backup_upload_error_message = "" def run(self) -> None: - Message(Settings.translatable_messages["uploading_backup"], title = Settings.MESSAGE_TITLE, - lifetime = 10).show() + Message(catalog.i18nc("@info:backup_status", "Uploading your backup..."), title = self.MESSAGE_TITLE).show() backup_upload = requests.put(self._signed_upload_url, data = self._backup_zip) if backup_upload.status_code not in (200, 201): self.backup_upload_error_message = backup_upload.text Logger.log("w", "Could not upload backup file: %s", backup_upload.text) - Message(Settings.translatable_messages["uploading_backup_error"], title = Settings.MESSAGE_TITLE, - lifetime = 10).show() + Message(catalog.i18nc("@info:backup_status", "There was an error while uploading your backup."), title = self.MESSAGE_TITLE).show() else: self._upload_success = True - Message(Settings.translatable_messages["uploading_backup_success"], title = Settings.MESSAGE_TITLE, - lifetime = 10).show() + Message(catalog.i18nc("@info:backup_status", "Your backup has finished uploading."), title = self.MESSAGE_TITLE).show() self.finished.emit(self) From 83fbb78c9ee4437d736696e5e74a9a747daabcad Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 3 Jan 2019 17:15:32 +0100 Subject: [PATCH 1143/1240] Backup uploading now shows a intermediate progress bar and hides when done CURA-6005 --- plugins/CuraDrive/src/UploadBackupJob.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/CuraDrive/src/UploadBackupJob.py b/plugins/CuraDrive/src/UploadBackupJob.py index bdcc8dd8a6..d9532a96f0 100644 --- a/plugins/CuraDrive/src/UploadBackupJob.py +++ b/plugins/CuraDrive/src/UploadBackupJob.py @@ -27,9 +27,12 @@ class UploadBackupJob(Job): self.backup_upload_error_message = "" def run(self) -> None: - Message(catalog.i18nc("@info:backup_status", "Uploading your backup..."), title = self.MESSAGE_TITLE).show() + upload_message = Message(catalog.i18nc("@info:backup_status", "Uploading your backup..."), title = self.MESSAGE_TITLE, progress = -1) + upload_message.show() backup_upload = requests.put(self._signed_upload_url, data = self._backup_zip) + upload_message.hide() + if backup_upload.status_code not in (200, 201): self.backup_upload_error_message = backup_upload.text Logger.log("w", "Could not upload backup file: %s", backup_upload.text) From 1578aaa301876cce04dfb6f7fc0c5c936fdad56a Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 3 Jan 2019 17:17:40 +0100 Subject: [PATCH 1144/1240] Minor code cleanup CURA-6005 --- plugins/CuraDrive/src/UploadBackupJob.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/plugins/CuraDrive/src/UploadBackupJob.py b/plugins/CuraDrive/src/UploadBackupJob.py index d9532a96f0..2e76ed9b4b 100644 --- a/plugins/CuraDrive/src/UploadBackupJob.py +++ b/plugins/CuraDrive/src/UploadBackupJob.py @@ -7,7 +7,6 @@ from UM.Job import Job from UM.Logger import Logger from UM.Message import Message -from .Settings import Settings from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") @@ -15,10 +14,8 @@ catalog = i18nCatalog("cura") class UploadBackupJob(Job): MESSAGE_TITLE = catalog.i18nc("@info:title", "Backups") - """ - This job is responsible for uploading the backup file to cloud storage. - As it can take longer than some other tasks, we schedule this using a Cura Job. - """ + # This job is responsible for uploading the backup file to cloud storage. + # As it can take longer than some other tasks, we schedule this using a Cura Job. def __init__(self, signed_upload_url: str, backup_zip: bytes) -> None: super().__init__() self._signed_upload_url = signed_upload_url @@ -33,7 +30,7 @@ class UploadBackupJob(Job): backup_upload = requests.put(self._signed_upload_url, data = self._backup_zip) upload_message.hide() - if backup_upload.status_code not in (200, 201): + if backup_upload.status_code >= 300: self.backup_upload_error_message = backup_upload.text Logger.log("w", "Could not upload backup file: %s", backup_upload.text) Message(catalog.i18nc("@info:backup_status", "There was an error while uploading your backup."), title = self.MESSAGE_TITLE).show() From efac7c39c05e441c59b583a866d9623abc961dd8 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Thu, 3 Jan 2019 17:22:22 +0100 Subject: [PATCH 1145/1240] Fix indentation --- resources/bundled_packages/cura.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/bundled_packages/cura.json b/resources/bundled_packages/cura.json index c32b94af3f..f912958f74 100644 --- a/resources/bundled_packages/cura.json +++ b/resources/bundled_packages/cura.json @@ -356,7 +356,7 @@ } } }, - "PreviewStage": { + "PreviewStage": { "package_info": { "package_id": "PreviewStage", "package_type": "plugin", @@ -1585,4 +1585,4 @@ } } } -} +} \ No newline at end of file From 49076a7103715703b9a7aeef591cbf2f74207c88 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 3 Jan 2019 17:42:58 +0100 Subject: [PATCH 1146/1240] Remove the unneeded BackupListModel CURA-6005 --- plugins/CuraDrive/src/DrivePluginExtension.py | 23 ++++++----- .../CuraDrive/src/models/BackupListModel.py | 40 ------------------- plugins/CuraDrive/src/models/__init__.py | 0 .../src/qml/components/BackupListItem.qml | 10 ++--- .../qml/components/BackupListItemDetails.qml | 10 ++--- 5 files changed, 22 insertions(+), 61 deletions(-) delete mode 100644 plugins/CuraDrive/src/models/BackupListModel.py delete mode 100644 plugins/CuraDrive/src/models/__init__.py diff --git a/plugins/CuraDrive/src/DrivePluginExtension.py b/plugins/CuraDrive/src/DrivePluginExtension.py index cd8274f83a..041c01a14d 100644 --- a/plugins/CuraDrive/src/DrivePluginExtension.py +++ b/plugins/CuraDrive/src/DrivePluginExtension.py @@ -3,18 +3,17 @@ import os from datetime import datetime -from typing import Optional +from typing import Optional, List from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal -from UM.Application import Application from UM.Extension import Extension +from UM.Logger import Logger from UM.Message import Message from cura.CuraApplication import CuraApplication from .Settings import Settings from .DriveApiService import DriveApiService -from .models.BackupListModel import BackupListModel from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") @@ -45,7 +44,7 @@ class DrivePluginExtension(QObject, Extension): # Local data caching for the UI. self._drive_window = None # type: Optional[QObject] - self._backups_list_model = BackupListModel() + self._backups = [] self._is_restoring_backup = False self._is_creating_backup = False @@ -126,13 +125,13 @@ class DrivePluginExtension(QObject, Extension): def autoBackupEnabled(self) -> bool: return bool(self._preferences.getValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY)) - @pyqtProperty(QObject, notify = backupsChanged) - def backups(self) -> BackupListModel: - return self._backups_list_model + @pyqtProperty("QVariantList", notify = backupsChanged) + def backups(self) -> List: + return self._backups @pyqtSlot(name = "refreshBackups") def refreshBackups(self) -> None: - self._backups_list_model.loadBackups(self._drive_api_service.getBackups()) + self._backups = self._drive_api_service.getBackups() self.backupsChanged.emit() @pyqtProperty(bool, notify = restoringStateChanged) @@ -145,9 +144,11 @@ class DrivePluginExtension(QObject, Extension): @pyqtSlot(str, name = "restoreBackup") def restoreBackup(self, backup_id: str) -> None: - index = self._backups_list_model.find("backup_id", backup_id) - backup = self._backups_list_model.getItem(index) - self._drive_api_service.restoreBackup(backup) + for backup in self._backups: + if backup.get("backup_id") == backup_id: + self._drive_api_service.restoreBackup(backup) + return + Logger.log("w", "Unable to find backup with the ID %s", backup_id) @pyqtSlot(name = "createBackup") def createBackup(self) -> None: diff --git a/plugins/CuraDrive/src/models/BackupListModel.py b/plugins/CuraDrive/src/models/BackupListModel.py deleted file mode 100644 index 06b256b22c..0000000000 --- a/plugins/CuraDrive/src/models/BackupListModel.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright (c) 2018 Ultimaker B.V. -# Cura is released under the terms of the LGPLv3 or higher. - -from typing import Any, List, Dict - -from UM.Qt.ListModel import ListModel - -from PyQt5.QtCore import Qt - - -class BackupListModel(ListModel): - """ - The BackupListModel transforms the backups data that came from the server so it can be served to the Qt UI. - """ - - def __init__(self, parent = None) -> None: - super().__init__(parent) - self.addRoleName(Qt.UserRole + 1, "backup_id") - self.addRoleName(Qt.UserRole + 2, "download_url") - self.addRoleName(Qt.UserRole + 3, "generated_time") - self.addRoleName(Qt.UserRole + 4, "md5_hash") - self.addRoleName(Qt.UserRole + 5, "data") - - def loadBackups(self, data: List[Dict[str, Any]]) -> None: - """ - Populate the model with server data. - :param data: - """ - items = [] - for backup in data: - # We do this loop because we only want to append these specific fields. - # Without this, ListModel will break. - items.append({ - "backup_id": backup["backup_id"], - "download_url": backup["download_url"], - "generated_time": backup["generated_time"], - "md5_hash": backup["md5_hash"], - "data": backup["metadata"] - }) - self.setItems(items) diff --git a/plugins/CuraDrive/src/models/__init__.py b/plugins/CuraDrive/src/models/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/CuraDrive/src/qml/components/BackupListItem.qml b/plugins/CuraDrive/src/qml/components/BackupListItem.qml index ba4f1a32a4..0cd897fada 100644 --- a/plugins/CuraDrive/src/qml/components/BackupListItem.qml +++ b/plugins/CuraDrive/src/qml/components/BackupListItem.qml @@ -44,7 +44,7 @@ Item Label { - text: new Date(model["generated_time"]).toLocaleString(UM.Preferences.getValue("general/language")) + text: new Date(modelData.generated_time).toLocaleString(UM.Preferences.getValue("general/language")) color: UM.Theme.getColor("text") elide: Text.ElideRight Layout.minimumWidth: 100 * screenScaleFactor @@ -55,7 +55,7 @@ Item Label { - text: model["data"]["description"] + text: modelData.metadata.description color: UM.Theme.getColor("text") elide: Text.ElideRight Layout.minimumWidth: 100 * screenScaleFactor @@ -85,7 +85,7 @@ Item BackupListItemDetails { id: backupDetails - backupDetailsData: model + backupDetailsData: modelData width: parent.width visible: parent.showDetails anchors.top: dataRow.bottom @@ -97,7 +97,7 @@ Item title: catalog.i18nc("@dialog:title", "Delete Backup") text: catalog.i18nc("@dialog:info", "Are you sure you want to delete this backup? This cannot be undone.") standardButtons: StandardButton.Yes | StandardButton.No - onYes: CuraDrive.deleteBackup(model["backup_id"]) + onYes: CuraDrive.deleteBackup(modelData.backup_id) } MessageDialog @@ -106,6 +106,6 @@ Item title: catalog.i18nc("@dialog:title", "Restore Backup") text: catalog.i18nc("@dialog:info", "You will need to restart Cura before your backup is restored. Do you want to close Cura now?") standardButtons: StandardButton.Yes | StandardButton.No - onYes: CuraDrive.restoreBackup(model["backup_id"]) + onYes: CuraDrive.restoreBackup(modelData.backup_id) } } diff --git a/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml b/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml index 38a2557c47..e84f1e0982 100644 --- a/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml +++ b/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml @@ -19,7 +19,7 @@ ColumnLayout { iconSource: "../images/cura.svg" label: catalog.i18nc("@backuplist:label", "Cura Version") - value: backupDetailsData["data"]["cura_release"] + value: backupDetailsData.metadata.cura_release } // Machine count. @@ -27,7 +27,7 @@ ColumnLayout { iconSource: "../images/printer.svg" label: catalog.i18nc("@backuplist:label", "Machines") - value: backupDetailsData["data"]["machine_count"] + value: backupDetailsData.metadata.machine_count } // Meterial count. @@ -35,7 +35,7 @@ ColumnLayout { iconSource: "../images/material.svg" label: catalog.i18nc("@backuplist:label", "Materials") - value: backupDetailsData["data"]["material_count"] + value: backupDetailsData.metadata.material_count } // Meterial count. @@ -43,7 +43,7 @@ ColumnLayout { iconSource: "../images/profile.svg" label: catalog.i18nc("@backuplist:label", "Profiles") - value: backupDetailsData["data"]["profile_count"] + value: backupDetailsData.metadata.profile_count } // Meterial count. @@ -51,7 +51,7 @@ ColumnLayout { iconSource: "../images/plugin.svg" label: catalog.i18nc("@backuplist:label", "Plugins") - value: backupDetailsData["data"]["plugin_count"] + value: backupDetailsData.metadata.plugin_count } // Spacer. From 0edc3f2680d03aa158a6218c30580290e37a6664 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 4 Jan 2019 10:17:06 +0100 Subject: [PATCH 1147/1240] Make some minor improvements to the qml This implements some of the best practices that weren't used yet for the QML CURA-6005 --- .../qml/components/BackupListItemDetails.qml | 2 +- .../components/BackupListItemDetailsRow.qml | 16 ++--- plugins/CuraDrive/src/qml/components/Icon.qml | 58 ------------------- plugins/CuraDrive/src/qml/main.qml | 2 +- .../CuraDrive/src/qml/pages/BackupsPage.qml | 2 +- .../CuraDrive/src/qml/pages/WelcomePage.qml | 5 +- 6 files changed, 16 insertions(+), 69 deletions(-) delete mode 100644 plugins/CuraDrive/src/qml/components/Icon.qml diff --git a/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml b/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml index e84f1e0982..d07bb50b11 100644 --- a/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml +++ b/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml @@ -11,7 +11,7 @@ ColumnLayout { id: backupDetails width: parent.width - spacing: 10 * screenScaleFactor + spacing: UM.Theme.getSize("default_margin").width property var backupDetailsData // Cura version diff --git a/plugins/CuraDrive/src/qml/components/BackupListItemDetailsRow.qml b/plugins/CuraDrive/src/qml/components/BackupListItemDetailsRow.qml index d1c8dd33d2..550bdaefab 100644 --- a/plugins/CuraDrive/src/qml/components/BackupListItemDetailsRow.qml +++ b/plugins/CuraDrive/src/qml/components/BackupListItemDetailsRow.qml @@ -13,9 +13,9 @@ RowLayout width: parent.width height: 40 * screenScaleFactor - property var iconSource - property var label - property var value + property alias iconSource: icon.source + property alias label: detailName.text + property alias value: detailValue.text // Spacing. Item @@ -23,16 +23,18 @@ RowLayout width: 40 * screenScaleFactor } - Icon + UM.RecolorImage { + id: icon width: 18 * screenScaleFactor - iconSource: detailsRow.iconSource + height: width + source: "" color: UM.Theme.getColor("text") } Label { - text: detailsRow.label + id: detailName color: UM.Theme.getColor("text") elide: Text.ElideRight Layout.minimumWidth: 50 * screenScaleFactor @@ -43,7 +45,7 @@ RowLayout Label { - text: detailsRow.value + id: detailValue color: UM.Theme.getColor("text") elide: Text.ElideRight Layout.minimumWidth: 50 * screenScaleFactor diff --git a/plugins/CuraDrive/src/qml/components/Icon.qml b/plugins/CuraDrive/src/qml/components/Icon.qml deleted file mode 100644 index 8d559bc2b4..0000000000 --- a/plugins/CuraDrive/src/qml/components/Icon.qml +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) 2018 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.7 -import QtQuick.Controls 2.1 -import QtGraphicalEffects 1.0 - -Item -{ - id: icon - width: parent.height - height: width - property var color: "transparent" - property var iconSource - property bool animated: false - - Image - { - id: iconImage - width: parent.height - height: width - smooth: true - source: icon.iconSource - sourceSize.width: width - sourceSize.height: height - antialiasing: true - visible: !icon.animated - } - - AnimatedImage - { - id: animatedIconImage - width: parent.height - height: width - smooth: true - antialiasing: true - source: "../images/loading.gif" - visible: icon.animated - } - - ColorOverlay - { - anchors.fill: iconImage - source: iconImage - color: icon.color - antialiasing: true - visible: !icon.animated - } - - ColorOverlay - { - anchors.fill: animatedIconImage - source: animatedIconImage - color: icon.color - antialiasing: true - visible: icon.animated - } -} diff --git a/plugins/CuraDrive/src/qml/main.qml b/plugins/CuraDrive/src/qml/main.qml index e8a49a49e5..48bf3b6ea4 100644 --- a/plugins/CuraDrive/src/qml/main.qml +++ b/plugins/CuraDrive/src/qml/main.qml @@ -27,7 +27,7 @@ Window UM.I18nCatalog { id: catalog - name: "cura_drive" + name: "cura" } WelcomePage diff --git a/plugins/CuraDrive/src/qml/pages/BackupsPage.qml b/plugins/CuraDrive/src/qml/pages/BackupsPage.qml index 0a7b00d2cb..3b905a4a39 100644 --- a/plugins/CuraDrive/src/qml/pages/BackupsPage.qml +++ b/plugins/CuraDrive/src/qml/pages/BackupsPage.qml @@ -18,7 +18,7 @@ Item ColumnLayout { - spacing: UM.Theme.getSize("default_margin").height * 2 + spacing: UM.Theme.getSize("wide_margin").height width: parent.width anchors.fill: parent diff --git a/plugins/CuraDrive/src/qml/pages/WelcomePage.qml b/plugins/CuraDrive/src/qml/pages/WelcomePage.qml index 19eecedf28..0b207bc170 100644 --- a/plugins/CuraDrive/src/qml/pages/WelcomePage.qml +++ b/plugins/CuraDrive/src/qml/pages/WelcomePage.qml @@ -10,12 +10,14 @@ import Cura 1.1 as Cura import "../components" + Column { id: welcomePage spacing: UM.Theme.getSize("wide_margin").height width: parent.width - topPadding: 150 * screenScaleFactor + height: childrenRect.height + anchors.centerIn: parent Image { @@ -51,3 +53,4 @@ Column fixedWidthMode: true } } + From 6492b51d7cbbefa0123f0c86bf9b4a984fd127b6 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 4 Jan 2019 10:25:22 +0100 Subject: [PATCH 1148/1240] Remove duplicated icons CURA-6005 --- .../src/qml/components/BackupListItemDetails.qml | 12 ++++++------ plugins/CuraDrive/src/qml/images/material.svg | 7 ------- plugins/CuraDrive/src/qml/images/plugin.svg | 7 ------- plugins/CuraDrive/src/qml/images/profile.svg | 3 --- 4 files changed, 6 insertions(+), 23 deletions(-) delete mode 100644 plugins/CuraDrive/src/qml/images/material.svg delete mode 100644 plugins/CuraDrive/src/qml/images/plugin.svg delete mode 100644 plugins/CuraDrive/src/qml/images/profile.svg diff --git a/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml b/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml index d07bb50b11..cf365501ec 100644 --- a/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml +++ b/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml @@ -30,26 +30,26 @@ ColumnLayout value: backupDetailsData.metadata.machine_count } - // Meterial count. + // Material count BackupListItemDetailsRow { - iconSource: "../images/material.svg" + iconSource: UM.Theme.getIcon("category_material") label: catalog.i18nc("@backuplist:label", "Materials") value: backupDetailsData.metadata.material_count } - // Meterial count. + // Profile count. BackupListItemDetailsRow { - iconSource: "../images/profile.svg" + iconSource: UM.Theme.getIcon("profile") label: catalog.i18nc("@backuplist:label", "Profiles") value: backupDetailsData.metadata.profile_count } - // Meterial count. + // Plugin count. BackupListItemDetailsRow { - iconSource: "../images/plugin.svg" + iconSource: UM.Theme.getIcon("plugin") label: catalog.i18nc("@backuplist:label", "Plugins") value: backupDetailsData.metadata.plugin_count } diff --git a/plugins/CuraDrive/src/qml/images/material.svg b/plugins/CuraDrive/src/qml/images/material.svg deleted file mode 100644 index eac724e471..0000000000 --- a/plugins/CuraDrive/src/qml/images/material.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/plugins/CuraDrive/src/qml/images/plugin.svg b/plugins/CuraDrive/src/qml/images/plugin.svg deleted file mode 100644 index 674eb99a54..0000000000 --- a/plugins/CuraDrive/src/qml/images/plugin.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/plugins/CuraDrive/src/qml/images/profile.svg b/plugins/CuraDrive/src/qml/images/profile.svg deleted file mode 100644 index ec2130f3d6..0000000000 --- a/plugins/CuraDrive/src/qml/images/profile.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file From 551f07552789505f4c456bcb4d35d6be4572ab15 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 4 Jan 2019 10:27:41 +0100 Subject: [PATCH 1149/1240] Removed unneeded destinction between get/put/delete URL They are all the same, so no real need to keep this seperate. CURA-6005 --- plugins/CuraDrive/src/DriveApiService.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/plugins/CuraDrive/src/DriveApiService.py b/plugins/CuraDrive/src/DriveApiService.py index f74f30bcda..d9185cc1d2 100644 --- a/plugins/CuraDrive/src/DriveApiService.py +++ b/plugins/CuraDrive/src/DriveApiService.py @@ -23,9 +23,7 @@ catalog = i18nCatalog("cura") ## The DriveApiService is responsible for interacting with the CuraDrive API and Cura's backup handling. class DriveApiService: - GET_BACKUPS_URL = "{}/backups".format(Settings.DRIVE_API_URL) - PUT_BACKUP_URL = "{}/backups".format(Settings.DRIVE_API_URL) - DELETE_BACKUP_URL = "{}/backups".format(Settings.DRIVE_API_URL) + BACKUP_URL = "{}/backups".format(Settings.DRIVE_API_URL) # Emit signal when restoring backup started or finished. onRestoringStateChanged = Signal() @@ -42,7 +40,7 @@ class DriveApiService: Logger.log("w", "Could not get access token.") return [] - backup_list_request = requests.get(self.GET_BACKUPS_URL, headers = { + backup_list_request = requests.get(self.BACKUP_URL, headers = { "Authorization": "Bearer {}".format(access_token) }) @@ -132,7 +130,7 @@ class DriveApiService: Logger.log("w", "Could not get access token.") return False - delete_backup = requests.delete("{}/{}".format(self.DELETE_BACKUP_URL, backup_id), headers = { + delete_backup = requests.delete("{}/{}".format(self.BACKUP_URL, backup_id), headers = { "Authorization": "Bearer {}".format(access_token) }) if delete_backup.status_code >= 300: @@ -151,7 +149,7 @@ class DriveApiService: Logger.log("w", "Could not get access token.") return None - backup_upload_request = requests.put(self.PUT_BACKUP_URL, json = { + backup_upload_request = requests.put(self.BACKUP_URL, json = { "data": { "backup_size": backup_size, "metadata": backup_metadata From 31156d623017f7a6a00a4cba1a574b4a86230037 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 4 Jan 2019 10:28:54 +0100 Subject: [PATCH 1150/1240] Add missing signalemitter decorater CURA-6005 --- plugins/CuraDrive/src/DriveApiService.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/CuraDrive/src/DriveApiService.py b/plugins/CuraDrive/src/DriveApiService.py index d9185cc1d2..d9116eccb2 100644 --- a/plugins/CuraDrive/src/DriveApiService.py +++ b/plugins/CuraDrive/src/DriveApiService.py @@ -11,7 +11,7 @@ import requests from UM.Logger import Logger from UM.Message import Message -from UM.Signal import Signal +from UM.Signal import Signal, signalemitter from cura.CuraApplication import CuraApplication from .UploadBackupJob import UploadBackupJob @@ -22,6 +22,7 @@ catalog = i18nCatalog("cura") ## The DriveApiService is responsible for interacting with the CuraDrive API and Cura's backup handling. +@signalemitter class DriveApiService: BACKUP_URL = "{}/backups".format(Settings.DRIVE_API_URL) From 8ebd4282fd661582e144531f75ca8ee833841c50 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 4 Jan 2019 10:39:39 +0100 Subject: [PATCH 1151/1240] Make the backupList use a scrollview instead of manually re-doing it CURA-6005 --- .../src/qml/components/BackupList.qml | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/plugins/CuraDrive/src/qml/components/BackupList.qml b/plugins/CuraDrive/src/qml/components/BackupList.qml index af7e72b6e6..10ca15afe5 100644 --- a/plugins/CuraDrive/src/qml/components/BackupList.qml +++ b/plugins/CuraDrive/src/qml/components/BackupList.qml @@ -2,32 +2,35 @@ // Cura is released under the terms of the LGPLv3 or higher. import QtQuick 2.7 -import QtQuick.Controls 2.1 +import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import UM 1.1 as UM -ListView +ScrollView { - id: backupList + property alias model: backupList.model width: parent.width - clip: true - delegate: Item + ListView { + id: backupList width: parent.width - height: childrenRect.height - - BackupListItem - { - id: backupListItem - width: parent.width - UM.Theme.getSize("default_margin").width // Add a margin, otherwise the scrollbar is be on top of the right most component - } - - Divider + delegate: Item { width: parent.width - anchors.top: backupListItem.bottom + height: childrenRect.height + + BackupListItem + { + id: backupListItem + width: parent.width + } + + Divider + { + width: parent.width + anchors.top: backupListItem.bottom + } } } - ScrollBar.vertical: RightSideScrollBar {} } From 87eb8634512f8f97a191d5074b2f3e07e700487f Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 4 Jan 2019 10:43:40 +0100 Subject: [PATCH 1152/1240] Remove the final traces of the translation indirection CURA-6005 --- plugins/CuraDrive/src/DriveApiService.py | 2 +- plugins/CuraDrive/src/DrivePluginExtension.py | 4 ++-- plugins/CuraDrive/src/Settings.py | 10 ++-------- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/plugins/CuraDrive/src/DriveApiService.py b/plugins/CuraDrive/src/DriveApiService.py index d9116eccb2..7ca009018b 100644 --- a/plugins/CuraDrive/src/DriveApiService.py +++ b/plugins/CuraDrive/src/DriveApiService.py @@ -47,7 +47,7 @@ class DriveApiService: if backup_list_request.status_code >= 300: Logger.log("w", "Could not get backups list from remote: %s", backup_list_request.text) - Message(catalog.i18nc("@info:backup_status", "There was an error listing your backups."), title = Settings.MESSAGE_TITLE).show() + Message(catalog.i18nc("@info:backup_status", "There was an error listing your backups."), title = catalog.i18nc("@info:title", "Backup")).show() return [] return backup_list_request.json()["data"] diff --git a/plugins/CuraDrive/src/DrivePluginExtension.py b/plugins/CuraDrive/src/DrivePluginExtension.py index 041c01a14d..edd89745ea 100644 --- a/plugins/CuraDrive/src/DrivePluginExtension.py +++ b/plugins/CuraDrive/src/DrivePluginExtension.py @@ -103,13 +103,13 @@ class DrivePluginExtension(QObject, Extension): self._is_restoring_backup = is_restoring self.restoringStateChanged.emit() if error_message: - Message(error_message, title = Settings.MESSAGE_TITLE, lifetime = 5).show() + Message(error_message, title = catalog.i18nc("@info:title", "Backup")).show() def _onCreatingStateChanged(self, is_creating: bool = False, error_message: str = None) -> None: self._is_creating_backup = is_creating self.creatingStateChanged.emit() if error_message: - Message(error_message, title = Settings.MESSAGE_TITLE, lifetime = 5).show() + Message(error_message, title = catalog.i18nc("@info:title", "Backup")).show() else: self._storeBackupDate() if not is_creating: diff --git a/plugins/CuraDrive/src/Settings.py b/plugins/CuraDrive/src/Settings.py index fcb05b8c04..c5383555b2 100644 --- a/plugins/CuraDrive/src/Settings.py +++ b/plugins/CuraDrive/src/Settings.py @@ -7,15 +7,9 @@ from cura import UltimakerCloudAuthentication class Settings: - """ - Keeps the application settings. - """ + # Keeps the plugin settings. DRIVE_API_VERSION = 1 DRIVE_API_URL = "{}/cura-drive/v{}".format(UltimakerCloudAuthentication.CuraCloudAPIRoot, str(DRIVE_API_VERSION)) AUTO_BACKUP_ENABLED_PREFERENCE_KEY = "cura_drive/auto_backup_enabled" - AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY = "cura_drive/auto_backup_date" - - I18N_CATALOG = i18nCatalog("cura") - - MESSAGE_TITLE = I18N_CATALOG.i18nc("@info:title", "Backups") \ No newline at end of file + AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY = "cura_drive/auto_backup_date" \ No newline at end of file From 63c2a901bb54a9f50cbdec259d06ae969192419e Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 4 Jan 2019 10:45:24 +0100 Subject: [PATCH 1153/1240] Prevent double emits / recalculations when not needed CURA-6005 --- plugins/CuraDrive/src/DrivePluginExtension.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/CuraDrive/src/DrivePluginExtension.py b/plugins/CuraDrive/src/DrivePluginExtension.py index edd89745ea..d1ea34f4a5 100644 --- a/plugins/CuraDrive/src/DrivePluginExtension.py +++ b/plugins/CuraDrive/src/DrivePluginExtension.py @@ -112,14 +112,13 @@ class DrivePluginExtension(QObject, Extension): Message(error_message, title = catalog.i18nc("@info:title", "Backup")).show() else: self._storeBackupDate() - if not is_creating: + if not is_creating and not error_message: # We've finished creating a new backup, to the list has to be updated. self.refreshBackups() @pyqtSlot(bool, name = "toggleAutoBackup") def toggleAutoBackup(self, enabled: bool) -> None: self._preferences.setValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY, enabled) - self.preferencesChanged.emit() @pyqtProperty(bool, notify = preferencesChanged) def autoBackupEnabled(self) -> bool: From 23315260ae1870f393f720393e8b3d5af6df0c47 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 4 Jan 2019 10:49:41 +0100 Subject: [PATCH 1154/1240] Fix some of the documentation CURA-6005 --- plugins/CuraDrive/src/DrivePluginExtension.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/plugins/CuraDrive/src/DrivePluginExtension.py b/plugins/CuraDrive/src/DrivePluginExtension.py index d1ea34f4a5..ce9a6651d5 100644 --- a/plugins/CuraDrive/src/DrivePluginExtension.py +++ b/plugins/CuraDrive/src/DrivePluginExtension.py @@ -18,11 +18,8 @@ from .DriveApiService import DriveApiService from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") - +# The DivePluginExtension provides functionality to backup and restore your Cura configuration to Ultimaker's cloud. class DrivePluginExtension(QObject, Extension): - """ - The DivePluginExtension provides functionality to backup and restore your Cura configuration to Ultimaker's cloud. - """ # Signal emitted when the list of backups changed. backupsChanged = pyqtSignal() @@ -38,7 +35,7 @@ class DrivePluginExtension(QObject, Extension): DATE_FORMAT = "%d/%m/%Y %H:%M:%S" - def __init__(self): + def __init__(self) -> None: QObject.__init__(self, None) Extension.__init__(self) @@ -69,7 +66,6 @@ class DrivePluginExtension(QObject, Extension): CuraApplication.getInstance().engineCreatedSignal.connect(self._autoBackup) def showDriveWindow(self) -> None: - """Show the Drive UI popup window.""" if not self._drive_window: path = os.path.join(os.path.dirname(__file__), "qml", "main.qml") self._drive_window = CuraApplication.getInstance().createQmlComponent(path, {"CuraDrive": self}) From 3b498d3a34cbfd1a1abd3ae909cec8383ec5f8b1 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 4 Jan 2019 10:51:07 +0100 Subject: [PATCH 1155/1240] Change name of the signals so they are consistent with rest of the code CURA-6005 --- plugins/CuraDrive/src/DriveApiService.py | 22 +++++++++---------- plugins/CuraDrive/src/DrivePluginExtension.py | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/plugins/CuraDrive/src/DriveApiService.py b/plugins/CuraDrive/src/DriveApiService.py index 7ca009018b..3bb416fce6 100644 --- a/plugins/CuraDrive/src/DriveApiService.py +++ b/plugins/CuraDrive/src/DriveApiService.py @@ -27,10 +27,10 @@ class DriveApiService: BACKUP_URL = "{}/backups".format(Settings.DRIVE_API_URL) # Emit signal when restoring backup started or finished. - onRestoringStateChanged = Signal() + restoringStateChanged = Signal() # Emit signal when creating backup started or finished. - onCreatingStateChanged = Signal() + creatingStateChanged = Signal() def __init__(self) -> None: self._cura_api = CuraApplication.getInstance().getCuraAPI() @@ -52,12 +52,12 @@ class DriveApiService: return backup_list_request.json()["data"] def createBackup(self) -> None: - self.onCreatingStateChanged.emit(is_creating = True) + self.creatingStateChanged.emit(is_creating = True) # Create the backup. backup_zip_file, backup_meta_data = self._cura_api.backups.createBackup() if not backup_zip_file or not backup_meta_data: - self.onCreatingStateChanged.emit(is_creating = False, error_message = "Could not create backup.") + self.creatingStateChanged.emit(is_creating = False, error_message ="Could not create backup.") return # Create an upload entry for the backup. @@ -65,7 +65,7 @@ class DriveApiService: backup_meta_data["description"] = "{}.backup.{}.cura.zip".format(timestamp, backup_meta_data["cura_release"]) backup_upload_url = self._requestBackupUpload(backup_meta_data, len(backup_zip_file)) if not backup_upload_url: - self.onCreatingStateChanged.emit(is_creating = False, error_message = "Could not upload backup.") + self.creatingStateChanged.emit(is_creating = False, error_message ="Could not upload backup.") return # Upload the backup to storage. @@ -76,12 +76,12 @@ class DriveApiService: def _onUploadFinished(self, job: "UploadBackupJob") -> None: if job.backup_upload_error_message != "": # If the job contains an error message we pass it along so the UI can display it. - self.onCreatingStateChanged.emit(is_creating = False, error_message = job.backup_upload_error_message) + self.creatingStateChanged.emit(is_creating = False, error_message = job.backup_upload_error_message) else: - self.onCreatingStateChanged.emit(is_creating = False) + self.creatingStateChanged.emit(is_creating = False) def restoreBackup(self, backup: Dict[str, Any]) -> None: - self.onRestoringStateChanged.emit(is_restoring = True) + self.restoringStateChanged.emit(is_restoring = True) download_url = backup.get("download_url") if not download_url: # If there is no download URL, we can't restore the backup. @@ -108,11 +108,11 @@ class DriveApiService: # Tell Cura to place the backup back in the user data folder. with open(temporary_backup_file.name, "rb") as read_backup: self._cura_api.backups.restoreBackup(read_backup.read(), backup.get("data")) - self.onRestoringStateChanged.emit(is_restoring = False) + self.restoringStateChanged.emit(is_restoring = False) def _emitRestoreError(self): - self.onRestoringStateChanged.emit(is_restoring = False, - error_message = catalog.i18nc("@info:backup_status", + self.restoringStateChanged.emit(is_restoring = False, + error_message = catalog.i18nc("@info:backup_status", "There was an error trying to restore your backup.")) # Verify the MD5 hash of a file. diff --git a/plugins/CuraDrive/src/DrivePluginExtension.py b/plugins/CuraDrive/src/DrivePluginExtension.py index ce9a6651d5..8944b9a980 100644 --- a/plugins/CuraDrive/src/DrivePluginExtension.py +++ b/plugins/CuraDrive/src/DrivePluginExtension.py @@ -51,8 +51,8 @@ class DrivePluginExtension(QObject, Extension): # Attach signals. CuraApplication.getInstance().getCuraAPI().account.loginStateChanged.connect(self._onLoginStateChanged) - self._drive_api_service.onRestoringStateChanged.connect(self._onRestoringStateChanged) - self._drive_api_service.onCreatingStateChanged.connect(self._onCreatingStateChanged) + self._drive_api_service.restoringStateChanged.connect(self._onRestoringStateChanged) + self._drive_api_service.creatingStateChanged.connect(self._onCreatingStateChanged) # Register preferences. self._preferences.addPreference(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY, False) From 0ec0b68360d7029349702f0e7099e9c8e28087bb Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 4 Jan 2019 10:54:23 +0100 Subject: [PATCH 1156/1240] Remove unneeded component CURA-6005 --- .../src/qml/components/RightSideScrollBar.qml | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 plugins/CuraDrive/src/qml/components/RightSideScrollBar.qml diff --git a/plugins/CuraDrive/src/qml/components/RightSideScrollBar.qml b/plugins/CuraDrive/src/qml/components/RightSideScrollBar.qml deleted file mode 100644 index 5ef8558ee7..0000000000 --- a/plugins/CuraDrive/src/qml/components/RightSideScrollBar.qml +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2018 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.7 -import QtQuick.Controls 2.1 -import QtQuick.Layouts 1.3 - -ScrollBar -{ - active: true - size: parent.height - anchors.top: parent.top - anchors.right: parent.right - anchors.bottom: parent.bottom -} From 74087ade05117ee2c048de387de023cfdd69c1a1 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 4 Jan 2019 10:58:51 +0100 Subject: [PATCH 1157/1240] Remove a number of orphaned files CURA-6005 --- .../CuraDrive/src/qml/images/avatar_default.png | Bin 3115 -> 0 bytes plugins/CuraDrive/src/qml/images/background.svg | 12 ------------ plugins/CuraDrive/src/qml/images/cura_logo.jpg | Bin 19308 -> 0 bytes plugins/CuraDrive/src/qml/images/cura_logo.png | Bin 13258 -> 0 bytes plugins/CuraDrive/src/qml/images/folder.svg | 7 ------- plugins/CuraDrive/src/qml/images/home.svg | 3 --- .../src/qml/images/inverted_circle.png | Bin 1608 -> 0 bytes plugins/CuraDrive/src/qml/images/restore.svg | 7 ------- 8 files changed, 29 deletions(-) delete mode 100644 plugins/CuraDrive/src/qml/images/avatar_default.png delete mode 100644 plugins/CuraDrive/src/qml/images/background.svg delete mode 100644 plugins/CuraDrive/src/qml/images/cura_logo.jpg delete mode 100644 plugins/CuraDrive/src/qml/images/cura_logo.png delete mode 100644 plugins/CuraDrive/src/qml/images/folder.svg delete mode 100644 plugins/CuraDrive/src/qml/images/home.svg delete mode 100644 plugins/CuraDrive/src/qml/images/inverted_circle.png delete mode 100644 plugins/CuraDrive/src/qml/images/restore.svg diff --git a/plugins/CuraDrive/src/qml/images/avatar_default.png b/plugins/CuraDrive/src/qml/images/avatar_default.png deleted file mode 100644 index 0c306680f721dba0de73fb7be90fe5c548608692..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3115 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 - - - Polygon 18 - Created with Sketch. - - - - - - - \ No newline at end of file diff --git a/plugins/CuraDrive/src/qml/images/cura_logo.jpg b/plugins/CuraDrive/src/qml/images/cura_logo.jpg deleted file mode 100644 index 621c03f035cddb3c9337099c25bccf19a5866968..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19308 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 V{_(E9+wT8(SKsZ~f4r-A{|^$iZYBT# diff --git a/plugins/CuraDrive/src/qml/images/cura_logo.png b/plugins/CuraDrive/src/qml/images/cura_logo.png deleted file mode 100644 index f846f2a0f0934f4843c3279ed741c149c77b2e1e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13258 zcmeHu;4V*)BSRu^XaTvbLN?uz4uJ)2XzHpY!LRVSFdoD6lJwuy+WyZdCve~ygc*u(2ZXX z6n8BJ=~tEGlzXpU@kS`gN`3M{In2g&`K-Ng?48+YOUK6%Q!&j{8UO%f(B`2OjGe6E z=pFl$ws^S=Htqi&Eu+nt`f5zkAyuM|<9+lUC7ObBbhAJ)l>)Q49oq{+g+>xugU({Y z!PhW}n#xpMueQi{{k(Xgq^qIfb|t>U@SfRMr>zd5Z{lC^?1wlPJ#bg}3RdVQbU~*? z`TxiNdl#7g_x2?FIG|)>n5~LZ=;-U{r$9xWcyi_~xxLz49$qf=d`tG6X zIStGm9v<#dC-s0$b#qRMgsHFQp^FeRDg%|8>pO3^vnue#Z%ltH62Zo)abYu)lLUvz zhB9bh6s{bH^OfNC;bPHuwsUnckKTeN_+-^P3q;Rl_BXG`4v(a2lIvSvy%OYFLvt*Z39TCgr_a$F=Mp8M>*tj%kWC~v)94VFN5 z1qXZl?GEiRi;f`o`qcv{-e76%eFdNVsQd3-SZTWP)QI7xn+|JN!Q5xxAATLlA*Y)u z8pe!KMjd{a`|_;R{4@b4QtZ`tcX#VkMd;L<4<)IL{^KurzKG;6p8r3E0*#`BRc1%Z4{vHItk9179tO)8Rr)A_% z^@S+;h%bJ(TWOfDQ?c0pN|yDfn~1yLxnevG5wggjcb=tILHvFOzr@qF%`oJMOSJ+{ zvx|e^`kBeW*=VvqpXlg0-=GhY0MM;vt$z~ATSwB$WVr;veV2t$H*iB= z9Qz*R+wd#7o~OW+AvB*R9cy`8kkDbTmzQzWzP^*cskOBg%~EfKP~j8Ris_&aPFLcN zUi#uzboWne?!(MqzJEh8^?ZVidSPC0n3@71p>*8%Z)6$;fFKy%0QQAvykQl%ipl86D zp6LP5w;D0G`6p$=CEh&yyO5|8W$0;Ch)sve$3xGmuBW@dNk;N!XYs*xus)kGap#&jjLnRa>E$AcC6{{L0hB2p z(d5UzFVqZCgKNN3ck+4~>(E_#s08-L_qzM?y77I))HmP8By}+g-%woX6(1QP(8wUT z8_$T)hmPi*wF8sEf`U=ZECL~5A8}a?!?V`I{7*d4ZwX7Lx}uJP?o@<9_<=V#dT;p< zkU)+;E_S^HTK+lpxLcUa<4?$|o6Zp!vVcu&0D#Dwt;O`Ed(S5i;&3`TIyU^WPfJ>o zMJ{QDzv6}BI!lIRtO*usuLGIrp1YIl0XL^Co4C=jrpf69aHA(>N7tQyTpu5(GS~d5%UqzuqpMu?ZR; zn5YT+op(z-ohE~l$Y}oqN`(%5@MOS)F7%$_xUvh1wzo$U0txB7mObYxX2K4(z=BQ| zLm$e;sDrk?iu{re_7*`Ura)P?pJy5L-*KN1(;Z=9a~CixFsrkfDi}Z}`u3eqNJx

    {tidI^DPfL*th*F*+_ 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 diff --git a/plugins/CuraDrive/src/qml/images/folder.svg b/plugins/CuraDrive/src/qml/images/folder.svg deleted file mode 100644 index f66f83a888..0000000000 --- a/plugins/CuraDrive/src/qml/images/folder.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/plugins/CuraDrive/src/qml/images/home.svg b/plugins/CuraDrive/src/qml/images/home.svg deleted file mode 100644 index 9d0e4d802c..0000000000 --- a/plugins/CuraDrive/src/qml/images/home.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/plugins/CuraDrive/src/qml/images/inverted_circle.png b/plugins/CuraDrive/src/qml/images/inverted_circle.png deleted file mode 100644 index 3612b37d4d38cd7b1218be42c943932a79f7b465..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1608 zcmV-O2DkZ%P)Px*0!c(cRCodHojY&bMihm$2^^$|ogzu+GJuKziK{dQ0@$6KbS?t}`UTGaabp-t z;i~kgR zm9(f#Bk`Hh#bQJToQuwp^t1Gf^xI;wNV0G!$A#wim2^+K zFMT83l8Q%Z1I|R?x%5mr7Vv*8h{%xl)y`e%JL#eHu~Z~V7{F5fT6!wI6!f=6GOrqy z86Rr&Tj`PXKw@pN3pPPU@weJJl)kXbZnu``Th&MUY7%<}t&q$U>GR!il>0P&t@@nZ zh};GoD-m{nlMMPUkwdn~WxyZm@quK!J<0Kd99kqasqEGcb@~(JT5IK!cP27c-S#u#kG0(8mB)x!EuI zv3W3&cHSKAY5+T(*>3Y==1IR!KzbqHGm%;v-v;2 z4RYK2u#!6cSnF!Qxky!b|3W&lo2<^=jwe1)mBu@+orz~NMqnp!_)z@gNL*nofa?r5*u znHuo8zp#~HypGs_SMis8IxPW_f3>Esk@uq6KaHk+GChGN|X7 z)YpLLr7x7tnD=OAT`}g0@KppSG~}9j4+8?Q4&1{|_)ITw&Af*J0a!~gU+;vEtZt4l z$ZzybRIHEaXxImETabq>ffaLB1BD-WvxU#5{|?|8IhXvfv%LWvraosAbfRrilDiR& zbC`5<74ws&0h7*T+MRa;3j5~MnScA#mXalmQ=Fbpry4LcAnp|E$84L0IpEm`5cgIC|7%EK z$vhZv->_kCYDHkhoYg?#mnIr67UI3WG%@T0Fu^a*H8KI#04KsGh^8rI?O{XU9qW}3 z%fEhuh5>w$-g<)uCETeZ?va-OjA6Zs+8V&9-=r77WEzU`k9FqtoiMNaZK|2TGh6X( zy-gJp{6_6eCcqZJ^@!I1-t$rg84M8D&ZP=4!HabyHUXx9jpMR4fX}cN_+2xJOn_y@ zRD))0Gk{Nd!KMTTh-0sUO~Iyv1bvwGECjN5$udmvqJ0AvAMqPpv=0-!Y-LXqU{d?E`(IhHzx!sYx(+Mf - - - - - - \ No newline at end of file From add5f17ae75bd23734142875e348f4aa2574b774 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 4 Jan 2019 11:06:43 +0100 Subject: [PATCH 1158/1240] And removed even more unneeded / duplicate / orphaned images CURA-6005 --- .../src/qml/components/BackupListFooter.qml | 2 +- .../src/qml/components/BackupListItemDetails.qml | 6 +++--- plugins/CuraDrive/src/qml/images/backup.svg | 3 --- plugins/CuraDrive/src/qml/images/cura.svg | 7 ------- .../CuraDrive/src/qml/images/preview_banner.png | Bin 8324 -> 0 bytes plugins/CuraDrive/src/qml/images/printer.svg | 14 -------------- 6 files changed, 4 insertions(+), 28 deletions(-) delete mode 100644 plugins/CuraDrive/src/qml/images/backup.svg delete mode 100644 plugins/CuraDrive/src/qml/images/cura.svg delete mode 100644 plugins/CuraDrive/src/qml/images/preview_banner.png delete mode 100644 plugins/CuraDrive/src/qml/images/printer.svg diff --git a/plugins/CuraDrive/src/qml/components/BackupListFooter.qml b/plugins/CuraDrive/src/qml/components/BackupListFooter.qml index a0bc212597..56706b9990 100644 --- a/plugins/CuraDrive/src/qml/components/BackupListFooter.qml +++ b/plugins/CuraDrive/src/qml/components/BackupListFooter.qml @@ -29,7 +29,7 @@ RowLayout { id: createBackupButton text: catalog.i18nc("@button", "Backup Now") - iconSource: "../images/backup.svg" + iconSource: UM.Theme.getIcon("plus") enabled: !CuraDrive.isCreatingBackup && !CuraDrive.isRestoringBackup && !backupListFooter.showInfoButton onClicked: CuraDrive.createBackup() busy: CuraDrive.isCreatingBackup diff --git a/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml b/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml index cf365501ec..2f2dd48e13 100644 --- a/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml +++ b/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml @@ -17,7 +17,7 @@ ColumnLayout // Cura version BackupListItemDetailsRow { - iconSource: "../images/cura.svg" + iconSource: UM.Theme.getIcon("application") label: catalog.i18nc("@backuplist:label", "Cura Version") value: backupDetailsData.metadata.cura_release } @@ -25,7 +25,7 @@ ColumnLayout // Machine count. BackupListItemDetailsRow { - iconSource: "../images/printer.svg" + iconSource: UM.Theme.getIcon("printer_single") label: catalog.i18nc("@backuplist:label", "Machines") value: backupDetailsData.metadata.machine_count } @@ -41,7 +41,7 @@ ColumnLayout // Profile count. BackupListItemDetailsRow { - iconSource: UM.Theme.getIcon("profile") + iconSource: UM.Theme.getIcon("settings") label: catalog.i18nc("@backuplist:label", "Profiles") value: backupDetailsData.metadata.profile_count } diff --git a/plugins/CuraDrive/src/qml/images/backup.svg b/plugins/CuraDrive/src/qml/images/backup.svg deleted file mode 100644 index 51f6be4cba..0000000000 --- a/plugins/CuraDrive/src/qml/images/backup.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/plugins/CuraDrive/src/qml/images/cura.svg b/plugins/CuraDrive/src/qml/images/cura.svg deleted file mode 100644 index 6b1b6c0c79..0000000000 --- a/plugins/CuraDrive/src/qml/images/cura.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/plugins/CuraDrive/src/qml/images/preview_banner.png b/plugins/CuraDrive/src/qml/images/preview_banner.png deleted file mode 100644 index 414019531beed2e503e05cc394fc7e6de246830a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8324 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 E2O#6GiU0rr diff --git a/plugins/CuraDrive/src/qml/images/printer.svg b/plugins/CuraDrive/src/qml/images/printer.svg deleted file mode 100644 index f7dc83987d..0000000000 --- a/plugins/CuraDrive/src/qml/images/printer.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - icn_singlePrinter - Created with Sketch. - - - - - - - - - \ No newline at end of file From 98153769ff64c306bcf7f6997c10875a2dffbe20 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 4 Jan 2019 11:23:34 +0100 Subject: [PATCH 1159/1240] Use wide_margin instead of multiplying default margin CURA-6005 --- plugins/CuraDrive/src/qml/pages/BackupsPage.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/CuraDrive/src/qml/pages/BackupsPage.qml b/plugins/CuraDrive/src/qml/pages/BackupsPage.qml index 3b905a4a39..0ba0cae09b 100644 --- a/plugins/CuraDrive/src/qml/pages/BackupsPage.qml +++ b/plugins/CuraDrive/src/qml/pages/BackupsPage.qml @@ -14,7 +14,7 @@ Item { id: backupsPage anchors.fill: parent - anchors.margins: UM.Theme.getSize("default_margin").width * 3 + anchors.margins: UM.Theme.getSize("wide_margin").width ColumnLayout { From b4911478e343dfcb0dde658014b20181790a40da Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 4 Jan 2019 11:35:34 +0100 Subject: [PATCH 1160/1240] Made the plugin depend more on the theme CURA-6005 --- plugins/CuraDrive/src/qml/components/BackupListItem.qml | 2 ++ .../src/qml/components/BackupListItemDetails.qml | 2 +- .../src/qml/components/BackupListItemDetailsRow.qml | 8 ++------ 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/plugins/CuraDrive/src/qml/components/BackupListItem.qml b/plugins/CuraDrive/src/qml/components/BackupListItem.qml index 0cd897fada..5cdb500b4e 100644 --- a/plugins/CuraDrive/src/qml/components/BackupListItem.qml +++ b/plugins/CuraDrive/src/qml/components/BackupListItem.qml @@ -50,6 +50,7 @@ Item Layout.minimumWidth: 100 * screenScaleFactor Layout.maximumWidth: 500 * screenScaleFactor Layout.fillWidth: true + font: UM.Theme.getFont("default") renderType: Text.NativeRendering } @@ -61,6 +62,7 @@ Item Layout.minimumWidth: 100 * screenScaleFactor Layout.maximumWidth: 500 * screenScaleFactor Layout.fillWidth: true + font: UM.Theme.getFont("default") renderType: Text.NativeRendering } diff --git a/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml b/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml index 2f2dd48e13..4da15c6f16 100644 --- a/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml +++ b/plugins/CuraDrive/src/qml/components/BackupListItemDetails.qml @@ -58,6 +58,6 @@ ColumnLayout Item { width: parent.width - height: 10 * screenScaleFactor + height: UM.Theme.getSize("default_margin").height } } diff --git a/plugins/CuraDrive/src/qml/components/BackupListItemDetailsRow.qml b/plugins/CuraDrive/src/qml/components/BackupListItemDetailsRow.qml index 550bdaefab..9e4612fcf8 100644 --- a/plugins/CuraDrive/src/qml/components/BackupListItemDetailsRow.qml +++ b/plugins/CuraDrive/src/qml/components/BackupListItemDetailsRow.qml @@ -17,12 +17,6 @@ RowLayout property alias label: detailName.text property alias value: detailValue.text - // Spacing. - Item - { - width: 40 * screenScaleFactor - } - UM.RecolorImage { id: icon @@ -40,6 +34,7 @@ RowLayout Layout.minimumWidth: 50 * screenScaleFactor Layout.maximumWidth: 100 * screenScaleFactor Layout.fillWidth: true + font: UM.Theme.getFont("default") renderType: Text.NativeRendering } @@ -51,6 +46,7 @@ RowLayout Layout.minimumWidth: 50 * screenScaleFactor Layout.maximumWidth: 100 * screenScaleFactor Layout.fillWidth: true + font: UM.Theme.getFont("default") renderType: Text.NativeRendering } } From c10b8b5c3f5f97a3c08be797614bd6e03662422c Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 4 Jan 2019 11:37:52 +0100 Subject: [PATCH 1161/1240] Fix crash when attempting to restore backup CURA-6005 --- plugins/CuraDrive/src/DriveApiService.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/CuraDrive/src/DriveApiService.py b/plugins/CuraDrive/src/DriveApiService.py index 3bb416fce6..79f49bf1a3 100644 --- a/plugins/CuraDrive/src/DriveApiService.py +++ b/plugins/CuraDrive/src/DriveApiService.py @@ -107,7 +107,7 @@ class DriveApiService: # Tell Cura to place the backup back in the user data folder. with open(temporary_backup_file.name, "rb") as read_backup: - self._cura_api.backups.restoreBackup(read_backup.read(), backup.get("data")) + self._cura_api.backups.restoreBackup(read_backup.read(), backup.get("metadata", {})) self.restoringStateChanged.emit(is_restoring = False) def _emitRestoreError(self): From 3df9b369f7aaa4aaddee974cfd2449cecd69064d Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 4 Jan 2019 11:40:09 +0100 Subject: [PATCH 1162/1240] Removed the divider as a seperate component Since it's only used in one place, there is no real point in making it a seperate file CURA-6005 --- plugins/CuraDrive/src/qml/components/BackupList.qml | 7 ++++--- plugins/CuraDrive/src/qml/components/Divider.qml | 13 ------------- 2 files changed, 4 insertions(+), 16 deletions(-) delete mode 100644 plugins/CuraDrive/src/qml/components/Divider.qml diff --git a/plugins/CuraDrive/src/qml/components/BackupList.qml b/plugins/CuraDrive/src/qml/components/BackupList.qml index 10ca15afe5..afa9538486 100644 --- a/plugins/CuraDrive/src/qml/components/BackupList.qml +++ b/plugins/CuraDrive/src/qml/components/BackupList.qml @@ -26,10 +26,11 @@ ScrollView width: parent.width } - Divider + Rectangle { - width: parent.width - anchors.top: backupListItem.bottom + id: divider + color: UM.Theme.getColor("lining") + height: UM.Theme.getSize("default_lining").height } } } diff --git a/plugins/CuraDrive/src/qml/components/Divider.qml b/plugins/CuraDrive/src/qml/components/Divider.qml deleted file mode 100644 index 202794fe23..0000000000 --- a/plugins/CuraDrive/src/qml/components/Divider.qml +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) 2018 Ultimaker B.V. -// Cura is released under the terms of the LGPLv3 or higher. - -import QtQuick 2.7 - -import UM 1.3 as UM - -Rectangle -{ - id: divider - color: UM.Theme.getColor("lining") - height: UM.Theme.getSize("default_lining").height -} From bca070d567937f5743b3707f25d84a095e1e31d9 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 4 Jan 2019 11:41:21 +0100 Subject: [PATCH 1163/1240] Fixed typing CURA-6005 --- cura/API/Backups.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cura/API/Backups.py b/cura/API/Backups.py index 8e5cd7b83a..ef74e74be0 100644 --- a/cura/API/Backups.py +++ b/cura/API/Backups.py @@ -1,6 +1,6 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from typing import Tuple, Optional, TYPE_CHECKING +from typing import Tuple, Optional, TYPE_CHECKING, Dict, Any from cura.Backups.BackupsManager import BackupsManager @@ -24,12 +24,12 @@ class Backups: ## Create a new back-up using the BackupsManager. # \return Tuple containing a ZIP file with the back-up data and a dict # with metadata about the back-up. - def createBackup(self) -> Tuple[Optional[bytes], Optional[dict]]: + def createBackup(self) -> Tuple[Optional[bytes], Optional[Dict[str, Any]]]: return self.manager.createBackup() ## Restore a back-up using the BackupsManager. # \param zip_file A ZIP file containing the actual back-up data. # \param meta_data Some metadata needed for restoring a back-up, like the # Cura version number. - def restoreBackup(self, zip_file: bytes, meta_data: dict) -> None: + def restoreBackup(self, zip_file: bytes, meta_data: Dict[str, Any]) -> None: return self.manager.restoreBackup(zip_file, meta_data) From b363be4afb861bb128357433d765bdc69104d746 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 4 Jan 2019 11:48:06 +0100 Subject: [PATCH 1164/1240] Fix typing --- cura/UltimakerCloudAuthentication.py | 2 +- plugins/CuraDrive/src/DrivePluginExtension.py | 4 ++-- plugins/Toolbox/src/Toolbox.py | 3 --- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/cura/UltimakerCloudAuthentication.py b/cura/UltimakerCloudAuthentication.py index 7ebdfd054b..ac752231b9 100644 --- a/cura/UltimakerCloudAuthentication.py +++ b/cura/UltimakerCloudAuthentication.py @@ -5,7 +5,7 @@ # Constants used for the Cloud API # --------- DEFAULT_CLOUD_API_ROOT = "https://api.ultimaker.com" # type: str -DEFAULT_CLOUD_API_VERSION = "1" # type: str +DEFAULT_CLOUD_API_VERSION = 1 # type: int DEFAULT_CLOUD_ACCOUNT_API_ROOT = "https://account.ultimaker.com" # type: str try: diff --git a/plugins/CuraDrive/src/DrivePluginExtension.py b/plugins/CuraDrive/src/DrivePluginExtension.py index 8944b9a980..baa8ce092e 100644 --- a/plugins/CuraDrive/src/DrivePluginExtension.py +++ b/plugins/CuraDrive/src/DrivePluginExtension.py @@ -3,7 +3,7 @@ import os from datetime import datetime -from typing import Optional, List +from typing import Optional, List, Dict, Any from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal @@ -41,7 +41,7 @@ class DrivePluginExtension(QObject, Extension): # Local data caching for the UI. self._drive_window = None # type: Optional[QObject] - self._backups = [] + self._backups = [] # type: List[Dict[str, Any]] self._is_restoring_backup = False self._is_creating_backup = False diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index e7e849f1d2..192471a357 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -31,9 +31,6 @@ i18n_catalog = i18nCatalog("cura") ## The Toolbox class is responsible of communicating with the server through the API class Toolbox(QObject, Extension): - DEFAULT_CLOUD_API_ROOT = "https://api.ultimaker.com" # type: str - DEFAULT_CLOUD_API_VERSION = 1 # type: int - def __init__(self, application: CuraApplication) -> None: super().__init__() From b4b7e1fc2138718a92a3470254c957d99a2d2b9d Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 4 Jan 2019 11:50:02 +0100 Subject: [PATCH 1165/1240] Codestyle & typing --- plugins/CuraDrive/__init__.py | 2 ++ plugins/CuraDrive/src/DriveApiService.py | 3 +-- plugins/CuraDrive/src/DrivePluginExtension.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/CuraDrive/__init__.py b/plugins/CuraDrive/__init__.py index dd7ffeaac3..eeb6b78689 100644 --- a/plugins/CuraDrive/__init__.py +++ b/plugins/CuraDrive/__init__.py @@ -3,8 +3,10 @@ from .src.DrivePluginExtension import DrivePluginExtension + def getMetaData(): return {} + def register(app): return {"extension": DrivePluginExtension()} diff --git a/plugins/CuraDrive/src/DriveApiService.py b/plugins/CuraDrive/src/DriveApiService.py index 79f49bf1a3..23e70a978c 100644 --- a/plugins/CuraDrive/src/DriveApiService.py +++ b/plugins/CuraDrive/src/DriveApiService.py @@ -110,7 +110,7 @@ class DriveApiService: self._cura_api.backups.restoreBackup(read_backup.read(), backup.get("metadata", {})) self.restoringStateChanged.emit(is_restoring = False) - def _emitRestoreError(self): + def _emitRestoreError(self) -> None: self.restoringStateChanged.emit(is_restoring = False, error_message = catalog.i18nc("@info:backup_status", "There was an error trying to restore your backup.")) @@ -144,7 +144,6 @@ class DriveApiService: # \param backup_size The size of the backup file in bytes. # \return: The upload URL for the actual backup file if successful, otherwise None. def _requestBackupUpload(self, backup_metadata: Dict[str, Any], backup_size: int) -> Optional[str]: - access_token = self._cura_api.account.accessToken if not access_token: Logger.log("w", "Could not get access token.") diff --git a/plugins/CuraDrive/src/DrivePluginExtension.py b/plugins/CuraDrive/src/DrivePluginExtension.py index baa8ce092e..fdf7fc1609 100644 --- a/plugins/CuraDrive/src/DrivePluginExtension.py +++ b/plugins/CuraDrive/src/DrivePluginExtension.py @@ -121,7 +121,7 @@ class DrivePluginExtension(QObject, Extension): return bool(self._preferences.getValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY)) @pyqtProperty("QVariantList", notify = backupsChanged) - def backups(self) -> List: + def backups(self) -> List[Dict[str, Any]]: return self._backups @pyqtSlot(name = "refreshBackups") From ffce9bd1723b71c42c0c5d0b34e4f94130cab3c3 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 4 Jan 2019 14:59:26 +0100 Subject: [PATCH 1166/1240] Ensure that the primetower gets drawn in the right color again CURA-5932 --- cura/LayerPolygon.py | 11 +++++++--- plugins/CuraEngineBackend/Cura.proto | 1 + .../ProcessSlicedLayersJob.py | 1 + plugins/SimulationView/layers3d.shader | 2 +- plugins/SimulationView/layers_shadow.shader | 20 ++++++++++++------- resources/themes/cura-light/theme.json | 1 + 6 files changed, 25 insertions(+), 11 deletions(-) diff --git a/cura/LayerPolygon.py b/cura/LayerPolygon.py index f33934de0c..1941a558ba 100644 --- a/cura/LayerPolygon.py +++ b/cura/LayerPolygon.py @@ -5,6 +5,8 @@ from UM.Application import Application from typing import Any import numpy +from UM.Logger import Logger + class LayerPolygon: NoneType = 0 @@ -18,7 +20,8 @@ class LayerPolygon: MoveCombingType = 8 MoveRetractionType = 9 SupportInterfaceType = 10 - __number_of_types = 11 + PrimeTower = 11 + __number_of_types = 12 __jump_map = numpy.logical_or(numpy.logical_or(numpy.arange(__number_of_types) == NoneType, numpy.arange(__number_of_types) == MoveCombingType), numpy.arange(__number_of_types) == MoveRetractionType) @@ -33,7 +36,8 @@ class LayerPolygon: self._extruder = extruder self._types = line_types for i in range(len(self._types)): - if self._types[i] >= self.__number_of_types: #Got faulty line data from the engine. + if self._types[i] >= self.__number_of_types: # Got faulty line data from the engine. + Logger.log("w", "Found an unknown line type: %s", i) self._types[i] = self.NoneType self._data = data self._line_widths = line_widths @@ -236,7 +240,8 @@ class LayerPolygon: theme.getColor("layerview_support_infill").getRgbF(), # SupportInfillType theme.getColor("layerview_move_combing").getRgbF(), # MoveCombingType theme.getColor("layerview_move_retraction").getRgbF(), # MoveRetractionType - theme.getColor("layerview_support_interface").getRgbF() # SupportInterfaceType + theme.getColor("layerview_support_interface").getRgbF(), # SupportInterfaceType + theme.getColor("layerview_prime_tower").getRgbF() ]) return cls.__color_map diff --git a/plugins/CuraEngineBackend/Cura.proto b/plugins/CuraEngineBackend/Cura.proto index c4f934bc00..2eabe62366 100644 --- a/plugins/CuraEngineBackend/Cura.proto +++ b/plugins/CuraEngineBackend/Cura.proto @@ -58,6 +58,7 @@ message Polygon { MoveCombingType = 8; MoveRetractionType = 9; SupportInterfaceType = 10; + PrimeTowerType = 11; } Type type = 1; // Type of move bytes points = 2; // The points of the polygon, or two points if only a line segment (Currently only line segments are used) diff --git a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py index 71c96880e8..3cc23130ea 100644 --- a/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py +++ b/plugins/CuraEngineBackend/ProcessSlicedLayersJob.py @@ -137,6 +137,7 @@ class ProcessSlicedLayersJob(Job): extruder = polygon.extruder line_types = numpy.fromstring(polygon.line_type, dtype="u1") # Convert bytearray to numpy array + line_types = line_types.reshape((-1,1)) points = numpy.fromstring(polygon.points, dtype="f4") # Convert bytearray to numpy array diff --git a/plugins/SimulationView/layers3d.shader b/plugins/SimulationView/layers3d.shader index de2b9335d8..a277606509 100644 --- a/plugins/SimulationView/layers3d.shader +++ b/plugins/SimulationView/layers3d.shader @@ -154,7 +154,7 @@ geometry41core = if ((u_show_travel_moves == 0) && ((v_line_type[0] == 8) || (v_line_type[0] == 9))) { return; } - if ((u_show_helpers == 0) && ((v_line_type[0] == 4) || (v_line_type[0] == 5) || (v_line_type[0] == 7) || (v_line_type[0] == 10))) { + if ((u_show_helpers == 0) && ((v_line_type[0] == 4) || (v_line_type[0] == 5) || (v_line_type[0] == 7) || (v_line_type[0] == 10) || v_line_type[0] == 11)) { return; } if ((u_show_skin == 0) && ((v_line_type[0] == 1) || (v_line_type[0] == 2) || (v_line_type[0] == 3))) { diff --git a/plugins/SimulationView/layers_shadow.shader b/plugins/SimulationView/layers_shadow.shader index 7ceccff21e..6149cc1703 100644 --- a/plugins/SimulationView/layers_shadow.shader +++ b/plugins/SimulationView/layers_shadow.shader @@ -45,19 +45,23 @@ fragment = void main() { - if ((u_show_travel_moves == 0) && (v_line_type >= 7.5) && (v_line_type <= 9.5)) { // actually, 8 and 9 + if ((u_show_travel_moves == 0) && (v_line_type >= 7.5) && (v_line_type <= 9.5)) + { // actually, 8 and 9 // discard movements discard; } - // support: 4, 5, 7, 10 + // support: 4, 5, 7, 10, 11 if ((u_show_helpers == 0) && ( ((v_line_type >= 3.5) && (v_line_type <= 4.5)) || ((v_line_type >= 6.5) && (v_line_type <= 7.5)) || ((v_line_type >= 9.5) && (v_line_type <= 10.5)) || - ((v_line_type >= 4.5) && (v_line_type <= 5.5)) - )) { + ((v_line_type >= 4.5) && (v_line_type <= 5.5)) || + ((v_line_type >= 10.5) && (v_line_type <= 11.5)) + )) + { discard; } + // skin: 1, 2, 3 if ((u_show_skin == 0) && ( (v_line_type >= 0.5) && (v_line_type <= 3.5) @@ -65,7 +69,8 @@ fragment = discard; } // infill: - if ((u_show_infill == 0) && (v_line_type >= 5.5) && (v_line_type <= 6.5)) { + if ((u_show_infill == 0) && (v_line_type >= 5.5) && (v_line_type <= 6.5)) + { // discard movements discard; } @@ -117,12 +122,13 @@ fragment41core = // discard movements discard; } - // helpers: 4, 5, 7, 10 + // helpers: 4, 5, 7, 10, 11 if ((u_show_helpers == 0) && ( ((v_line_type >= 3.5) && (v_line_type <= 4.5)) || ((v_line_type >= 6.5) && (v_line_type <= 7.5)) || ((v_line_type >= 9.5) && (v_line_type <= 10.5)) || - ((v_line_type >= 4.5) && (v_line_type <= 5.5)) + ((v_line_type >= 4.5) && (v_line_type <= 5.5)) || + ((v_line_type >= 10.5) && (v_line_type <= 11.5)) )) { discard; } diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 2b9ce3d218..9770994e74 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -294,6 +294,7 @@ "layerview_move_combing": [0, 0, 255, 255], "layerview_move_retraction": [128, 128, 255, 255], "layerview_support_interface": [64, 192, 255, 255], + "layerview_prime_tower": [0, 255, 255, 255], "layerview_nozzle": [181, 166, 66, 50], "tab_status_connected": [50, 130, 255, 255], From 4c1f68a44727f9d3c303d85c418bf4a62b3b6b01 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 4 Jan 2019 15:24:01 +0100 Subject: [PATCH 1167/1240] Ensure that the connectiontype of a connected printer is set correctly CURA-6011 --- plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py index 80212fcf00..57bc96b0e0 100644 --- a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py @@ -114,6 +114,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): if key == um_network_key: if not self._discovered_devices[key].isConnected(): Logger.log("d", "Attempting to connect with [%s]" % key) + active_machine.setMetaDataEntry("connection_type", self._discovered_devices[key].getConnectionType().value) self._discovered_devices[key].connect() self._discovered_devices[key].connectionStateChanged.connect(self._onDeviceConnectionStateChanged) else: From 39cdb3ed0a115ce52dcff1bc2c57512241c40748 Mon Sep 17 00:00:00 2001 From: Nico Salvador Date: Sun, 6 Jan 2019 10:19:21 +0800 Subject: [PATCH 1168/1240] Fixed typo in ChangeAtZ.py --- plugins/PostProcessingPlugin/scripts/ChangeAtZ.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py b/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py index 919b06d28e..be9f93c0f6 100644 --- a/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py +++ b/plugins/PostProcessingPlugin/scripts/ChangeAtZ.py @@ -112,7 +112,7 @@ class ChangeAtZ(Script): "e1_Change_speed": { "label": "Change Speed", - "description": "Select if total speed (print and travel) has to be cahnged", + "description": "Select if total speed (print and travel) has to be changed", "type": "bool", "default_value": false }, From d21bf53230a52a768d7794efc02fc3f7f318fa2a Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 7 Jan 2019 10:33:14 +0100 Subject: [PATCH 1169/1240] Add version upgrade for 4.0 Contributes to CURA-6011 and CURA-5995 --- cura/CuraApplication.py | 2 +- .../VersionUpgrade35to40.py | 68 +++++++++++++++++++ .../VersionUpgrade35to40/__init__.py | 56 +++++++++++++++ .../VersionUpgrade35to40/plugin.json | 8 +++ .../quality/abax_pri3/apri3_pla_fast.inst.cfg | 2 +- .../quality/abax_pri3/apri3_pla_high.inst.cfg | 2 +- .../abax_pri3/apri3_pla_normal.inst.cfg | 2 +- .../quality/abax_pri5/apri5_pla_fast.inst.cfg | 2 +- .../quality/abax_pri5/apri5_pla_high.inst.cfg | 2 +- .../abax_pri5/apri5_pla_normal.inst.cfg | 2 +- .../abax_titan/atitan_pla_fast.inst.cfg | 2 +- .../abax_titan/atitan_pla_high.inst.cfg | 2 +- .../abax_titan/atitan_pla_normal.inst.cfg | 2 +- .../abs/anycubic_4max_abs_draft.inst.cfg | 2 +- .../abs/anycubic_4max_abs_high.inst.cfg | 2 +- .../abs/anycubic_4max_abs_normal.inst.cfg | 2 +- .../anycubic_4max_draft.inst.cfg | 2 +- .../anycubic_4max/anycubic_4max_high.inst.cfg | 2 +- .../anycubic_4max_normal.inst.cfg | 2 +- .../hips/anycubic_4max_hips_draft.inst.cfg | 2 +- .../hips/anycubic_4max_hips_high.inst.cfg | 2 +- .../hips/anycubic_4max_hips_normal.inst.cfg | 2 +- .../petg/anycubic_4max_petg_draft.inst.cfg | 2 +- .../petg/anycubic_4max_petg_high.inst.cfg | 2 +- .../petg/anycubic_4max_petg_normal.inst.cfg | 2 +- .../pla/anycubic_4max_pla_draft.inst.cfg | 2 +- .../pla/anycubic_4max_pla_high.inst.cfg | 2 +- .../pla/anycubic_4max_pla_normal.inst.cfg | 2 +- .../anycubic_i3_mega_draft.inst.cfg | 2 +- .../anycubic_i3_mega_high.inst.cfg | 2 +- .../anycubic_i3_mega_normal.inst.cfg | 2 +- .../bp_BVOH_Coarse_Quality.inst.cfg | 2 +- .../bp_BVOH_High_Quality.inst.cfg | 2 +- .../bp_BVOH_Normal_Quality.inst.cfg | 2 +- .../bp_Innoflex60_Coarse_Quality.inst.cfg | 2 +- .../bp_Innoflex60_High_Quality.inst.cfg | 2 +- .../bp_Innoflex60_Normal_Quality.inst.cfg | 2 +- .../bp_PET_Coarse_Quality.inst.cfg | 2 +- .../bp_PET_High_Quality.inst.cfg | 2 +- .../bp_PET_Normal_Quality.inst.cfg | 2 +- .../bp_PLA_Coarse_Quality.inst.cfg | 2 +- .../bp_PLA_High_Quality.inst.cfg | 2 +- .../bp_PLA_Normal_Quality.inst.cfg | 2 +- .../bp_PVA_Coarse_Quality.inst.cfg | 2 +- .../bp_PVA_High_Quality.inst.cfg | 2 +- .../bp_PVA_Normal_Quality.inst.cfg | 2 +- .../bp_global_Coarse_Quality.inst.cfg | 2 +- .../bp_global_High_Quality.inst.cfg | 2 +- .../bp_global_Normal_Quality.inst.cfg | 2 +- .../abs/cartesio_0.25_abs_high.inst.cfg | 2 +- .../abs/cartesio_0.25_abs_normal.inst.cfg | 2 +- .../abs/cartesio_0.4_abs_high.inst.cfg | 2 +- .../abs/cartesio_0.4_abs_normal.inst.cfg | 2 +- .../abs/cartesio_0.8_abs_coarse.inst.cfg | 2 +- .../cartesio_0.8_abs_extra_coarse.inst.cfg | 2 +- .../abs/cartesio_0.8_abs_high.inst.cfg | 2 +- .../abs/cartesio_0.8_abs_normal.inst.cfg | 2 +- .../cartesio_0.4_arnitel2045_high.inst.cfg | 2 +- .../cartesio_0.4_arnitel2045_normal.inst.cfg | 2 +- .../cartesio_global_Coarse_Quality.inst.cfg | 2 +- ...tesio_global_Extra_Coarse_Quality.inst.cfg | 2 +- .../cartesio_global_High_Quality.inst.cfg | 2 +- .../cartesio_global_Normal_Quality.inst.cfg | 2 +- .../hips/cartesio_0.25_hips_high.inst.cfg | 2 +- .../hips/cartesio_0.25_hips_normal.inst.cfg | 2 +- .../hips/cartesio_0.4_hips_high.inst.cfg | 2 +- .../hips/cartesio_0.4_hips_normal.inst.cfg | 2 +- .../hips/cartesio_0.8_hips_coarse.inst.cfg | 2 +- .../cartesio_0.8_hips_extra_coarse.inst.cfg | 2 +- .../hips/cartesio_0.8_hips_high.inst.cfg | 2 +- .../hips/cartesio_0.8_hips_normal.inst.cfg | 2 +- .../nylon/cartesio_0.25_nylon_high.inst.cfg | 2 +- .../nylon/cartesio_0.25_nylon_normal.inst.cfg | 2 +- .../nylon/cartesio_0.4_nylon_high.inst.cfg | 2 +- .../nylon/cartesio_0.4_nylon_normal.inst.cfg | 2 +- .../nylon/cartesio_0.8_nylon_coarse.inst.cfg | 2 +- .../cartesio_0.8_nylon_extra_coarse.inst.cfg | 2 +- .../nylon/cartesio_0.8_nylon_high.inst.cfg | 2 +- .../nylon/cartesio_0.8_nylon_normal.inst.cfg | 2 +- .../pc/cartesio_0.25_pc_high.inst.cfg | 2 +- .../pc/cartesio_0.25_pc_normal.inst.cfg | 2 +- .../cartesio/pc/cartesio_0.4_pc_high.inst.cfg | 2 +- .../pc/cartesio_0.4_pc_normal.inst.cfg | 2 +- .../pc/cartesio_0.8_pc_coarse.inst.cfg | 2 +- .../pc/cartesio_0.8_pc_extra_coarse.inst.cfg | 2 +- .../cartesio/pc/cartesio_0.8_pc_high.inst.cfg | 2 +- .../pc/cartesio_0.8_pc_normal.inst.cfg | 2 +- .../petg/cartesio_0.25_petg_high.inst.cfg | 2 +- .../petg/cartesio_0.25_petg_normal.inst.cfg | 2 +- .../petg/cartesio_0.4_petg_high.inst.cfg | 2 +- .../petg/cartesio_0.4_petg_normal.inst.cfg | 2 +- .../petg/cartesio_0.8_petg_coarse.inst.cfg | 2 +- .../cartesio_0.8_petg_extra_coarse.inst.cfg | 2 +- .../petg/cartesio_0.8_petg_high.inst.cfg | 2 +- .../petg/cartesio_0.8_petg_normal.inst.cfg | 2 +- .../pla/cartesio_0.25_pla_high.inst.cfg | 2 +- .../pla/cartesio_0.25_pla_normal.inst.cfg | 2 +- .../pla/cartesio_0.4_pla_high.inst.cfg | 2 +- .../pla/cartesio_0.4_pla_normal.inst.cfg | 2 +- .../pla/cartesio_0.8_pla_coarse.inst.cfg | 2 +- .../cartesio_0.8_pla_extra_coarse.inst.cfg | 2 +- .../pla/cartesio_0.8_pla_high.inst.cfg | 2 +- .../pla/cartesio_0.8_pla_normal.inst.cfg | 2 +- .../pva/cartesio_0.25_pva_high.inst.cfg | 2 +- .../pva/cartesio_0.25_pva_normal.inst.cfg | 2 +- .../pva/cartesio_0.4_pva_high.inst.cfg | 2 +- .../pva/cartesio_0.4_pva_normal.inst.cfg | 2 +- .../pva/cartesio_0.8_pva_coarse.inst.cfg | 2 +- .../cartesio_0.8_pva_extra_coarse.inst.cfg | 2 +- .../pva/cartesio_0.8_pva_high.inst.cfg | 2 +- .../pva/cartesio_0.8_pva_normal.inst.cfg | 2 +- resources/quality/coarse.inst.cfg | 2 +- .../dagoma_discoeasy200_pla_fast.inst.cfg | 2 +- .../dagoma_discoeasy200_pla_fine.inst.cfg | 2 +- .../dagoma_discoeasy200_pla_standard.inst.cfg | 2 +- .../dagoma/dagoma_global_fast.inst.cfg | 2 +- .../dagoma/dagoma_global_fine.inst.cfg | 2 +- .../dagoma/dagoma_global_standard.inst.cfg | 2 +- .../dagoma/dagoma_magis_pla_fast.inst.cfg | 2 +- .../dagoma/dagoma_magis_pla_fine.inst.cfg | 2 +- .../dagoma/dagoma_magis_pla_standard.inst.cfg | 2 +- .../dagoma/dagoma_neva_pla_fast.inst.cfg | 2 +- .../dagoma/dagoma_neva_pla_fine.inst.cfg | 2 +- .../dagoma/dagoma_neva_pla_standard.inst.cfg | 2 +- .../deltacomb_abs_Draft_Quality.inst.cfg | 2 +- .../deltacomb_abs_Fast_Quality.inst.cfg | 2 +- .../deltacomb_abs_High_Quality.inst.cfg | 2 +- .../deltacomb_abs_Normal_Quality.inst.cfg | 2 +- .../deltacomb_abs_Verydraft_Quality.inst.cfg | 2 +- .../deltacomb_global_Draft_Quality.inst.cfg | 2 +- .../deltacomb_global_Fast_Quality.inst.cfg | 2 +- .../deltacomb_global_High_Quality.inst.cfg | 2 +- .../deltacomb_global_Normal_Quality.inst.cfg | 2 +- ...eltacomb_global_Verydraft_Quality.inst.cfg | 2 +- .../deltacomb_pla_Draft_Quality.inst.cfg | 2 +- .../deltacomb_pla_Fast_Quality.inst.cfg | 2 +- .../deltacomb_pla_High_Quality.inst.cfg | 2 +- .../deltacomb_pla_Normal_Quality.inst.cfg | 2 +- .../deltacomb_pla_Verydraft_Quality.inst.cfg | 2 +- resources/quality/draft.inst.cfg | 2 +- resources/quality/extra_coarse.inst.cfg | 2 +- resources/quality/extra_fast.inst.cfg | 2 +- .../fabtotum/fabtotum_abs_fast.inst.cfg | 2 +- .../fabtotum/fabtotum_abs_high.inst.cfg | 2 +- .../fabtotum/fabtotum_abs_normal.inst.cfg | 2 +- .../fabtotum/fabtotum_nylon_fast.inst.cfg | 2 +- .../fabtotum/fabtotum_nylon_high.inst.cfg | 2 +- .../fabtotum/fabtotum_nylon_normal.inst.cfg | 2 +- .../fabtotum/fabtotum_pla_fast.inst.cfg | 2 +- .../fabtotum/fabtotum_pla_high.inst.cfg | 2 +- .../fabtotum/fabtotum_pla_normal.inst.cfg | 2 +- .../fabtotum/fabtotum_tpu_fast.inst.cfg | 2 +- .../fabtotum/fabtotum_tpu_high.inst.cfg | 2 +- .../fabtotum/fabtotum_tpu_normal.inst.cfg | 2 +- resources/quality/fast.inst.cfg | 2 +- .../gmax15plus_pla_dual_normal.inst.cfg | 2 +- .../gmax15plus_pla_dual_thick.inst.cfg | 2 +- .../gmax15plus_pla_dual_thin.inst.cfg | 2 +- .../gmax15plus_pla_dual_very_thick.inst.cfg | 2 +- .../gmax15plus/gmax15plus_pla_normal.inst.cfg | 2 +- .../gmax15plus/gmax15plus_pla_thick.inst.cfg | 2 +- .../gmax15plus/gmax15plus_pla_thin.inst.cfg | 2 +- .../gmax15plus_pla_very_thick.inst.cfg | 2 +- resources/quality/high.inst.cfg | 2 +- .../generic_petg_0.4_coarse.inst.cfg | 2 +- .../generic_petg_0.4_coarse_2-fans.inst.cfg | 2 +- .../generic_petg_0.4_medium.inst.cfg | 2 +- .../generic_petg_0.4_medium_2-fans.inst.cfg | 2 +- .../generic_pla_0.4_coarse.inst.cfg | 2 +- .../generic_pla_0.4_coarse_2-fans.inst.cfg | 2 +- .../generic_pla_0.4_fine.inst.cfg | 2 +- .../generic_pla_0.4_fine_2-fans.inst.cfg | 2 +- .../generic_pla_0.4_medium.inst.cfg | 2 +- .../generic_pla_0.4_medium_2-fans.inst.cfg | 2 +- .../generic_pla_0.4_ultrafine.inst.cfg | 2 +- .../generic_pla_0.4_ultrafine_2-fans.inst.cfg | 2 +- .../imade3d_jellybox_coarse.inst.cfg | 2 +- .../imade3d_jellybox_fine.inst.cfg | 2 +- .../imade3d_jellybox_normal.inst.cfg | 2 +- .../imade3d_jellybox_ultrafine.inst.cfg | 2 +- .../kemiq_q2/kemiq_q2_beta_abs_draft.inst.cfg | 2 +- .../kemiq_q2_beta_abs_extra_fine.inst.cfg | 2 +- .../kemiq_q2/kemiq_q2_beta_abs_fine.inst.cfg | 2 +- .../kemiq_q2/kemiq_q2_beta_abs_low.inst.cfg | 2 +- .../kemiq_q2_beta_abs_normal.inst.cfg | 2 +- .../kemiq_q2/kemiq_q2_beta_pla_draft.inst.cfg | 2 +- .../kemiq_q2_beta_pla_extra_fine.inst.cfg | 2 +- .../kemiq_q2/kemiq_q2_beta_pla_fine.inst.cfg | 2 +- .../kemiq_q2/kemiq_q2_beta_pla_low.inst.cfg | 2 +- .../kemiq_q2_beta_pla_normal.inst.cfg | 2 +- .../kemiq_q2/kemiq_q2_gama_pla_draft.inst.cfg | 2 +- .../kemiq_q2_gama_pla_extra_fine.inst.cfg | 2 +- .../kemiq_q2/kemiq_q2_gama_pla_fine.inst.cfg | 2 +- .../kemiq_q2/kemiq_q2_gama_pla_low.inst.cfg | 2 +- .../kemiq_q2_gama_pla_normal.inst.cfg | 2 +- .../abs/malyan_m200_abs_draft.inst.cfg | 2 +- .../abs/malyan_m200_abs_fast.inst.cfg | 2 +- .../abs/malyan_m200_abs_high.inst.cfg | 2 +- .../abs/malyan_m200_abs_normal.inst.cfg | 2 +- .../abs/malyan_m200_abs_superdraft.inst.cfg | 2 +- .../abs/malyan_m200_abs_thickerdraft.inst.cfg | 2 +- .../abs/malyan_m200_abs_ultra.inst.cfg | 2 +- .../abs/malyan_m200_abs_verydraft.inst.cfg | 2 +- .../malyan_m200_global_Draft_Quality.inst.cfg | 2 +- .../malyan_m200_global_Fast_Quality.inst.cfg | 2 +- .../malyan_m200_global_High_Quality.inst.cfg | 2 +- ...malyan_m200_global_Normal_Quality.inst.cfg | 2 +- ...an_m200_global_SuperDraft_Quality.inst.cfg | 2 +- ..._m200_global_ThickerDraft_Quality.inst.cfg | 2 +- .../malyan_m200_global_Ultra_Quality.inst.cfg | 2 +- ...yan_m200_global_VeryDraft_Quality.inst.cfg | 2 +- .../petg/malyan_m200_petg_draft.inst.cfg | 2 +- .../petg/malyan_m200_petg_fast.inst.cfg | 2 +- .../petg/malyan_m200_petg_high.inst.cfg | 2 +- .../petg/malyan_m200_petg_normal.inst.cfg | 2 +- .../petg/malyan_m200_petg_superdraft.inst.cfg | 2 +- .../malyan_m200_petg_thickerdraft.inst.cfg | 2 +- .../petg/malyan_m200_petg_ultra.inst.cfg | 2 +- .../petg/malyan_m200_petg_verydraft.inst.cfg | 2 +- .../pla/malyan_m200_pla_draft.inst.cfg | 2 +- .../pla/malyan_m200_pla_fast.inst.cfg | 2 +- .../pla/malyan_m200_pla_high.inst.cfg | 2 +- .../pla/malyan_m200_pla_normal.inst.cfg | 2 +- .../pla/malyan_m200_pla_superdraft.inst.cfg | 2 +- .../pla/malyan_m200_pla_thickerdraft.inst.cfg | 2 +- .../pla/malyan_m200_pla_ultra.inst.cfg | 2 +- .../pla/malyan_m200_pla_verydraft.inst.cfg | 2 +- ...onoprice_select_mini_v2_abs_draft.inst.cfg | 2 +- ...monoprice_select_mini_v2_abs_fast.inst.cfg | 2 +- ...monoprice_select_mini_v2_abs_high.inst.cfg | 2 +- ...noprice_select_mini_v2_abs_normal.inst.cfg | 2 +- ...ice_select_mini_v2_abs_superdraft.inst.cfg | 2 +- ...e_select_mini_v2_abs_thickerdraft.inst.cfg | 2 +- ...onoprice_select_mini_v2_abs_ultra.inst.cfg | 2 +- ...rice_select_mini_v2_abs_verydraft.inst.cfg | 2 +- ...lect_mini_v2_global_Draft_Quality.inst.cfg | 2 +- ...elect_mini_v2_global_Fast_Quality.inst.cfg | 2 +- ...elect_mini_v2_global_High_Quality.inst.cfg | 2 +- ...ect_mini_v2_global_Normal_Quality.inst.cfg | 2 +- ...mini_v2_global_SuperDraft_Quality.inst.cfg | 2 +- ...ni_v2_global_ThickerDraft_Quality.inst.cfg | 2 +- ...lect_mini_v2_global_Ultra_Quality.inst.cfg | 2 +- ..._mini_v2_global_VeryDraft_Quality.inst.cfg | 2 +- ...oprice_select_mini_v2_nylon_draft.inst.cfg | 2 +- ...noprice_select_mini_v2_nylon_fast.inst.cfg | 2 +- ...noprice_select_mini_v2_nylon_high.inst.cfg | 2 +- ...price_select_mini_v2_nylon_normal.inst.cfg | 2 +- ...e_select_mini_v2_nylon_superdraft.inst.cfg | 2 +- ...select_mini_v2_nylon_thickerdraft.inst.cfg | 2 +- ...oprice_select_mini_v2_nylon_ultra.inst.cfg | 2 +- ...ce_select_mini_v2_nylon_verydraft.inst.cfg | 2 +- ...monoprice_select_mini_v2_pc_draft.inst.cfg | 2 +- .../monoprice_select_mini_v2_pc_fast.inst.cfg | 2 +- .../monoprice_select_mini_v2_pc_high.inst.cfg | 2 +- ...onoprice_select_mini_v2_pc_normal.inst.cfg | 2 +- ...rice_select_mini_v2_pc_superdraft.inst.cfg | 2 +- ...ce_select_mini_v2_pc_thickerdraft.inst.cfg | 2 +- ...monoprice_select_mini_v2_pc_ultra.inst.cfg | 2 +- ...price_select_mini_v2_pc_verydraft.inst.cfg | 2 +- ...noprice_select_mini_v2_petg_draft.inst.cfg | 2 +- ...onoprice_select_mini_v2_petg_fast.inst.cfg | 2 +- ...onoprice_select_mini_v2_petg_high.inst.cfg | 2 +- ...oprice_select_mini_v2_petg_normal.inst.cfg | 2 +- ...ce_select_mini_v2_petg_superdraft.inst.cfg | 2 +- ..._select_mini_v2_petg_thickerdraft.inst.cfg | 2 +- ...noprice_select_mini_v2_petg_ultra.inst.cfg | 2 +- ...ice_select_mini_v2_petg_verydraft.inst.cfg | 2 +- ...onoprice_select_mini_v2_pla_draft.inst.cfg | 2 +- ...monoprice_select_mini_v2_pla_fast.inst.cfg | 2 +- ...monoprice_select_mini_v2_pla_high.inst.cfg | 2 +- ...noprice_select_mini_v2_pla_normal.inst.cfg | 2 +- ...ice_select_mini_v2_pla_superdraft.inst.cfg | 2 +- ...e_select_mini_v2_pla_thickerdraft.inst.cfg | 2 +- ...onoprice_select_mini_v2_pla_ultra.inst.cfg | 2 +- ...rice_select_mini_v2_pla_verydraft.inst.cfg | 2 +- resources/quality/normal.inst.cfg | 2 +- .../peopoly_moai/peopoly_moai_coarse.inst.cfg | 2 +- .../peopoly_moai/peopoly_moai_draft.inst.cfg | 2 +- .../peopoly_moai_extra_high.inst.cfg | 2 +- .../peopoly_moai/peopoly_moai_high.inst.cfg | 2 +- .../peopoly_moai/peopoly_moai_normal.inst.cfg | 2 +- .../tevo_blackwidow_draft.inst.cfg | 2 +- .../tevo_blackwidow_high.inst.cfg | 2 +- .../tevo_blackwidow_normal.inst.cfg | 2 +- .../tizyx_k25/tizyx_k25_normal.inst.cfg | 2 +- .../quality/ultimaker2/um2_draft.inst.cfg | 2 +- .../quality/ultimaker2/um2_fast.inst.cfg | 2 +- .../quality/ultimaker2/um2_high.inst.cfg | 2 +- .../quality/ultimaker2/um2_normal.inst.cfg | 2 +- .../ultimaker2_plus/pla_0.25_normal.inst.cfg | 2 +- .../ultimaker2_plus/pla_0.4_fast.inst.cfg | 2 +- .../ultimaker2_plus/pla_0.4_high.inst.cfg | 2 +- .../ultimaker2_plus/pla_0.4_normal.inst.cfg | 2 +- .../ultimaker2_plus/pla_0.6_normal.inst.cfg | 2 +- .../ultimaker2_plus/pla_0.8_normal.inst.cfg | 2 +- .../um2p_abs_0.25_normal.inst.cfg | 2 +- .../um2p_abs_0.4_fast.inst.cfg | 2 +- .../um2p_abs_0.4_high.inst.cfg | 2 +- .../um2p_abs_0.4_normal.inst.cfg | 2 +- .../um2p_abs_0.6_normal.inst.cfg | 2 +- .../um2p_abs_0.8_normal.inst.cfg | 2 +- .../um2p_cpe_0.25_normal.inst.cfg | 2 +- .../um2p_cpe_0.4_fast.inst.cfg | 2 +- .../um2p_cpe_0.4_high.inst.cfg | 2 +- .../um2p_cpe_0.4_normal.inst.cfg | 2 +- .../um2p_cpe_0.6_normal.inst.cfg | 2 +- .../um2p_cpe_0.8_normal.inst.cfg | 2 +- .../um2p_cpep_0.4_draft.inst.cfg | 2 +- .../um2p_cpep_0.4_normal.inst.cfg | 2 +- .../um2p_cpep_0.6_draft.inst.cfg | 2 +- .../um2p_cpep_0.6_normal.inst.cfg | 2 +- .../um2p_cpep_0.8_draft.inst.cfg | 2 +- .../um2p_cpep_0.8_normal.inst.cfg | 2 +- .../um2p_global_Coarse_Quality.inst.cfg | 2 +- .../um2p_global_Draft_Quality.inst.cfg | 2 +- .../um2p_global_Extra_Coarse_Quality.inst.cfg | 2 +- .../um2p_global_Fast_Quality.inst.cfg | 2 +- .../um2p_global_High_Quality.inst.cfg | 2 +- .../um2p_global_Normal_Quality.inst.cfg | 2 +- .../um2p_nylon_0.25_high.inst.cfg | 2 +- .../um2p_nylon_0.25_normal.inst.cfg | 2 +- .../um2p_nylon_0.4_fast.inst.cfg | 2 +- .../um2p_nylon_0.4_normal.inst.cfg | 2 +- .../um2p_nylon_0.6_fast.inst.cfg | 2 +- .../um2p_nylon_0.6_normal.inst.cfg | 2 +- .../um2p_nylon_0.8_draft.inst.cfg | 2 +- .../um2p_nylon_0.8_normal.inst.cfg | 2 +- .../um2p_pc_0.25_high.inst.cfg | 2 +- .../um2p_pc_0.25_normal.inst.cfg | 2 +- .../ultimaker2_plus/um2p_pc_0.4_fast.inst.cfg | 2 +- .../um2p_pc_0.4_normal.inst.cfg | 2 +- .../ultimaker2_plus/um2p_pc_0.6_fast.inst.cfg | 2 +- .../um2p_pc_0.6_normal.inst.cfg | 2 +- .../um2p_pc_0.8_draft.inst.cfg | 2 +- .../um2p_pc_0.8_normal.inst.cfg | 2 +- .../ultimaker2_plus/um2p_pp_0.4_fast.inst.cfg | 2 +- .../um2p_pp_0.4_normal.inst.cfg | 2 +- .../um2p_pp_0.6_draft.inst.cfg | 2 +- .../ultimaker2_plus/um2p_pp_0.6_fast.inst.cfg | 2 +- .../um2p_pp_0.8_draft.inst.cfg | 2 +- .../um2p_pp_0.8_verydraft.inst.cfg | 2 +- .../um2p_tpu_0.25_high.inst.cfg | 2 +- .../um2p_tpu_0.4_normal.inst.cfg | 2 +- .../um2p_tpu_0.6_fast.inst.cfg | 2 +- .../um3_aa0.25_ABS_Normal_Quality.inst.cfg | 2 +- .../um3_aa0.25_CPE_Normal_Quality.inst.cfg | 2 +- .../um3_aa0.25_Nylon_Normal_Quality.inst.cfg | 2 +- .../um3_aa0.25_PC_Normal_Quality.inst.cfg | 2 +- .../um3_aa0.25_PLA_Normal_Quality.inst.cfg | 2 +- .../um3_aa0.25_PP_Normal_Quality.inst.cfg | 2 +- .../um3_aa0.25_TPLA_Normal_Quality.inst.cfg | 2 +- .../um3_aa0.4_ABS_Draft_Print.inst.cfg | 2 +- .../um3_aa0.4_ABS_Fast_Print.inst.cfg | 2 +- .../um3_aa0.4_ABS_High_Quality.inst.cfg | 2 +- .../um3_aa0.4_ABS_Normal_Quality.inst.cfg | 2 +- .../um3_aa0.4_BAM_Draft_Print.inst.cfg | 2 +- .../um3_aa0.4_BAM_Fast_Print.inst.cfg | 2 +- .../um3_aa0.4_BAM_Normal_Quality.inst.cfg | 2 +- .../um3_aa0.4_CPEP_Draft_Print.inst.cfg | 2 +- .../um3_aa0.4_CPEP_Fast_Print.inst.cfg | 2 +- .../um3_aa0.4_CPEP_High_Quality.inst.cfg | 2 +- .../um3_aa0.4_CPEP_Normal_Quality.inst.cfg | 2 +- .../um3_aa0.4_CPE_Draft_Print.inst.cfg | 2 +- .../um3_aa0.4_CPE_Fast_Print.inst.cfg | 2 +- .../um3_aa0.4_CPE_High_Quality.inst.cfg | 2 +- .../um3_aa0.4_CPE_Normal_Quality.inst.cfg | 2 +- .../um3_aa0.4_Nylon_Draft_Print.inst.cfg | 2 +- .../um3_aa0.4_Nylon_Fast_Print.inst.cfg | 2 +- .../um3_aa0.4_Nylon_High_Quality.inst.cfg | 2 +- .../um3_aa0.4_Nylon_Normal_Quality.inst.cfg | 2 +- .../um3_aa0.4_PC_Draft_Print.inst.cfg | 2 +- .../um3_aa0.4_PC_Fast_Print.inst.cfg | 2 +- .../um3_aa0.4_PC_High_Quality.inst.cfg | 2 +- .../um3_aa0.4_PC_Normal_Quality.inst.cfg | 2 +- .../um3_aa0.4_PLA_Draft_Print.inst.cfg | 2 +- .../um3_aa0.4_PLA_Fast_Print.inst.cfg | 2 +- .../um3_aa0.4_PLA_High_Quality.inst.cfg | 2 +- .../um3_aa0.4_PLA_Normal_Quality.inst.cfg | 2 +- .../um3_aa0.4_PP_Draft_Print.inst.cfg | 2 +- .../um3_aa0.4_PP_Fast_Print.inst.cfg | 2 +- .../um3_aa0.4_PP_Normal_Quality.inst.cfg | 2 +- .../um3_aa0.4_TPLA_Draft_Print.inst.cfg | 2 +- .../um3_aa0.4_TPLA_Fast_Print.inst.cfg | 2 +- .../um3_aa0.4_TPLA_Normal_Quality.inst.cfg | 2 +- .../um3_aa0.4_TPU_Draft_Print.inst.cfg | 2 +- .../um3_aa0.4_TPU_Fast_Print.inst.cfg | 2 +- .../um3_aa0.4_TPU_Normal_Quality.inst.cfg | 2 +- .../um3_aa0.8_ABS_Draft_Print.inst.cfg | 2 +- .../um3_aa0.8_ABS_Superdraft_Print.inst.cfg | 2 +- .../um3_aa0.8_ABS_Verydraft_Print.inst.cfg | 2 +- .../um3_aa0.8_CPEP_Fast_Print.inst.cfg | 2 +- .../um3_aa0.8_CPEP_Superdraft_Print.inst.cfg | 2 +- .../um3_aa0.8_CPEP_Verydraft_Print.inst.cfg | 2 +- .../um3_aa0.8_CPE_Draft_Print.inst.cfg | 2 +- .../um3_aa0.8_CPE_Superdraft_Print.inst.cfg | 2 +- .../um3_aa0.8_CPE_Verydraft_Print.inst.cfg | 2 +- .../um3_aa0.8_Nylon_Draft_Print.inst.cfg | 2 +- .../um3_aa0.8_Nylon_Superdraft_Print.inst.cfg | 2 +- .../um3_aa0.8_Nylon_Verydraft_Print.inst.cfg | 2 +- .../um3_aa0.8_PC_Fast_Print.inst.cfg | 2 +- .../um3_aa0.8_PC_Superdraft_Print.inst.cfg | 2 +- .../um3_aa0.8_PC_Verydraft_Print.inst.cfg | 2 +- .../um3_aa0.8_PLA_Draft_Print.inst.cfg | 2 +- .../um3_aa0.8_PLA_Superdraft_Print.inst.cfg | 2 +- .../um3_aa0.8_PLA_Verydraft_Print.inst.cfg | 2 +- .../um3_aa0.8_PP_Draft_Print.inst.cfg | 2 +- .../um3_aa0.8_PP_Superdraft_Print.inst.cfg | 2 +- .../um3_aa0.8_PP_Verydraft_Print.inst.cfg | 2 +- .../um3_aa0.8_TPLA_Draft_Print.inst.cfg | 2 +- .../um3_aa0.8_TPLA_Superdraft_Print.inst.cfg | 2 +- .../um3_aa0.8_TPLA_Verydraft_Print.inst.cfg | 2 +- .../um3_aa0.8_TPU_Draft_Print.inst.cfg | 2 +- .../um3_aa0.8_TPU_Superdraft_Print.inst.cfg | 2 +- .../um3_aa0.8_TPU_Verydraft_Print.inst.cfg | 2 +- .../um3_bb0.4_PVA_Draft_Print.inst.cfg | 2 +- .../um3_bb0.4_PVA_Fast_Print.inst.cfg | 2 +- .../um3_bb0.4_PVA_High_Quality.inst.cfg | 2 +- .../um3_bb0.4_PVA_Normal_Quality.inst.cfg | 2 +- .../um3_bb0.8_PVA_Draft_Print.inst.cfg | 2 +- .../um3_bb0.8_PVA_Superdraft_Print.inst.cfg | 2 +- .../um3_bb0.8_PVA_Verydraft_Print.inst.cfg | 2 +- .../um3_global_Draft_Quality.inst.cfg | 2 +- .../um3_global_Fast_Quality.inst.cfg | 2 +- .../um3_global_High_Quality.inst.cfg | 2 +- .../um3_global_Normal_Quality.inst.cfg | 2 +- .../um3_global_Superdraft_Quality.inst.cfg | 2 +- .../um3_global_Verydraft_Quality.inst.cfg | 2 +- .../umo_global_Coarse_Quality.inst.cfg | 2 +- .../umo_global_Draft_Quality.inst.cfg | 2 +- .../umo_global_Extra_Coarse_Quality.inst.cfg | 2 +- .../umo_global_Fast_Quality.inst.cfg | 2 +- .../umo_global_High_Quality.inst.cfg | 2 +- .../umo_global_Normal_Quality.inst.cfg | 2 +- .../um_s5_aa0.25_ABS_Normal_Quality.inst.cfg | 2 +- .../um_s5_aa0.25_CPE_Normal_Quality.inst.cfg | 2 +- ...um_s5_aa0.25_Nylon_Normal_Quality.inst.cfg | 2 +- .../um_s5_aa0.25_PC_Normal_Quality.inst.cfg | 2 +- .../um_s5_aa0.25_PLA_Normal_Quality.inst.cfg | 2 +- .../um_s5_aa0.25_PP_Normal_Quality.inst.cfg | 2 +- .../um_s5_aa0.25_TPLA_Normal_Quality.inst.cfg | 2 +- .../um_s5_aa0.4_ABS_Draft_Print.inst.cfg | 2 +- .../um_s5_aa0.4_ABS_Fast_Print.inst.cfg | 2 +- .../um_s5_aa0.4_ABS_High_Quality.inst.cfg | 2 +- .../um_s5_aa0.4_ABS_Normal_Quality.inst.cfg | 2 +- .../um_s5_aa0.4_BAM_Draft_Print.inst.cfg | 2 +- .../um_s5_aa0.4_BAM_Fast_Print.inst.cfg | 2 +- .../um_s5_aa0.4_BAM_Normal_Quality.inst.cfg | 2 +- .../um_s5_aa0.4_CPEP_Draft_Print.inst.cfg | 2 +- .../um_s5_aa0.4_CPEP_Fast_Print.inst.cfg | 2 +- .../um_s5_aa0.4_CPEP_High_Quality.inst.cfg | 2 +- .../um_s5_aa0.4_CPEP_Normal_Quality.inst.cfg | 2 +- .../um_s5_aa0.4_CPE_Draft_Print.inst.cfg | 2 +- .../um_s5_aa0.4_CPE_Fast_Print.inst.cfg | 2 +- .../um_s5_aa0.4_CPE_High_Quality.inst.cfg | 2 +- .../um_s5_aa0.4_CPE_Normal_Quality.inst.cfg | 2 +- .../um_s5_aa0.4_Nylon_Draft_Print.inst.cfg | 2 +- .../um_s5_aa0.4_Nylon_Fast_Print.inst.cfg | 2 +- .../um_s5_aa0.4_Nylon_High_Quality.inst.cfg | 2 +- .../um_s5_aa0.4_Nylon_Normal_Quality.inst.cfg | 2 +- .../um_s5_aa0.4_PC_Draft_Print.inst.cfg | 2 +- .../um_s5_aa0.4_PC_Fast_Print.inst.cfg | 2 +- .../um_s5_aa0.4_PC_High_Quality.inst.cfg | 2 +- .../um_s5_aa0.4_PC_Normal_Quality.inst.cfg | 2 +- .../um_s5_aa0.4_PLA_Draft_Print.inst.cfg | 2 +- .../um_s5_aa0.4_PLA_Fast_Print.inst.cfg | 2 +- .../um_s5_aa0.4_PLA_High_Quality.inst.cfg | 2 +- .../um_s5_aa0.4_PLA_Normal_Quality.inst.cfg | 2 +- .../um_s5_aa0.4_PP_Draft_Print.inst.cfg | 2 +- .../um_s5_aa0.4_PP_Fast_Print.inst.cfg | 2 +- .../um_s5_aa0.4_PP_Normal_Quality.inst.cfg | 2 +- .../um_s5_aa0.4_TPLA_Draft_Print.inst.cfg | 2 +- .../um_s5_aa0.4_TPLA_Fast_Print.inst.cfg | 2 +- .../um_s5_aa0.4_TPLA_High_Quality.inst.cfg | 2 +- .../um_s5_aa0.4_TPLA_Normal_Quality.inst.cfg | 2 +- .../um_s5_aa0.4_TPU_Draft_Print.inst.cfg | 2 +- .../um_s5_aa0.4_TPU_Fast_Print.inst.cfg | 2 +- .../um_s5_aa0.4_TPU_Normal_Quality.inst.cfg | 2 +- ...s5_aa0.4_aluminum_ABS_Draft_Print.inst.cfg | 2 +- ..._s5_aa0.4_aluminum_ABS_Fast_Print.inst.cfg | 2 +- ...5_aa0.4_aluminum_ABS_High_Quality.inst.cfg | 2 +- ...aa0.4_aluminum_ABS_Normal_Quality.inst.cfg | 2 +- ...5_aa0.4_aluminum_CPEP_Draft_Print.inst.cfg | 2 +- ...s5_aa0.4_aluminum_CPEP_Fast_Print.inst.cfg | 2 +- ..._aa0.4_aluminum_CPEP_High_Quality.inst.cfg | 2 +- ...a0.4_aluminum_CPEP_Normal_Quality.inst.cfg | 2 +- ...s5_aa0.4_aluminum_CPE_Draft_Print.inst.cfg | 2 +- ..._s5_aa0.4_aluminum_CPE_Fast_Print.inst.cfg | 2 +- ...5_aa0.4_aluminum_CPE_High_Quality.inst.cfg | 2 +- ...aa0.4_aluminum_CPE_Normal_Quality.inst.cfg | 2 +- ..._s5_aa0.4_aluminum_PC_Draft_Print.inst.cfg | 2 +- ...m_s5_aa0.4_aluminum_PC_Fast_Print.inst.cfg | 2 +- ...s5_aa0.4_aluminum_PC_High_Quality.inst.cfg | 2 +- ..._aa0.4_aluminum_PC_Normal_Quality.inst.cfg | 2 +- ..._s5_aa0.4_aluminum_PP_Draft_Print.inst.cfg | 2 +- ...m_s5_aa0.4_aluminum_PP_Fast_Print.inst.cfg | 2 +- ..._aa0.4_aluminum_PP_Normal_Quality.inst.cfg | 2 +- .../um_s5_aa0.8_ABS_Draft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_ABS_Superdraft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_ABS_Verydraft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_CPEP_Fast_Print.inst.cfg | 2 +- ...um_s5_aa0.8_CPEP_Superdraft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_CPEP_Verydraft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_CPE_Draft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_CPE_Superdraft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_CPE_Verydraft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_Nylon_Draft_Print.inst.cfg | 2 +- ...m_s5_aa0.8_Nylon_Superdraft_Print.inst.cfg | 2 +- ...um_s5_aa0.8_Nylon_Verydraft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_PC_Fast_Print.inst.cfg | 2 +- .../um_s5_aa0.8_PC_Superdraft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_PC_Verydraft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_PLA_Draft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_PLA_Superdraft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_PLA_Verydraft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_PP_Draft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_PP_Superdraft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_PP_Verydraft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_TPLA_Draft_Print.inst.cfg | 2 +- ...um_s5_aa0.8_TPLA_Superdraft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_TPLA_Verydraft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_TPU_Draft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_TPU_Superdraft_Print.inst.cfg | 2 +- .../um_s5_aa0.8_TPU_Verydraft_Print.inst.cfg | 2 +- ...s5_aa0.8_aluminum_ABS_Draft_Print.inst.cfg | 2 +- ...0.8_aluminum_ABS_Superdraft_Print.inst.cfg | 2 +- ...a0.8_aluminum_ABS_Verydraft_Print.inst.cfg | 2 +- ...s5_aa0.8_aluminum_CPEP_Fast_Print.inst.cfg | 2 +- ....8_aluminum_CPEP_Superdraft_Print.inst.cfg | 2 +- ...0.8_aluminum_CPEP_Verydraft_Print.inst.cfg | 2 +- ...s5_aa0.8_aluminum_CPE_Draft_Print.inst.cfg | 2 +- ...0.8_aluminum_CPE_Superdraft_Print.inst.cfg | 2 +- ...a0.8_aluminum_CPE_Verydraft_Print.inst.cfg | 2 +- ...m_s5_aa0.8_aluminum_PC_Fast_Print.inst.cfg | 2 +- ...a0.8_aluminum_PC_Superdraft_Print.inst.cfg | 2 +- ...aa0.8_aluminum_PC_Verydraft_Print.inst.cfg | 2 +- ..._s5_aa0.8_aluminum_PP_Draft_Print.inst.cfg | 2 +- ...a0.8_aluminum_PP_Superdraft_Print.inst.cfg | 2 +- ...aa0.8_aluminum_PP_Verydraft_Print.inst.cfg | 2 +- .../um_s5_bb0.4_PVA_Draft_Print.inst.cfg | 2 +- .../um_s5_bb0.4_PVA_Fast_Print.inst.cfg | 2 +- .../um_s5_bb0.4_PVA_High_Quality.inst.cfg | 2 +- .../um_s5_bb0.4_PVA_Normal_Quality.inst.cfg | 2 +- .../um_s5_bb0.8_PVA_Draft_Print.inst.cfg | 2 +- .../um_s5_bb0.8_PVA_Superdraft_Print.inst.cfg | 2 +- .../um_s5_bb0.8_PVA_Verydraft_Print.inst.cfg | 2 +- .../um_s5_cc0.6_CFFCPE_Draft_Print.inst.cfg | 2 +- .../um_s5_cc0.6_CFFPA_Draft_Print.inst.cfg | 2 +- .../um_s5_cc0.6_GFFCPE_Draft_Print.inst.cfg | 2 +- .../um_s5_cc0.6_GFFPA_Draft_Print.inst.cfg | 2 +- .../um_s5_global_Draft_Quality.inst.cfg | 2 +- .../um_s5_global_Fast_Quality.inst.cfg | 2 +- .../um_s5_global_High_Quality.inst.cfg | 2 +- .../um_s5_global_Normal_Quality.inst.cfg | 2 +- .../um_s5_global_Superdraft_Quality.inst.cfg | 2 +- .../um_s5_global_Verydraft_Quality.inst.cfg | 2 +- .../k8800_ABS_Extreme_Quality.inst.cfg | 2 +- .../k8800_ABS_High_Quality.inst.cfg | 2 +- .../k8800_ABS_Normal_Quality.inst.cfg | 2 +- .../k8800_Global_Extreme_Quality.inst.cfg | 2 +- .../k8800_Global_High_Quality.inst.cfg | 2 +- .../k8800_Global_Normal_Quality.inst.cfg | 2 +- .../k8800_PET_Extreme_Quality.inst.cfg | 2 +- .../k8800_PET_High_Quality.inst.cfg | 2 +- .../k8800_PET_Normal_Quality.inst.cfg | 2 +- .../k8800_PLA_Extreme_Quality.inst.cfg | 2 +- .../k8800_PLA_High_Quality.inst.cfg | 2 +- .../k8800_PLA_Normal_Quality.inst.cfg | 2 +- .../k8800_TPU_Extreme_Quality.inst.cfg | 2 +- .../k8800_TPU_High_Quality.inst.cfg | 2 +- .../k8800_TPU_Normal_Quality.inst.cfg | 2 +- .../zyyx/zyyx_agile_global_fast.inst.cfg | 2 +- .../zyyx/zyyx_agile_global_fine.inst.cfg | 2 +- .../zyyx/zyyx_agile_global_normal.inst.cfg | 2 +- .../zyyx/zyyx_agile_pro_flex_fast.inst.cfg | 2 +- .../zyyx/zyyx_agile_pro_flex_fine.inst.cfg | 2 +- .../zyyx/zyyx_agile_pro_flex_normal.inst.cfg | 2 +- .../zyyx/zyyx_agile_pro_pla_fast.inst.cfg | 2 +- .../zyyx/zyyx_agile_pro_pla_fine.inst.cfg | 2 +- .../zyyx/zyyx_agile_pro_pla_normal.inst.cfg | 2 +- resources/variants/cartesio_0.25.inst.cfg | 2 +- resources/variants/cartesio_0.4.inst.cfg | 2 +- resources/variants/cartesio_0.8.inst.cfg | 2 +- resources/variants/fabtotum_hyb35.inst.cfg | 2 +- resources/variants/fabtotum_lite04.inst.cfg | 2 +- resources/variants/fabtotum_lite06.inst.cfg | 2 +- resources/variants/fabtotum_pro02.inst.cfg | 2 +- resources/variants/fabtotum_pro04.inst.cfg | 2 +- resources/variants/fabtotum_pro06.inst.cfg | 2 +- resources/variants/fabtotum_pro08.inst.cfg | 2 +- resources/variants/felixtec4_0.25.inst.cfg | 2 +- resources/variants/felixtec4_0.35.inst.cfg | 2 +- resources/variants/felixtec4_0.50.inst.cfg | 2 +- resources/variants/felixtec4_0.70.inst.cfg | 2 +- .../variants/gmax15plus_025_e3d.inst.cfg | 2 +- resources/variants/gmax15plus_04_e3d.inst.cfg | 2 +- resources/variants/gmax15plus_05_e3d.inst.cfg | 2 +- .../variants/gmax15plus_05_jhead.inst.cfg | 2 +- resources/variants/gmax15plus_06_e3d.inst.cfg | 2 +- resources/variants/gmax15plus_08_e3d.inst.cfg | 2 +- .../variants/gmax15plus_10_jhead.inst.cfg | 2 +- .../variants/gmax15plus_dual_025_e3d.inst.cfg | 2 +- .../variants/gmax15plus_dual_04_e3d.inst.cfg | 2 +- .../variants/gmax15plus_dual_05_e3d.inst.cfg | 2 +- .../gmax15plus_dual_05_jhead.inst.cfg | 2 +- .../variants/gmax15plus_dual_06_e3d.inst.cfg | 2 +- .../variants/gmax15plus_dual_08_e3d.inst.cfg | 2 +- .../gmax15plus_dual_10_jhead.inst.cfg | 2 +- .../variants/imade3d_jellybox_0.4.inst.cfg | 2 +- .../imade3d_jellybox_0.4_2-fans.inst.cfg | 2 +- resources/variants/tizyx_k25_0.2.inst.cfg | 2 +- resources/variants/tizyx_k25_0.3.inst.cfg | 2 +- resources/variants/tizyx_k25_0.4.inst.cfg | 2 +- resources/variants/tizyx_k25_0.5.inst.cfg | 2 +- resources/variants/tizyx_k25_0.6.inst.cfg | 2 +- resources/variants/tizyx_k25_0.8.inst.cfg | 2 +- resources/variants/tizyx_k25_1.0.inst.cfg | 2 +- resources/variants/ultimaker2_0.25.inst.cfg | 2 +- resources/variants/ultimaker2_0.4.inst.cfg | 2 +- resources/variants/ultimaker2_0.6.inst.cfg | 2 +- resources/variants/ultimaker2_0.8.inst.cfg | 2 +- .../ultimaker2_extended_0.25.inst.cfg | 2 +- .../variants/ultimaker2_extended_0.4.inst.cfg | 2 +- .../variants/ultimaker2_extended_0.6.inst.cfg | 2 +- .../variants/ultimaker2_extended_0.8.inst.cfg | 2 +- .../ultimaker2_extended_plus_0.25.inst.cfg | 2 +- .../ultimaker2_extended_plus_0.4.inst.cfg | 2 +- .../ultimaker2_extended_plus_0.6.inst.cfg | 2 +- .../ultimaker2_extended_plus_0.8.inst.cfg | 2 +- .../variants/ultimaker2_plus_0.25.inst.cfg | 2 +- .../variants/ultimaker2_plus_0.4.inst.cfg | 2 +- .../variants/ultimaker2_plus_0.6.inst.cfg | 2 +- .../variants/ultimaker2_plus_0.8.inst.cfg | 2 +- resources/variants/ultimaker3_aa0.25.inst.cfg | 2 +- resources/variants/ultimaker3_aa0.8.inst.cfg | 2 +- resources/variants/ultimaker3_aa04.inst.cfg | 2 +- resources/variants/ultimaker3_bb0.8.inst.cfg | 2 +- resources/variants/ultimaker3_bb04.inst.cfg | 2 +- .../ultimaker3_extended_aa0.25.inst.cfg | 2 +- .../ultimaker3_extended_aa0.8.inst.cfg | 2 +- .../ultimaker3_extended_aa04.inst.cfg | 2 +- .../ultimaker3_extended_bb0.8.inst.cfg | 2 +- .../ultimaker3_extended_bb04.inst.cfg | 2 +- .../variants/ultimaker_s5_aa0.25.inst.cfg | 2 +- .../variants/ultimaker_s5_aa0.8.inst.cfg | 2 +- resources/variants/ultimaker_s5_aa04.inst.cfg | 2 +- .../variants/ultimaker_s5_aluminum.inst.cfg | 2 +- .../variants/ultimaker_s5_bb0.8.inst.cfg | 2 +- resources/variants/ultimaker_s5_bb04.inst.cfg | 2 +- resources/variants/ultimaker_s5_cc06.inst.cfg | 2 +- .../variants/ultimaker_s5_glass.inst.cfg | 2 +- 650 files changed, 779 insertions(+), 647 deletions(-) create mode 100644 plugins/VersionUpgrade/VersionUpgrade35to40/VersionUpgrade35to40.py create mode 100644 plugins/VersionUpgrade/VersionUpgrade35to40/__init__.py create mode 100644 plugins/VersionUpgrade/VersionUpgrade35to40/plugin.json diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 4d1c530237..c64e7416c9 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -143,7 +143,7 @@ class CuraApplication(QtApplication): # SettingVersion represents the set of settings available in the machine/extruder definitions. # You need to make sure that this version number needs to be increased if there is any non-backwards-compatible # changes of the settings. - SettingVersion = 5 + SettingVersion = 6 Created = False diff --git a/plugins/VersionUpgrade/VersionUpgrade35to40/VersionUpgrade35to40.py b/plugins/VersionUpgrade/VersionUpgrade35to40/VersionUpgrade35to40.py new file mode 100644 index 0000000000..52cd7cf3cb --- /dev/null +++ b/plugins/VersionUpgrade/VersionUpgrade35to40/VersionUpgrade35to40.py @@ -0,0 +1,68 @@ +import configparser +from typing import Tuple, List, Set +import io +from UM.VersionUpgrade import VersionUpgrade +from cura.PrinterOutputDevice import ConnectionType +deleted_settings = {"bridge_wall_max_overhang"} # type: Set[str] + + +class VersionUpgrade35to40(VersionUpgrade): + # Upgrades stacks to have the new version number. + def upgradeStack(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]: + parser = configparser.ConfigParser(interpolation=None) + parser.read_string(serialized) + + # Update version number. + parser["general"]["version"] = "4" + parser["metadata"]["setting_version"] = "6" + + if parser["metadata"].get("um_network_key") is not None or parser["metadata"].get("octoprint_api_key") is not None: + # Set the connection type if um_network_key or the octoprint key is set. + parser["metadata"]["connection_type"] = str(ConnectionType.NetworkConnection.value) + + result = io.StringIO() + parser.write(result) + return [filename], [result.getvalue()] + pass + + def getCfgVersion(self, serialised: str) -> int: + parser = configparser.ConfigParser(interpolation = None) + parser.read_string(serialised) + format_version = int(parser.get("general", "version")) #Explicitly give an exception when this fails. That means that the file format is not recognised. + setting_version = int(parser.get("metadata", "setting_version", fallback = "0")) + return format_version * 1000000 + setting_version + + ## Upgrades Preferences to have the new version number. + def upgradePreferences(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]: + parser = configparser.ConfigParser(interpolation=None) + parser.read_string(serialized) + + if "metadata" not in parser: + parser["metadata"] = {} + parser["general"]["version"] = "6" + parser["metadata"]["setting_version"] = "6" + + result = io.StringIO() + parser.write(result) + return [filename], [result.getvalue()] + + ## Upgrades instance containers to have the new version + # number. + def upgradeInstanceContainer(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]: + parser = configparser.ConfigParser(interpolation=None) + parser.read_string(serialized) + + # Update version number. + parser["general"]["version"] = "4" + parser["metadata"]["setting_version"] = "6" + + #self._resetConcentric3DInfillPattern(parser) + if "values" in parser: + for deleted_setting in deleted_settings: + if deleted_setting not in parser["values"]: + continue + del parser["values"][deleted_setting] + + result = io.StringIO() + parser.write(result) + return [filename], [result.getvalue()] \ No newline at end of file diff --git a/plugins/VersionUpgrade/VersionUpgrade35to40/__init__.py b/plugins/VersionUpgrade/VersionUpgrade35to40/__init__.py new file mode 100644 index 0000000000..2ad1dddbf2 --- /dev/null +++ b/plugins/VersionUpgrade/VersionUpgrade35to40/__init__.py @@ -0,0 +1,56 @@ +from typing import Dict, Any + +from . import VersionUpgrade35to40 + +upgrade = VersionUpgrade35to40.VersionUpgrade35to40() + + +def getMetaData() -> Dict[str, Any]: + return { + "version_upgrade": { + # From To Upgrade function + ("preferences", 6000005): ("preferences", 6000006, upgrade.upgradePreferences), + + ("definition_changes", 4000005): ("definition_changes", 4000006, upgrade.upgradeInstanceContainer), + ("quality_changes", 4000005): ("quality_changes", 4000006, upgrade.upgradeInstanceContainer), + ("quality", 4000005): ("quality", 4000006, upgrade.upgradeInstanceContainer), + ("user", 4000005): ("user", 4000006, upgrade.upgradeInstanceContainer), + + ("machine_stack", 4000005): ("machine_stack", 4000006, upgrade.upgradeStack), + ("extruder_train", 4000005): ("extruder_train", 4000006, upgrade.upgradeStack), + }, + "sources": { + "preferences": { + "get_version": upgrade.getCfgVersion, + "location": {"."} + }, + "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"} + }, + "quality_changes": { + "get_version": upgrade.getCfgVersion, + "location": {"./quality_changes"} + }, + "quality": { + "get_version": upgrade.getCfgVersion, + "location": {"./quality"} + }, + "user": { + "get_version": upgrade.getCfgVersion, + "location": {"./user"} + } + } + } + + +def register(app) -> Dict[str, Any]: + return {"version_upgrade": upgrade} \ No newline at end of file diff --git a/plugins/VersionUpgrade/VersionUpgrade35to40/plugin.json b/plugins/VersionUpgrade/VersionUpgrade35to40/plugin.json new file mode 100644 index 0000000000..578594fb6d --- /dev/null +++ b/plugins/VersionUpgrade/VersionUpgrade35to40/plugin.json @@ -0,0 +1,8 @@ + { + "name": "Version Upgrade 3.5 to 4.0", + "author": "Ultimaker B.V.", + "version": "1.0.0", + "description": "Upgrades configurations from Cura 3.5 to Cura 4.0.", + "api": "6.0", + "i18n-catalog": "cura" +} diff --git a/resources/quality/abax_pri3/apri3_pla_fast.inst.cfg b/resources/quality/abax_pri3/apri3_pla_fast.inst.cfg index 1d406e4387..fcd6b46f2d 100644 --- a/resources/quality/abax_pri3/apri3_pla_fast.inst.cfg +++ b/resources/quality/abax_pri3/apri3_pla_fast.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = abax_pri3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = -1 diff --git a/resources/quality/abax_pri3/apri3_pla_high.inst.cfg b/resources/quality/abax_pri3/apri3_pla_high.inst.cfg index 20d2c024aa..e9e8d53a03 100644 --- a/resources/quality/abax_pri3/apri3_pla_high.inst.cfg +++ b/resources/quality/abax_pri3/apri3_pla_high.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = abax_pri3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/abax_pri3/apri3_pla_normal.inst.cfg b/resources/quality/abax_pri3/apri3_pla_normal.inst.cfg index 0a4d1f1c62..40399c9548 100644 --- a/resources/quality/abax_pri3/apri3_pla_normal.inst.cfg +++ b/resources/quality/abax_pri3/apri3_pla_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = abax_pri3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/abax_pri5/apri5_pla_fast.inst.cfg b/resources/quality/abax_pri5/apri5_pla_fast.inst.cfg index 6d0fdd40d2..6747ada00d 100644 --- a/resources/quality/abax_pri5/apri5_pla_fast.inst.cfg +++ b/resources/quality/abax_pri5/apri5_pla_fast.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = abax_pri5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = -1 diff --git a/resources/quality/abax_pri5/apri5_pla_high.inst.cfg b/resources/quality/abax_pri5/apri5_pla_high.inst.cfg index 212c92e7d1..99bd10d654 100644 --- a/resources/quality/abax_pri5/apri5_pla_high.inst.cfg +++ b/resources/quality/abax_pri5/apri5_pla_high.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = abax_pri5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/abax_pri5/apri5_pla_normal.inst.cfg b/resources/quality/abax_pri5/apri5_pla_normal.inst.cfg index 8a1338f28c..55e0a7755a 100644 --- a/resources/quality/abax_pri5/apri5_pla_normal.inst.cfg +++ b/resources/quality/abax_pri5/apri5_pla_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = abax_pri5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/abax_titan/atitan_pla_fast.inst.cfg b/resources/quality/abax_titan/atitan_pla_fast.inst.cfg index 71740ede84..e1e558be6d 100644 --- a/resources/quality/abax_titan/atitan_pla_fast.inst.cfg +++ b/resources/quality/abax_titan/atitan_pla_fast.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = abax_titan [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = -1 diff --git a/resources/quality/abax_titan/atitan_pla_high.inst.cfg b/resources/quality/abax_titan/atitan_pla_high.inst.cfg index 73cd31f3fd..7dd5833297 100644 --- a/resources/quality/abax_titan/atitan_pla_high.inst.cfg +++ b/resources/quality/abax_titan/atitan_pla_high.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = abax_titan [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/abax_titan/atitan_pla_normal.inst.cfg b/resources/quality/abax_titan/atitan_pla_normal.inst.cfg index c356e197ce..b6aae46c6d 100644 --- a/resources/quality/abax_titan/atitan_pla_normal.inst.cfg +++ b/resources/quality/abax_titan/atitan_pla_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = abax_titan [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/anycubic_4max/abs/anycubic_4max_abs_draft.inst.cfg b/resources/quality/anycubic_4max/abs/anycubic_4max_abs_draft.inst.cfg index f5baa55029..3a85bd77b5 100644 --- a/resources/quality/anycubic_4max/abs/anycubic_4max_abs_draft.inst.cfg +++ b/resources/quality/anycubic_4max/abs/anycubic_4max_abs_draft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = 0 diff --git a/resources/quality/anycubic_4max/abs/anycubic_4max_abs_high.inst.cfg b/resources/quality/anycubic_4max/abs/anycubic_4max_abs_high.inst.cfg index bd613c6aad..a3b4275f39 100644 --- a/resources/quality/anycubic_4max/abs/anycubic_4max_abs_high.inst.cfg +++ b/resources/quality/anycubic_4max/abs/anycubic_4max_abs_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 2 diff --git a/resources/quality/anycubic_4max/abs/anycubic_4max_abs_normal.inst.cfg b/resources/quality/anycubic_4max/abs/anycubic_4max_abs_normal.inst.cfg index 7cff1db4d2..e3add50a4a 100644 --- a/resources/quality/anycubic_4max/abs/anycubic_4max_abs_normal.inst.cfg +++ b/resources/quality/anycubic_4max/abs/anycubic_4max_abs_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 1 diff --git a/resources/quality/anycubic_4max/anycubic_4max_draft.inst.cfg b/resources/quality/anycubic_4max/anycubic_4max_draft.inst.cfg index c0114e3d6c..78c61b112b 100644 --- a/resources/quality/anycubic_4max/anycubic_4max_draft.inst.cfg +++ b/resources/quality/anycubic_4max/anycubic_4max_draft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = 0 diff --git a/resources/quality/anycubic_4max/anycubic_4max_high.inst.cfg b/resources/quality/anycubic_4max/anycubic_4max_high.inst.cfg index 4a0993412a..722403ed23 100644 --- a/resources/quality/anycubic_4max/anycubic_4max_high.inst.cfg +++ b/resources/quality/anycubic_4max/anycubic_4max_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 2 diff --git a/resources/quality/anycubic_4max/anycubic_4max_normal.inst.cfg b/resources/quality/anycubic_4max/anycubic_4max_normal.inst.cfg index eeb1d699e4..b57f0357e3 100644 --- a/resources/quality/anycubic_4max/anycubic_4max_normal.inst.cfg +++ b/resources/quality/anycubic_4max/anycubic_4max_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 1 diff --git a/resources/quality/anycubic_4max/hips/anycubic_4max_hips_draft.inst.cfg b/resources/quality/anycubic_4max/hips/anycubic_4max_hips_draft.inst.cfg index 3cd0226bd4..4f04512596 100644 --- a/resources/quality/anycubic_4max/hips/anycubic_4max_hips_draft.inst.cfg +++ b/resources/quality/anycubic_4max/hips/anycubic_4max_hips_draft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = 0 diff --git a/resources/quality/anycubic_4max/hips/anycubic_4max_hips_high.inst.cfg b/resources/quality/anycubic_4max/hips/anycubic_4max_hips_high.inst.cfg index ff5c6bee2f..a3749d734e 100644 --- a/resources/quality/anycubic_4max/hips/anycubic_4max_hips_high.inst.cfg +++ b/resources/quality/anycubic_4max/hips/anycubic_4max_hips_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 2 diff --git a/resources/quality/anycubic_4max/hips/anycubic_4max_hips_normal.inst.cfg b/resources/quality/anycubic_4max/hips/anycubic_4max_hips_normal.inst.cfg index c4701ae246..23b0ded57a 100644 --- a/resources/quality/anycubic_4max/hips/anycubic_4max_hips_normal.inst.cfg +++ b/resources/quality/anycubic_4max/hips/anycubic_4max_hips_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 1 diff --git a/resources/quality/anycubic_4max/petg/anycubic_4max_petg_draft.inst.cfg b/resources/quality/anycubic_4max/petg/anycubic_4max_petg_draft.inst.cfg index 5e0c3e204a..3d44ec8d1b 100644 --- a/resources/quality/anycubic_4max/petg/anycubic_4max_petg_draft.inst.cfg +++ b/resources/quality/anycubic_4max/petg/anycubic_4max_petg_draft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = 0 diff --git a/resources/quality/anycubic_4max/petg/anycubic_4max_petg_high.inst.cfg b/resources/quality/anycubic_4max/petg/anycubic_4max_petg_high.inst.cfg index 57a89c4ec2..74e7e96d6e 100644 --- a/resources/quality/anycubic_4max/petg/anycubic_4max_petg_high.inst.cfg +++ b/resources/quality/anycubic_4max/petg/anycubic_4max_petg_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 2 diff --git a/resources/quality/anycubic_4max/petg/anycubic_4max_petg_normal.inst.cfg b/resources/quality/anycubic_4max/petg/anycubic_4max_petg_normal.inst.cfg index 14a4607ceb..2aa77a4089 100644 --- a/resources/quality/anycubic_4max/petg/anycubic_4max_petg_normal.inst.cfg +++ b/resources/quality/anycubic_4max/petg/anycubic_4max_petg_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 1 diff --git a/resources/quality/anycubic_4max/pla/anycubic_4max_pla_draft.inst.cfg b/resources/quality/anycubic_4max/pla/anycubic_4max_pla_draft.inst.cfg index eae9e3b5ef..e479ee4a1f 100644 --- a/resources/quality/anycubic_4max/pla/anycubic_4max_pla_draft.inst.cfg +++ b/resources/quality/anycubic_4max/pla/anycubic_4max_pla_draft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = 0 diff --git a/resources/quality/anycubic_4max/pla/anycubic_4max_pla_high.inst.cfg b/resources/quality/anycubic_4max/pla/anycubic_4max_pla_high.inst.cfg index c856fc66a7..40ca97078c 100644 --- a/resources/quality/anycubic_4max/pla/anycubic_4max_pla_high.inst.cfg +++ b/resources/quality/anycubic_4max/pla/anycubic_4max_pla_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 2 diff --git a/resources/quality/anycubic_4max/pla/anycubic_4max_pla_normal.inst.cfg b/resources/quality/anycubic_4max/pla/anycubic_4max_pla_normal.inst.cfg index be33bfe53a..17f9cfe7cd 100644 --- a/resources/quality/anycubic_4max/pla/anycubic_4max_pla_normal.inst.cfg +++ b/resources/quality/anycubic_4max/pla/anycubic_4max_pla_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = anycubic_4max [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 1 diff --git a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg index e94b9f01d1..96b423d9d0 100644 --- a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg +++ b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_draft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = anycubic_i3_mega [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = 0 diff --git a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg index c8c4bf9a81..90d6135c4c 100644 --- a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg +++ b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = anycubic_i3_mega [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 2 diff --git a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg index 399c3ebc55..2f24a4c6f0 100644 --- a/resources/quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg +++ b/resources/quality/anycubic_i3_mega/anycubic_i3_mega_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = anycubic_i3_mega [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 1 diff --git a/resources/quality/builder_premium/bp_BVOH_Coarse_Quality.inst.cfg b/resources/quality/builder_premium/bp_BVOH_Coarse_Quality.inst.cfg index 10c8bc553f..491d24c919 100644 --- a/resources/quality/builder_premium/bp_BVOH_Coarse_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_BVOH_Coarse_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = -1 diff --git a/resources/quality/builder_premium/bp_BVOH_High_Quality.inst.cfg b/resources/quality/builder_premium/bp_BVOH_High_Quality.inst.cfg index b09ceb8872..3ff0f07ac1 100644 --- a/resources/quality/builder_premium/bp_BVOH_High_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_BVOH_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = High Quality definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/builder_premium/bp_BVOH_Normal_Quality.inst.cfg b/resources/quality/builder_premium/bp_BVOH_Normal_Quality.inst.cfg index c2425a6d86..cf67185706 100644 --- a/resources/quality/builder_premium/bp_BVOH_Normal_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_BVOH_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/builder_premium/bp_Innoflex60_Coarse_Quality.inst.cfg b/resources/quality/builder_premium/bp_Innoflex60_Coarse_Quality.inst.cfg index 038fd02fff..aedb45a2b1 100644 --- a/resources/quality/builder_premium/bp_Innoflex60_Coarse_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_Innoflex60_Coarse_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = -1 diff --git a/resources/quality/builder_premium/bp_Innoflex60_High_Quality.inst.cfg b/resources/quality/builder_premium/bp_Innoflex60_High_Quality.inst.cfg index e9512e30f8..08fb395a36 100644 --- a/resources/quality/builder_premium/bp_Innoflex60_High_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_Innoflex60_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = High Quality definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/builder_premium/bp_Innoflex60_Normal_Quality.inst.cfg b/resources/quality/builder_premium/bp_Innoflex60_Normal_Quality.inst.cfg index fb6ec6c89f..008de91758 100644 --- a/resources/quality/builder_premium/bp_Innoflex60_Normal_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_Innoflex60_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/builder_premium/bp_PET_Coarse_Quality.inst.cfg b/resources/quality/builder_premium/bp_PET_Coarse_Quality.inst.cfg index 572e83d4b3..0e2c26812b 100644 --- a/resources/quality/builder_premium/bp_PET_Coarse_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_PET_Coarse_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = -1 diff --git a/resources/quality/builder_premium/bp_PET_High_Quality.inst.cfg b/resources/quality/builder_premium/bp_PET_High_Quality.inst.cfg index f4a45dd43a..8217ca9156 100644 --- a/resources/quality/builder_premium/bp_PET_High_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_PET_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = High Quality definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/builder_premium/bp_PET_Normal_Quality.inst.cfg b/resources/quality/builder_premium/bp_PET_Normal_Quality.inst.cfg index d927ab0851..48b2df0cae 100644 --- a/resources/quality/builder_premium/bp_PET_Normal_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_PET_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/builder_premium/bp_PLA_Coarse_Quality.inst.cfg b/resources/quality/builder_premium/bp_PLA_Coarse_Quality.inst.cfg index fc877df05d..6834cbb2c4 100644 --- a/resources/quality/builder_premium/bp_PLA_Coarse_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_PLA_Coarse_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = -1 diff --git a/resources/quality/builder_premium/bp_PLA_High_Quality.inst.cfg b/resources/quality/builder_premium/bp_PLA_High_Quality.inst.cfg index 586133a500..9fc5426cf9 100644 --- a/resources/quality/builder_premium/bp_PLA_High_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_PLA_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = High Quality definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/builder_premium/bp_PLA_Normal_Quality.inst.cfg b/resources/quality/builder_premium/bp_PLA_Normal_Quality.inst.cfg index e84b2eaa87..161bee24df 100644 --- a/resources/quality/builder_premium/bp_PLA_Normal_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_PLA_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/builder_premium/bp_PVA_Coarse_Quality.inst.cfg b/resources/quality/builder_premium/bp_PVA_Coarse_Quality.inst.cfg index f661e58b6d..149b938bac 100644 --- a/resources/quality/builder_premium/bp_PVA_Coarse_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_PVA_Coarse_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = -1 diff --git a/resources/quality/builder_premium/bp_PVA_High_Quality.inst.cfg b/resources/quality/builder_premium/bp_PVA_High_Quality.inst.cfg index 31b9c094df..ba310a0890 100644 --- a/resources/quality/builder_premium/bp_PVA_High_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_PVA_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = High Quality definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/builder_premium/bp_PVA_Normal_Quality.inst.cfg b/resources/quality/builder_premium/bp_PVA_Normal_Quality.inst.cfg index 8c3ca85124..4ab949957f 100644 --- a/resources/quality/builder_premium/bp_PVA_Normal_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_PVA_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/builder_premium/bp_global_Coarse_Quality.inst.cfg b/resources/quality/builder_premium/bp_global_Coarse_Quality.inst.cfg index a1d80774a1..9a85c924ee 100644 --- a/resources/quality/builder_premium/bp_global_Coarse_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_global_Coarse_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = -1 diff --git a/resources/quality/builder_premium/bp_global_High_Quality.inst.cfg b/resources/quality/builder_premium/bp_global_High_Quality.inst.cfg index 465ea6090a..ca2f8c843f 100644 --- a/resources/quality/builder_premium/bp_global_High_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_global_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = High Quality definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/builder_premium/bp_global_Normal_Quality.inst.cfg b/resources/quality/builder_premium/bp_global_Normal_Quality.inst.cfg index e872561b90..3b24f9963d 100644 --- a/resources/quality/builder_premium/bp_global_Normal_Quality.inst.cfg +++ b/resources/quality/builder_premium/bp_global_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = builder_premium_small [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/cartesio/abs/cartesio_0.25_abs_high.inst.cfg b/resources/quality/cartesio/abs/cartesio_0.25_abs_high.inst.cfg index 5626ed58de..f19635331d 100644 --- a/resources/quality/cartesio/abs/cartesio_0.25_abs_high.inst.cfg +++ b/resources/quality/cartesio/abs/cartesio_0.25_abs_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/abs/cartesio_0.25_abs_normal.inst.cfg b/resources/quality/cartesio/abs/cartesio_0.25_abs_normal.inst.cfg index f74918ef4f..e203e88e0c 100644 --- a/resources/quality/cartesio/abs/cartesio_0.25_abs_normal.inst.cfg +++ b/resources/quality/cartesio/abs/cartesio_0.25_abs_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 2 diff --git a/resources/quality/cartesio/abs/cartesio_0.4_abs_high.inst.cfg b/resources/quality/cartesio/abs/cartesio_0.4_abs_high.inst.cfg index d4dd85b09d..99e1290c77 100644 --- a/resources/quality/cartesio/abs/cartesio_0.4_abs_high.inst.cfg +++ b/resources/quality/cartesio/abs/cartesio_0.4_abs_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/abs/cartesio_0.4_abs_normal.inst.cfg b/resources/quality/cartesio/abs/cartesio_0.4_abs_normal.inst.cfg index ab72092035..54a7d094ce 100644 --- a/resources/quality/cartesio/abs/cartesio_0.4_abs_normal.inst.cfg +++ b/resources/quality/cartesio/abs/cartesio_0.4_abs_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 2 diff --git a/resources/quality/cartesio/abs/cartesio_0.8_abs_coarse.inst.cfg b/resources/quality/cartesio/abs/cartesio_0.8_abs_coarse.inst.cfg index d15efb770f..5496468fd6 100644 --- a/resources/quality/cartesio/abs/cartesio_0.8_abs_coarse.inst.cfg +++ b/resources/quality/cartesio/abs/cartesio_0.8_abs_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = 3 diff --git a/resources/quality/cartesio/abs/cartesio_0.8_abs_extra_coarse.inst.cfg b/resources/quality/cartesio/abs/cartesio_0.8_abs_extra_coarse.inst.cfg index 7467da765f..360397ddc9 100644 --- a/resources/quality/cartesio/abs/cartesio_0.8_abs_extra_coarse.inst.cfg +++ b/resources/quality/cartesio/abs/cartesio_0.8_abs_extra_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Extra Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extra coarse weight = 4 diff --git a/resources/quality/cartesio/abs/cartesio_0.8_abs_high.inst.cfg b/resources/quality/cartesio/abs/cartesio_0.8_abs_high.inst.cfg index caa6b71a4a..82e0afa853 100644 --- a/resources/quality/cartesio/abs/cartesio_0.8_abs_high.inst.cfg +++ b/resources/quality/cartesio/abs/cartesio_0.8_abs_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/abs/cartesio_0.8_abs_normal.inst.cfg b/resources/quality/cartesio/abs/cartesio_0.8_abs_normal.inst.cfg index 9eea2177a8..a3f2c1d796 100644 --- a/resources/quality/cartesio/abs/cartesio_0.8_abs_normal.inst.cfg +++ b/resources/quality/cartesio/abs/cartesio_0.8_abs_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 2 diff --git a/resources/quality/cartesio/arnitel/cartesio_0.4_arnitel2045_high.inst.cfg b/resources/quality/cartesio/arnitel/cartesio_0.4_arnitel2045_high.inst.cfg index 17f2acd8d1..fa89b4c3aa 100644 --- a/resources/quality/cartesio/arnitel/cartesio_0.4_arnitel2045_high.inst.cfg +++ b/resources/quality/cartesio/arnitel/cartesio_0.4_arnitel2045_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/arnitel/cartesio_0.4_arnitel2045_normal.inst.cfg b/resources/quality/cartesio/arnitel/cartesio_0.4_arnitel2045_normal.inst.cfg index 8215eb2f50..5a2be13737 100644 --- a/resources/quality/cartesio/arnitel/cartesio_0.4_arnitel2045_normal.inst.cfg +++ b/resources/quality/cartesio/arnitel/cartesio_0.4_arnitel2045_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 2 diff --git a/resources/quality/cartesio/cartesio_global_Coarse_Quality.inst.cfg b/resources/quality/cartesio/cartesio_global_Coarse_Quality.inst.cfg index f92fc49981..7ce1111dd8 100644 --- a/resources/quality/cartesio/cartesio_global_Coarse_Quality.inst.cfg +++ b/resources/quality/cartesio/cartesio_global_Coarse_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = -3 diff --git a/resources/quality/cartesio/cartesio_global_Extra_Coarse_Quality.inst.cfg b/resources/quality/cartesio/cartesio_global_Extra_Coarse_Quality.inst.cfg index 1681e83279..0369c74e2a 100644 --- a/resources/quality/cartesio/cartesio_global_Extra_Coarse_Quality.inst.cfg +++ b/resources/quality/cartesio/cartesio_global_Extra_Coarse_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extra coarse weight = -4 diff --git a/resources/quality/cartesio/cartesio_global_High_Quality.inst.cfg b/resources/quality/cartesio/cartesio_global_High_Quality.inst.cfg index 95d1f3b37e..dafe5ef621 100644 --- a/resources/quality/cartesio/cartesio_global_High_Quality.inst.cfg +++ b/resources/quality/cartesio/cartesio_global_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/cartesio_global_Normal_Quality.inst.cfg b/resources/quality/cartesio/cartesio_global_Normal_Quality.inst.cfg index 6bb348282e..100da3c70a 100644 --- a/resources/quality/cartesio/cartesio_global_Normal_Quality.inst.cfg +++ b/resources/quality/cartesio/cartesio_global_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/cartesio/hips/cartesio_0.25_hips_high.inst.cfg b/resources/quality/cartesio/hips/cartesio_0.25_hips_high.inst.cfg index 3e212b2446..93e04e1514 100644 --- a/resources/quality/cartesio/hips/cartesio_0.25_hips_high.inst.cfg +++ b/resources/quality/cartesio/hips/cartesio_0.25_hips_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/hips/cartesio_0.25_hips_normal.inst.cfg b/resources/quality/cartesio/hips/cartesio_0.25_hips_normal.inst.cfg index 0cf82847a0..a90a0823df 100644 --- a/resources/quality/cartesio/hips/cartesio_0.25_hips_normal.inst.cfg +++ b/resources/quality/cartesio/hips/cartesio_0.25_hips_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 2 diff --git a/resources/quality/cartesio/hips/cartesio_0.4_hips_high.inst.cfg b/resources/quality/cartesio/hips/cartesio_0.4_hips_high.inst.cfg index a9664cf9d1..91b1627650 100644 --- a/resources/quality/cartesio/hips/cartesio_0.4_hips_high.inst.cfg +++ b/resources/quality/cartesio/hips/cartesio_0.4_hips_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/hips/cartesio_0.4_hips_normal.inst.cfg b/resources/quality/cartesio/hips/cartesio_0.4_hips_normal.inst.cfg index 8101fb6dd8..f9242fe62a 100644 --- a/resources/quality/cartesio/hips/cartesio_0.4_hips_normal.inst.cfg +++ b/resources/quality/cartesio/hips/cartesio_0.4_hips_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 2 diff --git a/resources/quality/cartesio/hips/cartesio_0.8_hips_coarse.inst.cfg b/resources/quality/cartesio/hips/cartesio_0.8_hips_coarse.inst.cfg index f009383ad8..6b84c5e864 100644 --- a/resources/quality/cartesio/hips/cartesio_0.8_hips_coarse.inst.cfg +++ b/resources/quality/cartesio/hips/cartesio_0.8_hips_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = 3 diff --git a/resources/quality/cartesio/hips/cartesio_0.8_hips_extra_coarse.inst.cfg b/resources/quality/cartesio/hips/cartesio_0.8_hips_extra_coarse.inst.cfg index 1adbbb0fb9..16f299813b 100644 --- a/resources/quality/cartesio/hips/cartesio_0.8_hips_extra_coarse.inst.cfg +++ b/resources/quality/cartesio/hips/cartesio_0.8_hips_extra_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Extra Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extra coarse weight = 4 diff --git a/resources/quality/cartesio/hips/cartesio_0.8_hips_high.inst.cfg b/resources/quality/cartesio/hips/cartesio_0.8_hips_high.inst.cfg index d3e6df227f..686759bc4d 100644 --- a/resources/quality/cartesio/hips/cartesio_0.8_hips_high.inst.cfg +++ b/resources/quality/cartesio/hips/cartesio_0.8_hips_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/hips/cartesio_0.8_hips_normal.inst.cfg b/resources/quality/cartesio/hips/cartesio_0.8_hips_normal.inst.cfg index 0b019d555f..724abcd63a 100644 --- a/resources/quality/cartesio/hips/cartesio_0.8_hips_normal.inst.cfg +++ b/resources/quality/cartesio/hips/cartesio_0.8_hips_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 2 diff --git a/resources/quality/cartesio/nylon/cartesio_0.25_nylon_high.inst.cfg b/resources/quality/cartesio/nylon/cartesio_0.25_nylon_high.inst.cfg index 9eb5d5c4e9..bd2dca940f 100644 --- a/resources/quality/cartesio/nylon/cartesio_0.25_nylon_high.inst.cfg +++ b/resources/quality/cartesio/nylon/cartesio_0.25_nylon_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/nylon/cartesio_0.25_nylon_normal.inst.cfg b/resources/quality/cartesio/nylon/cartesio_0.25_nylon_normal.inst.cfg index e0100d37ec..ce7e344ad9 100644 --- a/resources/quality/cartesio/nylon/cartesio_0.25_nylon_normal.inst.cfg +++ b/resources/quality/cartesio/nylon/cartesio_0.25_nylon_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 2 diff --git a/resources/quality/cartesio/nylon/cartesio_0.4_nylon_high.inst.cfg b/resources/quality/cartesio/nylon/cartesio_0.4_nylon_high.inst.cfg index d4b261b99f..05e9abbe8f 100644 --- a/resources/quality/cartesio/nylon/cartesio_0.4_nylon_high.inst.cfg +++ b/resources/quality/cartesio/nylon/cartesio_0.4_nylon_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/nylon/cartesio_0.4_nylon_normal.inst.cfg b/resources/quality/cartesio/nylon/cartesio_0.4_nylon_normal.inst.cfg index 7d899ae927..87cfd33f51 100644 --- a/resources/quality/cartesio/nylon/cartesio_0.4_nylon_normal.inst.cfg +++ b/resources/quality/cartesio/nylon/cartesio_0.4_nylon_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 2 diff --git a/resources/quality/cartesio/nylon/cartesio_0.8_nylon_coarse.inst.cfg b/resources/quality/cartesio/nylon/cartesio_0.8_nylon_coarse.inst.cfg index 21e6d357b0..279e2d98bf 100644 --- a/resources/quality/cartesio/nylon/cartesio_0.8_nylon_coarse.inst.cfg +++ b/resources/quality/cartesio/nylon/cartesio_0.8_nylon_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = 3 diff --git a/resources/quality/cartesio/nylon/cartesio_0.8_nylon_extra_coarse.inst.cfg b/resources/quality/cartesio/nylon/cartesio_0.8_nylon_extra_coarse.inst.cfg index 15128584e1..69f9a8caad 100644 --- a/resources/quality/cartesio/nylon/cartesio_0.8_nylon_extra_coarse.inst.cfg +++ b/resources/quality/cartesio/nylon/cartesio_0.8_nylon_extra_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Extra Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extra coarse weight = 4 diff --git a/resources/quality/cartesio/nylon/cartesio_0.8_nylon_high.inst.cfg b/resources/quality/cartesio/nylon/cartesio_0.8_nylon_high.inst.cfg index 1d78bd0f1d..ff4d152ef2 100644 --- a/resources/quality/cartesio/nylon/cartesio_0.8_nylon_high.inst.cfg +++ b/resources/quality/cartesio/nylon/cartesio_0.8_nylon_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/nylon/cartesio_0.8_nylon_normal.inst.cfg b/resources/quality/cartesio/nylon/cartesio_0.8_nylon_normal.inst.cfg index 95be159ff2..2e74b2cc77 100644 --- a/resources/quality/cartesio/nylon/cartesio_0.8_nylon_normal.inst.cfg +++ b/resources/quality/cartesio/nylon/cartesio_0.8_nylon_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 2 diff --git a/resources/quality/cartesio/pc/cartesio_0.25_pc_high.inst.cfg b/resources/quality/cartesio/pc/cartesio_0.25_pc_high.inst.cfg index c45194e18b..15c7f17451 100644 --- a/resources/quality/cartesio/pc/cartesio_0.25_pc_high.inst.cfg +++ b/resources/quality/cartesio/pc/cartesio_0.25_pc_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/pc/cartesio_0.25_pc_normal.inst.cfg b/resources/quality/cartesio/pc/cartesio_0.25_pc_normal.inst.cfg index 248517d769..020c2a1111 100644 --- a/resources/quality/cartesio/pc/cartesio_0.25_pc_normal.inst.cfg +++ b/resources/quality/cartesio/pc/cartesio_0.25_pc_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 2 diff --git a/resources/quality/cartesio/pc/cartesio_0.4_pc_high.inst.cfg b/resources/quality/cartesio/pc/cartesio_0.4_pc_high.inst.cfg index 8c46693c91..73e1ff6bc5 100644 --- a/resources/quality/cartesio/pc/cartesio_0.4_pc_high.inst.cfg +++ b/resources/quality/cartesio/pc/cartesio_0.4_pc_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/pc/cartesio_0.4_pc_normal.inst.cfg b/resources/quality/cartesio/pc/cartesio_0.4_pc_normal.inst.cfg index a0b71f1f0a..44a0c4874a 100644 --- a/resources/quality/cartesio/pc/cartesio_0.4_pc_normal.inst.cfg +++ b/resources/quality/cartesio/pc/cartesio_0.4_pc_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 2 diff --git a/resources/quality/cartesio/pc/cartesio_0.8_pc_coarse.inst.cfg b/resources/quality/cartesio/pc/cartesio_0.8_pc_coarse.inst.cfg index 04f01db6ba..cba868e95e 100644 --- a/resources/quality/cartesio/pc/cartesio_0.8_pc_coarse.inst.cfg +++ b/resources/quality/cartesio/pc/cartesio_0.8_pc_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = 3 diff --git a/resources/quality/cartesio/pc/cartesio_0.8_pc_extra_coarse.inst.cfg b/resources/quality/cartesio/pc/cartesio_0.8_pc_extra_coarse.inst.cfg index 53e21ec4d0..4aff24ed02 100644 --- a/resources/quality/cartesio/pc/cartesio_0.8_pc_extra_coarse.inst.cfg +++ b/resources/quality/cartesio/pc/cartesio_0.8_pc_extra_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Extra Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extra coarse weight = 4 diff --git a/resources/quality/cartesio/pc/cartesio_0.8_pc_high.inst.cfg b/resources/quality/cartesio/pc/cartesio_0.8_pc_high.inst.cfg index 0b2b9dcb26..f00e83f63e 100644 --- a/resources/quality/cartesio/pc/cartesio_0.8_pc_high.inst.cfg +++ b/resources/quality/cartesio/pc/cartesio_0.8_pc_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/pc/cartesio_0.8_pc_normal.inst.cfg b/resources/quality/cartesio/pc/cartesio_0.8_pc_normal.inst.cfg index 173ad406f3..60e1e4e3ef 100644 --- a/resources/quality/cartesio/pc/cartesio_0.8_pc_normal.inst.cfg +++ b/resources/quality/cartesio/pc/cartesio_0.8_pc_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 2 diff --git a/resources/quality/cartesio/petg/cartesio_0.25_petg_high.inst.cfg b/resources/quality/cartesio/petg/cartesio_0.25_petg_high.inst.cfg index 9dc7adfc88..3c8dcc7af6 100644 --- a/resources/quality/cartesio/petg/cartesio_0.25_petg_high.inst.cfg +++ b/resources/quality/cartesio/petg/cartesio_0.25_petg_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/petg/cartesio_0.25_petg_normal.inst.cfg b/resources/quality/cartesio/petg/cartesio_0.25_petg_normal.inst.cfg index 019af9d905..6c704fb9d5 100644 --- a/resources/quality/cartesio/petg/cartesio_0.25_petg_normal.inst.cfg +++ b/resources/quality/cartesio/petg/cartesio_0.25_petg_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 2 diff --git a/resources/quality/cartesio/petg/cartesio_0.4_petg_high.inst.cfg b/resources/quality/cartesio/petg/cartesio_0.4_petg_high.inst.cfg index 93388787e6..3788a4dd51 100644 --- a/resources/quality/cartesio/petg/cartesio_0.4_petg_high.inst.cfg +++ b/resources/quality/cartesio/petg/cartesio_0.4_petg_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/petg/cartesio_0.4_petg_normal.inst.cfg b/resources/quality/cartesio/petg/cartesio_0.4_petg_normal.inst.cfg index ed17ccce31..46df45cbb0 100644 --- a/resources/quality/cartesio/petg/cartesio_0.4_petg_normal.inst.cfg +++ b/resources/quality/cartesio/petg/cartesio_0.4_petg_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 2 diff --git a/resources/quality/cartesio/petg/cartesio_0.8_petg_coarse.inst.cfg b/resources/quality/cartesio/petg/cartesio_0.8_petg_coarse.inst.cfg index 754c55caf5..d7d6e35a3c 100644 --- a/resources/quality/cartesio/petg/cartesio_0.8_petg_coarse.inst.cfg +++ b/resources/quality/cartesio/petg/cartesio_0.8_petg_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = 3 diff --git a/resources/quality/cartesio/petg/cartesio_0.8_petg_extra_coarse.inst.cfg b/resources/quality/cartesio/petg/cartesio_0.8_petg_extra_coarse.inst.cfg index 81b1de32a2..784bd0d6af 100644 --- a/resources/quality/cartesio/petg/cartesio_0.8_petg_extra_coarse.inst.cfg +++ b/resources/quality/cartesio/petg/cartesio_0.8_petg_extra_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Extra Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extra coarse weight = 4 diff --git a/resources/quality/cartesio/petg/cartesio_0.8_petg_high.inst.cfg b/resources/quality/cartesio/petg/cartesio_0.8_petg_high.inst.cfg index 86e93c8a32..bced328a9c 100644 --- a/resources/quality/cartesio/petg/cartesio_0.8_petg_high.inst.cfg +++ b/resources/quality/cartesio/petg/cartesio_0.8_petg_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/petg/cartesio_0.8_petg_normal.inst.cfg b/resources/quality/cartesio/petg/cartesio_0.8_petg_normal.inst.cfg index 08f918f59b..4f588b7b7b 100644 --- a/resources/quality/cartesio/petg/cartesio_0.8_petg_normal.inst.cfg +++ b/resources/quality/cartesio/petg/cartesio_0.8_petg_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 2 diff --git a/resources/quality/cartesio/pla/cartesio_0.25_pla_high.inst.cfg b/resources/quality/cartesio/pla/cartesio_0.25_pla_high.inst.cfg index 4841f9f368..15456a0b78 100644 --- a/resources/quality/cartesio/pla/cartesio_0.25_pla_high.inst.cfg +++ b/resources/quality/cartesio/pla/cartesio_0.25_pla_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/pla/cartesio_0.25_pla_normal.inst.cfg b/resources/quality/cartesio/pla/cartesio_0.25_pla_normal.inst.cfg index 3ba36f9436..bfa792f133 100644 --- a/resources/quality/cartesio/pla/cartesio_0.25_pla_normal.inst.cfg +++ b/resources/quality/cartesio/pla/cartesio_0.25_pla_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/cartesio/pla/cartesio_0.4_pla_high.inst.cfg b/resources/quality/cartesio/pla/cartesio_0.4_pla_high.inst.cfg index 7c785a750a..e64b67b59b 100644 --- a/resources/quality/cartesio/pla/cartesio_0.4_pla_high.inst.cfg +++ b/resources/quality/cartesio/pla/cartesio_0.4_pla_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/pla/cartesio_0.4_pla_normal.inst.cfg b/resources/quality/cartesio/pla/cartesio_0.4_pla_normal.inst.cfg index b24394daf0..704a974b63 100644 --- a/resources/quality/cartesio/pla/cartesio_0.4_pla_normal.inst.cfg +++ b/resources/quality/cartesio/pla/cartesio_0.4_pla_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/cartesio/pla/cartesio_0.8_pla_coarse.inst.cfg b/resources/quality/cartesio/pla/cartesio_0.8_pla_coarse.inst.cfg index 6443f2d526..b8ff99c256 100644 --- a/resources/quality/cartesio/pla/cartesio_0.8_pla_coarse.inst.cfg +++ b/resources/quality/cartesio/pla/cartesio_0.8_pla_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = -3 diff --git a/resources/quality/cartesio/pla/cartesio_0.8_pla_extra_coarse.inst.cfg b/resources/quality/cartesio/pla/cartesio_0.8_pla_extra_coarse.inst.cfg index 51a93f76d9..da613e6e1a 100644 --- a/resources/quality/cartesio/pla/cartesio_0.8_pla_extra_coarse.inst.cfg +++ b/resources/quality/cartesio/pla/cartesio_0.8_pla_extra_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Extra Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extra coarse weight = -4 diff --git a/resources/quality/cartesio/pla/cartesio_0.8_pla_high.inst.cfg b/resources/quality/cartesio/pla/cartesio_0.8_pla_high.inst.cfg index 2f72d9d158..05c0f72686 100644 --- a/resources/quality/cartesio/pla/cartesio_0.8_pla_high.inst.cfg +++ b/resources/quality/cartesio/pla/cartesio_0.8_pla_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/pla/cartesio_0.8_pla_normal.inst.cfg b/resources/quality/cartesio/pla/cartesio_0.8_pla_normal.inst.cfg index 431f7c0fff..8434f4abc6 100644 --- a/resources/quality/cartesio/pla/cartesio_0.8_pla_normal.inst.cfg +++ b/resources/quality/cartesio/pla/cartesio_0.8_pla_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/cartesio/pva/cartesio_0.25_pva_high.inst.cfg b/resources/quality/cartesio/pva/cartesio_0.25_pva_high.inst.cfg index fe21c17e22..7df550917f 100644 --- a/resources/quality/cartesio/pva/cartesio_0.25_pva_high.inst.cfg +++ b/resources/quality/cartesio/pva/cartesio_0.25_pva_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/pva/cartesio_0.25_pva_normal.inst.cfg b/resources/quality/cartesio/pva/cartesio_0.25_pva_normal.inst.cfg index 6525991986..c31a97c41a 100644 --- a/resources/quality/cartesio/pva/cartesio_0.25_pva_normal.inst.cfg +++ b/resources/quality/cartesio/pva/cartesio_0.25_pva_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 2 diff --git a/resources/quality/cartesio/pva/cartesio_0.4_pva_high.inst.cfg b/resources/quality/cartesio/pva/cartesio_0.4_pva_high.inst.cfg index cdf8f8cae7..faf1633ce2 100644 --- a/resources/quality/cartesio/pva/cartesio_0.4_pva_high.inst.cfg +++ b/resources/quality/cartesio/pva/cartesio_0.4_pva_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/pva/cartesio_0.4_pva_normal.inst.cfg b/resources/quality/cartesio/pva/cartesio_0.4_pva_normal.inst.cfg index e7267735e4..e6eb02409c 100644 --- a/resources/quality/cartesio/pva/cartesio_0.4_pva_normal.inst.cfg +++ b/resources/quality/cartesio/pva/cartesio_0.4_pva_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 2 diff --git a/resources/quality/cartesio/pva/cartesio_0.8_pva_coarse.inst.cfg b/resources/quality/cartesio/pva/cartesio_0.8_pva_coarse.inst.cfg index fc35e14fc6..35b2d1690d 100644 --- a/resources/quality/cartesio/pva/cartesio_0.8_pva_coarse.inst.cfg +++ b/resources/quality/cartesio/pva/cartesio_0.8_pva_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = 3 diff --git a/resources/quality/cartesio/pva/cartesio_0.8_pva_extra_coarse.inst.cfg b/resources/quality/cartesio/pva/cartesio_0.8_pva_extra_coarse.inst.cfg index 0e1b8b1241..3d41308a5d 100644 --- a/resources/quality/cartesio/pva/cartesio_0.8_pva_extra_coarse.inst.cfg +++ b/resources/quality/cartesio/pva/cartesio_0.8_pva_extra_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Extra Coarse definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extra coarse weight = 4 diff --git a/resources/quality/cartesio/pva/cartesio_0.8_pva_high.inst.cfg b/resources/quality/cartesio/pva/cartesio_0.8_pva_high.inst.cfg index 249cf6485e..0293ee9acb 100644 --- a/resources/quality/cartesio/pva/cartesio_0.8_pva_high.inst.cfg +++ b/resources/quality/cartesio/pva/cartesio_0.8_pva_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/cartesio/pva/cartesio_0.8_pva_normal.inst.cfg b/resources/quality/cartesio/pva/cartesio_0.8_pva_normal.inst.cfg index a6c0a89fcc..247fa7b787 100644 --- a/resources/quality/cartesio/pva/cartesio_0.8_pva_normal.inst.cfg +++ b/resources/quality/cartesio/pva/cartesio_0.8_pva_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = cartesio [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 2 diff --git a/resources/quality/coarse.inst.cfg b/resources/quality/coarse.inst.cfg index e9b8156a70..77b9004aa6 100644 --- a/resources/quality/coarse.inst.cfg +++ b/resources/quality/coarse.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = fdmprinter [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = -3 diff --git a/resources/quality/dagoma/dagoma_discoeasy200_pla_fast.inst.cfg b/resources/quality/dagoma/dagoma_discoeasy200_pla_fast.inst.cfg index a302f5b513..b12a92dc1c 100644 --- a/resources/quality/dagoma/dagoma_discoeasy200_pla_fast.inst.cfg +++ b/resources/quality/dagoma/dagoma_discoeasy200_pla_fast.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = dagoma_discoeasy200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/dagoma/dagoma_discoeasy200_pla_fine.inst.cfg b/resources/quality/dagoma/dagoma_discoeasy200_pla_fine.inst.cfg index b26eb1d910..055b3e30d6 100644 --- a/resources/quality/dagoma/dagoma_discoeasy200_pla_fine.inst.cfg +++ b/resources/quality/dagoma/dagoma_discoeasy200_pla_fine.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = dagoma_discoeasy200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/dagoma/dagoma_discoeasy200_pla_standard.inst.cfg b/resources/quality/dagoma/dagoma_discoeasy200_pla_standard.inst.cfg index 9ec56f696a..7c244b3f18 100644 --- a/resources/quality/dagoma/dagoma_discoeasy200_pla_standard.inst.cfg +++ b/resources/quality/dagoma/dagoma_discoeasy200_pla_standard.inst.cfg @@ -4,7 +4,7 @@ name = Standard definition = dagoma_discoeasy200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/dagoma/dagoma_global_fast.inst.cfg b/resources/quality/dagoma/dagoma_global_fast.inst.cfg index 28569387f2..f0d312e0a7 100644 --- a/resources/quality/dagoma/dagoma_global_fast.inst.cfg +++ b/resources/quality/dagoma/dagoma_global_fast.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = dagoma_discoeasy200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/dagoma/dagoma_global_fine.inst.cfg b/resources/quality/dagoma/dagoma_global_fine.inst.cfg index 1f7d577c1b..bf0a173ec3 100644 --- a/resources/quality/dagoma/dagoma_global_fine.inst.cfg +++ b/resources/quality/dagoma/dagoma_global_fine.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = dagoma_discoeasy200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/dagoma/dagoma_global_standard.inst.cfg b/resources/quality/dagoma/dagoma_global_standard.inst.cfg index 167062c1d7..afeb925e96 100644 --- a/resources/quality/dagoma/dagoma_global_standard.inst.cfg +++ b/resources/quality/dagoma/dagoma_global_standard.inst.cfg @@ -4,7 +4,7 @@ name = Standard definition = dagoma_discoeasy200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/dagoma/dagoma_magis_pla_fast.inst.cfg b/resources/quality/dagoma/dagoma_magis_pla_fast.inst.cfg index d87c913eb6..8387ff2401 100644 --- a/resources/quality/dagoma/dagoma_magis_pla_fast.inst.cfg +++ b/resources/quality/dagoma/dagoma_magis_pla_fast.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = dagoma_magis [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/dagoma/dagoma_magis_pla_fine.inst.cfg b/resources/quality/dagoma/dagoma_magis_pla_fine.inst.cfg index d046726e0e..60c0cae7ec 100644 --- a/resources/quality/dagoma/dagoma_magis_pla_fine.inst.cfg +++ b/resources/quality/dagoma/dagoma_magis_pla_fine.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = dagoma_magis [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/dagoma/dagoma_magis_pla_standard.inst.cfg b/resources/quality/dagoma/dagoma_magis_pla_standard.inst.cfg index 39961ea93b..b406d43cb1 100644 --- a/resources/quality/dagoma/dagoma_magis_pla_standard.inst.cfg +++ b/resources/quality/dagoma/dagoma_magis_pla_standard.inst.cfg @@ -4,7 +4,7 @@ name = Standard definition = dagoma_magis [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/dagoma/dagoma_neva_pla_fast.inst.cfg b/resources/quality/dagoma/dagoma_neva_pla_fast.inst.cfg index efdf2f7d32..c0e8d7fe94 100644 --- a/resources/quality/dagoma/dagoma_neva_pla_fast.inst.cfg +++ b/resources/quality/dagoma/dagoma_neva_pla_fast.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = dagoma_neva [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/dagoma/dagoma_neva_pla_fine.inst.cfg b/resources/quality/dagoma/dagoma_neva_pla_fine.inst.cfg index 50915db112..b6d6966c81 100644 --- a/resources/quality/dagoma/dagoma_neva_pla_fine.inst.cfg +++ b/resources/quality/dagoma/dagoma_neva_pla_fine.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = dagoma_neva [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/dagoma/dagoma_neva_pla_standard.inst.cfg b/resources/quality/dagoma/dagoma_neva_pla_standard.inst.cfg index ed67800eac..eff587c908 100644 --- a/resources/quality/dagoma/dagoma_neva_pla_standard.inst.cfg +++ b/resources/quality/dagoma/dagoma_neva_pla_standard.inst.cfg @@ -4,7 +4,7 @@ name = Standard definition = dagoma_neva [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/deltacomb/deltacomb_abs_Draft_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_abs_Draft_Quality.inst.cfg index f540400575..9316a84175 100644 --- a/resources/quality/deltacomb/deltacomb_abs_Draft_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_abs_Draft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fast (beta) definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/deltacomb/deltacomb_abs_Fast_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_abs_Fast_Quality.inst.cfg index 2214813913..a2c67ff890 100644 --- a/resources/quality/deltacomb/deltacomb_abs_Fast_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_abs_Fast_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal (beta) definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/deltacomb/deltacomb_abs_High_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_abs_High_Quality.inst.cfg index c196209553..6322c311d0 100644 --- a/resources/quality/deltacomb/deltacomb_abs_High_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_abs_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine (beta) definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 0 diff --git a/resources/quality/deltacomb/deltacomb_abs_Normal_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_abs_Normal_Quality.inst.cfg index 332e1890c6..1f4184f3ad 100644 --- a/resources/quality/deltacomb/deltacomb_abs_Normal_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_abs_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine (beta) definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/deltacomb/deltacomb_abs_Verydraft_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_abs_Verydraft_Quality.inst.cfg index 674174c0bd..37925e0e4c 100644 --- a/resources/quality/deltacomb/deltacomb_abs_Verydraft_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_abs_Verydraft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast (beta) definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/deltacomb/deltacomb_global_Draft_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_global_Draft_Quality.inst.cfg index f8887810d5..072f0435f6 100755 --- a/resources/quality/deltacomb/deltacomb_global_Draft_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_global_Draft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/deltacomb/deltacomb_global_Fast_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_global_Fast_Quality.inst.cfg index 99030a084b..eef96e35b8 100755 --- a/resources/quality/deltacomb/deltacomb_global_Fast_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_global_Fast_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/deltacomb/deltacomb_global_High_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_global_High_Quality.inst.cfg index d6d853466a..bb120877aa 100755 --- a/resources/quality/deltacomb/deltacomb_global_High_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_global_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 0 diff --git a/resources/quality/deltacomb/deltacomb_global_Normal_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_global_Normal_Quality.inst.cfg index a3bafadeec..4c95c74cfa 100755 --- a/resources/quality/deltacomb/deltacomb_global_Normal_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_global_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/deltacomb/deltacomb_global_Verydraft_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_global_Verydraft_Quality.inst.cfg index 84c6e66f61..0ba6ca588f 100755 --- a/resources/quality/deltacomb/deltacomb_global_Verydraft_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_global_Verydraft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/deltacomb/deltacomb_pla_Draft_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_pla_Draft_Quality.inst.cfg index c4f884486e..e0e695570d 100644 --- a/resources/quality/deltacomb/deltacomb_pla_Draft_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_pla_Draft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/deltacomb/deltacomb_pla_Fast_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_pla_Fast_Quality.inst.cfg index 714d4e3517..3f43871a8f 100644 --- a/resources/quality/deltacomb/deltacomb_pla_Fast_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_pla_Fast_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/deltacomb/deltacomb_pla_High_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_pla_High_Quality.inst.cfg index 774b8969c0..ba435c8bde 100644 --- a/resources/quality/deltacomb/deltacomb_pla_High_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_pla_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 0 diff --git a/resources/quality/deltacomb/deltacomb_pla_Normal_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_pla_Normal_Quality.inst.cfg index 58470ed650..98ec4f46cd 100644 --- a/resources/quality/deltacomb/deltacomb_pla_Normal_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_pla_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/deltacomb/deltacomb_pla_Verydraft_Quality.inst.cfg b/resources/quality/deltacomb/deltacomb_pla_Verydraft_Quality.inst.cfg index 9c00877c24..f67a2d8a26 100644 --- a/resources/quality/deltacomb/deltacomb_pla_Verydraft_Quality.inst.cfg +++ b/resources/quality/deltacomb/deltacomb_pla_Verydraft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = deltacomb [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/draft.inst.cfg b/resources/quality/draft.inst.cfg index ac1d9ec52f..a605b9c83c 100644 --- a/resources/quality/draft.inst.cfg +++ b/resources/quality/draft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = fdmprinter [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/extra_coarse.inst.cfg b/resources/quality/extra_coarse.inst.cfg index 2a1a203d22..bf22b7ac3c 100644 --- a/resources/quality/extra_coarse.inst.cfg +++ b/resources/quality/extra_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Extra Coarse definition = fdmprinter [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extra coarse weight = -4 diff --git a/resources/quality/extra_fast.inst.cfg b/resources/quality/extra_fast.inst.cfg index da890f1653..eb1e7b3a42 100644 --- a/resources/quality/extra_fast.inst.cfg +++ b/resources/quality/extra_fast.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = fdmprinter [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/fabtotum/fabtotum_abs_fast.inst.cfg b/resources/quality/fabtotum/fabtotum_abs_fast.inst.cfg index 95e8b93b36..036d49a94f 100644 --- a/resources/quality/fabtotum/fabtotum_abs_fast.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_abs_fast.inst.cfg @@ -4,7 +4,7 @@ definition = fabtotum name = Fast Quality [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/fabtotum/fabtotum_abs_high.inst.cfg b/resources/quality/fabtotum/fabtotum_abs_high.inst.cfg index baedf0ed2b..f3d6f33952 100644 --- a/resources/quality/fabtotum/fabtotum_abs_high.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_abs_high.inst.cfg @@ -4,7 +4,7 @@ definition = fabtotum name = High Quality [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/fabtotum/fabtotum_abs_normal.inst.cfg b/resources/quality/fabtotum/fabtotum_abs_normal.inst.cfg index 58933486ee..d01599a392 100644 --- a/resources/quality/fabtotum/fabtotum_abs_normal.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_abs_normal.inst.cfg @@ -4,7 +4,7 @@ definition = fabtotum name = Normal Quality [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/fabtotum/fabtotum_nylon_fast.inst.cfg b/resources/quality/fabtotum/fabtotum_nylon_fast.inst.cfg index 00f0737227..1f0c050126 100644 --- a/resources/quality/fabtotum/fabtotum_nylon_fast.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_nylon_fast.inst.cfg @@ -4,7 +4,7 @@ name = Fast Quality definition = fabtotum [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/fabtotum/fabtotum_nylon_high.inst.cfg b/resources/quality/fabtotum/fabtotum_nylon_high.inst.cfg index bd7f32c9ba..4e234f62f8 100644 --- a/resources/quality/fabtotum/fabtotum_nylon_high.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_nylon_high.inst.cfg @@ -4,7 +4,7 @@ name = High Quality definition = fabtotum [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/fabtotum/fabtotum_nylon_normal.inst.cfg b/resources/quality/fabtotum/fabtotum_nylon_normal.inst.cfg index 6a450e7ffe..67998dea38 100644 --- a/resources/quality/fabtotum/fabtotum_nylon_normal.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_nylon_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal Quality definition = fabtotum [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/fabtotum/fabtotum_pla_fast.inst.cfg b/resources/quality/fabtotum/fabtotum_pla_fast.inst.cfg index afac0b0884..e27477e8de 100644 --- a/resources/quality/fabtotum/fabtotum_pla_fast.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_pla_fast.inst.cfg @@ -4,7 +4,7 @@ definition = fabtotum name = Fast Quality [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/fabtotum/fabtotum_pla_high.inst.cfg b/resources/quality/fabtotum/fabtotum_pla_high.inst.cfg index 89dc6d9b33..bab6d5fc82 100644 --- a/resources/quality/fabtotum/fabtotum_pla_high.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_pla_high.inst.cfg @@ -4,7 +4,7 @@ definition = fabtotum name = High Quality [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/fabtotum/fabtotum_pla_normal.inst.cfg b/resources/quality/fabtotum/fabtotum_pla_normal.inst.cfg index e5496a13d4..7bd3bc024e 100644 --- a/resources/quality/fabtotum/fabtotum_pla_normal.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_pla_normal.inst.cfg @@ -4,7 +4,7 @@ definition = fabtotum name = Normal Quality [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/fabtotum/fabtotum_tpu_fast.inst.cfg b/resources/quality/fabtotum/fabtotum_tpu_fast.inst.cfg index 7917c92514..5668d4a5b9 100644 --- a/resources/quality/fabtotum/fabtotum_tpu_fast.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_tpu_fast.inst.cfg @@ -5,7 +5,7 @@ name = Fast Quality [metadata] type = quality -setting_version = 5 +setting_version = 6 material = generic_tpu quality_type = fast weight = -1 diff --git a/resources/quality/fabtotum/fabtotum_tpu_high.inst.cfg b/resources/quality/fabtotum/fabtotum_tpu_high.inst.cfg index 1c31967d79..6af9e88f00 100644 --- a/resources/quality/fabtotum/fabtotum_tpu_high.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_tpu_high.inst.cfg @@ -5,7 +5,7 @@ name = High Quality [metadata] type = quality -setting_version = 5 +setting_version = 6 material = generic_tpu quality_type = high weight = 1 diff --git a/resources/quality/fabtotum/fabtotum_tpu_normal.inst.cfg b/resources/quality/fabtotum/fabtotum_tpu_normal.inst.cfg index 0a3821f953..13faf5bc2f 100644 --- a/resources/quality/fabtotum/fabtotum_tpu_normal.inst.cfg +++ b/resources/quality/fabtotum/fabtotum_tpu_normal.inst.cfg @@ -5,7 +5,7 @@ name = Normal Quality [metadata] type = quality -setting_version = 5 +setting_version = 6 material = generic_tpu quality_type = normal weight = 0 diff --git a/resources/quality/fast.inst.cfg b/resources/quality/fast.inst.cfg index 7568c42e8f..94a7961320 100644 --- a/resources/quality/fast.inst.cfg +++ b/resources/quality/fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = fdmprinter [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/gmax15plus/gmax15plus_pla_dual_normal.inst.cfg b/resources/quality/gmax15plus/gmax15plus_pla_dual_normal.inst.cfg index 7fd2ab2296..b04d933d5d 100644 --- a/resources/quality/gmax15plus/gmax15plus_pla_dual_normal.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_pla_dual_normal.inst.cfg @@ -4,7 +4,7 @@ name = gMax 1.5+ Dual Normal Layers definition = gmax15plus_dual [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = -1 diff --git a/resources/quality/gmax15plus/gmax15plus_pla_dual_thick.inst.cfg b/resources/quality/gmax15plus/gmax15plus_pla_dual_thick.inst.cfg index 30a99ef243..c1282c4a80 100644 --- a/resources/quality/gmax15plus/gmax15plus_pla_dual_thick.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_pla_dual_thick.inst.cfg @@ -4,7 +4,7 @@ name = gMax 1.5+ Dual Thick Layers definition = gmax15plus_dual [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = course weight = -2 diff --git a/resources/quality/gmax15plus/gmax15plus_pla_dual_thin.inst.cfg b/resources/quality/gmax15plus/gmax15plus_pla_dual_thin.inst.cfg index decafac241..eaf86ee152 100644 --- a/resources/quality/gmax15plus/gmax15plus_pla_dual_thin.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_pla_dual_thin.inst.cfg @@ -4,7 +4,7 @@ name = gMax 1.5+ Dual Thin Layers definition = gmax15plus_dual [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 0 diff --git a/resources/quality/gmax15plus/gmax15plus_pla_dual_very_thick.inst.cfg b/resources/quality/gmax15plus/gmax15plus_pla_dual_very_thick.inst.cfg index a74bdfdd78..95e93f7932 100644 --- a/resources/quality/gmax15plus/gmax15plus_pla_dual_very_thick.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_pla_dual_very_thick.inst.cfg @@ -4,7 +4,7 @@ name = gMax 1.5+ Dual Very Thick Layers definition = gmax15plus_dual [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extra_course weight = -3 diff --git a/resources/quality/gmax15plus/gmax15plus_pla_normal.inst.cfg b/resources/quality/gmax15plus/gmax15plus_pla_normal.inst.cfg index ddf5a4c491..400966c645 100644 --- a/resources/quality/gmax15plus/gmax15plus_pla_normal.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_pla_normal.inst.cfg @@ -4,7 +4,7 @@ name = gMax 1.5+ Normal Layers definition = gmax15plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = -1 diff --git a/resources/quality/gmax15plus/gmax15plus_pla_thick.inst.cfg b/resources/quality/gmax15plus/gmax15plus_pla_thick.inst.cfg index e6cb2b5854..2dbfd27dd5 100644 --- a/resources/quality/gmax15plus/gmax15plus_pla_thick.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_pla_thick.inst.cfg @@ -4,7 +4,7 @@ name = gMax 1.5+ Thick Layers definition = gmax15plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = course weight = -2 diff --git a/resources/quality/gmax15plus/gmax15plus_pla_thin.inst.cfg b/resources/quality/gmax15plus/gmax15plus_pla_thin.inst.cfg index d119539d32..516054a378 100644 --- a/resources/quality/gmax15plus/gmax15plus_pla_thin.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_pla_thin.inst.cfg @@ -4,7 +4,7 @@ name = gMax 1.5+ Thin Layers definition = gmax15plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 0 diff --git a/resources/quality/gmax15plus/gmax15plus_pla_very_thick.inst.cfg b/resources/quality/gmax15plus/gmax15plus_pla_very_thick.inst.cfg index 884029a4ae..1b260e4388 100644 --- a/resources/quality/gmax15plus/gmax15plus_pla_very_thick.inst.cfg +++ b/resources/quality/gmax15plus/gmax15plus_pla_very_thick.inst.cfg @@ -4,7 +4,7 @@ name = gMax 1.5+ Very Thick Layers definition = gmax15plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extra_course weight = -3 diff --git a/resources/quality/high.inst.cfg b/resources/quality/high.inst.cfg index 1b687bf5e4..2ef1aba9e1 100644 --- a/resources/quality/high.inst.cfg +++ b/resources/quality/high.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = fdmprinter [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/imade3d_jellybox/generic_petg_0.4_coarse.inst.cfg b/resources/quality/imade3d_jellybox/generic_petg_0.4_coarse.inst.cfg index 6a50e24678..e299e675e0 100644 --- a/resources/quality/imade3d_jellybox/generic_petg_0.4_coarse.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_petg_0.4_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/imade3d_jellybox/generic_petg_0.4_coarse_2-fans.inst.cfg b/resources/quality/imade3d_jellybox/generic_petg_0.4_coarse_2-fans.inst.cfg index 2c8a43874a..7123c6440a 100644 --- a/resources/quality/imade3d_jellybox/generic_petg_0.4_coarse_2-fans.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_petg_0.4_coarse_2-fans.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/imade3d_jellybox/generic_petg_0.4_medium.inst.cfg b/resources/quality/imade3d_jellybox/generic_petg_0.4_medium.inst.cfg index 264c95c933..297531f989 100644 --- a/resources/quality/imade3d_jellybox/generic_petg_0.4_medium.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_petg_0.4_medium.inst.cfg @@ -4,7 +4,7 @@ name = Medium definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/imade3d_jellybox/generic_petg_0.4_medium_2-fans.inst.cfg b/resources/quality/imade3d_jellybox/generic_petg_0.4_medium_2-fans.inst.cfg index dfdfc2458c..d15c4a3b2d 100644 --- a/resources/quality/imade3d_jellybox/generic_petg_0.4_medium_2-fans.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_petg_0.4_medium_2-fans.inst.cfg @@ -4,7 +4,7 @@ name = Medium definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/imade3d_jellybox/generic_pla_0.4_coarse.inst.cfg b/resources/quality/imade3d_jellybox/generic_pla_0.4_coarse.inst.cfg index 68eec0c753..9ff4e6d49e 100644 --- a/resources/quality/imade3d_jellybox/generic_pla_0.4_coarse.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_pla_0.4_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/imade3d_jellybox/generic_pla_0.4_coarse_2-fans.inst.cfg b/resources/quality/imade3d_jellybox/generic_pla_0.4_coarse_2-fans.inst.cfg index 46f81d0c1a..88635bb56d 100644 --- a/resources/quality/imade3d_jellybox/generic_pla_0.4_coarse_2-fans.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_pla_0.4_coarse_2-fans.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/imade3d_jellybox/generic_pla_0.4_fine.inst.cfg b/resources/quality/imade3d_jellybox/generic_pla_0.4_fine.inst.cfg index f614b7cc3c..89dcca901c 100644 --- a/resources/quality/imade3d_jellybox/generic_pla_0.4_fine.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_pla_0.4_fine.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/imade3d_jellybox/generic_pla_0.4_fine_2-fans.inst.cfg b/resources/quality/imade3d_jellybox/generic_pla_0.4_fine_2-fans.inst.cfg index 5525899ea7..c7fd702eeb 100644 --- a/resources/quality/imade3d_jellybox/generic_pla_0.4_fine_2-fans.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_pla_0.4_fine_2-fans.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/imade3d_jellybox/generic_pla_0.4_medium.inst.cfg b/resources/quality/imade3d_jellybox/generic_pla_0.4_medium.inst.cfg index 28b4307cea..7f030b53e2 100644 --- a/resources/quality/imade3d_jellybox/generic_pla_0.4_medium.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_pla_0.4_medium.inst.cfg @@ -4,7 +4,7 @@ name = Medium definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/imade3d_jellybox/generic_pla_0.4_medium_2-fans.inst.cfg b/resources/quality/imade3d_jellybox/generic_pla_0.4_medium_2-fans.inst.cfg index 52a8594505..2759a24a8d 100644 --- a/resources/quality/imade3d_jellybox/generic_pla_0.4_medium_2-fans.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_pla_0.4_medium_2-fans.inst.cfg @@ -4,7 +4,7 @@ name = Medium definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/imade3d_jellybox/generic_pla_0.4_ultrafine.inst.cfg b/resources/quality/imade3d_jellybox/generic_pla_0.4_ultrafine.inst.cfg index bf3d409050..b74b9434b7 100644 --- a/resources/quality/imade3d_jellybox/generic_pla_0.4_ultrafine.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_pla_0.4_ultrafine.inst.cfg @@ -4,7 +4,7 @@ name = UltraFine definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = ultrahigh weight = 2 diff --git a/resources/quality/imade3d_jellybox/generic_pla_0.4_ultrafine_2-fans.inst.cfg b/resources/quality/imade3d_jellybox/generic_pla_0.4_ultrafine_2-fans.inst.cfg index 676ea84825..21df5445ab 100644 --- a/resources/quality/imade3d_jellybox/generic_pla_0.4_ultrafine_2-fans.inst.cfg +++ b/resources/quality/imade3d_jellybox/generic_pla_0.4_ultrafine_2-fans.inst.cfg @@ -4,7 +4,7 @@ name = UltraFine definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = ultrahigh weight = 2 diff --git a/resources/quality/imade3d_jellybox/imade3d_jellybox_coarse.inst.cfg b/resources/quality/imade3d_jellybox/imade3d_jellybox_coarse.inst.cfg index 819501c727..1c6ef69145 100644 --- a/resources/quality/imade3d_jellybox/imade3d_jellybox_coarse.inst.cfg +++ b/resources/quality/imade3d_jellybox/imade3d_jellybox_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/imade3d_jellybox/imade3d_jellybox_fine.inst.cfg b/resources/quality/imade3d_jellybox/imade3d_jellybox_fine.inst.cfg index 0f312aa62d..702c5182c9 100644 --- a/resources/quality/imade3d_jellybox/imade3d_jellybox_fine.inst.cfg +++ b/resources/quality/imade3d_jellybox/imade3d_jellybox_fine.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/imade3d_jellybox/imade3d_jellybox_normal.inst.cfg b/resources/quality/imade3d_jellybox/imade3d_jellybox_normal.inst.cfg index 25d0e65d74..87256a9af7 100644 --- a/resources/quality/imade3d_jellybox/imade3d_jellybox_normal.inst.cfg +++ b/resources/quality/imade3d_jellybox/imade3d_jellybox_normal.inst.cfg @@ -4,7 +4,7 @@ name = Medium definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/imade3d_jellybox/imade3d_jellybox_ultrafine.inst.cfg b/resources/quality/imade3d_jellybox/imade3d_jellybox_ultrafine.inst.cfg index ebe5ab620c..ffa7ed4550 100644 --- a/resources/quality/imade3d_jellybox/imade3d_jellybox_ultrafine.inst.cfg +++ b/resources/quality/imade3d_jellybox/imade3d_jellybox_ultrafine.inst.cfg @@ -4,7 +4,7 @@ name = UltraFine definition = imade3d_jellybox [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = ultrahigh weight = 2 diff --git a/resources/quality/kemiq_q2/kemiq_q2_beta_abs_draft.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_beta_abs_draft.inst.cfg index edba5e79ce..e4b6411ef6 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_beta_abs_draft.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_beta_abs_draft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = kemiq_q2_beta [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = -3 diff --git a/resources/quality/kemiq_q2/kemiq_q2_beta_abs_extra_fine.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_beta_abs_extra_fine.inst.cfg index 899af00b78..faeaee854d 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_beta_abs_extra_fine.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_beta_abs_extra_fine.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = kemiq_q2_beta [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/kemiq_q2/kemiq_q2_beta_abs_fine.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_beta_abs_fine.inst.cfg index 9b7ae123df..1ee9821018 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_beta_abs_fine.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_beta_abs_fine.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = kemiq_q2_beta [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/kemiq_q2/kemiq_q2_beta_abs_low.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_beta_abs_low.inst.cfg index 96888821e3..c47239ee4a 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_beta_abs_low.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_beta_abs_low.inst.cfg @@ -4,7 +4,7 @@ name = Low definition = kemiq_q2_beta [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/kemiq_q2/kemiq_q2_beta_abs_normal.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_beta_abs_normal.inst.cfg index c8f27f6a8f..8eac1515ca 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_beta_abs_normal.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_beta_abs_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = kemiq_q2_beta [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/kemiq_q2/kemiq_q2_beta_pla_draft.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_beta_pla_draft.inst.cfg index a121d921cc..e51e3f6cd8 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_beta_pla_draft.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_beta_pla_draft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = kemiq_q2_beta [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = -3 diff --git a/resources/quality/kemiq_q2/kemiq_q2_beta_pla_extra_fine.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_beta_pla_extra_fine.inst.cfg index 211da57f0c..f3d9aa9409 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_beta_pla_extra_fine.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_beta_pla_extra_fine.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = kemiq_q2_beta [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/kemiq_q2/kemiq_q2_beta_pla_fine.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_beta_pla_fine.inst.cfg index 5879a76012..92d1a98e2b 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_beta_pla_fine.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_beta_pla_fine.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = kemiq_q2_beta [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/kemiq_q2/kemiq_q2_beta_pla_low.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_beta_pla_low.inst.cfg index 0b437327d8..3fa48b32a6 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_beta_pla_low.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_beta_pla_low.inst.cfg @@ -4,7 +4,7 @@ name = Low definition = kemiq_q2_beta [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/kemiq_q2/kemiq_q2_beta_pla_normal.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_beta_pla_normal.inst.cfg index ca5ac5bd0e..277ddc7ec5 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_beta_pla_normal.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_beta_pla_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = kemiq_q2_beta [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/kemiq_q2/kemiq_q2_gama_pla_draft.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_gama_pla_draft.inst.cfg index cd6ee5356e..e73ff9b982 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_gama_pla_draft.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_gama_pla_draft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = kemiq_q2_gama [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = -3 diff --git a/resources/quality/kemiq_q2/kemiq_q2_gama_pla_extra_fine.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_gama_pla_extra_fine.inst.cfg index 345fdc2f0d..0175eb8ffa 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_gama_pla_extra_fine.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_gama_pla_extra_fine.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = kemiq_q2_gama [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/kemiq_q2/kemiq_q2_gama_pla_fine.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_gama_pla_fine.inst.cfg index bafc8f0016..415fbef684 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_gama_pla_fine.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_gama_pla_fine.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = kemiq_q2_gama [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/kemiq_q2/kemiq_q2_gama_pla_low.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_gama_pla_low.inst.cfg index c8aa72def2..94aa963911 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_gama_pla_low.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_gama_pla_low.inst.cfg @@ -4,7 +4,7 @@ name = Low definition = kemiq_q2_gama [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/kemiq_q2/kemiq_q2_gama_pla_normal.inst.cfg b/resources/quality/kemiq_q2/kemiq_q2_gama_pla_normal.inst.cfg index 62c43104e1..cc69216ecd 100644 --- a/resources/quality/kemiq_q2/kemiq_q2_gama_pla_normal.inst.cfg +++ b/resources/quality/kemiq_q2/kemiq_q2_gama_pla_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = kemiq_q2_gama [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/malyan_m200/abs/malyan_m200_abs_draft.inst.cfg b/resources/quality/malyan_m200/abs/malyan_m200_abs_draft.inst.cfg index 5bb967b78a..d54f0071cd 100644 --- a/resources/quality/malyan_m200/abs/malyan_m200_abs_draft.inst.cfg +++ b/resources/quality/malyan_m200/abs/malyan_m200_abs_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/malyan_m200/abs/malyan_m200_abs_fast.inst.cfg b/resources/quality/malyan_m200/abs/malyan_m200_abs_fast.inst.cfg index 30641b863a..bdd9a1ccb8 100644 --- a/resources/quality/malyan_m200/abs/malyan_m200_abs_fast.inst.cfg +++ b/resources/quality/malyan_m200/abs/malyan_m200_abs_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/malyan_m200/abs/malyan_m200_abs_high.inst.cfg b/resources/quality/malyan_m200/abs/malyan_m200_abs_high.inst.cfg index 746b579c70..7a4722d599 100644 --- a/resources/quality/malyan_m200/abs/malyan_m200_abs_high.inst.cfg +++ b/resources/quality/malyan_m200/abs/malyan_m200_abs_high.inst.cfg @@ -4,7 +4,7 @@ name = Finer definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/malyan_m200/abs/malyan_m200_abs_normal.inst.cfg b/resources/quality/malyan_m200/abs/malyan_m200_abs_normal.inst.cfg index e4ab115bc5..4cd3f34466 100644 --- a/resources/quality/malyan_m200/abs/malyan_m200_abs_normal.inst.cfg +++ b/resources/quality/malyan_m200/abs/malyan_m200_abs_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/malyan_m200/abs/malyan_m200_abs_superdraft.inst.cfg b/resources/quality/malyan_m200/abs/malyan_m200_abs_superdraft.inst.cfg index d8a7607c5c..bd349b6d72 100644 --- a/resources/quality/malyan_m200/abs/malyan_m200_abs_superdraft.inst.cfg +++ b/resources/quality/malyan_m200/abs/malyan_m200_abs_superdraft.inst.cfg @@ -4,7 +4,7 @@ name = Lowest Quality Draft definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -5 diff --git a/resources/quality/malyan_m200/abs/malyan_m200_abs_thickerdraft.inst.cfg b/resources/quality/malyan_m200/abs/malyan_m200_abs_thickerdraft.inst.cfg index 44cad5a198..a01282748b 100644 --- a/resources/quality/malyan_m200/abs/malyan_m200_abs_thickerdraft.inst.cfg +++ b/resources/quality/malyan_m200/abs/malyan_m200_abs_thickerdraft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = thickerdraft weight = -3 diff --git a/resources/quality/malyan_m200/abs/malyan_m200_abs_ultra.inst.cfg b/resources/quality/malyan_m200/abs/malyan_m200_abs_ultra.inst.cfg index 995074f330..ac82e263a7 100644 --- a/resources/quality/malyan_m200/abs/malyan_m200_abs_ultra.inst.cfg +++ b/resources/quality/malyan_m200/abs/malyan_m200_abs_ultra.inst.cfg @@ -4,7 +4,7 @@ name = Ultra Fine definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = ultra weight = 2 diff --git a/resources/quality/malyan_m200/abs/malyan_m200_abs_verydraft.inst.cfg b/resources/quality/malyan_m200/abs/malyan_m200_abs_verydraft.inst.cfg index 373908b2bd..fbdcb22262 100644 --- a/resources/quality/malyan_m200/abs/malyan_m200_abs_verydraft.inst.cfg +++ b/resources/quality/malyan_m200/abs/malyan_m200_abs_verydraft.inst.cfg @@ -4,7 +4,7 @@ name = Low Detail Draft definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -4 diff --git a/resources/quality/malyan_m200/malyan_m200_global_Draft_Quality.inst.cfg b/resources/quality/malyan_m200/malyan_m200_global_Draft_Quality.inst.cfg index e53aa86a94..aa1f30d0b3 100644 --- a/resources/quality/malyan_m200/malyan_m200_global_Draft_Quality.inst.cfg +++ b/resources/quality/malyan_m200/malyan_m200_global_Draft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/malyan_m200/malyan_m200_global_Fast_Quality.inst.cfg b/resources/quality/malyan_m200/malyan_m200_global_Fast_Quality.inst.cfg index 4379d9fdcb..8cbbdac2e4 100644 --- a/resources/quality/malyan_m200/malyan_m200_global_Fast_Quality.inst.cfg +++ b/resources/quality/malyan_m200/malyan_m200_global_Fast_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/malyan_m200/malyan_m200_global_High_Quality.inst.cfg b/resources/quality/malyan_m200/malyan_m200_global_High_Quality.inst.cfg index a9a5720e14..a56a5771d8 100644 --- a/resources/quality/malyan_m200/malyan_m200_global_High_Quality.inst.cfg +++ b/resources/quality/malyan_m200/malyan_m200_global_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Finer definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/malyan_m200/malyan_m200_global_Normal_Quality.inst.cfg b/resources/quality/malyan_m200/malyan_m200_global_Normal_Quality.inst.cfg index 96b982e86a..f24faf98f0 100644 --- a/resources/quality/malyan_m200/malyan_m200_global_Normal_Quality.inst.cfg +++ b/resources/quality/malyan_m200/malyan_m200_global_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/malyan_m200/malyan_m200_global_SuperDraft_Quality.inst.cfg b/resources/quality/malyan_m200/malyan_m200_global_SuperDraft_Quality.inst.cfg index 0550efd603..ff0d97b1cc 100644 --- a/resources/quality/malyan_m200/malyan_m200_global_SuperDraft_Quality.inst.cfg +++ b/resources/quality/malyan_m200/malyan_m200_global_SuperDraft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Lowest Quality Draft definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -5 diff --git a/resources/quality/malyan_m200/malyan_m200_global_ThickerDraft_Quality.inst.cfg b/resources/quality/malyan_m200/malyan_m200_global_ThickerDraft_Quality.inst.cfg index 9a0454fc75..623b48acff 100644 --- a/resources/quality/malyan_m200/malyan_m200_global_ThickerDraft_Quality.inst.cfg +++ b/resources/quality/malyan_m200/malyan_m200_global_ThickerDraft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = thickerdraft weight = -3 diff --git a/resources/quality/malyan_m200/malyan_m200_global_Ultra_Quality.inst.cfg b/resources/quality/malyan_m200/malyan_m200_global_Ultra_Quality.inst.cfg index 76eaa3463f..ec04f9055b 100644 --- a/resources/quality/malyan_m200/malyan_m200_global_Ultra_Quality.inst.cfg +++ b/resources/quality/malyan_m200/malyan_m200_global_Ultra_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Ultra Fine definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = ultra weight = 2 diff --git a/resources/quality/malyan_m200/malyan_m200_global_VeryDraft_Quality.inst.cfg b/resources/quality/malyan_m200/malyan_m200_global_VeryDraft_Quality.inst.cfg index 42c670920f..637b37c1ca 100644 --- a/resources/quality/malyan_m200/malyan_m200_global_VeryDraft_Quality.inst.cfg +++ b/resources/quality/malyan_m200/malyan_m200_global_VeryDraft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Low Detail Draft definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -4 diff --git a/resources/quality/malyan_m200/petg/malyan_m200_petg_draft.inst.cfg b/resources/quality/malyan_m200/petg/malyan_m200_petg_draft.inst.cfg index 96144c193f..6f36402470 100644 --- a/resources/quality/malyan_m200/petg/malyan_m200_petg_draft.inst.cfg +++ b/resources/quality/malyan_m200/petg/malyan_m200_petg_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/malyan_m200/petg/malyan_m200_petg_fast.inst.cfg b/resources/quality/malyan_m200/petg/malyan_m200_petg_fast.inst.cfg index f390034a1f..ff14d6a96e 100644 --- a/resources/quality/malyan_m200/petg/malyan_m200_petg_fast.inst.cfg +++ b/resources/quality/malyan_m200/petg/malyan_m200_petg_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/malyan_m200/petg/malyan_m200_petg_high.inst.cfg b/resources/quality/malyan_m200/petg/malyan_m200_petg_high.inst.cfg index 693c6efc08..74ab347def 100644 --- a/resources/quality/malyan_m200/petg/malyan_m200_petg_high.inst.cfg +++ b/resources/quality/malyan_m200/petg/malyan_m200_petg_high.inst.cfg @@ -4,7 +4,7 @@ name = Finer definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/malyan_m200/petg/malyan_m200_petg_normal.inst.cfg b/resources/quality/malyan_m200/petg/malyan_m200_petg_normal.inst.cfg index 09c5c2b14d..0862f96460 100644 --- a/resources/quality/malyan_m200/petg/malyan_m200_petg_normal.inst.cfg +++ b/resources/quality/malyan_m200/petg/malyan_m200_petg_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/malyan_m200/petg/malyan_m200_petg_superdraft.inst.cfg b/resources/quality/malyan_m200/petg/malyan_m200_petg_superdraft.inst.cfg index 0faec6b357..df50ebcc42 100644 --- a/resources/quality/malyan_m200/petg/malyan_m200_petg_superdraft.inst.cfg +++ b/resources/quality/malyan_m200/petg/malyan_m200_petg_superdraft.inst.cfg @@ -4,7 +4,7 @@ name = Lowest Quality Draft definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -5 diff --git a/resources/quality/malyan_m200/petg/malyan_m200_petg_thickerdraft.inst.cfg b/resources/quality/malyan_m200/petg/malyan_m200_petg_thickerdraft.inst.cfg index 988082f783..3697573e0e 100644 --- a/resources/quality/malyan_m200/petg/malyan_m200_petg_thickerdraft.inst.cfg +++ b/resources/quality/malyan_m200/petg/malyan_m200_petg_thickerdraft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = thickerdraft weight = -3 diff --git a/resources/quality/malyan_m200/petg/malyan_m200_petg_ultra.inst.cfg b/resources/quality/malyan_m200/petg/malyan_m200_petg_ultra.inst.cfg index 4eb84672e7..c9858b63c7 100644 --- a/resources/quality/malyan_m200/petg/malyan_m200_petg_ultra.inst.cfg +++ b/resources/quality/malyan_m200/petg/malyan_m200_petg_ultra.inst.cfg @@ -4,7 +4,7 @@ name = Ultra Fine definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = ultra weight = 2 diff --git a/resources/quality/malyan_m200/petg/malyan_m200_petg_verydraft.inst.cfg b/resources/quality/malyan_m200/petg/malyan_m200_petg_verydraft.inst.cfg index 2f33760afa..4bb5c5cbe4 100644 --- a/resources/quality/malyan_m200/petg/malyan_m200_petg_verydraft.inst.cfg +++ b/resources/quality/malyan_m200/petg/malyan_m200_petg_verydraft.inst.cfg @@ -4,7 +4,7 @@ name = Low Detail Draft definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -4 diff --git a/resources/quality/malyan_m200/pla/malyan_m200_pla_draft.inst.cfg b/resources/quality/malyan_m200/pla/malyan_m200_pla_draft.inst.cfg index 9a26d1b2a1..7a3af8285a 100644 --- a/resources/quality/malyan_m200/pla/malyan_m200_pla_draft.inst.cfg +++ b/resources/quality/malyan_m200/pla/malyan_m200_pla_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/malyan_m200/pla/malyan_m200_pla_fast.inst.cfg b/resources/quality/malyan_m200/pla/malyan_m200_pla_fast.inst.cfg index ef4d002ce3..f813e062dd 100644 --- a/resources/quality/malyan_m200/pla/malyan_m200_pla_fast.inst.cfg +++ b/resources/quality/malyan_m200/pla/malyan_m200_pla_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/malyan_m200/pla/malyan_m200_pla_high.inst.cfg b/resources/quality/malyan_m200/pla/malyan_m200_pla_high.inst.cfg index 6ff347a7cc..5fe854ddab 100644 --- a/resources/quality/malyan_m200/pla/malyan_m200_pla_high.inst.cfg +++ b/resources/quality/malyan_m200/pla/malyan_m200_pla_high.inst.cfg @@ -4,7 +4,7 @@ name = Finer definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/malyan_m200/pla/malyan_m200_pla_normal.inst.cfg b/resources/quality/malyan_m200/pla/malyan_m200_pla_normal.inst.cfg index b6aa938b94..cce3508550 100644 --- a/resources/quality/malyan_m200/pla/malyan_m200_pla_normal.inst.cfg +++ b/resources/quality/malyan_m200/pla/malyan_m200_pla_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/malyan_m200/pla/malyan_m200_pla_superdraft.inst.cfg b/resources/quality/malyan_m200/pla/malyan_m200_pla_superdraft.inst.cfg index 8a4b63691f..477256a933 100644 --- a/resources/quality/malyan_m200/pla/malyan_m200_pla_superdraft.inst.cfg +++ b/resources/quality/malyan_m200/pla/malyan_m200_pla_superdraft.inst.cfg @@ -4,7 +4,7 @@ name = Lowest Quality Draft definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -5 diff --git a/resources/quality/malyan_m200/pla/malyan_m200_pla_thickerdraft.inst.cfg b/resources/quality/malyan_m200/pla/malyan_m200_pla_thickerdraft.inst.cfg index 98a8363c85..1841b91d3d 100644 --- a/resources/quality/malyan_m200/pla/malyan_m200_pla_thickerdraft.inst.cfg +++ b/resources/quality/malyan_m200/pla/malyan_m200_pla_thickerdraft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = thickerdraft weight = -3 diff --git a/resources/quality/malyan_m200/pla/malyan_m200_pla_ultra.inst.cfg b/resources/quality/malyan_m200/pla/malyan_m200_pla_ultra.inst.cfg index 5a334bd9c1..24717cda19 100644 --- a/resources/quality/malyan_m200/pla/malyan_m200_pla_ultra.inst.cfg +++ b/resources/quality/malyan_m200/pla/malyan_m200_pla_ultra.inst.cfg @@ -4,7 +4,7 @@ name = Ultra Fine definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = ultra weight = 2 diff --git a/resources/quality/malyan_m200/pla/malyan_m200_pla_verydraft.inst.cfg b/resources/quality/malyan_m200/pla/malyan_m200_pla_verydraft.inst.cfg index 7d96a9f56d..8b007f5c03 100644 --- a/resources/quality/malyan_m200/pla/malyan_m200_pla_verydraft.inst.cfg +++ b/resources/quality/malyan_m200/pla/malyan_m200_pla_verydraft.inst.cfg @@ -4,7 +4,7 @@ name = Low Detail Draft definition = malyan_m200 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -4 diff --git a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_draft.inst.cfg b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_draft.inst.cfg index ef579a567e..e8959a1235 100644 --- a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_draft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_fast.inst.cfg b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_fast.inst.cfg index 33296bf677..256eea1976 100644 --- a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_fast.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_high.inst.cfg b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_high.inst.cfg index e282847f14..6d0250121f 100644 --- a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_high.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_high.inst.cfg @@ -4,7 +4,7 @@ name = Finer definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_normal.inst.cfg b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_normal.inst.cfg index 2979f574a4..88c12a38f0 100644 --- a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_normal.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_superdraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_superdraft.inst.cfg index 2d4d3c7461..2eacd312f4 100644 --- a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_superdraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_superdraft.inst.cfg @@ -4,7 +4,7 @@ name = Lowest Quality Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -5 diff --git a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_thickerdraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_thickerdraft.inst.cfg index ed43dec3ae..4b311bcfed 100644 --- a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_thickerdraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_thickerdraft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = thickerdraft weight = -3 diff --git a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_ultra.inst.cfg b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_ultra.inst.cfg index eb556fe862..e9857b76b3 100644 --- a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_ultra.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_ultra.inst.cfg @@ -4,7 +4,7 @@ name = Ultra Fine definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = thickerdraft weight = 2 diff --git a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_verydraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_verydraft.inst.cfg index 279f58d64f..7c39073c34 100644 --- a/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_verydraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/abs/monoprice_select_mini_v2_abs_verydraft.inst.cfg @@ -4,7 +4,7 @@ name = Low Detail Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -4 diff --git a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Draft_Quality.inst.cfg b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Draft_Quality.inst.cfg index 1d81d7535e..add2590cce 100644 --- a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Draft_Quality.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Draft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Fast_Quality.inst.cfg b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Fast_Quality.inst.cfg index 6a5a041244..2feddbd32a 100644 --- a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Fast_Quality.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Fast_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_High_Quality.inst.cfg b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_High_Quality.inst.cfg index 65925a1ea4..263bd8544c 100644 --- a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_High_Quality.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Finer definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Normal_Quality.inst.cfg b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Normal_Quality.inst.cfg index 0ac150c117..52e2ec79f3 100644 --- a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Normal_Quality.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_SuperDraft_Quality.inst.cfg b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_SuperDraft_Quality.inst.cfg index b02910cf12..dfbd2d4d3d 100644 --- a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_SuperDraft_Quality.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_SuperDraft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Lowest Quality Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -5 diff --git a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_ThickerDraft_Quality.inst.cfg b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_ThickerDraft_Quality.inst.cfg index 46434555ad..f05617c4f0 100644 --- a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_ThickerDraft_Quality.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_ThickerDraft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = thickerdraft weight = -3 diff --git a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Ultra_Quality.inst.cfg b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Ultra_Quality.inst.cfg index dfb4942b29..e6d835d683 100644 --- a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Ultra_Quality.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_Ultra_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Ultra Fine definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = ultra weight = 2 diff --git a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_VeryDraft_Quality.inst.cfg b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_VeryDraft_Quality.inst.cfg index 79d9c8abb9..8e1a86a732 100644 --- a/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_VeryDraft_Quality.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/monoprice_select_mini_v2_global_VeryDraft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Low Detail Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -4 diff --git a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_draft.inst.cfg b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_draft.inst.cfg index 4d7d1cd848..c7701b56d3 100644 --- a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_draft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_fast.inst.cfg b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_fast.inst.cfg index 1a18df9651..dbe3b03148 100644 --- a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_fast.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_high.inst.cfg b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_high.inst.cfg index ac0ca29caa..4612b19638 100644 --- a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_high.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_high.inst.cfg @@ -4,7 +4,7 @@ name = Finer definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_normal.inst.cfg b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_normal.inst.cfg index 32c5e33f0d..7b887afb8d 100644 --- a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_normal.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_superdraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_superdraft.inst.cfg index 26f260f861..c696c71fd5 100644 --- a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_superdraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_superdraft.inst.cfg @@ -4,7 +4,7 @@ name = Lowest Quality Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -5 diff --git a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_thickerdraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_thickerdraft.inst.cfg index b5bcc7d455..c561bcda04 100644 --- a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_thickerdraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_thickerdraft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = thickerdraft weight = -3 diff --git a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_ultra.inst.cfg b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_ultra.inst.cfg index 6fc41e34b6..e16e044708 100644 --- a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_ultra.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_ultra.inst.cfg @@ -4,7 +4,7 @@ name = Ultra Fine definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = ultra weight = 2 diff --git a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_verydraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_verydraft.inst.cfg index 8f568d3948..319bf8b39a 100644 --- a/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_verydraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/nylon/monoprice_select_mini_v2_nylon_verydraft.inst.cfg @@ -4,7 +4,7 @@ name = Low Detail Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -4 diff --git a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_draft.inst.cfg b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_draft.inst.cfg index 76b210eba1..6cc27cd3e8 100644 --- a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_draft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_fast.inst.cfg b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_fast.inst.cfg index 53a099f42d..95ef667be9 100644 --- a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_fast.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_high.inst.cfg b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_high.inst.cfg index 1a6193fbaf..c874fc5c42 100644 --- a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_high.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_high.inst.cfg @@ -4,7 +4,7 @@ name = Finer definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_normal.inst.cfg b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_normal.inst.cfg index 72e94d1828..62fb4046aa 100644 --- a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_normal.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_superdraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_superdraft.inst.cfg index 13914d07e7..e1c88805e7 100644 --- a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_superdraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_superdraft.inst.cfg @@ -4,7 +4,7 @@ name = Lowest Quality Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -5 diff --git a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_thickerdraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_thickerdraft.inst.cfg index d02a793b25..0cec8831bf 100644 --- a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_thickerdraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_thickerdraft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = thickerdraft weight = -3 diff --git a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_ultra.inst.cfg b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_ultra.inst.cfg index 41b3821727..6aaf8680f5 100644 --- a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_ultra.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_ultra.inst.cfg @@ -4,7 +4,7 @@ name = Ultra Fine definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = ultra weight = 2 diff --git a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_verydraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_verydraft.inst.cfg index 23b72a3a3b..7a3b3d6f3e 100644 --- a/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_verydraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pc/monoprice_select_mini_v2_pc_verydraft.inst.cfg @@ -4,7 +4,7 @@ name = Low Detail Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -4 diff --git a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_draft.inst.cfg b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_draft.inst.cfg index c2c8889127..4e31a46fbd 100644 --- a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_draft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_fast.inst.cfg b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_fast.inst.cfg index 881311d395..e0d7d393c5 100644 --- a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_fast.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_high.inst.cfg b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_high.inst.cfg index f9f1e169cb..64451b0788 100644 --- a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_high.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_high.inst.cfg @@ -4,7 +4,7 @@ name = Finer definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_normal.inst.cfg b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_normal.inst.cfg index 026279f012..1f5b6c956c 100644 --- a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_normal.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_superdraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_superdraft.inst.cfg index 3ff71bf416..9b269ee95b 100644 --- a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_superdraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_superdraft.inst.cfg @@ -4,7 +4,7 @@ name = Lowest Quality Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -5 diff --git a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_thickerdraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_thickerdraft.inst.cfg index 5524ec59ea..e180b50a89 100644 --- a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_thickerdraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_thickerdraft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = thickerdraft weight = -3 diff --git a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_ultra.inst.cfg b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_ultra.inst.cfg index 99d1da45c7..fe77254c56 100644 --- a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_ultra.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_ultra.inst.cfg @@ -4,7 +4,7 @@ name = Ultra Fine definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = ultra weight = 2 diff --git a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_verydraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_verydraft.inst.cfg index 99874f0571..0dd0300db3 100644 --- a/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_verydraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/petg/monoprice_select_mini_v2_petg_verydraft.inst.cfg @@ -4,7 +4,7 @@ name = Low Detail Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -4 diff --git a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_draft.inst.cfg b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_draft.inst.cfg index a64020a89e..8333cd10ac 100644 --- a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_draft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_fast.inst.cfg b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_fast.inst.cfg index 994d6ff0fd..ab83dacbe8 100644 --- a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_fast.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = 0 diff --git a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_high.inst.cfg b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_high.inst.cfg index 9f64ce57f0..9a931d6a81 100644 --- a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_high.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_high.inst.cfg @@ -4,7 +4,7 @@ name = Finer definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 0 diff --git a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_normal.inst.cfg b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_normal.inst.cfg index afbc03d5af..57fb63fdea 100644 --- a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_normal.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_superdraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_superdraft.inst.cfg index ec30cfe2b9..77c3a18b54 100644 --- a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_superdraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_superdraft.inst.cfg @@ -4,7 +4,7 @@ name = Lowest Quality Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -5 diff --git a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_thickerdraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_thickerdraft.inst.cfg index 916285385d..52b871217b 100644 --- a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_thickerdraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_thickerdraft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = thickerdraft weight = -3 diff --git a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_ultra.inst.cfg b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_ultra.inst.cfg index 5d627b6d35..36ef492905 100644 --- a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_ultra.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_ultra.inst.cfg @@ -4,7 +4,7 @@ name = Ultra Fine definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = ultra weight = 2 diff --git a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_verydraft.inst.cfg b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_verydraft.inst.cfg index 9f22fb3692..e1d636b2b1 100644 --- a/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_verydraft.inst.cfg +++ b/resources/quality/monoprice_select_mini_v2/pla/monoprice_select_mini_v2_pla_verydraft.inst.cfg @@ -4,7 +4,7 @@ name = Low Detail Draft definition = monoprice_select_mini_v2 [metadata] -setting_version = 5 +setting_version = 6 type = quality material = generic_pla weight = 0 diff --git a/resources/quality/normal.inst.cfg b/resources/quality/normal.inst.cfg index f32c87bc60..02b9ebf621 100644 --- a/resources/quality/normal.inst.cfg +++ b/resources/quality/normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = fdmprinter [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/peopoly_moai/peopoly_moai_coarse.inst.cfg b/resources/quality/peopoly_moai/peopoly_moai_coarse.inst.cfg index ac4f9ee81d..9ca6cec759 100644 --- a/resources/quality/peopoly_moai/peopoly_moai_coarse.inst.cfg +++ b/resources/quality/peopoly_moai/peopoly_moai_coarse.inst.cfg @@ -4,7 +4,7 @@ name = Coarse definition = peopoly_moai [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = 3 diff --git a/resources/quality/peopoly_moai/peopoly_moai_draft.inst.cfg b/resources/quality/peopoly_moai/peopoly_moai_draft.inst.cfg index 2d21b1f7e0..1989675688 100644 --- a/resources/quality/peopoly_moai/peopoly_moai_draft.inst.cfg +++ b/resources/quality/peopoly_moai/peopoly_moai_draft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = peopoly_moai [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = 4 diff --git a/resources/quality/peopoly_moai/peopoly_moai_extra_high.inst.cfg b/resources/quality/peopoly_moai/peopoly_moai_extra_high.inst.cfg index 796c2cff3c..30d41aa7f8 100644 --- a/resources/quality/peopoly_moai/peopoly_moai_extra_high.inst.cfg +++ b/resources/quality/peopoly_moai/peopoly_moai_extra_high.inst.cfg @@ -4,7 +4,7 @@ name = Extra High definition = peopoly_moai [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extra_high weight = 0 diff --git a/resources/quality/peopoly_moai/peopoly_moai_high.inst.cfg b/resources/quality/peopoly_moai/peopoly_moai_high.inst.cfg index b36163d9f1..37442ed20c 100644 --- a/resources/quality/peopoly_moai/peopoly_moai_high.inst.cfg +++ b/resources/quality/peopoly_moai/peopoly_moai_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = peopoly_moai [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/peopoly_moai/peopoly_moai_normal.inst.cfg b/resources/quality/peopoly_moai/peopoly_moai_normal.inst.cfg index cf67591ab2..9d80b972b4 100644 --- a/resources/quality/peopoly_moai/peopoly_moai_normal.inst.cfg +++ b/resources/quality/peopoly_moai/peopoly_moai_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = peopoly_moai [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 2 diff --git a/resources/quality/tevo_blackwidow/tevo_blackwidow_draft.inst.cfg b/resources/quality/tevo_blackwidow/tevo_blackwidow_draft.inst.cfg index be83533e0b..13f12ef643 100644 --- a/resources/quality/tevo_blackwidow/tevo_blackwidow_draft.inst.cfg +++ b/resources/quality/tevo_blackwidow/tevo_blackwidow_draft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = tevo_blackwidow [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/tevo_blackwidow/tevo_blackwidow_high.inst.cfg b/resources/quality/tevo_blackwidow/tevo_blackwidow_high.inst.cfg index 5ca8a6e4ef..0f0ccf64c6 100644 --- a/resources/quality/tevo_blackwidow/tevo_blackwidow_high.inst.cfg +++ b/resources/quality/tevo_blackwidow/tevo_blackwidow_high.inst.cfg @@ -4,7 +4,7 @@ name = High definition = tevo_blackwidow [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/tevo_blackwidow/tevo_blackwidow_normal.inst.cfg b/resources/quality/tevo_blackwidow/tevo_blackwidow_normal.inst.cfg index f542952fab..ebd5997027 100644 --- a/resources/quality/tevo_blackwidow/tevo_blackwidow_normal.inst.cfg +++ b/resources/quality/tevo_blackwidow/tevo_blackwidow_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = tevo_blackwidow [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/tizyx_k25/tizyx_k25_normal.inst.cfg b/resources/quality/tizyx_k25/tizyx_k25_normal.inst.cfg index 8b066f139f..f259ad1b34 100644 --- a/resources/quality/tizyx_k25/tizyx_k25_normal.inst.cfg +++ b/resources/quality/tizyx_k25/tizyx_k25_normal.inst.cfg @@ -5,7 +5,7 @@ definition = tizyx_k25 [metadata] quality_type = normal -setting_version = 5 +setting_version = 6 type = quality global_quality = True diff --git a/resources/quality/ultimaker2/um2_draft.inst.cfg b/resources/quality/ultimaker2/um2_draft.inst.cfg index 8c34d2c09d..7d38ba6bf4 100644 --- a/resources/quality/ultimaker2/um2_draft.inst.cfg +++ b/resources/quality/ultimaker2/um2_draft.inst.cfg @@ -4,7 +4,7 @@ name = Draft definition = ultimaker2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker2/um2_fast.inst.cfg b/resources/quality/ultimaker2/um2_fast.inst.cfg index 084ed05f92..39cd4da4ef 100644 --- a/resources/quality/ultimaker2/um2_fast.inst.cfg +++ b/resources/quality/ultimaker2/um2_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker2/um2_high.inst.cfg b/resources/quality/ultimaker2/um2_high.inst.cfg index 83bb6bb972..142440d53e 100644 --- a/resources/quality/ultimaker2/um2_high.inst.cfg +++ b/resources/quality/ultimaker2/um2_high.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker2/um2_normal.inst.cfg b/resources/quality/ultimaker2/um2_normal.inst.cfg index febee8581f..b5a87f07f3 100644 --- a/resources/quality/ultimaker2/um2_normal.inst.cfg +++ b/resources/quality/ultimaker2/um2_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/pla_0.25_normal.inst.cfg b/resources/quality/ultimaker2_plus/pla_0.25_normal.inst.cfg index ff830ae660..d27a136765 100644 --- a/resources/quality/ultimaker2_plus/pla_0.25_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/pla_0.25_normal.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker2_plus/pla_0.4_fast.inst.cfg b/resources/quality/ultimaker2_plus/pla_0.4_fast.inst.cfg index 94ea62a7ca..5bc62e5fd0 100644 --- a/resources/quality/ultimaker2_plus/pla_0.4_fast.inst.cfg +++ b/resources/quality/ultimaker2_plus/pla_0.4_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker2_plus/pla_0.4_high.inst.cfg b/resources/quality/ultimaker2_plus/pla_0.4_high.inst.cfg index 03de437ee2..2847ab8c15 100644 --- a/resources/quality/ultimaker2_plus/pla_0.4_high.inst.cfg +++ b/resources/quality/ultimaker2_plus/pla_0.4_high.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker2_plus/pla_0.4_normal.inst.cfg b/resources/quality/ultimaker2_plus/pla_0.4_normal.inst.cfg index 836d866eab..009fcf7877 100644 --- a/resources/quality/ultimaker2_plus/pla_0.4_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/pla_0.4_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/pla_0.6_normal.inst.cfg b/resources/quality/ultimaker2_plus/pla_0.6_normal.inst.cfg index de55623c0f..7c492f2ec0 100644 --- a/resources/quality/ultimaker2_plus/pla_0.6_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/pla_0.6_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/pla_0.8_normal.inst.cfg b/resources/quality/ultimaker2_plus/pla_0.8_normal.inst.cfg index c96260d52f..1b6cee0e1d 100644 --- a/resources/quality/ultimaker2_plus/pla_0.8_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/pla_0.8_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker2_plus/um2p_abs_0.25_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_abs_0.25_normal.inst.cfg index 886daf58e2..f55567f986 100644 --- a/resources/quality/ultimaker2_plus/um2p_abs_0.25_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_abs_0.25_normal.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker2_plus/um2p_abs_0.4_fast.inst.cfg b/resources/quality/ultimaker2_plus/um2p_abs_0.4_fast.inst.cfg index b727acc510..63904632dc 100644 --- a/resources/quality/ultimaker2_plus/um2p_abs_0.4_fast.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_abs_0.4_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker2_plus/um2p_abs_0.4_high.inst.cfg b/resources/quality/ultimaker2_plus/um2p_abs_0.4_high.inst.cfg index 20e217315b..f76efeb7b2 100644 --- a/resources/quality/ultimaker2_plus/um2p_abs_0.4_high.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_abs_0.4_high.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker2_plus/um2p_abs_0.4_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_abs_0.4_normal.inst.cfg index 853a87c751..fd7dfc86a7 100644 --- a/resources/quality/ultimaker2_plus/um2p_abs_0.4_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_abs_0.4_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_abs_0.6_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_abs_0.6_normal.inst.cfg index 6d3ef94b9d..aba3222412 100644 --- a/resources/quality/ultimaker2_plus/um2p_abs_0.6_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_abs_0.6_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_abs_0.8_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_abs_0.8_normal.inst.cfg index 7b39ce966a..23618e2e1d 100644 --- a/resources/quality/ultimaker2_plus/um2p_abs_0.8_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_abs_0.8_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker2_plus/um2p_cpe_0.25_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpe_0.25_normal.inst.cfg index eca5070eb9..c0434aca4f 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpe_0.25_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpe_0.25_normal.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = -1 diff --git a/resources/quality/ultimaker2_plus/um2p_cpe_0.4_fast.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpe_0.4_fast.inst.cfg index 1e181e23d1..2dc076b012 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpe_0.4_fast.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpe_0.4_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker2_plus/um2p_cpe_0.4_high.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpe_0.4_high.inst.cfg index 9ffb8a05bb..300d5242e2 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpe_0.4_high.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpe_0.4_high.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker2_plus/um2p_cpe_0.4_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpe_0.4_normal.inst.cfg index ee7101f2ec..17a9f1a579 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpe_0.4_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpe_0.4_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_cpe_0.6_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpe_0.6_normal.inst.cfg index d2de84eae6..6807a1a86a 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpe_0.6_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpe_0.6_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_cpe_0.8_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpe_0.8_normal.inst.cfg index 581dc0368d..7a15ed53ec 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpe_0.8_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpe_0.8_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker2_plus/um2p_cpep_0.4_draft.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpep_0.4_draft.inst.cfg index 7549c0081d..cc68478dd8 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpep_0.4_draft.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpep_0.4_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker2_plus/um2p_cpep_0.4_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpep_0.4_normal.inst.cfg index b1e5552562..0d0e0e2043 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpep_0.4_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpep_0.4_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_cpep_0.6_draft.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpep_0.6_draft.inst.cfg index 616f13f110..e08a7fc357 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpep_0.6_draft.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpep_0.6_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker2_plus/um2p_cpep_0.6_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpep_0.6_normal.inst.cfg index d28dc76af8..7c8589f09e 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpep_0.6_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpep_0.6_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_cpep_0.8_draft.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpep_0.8_draft.inst.cfg index 1c4fa746ad..1c42d28128 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpep_0.8_draft.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpep_0.8_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker2_plus/um2p_cpep_0.8_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_cpep_0.8_normal.inst.cfg index e40d6efc58..072081e092 100644 --- a/resources/quality/ultimaker2_plus/um2p_cpep_0.8_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_cpep_0.8_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_global_Coarse_Quality.inst.cfg b/resources/quality/ultimaker2_plus/um2p_global_Coarse_Quality.inst.cfg index a10cb4030c..2a4fe17a24 100644 --- a/resources/quality/ultimaker2_plus/um2p_global_Coarse_Quality.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_global_Coarse_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Coarse Quality definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = -4 diff --git a/resources/quality/ultimaker2_plus/um2p_global_Draft_Quality.inst.cfg b/resources/quality/ultimaker2_plus/um2p_global_Draft_Quality.inst.cfg index 5645bbee0b..bdf79827b2 100644 --- a/resources/quality/ultimaker2_plus/um2p_global_Draft_Quality.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_global_Draft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Draft Quality definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker2_plus/um2p_global_Extra_Coarse_Quality.inst.cfg b/resources/quality/ultimaker2_plus/um2p_global_Extra_Coarse_Quality.inst.cfg index d9afc804ba..e5338c0e34 100644 --- a/resources/quality/ultimaker2_plus/um2p_global_Extra_Coarse_Quality.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_global_Extra_Coarse_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Coarse Quality definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extra coarse weight = -3 diff --git a/resources/quality/ultimaker2_plus/um2p_global_Fast_Quality.inst.cfg b/resources/quality/ultimaker2_plus/um2p_global_Fast_Quality.inst.cfg index 7fd6d54c87..453def0668 100644 --- a/resources/quality/ultimaker2_plus/um2p_global_Fast_Quality.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_global_Fast_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker2_plus/um2p_global_High_Quality.inst.cfg b/resources/quality/ultimaker2_plus/um2p_global_High_Quality.inst.cfg index ade183d14a..f7ac2a62b6 100644 --- a/resources/quality/ultimaker2_plus/um2p_global_High_Quality.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_global_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_global_Normal_Quality.inst.cfg b/resources/quality/ultimaker2_plus/um2p_global_Normal_Quality.inst.cfg index 76c7b8163c..c901687f22 100644 --- a/resources/quality/ultimaker2_plus/um2p_global_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_global_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_nylon_0.25_high.inst.cfg b/resources/quality/ultimaker2_plus/um2p_nylon_0.25_high.inst.cfg index 2cab693c74..512f9e499b 100644 --- a/resources/quality/ultimaker2_plus/um2p_nylon_0.25_high.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_nylon_0.25_high.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker2_plus/um2p_nylon_0.25_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_nylon_0.25_normal.inst.cfg index f61a29a35a..ce607f7a83 100644 --- a/resources/quality/ultimaker2_plus/um2p_nylon_0.25_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_nylon_0.25_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_nylon_0.4_fast.inst.cfg b/resources/quality/ultimaker2_plus/um2p_nylon_0.4_fast.inst.cfg index 341dc7422f..7b06d5205c 100644 --- a/resources/quality/ultimaker2_plus/um2p_nylon_0.4_fast.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_nylon_0.4_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker2_plus/um2p_nylon_0.4_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_nylon_0.4_normal.inst.cfg index 63bc156e15..b65f694e11 100644 --- a/resources/quality/ultimaker2_plus/um2p_nylon_0.4_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_nylon_0.4_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_nylon_0.6_fast.inst.cfg b/resources/quality/ultimaker2_plus/um2p_nylon_0.6_fast.inst.cfg index 8aea23fb50..6caa1d2e82 100644 --- a/resources/quality/ultimaker2_plus/um2p_nylon_0.6_fast.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_nylon_0.6_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker2_plus/um2p_nylon_0.6_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_nylon_0.6_normal.inst.cfg index 28ccd6ffcf..3dad97297f 100644 --- a/resources/quality/ultimaker2_plus/um2p_nylon_0.6_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_nylon_0.6_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_nylon_0.8_draft.inst.cfg b/resources/quality/ultimaker2_plus/um2p_nylon_0.8_draft.inst.cfg index f868313ba9..fe5749aeff 100644 --- a/resources/quality/ultimaker2_plus/um2p_nylon_0.8_draft.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_nylon_0.8_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker2_plus/um2p_nylon_0.8_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_nylon_0.8_normal.inst.cfg index c30d849553..3e16229ad1 100644 --- a/resources/quality/ultimaker2_plus/um2p_nylon_0.8_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_nylon_0.8_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_pc_0.25_high.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pc_0.25_high.inst.cfg index 08b60eeb20..0195a6170f 100644 --- a/resources/quality/ultimaker2_plus/um2p_pc_0.25_high.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pc_0.25_high.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker2_plus/um2p_pc_0.25_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pc_0.25_normal.inst.cfg index dbc36f0c25..fe15f050c7 100644 --- a/resources/quality/ultimaker2_plus/um2p_pc_0.25_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pc_0.25_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_pc_0.4_fast.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pc_0.4_fast.inst.cfg index 18f299b64d..1268e4f5a5 100644 --- a/resources/quality/ultimaker2_plus/um2p_pc_0.4_fast.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pc_0.4_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker2_plus/um2p_pc_0.4_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pc_0.4_normal.inst.cfg index 9ebb46c4d3..31a55131bd 100644 --- a/resources/quality/ultimaker2_plus/um2p_pc_0.4_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pc_0.4_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_pc_0.6_fast.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pc_0.6_fast.inst.cfg index 47f84fe790..a4b0e7865e 100644 --- a/resources/quality/ultimaker2_plus/um2p_pc_0.6_fast.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pc_0.6_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker2_plus/um2p_pc_0.6_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pc_0.6_normal.inst.cfg index 2c857435c5..9fef7be2f8 100644 --- a/resources/quality/ultimaker2_plus/um2p_pc_0.6_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pc_0.6_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_pc_0.8_draft.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pc_0.8_draft.inst.cfg index 6450d9fe56..cfbd0795ab 100644 --- a/resources/quality/ultimaker2_plus/um2p_pc_0.8_draft.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pc_0.8_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker2_plus/um2p_pc_0.8_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pc_0.8_normal.inst.cfg index 91c990712e..fd24f29c40 100644 --- a/resources/quality/ultimaker2_plus/um2p_pc_0.8_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pc_0.8_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_pp_0.4_fast.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pp_0.4_fast.inst.cfg index 4266bcd46b..8e320bef25 100644 --- a/resources/quality/ultimaker2_plus/um2p_pp_0.4_fast.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pp_0.4_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker2_plus/um2p_pp_0.4_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pp_0.4_normal.inst.cfg index b995c92922..94d7a22965 100644 --- a/resources/quality/ultimaker2_plus/um2p_pp_0.4_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pp_0.4_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_pp_0.6_draft.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pp_0.6_draft.inst.cfg index a9e4917fa2..aee8437e39 100644 --- a/resources/quality/ultimaker2_plus/um2p_pp_0.6_draft.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pp_0.6_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker2_plus/um2p_pp_0.6_fast.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pp_0.6_fast.inst.cfg index 2fec539e2f..f1e709495b 100644 --- a/resources/quality/ultimaker2_plus/um2p_pp_0.6_fast.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pp_0.6_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker2_plus/um2p_pp_0.8_draft.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pp_0.8_draft.inst.cfg index 2bba1be3d4..a3170f49d6 100644 --- a/resources/quality/ultimaker2_plus/um2p_pp_0.8_draft.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pp_0.8_draft.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -2 diff --git a/resources/quality/ultimaker2_plus/um2p_pp_0.8_verydraft.inst.cfg b/resources/quality/ultimaker2_plus/um2p_pp_0.8_verydraft.inst.cfg index 15f7577bdb..eaa18ad931 100644 --- a/resources/quality/ultimaker2_plus/um2p_pp_0.8_verydraft.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_pp_0.8_verydraft.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -3 diff --git a/resources/quality/ultimaker2_plus/um2p_tpu_0.25_high.inst.cfg b/resources/quality/ultimaker2_plus/um2p_tpu_0.25_high.inst.cfg index e0c016abee..47bf078c2f 100644 --- a/resources/quality/ultimaker2_plus/um2p_tpu_0.25_high.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_tpu_0.25_high.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker2_plus/um2p_tpu_0.4_normal.inst.cfg b/resources/quality/ultimaker2_plus/um2p_tpu_0.4_normal.inst.cfg index 127f281913..d8543f1f8a 100644 --- a/resources/quality/ultimaker2_plus/um2p_tpu_0.4_normal.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_tpu_0.4_normal.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker2_plus/um2p_tpu_0.6_fast.inst.cfg b/resources/quality/ultimaker2_plus/um2p_tpu_0.6_fast.inst.cfg index c39ea9cec3..1983878a79 100644 --- a/resources/quality/ultimaker2_plus/um2p_tpu_0.6_fast.inst.cfg +++ b/resources/quality/ultimaker2_plus/um2p_tpu_0.6_fast.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker3/um3_aa0.25_ABS_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.25_ABS_Normal_Quality.inst.cfg index 5139a1fea8..dda9fd3891 100644 --- a/resources/quality/ultimaker3/um3_aa0.25_ABS_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.25_ABS_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.25_CPE_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.25_CPE_Normal_Quality.inst.cfg index 4e81b4f39e..922366d716 100644 --- a/resources/quality/ultimaker3/um3_aa0.25_CPE_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.25_CPE_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.25_Nylon_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.25_Nylon_Normal_Quality.inst.cfg index 04dc2ec79b..3535bc4cba 100644 --- a/resources/quality/ultimaker3/um3_aa0.25_Nylon_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.25_Nylon_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.25_PC_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.25_PC_Normal_Quality.inst.cfg index 4d585b54c1..f24c53aaee 100644 --- a/resources/quality/ultimaker3/um3_aa0.25_PC_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.25_PC_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine - Experimental definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.25_PLA_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.25_PLA_Normal_Quality.inst.cfg index 8dbca3cd05..a59c404754 100644 --- a/resources/quality/ultimaker3/um3_aa0.25_PLA_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.25_PLA_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.25_PP_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.25_PP_Normal_Quality.inst.cfg index bee345e302..6f9734511b 100644 --- a/resources/quality/ultimaker3/um3_aa0.25_PP_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.25_PP_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine - Experimental definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.25_TPLA_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.25_TPLA_Normal_Quality.inst.cfg index 768864bfef..af9bea762a 100644 --- a/resources/quality/ultimaker3/um3_aa0.25_TPLA_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.25_TPLA_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.4_ABS_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_ABS_Draft_Print.inst.cfg index 8877912a33..a6ba0a666b 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_ABS_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_ABS_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.4_ABS_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_ABS_Fast_Print.inst.cfg index 926cfd6995..4bd04e36af 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_ABS_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_ABS_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_ABS_High_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_ABS_High_Quality.inst.cfg index 4e79728945..6ef2fa3cb4 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_ABS_High_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_ABS_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_ABS_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_ABS_Normal_Quality.inst.cfg index 3bded3b97c..e0862a137c 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_ABS_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_ABS_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.4_BAM_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_BAM_Draft_Print.inst.cfg index df7f0fdf02..3de7252b86 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_BAM_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_BAM_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.4_BAM_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_BAM_Fast_Print.inst.cfg index cf330dc984..28bce4eb15 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_BAM_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_BAM_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_BAM_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_BAM_Normal_Quality.inst.cfg index 705c9c4105..0f15931e59 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_BAM_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_BAM_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.4_CPEP_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_CPEP_Draft_Print.inst.cfg index 4e94789a6b..c27fc027fb 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_CPEP_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_CPEP_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.4_CPEP_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_CPEP_Fast_Print.inst.cfg index d93915d721..6a035522f1 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_CPEP_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_CPEP_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_CPEP_High_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_CPEP_High_Quality.inst.cfg index 082152c50f..3387430d26 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_CPEP_High_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_CPEP_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_CPEP_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_CPEP_Normal_Quality.inst.cfg index 889b94e001..8d0283a20f 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_CPEP_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_CPEP_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.4_CPE_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_CPE_Draft_Print.inst.cfg index 1891a274c8..a8d5c26315 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_CPE_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_CPE_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.4_CPE_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_CPE_Fast_Print.inst.cfg index e4cfdb67fc..e4b4e2e21b 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_CPE_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_CPE_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_CPE_High_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_CPE_High_Quality.inst.cfg index cec4b950cf..3964608aec 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_CPE_High_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_CPE_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_CPE_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_CPE_Normal_Quality.inst.cfg index 892083b264..b0e7234c90 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_CPE_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_CPE_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.4_Nylon_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_Nylon_Draft_Print.inst.cfg index 2e4b8f8dcc..5bfa29fd61 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_Nylon_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_Nylon_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.4_Nylon_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_Nylon_Fast_Print.inst.cfg index 9b271c47cd..eaec1cdbdb 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_Nylon_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_Nylon_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_Nylon_High_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_Nylon_High_Quality.inst.cfg index 16c0b6febb..7a0a04b6c2 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_Nylon_High_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_Nylon_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_Nylon_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_Nylon_Normal_Quality.inst.cfg index 17661efbb8..8b18ee20df 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_Nylon_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_Nylon_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.4_PC_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PC_Draft_Print.inst.cfg index 96acf403a5..fafb839f17 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_PC_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_PC_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.4_PC_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PC_Fast_Print.inst.cfg index f9159b5fca..57527f928c 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_PC_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_PC_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_PC_High_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PC_High_Quality.inst.cfg index d175e99ad6..37e31c5863 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_PC_High_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_PC_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_PC_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PC_Normal_Quality.inst.cfg index 557a449022..0961b3ccd6 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_PC_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_PC_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.4_PLA_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PLA_Draft_Print.inst.cfg index 16626dc544..c24c2cacdf 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_PLA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_PLA_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.4_PLA_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PLA_Fast_Print.inst.cfg index 7f50a2a6f0..d20a06a94a 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_PLA_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_PLA_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_PLA_High_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PLA_High_Quality.inst.cfg index 507afc5526..bd365c56ae 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_PLA_High_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_PLA_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_PLA_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PLA_Normal_Quality.inst.cfg index 05febab06d..a291357e58 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_PLA_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_PLA_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.4_PP_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PP_Draft_Print.inst.cfg index 4efa5199cb..6dbb444454 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_PP_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_PP_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.4_PP_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PP_Fast_Print.inst.cfg index ad03df5d86..60c304794b 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_PP_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_PP_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_PP_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_PP_Normal_Quality.inst.cfg index d8d51dd716..92d53d6295 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_PP_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_PP_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.4_TPLA_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_TPLA_Draft_Print.inst.cfg index 9959a39457..6d8c6690a2 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_TPLA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_TPLA_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.4_TPLA_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_TPLA_Fast_Print.inst.cfg index 5c68557e9b..c8fbe827e3 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_TPLA_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_TPLA_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_TPLA_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_TPLA_Normal_Quality.inst.cfg index 90556ea487..d1a1bc994c 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_TPLA_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_TPLA_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.4_TPU_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_TPU_Draft_Print.inst.cfg index 9b9dca3a16..13bc7c0b7e 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_TPU_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_TPU_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.4_TPU_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_TPU_Fast_Print.inst.cfg index e6233a8184..cb3692a84f 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_TPU_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_TPU_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker3/um3_aa0.4_TPU_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_aa0.4_TPU_Normal_Quality.inst.cfg index e725615854..a5264ce6ec 100644 --- a/resources/quality/ultimaker3/um3_aa0.4_TPU_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.4_TPU_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.8_ABS_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_ABS_Draft_Print.inst.cfg index e71ea07531..474b24a462 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_ABS_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_ABS_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.8_ABS_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_ABS_Superdraft_Print.inst.cfg index 39aa103631..e9c4f9e3e9 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_ABS_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_ABS_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker3/um3_aa0.8_ABS_Verydraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_ABS_Verydraft_Print.inst.cfg index 3a08643086..213298653a 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_ABS_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_ABS_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker3/um3_aa0.8_CPEP_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_CPEP_Fast_Print.inst.cfg index fc4acf3cb0..bb2aa2d6ef 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_CPEP_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_CPEP_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast - Experimental definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.8_CPEP_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_CPEP_Superdraft_Print.inst.cfg index 36b3ef603f..98086b2bf1 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_CPEP_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_CPEP_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint - Experimental definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker3/um3_aa0.8_CPEP_Verydraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_CPEP_Verydraft_Print.inst.cfg index 14e08cb14d..0684b9ac1e 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_CPEP_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_CPEP_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast - Experimental definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker3/um3_aa0.8_CPE_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_CPE_Draft_Print.inst.cfg index 170643275c..05294737b6 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_CPE_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_CPE_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.8_CPE_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_CPE_Superdraft_Print.inst.cfg index 5b3cb52f18..555d6da1bf 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_CPE_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_CPE_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker3/um3_aa0.8_CPE_Verydraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_CPE_Verydraft_Print.inst.cfg index fff96ba9fc..da71c679ff 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_CPE_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_CPE_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker3/um3_aa0.8_Nylon_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_Nylon_Draft_Print.inst.cfg index e9b0873716..ab11f0fe86 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_Nylon_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_Nylon_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.8_Nylon_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_Nylon_Superdraft_Print.inst.cfg index 7518acc7f0..46f5a87a1b 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_Nylon_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_Nylon_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker3/um3_aa0.8_Nylon_Verydraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_Nylon_Verydraft_Print.inst.cfg index 040632efec..12d96e9f7d 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_Nylon_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_Nylon_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker3/um3_aa0.8_PC_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_PC_Fast_Print.inst.cfg index c2bb6d4988..7fbd9dbf57 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_PC_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_PC_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast - Experimental definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = 0 diff --git a/resources/quality/ultimaker3/um3_aa0.8_PC_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_PC_Superdraft_Print.inst.cfg index e815b673d1..5e0b65507f 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_PC_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_PC_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint - Experimental definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.8_PC_Verydraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_PC_Verydraft_Print.inst.cfg index c50cee576d..35558f7b80 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_PC_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_PC_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast - Experimental definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -1 diff --git a/resources/quality/ultimaker3/um3_aa0.8_PLA_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_PLA_Draft_Print.inst.cfg index 9b861030d8..c5000b0a43 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_PLA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_PLA_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.8_PLA_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_PLA_Superdraft_Print.inst.cfg index 42a499f22c..e03663bc2b 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_PLA_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_PLA_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker3/um3_aa0.8_PLA_Verydraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_PLA_Verydraft_Print.inst.cfg index d1f3937244..06bf4cf224 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_PLA_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_PLA_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker3/um3_aa0.8_PP_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_PP_Draft_Print.inst.cfg index 19496565bc..b577ba8185 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_PP_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_PP_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.8_PP_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_PP_Superdraft_Print.inst.cfg index aeee3b4e09..c0917a9e8e 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_PP_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_PP_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker3/um3_aa0.8_PP_Verydraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_PP_Verydraft_Print.inst.cfg index fcd4fcd999..00eb39a400 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_PP_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_PP_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker3/um3_aa0.8_TPLA_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_TPLA_Draft_Print.inst.cfg index 3f679870fd..30ce94000f 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_TPLA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_TPLA_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.8_TPLA_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_TPLA_Superdraft_Print.inst.cfg index 17dbd1faf9..ea3525c7ee 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_TPLA_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_TPLA_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker3/um3_aa0.8_TPLA_Verydraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_TPLA_Verydraft_Print.inst.cfg index 624496a9ec..1027f52b38 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_TPLA_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_TPLA_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker3/um3_aa0.8_TPU_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_TPU_Draft_Print.inst.cfg index 90b5103f20..83fb56a84d 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_TPU_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_TPU_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_aa0.8_TPU_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_TPU_Superdraft_Print.inst.cfg index a9fab40d4e..36e38654db 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_TPU_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_TPU_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker3/um3_aa0.8_TPU_Verydraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_aa0.8_TPU_Verydraft_Print.inst.cfg index e2ced0a364..96b1406adb 100644 --- a/resources/quality/ultimaker3/um3_aa0.8_TPU_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_aa0.8_TPU_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker3/um3_bb0.4_PVA_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_bb0.4_PVA_Draft_Print.inst.cfg index 7010d292b2..a41b0c0210 100644 --- a/resources/quality/ultimaker3/um3_bb0.4_PVA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_bb0.4_PVA_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_bb0.4_PVA_Fast_Print.inst.cfg b/resources/quality/ultimaker3/um3_bb0.4_PVA_Fast_Print.inst.cfg index 325609362f..d7a8885bf9 100644 --- a/resources/quality/ultimaker3/um3_bb0.4_PVA_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_bb0.4_PVA_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker3/um3_bb0.4_PVA_High_Quality.inst.cfg b/resources/quality/ultimaker3/um3_bb0.4_PVA_High_Quality.inst.cfg index a0507299fb..be4eedadc8 100644 --- a/resources/quality/ultimaker3/um3_bb0.4_PVA_High_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_bb0.4_PVA_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker3/um3_bb0.4_PVA_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_bb0.4_PVA_Normal_Quality.inst.cfg index 086f811b36..ae576790a8 100644 --- a/resources/quality/ultimaker3/um3_bb0.4_PVA_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_bb0.4_PVA_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_bb0.8_PVA_Draft_Print.inst.cfg b/resources/quality/ultimaker3/um3_bb0.8_PVA_Draft_Print.inst.cfg index 28556ca7bf..247e7f12c6 100644 --- a/resources/quality/ultimaker3/um3_bb0.8_PVA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_bb0.8_PVA_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_bb0.8_PVA_Superdraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_bb0.8_PVA_Superdraft_Print.inst.cfg index 9ad5499f18..8a4c0cc992 100644 --- a/resources/quality/ultimaker3/um3_bb0.8_PVA_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_bb0.8_PVA_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker3/um3_bb0.8_PVA_Verydraft_Print.inst.cfg b/resources/quality/ultimaker3/um3_bb0.8_PVA_Verydraft_Print.inst.cfg index e616214704..c931e6ad5d 100644 --- a/resources/quality/ultimaker3/um3_bb0.8_PVA_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker3/um3_bb0.8_PVA_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker3/um3_global_Draft_Quality.inst.cfg b/resources/quality/ultimaker3/um3_global_Draft_Quality.inst.cfg index a421203220..b95ddb1aa9 100644 --- a/resources/quality/ultimaker3/um3_global_Draft_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_global_Draft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker3/um3_global_Fast_Quality.inst.cfg b/resources/quality/ultimaker3/um3_global_Fast_Quality.inst.cfg index 2ecf7526a2..3510a05343 100644 --- a/resources/quality/ultimaker3/um3_global_Fast_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_global_Fast_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker3/um3_global_High_Quality.inst.cfg b/resources/quality/ultimaker3/um3_global_High_Quality.inst.cfg index ce5497bd39..a738449255 100644 --- a/resources/quality/ultimaker3/um3_global_High_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_global_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 0 diff --git a/resources/quality/ultimaker3/um3_global_Normal_Quality.inst.cfg b/resources/quality/ultimaker3/um3_global_Normal_Quality.inst.cfg index afadda378a..cffacfd312 100644 --- a/resources/quality/ultimaker3/um3_global_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_global_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker3/um3_global_Superdraft_Quality.inst.cfg b/resources/quality/ultimaker3/um3_global_Superdraft_Quality.inst.cfg index f88f5df85f..5fd966bb9f 100644 --- a/resources/quality/ultimaker3/um3_global_Superdraft_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_global_Superdraft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker3/um3_global_Verydraft_Quality.inst.cfg b/resources/quality/ultimaker3/um3_global_Verydraft_Quality.inst.cfg index df626dc724..7e65a6c4c4 100644 --- a/resources/quality/ultimaker3/um3_global_Verydraft_Quality.inst.cfg +++ b/resources/quality/ultimaker3/um3_global_Verydraft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_original/umo_global_Coarse_Quality.inst.cfg b/resources/quality/ultimaker_original/umo_global_Coarse_Quality.inst.cfg index 34f3a2a901..6125d0f687 100644 --- a/resources/quality/ultimaker_original/umo_global_Coarse_Quality.inst.cfg +++ b/resources/quality/ultimaker_original/umo_global_Coarse_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Coarse Quality definition = ultimaker_original [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = coarse weight = -3 diff --git a/resources/quality/ultimaker_original/umo_global_Draft_Quality.inst.cfg b/resources/quality/ultimaker_original/umo_global_Draft_Quality.inst.cfg index ed8c0ddb97..ebabe4dd46 100644 --- a/resources/quality/ultimaker_original/umo_global_Draft_Quality.inst.cfg +++ b/resources/quality/ultimaker_original/umo_global_Draft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Draft Quality definition = ultimaker_original [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_original/umo_global_Extra_Coarse_Quality.inst.cfg b/resources/quality/ultimaker_original/umo_global_Extra_Coarse_Quality.inst.cfg index 1ad10ac4db..b625ec45ea 100644 --- a/resources/quality/ultimaker_original/umo_global_Extra_Coarse_Quality.inst.cfg +++ b/resources/quality/ultimaker_original/umo_global_Extra_Coarse_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Coarse Quality definition = ultimaker_original [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extra coarse weight = -4 diff --git a/resources/quality/ultimaker_original/umo_global_Fast_Quality.inst.cfg b/resources/quality/ultimaker_original/umo_global_Fast_Quality.inst.cfg index 6c83239164..e2bceeae7e 100644 --- a/resources/quality/ultimaker_original/umo_global_Fast_Quality.inst.cfg +++ b/resources/quality/ultimaker_original/umo_global_Fast_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_original [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_original/umo_global_High_Quality.inst.cfg b/resources/quality/ultimaker_original/umo_global_High_Quality.inst.cfg index 19752f07bf..883048f557 100644 --- a/resources/quality/ultimaker_original/umo_global_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_original/umo_global_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker_original [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker_original/umo_global_Normal_Quality.inst.cfg b/resources/quality/ultimaker_original/umo_global_Normal_Quality.inst.cfg index a7dedc9b88..2cec4bfbda 100644 --- a/resources/quality/ultimaker_original/umo_global_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_original/umo_global_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_original [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.25_ABS_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.25_ABS_Normal_Quality.inst.cfg index f2e05b08e8..be8e56d133 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.25_ABS_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.25_ABS_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.25_CPE_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.25_CPE_Normal_Quality.inst.cfg index c15311d104..3eaeda67d5 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.25_CPE_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.25_CPE_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.25_Nylon_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.25_Nylon_Normal_Quality.inst.cfg index 3a8ed8e773..9407fb72b0 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.25_Nylon_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.25_Nylon_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.25_PC_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.25_PC_Normal_Quality.inst.cfg index 53c319d6e6..7cde76a1e1 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.25_PC_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.25_PC_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine - Experimental definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.25_PLA_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.25_PLA_Normal_Quality.inst.cfg index a06f2158fe..ab45d82a3b 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.25_PLA_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.25_PLA_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.25_PP_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.25_PP_Normal_Quality.inst.cfg index b4d34cc392..1474cad5c1 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.25_PP_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.25_PP_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine - Experimental definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.25_TPLA_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.25_TPLA_Normal_Quality.inst.cfg index e9628225bc..ea4375b854 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.25_TPLA_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.25_TPLA_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Draft_Print.inst.cfg index b86c61b3a2..6d26c2bb97 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Fast_Print.inst.cfg index f3c099724a..e2e8bd82e5 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_High_Quality.inst.cfg index 8d016a2ee4..fc39383f04 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Normal_Quality.inst.cfg index 6ce623b66e..8ec7e6453b 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_ABS_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_BAM_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_BAM_Draft_Print.inst.cfg index 254afbc109..93665c36de 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_BAM_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_BAM_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_BAM_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_BAM_Fast_Print.inst.cfg index 39bedce77f..478df58f43 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_BAM_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_BAM_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_BAM_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_BAM_Normal_Quality.inst.cfg index c87d590650..e936d0f131 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_BAM_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_BAM_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Draft_Print.inst.cfg index 627302e0ab..da639d22c9 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Fast_Print.inst.cfg index cda8d85211..2c2eb99fb0 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_High_Quality.inst.cfg index 3ce76bf6be..20fbcd9430 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Normal_Quality.inst.cfg index d402b663c6..e40949fc7d 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPEP_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Draft_Print.inst.cfg index 505cd952d2..7ccfe4f786 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Fast_Print.inst.cfg index cc5df0abb9..f12c90bc05 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_High_Quality.inst.cfg index c81dc0f5a7..04957025f1 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Normal_Quality.inst.cfg index 7d29f8fb7c..b0a5fb820f 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_CPE_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Draft_Print.inst.cfg index 991ad30a5a..1a198f3e03 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Fast_Print.inst.cfg index 695ce2c8fb..e6b882d2af 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_High_Quality.inst.cfg index e55867efe5..292ef73a4c 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Normal_Quality.inst.cfg index 41e28c51d5..2b71a01e0d 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_Nylon_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_Draft_Print.inst.cfg index 5d03e1c980..e27a6894e3 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_Fast_Print.inst.cfg index b630ab6232..db1ecee3bc 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_High_Quality.inst.cfg index 1c080c3b47..ca80494f3d 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_Normal_Quality.inst.cfg index 79ce686da5..d2d5171fed 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_PC_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Draft_Print.inst.cfg index c7a4864328..9bf58d4f39 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Fast_Print.inst.cfg index 42048fa297..017bf46bd8 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_High_Quality.inst.cfg index b7ad8bd5c4..314f7fe0bb 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Normal_Quality.inst.cfg index 911fa9a0a8..2bb0d31d76 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_PLA_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_PP_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_PP_Draft_Print.inst.cfg index 68558bcf93..8b8bdfd999 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_PP_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_PP_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_PP_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_PP_Fast_Print.inst.cfg index 1145d1900f..0fd70e5973 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_PP_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_PP_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_PP_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_PP_Normal_Quality.inst.cfg index c0b094f0a2..0d683a9f87 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_PP_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_PP_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_Draft_Print.inst.cfg index 280e5c4bfb..df45c190e1 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_Fast_Print.inst.cfg index 304c170b55..96b77ebca9 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_High_Quality.inst.cfg index cd5c598d4f..3a2cf495a3 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_Normal_Quality.inst.cfg index 2522c0f20f..2d93d1e980 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_TPLA_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_TPU_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_TPU_Draft_Print.inst.cfg index 9b4ab52543..30a1e0c29f 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_TPU_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_TPU_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_TPU_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_TPU_Fast_Print.inst.cfg index 35cf66a93b..3b57900a46 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_TPU_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_TPU_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_TPU_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_TPU_Normal_Quality.inst.cfg index 4357d765df..368bfd9e48 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_TPU_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_TPU_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_Draft_Print.inst.cfg index c8d64f9dcb..a2b7aa7b87 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_Fast_Print.inst.cfg index c7fa604e89..8e82e27210 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_High_Quality.inst.cfg index 187023d3c0..7e4f17cbf1 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_Normal_Quality.inst.cfg index 81cb27f060..959d9241c7 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_ABS_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Draft_Print.inst.cfg index 4a55f5d24c..c9083cf2ab 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Fast_Print.inst.cfg index 730e058212..9d7663b6e1 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_High_Quality.inst.cfg index e6921e63d8..ba796da940 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Normal_Quality.inst.cfg index a4eec45e38..96ec8d9e36 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPEP_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Draft_Print.inst.cfg index 4f085f10a6..bae8b09016 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Fast_Print.inst.cfg index 2580bf952d..f585c2a787 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_High_Quality.inst.cfg index d6f07c37a5..a580a27176 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Normal_Quality.inst.cfg index 6032ff3845..3db4053d2a 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_CPE_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_Draft_Print.inst.cfg index f05ecddc25..e86c3b363b 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_Fast_Print.inst.cfg index 6103519f1c..0da88626b6 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_High_Quality.inst.cfg index 130afb8c91..d7552ba921 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_Normal_Quality.inst.cfg index 9e1bf394d4..638ce2323e 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PC_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PP_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PP_Draft_Print.inst.cfg index 6124dff257..c0ca84281d 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PP_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PP_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PP_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PP_Fast_Print.inst.cfg index 2791e9f5d5..43b77f6492 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PP_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PP_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PP_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PP_Normal_Quality.inst.cfg index f78b4048fb..04abd0d18b 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PP_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.4_aluminum_PP_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_ABS_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_ABS_Draft_Print.inst.cfg index 911fc6e78e..fb786c7759 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_ABS_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_ABS_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_ABS_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_ABS_Superdraft_Print.inst.cfg index 0fcdac4a85..cfacaebad0 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_ABS_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_ABS_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_ABS_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_ABS_Verydraft_Print.inst.cfg index e3346bbd1d..18ce266cd6 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_ABS_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_ABS_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Fast_Print.inst.cfg index d126dddaad..a4a7a5e08c 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast - Experimental definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Superdraft_Print.inst.cfg index dd8b20652a..e4f330099b 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint - Experimental definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Verydraft_Print.inst.cfg index bc107422f1..0685b27b8a 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPEP_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast - Experimental definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Draft_Print.inst.cfg index 7cb69ba3eb..a57bde98b1 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Superdraft_Print.inst.cfg index 6c323fe602..2f0851d9fb 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Verydraft_Print.inst.cfg index a0380ecc0e..6ccca75b44 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_CPE_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_Nylon_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_Nylon_Draft_Print.inst.cfg index 2108839d3f..5d04892443 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_Nylon_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_Nylon_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_Nylon_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_Nylon_Superdraft_Print.inst.cfg index 0702d174a0..5c31e26bd6 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_Nylon_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_Nylon_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_Nylon_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_Nylon_Verydraft_Print.inst.cfg index d02d410ed6..d04482c94c 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_Nylon_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_Nylon_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Fast_Print.inst.cfg index c24aa9a98d..0513aa7920 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast - Experimental definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Superdraft_Print.inst.cfg index 5fc306b1f1..f37f3fd6f5 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint - Experimental definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Verydraft_Print.inst.cfg index 996adf5be7..7b4da2d7cc 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_PC_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast - Experimental definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_PLA_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_PLA_Draft_Print.inst.cfg index 7db4e96311..1410290859 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_PLA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_PLA_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_PLA_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_PLA_Superdraft_Print.inst.cfg index c59f015b5d..2c65968d77 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_PLA_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_PLA_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_PLA_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_PLA_Verydraft_Print.inst.cfg index 6fdff8bf8d..9b6e7c2ed9 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_PLA_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_PLA_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_PP_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_PP_Draft_Print.inst.cfg index fee58b367d..c5b01ab1b1 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_PP_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_PP_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_PP_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_PP_Superdraft_Print.inst.cfg index aaa810e864..5c83f24395 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_PP_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_PP_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_PP_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_PP_Verydraft_Print.inst.cfg index 5b8aa6d2e1..f182d5c6cb 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_PP_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_PP_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPLA_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPLA_Draft_Print.inst.cfg index 50dead746b..5b39508f3c 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPLA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPLA_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPLA_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPLA_Superdraft_Print.inst.cfg index 0bdb088f8c..b07a78419a 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPLA_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPLA_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPLA_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPLA_Verydraft_Print.inst.cfg index c7cb5902a2..46a61bb79f 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPLA_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPLA_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Draft_Print.inst.cfg index e8276d54c5..088414fa34 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Superdraft_Print.inst.cfg index 7da73a200d..d74309c476 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Verydraft_Print.inst.cfg index 60dbbf38e6..deb2b55dbb 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_TPU_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_ABS_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_ABS_Draft_Print.inst.cfg index 37dceff349..062fcfdd5b 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_ABS_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_ABS_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_ABS_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_ABS_Superdraft_Print.inst.cfg index eac339baa8..38c341af28 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_ABS_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_ABS_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_ABS_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_ABS_Verydraft_Print.inst.cfg index 590496df0f..8a38bb9aa3 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_ABS_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_ABS_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Fast_Print.inst.cfg index 50091a6fb4..d9dd09c7b6 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast - Experimental definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Superdraft_Print.inst.cfg index b9c9ef6611..bc6a04ae55 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Verydraft_Print.inst.cfg index be2cc62b08..b384bd7083 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPEP_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Draft_Print.inst.cfg index 0d0ed8f8b2..28409d4181 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Superdraft_Print.inst.cfg index a163e0c735..33c5d73a59 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Verydraft_Print.inst.cfg index 2137cf740b..8db23c11c2 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_CPE_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Fast_Print.inst.cfg index 6fdd22c6b0..c853d3ebc5 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast - Experimental definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Superdraft_Print.inst.cfg index 689652dc06..f78e6a92d2 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Verydraft_Print.inst.cfg index 0480ee5620..b1d199ec9b 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PC_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PP_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PP_Draft_Print.inst.cfg index b80af1b75f..5a2c3005c9 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PP_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PP_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PP_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PP_Superdraft_Print.inst.cfg index 970e0971a9..aaa8cbfcd3 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PP_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PP_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PP_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PP_Verydraft_Print.inst.cfg index e51ba3207b..782cadce6b 100644 --- a/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PP_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_aa0.8_aluminum_PP_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_Draft_Print.inst.cfg index 73639be0b6..34b078278c 100644 --- a/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_Fast_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_Fast_Print.inst.cfg index 5da25be32d..5677c5931c 100644 --- a/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_Fast_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_Fast_Print.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_High_Quality.inst.cfg index 36634af2c8..b9e32cb7c5 100644 --- a/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_Normal_Quality.inst.cfg index f76c4c944a..081ee2261e 100644 --- a/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_bb0.4_PVA_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_bb0.8_PVA_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_bb0.8_PVA_Draft_Print.inst.cfg index e4e3ab772a..0193081db6 100644 --- a/resources/quality/ultimaker_s5/um_s5_bb0.8_PVA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_bb0.8_PVA_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_bb0.8_PVA_Superdraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_bb0.8_PVA_Superdraft_Print.inst.cfg index 5e78e51014..121b32f903 100644 --- a/resources/quality/ultimaker_s5/um_s5_bb0.8_PVA_Superdraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_bb0.8_PVA_Superdraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_bb0.8_PVA_Verydraft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_bb0.8_PVA_Verydraft_Print.inst.cfg index 5af09aebcc..f0296c9279 100644 --- a/resources/quality/ultimaker_s5/um_s5_bb0.8_PVA_Verydraft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_bb0.8_PVA_Verydraft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_cc0.6_CFFCPE_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_cc0.6_CFFCPE_Draft_Print.inst.cfg index 99b4b142fa..4aa31aaba3 100644 --- a/resources/quality/ultimaker_s5/um_s5_cc0.6_CFFCPE_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_cc0.6_CFFCPE_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_cc0.6_CFFPA_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_cc0.6_CFFPA_Draft_Print.inst.cfg index 80c383aa8d..14aeca4194 100644 --- a/resources/quality/ultimaker_s5/um_s5_cc0.6_CFFPA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_cc0.6_CFFPA_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_cc0.6_GFFCPE_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_cc0.6_GFFCPE_Draft_Print.inst.cfg index c94d239c81..1a0abc1ee2 100644 --- a/resources/quality/ultimaker_s5/um_s5_cc0.6_GFFCPE_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_cc0.6_GFFCPE_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_cc0.6_GFFPA_Draft_Print.inst.cfg b/resources/quality/ultimaker_s5/um_s5_cc0.6_GFFPA_Draft_Print.inst.cfg index e7d4d1955b..7f462e5634 100644 --- a/resources/quality/ultimaker_s5/um_s5_cc0.6_GFFPA_Draft_Print.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_cc0.6_GFFPA_Draft_Print.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -3 diff --git a/resources/quality/ultimaker_s5/um_s5_global_Draft_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_global_Draft_Quality.inst.cfg index ed5303637b..f0121fa4d7 100644 --- a/resources/quality/ultimaker_s5/um_s5_global_Draft_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_global_Draft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = draft weight = -2 diff --git a/resources/quality/ultimaker_s5/um_s5_global_Fast_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_global_Fast_Quality.inst.cfg index ee9c6a8409..b9bb0beff6 100644 --- a/resources/quality/ultimaker_s5/um_s5_global_Fast_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_global_Fast_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = -1 diff --git a/resources/quality/ultimaker_s5/um_s5_global_High_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_global_High_Quality.inst.cfg index cd1c269b1d..aa07e1dc3c 100644 --- a/resources/quality/ultimaker_s5/um_s5_global_High_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_global_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_global_Normal_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_global_Normal_Quality.inst.cfg index 099ba7c584..8bb8f7f282 100644 --- a/resources/quality/ultimaker_s5/um_s5_global_Normal_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_global_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/ultimaker_s5/um_s5_global_Superdraft_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_global_Superdraft_Quality.inst.cfg index 4c0bd40bd1..ec46668b61 100644 --- a/resources/quality/ultimaker_s5/um_s5_global_Superdraft_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_global_Superdraft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Sprint definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = superdraft weight = -4 diff --git a/resources/quality/ultimaker_s5/um_s5_global_Verydraft_Quality.inst.cfg b/resources/quality/ultimaker_s5/um_s5_global_Verydraft_Quality.inst.cfg index ec4ec910ff..dd6c7bb4b8 100644 --- a/resources/quality/ultimaker_s5/um_s5_global_Verydraft_Quality.inst.cfg +++ b/resources/quality/ultimaker_s5/um_s5_global_Verydraft_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extra Fast definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = verydraft weight = -3 diff --git a/resources/quality/vertex_delta_k8800/k8800_ABS_Extreme_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_ABS_Extreme_Quality.inst.cfg index 70aac3f666..370cc149a2 100644 --- a/resources/quality/vertex_delta_k8800/k8800_ABS_Extreme_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_ABS_Extreme_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extreme definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extreme weight = 2 diff --git a/resources/quality/vertex_delta_k8800/k8800_ABS_High_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_ABS_High_Quality.inst.cfg index 564b330132..6c96b47169 100644 --- a/resources/quality/vertex_delta_k8800/k8800_ABS_High_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_ABS_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = High definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/vertex_delta_k8800/k8800_ABS_Normal_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_ABS_Normal_Quality.inst.cfg index e2f740a60a..19150baab9 100644 --- a/resources/quality/vertex_delta_k8800/k8800_ABS_Normal_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_ABS_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/vertex_delta_k8800/k8800_Global_Extreme_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_Global_Extreme_Quality.inst.cfg index 48e80b5512..00d626d72c 100644 --- a/resources/quality/vertex_delta_k8800/k8800_Global_Extreme_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_Global_Extreme_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extreme definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extreme weight = 2 diff --git a/resources/quality/vertex_delta_k8800/k8800_Global_High_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_Global_High_Quality.inst.cfg index 496144772c..0bd519affe 100644 --- a/resources/quality/vertex_delta_k8800/k8800_Global_High_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_Global_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = High definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/vertex_delta_k8800/k8800_Global_Normal_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_Global_Normal_Quality.inst.cfg index 75ae5f15e6..2b18891998 100644 --- a/resources/quality/vertex_delta_k8800/k8800_Global_Normal_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_Global_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/vertex_delta_k8800/k8800_PET_Extreme_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_PET_Extreme_Quality.inst.cfg index 8309106d9f..8715e573ab 100644 --- a/resources/quality/vertex_delta_k8800/k8800_PET_Extreme_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_PET_Extreme_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extreme definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extreme weight = 2 diff --git a/resources/quality/vertex_delta_k8800/k8800_PET_High_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_PET_High_Quality.inst.cfg index 6efaa3299f..0c94cc5ca4 100644 --- a/resources/quality/vertex_delta_k8800/k8800_PET_High_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_PET_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = High definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/vertex_delta_k8800/k8800_PET_Normal_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_PET_Normal_Quality.inst.cfg index bd3b0c35fb..40698f134d 100644 --- a/resources/quality/vertex_delta_k8800/k8800_PET_Normal_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_PET_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/vertex_delta_k8800/k8800_PLA_Extreme_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_PLA_Extreme_Quality.inst.cfg index d10b4c3f8d..f81c82fa9b 100644 --- a/resources/quality/vertex_delta_k8800/k8800_PLA_Extreme_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_PLA_Extreme_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extreme definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extreme weight = 2 diff --git a/resources/quality/vertex_delta_k8800/k8800_PLA_High_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_PLA_High_Quality.inst.cfg index ede77b0dfe..e4b4771b0e 100644 --- a/resources/quality/vertex_delta_k8800/k8800_PLA_High_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_PLA_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = High definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/vertex_delta_k8800/k8800_PLA_Normal_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_PLA_Normal_Quality.inst.cfg index a75cff6968..41950da7a1 100644 --- a/resources/quality/vertex_delta_k8800/k8800_PLA_Normal_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_PLA_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/vertex_delta_k8800/k8800_TPU_Extreme_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_TPU_Extreme_Quality.inst.cfg index bee6b3bf11..32077f6cc1 100644 --- a/resources/quality/vertex_delta_k8800/k8800_TPU_Extreme_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_TPU_Extreme_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Extreme definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = extreme weight = 2 diff --git a/resources/quality/vertex_delta_k8800/k8800_TPU_High_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_TPU_High_Quality.inst.cfg index 8b0f87cc77..a7ef7d8cc8 100644 --- a/resources/quality/vertex_delta_k8800/k8800_TPU_High_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_TPU_High_Quality.inst.cfg @@ -4,7 +4,7 @@ name = High definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = high weight = 1 diff --git a/resources/quality/vertex_delta_k8800/k8800_TPU_Normal_Quality.inst.cfg b/resources/quality/vertex_delta_k8800/k8800_TPU_Normal_Quality.inst.cfg index bd4e04744f..17a09f366f 100644 --- a/resources/quality/vertex_delta_k8800/k8800_TPU_Normal_Quality.inst.cfg +++ b/resources/quality/vertex_delta_k8800/k8800_TPU_Normal_Quality.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = vertex_delta_k8800 [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 0 diff --git a/resources/quality/zyyx/zyyx_agile_global_fast.inst.cfg b/resources/quality/zyyx/zyyx_agile_global_fast.inst.cfg index 67e350b39e..19cda650d3 100644 --- a/resources/quality/zyyx/zyyx_agile_global_fast.inst.cfg +++ b/resources/quality/zyyx/zyyx_agile_global_fast.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = zyyx_agile [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = 1 diff --git a/resources/quality/zyyx/zyyx_agile_global_fine.inst.cfg b/resources/quality/zyyx/zyyx_agile_global_fine.inst.cfg index 58e13b22c5..f5b6aca497 100644 --- a/resources/quality/zyyx/zyyx_agile_global_fine.inst.cfg +++ b/resources/quality/zyyx/zyyx_agile_global_fine.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = zyyx_agile [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fine weight = 3 diff --git a/resources/quality/zyyx/zyyx_agile_global_normal.inst.cfg b/resources/quality/zyyx/zyyx_agile_global_normal.inst.cfg index cb4e042e7b..46d31c9749 100644 --- a/resources/quality/zyyx/zyyx_agile_global_normal.inst.cfg +++ b/resources/quality/zyyx/zyyx_agile_global_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = zyyx_agile [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 2 diff --git a/resources/quality/zyyx/zyyx_agile_pro_flex_fast.inst.cfg b/resources/quality/zyyx/zyyx_agile_pro_flex_fast.inst.cfg index 188bdd25e5..d6f715c09f 100644 --- a/resources/quality/zyyx/zyyx_agile_pro_flex_fast.inst.cfg +++ b/resources/quality/zyyx/zyyx_agile_pro_flex_fast.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = zyyx_agile [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = 1 diff --git a/resources/quality/zyyx/zyyx_agile_pro_flex_fine.inst.cfg b/resources/quality/zyyx/zyyx_agile_pro_flex_fine.inst.cfg index 6654889c10..c617eb646e 100644 --- a/resources/quality/zyyx/zyyx_agile_pro_flex_fine.inst.cfg +++ b/resources/quality/zyyx/zyyx_agile_pro_flex_fine.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = zyyx_agile [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fine weight = 3 diff --git a/resources/quality/zyyx/zyyx_agile_pro_flex_normal.inst.cfg b/resources/quality/zyyx/zyyx_agile_pro_flex_normal.inst.cfg index f56355100c..a9164b5118 100644 --- a/resources/quality/zyyx/zyyx_agile_pro_flex_normal.inst.cfg +++ b/resources/quality/zyyx/zyyx_agile_pro_flex_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = zyyx_agile [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 2 diff --git a/resources/quality/zyyx/zyyx_agile_pro_pla_fast.inst.cfg b/resources/quality/zyyx/zyyx_agile_pro_pla_fast.inst.cfg index 7ae4be06b0..5b32c3f07b 100644 --- a/resources/quality/zyyx/zyyx_agile_pro_pla_fast.inst.cfg +++ b/resources/quality/zyyx/zyyx_agile_pro_pla_fast.inst.cfg @@ -4,7 +4,7 @@ name = Fast definition = zyyx_agile [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fast weight = 1 diff --git a/resources/quality/zyyx/zyyx_agile_pro_pla_fine.inst.cfg b/resources/quality/zyyx/zyyx_agile_pro_pla_fine.inst.cfg index 64c7d4afc8..8ec5495334 100644 --- a/resources/quality/zyyx/zyyx_agile_pro_pla_fine.inst.cfg +++ b/resources/quality/zyyx/zyyx_agile_pro_pla_fine.inst.cfg @@ -4,7 +4,7 @@ name = Fine definition = zyyx_agile [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = fine weight = 3 diff --git a/resources/quality/zyyx/zyyx_agile_pro_pla_normal.inst.cfg b/resources/quality/zyyx/zyyx_agile_pro_pla_normal.inst.cfg index dbdd600ece..c00daca9af 100644 --- a/resources/quality/zyyx/zyyx_agile_pro_pla_normal.inst.cfg +++ b/resources/quality/zyyx/zyyx_agile_pro_pla_normal.inst.cfg @@ -4,7 +4,7 @@ name = Normal definition = zyyx_agile [metadata] -setting_version = 5 +setting_version = 6 type = quality quality_type = normal weight = 2 diff --git a/resources/variants/cartesio_0.25.inst.cfg b/resources/variants/cartesio_0.25.inst.cfg index b3aae8a393..53048622f2 100644 --- a/resources/variants/cartesio_0.25.inst.cfg +++ b/resources/variants/cartesio_0.25.inst.cfg @@ -5,7 +5,7 @@ definition = cartesio [metadata] author = Cartesio -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/cartesio_0.4.inst.cfg b/resources/variants/cartesio_0.4.inst.cfg index 5cea5823c4..3ad6b3f3d9 100644 --- a/resources/variants/cartesio_0.4.inst.cfg +++ b/resources/variants/cartesio_0.4.inst.cfg @@ -5,7 +5,7 @@ definition = cartesio [metadata] author = Cartesio -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/cartesio_0.8.inst.cfg b/resources/variants/cartesio_0.8.inst.cfg index b4009cf9ed..f19c4d58d4 100644 --- a/resources/variants/cartesio_0.8.inst.cfg +++ b/resources/variants/cartesio_0.8.inst.cfg @@ -5,7 +5,7 @@ definition = cartesio [metadata] author = Cartesio -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/fabtotum_hyb35.inst.cfg b/resources/variants/fabtotum_hyb35.inst.cfg index d3f0077792..5ed9eca26a 100644 --- a/resources/variants/fabtotum_hyb35.inst.cfg +++ b/resources/variants/fabtotum_hyb35.inst.cfg @@ -5,7 +5,7 @@ definition = fabtotum [metadata] author = FABtotum -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/fabtotum_lite04.inst.cfg b/resources/variants/fabtotum_lite04.inst.cfg index 226c136564..7b52bf5c29 100644 --- a/resources/variants/fabtotum_lite04.inst.cfg +++ b/resources/variants/fabtotum_lite04.inst.cfg @@ -5,7 +5,7 @@ definition = fabtotum [metadata] author = FABtotum -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/fabtotum_lite06.inst.cfg b/resources/variants/fabtotum_lite06.inst.cfg index 62e3014b60..f518f60ce9 100644 --- a/resources/variants/fabtotum_lite06.inst.cfg +++ b/resources/variants/fabtotum_lite06.inst.cfg @@ -5,7 +5,7 @@ definition = fabtotum [metadata] author = FABtotum -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/fabtotum_pro02.inst.cfg b/resources/variants/fabtotum_pro02.inst.cfg index 3e4661ee2c..897bd5aea9 100644 --- a/resources/variants/fabtotum_pro02.inst.cfg +++ b/resources/variants/fabtotum_pro02.inst.cfg @@ -5,7 +5,7 @@ definition = fabtotum [metadata] author = FABtotum -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/fabtotum_pro04.inst.cfg b/resources/variants/fabtotum_pro04.inst.cfg index 3fe140f8be..d27be2f3b8 100644 --- a/resources/variants/fabtotum_pro04.inst.cfg +++ b/resources/variants/fabtotum_pro04.inst.cfg @@ -5,7 +5,7 @@ definition = fabtotum [metadata] author = FABtotum -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/fabtotum_pro06.inst.cfg b/resources/variants/fabtotum_pro06.inst.cfg index fcb5c71ef0..25b9d7c710 100644 --- a/resources/variants/fabtotum_pro06.inst.cfg +++ b/resources/variants/fabtotum_pro06.inst.cfg @@ -5,7 +5,7 @@ definition = fabtotum [metadata] author = FABtotum -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/fabtotum_pro08.inst.cfg b/resources/variants/fabtotum_pro08.inst.cfg index bef04734eb..627e26e2a2 100644 --- a/resources/variants/fabtotum_pro08.inst.cfg +++ b/resources/variants/fabtotum_pro08.inst.cfg @@ -5,7 +5,7 @@ definition = fabtotum [metadata] author = FABtotum -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/felixtec4_0.25.inst.cfg b/resources/variants/felixtec4_0.25.inst.cfg index 7d8bca94b0..3dda15171b 100644 --- a/resources/variants/felixtec4_0.25.inst.cfg +++ b/resources/variants/felixtec4_0.25.inst.cfg @@ -6,7 +6,7 @@ definition = felixtec4dual [metadata] author = kerog777 type = variant -setting_version = 5 +setting_version = 6 hardware_type = nozzle [values] diff --git a/resources/variants/felixtec4_0.35.inst.cfg b/resources/variants/felixtec4_0.35.inst.cfg index f061aa1cbc..db79c3bad4 100644 --- a/resources/variants/felixtec4_0.35.inst.cfg +++ b/resources/variants/felixtec4_0.35.inst.cfg @@ -6,7 +6,7 @@ definition = felixtec4dual [metadata] author = kerog777 type = variant -setting_version = 5 +setting_version = 6 hardware_type = nozzle [values] diff --git a/resources/variants/felixtec4_0.50.inst.cfg b/resources/variants/felixtec4_0.50.inst.cfg index 3c68c42dae..6d52881ee5 100644 --- a/resources/variants/felixtec4_0.50.inst.cfg +++ b/resources/variants/felixtec4_0.50.inst.cfg @@ -7,7 +7,7 @@ definition = felixtec4dual author = kerog777 type = variant hardware_type = nozzle -setting_version = 5 +setting_version = 6 [values] machine_nozzle_size = 0.5 diff --git a/resources/variants/felixtec4_0.70.inst.cfg b/resources/variants/felixtec4_0.70.inst.cfg index 3a52644714..4edeebbc84 100644 --- a/resources/variants/felixtec4_0.70.inst.cfg +++ b/resources/variants/felixtec4_0.70.inst.cfg @@ -7,7 +7,7 @@ definition = felixtec4dual author = kerog777 type = variant hardware_type = nozzle -setting_version = 5 +setting_version = 6 [values] machine_nozzle_size = 0.70 diff --git a/resources/variants/gmax15plus_025_e3d.inst.cfg b/resources/variants/gmax15plus_025_e3d.inst.cfg index 8a6b37067d..a085be0526 100644 --- a/resources/variants/gmax15plus_025_e3d.inst.cfg +++ b/resources/variants/gmax15plus_025_e3d.inst.cfg @@ -5,7 +5,7 @@ definition = gmax15plus [metadata] author = gcreate -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_04_e3d.inst.cfg b/resources/variants/gmax15plus_04_e3d.inst.cfg index a2f779f426..a76e04074b 100644 --- a/resources/variants/gmax15plus_04_e3d.inst.cfg +++ b/resources/variants/gmax15plus_04_e3d.inst.cfg @@ -5,7 +5,7 @@ definition = gmax15plus [metadata] author = gcreate -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_05_e3d.inst.cfg b/resources/variants/gmax15plus_05_e3d.inst.cfg index 68ee111aa1..71e8251fa6 100644 --- a/resources/variants/gmax15plus_05_e3d.inst.cfg +++ b/resources/variants/gmax15plus_05_e3d.inst.cfg @@ -5,7 +5,7 @@ definition = gmax15plus [metadata] author = gcreate -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_05_jhead.inst.cfg b/resources/variants/gmax15plus_05_jhead.inst.cfg index 6d0b084969..d480b47367 100644 --- a/resources/variants/gmax15plus_05_jhead.inst.cfg +++ b/resources/variants/gmax15plus_05_jhead.inst.cfg @@ -5,7 +5,7 @@ definition = gmax15plus [metadata] author = gcreate -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_06_e3d.inst.cfg b/resources/variants/gmax15plus_06_e3d.inst.cfg index 987e882a09..4c4684cbc8 100644 --- a/resources/variants/gmax15plus_06_e3d.inst.cfg +++ b/resources/variants/gmax15plus_06_e3d.inst.cfg @@ -5,7 +5,7 @@ definition = gmax15plus [metadata] author = gcreate -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_08_e3d.inst.cfg b/resources/variants/gmax15plus_08_e3d.inst.cfg index bf59b47da0..1265dbf474 100644 --- a/resources/variants/gmax15plus_08_e3d.inst.cfg +++ b/resources/variants/gmax15plus_08_e3d.inst.cfg @@ -5,7 +5,7 @@ definition = gmax15plus [metadata] author = gcreate -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_10_jhead.inst.cfg b/resources/variants/gmax15plus_10_jhead.inst.cfg index 47355f344c..aa9e4b76ac 100644 --- a/resources/variants/gmax15plus_10_jhead.inst.cfg +++ b/resources/variants/gmax15plus_10_jhead.inst.cfg @@ -5,7 +5,7 @@ definition = gmax15plus [metadata] author = gcreate -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_dual_025_e3d.inst.cfg b/resources/variants/gmax15plus_dual_025_e3d.inst.cfg index 750a5381b3..185166d3b5 100644 --- a/resources/variants/gmax15plus_dual_025_e3d.inst.cfg +++ b/resources/variants/gmax15plus_dual_025_e3d.inst.cfg @@ -5,7 +5,7 @@ definition = gmax15plus_dual [metadata] author = gcreate -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_dual_04_e3d.inst.cfg b/resources/variants/gmax15plus_dual_04_e3d.inst.cfg index 4b5a71c53b..7798d965d6 100644 --- a/resources/variants/gmax15plus_dual_04_e3d.inst.cfg +++ b/resources/variants/gmax15plus_dual_04_e3d.inst.cfg @@ -5,7 +5,7 @@ definition = gmax15plus_dual [metadata] author = gcreate -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_dual_05_e3d.inst.cfg b/resources/variants/gmax15plus_dual_05_e3d.inst.cfg index 05d9a88d54..f1d67509a9 100644 --- a/resources/variants/gmax15plus_dual_05_e3d.inst.cfg +++ b/resources/variants/gmax15plus_dual_05_e3d.inst.cfg @@ -5,7 +5,7 @@ definition = gmax15plus_dual [metadata] author = gcreate -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_dual_05_jhead.inst.cfg b/resources/variants/gmax15plus_dual_05_jhead.inst.cfg index 54a237e848..bc72c4ae20 100644 --- a/resources/variants/gmax15plus_dual_05_jhead.inst.cfg +++ b/resources/variants/gmax15plus_dual_05_jhead.inst.cfg @@ -5,7 +5,7 @@ definition = gmax15plus_dual [metadata] author = gcreate -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_dual_06_e3d.inst.cfg b/resources/variants/gmax15plus_dual_06_e3d.inst.cfg index 39c41be968..545cfcb238 100644 --- a/resources/variants/gmax15plus_dual_06_e3d.inst.cfg +++ b/resources/variants/gmax15plus_dual_06_e3d.inst.cfg @@ -5,7 +5,7 @@ definition = gmax15plus_dual [metadata] author = gcreate -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_dual_08_e3d.inst.cfg b/resources/variants/gmax15plus_dual_08_e3d.inst.cfg index 1f2d7b9790..1923aac7f7 100644 --- a/resources/variants/gmax15plus_dual_08_e3d.inst.cfg +++ b/resources/variants/gmax15plus_dual_08_e3d.inst.cfg @@ -5,7 +5,7 @@ definition = gmax15plus_dual [metadata] author = gcreate -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/gmax15plus_dual_10_jhead.inst.cfg b/resources/variants/gmax15plus_dual_10_jhead.inst.cfg index cf615bb874..31a1918ab2 100644 --- a/resources/variants/gmax15plus_dual_10_jhead.inst.cfg +++ b/resources/variants/gmax15plus_dual_10_jhead.inst.cfg @@ -5,7 +5,7 @@ definition = gmax15plus_dual [metadata] author = gcreate -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/imade3d_jellybox_0.4.inst.cfg b/resources/variants/imade3d_jellybox_0.4.inst.cfg index 2bd0f578cf..1623e6755c 100644 --- a/resources/variants/imade3d_jellybox_0.4.inst.cfg +++ b/resources/variants/imade3d_jellybox_0.4.inst.cfg @@ -5,7 +5,7 @@ definition = imade3d_jellybox [metadata] author = IMADE3D -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/imade3d_jellybox_0.4_2-fans.inst.cfg b/resources/variants/imade3d_jellybox_0.4_2-fans.inst.cfg index 6a93cdf13d..2f1cb002b4 100644 --- a/resources/variants/imade3d_jellybox_0.4_2-fans.inst.cfg +++ b/resources/variants/imade3d_jellybox_0.4_2-fans.inst.cfg @@ -5,7 +5,7 @@ definition = imade3d_jellybox [metadata] author = IMADE3D -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/tizyx_k25_0.2.inst.cfg b/resources/variants/tizyx_k25_0.2.inst.cfg index cd9f1bcbd1..c616579911 100644 --- a/resources/variants/tizyx_k25_0.2.inst.cfg +++ b/resources/variants/tizyx_k25_0.2.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = tizyx_k25 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/tizyx_k25_0.3.inst.cfg b/resources/variants/tizyx_k25_0.3.inst.cfg index 8b34d23bf6..180d831cca 100644 --- a/resources/variants/tizyx_k25_0.3.inst.cfg +++ b/resources/variants/tizyx_k25_0.3.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = tizyx_k25 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/tizyx_k25_0.4.inst.cfg b/resources/variants/tizyx_k25_0.4.inst.cfg index c147eb0ad0..07ef3ca8ed 100644 --- a/resources/variants/tizyx_k25_0.4.inst.cfg +++ b/resources/variants/tizyx_k25_0.4.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = tizyx_k25 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/tizyx_k25_0.5.inst.cfg b/resources/variants/tizyx_k25_0.5.inst.cfg index 14102fb2c7..a09a207e7a 100644 --- a/resources/variants/tizyx_k25_0.5.inst.cfg +++ b/resources/variants/tizyx_k25_0.5.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = tizyx_k25 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/tizyx_k25_0.6.inst.cfg b/resources/variants/tizyx_k25_0.6.inst.cfg index 00f69f71f4..751cf8e794 100644 --- a/resources/variants/tizyx_k25_0.6.inst.cfg +++ b/resources/variants/tizyx_k25_0.6.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = tizyx_k25 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/tizyx_k25_0.8.inst.cfg b/resources/variants/tizyx_k25_0.8.inst.cfg index c80f5e70d2..cca0986ed5 100644 --- a/resources/variants/tizyx_k25_0.8.inst.cfg +++ b/resources/variants/tizyx_k25_0.8.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = tizyx_k25 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/tizyx_k25_1.0.inst.cfg b/resources/variants/tizyx_k25_1.0.inst.cfg index ce8593b1e8..d99948c26c 100644 --- a/resources/variants/tizyx_k25_1.0.inst.cfg +++ b/resources/variants/tizyx_k25_1.0.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = tizyx_k25 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_0.25.inst.cfg b/resources/variants/ultimaker2_0.25.inst.cfg index a58b4d9a56..004cdaf671 100644 --- a/resources/variants/ultimaker2_0.25.inst.cfg +++ b/resources/variants/ultimaker2_0.25.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_0.4.inst.cfg b/resources/variants/ultimaker2_0.4.inst.cfg index 46845d974e..607d0c4f29 100644 --- a/resources/variants/ultimaker2_0.4.inst.cfg +++ b/resources/variants/ultimaker2_0.4.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_0.6.inst.cfg b/resources/variants/ultimaker2_0.6.inst.cfg index f9ab1f1358..1ddc07817b 100644 --- a/resources/variants/ultimaker2_0.6.inst.cfg +++ b/resources/variants/ultimaker2_0.6.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_0.8.inst.cfg b/resources/variants/ultimaker2_0.8.inst.cfg index 3d9c273783..938b472c2c 100644 --- a/resources/variants/ultimaker2_0.8.inst.cfg +++ b/resources/variants/ultimaker2_0.8.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_extended_0.25.inst.cfg b/resources/variants/ultimaker2_extended_0.25.inst.cfg index f5471fc505..45546c55aa 100644 --- a/resources/variants/ultimaker2_extended_0.25.inst.cfg +++ b/resources/variants/ultimaker2_extended_0.25.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2_extended [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_extended_0.4.inst.cfg b/resources/variants/ultimaker2_extended_0.4.inst.cfg index a7d03f2408..fb3d8c1116 100644 --- a/resources/variants/ultimaker2_extended_0.4.inst.cfg +++ b/resources/variants/ultimaker2_extended_0.4.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2_extended [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_extended_0.6.inst.cfg b/resources/variants/ultimaker2_extended_0.6.inst.cfg index 25c180e07e..50f7dc04c6 100644 --- a/resources/variants/ultimaker2_extended_0.6.inst.cfg +++ b/resources/variants/ultimaker2_extended_0.6.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2_extended [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_extended_0.8.inst.cfg b/resources/variants/ultimaker2_extended_0.8.inst.cfg index c33f483da3..178737dbd5 100644 --- a/resources/variants/ultimaker2_extended_0.8.inst.cfg +++ b/resources/variants/ultimaker2_extended_0.8.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2_extended [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_extended_plus_0.25.inst.cfg b/resources/variants/ultimaker2_extended_plus_0.25.inst.cfg index c65940251c..7e67824d16 100644 --- a/resources/variants/ultimaker2_extended_plus_0.25.inst.cfg +++ b/resources/variants/ultimaker2_extended_plus_0.25.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2_extended_plus [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_extended_plus_0.4.inst.cfg b/resources/variants/ultimaker2_extended_plus_0.4.inst.cfg index 7493f2af44..1150c6127c 100644 --- a/resources/variants/ultimaker2_extended_plus_0.4.inst.cfg +++ b/resources/variants/ultimaker2_extended_plus_0.4.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2_extended_plus [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_extended_plus_0.6.inst.cfg b/resources/variants/ultimaker2_extended_plus_0.6.inst.cfg index c4a3ab6340..fbdef77918 100644 --- a/resources/variants/ultimaker2_extended_plus_0.6.inst.cfg +++ b/resources/variants/ultimaker2_extended_plus_0.6.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2_extended_plus [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_extended_plus_0.8.inst.cfg b/resources/variants/ultimaker2_extended_plus_0.8.inst.cfg index e77ec2a5c2..106537e0a7 100644 --- a/resources/variants/ultimaker2_extended_plus_0.8.inst.cfg +++ b/resources/variants/ultimaker2_extended_plus_0.8.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2_extended_plus [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_plus_0.25.inst.cfg b/resources/variants/ultimaker2_plus_0.25.inst.cfg index 7fd7f3980f..c07b80c246 100644 --- a/resources/variants/ultimaker2_plus_0.25.inst.cfg +++ b/resources/variants/ultimaker2_plus_0.25.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_plus_0.4.inst.cfg b/resources/variants/ultimaker2_plus_0.4.inst.cfg index 3b54e0cdef..623fffbeb9 100644 --- a/resources/variants/ultimaker2_plus_0.4.inst.cfg +++ b/resources/variants/ultimaker2_plus_0.4.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_plus_0.6.inst.cfg b/resources/variants/ultimaker2_plus_0.6.inst.cfg index d8fea055e5..b57fa81dfe 100644 --- a/resources/variants/ultimaker2_plus_0.6.inst.cfg +++ b/resources/variants/ultimaker2_plus_0.6.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker2_plus_0.8.inst.cfg b/resources/variants/ultimaker2_plus_0.8.inst.cfg index 3ae902ac2f..702ec2ef31 100644 --- a/resources/variants/ultimaker2_plus_0.8.inst.cfg +++ b/resources/variants/ultimaker2_plus_0.8.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker2_plus [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker3_aa0.25.inst.cfg b/resources/variants/ultimaker3_aa0.25.inst.cfg index b46fdf5dfb..fc8cc3b090 100644 --- a/resources/variants/ultimaker3_aa0.25.inst.cfg +++ b/resources/variants/ultimaker3_aa0.25.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker3_aa0.8.inst.cfg b/resources/variants/ultimaker3_aa0.8.inst.cfg index 56740233dd..308bed6fcb 100644 --- a/resources/variants/ultimaker3_aa0.8.inst.cfg +++ b/resources/variants/ultimaker3_aa0.8.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker3_aa04.inst.cfg b/resources/variants/ultimaker3_aa04.inst.cfg index ce91e89d26..25230cd30b 100644 --- a/resources/variants/ultimaker3_aa04.inst.cfg +++ b/resources/variants/ultimaker3_aa04.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker3_bb0.8.inst.cfg b/resources/variants/ultimaker3_bb0.8.inst.cfg index ace0bf3a94..5ccf2816ff 100644 --- a/resources/variants/ultimaker3_bb0.8.inst.cfg +++ b/resources/variants/ultimaker3_bb0.8.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker3_bb04.inst.cfg b/resources/variants/ultimaker3_bb04.inst.cfg index d571cabc9b..d919e5aab7 100644 --- a/resources/variants/ultimaker3_bb04.inst.cfg +++ b/resources/variants/ultimaker3_bb04.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker3 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker3_extended_aa0.25.inst.cfg b/resources/variants/ultimaker3_extended_aa0.25.inst.cfg index 714b017653..ce0f20fa7e 100644 --- a/resources/variants/ultimaker3_extended_aa0.25.inst.cfg +++ b/resources/variants/ultimaker3_extended_aa0.25.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker3_extended [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker3_extended_aa0.8.inst.cfg b/resources/variants/ultimaker3_extended_aa0.8.inst.cfg index f72c96b551..f209508875 100644 --- a/resources/variants/ultimaker3_extended_aa0.8.inst.cfg +++ b/resources/variants/ultimaker3_extended_aa0.8.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker3_extended [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker3_extended_aa04.inst.cfg b/resources/variants/ultimaker3_extended_aa04.inst.cfg index f354784fc6..714d19051f 100644 --- a/resources/variants/ultimaker3_extended_aa04.inst.cfg +++ b/resources/variants/ultimaker3_extended_aa04.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker3_extended [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker3_extended_bb0.8.inst.cfg b/resources/variants/ultimaker3_extended_bb0.8.inst.cfg index fe760c93b8..528c7f70ec 100644 --- a/resources/variants/ultimaker3_extended_bb0.8.inst.cfg +++ b/resources/variants/ultimaker3_extended_bb0.8.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker3_extended [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker3_extended_bb04.inst.cfg b/resources/variants/ultimaker3_extended_bb04.inst.cfg index 742dc9896e..5ee562ee38 100644 --- a/resources/variants/ultimaker3_extended_bb04.inst.cfg +++ b/resources/variants/ultimaker3_extended_bb04.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker3_extended [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker_s5_aa0.25.inst.cfg b/resources/variants/ultimaker_s5_aa0.25.inst.cfg index 643513faad..ebdb096b6f 100644 --- a/resources/variants/ultimaker_s5_aa0.25.inst.cfg +++ b/resources/variants/ultimaker_s5_aa0.25.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker_s5_aa0.8.inst.cfg b/resources/variants/ultimaker_s5_aa0.8.inst.cfg index eca8c400d0..d8ff1c020e 100644 --- a/resources/variants/ultimaker_s5_aa0.8.inst.cfg +++ b/resources/variants/ultimaker_s5_aa0.8.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker_s5_aa04.inst.cfg b/resources/variants/ultimaker_s5_aa04.inst.cfg index b5b694d0c1..ac377e3e78 100644 --- a/resources/variants/ultimaker_s5_aa04.inst.cfg +++ b/resources/variants/ultimaker_s5_aa04.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker_s5_aluminum.inst.cfg b/resources/variants/ultimaker_s5_aluminum.inst.cfg index 1018b7e5ab..ca457bd7e7 100644 --- a/resources/variants/ultimaker_s5_aluminum.inst.cfg +++ b/resources/variants/ultimaker_s5_aluminum.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = buildplate diff --git a/resources/variants/ultimaker_s5_bb0.8.inst.cfg b/resources/variants/ultimaker_s5_bb0.8.inst.cfg index c1c5c1a10b..bb7f2d1420 100644 --- a/resources/variants/ultimaker_s5_bb0.8.inst.cfg +++ b/resources/variants/ultimaker_s5_bb0.8.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker_s5_bb04.inst.cfg b/resources/variants/ultimaker_s5_bb04.inst.cfg index b5ff8d51f6..cda1036507 100644 --- a/resources/variants/ultimaker_s5_bb04.inst.cfg +++ b/resources/variants/ultimaker_s5_bb04.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker_s5_cc06.inst.cfg b/resources/variants/ultimaker_s5_cc06.inst.cfg index 7adf7ab7a0..afbeb44462 100644 --- a/resources/variants/ultimaker_s5_cc06.inst.cfg +++ b/resources/variants/ultimaker_s5_cc06.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = nozzle diff --git a/resources/variants/ultimaker_s5_glass.inst.cfg b/resources/variants/ultimaker_s5_glass.inst.cfg index d74eb3c6c9..e7431c933f 100644 --- a/resources/variants/ultimaker_s5_glass.inst.cfg +++ b/resources/variants/ultimaker_s5_glass.inst.cfg @@ -4,7 +4,7 @@ version = 4 definition = ultimaker_s5 [metadata] -setting_version = 5 +setting_version = 6 type = variant hardware_type = buildplate From a8128919afe233adc5539791028d08cacb22f92d Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 7 Jan 2019 10:41:03 +0100 Subject: [PATCH 1170/1240] Make resize bar slim CURA-6054 --- .../PrintSetupSelectorContents.qml | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml index 77b6cd9b63..0b8fb89311 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml @@ -180,16 +180,9 @@ Item UM.RecolorImage { width: parent.width * 0.05 - sourceSize.height: height - sourceSize.width: width - anchors - { - horizontalCenter: parent.horizontalCenter - top: parent.top - topMargin: UM.Theme.getSize("thick_lining").height - bottom: parent.bottom - bottomMargin: UM.Theme.getSize("thick_lining").height - } + height: parent.height * 0.3 + + anchors.centerIn: parent source: UM.Theme.getIcon("grip_lines") color: UM.Theme.getColor("lining") From 059fede06e7e5e06971cf59d8f34acf86b4562d1 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 7 Jan 2019 10:53:18 +0100 Subject: [PATCH 1171/1240] Mkae default custom settings panel height smaller CURA-6054 --- cura/CuraApplication.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 4d1c530237..f927e64044 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -499,7 +499,7 @@ class CuraApplication(QtApplication): preferences.addPreference("cura/choice_on_profile_override", "always_ask") preferences.addPreference("cura/choice_on_open_project", "always_ask") preferences.addPreference("cura/use_multi_build_plate", False) - preferences.addPreference("view/settings_list_height", 600) + preferences.addPreference("view/settings_list_height", 400) preferences.addPreference("view/settings_visible", False) preferences.addPreference("cura/currency", "€") preferences.addPreference("cura/material_settings", "{}") From a43900b0adea19c85222793f68290f06d79fe814 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 7 Jan 2019 11:17:08 +0100 Subject: [PATCH 1172/1240] Fix version upgrade for XMLMaterialProfile CURA-5995 --- plugins/XmlMaterialProfile/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/XmlMaterialProfile/__init__.py b/plugins/XmlMaterialProfile/__init__.py index e8bde78424..c50df69516 100644 --- a/plugins/XmlMaterialProfile/__init__.py +++ b/plugins/XmlMaterialProfile/__init__.py @@ -16,7 +16,7 @@ def getMetaData(): "mimetype": "application/x-ultimaker-material-profile" }, "version_upgrade": { - ("materials", 1000000): ("materials", 1000004, upgrader.upgradeMaterial), + ("materials", 1000000): ("materials", 1000006, upgrader.upgradeMaterial), }, "sources": { "materials": { From d4b0c157aab37e57e3dc2b785eea72f142abaf1c Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 7 Jan 2019 13:18:28 +0100 Subject: [PATCH 1173/1240] Fix layers.shader to check feature prime_tower CURA-5932 --- plugins/SimulationView/layers.shader | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/SimulationView/layers.shader b/plugins/SimulationView/layers.shader index 30f23a3189..69c7c61ee5 100644 --- a/plugins/SimulationView/layers.shader +++ b/plugins/SimulationView/layers.shader @@ -49,12 +49,13 @@ fragment = // discard movements discard; } - // support: 4, 5, 7, 10 + // support: 4, 5, 7, 10, 11 (prime tower) if ((u_show_helpers == 0) && ( ((v_line_type >= 3.5) && (v_line_type <= 4.5)) || + ((v_line_type >= 4.5) && (v_line_type <= 5.5)) || ((v_line_type >= 6.5) && (v_line_type <= 7.5)) || ((v_line_type >= 9.5) && (v_line_type <= 10.5)) || - ((v_line_type >= 4.5) && (v_line_type <= 5.5)) + ((v_line_type >= 10.5) && (v_line_type <= 11.5)) )) { discard; } From 4dbab887925dab91001e33ff2dd9bd5a2773f46b Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Mon, 7 Jan 2019 13:56:04 +0100 Subject: [PATCH 1174/1240] Add dummy printer for loading --- plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml index d20c2247b4..fe7892840a 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml @@ -67,7 +67,7 @@ Component MonitorCarousel { id: carousel - printers: OutputDevice.printers + printers: OutputDevice.printers.concat([null]) } } From af634b82b25fdd765a134288b101588ed2fdc22c Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 8 Jan 2019 09:39:46 +0100 Subject: [PATCH 1175/1240] Chnage default font sizes and unify font weights CURA-6085 --- resources/themes/cura-light/theme.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 42ef632673..c277ee7f6c 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -6,7 +6,7 @@ "fonts": { "large": { "size": 1.35, - "weight": 40, + "weight": 50, "family": "Noto Sans" }, "large_bold": { @@ -16,7 +16,7 @@ }, "medium": { "size": 1.16, - "weight": 40, + "weight": 50, "family": "Noto Sans" }, "medium_bold": { @@ -25,24 +25,24 @@ "family": "Noto Sans" }, "default": { - "size": 0.95, - "weight": 40, + "size": 1.0, + "weight": 50, "family": "Noto Sans" }, "default_bold": { - "size": 0.95, + "size": 1.0, "weight": 63, "family": "Noto Sans" }, "default_italic": { - "size": 0.95, - "weight": 40, + "size": 1.0, + "weight": 50, "italic": true, "family": "Noto Sans" }, "small": { "size": 0.7, - "weight": 40, + "weight": 50, "family": "Noto Sans" } }, From 8e898148f619b1580a63a5b17d2728d545bfe210 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 8 Jan 2019 09:42:50 +0100 Subject: [PATCH 1176/1240] Make checkboxes and texts more aligned CURA-6085 --- plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml b/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml index 333d4dd50a..f50c3f3ac6 100644 --- a/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxInstalledTile.qml @@ -30,6 +30,7 @@ Item CheckBox { id: disableButton + anchors.verticalCenter: pluginInfo.verticalCenter checked: isEnabled visible: model.type == "plugin" width: visible ? UM.Theme.getSize("checkbox").width : 0 From 2124de3d4dcfb69c7b7a61297ba48b153021034f Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 8 Jan 2019 10:45:52 +0100 Subject: [PATCH 1177/1240] Revert "Chnage default font sizes and unify font weights" This reverts commit af634b82b25fdd765a134288b101588ed2fdc22c. CURA-6085 --- resources/themes/cura-light/theme.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index c277ee7f6c..42ef632673 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -6,7 +6,7 @@ "fonts": { "large": { "size": 1.35, - "weight": 50, + "weight": 40, "family": "Noto Sans" }, "large_bold": { @@ -16,7 +16,7 @@ }, "medium": { "size": 1.16, - "weight": 50, + "weight": 40, "family": "Noto Sans" }, "medium_bold": { @@ -25,24 +25,24 @@ "family": "Noto Sans" }, "default": { - "size": 1.0, - "weight": 50, + "size": 0.95, + "weight": 40, "family": "Noto Sans" }, "default_bold": { - "size": 1.0, + "size": 0.95, "weight": 63, "family": "Noto Sans" }, "default_italic": { - "size": 1.0, - "weight": 50, + "size": 0.95, + "weight": 40, "italic": true, "family": "Noto Sans" }, "small": { "size": 0.7, - "weight": 50, + "weight": 40, "family": "Noto Sans" } }, From 578027182e91c543c9937956e17430b37ef5800c Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 8 Jan 2019 10:49:55 +0100 Subject: [PATCH 1178/1240] Prevent setting items from overlapping with column headers --- resources/qml/Preferences/ProfilesPage.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/qml/Preferences/ProfilesPage.qml b/resources/qml/Preferences/ProfilesPage.qml index d9b679e344..f23a04d800 100644 --- a/resources/qml/Preferences/ProfilesPage.qml +++ b/resources/qml/Preferences/ProfilesPage.qml @@ -376,6 +376,7 @@ Item width: true ? (parent.width * 0.4) | 0 : parent.width frameVisible: true + clip: true ListView { From 4cb853cdb07d5785aeb21cacb048a7182f772218 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 8 Jan 2019 10:51:27 +0100 Subject: [PATCH 1179/1240] Fix (bigger) fonts for Chinese and Japanese CURA-6085 --- resources/themes/cura-light/theme.json | 93 ++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 42ef632673..ea1c64e2ac 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -9,6 +9,21 @@ "weight": 40, "family": "Noto Sans" }, + "large_ja_JP": { + "size": 1.35, + "weight": 50, + "family": "Noto Sans" + }, + "large_zh_CN": { + "size": 1.35, + "weight": 50, + "family": "Noto Sans" + }, + "large_zh_TW": { + "size": 1.35, + "weight": 50, + "family": "Noto Sans" + }, "large_bold": { "size": 1.35, "weight": 63, @@ -19,6 +34,21 @@ "weight": 40, "family": "Noto Sans" }, + "medium_ja_JP": { + "size": 1.16, + "weight": 50, + "family": "Noto Sans" + }, + "medium_zh_CN": { + "size": 1.16, + "weight": 50, + "family": "Noto Sans" + }, + "medium_zh_TW": { + "size": 1.16, + "weight": 50, + "family": "Noto Sans" + }, "medium_bold": { "size": 1.16, "weight": 63, @@ -29,21 +59,84 @@ "weight": 40, "family": "Noto Sans" }, + "default_ja_JP": { + "size": 1.0, + "weight": 50, + "family": "Noto Sans" + }, + "default_zh_CN": { + "size": 1.0, + "weight": 50, + "family": "Noto Sans" + }, + "default_zh_TW": { + "size": 1.0, + "weight": 50, + "family": "Noto Sans" + }, "default_bold": { "size": 0.95, "weight": 63, "family": "Noto Sans" }, + "default_bold_ja_JP": { + "size": 1.0, + "weight": 63, + "family": "Noto Sans" + }, + "default_bold_zh_CN": { + "size": 1.0, + "weight": 63, + "family": "Noto Sans" + }, + "default_bold_zh_TW": { + "size": 1.0, + "weight": 63, + "family": "Noto Sans" + }, "default_italic": { "size": 0.95, "weight": 40, "italic": true, "family": "Noto Sans" }, + "default_italic_ja_JP": { + "size": 1.0, + "weight": 50, + "italic": true, + "family": "Noto Sans" + }, + "default_italic_zh_CN": { + "size": 1.0, + "weight": 50, + "italic": true, + "family": "Noto Sans" + }, + "default_italic_zh_TW": { + "size": 1.0, + "weight": 50, + "italic": true, + "family": "Noto Sans" + }, "small": { "size": 0.7, "weight": 40, "family": "Noto Sans" + }, + "small_ja_JP": { + "size": 0.7, + "weight": 50, + "family": "Noto Sans" + }, + "small_zh_CN": { + "size": 0.7, + "weight": 50, + "family": "Noto Sans" + }, + "small_zh_TW": { + "size": 0.7, + "weight": 50, + "family": "Noto Sans" } }, From 7c7bca31c75432dca58f14c2e1db8a6672753931 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 8 Jan 2019 11:52:27 +0100 Subject: [PATCH 1180/1240] Fix renamed property connectionType --- plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py index 5f6b6b15bf..4a510903dd 100644 --- a/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py +++ b/plugins/UM3NetworkPrinting/src/UM3OutputDevicePlugin.py @@ -118,7 +118,7 @@ class UM3OutputDevicePlugin(OutputDevicePlugin): if key == um_network_key: if not self._discovered_devices[key].isConnected(): Logger.log("d", "Attempting to connect with [%s]" % key) - active_machine.setMetaDataEntry("connection_type", self._discovered_devices[key].getConnectionType().value) + active_machine.setMetaDataEntry("connection_type", self._discovered_devices[key].connectionType.value) self._discovered_devices[key].connect() self._discovered_devices[key].connectionStateChanged.connect(self._onDeviceConnectionStateChanged) else: From 485b471522968e1099cee3aa1e0055f01cd5dbec Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 8 Jan 2019 12:19:00 +0100 Subject: [PATCH 1181/1240] Fix some review comments --- .../NetworkedPrinterOutputDevice.py | 4 +- .../src/Cloud/CloudApiClient.py | 35 ++++++------ .../src/Cloud/CloudOutputDevice.py | 55 +++++-------------- .../src/Cloud/CloudOutputDeviceManager.py | 4 +- .../{CloudErrorObject.py => CloudError.py} | 2 +- .../src/Cloud/Translations.py | 31 +++++++++++ .../tests/Cloud/TestCloudApiClient.py | 4 +- 7 files changed, 73 insertions(+), 62 deletions(-) rename plugins/UM3NetworkPrinting/src/Cloud/Models/{CloudErrorObject.py => CloudError.py} (97%) create mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Translations.py diff --git a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py index 3dcc43dd00..4a8aa0a6b2 100644 --- a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py +++ b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py @@ -18,6 +18,7 @@ from enum import IntEnum import os # To get the username import gzip + class AuthState(IntEnum): NotAuthenticated = 1 AuthenticationRequested = 2 @@ -207,7 +208,8 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): self._last_request_time = time() if not self._manager: - return Logger.log("e", "No network manager was created to execute the PUT call with.") + Logger.log("e", "No network manager was created to execute the PUT call with.") + return body = data if isinstance(data, bytes) else data.encode() # type: bytes reply = self._manager.put(request, body) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py index 302ca86d32..8f10d02802 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py @@ -14,13 +14,17 @@ from cura.API import Account from .MeshUploader import MeshUploader from ..Models import BaseModel from .Models.CloudClusterResponse import CloudClusterResponse -from .Models.CloudErrorObject import CloudErrorObject +from .Models.CloudError import CloudError from .Models.CloudClusterStatus import CloudClusterStatus from .Models.CloudPrintJobUploadRequest import CloudPrintJobUploadRequest from .Models.CloudPrintResponse import CloudPrintResponse from .Models.CloudPrintJobResponse import CloudPrintJobResponse +## The generic type variable used to document the methods below. +CloudApiClientModel = TypeVar("Model", bound = BaseModel) + + ## The cloud API client is responsible for handling the requests and responses from the cloud. # Each method should only handle models instead of exposing Any HTTP details. class CloudApiClient: @@ -33,7 +37,7 @@ class CloudApiClient: ## Initializes a new cloud API client. # \param account: The user's account object # \param on_error: The callback to be called whenever we receive errors from the server. - def __init__(self, account: Account, on_error: Callable[[List[CloudErrorObject]], None]) -> None: + def __init__(self, account: Account, on_error: Callable[[List[CloudError]], None]) -> None: super().__init__() self._manager = QNetworkAccessManager() self._account = account @@ -115,33 +119,31 @@ class CloudApiClient: # Logger.log("i", "Received a reply %s from %s with %s", status_code, reply.url().toString(), response) return status_code, json.loads(response) except (UnicodeDecodeError, JSONDecodeError, ValueError) as err: - error = CloudErrorObject(code=type(err).__name__, title=str(err), http_code=str(status_code), - id=str(time()), http_status="500") + error = CloudError(code=type(err).__name__, title=str(err), http_code=str(status_code), + id=str(time()), http_status="500") Logger.logException("e", "Could not parse the stardust response: %s", error) return status_code, {"errors": [error.toDict()]} - ## The generic type variable used to document the methods below. - Model = TypeVar("Model", bound=BaseModel) - ## Parses the given models and calls the correct callback depending on the result. # \param response: The response from the server, after being converted to a dict. # \param on_finished: The callback in case the response is successful. # \param model_class: The type of the model to convert the response to. It may either be a single record or a list. def _parseModels(self, response: Dict[str, Any], - on_finished: Union[Callable[[Model], Any], Callable[[List[Model]], Any]], - model_class: Type[Model]) -> None: + on_finished: Union[Callable[[CloudApiClientModel], Any], + Callable[[List[CloudApiClientModel]], Any]], + model_class: Type[CloudApiClientModel]) -> None: if "data" in response: data = response["data"] if isinstance(data, list): - results = [model_class(**c) for c in data] # type: List[CloudApiClient.Model] - on_finished_list = cast(Callable[[List[CloudApiClient.Model]], Any], on_finished) + results = [model_class(**c) for c in data] # type: List[CloudApiClientModel] + on_finished_list = cast(Callable[[List[CloudApiClientModel]], Any], on_finished) on_finished_list(results) else: - result = model_class(**data) # type: CloudApiClient.Model - on_finished_item = cast(Callable[[CloudApiClient.Model], Any], on_finished) + result = model_class(**data) # type: CloudApiClientModel + on_finished_item = cast(Callable[[CloudApiClientModel], Any], on_finished) on_finished_item(result) elif "errors" in response: - self._on_error([CloudErrorObject(**error) for error in response["errors"]]) + self._on_error([CloudError(**error) for error in response["errors"]]) else: Logger.log("e", "Cannot find data or errors in the cloud response: %s", response) @@ -153,8 +155,9 @@ class CloudApiClient: # \param model: The type of the model to convert the response to. def _addCallback(self, reply: QNetworkReply, - on_finished: Union[Callable[[Model], Any], Callable[[List[Model]], Any]], - model: Type[Model], + on_finished: Union[Callable[[CloudApiClientModel], Any], + Callable[[List[CloudApiClientModel]], Any]], + model: Type[CloudApiClientModel], ) -> None: def parse() -> None: status_code, response = self._parseReply(reply) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 093aa05ea9..e866303d27 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -7,7 +7,6 @@ from typing import Dict, List, Optional, Set, cast from PyQt5.QtCore import QObject, QUrl, pyqtProperty, pyqtSignal, pyqtSlot -from UM import i18nCatalog from UM.Backend.Backend import BackendState from UM.FileHandler.FileHandler import FileHandler from UM.Logger import Logger @@ -18,7 +17,8 @@ from cura.CuraApplication import CuraApplication from cura.PrinterOutput.NetworkedPrinterOutputDevice import AuthState, NetworkedPrinterOutputDevice from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel from cura.PrinterOutputDevice import ConnectionType -from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputController import CloudOutputController + +from .CloudOutputController import CloudOutputController from ..MeshFormatHandler import MeshFormatHandler from ..UM3PrintJobOutputModel import UM3PrintJobOutputModel from .CloudProgressMessage import CloudProgressMessage @@ -30,37 +30,10 @@ from .Models.CloudPrintResponse import CloudPrintResponse from .Models.CloudPrintJobResponse import CloudPrintJobResponse from .Models.CloudClusterPrinterStatus import CloudClusterPrinterStatus from .Models.CloudClusterPrintJobStatus import CloudClusterPrintJobStatus +from .Translations import Translations from .Utils import findChanges, formatDateCompleted, formatTimeCompleted -## Class that contains all the translations for this module. -class T: - # The translation catalog for this device. - - _I18N_CATALOG = i18nCatalog("cura") - - PRINT_VIA_CLOUD_BUTTON = _I18N_CATALOG.i18nc("@action:button", "Print via Cloud") - PRINT_VIA_CLOUD_TOOLTIP = _I18N_CATALOG.i18nc("@properties:tooltip", "Print via Cloud") - - CONNECTED_VIA_CLOUD = _I18N_CATALOG.i18nc("@info:status", "Connected via Cloud") - BLOCKED_UPLOADING = _I18N_CATALOG.i18nc("@info:status", "Sending new jobs (temporarily) blocked, still sending " - "the previous print job.") - - COULD_NOT_EXPORT = _I18N_CATALOG.i18nc("@info:status", "Could not export print job.") - - ERROR = _I18N_CATALOG.i18nc("@info:title", "Error") - UPLOAD_ERROR = _I18N_CATALOG.i18nc("@info:text", "Could not upload the data to the printer.") - - UPLOAD_SUCCESS_TITLE = _I18N_CATALOG.i18nc("@info:title", "Data Sent") - UPLOAD_SUCCESS_TEXT = _I18N_CATALOG.i18nc("@info:status", "Print job was successfully sent to the printer.") - - JOB_COMPLETED_TITLE = _I18N_CATALOG.i18nc("@info:status", "Print finished") - JOB_COMPLETED_PRINTER = _I18N_CATALOG.i18nc("@info:status", - "Printer '{printer_name}' has finished printing '{job_name}'.") - - JOB_COMPLETED_NO_PRINTER = _I18N_CATALOG.i18nc("@info:status", "The print job '{job_name}' was finished.") - - ## The cloud output device is a network output device that works remotely but has limited functionality. # Currently it only supports viewing the printer and print job status and adding a new job to the queue. # As such, those methods have been implemented here. @@ -159,9 +132,9 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def _setInterfaceElements(self) -> None: self.setPriority(2) # make sure we end up below the local networking and above 'save to file' self.setName(self._id) - self.setShortDescription(T.PRINT_VIA_CLOUD_BUTTON) - self.setDescription(T.PRINT_VIA_CLOUD_TOOLTIP) - self.setConnectionText(T.CONNECTED_VIA_CLOUD) + self.setShortDescription(Translations.PRINT_VIA_CLOUD_BUTTON) + self.setDescription(Translations.PRINT_VIA_CLOUD_TOOLTIP) + self.setConnectionText(Translations.CONNECTED_VIA_CLOUD) ## Called when Cura requests an output device to receive a (G-code) file. def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False, @@ -169,7 +142,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # Show an error message if we're already sending a job. if self._progress.visible: - message = Message(text = T.BLOCKED_UPLOADING, title = T.ERROR, lifetime = 10) + message = Message(text = Translations.BLOCKED_UPLOADING, title = Translations.ERROR, lifetime = 10) message.show() return @@ -184,7 +157,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): mesh_format = MeshFormatHandler(file_handler, self.firmwareVersion) if not mesh_format.is_valid: Logger.log("e", "Missing file or mesh writer!") - return self._onUploadError(T.COULD_NOT_EXPORT) + return self._onUploadError(Translations.COULD_NOT_EXPORT) mesh = mesh_format.getBytes(nodes) @@ -292,9 +265,11 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): if job.state == "wait_cleanup" and job.key not in self._finished_jobs and job.owner == user_name: self._finished_jobs.add(job.key) Message( - title = T.JOB_COMPLETED_TITLE, - text = (T.JOB_COMPLETED_PRINTER.format(printer_name=job.assignedPrinter.name, job_name=job.name) - if job.assignedPrinter else T.JOB_COMPLETED_NO_PRINTER.format(job_name=job.name)), + title = Translations.JOB_COMPLETED_TITLE, + text = (Translations.JOB_COMPLETED_PRINTER.format(printer_name=job.assignedPrinter.name, + job_name=job.name) + if job.assignedPrinter else + Translations.JOB_COMPLETED_NO_PRINTER.format(job_name=job.name)), ).show() # Ensure UI gets updated @@ -330,7 +305,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def _onUploadError(self, message = None) -> None: self._progress.hide() self._uploaded_print_job = None - Message(text = message or T.UPLOAD_ERROR, title = T.ERROR, lifetime = 10).show() + Message(text = message or Translations.UPLOAD_ERROR, title = Translations.ERROR, lifetime = 10).show() self.writeError.emit() ## Shows a message when the upload has succeeded @@ -338,7 +313,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def _onPrintRequested(self, response: CloudPrintResponse) -> None: Logger.log("d", "The cluster will be printing this print job with the ID %s", response.cluster_job_id) self._progress.hide() - Message(text = T.UPLOAD_SUCCESS_TEXT, title = T.UPLOAD_SUCCESS_TITLE, lifetime = 5).show() + Message(text = Translations.UPLOAD_SUCCESS_TEXT, title = Translations.UPLOAD_SUCCESS_TITLE, lifetime = 5).show() self.writeFinished.emit() ## Gets the remote printers. diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index b1dc13e34f..72ac34ff34 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -13,7 +13,7 @@ from cura.Settings.GlobalStack import GlobalStack from .CloudApiClient import CloudApiClient from .CloudOutputDevice import CloudOutputDevice from .Models.CloudClusterResponse import CloudClusterResponse -from .Models.CloudErrorObject import CloudErrorObject +from .Models.CloudError import CloudError from .Utils import findChanges @@ -138,7 +138,7 @@ class CloudOutputDeviceManager: ## Handles an API error received from the cloud. # \param errors: The errors received - def _onApiError(self, errors: List[CloudErrorObject]) -> None: + def _onApiError(self, errors: List[CloudError]) -> None: text = ". ".join(e.title for e in errors) # TODO: translate errors message = Message( text = text, diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudErrorObject.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudError.py similarity index 97% rename from plugins/UM3NetworkPrinting/src/Cloud/Models/CloudErrorObject.py rename to plugins/UM3NetworkPrinting/src/Cloud/Models/CloudError.py index 28b4d916a1..b53361022e 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudErrorObject.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudError.py @@ -7,7 +7,7 @@ from .BaseCloudModel import BaseCloudModel ## Class representing errors generated by the cloud servers, according to the JSON-API standard. # Spec: https://api-staging.ultimaker.com/connect/v1/spec -class CloudErrorObject(BaseCloudModel): +class CloudError(BaseCloudModel): ## Creates a new error object. # \param id: Unique identifier for this particular occurrence of the problem. # \param title: A short, human-readable summary of the problem that SHOULD NOT change from occurrence to occurrence diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Translations.py b/plugins/UM3NetworkPrinting/src/Cloud/Translations.py new file mode 100644 index 0000000000..278bf91c37 --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/Translations.py @@ -0,0 +1,31 @@ +# Copyright (c) 2018 Ultimaker B.V. +# Cura is released under the terms of the LGPLv3 or higher. +from UM import i18nCatalog + + +## Class that contains all the translations for this module. +class Translations: + # The translation catalog for this device. + + _I18N_CATALOG = i18nCatalog("cura") + + PRINT_VIA_CLOUD_BUTTON = _I18N_CATALOG.i18nc("@action:button", "Print via Cloud") + PRINT_VIA_CLOUD_TOOLTIP = _I18N_CATALOG.i18nc("@properties:tooltip", "Print via Cloud") + + CONNECTED_VIA_CLOUD = _I18N_CATALOG.i18nc("@info:status", "Connected via Cloud") + BLOCKED_UPLOADING = _I18N_CATALOG.i18nc("@info:status", "Sending new jobs (temporarily) blocked, still sending " + "the previous print job.") + + COULD_NOT_EXPORT = _I18N_CATALOG.i18nc("@info:status", "Could not export print job.") + + ERROR = _I18N_CATALOG.i18nc("@info:title", "Error") + UPLOAD_ERROR = _I18N_CATALOG.i18nc("@info:text", "Could not upload the data to the printer.") + + UPLOAD_SUCCESS_TITLE = _I18N_CATALOG.i18nc("@info:title", "Data Sent") + UPLOAD_SUCCESS_TEXT = _I18N_CATALOG.i18nc("@info:status", "Print job was successfully sent to the printer.") + + JOB_COMPLETED_TITLE = _I18N_CATALOG.i18nc("@info:status", "Print finished") + JOB_COMPLETED_PRINTER = _I18N_CATALOG.i18nc("@info:status", + "Printer '{printer_name}' has finished printing '{job_name}'.") + + JOB_COMPLETED_NO_PRINTER = _I18N_CATALOG.i18nc("@info:status", "The print job '{job_name}' was finished.") \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py index 0c0c8cffdf..b57334b2da 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py @@ -12,7 +12,7 @@ from src.Cloud.Models.CloudClusterResponse import CloudClusterResponse from src.Cloud.Models.CloudClusterStatus import CloudClusterStatus from src.Cloud.Models.CloudPrintJobResponse import CloudPrintJobResponse from src.Cloud.Models.CloudPrintJobUploadRequest import CloudPrintJobUploadRequest -from src.Cloud.Models.CloudErrorObject import CloudErrorObject +from src.Cloud.Models.CloudError import CloudError from tests.Cloud.Fixtures import readFixture, parseFixture from .NetworkManagerMock import NetworkManagerMock @@ -20,7 +20,7 @@ from .NetworkManagerMock import NetworkManagerMock class TestCloudApiClient(TestCase): maxDiff = None - def _errorHandler(self, errors: List[CloudErrorObject]): + def _errorHandler(self, errors: List[CloudError]): raise Exception("Received unexpected error: {}".format(errors)) def setUp(self): From b8da720c1d528b3a7becfda00cf006543bc162c8 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 8 Jan 2019 13:10:42 +0100 Subject: [PATCH 1182/1240] Move translations in-line --- .../src/Cloud/CloudOutputDevice.py | 44 +++++++++++++------ .../src/Cloud/Translations.py | 31 ------------- 2 files changed, 31 insertions(+), 44 deletions(-) delete mode 100644 plugins/UM3NetworkPrinting/src/Cloud/Translations.py diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index e866303d27..f3c4830e24 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -7,6 +7,7 @@ from typing import Dict, List, Optional, Set, cast from PyQt5.QtCore import QObject, QUrl, pyqtProperty, pyqtSignal, pyqtSlot +from UM import i18nCatalog from UM.Backend.Backend import BackendState from UM.FileHandler.FileHandler import FileHandler from UM.Logger import Logger @@ -30,10 +31,12 @@ from .Models.CloudPrintResponse import CloudPrintResponse from .Models.CloudPrintJobResponse import CloudPrintJobResponse from .Models.CloudClusterPrinterStatus import CloudClusterPrinterStatus from .Models.CloudClusterPrintJobStatus import CloudClusterPrintJobStatus -from .Translations import Translations from .Utils import findChanges, formatDateCompleted, formatTimeCompleted +I18N_CATALOG = i18nCatalog("cura") + + ## The cloud output device is a network output device that works remotely but has limited functionality. # Currently it only supports viewing the printer and print job status and adding a new job to the queue. # As such, those methods have been implemented here. @@ -132,9 +135,9 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def _setInterfaceElements(self) -> None: self.setPriority(2) # make sure we end up below the local networking and above 'save to file' self.setName(self._id) - self.setShortDescription(Translations.PRINT_VIA_CLOUD_BUTTON) - self.setDescription(Translations.PRINT_VIA_CLOUD_TOOLTIP) - self.setConnectionText(Translations.CONNECTED_VIA_CLOUD) + self.setShortDescription(I18N_CATALOG.i18nc("@action:button", "Print via Cloud")) + self.setDescription(I18N_CATALOG.i18nc("@properties:tooltip", "Print via Cloud")) + self.setConnectionText(I18N_CATALOG.i18nc("@info:status", "Connected via Cloud")) ## Called when Cura requests an output device to receive a (G-code) file. def requestWrite(self, nodes: List[SceneNode], file_name: Optional[str] = None, limit_mimetypes: bool = False, @@ -142,7 +145,11 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # Show an error message if we're already sending a job. if self._progress.visible: - message = Message(text = Translations.BLOCKED_UPLOADING, title = Translations.ERROR, lifetime = 10) + message = Message( + text = I18N_CATALOG.i18nc("@info:status", "Sending new jobs (temporarily) blocked, still sending the previous print job."), + title = I18N_CATALOG.i18nc("@info:title", "Cloud error"), + lifetime = 10 + ) message.show() return @@ -157,7 +164,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): mesh_format = MeshFormatHandler(file_handler, self.firmwareVersion) if not mesh_format.is_valid: Logger.log("e", "Missing file or mesh writer!") - return self._onUploadError(Translations.COULD_NOT_EXPORT) + return self._onUploadError(I18N_CATALOG.i18nc("@info:status", "Could not export print job.")) mesh = mesh_format.getBytes(nodes) @@ -265,11 +272,14 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): if job.state == "wait_cleanup" and job.key not in self._finished_jobs and job.owner == user_name: self._finished_jobs.add(job.key) Message( - title = Translations.JOB_COMPLETED_TITLE, - text = (Translations.JOB_COMPLETED_PRINTER.format(printer_name=job.assignedPrinter.name, - job_name=job.name) - if job.assignedPrinter else - Translations.JOB_COMPLETED_NO_PRINTER.format(job_name=job.name)), + title = I18N_CATALOG.i18nc("@info:status", "Print finished"), + text = (I18N_CATALOG.i18nc("@info:status", "Printer '{printer_name}' has finished printing '{job_name}'.").format( + printer_name = job.assignedPrinter.name, + job_name = job.name + ) if job.assignedPrinter else + I18N_CATALOG.i18nc("@info:status", "The print job '{job_name}' was finished.").format( + job_name = job.name + )), ).show() # Ensure UI gets updated @@ -305,7 +315,11 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def _onUploadError(self, message = None) -> None: self._progress.hide() self._uploaded_print_job = None - Message(text = message or Translations.UPLOAD_ERROR, title = Translations.ERROR, lifetime = 10).show() + Message( + text = message or I18N_CATALOG.i18nc("@info:text", "Could not upload the data to the printer."), + title = I18N_CATALOG.i18nc("@info:title", "Cloud error"), + lifetime = 10 + ).show() self.writeError.emit() ## Shows a message when the upload has succeeded @@ -313,7 +327,11 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def _onPrintRequested(self, response: CloudPrintResponse) -> None: Logger.log("d", "The cluster will be printing this print job with the ID %s", response.cluster_job_id) self._progress.hide() - Message(text = Translations.UPLOAD_SUCCESS_TEXT, title = Translations.UPLOAD_SUCCESS_TITLE, lifetime = 5).show() + Message( + text = I18N_CATALOG.i18nc("@info:status", "Print job was successfully sent to the printer."), + title = I18N_CATALOG.i18nc("@info:title", "Data Sent"), + lifetime = 5 + ).show() self.writeFinished.emit() ## Gets the remote printers. diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Translations.py b/plugins/UM3NetworkPrinting/src/Cloud/Translations.py deleted file mode 100644 index 278bf91c37..0000000000 --- a/plugins/UM3NetworkPrinting/src/Cloud/Translations.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (c) 2018 Ultimaker B.V. -# Cura is released under the terms of the LGPLv3 or higher. -from UM import i18nCatalog - - -## Class that contains all the translations for this module. -class Translations: - # The translation catalog for this device. - - _I18N_CATALOG = i18nCatalog("cura") - - PRINT_VIA_CLOUD_BUTTON = _I18N_CATALOG.i18nc("@action:button", "Print via Cloud") - PRINT_VIA_CLOUD_TOOLTIP = _I18N_CATALOG.i18nc("@properties:tooltip", "Print via Cloud") - - CONNECTED_VIA_CLOUD = _I18N_CATALOG.i18nc("@info:status", "Connected via Cloud") - BLOCKED_UPLOADING = _I18N_CATALOG.i18nc("@info:status", "Sending new jobs (temporarily) blocked, still sending " - "the previous print job.") - - COULD_NOT_EXPORT = _I18N_CATALOG.i18nc("@info:status", "Could not export print job.") - - ERROR = _I18N_CATALOG.i18nc("@info:title", "Error") - UPLOAD_ERROR = _I18N_CATALOG.i18nc("@info:text", "Could not upload the data to the printer.") - - UPLOAD_SUCCESS_TITLE = _I18N_CATALOG.i18nc("@info:title", "Data Sent") - UPLOAD_SUCCESS_TEXT = _I18N_CATALOG.i18nc("@info:status", "Print job was successfully sent to the printer.") - - JOB_COMPLETED_TITLE = _I18N_CATALOG.i18nc("@info:status", "Print finished") - JOB_COMPLETED_PRINTER = _I18N_CATALOG.i18nc("@info:status", - "Printer '{printer_name}' has finished printing '{job_name}'.") - - JOB_COMPLETED_NO_PRINTER = _I18N_CATALOG.i18nc("@info:status", "The print job '{job_name}' was finished.") \ No newline at end of file From 53cb2ce1891066c0437effb7a961a1c982f320f2 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 8 Jan 2019 13:29:45 +0100 Subject: [PATCH 1183/1240] Add comments for http status >=300 check CURA-6005 --- plugins/CuraDrive/src/DriveApiService.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/CuraDrive/src/DriveApiService.py b/plugins/CuraDrive/src/DriveApiService.py index 23e70a978c..7c1f8faa83 100644 --- a/plugins/CuraDrive/src/DriveApiService.py +++ b/plugins/CuraDrive/src/DriveApiService.py @@ -45,6 +45,8 @@ class DriveApiService: "Authorization": "Bearer {}".format(access_token) }) + # HTTP status 300s mean redirection. 400s and 500s are errors. + # Technically 300s are not errors, but the use case here relies on "requests" to handle redirects automatically. if backup_list_request.status_code >= 300: Logger.log("w", "Could not get backups list from remote: %s", backup_list_request.text) Message(catalog.i18nc("@info:backup_status", "There was an error listing your backups."), title = catalog.i18nc("@info:title", "Backup")).show() From a8fe5ced89c92bc7a8362fe280b0c1b5e62cc14e Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 8 Jan 2019 13:33:46 +0100 Subject: [PATCH 1184/1240] Remove unused import CURA-6005 --- plugins/CuraDrive/src/Settings.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/CuraDrive/src/Settings.py b/plugins/CuraDrive/src/Settings.py index c5383555b2..abe64e0acd 100644 --- a/plugins/CuraDrive/src/Settings.py +++ b/plugins/CuraDrive/src/Settings.py @@ -1,8 +1,6 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from UM import i18nCatalog - from cura import UltimakerCloudAuthentication @@ -12,4 +10,4 @@ class Settings: DRIVE_API_URL = "{}/cura-drive/v{}".format(UltimakerCloudAuthentication.CuraCloudAPIRoot, str(DRIVE_API_VERSION)) AUTO_BACKUP_ENABLED_PREFERENCE_KEY = "cura_drive/auto_backup_enabled" - AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY = "cura_drive/auto_backup_date" \ No newline at end of file + AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY = "cura_drive/auto_backup_date" From e81742d296eab1d9d7ba81d3b9ea2ac5ce897445 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Tue, 8 Jan 2019 14:27:54 +0100 Subject: [PATCH 1185/1240] Add skeleton loading to printer cards Contributes to CL-1157 --- .../qml/MonitorBuildplateConfiguration.qml | 17 ++- .../resources/qml/MonitorCarousel.qml | 1 + .../qml/MonitorConfigOverrideDialog.qml | 2 +- .../qml/MonitorExtruderConfiguration.qml | 62 +++++--- .../resources/qml/MonitorIconExtruder.qml | 1 + .../resources/qml/MonitorPrintJobPreview.qml | 23 +-- .../qml/MonitorPrintJobProgressBar.qml | 8 +- .../resources/qml/MonitorPrinterCard.qml | 143 +++++++++++++----- .../qml/MonitorPrinterConfiguration.qml | 12 +- .../resources/qml/MonitorPrinterPill.qml | 7 +- 10 files changed, 192 insertions(+), 84 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml index 44bd47f904..192a5a7f76 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorBuildplateConfiguration.qml @@ -18,7 +18,7 @@ import UM 1.3 as UM Item { // The buildplate name - property alias buildplate: buildplateLabel.text + property var buildplate: null // Height is one 18px label/icon height: 18 * screenScaleFactor // TODO: Theme! @@ -34,7 +34,16 @@ Item Item { height: parent.height - width: 32 * screenScaleFactor // TODO: Theme! (Should be same as extruder icon width) + width: 32 * screenScaleFactor // Ensure the icon is centered under the extruder icon (same width) + + Rectangle + { + anchors.centerIn: parent + height: parent.height + width: height + color: buildplateIcon.visible > 0 ? "transparent" : "#eeeeee" // TODO: Theme! + radius: Math.floor(height / 2) + } UM.RecolorImage { @@ -44,6 +53,7 @@ Item height: parent.height source: "../svg/icons/buildplate.svg" width: height + visible: buildplate } } @@ -53,7 +63,8 @@ Item color: "#191919" // TODO: Theme! elide: Text.ElideRight font: UM.Theme.getFont("default") // 12pt, regular - text: "" + text: buildplate ? buildplate : "" + visible: text !== "" // FIXED-LINE-HEIGHT: height: 18 * screenScaleFactor // TODO: Theme! diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml index 7c0d9b95b6..de24ee5a8c 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorCarousel.qml @@ -230,6 +230,7 @@ Item topMargin: 36 * screenScaleFactor // TODO: Theme! } spacing: 8 * screenScaleFactor // TODO: Theme! + visible: printers.length > 1 Repeater { model: printers diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml index 6a32310dd5..1718994d83 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorConfigOverrideDialog.qml @@ -54,7 +54,7 @@ UM.Dialog wrapMode: Text.WordWrap text: { - if (!printer.activePrintJob) + if (!printer || !printer.activePrintJob) { return "" } diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorExtruderConfiguration.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorExtruderConfiguration.qml index 1e53191d8c..17c0fa8651 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorExtruderConfiguration.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorExtruderConfiguration.qml @@ -39,38 +39,62 @@ Item color: "#eeeeee" // TODO: Theme! position: 0 } - Label + + Rectangle { - id: materialLabel + id: materialLabelWrapper anchors { left: extruderIcon.right leftMargin: 12 * screenScaleFactor // TODO: Theme! } - color: "#191919" // TODO: Theme! - elide: Text.ElideRight - font: UM.Theme.getFont("default") // 12pt, regular - text: "" - - // FIXED-LINE-HEIGHT: + color: materialLabel.visible > 0 ? "transparent" : "#eeeeee" // TODO: Theme! height: 18 * screenScaleFactor // TODO: Theme! - verticalAlignment: Text.AlignVCenter + width: Math.max(materialLabel.contentWidth, 60 * screenScaleFactor) // TODO: Theme! + radius: 2 * screenScaleFactor // TODO: Theme! + + Label + { + id: materialLabel + + color: "#191919" // TODO: Theme! + elide: Text.ElideRight + font: UM.Theme.getFont("default") // 12pt, regular + text: "" + visible: text !== "" + + // FIXED-LINE-HEIGHT: + height: parent.height + verticalAlignment: Text.AlignVCenter + } } - Label + + Rectangle { - id: printCoreLabel + id: printCoreLabelWrapper anchors { - left: materialLabel.left + left: materialLabelWrapper.left bottom: parent.bottom } - color: "#191919" // TODO: Theme! - elide: Text.ElideRight - font: UM.Theme.getFont("default_bold") // 12pt, bold - text: "" - - // FIXED-LINE-HEIGHT: + color: printCoreLabel.visible > 0 ? "transparent" : "#eeeeee" // TODO: Theme! height: 18 * screenScaleFactor // TODO: Theme! - verticalAlignment: Text.AlignVCenter + width: Math.max(printCoreLabel.contentWidth, 36 * screenScaleFactor) // TODO: Theme! + radius: 2 * screenScaleFactor // TODO: Theme! + + Label + { + id: printCoreLabel + + color: "#191919" // TODO: Theme! + elide: Text.ElideRight + font: UM.Theme.getFont("default_bold") // 12pt, bold + text: "" + visible: text !== "" + + // FIXED-LINE-HEIGHT: + height: parent.height + verticalAlignment: Text.AlignVCenter + } } } \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorIconExtruder.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorIconExtruder.qml index 971c6b2251..93dbebc8c6 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorIconExtruder.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorIconExtruder.qml @@ -56,5 +56,6 @@ Item x: Math.round(size * 0.25) * screenScaleFactor y: Math.round(size * 0.15625) * screenScaleFactor // TODO: Once 'size' is themed, screenScaleFactor won't be needed + visible: position >= 0 } } \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml index 2f17db0c65..d0bad63258 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobPreview.qml @@ -16,23 +16,28 @@ Item width: size height: size - // Actual content - Image + Rectangle { - id: previewImage anchors.fill: parent - opacity: + color: printJob ? "transparent" : "#eeeeee" // TODO: Theme! + radius: 8 // TODO: Theme! + Image { - if (printJob && (printJob.state == "error" || printJob.configurationChanges.length > 0 || !printJob.isActive)) + id: previewImage + anchors.fill: parent + opacity: { - return 0.5 + if (printJob && (printJob.state == "error" || printJob.configurationChanges.length > 0 || !printJob.isActive)) + { + return 0.5 + } + return 1.0 } - return 1.0 + source: printJob ? printJob.previewImageUrl : "" } - source: printJob ? printJob.previewImageUrl : "" - visible: printJob } + UM.RecolorImage { id: ultiBotImage diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml index cfb7aba84d..d5d4705a36 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobProgressBar.qml @@ -34,16 +34,16 @@ Item { background: Rectangle { - color: printJob && printJob.isActive ? "#e4e4f2" : "#f3f3f9" // TODO: Theme! + color: "#f5f5f5" // TODO: Theme! implicitHeight: visible ? 8 * screenScaleFactor : 0 // TODO: Theme! implicitWidth: 180 * screenScaleFactor // TODO: Theme! - radius: 4 * screenScaleFactor // TODO: Theme! + radius: 2 * screenScaleFactor // TODO: Theme! } progress: Rectangle { id: progressItem; - color: printJob && printJob.isActive ? "#0a0850" : "#9392b2" // TODO: Theme! - radius: 4 * screenScaleFactor // TODO: Theme! + color: printJob && printJob.isActive ? "#3282ff" : "#CCCCCC" // TODO: Theme! + radius: 2 * screenScaleFactor // TODO: Theme! } } } diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml index b8c4353811..facfaaaaaf 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterCard.qml @@ -33,16 +33,24 @@ Item width: 834 * screenScaleFactor // TODO: Theme! height: childrenRect.height - // Printer portion Rectangle { - id: printerInfo + id: background + anchors.fill: parent + color: "#FFFFFF" // TODO: Theme! border { color: "#CCCCCC" // TODO: Theme! width: borderSize // TODO: Remove once themed } - color: "white" // TODO: Theme! + radius: 2 * screenScaleFactor // TODO: Theme! + } + + // Printer portion + Item + { + id: printerInfo + width: parent.width height: 144 * screenScaleFactor // TODO: Theme! @@ -56,15 +64,22 @@ Item } spacing: 18 * screenScaleFactor // TODO: Theme! - Image + Rectangle { id: printerImage width: 108 * screenScaleFactor // TODO: Theme! height: 108 * screenScaleFactor // TODO: Theme! - fillMode: Image.PreserveAspectFit - source: "../png/" + printer.type + ".png" - mipmap: true + color: printer ? "transparent" : "#eeeeee" // TODO: Theme! + radius: 8 // TODO: Theme! + Image + { + anchors.fill: parent + fillMode: Image.PreserveAspectFit + source: printer ? "../png/" + printer.type + ".png" : "" + mipmap: true + } } + Item { @@ -75,20 +90,38 @@ Item width: 180 * screenScaleFactor // TODO: Theme! height: printerNameLabel.height + printerFamilyPill.height + 6 * screenScaleFactor // TODO: Theme! - Label + Rectangle { id: printerNameLabel - text: printer && printer.name ? printer.name : "" - color: "#414054" // TODO: Theme! - elide: Text.ElideRight - font: UM.Theme.getFont("large_bold") // 16pt, bold - width: parent.width - - // FIXED-LINE-HEIGHT: + // color: "#414054" // TODO: Theme! + color: printer ? "transparent" : "#eeeeee" // TODO: Theme! height: 18 * screenScaleFactor // TODO: Theme! - verticalAlignment: Text.AlignVCenter + width: parent.width + radius: 2 * screenScaleFactor // TODO: Theme! + + Label + { + text: printer && printer.name ? printer.name : "" + color: "#414054" // TODO: Theme! + elide: Text.ElideRight + font: UM.Theme.getFont("large") // 16pt, bold + width: parent.width + visible: printer + + // FIXED-LINE-HEIGHT: + height: parent.height + verticalAlignment: Text.AlignVCenter + } } + Rectangle + { + color: "#eeeeee" // TODO: Theme! + height: 18 * screenScaleFactor // TODO: Theme! + radius: 2 * screenScaleFactor // TODO: Theme! + visible: !printer + width: 48 * screenScaleFactor // TODO: Theme! + } MonitorPrinterPill { id: printerFamilyPill @@ -98,7 +131,7 @@ Item topMargin: 6 * screenScaleFactor // TODO: Theme! left: printerNameLabel.left } - text: printer.type + text: printer ? printer.type : "" } } @@ -106,16 +139,30 @@ Item { id: printerConfiguration anchors.verticalCenter: parent.verticalCenter - buildplate: "Glass" + buildplate: printer ? "Glass" : null // 'Glass' as a default configurations: - [ - base.printer.printerConfiguration.extruderConfigurations[0], - base.printer.printerConfiguration.extruderConfigurations[1] - ] - height: 72 * screenScaleFactor // TODO: Theme! + { + var configs = [] + if (printer) + { + configs.push(printer.printerConfiguration.extruderConfigurations[0]) + configs.push(printer.printerConfiguration.extruderConfigurations[1]) + } + else + { + configs.push(null, null) + } + return configs + } + height: 72 * screenScaleFactor // TODO: Theme!te theRect's x property } + + // TODO: Make this work. + PropertyAnimation { target: printerConfiguration; property: "visible"; to: 0; loops: Animation.Infinite; duration: 500 } } + + PrintJobContextMenu { id: contextButton @@ -126,10 +173,11 @@ Item top: parent.top topMargin: 12 * screenScaleFactor // TODO: Theme! } - printJob: printer.activePrintJob + printJob: printer ? printer.activePrintJob : null width: 36 * screenScaleFactor // TODO: Theme! height: 36 * screenScaleFactor // TODO: Theme! enabled: base.enabled + visible: printer } CameraButton { @@ -143,10 +191,24 @@ Item } iconSource: "../svg/icons/camera.svg" enabled: base.enabled + visible: printer } } + // Divider + Rectangle + { + anchors + { + top: printJobInfo.top + left: printJobInfo.left + right: printJobInfo.right + } + height: borderSize // Remove once themed + color: background.border.color + } + // Print job portion Rectangle { @@ -158,10 +220,10 @@ Item } border { - color: printer.activePrintJob && printer.activePrintJob.configurationChanges.length > 0 ? "#f5a623" : "#CCCCCC" // TODO: Theme! + color: printer && printer.activePrintJob && printer.activePrintJob.configurationChanges.length > 0 ? "#f5a623" : "transparent" // TODO: Theme! width: borderSize // TODO: Remove once themed } - color: "white" // TODO: Theme! + color: "transparent" // TODO: Theme! height: 84 * screenScaleFactor + borderSize // TODO: Remove once themed width: parent.width @@ -184,9 +246,12 @@ Item { verticalCenter: parent.verticalCenter } - color: "#414054" // TODO: Theme! + color: printer ? "#414054" : "#aaaaaa" // TODO: Theme! font: UM.Theme.getFont("large_bold") // 16pt, bold text: { + if (!printer) { + return catalog.i18nc("@label:status", "Loading...") + } if (printer && printer.state == "disabled") { return catalog.i18nc("@label:status", "Unavailable") @@ -215,10 +280,10 @@ Item MonitorPrintJobPreview { anchors.centerIn: parent - printJob: base.printer.activePrintJob + printJob: printer ? printer.activePrintJob : null size: parent.height } - visible: printer.activePrintJob + visible: printer && printer.activePrintJob && !printerStatus.visible } Item @@ -229,15 +294,15 @@ Item } width: 180 * screenScaleFactor // TODO: Theme! height: printerNameLabel.height + printerFamilyPill.height + 6 * screenScaleFactor // TODO: Theme! - visible: printer.activePrintJob + visible: printer && printer.activePrintJob && !printerStatus.visible Label { id: printerJobNameLabel - color: printer.activePrintJob && printer.activePrintJob.isActive ? "#414054" : "#babac1" // TODO: Theme! + color: printer && printer.activePrintJob && printer.activePrintJob.isActive ? "#414054" : "#babac1" // TODO: Theme! elide: Text.ElideRight - font: UM.Theme.getFont("large_bold") // 16pt, bold - text: base.printer.activePrintJob ? base.printer.activePrintJob.name : "Untitled" // TODO: I18N + font: UM.Theme.getFont("large") // 16pt, bold + text: printer && printer.activePrintJob ? printer.activePrintJob.name : "Untitled" // TODO: I18N width: parent.width // FIXED-LINE-HEIGHT: @@ -254,10 +319,10 @@ Item topMargin: 6 * screenScaleFactor // TODO: Theme! left: printerJobNameLabel.left } - color: printer.activePrintJob && printer.activePrintJob.isActive ? "#53657d" : "#babac1" // TODO: Theme! + color: printer && printer.activePrintJob && printer.activePrintJob.isActive ? "#53657d" : "#babac1" // TODO: Theme! elide: Text.ElideRight font: UM.Theme.getFont("default") // 12pt, regular - text: printer.activePrintJob ? printer.activePrintJob.owner : "Anonymous" // TODO: I18N + text: printer && printer.activePrintJob ? printer.activePrintJob.owner : "Anonymous" // TODO: I18N width: parent.width // FIXED-LINE-HEIGHT: @@ -272,8 +337,8 @@ Item { verticalCenter: parent.verticalCenter } - printJob: printer.activePrintJob - visible: printer.activePrintJob && printer.activePrintJob.configurationChanges.length === 0 + printJob: printer && printer.activePrintJob + visible: printer && printer.activePrintJob && printer.activePrintJob.configurationChanges.length === 0 && !printerStatus.visible } Label @@ -284,7 +349,7 @@ Item } font: UM.Theme.getFont("default") text: "Requires configuration changes" - visible: printer.activePrintJob && printer.activePrintJob.configurationChanges.length > 0 + visible: printer && printer.activePrintJob && printer.activePrintJob.configurationChanges.length > 0 && !printerStatus.visible // FIXED-LINE-HEIGHT: height: 18 * screenScaleFactor // TODO: Theme! @@ -326,7 +391,7 @@ Item } implicitHeight: 32 * screenScaleFactor // TODO: Theme! implicitWidth: 96 * screenScaleFactor // TODO: Theme! - visible: printer.activePrintJob && printer.activePrintJob.configurationChanges.length > 0 + visible: printer && printer.activePrintJob && printer.activePrintJob.configurationChanges.length > 0 && !printerStatus.visible onClicked: base.enabled ? overrideConfirmationDialog.open() : {} } } diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterConfiguration.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterConfiguration.qml index 6aa11528de..78af227408 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterConfiguration.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterConfiguration.qml @@ -19,7 +19,7 @@ Item property alias buildplate: buildplateConfig.buildplate // Array of extracted extruder configurations - property var configurations: null + property var configurations: [null,null] // Default size, but should be stretched to fill parent height: 72 * parent.height @@ -37,10 +37,10 @@ Item MonitorExtruderConfiguration { - color: modelData.activeMaterial ? modelData.activeMaterial.color : "#eeeeee" // TODO: Theme! - material: modelData.activeMaterial ? modelData.activeMaterial.name : "" - position: modelData.position - printCore: modelData.hotendID + color: modelData && modelData.activeMaterial ? modelData.activeMaterial.color : "#eeeeee" // TODO: Theme! + material: modelData && modelData.activeMaterial ? modelData.activeMaterial.name : "" + position: modelData && modelData.position ? modelData.position : -1 // Use negative one to create empty extruder number + printCore: modelData ? modelData.hotendID : "" // Keep things responsive! width: Math.floor((base.width - (configurations.length - 1) * extruderConfigurationRow.spacing) / configurations.length) @@ -53,6 +53,6 @@ Item { id: buildplateConfig anchors.bottom: parent.bottom - buildplate: "Glass" // 'Glass' as a default + buildplate: null } } \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterPill.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterPill.qml index 80a089cc2a..2408089e1e 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterPill.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterPill.qml @@ -27,12 +27,12 @@ Item } implicitHeight: 18 * screenScaleFactor // TODO: Theme! - implicitWidth: printerNameLabel.contentWidth + 12 // TODO: Theme! + implicitWidth: Math.max(printerNameLabel.contentWidth + 12 * screenScaleFactor, 36 * screenScaleFactor) // TODO: Theme! Rectangle { id: background anchors.fill: parent - color: "#e4e4f2" // TODO: Theme! + color: printerNameLabel.visible ? "#e4e4f2" : "#eeeeee"// TODO: Theme! radius: 2 * screenScaleFactor // TODO: Theme! } @@ -41,6 +41,7 @@ Item anchors.centerIn: parent color: "#535369" // TODO: Theme! text: tagText - font.pointSize: 10 + font.pointSize: 10 // TODO: Theme! + visible: text !== "" } } \ No newline at end of file From 8f37b65ffecbb3d54ae4232d2439cf56ed8937f8 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Tue, 8 Jan 2019 15:15:21 +0100 Subject: [PATCH 1186/1240] Add skeleton loading to print job queue Contributes to CL-1157 --- .../resources/qml/ExpandableCard.qml | 11 ++- .../resources/qml/MonitorPrintJobCard.qml | 76 ++++++++++++++----- .../resources/qml/MonitorQueue.qml | 3 +- 3 files changed, 64 insertions(+), 26 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/ExpandableCard.qml b/plugins/UM3NetworkPrinting/resources/qml/ExpandableCard.qml index f86135ae62..d4c123652d 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/ExpandableCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/ExpandableCard.qml @@ -15,6 +15,7 @@ Item id: base property bool expanded: false + property bool enabled: true property var borderWidth: 1 property color borderColor: "#CCCCCC" property color headerBackgroundColor: "white" @@ -34,7 +35,7 @@ Item color: borderColor width: borderWidth } - color: headerMouseArea.containsMouse ? headerHoverColor : headerBackgroundColor + color: base.enabled && headerMouseArea.containsMouse ? headerHoverColor : headerBackgroundColor height: childrenRect.height width: parent.width Behavior on color @@ -50,8 +51,12 @@ Item { id: headerMouseArea anchors.fill: header - onClicked: base.expanded = !base.expanded - hoverEnabled: true + onClicked: + { + if (!base.enabled) return + base.expanded = !base.expanded + } + hoverEnabled: base.enabled } Rectangle diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml index f431ef1c52..f2b9c3cff7 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrintJobCard.qml @@ -26,6 +26,7 @@ Item ExpandableCard { + enabled: printJob != null borderColor: printJob.configurationChanges.length !== 0 ? "#f5a623" : "#CCCCCC" // TODO: Theme! headerItem: Row { @@ -41,32 +42,56 @@ Item anchors.verticalCenter: parent.verticalCenter } - Label + Item { - text: printJob && printJob.name ? printJob.name : "" - color: "#374355" - elide: Text.ElideRight - font: UM.Theme.getFont("medium") // 14pt, regular anchors.verticalCenter: parent.verticalCenter - width: 216 * screenScaleFactor // TODO: Theme! (Should match column size) - - // FIXED-LINE-HEIGHT: height: 18 * screenScaleFactor // TODO: Theme! - verticalAlignment: Text.AlignVCenter + width: 216 * screenScaleFactor // TODO: Theme! (Should match column size) + Rectangle + { + color: "#eeeeee" + width: Math.round(parent.width / 2) + height: parent.height + visible: !printJob + } + Label + { + text: printJob && printJob.name ? printJob.name : "" + color: "#374355" + elide: Text.ElideRight + font: UM.Theme.getFont("medium") // 14pt, regular + visible: printJob + + // FIXED-LINE-HEIGHT: + height: parent.height + verticalAlignment: Text.AlignVCenter + } } - - Label - { - text: printJob ? OutputDevice.formatDuration(printJob.timeTotal) : "" - color: "#374355" - elide: Text.ElideRight - font: UM.Theme.getFont("medium") // 14pt, regular - anchors.verticalCenter: parent.verticalCenter - width: 216 * screenScaleFactor // TODO: Theme! (Should match column size) - // FIXED-LINE-HEIGHT: + Item + { + anchors.verticalCenter: parent.verticalCenter height: 18 * screenScaleFactor // TODO: Theme! - verticalAlignment: Text.AlignVCenter + width: 216 * screenScaleFactor // TODO: Theme! (Should match column size) + Rectangle + { + color: "#eeeeee" + width: Math.round(parent.width / 3) + height: parent.height + visible: !printJob + } + Label + { + text: printJob ? OutputDevice.formatDuration(printJob.timeTotal) : "" + color: "#374355" + elide: Text.ElideRight + font: UM.Theme.getFont("medium") // 14pt, regular + visible: printJob + + // FIXED-LINE-HEIGHT: + height: 18 * screenScaleFactor // TODO: Theme! + verticalAlignment: Text.AlignVCenter + } } Item @@ -75,6 +100,14 @@ Item height: 18 * screenScaleFactor // TODO: This should be childrenRect.height but QML throws warnings width: childrenRect.width + Rectangle + { + color: "#eeeeee" + width: 72 * screenScaleFactor // TODO: Theme! + height: parent.height + visible: !printJob + } + Label { id: printerAssignmentLabel @@ -100,7 +133,7 @@ Item width: 120 * screenScaleFactor // TODO: Theme! // FIXED-LINE-HEIGHT: - height: 18 * screenScaleFactor // TODO: Theme! + height: parent.height verticalAlignment: Text.AlignVCenter } @@ -115,6 +148,7 @@ Item } height: childrenRect.height spacing: 6 // TODO: Theme! + visible: printJob Repeater { diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorQueue.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorQueue.qml index f2a0e785b8..2fa524c4be 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorQueue.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorQueue.qml @@ -144,7 +144,6 @@ Item topMargin: 12 * screenScaleFactor // TODO: Theme! } style: UM.Theme.styles.scrollview - visible: OutputDevice.receivedPrintJobs width: parent.width ListView @@ -160,7 +159,7 @@ Item } printJob: modelData } - model: OutputDevice.queuedPrintJobs + model: OutputDevice.receivedPrintJobs ? OutputDevice.queuedPrintJobs.concat([null,null]) : [null,null] spacing: 6 // TODO: Theme! } } From 97e17d9fb88873737bb5aacd69be1ea07c863490 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Tue, 8 Jan 2019 15:17:42 +0100 Subject: [PATCH 1187/1240] Remove debug behavior Contributes to CL-1157 --- plugins/UM3NetworkPrinting/resources/qml/MonitorQueue.qml | 2 +- plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorQueue.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorQueue.qml index 2fa524c4be..124b268300 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorQueue.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorQueue.qml @@ -159,7 +159,7 @@ Item } printJob: modelData } - model: OutputDevice.receivedPrintJobs ? OutputDevice.queuedPrintJobs.concat([null,null]) : [null,null] + model: OutputDevice.receivedPrintJobs ? OutputDevice.queuedPrintJobs : [null,null] spacing: 6 // TODO: Theme! } } diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml index fe7892840a..8723e6f46e 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorStage.qml @@ -67,7 +67,7 @@ Component MonitorCarousel { id: carousel - printers: OutputDevice.printers.concat([null]) + printers: OutputDevice.receivedPrintJobs ? OutputDevice.printers : [null] } } From aee4034e3e5d01f43ba34324efb38dc676a6c7c9 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Tue, 8 Jan 2019 15:49:34 +0100 Subject: [PATCH 1188/1240] No caching preferences CURA-6005 --- plugins/CuraDrive/src/DrivePluginExtension.py | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/plugins/CuraDrive/src/DrivePluginExtension.py b/plugins/CuraDrive/src/DrivePluginExtension.py index fdf7fc1609..1f66706ce6 100644 --- a/plugins/CuraDrive/src/DrivePluginExtension.py +++ b/plugins/CuraDrive/src/DrivePluginExtension.py @@ -18,6 +18,7 @@ from .DriveApiService import DriveApiService from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") + # The DivePluginExtension provides functionality to backup and restore your Cura configuration to Ultimaker's cloud. class DrivePluginExtension(QObject, Extension): @@ -32,7 +33,7 @@ class DrivePluginExtension(QObject, Extension): # Signal emitted when preferences changed (like auto-backup). preferencesChanged = pyqtSignal() - + DATE_FORMAT = "%d/%m/%Y %H:%M:%S" def __init__(self) -> None: @@ -46,7 +47,7 @@ class DrivePluginExtension(QObject, Extension): self._is_creating_backup = False # Initialize services. - self._preferences = CuraApplication.getInstance().getPreferences() + preferences = CuraApplication.getInstance().getPreferences() self._drive_api_service = DriveApiService() # Attach signals. @@ -55,10 +56,10 @@ class DrivePluginExtension(QObject, Extension): self._drive_api_service.creatingStateChanged.connect(self._onCreatingStateChanged) # Register preferences. - self._preferences.addPreference(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY, False) - self._preferences.addPreference(Settings.AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY, datetime.now() - .strftime(self.DATE_FORMAT)) - + preferences.addPreference(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY, False) + preferences.addPreference(Settings.AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY, + datetime.now().strftime(self.DATE_FORMAT)) + # Register the menu item self.addMenuItem(catalog.i18nc("@item:inmenu", "Manage backups"), self.showDriveWindow) @@ -74,9 +75,10 @@ class DrivePluginExtension(QObject, Extension): self._drive_window.show() def _autoBackup(self) -> None: - if self._preferences.getValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY) and self._isLastBackupTooLongAgo(): + preferences = CuraApplication.getInstance().getPreferences() + if preferences.getValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY) and self._isLastBackupTooLongAgo(): self.createBackup() - + def _isLastBackupTooLongAgo(self) -> bool: current_date = datetime.now() last_backup_date = self._getLastBackupDate() @@ -84,12 +86,14 @@ class DrivePluginExtension(QObject, Extension): return date_diff.days > 1 def _getLastBackupDate(self) -> "datetime": - last_backup_date = self._preferences.getValue(Settings.AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY) + preferences = CuraApplication.getInstance().getPreferences() + last_backup_date = preferences.getValue(Settings.AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY) return datetime.strptime(last_backup_date, self.DATE_FORMAT) def _storeBackupDate(self) -> None: backup_date = datetime.now().strftime(self.DATE_FORMAT) - self._preferences.setValue(Settings.AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY, backup_date) + preferences = CuraApplication.getInstance().getPreferences() + preferences.setValue(Settings.AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY, backup_date) def _onLoginStateChanged(self, logged_in: bool = False) -> None: if logged_in: @@ -114,11 +118,13 @@ class DrivePluginExtension(QObject, Extension): @pyqtSlot(bool, name = "toggleAutoBackup") def toggleAutoBackup(self, enabled: bool) -> None: - self._preferences.setValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY, enabled) + preferences = CuraApplication.getInstance().getPreferences() + preferences.setValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY, enabled) @pyqtProperty(bool, notify = preferencesChanged) def autoBackupEnabled(self) -> bool: - return bool(self._preferences.getValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY)) + preferences = CuraApplication.getInstance().getPreferences() + return bool(preferences.getValue(Settings.AUTO_BACKUP_ENABLED_PREFERENCE_KEY)) @pyqtProperty("QVariantList", notify = backupsChanged) def backups(self) -> List[Dict[str, Any]]: From 520b34ab8901df47a55c90fe98bf827cef9d2499 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 9 Jan 2019 08:41:13 +0100 Subject: [PATCH 1189/1240] Use getPluginPath() CURA-6005 --- plugins/CuraDrive/src/DrivePluginExtension.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/CuraDrive/src/DrivePluginExtension.py b/plugins/CuraDrive/src/DrivePluginExtension.py index 1f66706ce6..060f1496f1 100644 --- a/plugins/CuraDrive/src/DrivePluginExtension.py +++ b/plugins/CuraDrive/src/DrivePluginExtension.py @@ -68,7 +68,8 @@ class DrivePluginExtension(QObject, Extension): def showDriveWindow(self) -> None: if not self._drive_window: - path = os.path.join(os.path.dirname(__file__), "qml", "main.qml") + plugin_dir_path = CuraApplication.getInstance().getPluginRegistry().getPluginPath("CuraDrive") + path = os.path.join(plugin_dir_path, "src", "qml", "main.qml") self._drive_window = CuraApplication.getInstance().createQmlComponent(path, {"CuraDrive": self}) self.refreshBackups() if self._drive_window: From 580a69f11e864f1d5328143613398abbec29ec39 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Wed, 9 Jan 2019 09:27:31 +0100 Subject: [PATCH 1190/1240] Add CuraCloudAccountAPIRoot to CuraVersion.py.in CURA-6005 --- cura/CuraVersion.py.in | 1 + 1 file changed, 1 insertion(+) diff --git a/cura/CuraVersion.py.in b/cura/CuraVersion.py.in index 7c6304231d..d0f07ebb8c 100644 --- a/cura/CuraVersion.py.in +++ b/cura/CuraVersion.py.in @@ -8,3 +8,4 @@ CuraDebugMode = True if "@_cura_debugmode@" == "ON" else False CuraSDKVersion = "@CURA_SDK_VERSION@" CuraCloudAPIRoot = "@CURA_CLOUD_API_ROOT@" CuraCloudAPIVersion = "@CURA_CLOUD_API_VERSION@" +CuraCloudAccountAPIRoot = "CURA_CLOUD_ACCOUNT_API_ROOT" From 862c76a2eac51f7280e8118bcce56c1ba2a483fc Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 9 Jan 2019 12:51:20 +0100 Subject: [PATCH 1191/1240] Check if the optimised layer data is stored before attempting to delete it --- plugins/CuraEngineBackend/CuraEngineBackend.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index f12a5b1222..ef0898bb04 100755 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -833,7 +833,10 @@ class CuraEngineBackend(QObject, Backend): self._onChanged() def _onProcessLayersFinished(self, job: ProcessSlicedLayersJob) -> None: - del self._stored_optimized_layer_data[job.getBuildPlate()] + if job.getBuildPlate() in self._stored_optimized_layer_data: + del self._stored_optimized_layer_data[job.getBuildPlate()] + else: + Logger.log("w", "The optimized layer data was already deleted for buildplate %s", job.getBuildPlate()) self._process_layers_job = None Logger.log("d", "See if there is more to slice(2)...") self._invokeSlice() From d928e0979d4bbfba4ecf06bcc30c0636c6fbc3d0 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 9 Jan 2019 12:54:33 +0100 Subject: [PATCH 1192/1240] Handle attempting to remove a favorite material that was already removed --- cura/Machines/MaterialManager.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cura/Machines/MaterialManager.py b/cura/Machines/MaterialManager.py index 4d9574bf38..160508e7a6 100644 --- a/cura/Machines/MaterialManager.py +++ b/cura/Machines/MaterialManager.py @@ -683,7 +683,11 @@ class MaterialManager(QObject): @pyqtSlot(str) def removeFavorite(self, root_material_id: str) -> None: - self._favorites.remove(root_material_id) + try: + self._favorites.remove(root_material_id) + except KeyError: + Logger.log("w", "Could not delete material %s from favorites as it was already deleted", root_material_id) + return self.materialsUpdated.emit() # Ensure all settings are saved. @@ -692,4 +696,4 @@ class MaterialManager(QObject): @pyqtSlot() def getFavorites(self): - return self._favorites \ No newline at end of file + return self._favorites From 366d2c8114a2de79786c1bf1a43b9a348ef0ed88 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 9 Jan 2019 12:56:49 +0100 Subject: [PATCH 1193/1240] Prevent crash for firmware updater if the activePrinter is not set --- plugins/FirmwareUpdater/FirmwareUpdaterMachineAction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/FirmwareUpdater/FirmwareUpdaterMachineAction.py b/plugins/FirmwareUpdater/FirmwareUpdaterMachineAction.py index 0a3e3a0ff0..59552775b6 100644 --- a/plugins/FirmwareUpdater/FirmwareUpdaterMachineAction.py +++ b/plugins/FirmwareUpdater/FirmwareUpdaterMachineAction.py @@ -57,7 +57,7 @@ class FirmwareUpdaterMachineAction(MachineAction): outputDeviceCanUpdateFirmwareChanged = pyqtSignal() @pyqtProperty(QObject, notify = outputDeviceCanUpdateFirmwareChanged) def firmwareUpdater(self) -> Optional["FirmwareUpdater"]: - if self._active_output_device and self._active_output_device.activePrinter.getController().can_update_firmware: + if self._active_output_device and self._active_output_device.activePrinter and self._active_output_device.activePrinter.getController().can_update_firmware: self._active_firmware_updater = self._active_output_device.getFirmwareUpdater() return self._active_firmware_updater From 8831ba04e1ae3e6d2a0bafa559246874afcae6b6 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Wed, 9 Jan 2019 17:05:10 +0100 Subject: [PATCH 1194/1240] Fix missing extruder number --- .../resources/qml/MonitorPrinterConfiguration.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterConfiguration.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterConfiguration.qml index 78af227408..debc8b7959 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterConfiguration.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorPrinterConfiguration.qml @@ -39,7 +39,7 @@ Item { color: modelData && modelData.activeMaterial ? modelData.activeMaterial.color : "#eeeeee" // TODO: Theme! material: modelData && modelData.activeMaterial ? modelData.activeMaterial.name : "" - position: modelData && modelData.position ? modelData.position : -1 // Use negative one to create empty extruder number + position: modelData && typeof(modelData.position) === "number" ? modelData.position : -1 // Use negative one to create empty extruder number printCore: modelData ? modelData.hotendID : "" // Keep things responsive! From 03b4121d2289a89d0b779309190bd3d0b8d4efa6 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Thu, 10 Jan 2019 13:59:22 +0100 Subject: [PATCH 1195/1240] Fix broken shortcut for slice or stop slicing. --- resources/qml/ActionPanel/SliceProcessWidget.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index 1695be8748..0f415a6a2d 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -194,7 +194,7 @@ Column shortcut: "Ctrl+P" onTriggered: { - if (prepareButton.enabled) + if (sliceButton.enabled) { sliceOrStopSlicing() } From a042de128b64b91421625507e4410496cf050b1a Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Thu, 10 Jan 2019 14:16:20 +0100 Subject: [PATCH 1196/1240] Tag CuraCloudAPIVersion-string-value as expression instead of literal. [CURA-6005] --- cura/CuraVersion.py.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/CuraVersion.py.in b/cura/CuraVersion.py.in index d0f07ebb8c..770a0efd7b 100644 --- a/cura/CuraVersion.py.in +++ b/cura/CuraVersion.py.in @@ -8,4 +8,4 @@ CuraDebugMode = True if "@_cura_debugmode@" == "ON" else False CuraSDKVersion = "@CURA_SDK_VERSION@" CuraCloudAPIRoot = "@CURA_CLOUD_API_ROOT@" CuraCloudAPIVersion = "@CURA_CLOUD_API_VERSION@" -CuraCloudAccountAPIRoot = "CURA_CLOUD_ACCOUNT_API_ROOT" +CuraCloudAccountAPIRoot = "@CURA_CLOUD_ACCOUNT_API_ROOT@" From 36191fbe0f57aa2a3c88b358e7de408b9b94fa83 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Thu, 10 Jan 2019 15:36:12 +0100 Subject: [PATCH 1197/1240] Fix typevar typing issue --- plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py | 2 +- plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py index 2ff323555a..1b60ee7aae 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py @@ -22,7 +22,7 @@ from .Models.CloudPrintJobResponse import CloudPrintJobResponse ## The generic type variable used to document the methods below. -CloudApiClientModel = TypeVar("Model", bound = BaseModel) +CloudApiClientModel = TypeVar("CloudApiClientModel", bound = BaseModel) ## The cloud API client is responsible for handling the requests and responses from the cloud. diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index f3c4830e24..e8356cb897 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -44,7 +44,7 @@ I18N_CATALOG = i18nCatalog("cura") class CloudOutputDevice(NetworkedPrinterOutputDevice): # The interval with which the remote clusters are checked - CHECK_CLUSTER_INTERVAL = 50.0 # seconds + CHECK_CLUSTER_INTERVAL = 20.0 # seconds # Signal triggered when the print jobs in the queue were changed. printJobsChanged = pyqtSignal() From 614b1000fd8642d77ea38b1f6de21e0d24d1adbe Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 10 Jan 2019 15:36:36 +0100 Subject: [PATCH 1198/1240] Added missing material compatibility chart link CURA-6090 --- .../ConfigurationMenu/ConfigurationMenu.qml | 42 +++++++++++++++++++ .../themes/cura-light/icons/external_link.svg | 8 ++++ 2 files changed, 50 insertions(+) create mode 100644 resources/themes/cura-light/icons/external_link.svg diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index 3001efac54..491a2f069f 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -173,6 +173,48 @@ Cura.ExpandablePopup } } + Item + { + height: visible ? childrenRect.height: 0 + anchors.right: parent.right + anchors.rightMargin: UM.Theme.getSize("default_margin").width + width: childrenRect.width + UM.Theme.getSize("default_margin").width + visible: popupItem.configuration_method == ConfigurationMenu.ConfigurationMethod.Custom + UM.RecolorImage + { + id: externalLinkIcon + anchors.left: parent.left + anchors.leftMargin: UM.Theme.getSize("default_margin").width + height: materialInfoLabel.height + width: height + sourceSize.height: width + color: UM.Theme.getColor("text_link") + source: UM.Theme.getIcon("external_link") + } + + Label + { + id: materialInfoLabel + wrapMode: Text.WordWrap + text: catalog.i18nc("@label", "See the material compatibility chart") + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text_link") + anchors.left: externalLinkIcon.right + anchors.leftMargin: UM.Theme.getSize("narrow_margin").width + + MouseArea + { + anchors.fill: parent + onClicked: + { + // open the material URL with web browser + var url = "https://ultimaker.com/incoming-links/cura/material-compatibilty" + Qt.openUrlExternally(url) + } + } + } + } + Rectangle { id: separator diff --git a/resources/themes/cura-light/icons/external_link.svg b/resources/themes/cura-light/icons/external_link.svg new file mode 100644 index 0000000000..a2130fb97b --- /dev/null +++ b/resources/themes/cura-light/icons/external_link.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file From ff79e91686c070c1df206c2dc7f93555f04758fd Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Thu, 10 Jan 2019 16:32:43 +0100 Subject: [PATCH 1199/1240] Move the additional components for the save button out of the action panel CURA-6097 --- .../PostProcessingPlugin.qml | 2 +- .../qml/ActionPanel/OutputProcessWidget.qml | 50 +++---------------- .../qml/ActionPanel/SliceProcessWidget.qml | 38 +------------- resources/qml/Cura.qml | 34 +++++++++++++ 4 files changed, 44 insertions(+), 80 deletions(-) diff --git a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml index d5fe618b2d..cd8303d1d3 100644 --- a/plugins/PostProcessingPlugin/PostProcessingPlugin.qml +++ b/plugins/PostProcessingPlugin/PostProcessingPlugin.qml @@ -488,7 +488,7 @@ UM.Dialog { objectName: "postProcessingSaveAreaButton" visible: activeScriptsList.count > 0 - height: UM.Theme.getSize("save_button_save_to_button").height + height: UM.Theme.getSize("action_button").height width: height tooltip: catalog.i18nc("@info:tooltip", "Change active post-processing scripts") onClicked: dialog.show() diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml index 15214f212c..e3b623b675 100644 --- a/resources/qml/ActionPanel/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -31,6 +31,13 @@ Column id: information width: parent.width height: childrenRect.height + + PrintInformationWidget + { + id: printInformationPanel + visible: !preSlicedData + anchors.right: parent.right + } Column { @@ -51,14 +58,6 @@ Column text: preSlicedData ? catalog.i18nc("@label", "No time estimation available") : PrintInformation.currentPrintTime.getDisplayString(UM.DurationFormat.Long) source: UM.Theme.getIcon("clock") font: UM.Theme.getFont("large_bold") - - PrintInformationWidget - { - id: printInformationPanel - visible: !preSlicedData - anchors.left: parent.left - anchors.leftMargin: parent.contentWidth + UM.Theme.getSize("default_margin").width - } } Cura.IconWithText @@ -91,43 +90,8 @@ Column return totalWeights + "g · " + totalLengths.toFixed(2) + "m" } source: UM.Theme.getIcon("spool") - - Item - { - id: additionalComponents - width: childrenRect.width - anchors.right: parent.right - height: parent.height - Row - { - id: additionalComponentsRow - anchors.right: parent.right - anchors.bottom: parent.bottom - spacing: UM.Theme.getSize("default_margin").width - } - } - Component.onCompleted: addAdditionalComponents("saveButton") - - Connections - { - target: CuraApplication - onAdditionalComponentsChanged: addAdditionalComponents("saveButton") - } - - function addAdditionalComponents (areaId) - { - if(areaId == "saveButton") - { - for (var component in CuraApplication.additionalComponents["saveButton"]) - { - CuraApplication.additionalComponents["saveButton"][component].parent = additionalComponentsRow - } - } - } } } - - } Item diff --git a/resources/qml/ActionPanel/SliceProcessWidget.qml b/resources/qml/ActionPanel/SliceProcessWidget.qml index 0f415a6a2d..08966ce82c 100644 --- a/resources/qml/ActionPanel/SliceProcessWidget.qml +++ b/resources/qml/ActionPanel/SliceProcessWidget.qml @@ -110,8 +110,7 @@ Column height: parent.height - anchors.right: additionalComponents.left - anchors.rightMargin: additionalComponents.width != 0 ? UM.Theme.getSize("default_margin").width : 0 + anchors.right: parent.right anchors.left: parent.left text: catalog.i18nc("@button", "Slice") @@ -128,45 +127,12 @@ Column height: parent.height anchors.left: parent.left - anchors.right: additionalComponents.left - anchors.rightMargin: additionalComponents.width != 0 ? UM.Theme.getSize("default_margin").width : 0 + anchors.right: parent.right text: catalog.i18nc("@button", "Cancel") enabled: sliceButton.enabled visible: !sliceButton.visible onClicked: sliceOrStopSlicing() } - - Item - { - id: additionalComponents - width: childrenRect.width - anchors.right: parent.right - height: parent.height - Row - { - id: additionalComponentsRow - anchors.verticalCenter: parent.verticalCenter - spacing: UM.Theme.getSize("default_margin").width - } - } - Component.onCompleted: prepareButtons.addAdditionalComponents("saveButton") - - Connections - { - target: CuraApplication - onAdditionalComponentsChanged: prepareButtons.addAdditionalComponents("saveButton") - } - - function addAdditionalComponents (areaId) - { - if(areaId == "saveButton") - { - for (var component in CuraApplication.additionalComponents["saveButton"]) - { - CuraApplication.additionalComponents["saveButton"][component].parent = additionalComponentsRow - } - } - } } diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 4a031e33fa..f3d2e7295a 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -248,6 +248,7 @@ UM.MainWindow Cura.ActionPanelWidget { + id: actionPanelWidget anchors.right: parent.right anchors.bottom: parent.bottom anchors.rightMargin: UM.Theme.getSize("thick_margin").width @@ -269,6 +270,39 @@ UM.MainWindow visible: CuraApplication.platformActivity && (main.item == null || !qmlTypeOf(main.item, "QQuickRectangle")) } + Item + { + id: additionalComponents + width: childrenRect.width + anchors.right: actionPanelWidget.left + anchors.rightMargin: UM.Theme.getSize("default_margin").width + anchors.bottom: actionPanelWidget.bottom + anchors.bottomMargin: UM.Theme.getSize("thick_margin").height * 2 + visible: actionPanelWidget.visible + Row + { + id: additionalComponentsRow + anchors.verticalCenter: parent.verticalCenter + spacing: UM.Theme.getSize("default_margin").width + } + } + + Component.onCompleted: contentItem.addAdditionalComponents() + + Connections + { + target: CuraApplication + onAdditionalComponentsChanged: contentItem.addAdditionalComponents("saveButton") + } + + function addAdditionalComponents() + { + for (var component in CuraApplication.additionalComponents["saveButton"]) + { + CuraApplication.additionalComponents["saveButton"][component].parent = additionalComponentsRow + } + } + Loader { // A stage can control this area. If nothing is set, it will therefore show the 3D view. From d4621ec5043f2be107abfee6e5779cbdaaebccad Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 11 Jan 2019 13:20:14 +0100 Subject: [PATCH 1200/1240] Ensure that if no CuraCloudApiRoot is set that the default is used CURA-6005 --- cura/UltimakerCloudAuthentication.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cura/UltimakerCloudAuthentication.py b/cura/UltimakerCloudAuthentication.py index ac752231b9..5f69329dbb 100644 --- a/cura/UltimakerCloudAuthentication.py +++ b/cura/UltimakerCloudAuthentication.py @@ -10,6 +10,8 @@ DEFAULT_CLOUD_ACCOUNT_API_ROOT = "https://account.ultimaker.com" # type: str try: from cura.CuraVersion import CuraCloudAPIRoot # type: ignore + if CuraCloudAPIRoot == "": + CuraCloudAPIRoot = DEFAULT_CLOUD_API_ROOT except ImportError: CuraCloudAPIRoot = DEFAULT_CLOUD_API_ROOT @@ -20,5 +22,7 @@ except ImportError: try: from cura.CuraVersion import CuraCloudAccountAPIRoot # type: ignore + if CuraCloudAccountAPIRoot == "": + CuraCloudAccountAPIRoot = DEFAULT_CLOUD_ACCOUNT_API_ROOT except ImportError: CuraCloudAccountAPIRoot = DEFAULT_CLOUD_ACCOUNT_API_ROOT From 2dead8759329767f2348c07a4b8df6516dbe519c Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 11 Jan 2019 14:43:20 +0100 Subject: [PATCH 1201/1240] Ensure that the installed & canUpdate properties get set correctly CURA-6053 --- plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml b/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml index 7160dafa2d..87fc5d6955 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDetailTileActions.qml @@ -91,5 +91,10 @@ Column target: toolbox onInstallChanged: installed = toolbox.isInstalled(model.id) onMetadataChanged: canUpdate = toolbox.canUpdate(model.id) + onFilterChanged: + { + installed = toolbox.isInstalled(model.id) + canUpdate = toolbox.canUpdate(model.id) + } } } From e7060206396f7cc4a30e82e3d4b8c7036e152d4b Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 11 Jan 2019 14:44:49 +0100 Subject: [PATCH 1202/1240] Improve the text when there is no configuration available It can happen that the list is empty because the data is still coming or because the connection has been lost. There are also some improvements in the link to the compatibility chart. Now the two external links in Cura have the same behaviour. Contributes to CURA-6011. --- .../UM3NetworkPrinting/resources/qml/MonitorQueue.qml | 9 +++++---- .../resources/svg/icons/external_link.svg | 8 -------- .../Menus/ConfigurationMenu/ConfigurationListView.qml | 8 ++++++-- .../qml/Menus/ConfigurationMenu/ConfigurationMenu.qml | 11 +++++++++++ 4 files changed, 22 insertions(+), 14 deletions(-) delete mode 100644 plugins/UM3NetworkPrinting/resources/svg/icons/external_link.svg diff --git a/plugins/UM3NetworkPrinting/resources/qml/MonitorQueue.qml b/plugins/UM3NetworkPrinting/resources/qml/MonitorQueue.qml index 124b268300..f2dc09de95 100644 --- a/plugins/UM3NetworkPrinting/resources/qml/MonitorQueue.qml +++ b/plugins/UM3NetworkPrinting/resources/qml/MonitorQueue.qml @@ -42,8 +42,8 @@ Item { id: externalLinkIcon anchors.verticalCenter: manageQueueLabel.verticalCenter - color: UM.Theme.getColor("primary") - source: "../svg/icons/external_link.svg" + color: UM.Theme.getColor("text_link") + source: UM.Theme.getIcon("external_link") width: 16 * screenScaleFactor // TODO: Theme! (Y U NO USE 18 LIKE ALL OTHER ICONS?!) height: 16 * screenScaleFactor // TODO: Theme! (Y U NO USE 18 LIKE ALL OTHER ICONS?!) } @@ -56,10 +56,11 @@ Item leftMargin: 6 * screenScaleFactor // TODO: Theme! verticalCenter: externalLinkIcon.verticalCenter } - color: UM.Theme.getColor("primary") + color: UM.Theme.getColor("text_link") font: UM.Theme.getFont("default") // 12pt, regular - linkColor: UM.Theme.getColor("primary") + linkColor: UM.Theme.getColor("text_link") text: catalog.i18nc("@label link to connect manager", "Manage queue in Cura Connect") + renderType: Text.NativeRendering } } diff --git a/plugins/UM3NetworkPrinting/resources/svg/icons/external_link.svg b/plugins/UM3NetworkPrinting/resources/svg/icons/external_link.svg deleted file mode 100644 index a2130fb97b..0000000000 --- a/plugins/UM3NetworkPrinting/resources/svg/icons/external_link.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml index e57b21cb78..15d882fdf5 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationListView.qml @@ -23,7 +23,7 @@ Item } } - // This component will appear when there is no configurations (e.g. when losing connection) + // This component will appear when there are no configurations (e.g. when losing connection or when they are being loaded) Item { width: parent.width @@ -51,7 +51,11 @@ Item anchors.left: icon.right anchors.right: parent.right anchors.leftMargin: UM.Theme.getSize("default_margin").width - text: catalog.i18nc("@label", "Downloading the configurations from the remote printer") + // There are two cases that we want to diferenciate, one is when Cura is loading the configurations and the + // other when the connection was lost + text: Cura.MachineManager.printerConnected ? + catalog.i18nc("@label", "Loading available configurations from the printer...") : + catalog.i18nc("@label", "The configurations are not available because the printer is disconnected.") color: UM.Theme.getColor("text") font: UM.Theme.getFont("default") renderType: Text.NativeRendering diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index 491a2f069f..7d09f4be38 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -199,18 +199,29 @@ Cura.ExpandablePopup text: catalog.i18nc("@label", "See the material compatibility chart") font: UM.Theme.getFont("default") color: UM.Theme.getColor("text_link") + linkColor: UM.Theme.getColor("text_link") anchors.left: externalLinkIcon.right anchors.leftMargin: UM.Theme.getSize("narrow_margin").width + renderType: Text.NativeRendering MouseArea { anchors.fill: parent + hoverEnabled: true onClicked: { // open the material URL with web browser var url = "https://ultimaker.com/incoming-links/cura/material-compatibilty" Qt.openUrlExternally(url) } + onEntered: + { + materialInfoLabel.font.underline = true + } + onExited: + { + materialInfoLabel.font.underline = false + } } } } From cbeadb234314a3dc18372cced43dacee7f859aa1 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Fri, 11 Jan 2019 14:47:59 +0100 Subject: [PATCH 1203/1240] Set the font of the time estimation to medium bold This way it will also fit when a very long time is used in german. If we don't do this, 14 Tages 12 Stunden 33 Minuten didn't fit. CURA-6097 --- resources/qml/ActionPanel/OutputProcessWidget.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/ActionPanel/OutputProcessWidget.qml b/resources/qml/ActionPanel/OutputProcessWidget.qml index e3b623b675..63974d7f34 100644 --- a/resources/qml/ActionPanel/OutputProcessWidget.qml +++ b/resources/qml/ActionPanel/OutputProcessWidget.qml @@ -57,7 +57,7 @@ Column text: preSlicedData ? catalog.i18nc("@label", "No time estimation available") : PrintInformation.currentPrintTime.getDisplayString(UM.DurationFormat.Long) source: UM.Theme.getIcon("clock") - font: UM.Theme.getFont("large_bold") + font: UM.Theme.getFont("medium_bold") } Cura.IconWithText From b78ac0664f1ebd869a6487f64f9e9b6859481dac Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Fri, 11 Jan 2019 16:07:08 +0100 Subject: [PATCH 1204/1240] Fix return types from review --- cura/PrinterOutput/NetworkedPrinterOutputDevice.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py index 4a8aa0a6b2..985d742728 100644 --- a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py +++ b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py @@ -228,7 +228,8 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): self._last_request_time = time() if not self._manager: - return Logger.log("e", "No network manager was created to execute the DELETE call with.") + Logger.log("e", "No network manager was created to execute the DELETE call with.") + return reply = self._manager.deleteResource(request) self._registerOnFinishedCallback(reply, on_finished) @@ -243,7 +244,8 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): self._last_request_time = time() if not self._manager: - return Logger.log("e", "No network manager was created to execute the GET call with.") + Logger.log("e", "No network manager was created to execute the GET call with.") + return reply = self._manager.get(request) self._registerOnFinishedCallback(reply, on_finished) @@ -262,7 +264,8 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): self._last_request_time = time() if not self._manager: - return Logger.log("e", "Could not find manager.") + Logger.log("e", "Could not find manager.") + return body = data if isinstance(data, bytes) else data.encode() # type: bytes reply = self._manager.post(request, body) From 36e49ee6bb0ee7261268135a3ab212f036fe2fc7 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Fri, 11 Jan 2019 16:08:46 +0100 Subject: [PATCH 1205/1240] Make activeMachineIsGroup more robust --- 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 2f9cb106fb..5f33be1c54 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -534,7 +534,7 @@ class MachineManager(QObject): @pyqtProperty(bool, notify = printerConnectedStatusChanged) def activeMachineIsGroup(self) -> bool: - return bool(self._printer_output_devices) and self._printer_output_devices[0].clusterSize > 1 + return bool(self._printer_output_devices) and len(self._printer_output_devices[0].printers) > 1 @pyqtProperty(bool, notify = printerConnectedStatusChanged) def activeMachineHasActiveNetworkConnection(self) -> bool: From d8b5f75e2a297c9ce269bc7b0208f2d65f90239e Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Fri, 11 Jan 2019 16:14:55 +0100 Subject: [PATCH 1206/1240] Solve some review comments --- cura/PrinterOutput/NetworkedPrinterOutputDevice.py | 14 +++++++------- .../UM3NetworkPrinting/src/Cloud/CloudApiClient.py | 4 +--- .../src/Cloud/CloudOutputDevice.py | 2 +- .../src/Cloud/CloudProgressMessage.py | 11 +++-------- .../UM3NetworkPrinting/src/MeshFormatHandler.py | 10 ++++------ 5 files changed, 16 insertions(+), 25 deletions(-) diff --git a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py index 985d742728..6b19ca9564 100644 --- a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py +++ b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py @@ -194,11 +194,11 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): assert (self._manager is not None) ## Sends a put request to the given path. - # url: The path after the API prefix. - # data: The data to be sent in the body - # content_type: The content type of the body data. - # on_finished: The function to call when the response is received. - # on_progress: The function to call when the progress changes. Parameters are bytes_sent / bytes_total. + # \param url: The path after the API prefix. + # \param data: The data to be sent in the body + # \param content_type: The content type of the body data. + # \param on_finished: The function to call when the response is received. + # \param on_progress: The function to call when the progress changes. Parameters are bytes_sent / bytes_total. def put(self, url: str, data: Union[str, bytes], content_type: Optional[str] = None, on_finished: Optional[Callable[[QNetworkReply], None]] = None, on_progress: Optional[Callable[[int, int], None]] = None) -> None: @@ -219,8 +219,8 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): reply.uploadProgress.connect(on_progress) ## Sends a delete request to the given path. - # url: The path after the API prefix. - # on_finished: The function to be call when the response is received. + # \param url: The path after the API prefix. + # \param on_finished: The function to be call when the response is received. def delete(self, url: str, on_finished: Optional[Callable[[QNetworkReply], None]]) -> None: self._validateManager() diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py index 1b60ee7aae..836a0eb393 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py @@ -43,7 +43,7 @@ class CloudApiClient: self._account = account self._on_error = on_error self._upload = None # type: Optional[MeshUploader] - # in order to avoid garbage collection we keep the callbacks in this list. + # In order to avoid garbage collection we keep the callbacks in this list. self._anti_gc_callbacks = [] # type: List[Callable[[], None]] ## Gets the account used for the API. @@ -105,7 +105,6 @@ class CloudApiClient: request.setHeader(QNetworkRequest.ContentTypeHeader, content_type) if self._account.isLoggedIn: request.setRawHeader(b"Authorization", "Bearer {}".format(self._account.accessToken).encode()) - # Logger.log("i", "Created request for URL %s. Logged in = %s", path, self._account.isLoggedIn) return request ## Parses the given JSON network reply into a status code and a dictionary, handling unexpected errors as well. @@ -116,7 +115,6 @@ class CloudApiClient: status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) try: response = bytes(reply.readAll()).decode() - # Logger.log("i", "Received a reply %s from %s with %s", status_code, reply.url().toString(), response) return status_code, json.loads(response) except (UnicodeDecodeError, JSONDecodeError, ValueError) as err: error = CloudError(code=type(err).__name__, title=str(err), http_code=str(status_code), diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index e8356cb897..0b832fc8b4 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -107,7 +107,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Disconnects the device def disconnect(self) -> None: super().disconnect() - Logger.log("i", "Disconnected to cluster %s", self.key) + Logger.log("i", "Disconnected from cluster %s", self.key) CuraApplication.getInstance().getBackend().backendStateChange.disconnect(self._onBackendStateChange) ## Resets the print job that was uploaded to force a new upload, runs whenever the user re-slices. diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudProgressMessage.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudProgressMessage.py index aefe59cc85..c4618c1d50 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudProgressMessage.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudProgressMessage.py @@ -4,20 +4,15 @@ from UM import i18nCatalog from UM.Message import Message -## Class that contains all the translations for this module. -class T: - _I18N_CATALOG = i18nCatalog("cura") - - SENDING_DATA_TEXT = _I18N_CATALOG.i18nc("@info:status", "Sending data to remote cluster") - SENDING_DATA_TITLE = _I18N_CATALOG.i18nc("@info:status", "Sending data to remote cluster") +I18N_CATALOG = i18nCatalog("cura") ## Class responsible for showing a progress message while a mesh is being uploaded to the cloud. class CloudProgressMessage(Message): def __init__(self): super().__init__( - text = T.SENDING_DATA_TEXT, - title = T.SENDING_DATA_TITLE, + text = I18N_CATALOG.i18nc("@info:status", "Sending data to remote cluster"), + title = I18N_CATALOG.i18nc("@info:status", "Sending data to remote cluster"), progress = -1, lifetime = 0, dismissable = False, diff --git a/plugins/UM3NetworkPrinting/src/MeshFormatHandler.py b/plugins/UM3NetworkPrinting/src/MeshFormatHandler.py index 72da3c4e6b..c3cd82a86d 100644 --- a/plugins/UM3NetworkPrinting/src/MeshFormatHandler.py +++ b/plugins/UM3NetworkPrinting/src/MeshFormatHandler.py @@ -13,11 +13,7 @@ from UM.i18n import i18nCatalog from cura.CuraApplication import CuraApplication -## Class that contains all the translations for this module. -class T: - # The translation catalog for this module. - _I18N_CATALOG = i18nCatalog("cura") - NO_FORMATS_AVAILABLE = _I18N_CATALOG.i18nc("@info:status", "There are no file formats available to write with!") +I18N_CATALOG = i18nCatalog("cura") ## This class is responsible for choosing the formats used by the connected clusters. @@ -106,7 +102,9 @@ class MeshFormatHandler: if len(file_formats) == 0: Logger.log("e", "There are no file formats available to write with!") - raise OutputDeviceError.WriteRequestFailedError(T.NO_FORMATS_AVAILABLE) + raise OutputDeviceError.WriteRequestFailedError( + I18N_CATALOG.i18nc("@info:status", "There are no file formats available to write with!") + ) return file_formats[0] ## Gets the file writer for the given file handler and mime type. From e465bd771ae8a5261980e53203e3b76178c78203 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Fri, 11 Jan 2019 16:18:47 +0100 Subject: [PATCH 1207/1240] Use toolpath instead of mesh, some review fixes --- cura/PrinterOutput/NetworkedPrinterOutputDevice.py | 2 +- plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py | 8 ++++---- plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py | 2 +- .../UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py index 6b19ca9564..47a6caf3e5 100644 --- a/cura/PrinterOutput/NetworkedPrinterOutputDevice.py +++ b/cura/PrinterOutput/NetworkedPrinterOutputDevice.py @@ -275,7 +275,7 @@ class NetworkedPrinterOutputDevice(PrinterOutputDevice): def postFormWithParts(self, target: str, parts: List[QHttpPart], on_finished: Optional[Callable[[QNetworkReply], None]], - on_progress: Callable = None) -> QNetworkReply: + on_progress: Optional[Callable[[int, int], None]] = None) -> QNetworkReply: self._validateManager() request = self._createEmptyRequest(target, content_type=None) multi_post_part = QHttpMultiPart(QHttpMultiPart.FormDataType) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py index 836a0eb393..f82d244fa9 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py @@ -76,14 +76,14 @@ class CloudApiClient: reply = self._manager.put(self._createEmptyRequest(url), body.encode()) self._addCallback(reply, on_finished, CloudPrintJobResponse) - ## Uploads a print job mesh to the cloud. + ## Uploads a print job tool path to the cloud. # \param print_job: The object received after requesting an upload with `self.requestUpload`. - # \param mesh: The mesh data to be uploaded. + # \param mesh: The tool path data to be uploaded. # \param on_finished: The function to be called after the upload is successful. # \param on_progress: A function to be called during upload progress. It receives a percentage (0-100). # \param on_error: A function to be called if the upload fails. - def uploadMesh(self, print_job: CloudPrintJobResponse, mesh: bytes, on_finished: Callable[[], Any], - on_progress: Callable[[int], Any], on_error: Callable[[], Any]): + def uploadToolPath(self, print_job: CloudPrintJobResponse, mesh: bytes, on_finished: Callable[[], Any], + on_progress: Callable[[int], Any], on_error: Callable[[], Any]): self._upload = MeshUploader(self._manager, print_job, mesh, on_finished, on_progress, on_error) self._upload.start() diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 0b832fc8b4..054c465ef2 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -302,7 +302,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self._progress.show() self._uploaded_print_job = job_response mesh = cast(bytes, self._mesh) - self._api.uploadMesh(job_response, mesh, self._onPrintJobUploaded, self._progress.update, self._onUploadError) + self._api.uploadToolPath(job_response, mesh, self._onPrintJobUploaded, self._progress.update, self._onUploadError) ## Requests the print to be sent to the printer when we finished uploading the mesh. def _onPrintJobUploaded(self) -> None: diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py index b57334b2da..3c0617a290 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py @@ -74,7 +74,7 @@ class TestCloudApiClient(TestCase): self.assertEqual(["text/plain"], [r.content_type for r in results]) self.assertEqual(["uploading"], [r.status for r in results]) - def test_uploadMesh(self): + def test_uploadToolPath(self): results = [] progress = MagicMock() @@ -86,7 +86,7 @@ class TestCloudApiClient(TestCase): self.network.prepareReply("PUT", upload_response.upload_url, 200, b'{}') mesh = ("1234" * 100000).encode() - self.api.uploadMesh(upload_response, mesh, lambda: results.append("sent"), progress.advance, progress.error) + self.api.uploadToolPath(upload_response, mesh, lambda: results.append("sent"), progress.advance, progress.error) for _ in range(10): self.network.flushReplies() From 3c10cca0dee6c4a0fc0599bc33e51aaff7f4e5d9 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Fri, 11 Jan 2019 16:33:49 +0100 Subject: [PATCH 1208/1240] Fixed all codestyle and nitpicks from review --- .../src/Cloud/CloudOutputDevice.py | 32 +++++++++---------- .../src/Cloud/CloudOutputDeviceManager.py | 6 ++-- .../src/Cloud/CloudProgressMessage.py | 5 --- .../src/ClusterUM3OutputDevice.py | 2 +- .../tests/Cloud/NetworkManagerMock.py | 8 ++--- .../tests/Cloud/TestCloudApiClient.py | 2 +- .../qml/PrinterSelector/MachineSelector.qml | 22 +++++++++---- 7 files changed, 39 insertions(+), 38 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 054c465ef2..2cf6a3c236 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -95,7 +95,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): self._finished_jobs = set() # type: Set[str] # Reference to the uploaded print job / mesh - self._mesh = None # type: Optional[bytes] + self._tool_path = None # type: Optional[bytes] self._uploaded_print_job = None # type: Optional[CloudPrintJobResponse] ## Connects this device. @@ -112,7 +112,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Resets the print job that was uploaded to force a new upload, runs whenever the user re-slices. def _onBackendStateChange(self, _: BackendState) -> None: - self._mesh = None + self._tool_path = None self._uploaded_print_job = None ## Gets the cluster response from which this device was created. @@ -133,7 +133,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Set all the interface elements and texts for this output device. def _setInterfaceElements(self) -> None: - self.setPriority(2) # make sure we end up below the local networking and above 'save to file' + self.setPriority(2) # Make sure we end up below the local networking and above 'save to file' self.setName(self._id) self.setShortDescription(I18N_CATALOG.i18nc("@action:button", "Print via Cloud")) self.setDescription(I18N_CATALOG.i18nc("@properties:tooltip", "Print via Cloud")) @@ -154,8 +154,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): return if self._uploaded_print_job: - # the mesh didn't change, let's not upload it again - self._api.requestPrint(self.key, self._uploaded_print_job.job_id, self._onPrintRequested) + # The mesh didn't change, let's not upload it again + self._api.requestPrint(self.key, self._uploaded_print_job.job_id, self._onPrintUploadCompleted) return # Indicate we have started sending a job. @@ -168,7 +168,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): mesh = mesh_format.getBytes(nodes) - self._mesh = mesh + self._tool_path = mesh request = CloudPrintJobUploadRequest( job_name = file_name or mesh_format.file_extension, file_size = len(mesh), @@ -180,7 +180,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def _update(self) -> None: super()._update() if self._last_request_time and time() - self._last_request_time < self.CHECK_CLUSTER_INTERVAL: - return # avoid calling the cloud too often + return # Avoid calling the cloud too often Logger.log("d", "Updating: %s - %s >= %s", time(), self._last_request_time, self.CHECK_CLUSTER_INTERVAL) if self._account.isLoggedIn: @@ -226,7 +226,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): if self._printers and not self._active_printer: self.setActivePrinter(self._printers[0]) - if added_printers or removed_printers or updated_printers: + if added_printers or removed_printers: self.printersChanged.emit() ## Updates the local list of print jobs with the list received from the cloud. @@ -253,7 +253,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): # We only have to update when jobs are added or removed # updated jobs push their changes via their output model - if added_jobs or removed_jobs or updated_jobs: + if added_jobs or removed_jobs: self.printJobsChanged.emit() ## Registers a new print job received via the cloud API. @@ -268,6 +268,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Handles the event of a change in a print job state def _onPrintJobStateChanged(self) -> None: user_name = self._getUserName() + # TODO: confirm that notifications in Cura are still required for job in self._print_jobs: if job.state == "wait_cleanup" and job.key not in self._finished_jobs and job.owner == user_name: self._finished_jobs.add(job.key) @@ -282,9 +283,6 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): )), ).show() - # Ensure UI gets updated - self.printJobsChanged.emit() - ## Updates the printer assignment for the given print job model. def _updateAssignedPrinter(self, model: UM3PrintJobOutputModel, printer_uuid: str) -> None: printer = next((p for p in self._printers if printer_uuid == p.key), None) @@ -301,18 +299,18 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): def _onPrintJobCreated(self, job_response: CloudPrintJobResponse) -> None: self._progress.show() self._uploaded_print_job = job_response - mesh = cast(bytes, self._mesh) - self._api.uploadToolPath(job_response, mesh, self._onPrintJobUploaded, self._progress.update, self._onUploadError) + tool_path = cast(bytes, self._tool_path) + self._api.uploadToolPath(job_response, tool_path, self._onPrintJobUploaded, self._progress.update, self._onUploadError) ## Requests the print to be sent to the printer when we finished uploading the mesh. def _onPrintJobUploaded(self) -> None: self._progress.update(100) print_job = cast(CloudPrintJobResponse, self._uploaded_print_job) - self._api.requestPrint(self.key, print_job.job_id, self._onPrintRequested) + self._api.requestPrint(self.key, print_job.job_id, self._onPrintUploadCompleted) ## Displays the given message if uploading the mesh has failed # \param message: The message to display. - def _onUploadError(self, message = None) -> None: + def _onUploadError(self, message: str = None) -> None: self._progress.hide() self._uploaded_print_job = None Message( @@ -324,7 +322,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Shows a message when the upload has succeeded # \param response: The response from the cloud API. - def _onPrintRequested(self, response: CloudPrintResponse) -> None: + def _onPrintUploadCompleted(self, response: CloudPrintResponse) -> None: Logger.log("d", "The cluster will be printing this print job with the ID %s", response.cluster_job_id) self._progress.hide() Message( diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 72ac34ff34..ba852e36c0 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -41,7 +41,7 @@ class CloudOutputDeviceManager: self._account = application.getCuraAPI().account # type: Account self._api = CloudApiClient(self._account, self._onApiError) - # create a timer to update the remote cluster list + # Create a timer to update the remote cluster list self._update_timer = QTimer(application) self._update_timer.setInterval(int(self.CHECK_CLUSTER_INTERVAL * 1000)) self._update_timer.setSingleShot(False) @@ -99,7 +99,6 @@ class CloudOutputDeviceManager: def _connectToActiveMachine(self) -> None: active_machine = CuraApplication.getInstance().getGlobalContainerStack() if not active_machine: - Logger.log("d", "no active machine") return # Remove all output devices that we have registered. @@ -143,8 +142,7 @@ class CloudOutputDeviceManager: message = Message( text = text, title = self.I18N_CATALOG.i18nc("@info:title", "Error"), - lifetime = 10, - dismissable = True + lifetime = 10 ) message.show() diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudProgressMessage.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudProgressMessage.py index c4618c1d50..d85f49c1a0 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudProgressMessage.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudProgressMessage.py @@ -30,8 +30,3 @@ class CloudProgressMessage(Message): if not self._visible: super().show() self.setProgress(percentage) - - ## Returns a boolean indicating whether the message is currently visible. - @property - def visible(self) -> bool: - return self._visible diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index e40cad10f8..84740ae856 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -64,7 +64,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): self._monitor_view_qml_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../resources/qml/MonitorStage.qml") - # trigger the printersChanged signal when the private signal is triggered + # Trigger the printersChanged signal when the private signal is triggered self.printersChanged.connect(self._clusterPrintersChanged) self._accepts_commands = True # type: bool diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py b/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py index 5b5d89ca54..e504509d67 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/NetworkManagerMock.py @@ -30,7 +30,7 @@ class FakeSignal: # Any requests not prepared beforehand will cause KeyErrors. class NetworkManagerMock: - # an enumeration of the supported operations and their code for the network access manager. + # An enumeration of the supported operations and their code for the network access manager. _OPERATIONS = { "GET": QNetworkAccessManager.GetOperation, "POST": QNetworkAccessManager.PostOperation, @@ -41,11 +41,11 @@ class NetworkManagerMock: ## Initializes the network manager mock. def __init__(self) -> None: - # a dict with the prepared replies, using the format {(http_method, url): reply} + # A dict with the prepared replies, using the format {(http_method, url): reply} self.replies = {} # type: Dict[Tuple[str, str], MagicMock] self.request_bodies = {} # type: Dict[Tuple[str, str], bytes] - # signals used in the network manager. + # Signals used in the network manager. self.finished = Signal() self.authenticationRequired = Signal() @@ -55,7 +55,7 @@ class NetworkManagerMock: # \return The mocked function, if the method name is known. Defaults to the standard getattr function. def __getattr__(self, method: str) -> Any: ## This mock implementation will simply return the reply from the prepared ones. - # it raises a KeyError if requests are done without being prepared. + # it raises a KeyError if requests are done without being prepared. def doRequest(request: QNetworkRequest, body: Optional[bytes] = None, *_): key = method.upper(), request.url().toString() if body: diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py index 3c0617a290..acce55751c 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py @@ -39,7 +39,7 @@ class TestCloudApiClient(TestCase): data = parseFixture("getClusters")["data"] self.network.prepareReply("GET", CuraCloudAPIRoot + "/connect/v1/clusters", 200, response) - # the callback is a function that adds the result of the call to getClusters to the result list + # The callback is a function that adds the result of the call to getClusters to the result list self.api.getClusters(lambda clusters: result.extend(clusters)) self.network.flushReplies() diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index 7018131f45..84f63a854a 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -29,11 +29,16 @@ Cura.ExpandablePopup text: isNetworkPrinter ? Cura.MachineManager.activeMachineNetworkGroupName : Cura.MachineManager.activeMachineName source: { - if (isGroup) { + if (isGroup) + { return UM.Theme.getIcon("printer_group") - } else if (isNetworkPrinter || isCloudPrinter) { + } + else if (isNetworkPrinter || isCloudPrinter) + { return UM.Theme.getIcon("printer_single") - } else { + } + else + { return "" } } @@ -52,11 +57,16 @@ Cura.ExpandablePopup source: { - if (isNetworkPrinter) { + if (isNetworkPrinter) + { return UM.Theme.getIcon("printer_connected") - } else if (isCloudPrinter) { + } + else if (isCloudPrinter) + { return UM.Theme.getIcon("printer_cloud_connected") - } else { + } + else + { return "" } } From 7bbd43928a4d8d3adcc7d8119995add058c57bea Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Fri, 11 Jan 2019 16:38:25 +0100 Subject: [PATCH 1209/1240] Fix more review comments --- plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py | 6 +++--- plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py | 2 ++ .../src/Cloud/CloudOutputDeviceManager.py | 3 +-- .../src/Cloud/{MeshUploader.py => ToolPathUploader.py} | 2 +- .../tests/Cloud/TestCloudOutputDeviceManager.py | 1 - 5 files changed, 7 insertions(+), 7 deletions(-) rename plugins/UM3NetworkPrinting/src/Cloud/{MeshUploader.py => ToolPathUploader.py} (99%) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py index f82d244fa9..9d6c29c0a4 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py @@ -11,7 +11,7 @@ from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply, QNetworkAccessManage from UM.Logger import Logger from cura import UltimakerCloudAuthentication from cura.API import Account -from .MeshUploader import MeshUploader +from .ToolPathUploader import ToolPathUploader from ..Models import BaseModel from .Models.CloudClusterResponse import CloudClusterResponse from .Models.CloudError import CloudError @@ -42,7 +42,7 @@ class CloudApiClient: self._manager = QNetworkAccessManager() self._account = account self._on_error = on_error - self._upload = None # type: Optional[MeshUploader] + self._upload = None # type: Optional[ToolPathUploader] # In order to avoid garbage collection we keep the callbacks in this list. self._anti_gc_callbacks = [] # type: List[Callable[[], None]] @@ -84,7 +84,7 @@ class CloudApiClient: # \param on_error: A function to be called if the upload fails. def uploadToolPath(self, print_job: CloudPrintJobResponse, mesh: bytes, on_finished: Callable[[], Any], on_progress: Callable[[int], Any], on_error: Callable[[], Any]): - self._upload = MeshUploader(self._manager, print_job, mesh, on_finished, on_progress, on_error) + self._upload = ToolPathUploader(self._manager, print_job, mesh, on_finished, on_progress, on_error) self._upload.start() # Requests a cluster to print the given print job. diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 2cf6a3c236..2947cd421c 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -100,6 +100,8 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice): ## Connects this device. def connect(self) -> None: + if self.isConnected(): + return super().connect() Logger.log("i", "Connected to cluster %s", self.key) CuraApplication.getInstance().getBackend().backendStateChange.connect(self._onBackendStateChange) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index ba852e36c0..f9bd635bbd 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -131,8 +131,7 @@ class CloudOutputDeviceManager: ## Connects to an output device and makes sure it is registered in the output device manager. def _connectToOutputDevice(self, device: CloudOutputDevice) -> None: - if not device.isConnected(): - device.connect() + device.connect() self._output_device_manager.addOutputDevice(device) ## Handles an API error received from the cloud. diff --git a/plugins/UM3NetworkPrinting/src/Cloud/MeshUploader.py b/plugins/UM3NetworkPrinting/src/Cloud/ToolPathUploader.py similarity index 99% rename from plugins/UM3NetworkPrinting/src/Cloud/MeshUploader.py rename to plugins/UM3NetworkPrinting/src/Cloud/ToolPathUploader.py index cb721b872e..176b7e6ab7 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/MeshUploader.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/ToolPathUploader.py @@ -10,7 +10,7 @@ from .Models.CloudPrintJobResponse import CloudPrintJobResponse ## Class responsible for uploading meshes to the cloud in separate requests. -class MeshUploader: +class ToolPathUploader: # The maximum amount of times to retry if the server returns one of the RETRY_HTTP_CODES MAX_RETRIES = 10 diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py index 96137a3edb..8b72c9d62d 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py @@ -125,5 +125,4 @@ class TestCloudOutputDeviceManager(TestCase): } self.network.prepareReply("GET", self.URL, 200, self.clusters_response) self._loadData() - message_mock.assert_called_once_with(text='Not found!', title='Error', lifetime=10, dismissable=True) message_mock.return_value.show.assert_called_once_with() From 801a43c874dabc93c2b1ad8555b457544d16f55e Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Fri, 11 Jan 2019 17:04:51 +0100 Subject: [PATCH 1210/1240] Add a small margin in the right so the close button is never behind the scrollbar Contributes to CURA-6005. --- plugins/CuraDrive/src/qml/components/BackupList.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/CuraDrive/src/qml/components/BackupList.qml b/plugins/CuraDrive/src/qml/components/BackupList.qml index afa9538486..a4a460a885 100644 --- a/plugins/CuraDrive/src/qml/components/BackupList.qml +++ b/plugins/CuraDrive/src/qml/components/BackupList.qml @@ -11,13 +11,15 @@ ScrollView { property alias model: backupList.model width: parent.width + clip: true ListView { id: backupList width: parent.width delegate: Item { - width: parent.width + // Add a margin, otherwise the scrollbar is on top of the right most component + width: parent.width - UM.Theme.getSize("default_margin").width height: childrenRect.height BackupListItem From 7b3cf6be64e5e243baa9bf4f21bf0f55d3e1c897 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Fri, 11 Jan 2019 17:32:15 +0100 Subject: [PATCH 1211/1240] Fix resizable Settings-Panel for change in window size. [CURA-6054] --- .../qml/PrintSetupSelector/PrintSetupSelectorContents.qml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml index 0b8fb89311..edf5d952a5 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml @@ -77,7 +77,12 @@ Item target: UM.Preferences onPreferenceChanged: { - customPrintSetup.height = UM.Preferences.getValue("view/settings_list_height"); + customPrintSetup.height = + Math.min + ( + UM.Preferences.getValue("view/settings_list_height"), + base.height - (customPrintSetup.mapToItem(null, 0, 0).y + buttonRow.height) + ); } } visible: currentModeIndex == PrintSetupSelectorContents.Mode.Custom From c81689d23361af8d8a86bba129e0336112baf819 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 11 Jan 2019 17:40:36 +0100 Subject: [PATCH 1212/1240] Prevent a recurring qml warning Note that the connect_group metadata is also queried for non-connected printers; the model is not filtered, but non-connected printers are set to be invisible. --- resources/qml/Menus/NetworkPrinterMenu.qml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/resources/qml/Menus/NetworkPrinterMenu.qml b/resources/qml/Menus/NetworkPrinterMenu.qml index 3cb0aae016..41f3054e92 100644 --- a/resources/qml/Menus/NetworkPrinterMenu.qml +++ b/resources/qml/Menus/NetworkPrinterMenu.qml @@ -12,10 +12,18 @@ Instantiator model: Cura.GlobalStacksModel {} MenuItem { - text: model.metadata["connect_group_name"] + property string connectGroupName: + { + if("connect_group_name" in model.metadata) + { + return model.metadata["connect_group_name"] + } + return "" + } + text: connectGroupName checkable: true visible: model.hasRemoteConnection - checked: Cura.MachineManager.activeMachineNetworkGroupName == model.metadata["connect_group_name"] + checked: Cura.MachineManager.activeMachineNetworkGroupName == connectGroupName exclusiveGroup: group onTriggered: Cura.MachineManager.setActiveMachine(model.id) } From c6fa47e584a38998809916d6d5ff694afe21f75a Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 11 Jan 2019 17:52:28 +0100 Subject: [PATCH 1213/1240] Mark PrinterOutputDevice without printer type as invalid Since self._printer_type was defined as an empty string and the setter only accepts strings, it could never be None --- cura/PrinterOutput/ConfigurationModel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/PrinterOutput/ConfigurationModel.py b/cura/PrinterOutput/ConfigurationModel.py index 89e609c913..f9d0c7e36b 100644 --- a/cura/PrinterOutput/ConfigurationModel.py +++ b/cura/PrinterOutput/ConfigurationModel.py @@ -54,7 +54,7 @@ class ConfigurationModel(QObject): for configuration in self._extruder_configurations: if configuration is None: return False - return self._printer_type is not None + return self._printer_type != "" def __str__(self): message_chunks = [] From d03ec22dee514c305edc8e4482b9e43584f2c7b0 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 11 Jan 2019 17:55:06 +0100 Subject: [PATCH 1214/1240] Hide empty abbreviated machinename Connected printers that are not Cura Connect printers may not have a connect_group_name. --- resources/qml/PrinterSelector/MachineSelector.qml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index 9f0d3b4ac6..0a2e304efc 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -26,7 +26,14 @@ Cura.ExpandablePopup headerItem: Cura.IconWithText { - text: isNetworkPrinter ? Cura.MachineManager.activeMachineNetworkGroupName : Cura.MachineManager.activeMachineName + text: + { + if (isNetworkPrinter && Cura.MachineManager.activeMachineNetworkGroupName != "") + { + Cura.MachineManager.activeMachineNetworkGroupName + } + return Cura.MachineManager.activeMachineName + } source: { if (isNetworkPrinter) From f8bfca6079569835550a80f4eba2095d97cc53a3 Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Fri, 11 Jan 2019 18:27:44 +0100 Subject: [PATCH 1215/1240] Don't show auto configuration selection when there are no configurations --- resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml index 7d09f4be38..86cb7f9acf 100644 --- a/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml +++ b/resources/qml/Menus/ConfigurationMenu/ConfigurationMenu.qml @@ -136,9 +136,9 @@ Cura.ExpandablePopup onVisibleChanged: { - is_connected = Cura.MachineManager.activeMachineHasRemoteConnection && Cura.MachineManager.printerConnected //Re-evaluate. + is_connected = Cura.MachineManager.activeMachineHasRemoteConnection && Cura.MachineManager.printerConnected && Cura.MachineManager.printerOutputDevices[0].uniqueConfigurations.length > 0 //Re-evaluate. - // If the printer is not connected, we switch always to the custom mode. If is connected instead, the auto mode + // If the printer is not connected or does not have configurations, we switch always to the custom mode. If is connected instead, the auto mode // or the previous state is selected configuration_method = is_connected ? (manual_selected_method == -1 ? ConfigurationMenu.ConfigurationMethod.Auto : manual_selected_method) : ConfigurationMenu.ConfigurationMethod.Custom } From 81abc84741c161ead9c8a7b7825e7b714ad54292 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 14 Jan 2019 10:41:10 +0100 Subject: [PATCH 1216/1240] Fix typing of CloudOutputDevice in controller --- plugins/UM3NetworkPrinting/src/Cloud/CloudOutputController.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputController.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputController.py index c139be0c38..af98b55587 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputController.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputController.py @@ -1,11 +1,11 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from UM.OutputDevice.OutputDevice import OutputDevice from cura.PrinterOutput.PrinterOutputController import PrinterOutputController +from .CloudOutputDevice import CloudOutputDevice class CloudOutputController(PrinterOutputController): - def __init__(self, output_device: OutputDevice) -> None: + def __init__(self, output_device: CloudOutputDevice) -> None: super().__init__(output_device) # The cloud connection only supports fetching the printer and queue status and adding a job to the queue. From 13d390a7d9dc0949262107fd445e1a10cce24e3e Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 14 Jan 2019 10:43:32 +0100 Subject: [PATCH 1217/1240] Fix imports in tests, some cleanup --- .../tests/Cloud/TestCloudApiClient.py | 17 ++++++++--------- .../tests/Cloud/TestCloudOutputDevice.py | 12 +++++------- .../tests/Cloud/TestCloudOutputDeviceManager.py | 8 ++++---- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py index acce55751c..fcaa14b055 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py @@ -1,19 +1,18 @@ # Copyright (c) 2018 Ultimaker B.V. # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -import os from typing import List from unittest import TestCase from unittest.mock import patch, MagicMock -from cura.CuraConstants import CuraCloudAPIRoot -from src.Cloud.CloudApiClient import CloudApiClient -from src.Cloud.Models.CloudClusterResponse import CloudClusterResponse -from src.Cloud.Models.CloudClusterStatus import CloudClusterStatus -from src.Cloud.Models.CloudPrintJobResponse import CloudPrintJobResponse -from src.Cloud.Models.CloudPrintJobUploadRequest import CloudPrintJobUploadRequest -from src.Cloud.Models.CloudError import CloudError -from tests.Cloud.Fixtures import readFixture, parseFixture +from cura.UltimakerCloudAuthentication import CuraCloudAPIRoot +from ...src.Cloud.CloudApiClient import CloudApiClient +from ...src.Cloud.Models.CloudClusterResponse import CloudClusterResponse +from ...src.Cloud.Models.CloudClusterStatus import CloudClusterStatus +from ...src.Cloud.Models.CloudPrintJobResponse import CloudPrintJobResponse +from ...src.Cloud.Models.CloudPrintJobUploadRequest import CloudPrintJobUploadRequest +from ...src.Cloud.Models.CloudError import CloudError +from .Fixtures import readFixture, parseFixture from .NetworkManagerMock import NetworkManagerMock diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py index 34e04689c2..0e2220ee04 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py @@ -5,14 +5,12 @@ from unittest import TestCase from unittest.mock import patch, MagicMock from UM.Scene.SceneNode import SceneNode -from UM.Signal import Signal -from cura.CuraApplication import CuraApplication -from cura.CuraConstants import CuraCloudAPIRoot +from cura.UltimakerCloudAuthentication import CuraCloudAPIRoot from cura.PrinterOutput.PrinterOutputModel import PrinterOutputModel -from src.Cloud.CloudApiClient import CloudApiClient -from src.Cloud.CloudOutputDevice import CloudOutputDevice -from src.Cloud.Models.CloudClusterResponse import CloudClusterResponse -from tests.Cloud.Fixtures import readFixture, parseFixture +from ...src.Cloud.CloudApiClient import CloudApiClient +from ...src.Cloud.CloudOutputDevice import CloudOutputDevice +from ...src.Cloud.Models.CloudClusterResponse import CloudClusterResponse +from .Fixtures import readFixture, parseFixture from .NetworkManagerMock import NetworkManagerMock diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py index 8b72c9d62d..5388cf152d 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py @@ -4,10 +4,10 @@ from unittest import TestCase from unittest.mock import patch, MagicMock from UM.OutputDevice.OutputDeviceManager import OutputDeviceManager -from cura.CuraConstants import CuraCloudAPIRoot -from src.Cloud.CloudOutputDevice import CloudOutputDevice -from src.Cloud.CloudOutputDeviceManager import CloudOutputDeviceManager -from tests.Cloud.Fixtures import parseFixture, readFixture +from cura.UltimakerCloudAuthentication import CuraCloudAPIRoot +from ...src.Cloud.CloudOutputDevice import CloudOutputDevice +from ...src.Cloud.CloudOutputDeviceManager import CloudOutputDeviceManager +from .Fixtures import parseFixture, readFixture from .NetworkManagerMock import NetworkManagerMock, FakeSignal From 1e2a8aa23e366491bbe0597d3eeca204e27f6b8d Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 14 Jan 2019 10:52:50 +0100 Subject: [PATCH 1218/1240] revert to prevent circular dependency --- plugins/UM3NetworkPrinting/src/Cloud/CloudOutputController.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputController.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputController.py index af98b55587..ec89b1d6c4 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputController.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputController.py @@ -1,11 +1,11 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from UM.OutputDevice.OutputDevice import OutputDevice from cura.PrinterOutput.PrinterOutputController import PrinterOutputController -from .CloudOutputDevice import CloudOutputDevice class CloudOutputController(PrinterOutputController): - def __init__(self, output_device: CloudOutputDevice) -> None: + def __init__(self, output_device: "OutputDevice") -> None: super().__init__(output_device) # The cloud connection only supports fetching the printer and queue status and adding a job to the queue. From 29bb31729350ebeada5c61be6ade7e16b8499443 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 14 Jan 2019 10:54:28 +0100 Subject: [PATCH 1219/1240] Add a margin to settings panel bottom CURA-6054 --- resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml index edf5d952a5..7c82a7324d 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorContents.qml @@ -81,7 +81,7 @@ Item Math.min ( UM.Preferences.getValue("view/settings_list_height"), - base.height - (customPrintSetup.mapToItem(null, 0, 0).y + buttonRow.height) + base.height - (customPrintSetup.mapToItem(null, 0, 0).y + buttonRow.height + UM.Theme.getSize("default_margin").height) ); } } From 402097f4d087867906ed2b4e67f484fe552900ed Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 14 Jan 2019 10:57:50 +0100 Subject: [PATCH 1220/1240] Fix imports --- .../src/Cloud/Models/CloudClusterPrintJobStatus.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobStatus.py b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobStatus.py index 5d62471710..45b7d838a5 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobStatus.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/Models/CloudClusterPrintJobStatus.py @@ -3,17 +3,15 @@ from typing import List, Optional, Union, Dict, Any from cura.PrinterOutput.ConfigurationModel import ConfigurationModel -from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputController import CloudOutputController -from plugins.UM3NetworkPrinting.src.ConfigurationChangeModel import ConfigurationChangeModel +from ...UM3PrintJobOutputModel import UM3PrintJobOutputModel +from ...ConfigurationChangeModel import ConfigurationChangeModel +from ..CloudOutputController import CloudOutputController +from .BaseCloudModel import BaseCloudModel from .CloudClusterBuildPlate import CloudClusterBuildPlate from .CloudClusterPrintJobConfigurationChange import CloudClusterPrintJobConfigurationChange from .CloudClusterPrintJobImpediment import CloudClusterPrintJobImpediment from .CloudClusterPrintCoreConfiguration import CloudClusterPrintCoreConfiguration from .CloudClusterPrintJobConstraint import CloudClusterPrintJobConstraints -from .BaseCloudModel import BaseCloudModel - -## Class representing a print job -from plugins.UM3NetworkPrinting.src.UM3PrintJobOutputModel import UM3PrintJobOutputModel ## Model for the status of a single print job in a cluster. From cd19eec98a8e69f0f454276ae7be8e6d8e62b95c Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 14 Jan 2019 11:06:16 +0100 Subject: [PATCH 1221/1240] Remove weird argument for timer --- .../UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index f9bd635bbd..c282463e55 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -42,7 +42,7 @@ class CloudOutputDeviceManager: self._api = CloudApiClient(self._account, self._onApiError) # Create a timer to update the remote cluster list - self._update_timer = QTimer(application) + self._update_timer = QTimer() self._update_timer.setInterval(int(self.CHECK_CLUSTER_INTERVAL * 1000)) self._update_timer.setSingleShot(False) From 71036abd870ce0b517b5c8636a6444505cd9cff1 Mon Sep 17 00:00:00 2001 From: Lipu Fei Date: Mon, 14 Jan 2019 12:11:41 +0100 Subject: [PATCH 1222/1240] Add troubleshooting link for recommended CURA-6114 --- .../Recommended/RecommendedPrintSetup.qml | 5 +++ .../RecommendedTroubleshootingGuides.qml | 36 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 resources/qml/PrintSetupSelector/Recommended/RecommendedTroubleshootingGuides.qml diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml index 6885f8c041..44b3abf7cd 100644 --- a/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedPrintSetup.qml @@ -68,6 +68,11 @@ Item // TODO Create a reusable component with these properties to not define them separately for each component labelColumnWidth: parent.firstColumnWidth } + + RecommendedTroubleshootingGuides + { + width: parent.width + } } UM.SettingPropertyProvider diff --git a/resources/qml/PrintSetupSelector/Recommended/RecommendedTroubleshootingGuides.qml b/resources/qml/PrintSetupSelector/Recommended/RecommendedTroubleshootingGuides.qml new file mode 100644 index 0000000000..846e343028 --- /dev/null +++ b/resources/qml/PrintSetupSelector/Recommended/RecommendedTroubleshootingGuides.qml @@ -0,0 +1,36 @@ +// Copyright (c) 2019 Ultimaker B.V. +// Cura is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.10 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 + +import UM 1.2 as UM +import Cura 1.0 as Cura + + +Item +{ + id: tipsCell + anchors.top: adhesionCheckBox.visible ? adhesionCheckBox.bottom : (enableSupportCheckBox.visible ? supportExtruderCombobox.bottom : infillCellRight.bottom) + anchors.topMargin: Math.round(UM.Theme.getSize("sidebar_margin").height * 2) + anchors.left: parent.left + width: parent.width + height: tipsText.contentHeight * tipsText.lineCount + + Label + { + id: tipsText + anchors.left: parent.left + anchors.leftMargin: UM.Theme.getSize("sidebar_margin").width + anchors.right: parent.right + anchors.rightMargin: UM.Theme.getSize("sidebar_margin").width + anchors.top: parent.top + wrapMode: Text.WordWrap + text: catalog.i18nc("@label", "Need help improving your prints?
    Read the Ultimaker Troubleshooting Guides").arg("https://ultimaker.com/en/troubleshooting") + font: UM.Theme.getFont("default") + color: UM.Theme.getColor("text") + linkColor: UM.Theme.getColor("text_link") + onLinkActivated: Qt.openUrlExternally(link) + } +} \ No newline at end of file From 9d94b55596ea9b805d9088b46a498d7a648fe09a Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 14 Jan 2019 13:35:10 +0100 Subject: [PATCH 1223/1240] Ensure that the toolbox correctly handles multiple supported SDK versions CURA-6087 --- plugins/Toolbox/src/Toolbox.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index 192471a357..ccd181cdbc 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -235,12 +235,17 @@ class Toolbox(QObject, Extension): def _convertPluginMetadata(self, plugin_data: Dict[str, Any]) -> Optional[Dict[str, Any]]: try: + highest_sdk_version_supported = Version(0) + for supported_version in plugin_data["plugin"]["supported_sdk_versions"]: + if supported_version > highest_sdk_version_supported: + highest_sdk_version_supported = supported_version + formatted = { "package_id": plugin_data["id"], "package_type": "plugin", "display_name": plugin_data["plugin"]["name"], "package_version": plugin_data["plugin"]["version"], - "sdk_version": plugin_data["plugin"]["api"], + "sdk_version": highest_sdk_version_supported, "author": { "author_id": plugin_data["plugin"]["author"], "display_name": plugin_data["plugin"]["author"] From b1b61fa466eafa51121ce7a276dc2046d5f38ea7 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Mon, 14 Jan 2019 14:14:47 +0100 Subject: [PATCH 1224/1240] Add missing return --- resources/qml/PrinterSelector/MachineSelector.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index 0a2e304efc..4bee917751 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -30,7 +30,7 @@ Cura.ExpandablePopup { if (isNetworkPrinter && Cura.MachineManager.activeMachineNetworkGroupName != "") { - Cura.MachineManager.activeMachineNetworkGroupName + return Cura.MachineManager.activeMachineNetworkGroupName } return Cura.MachineManager.activeMachineName } From 7bf319dfd12ce82385ff2c290dfdceb64921b778 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 14 Jan 2019 14:17:36 +0100 Subject: [PATCH 1225/1240] Better fix for circular dependency --- .../UM3NetworkPrinting/src/Cloud/CloudOutputController.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputController.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputController.py index ec89b1d6c4..bd56ef3185 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputController.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputController.py @@ -1,11 +1,14 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from UM.OutputDevice.OutputDevice import OutputDevice from cura.PrinterOutput.PrinterOutputController import PrinterOutputController +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from .CloudOutputDevice import CloudOutputDevice + class CloudOutputController(PrinterOutputController): - def __init__(self, output_device: "OutputDevice") -> None: + def __init__(self, output_device: "CloudOutputDevice") -> None: super().__init__(output_device) # The cloud connection only supports fetching the printer and queue status and adding a job to the queue. From f478653c371ee9a97fc1f44bdb78421f108d1afe Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Mon, 14 Jan 2019 14:56:14 +0100 Subject: [PATCH 1226/1240] Uncomment code that was needed for testing --- plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py index 84740ae856..b48f9380e1 100644 --- a/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/ClusterUM3OutputDevice.py @@ -389,9 +389,8 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice): ## Called when the connection to the cluster changes. def connect(self) -> None: - # super().connect() - # self.sendMaterialProfiles() - pass + super().connect() + self.sendMaterialProfiles() def _onGetPreviewImageFinished(self, reply: QNetworkReply) -> None: reply_url = reply.url().toString() From fa7b38243c8a8fd16d9fdfd98220200abd57a773 Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 14 Jan 2019 17:02:02 +0100 Subject: [PATCH 1227/1240] Add VersionUpgrade35to40 as a bundled package Otherwise it will show up as an old plugin and the uninstall button will appear in the Markeplace. --- resources/bundled_packages/cura.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/resources/bundled_packages/cura.json b/resources/bundled_packages/cura.json index 172b27b452..07a1ae5276 100644 --- a/resources/bundled_packages/cura.json +++ b/resources/bundled_packages/cura.json @@ -730,6 +730,23 @@ } } }, + "VersionUpgrade35to40": { + "package_info": { + "package_id": "VersionUpgrade35to40", + "package_type": "plugin", + "display_name": "Version Upgrade 3.5 to 4.0", + "description": "Upgrades configurations from Cura 3.5 to Cura 4.0.", + "package_version": "1.0.0", + "sdk_version": "6.0", + "website": "https://ultimaker.com", + "author": { + "author_id": "UltimakerPackages", + "display_name": "Ultimaker B.V.", + "email": "plugins@ultimaker.com", + "website": "https://ultimaker.com" + } + } + }, "X3DReader": { "package_info": { "package_id": "X3DReader", From f16735a5bed7c905016c32352b433a5353d4482c Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Mon, 14 Jan 2019 17:10:52 +0100 Subject: [PATCH 1228/1240] Add VersionUpgrade40to41 as a bundled package Otherwise it will show up as an old plugin and the uninstall button will appear in the Markeplace. --- resources/bundled_packages/cura.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/resources/bundled_packages/cura.json b/resources/bundled_packages/cura.json index 709ffd66e6..21da1d9fdb 100644 --- a/resources/bundled_packages/cura.json +++ b/resources/bundled_packages/cura.json @@ -747,6 +747,23 @@ } } }, + "VersionUpgrade40to41": { + "package_info": { + "package_id": "VersionUpgrade40to41", + "package_type": "plugin", + "display_name": "Version Upgrade 4.0 to 4.1", + "description": "Upgrades configurations from Cura 4.0 to Cura 4.1.", + "package_version": "1.0.1", + "sdk_version": "6.0", + "website": "https://ultimaker.com", + "author": { + "author_id": "UltimakerPackages", + "display_name": "Ultimaker B.V.", + "email": "plugins@ultimaker.com", + "website": "https://ultimaker.com" + } + } + }, "X3DReader": { "package_info": { "package_id": "X3DReader", From 77c30c891f92d68e00dde62044317c90b978a706 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Tue, 15 Jan 2019 08:20:39 +0100 Subject: [PATCH 1229/1240] Fix tests --- cura/UltimakerCloudAuthentication.py | 2 +- .../src/Cloud/CloudOutputDeviceManager.py | 2 +- .../src/Cloud/CloudProgressMessage.py | 5 ++++ .../tests/Cloud/TestCloudApiClient.py | 2 +- .../tests/Cloud/TestCloudOutputDevice.py | 3 +- .../Cloud/TestCloudOutputDeviceManager.py | 30 ++++++++----------- 6 files changed, 23 insertions(+), 21 deletions(-) diff --git a/cura/UltimakerCloudAuthentication.py b/cura/UltimakerCloudAuthentication.py index 5f69329dbb..69bb577354 100644 --- a/cura/UltimakerCloudAuthentication.py +++ b/cura/UltimakerCloudAuthentication.py @@ -5,7 +5,7 @@ # Constants used for the Cloud API # --------- DEFAULT_CLOUD_API_ROOT = "https://api.ultimaker.com" # type: str -DEFAULT_CLOUD_API_VERSION = 1 # type: int +DEFAULT_CLOUD_API_VERSION = "1" # type: str DEFAULT_CLOUD_ACCOUNT_API_ROOT = "https://account.ultimaker.com" # type: str try: diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index c282463e55..237947b21e 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -101,7 +101,7 @@ class CloudOutputDeviceManager: if not active_machine: return - # Remove all output devices that we have registered. + # Remove all output devices that we have registered. TODO: Why?? for stored_cluster_id in self._remote_clusters: self._output_device_manager.removeOutputDevice(stored_cluster_id) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudProgressMessage.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudProgressMessage.py index d85f49c1a0..32aa6044d3 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudProgressMessage.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudProgressMessage.py @@ -19,6 +19,11 @@ class CloudProgressMessage(Message): use_inactivity_timer = False ) + ## Returns a boolean indicating whether this message is currently visible + @property + def visible(self) -> bool: + return self._visible + ## Shows the progress message. def show(self): self.setProgress(0) diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py index fcaa14b055..0be1d82141 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py @@ -28,7 +28,7 @@ class TestCloudApiClient(TestCase): self.account.isLoggedIn.return_value = True self.network = NetworkManagerMock() - with patch("src.Cloud.CloudApiClient.QNetworkAccessManager", return_value = self.network): + with patch("plugins.UM3NetworkPrinting.src.Cloud.CloudApiClient.QNetworkAccessManager", return_value = self.network): self.api = CloudApiClient(self.account, self._errorHandler) def test_getClusters(self): diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py index 0e2220ee04..191b92bdd5 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py @@ -41,7 +41,8 @@ class TestCloudOutputDevice(TestCase): self.network = NetworkManagerMock() self.account = MagicMock(isLoggedIn=True, accessToken="TestAccessToken") self.onError = MagicMock() - with patch("src.Cloud.CloudApiClient.QNetworkAccessManager", return_value = self.network): + with patch("plugins.UM3NetworkPrinting.src.Cloud.CloudApiClient.QNetworkAccessManager", + return_value = self.network): self._api = CloudApiClient(self.account, self.onError) self.device = CloudOutputDevice(self._api, self.cluster) diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py index 5388cf152d..c5006f35a1 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py @@ -5,7 +5,6 @@ from unittest.mock import patch, MagicMock from UM.OutputDevice.OutputDeviceManager import OutputDeviceManager from cura.UltimakerCloudAuthentication import CuraCloudAPIRoot -from ...src.Cloud.CloudOutputDevice import CloudOutputDevice from ...src.Cloud.CloudOutputDeviceManager import CloudOutputDeviceManager from .Fixtures import parseFixture, readFixture from .NetworkManagerMock import NetworkManagerMock, FakeSignal @@ -29,8 +28,10 @@ class TestCloudOutputDeviceManager(TestCase): self.network = NetworkManagerMock() self.timer = MagicMock(timeout = FakeSignal()) - with patch("src.Cloud.CloudApiClient.QNetworkAccessManager", return_value = self.network), \ - patch("src.Cloud.CloudOutputDeviceManager.QTimer", return_value = self.timer): + with patch("plugins.UM3NetworkPrinting.src.Cloud.CloudApiClient.QNetworkAccessManager", + return_value = self.network), \ + patch("plugins.UM3NetworkPrinting.src.Cloud.CloudOutputDeviceManager.QTimer", + return_value = self.timer): self.manager = CloudOutputDeviceManager() self.clusters_response = parseFixture("getClusters") self.network.prepareReply("GET", self.URL, 200, readFixture("getClusters")) @@ -53,16 +54,12 @@ class TestCloudOutputDeviceManager(TestCase): self.network.flushReplies() # get the created devices devices = self.device_manager.getOutputDevices() - # get the server data - clusters = self.clusters_response.get("data", []) - self.assertEqual([CloudOutputDevice] * len(clusters), [type(d) for d in devices]) - self.assertEqual({cluster["cluster_id"] for cluster in clusters}, {device.key for device in devices}) - self.assertEqual(clusters, sorted((device.clusterData.toDict() for device in devices), - key=lambda device_dict: device_dict["host_version"])) + # TODO: Check active device - for device in clusters: - self.device_manager.getOutputDevice(device["cluster_id"]).close() - self.device_manager.removeOutputDevice(device["cluster_id"]) + response_clusters = self.clusters_response.get("data", []) + manager_clusters = sorted([device.clusterData.toDict() for device in self.manager._remote_clusters.values()], + key=lambda cluster: cluster['cluster_id'], reverse=True) + self.assertEqual(response_clusters, manager_clusters) ## Runs the initial request to retrieve the clusters. def _loadData(self): @@ -100,7 +97,7 @@ class TestCloudOutputDeviceManager(TestCase): self._loadData() self.assertTrue(self.device_manager.getOutputDevice(cluster1["cluster_id"]).isConnected()) - self.assertFalse(self.device_manager.getOutputDevice(cluster2["cluster_id"]).isConnected()) + self.assertIsNone(self.device_manager.getOutputDevice(cluster2["cluster_id"])) self.assertEquals([], active_machine_mock.setMetaDataEntry.mock_calls) def test_device_connects_by_network_key(self): @@ -112,13 +109,12 @@ class TestCloudOutputDeviceManager(TestCase): self._loadData() - self.assertEqual([False, True], - [self.device_manager.getOutputDevice(cluster["cluster_id"]).isConnected() - for cluster in (cluster1, cluster2)]) + self.assertIsNone(self.device_manager.getOutputDevice(cluster1["cluster_id"])) + self.assertTrue(self.device_manager.getOutputDevice(cluster2["cluster_id"]).isConnected()) active_machine_mock.setMetaDataEntry.assert_called_with("um_cloud_cluster_id", cluster2["cluster_id"]) - @patch("src.Cloud.CloudOutputDeviceManager.Message") + @patch("plugins.UM3NetworkPrinting.src.Cloud.CloudOutputDeviceManager.Message") def test_api_error(self, message_mock): self.clusters_response = { "errors": [{"id": "notFound", "title": "Not found!", "http_status": "404", "code": "notFound"}] From 7785555a943b600636809ae5c5e50cabecdd79fd Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Tue, 15 Jan 2019 08:21:17 +0100 Subject: [PATCH 1230/1240] Remove mock changes used locally --- .../UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py | 2 +- .../UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py | 2 +- .../tests/Cloud/TestCloudOutputDeviceManager.py | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py index 0be1d82141..fcaa14b055 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py @@ -28,7 +28,7 @@ class TestCloudApiClient(TestCase): self.account.isLoggedIn.return_value = True self.network = NetworkManagerMock() - with patch("plugins.UM3NetworkPrinting.src.Cloud.CloudApiClient.QNetworkAccessManager", return_value = self.network): + with patch("src.Cloud.CloudApiClient.QNetworkAccessManager", return_value = self.network): self.api = CloudApiClient(self.account, self._errorHandler) def test_getClusters(self): diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py index 191b92bdd5..1a72c170d3 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py @@ -41,7 +41,7 @@ class TestCloudOutputDevice(TestCase): self.network = NetworkManagerMock() self.account = MagicMock(isLoggedIn=True, accessToken="TestAccessToken") self.onError = MagicMock() - with patch("plugins.UM3NetworkPrinting.src.Cloud.CloudApiClient.QNetworkAccessManager", + with patch("src.Cloud.CloudApiClient.QNetworkAccessManager", return_value = self.network): self._api = CloudApiClient(self.account, self.onError) diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py index c5006f35a1..6fe17f7759 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py @@ -28,9 +28,9 @@ class TestCloudOutputDeviceManager(TestCase): self.network = NetworkManagerMock() self.timer = MagicMock(timeout = FakeSignal()) - with patch("plugins.UM3NetworkPrinting.src.Cloud.CloudApiClient.QNetworkAccessManager", + with patch("src.Cloud.CloudApiClient.QNetworkAccessManager", return_value = self.network), \ - patch("plugins.UM3NetworkPrinting.src.Cloud.CloudOutputDeviceManager.QTimer", + patch("src.Cloud.CloudOutputDeviceManager.QTimer", return_value = self.timer): self.manager = CloudOutputDeviceManager() self.clusters_response = parseFixture("getClusters") @@ -114,7 +114,7 @@ class TestCloudOutputDeviceManager(TestCase): active_machine_mock.setMetaDataEntry.assert_called_with("um_cloud_cluster_id", cluster2["cluster_id"]) - @patch("plugins.UM3NetworkPrinting.src.Cloud.CloudOutputDeviceManager.Message") + @patch("src.Cloud.CloudOutputDeviceManager.Message") def test_api_error(self, message_mock): self.clusters_response = { "errors": [{"id": "notFound", "title": "Not found!", "http_status": "404", "code": "notFound"}] From de24c7d9c34e5bd80e6311e4d1c7ea1e1eaaea98 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 15 Jan 2019 09:35:28 +0100 Subject: [PATCH 1231/1240] Revert "Remove mock changes used locally" This reverts commit 7785555a943b600636809ae5c5e50cabecdd79fd. --- .../UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py | 2 +- .../UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py | 2 +- .../tests/Cloud/TestCloudOutputDeviceManager.py | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py index fcaa14b055..0be1d82141 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudApiClient.py @@ -28,7 +28,7 @@ class TestCloudApiClient(TestCase): self.account.isLoggedIn.return_value = True self.network = NetworkManagerMock() - with patch("src.Cloud.CloudApiClient.QNetworkAccessManager", return_value = self.network): + with patch("plugins.UM3NetworkPrinting.src.Cloud.CloudApiClient.QNetworkAccessManager", return_value = self.network): self.api = CloudApiClient(self.account, self._errorHandler) def test_getClusters(self): diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py index 1a72c170d3..191b92bdd5 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDevice.py @@ -41,7 +41,7 @@ class TestCloudOutputDevice(TestCase): self.network = NetworkManagerMock() self.account = MagicMock(isLoggedIn=True, accessToken="TestAccessToken") self.onError = MagicMock() - with patch("src.Cloud.CloudApiClient.QNetworkAccessManager", + with patch("plugins.UM3NetworkPrinting.src.Cloud.CloudApiClient.QNetworkAccessManager", return_value = self.network): self._api = CloudApiClient(self.account, self.onError) diff --git a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py index 6fe17f7759..c5006f35a1 100644 --- a/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/tests/Cloud/TestCloudOutputDeviceManager.py @@ -28,9 +28,9 @@ class TestCloudOutputDeviceManager(TestCase): self.network = NetworkManagerMock() self.timer = MagicMock(timeout = FakeSignal()) - with patch("src.Cloud.CloudApiClient.QNetworkAccessManager", + with patch("plugins.UM3NetworkPrinting.src.Cloud.CloudApiClient.QNetworkAccessManager", return_value = self.network), \ - patch("src.Cloud.CloudOutputDeviceManager.QTimer", + patch("plugins.UM3NetworkPrinting.src.Cloud.CloudOutputDeviceManager.QTimer", return_value = self.timer): self.manager = CloudOutputDeviceManager() self.clusters_response = parseFixture("getClusters") @@ -114,7 +114,7 @@ class TestCloudOutputDeviceManager(TestCase): active_machine_mock.setMetaDataEntry.assert_called_with("um_cloud_cluster_id", cluster2["cluster_id"]) - @patch("src.Cloud.CloudOutputDeviceManager.Message") + @patch("plugins.UM3NetworkPrinting.src.Cloud.CloudOutputDeviceManager.Message") def test_api_error(self, message_mock): self.clusters_response = { "errors": [{"id": "notFound", "title": "Not found!", "http_status": "404", "code": "notFound"}] From b7fb969524fc8dbb3ebbe66b26a2598c9791cfba Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 15 Jan 2019 09:37:38 +0100 Subject: [PATCH 1232/1240] Fixes for review --- .../UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py | 4 +++- plugins/UM3NetworkPrinting/src/Cloud/CloudProgressMessage.py | 5 ----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 237947b21e..541b30b05d 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -101,7 +101,9 @@ class CloudOutputDeviceManager: if not active_machine: return - # Remove all output devices that we have registered. TODO: Why?? + # Remove all output devices that we have registered. + # This is needed because when we switch machines we can only leave + # output devices that are meant for that machine. for stored_cluster_id in self._remote_clusters: self._output_device_manager.removeOutputDevice(stored_cluster_id) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudProgressMessage.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudProgressMessage.py index 32aa6044d3..d85f49c1a0 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudProgressMessage.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudProgressMessage.py @@ -19,11 +19,6 @@ class CloudProgressMessage(Message): use_inactivity_timer = False ) - ## Returns a boolean indicating whether this message is currently visible - @property - def visible(self) -> bool: - return self._visible - ## Shows the progress message. def show(self): self.setProgress(0) From ed96d32aca2a84c05ddfc63705e43212891b0741 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 15 Jan 2019 09:59:17 +0100 Subject: [PATCH 1233/1240] trigger re-build From d4b47c75abf8fe3445b9b906b45d2af0b84a5cfb Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Tue, 15 Jan 2019 11:15:37 +0100 Subject: [PATCH 1234/1240] Fix typo in multiply message --- cura/MultiplyObjectsJob.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/MultiplyObjectsJob.py b/cura/MultiplyObjectsJob.py index 3cbf795952..e71bbf6668 100644 --- a/cura/MultiplyObjectsJob.py +++ b/cura/MultiplyObjectsJob.py @@ -25,7 +25,7 @@ class MultiplyObjectsJob(Job): def run(self): status_message = Message(i18n_catalog.i18nc("@info:status", "Multiplying and placing objects"), lifetime=0, - dismissable=False, progress=0, title = i18n_catalog.i18nc("@info:title", "Placing Object")) + dismissable=False, progress=0, title = i18n_catalog.i18nc("@info:title", "Placing Objects")) status_message.show() scene = Application.getInstance().getController().getScene() From a702661b5de80bbd1d0ef3063b5c0f16608992f2 Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Tue, 15 Jan 2019 14:17:38 +0100 Subject: [PATCH 1235/1240] Decrease the API call intervals to get quicker UI updates --- plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py | 2 +- .../UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py index 2947cd421c..33968beb6d 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDevice.py @@ -44,7 +44,7 @@ I18N_CATALOG = i18nCatalog("cura") class CloudOutputDevice(NetworkedPrinterOutputDevice): # The interval with which the remote clusters are checked - CHECK_CLUSTER_INTERVAL = 20.0 # seconds + CHECK_CLUSTER_INTERVAL = 10.0 # seconds # Signal triggered when the print jobs in the queue were changed. printJobsChanged = pyqtSignal() diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 541b30b05d..f9a0a59c81 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -26,7 +26,7 @@ class CloudOutputDeviceManager: META_CLUSTER_ID = "um_cloud_cluster_id" # The interval with which the remote clusters are checked - CHECK_CLUSTER_INTERVAL = 50.0 # seconds + CHECK_CLUSTER_INTERVAL = 30.0 # seconds # The translation catalog for this device. I18N_CATALOG = i18nCatalog("cura") From 1b7b302f42f82f2a5424d969b370d896febacc33 Mon Sep 17 00:00:00 2001 From: Ghostkeeper Date: Tue, 15 Jan 2019 17:31:51 +0100 Subject: [PATCH 1236/1240] Fix some typos --- cura/Scene/ConvexHullDecorator.py | 2 +- cura/Scene/ConvexHullNode.py | 2 +- plugins/ChangeLogPlugin/ChangeLog.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cura/Scene/ConvexHullDecorator.py b/cura/Scene/ConvexHullDecorator.py index 661106dec7..9bdb6a2c0e 100644 --- a/cura/Scene/ConvexHullDecorator.py +++ b/cura/Scene/ConvexHullDecorator.py @@ -242,7 +242,7 @@ class ConvexHullDecorator(SceneNodeDecorator): # See http://stackoverflow.com/questions/16970982/find-unique-rows-in-numpy-array vertex_byte_view = numpy.ascontiguousarray(vertex_data).view( numpy.dtype((numpy.void, vertex_data.dtype.itemsize * vertex_data.shape[1]))) - _, idx = numpy.unique(vertex_byte_view, return_index=True) + _, idx = numpy.unique(vertex_byte_view, return_index = True) vertex_data = vertex_data[idx] # Select the unique rows by index. hull = Polygon(vertex_data) diff --git a/cura/Scene/ConvexHullNode.py b/cura/Scene/ConvexHullNode.py index 886ed93ad3..90bf536308 100644 --- a/cura/Scene/ConvexHullNode.py +++ b/cura/Scene/ConvexHullNode.py @@ -54,7 +54,7 @@ class ConvexHullNode(SceneNode): if hull_mesh_builder.addConvexPolygonExtrusion( self._hull.getPoints()[::-1], # bottom layer is reversed - self._mesh_height-thickness, self._mesh_height, color=self._color): + self._mesh_height - thickness, self._mesh_height, color = self._color): hull_mesh = hull_mesh_builder.build() self.setMeshData(hull_mesh) diff --git a/plugins/ChangeLogPlugin/ChangeLog.txt b/plugins/ChangeLogPlugin/ChangeLog.txt index 7e5cf2dd3b..651abb0cac 100755 --- a/plugins/ChangeLogPlugin/ChangeLog.txt +++ b/plugins/ChangeLogPlugin/ChangeLog.txt @@ -943,7 +943,7 @@ This release adds support for printers with elliptic buildplates. This feature h *AppImage for Linux The Linux distribution is now in AppImage format, which makes Cura easier to install. -*bugfixes +*Bugfixes The user is now notified when a new version of Cura is available. When searching in the setting visibility preferences, the category for each setting is always displayed. 3MF files are now saved and loaded correctly. From d9a8bb0eb7940b4c606627275a6572b8ca4ccdfa Mon Sep 17 00:00:00 2001 From: ChrisTerBeke Date: Wed, 16 Jan 2019 15:18:04 +0100 Subject: [PATCH 1237/1240] Remove empty init py in plugins dir --- plugins/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 plugins/__init__.py diff --git a/plugins/__init__.py b/plugins/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 From 7c7f6a1f038aa985d857e174fdf835fb476e4681 Mon Sep 17 00:00:00 2001 From: Jaime van Kessel Date: Wed, 16 Jan 2019 15:28:16 +0100 Subject: [PATCH 1238/1240] Ensure sane defaults for the ApplicationMetada when making builds --- cura/ApplicationMetadata.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cura/ApplicationMetadata.py b/cura/ApplicationMetadata.py index e2ac4453eb..4cb19edb72 100644 --- a/cura/ApplicationMetadata.py +++ b/cura/ApplicationMetadata.py @@ -12,11 +12,15 @@ DEFAULT_CURA_SDK_VERSION = "6.0.0" try: from cura.CuraVersion import CuraAppDisplayName # type: ignore + if CuraAppDisplayName == "": + CuraAppDisplayName = DEFAULT_CURA_DISPLAY_NAME except ImportError: CuraAppDisplayName = DEFAULT_CURA_DISPLAY_NAME try: from cura.CuraVersion import CuraVersion # type: ignore + if CuraVersion == "": + CuraVersion = DEFAULT_CURA_VERSION except ImportError: CuraVersion = DEFAULT_CURA_VERSION # [CodeStyle: Reflecting imported value] @@ -32,5 +36,7 @@ except ImportError: try: from cura.CuraVersion import CuraSDKVersion # type: ignore + if CuraSDKVersion == "": + CuraSDKVersion = DEFAULT_CURA_SDK_VERSION except ImportError: CuraSDKVersion = DEFAULT_CURA_SDK_VERSION From a0dd3f06e6031b4a263b7da4735e1977a1f401a6 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 16 Jan 2019 15:41:09 +0100 Subject: [PATCH 1239/1240] Add 'Experimental' tag for quality profiles in the header. When an experimental quality profile is active, the stage panel does not show that it is experimental. It is however shown on the quality drop-down menu in Custom mode. This commit also fixes the untranslated (hardcoded) 'Experimental' string in the dropdown in Custom. Fixes the issue reported in CURA-6118. --- resources/qml/Menus/ProfileMenu.qml | 2 +- resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/qml/Menus/ProfileMenu.qml b/resources/qml/Menus/ProfileMenu.qml index bf950aa409..68260f2502 100644 --- a/resources/qml/Menus/ProfileMenu.qml +++ b/resources/qml/Menus/ProfileMenu.qml @@ -20,7 +20,7 @@ Menu text: { var full_text = (model.layer_height != "") ? model.name + " - " + model.layer_height + model.layer_height_unit : model.name - full_text += model.is_experimental ? " - Experimental" : "" + full_text += model.is_experimental ? " - " + catalog.i18nc("@label", "Experimental") : "" return full_text } checkable: true diff --git a/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml b/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml index 5ae9488cd3..96b244d803 100644 --- a/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml +++ b/resources/qml/PrintSetupSelector/PrintSetupSelectorHeader.qml @@ -24,6 +24,7 @@ RowLayout if (!Cura.MachineManager.hasNotSupportedQuality) { text += " " + layerHeight.properties.value + "mm" + text += Cura.MachineManager.isActiveQualityExperimental ? " - " + catalog.i18nc("@label", "Experimental") : "" } return text } From c28aefac4ed0eeca2d96a0459c168cb89df87c3b Mon Sep 17 00:00:00 2001 From: Diego Prado Gesto Date: Wed, 16 Jan 2019 17:33:14 +0100 Subject: [PATCH 1240/1240] Add the maximum backups message to the backups plugin It didn't work after previous changes, so now it will show again when the user already has 5 backups. Contributes to CURA-6005 --- plugins/CuraDrive/src/qml/components/BackupListFooter.qml | 2 +- plugins/CuraDrive/src/qml/pages/BackupsPage.qml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/CuraDrive/src/qml/components/BackupListFooter.qml b/plugins/CuraDrive/src/qml/components/BackupListFooter.qml index 56706b9990..8decdc5c27 100644 --- a/plugins/CuraDrive/src/qml/components/BackupListFooter.qml +++ b/plugins/CuraDrive/src/qml/components/BackupListFooter.qml @@ -30,7 +30,7 @@ RowLayout id: createBackupButton text: catalog.i18nc("@button", "Backup Now") iconSource: UM.Theme.getIcon("plus") - enabled: !CuraDrive.isCreatingBackup && !CuraDrive.isRestoringBackup && !backupListFooter.showInfoButton + enabled: !CuraDrive.isCreatingBackup && !CuraDrive.isRestoringBackup onClicked: CuraDrive.createBackup() busy: CuraDrive.isCreatingBackup } diff --git a/plugins/CuraDrive/src/qml/pages/BackupsPage.qml b/plugins/CuraDrive/src/qml/pages/BackupsPage.qml index 0ba0cae09b..c337294744 100644 --- a/plugins/CuraDrive/src/qml/pages/BackupsPage.qml +++ b/plugins/CuraDrive/src/qml/pages/BackupsPage.qml @@ -40,7 +40,7 @@ Item font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") wrapMode: Label.WordWrap - visible: backupList.count == 0 + visible: backupList.model.length == 0 Layout.fillWidth: true Layout.fillHeight: true renderType: Text.NativeRendering @@ -62,14 +62,14 @@ Item font: UM.Theme.getFont("default") color: UM.Theme.getColor("text") wrapMode: Label.WordWrap - visible: backupList.count > 4 + visible: backupList.model.length > 4 renderType: Text.NativeRendering } BackupListFooter { id: backupListFooter - showInfoButton: backupList.count > 4 + showInfoButton: backupList.model.length > 4 } } }

    fcgH6pO(=&uw9i7{^0?z7UA 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

    yIHgERaU=JNy7=q=;-jo7etgJi>{^i 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­1b1ri#7y-f4`n@X-6bylLTqFUm5RTM1i+O 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