mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-08-05 13:03:59 -06:00
Merge branch 'master' into CURA-5536_installed_check
This commit is contained in:
commit
a8727d7b39
12 changed files with 241 additions and 192 deletions
|
@ -5,6 +5,7 @@ import copy
|
|||
import os
|
||||
import sys
|
||||
import time
|
||||
from typing import cast, TYPE_CHECKING, Optional
|
||||
|
||||
import numpy
|
||||
|
||||
|
@ -13,8 +14,6 @@ from PyQt5.QtGui import QColor, QIcon
|
|||
from PyQt5.QtWidgets import QMessageBox
|
||||
from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType
|
||||
|
||||
from typing import cast, TYPE_CHECKING
|
||||
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Scene.Camera import Camera
|
||||
from UM.Math.Vector import Vector
|
||||
|
@ -97,6 +96,8 @@ from . import CuraSplashScreen
|
|||
from . import CameraImageProvider
|
||||
from . import MachineActionManager
|
||||
|
||||
from cura.TaskManagement.OnExitCallbackManager import OnExitCallbackManager
|
||||
|
||||
from cura.Settings.MachineManager import MachineManager
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
from cura.Settings.UserChangesModel import UserChangesModel
|
||||
|
@ -158,6 +159,8 @@ class CuraApplication(QtApplication):
|
|||
|
||||
self._boot_loading_time = time.time()
|
||||
|
||||
self._on_exit_callback_manager = OnExitCallbackManager(self)
|
||||
|
||||
# Variables set from CLI
|
||||
self._files_to_open = []
|
||||
self._use_single_instance = False
|
||||
|
@ -279,6 +282,8 @@ class CuraApplication(QtApplication):
|
|||
self._machine_action_manager = MachineActionManager.MachineActionManager(self)
|
||||
self._machine_action_manager.initialize()
|
||||
|
||||
self.change_log_url = "https://ultimaker.com/ultimaker-cura-latest-features"
|
||||
|
||||
def __sendCommandToSingleInstance(self):
|
||||
self._single_instance = SingleInstance(self, self._files_to_open)
|
||||
|
||||
|
@ -520,8 +525,8 @@ class CuraApplication(QtApplication):
|
|||
def setNeedToShowUserAgreement(self, set_value = True):
|
||||
self._need_to_show_user_agreement = set_value
|
||||
|
||||
## The "Quit" button click event handler.
|
||||
@pyqtSlot()
|
||||
# DO NOT call this function to close the application, use checkAndExitApplication() instead which will perform
|
||||
# pre-exit checks such as checking for in-progress USB printing, etc.
|
||||
def closeApplication(self):
|
||||
Logger.log("i", "Close application")
|
||||
main_window = self.getMainWindow()
|
||||
|
@ -530,6 +535,32 @@ class CuraApplication(QtApplication):
|
|||
else:
|
||||
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
|
||||
showPreferencesWindow = pyqtSignal()
|
||||
|
||||
|
@ -1701,22 +1732,3 @@ 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
|
||||
|
|
|
@ -46,19 +46,21 @@ class ContainerManager(QObject):
|
|||
self._quality_manager = self._application.getQualityManager()
|
||||
self._container_name_filters = {} # type: Dict[str, Dict[str, Any]]
|
||||
|
||||
@pyqtSlot(str, str, str, result=str)
|
||||
def getContainerMetaDataEntry(self, container_id, entry_name, sub_entry: Optional[str] = None):
|
||||
@pyqtSlot(str, str, result=str)
|
||||
def getContainerMetaDataEntry(self, container_id: str, entry_names: str) -> str:
|
||||
metadatas = self._container_registry.findContainersMetadata(id = container_id)
|
||||
if not metadatas:
|
||||
Logger.log("w", "Could not get metadata of container %s because it was not found.", container_id)
|
||||
return ""
|
||||
|
||||
sub_data = metadatas[0].get(entry_name, "")
|
||||
result = str(sub_data)
|
||||
if sub_entry:
|
||||
result = str(sub_data.get(sub_entry, ""))
|
||||
|
||||
return result
|
||||
entries = entry_names.split("/")
|
||||
result = metadatas[0]
|
||||
while entries:
|
||||
entry = entries.pop(0)
|
||||
result = result.get(entry, {})
|
||||
if not result:
|
||||
return ""
|
||||
return str(result)
|
||||
|
||||
## Set a metadata entry of the specified container.
|
||||
#
|
||||
|
@ -73,6 +75,7 @@ class ContainerManager(QObject):
|
|||
#
|
||||
# \return True if successful, False if not.
|
||||
# TODO: This is ONLY used by MaterialView for material containers. Maybe refactor this.
|
||||
# Update: In order for QML to use objects and sub objects, those (sub) objects must all be QObject. Is that what we want?
|
||||
@pyqtSlot("QVariant", str, str)
|
||||
def setContainerMetaDataEntry(self, container_node, entry_name, entry_value):
|
||||
root_material_id = container_node.metadata["base_file"]
|
||||
|
@ -107,63 +110,6 @@ class ContainerManager(QObject):
|
|||
if sub_item_changed: #If it was only a sub-item that has changed then the setMetaDataEntry won't correctly notice that something changed, and we must manually signal that the metadata changed.
|
||||
container.metaDataChanged.emit(container)
|
||||
|
||||
## Set a setting property of the specified container.
|
||||
#
|
||||
# This will set the specified property of the specified setting of the container
|
||||
# and all containers that share the same base_file (if any). The latter only
|
||||
# happens for material containers.
|
||||
#
|
||||
# \param container_id \type{str} The ID of the container to change.
|
||||
# \param setting_key \type{str} The key of the setting.
|
||||
# \param property_name \type{str} The name of the property, eg "value".
|
||||
# \param property_value \type{str} The new value of the property.
|
||||
#
|
||||
# \return True if successful, False if not.
|
||||
@pyqtSlot(str, str, str, str, result = bool)
|
||||
def setContainerProperty(self, container_id, setting_key, property_name, property_value):
|
||||
if self._container_registry.isReadOnly(container_id):
|
||||
Logger.log("w", "Cannot set properties of read-only container %s.", container_id)
|
||||
return False
|
||||
|
||||
containers = self._container_registry.findContainers(id = container_id)
|
||||
if not containers:
|
||||
Logger.log("w", "Could not set properties of container %s because it was not found.", container_id)
|
||||
return False
|
||||
|
||||
container = containers[0]
|
||||
|
||||
container.setProperty(setting_key, property_name, property_value)
|
||||
|
||||
basefile = container.getMetaDataEntry("base_file", container_id)
|
||||
for sibbling_container in self._container_registry.findInstanceContainers(base_file = basefile):
|
||||
if sibbling_container != container:
|
||||
sibbling_container.setProperty(setting_key, property_name, property_value)
|
||||
|
||||
return True
|
||||
|
||||
## Get a setting property of the specified container.
|
||||
#
|
||||
# This will get the specified property of the specified setting of the
|
||||
# specified container.
|
||||
#
|
||||
# \param container_id The ID of the container to get the setting property
|
||||
# of.
|
||||
# \param setting_key The key of the setting to get the property of.
|
||||
# \param property_name The property to obtain.
|
||||
# \return The value of the specified property. The type of this property
|
||||
# value depends on the type of the property. For instance, the "value"
|
||||
# property of an integer setting will be a Python int, but the "value"
|
||||
# property of an enum setting will be a Python str.
|
||||
@pyqtSlot(str, str, str, result = QVariant)
|
||||
def getContainerProperty(self, container_id: str, setting_key: str, property_name: str):
|
||||
containers = self._container_registry.findContainers(id = container_id)
|
||||
if not containers:
|
||||
Logger.log("w", "Could not get properties of container %s because it was not found.", container_id)
|
||||
return ""
|
||||
container = containers[0]
|
||||
|
||||
return container.getProperty(setting_key, property_name)
|
||||
|
||||
@pyqtSlot(str, result = str)
|
||||
def makeUniqueName(self, original_name):
|
||||
return self._container_registry.uniqueName(original_name)
|
||||
|
|
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, Callable, 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
|
@ -457,7 +457,8 @@ class CuraEngineBackend(QObject, Backend):
|
|||
# Only count sliceable objects
|
||||
if node.callDecoration("isSliceable"):
|
||||
build_plate_number = node.callDecoration("getBuildPlateNumber")
|
||||
num_objects[build_plate_number] += 1
|
||||
if build_plate_number is not None:
|
||||
num_objects[build_plate_number] += 1
|
||||
return num_objects
|
||||
|
||||
## Listener for when the scene has changed.
|
||||
|
@ -490,7 +491,9 @@ class CuraEngineBackend(QObject, Backend):
|
|||
if mesh_data and mesh_data.getVertices() is None:
|
||||
return
|
||||
|
||||
build_plate_changed.add(source_build_plate_number)
|
||||
# There are some SceneNodes that do not have any build plate associated, then do not add to the list.
|
||||
if source_build_plate_number is not None:
|
||||
build_plate_changed.add(source_build_plate_number)
|
||||
|
||||
if not build_plate_changed:
|
||||
return
|
||||
|
|
|
@ -88,6 +88,25 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
self._command_received = Event()
|
||||
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(catalog.i18nc("@label", "A USB print is in progress, closing Cura will stop this print. Are you sure?"))
|
||||
|
||||
def _onConfirmExitDialogResult(self, result: bool) -> None:
|
||||
if result:
|
||||
application = CuraApplication.getInstance()
|
||||
application.triggerNextExitCheck()
|
||||
|
||||
## Reset USB device settings
|
||||
#
|
||||
def resetDeviceSettings(self):
|
||||
|
@ -435,9 +454,6 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||
|
||||
self._gcode_position += 1
|
||||
|
||||
def getIsPrinting(self)-> bool:
|
||||
return self._is_printing
|
||||
|
||||
|
||||
class FirmwareUpdateState(IntEnum):
|
||||
idle = 0
|
||||
|
|
|
@ -6,8 +6,7 @@ import platform
|
|||
import time
|
||||
import serial.tools.list_ports
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal, QCoreApplication
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal
|
||||
|
||||
from UM.Logger import Logger
|
||||
from UM.Resources import Resources
|
||||
|
@ -51,11 +50,6 @@ 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():
|
||||
|
@ -190,51 +184,3 @@ 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
|
|
@ -40,6 +40,14 @@
|
|||
[18, -18]
|
||||
]
|
||||
},
|
||||
"machine_head_with_fans_polygon": {
|
||||
"default_value": [
|
||||
[-75, -18],
|
||||
[-75, 35],
|
||||
[18, 35],
|
||||
[18, -18]
|
||||
]
|
||||
},
|
||||
"gantry_height": {
|
||||
"default_value": 55
|
||||
},
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// Copyright (c) 2017 Ultimaker B.V.
|
||||
// Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Controls.Styles 1.1
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Dialogs 1.1
|
||||
import QtQuick.Dialogs 1.2
|
||||
|
||||
import UM 1.3 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
@ -22,12 +22,6 @@ 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
|
||||
|
@ -120,31 +114,10 @@ UM.MainWindow
|
|||
|
||||
RecentFilesMenu { }
|
||||
|
||||
MenuSeparator { }
|
||||
|
||||
MenuItem
|
||||
{
|
||||
text: catalog.i18nc("@action:inmenu menubar:file", "&Save Selection to File");
|
||||
enabled: UM.Selection.hasSelection;
|
||||
iconName: "document-save-as";
|
||||
onTriggered: UM.OutputDeviceManager.requestWriteSelectionToDevice("local_file", PrintInformation.jobName, { "filter_by_machine": false, "preferred_mimetype": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml"});
|
||||
}
|
||||
|
||||
MenuItem
|
||||
{
|
||||
id: saveAsMenu
|
||||
text: catalog.i18nc("@title:menu menubar:file", "Save &As...")
|
||||
onTriggered:
|
||||
{
|
||||
var localDeviceId = "local_file";
|
||||
UM.OutputDeviceManager.requestWriteToDevice(localDeviceId, PrintInformation.jobName, { "filter_by_machine": false, "preferred_mimetype": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml"});
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem
|
||||
{
|
||||
id: saveWorkspaceMenu
|
||||
text: catalog.i18nc("@title:menu menubar:file","Save &Project...")
|
||||
text: catalog.i18nc("@title:menu menubar:file","&Save...")
|
||||
onTriggered:
|
||||
{
|
||||
var args = { "filter_by_machine": false, "file_type": "workspace", "preferred_mimetype": "application/x-curaproject+xml" };
|
||||
|
@ -160,6 +133,29 @@ UM.MainWindow
|
|||
}
|
||||
}
|
||||
|
||||
MenuSeparator { }
|
||||
|
||||
MenuItem
|
||||
{
|
||||
id: saveAsMenu
|
||||
text: catalog.i18nc("@title:menu menubar:file", "&Export...")
|
||||
onTriggered:
|
||||
{
|
||||
var localDeviceId = "local_file";
|
||||
UM.OutputDeviceManager.requestWriteToDevice(localDeviceId, PrintInformation.jobName, { "filter_by_machine": false, "preferred_mimetype": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml"});
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem
|
||||
{
|
||||
text: catalog.i18nc("@action:inmenu menubar:file", "Export Selection...");
|
||||
enabled: UM.Selection.hasSelection;
|
||||
iconName: "document-save-as";
|
||||
onTriggered: UM.OutputDeviceManager.requestWriteSelectionToDevice("local_file", PrintInformation.jobName, { "filter_by_machine": false, "preferred_mimetype": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml"});
|
||||
}
|
||||
|
||||
MenuSeparator { }
|
||||
|
||||
MenuItem { action: Cura.Actions.reloadAll; }
|
||||
|
||||
MenuSeparator { }
|
||||
|
@ -704,10 +700,50 @@ UM.MainWindow
|
|||
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)
|
||||
onVisibilityChanged:
|
||||
{
|
||||
if (!visible)
|
||||
{
|
||||
// reset the text to default because other modules may change the message text.
|
||||
text = catalog.i18nc("@label", "Are you sure you want to exit Cura?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections
|
||||
{
|
||||
target: CuraApplication
|
||||
onShowConfirmExitDialog:
|
||||
{
|
||||
exitConfirmationDialog.text = message;
|
||||
exitConfirmationDialog.open();
|
||||
}
|
||||
}
|
||||
|
||||
Connections
|
||||
{
|
||||
target: Cura.Actions.quit
|
||||
onTriggered: CuraApplication.closeApplication();
|
||||
onTriggered: CuraApplication.exitApplication();
|
||||
}
|
||||
|
||||
Connections
|
||||
|
|
|
@ -81,7 +81,7 @@ Item {
|
|||
text: PrintInformation.jobName
|
||||
horizontalAlignment: TextInput.AlignRight
|
||||
onEditingFinished: {
|
||||
var new_name = text == "" ? "unnamed" : text;
|
||||
var new_name = text == "" ? catalog.i18nc("@text Print job name", "unnamed") : text;
|
||||
PrintInformation.setJobName(new_name, true);
|
||||
printJobTextfield.focus = false;
|
||||
}
|
||||
|
|
|
@ -103,7 +103,6 @@ TabView
|
|||
|
||||
onYes:
|
||||
{
|
||||
Cura.ContainerManager.setContainerProperty(base.containerId, "material_diameter", "value", new_diameter_value);
|
||||
base.setMetaDataEntry("approximate_diameter", old_approximate_diameter_value, getApproximateDiameter(new_diameter_value).toString());
|
||||
base.setMetaDataEntry("properties/diameter", properties.diameter, new_diameter_value);
|
||||
}
|
||||
|
@ -230,7 +229,7 @@ TabView
|
|||
{
|
||||
// This does not use a SettingPropertyProvider, because we need to make the change to all containers
|
||||
// which derive from the same base_file
|
||||
var old_diameter = Cura.ContainerManager.getContainerProperty(base.containerId, "material_diameter", "value").toString();
|
||||
var old_diameter = Cura.ContainerManager.getContainerMetaDataEntry(base.containerId, "properties/diameter");
|
||||
var old_approximate_diameter = Cura.ContainerManager.getContainerMetaDataEntry(base.containerId, "approximate_diameter");
|
||||
var new_approximate_diameter = getApproximateDiameter(value);
|
||||
if (new_approximate_diameter != Cura.ExtruderManager.getActiveExtruderStack().approximateMaterialDiameter)
|
||||
|
@ -242,7 +241,6 @@ TabView
|
|||
confirmDiameterChangeDialog.open()
|
||||
}
|
||||
else {
|
||||
Cura.ContainerManager.setContainerProperty(base.containerId, "material_diameter", "value", value);
|
||||
base.setMetaDataEntry("approximate_diameter", old_approximate_diameter, getApproximateDiameter(value).toString());
|
||||
base.setMetaDataEntry("properties/diameter", properties.diameter, value);
|
||||
}
|
||||
|
@ -271,7 +269,7 @@ TabView
|
|||
{
|
||||
id: spoolWeightSpinBox
|
||||
width: scrollView.columnWidth
|
||||
value: base.getMaterialPreferenceValue(properties.guid, "spool_weight")
|
||||
value: base.getMaterialPreferenceValue(properties.guid, "spool_weight", Cura.ContainerManager.getContainerMetaDataEntry(properties.container_id, "properties/weight"))
|
||||
suffix: " g"
|
||||
stepSize: 100
|
||||
decimals: 0
|
||||
|
@ -468,7 +466,7 @@ TabView
|
|||
}
|
||||
if(!spoolWeight)
|
||||
{
|
||||
spoolWeight = base.getMaterialPreferenceValue(properties.guid, "spool_weight");
|
||||
spoolWeight = base.getMaterialPreferenceValue(properties.guid, "spool_weight", Cura.ContainerManager.getContainerMetaDataEntry(properties.container_id, "properties/weight"));
|
||||
}
|
||||
|
||||
if (diameter == 0 || density == 0 || spoolWeight == 0)
|
||||
|
@ -517,21 +515,34 @@ TabView
|
|||
// value has not changed
|
||||
return;
|
||||
}
|
||||
materialPreferenceValues[material_guid][entry_name] = new_value;
|
||||
if (entry_name in materialPreferenceValues[material_guid] && new_value.toString() == 0)
|
||||
{
|
||||
// no need to store a 0, that's the default, so remove it
|
||||
materialPreferenceValues[material_guid].delete(entry_name);
|
||||
if (!(materialPreferenceValues[material_guid]))
|
||||
{
|
||||
// remove empty map
|
||||
materialPreferenceValues.delete(material_guid);
|
||||
}
|
||||
}
|
||||
if (new_value.toString() != 0)
|
||||
{
|
||||
// store new value
|
||||
materialPreferenceValues[material_guid][entry_name] = new_value;
|
||||
}
|
||||
|
||||
// store preference
|
||||
UM.Preferences.setValue("cura/material_settings", JSON.stringify(materialPreferenceValues));
|
||||
}
|
||||
|
||||
function getMaterialPreferenceValue(material_guid, entry_name)
|
||||
function getMaterialPreferenceValue(material_guid, entry_name, default_value)
|
||||
{
|
||||
if(material_guid in materialPreferenceValues && entry_name in materialPreferenceValues[material_guid])
|
||||
{
|
||||
return materialPreferenceValues[material_guid][entry_name];
|
||||
}
|
||||
|
||||
var material_weight = Cura.ContainerManager.getContainerMetaDataEntry(base.containerId, "properties", "weight");
|
||||
return material_weight || 0;
|
||||
default_value = default_value | 0;
|
||||
return default_value;
|
||||
}
|
||||
|
||||
// update the display name of the material
|
||||
|
|
|
@ -486,6 +486,7 @@ Item
|
|||
|
||||
materialProperties.name = currentItem.name ? currentItem.name : "Unknown";
|
||||
materialProperties.guid = currentItem.guid;
|
||||
materialProperties.container_id = currentItem.container_id;
|
||||
|
||||
materialProperties.brand = currentItem.brand ? currentItem.brand : "Unknown";
|
||||
materialProperties.material = currentItem.material ? currentItem.material : "Unknown";
|
||||
|
@ -543,6 +544,7 @@ Item
|
|||
id: materialProperties
|
||||
|
||||
property string guid: "00000000-0000-0000-0000-000000000000"
|
||||
property string container_id: "Unknown";
|
||||
property string name: "Unknown";
|
||||
property string profile_type: "Unknown";
|
||||
property string brand: "Unknown";
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue