diff --git a/conandata.yml b/conandata.yml index 5500df2f22..2aa9cb7781 100644 --- a/conandata.yml +++ b/conandata.yml @@ -1,17 +1,17 @@ -version: "5.11.0" +version: "5.12.0-alpha.0" commit: "unknown" requirements: - - "cura_resources/5.11.0" - - "uranium/5.11.0" - - "curaengine/5.11.0" - - "cura_binary_data/5.11.0" - - "fdm_materials/5.11.0" + - "cura_resources/5.12.0-alpha.0" + - "uranium/5.12.0-alpha.0" + - "curaengine/5.12.0-alpha.0" + - "cura_binary_data/5.12.0-alpha.0" + - "fdm_materials/5.12.0-alpha.0" - "dulcificum/5.10.0" - "pysavitar/5.11.0-alpha.0" - "pynest2d/5.10.0" requirements_internal: - - "fdm_materials/5.11.0" - - "cura_private_data/5.11.0-alpha.0@internal/testing" + - "fdm_materials/5.12.0-alpha.0" + - "cura_private_data/5.12.0-alpha.0@internal/testing" requirements_enterprise: - "native_cad_plugin/2.0.0" urls: diff --git a/cura/Settings/ExtruderManager.py b/cura/Settings/ExtruderManager.py index a0ec63e1cf..a2554f2e34 100755 --- a/cura/Settings/ExtruderManager.py +++ b/cura/Settings/ExtruderManager.py @@ -248,11 +248,11 @@ class ExtruderManager(QObject): stack_to_use = container_registry.findContainerStacks(id = extruder_stack_id)[0] if not support_enabled: - support_enabled |= stack_to_use.getProperty("support_enable", "value") + support_enabled |= parseBool(stack_to_use.getProperty("support_enable", "value")) if not support_bottom_enabled: - support_bottom_enabled |= stack_to_use.getProperty("support_bottom_enable", "value") + support_bottom_enabled |= parseBool(stack_to_use.getProperty("support_bottom_enable", "value")) if not support_roof_enabled: - support_roof_enabled |= stack_to_use.getProperty("support_roof_enable", "value") + support_roof_enabled |= parseBool(stack_to_use.getProperty("support_roof_enable", "value")) painted_extruders = node.callDecoration("getPaintedExtruders") if painted_extruders is not None: diff --git a/plugins/CuraDrive/src/RestoreBackupJob.py b/plugins/CuraDrive/src/RestoreBackupJob.py index 503b39547a..916a0f80fd 100644 --- a/plugins/CuraDrive/src/RestoreBackupJob.py +++ b/plugins/CuraDrive/src/RestoreBackupJob.py @@ -157,7 +157,7 @@ class RestoreBackupJob(Job): Logger.info("All packages redownloaded!") self._job_done.set() else: - msgs = "\n - ".join(redownload_errors) + msgs = "\n".join([" - " + str(x) for x in redownload_errors]) Logger.error(f"Couldn't re-install at least one package(s) because: {msgs}") self.restore_backup_error_message = self.DEFAULT_ERROR_MESSAGE self._job_done.set() diff --git a/plugins/UM3NetworkPrinting/src/Messages/AuthorizationRequiredMessage.py b/plugins/UM3NetworkPrinting/src/Messages/AuthorizationRequiredMessage.py new file mode 100644 index 0000000000..7dc95c554f --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Messages/AuthorizationRequiredMessage.py @@ -0,0 +1,34 @@ +# Copyright (c) 2025 UltiMaker +# Cura is released under the terms of the LGPLv3 or higher. + +from UM.Message import Message + + +class AuthorizationRequiredMessage: + + _inner_message_instance = None + class InnerMessage(Message): + def __init__(self, printer_name: str, err_message: str) -> None: + super().__init__( + text = printer_name, + title = err_message, + message_type = Message.MessageType.WARNING, + lifetime = 0 + ) + + @classmethod + def _getInstance(cls) -> Message: + if cls._inner_message_instance is None: + cls._inner_message_instance = cls.InnerMessage("", "") + return cls._inner_message_instance + + @classmethod + def show(cls, printer_name: str, err_message: str) -> None: + msg = cls._getInstance() + msg.setText(printer_name) + msg.setTitle(err_message) + msg.show() + + @classmethod + def hide(cls) -> None: + cls._getInstance().hide() diff --git a/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py b/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py index a87f4d442b..d483e3d862 100644 --- a/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Network/ClusterApiClient.py @@ -16,6 +16,7 @@ from UM.Logger import Logger from cura.CuraApplication import CuraApplication +from ..Messages.AuthorizationRequiredMessage import AuthorizationRequiredMessage from ..Models.BaseModel import BaseModel from ..Models.Http.ClusterPrintJobStatus import ClusterPrintJobStatus from ..Models.Http.ClusterPrinterStatus import ClusterPrinterStatus @@ -56,7 +57,7 @@ class ClusterApiClient: # In order to avoid garbage collection we keep the callbacks in this list. _anti_gc_callbacks = [] # type: List[Callable[[], None]] - def __init__(self, address: str, on_error: Callable) -> None: + def __init__(self, address: str, on_error: Callable, on_auth_required: Callable) -> None: """Initializes a new cluster API client. :param address: The network address of the cluster to call. @@ -68,6 +69,7 @@ class ClusterApiClient: self._on_error = on_error self._auth_tries = 0 + self._on_auth_required = on_auth_required prefs = CuraApplication.getInstance().getPreferences() prefs.addPreference("cluster_api/auth_ids", "{}") @@ -302,6 +304,10 @@ class ClusterApiClient: if reply.error() != QNetworkReply.NetworkError.NoError: if reply.error() == QNetworkReply.NetworkError.AuthenticationRequiredError: + self._auth_id = None + self._auth_key = None + + self._on_auth_required(reply.errorString()) nonce_match = re.search(r'nonce="([^"]+)', str(reply.rawHeader(b"WWW-Authenticate"))) if nonce_match: self._nonce = nonce_match.group(1) @@ -309,9 +315,13 @@ class ClusterApiClient: self._setLocalValueToPrefDict("cluster_api/nonce_counts", self._nonce_count) self._setLocalValueToPrefDict("cluster_api/nonces", self._nonce) CuraApplication.getInstance().savePreferences() - self._on_error(reply.errorString()) + else: + self._on_error(reply.errorString()) return + if self._auth_id and self._auth_key and self._nonce_count > 1: + AuthorizationRequiredMessage.hide() + # If no parse model is given, simply return the raw data in the callback. if not model: on_finished(reply.readAll()) diff --git a/plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py b/plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py index f51ff5a4e8..8ce237b22b 100644 --- a/plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py +++ b/plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py @@ -21,6 +21,7 @@ from cura.PrinterOutput.PrinterOutputDevice import ConnectionType from .ClusterApiClient import ClusterApiClient from .SendMaterialJob import SendMaterialJob from ..ExportFileJob import ExportFileJob +from ..Messages.AuthorizationRequiredMessage import AuthorizationRequiredMessage from ..UltimakerNetworkedPrinterOutputDevice import UltimakerNetworkedPrinterOutputDevice from ..Messages.PrintJobUploadBlockedMessage import PrintJobUploadBlockedMessage from ..Messages.PrintJobUploadErrorMessage import PrintJobUploadErrorMessage @@ -239,9 +240,19 @@ class LocalClusterOutputDevice(UltimakerNetworkedPrinterOutputDevice): if print_job.getPreviewImage() is None: self.getApiClient().getPrintJobPreviewImage(print_job.key, print_job.updatePreviewImageData) + def _onAuthRequired(self, error_msg: str) -> None: + active_name = CuraApplication.getInstance().getOutputDeviceManager().getActiveDevice().getName() + if self._name == active_name: + Logger.info(f"Authorization required for {self._name}: {error_msg}") + AuthorizationRequiredMessage.show(self._name, error_msg) + def getApiClient(self) -> ClusterApiClient: """Get the API client instance.""" if not self._cluster_api: - self._cluster_api = ClusterApiClient(self.address, on_error = lambda error: Logger.log("e", str(error))) + self._cluster_api = ClusterApiClient( + self._address, + on_error = lambda error: Logger.log("e", str(error)), + on_auth_required = self._onAuthRequired + ) return self._cluster_api diff --git a/plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDeviceManager.py index d35e409086..2b99a1410d 100644 --- a/plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDeviceManager.py @@ -15,6 +15,7 @@ from cura.Settings.GlobalStack import GlobalStack from .ZeroConfClient import ZeroConfClient from .ClusterApiClient import ClusterApiClient from .LocalClusterOutputDevice import LocalClusterOutputDevice +from ..Messages.AuthorizationRequiredMessage import AuthorizationRequiredMessage from ..UltimakerNetworkedPrinterOutputDevice import UltimakerNetworkedPrinterOutputDevice from ..Messages.CloudFlowMessage import CloudFlowMessage from ..Messages.LegacyDeviceNoLongerSupportedMessage import LegacyDeviceNoLongerSupportedMessage @@ -44,6 +45,7 @@ class LocalClusterOutputDeviceManager: # Persistent dict containing the networked clusters. self._discovered_devices = {} # type: Dict[str, LocalClusterOutputDevice] self._output_device_manager = CuraApplication.getInstance().getOutputDeviceManager() + self._output_device_manager.activeDeviceChanged.connect(self._onActiveDeviceChanged) # Hook up ZeroConf client. self._zero_conf_client = ZeroConfClient() @@ -69,10 +71,17 @@ class LocalClusterOutputDeviceManager: self.stop() self.start() + def _onActiveDeviceChanged(self): + AuthorizationRequiredMessage.hide() + + def _onAuthRequired(self, error_msg: str) -> None: + Logger.info(f"Authorization required: {error_msg}") + AuthorizationRequiredMessage.show("", error_msg) + def addManualDevice(self, address: str, callback: Optional[Callable[[bool, str], None]] = None) -> None: """Add a networked printer manually by address.""" - api_client = ClusterApiClient(address, lambda error: Logger.log("e", str(error))) + api_client = ClusterApiClient(address, lambda error: Logger.log("e", str(error)), self._onAuthRequired) api_client.getSystem(lambda status: self._onCheckManualDeviceResponse(address, status, callback)) def removeManualDevice(self, device_id: str, address: Optional[str] = None) -> None: diff --git a/resources/definitions/fdmprinter.def.json b/resources/definitions/fdmprinter.def.json index 0dc68790e0..09cd190e56 100644 --- a/resources/definitions/fdmprinter.def.json +++ b/resources/definitions/fdmprinter.def.json @@ -1551,7 +1551,7 @@ "default_value": "0", "value": "0", "minimum_value": "0", - "maximum_value_warning": "roofing_line_width * 10", + "maximum_value_warning": "roofing_line_width * 3", "enabled": "roofing_layer_count > 0 and top_layers > 0", "limit_to_extruder": "roofing_extruder_nr", "settable_per_mesh": true diff --git a/resources/texts/change_log.txt b/resources/texts/change_log.txt index d1f756f077..4cd8cde2c1 100644 --- a/resources/texts/change_log.txt +++ b/resources/texts/change_log.txt @@ -24,6 +24,25 @@ - Updated Insert At Layerchange post processing plug-in to allow for lowercase commands, resolves https://github.com/Ultimaker/Cura/issues/20441 - Introduced a Post Processing Script that allows for defining Z-Hop on Travel, contributed by @GregValiant +* Bugs resolved since the Beta release: +- Improved slicing time on Windows machines making it significantly faster for a number of models +- Introduced Top Surface Extension setting to improve quality of details like text on the top of prints +- Updated traveling directions for monotonic top surface, to reduce traveling and improve part quality +- Fixed a bug where painting closed shapes would not respect the drawn lines but would close filled shapes +- Introduced a workaround for deprecated face selection methods to remain backwards compatible for plugins +- Multi material settings are now settable per mesh +- Fixed a bug where you had to reslice to see your preview again after saving or sending the model +- Fixed group handling for painting features, and disabled painting for grouped objects because the painting is currently designed to allow painting a single object at once +- Fixed a bug where Cura would crash if you switched to another printer with a painted model +- Fixed a bug where the model and a prime tower could collide when a model was painted +- Fixed a bug where you could slice with a painted disabled extruder +- Improved handling of rotating your view when you are in paint on mode +- Updated settings to improve overhang, top/bottom settings on the UltiMaker S6 and S8 +- Updated settings to improve the Z seam settings for Factor 4 +- Fixed a bug with the translations where French translations had some Italian text in them on the recommended window +- Fixed a bug where the Makerbot Replicator+ could not start a print with a Smart Extruder+ Tough +- Introduced messages when you cannot start a printjob over network because the printer Requires Authentication + * Bug fixes: - Fixed a bug where Cura would not start if you have an extremely strict AntiVirus program installed on the same PC - Fixed a bug on Mac where Pop Up Windows would hide behind the main window @@ -72,11 +91,10 @@ - Introduced BIQU B2, contributed by @bjuraga - Introduced Geetech M1 and Geetech M1S Profiles, and updated the Geetech Thunder Profiles, contributed by @whoseyoung -* Known Issues, but we intend to resolve them for stable -- Painting can still be slow with very detailed models -- Prime tower area is not displayed nor considered when using multi-material via painting -- Painting does not work on models contained in a group -- Painting undo/redo/clear does not invalidate the slice result +* Known Issues +- When using a model with overlapping meshes, the paint on material may produce unexpected results +- Painting might still be slow with very detailed models + [5.10.2]