mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-08-12 22:58:58 +08:00
Merge branch '2.3' of github.com:Ultimaker/Cura into 2.3
This commit is contained in:
commit
84fb59a0f3
@ -67,6 +67,9 @@ class BuildVolume(SceneNode):
|
|||||||
self._disallowed_areas = []
|
self._disallowed_areas = []
|
||||||
self._disallowed_area_mesh = None
|
self._disallowed_area_mesh = None
|
||||||
|
|
||||||
|
self._prime_tower_area = None
|
||||||
|
self._prime_tower_area_mesh = None
|
||||||
|
|
||||||
self.setCalculateBoundingBox(False)
|
self.setCalculateBoundingBox(False)
|
||||||
self._volume_aabb = None
|
self._volume_aabb = None
|
||||||
|
|
||||||
@ -82,6 +85,8 @@ class BuildVolume(SceneNode):
|
|||||||
ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged)
|
ExtruderManager.getInstance().activeExtruderChanged.connect(self._onActiveExtruderStackChanged)
|
||||||
self._onActiveExtruderStackChanged()
|
self._onActiveExtruderStackChanged()
|
||||||
|
|
||||||
|
self._has_errors = False
|
||||||
|
|
||||||
def setWidth(self, width):
|
def setWidth(self, width):
|
||||||
if width: self._width = width
|
if width: self._width = width
|
||||||
|
|
||||||
@ -110,6 +115,10 @@ class BuildVolume(SceneNode):
|
|||||||
if self._disallowed_area_mesh:
|
if self._disallowed_area_mesh:
|
||||||
renderer.queueNode(self, mesh = self._disallowed_area_mesh, shader = self._shader, transparent = True, backface_cull = True, sort = -9)
|
renderer.queueNode(self, mesh = self._disallowed_area_mesh, shader = self._shader, transparent = True, backface_cull = True, sort = -9)
|
||||||
|
|
||||||
|
if self._prime_tower_area_mesh:
|
||||||
|
renderer.queueNode(self, mesh = self._prime_tower_area_mesh, shader = self._shader, transparent=True,
|
||||||
|
backface_cull=True, sort=-8)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
## Recalculates the build volume & disallowed areas.
|
## Recalculates the build volume & disallowed areas.
|
||||||
@ -184,6 +193,24 @@ class BuildVolume(SceneNode):
|
|||||||
else:
|
else:
|
||||||
self._disallowed_area_mesh = None
|
self._disallowed_area_mesh = None
|
||||||
|
|
||||||
|
if self._prime_tower_area:
|
||||||
|
mb = MeshBuilder()
|
||||||
|
color = Color(1.0, 0.0, 0.0, 0.5)
|
||||||
|
points = self._prime_tower_area.getPoints()
|
||||||
|
first = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height,
|
||||||
|
self._clamp(points[0][1], min_d, max_d))
|
||||||
|
previous_point = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height,
|
||||||
|
self._clamp(points[0][1], min_d, max_d))
|
||||||
|
for point in points:
|
||||||
|
new_point = Vector(self._clamp(point[0], min_w, max_w), disallowed_area_height,
|
||||||
|
self._clamp(point[1], min_d, max_d))
|
||||||
|
mb.addFace(first, previous_point, new_point, color=color)
|
||||||
|
previous_point = new_point
|
||||||
|
|
||||||
|
self._prime_tower_area_mesh = mb.build()
|
||||||
|
else:
|
||||||
|
self._prime_tower_area_mesh = None
|
||||||
|
|
||||||
self._volume_aabb = AxisAlignedBox(
|
self._volume_aabb = AxisAlignedBox(
|
||||||
minimum = Vector(min_w, min_h - 1.0, min_d),
|
minimum = Vector(min_w, min_h - 1.0, min_d),
|
||||||
maximum = Vector(max_w, max_h - self._raft_thickness, max_d))
|
maximum = Vector(max_w, max_h - self._raft_thickness, max_d))
|
||||||
@ -291,24 +318,34 @@ class BuildVolume(SceneNode):
|
|||||||
if rebuild_me:
|
if rebuild_me:
|
||||||
self.rebuild()
|
self.rebuild()
|
||||||
|
|
||||||
|
def hasErrors(self):
|
||||||
|
return self._has_errors
|
||||||
|
|
||||||
def _updateDisallowedAreas(self):
|
def _updateDisallowedAreas(self):
|
||||||
if not self._global_container_stack:
|
if not self._global_container_stack:
|
||||||
return
|
return
|
||||||
|
self._has_errors = False # Reset.
|
||||||
disallowed_areas = copy.deepcopy(
|
disallowed_areas = copy.deepcopy(
|
||||||
self._global_container_stack.getProperty("machine_disallowed_areas", "value"))
|
self._global_container_stack.getProperty("machine_disallowed_areas", "value"))
|
||||||
areas = []
|
areas = []
|
||||||
|
|
||||||
machine_width = self._global_container_stack.getProperty("machine_width", "value")
|
machine_width = self._global_container_stack.getProperty("machine_width", "value")
|
||||||
machine_depth = self._global_container_stack.getProperty("machine_depth", "value")
|
machine_depth = self._global_container_stack.getProperty("machine_depth", "value")
|
||||||
|
self._prime_tower_area = None
|
||||||
# Add prime tower location as disallowed area.
|
# Add prime tower location as disallowed area.
|
||||||
if self._global_container_stack.getProperty("prime_tower_enable", "value") == True:
|
if self._global_container_stack.getProperty("prime_tower_enable", "value") == True:
|
||||||
prime_tower_size = self._global_container_stack.getProperty("prime_tower_size", "value")
|
prime_tower_size = self._global_container_stack.getProperty("prime_tower_size", "value")
|
||||||
prime_tower_x = self._global_container_stack.getProperty("prime_tower_position_x", "value") - machine_width / 2
|
prime_tower_x = self._global_container_stack.getProperty("prime_tower_position_x", "value") - machine_width / 2
|
||||||
prime_tower_y = - self._global_container_stack.getProperty("prime_tower_position_y", "value") + machine_depth / 2
|
prime_tower_y = - self._global_container_stack.getProperty("prime_tower_position_y", "value") + machine_depth / 2
|
||||||
|
|
||||||
disallowed_areas.append([
|
'''disallowed_areas.append([
|
||||||
|
[prime_tower_x - prime_tower_size, prime_tower_y - prime_tower_size],
|
||||||
|
[prime_tower_x, prime_tower_y - prime_tower_size],
|
||||||
|
[prime_tower_x, prime_tower_y],
|
||||||
|
[prime_tower_x - prime_tower_size, prime_tower_y],
|
||||||
|
])'''
|
||||||
|
|
||||||
|
self._prime_tower_area = Polygon([
|
||||||
[prime_tower_x - prime_tower_size, prime_tower_y - prime_tower_size],
|
[prime_tower_x - prime_tower_size, prime_tower_y - prime_tower_size],
|
||||||
[prime_tower_x, prime_tower_y - prime_tower_size],
|
[prime_tower_x, prime_tower_y - prime_tower_size],
|
||||||
[prime_tower_x, prime_tower_y],
|
[prime_tower_x, prime_tower_y],
|
||||||
@ -344,6 +381,9 @@ class BuildVolume(SceneNode):
|
|||||||
|
|
||||||
areas.append(poly)
|
areas.append(poly)
|
||||||
|
|
||||||
|
if self._prime_tower_area:
|
||||||
|
self._prime_tower_area = self._prime_tower_area.getMinkowskiHull(Polygon(approximatedCircleVertices(bed_adhesion_size)))
|
||||||
|
|
||||||
# Add the skirt areas around the borders of the build plate.
|
# Add the skirt areas around the borders of the build plate.
|
||||||
if bed_adhesion_size > 0:
|
if bed_adhesion_size > 0:
|
||||||
half_machine_width = self._global_container_stack.getProperty("machine_width", "value") / 2
|
half_machine_width = self._global_container_stack.getProperty("machine_width", "value") / 2
|
||||||
@ -377,6 +417,19 @@ class BuildVolume(SceneNode):
|
|||||||
[half_machine_width - bed_adhesion_size, -half_machine_depth + bed_adhesion_size]
|
[half_machine_width - bed_adhesion_size, -half_machine_depth + bed_adhesion_size]
|
||||||
], numpy.float32)))
|
], numpy.float32)))
|
||||||
|
|
||||||
|
# Check if the prime tower area intersects with any of the other areas.
|
||||||
|
# If this is the case, keep the polygon seperate, so it can be drawn in red.
|
||||||
|
# If not, add it back to disallowed area's, so it's rendered as normal.
|
||||||
|
collision = False
|
||||||
|
if self._prime_tower_area:
|
||||||
|
for area in areas:
|
||||||
|
if self._prime_tower_area.intersectsPolygon(area) is not None:
|
||||||
|
collision = True
|
||||||
|
break
|
||||||
|
if not collision:
|
||||||
|
areas.append(self._prime_tower_area)
|
||||||
|
self._prime_tower_area = None
|
||||||
|
self._has_errors = collision
|
||||||
self._disallowed_areas = areas
|
self._disallowed_areas = areas
|
||||||
|
|
||||||
## Convenience function to calculate the size of the bed adhesion in directions x, y.
|
## Convenience function to calculate the size of the bed adhesion in directions x, y.
|
||||||
|
@ -322,6 +322,7 @@ class CuraApplication(QtApplication):
|
|||||||
path = Resources.getStoragePath(self.ResourceTypes.VariantInstanceContainer, file_name)
|
path = Resources.getStoragePath(self.ResourceTypes.VariantInstanceContainer, file_name)
|
||||||
|
|
||||||
if path:
|
if path:
|
||||||
|
instance.setPath(path)
|
||||||
with SaveFile(path, "wt", -1, "utf-8") as f:
|
with SaveFile(path, "wt", -1, "utf-8") as f:
|
||||||
f.write(data)
|
f.write(data)
|
||||||
|
|
||||||
@ -346,6 +347,7 @@ class CuraApplication(QtApplication):
|
|||||||
elif stack_type == "extruder_train":
|
elif stack_type == "extruder_train":
|
||||||
path = Resources.getStoragePath(self.ResourceTypes.ExtruderStack, file_name)
|
path = Resources.getStoragePath(self.ResourceTypes.ExtruderStack, file_name)
|
||||||
if path:
|
if path:
|
||||||
|
stack.setPath(path)
|
||||||
with SaveFile(path, "wt", -1, "utf-8") as f:
|
with SaveFile(path, "wt", -1, "utf-8") as f:
|
||||||
f.write(data)
|
f.write(data)
|
||||||
|
|
||||||
@ -693,12 +695,12 @@ class CuraApplication(QtApplication):
|
|||||||
continue # Node that doesnt have a mesh and is not a group.
|
continue # Node that doesnt have a mesh and is not a group.
|
||||||
if node.getParent() and node.getParent().callDecoration("isGroup"):
|
if node.getParent() and node.getParent().callDecoration("isGroup"):
|
||||||
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
|
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
|
||||||
|
|
||||||
nodes.append(node)
|
nodes.append(node)
|
||||||
|
|
||||||
if nodes:
|
if nodes:
|
||||||
op = GroupedOperation()
|
op = GroupedOperation()
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
|
# Ensure that the object is above the build platform
|
||||||
node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator)
|
node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator)
|
||||||
op.addOperation(SetTransformOperation(node, Vector(0, node.getWorldPosition().y - node.getBoundingBox().bottom, 0)))
|
op.addOperation(SetTransformOperation(node, Vector(0, node.getWorldPosition().y - node.getBoundingBox().bottom, 0)))
|
||||||
op.push()
|
op.push()
|
||||||
@ -719,13 +721,15 @@ class CuraApplication(QtApplication):
|
|||||||
|
|
||||||
if nodes:
|
if nodes:
|
||||||
op = GroupedOperation()
|
op = GroupedOperation()
|
||||||
|
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
# Ensure that the object is above the build platform
|
# Ensure that the object is above the build platform
|
||||||
node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator)
|
node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator)
|
||||||
|
center_y = 0
|
||||||
op.addOperation(SetTransformOperation(node, Vector(0, node.getMeshData().getCenterPosition().y, 0), Quaternion(), Vector(1, 1, 1)))
|
if node.callDecoration("isGroup"):
|
||||||
|
center_y = node.getWorldPosition().y - node.getBoundingBox().bottom
|
||||||
|
else:
|
||||||
|
center_y = node.getMeshData().getCenterPosition().y
|
||||||
|
op.addOperation(SetTransformOperation(node, Vector(0, center_y, 0), Quaternion(), Vector(1, 1, 1)))
|
||||||
op.push()
|
op.push()
|
||||||
|
|
||||||
## Reload all mesh data on the screen from file.
|
## Reload all mesh data on the screen from file.
|
||||||
|
@ -16,7 +16,7 @@ class LayerPolygon:
|
|||||||
MoveRetractionType = 9
|
MoveRetractionType = 9
|
||||||
SupportInterfaceType = 10
|
SupportInterfaceType = 10
|
||||||
|
|
||||||
__jump_map = numpy.logical_or( numpy.arange(11) == NoneType, numpy.arange(11) >= MoveRetractionType )
|
__jump_map = numpy.logical_or(numpy.logical_or(numpy.arange(11) == NoneType, numpy.arange(11) == MoveCombingType), numpy.arange(11) == MoveRetractionType)
|
||||||
|
|
||||||
def __init__(self, mesh, extruder, line_types, data, line_widths):
|
def __init__(self, mesh, extruder, line_types, data, line_widths):
|
||||||
self._mesh = mesh
|
self._mesh = mesh
|
||||||
@ -42,7 +42,7 @@ class LayerPolygon:
|
|||||||
|
|
||||||
# When type is used as index returns true if type == LayerPolygon.InfillType or type == LayerPolygon.SkinType or type == LayerPolygon.SupportInfillType
|
# When type is used as index returns true if type == LayerPolygon.InfillType or type == LayerPolygon.SkinType or type == LayerPolygon.SupportInfillType
|
||||||
# Should be generated in better way, not hardcoded.
|
# Should be generated in better way, not hardcoded.
|
||||||
self._isInfillOrSkinTypeMap = numpy.array([0, 0, 0, 1, 0, 0, 1, 1, 0, 0], dtype=numpy.bool)
|
self._isInfillOrSkinTypeMap = numpy.array([0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1], dtype=numpy.bool)
|
||||||
|
|
||||||
self._build_cache_line_mesh_mask = None
|
self._build_cache_line_mesh_mask = None
|
||||||
self._build_cache_needed_points = None
|
self._build_cache_needed_points = None
|
||||||
|
@ -15,6 +15,9 @@ from cura.ConvexHullDecorator import ConvexHullDecorator
|
|||||||
from . import PlatformPhysicsOperation
|
from . import PlatformPhysicsOperation
|
||||||
from . import ZOffsetDecorator
|
from . import ZOffsetDecorator
|
||||||
|
|
||||||
|
import random # used for list shuffling
|
||||||
|
|
||||||
|
|
||||||
class PlatformPhysics:
|
class PlatformPhysics:
|
||||||
def __init__(self, controller, volume):
|
def __init__(self, controller, volume):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@ -48,7 +51,12 @@ class PlatformPhysics:
|
|||||||
# same direction.
|
# same direction.
|
||||||
transformed_nodes = []
|
transformed_nodes = []
|
||||||
|
|
||||||
for node in BreadthFirstIterator(root):
|
group_nodes = []
|
||||||
|
# We try to shuffle all the nodes to prevent "locked" situations, where iteration B inverts iteration A.
|
||||||
|
# By shuffling the order of the nodes, this might happen a few times, but at some point it will resolve.
|
||||||
|
nodes = list(BreadthFirstIterator(root))
|
||||||
|
random.shuffle(nodes)
|
||||||
|
for node in nodes:
|
||||||
if node is root or type(node) is not SceneNode or node.getBoundingBox() is None:
|
if node is root or type(node) is not SceneNode or node.getBoundingBox() is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -69,6 +77,9 @@ class PlatformPhysics:
|
|||||||
if build_volume_bounding_box.intersectsBox(bbox) != AxisAlignedBox.IntersectionResult.FullIntersection:
|
if build_volume_bounding_box.intersectsBox(bbox) != AxisAlignedBox.IntersectionResult.FullIntersection:
|
||||||
node._outside_buildarea = True
|
node._outside_buildarea = True
|
||||||
|
|
||||||
|
if node.callDecoration("isGroup"):
|
||||||
|
group_nodes.append(node) # Keep list of affected group_nodes
|
||||||
|
|
||||||
# Move it downwards if bottom is above platform
|
# Move it downwards if bottom is above platform
|
||||||
move_vector = Vector()
|
move_vector = Vector()
|
||||||
if Preferences.getInstance().getValue("physics/automatic_drop_down") and not (node.getParent() and node.getParent().callDecoration("isGroup")): #If an object is grouped, don't move it down
|
if Preferences.getInstance().getValue("physics/automatic_drop_down") and not (node.getParent() and node.getParent().callDecoration("isGroup")): #If an object is grouped, don't move it down
|
||||||
@ -102,7 +113,6 @@ class PlatformPhysics:
|
|||||||
continue # Other node is already moving, wait for next pass.
|
continue # Other node is already moving, wait for next pass.
|
||||||
|
|
||||||
overlap = (0, 0) # Start loop with no overlap
|
overlap = (0, 0) # Start loop with no overlap
|
||||||
move_vector = move_vector.set(x=overlap[0] * self._move_factor, z=overlap[1] * self._move_factor)
|
|
||||||
current_overlap_checks = 0
|
current_overlap_checks = 0
|
||||||
# Continue to check the overlap until we no longer find one.
|
# Continue to check the overlap until we no longer find one.
|
||||||
while overlap and current_overlap_checks < self._max_overlap_checks:
|
while overlap and current_overlap_checks < self._max_overlap_checks:
|
||||||
@ -144,7 +154,6 @@ class PlatformPhysics:
|
|||||||
overlap = convex_hull.intersectsPolygon(area)
|
overlap = convex_hull.intersectsPolygon(area)
|
||||||
if overlap is None:
|
if overlap is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
node._outside_buildarea = True
|
node._outside_buildarea = True
|
||||||
|
|
||||||
if not Vector.Null.equals(move_vector, epsilon=1e-5):
|
if not Vector.Null.equals(move_vector, epsilon=1e-5):
|
||||||
@ -152,6 +161,12 @@ class PlatformPhysics:
|
|||||||
op = PlatformPhysicsOperation.PlatformPhysicsOperation(node, move_vector)
|
op = PlatformPhysicsOperation.PlatformPhysicsOperation(node, move_vector)
|
||||||
op.push()
|
op.push()
|
||||||
|
|
||||||
|
# Group nodes should override the _outside_buildarea property of their children.
|
||||||
|
for group_node in group_nodes:
|
||||||
|
for child_node in group_node.getAllChildren():
|
||||||
|
child_node._outside_buildarea = group_node._outside_buildarea
|
||||||
|
|
||||||
|
|
||||||
def _onToolOperationStarted(self, tool):
|
def _onToolOperationStarted(self, tool):
|
||||||
self._enabled = False
|
self._enabled = False
|
||||||
|
|
||||||
|
@ -167,6 +167,19 @@ class ContainerManager(QObject):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@pyqtSlot(str, str, result=str)
|
||||||
|
def getContainerMetaDataEntry(self, container_id, entry_name):
|
||||||
|
containers = self._container_registry.findContainers(None, id=container_id)
|
||||||
|
if not containers:
|
||||||
|
UM.Logger.log("w", "Could not get metadata of container %s because it was not found.", container_id)
|
||||||
|
return False
|
||||||
|
|
||||||
|
result = containers[0].getMetaDataEntry(entry_name)
|
||||||
|
if result:
|
||||||
|
return result
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
## Set a metadata entry of the specified container.
|
## Set a metadata entry of the specified container.
|
||||||
#
|
#
|
||||||
# This will set the specified entry of the container's metadata to the specified
|
# This will set the specified entry of the container's metadata to the specified
|
||||||
@ -254,6 +267,10 @@ class ContainerManager(QObject):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@pyqtSlot(str, result = str)
|
||||||
|
def makeUniqueName(self, original_name):
|
||||||
|
return self._container_registry.uniqueName(original_name)
|
||||||
|
|
||||||
## Get a list of string that can be used as name filters for a Qt File Dialog
|
## Get a list of string that can be used as name filters for a Qt File Dialog
|
||||||
#
|
#
|
||||||
# This will go through the list of available container types and generate a list of strings
|
# This will go through the list of available container types and generate a list of strings
|
||||||
@ -573,6 +590,28 @@ class ContainerManager(QObject):
|
|||||||
|
|
||||||
return new_name
|
return new_name
|
||||||
|
|
||||||
|
@pyqtSlot(str, result = str)
|
||||||
|
def duplicateMaterial(self, material_id):
|
||||||
|
containers = self._container_registry.findInstanceContainers(id=material_id)
|
||||||
|
if not containers:
|
||||||
|
UM.Logger.log("d", "Unable to duplicate the material with id %s, because it doesn't exist.", material_id)
|
||||||
|
return ""
|
||||||
|
|
||||||
|
# Ensure all settings are saved.
|
||||||
|
UM.Application.getInstance().saveSettings()
|
||||||
|
|
||||||
|
# Create a new ID & container to hold the data.
|
||||||
|
new_id = self._container_registry.uniqueName(material_id)
|
||||||
|
container_type = type(containers[0]) # Could be either a XMLMaterialProfile or a InstanceContainer
|
||||||
|
duplicated_container = container_type(new_id)
|
||||||
|
|
||||||
|
# Instead of duplicating we load the data from the basefile again.
|
||||||
|
# This ensures that the inheritance goes well and all "cut up" subclasses of the xmlMaterial profile
|
||||||
|
# are also correctly created.
|
||||||
|
with open(containers[0].getPath(), encoding="utf-8") as f:
|
||||||
|
duplicated_container.deserialize(f.read())
|
||||||
|
self._container_registry.addContainer(duplicated_container)
|
||||||
|
|
||||||
# Factory function, used by QML
|
# Factory function, used by QML
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def createContainerManager(engine, js_engine):
|
def createContainerManager(engine, js_engine):
|
||||||
|
@ -71,7 +71,7 @@ class ThreeMFReader(MeshReader):
|
|||||||
rotation.setByRotationAxis(-0.5 * math.pi, Vector(1, 0, 0))
|
rotation.setByRotationAxis(-0.5 * math.pi, Vector(1, 0, 0))
|
||||||
|
|
||||||
# TODO: We currently do not check for normals and simply recalculate them.
|
# TODO: We currently do not check for normals and simply recalculate them.
|
||||||
mesh_builder.calculateNormals()
|
mesh_builder.calculateNormals(flip = True)
|
||||||
mesh_builder.setFileName(file_name)
|
mesh_builder.setFileName(file_name)
|
||||||
node.setMeshData(mesh_builder.build().getTransformed(rotation))
|
node.setMeshData(mesh_builder.build().getTransformed(rotation))
|
||||||
node.setSelectable(True)
|
node.setSelectable(True)
|
||||||
|
@ -78,6 +78,10 @@ class StartSliceJob(Job):
|
|||||||
self.setResult(StartJobResult.SettingError)
|
self.setResult(StartJobResult.SettingError)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if Application.getInstance().getBuildVolume().hasErrors():
|
||||||
|
self.setResult(StartJobResult.SettingError)
|
||||||
|
return
|
||||||
|
|
||||||
# Don't slice if there is a per object setting with an error value.
|
# Don't slice if there is a per object setting with an error value.
|
||||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||||
if type(node) is not SceneNode or not node.isSelectable():
|
if type(node) is not SceneNode or not node.isSelectable():
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# Copyright (c) 2015 Ultimaker B.V.
|
# Copyright (c) 2016 Ultimaker B.V.
|
||||||
# Cura is released under the terms of the AGPLv3 or higher.
|
# Cura is released under the terms of the AGPLv3 or higher.
|
||||||
|
import configparser
|
||||||
|
|
||||||
import os.path
|
from UM import PluginRegistry
|
||||||
|
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
from UM.Settings.InstanceContainer import InstanceContainer # The new profile to make.
|
from UM.Settings.InstanceContainer import InstanceContainer # The new profile to make.
|
||||||
from cura.ProfileReader import ProfileReader
|
from cura.ProfileReader import ProfileReader
|
||||||
@ -27,22 +27,76 @@ class CuraProfileReader(ProfileReader):
|
|||||||
# returned.
|
# returned.
|
||||||
def read(self, file_name):
|
def read(self, file_name):
|
||||||
try:
|
try:
|
||||||
archive = zipfile.ZipFile(file_name, "r")
|
with zipfile.ZipFile(file_name, "r") as archive:
|
||||||
except Exception:
|
results = []
|
||||||
# zipfile doesn't give proper exceptions, so we can only catch broad ones
|
for profile_id in archive.namelist():
|
||||||
|
with archive.open(profile_id) as f:
|
||||||
|
serialized = f.read()
|
||||||
|
profile = self._loadProfile(serialized.decode("utf-8"), profile_id)
|
||||||
|
if profile is not None:
|
||||||
|
results.append(profile)
|
||||||
|
return results
|
||||||
|
|
||||||
|
except zipfile.BadZipFile:
|
||||||
|
# It must be an older profile from Cura 2.1.
|
||||||
|
with open(file_name, encoding="utf-8") as fhandle:
|
||||||
|
serialized = fhandle.read()
|
||||||
|
return [self._loadProfile(serialized, profile_id) for serialized, profile_id in self._upgradeProfile(serialized, file_name)]
|
||||||
|
|
||||||
|
## Convert a profile from an old Cura to this Cura if needed.
|
||||||
|
#
|
||||||
|
# \param serialized \type{str} The profile data to convert in the serialized on-disk format.
|
||||||
|
# \param profile_id \type{str} The name of the profile.
|
||||||
|
# \return \type{List[Tuple[str,str]]} List of serialized profile strings and matching profile names.
|
||||||
|
def _upgradeProfile(self, serialized, profile_id):
|
||||||
|
parser = configparser.ConfigParser(interpolation=None)
|
||||||
|
parser.read_string(serialized)
|
||||||
|
|
||||||
|
if not "general" in parser:
|
||||||
|
Logger.log("w", "Missing required section 'general'.")
|
||||||
|
return None
|
||||||
|
if not "version" in parser["general"]:
|
||||||
|
Logger.log("w", "Missing required 'version' property")
|
||||||
|
return None
|
||||||
|
|
||||||
|
version = int(parser["general"]["version"])
|
||||||
|
if InstanceContainer.Version != version:
|
||||||
|
name = parser["general"]["name"]
|
||||||
|
return self._upgradeProfileVersion(serialized, name, version)
|
||||||
|
else:
|
||||||
|
return [(serialized, profile_id)]
|
||||||
|
|
||||||
|
## Load a profile from a serialized string.
|
||||||
|
#
|
||||||
|
# \param serialized \type{str} The profile data to read.
|
||||||
|
# \param profile_id \type{str} The name of the profile.
|
||||||
|
# \return \type{InstanceContainer|None}
|
||||||
|
def _loadProfile(self, serialized, profile_id):
|
||||||
|
# Create an empty profile.
|
||||||
|
profile = InstanceContainer(profile_id)
|
||||||
|
profile.addMetaDataEntry("type", "quality_changes")
|
||||||
|
try:
|
||||||
|
profile.deserialize(serialized)
|
||||||
|
except Exception as e: # Parsing error. This is not a (valid) Cura profile then.
|
||||||
|
Logger.log("e", "Error while trying to parse profile: %s", str(e))
|
||||||
|
return None
|
||||||
|
return profile
|
||||||
|
|
||||||
|
## Upgrade a serialized profile to the current profile format.
|
||||||
|
#
|
||||||
|
# \param serialized \type{str} The profile data to convert.
|
||||||
|
# \param profile_id \type{str} The name of the profile.
|
||||||
|
# \param source_version \type{int} The profile version of 'serialized'.
|
||||||
|
# \return \type{List[Tuple[str,str]]} List of serialized profile strings and matching profile names.
|
||||||
|
def _upgradeProfileVersion(self, serialized, profile_id, source_version):
|
||||||
|
converter_plugins = PluginRegistry.getInstance().getAllMetaData(filter={"version_upgrade": {} }, active_only=True)
|
||||||
|
|
||||||
|
source_format = ("profile", source_version)
|
||||||
|
profile_convert_funcs = [plugin["version_upgrade"][source_format][2] for plugin in converter_plugins
|
||||||
|
if source_format in plugin["version_upgrade"] and plugin["version_upgrade"][source_format][1] == InstanceContainer.Version]
|
||||||
|
|
||||||
|
if not profile_convert_funcs:
|
||||||
return []
|
return []
|
||||||
results = []
|
|
||||||
for profile_id in archive.namelist():
|
filenames, outputs = profile_convert_funcs[0](serialized, profile_id)
|
||||||
# Create an empty profile.
|
return list(zip(outputs, filenames))
|
||||||
profile = InstanceContainer(profile_id)
|
|
||||||
profile.addMetaDataEntry("type", "quality_changes")
|
|
||||||
serialized = ""
|
|
||||||
with archive.open(profile_id) as f:
|
|
||||||
serialized = f.read()
|
|
||||||
try:
|
|
||||||
profile.deserialize(serialized.decode("utf-8") )
|
|
||||||
except Exception as e: # Parsing error. This is not a (valid) Cura profile then.
|
|
||||||
Logger.log("e", "Error while trying to parse profile: %s", str(e))
|
|
||||||
continue
|
|
||||||
results.append(profile)
|
|
||||||
return results
|
|
||||||
|
@ -66,7 +66,10 @@ class GCodeWriter(MeshWriter):
|
|||||||
## Create a new container with container 2 as base and container 1 written over it.
|
## Create a new container with container 2 as base and container 1 written over it.
|
||||||
def _createFlattenedContainerInstance(self, instance_container1, instance_container2):
|
def _createFlattenedContainerInstance(self, instance_container1, instance_container2):
|
||||||
flat_container = InstanceContainer(instance_container2.getName())
|
flat_container = InstanceContainer(instance_container2.getName())
|
||||||
flat_container.setDefinition(instance_container2.getDefinition())
|
if instance_container1.getDefinition():
|
||||||
|
flat_container.setDefinition(instance_container1.getDefinition())
|
||||||
|
else:
|
||||||
|
flat_container.setDefinition(instance_container2.getDefinition())
|
||||||
flat_container.setMetaData(instance_container2.getMetaData())
|
flat_container.setMetaData(instance_container2.getMetaData())
|
||||||
|
|
||||||
for key in instance_container2.getAllKeys():
|
for key in instance_container2.getAllKeys():
|
||||||
|
@ -70,7 +70,8 @@
|
|||||||
"magic_spiralize": "spiralize",
|
"magic_spiralize": "spiralize",
|
||||||
"prime_tower_enable": "wipe_tower",
|
"prime_tower_enable": "wipe_tower",
|
||||||
"prime_tower_size": "math.sqrt(float(wipe_tower_volume) / float(layer_height))",
|
"prime_tower_size": "math.sqrt(float(wipe_tower_volume) / float(layer_height))",
|
||||||
"ooze_shield_enabled": "ooze_shield"
|
"ooze_shield_enabled": "ooze_shield",
|
||||||
|
"skin_overlap": "fill_overlap"
|
||||||
},
|
},
|
||||||
|
|
||||||
"defaults": {
|
"defaults": {
|
||||||
|
@ -69,7 +69,7 @@ class PerObjectSettingVisibilityHandler(UM.Settings.Models.SettingVisibilityHand
|
|||||||
stack = UM.Settings.ContainerRegistry.getInstance().findContainerStacks(id = ExtruderManager.getInstance().extruderIds[stack_nr])[0]
|
stack = UM.Settings.ContainerRegistry.getInstance().findContainerStacks(id = ExtruderManager.getInstance().extruderIds[stack_nr])[0]
|
||||||
else:
|
else:
|
||||||
stack = UM.Application.getInstance().getGlobalContainerStack()
|
stack = UM.Application.getInstance().getGlobalContainerStack()
|
||||||
new_instance.setProperty("value", stack.getProperty(item, "value"))
|
new_instance.setProperty("value", stack.getRawProperty(item, "value"))
|
||||||
new_instance.resetState() # Ensure that the state is not seen as a user state.
|
new_instance.resetState() # Ensure that the state is not seen as a user state.
|
||||||
settings.addInstance(new_instance)
|
settings.addInstance(new_instance)
|
||||||
visibility_changed = True
|
visibility_changed = True
|
||||||
|
@ -7,6 +7,7 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
|||||||
from UM.Application import Application
|
from UM.Application import Application
|
||||||
from UM.Preferences import Preferences
|
from UM.Preferences import Preferences
|
||||||
from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
|
from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
|
||||||
|
from cura.Settings.ExtruderManager import ExtruderManager
|
||||||
|
|
||||||
|
|
||||||
## This tool allows the user to add & change settings per node in the scene.
|
## This tool allows the user to add & change settings per node in the scene.
|
||||||
@ -71,11 +72,20 @@ class PerObjectSettingsTool(Tool):
|
|||||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||||
if global_container_stack:
|
if global_container_stack:
|
||||||
self._multi_extrusion = global_container_stack.getProperty("machine_extruder_count", "value") > 1
|
self._multi_extrusion = global_container_stack.getProperty("machine_extruder_count", "value") > 1
|
||||||
|
|
||||||
|
# Ensure that all extruder data is reset
|
||||||
if not self._multi_extrusion:
|
if not self._multi_extrusion:
|
||||||
# Ensure that all extruder data is reset
|
default_stack_id = global_container_stack.getId()
|
||||||
root_node = Application.getInstance().getController().getScene().getRoot()
|
else:
|
||||||
for node in DepthFirstIterator(root_node):
|
default_stack = ExtruderManager.getInstance().getExtruderStack(0)
|
||||||
node.callDecoration("setActiveExtruder", global_container_stack.getId())
|
if default_stack:
|
||||||
|
default_stack_id = default_stack.getId()
|
||||||
|
else: default_stack_id = global_container_stack.getId()
|
||||||
|
|
||||||
|
root_node = Application.getInstance().getController().getScene().getRoot()
|
||||||
|
for node in DepthFirstIterator(root_node):
|
||||||
|
node.callDecoration("setActiveExtruder", default_stack_id)
|
||||||
|
|
||||||
self._updateEnabled()
|
self._updateEnabled()
|
||||||
|
|
||||||
def _updateEnabled(self):
|
def _updateEnabled(self):
|
||||||
|
@ -49,7 +49,7 @@ class RemovableDrivePlugin(OutputDevicePlugin):
|
|||||||
message = Message(catalog.i18nc("@info:status", "Ejected {0}. You can now safely remove the drive.").format(device.getName()))
|
message = Message(catalog.i18nc("@info:status", "Ejected {0}. You can now safely remove the drive.").format(device.getName()))
|
||||||
message.show()
|
message.show()
|
||||||
else:
|
else:
|
||||||
message = Message(catalog.i18nc("@info:status", "Failed to eject {0}. Maybe it is still in use?").format(device.getName()))
|
message = Message(catalog.i18nc("@info:status", "Failed to eject {0}. Another program may be using the drive.").format(device.getName()))
|
||||||
message.show()
|
message.show()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ class SliceInfo(Extension):
|
|||||||
Preferences.getInstance().addPreference("info/asked_send_slice_info", False)
|
Preferences.getInstance().addPreference("info/asked_send_slice_info", False)
|
||||||
|
|
||||||
if not Preferences.getInstance().getValue("info/asked_send_slice_info"):
|
if not Preferences.getInstance().getValue("info/asked_send_slice_info"):
|
||||||
self.send_slice_info_message = Message(catalog.i18nc("@info", "Cura automatically sends slice info. You can disable this in preferences"), lifetime = 0, dismissable = False)
|
self.send_slice_info_message = Message(catalog.i18nc("@info", "Cura collects anonymised slicing statistics. You can disable this in preferences"), lifetime = 0, dismissable = False)
|
||||||
self.send_slice_info_message.addAction("Dismiss", catalog.i18nc("@action:button", "Dismiss"), None, "")
|
self.send_slice_info_message.addAction("Dismiss", catalog.i18nc("@action:button", "Dismiss"), None, "")
|
||||||
self.send_slice_info_message.actionTriggered.connect(self.messageActionTriggered)
|
self.send_slice_info_message.actionTriggered.connect(self.messageActionTriggered)
|
||||||
self.send_slice_info_message.show()
|
self.send_slice_info_message.show()
|
||||||
|
@ -13,6 +13,7 @@ from UM.Settings.Validator import ValidatorState
|
|||||||
from UM.View.GL.OpenGL import OpenGL
|
from UM.View.GL.OpenGL import OpenGL
|
||||||
|
|
||||||
import cura.Settings
|
import cura.Settings
|
||||||
|
from cura.Settings.ExtruderManager import ExtruderManager
|
||||||
|
|
||||||
import math
|
import math
|
||||||
|
|
||||||
@ -45,8 +46,18 @@ class SolidView(View):
|
|||||||
|
|
||||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||||
if global_container_stack:
|
if global_container_stack:
|
||||||
|
multi_extrusion = global_container_stack.getProperty("machine_extruder_count", "value") > 1
|
||||||
|
|
||||||
|
if multi_extrusion:
|
||||||
|
support_extruder_nr = global_container_stack.getProperty("support_extruder_nr", "value")
|
||||||
|
support_angle_stack = ExtruderManager.getInstance().getExtruderStack(support_extruder_nr)
|
||||||
|
if not support_angle_stack:
|
||||||
|
support_angle_stack = global_container_stack
|
||||||
|
else:
|
||||||
|
support_angle_stack = global_container_stack
|
||||||
|
|
||||||
if Preferences.getInstance().getValue("view/show_overhang"):
|
if Preferences.getInstance().getValue("view/show_overhang"):
|
||||||
angle = global_container_stack.getProperty("support_angle", "value")
|
angle = support_angle_stack.getProperty("support_angle", "value")
|
||||||
# Make sure the overhang angle is valid before passing it to the shader
|
# Make sure the overhang angle is valid before passing it to the shader
|
||||||
# Note: if the overhang angle is set to its default value, it does not need to get validated (validationState = None)
|
# Note: if the overhang angle is set to its default value, it does not need to get validated (validationState = None)
|
||||||
if angle is not None and global_container_stack.getProperty("support_angle", "validationState") in [None, ValidatorState.Valid]:
|
if angle is not None and global_container_stack.getProperty("support_angle", "validationState") in [None, ValidatorState.Valid]:
|
||||||
@ -56,7 +67,6 @@ class SolidView(View):
|
|||||||
else:
|
else:
|
||||||
self._enabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(0)))
|
self._enabled_shader.setUniformValue("u_overhangAngle", math.cos(math.radians(0)))
|
||||||
|
|
||||||
multi_extrusion = global_container_stack.getProperty("machine_extruder_count", "value") > 1
|
|
||||||
|
|
||||||
for node in DepthFirstIterator(scene.getRoot()):
|
for node in DepthFirstIterator(scene.getRoot()):
|
||||||
if not node.render(renderer):
|
if not node.render(renderer):
|
||||||
|
@ -140,7 +140,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
|
|||||||
# \param gcode_list List with gcode (strings).
|
# \param gcode_list List with gcode (strings).
|
||||||
def printGCode(self, gcode_list):
|
def printGCode(self, gcode_list):
|
||||||
if self._progress or self._connection_state != ConnectionState.connected:
|
if self._progress or self._connection_state != ConnectionState.connected:
|
||||||
self._error_message = Message(catalog.i18nc("@info:status", "Printer is busy or not connected. Unable to start a new job."))
|
self._error_message = Message(catalog.i18nc("@info:status", "Unable to start a new job because the printer is busy or not connected."))
|
||||||
self._error_message.show()
|
self._error_message.show()
|
||||||
Logger.log("d", "Printer is busy or not connected, aborting print")
|
Logger.log("d", "Printer is busy or not connected, aborting print")
|
||||||
self.writeError.emit(self)
|
self.writeError.emit(self)
|
||||||
|
@ -105,9 +105,10 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension):
|
|||||||
|
|
||||||
@pyqtSlot(str)
|
@pyqtSlot(str)
|
||||||
def updateAllFirmware(self, file_name):
|
def updateAllFirmware(self, file_name):
|
||||||
file_name = file_name.replace("file://", "") # File dialogs prepend the path with file://, which we don't need / want
|
if file_name.startswith("file://"):
|
||||||
|
file_name = QUrl(file_name).toLocalFile() # File dialogs prepend the path with file://, which we don't need / want
|
||||||
if not self._usb_output_devices:
|
if not self._usb_output_devices:
|
||||||
Message(i18n_catalog.i18nc("@info", "Cannot update firmware, there were no connected printers found.")).show()
|
Message(i18n_catalog.i18nc("@info", "Unable to update firmware because there are no printers connected.")).show()
|
||||||
return
|
return
|
||||||
|
|
||||||
for printer_connection in self._usb_output_devices:
|
for printer_connection in self._usb_output_devices:
|
||||||
|
@ -31,7 +31,7 @@ class UMOUpgradeSelection(MachineAction):
|
|||||||
if variant:
|
if variant:
|
||||||
if variant.getId() == "empty_variant":
|
if variant.getId() == "empty_variant":
|
||||||
variant_index = global_container_stack.getContainerIndex(variant)
|
variant_index = global_container_stack.getContainerIndex(variant)
|
||||||
self._createVariant(global_container_stack, variant_index)
|
variant = self._createVariant(global_container_stack, variant_index)
|
||||||
variant.setProperty("machine_heated_bed", "value", heated_bed)
|
variant.setProperty("machine_heated_bed", "value", heated_bed)
|
||||||
self.heatedBedChanged.emit()
|
self.heatedBedChanged.emit()
|
||||||
|
|
||||||
@ -42,3 +42,4 @@ class UMOUpgradeSelection(MachineAction):
|
|||||||
new_variant.setDefinition(global_container_stack.getBottom())
|
new_variant.setDefinition(global_container_stack.getBottom())
|
||||||
UM.Settings.ContainerRegistry.getInstance().addContainer(new_variant)
|
UM.Settings.ContainerRegistry.getInstance().addContainer(new_variant)
|
||||||
global_container_stack.replaceContainer(variant_index, new_variant)
|
global_container_stack.replaceContainer(variant_index, new_variant)
|
||||||
|
return new_variant
|
@ -279,6 +279,8 @@ class VersionUpgrade21to22(VersionUpgrade):
|
|||||||
elif key in _setting_name_translations:
|
elif key in _setting_name_translations:
|
||||||
del settings[key]
|
del settings[key]
|
||||||
settings[_setting_name_translations[key]] = value
|
settings[_setting_name_translations[key]] = value
|
||||||
|
if "infill_overlap" in settings: # New setting, added in 2.3
|
||||||
|
settings["skin_overlap"] = settings["infill_overlap"]
|
||||||
return settings
|
return settings
|
||||||
|
|
||||||
## Translates a setting name for the change from Cura 2.1 to 2.2.
|
## Translates a setting name for the change from Cura 2.1 to 2.2.
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
"manufacturer": "Ultimaker",
|
"manufacturer": "Ultimaker",
|
||||||
"file_formats": "text/x-gcode;application/x-stl-ascii;application/x-stl-binary;application/x-wavefront-obj;application/x3g",
|
"file_formats": "text/x-gcode;application/x-stl-ascii;application/x-stl-binary;application/x-wavefront-obj;application/x3g",
|
||||||
"visible": false,
|
"visible": false,
|
||||||
|
"has_materials": true,
|
||||||
"preferred_material": "*generic_pla*",
|
"preferred_material": "*generic_pla*",
|
||||||
"preferred_quality": "*normal*",
|
"preferred_quality": "*normal*",
|
||||||
"machine_extruder_trains":
|
"machine_extruder_trains":
|
||||||
@ -403,7 +404,7 @@
|
|||||||
"description": "The maximum speed of the filament.",
|
"description": "The maximum speed of the filament.",
|
||||||
"unit": "mm/s",
|
"unit": "mm/s",
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"default_value": 25,
|
"default_value": 299792458000,
|
||||||
"settable_per_mesh": false,
|
"settable_per_mesh": false,
|
||||||
"settable_per_extruder": false,
|
"settable_per_extruder": false,
|
||||||
"settable_per_meshgroup": false
|
"settable_per_meshgroup": false
|
||||||
@ -1161,7 +1162,7 @@
|
|||||||
"default_value": 25,
|
"default_value": 25,
|
||||||
"minimum_value": "0",
|
"minimum_value": "0",
|
||||||
"maximum_value": "machine_max_feedrate_e",
|
"maximum_value": "machine_max_feedrate_e",
|
||||||
"maximum_value_warning": "100",
|
"maximum_value_warning": "25",
|
||||||
"enabled": "retraction_enable",
|
"enabled": "retraction_enable",
|
||||||
"settable_per_mesh": false,
|
"settable_per_mesh": false,
|
||||||
"settable_per_extruder": true,
|
"settable_per_extruder": true,
|
||||||
@ -1174,7 +1175,7 @@
|
|||||||
"default_value": 25,
|
"default_value": 25,
|
||||||
"minimum_value": "0",
|
"minimum_value": "0",
|
||||||
"maximum_value": "machine_max_feedrate_e",
|
"maximum_value": "machine_max_feedrate_e",
|
||||||
"maximum_value_warning": "100",
|
"maximum_value_warning": "25",
|
||||||
"enabled": "retraction_enable",
|
"enabled": "retraction_enable",
|
||||||
"value": "retraction_speed",
|
"value": "retraction_speed",
|
||||||
"settable_per_mesh": false,
|
"settable_per_mesh": false,
|
||||||
@ -1188,7 +1189,7 @@
|
|||||||
"default_value": 25,
|
"default_value": 25,
|
||||||
"minimum_value": "0",
|
"minimum_value": "0",
|
||||||
"maximum_value": "machine_max_feedrate_e",
|
"maximum_value": "machine_max_feedrate_e",
|
||||||
"maximum_value_warning": "100",
|
"maximum_value_warning": "25",
|
||||||
"enabled": "retraction_enable",
|
"enabled": "retraction_enable",
|
||||||
"value": "retraction_speed",
|
"value": "retraction_speed",
|
||||||
"settable_per_mesh": false,
|
"settable_per_mesh": false,
|
||||||
@ -3207,6 +3208,7 @@
|
|||||||
"type": "category",
|
"type": "category",
|
||||||
"icon": "category_dual",
|
"icon": "category_dual",
|
||||||
"description": "Settings used for printing with multiple extruders.",
|
"description": "Settings used for printing with multiple extruders.",
|
||||||
|
"enabled": "machine_extruder_count > 1",
|
||||||
"children":
|
"children":
|
||||||
{
|
{
|
||||||
"adhesion_extruder_nr":
|
"adhesion_extruder_nr":
|
||||||
@ -3284,6 +3286,7 @@
|
|||||||
"default_value": 15,
|
"default_value": 15,
|
||||||
"value": "15 if prime_tower_enable else 0",
|
"value": "15 if prime_tower_enable else 0",
|
||||||
"minimum_value": "0",
|
"minimum_value": "0",
|
||||||
|
"maximum_value": "min(0.5 * machine_width, 0.5 * machine_depth)",
|
||||||
"maximum_value_warning": "20",
|
"maximum_value_warning": "20",
|
||||||
"settable_per_mesh": false,
|
"settable_per_mesh": false,
|
||||||
"settable_per_extruder": false
|
"settable_per_extruder": false
|
||||||
|
@ -9,6 +9,9 @@
|
|||||||
"visible": false
|
"visible": false
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
|
"machine_max_feedrate_e": {
|
||||||
|
"default_value": 45
|
||||||
|
},
|
||||||
"material_print_temperature": {
|
"material_print_temperature": {
|
||||||
"minimum_value": "0"
|
"minimum_value": "0"
|
||||||
},
|
},
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
"platform": "ultimaker2_platform.obj",
|
"platform": "ultimaker2_platform.obj",
|
||||||
"platform_texture": "Ultimaker2backplate.png",
|
"platform_texture": "Ultimaker2backplate.png",
|
||||||
"platform_offset": [9, 0, 0],
|
"platform_offset": [9, 0, 0],
|
||||||
|
"has_materials": false,
|
||||||
"supported_actions":["UpgradeFirmware"]
|
"supported_actions":["UpgradeFirmware"]
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
|
@ -78,7 +78,7 @@
|
|||||||
"default_value": 185
|
"default_value": 185
|
||||||
},
|
},
|
||||||
"prime_tower_position_y": {
|
"prime_tower_position_y": {
|
||||||
"default_value": 175
|
"default_value": 160
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,11 +14,10 @@ Generic Nylon profile. Serves as an example file, data in this file is not corre
|
|||||||
<color_code>#3DF266</color_code>
|
<color_code>#3DF266</color_code>
|
||||||
</metadata>
|
</metadata>
|
||||||
<properties>
|
<properties>
|
||||||
<density>1.19</density>
|
<density>1.14</density>
|
||||||
<diameter>2.85</diameter>
|
<diameter>2.85</diameter>
|
||||||
</properties>
|
</properties>
|
||||||
<settings>
|
<settings>
|
||||||
<setting key="hardware compatible">no</setting><!-- This material is not supported on most printers due to high temperatures -->
|
|
||||||
<setting key="print temperature">250</setting>
|
<setting key="print temperature">250</setting>
|
||||||
<setting key="heated bed temperature">60</setting>
|
<setting key="heated bed temperature">60</setting>
|
||||||
<setting key="standby temperature">175</setting>
|
<setting key="standby temperature">175</setting>
|
||||||
|
@ -14,7 +14,7 @@ Generic PC profile. Serves as an example file, data in this file is not correct.
|
|||||||
<color_code>#F29030</color_code>
|
<color_code>#F29030</color_code>
|
||||||
</metadata>
|
</metadata>
|
||||||
<properties>
|
<properties>
|
||||||
<density>1.18</density>
|
<density>1.19</density>
|
||||||
<diameter>2.85</diameter>
|
<diameter>2.85</diameter>
|
||||||
</properties>
|
</properties>
|
||||||
<settings>
|
<settings>
|
||||||
|
@ -14,7 +14,7 @@ Generic TPU 95A profile. Serves as an example file, data in this file is not cor
|
|||||||
<color_code>#B22744</color_code>
|
<color_code>#B22744</color_code>
|
||||||
</metadata>
|
</metadata>
|
||||||
<properties>
|
<properties>
|
||||||
<density>1.19</density>
|
<density>1.22</density>
|
||||||
<diameter>2.85</diameter>
|
<diameter>2.85</diameter>
|
||||||
</properties>
|
</properties>
|
||||||
<settings>
|
<settings>
|
||||||
|
@ -464,12 +464,11 @@ UM.MainWindow
|
|||||||
target: Cura.Actions.addProfile
|
target: Cura.Actions.addProfile
|
||||||
onTriggered:
|
onTriggered:
|
||||||
{
|
{
|
||||||
Cura.ContainerManager.createQualityChanges(null);
|
|
||||||
preferences.setPage(4);
|
preferences.setPage(4);
|
||||||
preferences.show();
|
preferences.show();
|
||||||
|
|
||||||
// Show the renameDialog after a very short delay so the preference page has time to initiate
|
// Create a new profile after a very short delay so the preference page has time to initiate
|
||||||
showProfileNameDialogTimer.start();
|
createProfileTimer.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -516,11 +515,11 @@ UM.MainWindow
|
|||||||
|
|
||||||
Timer
|
Timer
|
||||||
{
|
{
|
||||||
id: showProfileNameDialogTimer
|
id: createProfileTimer
|
||||||
repeat: false
|
repeat: false
|
||||||
interval: 1
|
interval: 1
|
||||||
|
|
||||||
onTriggered: preferences.getCurrentItem().showProfileNameDialog()
|
onTriggered: preferences.getCurrentItem().createProfile()
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlurSettings is a way to force the focus away from any of the setting items.
|
// BlurSettings is a way to force the focus away from any of the setting items.
|
||||||
|
@ -115,7 +115,7 @@ TabView
|
|||||||
width: base.secondColumnWidth;
|
width: base.secondColumnWidth;
|
||||||
value: properties.density;
|
value: properties.density;
|
||||||
decimals: 2
|
decimals: 2
|
||||||
suffix: "g/cm"
|
suffix: "g/cm³"
|
||||||
stepSize: 0.01
|
stepSize: 0.01
|
||||||
readOnly: !base.editingEnabled;
|
readOnly: !base.editingEnabled;
|
||||||
|
|
||||||
@ -128,7 +128,7 @@ TabView
|
|||||||
width: base.secondColumnWidth;
|
width: base.secondColumnWidth;
|
||||||
value: properties.diameter;
|
value: properties.diameter;
|
||||||
decimals: 2
|
decimals: 2
|
||||||
suffix: "mm³"
|
suffix: "mm"
|
||||||
stepSize: 0.01
|
stepSize: 0.01
|
||||||
readOnly: !base.editingEnabled;
|
readOnly: !base.editingEnabled;
|
||||||
|
|
||||||
|
@ -129,30 +129,24 @@ UM.ManagementPage
|
|||||||
enabled: base.currentItem != null && base.currentItem.id != Cura.MachineManager.activeMaterialId
|
enabled: base.currentItem != null && base.currentItem.id != Cura.MachineManager.activeMaterialId
|
||||||
onClicked: Cura.MachineManager.setActiveMaterial(base.currentItem.id)
|
onClicked: Cura.MachineManager.setActiveMaterial(base.currentItem.id)
|
||||||
},
|
},
|
||||||
/* // apparently visible does not work on OS X
|
// apparently visible does not work on OS X
|
||||||
Button
|
/*Button
|
||||||
{
|
{
|
||||||
text: catalog.i18nc("@action:button", "Duplicate");
|
text: catalog.i18nc("@action:button", "Duplicate");
|
||||||
iconName: "list-add";
|
iconName: "list-add";
|
||||||
enabled: base.currentItem != null
|
enabled: base.currentItem != null
|
||||||
onClicked:
|
onClicked:
|
||||||
{
|
{
|
||||||
var material_id = Cura.ContainerManager.duplicateContainer(base.currentItem.id)
|
var base_file = Cura.ContainerManager.getContainerMetaDataEntry(base.currentItem.id, "base_file")
|
||||||
|
// We need to copy the base container instead of the specific variant.
|
||||||
|
var material_id = base_file == "" ? Cura.ContainerManager.duplicateMaterial(base.currentItem.id): Cura.ContainerManager.duplicateMaterial(base_file)
|
||||||
if(material_id == "")
|
if(material_id == "")
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Cura.MachineManager.filterQualityByMachine)
|
|
||||||
{
|
|
||||||
var quality_id = Cura.ContainerManager.duplicateContainer(Cura.MachineManager.activeQualityId)
|
|
||||||
Cura.ContainerManager.setContainerMetaDataEntry(quality_id, "material", material_id)
|
|
||||||
Cura.MachineManager.setActiveQuality(quality_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
Cura.MachineManager.setActiveMaterial(material_id)
|
Cura.MachineManager.setActiveMaterial(material_id)
|
||||||
}
|
}
|
||||||
visible: false;
|
|
||||||
},
|
},
|
||||||
*/
|
*/
|
||||||
Button
|
Button
|
||||||
|
@ -84,7 +84,7 @@ UM.ManagementPage
|
|||||||
|
|
||||||
onClicked:
|
onClicked:
|
||||||
{
|
{
|
||||||
newNameDialog.object = base.currentItem != null ? base.currentItem.name : "";
|
newNameDialog.object = base.currentItem != null ? Cura.ContainerManager.makeUniqueName(base.currentItem.name) : "";
|
||||||
newNameDialog.open();
|
newNameDialog.open();
|
||||||
newNameDialog.selectText();
|
newNameDialog.selectText();
|
||||||
}
|
}
|
||||||
@ -100,7 +100,7 @@ UM.ManagementPage
|
|||||||
|
|
||||||
onClicked:
|
onClicked:
|
||||||
{
|
{
|
||||||
newDuplicateNameDialog.object = base.currentItem.name;
|
newDuplicateNameDialog.object = Cura.ContainerManager.makeUniqueName(base.currentItem.name);
|
||||||
newDuplicateNameDialog.open();
|
newDuplicateNameDialog.open();
|
||||||
newDuplicateNameDialog.selectText();
|
newDuplicateNameDialog.selectText();
|
||||||
}
|
}
|
||||||
@ -141,11 +141,12 @@ UM.ManagementPage
|
|||||||
|
|
||||||
scrollviewCaption: catalog.i18nc("@label %1 is printer name","Printer: %1").arg(Cura.MachineManager.activeMachineName)
|
scrollviewCaption: catalog.i18nc("@label %1 is printer name","Printer: %1").arg(Cura.MachineManager.activeMachineName)
|
||||||
|
|
||||||
signal showProfileNameDialog()
|
signal createProfile()
|
||||||
onShowProfileNameDialog:
|
onCreateProfile:
|
||||||
{
|
{
|
||||||
renameDialog.open();
|
newNameDialog.object = base.currentItem != null ? Cura.ContainerManager.makeUniqueName(base.currentItem.name) : "";
|
||||||
renameDialog.selectText();
|
newNameDialog.open();
|
||||||
|
newNameDialog.selectText();
|
||||||
}
|
}
|
||||||
|
|
||||||
signal selectContainer(string name)
|
signal selectContainer(string name)
|
||||||
@ -267,6 +268,7 @@ UM.ManagementPage
|
|||||||
|
|
||||||
UM.RenameDialog
|
UM.RenameDialog
|
||||||
{
|
{
|
||||||
|
title: catalog.i18nc("@title:window", "Rename Profile")
|
||||||
id: renameDialog;
|
id: renameDialog;
|
||||||
object: base.currentItem != null ? base.currentItem.name : ""
|
object: base.currentItem != null ? base.currentItem.name : ""
|
||||||
onAccepted:
|
onAccepted:
|
||||||
@ -279,6 +281,7 @@ UM.ManagementPage
|
|||||||
// Dialog to request a name when creating a new profile
|
// Dialog to request a name when creating a new profile
|
||||||
UM.RenameDialog
|
UM.RenameDialog
|
||||||
{
|
{
|
||||||
|
title: catalog.i18nc("@title:window", "Create Profile")
|
||||||
id: newNameDialog;
|
id: newNameDialog;
|
||||||
object: "<new name>";
|
object: "<new name>";
|
||||||
onAccepted:
|
onAccepted:
|
||||||
@ -292,6 +295,7 @@ UM.ManagementPage
|
|||||||
// Dialog to request a name when duplicating a new profile
|
// Dialog to request a name when duplicating a new profile
|
||||||
UM.RenameDialog
|
UM.RenameDialog
|
||||||
{
|
{
|
||||||
|
title: catalog.i18nc("@title:window", "Duplicate Profile")
|
||||||
id: newDuplicateNameDialog;
|
id: newDuplicateNameDialog;
|
||||||
object: "<new name>";
|
object: "<new name>";
|
||||||
onAccepted:
|
onAccepted:
|
||||||
|
@ -138,7 +138,7 @@ Item {
|
|||||||
{
|
{
|
||||||
id: linkedSettingIcon;
|
id: linkedSettingIcon;
|
||||||
|
|
||||||
visible: Cura.MachineManager.activeStackId != Cura.MachineManager.activeMachineId && !definition.settable_per_extruder && base.showLinkedSettingIcon
|
visible: Cura.MachineManager.activeStackId != Cura.MachineManager.activeMachineId && (!definition.settable_per_extruder || definition.global_inherits_stack != "-1") && base.showLinkedSettingIcon
|
||||||
|
|
||||||
height: parent.height;
|
height: parent.height;
|
||||||
width: height;
|
width: height;
|
||||||
|
@ -40,7 +40,7 @@ ScrollView
|
|||||||
id: delegate
|
id: delegate
|
||||||
|
|
||||||
width: UM.Theme.getSize("sidebar").width;
|
width: UM.Theme.getSize("sidebar").width;
|
||||||
height: provider.properties.enabled == "True" ? UM.Theme.getSize("section").height : 0
|
height: provider.properties.enabled == "True" ? UM.Theme.getSize("section").height : - contents.spacing
|
||||||
Behavior on height { NumberAnimation { duration: 100 } }
|
Behavior on height { NumberAnimation { duration: 100 } }
|
||||||
opacity: provider.properties.enabled == "True" ? 1 : 0
|
opacity: provider.properties.enabled == "True" ? 1 : 0
|
||||||
Behavior on opacity { NumberAnimation { duration: 100 } }
|
Behavior on opacity { NumberAnimation { duration: 100 } }
|
||||||
|
@ -199,18 +199,32 @@ QtObject {
|
|||||||
|
|
||||||
property Component progressbar: Component{
|
property Component progressbar: Component{
|
||||||
ProgressBarStyle {
|
ProgressBarStyle {
|
||||||
background:Rectangle {
|
background: Rectangle {
|
||||||
implicitWidth: Theme.getSize("message").width - (Theme.getSize("default_margin").width * 2)
|
implicitWidth: Theme.getSize("message").width - (Theme.getSize("default_margin").width * 2)
|
||||||
implicitHeight: Theme.getSize("progressbar").height
|
implicitHeight: Theme.getSize("progressbar").height
|
||||||
radius: Theme.getSize("progressbar_radius").width
|
radius: Theme.getSize("progressbar_radius").width
|
||||||
color: Theme.getColor("progressbar_background")
|
color: control.hasOwnProperty("backgroundColor") ? control.backgroundColor : Theme.getColor("progressbar_background")
|
||||||
}
|
}
|
||||||
progress: Rectangle {
|
progress: Rectangle {
|
||||||
color: control.indeterminate ? "transparent" : Theme.getColor("progressbar_control")
|
color:
|
||||||
|
{
|
||||||
|
if(control.indeterminate)
|
||||||
|
{
|
||||||
|
return "transparent";
|
||||||
|
}
|
||||||
|
else if(control.hasOwnProperty("controlColor"))
|
||||||
|
{
|
||||||
|
return control.controlColor;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Theme.getColor("progressbar_control");
|
||||||
|
}
|
||||||
|
}
|
||||||
radius: Theme.getSize("progressbar_radius").width
|
radius: Theme.getSize("progressbar_radius").width
|
||||||
Rectangle{
|
Rectangle{
|
||||||
radius: Theme.getSize("progressbar_radius").width
|
radius: Theme.getSize("progressbar_radius").width
|
||||||
color: Theme.getColor("progressbar_control")
|
color: control.hasOwnProperty("controlColor") ? control.controlColor : Theme.getColor("progressbar_control")
|
||||||
width: Theme.getSize("progressbar_control").width
|
width: Theme.getSize("progressbar_control").width
|
||||||
height: Theme.getSize("progressbar_control").height
|
height: Theme.getSize("progressbar_control").height
|
||||||
visible: control.indeterminate
|
visible: control.indeterminate
|
||||||
|
@ -159,9 +159,17 @@
|
|||||||
"tooltip": [12, 169, 227, 255],
|
"tooltip": [12, 169, 227, 255],
|
||||||
"tooltip_text": [255, 255, 255, 255],
|
"tooltip_text": [255, 255, 255, 255],
|
||||||
|
|
||||||
"message_background": [255, 255, 255, 255],
|
"message_background": [24, 41, 77, 255],
|
||||||
"message_text": [32, 166, 219, 255],
|
"message_text": [255, 255, 255, 255],
|
||||||
"message_dismiss": [127, 127, 127, 255],
|
"message_border": [24, 41, 77, 255],
|
||||||
|
"message_button": [255, 255, 255, 255],
|
||||||
|
"message_button_hover": [12, 169, 227, 255],
|
||||||
|
"message_button_active": [32, 166, 219, 255],
|
||||||
|
"message_button_text": [24, 41, 77, 255],
|
||||||
|
"message_button_text_hover": [255, 255, 255, 255],
|
||||||
|
"message_button_text_active": [255, 255, 255, 255],
|
||||||
|
"message_progressbar_background": [255, 255, 255, 255],
|
||||||
|
"message_progressbar_control": [12, 169, 227, 255],
|
||||||
|
|
||||||
"tool_panel_background": [255, 255, 255, 255],
|
"tool_panel_background": [255, 255, 255, 255],
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user