diff --git a/.gitignore b/.gitignore index 0c3004a7ec..2b1cfc37e0 100644 --- a/.gitignore +++ b/.gitignore @@ -78,3 +78,5 @@ CuraEngine #Prevents import failures when plugin running tests plugins/__init__.py + +/venv diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index bae212917a..82797cd231 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/jobname_prefix", True) + preferences.addPreference("cura/job_name_template", "{machine_name_short}_{project_name}") 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 ae4aab0407..95aceb67e1 100644 --- a/cura/UI/PrintInformation.py +++ b/cura/UI/PrintInformation.py @@ -252,11 +252,11 @@ class PrintInformation(QObject): self.materialNamesChanged.emit() def _onPreferencesChanged(self, preference: str) -> None: - if preference != "cura/material_settings": - return - - for build_plate_number in range(self._multi_build_plate_model.maxBuildPlate + 1): - self._calculateInformation(build_plate_number) + 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) def _onActiveBuildPlateChanged(self) -> None: new_active_build_plate = self._multi_build_plate_model.activeBuildPlate @@ -305,12 +305,8 @@ class PrintInformation(QObject): # Only update the job name when it's not user-specified. if not self._is_user_specified_job_name: - 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 + if not self._pre_sliced: + self._job_name = self.parseTemplate() else: self._job_name = base_name @@ -440,3 +436,28 @@ 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/UFPWriter/UFPWriter.py b/plugins/UFPWriter/UFPWriter.py index 122f943766..c422dde612 100644 --- a/plugins/UFPWriter/UFPWriter.py +++ b/plugins/UFPWriter/UFPWriter.py @@ -151,8 +151,8 @@ class UFPWriter(MeshWriter): To retrieve, use: `archive.getMetadata(METADATA_OBJECTS_PATH)` """ - objectsModel = CuraApplication.getInstance().getObjectsModel() - objectMetas = [{"name": item["name"]} for item in objectsModel.items] + objects_model = CuraApplication.getInstance().getObjectsModel() + object_metas = [{"name": item["name"]} for item in objects_model.items] - data = {METADATA_OBJECTS_PATH: objectMetas} + data = {METADATA_OBJECTS_PATH: object_metas} archive.setMetadata(data) diff --git a/plugins/VersionUpgrade/VersionUpgrade462to47/VersionUpgrade462to47.py b/plugins/VersionUpgrade/VersionUpgrade462to47/VersionUpgrade462to47.py index 7bee545c16..a167c9917e 100644 --- a/plugins/VersionUpgrade/VersionUpgrade462to47/VersionUpgrade462to47.py +++ b/plugins/VersionUpgrade/VersionUpgrade462to47/VersionUpgrade462to47.py @@ -2,7 +2,7 @@ # Cura is released under the terms of the LGPLv3 or higher. import configparser -from typing import Tuple, List, Dict +from typing import Tuple, List, Dict, Set import io from UM.VersionUpgrade import VersionUpgrade @@ -10,8 +10,17 @@ from UM.VersionUpgrade import VersionUpgrade # Renamed definition files _RENAMED_DEFINITION_DICT = { "dagoma_discoeasy200": "dagoma_discoeasy200_bicolor", -} # type: Dict[str, str] +} # type: Dict[str, str] +_removed_settings = { + "spaghetti_infill_enabled", + "spaghetti_infill_stepped", + "spaghetti_max_infill_angle", + "spaghetti_max_height", + "spaghetti_inset", + "spaghetti_flow", + "spaghetti_infill_extra_volume" +} # type: Set[str] class VersionUpgrade462to47(VersionUpgrade): def upgradePreferences(self, serialized: str, filename: str) -> Tuple[List[str], List[str]]: @@ -27,6 +36,11 @@ class VersionUpgrade462to47(VersionUpgrade): # Update version number. parser["metadata"]["setting_version"] = "15" + + # Remove deleted settings from the visible settings list. + if "general" in parser and "visible_settings" in parser["general"]: + parser["general"]["visible_settings"] = ";".join( + set(parser["general"]["visible_settings"].split(";")).difference(_removed_settings)) result = io.StringIO() parser.write(result) @@ -77,6 +91,9 @@ class VersionUpgrade462to47(VersionUpgrade): correction = " + skin_line_width * (1.0 - ironing_flow / 100) / 2" ironing_inset = "=(" + ironing_inset + ")" + correction parser["values"]["ironing_inset"] = ironing_inset + + for removed in set(parser["values"].keys()).intersection(_removed_settings): + del parser["values"][removed] # Check renamed definitions if "definition" in parser["general"] and parser["general"]["definition"] in _RENAMED_DEFINITION_DICT: diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 8483b7a9cb..d7fa82a2c2 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -1510,11 +1510,12 @@ { "label": "Extra Skin Wall Count", "description": "Replaces the outermost part of the top/bottom pattern with a number of concentric lines. Using one or two lines improves roofs that start on infill material.", + "value": "0 if top_bottom_pattern == 'concentric' else 1", "default_value": 1, "minimum_value": "0", "maximum_value_warning": "10", "type": "int", - "enabled": "top_layers > 0 or bottom_layers > 0", + "enabled": "(top_layers > 0 or bottom_layers > 0) and top_bottom_pattern != 'concentric'", "limit_to_extruder": "top_bottom_extruder_nr", "settable_per_mesh": true }, @@ -1816,7 +1817,7 @@ "type": "int", "minimum_value": "1", "maximum_value_warning": "infill_line_distance / infill_line_width", - "enabled": "infill_sparse_density > 0 and not spaghetti_infill_enabled and infill_pattern != 'zigzag'", + "enabled": "infill_sparse_density > 0 and infill_pattern != 'zigzag'", "limit_to_extruder": "infill_extruder_nr", "settable_per_mesh": true }, @@ -1827,7 +1828,7 @@ "default_value": 0, "type": "int", "minimum_value": "0", - "enabled": "infill_sparse_density > 0 and not spaghetti_infill_enabled", + "enabled": "infill_sparse_density > 0", "limit_to_extruder": "infill_extruder_nr", "settable_per_mesh": true }, @@ -1898,9 +1899,9 @@ "default_value": 0.1, "minimum_value": "resolveOrValue('layer_height') / 2 if infill_line_distance > 0 else -999999", "maximum_value_warning": "0.75 * machine_nozzle_size", - "maximum_value": "resolveOrValue('layer_height') * (1.45 if spaghetti_infill_enabled else 8) if infill_line_distance > 0 else 999999", + "maximum_value": "resolveOrValue('layer_height') * 8 if infill_line_distance > 0 else 999999", "value": "resolveOrValue('layer_height')", - "enabled": "infill_sparse_density > 0 and not spaghetti_infill_enabled", + "enabled": "infill_sparse_density > 0", "limit_to_extruder": "infill_extruder_nr", "settable_per_mesh": true }, @@ -1912,8 +1913,8 @@ "type": "int", "minimum_value": "0", "maximum_value_warning": "1 if (infill_pattern == 'cross' or infill_pattern == 'cross_3d' or support_pattern == 'concentric') else 5", - "maximum_value": "0 if spaghetti_infill_enabled else (999999 if infill_line_distance == 0 else (20 - math.log(infill_line_distance) / math.log(2)))", - "enabled": "infill_sparse_density > 0 and infill_pattern != 'cubicsubdiv' and not spaghetti_infill_enabled", + "maximum_value": "999999 if infill_line_distance == 0 else (20 - math.log(infill_line_distance) / math.log(2))", + "enabled": "infill_sparse_density > 0 and infill_pattern != 'cubicsubdiv'", "limit_to_extruder": "infill_extruder_nr", "settable_per_mesh": true }, @@ -6584,92 +6585,6 @@ "settable_per_mesh": false, "settable_per_extruder": true }, - "spaghetti_infill_enabled": - { - "label": "Spaghetti Infill", - "description": "Print the infill every so often, so that the filament will curl up chaotically inside the object. This reduces print time, but the behaviour is rather unpredictable.", - "type": "bool", - "default_value": false, - "enabled": "infill_sparse_density > 0", - "limit_to_extruder": "infill_extruder_nr", - "settable_per_mesh": true - }, - "spaghetti_infill_stepped": - { - "label": "Spaghetti Infill Stepping", - "description": "Whether to print spaghetti infill in steps or extrude all the infill filament at the end of the print.", - "type": "bool", - "default_value": true, - "enabled": "infill_sparse_density > 0 and spaghetti_infill_enabled", - "limit_to_extruder": "infill_extruder_nr", - "settable_per_mesh": true - }, - "spaghetti_max_infill_angle": - { - "label": "Spaghetti Maximum Infill Angle", - "description": "The maximum angle w.r.t. the Z axis of the inside of the print for areas which are to be filled with spaghetti infill afterwards. Lowering this value causes more angled parts in your model to be filled on each layer.", - "unit": "°", - "type": "float", - "default_value": 10, - "minimum_value": "0", - "maximum_value": "90", - "maximum_value_warning": "45", - "enabled": "infill_sparse_density > 0 and spaghetti_infill_enabled and spaghetti_infill_stepped", - "limit_to_extruder": "infill_extruder_nr", - "settable_per_mesh": true - }, - "spaghetti_max_height": - { - "label": "Spaghetti Infill Maximum Height", - "description": "The maximum height of inside space which can be combined and filled from the top.", - "unit": "mm", - "type": "float", - "default_value": 2.0, - "minimum_value": "layer_height", - "maximum_value_warning": "10.0", - "enabled": "infill_sparse_density > 0 and spaghetti_infill_enabled and spaghetti_infill_stepped", - "limit_to_extruder": "infill_extruder_nr", - "settable_per_mesh": true - }, - "spaghetti_inset": - { - "label": "Spaghetti Inset", - "description": "The offset from the walls from where the spaghetti infill will be printed.", - "unit": "mm", - "type": "float", - "default_value": 0.2, - "minimum_value_warning": "0", - "maximum_value_warning": "5.0", - "enabled": "infill_sparse_density > 0 and spaghetti_infill_enabled", - "limit_to_extruder": "infill_extruder_nr", - "settable_per_mesh": true - }, - "spaghetti_flow": - { - "label": "Spaghetti Flow", - "description": "Adjusts the density of the spaghetti infill. Note that the Infill Density only controls the line spacing of the filling pattern, not the amount of extrusion for spaghetti infill.", - "unit": "%", - "type": "float", - "default_value": 20, - "minimum_value": "0", - "maximum_value_warning": "100", - "enabled": "infill_sparse_density > 0 and spaghetti_infill_enabled", - "limit_to_extruder": "infill_extruder_nr", - "settable_per_mesh": true - }, - "spaghetti_infill_extra_volume": - { - "label": "Spaghetti Infill Extra Volume", - "description": "A correction term to adjust the total volume being extruded each time when filling spaghetti.", - "unit": "mm³", - "type": "float", - "default_value": 0, - "minimum_value_warning": "0", - "maximum_value_warning": "100", - "enabled": "infill_sparse_density > 0 and spaghetti_infill_enabled", - "limit_to_extruder": "infill_extruder_nr", - "settable_per_mesh": true - }, "support_conical_enabled": { "label": "Enable Conical Support", diff --git a/resources/definitions/skriware_2.def.json b/resources/definitions/skriware_2.def.json index ab5532db81..2554689be4 100644 --- a/resources/definitions/skriware_2.def.json +++ b/resources/definitions/skriware_2.def.json @@ -447,7 +447,7 @@ "value": "5" }, "skin_outline_count": { - "default_value": 0 + "value": 0 }, "skirt_brim_speed": { "value": "10.0" diff --git a/resources/qml/Dialogs/AboutDialog.qml b/resources/qml/Dialogs/AboutDialog.qml index e83a51ca7f..b926d2ca0e 100644 --- a/resources/qml/Dialogs/AboutDialog.qml +++ b/resources/qml/Dialogs/AboutDialog.qml @@ -147,13 +147,15 @@ UM.Dialog 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: "mypy", description: catalog.i18nc("@Label", "Static type checker for Python"), license: "MIT", url: "http://mypy-lang.org/" }); + projectsModel.append({ name: "certifi", description: catalog.i18nc("@Label", "Root Certificates for validating SSL trustworthiness"), license: "MPL", url: "https://github.com/certifi/python-certifi" }); + projectsModel.append({ name: "cryptography", description: catalog.i18nc("@Label", "Root Certificates for validating SSL trustworthiness"), license: "APACHE and BSD", url: "https://cryptography.io/" }); + projectsModel.append({ name: "Sentry", description: catalog.i18nc("@Label", "Python Error tracking library"), license: "BSD 2-Clause 'Simplified'", url: "https://sentry.io/for/python/" }); 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" }); diff --git a/resources/qml/Preferences/GeneralPage.qml b/resources/qml/Preferences/GeneralPage.qml index 62768556e4..0d3f6ea586 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/jobname_prefix") - prefixJobNameCheckbox.checked = boolCheck(UM.Preferences.getValue("cura/jobname_prefix")) + UM.Preferences.resetPreference("cura/job_name_template") + jobnameTemplateTextField.text = UM.Preferences.getValue("cura/job_name_template") UM.Preferences.resetPreference("view/show_overhang"); showOverhangCheckbox.checked = boolCheck(UM.Preferences.getValue("view/show_overhang")) UM.Preferences.resetPreference("view/show_xray_warning"); @@ -627,14 +627,25 @@ UM.PreferencesPage { width: childrenRect.width height: childrenRect.height - text: catalog.i18nc("@info:tooltip", "Should a prefix based on the printer name be added to the print job name automatically?") + 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") - CheckBox + Column { - 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) + 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) + } } } @@ -670,7 +681,7 @@ UM.PreferencesPage ComboBox { id: choiceOnOpenProjectDropDownButton - width: 200 * screenScaleFactor + width: 250 * screenScaleFactor model: ListModel { @@ -736,7 +747,7 @@ UM.PreferencesPage ComboBox { id: choiceOnProfileOverrideDropDownButton - width: 200 * screenScaleFactor + width: 250 * screenScaleFactor model: ListModel { diff --git a/resources/qml/Settings/SettingView.qml b/resources/qml/Settings/SettingView.qml index 4557bd2019..5dfba3e464 100644 --- a/resources/qml/Settings/SettingView.qml +++ b/resources/qml/Settings/SettingView.qml @@ -85,7 +85,7 @@ Item onEditingFinished: { - definitionsModel.filter = {"i18n_label": "*" + text} + definitionsModel.filter = {"i18n_label|i18n_description" : "*" + text} findingSettings = (text.length > 0) if (findingSettings != lastFindingSettings) { diff --git a/resources/setting_visibility/expert.cfg b/resources/setting_visibility/expert.cfg index 8ab532db8c..a5619ae784 100644 --- a/resources/setting_visibility/expert.cfg +++ b/resources/setting_visibility/expert.cfg @@ -378,13 +378,6 @@ coasting_volume coasting_min_volume coasting_speed cross_infill_pocket_size -spaghetti_infill_enabled -spaghetti_infill_stepped -spaghetti_max_infill_angle -spaghetti_max_height -spaghetti_inset -spaghetti_flow -spaghetti_infill_extra_volume support_conical_enabled support_conical_angle support_conical_min_width diff --git a/resources/texts/change_log.txt b/resources/texts/change_log.txt index 8ce125d932..7621f6f713 100644 --- a/resources/texts/change_log.txt +++ b/resources/texts/change_log.txt @@ -140,7 +140,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 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. +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. * Property checks on instance containers. A new speed optimization for reading setting values from profiles. diff --git a/tests/TestPrintInformation.py b/tests/TestPrintInformation.py index 5133dfcafb..577827f326 100644 --- a/tests/TestPrintInformation.py +++ b/tests/TestPrintInformation.py @@ -8,6 +8,13 @@ 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") @@ -19,7 +26,7 @@ def getPrintInformation(printer_name) -> PrintInformation: mocked_extruder_stack.material = mocked_material mock_application.getInstance = MagicMock(return_value = mock_application) - mocked_preferences.getValue = MagicMock(return_value = '{"omgzomg": {"spool_weight": 10, "spool_cost": 9}}') + mocked_preferences.getValue = MagicMock(side_effect = preferencesGetValue) global_container_stack = MagicMock() global_container_stack.definition.getName = MagicMock(return_value = printer_name)