mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-07 06:57:28 -06:00
Move the start of slicing to a proper job
This way it can be properly threaded (with a generous sprinkling of "yieldThread") so we do not block the UI when slicing starts. Contributes to CURA-358
This commit is contained in:
parent
20b828ecee
commit
cb05aee391
2 changed files with 54 additions and 152 deletions
|
@ -17,6 +17,7 @@ from cura.OneAtATimeIterator import OneAtATimeIterator
|
||||||
from . import Cura_pb2
|
from . import Cura_pb2
|
||||||
from . import ProcessSlicedObjectListJob
|
from . import ProcessSlicedObjectListJob
|
||||||
from . import ProcessGCodeJob
|
from . import ProcessGCodeJob
|
||||||
|
from . import StartSliceJob
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
@ -67,12 +68,8 @@ class CuraEngineBackend(Backend):
|
||||||
|
|
||||||
self._slicing = False
|
self._slicing = False
|
||||||
self._restart = False
|
self._restart = False
|
||||||
|
|
||||||
self._save_gcode = True
|
|
||||||
self._save_polygons = True
|
|
||||||
self._report_progress = True
|
|
||||||
|
|
||||||
self._enabled = True
|
self._enabled = True
|
||||||
|
self._always_restart = True
|
||||||
|
|
||||||
self._message = None
|
self._message = None
|
||||||
|
|
||||||
|
@ -97,24 +94,12 @@ class CuraEngineBackend(Backend):
|
||||||
## Emitted whne the slicing process is aborted forcefully.
|
## Emitted whne the slicing process is aborted forcefully.
|
||||||
slicingCancelled = Signal()
|
slicingCancelled = Signal()
|
||||||
|
|
||||||
## Perform a slice of the scene with the given set of settings.
|
## Perform a slice of the scene.
|
||||||
#
|
def slice(self):
|
||||||
# \param kwargs Keyword arguments.
|
|
||||||
# Valid values are:
|
|
||||||
# - settings: The settings to use for the slice. The default is the active machine.
|
|
||||||
# - save_gcode: True if the generated gcode should be saved, False if not. True by default.
|
|
||||||
# - save_polygons: True if the generated polygon data should be saved, False if not. True by default.
|
|
||||||
# - force_restart: True if the slicing process should be forcefully restarted if it is already slicing.
|
|
||||||
# If False, this method will do nothing when already slicing. True by default.
|
|
||||||
# - report_progress: True if the slicing progress should be reported, False if not. Default is True.
|
|
||||||
def slice(self, **kwargs):
|
|
||||||
if not self._enabled:
|
if not self._enabled:
|
||||||
return
|
return
|
||||||
|
|
||||||
if self._slicing:
|
if self._slicing:
|
||||||
if not kwargs.get("force_restart", True):
|
|
||||||
return
|
|
||||||
|
|
||||||
self._slicing = False
|
self._slicing = False
|
||||||
self._restart = True
|
self._restart = True
|
||||||
if self._process is not None:
|
if self._process is not None:
|
||||||
|
@ -123,41 +108,15 @@ class CuraEngineBackend(Backend):
|
||||||
self._process.terminate()
|
self._process.terminate()
|
||||||
except: # terminating a process that is already terminating causes an exception, silently ignore this.
|
except: # terminating a process that is already terminating causes an exception, silently ignore this.
|
||||||
pass
|
pass
|
||||||
self.slicingCancelled.emit()
|
|
||||||
return
|
|
||||||
Logger.log("d", "Preparing to send slice data to engine.")
|
|
||||||
object_groups = []
|
|
||||||
if self._profile.getSettingValue("print_sequence") == "one_at_a_time":
|
|
||||||
for node in OneAtATimeIterator(self._scene.getRoot()):
|
|
||||||
temp_list = []
|
|
||||||
children = node.getAllChildren()
|
|
||||||
children.append(node)
|
|
||||||
for child_node in children:
|
|
||||||
if type(child_node) is SceneNode and child_node.getMeshData() and child_node.getMeshData().getVertices() is not None:
|
|
||||||
temp_list.append(child_node)
|
|
||||||
object_groups.append(temp_list)
|
|
||||||
else:
|
|
||||||
temp_list = []
|
|
||||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
|
||||||
if type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None:
|
|
||||||
if not getattr(node, "_outside_buildarea", False):
|
|
||||||
temp_list.append(node)
|
|
||||||
if len(temp_list) == 0:
|
|
||||||
self.processingProgress.emit(0.0)
|
|
||||||
return
|
|
||||||
object_groups.append(temp_list)
|
|
||||||
#for node in DepthFirstIterator(self._scene.getRoot()):
|
|
||||||
# if type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None:
|
|
||||||
# if not getattr(node, "_outside_buildarea", False):
|
|
||||||
# objects.append(node)
|
|
||||||
|
|
||||||
if len(object_groups) == 0:
|
|
||||||
if self._message:
|
if self._message:
|
||||||
self._message.hide()
|
self._message.hide()
|
||||||
self._message = None
|
self._message = None
|
||||||
return #No point in slicing an empty build plate
|
|
||||||
|
|
||||||
if kwargs.get("profile", self._profile).hasErrorValue():
|
self.slicingCancelled.emit()
|
||||||
|
return
|
||||||
|
|
||||||
|
if self._profile.hasErrorValue():
|
||||||
Logger.log('w', "Profile has error values. Aborting slicing")
|
Logger.log('w', "Profile has error values. Aborting slicing")
|
||||||
if self._message:
|
if self._message:
|
||||||
self._message.hide()
|
self._message.hide()
|
||||||
|
@ -165,62 +124,27 @@ class CuraEngineBackend(Backend):
|
||||||
self._message = Message(catalog.i18nc("@info:status", "Unable to slice. Please check your setting values for errors."))
|
self._message = Message(catalog.i18nc("@info:status", "Unable to slice. Please check your setting values for errors."))
|
||||||
self._message.show()
|
self._message.show()
|
||||||
return #No slicing if we have error values since those are by definition illegal values.
|
return #No slicing if we have error values since those are by definition illegal values.
|
||||||
# Remove existing layer data (if any)
|
|
||||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
self.processingProgress.emit(0.0)
|
||||||
if type(node) is SceneNode and node.getMeshData():
|
if not self._message:
|
||||||
if node.callDecoration("getLayerData"):
|
self._message = Message(catalog.i18nc("@info:status", "Slicing..."), 0, False, -1)
|
||||||
Application.getInstance().getController().getScene().getRoot().removeChild(node)
|
self._message.show()
|
||||||
break
|
else:
|
||||||
Application.getInstance().getController().getScene().gcode_list = None
|
self._message.setProgress(-1)
|
||||||
|
|
||||||
|
self._scene.gcode_list = []
|
||||||
self._slicing = True
|
self._slicing = True
|
||||||
self.slicingStarted.emit()
|
|
||||||
|
|
||||||
self._report_progress = kwargs.get("report_progress", True)
|
job = StartSliceJob.StartSliceJob(self._profile, self._socket)
|
||||||
if self._report_progress:
|
job.start()
|
||||||
self.processingProgress.emit(0.0)
|
job.finished.connect(self._onStartSliceCompleted)
|
||||||
if not self._message:
|
|
||||||
self._message = Message(catalog.i18nc("@info:status", "Slicing..."), 0, False, -1)
|
|
||||||
self._message.show()
|
|
||||||
else:
|
|
||||||
self._message.setProgress(-1)
|
|
||||||
|
|
||||||
self._sendSettings(kwargs.get("profile", self._profile))
|
def _onStartSliceCompleted(self, job):
|
||||||
|
if job.getError() or job.getResult() != True:
|
||||||
self._scene.acquireLock()
|
if self._message:
|
||||||
|
self._message.hide()
|
||||||
# Set the gcode as an empty list. This will be filled with strings by GCodeLayer messages.
|
self._message = None
|
||||||
# This is done so the gcode can be fragmented in memory and does not need a continues memory space.
|
return
|
||||||
# (AKA. This prevents MemoryErrors)
|
|
||||||
self._save_gcode = kwargs.get("save_gcode", True)
|
|
||||||
if self._save_gcode:
|
|
||||||
setattr(self._scene, "gcode_list", [])
|
|
||||||
|
|
||||||
self._save_polygons = kwargs.get("save_polygons", True)
|
|
||||||
|
|
||||||
slice_message = Cura_pb2.Slice()
|
|
||||||
|
|
||||||
for group in object_groups:
|
|
||||||
group_message = slice_message.object_lists.add()
|
|
||||||
for object in group:
|
|
||||||
mesh_data = object.getMeshData().getTransformed(object.getWorldTransformation())
|
|
||||||
|
|
||||||
obj = group_message.objects.add()
|
|
||||||
obj.id = id(object)
|
|
||||||
|
|
||||||
verts = numpy.array(mesh_data.getVertices())
|
|
||||||
verts[:,[1,2]] = verts[:,[2,1]]
|
|
||||||
verts[:,1] *= -1
|
|
||||||
obj.vertices = verts.tostring()
|
|
||||||
|
|
||||||
self._handlePerObjectSettings(object, obj)
|
|
||||||
|
|
||||||
# Hack to add per-object settings also to the "MeshGroup" in CuraEngine
|
|
||||||
# We really should come up with a better solution for this.
|
|
||||||
self._handlePerObjectSettings(group[0], group_message)
|
|
||||||
|
|
||||||
self._scene.releaseLock()
|
|
||||||
Logger.log("d", "Sending data to engine for slicing.")
|
|
||||||
self._socket.sendMessage(slice_message)
|
|
||||||
|
|
||||||
def _onSceneChanged(self, source):
|
def _onSceneChanged(self, source):
|
||||||
if type(source) is not SceneNode:
|
if type(source) is not SceneNode:
|
||||||
|
@ -250,41 +174,42 @@ class CuraEngineBackend(Backend):
|
||||||
self._onChanged()
|
self._onChanged()
|
||||||
|
|
||||||
def _onSlicedObjectListMessage(self, message):
|
def _onSlicedObjectListMessage(self, message):
|
||||||
if self._save_polygons:
|
if self._layer_view_active:
|
||||||
if self._layer_view_active:
|
job = ProcessSlicedObjectListJob.ProcessSlicedObjectListJob(message)
|
||||||
job = ProcessSlicedObjectListJob.ProcessSlicedObjectListJob(message)
|
job.start()
|
||||||
job.start()
|
else :
|
||||||
else :
|
self._stored_layer_data = message
|
||||||
self._stored_layer_data = message
|
|
||||||
|
|
||||||
def _onProgressMessage(self, message):
|
def _onProgressMessage(self, message):
|
||||||
if message.amount >= 0.99:
|
|
||||||
self._slicing = False
|
|
||||||
|
|
||||||
if self._message:
|
|
||||||
self._message.setProgress(100)
|
|
||||||
self._message.hide()
|
|
||||||
self._message = None
|
|
||||||
|
|
||||||
if self._message:
|
if self._message:
|
||||||
self._message.setProgress(round(message.amount * 100))
|
self._message.setProgress(round(message.amount * 100))
|
||||||
|
|
||||||
if self._report_progress:
|
self.processingProgress.emit(message.amount)
|
||||||
self.processingProgress.emit(message.amount)
|
|
||||||
|
|
||||||
def _onGCodeLayerMessage(self, message):
|
def _onGCodeLayerMessage(self, message):
|
||||||
if self._save_gcode:
|
self._scene.gcode_list.append(message.data.decode("utf-8", "replace"))
|
||||||
job = ProcessGCodeJob.ProcessGCodeLayerJob(message)
|
|
||||||
job.start()
|
|
||||||
|
|
||||||
def _onGCodePrefixMessage(self, message):
|
def _onGCodePrefixMessage(self, message):
|
||||||
if self._save_gcode:
|
self._scene.gcode_list.insert(0, message.data.decode("utf-8", "replace"))
|
||||||
self._scene.gcode_list.insert(0, message.data.decode("utf-8", "replace"))
|
|
||||||
|
|
||||||
def _onObjectPrintTimeMessage(self, message):
|
def _onObjectPrintTimeMessage(self, message):
|
||||||
self.printDurationMessage.emit(message.time, message.material_amount)
|
self.printDurationMessage.emit(message.time, message.material_amount)
|
||||||
self.processingProgress.emit(1.0)
|
self.processingProgress.emit(1.0)
|
||||||
|
|
||||||
|
self._slicing = False
|
||||||
|
|
||||||
|
if self._message:
|
||||||
|
self._message.setProgress(100)
|
||||||
|
self._message.hide()
|
||||||
|
self._message = None
|
||||||
|
|
||||||
|
if self._always_restart:
|
||||||
|
try:
|
||||||
|
self._process.terminate()
|
||||||
|
self._createSocket()
|
||||||
|
except: # terminating a process that is already terminating causes an exception, silently ignore this.
|
||||||
|
pass
|
||||||
|
|
||||||
def _createSocket(self):
|
def _createSocket(self):
|
||||||
super()._createSocket()
|
super()._createSocket()
|
||||||
|
|
||||||
|
@ -306,15 +231,6 @@ class CuraEngineBackend(Backend):
|
||||||
|
|
||||||
self._change_timer.start()
|
self._change_timer.start()
|
||||||
|
|
||||||
def _sendSettings(self, profile):
|
|
||||||
msg = Cura_pb2.SettingList()
|
|
||||||
for key, value in profile.getAllSettingValues(include_machine = True).items():
|
|
||||||
s = msg.settings.add()
|
|
||||||
s.name = key
|
|
||||||
s.value = str(value).encode("utf-8")
|
|
||||||
|
|
||||||
self._socket.sendMessage(msg)
|
|
||||||
|
|
||||||
def _onBackendConnected(self):
|
def _onBackendConnected(self):
|
||||||
if self._restart:
|
if self._restart:
|
||||||
self._onChanged()
|
self._onChanged()
|
||||||
|
@ -338,20 +254,3 @@ class CuraEngineBackend(Backend):
|
||||||
self._stored_layer_data = None
|
self._stored_layer_data = None
|
||||||
else:
|
else:
|
||||||
self._layer_view_active = False
|
self._layer_view_active = False
|
||||||
|
|
||||||
def _handlePerObjectSettings(self, node, message):
|
|
||||||
profile = node.callDecoration("getProfile")
|
|
||||||
if profile:
|
|
||||||
for key, value in profile.getChangedSettingValues().items():
|
|
||||||
setting = message.settings.add()
|
|
||||||
setting.name = key
|
|
||||||
setting.value = str(value).encode()
|
|
||||||
|
|
||||||
object_settings = node.callDecoration("getAllSettingValues")
|
|
||||||
if not object_settings:
|
|
||||||
return
|
|
||||||
|
|
||||||
for key, value in object_settings.items():
|
|
||||||
setting = message.settings.add()
|
|
||||||
setting.name = key
|
|
||||||
setting.value = str(value).encode()
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ from cura.OneAtATimeIterator import OneAtATimeIterator
|
||||||
|
|
||||||
from . import Cura_pb2
|
from . import Cura_pb2
|
||||||
|
|
||||||
|
## Job class that handles sending the current scene data to CuraEngine
|
||||||
class StartSliceJob(Job):
|
class StartSliceJob(Job):
|
||||||
def __init__(self, profile, socket):
|
def __init__(self, profile, socket):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
@ -45,7 +46,8 @@ class StartSliceJob(Job):
|
||||||
if type(child_node) is SceneNode and child_node.getMeshData() and child_node.getMeshData().getVertices() is not None:
|
if type(child_node) is SceneNode and child_node.getMeshData() and child_node.getMeshData().getVertices() is not None:
|
||||||
temp_list.append(child_node)
|
temp_list.append(child_node)
|
||||||
|
|
||||||
object_groups.append(temp_list)
|
if temp_list:
|
||||||
|
object_groups.append(temp_list)
|
||||||
Job.yieldThread()
|
Job.yieldThread()
|
||||||
else:
|
else:
|
||||||
temp_list = []
|
temp_list = []
|
||||||
|
@ -54,7 +56,9 @@ class StartSliceJob(Job):
|
||||||
if not getattr(node, "_outside_buildarea", False):
|
if not getattr(node, "_outside_buildarea", False):
|
||||||
temp_list.append(node)
|
temp_list.append(node)
|
||||||
Job.yieldThread()
|
Job.yieldThread()
|
||||||
object_groups.append(temp_list)
|
|
||||||
|
if temp_list:
|
||||||
|
object_groups.append(temp_list)
|
||||||
|
|
||||||
self._scene.releaseLock()
|
self._scene.releaseLock()
|
||||||
|
|
||||||
|
@ -68,7 +72,6 @@ class StartSliceJob(Job):
|
||||||
for group in object_groups:
|
for group in object_groups:
|
||||||
group_message = slice_message.object_lists.add()
|
group_message = slice_message.object_lists.add()
|
||||||
for object in group:
|
for object in group:
|
||||||
print(object)
|
|
||||||
mesh_data = object.getMeshData().getTransformed(object.getWorldTransformation())
|
mesh_data = object.getMeshData().getTransformed(object.getWorldTransformation())
|
||||||
|
|
||||||
obj = group_message.objects.add()
|
obj = group_message.objects.add()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue