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):
|
||||
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:
|
||||
"""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__()
|
||||
# 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.
|
||||
|
@ -177,18 +179,22 @@ class CuraEngineBackend(QObject, Backend):
|
|||
self._machine_error_checker = self._application.getMachineErrorChecker()
|
||||
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:
|
||||
"""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
|
||||
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]:
|
||||
"""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), ""]
|
||||
|
||||
parser = argparse.ArgumentParser(prog = "cura", add_help = False)
|
||||
|
@ -199,17 +205,18 @@ class CuraEngineBackend(QObject, Backend):
|
|||
|
||||
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()
|
||||
|
||||
## Emitted when the slicing process starts.
|
||||
"""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.
|
||||
"""
|
||||
slicingStarted = Signal()
|
||||
"""Emitted when the slicing process starts."""
|
||||
|
||||
## Emitted when the slicing process is aborted forcefully.
|
||||
slicingCancelled = Signal()
|
||||
"""Emitted when the slicing process is aborted forcefully."""
|
||||
|
||||
@pyqtSlot()
|
||||
def stopSlicing(self) -> None:
|
||||
|
@ -226,14 +233,16 @@ class CuraEngineBackend(QObject, Backend):
|
|||
if self._error_message:
|
||||
self._error_message.hide()
|
||||
|
||||
## Manually triggers a reslice
|
||||
@pyqtSlot()
|
||||
def forceSlice(self) -> None:
|
||||
"""Manually triggers a reslice"""
|
||||
|
||||
self.markSliceAll()
|
||||
self.slice()
|
||||
|
||||
## Perform a slice of the scene.
|
||||
def slice(self) -> None:
|
||||
"""Perform a slice of the scene."""
|
||||
|
||||
Logger.log("i", "Starting to slice...")
|
||||
self._slice_start_time = time()
|
||||
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.finished.connect(self._onStartSliceCompleted)
|
||||
|
||||
## Terminate the engine process.
|
||||
# Start the engine process by calling _createSocket()
|
||||
def _terminate(self) -> None:
|
||||
"""Terminate the engine process.
|
||||
|
||||
Start the engine process by calling _createSocket()
|
||||
"""
|
||||
self._slicing = False
|
||||
self._stored_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.
|
||||
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:
|
||||
"""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:
|
||||
self._error_message.hide()
|
||||
|
||||
|
@ -443,11 +456,13 @@ class CuraEngineBackend(QObject, Backend):
|
|||
if 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:
|
||||
"""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
|
||||
self._is_disabled = False
|
||||
|
||||
|
@ -472,8 +487,9 @@ class CuraEngineBackend(QObject, Backend):
|
|||
self.disableTimer()
|
||||
return False
|
||||
|
||||
## Return a dict with number of objects per build plate
|
||||
def _numObjectsPerBuildPlate(self) -> Dict[int, int]:
|
||||
"""Return a dict with number of objects per build plate"""
|
||||
|
||||
num_objects = defaultdict(int) #type: Dict[int, int]
|
||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||
# Only count sliceable objects
|
||||
|
@ -483,12 +499,14 @@ class CuraEngineBackend(QObject, Backend):
|
|||
num_objects[build_plate_number] += 1
|
||||
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:
|
||||
"""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"):
|
||||
return
|
||||
|
||||
|
@ -536,10 +554,12 @@ class CuraEngineBackend(QObject, Backend):
|
|||
|
||||
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:
|
||||
"""Called when an error occurs in the socket connection towards the engine.
|
||||
|
||||
:param error: The exception that occurred.
|
||||
"""
|
||||
|
||||
if self._application.isShuttingDown():
|
||||
return
|
||||
|
||||
|
@ -567,8 +587,9 @@ class CuraEngineBackend(QObject, Backend):
|
|||
break
|
||||
return has_slicable
|
||||
|
||||
## Remove old layer data (if any)
|
||||
def _clearLayerData(self, build_plate_numbers: Set = None) -> None:
|
||||
"""Remove old layer data (if any)"""
|
||||
|
||||
# Clear out any old gcode
|
||||
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:
|
||||
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:
|
||||
"""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
|
||||
# gcode. This can break Gcode file loading if it tries to remove it afterwards.
|
||||
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.
|
||||
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:
|
||||
"""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.
|
||||
self.needsSlicing()
|
||||
self._onChanged()
|
||||
|
@ -618,25 +642,31 @@ class CuraEngineBackend(QObject, Backend):
|
|||
self.needsSlicing()
|
||||
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:
|
||||
"""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)
|
||||
|
||||
## 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:
|
||||
"""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 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].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:
|
||||
"""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.setState(BackendState.Processing)
|
||||
|
||||
|
@ -653,10 +683,12 @@ class CuraEngineBackend(QObject, Backend):
|
|||
else:
|
||||
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:
|
||||
"""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.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._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:
|
||||
"""Called when a g-code message is received from the engine.
|
||||
|
||||
:param message: The protobuf message containing g-code, encoded as UTF-8.
|
||||
"""
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
## 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:
|
||||
"""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:
|
||||
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.
|
||||
pass # Throw the message away.
|
||||
|
||||
## Creates a new socket connection.
|
||||
def _createSocket(self, protocol_file: str = None) -> None:
|
||||
"""Creates a new socket connection."""
|
||||
|
||||
if not protocol_file:
|
||||
plugin_path = PluginRegistry.getInstance().getPluginPath(self.getPluginId())
|
||||
if not plugin_path:
|
||||
|
@ -728,10 +765,12 @@ class CuraEngineBackend(QObject, Backend):
|
|||
super()._createSocket(protocol_file)
|
||||
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:
|
||||
"""Called when anything has changed to the stuff that needs to be sliced.
|
||||
|
||||
This indicates that we should probably re-slice soon.
|
||||
"""
|
||||
|
||||
self.needsSlicing()
|
||||
if self._use_timer:
|
||||
# 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:
|
||||
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:
|
||||
"""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 = []
|
||||
for index in range(message.repeatedMessageCount("materialEstimates")):
|
||||
material_amounts.append(message.getRepeatedMessage("materialEstimates", index).material_amount)
|
||||
|
@ -757,10 +798,12 @@ class CuraEngineBackend(QObject, Backend):
|
|||
times = self._parseMessagePrintTimes(message)
|
||||
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]:
|
||||
"""Called for parsing message to retrieve estimated time per feature
|
||||
|
||||
:param message: The protobuf message containing the print time per feature
|
||||
"""
|
||||
|
||||
result = {
|
||||
"inset_0": message.time_inset_0,
|
||||
"inset_x": message.time_inset_x,
|
||||
|
@ -777,19 +820,22 @@ class CuraEngineBackend(QObject, Backend):
|
|||
}
|
||||
return result
|
||||
|
||||
## Called when the back-end connects to the front-end.
|
||||
def _onBackendConnected(self) -> None:
|
||||
"""Called when the back-end connects to the front-end."""
|
||||
|
||||
if self._restart:
|
||||
self._restart = False
|
||||
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:
|
||||
"""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.disableTimer()
|
||||
# Restart engine as soon as possible, we know we want to slice afterwards
|
||||
|
@ -797,12 +843,14 @@ class CuraEngineBackend(QObject, Backend):
|
|||
self._terminate()
|
||||
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:
|
||||
"""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.determineAutoSlicing() # Switch timer on if appropriate
|
||||
# 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.start()
|
||||
|
||||
## Called when the user changes the active view mode.
|
||||
def _onActiveViewChanged(self) -> None:
|
||||
"""Called when the user changes the active view mode."""
|
||||
|
||||
view = self._application.getController().getActiveView()
|
||||
if view:
|
||||
active_build_plate = self._application.getMultiBuildPlateModel().activeBuildPlate
|
||||
|
@ -835,17 +884,20 @@ class CuraEngineBackend(QObject, Backend):
|
|||
else:
|
||||
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:
|
||||
"""Called when the back-end self-terminates.
|
||||
|
||||
We should reset our state and start listening for new connections.
|
||||
"""
|
||||
|
||||
if not self._restart:
|
||||
if self._process: # 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
|
||||
|
||||
## Called when the global container stack changes
|
||||
def _onGlobalStackChanged(self) -> None:
|
||||
"""Called when the global container stack changes"""
|
||||
|
||||
if self._global_container_stack:
|
||||
self._global_container_stack.propertyChanged.disconnect(self._onSettingChanged)
|
||||
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)...")
|
||||
self._invokeSlice()
|
||||
|
||||
## Connect slice function to timer.
|
||||
def enableTimer(self) -> None:
|
||||
"""Connect slice function to timer."""
|
||||
|
||||
if not self._use_timer:
|
||||
self._change_timer.timeout.connect(self.slice)
|
||||
self._use_timer = True
|
||||
|
||||
## Disconnect slice function from timer.
|
||||
# This means that slicing will not be triggered automatically
|
||||
def disableTimer(self) -> None:
|
||||
"""Disconnect slice function from timer.
|
||||
|
||||
This means that slicing will not be triggered automatically
|
||||
"""
|
||||
if self._use_timer:
|
||||
self._use_timer = False
|
||||
self._change_timer.timeout.disconnect(self.slice)
|
||||
|
@ -894,8 +949,9 @@ class CuraEngineBackend(QObject, Backend):
|
|||
if auto_slice:
|
||||
self._change_timer.start()
|
||||
|
||||
## Tickle the backend so in case of auto slicing, it starts the timer.
|
||||
def tickle(self) -> None:
|
||||
"""Tickle the backend so in case of auto slicing, it starts the timer."""
|
||||
|
||||
if self._use_timer:
|
||||
self._change_timer.start()
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue