mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-07 23:17:32 -06:00
Add on-exit callback management and check for active USB printing
CURA-5384
This commit is contained in:
parent
ac3d3bc5c0
commit
c0b7e40b0d
5 changed files with 158 additions and 9 deletions
|
@ -5,6 +5,7 @@ import copy
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
from typing import cast, TYPE_CHECKING, Optional
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
|
|
||||||
|
@ -13,8 +14,6 @@ from PyQt5.QtGui import QColor, QIcon
|
||||||
from PyQt5.QtWidgets import QMessageBox
|
from PyQt5.QtWidgets import QMessageBox
|
||||||
from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType
|
from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType
|
||||||
|
|
||||||
from typing import cast, TYPE_CHECKING
|
|
||||||
|
|
||||||
from UM.Scene.SceneNode import SceneNode
|
from UM.Scene.SceneNode import SceneNode
|
||||||
from UM.Scene.Camera import Camera
|
from UM.Scene.Camera import Camera
|
||||||
from UM.Math.Vector import Vector
|
from UM.Math.Vector import Vector
|
||||||
|
@ -97,6 +96,8 @@ from . import CuraSplashScreen
|
||||||
from . import CameraImageProvider
|
from . import CameraImageProvider
|
||||||
from . import MachineActionManager
|
from . import MachineActionManager
|
||||||
|
|
||||||
|
from cura.TaskManagement.OnExitCallbackManager import OnExitCallbackManager
|
||||||
|
|
||||||
from cura.Settings.MachineManager import MachineManager
|
from cura.Settings.MachineManager import MachineManager
|
||||||
from cura.Settings.ExtruderManager import ExtruderManager
|
from cura.Settings.ExtruderManager import ExtruderManager
|
||||||
from cura.Settings.UserChangesModel import UserChangesModel
|
from cura.Settings.UserChangesModel import UserChangesModel
|
||||||
|
@ -158,6 +159,8 @@ class CuraApplication(QtApplication):
|
||||||
|
|
||||||
self._boot_loading_time = time.time()
|
self._boot_loading_time = time.time()
|
||||||
|
|
||||||
|
self._on_exit_callback_manager = OnExitCallbackManager(self)
|
||||||
|
|
||||||
# Variables set from CLI
|
# Variables set from CLI
|
||||||
self._files_to_open = []
|
self._files_to_open = []
|
||||||
self._use_single_instance = False
|
self._use_single_instance = False
|
||||||
|
@ -520,8 +523,8 @@ class CuraApplication(QtApplication):
|
||||||
def setNeedToShowUserAgreement(self, set_value = True):
|
def setNeedToShowUserAgreement(self, set_value = True):
|
||||||
self._need_to_show_user_agreement = set_value
|
self._need_to_show_user_agreement = set_value
|
||||||
|
|
||||||
## The "Quit" button click event handler.
|
# DO NOT call this function to close the application, use checkAndExitApplication() instead which will perform
|
||||||
@pyqtSlot()
|
# pre-exit checks such as checking for in-progress USB printing, etc.
|
||||||
def closeApplication(self):
|
def closeApplication(self):
|
||||||
Logger.log("i", "Close application")
|
Logger.log("i", "Close application")
|
||||||
main_window = self.getMainWindow()
|
main_window = self.getMainWindow()
|
||||||
|
@ -530,6 +533,32 @@ class CuraApplication(QtApplication):
|
||||||
else:
|
else:
|
||||||
self.exit(0)
|
self.exit(0)
|
||||||
|
|
||||||
|
# This function first performs all upon-exit checks such as USB printing that is in progress.
|
||||||
|
# Use this to close the application.
|
||||||
|
@pyqtSlot()
|
||||||
|
def checkAndExitApplication(self) -> None:
|
||||||
|
self._on_exit_callback_manager.resetCurrentState()
|
||||||
|
self._on_exit_callback_manager.triggerNextCallback()
|
||||||
|
|
||||||
|
@pyqtSlot(result = bool)
|
||||||
|
def getIsAllChecksPassed(self) -> bool:
|
||||||
|
return self._on_exit_callback_manager.getIsAllChecksPassed()
|
||||||
|
|
||||||
|
def getOnExitCallbackManager(self) -> "OnExitCallbackManager":
|
||||||
|
return self._on_exit_callback_manager
|
||||||
|
|
||||||
|
def triggerNextExitCheck(self) -> None:
|
||||||
|
self._on_exit_callback_manager.triggerNextCallback()
|
||||||
|
|
||||||
|
showConfirmExitDialog = pyqtSignal(str, arguments = ["message"])
|
||||||
|
|
||||||
|
def setConfirmExitDialogCallback(self, callback):
|
||||||
|
self._confirm_exit_dialog_callback = callback
|
||||||
|
|
||||||
|
@pyqtSlot(bool)
|
||||||
|
def callConfirmExitDialogCallback(self, yes_or_no: bool):
|
||||||
|
self._confirm_exit_dialog_callback(yes_or_no)
|
||||||
|
|
||||||
## Signal to connect preferences action in QML
|
## Signal to connect preferences action in QML
|
||||||
showPreferencesWindow = pyqtSignal()
|
showPreferencesWindow = pyqtSignal()
|
||||||
|
|
||||||
|
|
69
cura/TaskManagement/OnExitCallbackManager.py
Normal file
69
cura/TaskManagement/OnExitCallbackManager.py
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
# Copyright (c) 2018 Ultimaker B.V.
|
||||||
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING, List
|
||||||
|
|
||||||
|
from UM.Logger import Logger
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from cura.CuraApplication import CuraApplication
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# This class manages a all registered upon-exit checks that need to be perform when the application tries to exit.
|
||||||
|
# For example, to show a confirmation dialog when there is USB printing in progress, etc. All callbacks will be called
|
||||||
|
# in the order of when they got registered. If all callbacks "passes", that is, for example, if the user clicks "yes"
|
||||||
|
# on the exit confirmation dialog or nothing that's blocking the exit, then the application will quit after that.
|
||||||
|
#
|
||||||
|
class OnExitCallbackManager:
|
||||||
|
|
||||||
|
def __init__(self, application: "CuraApplication") -> None:
|
||||||
|
self._application = application
|
||||||
|
self._on_exit_callback_list = list() # type: List[callable]
|
||||||
|
self._current_callback_idx = 0
|
||||||
|
self._is_all_checks_passed = False
|
||||||
|
|
||||||
|
def addCallback(self, callback: callable) -> None:
|
||||||
|
self._on_exit_callback_list.append(callback)
|
||||||
|
Logger.log("d", "on-app-exit callback [%s] added.", callback)
|
||||||
|
|
||||||
|
# Reset the current state so the next time it will call all the callbacks again.
|
||||||
|
def resetCurrentState(self) -> None:
|
||||||
|
self._current_callback_idx = 0
|
||||||
|
self._is_all_checks_passed = False
|
||||||
|
|
||||||
|
def getIsAllChecksPassed(self) -> bool:
|
||||||
|
return self._is_all_checks_passed
|
||||||
|
|
||||||
|
# Trigger the next callback if available. If not, it means that all callbacks have "passed", which means we should
|
||||||
|
# not block the application to quit, and it will call the application to actually quit.
|
||||||
|
def triggerNextCallback(self) -> None:
|
||||||
|
# Get the next callback and schedule that if
|
||||||
|
this_callback = None
|
||||||
|
if self._current_callback_idx < len(self._on_exit_callback_list):
|
||||||
|
this_callback = self._on_exit_callback_list[self._current_callback_idx]
|
||||||
|
self._current_callback_idx += 1
|
||||||
|
|
||||||
|
if this_callback is not None:
|
||||||
|
Logger.log("d", "Scheduled the next on-app-exit callback [%s]", this_callback)
|
||||||
|
self._application.callLater(this_callback)
|
||||||
|
else:
|
||||||
|
Logger.log("d", "No more on-app-exit callbacks to process. Tell the app to exit.")
|
||||||
|
|
||||||
|
self._is_all_checks_passed = True
|
||||||
|
|
||||||
|
# Tell the application to exit
|
||||||
|
self._application.callLater(self._application.closeApplication)
|
||||||
|
|
||||||
|
# This is the callback function which an on-exit callback should call when it finishes, it should provide the
|
||||||
|
# "should_proceed" flag indicating whether this check has "passed", or in other words, whether quiting the
|
||||||
|
# application should be blocked. If the last on-exit callback doesn't block the quiting, it will call the next
|
||||||
|
# registered on-exit callback if available.
|
||||||
|
def onCurrentCallbackFinished(self, should_proceed: bool = True) -> None:
|
||||||
|
if not should_proceed:
|
||||||
|
Logger.log("d", "on-app-exit callback finished and we should not proceed.")
|
||||||
|
# Reset the state
|
||||||
|
self.resetCurrentState()
|
||||||
|
return
|
||||||
|
|
||||||
|
self.triggerNextCallback()
|
0
cura/TaskManagement/__init__.py
Normal file
0
cura/TaskManagement/__init__.py
Normal file
|
@ -88,6 +88,25 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
||||||
self._command_received = Event()
|
self._command_received = Event()
|
||||||
self._command_received.set()
|
self._command_received.set()
|
||||||
|
|
||||||
|
CuraApplication.getInstance().getOnExitCallbackManager().addCallback(self._checkActivePrintingUponAppExit)
|
||||||
|
|
||||||
|
# This is a callback function that checks if there is any printing in progress via USB when the application tries
|
||||||
|
# to exit. If so, it will show a confirmation before
|
||||||
|
def _checkActivePrintingUponAppExit(self) -> None:
|
||||||
|
application = CuraApplication.getInstance()
|
||||||
|
if not self._is_printing:
|
||||||
|
# This USB printer is not printing, so we have nothing to do. Call the next callback if exists.
|
||||||
|
application.triggerNextExitCheck()
|
||||||
|
return
|
||||||
|
|
||||||
|
application.setConfirmExitDialogCallback(self._onConfirmExitDialogResult)
|
||||||
|
application.showConfirmExitDialog.emit("USB printing is in progress")
|
||||||
|
|
||||||
|
def _onConfirmExitDialogResult(self, result: bool) -> None:
|
||||||
|
if result:
|
||||||
|
application = CuraApplication.getInstance()
|
||||||
|
application.triggerNextExitCheck()
|
||||||
|
|
||||||
## Reset USB device settings
|
## Reset USB device settings
|
||||||
#
|
#
|
||||||
def resetDeviceSettings(self):
|
def resetDeviceSettings(self):
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
// Copyright (c) 2017 Ultimaker B.V.
|
// Copyright (c) 2017 Ultimaker B.V.
|
||||||
// Cura is released under the terms of the LGPLv3 or higher.
|
// Cura is released under the terms of the LGPLv3 or higher.
|
||||||
|
|
||||||
import QtQuick 2.2
|
import QtQuick 2.7
|
||||||
import QtQuick.Controls 1.1
|
import QtQuick.Controls 1.4
|
||||||
import QtQuick.Controls.Styles 1.1
|
import QtQuick.Controls.Styles 1.4
|
||||||
import QtQuick.Layouts 1.1
|
import QtQuick.Layouts 1.1
|
||||||
import QtQuick.Dialogs 1.1
|
import QtQuick.Dialogs 1.2
|
||||||
|
|
||||||
import UM 1.3 as UM
|
import UM 1.3 as UM
|
||||||
import Cura 1.0 as Cura
|
import Cura 1.0 as Cura
|
||||||
|
@ -700,10 +700,42 @@ UM.MainWindow
|
||||||
id: contextMenu
|
id: contextMenu
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onPreClosing:
|
||||||
|
{
|
||||||
|
close.accepted = CuraApplication.getIsAllChecksPassed();
|
||||||
|
if (!close.accepted)
|
||||||
|
{
|
||||||
|
CuraApplication.checkAndExitApplication();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageDialog
|
||||||
|
{
|
||||||
|
id: exitConfirmationDialog
|
||||||
|
title: catalog.i18nc("@title:window", "Closing Cura")
|
||||||
|
text: catalog.i18nc("@label", "Are you sure you want to exit Cura?")
|
||||||
|
icon: StandardIcon.Question
|
||||||
|
modality: Qt.ApplicationModal
|
||||||
|
standardButtons: StandardButton.Yes | StandardButton.No
|
||||||
|
onYes: CuraApplication.callConfirmExitDialogCallback(true)
|
||||||
|
onNo: CuraApplication.callConfirmExitDialogCallback(false)
|
||||||
|
onRejected: CuraApplication.callConfirmExitDialogCallback(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections
|
||||||
|
{
|
||||||
|
target: CuraApplication
|
||||||
|
onShowConfirmExitDialog:
|
||||||
|
{
|
||||||
|
exitConfirmationDialog.text = message;
|
||||||
|
exitConfirmationDialog.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Connections
|
Connections
|
||||||
{
|
{
|
||||||
target: Cura.Actions.quit
|
target: Cura.Actions.quit
|
||||||
onTriggered: CuraApplication.closeApplication();
|
onTriggered: CuraApplication.exitApplication();
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections
|
Connections
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue