From bb18bf6a30914e7127d7e40a4dbf3baa5baf1d3e Mon Sep 17 00:00:00 2001 From: fieldOfView Date: Thu, 2 Jun 2016 16:38:06 +0200 Subject: [PATCH] Fix creating a jobname after loading a file Moves jobname creation out of qml and into python. CURA-1619 --- cura/CuraApplication.py | 15 ------- cura/PrintInformation.py | 56 ++++++++++++++++++++++-- resources/qml/Cura.qml | 4 +- resources/qml/JobSpecs.qml | 85 ++++++++++-------------------------- resources/qml/SaveButton.qml | 2 +- 5 files changed, 79 insertions(+), 83 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 49fcbbf405..3e3ab28010 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -436,21 +436,6 @@ class CuraApplication(QtApplication): self._platform_activity = True if count > 0 else False self.activityChanged.emit() - @pyqtSlot(str) - def setJobName(self, name): - # when a file is opened using the terminal; the filename comes from _onFileLoaded and still contains its - # extension. This cuts the extension off if necessary. - name = os.path.splitext(name)[0] - if self._job_name != name: - self._job_name = name - self.jobNameChanged.emit() - - jobNameChanged = pyqtSignal() - - @pyqtProperty(str, notify = jobNameChanged) - def jobName(self): - return self._job_name - # Remove all selected objects from the scene. @pyqtSlot() def deleteSelection(self): diff --git a/cura/PrintInformation.py b/cura/PrintInformation.py index 2663cab5a0..dd15d7a264 100644 --- a/cura/PrintInformation.py +++ b/cura/PrintInformation.py @@ -1,14 +1,17 @@ # Copyright (c) 2015 Ultimaker B.V. # Cura is released under the terms of the AGPLv3 or higher. -from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty +from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty, pyqtSlot from UM.Application import Application from UM.Qt.Duration import Duration +from UM.Preferences import Preferences import math +import os.path +import unicodedata -## A class for processing and calculating minimum, current and maximum print time. +## A class for processing and calculating minimum, current and maximum print time as well as managing the job name # # This class contains all the logic relating to calculation and slicing for the # time/quality slider concept. It is a rather tricky combination of event handling @@ -22,6 +25,8 @@ import math # - When that is done, we update the minimum print time and start the final slice pass, the "high quality settings pass". # - When the high quality pass is done, we update the maximum print time. # +# This class also mangles the current machine name and the filename of the first loaded mesh into a job name. +# This job name is requested by the JobSpecs qml file. class PrintInformation(QObject): class SlicePass: CurrentSettings = 1 @@ -45,14 +50,17 @@ class PrintInformation(QObject): if self._backend: self._backend.printDurationMessage.connect(self._onPrintDurationMessage) + self._job_name = "" + Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged) + currentPrintTimeChanged = pyqtSignal() - + @pyqtProperty(Duration, notify = currentPrintTimeChanged) def currentPrintTime(self): return self._current_print_time materialAmountChanged = pyqtSignal() - + @pyqtProperty(float, notify = materialAmountChanged) def materialAmount(self): return self._material_amount @@ -66,3 +74,43 @@ class PrintInformation(QObject): r = Application.getInstance().getGlobalContainerStack().getProperty("material_diameter", "value") / 2 self._material_amount = round((amount / (math.pi * r ** 2)) / 1000, 2) self.materialAmountChanged.emit() + + @pyqtSlot(str) + def setJobName(self, name): + # when a file is opened using the terminal; the filename comes from _onFileLoaded and still contains its + # extension. This cuts the extension off if necessary. + name = os.path.splitext(name)[0] + if self._job_name != name: + self._job_name = name + self.jobNameChanged.emit() + + jobNameChanged = pyqtSignal() + + @pyqtProperty(str, notify = jobNameChanged) + def jobName(self): + return self._job_name + + @pyqtSlot(str, result = str) + def createJobName(self, base_name): + base_name = self._stripAccents(base_name) + if Preferences.getInstance().getValue("cura/jobname_prefix"): + return self._abbr_machine + "_" + base_name + else: + return base_name + + def _onGlobalStackChanged(self): + global_stack_name = Application.getInstance().getGlobalContainerStack().getName() + split_name = global_stack_name.split(" ") + abbr_machine = "" + for word in split_name: + if(word.lower() == "ultimaker"): + abbr_machine += "UM" + elif word.isdigit(): + abbr_machine += word + else: + abbr_machine += self._stripAccents(word.strip("()[]{}#").upper())[0] + + self._abbr_machine = abbr_machine + + def _stripAccents(self, str): + return ''.join(char for char in unicodedata.normalize('NFD', str) if unicodedata.category(char) != 'Mn') \ No newline at end of file diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 32e1e79029..cf99b39864 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -93,7 +93,7 @@ UM.MainWindow text: catalog.i18nc("@action:inmenu menubar:file", "&Save Selection to File"); enabled: UM.Selection.hasSelection; iconName: "document-save-as"; - onTriggered: UM.OutputDeviceManager.requestWriteSelectionToDevice("local_file", Printer.jobName, { "filter_by_machine": false }); + onTriggered: UM.OutputDeviceManager.requestWriteSelectionToDevice("local_file", PrintInformation.jobName, { "filter_by_machine": false }); } Menu { @@ -109,7 +109,7 @@ UM.MainWindow MenuItem { text: model.description; - onTriggered: UM.OutputDeviceManager.requestWriteToDevice(model.id, Printer.jobName, { "filter_by_machine": false }); + onTriggered: UM.OutputDeviceManager.requestWriteToDevice(model.id, PrintInformation.jobName, { "filter_by_machine": false }); } onObjectAdded: saveAllMenu.insertItem(index, object) onObjectRemoved: saveAllMenu.removeItem(object) diff --git a/resources/qml/JobSpecs.qml b/resources/qml/JobSpecs.qml index 9551e3bcf5..e73bf145de 100644 --- a/resources/qml/JobSpecs.qml +++ b/resources/qml/JobSpecs.qml @@ -10,62 +10,25 @@ import UM 1.1 as UM import Cura 1.0 as Cura Rectangle { - id: base; + id: base - property bool activity: Printer.getPlatformActivity; + property bool activity: Printer.getPlatformActivity property string fileBaseName property variant activeMachineName: Cura.MachineManager.activeMachineName onActiveMachineNameChanged: { - base.createFileName() + printJobTextfield.text = PrintInformation.createJobName(base.fileBaseName); } UM.I18nCatalog { id: catalog; name:"cura"} - property variant printDuration: PrintInformation.currentPrintTime; - property real printMaterialAmount: PrintInformation.materialAmount; + property variant printDuration: PrintInformation.currentPrintTime + property real printMaterialAmount: PrintInformation.materialAmount height: childrenRect.height color: "transparent" - function createFileName() - { - var splitMachineName = Cura.MachineManager.activeMachineName.split(" ") - var abbrMachine = ""; - if ((UM.Preferences.getValue("cura/jobname_prefix"))) - { - for (var i = 0; i < splitMachineName.length; i++) - { - if (splitMachineName[i].search(/ultimaker/i) != -1) - { - abbrMachine += "UM"; - } - else - { - if (splitMachineName[i].charAt(0).search(/[0-9]/g) == -1) - { - abbrMachine += splitMachineName[i].charAt(0); - } - } - } - var regExpAdditives = /[0-9\+]/g; - var resultAdditives = splitMachineName[i].match(regExpAdditives); - if (resultAdditives != null) - { - for (var j = 0; j < resultAdditives.length; j++) - { - abbrMachine += resultAdditives[j]; - } - } - printJobTextfield.text = abbrMachine + "_" + base.fileBaseName; - } - else - { - printJobTextfield.text = base.fileBaseName; - } - } - Connections { target: backgroundItem @@ -78,20 +41,20 @@ Rectangle { onActivityChanged: { if (activity == true && base.fileBaseName == ''){ //this only runs when you open a file from the terminal (or something that works the same way; for example when you drag a file on the icon in MacOS or use 'open with' on Windows) - base.fileBaseName = Printer.jobName //it gets the fileBaseName from CuraApplication.py because this saves the filebase when the file is opened using the terminal (or something alike) - base.createFileName() + base.fileBaseName = PrintInformation.jobName; //get the fileBaseName from PrintInformation.py because this saves the filebase when the file is opened using the terminal (or something alike) + printJobTextfield.text = PrintInformation.createJobName(base.fileBaseName); } if (activity == true && base.fileBaseName != ''){ //this runs in all other cases where there is a mesh on the buildplate (activity == true). It uses the fileBaseName from the hasMesh signal - base.createFileName() + printJobTextfield.text = PrintInformation.createJobName(base.fileBaseName); } if (activity == false){ //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) - printJobTextfield.text = '' + printJobTextfield.text = ''; } } - Rectangle + Rectangle { id: jobNameRow anchors.top: parent.top @@ -112,22 +75,22 @@ Rectangle { width: UM.Theme.getSize("save_button_specs_icons").width height: UM.Theme.getSize("save_button_specs_icons").height - onClicked: + onClicked: { - printJobTextfield.selectAll() - printJobTextfield.focus = true + printJobTextfield.selectAll(); + printJobTextfield.focus = true; } style: ButtonStyle { background: Rectangle { color: "transparent" - 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 + 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("setting_control_button_hover") : UM.Theme.getColor("text"); source: UM.Theme.getIcon("pencil"); } @@ -147,15 +110,15 @@ Rectangle { text: '' horizontalAlignment: TextInput.AlignRight onTextChanged: { - Printer.setJobName(text) + PrintInformation.setJobName(text); } onEditingFinished: { if (printJobTextfield.text != ''){ - printJobTextfield.focus = false + printJobTextfield.focus = false; } } validator: RegExpValidator { - regExp: /^[^\\ \/ \.]*$/ + regExp: /^[^\\ \/ \*\?\|\[\]]*$/ } style: TextFieldStyle{ textColor: UM.Theme.getColor("setting_control_text"); @@ -200,7 +163,7 @@ Rectangle { sourceSize.width: width sourceSize.height: width color: UM.Theme.getColor("text_subtext") - source: UM.Theme.getIcon("print_time"); + source: UM.Theme.getIcon("print_time") } Label{ id: timeSpec @@ -221,7 +184,7 @@ Rectangle { sourceSize.width: width sourceSize.height: width color: UM.Theme.getColor("text_subtext") - source: UM.Theme.getIcon("category_material"); + source: UM.Theme.getIcon("category_material") } Label{ id: lengthSpec @@ -229,7 +192,7 @@ Rectangle { anchors.verticalCenter: parent.verticalCenter font: UM.Theme.getFont("small") color: UM.Theme.getColor("text_subtext") - text: base.printMaterialAmount <= 0 ? catalog.i18nc("@label", "0.0 m") : catalog.i18nc("@label", "%1 m").arg(base.printMaterialAmount) + text: catalog.i18nc("@label", "%1 m").arg(base.printMaterialAmount > 0 ? base.printMaterialAmount : 0) } } } diff --git a/resources/qml/SaveButton.qml b/resources/qml/SaveButton.qml index 1307e8f820..8b95de15ee 100644 --- a/resources/qml/SaveButton.qml +++ b/resources/qml/SaveButton.qml @@ -98,7 +98,7 @@ Rectangle { text: UM.OutputDeviceManager.activeDeviceShortDescription onClicked: { - UM.OutputDeviceManager.requestWriteToDevice(UM.OutputDeviceManager.activeDevice, Printer.jobName, { "filter_by_machine": true }) + UM.OutputDeviceManager.requestWriteToDevice(UM.OutputDeviceManager.activeDevice, PrintInformation.jobName, { "filter_by_machine": true }) } style: ButtonStyle {