mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-08-15 02:06:01 +08:00
Merge branch '15.10' of https://github.com/Ultimaker/Cura into 15.10
This commit is contained in:
commit
dc2ca67727
@ -31,6 +31,8 @@ class ConvexHullJob(Job):
|
||||
self._node.callDecoration("setConvexHullJob", None)
|
||||
return
|
||||
|
||||
Job.yieldThread()
|
||||
|
||||
else:
|
||||
if not self._node.getMeshData():
|
||||
return
|
||||
|
@ -134,6 +134,9 @@ class CuraApplication(QtApplication):
|
||||
parser.add_argument("--debug", dest="debug-mode", action="store_true", default=False, help="Enable detailed crash reports.")
|
||||
|
||||
def run(self):
|
||||
if not "PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION" in os.environ or os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] != "cpp":
|
||||
Logger.log("w", "Using Python implementation of Protobuf, expect bad performance!")
|
||||
|
||||
self._i18n_catalog = i18nCatalog("cura");
|
||||
|
||||
i18nCatalog.setTagReplacements({
|
||||
|
@ -4,6 +4,7 @@
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
def exceptHook(type, value, traceback):
|
||||
import cura.CrashHandler
|
||||
@ -11,6 +12,13 @@ def exceptHook(type, value, traceback):
|
||||
|
||||
sys.excepthook = exceptHook
|
||||
|
||||
try:
|
||||
from google.protobuf.pyext import _message
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] = "cpp"
|
||||
|
||||
import cura.CuraApplication
|
||||
|
||||
if sys.platform == "win32" and hasattr(sys, "frozen"):
|
||||
|
@ -10,6 +10,7 @@ from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Scene.GroupDecorator import GroupDecorator
|
||||
from UM.Math.Quaternion import Quaternion
|
||||
|
||||
from UM.Job import Job
|
||||
|
||||
import os
|
||||
import struct
|
||||
@ -53,6 +54,7 @@ class ThreeMFReader(MeshReader):
|
||||
#for vertex in object.mesh.vertices.vertex:
|
||||
for vertex in object.findall(".//3mf:vertex", self._namespaces):
|
||||
vertex_list.append([vertex.get("x"), vertex.get("y"), vertex.get("z")])
|
||||
Job.yieldThread()
|
||||
|
||||
triangles = object.findall(".//3mf:triangle", self._namespaces)
|
||||
|
||||
@ -64,6 +66,8 @@ class ThreeMFReader(MeshReader):
|
||||
v2 = int(triangle.get("v2"))
|
||||
v3 = int(triangle.get("v3"))
|
||||
mesh.addFace(vertex_list[v1][0],vertex_list[v1][1],vertex_list[v1][2],vertex_list[v2][0],vertex_list[v2][1],vertex_list[v2][2],vertex_list[v3][0],vertex_list[v3][1],vertex_list[v3][2])
|
||||
Job.yieldThread()
|
||||
|
||||
#TODO: We currently do not check for normals and simply recalculate them.
|
||||
mesh.calculateNormals()
|
||||
node.setMeshData(mesh)
|
||||
@ -116,6 +120,8 @@ class ThreeMFReader(MeshReader):
|
||||
node.rotate(rotation)
|
||||
result.addChild(node)
|
||||
|
||||
Job.yieldThread()
|
||||
|
||||
#If there is more then one object, group them.
|
||||
try:
|
||||
if len(objects) > 1:
|
||||
|
@ -17,6 +17,7 @@ from cura.OneAtATimeIterator import OneAtATimeIterator
|
||||
from . import Cura_pb2
|
||||
from . import ProcessSlicedObjectListJob
|
||||
from . import ProcessGCodeJob
|
||||
from . import StartSliceJob
|
||||
|
||||
import os
|
||||
import sys
|
||||
@ -67,12 +68,8 @@ class CuraEngineBackend(Backend):
|
||||
|
||||
self._slicing = False
|
||||
self._restart = False
|
||||
|
||||
self._save_gcode = True
|
||||
self._save_polygons = True
|
||||
self._report_progress = True
|
||||
|
||||
self._enabled = True
|
||||
self._always_restart = True
|
||||
|
||||
self._message = None
|
||||
|
||||
@ -97,24 +94,12 @@ class CuraEngineBackend(Backend):
|
||||
## Emitted whne the slicing process is aborted forcefully.
|
||||
slicingCancelled = Signal()
|
||||
|
||||
## Perform a slice of the scene with the given set of settings.
|
||||
#
|
||||
# \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):
|
||||
## Perform a slice of the scene.
|
||||
def slice(self):
|
||||
if not self._enabled:
|
||||
return
|
||||
|
||||
if self._slicing:
|
||||
if not kwargs.get("force_restart", True):
|
||||
return
|
||||
|
||||
self._slicing = False
|
||||
self._restart = True
|
||||
if self._process is not None:
|
||||
@ -123,41 +108,15 @@ class CuraEngineBackend(Backend):
|
||||
self._process.terminate()
|
||||
except: # terminating a process that is already terminating causes an exception, silently ignore this.
|
||||
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:
|
||||
self._message.hide()
|
||||
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")
|
||||
if self._message:
|
||||
self._message.hide()
|
||||
@ -165,18 +124,7 @@ class CuraEngineBackend(Backend):
|
||||
self._message = Message(catalog.i18nc("@info:status", "Unable to slice. Please check your setting values for errors."))
|
||||
self._message.show()
|
||||
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()):
|
||||
if type(node) is SceneNode and node.getMeshData():
|
||||
if node.callDecoration("getLayerData"):
|
||||
Application.getInstance().getController().getScene().getRoot().removeChild(node)
|
||||
break
|
||||
Application.getInstance().getController().getScene().gcode_list = None
|
||||
self._slicing = True
|
||||
self.slicingStarted.emit()
|
||||
|
||||
self._report_progress = kwargs.get("report_progress", True)
|
||||
if self._report_progress:
|
||||
self.processingProgress.emit(0.0)
|
||||
if not self._message:
|
||||
self._message = Message(catalog.i18nc("@info:status", "Slicing..."), 0, False, -1)
|
||||
@ -184,43 +132,19 @@ class CuraEngineBackend(Backend):
|
||||
else:
|
||||
self._message.setProgress(-1)
|
||||
|
||||
self._sendSettings(kwargs.get("profile", self._profile))
|
||||
self._scene.gcode_list = []
|
||||
self._slicing = True
|
||||
|
||||
self._scene.acquireLock()
|
||||
job = StartSliceJob.StartSliceJob(self._profile, self._socket)
|
||||
job.start()
|
||||
job.finished.connect(self._onStartSliceCompleted)
|
||||
|
||||
# Set the gcode as an empty list. This will be filled with strings by GCodeLayer messages.
|
||||
# This is done so the gcode can be fragmented in memory and does not need a continues memory space.
|
||||
# (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 _onStartSliceCompleted(self, job):
|
||||
if job.getError() or job.getResult() != True:
|
||||
if self._message:
|
||||
self._message.hide()
|
||||
self._message = None
|
||||
return
|
||||
|
||||
def _onSceneChanged(self, source):
|
||||
if type(source) is not SceneNode:
|
||||
@ -250,7 +174,6 @@ class CuraEngineBackend(Backend):
|
||||
self._onChanged()
|
||||
|
||||
def _onSlicedObjectListMessage(self, message):
|
||||
if self._save_polygons:
|
||||
if self._layer_view_active:
|
||||
job = ProcessSlicedObjectListJob.ProcessSlicedObjectListJob(message)
|
||||
job.start()
|
||||
@ -258,7 +181,21 @@ class CuraEngineBackend(Backend):
|
||||
self._stored_layer_data = message
|
||||
|
||||
def _onProgressMessage(self, message):
|
||||
if message.amount >= 0.99:
|
||||
if self._message:
|
||||
self._message.setProgress(round(message.amount * 100))
|
||||
|
||||
self.processingProgress.emit(message.amount)
|
||||
|
||||
def _onGCodeLayerMessage(self, message):
|
||||
self._scene.gcode_list.append(message.data.decode("utf-8", "replace"))
|
||||
|
||||
def _onGCodePrefixMessage(self, message):
|
||||
self._scene.gcode_list.insert(0, message.data.decode("utf-8", "replace"))
|
||||
|
||||
def _onObjectPrintTimeMessage(self, message):
|
||||
self.printDurationMessage.emit(message.time, message.material_amount)
|
||||
self.processingProgress.emit(1.0)
|
||||
|
||||
self._slicing = False
|
||||
|
||||
if self._message:
|
||||
@ -266,24 +203,12 @@ class CuraEngineBackend(Backend):
|
||||
self._message.hide()
|
||||
self._message = None
|
||||
|
||||
if self._message:
|
||||
self._message.setProgress(round(message.amount * 100))
|
||||
|
||||
if self._report_progress:
|
||||
self.processingProgress.emit(message.amount)
|
||||
|
||||
def _onGCodeLayerMessage(self, message):
|
||||
if self._save_gcode:
|
||||
job = ProcessGCodeJob.ProcessGCodeLayerJob(message)
|
||||
job.start()
|
||||
|
||||
def _onGCodePrefixMessage(self, message):
|
||||
if self._save_gcode:
|
||||
self._scene.gcode_list.insert(0, message.data.decode("utf-8", "replace"))
|
||||
|
||||
def _onObjectPrintTimeMessage(self, message):
|
||||
self.printDurationMessage.emit(message.time, message.material_amount)
|
||||
self.processingProgress.emit(1.0)
|
||||
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):
|
||||
super()._createSocket()
|
||||
@ -306,15 +231,6 @@ class CuraEngineBackend(Backend):
|
||||
|
||||
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):
|
||||
if self._restart:
|
||||
self._onChanged()
|
||||
@ -338,20 +254,3 @@ class CuraEngineBackend(Backend):
|
||||
self._stored_layer_data = None
|
||||
else:
|
||||
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()
|
||||
|
@ -63,6 +63,7 @@ class LayerData(MeshData):
|
||||
offset = data.build(offset, vertices, colors, indices)
|
||||
self._element_counts[layer] = data.elementCount
|
||||
|
||||
self.clear()
|
||||
self.addVertices(vertices)
|
||||
self.addColors(colors)
|
||||
self.addIndices(indices.flatten())
|
||||
@ -198,18 +199,14 @@ class Polygon():
|
||||
|
||||
def build(self, offset, vertices, colors, indices):
|
||||
self._begin = offset
|
||||
self._end = self._begin + len(self._data) - 1
|
||||
|
||||
color = self.getColor()
|
||||
color.setValues(color.r * 0.5, color.g * 0.5, color.b * 0.5, color.a)
|
||||
color = numpy.array([color.r, color.g, color.b, color.a], numpy.float32)
|
||||
|
||||
for i in range(len(self._data)):
|
||||
vertices[offset + i, :] = self._data[i, :]
|
||||
colors[offset + i, 0] = color.r
|
||||
colors[offset + i, 1] = color.g
|
||||
colors[offset + i, 2] = color.b
|
||||
colors[offset + i, 3] = color.a
|
||||
|
||||
self._end = self._begin + len(self._data) - 1
|
||||
vertices[self._begin:self._end + 1, :] = self._data[:, :]
|
||||
colors[self._begin:self._end + 1, :] = color
|
||||
|
||||
for i in range(self._begin, self._end):
|
||||
indices[i, 0] = i
|
||||
|
@ -38,10 +38,10 @@ class ProcessSlicedObjectListJob(Job):
|
||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||
if type(node) is SceneNode and node.getMeshData():
|
||||
if node.callDecoration("getLayerData"):
|
||||
#if hasattr(node.getMeshData(), "layerData"):
|
||||
self._scene.getRoot().removeChild(node)
|
||||
else:
|
||||
objectIdMap[id(node)] = node
|
||||
Job.yieldThread()
|
||||
|
||||
settings = Application.getInstance().getMachineManager().getActiveProfile()
|
||||
layerHeight = settings.getSettingValue("layer_height")
|
||||
@ -54,6 +54,20 @@ class ProcessSlicedObjectListJob(Job):
|
||||
|
||||
mesh = MeshData()
|
||||
layer_data = LayerData.LayerData()
|
||||
|
||||
#Add layerdata decorator to scene node to indicate that the node has layerdata
|
||||
decorator = LayerDataDecorator.LayerDataDecorator()
|
||||
decorator.setLayerData(layer_data)
|
||||
new_node.addDecorator(decorator)
|
||||
|
||||
new_node.setMeshData(mesh)
|
||||
new_node.setParent(self._scene.getRoot())
|
||||
|
||||
layer_count = 0
|
||||
for object in self._message.objects:
|
||||
layer_count += len(object.layers)
|
||||
|
||||
current_layer = 0
|
||||
for object in self._message.objects:
|
||||
try:
|
||||
node = objectIdMap[object.id]
|
||||
@ -73,22 +87,25 @@ class ProcessSlicedObjectListJob(Job):
|
||||
|
||||
points[:,2] *= -1
|
||||
|
||||
points -= numpy.array(center)
|
||||
points -= center
|
||||
|
||||
layer_data.addPolygon(layer.id, polygon.type, points, polygon.line_width)
|
||||
|
||||
Job.yieldThread()
|
||||
|
||||
current_layer += 1
|
||||
progress = (current_layer / layer_count) * 100
|
||||
# TODO: Rebuild the layer data mesh once the layer has been processed.
|
||||
# This needs some work in LayerData so we can add the new layers instead of recreating the entire mesh.
|
||||
|
||||
if self._progress:
|
||||
self._progress.setProgress(progress)
|
||||
|
||||
# We are done processing all the layers we got from the engine, now create a mesh out of the data
|
||||
layer_data.build()
|
||||
|
||||
|
||||
#Add layerdata decorator to scene node to indicate that the node has layerdata
|
||||
decorator = LayerDataDecorator.LayerDataDecorator()
|
||||
decorator.setLayerData(layer_data)
|
||||
new_node.addDecorator(decorator)
|
||||
|
||||
new_node.setMeshData(mesh)
|
||||
new_node.setParent(self._scene.getRoot())
|
||||
if self._progress:
|
||||
self._progress.setProgress(100)
|
||||
|
||||
view = Application.getInstance().getController().getActiveView()
|
||||
if view.getPluginId() == "LayerView":
|
||||
|
126
plugins/CuraEngineBackend/StartSliceJob.py
Normal file
126
plugins/CuraEngineBackend/StartSliceJob.py
Normal file
@ -0,0 +1,126 @@
|
||||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import time
|
||||
import numpy
|
||||
|
||||
from UM.Job import Job
|
||||
from UM.Application import Application
|
||||
from UM.Logger import Logger
|
||||
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||
|
||||
from cura.OneAtATimeIterator import OneAtATimeIterator
|
||||
|
||||
from . import Cura_pb2
|
||||
|
||||
## Job class that handles sending the current scene data to CuraEngine
|
||||
class StartSliceJob(Job):
|
||||
def __init__(self, profile, socket):
|
||||
super().__init__()
|
||||
|
||||
self._scene = Application.getInstance().getController().getScene()
|
||||
self._profile = profile
|
||||
self._socket = socket
|
||||
|
||||
def run(self):
|
||||
self._scene.acquireLock()
|
||||
|
||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||
if node.callDecoration("getLayerData"):
|
||||
node.getParent().removeChild(node)
|
||||
break
|
||||
|
||||
object_groups = []
|
||||
if self._profile.getSettingValue("print_sequence") == "one_at_a_time":
|
||||
for node in OneAtATimeIterator(self._scene.getRoot()):
|
||||
temp_list = []
|
||||
|
||||
if getattr(node, "_outside_buildarea", False):
|
||||
continue
|
||||
|
||||
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)
|
||||
|
||||
if temp_list:
|
||||
object_groups.append(temp_list)
|
||||
Job.yieldThread()
|
||||
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)
|
||||
Job.yieldThread()
|
||||
|
||||
if temp_list:
|
||||
object_groups.append(temp_list)
|
||||
|
||||
self._scene.releaseLock()
|
||||
|
||||
if not object_groups:
|
||||
return
|
||||
|
||||
self._sendSettings(self._profile)
|
||||
|
||||
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)
|
||||
|
||||
Job.yieldThread()
|
||||
|
||||
# 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)
|
||||
|
||||
Logger.log("d", "Sending data to engine for slicing.")
|
||||
self._socket.sendMessage(slice_message)
|
||||
|
||||
self.setResult(True)
|
||||
|
||||
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 _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()
|
||||
|
||||
Job.yieldThread()
|
||||
|
||||
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()
|
||||
|
||||
Job.yieldThread()
|
@ -1100,7 +1100,7 @@
|
||||
"unit": "mm",
|
||||
"min_value": "0",
|
||||
"max_value_warning": "10",
|
||||
"default": 0.15,
|
||||
"default": 0.1,
|
||||
"type": "float",
|
||||
"visible": false,
|
||||
"enabled": "support_enable"
|
||||
@ -1141,7 +1141,7 @@
|
||||
"description": "The height of the steps of the stair-like bottom of support resting on the model. Small steps can cause the support to be hard to remove from the top of the model.",
|
||||
"unit": "mm",
|
||||
"type": "float",
|
||||
"default": 2,
|
||||
"default": 0.3,
|
||||
"visible": false,
|
||||
"enabled": "support_enable"
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user