mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-06 22:47:29 -06:00
Convert doxygen to rst for CuraEngineBackend
This commit is contained in:
parent
8f3827d5ae
commit
797d6ed438
3 changed files with 220 additions and 143 deletions
|
@ -42,12 +42,14 @@ catalog = i18nCatalog("cura")
|
||||||
class CuraEngineBackend(QObject, Backend):
|
class CuraEngineBackend(QObject, Backend):
|
||||||
backendError = Signal()
|
backendError = Signal()
|
||||||
|
|
||||||
## Starts the back-end plug-in.
|
|
||||||
#
|
|
||||||
# This registers all the signal listeners and prepares for communication
|
|
||||||
# with the back-end in general.
|
|
||||||
# CuraEngineBackend is exposed to qml as well.
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
|
"""Starts the back-end plug-in.
|
||||||
|
|
||||||
|
This registers all the signal listeners and prepares for communication
|
||||||
|
with the back-end in general.
|
||||||
|
CuraEngineBackend is exposed to qml as well.
|
||||||
|
"""
|
||||||
|
|
||||||
super().__init__()
|
super().__init__()
|
||||||
# Find out where the engine is located, and how it is called.
|
# Find out where the engine is located, and how it is called.
|
||||||
# This depends on how Cura is packaged and which OS we are running on.
|
# This depends on how Cura is packaged and which OS we are running on.
|
||||||
|
@ -177,18 +179,22 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
self._machine_error_checker = self._application.getMachineErrorChecker()
|
self._machine_error_checker = self._application.getMachineErrorChecker()
|
||||||
self._machine_error_checker.errorCheckFinished.connect(self._onStackErrorCheckFinished)
|
self._machine_error_checker.errorCheckFinished.connect(self._onStackErrorCheckFinished)
|
||||||
|
|
||||||
## Terminate the engine process.
|
|
||||||
#
|
|
||||||
# This function should terminate the engine process.
|
|
||||||
# Called when closing the application.
|
|
||||||
def close(self) -> None:
|
def close(self) -> None:
|
||||||
|
"""Terminate the engine process.
|
||||||
|
|
||||||
|
This function should terminate the engine process.
|
||||||
|
Called when closing the application.
|
||||||
|
"""
|
||||||
|
|
||||||
# Terminate CuraEngine if it is still running at this point
|
# Terminate CuraEngine if it is still running at this point
|
||||||
self._terminate()
|
self._terminate()
|
||||||
|
|
||||||
## Get the command that is used to call the engine.
|
|
||||||
# This is useful for debugging and used to actually start the engine.
|
|
||||||
# \return list of commands and args / parameters.
|
|
||||||
def getEngineCommand(self) -> List[str]:
|
def getEngineCommand(self) -> List[str]:
|
||||||
|
"""Get the command that is used to call the engine.
|
||||||
|
|
||||||
|
This is useful for debugging and used to actually start the engine.
|
||||||
|
:return: list of commands and args / parameters.
|
||||||
|
"""
|
||||||
command = [self._application.getPreferences().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), ""]
|
command = [self._application.getPreferences().getValue("backend/location"), "connect", "127.0.0.1:{0}".format(self._port), ""]
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(prog = "cura", add_help = False)
|
parser = argparse.ArgumentParser(prog = "cura", add_help = False)
|
||||||
|
@ -199,17 +205,18 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
|
|
||||||
return command
|
return command
|
||||||
|
|
||||||
## Emitted when we get a message containing print duration and material amount.
|
|
||||||
# This also implies the slicing has finished.
|
|
||||||
# \param time The amount of time the print will take.
|
|
||||||
# \param material_amount The amount of material the print will use.
|
|
||||||
printDurationMessage = Signal()
|
printDurationMessage = Signal()
|
||||||
|
"""Emitted when we get a message containing print duration and material amount.
|
||||||
|
|
||||||
## Emitted when the slicing process starts.
|
This also implies the slicing has finished.
|
||||||
|
:param time: The amount of time the print will take.
|
||||||
|
:param material_amount: The amount of material the print will use.
|
||||||
|
"""
|
||||||
slicingStarted = Signal()
|
slicingStarted = Signal()
|
||||||
|
"""Emitted when the slicing process starts."""
|
||||||
|
|
||||||
## Emitted when the slicing process is aborted forcefully.
|
|
||||||
slicingCancelled = Signal()
|
slicingCancelled = Signal()
|
||||||
|
"""Emitted when the slicing process is aborted forcefully."""
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def stopSlicing(self) -> None:
|
def stopSlicing(self) -> None:
|
||||||
|
@ -226,14 +233,16 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
if self._error_message:
|
if self._error_message:
|
||||||
self._error_message.hide()
|
self._error_message.hide()
|
||||||
|
|
||||||
## Manually triggers a reslice
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def forceSlice(self) -> None:
|
def forceSlice(self) -> None:
|
||||||
|
"""Manually triggers a reslice"""
|
||||||
|
|
||||||
self.markSliceAll()
|
self.markSliceAll()
|
||||||
self.slice()
|
self.slice()
|
||||||
|
|
||||||
## Perform a slice of the scene.
|
|
||||||
def slice(self) -> None:
|
def slice(self) -> None:
|
||||||
|
"""Perform a slice of the scene."""
|
||||||
|
|
||||||
Logger.log("i", "Starting to slice...")
|
Logger.log("i", "Starting to slice...")
|
||||||
self._slice_start_time = time()
|
self._slice_start_time = time()
|
||||||
if not self._build_plates_to_be_sliced:
|
if not self._build_plates_to_be_sliced:
|
||||||
|
@ -289,9 +298,11 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
self._start_slice_job.start()
|
self._start_slice_job.start()
|
||||||
self._start_slice_job.finished.connect(self._onStartSliceCompleted)
|
self._start_slice_job.finished.connect(self._onStartSliceCompleted)
|
||||||
|
|
||||||
## Terminate the engine process.
|
|
||||||
# Start the engine process by calling _createSocket()
|
|
||||||
def _terminate(self) -> None:
|
def _terminate(self) -> None:
|
||||||
|
"""Terminate the engine process.
|
||||||
|
|
||||||
|
Start the engine process by calling _createSocket()
|
||||||
|
"""
|
||||||
self._slicing = False
|
self._slicing = False
|
||||||
self._stored_layer_data = []
|
self._stored_layer_data = []
|
||||||
if self._start_slice_job_build_plate in self._stored_optimized_layer_data:
|
if self._start_slice_job_build_plate in self._stored_optimized_layer_data:
|
||||||
|
@ -316,15 +327,17 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
except Exception as e: # terminating a process that is already terminating causes an exception, silently ignore this.
|
except Exception as e: # terminating a process that is already terminating causes an exception, silently ignore this.
|
||||||
Logger.log("d", "Exception occurred while trying to kill the engine %s", str(e))
|
Logger.log("d", "Exception occurred while trying to kill the engine %s", str(e))
|
||||||
|
|
||||||
## Event handler to call when the job to initiate the slicing process is
|
|
||||||
# completed.
|
|
||||||
#
|
|
||||||
# When the start slice job is successfully completed, it will be happily
|
|
||||||
# slicing. This function handles any errors that may occur during the
|
|
||||||
# bootstrapping of a slice job.
|
|
||||||
#
|
|
||||||
# \param job The start slice job that was just finished.
|
|
||||||
def _onStartSliceCompleted(self, job: StartSliceJob) -> None:
|
def _onStartSliceCompleted(self, job: StartSliceJob) -> None:
|
||||||
|
"""Event handler to call when the job to initiate the slicing process is
|
||||||
|
|
||||||
|
completed.
|
||||||
|
|
||||||
|
When the start slice job is successfully completed, it will be happily
|
||||||
|
slicing. This function handles any errors that may occur during the
|
||||||
|
bootstrapping of a slice job.
|
||||||
|
|
||||||
|
:param job: The start slice job that was just finished.
|
||||||
|
"""
|
||||||
if self._error_message:
|
if self._error_message:
|
||||||
self._error_message.hide()
|
self._error_message.hide()
|
||||||
|
|
||||||
|
@ -443,11 +456,13 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
if self._slice_start_time:
|
if self._slice_start_time:
|
||||||
Logger.log("d", "Sending slice message took %s seconds", time() - self._slice_start_time )
|
Logger.log("d", "Sending slice message took %s seconds", time() - self._slice_start_time )
|
||||||
|
|
||||||
## Determine enable or disable auto slicing. Return True for enable timer and False otherwise.
|
|
||||||
# It disables when
|
|
||||||
# - preference auto slice is off
|
|
||||||
# - decorator isBlockSlicing is found (used in g-code reader)
|
|
||||||
def determineAutoSlicing(self) -> bool:
|
def determineAutoSlicing(self) -> bool:
|
||||||
|
"""Determine enable or disable auto slicing. Return True for enable timer and False otherwise.
|
||||||
|
|
||||||
|
It disables when:
|
||||||
|
- preference auto slice is off
|
||||||
|
- decorator isBlockSlicing is found (used in g-code reader)
|
||||||
|
"""
|
||||||
enable_timer = True
|
enable_timer = True
|
||||||
self._is_disabled = False
|
self._is_disabled = False
|
||||||
|
|
||||||
|
@ -472,8 +487,9 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
self.disableTimer()
|
self.disableTimer()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
## Return a dict with number of objects per build plate
|
|
||||||
def _numObjectsPerBuildPlate(self) -> Dict[int, int]:
|
def _numObjectsPerBuildPlate(self) -> Dict[int, int]:
|
||||||
|
"""Return a dict with number of objects per build plate"""
|
||||||
|
|
||||||
num_objects = defaultdict(int) #type: Dict[int, int]
|
num_objects = defaultdict(int) #type: Dict[int, int]
|
||||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||||
# Only count sliceable objects
|
# Only count sliceable objects
|
||||||
|
@ -483,12 +499,14 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
num_objects[build_plate_number] += 1
|
num_objects[build_plate_number] += 1
|
||||||
return num_objects
|
return num_objects
|
||||||
|
|
||||||
## Listener for when the scene has changed.
|
|
||||||
#
|
|
||||||
# This should start a slice if the scene is now ready to slice.
|
|
||||||
#
|
|
||||||
# \param source The scene node that was changed.
|
|
||||||
def _onSceneChanged(self, source: SceneNode) -> None:
|
def _onSceneChanged(self, source: SceneNode) -> None:
|
||||||
|
"""Listener for when the scene has changed.
|
||||||
|
|
||||||
|
This should start a slice if the scene is now ready to slice.
|
||||||
|
|
||||||
|
:param source: The scene node that was changed.
|
||||||
|
"""
|
||||||
|
|
||||||
if not source.callDecoration("isSliceable"):
|
if not source.callDecoration("isSliceable"):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -536,10 +554,12 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
|
|
||||||
self._invokeSlice()
|
self._invokeSlice()
|
||||||
|
|
||||||
## Called when an error occurs in the socket connection towards the engine.
|
|
||||||
#
|
|
||||||
# \param error The exception that occurred.
|
|
||||||
def _onSocketError(self, error: Arcus.Error) -> None:
|
def _onSocketError(self, error: Arcus.Error) -> None:
|
||||||
|
"""Called when an error occurs in the socket connection towards the engine.
|
||||||
|
|
||||||
|
:param error: The exception that occurred.
|
||||||
|
"""
|
||||||
|
|
||||||
if self._application.isShuttingDown():
|
if self._application.isShuttingDown():
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -567,8 +587,9 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
break
|
break
|
||||||
return has_slicable
|
return has_slicable
|
||||||
|
|
||||||
## Remove old layer data (if any)
|
|
||||||
def _clearLayerData(self, build_plate_numbers: Set = None) -> None:
|
def _clearLayerData(self, build_plate_numbers: Set = None) -> None:
|
||||||
|
"""Remove old layer data (if any)"""
|
||||||
|
|
||||||
# Clear out any old gcode
|
# Clear out any old gcode
|
||||||
self._scene.gcode_dict = {} # type: ignore
|
self._scene.gcode_dict = {} # type: ignore
|
||||||
|
|
||||||
|
@ -583,8 +604,9 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
if build_plate_number not in self._build_plates_to_be_sliced:
|
if build_plate_number not in self._build_plates_to_be_sliced:
|
||||||
self._build_plates_to_be_sliced.append(build_plate_number)
|
self._build_plates_to_be_sliced.append(build_plate_number)
|
||||||
|
|
||||||
## Convenient function: mark everything to slice, emit state and clear layer data
|
|
||||||
def needsSlicing(self) -> None:
|
def needsSlicing(self) -> None:
|
||||||
|
"""Convenient function: mark everything to slice, emit state and clear layer data"""
|
||||||
|
|
||||||
# CURA-6604: If there's no slicable object, do not (try to) trigger slice, which will clear all the current
|
# CURA-6604: If there's no slicable object, do not (try to) trigger slice, which will clear all the current
|
||||||
# gcode. This can break Gcode file loading if it tries to remove it afterwards.
|
# gcode. This can break Gcode file loading if it tries to remove it afterwards.
|
||||||
if not self.hasSlicableObject():
|
if not self.hasSlicableObject():
|
||||||
|
@ -597,10 +619,12 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
# With manually having to slice, we want to clear the old invalid layer data.
|
# With manually having to slice, we want to clear the old invalid layer data.
|
||||||
self._clearLayerData()
|
self._clearLayerData()
|
||||||
|
|
||||||
## A setting has changed, so check if we must reslice.
|
|
||||||
# \param instance The setting instance that has changed.
|
|
||||||
# \param property The property of the setting instance that has changed.
|
|
||||||
def _onSettingChanged(self, instance: SettingInstance, property: str) -> None:
|
def _onSettingChanged(self, instance: SettingInstance, property: str) -> None:
|
||||||
|
"""A setting has changed, so check if we must reslice.
|
||||||
|
|
||||||
|
:param instance: The setting instance that has changed.
|
||||||
|
:param property: The property of the setting instance that has changed.
|
||||||
|
"""
|
||||||
if property == "value": # Only reslice if the value has changed.
|
if property == "value": # Only reslice if the value has changed.
|
||||||
self.needsSlicing()
|
self.needsSlicing()
|
||||||
self._onChanged()
|
self._onChanged()
|
||||||
|
@ -618,25 +642,31 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
self.needsSlicing()
|
self.needsSlicing()
|
||||||
self._onChanged()
|
self._onChanged()
|
||||||
|
|
||||||
## Called when a sliced layer data message is received from the engine.
|
|
||||||
#
|
|
||||||
# \param message The protobuf message containing sliced layer data.
|
|
||||||
def _onLayerMessage(self, message: Arcus.PythonMessage) -> None:
|
def _onLayerMessage(self, message: Arcus.PythonMessage) -> None:
|
||||||
|
"""Called when a sliced layer data message is received from the engine.
|
||||||
|
|
||||||
|
:param message: The protobuf message containing sliced layer data.
|
||||||
|
"""
|
||||||
|
|
||||||
self._stored_layer_data.append(message)
|
self._stored_layer_data.append(message)
|
||||||
|
|
||||||
## Called when an optimized sliced layer data message is received from the engine.
|
|
||||||
#
|
|
||||||
# \param message The protobuf message containing sliced layer data.
|
|
||||||
def _onOptimizedLayerMessage(self, message: Arcus.PythonMessage) -> None:
|
def _onOptimizedLayerMessage(self, message: Arcus.PythonMessage) -> None:
|
||||||
|
"""Called when an optimized sliced layer data message is received from the engine.
|
||||||
|
|
||||||
|
:param message: The protobuf message containing sliced layer data.
|
||||||
|
"""
|
||||||
|
|
||||||
if self._start_slice_job_build_plate is not None:
|
if self._start_slice_job_build_plate is not None:
|
||||||
if self._start_slice_job_build_plate not in self._stored_optimized_layer_data:
|
if self._start_slice_job_build_plate not in self._stored_optimized_layer_data:
|
||||||
self._stored_optimized_layer_data[self._start_slice_job_build_plate] = []
|
self._stored_optimized_layer_data[self._start_slice_job_build_plate] = []
|
||||||
self._stored_optimized_layer_data[self._start_slice_job_build_plate].append(message)
|
self._stored_optimized_layer_data[self._start_slice_job_build_plate].append(message)
|
||||||
|
|
||||||
## Called when a progress message is received from the engine.
|
|
||||||
#
|
|
||||||
# \param message The protobuf message containing the slicing progress.
|
|
||||||
def _onProgressMessage(self, message: Arcus.PythonMessage) -> None:
|
def _onProgressMessage(self, message: Arcus.PythonMessage) -> None:
|
||||||
|
"""Called when a progress message is received from the engine.
|
||||||
|
|
||||||
|
:param message: The protobuf message containing the slicing progress.
|
||||||
|
"""
|
||||||
|
|
||||||
self.processingProgress.emit(message.amount)
|
self.processingProgress.emit(message.amount)
|
||||||
self.setState(BackendState.Processing)
|
self.setState(BackendState.Processing)
|
||||||
|
|
||||||
|
@ -653,10 +683,12 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
else:
|
else:
|
||||||
self._change_timer.start()
|
self._change_timer.start()
|
||||||
|
|
||||||
## Called when the engine sends a message that slicing is finished.
|
|
||||||
#
|
|
||||||
# \param message The protobuf message signalling that slicing is finished.
|
|
||||||
def _onSlicingFinishedMessage(self, message: Arcus.PythonMessage) -> None:
|
def _onSlicingFinishedMessage(self, message: Arcus.PythonMessage) -> None:
|
||||||
|
"""Called when the engine sends a message that slicing is finished.
|
||||||
|
|
||||||
|
:param message: The protobuf message signalling that slicing is finished.
|
||||||
|
"""
|
||||||
|
|
||||||
self.setState(BackendState.Done)
|
self.setState(BackendState.Done)
|
||||||
self.processingProgress.emit(1.0)
|
self.processingProgress.emit(1.0)
|
||||||
|
|
||||||
|
@ -698,27 +730,32 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
self.enableTimer() # manually enable timer to be able to invoke slice, also when in manual slice mode
|
self.enableTimer() # manually enable timer to be able to invoke slice, also when in manual slice mode
|
||||||
self._invokeSlice()
|
self._invokeSlice()
|
||||||
|
|
||||||
## Called when a g-code message is received from the engine.
|
|
||||||
#
|
|
||||||
# \param message The protobuf message containing g-code, encoded as UTF-8.
|
|
||||||
def _onGCodeLayerMessage(self, message: Arcus.PythonMessage) -> None:
|
def _onGCodeLayerMessage(self, message: Arcus.PythonMessage) -> None:
|
||||||
|
"""Called when a g-code message is received from the engine.
|
||||||
|
|
||||||
|
:param message: The protobuf message containing g-code, encoded as UTF-8.
|
||||||
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._scene.gcode_dict[self._start_slice_job_build_plate].append(message.data.decode("utf-8", "replace")) #type: ignore #Because we generate this attribute dynamically.
|
self._scene.gcode_dict[self._start_slice_job_build_plate].append(message.data.decode("utf-8", "replace")) #type: ignore #Because we generate this attribute dynamically.
|
||||||
except KeyError: # Can occur if the g-code has been cleared while a slice message is still arriving from the other end.
|
except KeyError: # Can occur if the g-code has been cleared while a slice message is still arriving from the other end.
|
||||||
pass # Throw the message away.
|
pass # Throw the message away.
|
||||||
|
|
||||||
## Called when a g-code prefix message is received from the engine.
|
|
||||||
#
|
|
||||||
# \param message The protobuf message containing the g-code prefix,
|
|
||||||
# encoded as UTF-8.
|
|
||||||
def _onGCodePrefixMessage(self, message: Arcus.PythonMessage) -> None:
|
def _onGCodePrefixMessage(self, message: Arcus.PythonMessage) -> None:
|
||||||
|
"""Called when a g-code prefix message is received from the engine.
|
||||||
|
|
||||||
|
:param message: The protobuf message containing the g-code prefix,
|
||||||
|
encoded as UTF-8.
|
||||||
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._scene.gcode_dict[self._start_slice_job_build_plate].insert(0, message.data.decode("utf-8", "replace")) #type: ignore #Because we generate this attribute dynamically.
|
self._scene.gcode_dict[self._start_slice_job_build_plate].insert(0, message.data.decode("utf-8", "replace")) #type: ignore #Because we generate this attribute dynamically.
|
||||||
except KeyError: # Can occur if the g-code has been cleared while a slice message is still arriving from the other end.
|
except KeyError: # Can occur if the g-code has been cleared while a slice message is still arriving from the other end.
|
||||||
pass # Throw the message away.
|
pass # Throw the message away.
|
||||||
|
|
||||||
## Creates a new socket connection.
|
|
||||||
def _createSocket(self, protocol_file: str = None) -> None:
|
def _createSocket(self, protocol_file: str = None) -> None:
|
||||||
|
"""Creates a new socket connection."""
|
||||||
|
|
||||||
if not protocol_file:
|
if not protocol_file:
|
||||||
plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId())
|
plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId())
|
||||||
if not plugin_path:
|
if not plugin_path:
|
||||||
|
@ -728,10 +765,12 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
super()._createSocket(protocol_file)
|
super()._createSocket(protocol_file)
|
||||||
self._engine_is_fresh = True
|
self._engine_is_fresh = True
|
||||||
|
|
||||||
## Called when anything has changed to the stuff that needs to be sliced.
|
|
||||||
#
|
|
||||||
# This indicates that we should probably re-slice soon.
|
|
||||||
def _onChanged(self, *args: Any, **kwargs: Any) -> None:
|
def _onChanged(self, *args: Any, **kwargs: Any) -> None:
|
||||||
|
"""Called when anything has changed to the stuff that needs to be sliced.
|
||||||
|
|
||||||
|
This indicates that we should probably re-slice soon.
|
||||||
|
"""
|
||||||
|
|
||||||
self.needsSlicing()
|
self.needsSlicing()
|
||||||
if self._use_timer:
|
if self._use_timer:
|
||||||
# if the error check is scheduled, wait for the error check finish signal to trigger auto-slice,
|
# if the error check is scheduled, wait for the error check finish signal to trigger auto-slice,
|
||||||
|
@ -745,11 +784,13 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
else:
|
else:
|
||||||
self._change_timer.start()
|
self._change_timer.start()
|
||||||
|
|
||||||
## Called when a print time message is received from the engine.
|
|
||||||
#
|
|
||||||
# \param message The protobuf message containing the print time per feature and
|
|
||||||
# material amount per extruder
|
|
||||||
def _onPrintTimeMaterialEstimates(self, message: Arcus.PythonMessage) -> None:
|
def _onPrintTimeMaterialEstimates(self, message: Arcus.PythonMessage) -> None:
|
||||||
|
"""Called when a print time message is received from the engine.
|
||||||
|
|
||||||
|
:param message: The protobuf message containing the print time per feature and
|
||||||
|
material amount per extruder
|
||||||
|
"""
|
||||||
|
|
||||||
material_amounts = []
|
material_amounts = []
|
||||||
for index in range(message.repeatedMessageCount("materialEstimates")):
|
for index in range(message.repeatedMessageCount("materialEstimates")):
|
||||||
material_amounts.append(message.getRepeatedMessage("materialEstimates", index).material_amount)
|
material_amounts.append(message.getRepeatedMessage("materialEstimates", index).material_amount)
|
||||||
|
@ -757,10 +798,12 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
times = self._parseMessagePrintTimes(message)
|
times = self._parseMessagePrintTimes(message)
|
||||||
self.printDurationMessage.emit(self._start_slice_job_build_plate, times, material_amounts)
|
self.printDurationMessage.emit(self._start_slice_job_build_plate, times, material_amounts)
|
||||||
|
|
||||||
## Called for parsing message to retrieve estimated time per feature
|
|
||||||
#
|
|
||||||
# \param message The protobuf message containing the print time per feature
|
|
||||||
def _parseMessagePrintTimes(self, message: Arcus.PythonMessage) -> Dict[str, float]:
|
def _parseMessagePrintTimes(self, message: Arcus.PythonMessage) -> Dict[str, float]:
|
||||||
|
"""Called for parsing message to retrieve estimated time per feature
|
||||||
|
|
||||||
|
:param message: The protobuf message containing the print time per feature
|
||||||
|
"""
|
||||||
|
|
||||||
result = {
|
result = {
|
||||||
"inset_0": message.time_inset_0,
|
"inset_0": message.time_inset_0,
|
||||||
"inset_x": message.time_inset_x,
|
"inset_x": message.time_inset_x,
|
||||||
|
@ -777,19 +820,22 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
|
||||||
## Called when the back-end connects to the front-end.
|
|
||||||
def _onBackendConnected(self) -> None:
|
def _onBackendConnected(self) -> None:
|
||||||
|
"""Called when the back-end connects to the front-end."""
|
||||||
|
|
||||||
if self._restart:
|
if self._restart:
|
||||||
self._restart = False
|
self._restart = False
|
||||||
self._onChanged()
|
self._onChanged()
|
||||||
|
|
||||||
## Called when the user starts using some tool.
|
|
||||||
#
|
|
||||||
# When the user starts using a tool, we should pause slicing to prevent
|
|
||||||
# continuously slicing while the user is dragging some tool handle.
|
|
||||||
#
|
|
||||||
# \param tool The tool that the user is using.
|
|
||||||
def _onToolOperationStarted(self, tool: Tool) -> None:
|
def _onToolOperationStarted(self, tool: Tool) -> None:
|
||||||
|
"""Called when the user starts using some tool.
|
||||||
|
|
||||||
|
When the user starts using a tool, we should pause slicing to prevent
|
||||||
|
continuously slicing while the user is dragging some tool handle.
|
||||||
|
|
||||||
|
:param tool: The tool that the user is using.
|
||||||
|
"""
|
||||||
|
|
||||||
self._tool_active = True # Do not react on scene change
|
self._tool_active = True # Do not react on scene change
|
||||||
self.disableTimer()
|
self.disableTimer()
|
||||||
# Restart engine as soon as possible, we know we want to slice afterwards
|
# Restart engine as soon as possible, we know we want to slice afterwards
|
||||||
|
@ -797,12 +843,14 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
self._terminate()
|
self._terminate()
|
||||||
self._createSocket()
|
self._createSocket()
|
||||||
|
|
||||||
## Called when the user stops using some tool.
|
|
||||||
#
|
|
||||||
# This indicates that we can safely start slicing again.
|
|
||||||
#
|
|
||||||
# \param tool The tool that the user was using.
|
|
||||||
def _onToolOperationStopped(self, tool: Tool) -> None:
|
def _onToolOperationStopped(self, tool: Tool) -> None:
|
||||||
|
"""Called when the user stops using some tool.
|
||||||
|
|
||||||
|
This indicates that we can safely start slicing again.
|
||||||
|
|
||||||
|
:param tool: The tool that the user was using.
|
||||||
|
"""
|
||||||
|
|
||||||
self._tool_active = False # React on scene change again
|
self._tool_active = False # React on scene change again
|
||||||
self.determineAutoSlicing() # Switch timer on if appropriate
|
self.determineAutoSlicing() # Switch timer on if appropriate
|
||||||
# Process all the postponed scene changes
|
# Process all the postponed scene changes
|
||||||
|
@ -816,8 +864,9 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
self._process_layers_job.finished.connect(self._onProcessLayersFinished)
|
self._process_layers_job.finished.connect(self._onProcessLayersFinished)
|
||||||
self._process_layers_job.start()
|
self._process_layers_job.start()
|
||||||
|
|
||||||
## Called when the user changes the active view mode.
|
|
||||||
def _onActiveViewChanged(self) -> None:
|
def _onActiveViewChanged(self) -> None:
|
||||||
|
"""Called when the user changes the active view mode."""
|
||||||
|
|
||||||
view = self._application.getController().getActiveView()
|
view = self._application.getController().getActiveView()
|
||||||
if view:
|
if view:
|
||||||
active_build_plate = self._application.getMultiBuildPlateModel().activeBuildPlate
|
active_build_plate = self._application.getMultiBuildPlateModel().activeBuildPlate
|
||||||
|
@ -835,17 +884,20 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
else:
|
else:
|
||||||
self._layer_view_active = False
|
self._layer_view_active = False
|
||||||
|
|
||||||
## Called when the back-end self-terminates.
|
|
||||||
#
|
|
||||||
# We should reset our state and start listening for new connections.
|
|
||||||
def _onBackendQuit(self) -> None:
|
def _onBackendQuit(self) -> None:
|
||||||
|
"""Called when the back-end self-terminates.
|
||||||
|
|
||||||
|
We should reset our state and start listening for new connections.
|
||||||
|
"""
|
||||||
|
|
||||||
if not self._restart:
|
if not self._restart:
|
||||||
if self._process: # type: ignore
|
if self._process: # type: ignore
|
||||||
Logger.log("d", "Backend quit with return code %s. Resetting process and socket.", self._process.wait()) # type: ignore
|
Logger.log("d", "Backend quit with return code %s. Resetting process and socket.", self._process.wait()) # type: ignore
|
||||||
self._process = None # type: ignore
|
self._process = None # type: ignore
|
||||||
|
|
||||||
## Called when the global container stack changes
|
|
||||||
def _onGlobalStackChanged(self) -> None:
|
def _onGlobalStackChanged(self) -> None:
|
||||||
|
"""Called when the global container stack changes"""
|
||||||
|
|
||||||
if self._global_container_stack:
|
if self._global_container_stack:
|
||||||
self._global_container_stack.propertyChanged.disconnect(self._onSettingChanged)
|
self._global_container_stack.propertyChanged.disconnect(self._onSettingChanged)
|
||||||
self._global_container_stack.containersChanged.disconnect(self._onChanged)
|
self._global_container_stack.containersChanged.disconnect(self._onChanged)
|
||||||
|
@ -874,15 +926,18 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
Logger.log("d", "See if there is more to slice(2)...")
|
Logger.log("d", "See if there is more to slice(2)...")
|
||||||
self._invokeSlice()
|
self._invokeSlice()
|
||||||
|
|
||||||
## Connect slice function to timer.
|
|
||||||
def enableTimer(self) -> None:
|
def enableTimer(self) -> None:
|
||||||
|
"""Connect slice function to timer."""
|
||||||
|
|
||||||
if not self._use_timer:
|
if not self._use_timer:
|
||||||
self._change_timer.timeout.connect(self.slice)
|
self._change_timer.timeout.connect(self.slice)
|
||||||
self._use_timer = True
|
self._use_timer = True
|
||||||
|
|
||||||
## Disconnect slice function from timer.
|
|
||||||
# This means that slicing will not be triggered automatically
|
|
||||||
def disableTimer(self) -> None:
|
def disableTimer(self) -> None:
|
||||||
|
"""Disconnect slice function from timer.
|
||||||
|
|
||||||
|
This means that slicing will not be triggered automatically
|
||||||
|
"""
|
||||||
if self._use_timer:
|
if self._use_timer:
|
||||||
self._use_timer = False
|
self._use_timer = False
|
||||||
self._change_timer.timeout.disconnect(self.slice)
|
self._change_timer.timeout.disconnect(self.slice)
|
||||||
|
@ -894,8 +949,9 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
if auto_slice:
|
if auto_slice:
|
||||||
self._change_timer.start()
|
self._change_timer.start()
|
||||||
|
|
||||||
## Tickle the backend so in case of auto slicing, it starts the timer.
|
|
||||||
def tickle(self) -> None:
|
def tickle(self) -> None:
|
||||||
|
"""Tickle the backend so in case of auto slicing, it starts the timer."""
|
||||||
|
|
||||||
if self._use_timer:
|
if self._use_timer:
|
||||||
self._change_timer.start()
|
self._change_timer.start()
|
||||||
|
|
||||||
|
|
|
@ -28,10 +28,12 @@ from cura.Machines.Models.ExtrudersModel import ExtrudersModel
|
||||||
catalog = i18nCatalog("cura")
|
catalog = i18nCatalog("cura")
|
||||||
|
|
||||||
|
|
||||||
## Return a 4-tuple with floats 0-1 representing the html color code
|
|
||||||
#
|
|
||||||
# \param color_code html color code, i.e. "#FF0000" -> red
|
|
||||||
def colorCodeToRGBA(color_code):
|
def colorCodeToRGBA(color_code):
|
||||||
|
"""Return a 4-tuple with floats 0-1 representing the html color code
|
||||||
|
|
||||||
|
:param color_code: html color code, i.e. "#FF0000" -> red
|
||||||
|
"""
|
||||||
|
|
||||||
if color_code is None:
|
if color_code is None:
|
||||||
Logger.log("w", "Unable to convert color code, returning default")
|
Logger.log("w", "Unable to convert color code, returning default")
|
||||||
return [0, 0, 0, 1]
|
return [0, 0, 0, 1]
|
||||||
|
@ -51,13 +53,15 @@ class ProcessSlicedLayersJob(Job):
|
||||||
self._abort_requested = False
|
self._abort_requested = False
|
||||||
self._build_plate_number = None
|
self._build_plate_number = None
|
||||||
|
|
||||||
## Aborts the processing of layers.
|
|
||||||
#
|
|
||||||
# This abort is made on a best-effort basis, meaning that the actual
|
|
||||||
# job thread will check once in a while to see whether an abort is
|
|
||||||
# requested and then stop processing by itself. There is no guarantee
|
|
||||||
# that the abort will stop the job any time soon or even at all.
|
|
||||||
def abort(self):
|
def abort(self):
|
||||||
|
"""Aborts the processing of layers.
|
||||||
|
|
||||||
|
This abort is made on a best-effort basis, meaning that the actual
|
||||||
|
job thread will check once in a while to see whether an abort is
|
||||||
|
requested and then stop processing by itself. There is no guarantee
|
||||||
|
that the abort will stop the job any time soon or even at all.
|
||||||
|
"""
|
||||||
|
|
||||||
self._abort_requested = True
|
self._abort_requested = True
|
||||||
|
|
||||||
def setBuildPlate(self, new_value):
|
def setBuildPlate(self, new_value):
|
||||||
|
|
|
@ -40,8 +40,9 @@ class StartJobResult(IntEnum):
|
||||||
ObjectsWithDisabledExtruder = 8
|
ObjectsWithDisabledExtruder = 8
|
||||||
|
|
||||||
|
|
||||||
## Formatter class that handles token expansion in start/end gcode
|
|
||||||
class GcodeStartEndFormatter(Formatter):
|
class GcodeStartEndFormatter(Formatter):
|
||||||
|
"""Formatter class that handles token expansion in start/end gcode"""
|
||||||
|
|
||||||
def __init__(self, default_extruder_nr: int = -1) -> None:
|
def __init__(self, default_extruder_nr: int = -1) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._default_extruder_nr = default_extruder_nr
|
self._default_extruder_nr = default_extruder_nr
|
||||||
|
@ -82,8 +83,9 @@ class GcodeStartEndFormatter(Formatter):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
## Job class that builds up the message of scene data to send to CuraEngine.
|
|
||||||
class StartSliceJob(Job):
|
class StartSliceJob(Job):
|
||||||
|
"""Job class that builds up the message of scene data to send to CuraEngine."""
|
||||||
|
|
||||||
def __init__(self, slice_message: Arcus.PythonMessage) -> None:
|
def __init__(self, slice_message: Arcus.PythonMessage) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
|
@ -100,9 +102,11 @@ class StartSliceJob(Job):
|
||||||
def setBuildPlate(self, build_plate_number: int) -> None:
|
def setBuildPlate(self, build_plate_number: int) -> None:
|
||||||
self._build_plate_number = build_plate_number
|
self._build_plate_number = build_plate_number
|
||||||
|
|
||||||
## Check if a stack has any errors.
|
|
||||||
## returns true if it has errors, false otherwise.
|
|
||||||
def _checkStackForErrors(self, stack: ContainerStack) -> bool:
|
def _checkStackForErrors(self, stack: ContainerStack) -> bool:
|
||||||
|
"""Check if a stack has any errors."""
|
||||||
|
|
||||||
|
"""returns true if it has errors, false otherwise."""
|
||||||
|
|
||||||
if stack is None:
|
if stack is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -119,8 +123,9 @@ class StartSliceJob(Job):
|
||||||
Job.yieldThread()
|
Job.yieldThread()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
## Runs the job that initiates the slicing.
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
|
"""Runs the job that initiates the slicing."""
|
||||||
|
|
||||||
if self._build_plate_number is None:
|
if self._build_plate_number is None:
|
||||||
self.setResult(StartJobResult.Error)
|
self.setResult(StartJobResult.Error)
|
||||||
return
|
return
|
||||||
|
@ -323,14 +328,14 @@ class StartSliceJob(Job):
|
||||||
def setIsCancelled(self, value: bool):
|
def setIsCancelled(self, value: bool):
|
||||||
self._is_cancelled = value
|
self._is_cancelled = value
|
||||||
|
|
||||||
## Creates a dictionary of tokens to replace in g-code pieces.
|
|
||||||
#
|
|
||||||
# This indicates what should be replaced in the start and end g-codes.
|
|
||||||
# \param stack The stack to get the settings from to replace the tokens
|
|
||||||
# with.
|
|
||||||
# \return A dictionary of replacement tokens to the values they should be
|
|
||||||
# replaced with.
|
|
||||||
def _buildReplacementTokens(self, stack: ContainerStack) -> Dict[str, Any]:
|
def _buildReplacementTokens(self, stack: ContainerStack) -> Dict[str, Any]:
|
||||||
|
"""Creates a dictionary of tokens to replace in g-code pieces.
|
||||||
|
|
||||||
|
This indicates what should be replaced in the start and end g-codes.
|
||||||
|
:param stack: The stack to get the settings from to replace the tokens with.
|
||||||
|
:return: A dictionary of replacement tokens to the values they should be replaced with.
|
||||||
|
"""
|
||||||
|
|
||||||
result = {}
|
result = {}
|
||||||
for key in stack.getAllKeys():
|
for key in stack.getAllKeys():
|
||||||
value = stack.getProperty(key, "value")
|
value = stack.getProperty(key, "value")
|
||||||
|
@ -358,10 +363,12 @@ class StartSliceJob(Job):
|
||||||
extruder_nr = extruder_stack.getProperty("extruder_nr", "value")
|
extruder_nr = extruder_stack.getProperty("extruder_nr", "value")
|
||||||
self._all_extruders_settings[str(extruder_nr)] = self._buildReplacementTokens(extruder_stack)
|
self._all_extruders_settings[str(extruder_nr)] = self._buildReplacementTokens(extruder_stack)
|
||||||
|
|
||||||
## Replace setting tokens in a piece of g-code.
|
|
||||||
# \param value A piece of g-code to replace tokens in.
|
|
||||||
# \param default_extruder_nr Stack nr to use when no stack nr is specified, defaults to the global stack
|
|
||||||
def _expandGcodeTokens(self, value: str, default_extruder_nr: int = -1) -> str:
|
def _expandGcodeTokens(self, value: str, default_extruder_nr: int = -1) -> str:
|
||||||
|
"""Replace setting tokens in a piece of g-code.
|
||||||
|
|
||||||
|
:param value: A piece of g-code to replace tokens in.
|
||||||
|
:param default_extruder_nr: Stack nr to use when no stack nr is specified, defaults to the global stack
|
||||||
|
"""
|
||||||
if not self._all_extruders_settings:
|
if not self._all_extruders_settings:
|
||||||
self._cacheAllExtruderSettings()
|
self._cacheAllExtruderSettings()
|
||||||
|
|
||||||
|
@ -377,8 +384,9 @@ class StartSliceJob(Job):
|
||||||
Logger.logException("w", "Unable to do token replacement on start/end g-code")
|
Logger.logException("w", "Unable to do token replacement on start/end g-code")
|
||||||
return str(value)
|
return str(value)
|
||||||
|
|
||||||
## Create extruder message from stack
|
|
||||||
def _buildExtruderMessage(self, stack: ContainerStack) -> None:
|
def _buildExtruderMessage(self, stack: ContainerStack) -> None:
|
||||||
|
"""Create extruder message from stack"""
|
||||||
|
|
||||||
message = self._slice_message.addRepeatedMessage("extruders")
|
message = self._slice_message.addRepeatedMessage("extruders")
|
||||||
message.id = int(stack.getMetaDataEntry("position"))
|
message.id = int(stack.getMetaDataEntry("position"))
|
||||||
if not self._all_extruders_settings:
|
if not self._all_extruders_settings:
|
||||||
|
@ -407,11 +415,13 @@ class StartSliceJob(Job):
|
||||||
setting.value = str(value).encode("utf-8")
|
setting.value = str(value).encode("utf-8")
|
||||||
Job.yieldThread()
|
Job.yieldThread()
|
||||||
|
|
||||||
## Sends all global settings to the engine.
|
|
||||||
#
|
|
||||||
# The settings are taken from the global stack. This does not include any
|
|
||||||
# per-extruder settings or per-object settings.
|
|
||||||
def _buildGlobalSettingsMessage(self, stack: ContainerStack) -> None:
|
def _buildGlobalSettingsMessage(self, stack: ContainerStack) -> None:
|
||||||
|
"""Sends all global settings to the engine.
|
||||||
|
|
||||||
|
The settings are taken from the global stack. This does not include any
|
||||||
|
per-extruder settings or per-object settings.
|
||||||
|
"""
|
||||||
|
|
||||||
if not self._all_extruders_settings:
|
if not self._all_extruders_settings:
|
||||||
self._cacheAllExtruderSettings()
|
self._cacheAllExtruderSettings()
|
||||||
|
|
||||||
|
@ -445,15 +455,16 @@ class StartSliceJob(Job):
|
||||||
setting_message.value = str(value).encode("utf-8")
|
setting_message.value = str(value).encode("utf-8")
|
||||||
Job.yieldThread()
|
Job.yieldThread()
|
||||||
|
|
||||||
## Sends for some settings which extruder they should fallback to if not
|
|
||||||
# set.
|
|
||||||
#
|
|
||||||
# This is only set for settings that have the limit_to_extruder
|
|
||||||
# property.
|
|
||||||
#
|
|
||||||
# \param stack The global stack with all settings, from which to read the
|
|
||||||
# limit_to_extruder property.
|
|
||||||
def _buildGlobalInheritsStackMessage(self, stack: ContainerStack) -> None:
|
def _buildGlobalInheritsStackMessage(self, stack: ContainerStack) -> None:
|
||||||
|
"""Sends for some settings which extruder they should fallback to if not set.
|
||||||
|
|
||||||
|
This is only set for settings that have the limit_to_extruder
|
||||||
|
property.
|
||||||
|
|
||||||
|
:param stack: The global stack with all settings, from which to read the
|
||||||
|
limit_to_extruder property.
|
||||||
|
"""
|
||||||
|
|
||||||
for key in stack.getAllKeys():
|
for key in stack.getAllKeys():
|
||||||
extruder_position = int(round(float(stack.getProperty(key, "limit_to_extruder"))))
|
extruder_position = int(round(float(stack.getProperty(key, "limit_to_extruder"))))
|
||||||
if extruder_position >= 0: # Set to a specific extruder.
|
if extruder_position >= 0: # Set to a specific extruder.
|
||||||
|
@ -462,10 +473,13 @@ class StartSliceJob(Job):
|
||||||
setting_extruder.extruder = extruder_position
|
setting_extruder.extruder = extruder_position
|
||||||
Job.yieldThread()
|
Job.yieldThread()
|
||||||
|
|
||||||
## Check if a node has per object settings and ensure that they are set correctly in the message
|
|
||||||
# \param node Node to check.
|
|
||||||
# \param message object_lists message to put the per object settings in
|
|
||||||
def _handlePerObjectSettings(self, node: CuraSceneNode, message: Arcus.PythonMessage):
|
def _handlePerObjectSettings(self, node: CuraSceneNode, message: Arcus.PythonMessage):
|
||||||
|
"""Check if a node has per object settings and ensure that they are set correctly in the message
|
||||||
|
|
||||||
|
:param node: Node to check.
|
||||||
|
:param message: object_lists message to put the per object settings in
|
||||||
|
"""
|
||||||
|
|
||||||
stack = node.callDecoration("getStack")
|
stack = node.callDecoration("getStack")
|
||||||
|
|
||||||
# Check if the node has a stack attached to it and the stack has any settings in the top container.
|
# Check if the node has a stack attached to it and the stack has any settings in the top container.
|
||||||
|
@ -501,10 +515,13 @@ class StartSliceJob(Job):
|
||||||
|
|
||||||
Job.yieldThread()
|
Job.yieldThread()
|
||||||
|
|
||||||
## Recursive function to put all settings that require each other for value changes in a list
|
|
||||||
# \param relations_set Set of keys of settings that are influenced
|
|
||||||
# \param relations list of relation objects that need to be checked.
|
|
||||||
def _addRelations(self, relations_set: Set[str], relations: List[SettingRelation]):
|
def _addRelations(self, relations_set: Set[str], relations: List[SettingRelation]):
|
||||||
|
"""Recursive function to put all settings that require each other for value changes in a list
|
||||||
|
|
||||||
|
:param relations_set: Set of keys of settings that are influenced
|
||||||
|
:param relations: list of relation objects that need to be checked.
|
||||||
|
"""
|
||||||
|
|
||||||
for relation in filter(lambda r: r.role == "value" or r.role == "limit_to_extruder", relations):
|
for relation in filter(lambda r: r.role == "value" or r.role == "limit_to_extruder", relations):
|
||||||
if relation.type == RelationType.RequiresTarget:
|
if relation.type == RelationType.RequiresTarget:
|
||||||
continue
|
continue
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue