From 7b201fd0ae9669ec8cca92ee768fee90605cd625 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 25 Jan 2024 13:30:56 +0100 Subject: [PATCH 1/5] Add export sub-menu CURA-11561 --- UM/Qt/Bindings/Bindings.py | 4 ++++ UM/Qt/Bindings/MeshWritersModel.py | 35 ++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 UM/Qt/Bindings/MeshWritersModel.py diff --git a/UM/Qt/Bindings/Bindings.py b/UM/Qt/Bindings/Bindings.py index 0d81e3843..f4233f24d 100644 --- a/UM/Qt/Bindings/Bindings.py +++ b/UM/Qt/Bindings/Bindings.py @@ -31,6 +31,7 @@ from . import VisibleMessagesModel from . import Utilities from . import TableModel +from . import MeshWritersModel from UM.Settings.Models.SettingDefinitionsModel import SettingDefinitionsModel from UM.Settings.Models.DefinitionContainersModel import DefinitionContainersModel @@ -127,6 +128,9 @@ def register(self): qmlRegisterType(TableModel.TableModel, "UM", 1, 6, "TableModel") qmlRegisterType(Window.Window, "UM", 1, 6, "Window") + # Additions after 5.6 + qmlRegisterType(MeshWritersModel.MeshWritersModel, "UM", 1, 7, "MeshWritersModel") + @staticmethod def addRegisterType(class_type: type, qml_import_name: str, major_version: int, minor_version: int, class_name: str) -> None: qmlRegisterType(class_type, qml_import_name, major_version, minor_version, class_name) \ No newline at end of file diff --git a/UM/Qt/Bindings/MeshWritersModel.py b/UM/Qt/Bindings/MeshWritersModel.py new file mode 100644 index 000000000..739a92439 --- /dev/null +++ b/UM/Qt/Bindings/MeshWritersModel.py @@ -0,0 +1,35 @@ +# Copyright (c) 2024 Ultimaker B.V. +# Uranium is released under the terms of the LGPLv3 or higher. + +from typing import List + +from PyQt6.QtCore import Qt, pyqtSignal +from PyQt6.QtQml import QQmlEngine + +from UM.Application import Application +from UM.OutputDevice.OutputDeviceManager import OutputDeviceManager +from UM.OutputDevice.ProjectOutputDevice import ProjectOutputDevice +from UM.Qt.ListModel import ListModel + + +class MeshWritersModel(ListModel): + """A list model providing a list of all registered MeshWriters that can export meshes. + + Exposes the following roles: + * mime_type - The associated writable file mime type + * description - The human-readable name of the file format + + """ + + MimeTypeRole = Qt.ItemDataRole.UserRole + 1 + DescriptionRole = Qt.ItemDataRole.UserRole + 2 + + def __init__(self, parent = None): + super().__init__(parent) + # Ensure that this model doesn't get garbage collected (Now the bound object is destroyed when the wrapper is) + QQmlEngine.setObjectOwnership(self, QQmlEngine.ObjectOwnership.CppOwnership) + + self.addRoleName(self.MimeTypeRole, "mime_type") + self.addRoleName(self.DescriptionRole, "description") + + self.setItems(Application.getInstance().getMeshFileHandler().getSupportedFileTypesWrite()) From 1281b5486dcc60d49b16ca3a9517701fa1127b4e Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 25 Jan 2024 14:04:47 +0100 Subject: [PATCH 2/5] Fix exporting binary/ascii STL CURA-11561 --- UM/Qt/Bindings/MeshWritersModel.py | 8 ++++---- UM/Qt/Bindings/OutputDeviceManagerProxy.py | 20 ++++++++++++++----- .../LocalFileOutputDevice.py | 4 ++++ 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/UM/Qt/Bindings/MeshWritersModel.py b/UM/Qt/Bindings/MeshWritersModel.py index 739a92439..34d6ddf16 100644 --- a/UM/Qt/Bindings/MeshWritersModel.py +++ b/UM/Qt/Bindings/MeshWritersModel.py @@ -3,12 +3,10 @@ from typing import List -from PyQt6.QtCore import Qt, pyqtSignal +from PyQt6.QtCore import Qt from PyQt6.QtQml import QQmlEngine from UM.Application import Application -from UM.OutputDevice.OutputDeviceManager import OutputDeviceManager -from UM.OutputDevice.ProjectOutputDevice import ProjectOutputDevice from UM.Qt.ListModel import ListModel @@ -22,7 +20,8 @@ class MeshWritersModel(ListModel): """ MimeTypeRole = Qt.ItemDataRole.UserRole + 1 - DescriptionRole = Qt.ItemDataRole.UserRole + 2 + ModeRole = Qt.ItemDataRole.UserRole + 2 + DescriptionRole = Qt.ItemDataRole.UserRole + 3 def __init__(self, parent = None): super().__init__(parent) @@ -30,6 +29,7 @@ def __init__(self, parent = None): QQmlEngine.setObjectOwnership(self, QQmlEngine.ObjectOwnership.CppOwnership) self.addRoleName(self.MimeTypeRole, "mime_type") + self.addRoleName(self.ModeRole, "mode") self.addRoleName(self.DescriptionRole, "description") self.setItems(Application.getInstance().getMeshFileHandler().getSupportedFileTypesWrite()) diff --git a/UM/Qt/Bindings/OutputDeviceManagerProxy.py b/UM/Qt/Bindings/OutputDeviceManagerProxy.py index 076cac5c4..b867c5b49 100644 --- a/UM/Qt/Bindings/OutputDeviceManagerProxy.py +++ b/UM/Qt/Bindings/OutputDeviceManagerProxy.py @@ -87,12 +87,13 @@ def requestWriteToDevice(self, device_id: str, file_name: str, kwargs: Mapping[s """ limit_mimetypes = kwargs.get("limit_mimetypes", None) + limit_modes = kwargs.get("limit_modes", None) file_type = kwargs.get("file_type", "mesh") preferred_mimetypes = kwargs.get("preferred_mimetypes", None) # On Windows, calling requestWrite() on LocalFileOutputDevice crashes when called from a signal # handler attached to a QML MenuItem. So instead, defer the call to the next run of the event # loop, since that does work. - Application.getInstance().callLater(self._writeToDevice, [Application.getInstance().getController().getScene().getRoot()], device_id, file_name, limit_mimetypes, file_type, preferred_mimetypes = preferred_mimetypes) + Application.getInstance().callLater(self._writeToDevice, [Application.getInstance().getController().getScene().getRoot()], device_id, file_name, limit_mimetypes, limit_modes, file_type, preferred_mimetypes = preferred_mimetypes) @pyqtSlot(str, str, "QVariantMap") def requestWriteSelectionToDevice(self, device_id: str, file_name: str, kwargs: Mapping[str, str]) -> None: @@ -112,17 +113,25 @@ def requestWriteSelectionToDevice(self, device_id: str, file_name: str, kwargs: if not Selection.hasSelection(): return - limit_mimetypes = kwargs.get("limit_mimetypes", False) + limit_mimetypes = kwargs.get("limit_mimetypes", None) + limit_modes = kwargs.get("limit_mimetypes", None) preferred_mimetypes = kwargs.get("preferred_mimetypes", None) # On Windows, calling requestWrite() on LocalFileOutputDevice crashes when called from a signal # handler attached to a QML MenuItem. So instead, defer the call to the next run of the event # loop, since that does work. - Application.getInstance().callLater(self._writeToDevice, Selection.getAllSelectedObjects(), device_id, file_name, limit_mimetypes, preferred_mimetypes = preferred_mimetypes) + Application.getInstance().callLater(self._writeToDevice, Selection.getAllSelectedObjects(), device_id, file_name, limit_mimetypes, limit_modes, preferred_mimetypes = preferred_mimetypes) def _onActiveDeviceChanged(self) -> None: self.activeDeviceChanged.emit() - def _writeToDevice(self, nodes: List[SceneNode], device_id: str, file_name: str, limit_mimetypes: bool, file_type: str = "mesh", **kwargs) -> None: + def _writeToDevice(self, + nodes: List[SceneNode], + device_id: str, + file_name: str, + limit_mimetypes: Optional[List[str]], + limit_modes: Optional[List[str]], + file_type: str = "mesh", + **kwargs) -> None: """Writes the specified node to the output device. The output device to write with will be selected based on the device_id. @@ -134,6 +143,7 @@ def _writeToDevice(self, nodes: List[SceneNode], device_id: str, file_name: str, :param file_name: A suggestion for the file name to write to. Can be freely ignored if providing a file name makes no sense. :param limit_mimetypes: Limit the possible mimetypes to use for writing to these types. + :param limit_modes: If not None, limit available types to the ones given :param file_type: What file handler to get the writer from. """ @@ -147,7 +157,7 @@ def _writeToDevice(self, nodes: List[SceneNode], device_id: str, file_name: str, file_handler = UM.Qt.QtApplication.QtApplication.getInstance().getWorkspaceFileHandler() try: - device.requestWrite(nodes, file_name, limit_mimetypes, file_handler, **kwargs) + device.requestWrite(nodes, file_name, limit_mimetypes, file_handler, limit_modes = limit_modes, **kwargs) except OutputDeviceError.UserCanceledError: pass except OutputDeviceError.DeviceBusyError: diff --git a/plugins/LocalFileOutputDevice/LocalFileOutputDevice.py b/plugins/LocalFileOutputDevice/LocalFileOutputDevice.py index 4979dcbff..a796eb69d 100644 --- a/plugins/LocalFileOutputDevice/LocalFileOutputDevice.py +++ b/plugins/LocalFileOutputDevice/LocalFileOutputDevice.py @@ -81,6 +81,10 @@ def requestWrite(self, nodes, file_name = None, limit_mimetypes = None, file_han if limit_mimetypes: file_types = list(filter(lambda i: i["mime_type"] in limit_mimetypes, file_types)) + limit_modes = kwargs.get("limit_modes", None) + if limit_modes: + file_types = list(filter(lambda i: i["mode"] in limit_modes, file_types)) + file_types = [ft for ft in file_types if not ft["hide_in_file_dialog"]] if len(file_types) == 0: From 450ee38457634ad18749d455bed8b3cc8f136e9e Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 1 Feb 2024 10:34:19 +0100 Subject: [PATCH 3/5] Now displaying a help icon and value units CURA-11561 --- UM/Qt/qml/UM/HelpIcon.qml | 39 +++++++++++++++++++++++++++++++++++++++ UM/Qt/qml/UM/qmldir | 1 + 2 files changed, 40 insertions(+) create mode 100644 UM/Qt/qml/UM/HelpIcon.qml diff --git a/UM/Qt/qml/UM/HelpIcon.qml b/UM/Qt/qml/UM/HelpIcon.qml new file mode 100644 index 000000000..dd34c262b --- /dev/null +++ b/UM/Qt/qml/UM/HelpIcon.qml @@ -0,0 +1,39 @@ +// Copyright (c) 2024 Ultimaker B.V. +// Uranium is released under the terms of the LGPLv3 or higher. + +import QtQuick 2.7 +import QtQuick.Controls 2.3 + +import UM 1.8 as UM + +MouseArea +{ + property alias text: tooltip.text + property alias icon: image.source + + id: helpIconMouseArea + hoverEnabled: true + + implicitWidth: UM.Theme.getSize("section_icon").width + implicitHeight: UM.Theme.getSize("section_icon").height + + UM.ColorImage + { + id: image + anchors.fill: parent + color: UM.Theme.getColor("text") + source: UM.Theme.getIcon("Help") + + UM.ToolTip + { + UM.I18nCatalog { id: catalog; name: "cura" } + + id: tooltip + visible: helpIconMouseArea.containsMouse + targetPoint: Qt.point(parent.x + Math.round(parent.width / 2), parent.y) + x: 0 + y: parent.y + parent.height + UM.Theme.getSize("default_margin").height + width: UM.Theme.getSize("tooltip").width + } + } +} \ No newline at end of file diff --git a/UM/Qt/qml/UM/qmldir b/UM/Qt/qml/UM/qmldir index 163541e90..6299a9488 100644 --- a/UM/Qt/qml/UM/qmldir +++ b/UM/Qt/qml/UM/qmldir @@ -45,6 +45,7 @@ CategoryButton 1.5 CategoryButton.qml Switch 1.7 Switch.qml Slider 1.7 Slider.qml ComponentWithIcon 1.7 ComponentWithIcon.qml +HelpIcon 1.8 HelpIcon.qml # Validators FloatValidator 1.7 Validators/FloatValidator.qml From 73f209eb6e2d4a6e781eb02e20363b9fce566056 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 2 Feb 2024 10:08:30 +0100 Subject: [PATCH 4/5] Revert "Add export sub-menu" This reverts commit 7b201fd0ae9669ec8cca92ee768fee90605cd625. --- UM/Qt/Bindings/Bindings.py | 4 ---- UM/Qt/Bindings/MeshWritersModel.py | 35 ------------------------------ 2 files changed, 39 deletions(-) delete mode 100644 UM/Qt/Bindings/MeshWritersModel.py diff --git a/UM/Qt/Bindings/Bindings.py b/UM/Qt/Bindings/Bindings.py index f4233f24d..0d81e3843 100644 --- a/UM/Qt/Bindings/Bindings.py +++ b/UM/Qt/Bindings/Bindings.py @@ -31,7 +31,6 @@ from . import VisibleMessagesModel from . import Utilities from . import TableModel -from . import MeshWritersModel from UM.Settings.Models.SettingDefinitionsModel import SettingDefinitionsModel from UM.Settings.Models.DefinitionContainersModel import DefinitionContainersModel @@ -128,9 +127,6 @@ def register(self): qmlRegisterType(TableModel.TableModel, "UM", 1, 6, "TableModel") qmlRegisterType(Window.Window, "UM", 1, 6, "Window") - # Additions after 5.6 - qmlRegisterType(MeshWritersModel.MeshWritersModel, "UM", 1, 7, "MeshWritersModel") - @staticmethod def addRegisterType(class_type: type, qml_import_name: str, major_version: int, minor_version: int, class_name: str) -> None: qmlRegisterType(class_type, qml_import_name, major_version, minor_version, class_name) \ No newline at end of file diff --git a/UM/Qt/Bindings/MeshWritersModel.py b/UM/Qt/Bindings/MeshWritersModel.py deleted file mode 100644 index 34d6ddf16..000000000 --- a/UM/Qt/Bindings/MeshWritersModel.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) 2024 Ultimaker B.V. -# Uranium is released under the terms of the LGPLv3 or higher. - -from typing import List - -from PyQt6.QtCore import Qt -from PyQt6.QtQml import QQmlEngine - -from UM.Application import Application -from UM.Qt.ListModel import ListModel - - -class MeshWritersModel(ListModel): - """A list model providing a list of all registered MeshWriters that can export meshes. - - Exposes the following roles: - * mime_type - The associated writable file mime type - * description - The human-readable name of the file format - - """ - - MimeTypeRole = Qt.ItemDataRole.UserRole + 1 - ModeRole = Qt.ItemDataRole.UserRole + 2 - DescriptionRole = Qt.ItemDataRole.UserRole + 3 - - def __init__(self, parent = None): - super().__init__(parent) - # Ensure that this model doesn't get garbage collected (Now the bound object is destroyed when the wrapper is) - QQmlEngine.setObjectOwnership(self, QQmlEngine.ObjectOwnership.CppOwnership) - - self.addRoleName(self.MimeTypeRole, "mime_type") - self.addRoleName(self.ModeRole, "mode") - self.addRoleName(self.DescriptionRole, "description") - - self.setItems(Application.getInstance().getMeshFileHandler().getSupportedFileTypesWrite()) From 7f5d86cc3ca68053141598394349d7df5c5015ae Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Mon, 5 Feb 2024 12:46:39 +0100 Subject: [PATCH 5/5] Revert "Fix exporting binary/ascii STL" This reverts commit 1281b5486dcc60d49b16ca3a9517701fa1127b4e. --- UM/Qt/Bindings/OutputDeviceManagerProxy.py | 20 +++++-------------- .../LocalFileOutputDevice.py | 4 ---- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/UM/Qt/Bindings/OutputDeviceManagerProxy.py b/UM/Qt/Bindings/OutputDeviceManagerProxy.py index b867c5b49..076cac5c4 100644 --- a/UM/Qt/Bindings/OutputDeviceManagerProxy.py +++ b/UM/Qt/Bindings/OutputDeviceManagerProxy.py @@ -87,13 +87,12 @@ def requestWriteToDevice(self, device_id: str, file_name: str, kwargs: Mapping[s """ limit_mimetypes = kwargs.get("limit_mimetypes", None) - limit_modes = kwargs.get("limit_modes", None) file_type = kwargs.get("file_type", "mesh") preferred_mimetypes = kwargs.get("preferred_mimetypes", None) # On Windows, calling requestWrite() on LocalFileOutputDevice crashes when called from a signal # handler attached to a QML MenuItem. So instead, defer the call to the next run of the event # loop, since that does work. - Application.getInstance().callLater(self._writeToDevice, [Application.getInstance().getController().getScene().getRoot()], device_id, file_name, limit_mimetypes, limit_modes, file_type, preferred_mimetypes = preferred_mimetypes) + Application.getInstance().callLater(self._writeToDevice, [Application.getInstance().getController().getScene().getRoot()], device_id, file_name, limit_mimetypes, file_type, preferred_mimetypes = preferred_mimetypes) @pyqtSlot(str, str, "QVariantMap") def requestWriteSelectionToDevice(self, device_id: str, file_name: str, kwargs: Mapping[str, str]) -> None: @@ -113,25 +112,17 @@ def requestWriteSelectionToDevice(self, device_id: str, file_name: str, kwargs: if not Selection.hasSelection(): return - limit_mimetypes = kwargs.get("limit_mimetypes", None) - limit_modes = kwargs.get("limit_mimetypes", None) + limit_mimetypes = kwargs.get("limit_mimetypes", False) preferred_mimetypes = kwargs.get("preferred_mimetypes", None) # On Windows, calling requestWrite() on LocalFileOutputDevice crashes when called from a signal # handler attached to a QML MenuItem. So instead, defer the call to the next run of the event # loop, since that does work. - Application.getInstance().callLater(self._writeToDevice, Selection.getAllSelectedObjects(), device_id, file_name, limit_mimetypes, limit_modes, preferred_mimetypes = preferred_mimetypes) + Application.getInstance().callLater(self._writeToDevice, Selection.getAllSelectedObjects(), device_id, file_name, limit_mimetypes, preferred_mimetypes = preferred_mimetypes) def _onActiveDeviceChanged(self) -> None: self.activeDeviceChanged.emit() - def _writeToDevice(self, - nodes: List[SceneNode], - device_id: str, - file_name: str, - limit_mimetypes: Optional[List[str]], - limit_modes: Optional[List[str]], - file_type: str = "mesh", - **kwargs) -> None: + def _writeToDevice(self, nodes: List[SceneNode], device_id: str, file_name: str, limit_mimetypes: bool, file_type: str = "mesh", **kwargs) -> None: """Writes the specified node to the output device. The output device to write with will be selected based on the device_id. @@ -143,7 +134,6 @@ def _writeToDevice(self, :param file_name: A suggestion for the file name to write to. Can be freely ignored if providing a file name makes no sense. :param limit_mimetypes: Limit the possible mimetypes to use for writing to these types. - :param limit_modes: If not None, limit available types to the ones given :param file_type: What file handler to get the writer from. """ @@ -157,7 +147,7 @@ def _writeToDevice(self, file_handler = UM.Qt.QtApplication.QtApplication.getInstance().getWorkspaceFileHandler() try: - device.requestWrite(nodes, file_name, limit_mimetypes, file_handler, limit_modes = limit_modes, **kwargs) + device.requestWrite(nodes, file_name, limit_mimetypes, file_handler, **kwargs) except OutputDeviceError.UserCanceledError: pass except OutputDeviceError.DeviceBusyError: diff --git a/plugins/LocalFileOutputDevice/LocalFileOutputDevice.py b/plugins/LocalFileOutputDevice/LocalFileOutputDevice.py index a796eb69d..4979dcbff 100644 --- a/plugins/LocalFileOutputDevice/LocalFileOutputDevice.py +++ b/plugins/LocalFileOutputDevice/LocalFileOutputDevice.py @@ -81,10 +81,6 @@ def requestWrite(self, nodes, file_name = None, limit_mimetypes = None, file_han if limit_mimetypes: file_types = list(filter(lambda i: i["mime_type"] in limit_mimetypes, file_types)) - limit_modes = kwargs.get("limit_modes", None) - if limit_modes: - file_types = list(filter(lambda i: i["mode"] in limit_modes, file_types)) - file_types = [ft for ft in file_types if not ft["hide_in_file_dialog"]] if len(file_types) == 0: