|
@@ -6,7 +6,7 @@ import math
|
|
import os
|
|
import os
|
|
import unicodedata
|
|
import unicodedata
|
|
import re # To create abbreviations for printer names.
|
|
import re # To create abbreviations for printer names.
|
|
-from typing import Dict
|
|
|
|
|
|
+from typing import Dict, List, Optional
|
|
|
|
|
|
from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty, pyqtSlot
|
|
from PyQt5.QtCore import QObject, pyqtSignal, pyqtProperty, pyqtSlot
|
|
|
|
|
|
@@ -16,6 +16,12 @@ from UM.Scene.SceneNode import SceneNode
|
|
from UM.i18n import i18nCatalog
|
|
from UM.i18n import i18nCatalog
|
|
from UM.MimeTypeDatabase import MimeTypeDatabase
|
|
from UM.MimeTypeDatabase import MimeTypeDatabase
|
|
|
|
|
|
|
|
+
|
|
|
|
+from typing import TYPE_CHECKING
|
|
|
|
+
|
|
|
|
+if TYPE_CHECKING:
|
|
|
|
+ from cura.CuraApplication import CuraApplication
|
|
|
|
+
|
|
catalog = i18nCatalog("cura")
|
|
catalog = i18nCatalog("cura")
|
|
|
|
|
|
|
|
|
|
@@ -47,24 +53,26 @@ class PrintInformation(QObject):
|
|
ActiveMachineChanged = 3
|
|
ActiveMachineChanged = 3
|
|
Other = 4
|
|
Other = 4
|
|
|
|
|
|
- def __init__(self, application, parent = None):
|
|
|
|
|
|
+ UNTITLED_JOB_NAME = "Untitled"
|
|
|
|
+
|
|
|
|
+ def __init__(self, application: "CuraApplication", parent = None) -> None:
|
|
super().__init__(parent)
|
|
super().__init__(parent)
|
|
self._application = application
|
|
self._application = application
|
|
|
|
|
|
- self.UNTITLED_JOB_NAME = "Untitled"
|
|
|
|
-
|
|
|
|
self.initializeCuraMessagePrintTimeProperties()
|
|
self.initializeCuraMessagePrintTimeProperties()
|
|
|
|
|
|
- self._material_lengths = {} # indexed by build plate number
|
|
|
|
- self._material_weights = {}
|
|
|
|
- self._material_costs = {}
|
|
|
|
- self._material_names = {}
|
|
|
|
|
|
+ # Indexed by build plate number
|
|
|
|
+ self._material_lengths = {} # type: Dict[int, List[float]]
|
|
|
|
+ self._material_weights = {} # type: Dict[int, List[float]]
|
|
|
|
+ self._material_costs = {} # type: Dict[int, List[float]]
|
|
|
|
+ self._material_names = {} # type: Dict[int, List[str]]
|
|
|
|
|
|
self._pre_sliced = False
|
|
self._pre_sliced = False
|
|
|
|
|
|
self._backend = self._application.getBackend()
|
|
self._backend = self._application.getBackend()
|
|
if self._backend:
|
|
if self._backend:
|
|
self._backend.printDurationMessage.connect(self._onPrintDurationMessage)
|
|
self._backend.printDurationMessage.connect(self._onPrintDurationMessage)
|
|
|
|
+
|
|
self._application.getController().getScene().sceneChanged.connect(self._onSceneChanged)
|
|
self._application.getController().getScene().sceneChanged.connect(self._onSceneChanged)
|
|
|
|
|
|
self._is_user_specified_job_name = False
|
|
self._is_user_specified_job_name = False
|
|
@@ -76,25 +84,23 @@ class PrintInformation(QObject):
|
|
|
|
|
|
self._multi_build_plate_model = self._application.getMultiBuildPlateModel()
|
|
self._multi_build_plate_model = self._application.getMultiBuildPlateModel()
|
|
|
|
|
|
- ss = self._multi_build_plate_model.maxBuildPlate
|
|
|
|
-
|
|
|
|
self._application.globalContainerStackChanged.connect(self._updateJobName)
|
|
self._application.globalContainerStackChanged.connect(self._updateJobName)
|
|
self._application.globalContainerStackChanged.connect(self.setToZeroPrintInformation)
|
|
self._application.globalContainerStackChanged.connect(self.setToZeroPrintInformation)
|
|
self._application.fileLoaded.connect(self.setBaseName)
|
|
self._application.fileLoaded.connect(self.setBaseName)
|
|
self._application.workspaceLoaded.connect(self.setProjectName)
|
|
self._application.workspaceLoaded.connect(self.setProjectName)
|
|
- self._multi_build_plate_model.activeBuildPlateChanged.connect(self._onActiveBuildPlateChanged)
|
|
|
|
-
|
|
|
|
|
|
+ self._application.getMachineManager().rootMaterialChanged.connect(self._onActiveMaterialsChanged)
|
|
self._application.getInstance().getPreferences().preferenceChanged.connect(self._onPreferencesChanged)
|
|
self._application.getInstance().getPreferences().preferenceChanged.connect(self._onPreferencesChanged)
|
|
|
|
|
|
- self._application.getMachineManager().rootMaterialChanged.connect(self._onActiveMaterialsChanged)
|
|
|
|
|
|
+ self._multi_build_plate_model.activeBuildPlateChanged.connect(self._onActiveBuildPlateChanged)
|
|
|
|
+
|
|
self._onActiveMaterialsChanged()
|
|
self._onActiveMaterialsChanged()
|
|
|
|
|
|
- self._material_amounts = []
|
|
|
|
|
|
+ self._material_amounts = [] # type: List[float]
|
|
|
|
|
|
# Crate cura message translations and using translation keys initialize empty time Duration object for total time
|
|
# Crate cura message translations and using translation keys initialize empty time Duration object for total time
|
|
# and time for each feature
|
|
# and time for each feature
|
|
- def initializeCuraMessagePrintTimeProperties(self):
|
|
|
|
- self._current_print_time = {} # Duration(None, self)
|
|
|
|
|
|
+ def initializeCuraMessagePrintTimeProperties(self) -> None:
|
|
|
|
+ self._current_print_time = {} # type: Dict[int, Duration]
|
|
|
|
|
|
self._print_time_message_translations = {
|
|
self._print_time_message_translations = {
|
|
"inset_0": catalog.i18nc("@tooltip", "Outer Wall"),
|
|
"inset_0": catalog.i18nc("@tooltip", "Outer Wall"),
|
|
@@ -110,15 +116,15 @@ class PrintInformation(QObject):
|
|
"none": catalog.i18nc("@tooltip", "Other")
|
|
"none": catalog.i18nc("@tooltip", "Other")
|
|
}
|
|
}
|
|
|
|
|
|
- self._print_time_message_values = {}
|
|
|
|
|
|
+ self._print_time_message_values = {} # type: Dict[int, Dict[str, Duration]]
|
|
|
|
|
|
- def _initPrintTimeMessageValues(self, build_plate_number):
|
|
|
|
|
|
+ def _initPrintTimeMessageValues(self, build_plate_number: int) -> None:
|
|
# Full fill message values using keys from _print_time_message_translations
|
|
# Full fill message values using keys from _print_time_message_translations
|
|
self._print_time_message_values[build_plate_number] = {}
|
|
self._print_time_message_values[build_plate_number] = {}
|
|
for key in self._print_time_message_translations.keys():
|
|
for key in self._print_time_message_translations.keys():
|
|
self._print_time_message_values[build_plate_number][key] = Duration(None, self)
|
|
self._print_time_message_values[build_plate_number][key] = Duration(None, self)
|
|
|
|
|
|
- def _initVariablesWithBuildPlate(self, build_plate_number):
|
|
|
|
|
|
+ def _initVariablesWithBuildPlate(self, build_plate_number: int) -> None:
|
|
if build_plate_number not in self._print_time_message_values:
|
|
if build_plate_number not in self._print_time_message_values:
|
|
self._initPrintTimeMessageValues(build_plate_number)
|
|
self._initPrintTimeMessageValues(build_plate_number)
|
|
if self._active_build_plate not in self._material_lengths:
|
|
if self._active_build_plate not in self._material_lengths:
|
|
@@ -130,23 +136,24 @@ class PrintInformation(QObject):
|
|
if self._active_build_plate not in self._material_names:
|
|
if self._active_build_plate not in self._material_names:
|
|
self._material_names[self._active_build_plate] = []
|
|
self._material_names[self._active_build_plate] = []
|
|
if self._active_build_plate not in self._current_print_time:
|
|
if self._active_build_plate not in self._current_print_time:
|
|
- self._current_print_time[self._active_build_plate] = Duration(None, self)
|
|
|
|
|
|
+ self._current_print_time[self._active_build_plate] = Duration(parent = self)
|
|
|
|
|
|
currentPrintTimeChanged = pyqtSignal()
|
|
currentPrintTimeChanged = pyqtSignal()
|
|
|
|
|
|
preSlicedChanged = pyqtSignal()
|
|
preSlicedChanged = pyqtSignal()
|
|
|
|
|
|
@pyqtProperty(bool, notify=preSlicedChanged)
|
|
@pyqtProperty(bool, notify=preSlicedChanged)
|
|
- def preSliced(self):
|
|
|
|
|
|
+ def preSliced(self) -> bool:
|
|
return self._pre_sliced
|
|
return self._pre_sliced
|
|
|
|
|
|
- def setPreSliced(self, pre_sliced):
|
|
|
|
- self._pre_sliced = pre_sliced
|
|
|
|
- self._updateJobName()
|
|
|
|
- self.preSlicedChanged.emit()
|
|
|
|
|
|
+ def setPreSliced(self, pre_sliced: bool) -> None:
|
|
|
|
+ if self._pre_sliced != pre_sliced:
|
|
|
|
+ self._pre_sliced = pre_sliced
|
|
|
|
+ self._updateJobName()
|
|
|
|
+ self.preSlicedChanged.emit()
|
|
|
|
|
|
@pyqtProperty(Duration, notify = currentPrintTimeChanged)
|
|
@pyqtProperty(Duration, notify = currentPrintTimeChanged)
|
|
- def currentPrintTime(self):
|
|
|
|
|
|
+ def currentPrintTime(self) -> Duration:
|
|
return self._current_print_time[self._active_build_plate]
|
|
return self._current_print_time[self._active_build_plate]
|
|
|
|
|
|
materialLengthsChanged = pyqtSignal()
|
|
materialLengthsChanged = pyqtSignal()
|
|
@@ -176,33 +183,37 @@ class PrintInformation(QObject):
|
|
def printTimes(self):
|
|
def printTimes(self):
|
|
return self._print_time_message_values[self._active_build_plate]
|
|
return self._print_time_message_values[self._active_build_plate]
|
|
|
|
|
|
- def _onPrintDurationMessage(self, build_plate_number, print_time: Dict[str, int], material_amounts: list):
|
|
|
|
|
|
+ def _onPrintDurationMessage(self, build_plate_number: int, print_time: Dict[str, int], material_amounts: List[float]) -> None:
|
|
self._updateTotalPrintTimePerFeature(build_plate_number, print_time)
|
|
self._updateTotalPrintTimePerFeature(build_plate_number, print_time)
|
|
self.currentPrintTimeChanged.emit()
|
|
self.currentPrintTimeChanged.emit()
|
|
|
|
|
|
self._material_amounts = material_amounts
|
|
self._material_amounts = material_amounts
|
|
self._calculateInformation(build_plate_number)
|
|
self._calculateInformation(build_plate_number)
|
|
|
|
|
|
- def _updateTotalPrintTimePerFeature(self, build_plate_number, print_time: Dict[str, int]):
|
|
|
|
|
|
+ def _updateTotalPrintTimePerFeature(self, build_plate_number: int, print_times: Dict[str, int]) -> None:
|
|
total_estimated_time = 0
|
|
total_estimated_time = 0
|
|
|
|
|
|
if build_plate_number not in self._print_time_message_values:
|
|
if build_plate_number not in self._print_time_message_values:
|
|
self._initPrintTimeMessageValues(build_plate_number)
|
|
self._initPrintTimeMessageValues(build_plate_number)
|
|
|
|
|
|
- for feature, time in print_time.items():
|
|
|
|
|
|
+ for feature, time in print_times.items():
|
|
|
|
+ if feature not in self._print_time_message_values[build_plate_number]:
|
|
|
|
+ self._print_time_message_values[build_plate_number][feature] = Duration(parent=self)
|
|
|
|
+ duration = self._print_time_message_values[build_plate_number][feature]
|
|
|
|
+
|
|
if time != time: # Check for NaN. Engine can sometimes give us weird values.
|
|
if time != time: # Check for NaN. Engine can sometimes give us weird values.
|
|
- self._print_time_message_values[build_plate_number].get(feature).setDuration(0)
|
|
|
|
|
|
+ duration.setDuration(0)
|
|
Logger.log("w", "Received NaN for print duration message")
|
|
Logger.log("w", "Received NaN for print duration message")
|
|
continue
|
|
continue
|
|
|
|
|
|
total_estimated_time += time
|
|
total_estimated_time += time
|
|
- self._print_time_message_values[build_plate_number].get(feature).setDuration(time)
|
|
|
|
|
|
+ duration.setDuration(time)
|
|
|
|
|
|
if build_plate_number not in self._current_print_time:
|
|
if build_plate_number not in self._current_print_time:
|
|
self._current_print_time[build_plate_number] = Duration(None, self)
|
|
self._current_print_time[build_plate_number] = Duration(None, self)
|
|
self._current_print_time[build_plate_number].setDuration(total_estimated_time)
|
|
self._current_print_time[build_plate_number].setDuration(total_estimated_time)
|
|
|
|
|
|
- def _calculateInformation(self, build_plate_number):
|
|
|
|
|
|
+ def _calculateInformation(self, build_plate_number: int) -> None:
|
|
global_stack = self._application.getGlobalContainerStack()
|
|
global_stack = self._application.getGlobalContainerStack()
|
|
if global_stack is None:
|
|
if global_stack is None:
|
|
return
|
|
return
|
|
@@ -227,7 +238,7 @@ class PrintInformation(QObject):
|
|
radius = extruder_stack.getProperty("material_diameter", "value") / 2
|
|
radius = extruder_stack.getProperty("material_diameter", "value") / 2
|
|
|
|
|
|
weight = float(amount) * float(density) / 1000
|
|
weight = float(amount) * float(density) / 1000
|
|
- cost = 0
|
|
|
|
|
|
+ cost = 0.
|
|
material_name = catalog.i18nc("@label unknown material", "Unknown")
|
|
material_name = catalog.i18nc("@label unknown material", "Unknown")
|
|
if material:
|
|
if material:
|
|
material_guid = material.getMetaDataEntry("GUID")
|
|
material_guid = material.getMetaDataEntry("GUID")
|
|
@@ -258,14 +269,14 @@ class PrintInformation(QObject):
|
|
self.materialCostsChanged.emit()
|
|
self.materialCostsChanged.emit()
|
|
self.materialNamesChanged.emit()
|
|
self.materialNamesChanged.emit()
|
|
|
|
|
|
- def _onPreferencesChanged(self, preference):
|
|
|
|
|
|
+ def _onPreferencesChanged(self, preference: str) -> None:
|
|
if preference != "cura/material_settings":
|
|
if preference != "cura/material_settings":
|
|
return
|
|
return
|
|
|
|
|
|
for build_plate_number in range(self._multi_build_plate_model.maxBuildPlate + 1):
|
|
for build_plate_number in range(self._multi_build_plate_model.maxBuildPlate + 1):
|
|
self._calculateInformation(build_plate_number)
|
|
self._calculateInformation(build_plate_number)
|
|
|
|
|
|
- def _onActiveBuildPlateChanged(self):
|
|
|
|
|
|
+ def _onActiveBuildPlateChanged(self) -> None:
|
|
new_active_build_plate = self._multi_build_plate_model.activeBuildPlate
|
|
new_active_build_plate = self._multi_build_plate_model.activeBuildPlate
|
|
if new_active_build_plate != self._active_build_plate:
|
|
if new_active_build_plate != self._active_build_plate:
|
|
self._active_build_plate = new_active_build_plate
|
|
self._active_build_plate = new_active_build_plate
|
|
@@ -279,14 +290,14 @@ class PrintInformation(QObject):
|
|
self.materialNamesChanged.emit()
|
|
self.materialNamesChanged.emit()
|
|
self.currentPrintTimeChanged.emit()
|
|
self.currentPrintTimeChanged.emit()
|
|
|
|
|
|
- def _onActiveMaterialsChanged(self, *args, **kwargs):
|
|
|
|
|
|
+ def _onActiveMaterialsChanged(self, *args, **kwargs) -> None:
|
|
for build_plate_number in range(self._multi_build_plate_model.maxBuildPlate + 1):
|
|
for build_plate_number in range(self._multi_build_plate_model.maxBuildPlate + 1):
|
|
self._calculateInformation(build_plate_number)
|
|
self._calculateInformation(build_plate_number)
|
|
|
|
|
|
# Manual override of job name should also set the base name so that when the printer prefix is updated, it the
|
|
# Manual override of job name should also set the base name so that when the printer prefix is updated, it the
|
|
# prefix can be added to the manually added name, not the old base name
|
|
# prefix can be added to the manually added name, not the old base name
|
|
@pyqtSlot(str, bool)
|
|
@pyqtSlot(str, bool)
|
|
- def setJobName(self, name, is_user_specified_job_name = False):
|
|
|
|
|
|
+ def setJobName(self, name: str, is_user_specified_job_name = False) -> None:
|
|
self._is_user_specified_job_name = is_user_specified_job_name
|
|
self._is_user_specified_job_name = is_user_specified_job_name
|
|
self._job_name = name
|
|
self._job_name = name
|
|
self._base_name = name.replace(self._abbr_machine + "_", "")
|
|
self._base_name = name.replace(self._abbr_machine + "_", "")
|
|
@@ -300,7 +311,7 @@ class PrintInformation(QObject):
|
|
def jobName(self):
|
|
def jobName(self):
|
|
return self._job_name
|
|
return self._job_name
|
|
|
|
|
|
- def _updateJobName(self):
|
|
|
|
|
|
+ def _updateJobName(self) -> None:
|
|
if self._base_name == "":
|
|
if self._base_name == "":
|
|
self._job_name = self.UNTITLED_JOB_NAME
|
|
self._job_name = self.UNTITLED_JOB_NAME
|
|
self._is_user_specified_job_name = False
|
|
self._is_user_specified_job_name = False
|
|
@@ -335,12 +346,12 @@ class PrintInformation(QObject):
|
|
self.jobNameChanged.emit()
|
|
self.jobNameChanged.emit()
|
|
|
|
|
|
@pyqtSlot(str)
|
|
@pyqtSlot(str)
|
|
- def setProjectName(self, name):
|
|
|
|
|
|
+ def setProjectName(self, name: str) -> None:
|
|
self.setBaseName(name, is_project_file = True)
|
|
self.setBaseName(name, is_project_file = True)
|
|
|
|
|
|
baseNameChanged = pyqtSignal()
|
|
baseNameChanged = pyqtSignal()
|
|
|
|
|
|
- def setBaseName(self, base_name: str, is_project_file: bool = False):
|
|
|
|
|
|
+ def setBaseName(self, base_name: str, is_project_file: bool = False) -> None:
|
|
self._is_user_specified_job_name = False
|
|
self._is_user_specified_job_name = False
|
|
|
|
|
|
# Ensure that we don't use entire path but only filename
|
|
# Ensure that we don't use entire path but only filename
|
|
@@ -384,7 +395,7 @@ class PrintInformation(QObject):
|
|
## Created an acronym-like abbreviated machine name from the currently
|
|
## Created an acronym-like abbreviated machine name from the currently
|
|
# active machine name.
|
|
# active machine name.
|
|
# Called each time the global stack is switched.
|
|
# Called each time the global stack is switched.
|
|
- def _defineAbbreviatedMachineName(self):
|
|
|
|
|
|
+ def _defineAbbreviatedMachineName(self) -> None:
|
|
global_container_stack = self._application.getGlobalContainerStack()
|
|
global_container_stack = self._application.getGlobalContainerStack()
|
|
if not global_container_stack:
|
|
if not global_container_stack:
|
|
self._abbr_machine = ""
|
|
self._abbr_machine = ""
|
|
@@ -408,8 +419,8 @@ class PrintInformation(QObject):
|
|
self._abbr_machine = abbr_machine
|
|
self._abbr_machine = abbr_machine
|
|
|
|
|
|
## Utility method that strips accents from characters (eg: â -> a)
|
|
## Utility method that strips accents from characters (eg: â -> a)
|
|
- def _stripAccents(self, str):
|
|
|
|
- return ''.join(char for char in unicodedata.normalize('NFD', str) if unicodedata.category(char) != 'Mn')
|
|
|
|
|
|
+ def _stripAccents(self, to_strip: str) -> str:
|
|
|
|
+ return ''.join(char for char in unicodedata.normalize('NFD', to_strip) if unicodedata.category(char) != 'Mn')
|
|
|
|
|
|
@pyqtSlot(result = "QVariantMap")
|
|
@pyqtSlot(result = "QVariantMap")
|
|
def getFeaturePrintTimes(self):
|
|
def getFeaturePrintTimes(self):
|
|
@@ -424,7 +435,7 @@ class PrintInformation(QObject):
|
|
return result
|
|
return result
|
|
|
|
|
|
# Simulate message with zero time duration
|
|
# Simulate message with zero time duration
|
|
- def setToZeroPrintInformation(self, build_plate = None):
|
|
|
|
|
|
+ def setToZeroPrintInformation(self, build_plate: Optional[int] = None) -> None:
|
|
if build_plate is None:
|
|
if build_plate is None:
|
|
build_plate = self._active_build_plate
|
|
build_plate = self._active_build_plate
|
|
|
|
|
|
@@ -434,12 +445,12 @@ class PrintInformation(QObject):
|
|
self._print_time_message_values[build_plate] = {}
|
|
self._print_time_message_values[build_plate] = {}
|
|
for key in self._print_time_message_values[build_plate].keys():
|
|
for key in self._print_time_message_values[build_plate].keys():
|
|
temp_message[key] = 0
|
|
temp_message[key] = 0
|
|
- temp_material_amounts = [0]
|
|
|
|
|
|
+ temp_material_amounts = [0.]
|
|
|
|
|
|
self._onPrintDurationMessage(build_plate, temp_message, temp_material_amounts)
|
|
self._onPrintDurationMessage(build_plate, temp_message, temp_material_amounts)
|
|
|
|
|
|
## Listen to scene changes to check if we need to reset the print information
|
|
## Listen to scene changes to check if we need to reset the print information
|
|
- def _onSceneChanged(self, scene_node):
|
|
|
|
|
|
+ def _onSceneChanged(self, scene_node: SceneNode) -> None:
|
|
# Ignore any changes that are not related to sliceable objects
|
|
# Ignore any changes that are not related to sliceable objects
|
|
if not isinstance(scene_node, SceneNode)\
|
|
if not isinstance(scene_node, SceneNode)\
|
|
or not scene_node.callDecoration("isSliceable")\
|
|
or not scene_node.callDecoration("isSliceable")\
|