diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 82797cd231..bae212917a 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -533,7 +533,7 @@ class CuraApplication(QtApplication): preferences.addPreference("cura/active_mode", "simple") preferences.addPreference("cura/categories_expanded", "") - preferences.addPreference("cura/job_name_template", "{machine_name_short}_{project_name}") + preferences.addPreference("cura/jobname_prefix", True) preferences.addPreference("cura/select_models_on_load", False) preferences.addPreference("view/center_on_select", False) preferences.addPreference("mesh/scale_to_fit", False) diff --git a/cura/UI/PrintInformation.py b/cura/UI/PrintInformation.py index 95aceb67e1..22710165b3 100644 --- a/cura/UI/PrintInformation.py +++ b/cura/UI/PrintInformation.py @@ -202,7 +202,11 @@ class PrintInformation(QObject): self._material_costs[build_plate_number] = [] self._material_names[build_plate_number] = [] - material_preference_values = json.loads(self._application.getInstance().getPreferences().getValue("cura/material_settings")) + try: + material_preference_values = json.loads(self._application.getInstance().getPreferences().getValue("cura/material_settings")) + except json.JSONDecodeError: + Logger.warning("Material preference values are corrupt. Will revert to defaults!") + material_preference_values = {} for index, extruder_stack in enumerate(global_stack.extruderList): if index >= len(self._material_amounts): @@ -252,11 +256,11 @@ class PrintInformation(QObject): self.materialNamesChanged.emit() def _onPreferencesChanged(self, preference: str) -> None: - if preference == "cura/job_name_template": - self._updateJobName() - elif preference == "cura/material_settings": - for build_plate_number in range(self._multi_build_plate_model.maxBuildPlate + 1): - self._calculateInformation(build_plate_number) + if preference != "cura/material_settings": + return + + for build_plate_number in range(self._multi_build_plate_model.maxBuildPlate + 1): + self._calculateInformation(build_plate_number) def _onActiveBuildPlateChanged(self) -> None: new_active_build_plate = self._multi_build_plate_model.activeBuildPlate @@ -305,8 +309,12 @@ class PrintInformation(QObject): # Only update the job name when it's not user-specified. if not self._is_user_specified_job_name: - if not self._pre_sliced: - self._job_name = self.parseTemplate() + if self._application.getInstance().getPreferences().getValue("cura/jobname_prefix") and not self._pre_sliced: + # Don't add abbreviation if it already has the exact same abbreviation. + if base_name.startswith(self._abbr_machine + "_"): + self._job_name = base_name + else: + self._job_name = self._abbr_machine + "_" + base_name else: self._job_name = base_name @@ -436,28 +444,3 @@ class PrintInformation(QObject): """Listen to scene changes to check if we need to reset the print information""" self.setToZeroPrintInformation(self._active_build_plate) - - def parseTemplate(self) -> str: - """Generate a print job name from the job name template - - The template is a user preference: "cura/job_name_template" - """ - template = self._application.getInstance().getPreferences().getValue("cura/job_name_template") - output = template - - output = output.replace("{machine_name_short}", self._abbr_machine) - - if "{machine_name}" in template: - global_container_stack = self._application.getGlobalContainerStack() - active_machine_type_name = global_container_stack.definition.getName() \ - if global_container_stack \ - else "no_machine" - - active_machine_type_name = active_machine_type_name.replace(" ", "_") - output = output.replace("{machine_name}", active_machine_type_name) - - if "{project_name}" in template: - base_name = self._stripAccents(self._base_name) - output = output.replace("{project_name}", base_name) - - return output diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py index 546c6fc27b..2c29728d66 100755 --- a/plugins/3MFReader/ThreeMFReader.py +++ b/plugins/3MFReader/ThreeMFReader.py @@ -3,7 +3,7 @@ import os.path import zipfile -from typing import List, Optional, Union, TYPE_CHECKING +from typing import List, Optional, Union, TYPE_CHECKING, cast import Savitar import numpy @@ -169,8 +169,16 @@ class ThreeMFReader(MeshReader): setting_container.setProperty(key, "value", setting_value) if len(um_node.getChildren()) > 0 and um_node.getMeshData() is None: - group_decorator = GroupDecorator() - um_node.addDecorator(group_decorator) + if len(um_node.getAllChildren()) == 1: + # We don't want groups of one, so move the node up one "level" + child_node = um_node.getChildren()[0] + parent_transformation = um_node.getLocalTransformation() + child_transformation = child_node.getLocalTransformation() + child_node.setTransformation(parent_transformation.multiply(child_transformation)) + um_node = cast(CuraSceneNode, um_node.getChildren()[0]) + else: + group_decorator = GroupDecorator() + um_node.addDecorator(group_decorator) um_node.setSelectable(True) if um_node.getMeshData(): # Assuming that all nodes with mesh data are printable objects @@ -192,8 +200,8 @@ class ThreeMFReader(MeshReader): um_node = self._convertSavitarNodeToUMNode(node, file_name) if um_node is None: continue - # compensate for original center position, if object(s) is/are not around its zero position + # compensate for original center position, if object(s) is/are not around its zero position transform_matrix = Matrix() mesh_data = um_node.getMeshData() if mesh_data is not None: diff --git a/plugins/ImageReader/ImageReaderUI.py b/plugins/ImageReader/ImageReaderUI.py index f4732c2843..103cd6f7e8 100644 --- a/plugins/ImageReader/ImageReaderUI.py +++ b/plugins/ImageReader/ImageReaderUI.py @@ -172,7 +172,7 @@ class ImageReaderUI(QObject): @pyqtSlot(int) def onColorModelChanged(self, value): - self.use_transparency_model = (value == 0) + self.use_transparency_model = (value == 1) @pyqtSlot(int) def onTransmittanceChanged(self, value): diff --git a/plugins/PostProcessingPlugin/scripts/DisplayProgressOnLCD.py b/plugins/PostProcessingPlugin/scripts/DisplayProgressOnLCD.py new file mode 100644 index 0000000000..a445fb3a6e --- /dev/null +++ b/plugins/PostProcessingPlugin/scripts/DisplayProgressOnLCD.py @@ -0,0 +1,130 @@ +# Cura PostProcessingPlugin +# Author: Mathias Lyngklip Kjeldgaard, Alexander Gee +# Date: July 31, 2019 +# Modified: May 22, 2020 + +# Description: This plugin displays progress on the LCD. It can output the estimated time remaining and the completion percentage. + +from ..Script import Script + +import re +import datetime + +class DisplayProgressOnLCD(Script): + + def __init__(self): + super().__init__() + + def getSettingDataString(self): + return """{ + "name": "Display Progress On LCD", + "key": "DisplayProgressOnLCD", + "metadata": {}, + "version": 2, + "settings": + { + "time_remaining": + { + "label": "Time Remaining", + "description": "When enabled, write Time Left: HHMMSS on the display using M117. This is updated every layer.", + "type": "bool", + "default_value": false + }, + "percentage": + { + "label": "Percentage", + "description": "When enabled, set the completion bar percentage on the LCD using Marlin's M73 command.", + "type": "bool", + "default_value": false + } + } + }""" + + # Get the time value from a line as a float. + # Example line ;TIME_ELAPSED:1234.6789 or ;TIME:1337 + def getTimeValue(self, line): + list_split = re.split(":", line) # Split at ":" so we can get the numerical value + return float(list_split[1]) # Convert the numerical portion to a float + + def outputTime(self, lines, line_index, time_left): + # Do some math to get the time left in seconds into the right format. (HH,MM,SS) + m, s = divmod(time_left, 60) + h, m = divmod(m, 60) + # Create the string + current_time_string = "{:d}h{:02d}m{:02d}s".format(int(h), int(m), int(s)) + # And now insert that into the GCODE + lines.insert(line_index, "M117 Time Left {}".format(current_time_string)) + + def execute(self, data): + output_time = self.getSettingValueByKey("time_remaining") + output_percentage = self.getSettingValueByKey("percentage") + line_set = {} + if output_percentage or output_time: + total_time = -1 + previous_layer_end_percentage = 0 + for layer in data: + layer_index = data.index(layer) + lines = layer.split("\n") + + for line in lines: + if line.startswith(";TIME:") and total_time == -1: + # This line represents the total time required to print the gcode + total_time = self.getTimeValue(line) + line_index = lines.index(line) + + if output_time: + self.outputTime(lines, line_index, total_time) + if output_percentage: + # Emit 0 percent to sure Marlin knows we are overriding the completion percentage + lines.insert(line_index, "M73 P0") + + elif line.startswith(";TIME_ELAPSED:"): + # We've found one of the time elapsed values which are added at the end of layers + + # If we have seen this line before then skip processing it. We can see lines multiple times because we are adding + # intermediate percentages before the line being processed. This can cause the current line to shift back and be + # encountered more than once + if line in line_set: + continue + line_set[line] = True + + # If total_time was not already found then noop + if total_time == -1: + continue + + current_time = self.getTimeValue(line) + line_index = lines.index(line) + + if output_time: + # Here we calculate remaining time + self.outputTime(lines, line_index, total_time - current_time) + + if output_percentage: + # Calculate percentage value this layer ends at + layer_end_percentage = int((current_time / total_time) * 100) + + # Figure out how many percent of the total time is spent in this layer + layer_percentage_delta = layer_end_percentage - previous_layer_end_percentage + + # If this layer represents less than 1 percent then we don't need to emit anything, continue to the next layer + if layer_percentage_delta != 0: + # Grab the index of the current line and figure out how many lines represent one percent + step = line_index / layer_percentage_delta + + for percentage in range(1, layer_percentage_delta + 1): + # We add the percentage value here as while processing prior lines we will have inserted + # percentage lines before the current one. Failing to do this will upset the spacing + percentage_line_index = int((percentage * step) + percentage) + + # Due to integer truncation of the total time value in the gcode the percentage we + # calculate may slightly exceed 100, as that is not valid we cap the value here + output = min(percentage + previous_layer_end_percentage, 100) + + # Now insert the sanitized percentage into the GCODE + lines.insert(percentage_line_index, "M73 P{}".format(output)) + + previous_layer_end_percentage = layer_end_percentage + + # Join up the lines for this layer again and store them in the data array + data[layer_index] = "\n".join(lines) + return data diff --git a/plugins/PostProcessingPlugin/scripts/DisplayRemainingTimeOnLCD.py b/plugins/PostProcessingPlugin/scripts/DisplayRemainingTimeOnLCD.py deleted file mode 100644 index 7d9af10925..0000000000 --- a/plugins/PostProcessingPlugin/scripts/DisplayRemainingTimeOnLCD.py +++ /dev/null @@ -1,94 +0,0 @@ -# Cura PostProcessingPlugin -# Author: Mathias Lyngklip Kjeldgaard -# Date: July 31, 2019 -# Modified: November 26, 2019 - -# Description: This plugin displayes the remaining time on the LCD of the printer -# using the estimated print-time generated by Cura. - - - - -from ..Script import Script - -import re -import datetime - - -class DisplayRemainingTimeOnLCD(Script): - - def __init__(self): - super().__init__() - - - def getSettingDataString(self): - return """{ - "name":"Display Remaining Time on LCD", - "key":"DisplayRemainingTimeOnLCD", - "metadata": {}, - "version": 2, - "settings": - { - "TurnOn": - { - "label": "Enable", - "description": "When enabled, It will write Time Left: HHMMSS on the display. This is updated every layer.", - "type": "bool", - "default_value": false - } - } - }""" - - def execute(self, data): - if self.getSettingValueByKey("TurnOn"): - total_time = 0 - total_time_string = "" - for layer in data: - layer_index = data.index(layer) - lines = layer.split("\n") - for line in lines: - if line.startswith(";TIME:"): - # At this point, we have found a line in the GCODE with ";TIME:" - # which is the indication of total_time. Looks like: ";TIME:1337", where - # 1337 is the total print time in seconds. - line_index = lines.index(line) # We take a hold of that line - split_string = re.split(":", line) # Then we split it, so we can get the number - - string_with_numbers = "{}".format(split_string[1]) # Here we insert that number from the - # list into a string. - total_time = int(string_with_numbers) # Only to contert it to a int. - - m, s = divmod(total_time, 60) # Math to calculate - h, m = divmod(m, 60) # hours, minutes and seconds. - total_time_string = "{:d}h{:02d}m{:02d}s".format(h, m, s) # Now we put it into the string - lines[line_index] = "M117 Time Left {}".format(total_time_string) # And print that string instead of the original one - - - - - elif line.startswith(";TIME_ELAPSED:"): - - # As we didnt find the total time (";TIME:"), we have found a elapsed time mark - # This time represents the time the printer have printed. So with some math; - # totalTime - printTime = RemainingTime. - line_index = lines.index(line) # We get a hold of the line - list_split = re.split(":", line) # Again, we split at ":" so we can get the number - string_with_numbers = "{}".format(list_split[1]) # Then we put that number from the list, into a string - - current_time = float(string_with_numbers) # This time we convert to a float, as the line looks something like: - # ;TIME_ELAPSED:1234.6789 - # which is total time in seconds - - time_left = total_time - current_time # Here we calculate remaining time - m1, s1 = divmod(time_left, 60) # And some math to get the total time in seconds into - h1, m1 = divmod(m1, 60) # the right format. (HH,MM,SS) - current_time_string = "{:d}h{:2d}m{:2d}s".format(int(h1), int(m1), int(s1)) # Here we create the string holding our time - lines[line_index] = "M117 Time Left {}".format(current_time_string) # And now insert that into the GCODE - - - # Here we are OUT of the second for-loop - # Which means we have found and replaces all the occurences. - # Which also means we are ready to join the lines for that section of the GCODE file. - final_lines = "\n".join(lines) - data[layer_index] = final_lines - return data diff --git a/plugins/VersionUpgrade/VersionUpgrade462to47/VersionUpgrade462to47.py b/plugins/VersionUpgrade/VersionUpgrade462to47/VersionUpgrade462to47.py index 23352bf4e2..cb2e17dae5 100644 --- a/plugins/VersionUpgrade/VersionUpgrade462to47/VersionUpgrade462to47.py +++ b/plugins/VersionUpgrade/VersionUpgrade462to47/VersionUpgrade462to47.py @@ -45,13 +45,6 @@ class VersionUpgrade462to47(VersionUpgrade): parser["general"]["visible_settings"] = ";".join( set(parser["general"]["visible_settings"].split(";")).difference(_removed_settings)) - if "cura" in parser and "jobname_prefix" in parser["cura"]: - if not parseBool(parser["cura"]["jobname_prefix"]): - parser["cura"]["job_name_template"] = "{project_name}" - del parser["cura"]["jobname_prefix"] - # else: When the jobname_prefix preference is True or not set, - # the default value for job_name_template ("{machine_name_short}_{project_name}") will be used - result = io.StringIO() parser.write(result) return [filename], [result.getvalue()] @@ -168,6 +161,17 @@ class VersionUpgrade462to47(VersionUpgrade): if "redo_layers" in script_parser["PauseAtHeight"]: script_parser["PauseAtHeight"]["redo_layer"] = str(int(script_parser["PauseAtHeight"]["redo_layers"]) > 0) del script_parser["PauseAtHeight"]["redo_layers"] # Has been renamed to without the S. + + # Migrate DisplayCompleteOnLCD to DisplayProgressOnLCD + if script_id == "DisplayRemainingTimeOnLCD": + was_enabled = parseBool(script_parser[script_id]["TurnOn"]) if "TurnOn" in script_parser[script_id] else False + script_parser.remove_section(script_id) + + script_id = "DisplayProgressOnLCD" + script_parser.add_section(script_id) + if was_enabled: + script_parser.set(script_id, "time_remaining", "True") + script_io = io.StringIO() script_parser.write(script_io) script_str = script_io.getvalue() diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 74ccbfdc59..2655f2d63c 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -7151,6 +7151,7 @@ "description": "Detect bridges and modify print speed, flow and fan settings while bridges are printed.", "type": "bool", "default_value": false, + "resolve": "any(extruderValues('bridge_settings_enabled'))", "settable_per_mesh": true, "settable_per_extruder": false, "settable_per_meshgroup": false diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index c9a16e579a..c3d9dfbf96 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -866,6 +866,7 @@ UM.MainWindow title: catalog.i18nc("@title:window", "What's New") model: CuraApplication.getWhatsNewPagesModel() progressBarVisible: false + visible: false } Connections diff --git a/resources/qml/Menus/SettingsMenu.qml b/resources/qml/Menus/SettingsMenu.qml index a7c8d7479f..939ade5847 100644 --- a/resources/qml/Menus/SettingsMenu.qml +++ b/resources/qml/Menus/SettingsMenu.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 1.4 import UM 1.2 as UM import Cura 1.0 as Cura @@ -24,7 +24,15 @@ Menu title: modelData.name property var extruder: (base.activeMachine === null) ? null : activeMachine.extruderList[model.index] NozzleMenu { title: Cura.MachineManager.activeDefinitionVariantsName; visible: Cura.MachineManager.activeMachine.hasVariants; extruderIndex: index } - MaterialMenu { title: catalog.i18nc("@title:menu", "&Material"); visible: Cura.MachineManager.activeMachine.hasMaterials; extruderIndex: index } + MaterialMenu + { + title: catalog.i18nc("@title:menu", "&Material") + visible: Cura.MachineManager.activeMachine.hasMaterials + extruderIndex: index + updateModels: false + onAboutToShow: updateModels = true + onAboutToHide: updateModels = false + } MenuSeparator { diff --git a/resources/qml/Preferences/GeneralPage.qml b/resources/qml/Preferences/GeneralPage.qml index 0d3f6ea586..62768556e4 100644 --- a/resources/qml/Preferences/GeneralPage.qml +++ b/resources/qml/Preferences/GeneralPage.qml @@ -85,8 +85,8 @@ UM.PreferencesPage scaleTinyCheckbox.checked = boolCheck(UM.Preferences.getValue("mesh/scale_tiny_meshes")) UM.Preferences.resetPreference("cura/select_models_on_load") selectModelsOnLoadCheckbox.checked = boolCheck(UM.Preferences.getValue("cura/select_models_on_load")) - UM.Preferences.resetPreference("cura/job_name_template") - jobnameTemplateTextField.text = UM.Preferences.getValue("cura/job_name_template") + UM.Preferences.resetPreference("cura/jobname_prefix") + prefixJobNameCheckbox.checked = boolCheck(UM.Preferences.getValue("cura/jobname_prefix")) UM.Preferences.resetPreference("view/show_overhang"); showOverhangCheckbox.checked = boolCheck(UM.Preferences.getValue("view/show_overhang")) UM.Preferences.resetPreference("view/show_xray_warning"); @@ -627,25 +627,14 @@ UM.PreferencesPage { width: childrenRect.width height: childrenRect.height - text: catalog.i18nc("@info:tooltip. Note variable names themselves (ie. machine_name_short, project_name) should not be translated", "Variables: machine_name_short, machine_name, project_name") + text: catalog.i18nc("@info:tooltip", "Should a prefix based on the printer name be added to the print job name automatically?") - Column + CheckBox { - spacing: 4 * screenScaleFactor - - Label - { - id: jobNameTemplateLabel - text: catalog.i18nc("@label","Print job template:") - } - - TextField - { - id: jobNameTemplateTextField - width: 250 * screenScaleFactor - text: UM.Preferences.getValue("cura/job_name_template") - onTextChanged: UM.Preferences.setValue("cura/job_name_template", text) - } + id: prefixJobNameCheckbox + text: catalog.i18nc("@option:check", "Add machine prefix to job name") + checked: boolCheck(UM.Preferences.getValue("cura/jobname_prefix")) + onCheckedChanged: UM.Preferences.setValue("cura/jobname_prefix", checked) } } @@ -681,7 +670,7 @@ UM.PreferencesPage ComboBox { id: choiceOnOpenProjectDropDownButton - width: 250 * screenScaleFactor + width: 200 * screenScaleFactor model: ListModel { @@ -747,7 +736,7 @@ UM.PreferencesPage ComboBox { id: choiceOnProfileOverrideDropDownButton - width: 250 * screenScaleFactor + width: 200 * screenScaleFactor model: ListModel { diff --git a/resources/qml/Settings/SettingCategory.qml b/resources/qml/Settings/SettingCategory.qml index fd4a181a56..e3e12d7753 100644 --- a/resources/qml/Settings/SettingCategory.qml +++ b/resources/qml/Settings/SettingCategory.qml @@ -22,22 +22,10 @@ Button height: UM.Theme.getSize("section").height color: { - if (base.color) - { - return base.color - } - else if (!base.enabled) + if (!base.enabled) { return UM.Theme.getColor("setting_category_disabled") } - else if (base.hovered && base.expanded) - { - return UM.Theme.getColor("setting_category_active_hover") - } - else if (base.pressed || base.expanded) - { - return UM.Theme.getColor("setting_category_active") - } else if (base.hovered) { return UM.Theme.getColor("setting_category_hover") @@ -57,6 +45,21 @@ Button property var focusItem: base property bool expanded: definition.expanded + + property color text_color: + { + if (!base.enabled) + { + return UM.Theme.getColor("setting_category_disabled_text") + } else if (base.hovered || base.pressed || base.activeFocus) + { + return UM.Theme.getColor("setting_category_active_text") + } + + return UM.Theme.getColor("setting_category_text") + + } + contentItem: Item { anchors.fill: parent @@ -75,25 +78,7 @@ Button textFormat: Text.PlainText renderType: Text.NativeRendering font: UM.Theme.getFont("medium_bold") - color: - { - if (!base.enabled) - { - return UM.Theme.getColor("setting_category_disabled_text") - } else if ((base.hovered || base.activeFocus) && base.expanded) - { - return UM.Theme.getColor("setting_category_active_hover_text") - } else if (base.pressed || base.expanded) - { - 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") - } - } + color: base.text_color fontSizeMode: Text.HorizontalFit minimumPointSize: 8 } @@ -118,26 +103,7 @@ Button anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left 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.expanded) - { - return UM.Theme.getColor("setting_category_active_hover_text") - } - else if(base.pressed || base.expanded) - { - 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: base.text_color source: UM.Theme.getIcon(definition.icon) width: UM.Theme.getSize("section_icon").width height: UM.Theme.getSize("section_icon").height diff --git a/resources/qml/Settings/SettingItem.qml b/resources/qml/Settings/SettingItem.qml index 4f232cd7ee..f0c748d190 100644 --- a/resources/qml/Settings/SettingItem.qml +++ b/resources/qml/Settings/SettingItem.qml @@ -156,7 +156,7 @@ Item { id: settingControls - height: Math.round(parent.height / 2) + height: UM.Theme.getSize("section_control").height spacing: Math.round(UM.Theme.getSize("thick_margin").height / 2) anchors diff --git a/resources/qml/Settings/SettingTextField.qml b/resources/qml/Settings/SettingTextField.qml index c0c7772104..d57bbaad8a 100644 --- a/resources/qml/Settings/SettingTextField.qml +++ b/resources/qml/Settings/SettingTextField.qml @@ -153,7 +153,7 @@ SettingItem selectByMouse: true maximumLength: (definition.type == "str" || definition.type == "[int]") ? -1 : 10 - clip: true; //Hide any text that exceeds the width of the text box. + clip: definition.type == "[int]" // Only clip for the list 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 diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 5dfba3e464..8a390bbaef 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -246,25 +246,18 @@ Item } property int indexWithFocus: -1 + property double delegateHeight: UM.Theme.getSize("section").height + 2 * UM.Theme.getSize("default_lining").height property string activeMachineId: Cura.MachineManager.activeMachine !== null ? Cura.MachineManager.activeMachine.id : "" delegate: Loader { id: delegate width: scrollView.width - height: provider.properties.enabled === "True" ? UM.Theme.getSize("section").height + 2 * UM.Theme.getSize("default_lining").height : 0 + height: enabled ? contents.delegateHeight: 0 Behavior on height { NumberAnimation { duration: 100 } } - opacity: provider.properties.enabled === "True" ? 1 : 0 + opacity: enabled ? 1 : 0 Behavior on opacity { NumberAnimation { duration: 100 } } - enabled: - { - if (!Cura.ExtruderManager.activeExtruderStackId && machineExtruderCount.properties.value > 1) - { - // disable all controls on the global tab, except categories - return model.type === "category" - } - return provider.properties.enabled === "True" - } + enabled: provider.properties.enabled === "True" property var definition: model property var settingDefinitionsModel: definitionsModel @@ -272,10 +265,7 @@ Item property var globalPropertyProvider: inheritStackProvider property bool externalResetHandler: false - //Qt5.4.2 and earlier has a bug where this causes a crash: https://bugreports.qt.io/browse/QTBUG-35989 - //In addition, while it works for 5.5 and higher, the ordering of the actual combo box drop down changes, - //causing nasty issues when selecting different options. So disable asynchronous loading of enum type completely. - asynchronous: model.type !== "enum" && model.type !== "extruder" && model.type !== "optional_extruder" + asynchronous: true active: model.type !== undefined source: @@ -355,7 +345,7 @@ Item id: provider containerStackId: contents.activeMachineId - key: model.key ? model.key : "" + key: model.key watchedProperties: [ "value", "enabled", "state", "validationState", "settable_per_extruder", "resolve" ] storeIndex: 0 removeUnusedValue: model.resolve === undefined diff --git a/resources/qml/Toolbar.qml b/resources/qml/Toolbar.qml index 0bf09b4d18..89d64b06ad 100644 --- a/resources/qml/Toolbar.qml +++ b/resources/qml/Toolbar.qml @@ -78,6 +78,10 @@ Item { base.activeY = y; } + //Clear focus when tools change. This prevents the tool grabbing focus when activated. + //Grabbing focus prevents items from being deleted. + //Apparently this was only a problem on MacOS. + forceActiveFocus(); } //Workaround since using ToolButton's onClicked would break the binding of the checked property, instead diff --git a/resources/qml/WelcomePages/WizardDialog.qml b/resources/qml/WelcomePages/WizardDialog.qml index c14974cd03..3bee9fcb5c 100644 --- a/resources/qml/WelcomePages/WizardDialog.qml +++ b/resources/qml/WelcomePages/WizardDialog.qml @@ -40,6 +40,7 @@ Window id: wizardPanel anchors.fill: parent model: dialog.model + visible: dialog.visible } // Close this dialog when there's no more page to show diff --git a/resources/qml/WelcomePages/WizardPanel.qml b/resources/qml/WelcomePages/WizardPanel.qml index 418f4848fb..db4b66d18b 100644 --- a/resources/qml/WelcomePages/WizardPanel.qml +++ b/resources/qml/WelcomePages/WizardPanel.qml @@ -71,7 +71,7 @@ Item right: parent.right } source: base.pageUrl - enabled: base.visible + active: base.visible } } } diff --git a/resources/texts/change_log.txt b/resources/texts/change_log.txt index 7621f6f713..71eb566329 100644 --- a/resources/texts/change_log.txt +++ b/resources/texts/change_log.txt @@ -1,6 +1,15 @@ [4.6.2] -TODO +Patch release to fix some bugs that emerged with 4.6.1. + +* Persistent notifications in the Marketplace. +We fixed a frustrating bug where a package would keep issuing a badge notification to update, even after the package had been updated. + +* Removed Ultibot from Marketplace login screen. +For professionalism, Ultibot has been asked to leave the Marketplace login screen. He's now gone from everything. + +* Ultimaker 2+ Z-hop. +The Ultimaker 2+ included an unwanted travel move that could drag purged material into the start of a print. This is now fixed. [4.6.1] @@ -140,7 +149,7 @@ A new performance enhancement that limits re-rendering of the application interf Previous versions used different ways of handling HTTP requests. This version uses a unified method, for better performance. * Job names less sensitive to being touched. -A contribution from fieldOfview has fixed an issue where the job name in the bottom-left of the scene is no longer made static by clicking on it. If you load a model and change to another printer, the prefix is now correctly updated. +A contribution from fieldOfview has fixed an issue where the jobname in the bottom-left of the scene is no longer made static by clicking on it. If you load a model and change to another printer, the prefix is now correctly updated. * Property checks on instance containers. A new speed optimization for reading setting values from profiles. diff --git a/resources/themes/cura-dark/theme.json b/resources/themes/cura-dark/theme.json index 69bd14765a..311aefcee0 100644 --- a/resources/themes/cura-dark/theme.json +++ b/resources/themes/cura-dark/theme.json @@ -99,13 +99,9 @@ "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], "setting_category_active_text": [255, 255, 255, 204], - "setting_category_active_hover_text": [255, 255, 255, 204], "setting_category_border": [39, 44, 48, 0], "setting_category_disabled_border": [39, 44, 48, 0], "setting_category_hover_border": [12, 169, 227, 255], diff --git a/resources/themes/cura-light/theme.json b/resources/themes/cura-light/theme.json index 10cb353c60..a72c4eb5cd 100644 --- a/resources/themes/cura-light/theme.json +++ b/resources/themes/cura-light/theme.json @@ -274,13 +274,9 @@ "setting_category": [240, 240, 240, 255], "setting_category_disabled": [255, 255, 255, 255], "setting_category_hover": [232, 242, 252, 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": [240, 240, 240, 255], "setting_category_disabled_border": [240, 240, 240, 255], "setting_category_hover_border": [50, 130, 255, 255], @@ -502,6 +498,7 @@ "extruder_icon": [2.33, 2.33], "section": [0.0, 2], + "section_control": [0, 1], "section_icon": [1.6, 1.6], "section_icon_column": [2.8, 0.0], "rating_star": [1.0, 1.0], diff --git a/tests/TestPrintInformation.py b/tests/TestPrintInformation.py index 577827f326..5133dfcafb 100644 --- a/tests/TestPrintInformation.py +++ b/tests/TestPrintInformation.py @@ -8,13 +8,6 @@ from unittest.mock import MagicMock, patch from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType -def preferencesGetValue(key: str): - if key == "cura/job_name_template": - return "{machine_name_short}_{project_name}" - - return '{"omgzomg": {"spool_weight": 10, "spool_cost": 9}}' - - def getPrintInformation(printer_name) -> PrintInformation: mock_application = MagicMock(name = "mock_application") @@ -26,7 +19,7 @@ def getPrintInformation(printer_name) -> PrintInformation: mocked_extruder_stack.material = mocked_material mock_application.getInstance = MagicMock(return_value = mock_application) - mocked_preferences.getValue = MagicMock(side_effect = preferencesGetValue) + mocked_preferences.getValue = MagicMock(return_value = '{"omgzomg": {"spool_weight": 10, "spool_cost": 9}}') global_container_stack = MagicMock() global_container_stack.definition.getName = MagicMock(return_value = printer_name)