diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index e5c0c5688c..0de5066d8b 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1701,3 +1701,22 @@ class CuraApplication(QtApplication): @pyqtSlot() def showMoreInformationDialogForAnonymousDataCollection(self): cast(SliceInfo, self._plugin_registry.getPluginObject("SliceInfoPlugin")).showMoreInfoDialog() + + ## Signal to check whether the application can be closed or not + checkCuraCloseChange = pyqtSignal() + + # This variable is necessary to ensure that all methods that were subscribed for the checkCuraCloseChange event + # have been passed checks + _isCuraCanBeClosed = True + + def setCuraCanBeClosed(self, value: bool): + self._isCuraCanBeClosed = value + + @pyqtSlot(result=bool) + def preCloseEventHandler(self)-> bool: + + # If any of checks failed then then _isCuraCanBeClosed should be set to False and Cura will not be closed + # after clicking the quit button + self.checkCuraCloseChange.emit() + + return self._isCuraCanBeClosed diff --git a/plugins/USBPrinting/USBPrinterOutputDevice.py b/plugins/USBPrinting/USBPrinterOutputDevice.py index f9c6011f7b..fc9558056b 100644 --- a/plugins/USBPrinting/USBPrinterOutputDevice.py +++ b/plugins/USBPrinting/USBPrinterOutputDevice.py @@ -435,6 +435,9 @@ class USBPrinterOutputDevice(PrinterOutputDevice): self._gcode_position += 1 + def getIsPrinting(self)-> bool: + return self._is_printing + class FirmwareUpdateState(IntEnum): idle = 0 diff --git a/plugins/USBPrinting/USBPrinterOutputDeviceManager.py b/plugins/USBPrinting/USBPrinterOutputDeviceManager.py index 2ee85187ee..b2dc24480c 100644 --- a/plugins/USBPrinting/USBPrinterOutputDeviceManager.py +++ b/plugins/USBPrinting/USBPrinterOutputDeviceManager.py @@ -6,7 +6,8 @@ import platform import time import serial.tools.list_ports -from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal +from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal, QCoreApplication +from PyQt5.QtWidgets import QMessageBox from UM.Logger import Logger from UM.Resources import Resources @@ -50,6 +51,11 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin): self._application.globalContainerStackChanged.connect(self.updateUSBPrinterOutputDevices) + self._application.checkCuraCloseChange.connect(self.checkWheterUSBIsActiveOrNot) + + self._lock = threading.Lock() + self._confirm_dialog_visible = False + # The method updates/reset the USB settings for all connected USB devices def updateUSBPrinterOutputDevices(self): for key, device in self._usb_output_devices.items(): @@ -184,3 +190,51 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin): @classmethod def getInstance(cls, *args, **kwargs) -> "USBPrinterOutputDeviceManager": return cls.__instance + + # The method checks whether a printer is printing via USB or not before closing cura. If the printer is printing then pop up a + # dialog to confirm stop printing + def checkWheterUSBIsActiveOrNot(self)-> None: + + is_printing = False + for key, device in self._usb_output_devices.items(): + if type(device) is USBPrinterOutputDevice.USBPrinterOutputDevice: + if device.getIsPrinting(): + is_printing = True + break + + if is_printing: + if threading.current_thread() != threading.main_thread(): + self._lock.acquire() + self._confirm_dialog_visible = True + + CuraApplication.getInstance().messageBox(i18n_catalog.i18nc("@window:title", "Confirm stop printing"), + i18n_catalog.i18nc("@window:message","A USB print is in progress, closing Cura will stop this print. Are you sure?"), + buttons=QMessageBox.Yes + QMessageBox.No, + icon=QMessageBox.Question, + callback=self._messageBoxCallback) + # Wait for dialog result + self.waitForClose() + + ## Block thread until the dialog is closed. + def waitForClose(self)-> None: + if self._confirm_dialog_visible: + if threading.current_thread() != threading.main_thread(): + self._lock.acquire() + self._lock.release() + else: + # If this is not run from a separate thread, we need to ensure that the events are still processed. + while self._confirm_dialog_visible: + time.sleep(1 / 50) + QCoreApplication.processEvents() # Ensure that the GUI does not freeze. + + def _messageBoxCallback(self, button): + if button == QMessageBox.Yes: + self._application.setCuraCanBeClosed(True) + else: + self._application.setCuraCanBeClosed(False) + + self._confirm_dialog_visible = False + try: + self._lock.release() + except: + pass \ No newline at end of file diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 692a6fc259..20638e8850 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -22,6 +22,12 @@ UM.MainWindow backgroundColor: UM.Theme.getColor("viewport_background") + // Event which does the check before closing the window + onPreCloseChange: + { + event.accepted = CuraApplication.preCloseEventHandler() + } + // This connection is here to support legacy printer output devices that use the showPrintMonitor signal on Application to switch to the monitor stage // It should be phased out in newer plugin versions. Connections