diff --git a/.gitignore b/.gitignore index 5cc60c2f4c..0a66b6eb33 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ LC_MESSAGES *.lprof *~ *.qm +.directory .idea cura.desktop diff --git a/cura/Machines/Models/BaseMaterialsModel.py b/cura/Machines/Models/BaseMaterialsModel.py index 43b42c55ff..1b20e1188c 100644 --- a/cura/Machines/Models/BaseMaterialsModel.py +++ b/cura/Machines/Models/BaseMaterialsModel.py @@ -124,7 +124,7 @@ class BaseMaterialsModel(ListModel): "description": metadata["description"], "material": metadata["material"], "color_name": metadata["color_name"], - "color_code": metadata["color_code"], + "color_code": metadata.get("color_code", ""), "density": metadata.get("properties", {}).get("density", ""), "diameter": metadata.get("properties", {}).get("diameter", ""), "approximate_diameter": metadata["approximate_diameter"], diff --git a/cura/Machines/Models/QualityProfilesDropDownMenuModel.py b/cura/Machines/Models/QualityProfilesDropDownMenuModel.py index 59c4f4fa5b..a01cc1194f 100644 --- a/cura/Machines/Models/QualityProfilesDropDownMenuModel.py +++ b/cura/Machines/Models/QualityProfilesDropDownMenuModel.py @@ -6,10 +6,10 @@ from PyQt5.QtCore import Qt from UM.Application import Application from UM.Logger import Logger from UM.Qt.ListModel import ListModel +from UM.Settings.SettingFunction import SettingFunction from cura.Machines.QualityManager import QualityGroup - # # QML Model for all built-in quality profiles. This model is used for the drop-down quality menu. # @@ -106,4 +106,8 @@ class QualityProfilesDropDownMenuModel(ListModel): container = global_stack.definition if container and container.hasProperty("layer_height", "value"): layer_height = container.getProperty("layer_height", "value") + + if isinstance(layer_height, SettingFunction): + layer_height = layer_height(global_stack) + return float(layer_height) diff --git a/cura/Machines/QualityManager.py b/cura/Machines/QualityManager.py index cb2776429f..6869d99bc7 100644 --- a/cura/Machines/QualityManager.py +++ b/cura/Machines/QualityManager.py @@ -363,8 +363,19 @@ class QualityManager(QObject): @pyqtSlot(QObject) def removeQualityChangesGroup(self, quality_changes_group: "QualityChangesGroup"): Logger.log("i", "Removing quality changes group [%s]", quality_changes_group.name) + removed_quality_changes_ids = set() for node in quality_changes_group.getAllNodes(): - self._container_registry.removeContainer(node.getMetaDataEntry("id")) + container_id = node.getMetaDataEntry("id") + self._container_registry.removeContainer(container_id) + removed_quality_changes_ids.add(container_id) + + # Reset all machines that have activated this quality changes to empty. + for global_stack in self._container_registry.findContainerStacks(type = "machine"): + if global_stack.qualityChanges.getId() in removed_quality_changes_ids: + global_stack.qualityChanges = self._empty_quality_changes_container + for extruder_stack in self._container_registry.findContainerStacks(type = "extruder_train"): + if extruder_stack.qualityChanges.getId() in removed_quality_changes_ids: + extruder_stack.qualityChanges = self._empty_quality_changes_container # # Rename a set of quality changes containers. Returns the new name. diff --git a/plugins/3MFReader/ThreeMFWorkspaceReader.py b/plugins/3MFReader/ThreeMFWorkspaceReader.py index 16ab22473b..6d55e0643d 100755 --- a/plugins/3MFReader/ThreeMFWorkspaceReader.py +++ b/plugins/3MFReader/ThreeMFWorkspaceReader.py @@ -85,14 +85,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader): def __init__(self) -> None: super().__init__() - MimeTypeDatabase.addMimeType( - MimeType( - name="application/x-curaproject+xml", - comment="Cura Project File", - suffixes=["curaproject.3mf"] - ) - ) - self._supported_extensions = [".3mf"] self._dialog = WorkspaceDialog() self._3mf_mesh_reader = None @@ -726,8 +718,6 @@ class ThreeMFWorkspaceReader(WorkspaceReader): nodes = [] base_file_name = os.path.basename(file_name) - if base_file_name.endswith(".curaproject.3mf"): - base_file_name = base_file_name[:base_file_name.rfind(".curaproject.3mf")] self.setWorkspaceName(base_file_name) return nodes diff --git a/plugins/3MFReader/__init__.py b/plugins/3MFReader/__init__.py index 3a4fde4ab8..ce94bbe69c 100644 --- a/plugins/3MFReader/__init__.py +++ b/plugins/3MFReader/__init__.py @@ -18,11 +18,7 @@ catalog = i18nCatalog("cura") def getMetaData() -> Dict: - # Workaround for osx not supporting double file extensions correctly. - if Platform.isOSX(): - workspace_extension = "3mf" - else: - workspace_extension = "curaproject.3mf" + workspace_extension = "3mf" metaData = {} if "3MFReader.ThreeMFReader" in sys.modules: diff --git a/plugins/3MFReader/plugin.json b/plugins/3MFReader/plugin.json index 5d15123017..5e41975752 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/3MFWriter/__init__.py b/plugins/3MFWriter/__init__.py index e779628f7e..4b8a03888d 100644 --- a/plugins/3MFWriter/__init__.py +++ b/plugins/3MFWriter/__init__.py @@ -15,11 +15,7 @@ from UM.Platform import Platform i18n_catalog = i18nCatalog("uranium") def getMetaData(): - # Workarround for osx not supporting double file extensions correctly. - if Platform.isOSX(): - workspace_extension = "3mf" - else: - workspace_extension = "curaproject.3mf" + workspace_extension = "3mf" metaData = {} @@ -36,7 +32,7 @@ def getMetaData(): "output": [{ "extension": workspace_extension, "description": i18n_catalog.i18nc("@item:inlistbox", "Cura Project 3MF file"), - "mime_type": "application/x-curaproject+xml", + "mime_type": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml", "mode": ThreeMFWorkspaceWriter.ThreeMFWorkspaceWriter.OutputMode.BinaryMode }] } diff --git a/plugins/3MFWriter/plugin.json b/plugins/3MFWriter/plugin.json index 22d18b2cf1..9ec4fb0c20 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/ChangeLogPlugin/plugin.json b/plugins/ChangeLogPlugin/plugin.json index e9414b9b71..e09a08564a 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/CuraEngineBackend/plugin.json b/plugins/CuraEngineBackend/plugin.json index e5df06f228..111698d8d1 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": 4, + "api": 5, "version": "1.0.0", "i18n-catalog": "cura" } diff --git a/plugins/CuraProfileReader/plugin.json b/plugins/CuraProfileReader/plugin.json index 004a1ade4d..66a2a6a56b 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/CuraProfileWriter/plugin.json b/plugins/CuraProfileWriter/plugin.json index d9accce770..16c8c34152 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": 4, + "api": 5, "i18n-catalog":"cura" } diff --git a/plugins/FirmwareUpdateChecker/plugin.json b/plugins/FirmwareUpdateChecker/plugin.json index d6a9f9fbd7..cbbd41e420 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/GCodeGzReader/plugin.json b/plugins/GCodeGzReader/plugin.json index e9f14724e0..3bd6a4097d 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/GCodeGzWriter/plugin.json b/plugins/GCodeGzWriter/plugin.json index 9774e9a25c..4c6497317b 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/GCodeProfileReader/plugin.json b/plugins/GCodeProfileReader/plugin.json index f8f7d4c291..9677628c85 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/GCodeReader/plugin.json b/plugins/GCodeReader/plugin.json index f72a8cc12c..75b4d0cd4f 100644 --- a/plugins/GCodeReader/plugin.json +++ b/plugins/GCodeReader/plugin.json @@ -3,6 +3,6 @@ "author": "Victor Larchenko", "version": "1.0.0", "description": "Allows loading and displaying G-code files.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/GCodeWriter/plugin.json b/plugins/GCodeWriter/plugin.json index 5fcb1a3bd7..3bbbab8b95 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/ImageReader/plugin.json b/plugins/ImageReader/plugin.json index 2752c6e8f4..08195863e8 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/LegacyProfileReader/plugin.json b/plugins/LegacyProfileReader/plugin.json index 2dc71511a9..179f5444e0 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/MachineSettingsAction/plugin.json b/plugins/MachineSettingsAction/plugin.json index 703a145deb..571658e40a 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/ModelChecker/plugin.json b/plugins/ModelChecker/plugin.json index a9190adcaa..3753c0cc88 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": 4, + "api": 5, "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 cb3f55a80d..88b53840e0 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": 4, + "api": 5, "i18n-catalog": "cura" } \ No newline at end of file diff --git a/plugins/PerObjectSettingsTool/plugin.json b/plugins/PerObjectSettingsTool/plugin.json index 3254662d33..15fde63387 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/PostProcessingPlugin/plugin.json b/plugins/PostProcessingPlugin/plugin.json index ebfef8145a..fea061e93b 100644 --- a/plugins/PostProcessingPlugin/plugin.json +++ b/plugins/PostProcessingPlugin/plugin.json @@ -2,7 +2,7 @@ "name": "Post Processing", "author": "Ultimaker", "version": "2.2", - "api": 4, + "api": 5, "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 4fd55e955e..f0464313c7 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": 4, + "api": 5, "i18n-catalog": "cura" } \ No newline at end of file diff --git a/plugins/RemovableDriveOutputDevice/plugin.json b/plugins/RemovableDriveOutputDevice/plugin.json index df11644256..36bb9ae186 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/SimulationView/plugin.json b/plugins/SimulationView/plugin.json index 0e7bec0626..93df98068f 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/SliceInfoPlugin/plugin.json b/plugins/SliceInfoPlugin/plugin.json index d1c643266b..939e5ff235 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/SolidView/plugin.json b/plugins/SolidView/plugin.json index 6d6bda96f0..e70ec224dd 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": 4, + "api": 5, "i18n-catalog": "cura" } \ No newline at end of file diff --git a/plugins/SupportEraser/plugin.json b/plugins/SupportEraser/plugin.json index 5ccb639913..7af35e0fb5 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/Toolbox/plugin.json b/plugins/Toolbox/plugin.json index 12d4042b6b..2557185524 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": 4, + "api": 5, "description": "Find, manage and install new Cura packages." } diff --git a/plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml b/plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml index 04b055ed66..90b92bd832 100644 --- a/plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxAuthorPage.qml @@ -82,9 +82,16 @@ Item } spacing: Math.floor(UM.Theme.getSize("narrow_margin").height) width: childrenRect.width + Label { - text: catalog.i18nc("@label", "Contact") + ":" + text: catalog.i18nc("@label", "Website") + ":" + font: UM.Theme.getFont("very_small") + color: UM.Theme.getColor("text_medium") + } + Label + { + text: catalog.i18nc("@label", "Email") + ":" font: UM.Theme.getFont("very_small") color: UM.Theme.getColor("text_medium") } @@ -100,18 +107,32 @@ Item topMargin: UM.Theme.getSize("default_margin").height } spacing: Math.floor(UM.Theme.getSize("narrow_margin").height) + + Label + { + text: + { + if (details.website) + { + return "" + details.website + "" + } + return "" + } + font: UM.Theme.getFont("very_small") + color: UM.Theme.getColor("text") + linkColor: UM.Theme.getColor("text_link") + onLinkActivated: Qt.openUrlExternally(link) + } + Label { text: { if (details.email) { - return ""+details.name+"" - } - else - { - return ""+details.name+"" + return "" + details.email + "" } + return "" } font: UM.Theme.getFont("very_small") color: UM.Theme.getColor("text") diff --git a/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml b/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml index 1efcde2110..4a008f2a83 100644 --- a/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml +++ b/plugins/Toolbox/resources/qml/ToolboxCompatibilityChart.qml @@ -8,7 +8,18 @@ import UM 1.1 as UM Item { + id: base + property var packageData + property var technicalDataSheetUrl: { + var link = undefined + if ("Technical Data Sheet" in packageData.links) + { + link = packageData.links["Technical Data Sheet"] + } + return link + } + anchors.topMargin: UM.Theme.getSize("default_margin").height height: visible ? childrenRect.height : 0 visible: packageData.type == "material" && packageData.has_configs @@ -132,4 +143,25 @@ Item width: Math.floor(table.width * 0.1) } } + + Label + { + id: technical_data_sheet + anchors.top: table.bottom + anchors.topMargin: UM.Theme.getSize("default_margin").height / 2 + visible: base.technicalDataSheetUrl !== undefined + text: + { + if (base.technicalDataSheetUrl !== undefined) + { + return "%2".arg(base.technicalDataSheetUrl).arg("Technical Data Sheet") + } + return "" + } + 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/ToolboxDownloadsGridTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml index ebd4c006f8..4366db041c 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml @@ -9,7 +9,7 @@ import UM 1.1 as UM Item { - property int packageCount: (toolbox.viewCategory == "material" && model.type === undefined) ? toolbox.getTotalNumberOfPackagesByAuthor(model.id) : 1 + 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 Layout.alignment: Qt.AlignTop | Qt.AlignLeft diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsPage.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsPage.qml index 1089fcc51e..bcca02583d 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsPage.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsPage.qml @@ -30,7 +30,7 @@ ScrollView id: allPlugins width: parent.width heading: toolbox.viewCategory == "material" ? catalog.i18nc("@label", "Community Contributions") : catalog.i18nc("@label", "Community Plugins") - model: toolbox.viewCategory == "material" ? toolbox.authorsModel : toolbox.packagesModel + model: toolbox.viewCategory == "material" ? toolbox.materialsAvailableModel : toolbox.pluginsAvailableModel } ToolboxDownloadsGrid diff --git a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml index 15d1ae302c..845bbe8f91 100644 --- a/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml +++ b/plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml @@ -9,7 +9,7 @@ import UM 1.1 as UM Rectangle { - property int packageCount: toolbox.viewCategory == "material" ? toolbox.getTotalNumberOfPackagesByAuthor(model.id) : 1 + property int packageCount: toolbox.viewCategory == "material" ? toolbox.getTotalNumberOfMaterialPackagesByAuthor(model.id) : 1 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) diff --git a/plugins/Toolbox/src/PackagesModel.py b/plugins/Toolbox/src/PackagesModel.py index 8b9199b127..7892044d00 100644 --- a/plugins/Toolbox/src/PackagesModel.py +++ b/plugins/Toolbox/src/PackagesModel.py @@ -5,9 +5,13 @@ import re from typing import Dict from PyQt5.QtCore import Qt, pyqtProperty + +from UM.Logger import Logger from UM.Qt.ListModel import ListModel + from .ConfigsModel import ConfigsModel + ## Model that holds cura packages. By setting the filter property the instances held by this model can be changed. class PackagesModel(ListModel): def __init__(self, parent = None): @@ -34,6 +38,8 @@ class PackagesModel(ListModel): self.addRoleName(Qt.UserRole + 17, "supported_configs") self.addRoleName(Qt.UserRole + 18, "download_count") self.addRoleName(Qt.UserRole + 19, "tags") + self.addRoleName(Qt.UserRole + 20, "links") + self.addRoleName(Qt.UserRole + 21, "website") # List of filters for queries. The result is the union of the each list of results. self._filter = {} # type: Dict[str, str] @@ -45,10 +51,16 @@ class PackagesModel(ListModel): def _update(self): items = [] - for package in self._metadata: + if self._metadata is None: + Logger.logException("w", "Failed to load packages for Toolbox") + self.setItems(items) + return + for package in self._metadata: has_configs = False configs_model = None + + links_dict = {} if "data" in package: if "supported_configs" in package["data"]: if len(package["data"]["supported_configs"]) > 0: @@ -56,41 +68,48 @@ class PackagesModel(ListModel): configs_model = ConfigsModel() configs_model.setConfigs(package["data"]["supported_configs"]) + # Links is a list of dictionaries with "title" and "url". Convert this list into a dict so it's easier + # to process. + link_list = package['data']['links'] if 'links' in package['data'] else [] + links_dict = {d["title"]: d["url"] for d in link_list} + if "author_id" not in package["author"] or "display_name" not in package["author"]: package["author"]["author_id"] = "" package["author"]["display_name"] = "" # raise Exception("Detected a package with malformed author data.") items.append({ - "id": package["package_id"], - "type": package["package_type"], - "name": package["display_name"], - "version": package["package_version"], - "author_id": package["author"]["author_id"], - "author_name": package["author"]["display_name"], - "author_email": package["author"]["email"] if "email" in package["author"] else None, - "description": package["description"] if "description" in package else None, - "icon_url": package["icon_url"] if "icon_url" in package else None, - "image_urls": package["image_urls"] if "image_urls" in package else None, - "download_url": package["download_url"] if "download_url" in package else None, - "last_updated": package["last_updated"] if "last_updated" in package else None, - "is_bundled": package["is_bundled"] if "is_bundled" in package else False, - "is_active": package["is_active"] if "is_active" in package else False, - "is_installed": package["is_installed"] if "is_installed" in package else False, - "has_configs": has_configs, - "supported_configs": configs_model, - "download_count": package["download_count"] if "download_count" in package else 0, - "tags": package["tags"] if "tags" in package else [] + "id": package["package_id"], + "type": package["package_type"], + "name": package["display_name"], + "version": package["package_version"], + "author_id": package["author"]["author_id"], + "author_name": package["author"]["display_name"], + "author_email": package["author"]["email"] if "email" in package["author"] else None, + "description": package["description"] if "description" in package else None, + "icon_url": package["icon_url"] if "icon_url" in package else None, + "image_urls": package["image_urls"] if "image_urls" in package else None, + "download_url": package["download_url"] if "download_url" in package else None, + "last_updated": package["last_updated"] if "last_updated" in package else None, + "is_bundled": package["is_bundled"] if "is_bundled" in package else False, + "is_active": package["is_active"] if "is_active" in package else False, + "is_installed": package["is_installed"] if "is_installed" in package else False, + "has_configs": has_configs, + "supported_configs": configs_model, + "download_count": package["download_count"] if "download_count" in package else 0, + "tags": package["tags"] if "tags" in package else [], + "links": links_dict, + "website": package["website"] if "website" in package else None, }) # Filter on all the key-word arguments. for key, value in self._filter.items(): if key is "tags": - key_filter = lambda item, value = value: value in item["tags"] + key_filter = lambda item, v = value: v in item["tags"] elif "*" in value: - key_filter = lambda candidate, key = key, value = value: self._matchRegExp(candidate, key, value) + key_filter = lambda candidate, k = key, v = value: self._matchRegExp(candidate, k, v) else: - key_filter = lambda candidate, key = key, value = value: self._matchString(candidate, key, value) + key_filter = lambda candidate, k = key, v = value: self._matchString(candidate, k, v) items = filter(key_filter, items) # Execute all filters. diff --git a/plugins/Toolbox/src/Toolbox.py b/plugins/Toolbox/src/Toolbox.py index c4205b8ed5..00864c6b4e 100644 --- a/plugins/Toolbox/src/Toolbox.py +++ b/plugins/Toolbox/src/Toolbox.py @@ -83,7 +83,7 @@ class Toolbox(QObject, Extension): "plugins_available": PackagesModel(self), "plugins_installed": PackagesModel(self), "materials_showcase": AuthorsModel(self), - "materials_available": PackagesModel(self), + "materials_available": AuthorsModel(self), "materials_installed": PackagesModel(self), "materials_generic": PackagesModel(self) } # type: Dict[str, ListModel] @@ -514,12 +514,14 @@ class Toolbox(QObject, Extension): count += 1 return count + # This slot is only used to get the number of material packages by author, not any other type of packages. @pyqtSlot(str, result = int) - def getTotalNumberOfPackagesByAuthor(self, author_id: str) -> int: + def getTotalNumberOfMaterialPackagesByAuthor(self, author_id: str) -> int: count = 0 - for package in self._metadata["materials_available"]: - if package["author"]["author_id"] == author_id: - count += 1 + for package in self._metadata["packages"]: + if package["package_type"] == "material": + if package["author"]["author_id"] == author_id: + count += 1 return count @pyqtSlot(str, result = bool) @@ -606,8 +608,21 @@ 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", + "plugins_available", + "plugins_showcase", + ] + if reply.operation() == QNetworkAccessManager.GetOperation: for 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: + return + if reply.url() == url: if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) == 200: try: @@ -623,25 +638,16 @@ class Toolbox(QObject, Extension): if not self._models[type]: Logger.log("e", "Could not find the %s model.", type) break - - # HACK: Eventually get rid of the code from here... - if type is "plugins_showcase" or type is "materials_showcase": - self._metadata["plugins_showcase"] = json_data["data"]["plugin"]["packages"] - self._models["plugins_showcase"].setMetadata(self._metadata["plugins_showcase"]) - self._metadata["materials_showcase"] = json_data["data"]["material"]["authors"] - self._models["materials_showcase"].setMetadata(self._metadata["materials_showcase"]) - else: - # ...until here. - # This hack arises for multiple reasons but the main - # one is because there are not separate API calls - # for different kinds of showcases. - self._metadata[type] = json_data["data"] - self._models[type].setMetadata(self._metadata[type]) + + self._metadata[type] = json_data["data"] + self._models[type].setMetadata(self._metadata[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"}) + self.buildMaterialsModels() + self.buildPluginsModels() if type is "authors": self._models[type].setFilter({"package_types": "material"}) if type is "materials_generic": @@ -755,6 +761,10 @@ class Toolbox(QObject, Extension): def pluginsShowcaseModel(self) -> PackagesModel: return cast(PackagesModel, self._models["plugins_showcase"]) + @pyqtProperty(QObject, notify = metadataChanged) + def pluginsAvailableModel(self) -> PackagesModel: + return cast(PackagesModel, self._models["plugins_available"]) + @pyqtProperty(QObject, notify = metadataChanged) def pluginsInstalledModel(self) -> PackagesModel: return cast(PackagesModel, self._models["plugins_installed"]) @@ -763,6 +773,10 @@ class Toolbox(QObject, Extension): def materialsShowcaseModel(self) -> AuthorsModel: return cast(AuthorsModel, self._models["materials_showcase"]) + @pyqtProperty(QObject, notify = metadataChanged) + def materialsAvailableModel(self) -> AuthorsModel: + return cast(AuthorsModel, self._models["materials_available"]) + @pyqtProperty(QObject, notify = metadataChanged) def materialsInstalledModel(self) -> PackagesModel: return cast(PackagesModel, self._models["materials_installed"]) @@ -798,3 +812,46 @@ class Toolbox(QObject, Extension): return self._models[model_type].setFilter({}) self.filterChanged.emit() + + + # HACK(S): + # -------------------------------------------------------------------------- + def buildMaterialsModels(self) -> None: + + self._metadata["materials_showcase"] = [] + self._metadata["materials_available"] = [] + + processed_authors = [] # type: List[str] + + for item in self._metadata["packages"]: + if item["package_type"] == "material": + + author = item["author"] + if author["author_id"] in processed_authors: + continue + + if "showcase" in item["tags"]: + self._metadata["materials_showcase"].append(author) + else: + self._metadata["materials_available"].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"]) + + def buildPluginsModels(self) -> None: + + self._metadata["plugins_showcase"] = [] + self._metadata["plugins_available"] = [] + + for item in self._metadata["packages"]: + if item["package_type"] == "plugin": + + if "showcase" in item["tags"]: + self._metadata["plugins_showcase"].append(item) + else: + self._metadata["plugins_available"].append(item) + + self._models["plugins_showcase"].setMetadata(self._metadata["plugins_showcase"]) + self._models["plugins_available"].setMetadata(self._metadata["plugins_available"]) diff --git a/plugins/UFPWriter/UFPWriter.py b/plugins/UFPWriter/UFPWriter.py index ceb9d79087..a85ee489cb 100644 --- a/plugins/UFPWriter/UFPWriter.py +++ b/plugins/UFPWriter/UFPWriter.py @@ -13,6 +13,7 @@ from UM.PluginRegistry import PluginRegistry #To get the g-code writer. from PyQt5.QtCore import QBuffer from cura.Snapshot import Snapshot +from cura.Utils.Threading import call_on_qt_thread from UM.i18n import i18nCatalog catalog = i18nCatalog("cura") @@ -29,6 +30,11 @@ class UFPWriter(MeshWriter): Logger.log("d", "Creating thumbnail image...") self._snapshot = Snapshot.snapshot(width = 300, height = 300) + # This needs to be called on the main thread (Qt thread) because the serialization of material containers can + # trigger loading other containers. Because those loaded containers are QtObjects, they must be created on the + # Qt thread. The File read/write operations right now are executed on separated threads because they are scheduled + # by the Job class. + @call_on_qt_thread def write(self, stream, nodes, mode = MeshWriter.OutputMode.BinaryMode): archive = VirtualFile() archive.openStream(stream, "application/x-ufp", OpenMode.WriteOnly) @@ -60,5 +66,50 @@ class UFPWriter(MeshWriter): else: Logger.log("d", "Thumbnail not created, cannot save it") + # Store the material. + application = Application.getInstance() + machine_manager = application.getMachineManager() + material_manager = application.getMaterialManager() + global_stack = machine_manager.activeMachine + + material_extension = "xml.fdm_material" + material_mime_type = "application/x-ultimaker-material-profile" + + try: + archive.addContentType(extension = material_extension, mime_type = material_mime_type) + except: + Logger.log("w", "The material extension: %s was already added", material_extension) + + added_materials = [] + for extruder_stack in global_stack.extruders.values(): + material = extruder_stack.material + material_file_name = material.getMetaData()["base_file"] + ".xml.fdm_material" + material_file_name = "/Materials/" + material_file_name + + #Same material cannot be added + if material_file_name in added_materials: + continue + + material_root_id = material.getMetaDataEntry("base_file") + material_group = material_manager.getMaterialGroup(material_root_id) + if material_group is None: + Logger.log("e", "Cannot find material container with root id [%s]", material_root_id) + return False + + material_container = material_group.root_material_node.getContainer() + try: + serialized_material = material_container.serialize() + except NotImplementedError: + Logger.log("e", "Unable serialize material container with root id: %s", material_root_id) + return False + + material_file = archive.getStream(material_file_name) + material_file.write(serialized_material.encode("UTF-8")) + archive.addRelation(virtual_path = material_file_name, + relation_type = "http://schemas.ultimaker.org/package/2018/relationships/material", + origin = "/3D/model.gcode") + + added_materials.append(material_file_name) + archive.close() return True diff --git a/plugins/UFPWriter/plugin.json b/plugins/UFPWriter/plugin.json index 7d10b89ad4..ab590353e0 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": 4, + "api": 5, "i18n-catalog": "cura" } \ No newline at end of file diff --git a/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml b/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml index 127b3c35bd..b5b80a3010 100644 --- a/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml +++ b/plugins/UM3NetworkPrinting/DiscoverUM3Action.qml @@ -364,7 +364,6 @@ Cura.MachineAction { id: addressField width: parent.width - maximumLength: 40 validator: RegExpValidator { regExp: /[a-zA-Z0-9\.\-\_]*/ diff --git a/plugins/UM3NetworkPrinting/plugin.json b/plugins/UM3NetworkPrinting/plugin.json index e7b59fadd6..d415338374 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index 45b566fcab..4ceda52875 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -326,7 +326,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice): if self._firmware_name is None: self.sendCommand("M115") - if (b"ok " in line and b"T:" in line) or b"ok T:" in line or line.startswith(b"T:") or b"ok B:" in line or line.startswith(b"B:"): # Temperature message. 'T:' for extruder and 'B:' for bed + if (b"ok " in line and b"T:" in line) or line.startswith(b"T:") or b"ok B:" in line or line.startswith(b"B:"): # Temperature message. 'T:' for extruder and 'B:' for bed extruder_temperature_matches = re.findall(b"T(\d*): ?([\d\.]+) ?\/?([\d\.]+)?", line) # Update all temperature values matched_extruder_nrs = [] diff --git a/plugins/USBPrinting/plugin.json b/plugins/USBPrinting/plugin.json index 27e07c45b2..3484c8a48a 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.0", - "api": 4, + "api": 5, "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 57b3e6bc8f..b60c7df88e 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/UserAgreement/plugin.json b/plugins/UserAgreement/plugin.json index b10abc5640..50a2aa0441 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade21to22/plugin.json b/plugins/VersionUpgrade/VersionUpgrade21to22/plugin.json index 79115f931e..463fcdc941 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade22to24/plugin.json b/plugins/VersionUpgrade/VersionUpgrade22to24/plugin.json index d213042ad2..e7a0b1c559 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade25to26/plugin.json b/plugins/VersionUpgrade/VersionUpgrade25to26/plugin.json index 759b6368fd..3029539887 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade26to27/plugin.json b/plugins/VersionUpgrade/VersionUpgrade26to27/plugin.json index 3c3d7fff8c..225da67235 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade27to30/plugin.json b/plugins/VersionUpgrade/VersionUpgrade27to30/plugin.json index 3df84ff7e6..9a139851ec 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade30to31/plugin.json b/plugins/VersionUpgrade/VersionUpgrade30to31/plugin.json index d80b820976..cf42b3f6cd 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json b/plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json index fbce09c807..f9cc968dae 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json b/plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json index 164b79d504..f5ba7235d1 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/VersionUpgrade/VersionUpgrade34to40/plugin.json b/plugins/VersionUpgrade/VersionUpgrade34to40/plugin.json index 1059ca3e57..c07ae31c0a 100644 --- a/plugins/VersionUpgrade/VersionUpgrade34to40/plugin.json +++ b/plugins/VersionUpgrade/VersionUpgrade34to40/plugin.json @@ -3,6 +3,6 @@ "author": "Ultimaker B.V.", "version": "1.0.0", "description": "Upgrades configurations from Cura 3.4 to Cura 4.0.", - "api": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/X3DReader/plugin.json b/plugins/X3DReader/plugin.json index f18c7f033d..9ee09e43df 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/XRayView/plugin.json b/plugins/XRayView/plugin.json index 4e89690c13..576dec4656 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/plugins/XmlMaterialProfile/plugin.json b/plugins/XmlMaterialProfile/plugin.json index 17056dcb3e..4b2901c375 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": 4, + "api": 5, "i18n-catalog": "cura" } diff --git a/resources/definitions/bq_hephestos_2.def.json b/resources/definitions/bq_hephestos_2.def.json index ca0e66ada2..90a86433fb 100644 --- a/resources/definitions/bq_hephestos_2.def.json +++ b/resources/definitions/bq_hephestos_2.def.json @@ -17,8 +17,8 @@ "overrides": { "machine_name": { "default_value": "BQ Hephestos 2" }, - "machine_start_gcode": { "default_value": "; -- START GCODE --\nM104 S{material_print_temperature} ; Heat up extruder while leveling\nM800 ; Custom GCODE to fire start print procedure\nM109 S{material_print_temperature} ; Makes sure the temperature is correct before printing\n; -- end of START GCODE --" }, - "machine_end_gcode": { "default_value": "; -- END GCODE --\nM801 ; Custom GCODE to fire end print procedure\n; -- end of END GCODE --" }, + "machine_start_gcode": { "default_value": "; -- START GCODE --\nM104 S{material_print_temperature}\nG28 ; Zero-ing position\nG29 ; Auto bed-leveling\nG0 X4 Y297 Z15 F4000 ; Fast move to BQ's start position\nG90 ; Set to Absolute Positioning\nG92 E0 ; Reset extruder 0\nG1 F1800 ; Set default feedrate\nM109 S{material_print_temperature} ; Makes sure the temperature is correct before printing\n; -- end of START GCODE --" }, + "machine_end_gcode": { "default_value": "; -- END GCODE --\nM801 ; Marlin G-CODE to fire end print procedure\n; -- end of END GCODE --" }, "machine_width": { "default_value": 210 }, "machine_depth": { "default_value": 297 }, "machine_height": { "default_value": 220 }, diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 3eb7cb1c32..a85a251ac2 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -1819,9 +1819,9 @@ "unit": "mm", "type": "float", "default_value": 0.1, - "minimum_value": "resolveOrValue('layer_height')", + "minimum_value": "resolveOrValue('layer_height') 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)", + "maximum_value": "resolveOrValue('layer_height') * (1.45 if spaghetti_infill_enabled else 8) if infill_line_distance > 0 else 999999", "value": "resolveOrValue('layer_height')", "enabled": "infill_sparse_density > 0 and not spaghetti_infill_enabled", "limit_to_extruder": "infill_extruder_nr", @@ -3875,6 +3875,19 @@ } } }, + "support_infill_angle": + { + "label": "Support Infill Line Direction", + "description": "Orientation of the infill pattern for supports. The support infill pattern is rotated in the horizontal plane.", + "unit": "°", + "type": "float", + "minimum_value": "-180", + "maximum_value": "180", + "default_value": 0, + "enabled": "support_enable and support_pattern != 'concentric' and support_infill_rate > 0", + "settable_per_mesh": false, + "settable_per_extruder": true + }, "support_z_distance": { "label": "Support Z Distance", diff --git a/resources/definitions/peopoly_moai.def.json b/resources/definitions/peopoly_moai.def.json index 5c03444d49..a578cc4240 100644 --- a/resources/definitions/peopoly_moai.def.json +++ b/resources/definitions/peopoly_moai.def.json @@ -4,13 +4,14 @@ "inherits": "fdmprinter", "metadata": { "visible": true, - "author": "fieldOfView", + "author": "Peopoly", "manufacturer": "Peopoly", "file_formats": "text/x-gcode", "has_machine_quality": true, "has_materials": false, - "machine_extruder_trains": - { + "platform": "moai.obj", + "platform_texture": "moai.jpg", + "machine_extruder_trains": { "0": "peopoly_moai_extruder_0" } }, @@ -46,7 +47,6 @@ "machine_end_gcode": { "default_value": "M104 S0\nM140 S0\nG28 X0 Y0\nM84" }, - "line_width": { "minimum_value_warning": "machine_nozzle_size" }, @@ -75,7 +75,14 @@ "value": "0.1" }, "top_bottom_thickness": { - "minimum_value_warning": "0.1" + "minimum_value_warning": "0.1", + "value": "0.1" + }, + "top_thickness": { + "minimum_value_warning": "resolveOrValue('layer_height')" + }, + "bottom_thickness": { + "minimum_value_warning": "resolveOrValue('layer_height')" }, "infill_sparse_thickness": { "maximum_value_warning": "0.5" @@ -102,24 +109,23 @@ "value": "speed_print" }, "speed_travel": { - "value": "300" + "value": 150 }, "speed_travel_layer_0": { - "value": "300" + "value": 150 }, "speed_layer_0": { - "value": "5" + "value": 5 }, "speed_slowdown_layers": { - "value": "2" + "value": 3 }, "infill_overlap": { - "value": "15" + "value": 15 }, "adhesion_type": { - "value": "\"none\"" + "value": "'none'" }, - "acceleration_enabled": { "value": "False" }, @@ -139,6 +145,10 @@ "enabled": false, "value": "False" }, + "cool_fan_speed_min": { + "enabled": false, + "value": 0 + }, "retraction_enable": { "enabled": false, "value": "False" @@ -148,7 +158,8 @@ "value": "'off'" }, "retract_at_layer_change": { - "enabled": false + "enabled": false, + "value": false }, "cool_min_layer_time_fan_speed_max": { "enabled": false @@ -158,6 +169,117 @@ }, "cool_fan_full_layer": { "enabled": false + }, + "minimum_polygon_circumference": { + "value": "0.1" + }, + "meshfix_maximum_resolution": { + "value": "0.005" + }, + "skin_outline_count": { + "value": 0 + }, + "travel_compensate_overlapping_walls_enabled": { + "value": "False" + }, + "travel_compensate_overlapping_walls_0_enabled": { + "value": "False" + }, + "travel_compensate_overlapping_walls_x_enabled": { + "value": "False" + }, + "wall_0_wipe_dist": { + "value": "machine_nozzle_size / 3" + }, + "wall_thickness": { + "value": 0.5 + }, + "infill_sparse_density": { + "value": 70 + }, + "infill_pattern": { + "value": "'lines'" + }, + "infill_angles": { + "value": "[0,90]" + }, + "cool_min_layer_time": { + "enabled": false, + "value": 0 + }, + "cool_min_speed": { + "enabled": false, + "value": 0 + }, + "cool_lift_head": { + "enabled": false, + "value": "False" + }, + "material_flow": { + "enabled": false + }, + "material_flow_layer_0": { + "enabled": false + }, + "speed_equalize_flow_enabled": { + "enabled": false, + "value": "False" + }, + "draft_shield_enabled": { + "enabled": false, + "value": "False" + }, + "z_seam_corner": { + "value": "'z_seam_corner_none'" + }, + "z_seam_type": { + "value": "'shortest'" + }, + "skin_no_small_gaps_heuristic": { + "value": "False" + }, + "ironing_enabled": { + "enabled": false, + "value": "False" + }, + "skin_overlap": { + "value": 5 + }, + "infill_wipe_dist": { + "value": 0 + }, + "expand_skins_expand_distance": { + "value": "( wall_line_width_0 + (wall_line_count - 1) * wall_line_width_x ) / 2" + }, + "max_feedrate_z_override": { + "value": 0, + "enabled": false + }, + "flow_rate_max_extrusion_offset": { + "enabled": false + }, + "flow_rate_extrusion_offset_factor": { + "enabled": false + }, + "adaptive_layer_height_enabled": { + "value": "False", + "enabled": false + }, + "bridge_settings_enabled": { + "value": "False", + "enabled": false + }, + "acceleration_enabled": { + "value": "False", + "enabled": false + }, + "relative_extrusion": { + "value": "False", + "enabled": false + }, + "coasting_enable": { + "value": "False", + "enabled": false } } } diff --git a/resources/extruders/peopoly_moai_extruder_0.def.json b/resources/extruders/peopoly_moai_extruder_0.def.json index 7940002926..bbffd4ac4d 100644 --- a/resources/extruders/peopoly_moai_extruder_0.def.json +++ b/resources/extruders/peopoly_moai_extruder_0.def.json @@ -11,6 +11,9 @@ "overrides": { "extruder_nr": { "default_value": 0 }, "machine_nozzle_size": { "default_value": 0.067 }, - "material_diameter": { "default_value": 1.75 } + "material_diameter": { + "enabled": false, + "default_value": 1.75 + } } } diff --git a/resources/images/moai.jpg b/resources/images/moai.jpg new file mode 100644 index 0000000000..54c6c7561e Binary files /dev/null and b/resources/images/moai.jpg differ diff --git a/resources/meshes/moai.obj b/resources/meshes/moai.obj new file mode 100644 index 0000000000..f13f30d6f4 --- /dev/null +++ b/resources/meshes/moai.obj @@ -0,0 +1,32 @@ +# OBJ written from C:\Users\Flo\Desktop\Cura_FILES\moai.obj +mtllib moai.mtl +# Units millimeters + +g Mesh +v 65 -65 0 +v -65 -65 0 +v -65 65 0 +v 65 65 0 +v 65 -65 0 +v -65 -65 0 +v -65 65 0 +v 65 65 0 +v -65 65 18.8383 +v 65 65 18.8383 +vn 0 0 1 +vn 1 0 0 +vn -1 0 0 +vt 0.0975501 1 +vt 1 1 +vt 1 0.0977239 +vt 0.0975501 0.0977239 +vt 0.0186426 0.870052 +vt 0.0736426 0.870052 +vt 0.0186426 0.815052 +vt 0.0214764 0.912057 +vt 0.0764764 0.967057 +vt 0.0214764 0.967057 +usemtl red +f 1/1/1 4/2/1 3/3/1 2/4/1 +f 7/5/2 9/6/2 6/7/2 +f 5/8/3 10/9/3 8/10/3 diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index 21e6eebf58..db230ebca7 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -110,28 +110,28 @@ Item Action { id: view3DCameraAction; - text: catalog.i18nc("@action:inmenu menubar:view","&3D View"); + text: catalog.i18nc("@action:inmenu menubar:view","3D View"); onTriggered: UM.Controller.rotateView("3d", 0); } Action { id: viewFrontCameraAction; - text: catalog.i18nc("@action:inmenu menubar:view","&Front View"); + text: catalog.i18nc("@action:inmenu menubar:view","Front View"); onTriggered: UM.Controller.rotateView("home", 0); } Action { id: viewTopCameraAction; - text: catalog.i18nc("@action:inmenu menubar:view","&Top View"); + text: catalog.i18nc("@action:inmenu menubar:view","Top View"); onTriggered: UM.Controller.rotateView("y", 90); } Action { id: viewLeftSideCameraAction; - text: catalog.i18nc("@action:inmenu menubar:view","&Left Side View"); + text: catalog.i18nc("@action:inmenu menubar:view","Left Side View"); onTriggered: UM.Controller.rotateView("x", 90); } @@ -229,23 +229,13 @@ Item Action { id: deleteSelectionAction; - text: catalog.i18ncp("@action:inmenu menubar:edit", "Delete &Selected Model", "Delete &Selected Models", UM.Selection.selectionCount); + text: catalog.i18ncp("@action:inmenu menubar:edit", "Delete Selected Model", "Delete Selected Models", UM.Selection.selectionCount); enabled: UM.Controller.toolsEnabled && UM.Selection.hasSelection; iconName: "edit-delete"; shortcut: StandardKey.Delete; onTriggered: CuraActions.deleteSelection(); } - Action //Also add backspace as the same function as delete because on Macintosh keyboards the button called "delete" is actually a backspace, and the user expects it to function as a delete. - { - id: backspaceSelectionAction - text: catalog.i18ncp("@action:inmenu menubar:edit", "Delete &Selected Model", "Delete &Selected Models", UM.Selection.selectionCount) - enabled: UM.Controller.toolsEnabled && UM.Selection.hasSelection - iconName: "edit-delete" - shortcut: StandardKey.Backspace - onTriggered: CuraActions.deleteSelection() - } - Action { id: centerSelectionAction; @@ -328,7 +318,7 @@ Item Action { id: selectAllAction; - text: catalog.i18nc("@action:inmenu menubar:edit","&Select All Models"); + text: catalog.i18nc("@action:inmenu menubar:edit","Select All Models"); enabled: UM.Controller.toolsEnabled; iconName: "edit-select-all"; shortcut: "Ctrl+A"; @@ -338,7 +328,7 @@ Item Action { id: deleteAllAction; - text: catalog.i18nc("@action:inmenu menubar:edit","&Clear Build Plate"); + text: catalog.i18nc("@action:inmenu menubar:edit","Clear Build Plate"); enabled: UM.Controller.toolsEnabled; iconName: "edit-delete"; shortcut: "Ctrl+D"; @@ -348,7 +338,7 @@ Item Action { id: reloadAllAction; - text: catalog.i18nc("@action:inmenu menubar:file","Re&load All Models"); + text: catalog.i18nc("@action:inmenu menubar:file","Reload All Models"); iconName: "document-revert"; shortcut: "F5" onTriggered: CuraApplication.reloadAll(); diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index cd8a122bd2..97742de57a 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -120,7 +120,7 @@ UM.MainWindow text: catalog.i18nc("@title:menu menubar:file","&Save...") onTriggered: { - var args = { "filter_by_machine": false, "file_type": "workspace", "preferred_mimetypes": "application/x-curaproject+xml" }; + 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; diff --git a/resources/qml/Menus/MaterialMenu.qml b/resources/qml/Menus/MaterialMenu.qml index 186c5d1d2a..f9e343d2dd 100644 --- a/resources/qml/Menus/MaterialMenu.qml +++ b/resources/qml/Menus/MaterialMenu.qml @@ -10,7 +10,7 @@ import Cura 1.0 as Cura Menu { id: menu - title: "Material" + title: catalog.i18nc("@label:category menu label", "Material") property int extruderIndex: 0 @@ -32,6 +32,12 @@ Menu extruderPosition: menu.extruderIndex } + MenuItem + { + text: catalog.i18nc("@label:category menu label", "Favorites") + enabled: false + visible: favoriteMaterialsModel.items.length > 0 + } Instantiator { model: favoriteMaterialsModel @@ -52,7 +58,7 @@ Menu Menu { id: genericMenu - title: "Generic" + title: catalog.i18nc("@label:category menu label", "Generic") Instantiator { diff --git a/resources/quality/peopoly_moai/peopoly_moai_coarse.inst.cfg b/resources/quality/peopoly_moai/peopoly_moai_coarse.inst.cfg new file mode 100644 index 0000000000..ac4f9ee81d --- /dev/null +++ b/resources/quality/peopoly_moai/peopoly_moai_coarse.inst.cfg @@ -0,0 +1,17 @@ +[general] +version = 4 +name = Coarse +definition = peopoly_moai + +[metadata] +setting_version = 5 +type = quality +quality_type = coarse +weight = 3 + +[values] +layer_height = 0.08 +speed_print = 90 +speed_travel = 120 +speed_travel_layer_0 = 100 +speed_wall = 90 diff --git a/resources/quality/peopoly_moai/peopoly_moai_draft.inst.cfg b/resources/quality/peopoly_moai/peopoly_moai_draft.inst.cfg new file mode 100644 index 0000000000..2d21b1f7e0 --- /dev/null +++ b/resources/quality/peopoly_moai/peopoly_moai_draft.inst.cfg @@ -0,0 +1,18 @@ +[general] +version = 4 +name = Draft +definition = peopoly_moai + +[metadata] +setting_version = 5 +type = quality +quality_type = draft +weight = 4 + +[values] +layer_height = 0.1 +speed_print = 85 +speed_travel = 120 +speed_travel_layer_0 = 100 +speed_wall = 85 +speed_slowdown_layers = 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 new file mode 100644 index 0000000000..796c2cff3c --- /dev/null +++ b/resources/quality/peopoly_moai/peopoly_moai_extra_high.inst.cfg @@ -0,0 +1,18 @@ +[general] +version = 4 +name = Extra High +definition = peopoly_moai + +[metadata] +setting_version = 5 +type = quality +quality_type = extra_high +weight = 0 + +[values] +layer_height = 0.02 +speed_print = 185 +speed_travel = 185 +speed_travel_layer_0 = 100 +speed_wall = 185 +speed_slowdown_layers = 5 diff --git a/resources/quality/peopoly_moai/peopoly_moai_high.inst.cfg b/resources/quality/peopoly_moai/peopoly_moai_high.inst.cfg index 36b5f21ff8..b36163d9f1 100644 --- a/resources/quality/peopoly_moai/peopoly_moai_high.inst.cfg +++ b/resources/quality/peopoly_moai/peopoly_moai_high.inst.cfg @@ -1,6 +1,6 @@ [general] version = 4 -name = Extra Fine +name = High definition = peopoly_moai [metadata] @@ -10,8 +10,9 @@ quality_type = high weight = 1 [values] -infill_sparse_density = 70 -layer_height = 0.05 -top_bottom_thickness = 0.4 -wall_thickness = 0.4 -speed_print = 150 +layer_height = 0.04 +speed_print = 140 +speed_travel = 140 +speed_travel_layer_0 = 100 +speed_wall = 140 +speed_slowdown_layers = 4 diff --git a/resources/quality/peopoly_moai/peopoly_moai_max.inst.cfg b/resources/quality/peopoly_moai/peopoly_moai_max.inst.cfg deleted file mode 100644 index 48ffd07f33..0000000000 --- a/resources/quality/peopoly_moai/peopoly_moai_max.inst.cfg +++ /dev/null @@ -1,17 +0,0 @@ -[general] -version = 4 -name = Maximum Quality -definition = peopoly_moai - -[metadata] -setting_version = 5 -type = quality -quality_type = extra_high -weight = 2 - -[values] -infill_sparse_density = 70 -layer_height = 0.025 -top_bottom_thickness = 0.4 -wall_thickness = 0.4 -speed_print = 200 diff --git a/resources/quality/peopoly_moai/peopoly_moai_normal.inst.cfg b/resources/quality/peopoly_moai/peopoly_moai_normal.inst.cfg index f5fe799ac3..cf67591ab2 100644 --- a/resources/quality/peopoly_moai/peopoly_moai_normal.inst.cfg +++ b/resources/quality/peopoly_moai/peopoly_moai_normal.inst.cfg @@ -1,17 +1,17 @@ [general] version = 4 -name = Fine +name = Normal definition = peopoly_moai [metadata] setting_version = 5 type = quality quality_type = normal -weight = 0 +weight = 2 [values] -infill_sparse_density = 70 -layer_height = 0.1 -top_bottom_thickness = 0.4 -wall_thickness = 0.4 -speed_print = 100 +layer_height = 0.06 +speed_print = 120 +speed_travel = 120 +speed_travel_layer_0 = 100 +speed_wall = 120 diff --git a/resources/variants/ultimaker2_plus_0.4.inst.cfg b/resources/variants/ultimaker2_plus_0.4.inst.cfg index 544728f8a4..d9d982ef11 100644 --- a/resources/variants/ultimaker2_plus_0.4.inst.cfg +++ b/resources/variants/ultimaker2_plus_0.4.inst.cfg @@ -12,5 +12,5 @@ hardware_type = nozzle machine_nozzle_size = 0.4 machine_nozzle_tip_outer_diameter = 1.05 speed_wall = =round(speed_print / 1.25, 1) -speed_wall_0 = =1 if speed_wall < 10 else (speed_wall - 10) +speed_wall_0 = =min(speed_wall - 10, 1) speed_topbottom = =round(speed_print / 2.25, 1)