Merge pull request #2 from Ultimaker/master

update
This commit is contained in:
MaukCC 2017-04-18 09:27:48 +02:00 committed by GitHub
commit 817cff9d2f
149 changed files with 84777 additions and 67362 deletions

18
.gitignore vendored
View File

@ -19,6 +19,7 @@ LC_MESSAGES
*~ *~
*.qm *.qm
.idea .idea
cura.desktop
# Eclipse+PyDev # Eclipse+PyDev
.project .project
@ -33,4 +34,21 @@ plugins/Doodle3D-cura-plugin
plugins/GodMode plugins/GodMode
plugins/PostProcessingPlugin plugins/PostProcessingPlugin
plugins/X3GWriter plugins/X3GWriter
plugins/FlatProfileExporter
plugins/cura-god-mode-plugin
#Build stuff
CMakeCache.txt
CMakeFiles
CPackSourceConfig.cmake
Testing/
CTestTestfile.cmake
Makefile*
junit-pytest-*
CuraVersion.py
cmake_install.cmake
#Debug
*.gcode
run.sh

View File

@ -22,6 +22,9 @@ set(CURA_BUILDTYPE "" CACHE STRING "Build type of Cura, eg. 'PPA'")
configure_file(${CMAKE_SOURCE_DIR}/cura.desktop.in ${CMAKE_BINARY_DIR}/cura.desktop @ONLY) configure_file(${CMAKE_SOURCE_DIR}/cura.desktop.in ${CMAKE_BINARY_DIR}/cura.desktop @ONLY)
configure_file(cura/CuraVersion.py.in CuraVersion.py @ONLY) configure_file(cura/CuraVersion.py.in CuraVersion.py @ONLY)
if(NOT ${URANIUM_DIR} STREQUAL "")
set(CMAKE_MODULE_PATH "${URANIUM_DIR}/cmake")
endif()
if(NOT ${URANIUM_SCRIPTS_DIR} STREQUAL "") if(NOT ${URANIUM_SCRIPTS_DIR} STREQUAL "")
list(APPEND CMAKE_MODULE_PATH ${URANIUM_DIR}/cmake) list(APPEND CMAKE_MODULE_PATH ${URANIUM_DIR}/cmake)
include(UraniumTranslationTools) include(UraniumTranslationTools)
@ -64,5 +67,3 @@ else()
install(FILES ${CMAKE_BINARY_DIR}/CuraVersion.py install(FILES ${CMAKE_BINARY_DIR}/CuraVersion.py
DESTINATION lib/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages/cura) DESTINATION lib/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages/cura)
endif() endif()
include(CPackConfig.cmake)

View File

@ -1,16 +0,0 @@
set(CPACK_PACKAGE_VENDOR "Ultimaker B.V.")
set(CPACK_PACKAGE_CONTACT "Arjen Hiemstra <a.hiemstra@ultimaker.com>")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Cura application to drive the CuraEngine")
set(CPACK_PACKAGE_VERSION_MAJOR 15)
set(CPACK_PACKAGE_VERSION_MINOR 05)
set(CPACK_PACKAGE_VERSION_PATCH 90)
set(CPACK_PACKAGE_VERSION_REVISION 1)
set(CPACK_GENERATOR "DEB")
set(DEB_DEPENDS
"uranium (>= 15.05.93)"
)
string(REPLACE ";" ", " DEB_DEPENDS "${DEB_DEPENDS}")
set(CPACK_DEBIAN_PACKAGE_DEPENDS ${DEB_DEPENDS})
include(CPack)

188
cura/Arrange.py Executable file
View File

@ -0,0 +1,188 @@
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Logger import Logger
from UM.Math.Vector import Vector
from cura.ShapeArray import ShapeArray
from cura import ZOffsetDecorator
from collections import namedtuple
import numpy
import copy
## Return object for bestSpot
LocationSuggestion = namedtuple("LocationSuggestion", ["x", "y", "penalty_points", "priority"])
## The Arrange classed is used together with ShapeArray. Use it to find
# good locations for objects that you try to put on a build place.
# Different priority schemes can be defined so it alters the behavior while using
# the same logic.
class Arrange:
build_volume = None
def __init__(self, x, y, offset_x, offset_y, scale= 1.0):
self.shape = (y, x)
self._priority = numpy.zeros((x, y), dtype=numpy.int32)
self._priority_unique_values = []
self._occupied = numpy.zeros((x, y), dtype=numpy.int32)
self._scale = scale # convert input coordinates to arrange coordinates
self._offset_x = offset_x
self._offset_y = offset_y
self._last_priority = 0
## Helper to create an Arranger instance
#
# Either fill in scene_root and create will find all sliceable nodes by itself,
# or use fixed_nodes to provide the nodes yourself.
# \param scene_root Root for finding all scene nodes
# \param fixed_nodes Scene nodes to be placed
@classmethod
def create(cls, scene_root = None, fixed_nodes = None, scale = 0.5):
arranger = Arrange(220, 220, 110, 110, scale = scale)
arranger.centerFirst()
if fixed_nodes is None:
fixed_nodes = []
for node_ in DepthFirstIterator(scene_root):
# Only count sliceable objects
if node_.callDecoration("isSliceable"):
fixed_nodes.append(node_)
# Place all objects fixed nodes
for fixed_node in fixed_nodes:
vertices = fixed_node.callDecoration("getConvexHull")
points = copy.deepcopy(vertices._points)
shape_arr = ShapeArray.fromPolygon(points, scale = scale)
arranger.place(0, 0, shape_arr)
# If a build volume was set, add the disallowed areas
if Arrange.build_volume:
disallowed_areas = Arrange.build_volume.getDisallowedAreas()
for area in disallowed_areas:
points = copy.deepcopy(area._points)
shape_arr = ShapeArray.fromPolygon(points, scale = scale)
arranger.place(0, 0, shape_arr)
return arranger
## Find placement for a node (using offset shape) and place it (using hull shape)
# return the nodes that should be placed
# \param node
# \param offset_shape_arr ShapeArray with offset, used to find location
# \param hull_shape_arr ShapeArray without offset, for placing the shape
def findNodePlacement(self, node, offset_shape_arr, hull_shape_arr, step = 1):
new_node = copy.deepcopy(node)
best_spot = self.bestSpot(
offset_shape_arr, start_prio = self._last_priority, step = step)
x, y = best_spot.x, best_spot.y
# Save the last priority.
self._last_priority = best_spot.priority
# Ensure that the object is above the build platform
new_node.removeDecorator(ZOffsetDecorator.ZOffsetDecorator)
if new_node.getBoundingBox():
center_y = new_node.getWorldPosition().y - new_node.getBoundingBox().bottom
else:
center_y = 0
if x is not None: # We could find a place
new_node.setPosition(Vector(x, center_y, y))
found_spot = True
self.place(x, y, hull_shape_arr) # place the object in arranger
else:
Logger.log("d", "Could not find spot!"),
found_spot = False
new_node.setPosition(Vector(200, center_y, 100))
return new_node, found_spot
## Fill priority, center is best. Lower value is better
# This is a strategy for the arranger.
def centerFirst(self):
# Square distance: creates a more round shape
self._priority = numpy.fromfunction(
lambda i, j: (self._offset_x - i) ** 2 + (self._offset_y - j) ** 2, self.shape, dtype=numpy.int32)
self._priority_unique_values = numpy.unique(self._priority)
self._priority_unique_values.sort()
## Fill priority, back is best. Lower value is better
# This is a strategy for the arranger.
def backFirst(self):
self._priority = numpy.fromfunction(
lambda i, j: 10 * j + abs(self._offset_x - i), self.shape, dtype=numpy.int32)
self._priority_unique_values = numpy.unique(self._priority)
self._priority_unique_values.sort()
## Return the amount of "penalty points" for polygon, which is the sum of priority
# None if occupied
# \param x x-coordinate to check shape
# \param y y-coordinate
# \param shape_arr the ShapeArray object to place
def checkShape(self, x, y, shape_arr):
x = int(self._scale * x)
y = int(self._scale * y)
offset_x = x + self._offset_x + shape_arr.offset_x
offset_y = y + self._offset_y + shape_arr.offset_y
occupied_slice = self._occupied[
offset_y:offset_y + shape_arr.arr.shape[0],
offset_x:offset_x + shape_arr.arr.shape[1]]
try:
if numpy.any(occupied_slice[numpy.where(shape_arr.arr == 1)]):
return None
except IndexError: # out of bounds if you try to place an object outside
return None
prio_slice = self._priority[
offset_y:offset_y + shape_arr.arr.shape[0],
offset_x:offset_x + shape_arr.arr.shape[1]]
return numpy.sum(prio_slice[numpy.where(shape_arr.arr == 1)])
## Find "best" spot for ShapeArray
# Return namedtuple with properties x, y, penalty_points, priority
# \param shape_arr ShapeArray
# \param start_prio Start with this priority value (and skip the ones before)
# \param step Slicing value, higher = more skips = faster but less accurate
def bestSpot(self, shape_arr, start_prio = 0, step = 1):
start_idx_list = numpy.where(self._priority_unique_values == start_prio)
if start_idx_list:
start_idx = start_idx_list[0][0]
else:
start_idx = 0
for priority in self._priority_unique_values[start_idx::step]:
tryout_idx = numpy.where(self._priority == priority)
for idx in range(len(tryout_idx[0])):
x = tryout_idx[0][idx]
y = tryout_idx[1][idx]
projected_x = x - self._offset_x
projected_y = y - self._offset_y
# array to "world" coordinates
penalty_points = self.checkShape(projected_x, projected_y, shape_arr)
if penalty_points is not None:
return LocationSuggestion(x = projected_x, y = projected_y, penalty_points = penalty_points, priority = priority)
return LocationSuggestion(x = None, y = None, penalty_points = None, priority = priority) # No suitable location found :-(
## Place the object.
# Marks the locations in self._occupied and self._priority
# \param x x-coordinate
# \param y y-coordinate
# \param shape_arr ShapeArray object
def place(self, x, y, shape_arr):
x = int(self._scale * x)
y = int(self._scale * y)
offset_x = x + self._offset_x + shape_arr.offset_x
offset_y = y + self._offset_y + shape_arr.offset_y
shape_y, shape_x = self._occupied.shape
min_x = min(max(offset_x, 0), shape_x - 1)
min_y = min(max(offset_y, 0), shape_y - 1)
max_x = min(max(offset_x + shape_arr.arr.shape[1], 0), shape_x - 1)
max_y = min(max(offset_y + shape_arr.arr.shape[0], 0), shape_y - 1)
occupied_slice = self._occupied[min_y:max_y, min_x:max_x]
# we use a slice of shape because it can be out of bounds
occupied_slice[numpy.where(shape_arr.arr[
min_y - offset_y:max_y - offset_y, min_x - offset_x:max_x - offset_x] == 1)] = 1
# Set priority to low (= high number), so it won't get picked at trying out.
prio_slice = self._priority[min_y:max_y, min_x:max_x]
prio_slice[numpy.where(shape_arr.arr[
min_y - offset_y:max_y - offset_y, min_x - offset_x:max_x - offset_x] == 1)] = 999

86
cura/ArrangeObjectsJob.py Executable file
View File

@ -0,0 +1,86 @@
# Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.
from UM.Job import Job
from UM.Scene.SceneNode import SceneNode
from UM.Math.Vector import Vector
from UM.Operations.SetTransformOperation import SetTransformOperation
from UM.Operations.TranslateOperation import TranslateOperation
from UM.Operations.GroupedOperation import GroupedOperation
from UM.Logger import Logger
from UM.Message import Message
from UM.i18n import i18nCatalog
i18n_catalog = i18nCatalog("cura")
from cura.ZOffsetDecorator import ZOffsetDecorator
from cura.Arrange import Arrange
from cura.ShapeArray import ShapeArray
from typing import List
class ArrangeObjectsJob(Job):
def __init__(self, nodes: List[SceneNode], fixed_nodes: List[SceneNode], min_offset = 8):
super().__init__()
self._nodes = nodes
self._fixed_nodes = fixed_nodes
self._min_offset = min_offset
def run(self):
status_message = Message(i18n_catalog.i18nc("@info:status", "Finding new location for objects"), lifetime = 0, dismissable=False, progress = 0)
status_message.show()
arranger = Arrange.create(fixed_nodes = self._fixed_nodes)
# Collect nodes to be placed
nodes_arr = [] # fill with (size, node, offset_shape_arr, hull_shape_arr)
for node in self._nodes:
offset_shape_arr, hull_shape_arr = ShapeArray.fromNode(node, min_offset = self._min_offset)
nodes_arr.append((offset_shape_arr.arr.shape[0] * offset_shape_arr.arr.shape[1], node, offset_shape_arr, hull_shape_arr))
# Sort the nodes with the biggest area first.
nodes_arr.sort(key=lambda item: item[0])
nodes_arr.reverse()
# Place nodes one at a time
start_priority = 0
last_priority = start_priority
last_size = None
grouped_operation = GroupedOperation()
found_solution_for_all = True
for idx, (size, node, offset_shape_arr, hull_shape_arr) in enumerate(nodes_arr):
# For performance reasons, we assume that when a location does not fit,
# it will also not fit for the next object (while what can be untrue).
# We also skip possibilities by slicing through the possibilities (step = 10)
if last_size == size: # This optimization works if many of the objects have the same size
start_priority = last_priority
else:
start_priority = 0
best_spot = arranger.bestSpot(offset_shape_arr, start_prio=start_priority, step=10)
x, y = best_spot.x, best_spot.y
node.removeDecorator(ZOffsetDecorator)
if node.getBoundingBox():
center_y = node.getWorldPosition().y - node.getBoundingBox().bottom
else:
center_y = 0
if x is not None: # We could find a place
last_size = size
last_priority = best_spot.priority
arranger.place(x, y, hull_shape_arr) # take place before the next one
grouped_operation.addOperation(TranslateOperation(node, Vector(x, center_y, y), set_position = True))
else:
Logger.log("d", "Arrange all: could not find spot!")
found_solution_for_all = False
grouped_operation.addOperation(TranslateOperation(node, Vector(200, center_y, - idx * 20), set_position = True))
status_message.setProgress((idx + 1) / len(nodes_arr) * 100)
Job.yieldThread()
grouped_operation.push()
status_message.hide()
if not found_solution_for_all:
no_full_solution_message = Message(i18n_catalog.i18nc("@info:status", "Unable to find a location within the build volume for all objects"))
no_full_solution_message.show()

View File

@ -23,9 +23,10 @@ from UM.View.GL.OpenGL import OpenGL
catalog = i18nCatalog("cura") catalog = i18nCatalog("cura")
import numpy import numpy
import copy
import math import math
from typing import List
# Setting for clearance around the prime # Setting for clearance around the prime
PRIME_CLEARANCE = 6.5 PRIME_CLEARANCE = 6.5
@ -110,10 +111,11 @@ class BuildVolume(SceneNode):
def _onChangeTimerFinished(self): def _onChangeTimerFinished(self):
root = Application.getInstance().getController().getScene().getRoot() root = Application.getInstance().getController().getScene().getRoot()
new_scene_objects = set(node for node in BreadthFirstIterator(root) if node.getMeshData() and type(node) is SceneNode) new_scene_objects = set(node for node in BreadthFirstIterator(root) if node.callDecoration("isSliceable"))
if new_scene_objects != self._scene_objects: if new_scene_objects != self._scene_objects:
for node in new_scene_objects - self._scene_objects: #Nodes that were added to the scene. for node in new_scene_objects - self._scene_objects: #Nodes that were added to the scene.
node.decoratorsChanged.connect(self._onNodeDecoratorChanged) self._updateNodeListeners(node)
node.decoratorsChanged.connect(self._updateNodeListeners) # Make sure that decoration changes afterwards also receive the same treatment
for node in self._scene_objects - new_scene_objects: #Nodes that were removed from the scene. for node in self._scene_objects - new_scene_objects: #Nodes that were removed from the scene.
per_mesh_stack = node.callDecoration("getStack") per_mesh_stack = node.callDecoration("getStack")
if per_mesh_stack: if per_mesh_stack:
@ -121,7 +123,7 @@ class BuildVolume(SceneNode):
active_extruder_changed = node.callDecoration("getActiveExtruderChangedSignal") active_extruder_changed = node.callDecoration("getActiveExtruderChangedSignal")
if active_extruder_changed is not None: if active_extruder_changed is not None:
node.callDecoration("getActiveExtruderChangedSignal").disconnect(self._updateDisallowedAreasAndRebuild) node.callDecoration("getActiveExtruderChangedSignal").disconnect(self._updateDisallowedAreasAndRebuild)
node.decoratorsChanged.disconnect(self._onNodeDecoratorChanged) node.decoratorsChanged.disconnect(self._updateNodeListeners)
self._scene_objects = new_scene_objects self._scene_objects = new_scene_objects
self._onSettingPropertyChanged("print_sequence", "value") # Create fake event, so right settings are triggered. self._onSettingPropertyChanged("print_sequence", "value") # Create fake event, so right settings are triggered.
@ -129,7 +131,7 @@ class BuildVolume(SceneNode):
## Updates the listeners that listen for changes in per-mesh stacks. ## Updates the listeners that listen for changes in per-mesh stacks.
# #
# \param node The node for which the decorators changed. # \param node The node for which the decorators changed.
def _onNodeDecoratorChanged(self, node): def _updateNodeListeners(self, node: SceneNode):
per_mesh_stack = node.callDecoration("getStack") per_mesh_stack = node.callDecoration("getStack")
if per_mesh_stack: if per_mesh_stack:
per_mesh_stack.propertyChanged.connect(self._onSettingPropertyChanged) per_mesh_stack.propertyChanged.connect(self._onSettingPropertyChanged)
@ -139,21 +141,25 @@ class BuildVolume(SceneNode):
self._updateDisallowedAreasAndRebuild() self._updateDisallowedAreasAndRebuild()
def setWidth(self, width): def setWidth(self, width):
if width: self._width = width if width is not None:
self._width = width
def setHeight(self, height): def setHeight(self, height):
if height: self._height = height if height is not None:
self._height = height
def setDepth(self, depth): def setDepth(self, depth):
if depth: self._depth = depth if depth is not None:
self._depth = depth
def setShape(self, shape): def setShape(self, shape: str):
if shape: self._shape = shape if shape:
self._shape = shape
def getDisallowedAreas(self): def getDisallowedAreas(self) -> List[Polygon]:
return self._disallowed_areas return self._disallowed_areas
def setDisallowedAreas(self, areas): def setDisallowedAreas(self, areas: List[Polygon]):
self._disallowed_areas = areas self._disallowed_areas = areas
def render(self, renderer): def render(self, renderer):
@ -179,6 +185,53 @@ class BuildVolume(SceneNode):
return True return True
## For every sliceable node, update node._outside_buildarea
#
def updateNodeBoundaryCheck(self):
root = Application.getInstance().getController().getScene().getRoot()
nodes = list(BreadthFirstIterator(root))
group_nodes = []
build_volume_bounding_box = self.getBoundingBox()
if build_volume_bounding_box:
# It's over 9000!
build_volume_bounding_box = build_volume_bounding_box.set(bottom=-9001)
else:
# No bounding box. This is triggered when running Cura from command line with a model for the first time
# In that situation there is a model, but no machine (and therefore no build volume.
return
for node in nodes:
# Need to check group nodes later
if node.callDecoration("isGroup"):
group_nodes.append(node) # Keep list of affected group_nodes
if node.callDecoration("isSliceable") or node.callDecoration("isGroup"):
node._outside_buildarea = False
bbox = node.getBoundingBox()
# Mark the node as outside the build volume if the bounding box test fails.
if build_volume_bounding_box.intersectsBox(bbox) != AxisAlignedBox.IntersectionResult.FullIntersection:
node._outside_buildarea = True
continue
convex_hull = node.callDecoration("getConvexHull")
if convex_hull:
if not convex_hull.isValid():
return
# Check for collisions between disallowed areas and the object
for area in self.getDisallowedAreas():
overlap = convex_hull.intersectsPolygon(area)
if overlap is None:
continue
node._outside_buildarea = True
continue
# 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
## Recalculates the build volume & disallowed areas. ## Recalculates the build volume & disallowed areas.
def rebuild(self): def rebuild(self):
if not self._width or not self._height or not self._depth: if not self._width or not self._height or not self._depth:
@ -362,10 +415,12 @@ class BuildVolume(SceneNode):
Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds
def getBoundingBox(self): self.updateNodeBoundaryCheck()
def getBoundingBox(self) -> AxisAlignedBox:
return self._volume_aabb return self._volume_aabb
def getRaftThickness(self): def getRaftThickness(self) -> float:
return self._raft_thickness return self._raft_thickness
def _updateRaftThickness(self): def _updateRaftThickness(self):
@ -442,7 +497,7 @@ class BuildVolume(SceneNode):
self._engine_ready = True self._engine_ready = True
self.rebuild() self.rebuild()
def _onSettingPropertyChanged(self, setting_key, property_name): def _onSettingPropertyChanged(self, setting_key: str, property_name: str):
if property_name != "value": if property_name != "value":
return return
@ -475,7 +530,7 @@ class BuildVolume(SceneNode):
if rebuild_me: if rebuild_me:
self.rebuild() self.rebuild()
def hasErrors(self): def hasErrors(self) -> bool:
return self._has_errors return self._has_errors
## Calls _updateDisallowedAreas and makes sure the changes appear in the ## Calls _updateDisallowedAreas and makes sure the changes appear in the

View File

@ -59,7 +59,8 @@ class ConvexHullDecorator(SceneNodeDecorator):
hull = self._compute2DConvexHull() hull = self._compute2DConvexHull()
if self._global_stack and self._node: if self._global_stack and self._node:
if self._global_stack.getProperty("print_sequence", "value") == "one_at_a_time" and not self._node.getParent().callDecoration("isGroup"): # Parent can be None if node is just loaded.
if self._global_stack.getProperty("print_sequence", "value") == "one_at_a_time" and (self._node.getParent() is None or not self._node.getParent().callDecoration("isGroup")):
hull = hull.getMinkowskiHull(Polygon(numpy.array(self._global_stack.getProperty("machine_head_polygon", "value"), numpy.float32))) hull = hull.getMinkowskiHull(Polygon(numpy.array(self._global_stack.getProperty("machine_head_polygon", "value"), numpy.float32)))
hull = self._add2DAdhesionMargin(hull) hull = self._add2DAdhesionMargin(hull)
return hull return hull
@ -79,7 +80,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
return None return None
if self._global_stack: if self._global_stack:
if self._global_stack.getProperty("print_sequence", "value") == "one_at_a_time" and not self._node.getParent().callDecoration("isGroup"): if self._global_stack.getProperty("print_sequence", "value") == "one_at_a_time" and (self._node.getParent() is None or not self._node.getParent().callDecoration("isGroup")):
head_with_fans = self._compute2DConvexHeadMin() head_with_fans = self._compute2DConvexHeadMin()
head_with_fans_with_adhesion_margin = self._add2DAdhesionMargin(head_with_fans) head_with_fans_with_adhesion_margin = self._add2DAdhesionMargin(head_with_fans)
return head_with_fans_with_adhesion_margin return head_with_fans_with_adhesion_margin
@ -93,8 +94,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
return None return None
if self._global_stack: if self._global_stack:
if self._global_stack.getProperty("print_sequence", "value") == "one_at_a_time" and not self._node.getParent().callDecoration("isGroup"): if self._global_stack.getProperty("print_sequence", "value") == "one_at_a_time" and (self._node.getParent() is None or not self._node.getParent().callDecoration("isGroup")):
# Printing one at a time and it's not an object in a group # Printing one at a time and it's not an object in a group
return self._compute2DConvexHull() return self._compute2DConvexHull()
return None return None
@ -258,12 +258,16 @@ class ConvexHullDecorator(SceneNodeDecorator):
# influences the collision area. # influences the collision area.
def _offsetHull(self, convex_hull): def _offsetHull(self, convex_hull):
horizontal_expansion = self._getSettingProperty("xy_offset", "value") horizontal_expansion = self._getSettingProperty("xy_offset", "value")
if horizontal_expansion != 0: mold_width = 0
if self._getSettingProperty("mold_enabled", "value"):
mold_width = self._getSettingProperty("mold_width", "value")
hull_offset = horizontal_expansion + mold_width
if hull_offset != 0:
expansion_polygon = Polygon(numpy.array([ expansion_polygon = Polygon(numpy.array([
[-horizontal_expansion, -horizontal_expansion], [-hull_offset, -hull_offset],
[-horizontal_expansion, horizontal_expansion], [-hull_offset, hull_offset],
[horizontal_expansion, horizontal_expansion], [hull_offset, hull_offset],
[horizontal_expansion, -horizontal_expansion] [hull_offset, -hull_offset]
], numpy.float32)) ], numpy.float32))
return convex_hull.getMinkowskiHull(expansion_polygon) return convex_hull.getMinkowskiHull(expansion_polygon)
else: else:
@ -331,4 +335,4 @@ class ConvexHullDecorator(SceneNodeDecorator):
## Settings that change the convex hull. ## Settings that change the convex hull.
# #
# If these settings change, the convex hull should be recalculated. # If these settings change, the convex hull should be recalculated.
_influencing_settings = {"xy_offset"} _influencing_settings = {"xy_offset", "mold_enabled", "mold_width"}

View File

@ -9,7 +9,10 @@ from UM.Mesh.MeshBuilder import MeshBuilder # To create a mesh to display the c
from UM.View.GL.OpenGL import OpenGL from UM.View.GL.OpenGL import OpenGL
class ConvexHullNode(SceneNode): class ConvexHullNode(SceneNode):
shader = None # To prevent the shader from being re-built over and over again, only load it once.
## Convex hull node is a special type of scene node that is used to display an area, to indicate the ## Convex hull node is a special type of scene node that is used to display an area, to indicate the
# location an object uses on the buildplate. This area (or area's in case of one at a time printing) is # location an object uses on the buildplate. This area (or area's in case of one at a time printing) is
# then displayed as a transparent shadow. If the adhesion type is set to raft, the area is extruded # then displayed as a transparent shadow. If the adhesion type is set to raft, the area is extruded
@ -19,8 +22,6 @@ class ConvexHullNode(SceneNode):
self.setCalculateBoundingBox(False) self.setCalculateBoundingBox(False)
self._shader = None
self._original_parent = parent self._original_parent = parent
# Color of the drawn convex hull # Color of the drawn convex hull
@ -59,16 +60,16 @@ class ConvexHullNode(SceneNode):
return self._node return self._node
def render(self, renderer): def render(self, renderer):
if not self._shader: if not ConvexHullNode.shader:
self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "transparent_object.shader")) ConvexHullNode.shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "transparent_object.shader"))
self._shader.setUniformValue("u_diffuseColor", self._color) ConvexHullNode.shader.setUniformValue("u_diffuseColor", self._color)
self._shader.setUniformValue("u_opacity", 0.6) ConvexHullNode.shader.setUniformValue("u_opacity", 0.6)
if self.getParent(): if self.getParent():
if self.getMeshData(): if self.getMeshData():
renderer.queueNode(self, transparent = True, shader = self._shader, backface_cull = True, sort = -8) renderer.queueNode(self, transparent = True, shader = ConvexHullNode.shader, backface_cull = True, sort = -8)
if self._convex_hull_head_mesh: if self._convex_hull_head_mesh:
renderer.queueNode(self, shader = self._shader, transparent = True, mesh = self._convex_hull_head_mesh, backface_cull = True, sort = -8) renderer.queueNode(self, shader = ConvexHullNode.shader, transparent = True, mesh = self._convex_hull_head_mesh, backface_cull = True, sort = -8)
return True return True

View File

@ -4,7 +4,6 @@ from PyQt5.QtNetwork import QLocalServer
from PyQt5.QtNetwork import QLocalSocket from PyQt5.QtNetwork import QLocalSocket
from UM.Qt.QtApplication import QtApplication from UM.Qt.QtApplication import QtApplication
from UM.FileHandler.ReadFileJob import ReadFileJob
from UM.Scene.SceneNode import SceneNode from UM.Scene.SceneNode import SceneNode
from UM.Scene.Camera import Camera from UM.Scene.Camera import Camera
from UM.Math.Vector import Vector from UM.Math.Vector import Vector
@ -17,7 +16,6 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Mesh.ReadMeshJob import ReadMeshJob from UM.Mesh.ReadMeshJob import ReadMeshJob
from UM.Logger import Logger from UM.Logger import Logger
from UM.Preferences import Preferences from UM.Preferences import Preferences
from UM.JobQueue import JobQueue
from UM.SaveFile import SaveFile from UM.SaveFile import SaveFile
from UM.Scene.Selection import Selection from UM.Scene.Selection import Selection
from UM.Scene.GroupDecorator import GroupDecorator from UM.Scene.GroupDecorator import GroupDecorator
@ -33,10 +31,16 @@ from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation
from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation
from UM.Operations.GroupedOperation import GroupedOperation from UM.Operations.GroupedOperation import GroupedOperation
from UM.Operations.SetTransformOperation import SetTransformOperation from UM.Operations.SetTransformOperation import SetTransformOperation
from cura.Arrange import Arrange
from cura.ShapeArray import ShapeArray
from cura.ConvexHullDecorator import ConvexHullDecorator
from cura.SetParentOperation import SetParentOperation from cura.SetParentOperation import SetParentOperation
from cura.SliceableObjectDecorator import SliceableObjectDecorator from cura.SliceableObjectDecorator import SliceableObjectDecorator
from cura.BlockSlicingDecorator import BlockSlicingDecorator from cura.BlockSlicingDecorator import BlockSlicingDecorator
from cura.ArrangeObjectsJob import ArrangeObjectsJob
from cura.MultiplyObjectsJob import MultiplyObjectsJob
from UM.Settings.SettingDefinition import SettingDefinition, DefinitionPropertyType from UM.Settings.SettingDefinition import SettingDefinition, DefinitionPropertyType
from UM.Settings.ContainerRegistry import ContainerRegistry from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.SettingFunction import SettingFunction from UM.Settings.SettingFunction import SettingFunction
@ -90,6 +94,7 @@ if not MYPY:
CuraVersion = "master" # [CodeStyle: Reflecting imported value] CuraVersion = "master" # [CodeStyle: Reflecting imported value]
CuraBuildType = "" CuraBuildType = ""
class CuraApplication(QtApplication): class CuraApplication(QtApplication):
class ResourceTypes: class ResourceTypes:
QmlFiles = Resources.UserType + 1 QmlFiles = Resources.UserType + 1
@ -104,7 +109,6 @@ class CuraApplication(QtApplication):
Q_ENUMS(ResourceTypes) Q_ENUMS(ResourceTypes)
def __init__(self): def __init__(self):
Resources.addSearchPath(os.path.join(QtApplication.getInstallPrefix(), "share", "cura", "resources")) Resources.addSearchPath(os.path.join(QtApplication.getInstallPrefix(), "share", "cura", "resources"))
if not hasattr(sys, "frozen"): if not hasattr(sys, "frozen"):
Resources.addSearchPath(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "resources")) Resources.addSearchPath(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "resources"))
@ -184,7 +188,10 @@ class CuraApplication(QtApplication):
"SelectionTool", "SelectionTool",
"CameraTool", "CameraTool",
"GCodeWriter", "GCodeWriter",
"LocalFileOutputDevice" "LocalFileOutputDevice",
"TranslateTool",
"FileLogger",
"XmlMaterialProfile"
]) ])
self._physics = None self._physics = None
self._volume = None self._volume = None
@ -240,7 +247,7 @@ class CuraApplication(QtApplication):
ContainerRegistry.getInstance().load() ContainerRegistry.getInstance().load()
Preferences.getInstance().addPreference("cura/active_mode", "simple") Preferences.getInstance().addPreference("cura/active_mode", "simple")
Preferences.getInstance().addPreference("cura/recent_files", "")
Preferences.getInstance().addPreference("cura/categories_expanded", "") Preferences.getInstance().addPreference("cura/categories_expanded", "")
Preferences.getInstance().addPreference("cura/jobname_prefix", True) Preferences.getInstance().addPreference("cura/jobname_prefix", True)
Preferences.getInstance().addPreference("view/center_on_select", False) Preferences.getInstance().addPreference("view/center_on_select", False)
@ -316,20 +323,11 @@ class CuraApplication(QtApplication):
experimental experimental
""".replace("\n", ";").replace(" ", "")) """.replace("\n", ";").replace(" ", ""))
JobQueue.getInstance().jobFinished.connect(self._onJobFinished)
self.applicationShuttingDown.connect(self.saveSettings) self.applicationShuttingDown.connect(self.saveSettings)
self.engineCreatedSignal.connect(self._onEngineCreated) self.engineCreatedSignal.connect(self._onEngineCreated)
self.globalContainerStackChanged.connect(self._onGlobalContainerChanged) self.globalContainerStackChanged.connect(self._onGlobalContainerChanged)
self._onGlobalContainerChanged() self._onGlobalContainerChanged()
self._recent_files = []
files = Preferences.getInstance().getValue("cura/recent_files").split(";")
for f in files:
if not os.path.isfile(f):
continue
self._recent_files.append(QUrl.fromLocalFile(f))
def _onEngineCreated(self): def _onEngineCreated(self):
self._engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider()) self._engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider())
@ -594,6 +592,9 @@ class CuraApplication(QtApplication):
# The platform is a child of BuildVolume # The platform is a child of BuildVolume
self._volume = BuildVolume.BuildVolume(root) self._volume = BuildVolume.BuildVolume(root)
# Set the build volume of the arranger to the used build volume
Arrange.build_volume = self._volume
self.getRenderer().setBackgroundColor(QColor(245, 245, 245)) self.getRenderer().setBackgroundColor(QColor(245, 245, 245))
self._physics = PlatformPhysics.PlatformPhysics(controller, self._volume) self._physics = PlatformPhysics.PlatformPhysics(controller, self._volume)
@ -668,6 +669,7 @@ class CuraApplication(QtApplication):
# #
# \param engine The QML engine. # \param engine The QML engine.
def registerObjects(self, engine): def registerObjects(self, engine):
super().registerObjects(engine)
engine.rootContext().setContextProperty("Printer", self) engine.rootContext().setContextProperty("Printer", self)
engine.rootContext().setContextProperty("CuraApplication", self) engine.rootContext().setContextProperty("CuraApplication", self)
self._print_information = PrintInformation.PrintInformation() self._print_information = PrintInformation.PrintInformation()
@ -701,14 +703,11 @@ class CuraApplication(QtApplication):
if type_name in ("Cura", "Actions"): if type_name in ("Cura", "Actions"):
continue continue
qmlRegisterType(QUrl.fromLocalFile(path), "Cura", 1, 0, type_name) # Ignore anything that is not a QML file.
if not path.endswith(".qml"):
continue
## Get the backend of the application (the program that does the heavy lifting). qmlRegisterType(QUrl.fromLocalFile(path), "Cura", 1, 0, type_name)
# The backend is also a QObject, which can be used from qml.
# \returns Backend \type{Backend}
@pyqtSlot(result = "QObject*")
def getBackend(self):
return self._backend
def onSelectionChanged(self): def onSelectionChanged(self):
if Selection.hasSelection(): if Selection.hasSelection():
@ -847,24 +846,14 @@ class CuraApplication(QtApplication):
op.push() op.push()
## Create a number of copies of existing object. ## Create a number of copies of existing object.
# \param object_id
# \param count number of copies
# \param min_offset minimum offset to other objects.
@pyqtSlot("quint64", int) @pyqtSlot("quint64", int)
def multiplyObject(self, object_id, count): def multiplyObject(self, object_id, count, min_offset = 8):
node = self.getController().getScene().findObject(object_id) job = MultiplyObjectsJob(object_id, count, min_offset)
job.start()
if not node and object_id != 0: # Workaround for tool handles overlapping the selected object return
node = Selection.getSelectedObject(0)
if node:
current_node = node
# Find the topmost group
while current_node.getParent() and current_node.getParent().callDecoration("isGroup"):
current_node = current_node.getParent()
op = GroupedOperation()
for _ in range(count):
new_node = copy.deepcopy(current_node)
op.addOperation(AddSceneNodeOperation(new_node, current_node.getParent()))
op.push()
## Center object on platform. ## Center object on platform.
@pyqtSlot("quint64") @pyqtSlot("quint64")
@ -982,6 +971,52 @@ class CuraApplication(QtApplication):
op.addOperation(SetTransformOperation(node, Vector(0, center_y, 0), Quaternion(), Vector(1, 1, 1))) op.addOperation(SetTransformOperation(node, Vector(0, center_y, 0), Quaternion(), Vector(1, 1, 1)))
op.push() op.push()
## Arrange all objects.
@pyqtSlot()
def arrangeAll(self):
nodes = []
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if type(node) is not SceneNode:
continue
if not node.getMeshData() and not node.callDecoration("isGroup"):
continue # Node that doesnt have a mesh and is not a group.
if node.getParent() and node.getParent().callDecoration("isGroup"):
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
if not node.isSelectable():
continue # i.e. node with layer data
# Skip nodes that are too big
if node.getBoundingBox().width < self._volume.getBoundingBox().width or node.getBoundingBox().depth < self._volume.getBoundingBox().depth:
nodes.append(node)
self.arrange(nodes, fixed_nodes = [])
## Arrange Selection
@pyqtSlot()
def arrangeSelection(self):
nodes = Selection.getAllSelectedObjects()
# What nodes are on the build plate and are not being moved
fixed_nodes = []
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if type(node) is not SceneNode:
continue
if not node.getMeshData() and not node.callDecoration("isGroup"):
continue # Node that doesnt have a mesh and is not a group.
if node.getParent() and node.getParent().callDecoration("isGroup"):
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
if not node.isSelectable():
continue # i.e. node with layer data
if node in nodes: # exclude selected node from fixed_nodes
continue
fixed_nodes.append(node)
self.arrange(nodes, fixed_nodes)
## Arrange a set of nodes given a set of fixed nodes
# \param nodes nodes that we have to place
# \param fixed_nodes nodes that are placed in the arranger before finding spots for nodes
def arrange(self, nodes, fixed_nodes):
job = ArrangeObjectsJob(nodes, fixed_nodes)
job.start()
## Reload all mesh data on the screen from file. ## Reload all mesh data on the screen from file.
@pyqtSlot() @pyqtSlot()
def reloadAll(self): def reloadAll(self):
@ -1017,12 +1052,6 @@ class CuraApplication(QtApplication):
return log return log
recentFilesChanged = pyqtSignal()
@pyqtProperty("QVariantList", notify = recentFilesChanged)
def recentFiles(self):
return self._recent_files
@pyqtSlot("QStringList") @pyqtSlot("QStringList")
def setExpandedCategories(self, categories): def setExpandedCategories(self, categories):
categories = list(set(categories)) categories = list(set(categories))
@ -1130,25 +1159,6 @@ class CuraApplication(QtApplication):
fileLoaded = pyqtSignal(str) fileLoaded = pyqtSignal(str)
def _onJobFinished(self, job):
if (not isinstance(job, ReadMeshJob) and not isinstance(job, ReadFileJob)) or not job.getResult():
return
f = QUrl.fromLocalFile(job.getFileName())
if f in self._recent_files:
self._recent_files.remove(f)
self._recent_files.insert(0, f)
if len(self._recent_files) > 10:
del self._recent_files[10]
pref = ""
for path in self._recent_files:
pref += path.toLocalFile() + ";"
Preferences.getInstance().setValue("cura/recent_files", pref)
self.recentFilesChanged.emit()
def _reloadMeshFinished(self, job): def _reloadMeshFinished(self, job):
# TODO; This needs to be fixed properly. We now make the assumption that we only load a single mesh! # TODO; This needs to be fixed properly. We now make the assumption that we only load a single mesh!
mesh_data = job.getResult()[0].getMeshData() mesh_data = job.getResult()[0].getMeshData()
@ -1243,6 +1253,10 @@ class CuraApplication(QtApplication):
filename = job.getFileName() filename = job.getFileName()
self._currently_loading_files.remove(filename) self._currently_loading_files.remove(filename)
root = self.getController().getScene().getRoot()
arranger = Arrange.create(scene_root = root)
min_offset = 8
for node in nodes: for node in nodes:
node.setSelectable(True) node.setSelectable(True)
node.setName(os.path.basename(filename)) node.setName(os.path.basename(filename))
@ -1263,9 +1277,24 @@ class CuraApplication(QtApplication):
scene = self.getController().getScene() scene = self.getController().getScene()
# If there is no convex hull for the node, start calculating it and continue.
if not node.getDecorator(ConvexHullDecorator):
node.addDecorator(ConvexHullDecorator())
for child in node.getAllChildren():
if not child.getDecorator(ConvexHullDecorator):
child.addDecorator(ConvexHullDecorator())
if node.callDecoration("isSliceable"):
# Only check position if it's not already blatantly obvious that it won't fit.
if node.getBoundingBox().width < self._volume.getBoundingBox().width or node.getBoundingBox().depth < self._volume.getBoundingBox().depth:
# Find node location
offset_shape_arr, hull_shape_arr = ShapeArray.fromNode(node, min_offset = min_offset)
# Step is for skipping tests to make it a lot faster. it also makes the outcome somewhat rougher
node, _ = arranger.findNodePlacement(node, offset_shape_arr, hull_shape_arr, step = 10)
op = AddSceneNodeOperation(node, scene.getRoot()) op = AddSceneNodeOperation(node, scene.getRoot())
op.push() op.push()
scene.sceneChanged.emit(node) scene.sceneChanged.emit(node)
def addNonSliceableExtension(self, extension): def addNonSliceableExtension(self, extension):
@ -1276,10 +1305,14 @@ class CuraApplication(QtApplication):
""" """
Checks if the given file URL is a valid project file. Checks if the given file URL is a valid project file.
""" """
file_path = QUrl(file_url).toLocalFile() try:
workspace_reader = self.getWorkspaceFileHandler().getReaderForFile(file_path) file_path = QUrl(file_url).toLocalFile()
if workspace_reader is None: workspace_reader = self.getWorkspaceFileHandler().getReaderForFile(file_path)
return False # non-project files won't get a reader if workspace_reader is None:
return False # non-project files won't get a reader
result = workspace_reader.preRead(file_path, show_dialog=False) result = workspace_reader.preRead(file_path, show_dialog=False)
return result == WorkspaceReader.PreReadResult.accepted return result == WorkspaceReader.PreReadResult.accepted
except Exception as e:
Logger.log("e", "Could not check file %s: %s", file_url, e)
return False

View File

@ -0,0 +1,80 @@
# Copyright (c) 2017 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.
from UM.Job import Job
from UM.Scene.SceneNode import SceneNode
from UM.Math.Vector import Vector
from UM.Operations.SetTransformOperation import SetTransformOperation
from UM.Operations.TranslateOperation import TranslateOperation
from UM.Operations.GroupedOperation import GroupedOperation
from UM.Logger import Logger
from UM.Message import Message
from UM.i18n import i18nCatalog
i18n_catalog = i18nCatalog("cura")
from cura.ZOffsetDecorator import ZOffsetDecorator
from cura.Arrange import Arrange
from cura.ShapeArray import ShapeArray
from typing import List
from UM.Application import Application
from UM.Scene.Selection import Selection
from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation
class MultiplyObjectsJob(Job):
def __init__(self, object_id, count, min_offset = 8):
super().__init__()
self._object_id = object_id
self._count = count
self._min_offset = min_offset
def run(self):
status_message = Message(i18n_catalog.i18nc("@info:status", "Multiplying and placing objects"), lifetime=0,
dismissable=False, progress=0)
status_message.show()
scene = Application.getInstance().getController().getScene()
node = scene.findObject(self._object_id)
if not node and self._object_id != 0: # Workaround for tool handles overlapping the selected object
node = Selection.getSelectedObject(0)
# If object is part of a group, multiply group
current_node = node
while current_node.getParent() and current_node.getParent().callDecoration("isGroup"):
current_node = current_node.getParent()
root = scene.getRoot()
arranger = Arrange.create(scene_root=root)
node_too_big = False
if node.getBoundingBox().width < 300 or node.getBoundingBox().depth < 300:
offset_shape_arr, hull_shape_arr = ShapeArray.fromNode(current_node, min_offset=self._min_offset)
else:
node_too_big = True
nodes = []
found_solution_for_all = True
for i in range(self._count):
# We do place the nodes one by one, as we want to yield in between.
if not node_too_big:
node, solution_found = arranger.findNodePlacement(current_node, offset_shape_arr, hull_shape_arr)
if node_too_big or not solution_found:
found_solution_for_all = False
new_location = node.getPosition()
new_location = new_location.set(z = 100 - i * 20)
node.setPosition(new_location)
nodes.append(node)
Job.yieldThread()
status_message.setProgress((i + 1) / self._count * 100)
if nodes:
op = GroupedOperation()
for new_node in nodes:
op.addOperation(AddSceneNodeOperation(new_node, current_node.getParent()))
op.push()
status_message.hide()
if not found_solution_for_all:
no_full_solution_message = Message(i18n_catalog.i18nc("@info:status", "Unable to find a location within the build volume for all objects"))
no_full_solution_message.show()

44
cura/PlatformPhysics.py Normal file → Executable file
View File

@ -3,10 +3,10 @@
from PyQt5.QtCore import QTimer from PyQt5.QtCore import QTimer
from UM.Application import Application
from UM.Scene.SceneNode import SceneNode from UM.Scene.SceneNode import SceneNode
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
from UM.Math.Vector import Vector from UM.Math.Vector import Vector
from UM.Math.AxisAlignedBox import AxisAlignedBox
from UM.Scene.Selection import Selection from UM.Scene.Selection import Selection
from UM.Preferences import Preferences from UM.Preferences import Preferences
@ -51,10 +51,13 @@ class PlatformPhysics:
# same direction. # same direction.
transformed_nodes = [] transformed_nodes = []
group_nodes = []
# We try to shuffle all the nodes to prevent "locked" situations, where iteration B inverts iteration A. # 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. # By shuffling the order of the nodes, this might happen a few times, but at some point it will resolve.
nodes = list(BreadthFirstIterator(root)) nodes = list(BreadthFirstIterator(root))
# Only check nodes inside build area.
nodes = [node for node in nodes if (hasattr(node, "_outside_buildarea") and not node._outside_buildarea)]
random.shuffle(nodes) random.shuffle(nodes)
for node in 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:
@ -62,24 +65,6 @@ class PlatformPhysics:
bbox = node.getBoundingBox() bbox = node.getBoundingBox()
# Ignore intersections with the bottom
build_volume_bounding_box = self._build_volume.getBoundingBox()
if build_volume_bounding_box:
# It's over 9000!
build_volume_bounding_box = build_volume_bounding_box.set(bottom=-9001)
else:
# No bounding box. This is triggered when running Cura from command line with a model for the first time
# In that situation there is a model, but no machine (and therefore no build volume.
return
node._outside_buildarea = False
# Mark the node as outside the build volume if the bounding box test fails.
if build_volume_bounding_box.intersectsBox(bbox) != AxisAlignedBox.IntersectionResult.FullIntersection:
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")) and node.isEnabled(): #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")) and node.isEnabled(): #If an object is grouped, don't move it down
@ -145,27 +130,14 @@ class PlatformPhysics:
# Simply waiting for the next tick seems to resolve this correctly. # Simply waiting for the next tick seems to resolve this correctly.
overlap = None overlap = None
convex_hull = node.callDecoration("getConvexHull")
if convex_hull:
if not convex_hull.isValid():
return
# Check for collisions between disallowed areas and the object
for area in self._build_volume.getDisallowedAreas():
overlap = convex_hull.intersectsPolygon(area)
if overlap is None:
continue
node._outside_buildarea = True
if not Vector.Null.equals(move_vector, epsilon=1e-5): if not Vector.Null.equals(move_vector, epsilon=1e-5):
transformed_nodes.append(node) transformed_nodes.append(node)
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. # After moving, we have to evaluate the boundary checks for nodes
for group_node in group_nodes: build_volume = Application.getInstance().getBuildVolume()
for child_node in group_node.getAllChildren(): build_volume.updateNodeBoundaryCheck()
child_node._outside_buildarea = group_node._outside_buildarea
def _onToolOperationStarted(self, tool): def _onToolOperationStarted(self, tool):
self._enabled = False self._enabled = False

View File

@ -75,6 +75,8 @@ class PrintInformation(QObject):
Application.getInstance().getMachineManager().activeMaterialChanged.connect(self._onActiveMaterialChanged) Application.getInstance().getMachineManager().activeMaterialChanged.connect(self._onActiveMaterialChanged)
self._onActiveMaterialChanged() self._onActiveMaterialChanged()
self._material_amounts = []
currentPrintTimeChanged = pyqtSignal() currentPrintTimeChanged = pyqtSignal()
preSlicedChanged = pyqtSignal() preSlicedChanged = pyqtSignal()

View File

@ -16,9 +16,9 @@ class QualityManager:
## Get the singleton instance for this class. ## Get the singleton instance for this class.
@classmethod @classmethod
def getInstance(cls): def getInstance(cls) -> "QualityManager":
# Note: Explicit use of class name to prevent issues with inheritance. # Note: Explicit use of class name to prevent issues with inheritance.
if QualityManager.__instance is None: if not QualityManager.__instance:
QualityManager.__instance = cls() QualityManager.__instance = cls()
return QualityManager.__instance return QualityManager.__instance

View File

@ -244,7 +244,13 @@ class ExtruderManager(QObject):
material = materials[0] material = materials[0]
preferred_material_id = machine_definition.getMetaDataEntry("preferred_material") preferred_material_id = machine_definition.getMetaDataEntry("preferred_material")
if preferred_material_id: if preferred_material_id:
search_criteria = { "type": "material", "id": preferred_material_id} global_stack = ContainerRegistry.getInstance().findContainerStacks(id = machine_id)
if global_stack:
approximate_material_diameter = round(global_stack[0].getProperty("material_diameter", "value"))
else:
approximate_material_diameter = round(machine_definition.getProperty("material_diameter", "value"))
search_criteria = { "type": "material", "id": preferred_material_id, "approximate_diameter": approximate_material_diameter}
if machine_definition.getMetaDataEntry("has_machine_materials"): if machine_definition.getMetaDataEntry("has_machine_materials"):
search_criteria["definition"] = machine_definition_id search_criteria["definition"] = machine_definition_id
@ -255,7 +261,12 @@ class ExtruderManager(QObject):
preferred_materials = container_registry.findInstanceContainers(**search_criteria) preferred_materials = container_registry.findInstanceContainers(**search_criteria)
if len(preferred_materials) >= 1: if len(preferred_materials) >= 1:
material = preferred_materials[0] # In some cases we get multiple materials. In that case, prefer materials that are marked as read only.
read_only_preferred_materials = [preferred_material for preferred_material in preferred_materials if preferred_material.isReadOnly()]
if len(read_only_preferred_materials) >= 1:
material = read_only_preferred_materials[0]
else:
material = preferred_materials[0]
else: else:
Logger.log("w", "The preferred material \"%s\" of machine %s doesn't exist or is not a material profile.", preferred_material_id, machine_id) Logger.log("w", "The preferred material \"%s\" of machine %s doesn't exist or is not a material profile.", preferred_material_id, machine_id)
# And leave it at the default material. # And leave it at the default material.

View File

@ -2,7 +2,7 @@
# Cura is released under the terms of the AGPLv3 or higher. # Cura is released under the terms of the AGPLv3 or higher.
from typing import Union from typing import Union
from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal from PyQt5.QtCore import QObject, pyqtProperty, pyqtSignal, QTimer
from UM.FlameProfiler import pyqtSlot from UM.FlameProfiler import pyqtSlot
from PyQt5.QtWidgets import QMessageBox from PyQt5.QtWidgets import QMessageBox
from UM import Util from UM import Util
@ -15,9 +15,7 @@ from UM.Message import Message
from UM.Settings.ContainerRegistry import ContainerRegistry from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.ContainerStack import ContainerStack from UM.Settings.ContainerStack import ContainerStack
from UM.Settings.InstanceContainer import InstanceContainer from UM.Settings.InstanceContainer import InstanceContainer
from UM.Settings.SettingDefinition import SettingDefinition
from UM.Settings.SettingFunction import SettingFunction from UM.Settings.SettingFunction import SettingFunction
from UM.Settings.Validator import ValidatorState
from UM.Signal import postponeSignals from UM.Signal import postponeSignals
from cura.QualityManager import QualityManager from cura.QualityManager import QualityManager
@ -27,6 +25,11 @@ from cura.Settings.ExtruderManager import ExtruderManager
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura") catalog = i18nCatalog("cura")
from typing import TYPE_CHECKING, Optional
if TYPE_CHECKING:
from UM.Settings.DefinitionContainer import DefinitionContainer
import os import os
class MachineManager(QObject): class MachineManager(QObject):
@ -83,6 +86,11 @@ class MachineManager(QObject):
self._material_incompatible_message = Message(catalog.i18nc("@info:status", self._material_incompatible_message = Message(catalog.i18nc("@info:status",
"The selected material is incompatible with the selected machine or configuration.")) "The selected material is incompatible with the selected machine or configuration."))
self._error_check_timer = QTimer()
self._error_check_timer.setInterval(250)
self._error_check_timer.setSingleShot(True)
self._error_check_timer.timeout.connect(self._updateStacksHaveErrors)
globalContainerChanged = pyqtSignal() # Emitted whenever the global stack is changed (ie: when changing between printers, changing a global profile, but not when changing a value) globalContainerChanged = pyqtSignal() # Emitted whenever the global stack is changed (ie: when changing between printers, changing a global profile, but not when changing a value)
activeMaterialChanged = pyqtSignal() activeMaterialChanged = pyqtSignal()
activeVariantChanged = pyqtSignal() activeVariantChanged = pyqtSignal()
@ -306,33 +314,7 @@ class MachineManager(QObject):
self.activeStackValueChanged.emit() self.activeStackValueChanged.emit()
elif property_name == "validationState": elif property_name == "validationState":
if not self._stacks_have_errors: self._error_check_timer.start()
# fast update, we only have to look at the current changed property
if self._global_container_stack.getProperty("machine_extruder_count", "value") > 1 and self._active_container_stack.getProperty(key, "settable_per_extruder"):
extruder_index = int(self._active_container_stack.getProperty(key, "limit_to_extruder"))
if extruder_index >= 0: #We have to look up the value from a different extruder.
stack = ExtruderManager.getInstance().getExtruderStack(str(extruder_index))
else:
stack = self._active_container_stack
else:
stack = self._global_container_stack
changed_validation_state = stack.getProperty(key, property_name)
if changed_validation_state is None:
# Setting is not validated. This can happen if there is only a setting definition.
# We do need to validate it, because a setting defintions value can be set by a function, which could
# be an invalid setting.
definition = self._active_container_stack.getSettingDefinition(key)
validator_type = SettingDefinition.getValidatorForType(definition.type)
if validator_type:
validator = validator_type(key)
changed_validation_state = validator(self._active_container_stack)
if changed_validation_state in (ValidatorState.Exception, ValidatorState.MaximumError, ValidatorState.MinimumError):
self._stacks_have_errors = True
self.stacksValidationChanged.emit()
else:
# Normal check
self._updateStacksHaveErrors()
@pyqtSlot(str) @pyqtSlot(str)
def setActiveMachine(self, stack_id: str) -> None: def setActiveMachine(self, stack_id: str) -> None:
@ -350,10 +332,11 @@ class MachineManager(QObject):
name = self._createUniqueName("machine", "", name, definition.getName()) name = self._createUniqueName("machine", "", name, definition.getName())
new_global_stack = ContainerStack(name) new_global_stack = ContainerStack(name)
new_global_stack.addMetaDataEntry("type", "machine") new_global_stack.addMetaDataEntry("type", "machine")
new_global_stack.addContainer(definition)
container_registry.addContainer(new_global_stack) container_registry.addContainer(new_global_stack)
variant_instance_container = self._updateVariantContainer(definition) variant_instance_container = self._updateVariantContainer(definition)
material_instance_container = self._updateMaterialContainer(definition, variant_instance_container) material_instance_container = self._updateMaterialContainer(definition, new_global_stack, variant_instance_container)
quality_instance_container = self._updateQualityContainer(definition, variant_instance_container, material_instance_container) quality_instance_container = self._updateQualityContainer(definition, variant_instance_container, material_instance_container)
current_settings_instance_container = InstanceContainer(name + "_current_settings") current_settings_instance_container = InstanceContainer(name + "_current_settings")
@ -362,7 +345,7 @@ class MachineManager(QObject):
current_settings_instance_container.setDefinition(definitions[0]) current_settings_instance_container.setDefinition(definitions[0])
container_registry.addContainer(current_settings_instance_container) container_registry.addContainer(current_settings_instance_container)
new_global_stack.addContainer(definition)
if variant_instance_container: if variant_instance_container:
new_global_stack.addContainer(variant_instance_container) new_global_stack.addContainer(variant_instance_container)
if material_instance_container: if material_instance_container:
@ -781,7 +764,7 @@ class MachineManager(QObject):
if old_material: if old_material:
preferred_material_name = old_material.getName() preferred_material_name = old_material.getName()
self.setActiveMaterial(self._updateMaterialContainer(self._global_container_stack.getBottom(), containers[0], preferred_material_name).id) self.setActiveMaterial(self._updateMaterialContainer(self._global_container_stack.getBottom(), self._global_container_stack, containers[0], preferred_material_name).id)
else: else:
Logger.log("w", "While trying to set the active variant, no variant was found to replace.") Logger.log("w", "While trying to set the active variant, no variant was found to replace.")
@ -1115,7 +1098,7 @@ class MachineManager(QObject):
def createMachineManager(engine=None, script_engine=None): def createMachineManager(engine=None, script_engine=None):
return MachineManager() return MachineManager()
def _updateVariantContainer(self, definition): def _updateVariantContainer(self, definition: "DefinitionContainer"):
if not definition.getMetaDataEntry("has_variants"): if not definition.getMetaDataEntry("has_variants"):
return self._empty_variant_container return self._empty_variant_container
machine_definition_id = Application.getInstance().getMachineManager().getQualityDefinitionId(definition) machine_definition_id = Application.getInstance().getMachineManager().getQualityDefinitionId(definition)
@ -1131,11 +1114,12 @@ class MachineManager(QObject):
return self._empty_variant_container return self._empty_variant_container
def _updateMaterialContainer(self, definition, variant_container = None, preferred_material_name = None): def _updateMaterialContainer(self, definition: "DefinitionContainer", stack: "ContainerStack", variant_container: Optional["InstanceContainer"] = None, preferred_material_name: Optional[str] = None):
if not definition.getMetaDataEntry("has_materials"): if not definition.getMetaDataEntry("has_materials"):
return self._empty_material_container return self._empty_material_container
search_criteria = { "type": "material" } approximate_material_diameter = round(stack.getProperty("material_diameter", "value"))
search_criteria = { "type": "material", "approximate_diameter": approximate_material_diameter }
if definition.getMetaDataEntry("has_machine_materials"): if definition.getMetaDataEntry("has_machine_materials"):
search_criteria["definition"] = self.getQualityDefinitionId(definition) search_criteria["definition"] = self.getQualityDefinitionId(definition)
@ -1167,7 +1151,7 @@ class MachineManager(QObject):
Logger.log("w", "Unable to find a material container with provided criteria, returning an empty one instead.") Logger.log("w", "Unable to find a material container with provided criteria, returning an empty one instead.")
return self._empty_material_container return self._empty_material_container
def _updateQualityContainer(self, definition, variant_container, material_container = None, preferred_quality_name = None): def _updateQualityContainer(self, definition: "DefinitionContainer", variant_container: "ContainerStack", material_container = None, preferred_quality_name: Optional[str] = None):
container_registry = ContainerRegistry.getInstance() container_registry = ContainerRegistry.getInstance()
search_criteria = { "type": "quality" } search_criteria = { "type": "quality" }

View File

@ -32,9 +32,9 @@ class ProfilesModel(InstanceContainersModel):
## Get the singleton instance for this class. ## Get the singleton instance for this class.
@classmethod @classmethod
def getInstance(cls): def getInstance(cls) -> "ProfilesModel":
# Note: Explicit use of class name to prevent issues with inheritance. # Note: Explicit use of class name to prevent issues with inheritance.
if ProfilesModel.__instance is None: if not ProfilesModel.__instance:
ProfilesModel.__instance = cls() ProfilesModel.__instance = cls()
return ProfilesModel.__instance return ProfilesModel.__instance

113
cura/ShapeArray.py Executable file
View File

@ -0,0 +1,113 @@
import numpy
import copy
from UM.Math.Polygon import Polygon
## Polygon representation as an array for use with Arrange
class ShapeArray:
def __init__(self, arr, offset_x, offset_y, scale = 1):
self.arr = arr
self.offset_x = offset_x
self.offset_y = offset_y
self.scale = scale
## Instantiate from a bunch of vertices
# \param vertices
# \param scale scale the coordinates
@classmethod
def fromPolygon(cls, vertices, scale = 1):
# scale
vertices = vertices * scale
# flip y, x -> x, y
flip_vertices = numpy.zeros((vertices.shape))
flip_vertices[:, 0] = vertices[:, 1]
flip_vertices[:, 1] = vertices[:, 0]
flip_vertices = flip_vertices[::-1]
# offset, we want that all coordinates have positive values
offset_y = int(numpy.amin(flip_vertices[:, 0]))
offset_x = int(numpy.amin(flip_vertices[:, 1]))
flip_vertices[:, 0] = numpy.add(flip_vertices[:, 0], -offset_y)
flip_vertices[:, 1] = numpy.add(flip_vertices[:, 1], -offset_x)
shape = [int(numpy.amax(flip_vertices[:, 0])), int(numpy.amax(flip_vertices[:, 1]))]
arr = cls.arrayFromPolygon(shape, flip_vertices)
return cls(arr, offset_x, offset_y)
## Instantiate an offset and hull ShapeArray from a scene node.
# \param node source node where the convex hull must be present
# \param min_offset offset for the offset ShapeArray
# \param scale scale the coordinates
@classmethod
def fromNode(cls, node, min_offset, scale = 0.5):
transform = node._transformation
transform_x = transform._data[0][3]
transform_y = transform._data[2][3]
hull_verts = node.callDecoration("getConvexHull")
# For one_at_a_time printing you need the convex hull head.
hull_head_verts = node.callDecoration("getConvexHullHead") or hull_verts
offset_verts = hull_head_verts.getMinkowskiHull(Polygon.approximatedCircle(min_offset))
offset_points = copy.deepcopy(offset_verts._points) # x, y
offset_points[:, 0] = numpy.add(offset_points[:, 0], -transform_x)
offset_points[:, 1] = numpy.add(offset_points[:, 1], -transform_y)
offset_shape_arr = ShapeArray.fromPolygon(offset_points, scale = scale)
hull_points = copy.deepcopy(hull_verts._points)
hull_points[:, 0] = numpy.add(hull_points[:, 0], -transform_x)
hull_points[:, 1] = numpy.add(hull_points[:, 1], -transform_y)
hull_shape_arr = ShapeArray.fromPolygon(hull_points, scale = scale) # x, y
return offset_shape_arr, hull_shape_arr
## Create np.array with dimensions defined by shape
# Fills polygon defined by vertices with ones, all other values zero
# Only works correctly for convex hull vertices
# Originally from: http://stackoverflow.com/questions/37117878/generating-a-filled-polygon-inside-a-numpy-array
# \param shape numpy format shape, [x-size, y-size]
# \param vertices
@classmethod
def arrayFromPolygon(cls, shape, vertices):
base_array = numpy.zeros(shape, dtype=float) # Initialize your array of zeros
fill = numpy.ones(base_array.shape) * True # Initialize boolean array defining shape fill
# Create check array for each edge segment, combine into fill array
for k in range(vertices.shape[0]):
fill = numpy.all([fill, cls._check(vertices[k - 1], vertices[k], base_array)], axis=0)
# Set all values inside polygon to one
base_array[fill] = 1
return base_array
## Return indices that mark one side of the line, used by arrayFromPolygon
# Uses the line defined by p1 and p2 to check array of
# input indices against interpolated value
# Returns boolean array, with True inside and False outside of shape
# Originally from: http://stackoverflow.com/questions/37117878/generating-a-filled-polygon-inside-a-numpy-array
# \param p1 2-tuple with x, y for point 1
# \param p2 2-tuple with x, y for point 2
# \param base_array boolean array to project the line on
@classmethod
def _check(cls, p1, p2, base_array):
if p1[0] == p2[0] and p1[1] == p2[1]:
return
idxs = numpy.indices(base_array.shape) # Create 3D array of indices
p1 = p1.astype(float)
p2 = p2.astype(float)
if p2[0] == p1[0]:
sign = numpy.sign(p2[1] - p1[1])
return idxs[1] * sign
if p2[1] == p1[1]:
sign = numpy.sign(p2[0] - p1[0])
return idxs[1] * sign
# Calculate max column idx for each row idx based on interpolated line between two points
max_col_idx = (idxs[0] - p1[0]) / (p2[0] - p1[0]) * (p2[1] - p1[1]) + p1[1]
sign = numpy.sign(p2[0] - p1[0])
return idxs[1] * sign <= max_col_idx * sign

View File

@ -12,15 +12,15 @@ UM.Dialog
{ {
title: catalog.i18nc("@title:window", "Open Project") title: catalog.i18nc("@title:window", "Open Project")
width: 550 width: 550 * Screen.devicePixelRatio
minimumWidth: 550 minimumWidth: 550 * Screen.devicePixelRatio
maximumWidth: 550 maximumWidth: minimumWidth
height: 400 height: 400 * Screen.devicePixelRatio
minimumHeight: 400 minimumHeight: 400 * Screen.devicePixelRatio
maximumHeight: 400 maximumHeight: minimumHeight
property int comboboxHeight: 15 property int comboboxHeight: 15 * Screen.devicePixelRatio
property int spacerHeight: 10 property int spacerHeight: 10 * Screen.devicePixelRatio
onClosing: manager.notifyClosed() onClosing: manager.notifyClosed()
onVisibleChanged: onVisibleChanged:
{ {
@ -33,20 +33,17 @@ UM.Dialog
} }
Item Item
{ {
anchors.top: parent.top anchors.fill: parent
anchors.bottom: parent.bottom anchors.margins: 20 * Screen.devicePixelRatio
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
anchors.bottomMargin: 20
anchors.leftMargin:20
anchors.rightMargin: 20
UM.I18nCatalog UM.I18nCatalog
{ {
id: catalog; id: catalog
name: "cura"; name: "cura"
}
SystemPalette
{
id: palette
} }
ListModel ListModel
@ -70,12 +67,12 @@ UM.Dialog
{ {
id: titleLabel id: titleLabel
text: catalog.i18nc("@action:title", "Summary - Cura Project") text: catalog.i18nc("@action:title", "Summary - Cura Project")
font.pixelSize: 22 font.pointSize: 18
} }
Rectangle Rectangle
{ {
id: separator id: separator
color: "black" color: palette.text
width: parent.width width: parent.width
height: 1 height: 1
} }
@ -93,7 +90,7 @@ UM.Dialog
{ {
text: catalog.i18nc("@action:label", "Printer settings") text: catalog.i18nc("@action:label", "Printer settings")
font.bold: true font.bold: true
width: parent.width /3 width: parent.width / 3
} }
Item Item
{ {
@ -360,7 +357,7 @@ UM.Dialog
height: width height: width
source: UM.Theme.getIcon("notice") source: UM.Theme.getIcon("notice")
color: "black" color: palette.text
} }
Label Label
@ -392,4 +389,4 @@ UM.Dialog
anchors.right: parent.right anchors.right: parent.right
} }
} }
} }

View File

@ -1,9 +1,16 @@
# Copyright (c) 2015 Ultimaker B.V. # Copyright (c) 2015 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.
from typing import Dict from typing import Dict
import sys
from UM.Logger import Logger
try:
from . import ThreeMFReader
except ImportError:
Logger.log("w", "Could not import ThreeMFReader; libSavitar may be missing")
from . import ThreeMFReader
from . import ThreeMFWorkspaceReader from . import ThreeMFWorkspaceReader
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
from UM.Platform import Platform from UM.Platform import Platform
catalog = i18nCatalog("cura") catalog = i18nCatalog("cura")
@ -14,30 +21,36 @@ def getMetaData() -> Dict:
workspace_extension = "3mf" workspace_extension = "3mf"
else: else:
workspace_extension = "curaproject.3mf" workspace_extension = "curaproject.3mf"
return {
metaData = {
"plugin": { "plugin": {
"name": catalog.i18nc("@label", "3MF Reader"), "name": catalog.i18nc("@label", "3MF Reader"),
"author": "Ultimaker", "author": "Ultimaker",
"version": "1.0", "version": "1.0",
"description": catalog.i18nc("@info:whatsthis", "Provides support for reading 3MF files."), "description": catalog.i18nc("@info:whatsthis", "Provides support for reading 3MF files."),
"api": 3 "api": 3
}, }
"mesh_reader": [ }
if "3MFReader.ThreeMFReader" in sys.modules:
metaData["mesh_reader"] = [
{ {
"extension": "3mf", "extension": "3mf",
"description": catalog.i18nc("@item:inlistbox", "3MF File") "description": catalog.i18nc("@item:inlistbox", "3MF File")
} }
], ]
"workspace_reader": metaData["workspace_reader"] = [
[
{ {
"extension": workspace_extension, "extension": workspace_extension,
"description": catalog.i18nc("@item:inlistbox", "3MF File") "description": catalog.i18nc("@item:inlistbox", "3MF File")
} }
] ]
}
return metaData
def register(app): def register(app):
return {"mesh_reader": ThreeMFReader.ThreeMFReader(), if "3MFReader.ThreeMFReader" in sys.modules:
"workspace_reader": ThreeMFWorkspaceReader.ThreeMFWorkspaceReader()} return {"mesh_reader": ThreeMFReader.ThreeMFReader(),
"workspace_reader": ThreeMFWorkspaceReader.ThreeMFWorkspaceReader()}
else:
return {}

View File

@ -1,30 +1,39 @@
# Copyright (c) 2015 Ultimaker B.V. # Copyright (c) 2015 Ultimaker B.V.
# Uranium is released under the terms of the AGPLv3 or higher. # Uranium is released under the terms of the AGPLv3 or higher.
import sys
from UM.Logger import Logger
try:
from . import ThreeMFWriter
except ImportError:
Logger.log("w", "Could not import ThreeMFWriter; libSavitar may be missing")
from . import ThreeMFWorkspaceWriter
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
from . import ThreeMFWorkspaceWriter
from . import ThreeMFWriter
i18n_catalog = i18nCatalog("uranium") i18n_catalog = i18nCatalog("uranium")
def getMetaData(): def getMetaData():
return { metaData = {
"plugin": { "plugin": {
"name": i18n_catalog.i18nc("@label", "3MF Writer"), "name": i18n_catalog.i18nc("@label", "3MF Writer"),
"author": "Ultimaker", "author": "Ultimaker",
"version": "1.0", "version": "1.0",
"description": i18n_catalog.i18nc("@info:whatsthis", "Provides support for writing 3MF files."), "description": i18n_catalog.i18nc("@info:whatsthis", "Provides support for writing 3MF files."),
"api": 3 "api": 3
}, }
"mesh_writer": { }
if "3MFWriter.ThreeMFWriter" in sys.modules:
metaData["mesh_writer"] = {
"output": [{ "output": [{
"extension": "3mf", "extension": "3mf",
"description": i18n_catalog.i18nc("@item:inlistbox", "3MF file"), "description": i18n_catalog.i18nc("@item:inlistbox", "3MF file"),
"mime_type": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml", "mime_type": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml",
"mode": ThreeMFWriter.ThreeMFWriter.OutputMode.BinaryMode "mode": ThreeMFWriter.ThreeMFWriter.OutputMode.BinaryMode
}] }]
}, }
"workspace_writer": { metaData["workspace_writer"] = {
"output": [{ "output": [{
"extension": "curaproject.3mf", "extension": "curaproject.3mf",
"description": i18n_catalog.i18nc("@item:inlistbox", "Cura Project 3MF file"), "description": i18n_catalog.i18nc("@item:inlistbox", "Cura Project 3MF file"),
@ -32,7 +41,12 @@ def getMetaData():
"mode": ThreeMFWorkspaceWriter.ThreeMFWorkspaceWriter.OutputMode.BinaryMode "mode": ThreeMFWorkspaceWriter.ThreeMFWorkspaceWriter.OutputMode.BinaryMode
}] }]
} }
}
return metaData
def register(app): def register(app):
return {"mesh_writer": ThreeMFWriter.ThreeMFWriter(), "workspace_writer": ThreeMFWorkspaceWriter.ThreeMFWorkspaceWriter()} if "3MFWriter.ThreeMFWriter" in sys.modules:
return {"mesh_writer": ThreeMFWriter.ThreeMFWriter(),
"workspace_writer": ThreeMFWorkspaceWriter.ThreeMFWorkspaceWriter()}
else:
return {}

View File

@ -2,6 +2,7 @@
# Cura is released under the terms of the AGPLv3 or higher. # Cura is released under the terms of the AGPLv3 or higher.
from UM.Application import Application from UM.Application import Application
from UM.Backend import Backend
from UM.Job import Job from UM.Job import Job
from UM.Logger import Logger from UM.Logger import Logger
from UM.Math.AxisAlignedBox import AxisAlignedBox from UM.Math.AxisAlignedBox import AxisAlignedBox
@ -37,7 +38,6 @@ class GCodeReader(MeshReader):
self._message = None self._message = None
self._layer_number = 0 self._layer_number = 0
self._extruder_number = 0 self._extruder_number = 0
self._layer_type = LayerPolygon.Inset0Type
self._clearValues() self._clearValues()
self._scene_node = None self._scene_node = None
self._position = namedtuple('Position', ['x', 'y', 'z', 'e']) self._position = namedtuple('Position', ['x', 'y', 'z', 'e'])
@ -153,7 +153,6 @@ class GCodeReader(MeshReader):
self._previous_z = z self._previous_z = z
else: else:
path.append([x, y, z, LayerPolygon.MoveCombingType]) path.append([x, y, z, LayerPolygon.MoveCombingType])
return self._position(x, y, z, e) return self._position(x, y, z, e)
# G0 and G1 should be handled exactly the same. # G0 and G1 should be handled exactly the same.
@ -172,7 +171,6 @@ class GCodeReader(MeshReader):
def _gCode92(self, position, params, path): def _gCode92(self, position, params, path):
if params.e is not None: if params.e is not None:
position.e[self._extruder_number] = params.e position.e[self._extruder_number] = params.e
return self._position( return self._position(
params.x if params.x is not None else position.x, params.x if params.x is not None else position.x,
params.y if params.y is not None else position.y, params.y if params.y is not None else position.y,
@ -181,6 +179,7 @@ class GCodeReader(MeshReader):
def _processGCode(self, G, line, position, path): def _processGCode(self, G, line, position, path):
func = getattr(self, "_gCode%s" % G, None) func = getattr(self, "_gCode%s" % G, None)
line = line.split(";", 1)[0] # Remove comments (if any)
if func is not None: if func is not None:
s = line.upper().split(" ") s = line.upper().split(" ")
x, y, z, e = None, None, None, None x, y, z, e = None, None, None, None
@ -307,11 +306,11 @@ class GCodeReader(MeshReader):
G = self._getInt(line, "G") G = self._getInt(line, "G")
if G is not None: if G is not None:
current_position = self._processGCode(G, line, current_position, current_path) current_position = self._processGCode(G, line, current_position, current_path)
# < 2 is a heuristic for a movement only, that should not be counted as a layer # < 2 is a heuristic for a movement only, that should not be counted as a layer
if current_position.z > last_z and abs(current_position.z - last_z) < 2: if current_position.z > last_z and abs(current_position.z - last_z) < 2:
if self._createPolygon(self._current_layer_thickness, current_path, self._extruder_offsets.get(self._extruder_number, [0, 0])): if self._createPolygon(self._current_layer_thickness, current_path, self._extruder_offsets.get(self._extruder_number, [0, 0])):
current_path.clear() current_path.clear()
if not self._is_layers_in_file: if not self._is_layers_in_file:
self._layer_number += 1 self._layer_number += 1
@ -343,6 +342,8 @@ class GCodeReader(MeshReader):
gcode_list_decorator.setGCodeList(gcode_list) gcode_list_decorator.setGCodeList(gcode_list)
scene_node.addDecorator(gcode_list_decorator) scene_node.addDecorator(gcode_list_decorator)
Application.getInstance().getController().getScene().gcode_list = gcode_list
Logger.log("d", "Finished parsing %s" % file_name) Logger.log("d", "Finished parsing %s" % file_name)
self._message.hide() self._message.hide()
@ -364,4 +365,8 @@ class GCodeReader(MeshReader):
"Make sure the g-code is suitable for your printer and printer configuration before sending the file to it. The g-code representation may not be accurate."), lifetime=0) "Make sure the g-code is suitable for your printer and printer configuration before sending the file to it. The g-code representation may not be accurate."), lifetime=0)
caution_message.show() caution_message.show()
# The "save/print" button's state is bound to the backend state.
backend = Application.getInstance().getBackend()
backend.backendStateChange.emit(Backend.BackendState.Disabled)
return scene_node return scene_node

View File

@ -1,6 +1,8 @@
# Copyright (c) 2015 Ultimaker B.V. # Copyright (c) 2015 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 sys
from UM.PluginRegistry import PluginRegistry from UM.PluginRegistry import PluginRegistry
from UM.View.View import View from UM.View.View import View
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
@ -253,8 +255,17 @@ class LayerView(View):
if not layer_data: if not layer_data:
continue continue
if new_max_layers < len(layer_data.getLayers()): min_layer_number = sys.maxsize
new_max_layers = len(layer_data.getLayers()) - 1 max_layer_number = -sys.maxsize
for layer_id in layer_data.getLayers():
if max_layer_number < layer_id:
max_layer_number = layer_id
if min_layer_number > layer_id:
min_layer_number = layer_id
layer_count = max_layer_number - min_layer_number
if new_max_layers < layer_count:
new_max_layers = layer_count
if new_max_layers > 0 and new_max_layers != self._old_max_layers: if new_max_layers > 0 and new_max_layers != self._old_max_layers:
self._max_layers = new_max_layers self._max_layers = new_max_layers

View File

@ -351,7 +351,7 @@ Item
property bool roundValues: true property bool roundValues: true
property var activeHandle: upperHandle property var activeHandle: upperHandle
property bool layersVisible: UM.LayerView.layerActivity && Printer.platformActivity ? true : false property bool layersVisible: UM.LayerView.layerActivity && CuraApplication.platformActivity ? true : false
function getUpperValueFromHandle() function getUpperValueFromHandle()
{ {

View File

@ -200,7 +200,7 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice):
def _onAuthenticationRequired(self, reply, authenticator): def _onAuthenticationRequired(self, reply, authenticator):
if self._authentication_id is not None and self._authentication_key is not None: if self._authentication_id is not None and self._authentication_key is not None:
Logger.log("d", "Authentication was required. Setting up authenticator with ID %s and key", self._authentication_id, self._getSafeAuthKey()) Logger.log("d", "Authentication was required. Setting up authenticator with ID %s and key %s", self._authentication_id, self._getSafeAuthKey())
authenticator.setUser(self._authentication_id) authenticator.setUser(self._authentication_id)
authenticator.setPassword(self._authentication_key) authenticator.setPassword(self._authentication_key)
else: else:
@ -625,7 +625,7 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice):
if print_information.materialLengths: if print_information.materialLengths:
# Check if print cores / materials are loaded at all. Any failure in these results in an Error. # Check if print cores / materials are loaded at all. Any failure in these results in an Error.
for index in range(0, self._num_extruders): for index in range(0, self._num_extruders):
if print_information.materialLengths[index] != 0: if index < len(print_information.materialLengths) and print_information.materialLengths[index] != 0:
if self._json_printer_state["heads"][0]["extruders"][index]["hotend"]["id"] == "": if self._json_printer_state["heads"][0]["extruders"][index]["hotend"]["id"] == "":
Logger.log("e", "No cartridge loaded in slot %s, unable to start print", index + 1) Logger.log("e", "No cartridge loaded in slot %s, unable to start print", index + 1)
self._error_message = Message( self._error_message = Message(
@ -643,13 +643,13 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice):
for index in range(0, self._num_extruders): for index in range(0, self._num_extruders):
# Check if there is enough material. Any failure in these results in a warning. # Check if there is enough material. Any failure in these results in a warning.
material_length = self._json_printer_state["heads"][0]["extruders"][index]["active_material"]["length_remaining"] material_length = self._json_printer_state["heads"][0]["extruders"][index]["active_material"]["length_remaining"]
if material_length != -1 and print_information.materialLengths[index] > material_length: if material_length != -1 and index < len(print_information.materialLengths) and print_information.materialLengths[index] > material_length:
Logger.log("w", "Printer reports that there is not enough material left for extruder %s. We need %s and the printer has %s", index + 1, print_information.materialLengths[index], material_length) Logger.log("w", "Printer reports that there is not enough material left for extruder %s. We need %s and the printer has %s", index + 1, print_information.materialLengths[index], material_length)
warnings.append(i18n_catalog.i18nc("@label", "Not enough material for spool {0}.").format(index+1)) warnings.append(i18n_catalog.i18nc("@label", "Not enough material for spool {0}.").format(index+1))
# Check if the right cartridges are loaded. Any failure in these results in a warning. # Check if the right cartridges are loaded. Any failure in these results in a warning.
extruder_manager = cura.Settings.ExtruderManager.ExtruderManager.getInstance() extruder_manager = cura.Settings.ExtruderManager.ExtruderManager.getInstance()
if print_information.materialLengths[index] != 0: if index < len(print_information.materialLengths) and print_information.materialLengths[index] != 0:
variant = extruder_manager.getExtruderStack(index).findContainer({"type": "variant"}) variant = extruder_manager.getExtruderStack(index).findContainer({"type": "variant"})
core_name = self._json_printer_state["heads"][0]["extruders"][index]["hotend"]["id"] core_name = self._json_printer_state["heads"][0]["extruders"][index]["hotend"]["id"]
if variant: if variant:
@ -716,7 +716,8 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice):
## Start requesting data from printer ## Start requesting data from printer
def connect(self): def connect(self):
self.close() # Ensure that previous connection (if any) is killed. if self.isConnected():
self.close() # Close previous connection
self._createNetworkManager() self._createNetworkManager()
@ -796,19 +797,41 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice):
Logger.log("d", "Started sending g-code to remote printer.") Logger.log("d", "Started sending g-code to remote printer.")
self._compressing_print = True self._compressing_print = True
## Mash the data into single string ## Mash the data into single string
max_chars_per_line = 1024 * 1024 / 4 # 1 / 4 MB
byte_array_file_data = b"" byte_array_file_data = b""
batched_line = ""
def _compress_data_and_notify_qt(data_to_append):
compressed_data = gzip.compress(data_to_append.encode("utf-8"))
QCoreApplication.processEvents() # Ensure that the GUI does not freeze.
# Pretend that this is a response, as zipping might take a bit of time.
self._last_response_time = time()
return compressed_data
for line in self._gcode: for line in self._gcode:
if not self._compressing_print: if not self._compressing_print:
self._progress_message.hide() self._progress_message.hide()
return # Stop trying to zip, abort was called. return # Stop trying to zip, abort was called.
if self._use_gzip: if self._use_gzip:
byte_array_file_data += gzip.compress(line.encode("utf-8")) batched_line += line
QCoreApplication.processEvents() # Ensure that the GUI does not freeze. # if the gcode was read from a gcode file, self._gcode will be a list of all lines in that file.
# Pretend that this is a response, as zipping might take a bit of time. # Compressing line by line in this case is extremely slow, so we need to batch them.
self._last_response_time = time() if len(batched_line) < max_chars_per_line:
continue
byte_array_file_data += _compress_data_and_notify_qt(batched_line)
batched_line = ""
else: else:
byte_array_file_data += line.encode("utf-8") byte_array_file_data += line.encode("utf-8")
# don't miss the last batch if it's there
if self._use_gzip:
if batched_line:
byte_array_file_data += _compress_data_and_notify_qt(batched_line)
if self._use_gzip: if self._use_gzip:
file_name = "%s.gcode.gz" % Application.getInstance().getPrintInformation().jobName file_name = "%s.gcode.gz" % Application.getInstance().getPrintInformation().jobName
else: else:
@ -858,7 +881,10 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice):
url = QUrl("http://" + self._address + self._api_prefix + "auth/request") url = QUrl("http://" + self._address + self._api_prefix + "auth/request")
request = QNetworkRequest(url) request = QNetworkRequest(url)
request.setHeader(QNetworkRequest.ContentTypeHeader, "application/json") request.setHeader(QNetworkRequest.ContentTypeHeader, "application/json")
self._authentication_key = None
self._authentication_id = None
self._manager.post(request, json.dumps({"application": "Cura-" + Application.getInstance().getVersion(), "user": self._getUserName()}).encode()) self._manager.post(request, json.dumps({"application": "Cura-" + Application.getInstance().getVersion(), "user": self._getUserName()}).encode())
self.setAuthenticationState(AuthState.AuthenticationRequested)
## Send all material profiles to the printer. ## Send all material profiles to the printer.
def sendMaterialProfiles(self): def sendMaterialProfiles(self):
@ -1054,7 +1080,6 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice):
except json.decoder.JSONDecodeError: except json.decoder.JSONDecodeError:
Logger.log("w", "Received an invalid authentication request reply from printer: Not valid JSON.") Logger.log("w", "Received an invalid authentication request reply from printer: Not valid JSON.")
return return
self.setAuthenticationState(AuthState.AuthenticationRequested)
global_container_stack = Application.getInstance().getGlobalContainerStack() global_container_stack = Application.getInstance().getGlobalContainerStack()
if global_container_stack: # Remove any old data. if global_container_stack: # Remove any old data.
Logger.log("d", "Removing old network authentication data as a new one was requested.") Logger.log("d", "Removing old network authentication data as a new one was requested.")
@ -1064,7 +1089,7 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice):
self._authentication_key = data["key"] self._authentication_key = data["key"]
self._authentication_id = data["id"] self._authentication_id = data["id"]
Logger.log("i", "Got a new authentication ID (%s) and KEY (%S). Waiting for authorization.", self._authentication_id, self._getSafeAuthKey()) Logger.log("i", "Got a new authentication ID (%s) and KEY (%s). Waiting for authorization.", self._authentication_id, self._getSafeAuthKey())
# Check if the authentication is accepted. # Check if the authentication is accepted.
self._checkAuthentication() self._checkAuthentication()
@ -1142,4 +1167,4 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice):
result = self._authentication_key[-5:] result = self._authentication_key[-5:]
result = "********" + result result = "********" + result
return result return result
return self._authentication_key return self._authentication_key

View File

@ -157,13 +157,15 @@ class NetworkPrinterOutputDevicePlugin(OutputDevicePlugin):
for key in self._printers: for key in self._printers:
if key == active_machine.getMetaDataEntry("um_network_key"): if key == active_machine.getMetaDataEntry("um_network_key"):
Logger.log("d", "Connecting [%s]..." % key) if not self._printers[key].isConnected():
self._printers[key].connect() Logger.log("d", "Connecting [%s]..." % key)
self._printers[key].connectionStateChanged.connect(self._onPrinterConnectionStateChanged) self._printers[key].connect()
self._printers[key].connectionStateChanged.connect(self._onPrinterConnectionStateChanged)
else: else:
if self._printers[key].isConnected(): if self._printers[key].isConnected():
Logger.log("d", "Closing connection [%s]..." % key) Logger.log("d", "Closing connection [%s]..." % key)
self._printers[key].close() self._printers[key].close()
self._printers[key].connectionStateChanged.disconnect(self._onPrinterConnectionStateChanged)
## Because the model needs to be created in the same thread as the QMLEngine, we use a signal. ## Because the model needs to be created in the same thread as the QMLEngine, we use a signal.
def addPrinter(self, name, address, properties): def addPrinter(self, name, address, properties):
@ -181,9 +183,9 @@ class NetworkPrinterOutputDevicePlugin(OutputDevicePlugin):
printer = self._printers.pop(name, None) printer = self._printers.pop(name, None)
if printer: if printer:
if printer.isConnected(): if printer.isConnected():
printer.disconnect()
printer.connectionStateChanged.disconnect(self._onPrinterConnectionStateChanged) printer.connectionStateChanged.disconnect(self._onPrinterConnectionStateChanged)
Logger.log("d", "removePrinter, disconnecting [%s]..." % name) Logger.log("d", "removePrinter, disconnecting [%s]..." % name)
printer.disconnect()
self.printerListChanged.emit() self.printerListChanged.emit()
## Handler for when the connection state of one of the detected printers changes ## Handler for when the connection state of one of the detected printers changes

View File

@ -19,8 +19,8 @@ from PyQt5.QtCore import QUrl, pyqtSlot, pyqtSignal, pyqtProperty
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura") catalog = i18nCatalog("cura")
class USBPrinterOutputDevice(PrinterOutputDevice):
class USBPrinterOutputDevice(PrinterOutputDevice):
def __init__(self, serial_port): def __init__(self, serial_port):
super().__init__(serial_port) super().__init__(serial_port)
self.setName(catalog.i18nc("@item:inmenu", "USB printing")) self.setName(catalog.i18nc("@item:inmenu", "USB printing"))
@ -148,6 +148,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
## Start a print based on a g-code. ## Start a print based on a g-code.
# \param gcode_list List with gcode (strings). # \param gcode_list List with gcode (strings).
def printGCode(self, gcode_list): def printGCode(self, gcode_list):
Logger.log("d", "Started printing g-code")
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", "Unable to start a new job because the printer is busy or not connected.")) 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()
@ -183,6 +184,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
## Private function (threaded) that actually uploads the firmware. ## Private function (threaded) that actually uploads the firmware.
def _updateFirmware(self): def _updateFirmware(self):
Logger.log("d", "Attempting to update firmware")
self._error_code = 0 self._error_code = 0
self.setProgress(0, 100) self.setProgress(0, 100)
self._firmware_update_finished = False self._firmware_update_finished = False
@ -202,6 +204,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
try: try:
programmer.connect(self._serial_port) programmer.connect(self._serial_port)
except Exception: except Exception:
programmer.close()
pass pass
# Give programmer some time to connect. Might need more in some cases, but this worked in all tested cases. # Give programmer some time to connect. Might need more in some cases, but this worked in all tested cases.
@ -312,8 +315,10 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
programmer.connect(self._serial_port) # Connect with the serial, if this succeeds, it's an arduino based usb device. programmer.connect(self._serial_port) # Connect with the serial, if this succeeds, it's an arduino based usb device.
self._serial = programmer.leaveISP() self._serial = programmer.leaveISP()
except ispBase.IspError as e: except ispBase.IspError as e:
programmer.close()
Logger.log("i", "Could not establish connection on %s: %s. Device is not arduino based." %(self._serial_port,str(e))) Logger.log("i", "Could not establish connection on %s: %s. Device is not arduino based." %(self._serial_port,str(e)))
except Exception as e: except Exception as e:
programmer.close()
Logger.log("i", "Could not establish connection on %s, unknown reasons. Device is not arduino based." % self._serial_port) Logger.log("i", "Could not establish connection on %s, unknown reasons. Device is not arduino based." % self._serial_port)
# If the programmer connected, we know its an atmega based version. # If the programmer connected, we know its an atmega based version.
@ -533,6 +538,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
self._sendNextGcodeLine() self._sendNextGcodeLine()
elif b"resend" in line.lower() or b"rs" in line: # Because a resend can be asked with "resend" and "rs" elif b"resend" in line.lower() or b"rs" in line: # Because a resend can be asked with "resend" and "rs"
try: try:
Logger.log("d", "Got a resend response")
self._gcode_position = int(line.replace(b"N:",b" ").replace(b"N",b" ").replace(b":",b" ").split()[-1]) self._gcode_position = int(line.replace(b"N:",b" ").replace(b"N",b" ").replace(b":",b" ").split()[-1])
except: except:
if b"rs" in line: if b"rs" in line:
@ -559,15 +565,20 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
if ";" in line: if ";" in line:
line = line[:line.find(";")] line = line[:line.find(";")]
line = line.strip() line = line.strip()
# Don't send empty lines. But we do have to send something, so send
# m105 instead.
# Don't send the M0 or M1 to the machine, as M0 and M1 are handled as
# an LCD menu pause.
if line == "" or line == "M0" or line == "M1":
line = "M105"
try: try:
if line == "M0" or line == "M1":
line = "M105" # Don't send the M0 or M1 to the machine, as M0 and M1 are handled as an LCD menu pause.
if ("G0" in line or "G1" in line) and "Z" in line: if ("G0" in line or "G1" in line) and "Z" in line:
z = float(re.search("Z([0-9\.]*)", line).group(1)) z = float(re.search("Z([0-9\.]*)", line).group(1))
if self._current_z != z: if self._current_z != z:
self._current_z = z self._current_z = z
except Exception as e: except Exception as e:
Logger.log("e", "Unexpected error with printer connection: %s" % e) Logger.log("e", "Unexpected error with printer connection, could not parse current Z: %s: %s" % (e, line))
self._setErrorState("Unexpected error: %s" %e) self._setErrorState("Unexpected error: %s" %e)
checksum = functools.reduce(lambda x,y: x^y, map(ord, "N%d%s" % (self._gcode_position, line))) checksum = functools.reduce(lambda x,y: x^y, map(ord, "N%d%s" % (self._gcode_position, line)))
@ -666,4 +677,4 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
def cancelPreheatBed(self): def cancelPreheatBed(self):
Logger.log("i", "Cancelling pre-heating of the bed.") Logger.log("i", "Cancelling pre-heating of the bed.")
self._setTargetBedTemperature(0) self._setTargetBedTemperature(0)
self.preheatBedRemainingTimeChanged.emit() self.preheatBedRemainingTimeChanged.emit()

View File

@ -236,8 +236,7 @@ class USBPrinterOutputDeviceManager(QObject, OutputDevicePlugin, Extension):
self.getOutputDeviceManager().removeOutputDevice(serial_port) self.getOutputDeviceManager().removeOutputDevice(serial_port)
self.connectionStateChanged.emit() self.connectionStateChanged.emit()
except KeyError: except KeyError:
pass # no output device by this device_id found in connection list. Logger.log("w", "Connection state of %s changed, but it was not found in the list")
@pyqtProperty(QObject , notify = connectionStateChanged) @pyqtProperty(QObject , notify = connectionStateChanged)
def connectedPrinterList(self): def connectedPrinterList(self):

View File

@ -118,6 +118,7 @@ class XmlMaterialProfile(InstanceContainer):
metadata.pop("variant", "") metadata.pop("variant", "")
metadata.pop("type", "") metadata.pop("type", "")
metadata.pop("base_file", "") metadata.pop("base_file", "")
metadata.pop("approximate_diameter", "")
## Begin Name Block ## Begin Name Block
builder.start("name") builder.start("name")
@ -437,6 +438,7 @@ class XmlMaterialProfile(InstanceContainer):
Logger.log("d", "Unsupported material setting %s", key) Logger.log("d", "Unsupported material setting %s", key)
self._cached_values = global_setting_values self._cached_values = global_setting_values
meta_data["approximate_diameter"] = round(diameter)
meta_data["compatible"] = global_compatibility meta_data["compatible"] = global_compatibility
self.setMetaData(meta_data) self.setMetaData(meta_data)
self._dirty = False self._dirty = False

View File

@ -635,7 +635,7 @@
"description": "Width of a single line. Generally, the width of each line should correspond to the width of the nozzle. However, slightly reducing this value could produce better prints.", "description": "Width of a single line. Generally, the width of each line should correspond to the width of the nozzle. However, slightly reducing this value could produce better prints.",
"unit": "mm", "unit": "mm",
"minimum_value": "0.001", "minimum_value": "0.001",
"minimum_value_warning": "0.5 * machine_nozzle_size", "minimum_value_warning": "0.1 + 0.4 * machine_nozzle_size",
"maximum_value_warning": "2 * machine_nozzle_size", "maximum_value_warning": "2 * machine_nozzle_size",
"default_value": 0.4, "default_value": 0.4,
"type": "float", "type": "float",
@ -649,7 +649,7 @@
"description": "Width of a single wall line.", "description": "Width of a single wall line.",
"unit": "mm", "unit": "mm",
"minimum_value": "0.001", "minimum_value": "0.001",
"minimum_value_warning": "0.75 * machine_nozzle_size", "minimum_value_warning": "0.1 + 0.4 * machine_nozzle_size",
"maximum_value_warning": "2 * machine_nozzle_size", "maximum_value_warning": "2 * machine_nozzle_size",
"value": "line_width", "value": "line_width",
"default_value": 0.4, "default_value": 0.4,
@ -663,7 +663,7 @@
"description": "Width of the outermost wall line. By lowering this value, higher levels of detail can be printed.", "description": "Width of the outermost wall line. By lowering this value, higher levels of detail can be printed.",
"unit": "mm", "unit": "mm",
"minimum_value": "0.001", "minimum_value": "0.001",
"minimum_value_warning": "0.75 * machine_nozzle_size if outer_inset_first else 0.1 * machine_nozzle_size", "minimum_value_warning": "(0.1 + 0.4 * machine_nozzle_size) if outer_inset_first else 0.1 * machine_nozzle_size",
"maximum_value_warning": "2 * machine_nozzle_size", "maximum_value_warning": "2 * machine_nozzle_size",
"default_value": 0.4, "default_value": 0.4,
"value": "wall_line_width", "value": "wall_line_width",
@ -676,7 +676,7 @@
"description": "Width of a single wall line for all wall lines except the outermost one.", "description": "Width of a single wall line for all wall lines except the outermost one.",
"unit": "mm", "unit": "mm",
"minimum_value": "0.001", "minimum_value": "0.001",
"minimum_value_warning": "0.5 * machine_nozzle_size", "minimum_value_warning": "0.1 + 0.4 * machine_nozzle_size",
"maximum_value_warning": "2 * machine_nozzle_size", "maximum_value_warning": "2 * machine_nozzle_size",
"default_value": 0.4, "default_value": 0.4,
"value": "wall_line_width", "value": "wall_line_width",
@ -691,7 +691,7 @@
"description": "Width of a single top/bottom line.", "description": "Width of a single top/bottom line.",
"unit": "mm", "unit": "mm",
"minimum_value": "0.001", "minimum_value": "0.001",
"minimum_value_warning": "0.1 * machine_nozzle_size", "minimum_value_warning": "0.1 + 0.4 * machine_nozzle_size",
"maximum_value_warning": "2 * machine_nozzle_size", "maximum_value_warning": "2 * machine_nozzle_size",
"default_value": 0.4, "default_value": 0.4,
"type": "float", "type": "float",
@ -704,7 +704,7 @@
"description": "Width of a single infill line.", "description": "Width of a single infill line.",
"unit": "mm", "unit": "mm",
"minimum_value": "0.001", "minimum_value": "0.001",
"minimum_value_warning": "0.75 * machine_nozzle_size", "minimum_value_warning": "0.1 + 0.4 * machine_nozzle_size",
"maximum_value_warning": "3 * machine_nozzle_size", "maximum_value_warning": "3 * machine_nozzle_size",
"default_value": 0.4, "default_value": 0.4,
"type": "float", "type": "float",
@ -718,7 +718,7 @@
"description": "Width of a single skirt or brim line.", "description": "Width of a single skirt or brim line.",
"unit": "mm", "unit": "mm",
"minimum_value": "0.001", "minimum_value": "0.001",
"minimum_value_warning": "0.75 * machine_nozzle_size", "minimum_value_warning": "0.1 + 0.4 * machine_nozzle_size",
"maximum_value_warning": "3 * machine_nozzle_size", "maximum_value_warning": "3 * machine_nozzle_size",
"default_value": 0.4, "default_value": 0.4,
"type": "float", "type": "float",
@ -733,7 +733,7 @@
"description": "Width of a single support structure line.", "description": "Width of a single support structure line.",
"unit": "mm", "unit": "mm",
"minimum_value": "0.001", "minimum_value": "0.001",
"minimum_value_warning": "0.75 * machine_nozzle_size", "minimum_value_warning": "0.1 + 0.4 * machine_nozzle_size",
"maximum_value_warning": "3 * machine_nozzle_size", "maximum_value_warning": "3 * machine_nozzle_size",
"default_value": 0.4, "default_value": 0.4,
"type": "float", "type": "float",
@ -750,7 +750,7 @@
"unit": "mm", "unit": "mm",
"default_value": 0.4, "default_value": 0.4,
"minimum_value": "0.001", "minimum_value": "0.001",
"minimum_value_warning": "0.4 * machine_nozzle_size", "minimum_value_warning": "0.1 + 0.4 * machine_nozzle_size",
"maximum_value_warning": "2 * machine_nozzle_size", "maximum_value_warning": "2 * machine_nozzle_size",
"type": "float", "type": "float",
"enabled": "support_enable and support_interface_enable", "enabled": "support_enable and support_interface_enable",
@ -769,7 +769,7 @@
"default_value": 0.4, "default_value": 0.4,
"value": "line_width", "value": "line_width",
"minimum_value": "0.001", "minimum_value": "0.001",
"minimum_value_warning": "0.75 * machine_nozzle_size", "minimum_value_warning": "0.1 + 0.4 * machine_nozzle_size",
"maximum_value_warning": "2 * machine_nozzle_size", "maximum_value_warning": "2 * machine_nozzle_size",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true "settable_per_extruder": true
@ -845,7 +845,7 @@
"unit": "mm", "unit": "mm",
"default_value": 0.8, "default_value": 0.8,
"minimum_value": "0", "minimum_value": "0",
"minimum_value_warning": "3 * resolveOrValue('layer_height')", "minimum_value_warning": "0.2 + resolveOrValue('layer_height')",
"maximum_value": "machine_height", "maximum_value": "machine_height",
"type": "float", "type": "float",
"value": "top_bottom_thickness", "value": "top_bottom_thickness",
@ -860,7 +860,7 @@
"minimum_value": "0", "minimum_value": "0",
"maximum_value_warning": "100", "maximum_value_warning": "100",
"type": "int", "type": "int",
"minimum_value_warning": "4", "minimum_value_warning": "2",
"value": "0 if infill_sparse_density == 100 else math.ceil(round(top_thickness / resolveOrValue('layer_height'), 4))", "value": "0 if infill_sparse_density == 100 else math.ceil(round(top_thickness / resolveOrValue('layer_height'), 4))",
"settable_per_mesh": true "settable_per_mesh": true
} }
@ -873,7 +873,7 @@
"unit": "mm", "unit": "mm",
"default_value": 0.6, "default_value": 0.6,
"minimum_value": "0", "minimum_value": "0",
"minimum_value_warning": "3 * resolveOrValue('layer_height')", "minimum_value_warning": "0.2 + resolveOrValue('layer_height')",
"type": "float", "type": "float",
"value": "top_bottom_thickness", "value": "top_bottom_thickness",
"maximum_value": "machine_height", "maximum_value": "machine_height",
@ -885,7 +885,7 @@
"label": "Bottom Layers", "label": "Bottom Layers",
"description": "The number of bottom layers. When calculated by the bottom thickness, this value is rounded to a whole number.", "description": "The number of bottom layers. When calculated by the bottom thickness, this value is rounded to a whole number.",
"minimum_value": "0", "minimum_value": "0",
"minimum_value_warning": "4", "minimum_value_warning": "2",
"default_value": 6, "default_value": 6,
"type": "int", "type": "int",
"value": "999999 if infill_sparse_density == 100 else math.ceil(round(bottom_thickness / resolveOrValue('layer_height'), 4))", "value": "999999 if infill_sparse_density == 100 else math.ceil(round(bottom_thickness / resolveOrValue('layer_height'), 4))",
@ -1121,6 +1121,65 @@
"type": "[int]", "type": "[int]",
"default_value": "[ ]", "default_value": "[ ]",
"enabled": "infill_pattern != 'concentric' and infill_pattern != 'concentric_3d' and infill_pattern != 'cubicsubdiv'", "enabled": "infill_pattern != 'concentric' and infill_pattern != 'concentric_3d' and infill_pattern != 'cubicsubdiv'",
"enabled": "infill_sparse_density > 0",
"settable_per_mesh": true
},
"spaghetti_infill_enabled":
{
"label": "Spaghetti Infill",
"description": "Print the infill every so often, so that the filament will curl up chaotically inside the object. This reduces print time, but the behaviour is rather unpredictable.",
"type": "bool",
"default_value": false,
"enabled": "infill_sparse_density > 0",
"settable_per_mesh": true
},
"spaghetti_max_infill_angle":
{
"label": "Spaghetti Maximum Infill Angle",
"description": "The maximum angle w.r.t. the Z axis of the inside of the print for areas which are to be filled with spaghetti infill afterwards. Lowering this value causes more angled parts in your model to be filled on each layer.",
"unit": "°",
"type": "float",
"default_value": 10,
"minimum_value": "0",
"maximum_value": "90",
"maximum_value_warning": "45",
"enabled": "infill_sparse_density > 0 and spaghetti_infill_enabled",
"settable_per_mesh": true
},
"spaghetti_max_height":
{
"label": "Spaghetti Infill Maximum Height",
"description": "The maximum height of inside space which can be combined and filled from the top.",
"unit": "mm",
"type": "float",
"default_value": 2.0,
"minimum_value": "layer_height",
"maximum_value_warning": "10.0",
"enabled": "infill_sparse_density > 0 and spaghetti_infill_enabled",
"settable_per_mesh": true
},
"spaghetti_inset":
{
"label": "Spaghetti Inset",
"description": "The offset from the walls from where the spaghetti infill will be printed.",
"unit": "mm",
"type": "float",
"default_value": 0.2,
"minimum_value_warning": "0",
"maximum_value_warning": "5.0",
"enabled": "infill_sparse_density > 0 and spaghetti_infill_enabled",
"settable_per_mesh": true
},
"spaghetti_flow":
{
"label": "Spaghetti Flow",
"description": "Adjusts the density of the spaghetti infill. Note that the Infill Density only controls the line spacing of the filling pattern, not the amount of extrusion for spaghetti infill.",
"unit": "%",
"type": "float",
"default_value": 20,
"minimum_value": "0",
"maximum_value_warning": "100",
"enabled": "infill_sparse_density > 0 and spaghetti_infill_enabled",
"settable_per_mesh": true "settable_per_mesh": true
}, },
"sub_div_rad_mult": "sub_div_rad_mult":
@ -1229,9 +1288,9 @@
"default_value": 0.1, "default_value": 0.1,
"minimum_value": "resolveOrValue('layer_height')", "minimum_value": "resolveOrValue('layer_height')",
"maximum_value_warning": "0.75 * machine_nozzle_size", "maximum_value_warning": "0.75 * machine_nozzle_size",
"maximum_value": "resolveOrValue('layer_height') * 8", "maximum_value": "resolveOrValue('layer_height') * (1.45 if spaghetti_infill_enabled else 8)",
"value": "resolveOrValue('layer_height')", "value": "resolveOrValue('layer_height')",
"enabled": "infill_sparse_density > 0", "enabled": "infill_sparse_density > 0 and not spaghetti_infill_enabled",
"settable_per_mesh": true "settable_per_mesh": true
}, },
"gradual_infill_steps": "gradual_infill_steps":
@ -1242,8 +1301,8 @@
"type": "int", "type": "int",
"minimum_value": "0", "minimum_value": "0",
"maximum_value_warning": "4", "maximum_value_warning": "4",
"maximum_value": "(20 - math.log(infill_line_distance) / math.log(2)) if infill_line_distance > 0 else 0", "maximum_value": "(20 - math.log(infill_line_distance) / math.log(2)) if infill_line_distance > 0 and not spaghetti_infill_enabled else 0",
"enabled": "infill_sparse_density > 0 and infill_pattern != 'cubicsubdiv'", "enabled": "infill_sparse_density > 0 and infill_pattern != 'cubicsubdiv' and not spaghetti_infill_enabled",
"settable_per_mesh": true "settable_per_mesh": true
}, },
"gradual_infill_step_height": "gradual_infill_step_height":
@ -2273,7 +2332,6 @@
"unit": "mm/s", "unit": "mm/s",
"type": "float", "type": "float",
"minimum_value": "0.1", "minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50", "maximum_value_warning": "50",
"default_value": 20, "default_value": 20,
"enabled": "resolveOrValue('jerk_enabled')", "enabled": "resolveOrValue('jerk_enabled')",
@ -2287,7 +2345,6 @@
"unit": "mm/s", "unit": "mm/s",
"type": "float", "type": "float",
"minimum_value": "0.1", "minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50", "maximum_value_warning": "50",
"default_value": 20, "default_value": 20,
"value": "jerk_print", "value": "jerk_print",
@ -2301,7 +2358,6 @@
"unit": "mm/s", "unit": "mm/s",
"type": "float", "type": "float",
"minimum_value": "0.1", "minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50", "maximum_value_warning": "50",
"default_value": 20, "default_value": 20,
"value": "jerk_print", "value": "jerk_print",
@ -2316,7 +2372,6 @@
"unit": "mm/s", "unit": "mm/s",
"type": "float", "type": "float",
"minimum_value": "0.1", "minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50", "maximum_value_warning": "50",
"default_value": 20, "default_value": 20,
"value": "jerk_wall", "value": "jerk_wall",
@ -2330,7 +2385,6 @@
"unit": "mm/s", "unit": "mm/s",
"type": "float", "type": "float",
"minimum_value": "0.1", "minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50", "maximum_value_warning": "50",
"default_value": 20, "default_value": 20,
"value": "jerk_wall", "value": "jerk_wall",
@ -2346,7 +2400,6 @@
"unit": "mm/s", "unit": "mm/s",
"type": "float", "type": "float",
"minimum_value": "0.1", "minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50", "maximum_value_warning": "50",
"default_value": 20, "default_value": 20,
"value": "jerk_print", "value": "jerk_print",
@ -2360,7 +2413,6 @@
"unit": "mm/s", "unit": "mm/s",
"type": "float", "type": "float",
"minimum_value": "0.1", "minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50", "maximum_value_warning": "50",
"default_value": 20, "default_value": 20,
"value": "jerk_print", "value": "jerk_print",
@ -2379,7 +2431,6 @@
"default_value": 20, "default_value": 20,
"value": "jerk_support", "value": "jerk_support",
"minimum_value": "0.1", "minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50", "maximum_value_warning": "50",
"enabled": "resolveOrValue('jerk_enabled') and support_enable", "enabled": "resolveOrValue('jerk_enabled') and support_enable",
"limit_to_extruder": "support_infill_extruder_nr", "limit_to_extruder": "support_infill_extruder_nr",
@ -2395,7 +2446,6 @@
"default_value": 20, "default_value": 20,
"value": "jerk_support", "value": "jerk_support",
"minimum_value": "0.1", "minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50", "maximum_value_warning": "50",
"enabled": "resolveOrValue('jerk_enabled') and extruderValue(support_interface_extruder_nr, 'support_interface_enable') and support_enable", "enabled": "resolveOrValue('jerk_enabled') and extruderValue(support_interface_extruder_nr, 'support_interface_enable') and support_enable",
"limit_to_extruder": "support_interface_extruder_nr", "limit_to_extruder": "support_interface_extruder_nr",
@ -2411,7 +2461,6 @@
"unit": "mm/s", "unit": "mm/s",
"type": "float", "type": "float",
"minimum_value": "0.1", "minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50", "maximum_value_warning": "50",
"default_value": 20, "default_value": 20,
"value": "jerk_print", "value": "jerk_print",
@ -2428,7 +2477,6 @@
"type": "float", "type": "float",
"default_value": 30, "default_value": 30,
"minimum_value": "0.1", "minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50", "maximum_value_warning": "50",
"value": "jerk_print if magic_spiralize else 30", "value": "jerk_print if magic_spiralize else 30",
"enabled": "resolveOrValue('jerk_enabled')", "enabled": "resolveOrValue('jerk_enabled')",
@ -2443,7 +2491,6 @@
"default_value": 20, "default_value": 20,
"value": "jerk_print", "value": "jerk_print",
"minimum_value": "0.1", "minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50", "maximum_value_warning": "50",
"enabled": "resolveOrValue('jerk_enabled')", "enabled": "resolveOrValue('jerk_enabled')",
"settable_per_mesh": true, "settable_per_mesh": true,
@ -2458,7 +2505,6 @@
"default_value": 20, "default_value": 20,
"value": "jerk_layer_0", "value": "jerk_layer_0",
"minimum_value": "0.1", "minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50", "maximum_value_warning": "50",
"enabled": "resolveOrValue('jerk_enabled')", "enabled": "resolveOrValue('jerk_enabled')",
"settable_per_mesh": true "settable_per_mesh": true
@ -2472,7 +2518,6 @@
"default_value": 20, "default_value": 20,
"value": "jerk_layer_0 * jerk_travel / jerk_print", "value": "jerk_layer_0 * jerk_travel / jerk_print",
"minimum_value": "0.1", "minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50", "maximum_value_warning": "50",
"enabled": "resolveOrValue('jerk_enabled')", "enabled": "resolveOrValue('jerk_enabled')",
"settable_per_extruder": true, "settable_per_extruder": true,
@ -2488,7 +2533,6 @@
"type": "float", "type": "float",
"default_value": 20, "default_value": 20,
"minimum_value": "0.1", "minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50", "maximum_value_warning": "50",
"value": "jerk_layer_0", "value": "jerk_layer_0",
"enabled": "resolveOrValue('jerk_enabled')", "enabled": "resolveOrValue('jerk_enabled')",
@ -2792,8 +2836,8 @@
{ {
"support_enable": "support_enable":
{ {
"label": "Enable Support", "label": "Generate Support",
"description": "Enable support structures. These structures support parts of the model with severe overhangs.", "description": "Generate structures to support parts of the model which have overhangs. Without these structures, such parts would collapse during printing.",
"type": "bool", "type": "bool",
"default_value": false, "default_value": false,
"settable_per_mesh": true, "settable_per_mesh": true,
@ -3080,7 +3124,7 @@
"type": "float", "type": "float",
"default_value": 1, "default_value": 1,
"minimum_value": "0", "minimum_value": "0",
"minimum_value_warning": "3 * resolveOrValue('layer_height')", "minimum_value_warning": "0.2 + resolveOrValue('layer_height')",
"maximum_value_warning": "10", "maximum_value_warning": "10",
"limit_to_extruder": "support_interface_extruder_nr", "limit_to_extruder": "support_interface_extruder_nr",
"enabled": "extruderValue(support_interface_extruder_nr, 'support_interface_enable') and support_enable", "enabled": "extruderValue(support_interface_extruder_nr, 'support_interface_enable') and support_enable",
@ -3095,7 +3139,7 @@
"type": "float", "type": "float",
"default_value": 1, "default_value": 1,
"minimum_value": "0", "minimum_value": "0",
"minimum_value_warning": "3 * resolveOrValue('layer_height')", "minimum_value_warning": "0.2 + resolveOrValue('layer_height')",
"maximum_value_warning": "10", "maximum_value_warning": "10",
"value": "extruderValue(support_interface_extruder_nr, 'support_interface_height')", "value": "extruderValue(support_interface_extruder_nr, 'support_interface_height')",
"limit_to_extruder": "support_interface_extruder_nr", "limit_to_extruder": "support_interface_extruder_nr",
@ -3111,7 +3155,7 @@
"default_value": 1, "default_value": 1,
"value": "extruderValue(support_interface_extruder_nr, 'support_interface_height')", "value": "extruderValue(support_interface_extruder_nr, 'support_interface_height')",
"minimum_value": "0", "minimum_value": "0",
"minimum_value_warning": "min(3 * resolveOrValue('layer_height'), extruderValue(support_interface_extruder_nr, 'support_bottom_stair_step_height'))", "minimum_value_warning": "min(0.2 + resolveOrValue('layer_height'), extruderValue(support_interface_extruder_nr, 'support_bottom_stair_step_height'))",
"maximum_value_warning": "10", "maximum_value_warning": "10",
"limit_to_extruder": "support_interface_extruder_nr", "limit_to_extruder": "support_interface_extruder_nr",
"enabled": "extruderValue(support_interface_extruder_nr, 'support_interface_enable') and support_enable", "enabled": "extruderValue(support_interface_extruder_nr, 'support_interface_enable') and support_enable",
@ -3496,7 +3540,7 @@
"value": "resolveOrValue('layer_height') * 1.5", "value": "resolveOrValue('layer_height') * 1.5",
"minimum_value": "0.001", "minimum_value": "0.001",
"minimum_value_warning": "0.04", "minimum_value_warning": "0.04",
"maximum_value_warning": "0.75 * extruderValue(adhesion_extruder_nr, 'raft_interface_line_width')", "maximum_value_warning": "0.75 * extruderValue(adhesion_extruder_nr, 'machine_nozzle_size')",
"enabled": "resolveOrValue('adhesion_type') == 'raft'", "enabled": "resolveOrValue('adhesion_type') == 'raft'",
"settable_per_mesh": false, "settable_per_mesh": false,
"settable_per_extruder": true, "settable_per_extruder": true,
@ -3898,7 +3942,7 @@
"value": "round(max(2 * min(extruderValues('prime_tower_line_width')), 0.5 * (resolveOrValue('prime_tower_size') - math.sqrt(max(0, resolveOrValue('prime_tower_size') ** 2 - max(extruderValues('prime_tower_min_volume')) / resolveOrValue('layer_height'))))), 3)", "value": "round(max(2 * min(extruderValues('prime_tower_line_width')), 0.5 * (resolveOrValue('prime_tower_size') - math.sqrt(max(0, resolveOrValue('prime_tower_size') ** 2 - max(extruderValues('prime_tower_min_volume')) / resolveOrValue('layer_height'))))), 3)",
"resolve": "max(extruderValues('prime_tower_wall_thickness'))", "resolve": "max(extruderValues('prime_tower_wall_thickness'))",
"minimum_value": "0.001", "minimum_value": "0.001",
"minimum_value_warning": "2 * min(extruderValues('prime_tower_line_width'))", "minimum_value_warning": "2 * min(extruderValues('prime_tower_line_width')) - 0.0001",
"maximum_value_warning": "resolveOrValue('prime_tower_size') / 2", "maximum_value_warning": "resolveOrValue('prime_tower_size') / 2",
"enabled": "resolveOrValue('prime_tower_enable')", "enabled": "resolveOrValue('prime_tower_enable')",
"settable_per_mesh": false, "settable_per_mesh": false,
@ -4122,6 +4166,40 @@
"settable_per_meshgroup": false, "settable_per_meshgroup": false,
"settable_globally": false "settable_globally": false
}, },
"mold_enabled":
{
"label": "Mold",
"description": "Print models as a mold, which can be cast in order to get a model which resembles the models on the build plate.",
"type": "bool",
"default_value": false,
"settable_per_mesh": true
},
"mold_width":
{
"label": "Minimal Mold Width",
"description": "The minimal distance between the ouside of the mold and the outside of the model.",
"unit": "mm",
"type": "float",
"minimum_value_warning": "wall_line_width_0 * 2",
"maximum_value_warning": "100",
"default_value": 5,
"settable_per_mesh": true,
"enabled": "mold_enabled"
},
"mold_angle":
{
"label": "Mold Angle",
"description": "The angle of overhang of the outer walls created for the mold. 0° will make the outer shell of the mold vertical, while 90° will make the outside of the model follow the contour of the model.",
"unit": "°",
"type": "float",
"minimum_value": "-89",
"minimum_value_warning": "0",
"maximum_value_warning": "support_angle",
"maximum_value": "90",
"default_value": 40,
"settable_per_mesh": true,
"enabled": "mold_enabled"
},
"infill_mesh_order": "infill_mesh_order":
{ {
"label": "Infill Mesh Order", "label": "Infill Mesh Order",
@ -4147,6 +4225,18 @@
"settable_per_meshgroup": false, "settable_per_meshgroup": false,
"settable_globally": false "settable_globally": false
}, },
"support_mesh_drop_down":
{
"label": "Drop Down Support Mesh",
"description": "Make support everywhere below the support mesh, so that there's no overhang in the support mesh.",
"type": "bool",
"default_value": true,
"enabled": "support_mesh",
"settable_per_mesh": true,
"settable_per_extruder": false,
"settable_per_meshgroup": false,
"settable_globally": false
},
"anti_overhang_mesh": "anti_overhang_mesh":
{ {
"label": "Anti Overhang Mesh", "label": "Anti Overhang Mesh",
@ -4178,7 +4268,8 @@
"description": "Spiralize smooths out the Z move of the outer edge. This will create a steady Z increase over the whole print. This feature turns a solid model into a single walled print with a solid bottom. This feature used to be called Joris in older versions.", "description": "Spiralize smooths out the Z move of the outer edge. This will create a steady Z increase over the whole print. This feature turns a solid model into a single walled print with a solid bottom. This feature used to be called Joris in older versions.",
"type": "bool", "type": "bool",
"default_value": false, "default_value": false,
"settable_per_mesh": true "settable_per_mesh": false,
"settable_per_extruder": false
} }
} }
}, },

View File

@ -0,0 +1,41 @@
{
"id": "imade3d_jellybox",
"version": 2,
"name": "IMADE3D JellyBOX",
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "IMADE3D",
"manufacturer": "IMADE3D",
"category": "Other",
"platform": "imade3d_jellybox_platform.stl",
"platform_offset": [ 0, -0.3, 0],
"file_formats": "text/x-gcode",
"preferred_variant": "*0.4*",
"preferred_material": "*generic_pla*",
"preferred_quality": "*fast*",
"has_materials": true,
"has_variants": true,
"has_machine_materials": true,
"has_machine_quality": true
},
"overrides": {
"machine_head_with_fans_polygon": { "default_value": [[ 0, 0 ],[ 0, 0 ],[ 0, 0 ],[ 0, 0 ]]},
"machine_name": { "default_value": "IMADE3D JellyBOX" },
"machine_width": { "default_value": 170 },
"machine_height": { "default_value": 145 },
"machine_depth": { "default_value": 160 },
"machine_nozzle_size": { "default_value": 0.4 },
"material_diameter": { "default_value": 1.75 },
"machine_heated_bed": { "default_value": true },
"machine_center_is_zero": { "default_value": false },
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
"machine_start_gcode": {
"default_value": ";---------------------------------------\n; ; ; Jellybox Start Script Begin ; ; ;\n;_______________________________________\n; M92 E140 ;optionally adjust steps per mm for your filament\n\n; Print Settings Summary\n; (leave these alone: this is only a list of the slicing settings)\n; (overwriting these values will NOT change your printer's behavior)\n; sliced for : {machine_name}\n; nozzle diameter : {machine_nozzle_size}\n; filament diameter : {material_diameter}\n; layer height : {layer_height}\n; 1st layer height : {layer_height_0}\n; line width : {line_width}\n; outer wall wipe dist. : {wall_0_wipe_dist}\n; infill line width : {infill_line_width}\n; wall thickness : {wall_thickness}\n; top thickness : {top_thickness}\n; bottom thickness : {bottom_thickness}\n; infill density : {infill_sparse_density}\n; infill pattern : {infill_pattern}\n; print temperature : {material_print_temperature}\n; 1st layer print temp. : {material_print_temperature_layer_0}\n; heated bed temperature : {material_bed_temperature}\n; 1st layer bed temp. : {material_bed_temperature_layer_0}\n; regular fan speed : {cool_fan_speed_min}\n; max fan speed : {cool_fan_speed_max}\n; retraction amount : {retraction_amount}\n; retr. retract speed : {retraction_retract_speed}\n; retr. prime speed : {retraction_prime_speed}\n; build plate adhesion : {adhesion_type}\n; support ? {support_enable}\n; spiralized ? {magic_spiralize}\n\nM117 Preparing ;write Preparing\nM140 S{material_bed_temperature_layer_0} ;set bed temperature and move on\nM104 S{material_print_temperature_layer_0} ;set extruder temperature and move on\nM206 X10.0 Y0.0 ;set x homing offset for default bed leveling\nG21 ;metric values\nG90 ;absolute positioning\nM107 ;start with the fan off\nM82 ;set extruder to absolute mode\nG28 ;home all axes\nM203 Z4 ;slow Z speed down for greater accuracy when probing\nG29 ;auto bed leveling procedure\nM203 Z7 ;pick up z speed again for printing\nM190 S{material_bed_temperature_layer_0} ;wait for the bed to reach desired temperature\nM109 S{material_print_temperature_layer_0} ;wait for the extruder to reach desired temperature\nG92 E0 ;reset the extruder position\nG1 F1500 E15 ;extrude 15mm of feed stock\nG92 E0 ;reset the extruder position again\nM117 Print starting ;write Print starting\n;---------------------------------------------\n; ; ; Jellybox Printer Start Script End ; ; ;\n;_____________________________________________\n"
},
"machine_end_gcode": {
"default_value": "\n;---------------------------------\n;;; Jellybox End Script Begin ;;;\n;_________________________________\nM117 Finishing Up ;write Finishing Up\n\nM104 S0 ;extruder heater off\nM140 S0 ;bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG90 ;absolute positioning\nG28 X ;home x, so the head is out of the way\nG1 Y100 ;move Y forward, so the print is more accessible\nM84 ;steppers off\n\nM117 Print finished ;write Print finished\n;---------------------------------------\n;;; Jellybox End Script End ;;;\n;_______________________________________"
}
}
}

View File

@ -1,35 +0,0 @@
{
"id": "jellybox",
"version": 2,
"name": "JellyBOX",
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "IMADE3D",
"manufacturer": "IMADE3D",
"category": "Other",
"platform": "jellybox_platform.stl",
"platform_offset": [ 0, -0.3, 0],
"file_formats": "text/x-gcode",
"has_materials": true,
"has_machine_materials": true
},
"overrides": {
"machine_name": { "default_value": "IMADE3D JellyBOX" },
"machine_width": { "default_value": 170 },
"machine_height": { "default_value": 145 },
"machine_depth": { "default_value": 160 },
"machine_nozzle_size": { "default_value": 0.4 },
"material_diameter": { "default_value": 1.75 },
"machine_heated_bed": { "default_value": true },
"machine_center_is_zero": { "default_value": false },
"machine_gcode_flavor": { "default_value": "RepRap (Marlin/Sprinter)" },
"machine_start_gcode": {
"default_value": ";---------------------------------------\n; ; ; Jellybox Start Script Begin ; ; ;\n;_______________________________________\n; M92 E140 ;optionally adjust steps per mm for your filament\n\n; Print Settings Summary\n; (overwriting these values will NOT change your printer's behavior)\n; sliced for: {machine_name}\n; nozzle diameter: {machine_nozzle_size}\n; filament diameter: {material_diameter}\n; layer height: {layer_height}\n; 1st layer height: {layer_height_0}\n; line width: {line_width}\n; wall thickness: {wall_thickness}\n; infill density: {infill_sparse_density}\n; infill pattern: {infill_pattern}\n; print temperature: {material_print_temperature}\n; heated bed temperature: {material_bed_temperature}\n; regular fan speed: {cool_fan_speed_min}\n; max fan speed: {cool_fan_speed_max}\n; support? {support_enable}\n; spiralized? {magic_spiralize}\n\nM117 Preparing ;write Preparing\nM140 S{material_bed_temperature} ;set bed temperature and move on\nM104 S{material_print_temperature} ;set extruder temperature and move on\nM206 X10.0 Y0.0 ;set x homing offset for default bed leveling\nG21 ;metric values\nG90 ;absolute positioning\nM107 ;start with the fan off\nM82 ;set extruder to absolute mode\nG28 ;home all axes\nM203 Z5 ;slow Z speed down for greater accuracy when probing\nG29 ;auto bed leveling procedure\nM203 Z7 ;pick up z speed again for printing\nM190 S{material_bed_temperature} ;wait for the bed to reach desired temperature\nM109 S{material_print_temperature} ;wait for the extruder to reach desired temperature\nG92 E0 ;reset the extruder position\nG1 F200 E5 ;extrude 5mm of feed stock\nG92 E0 ;reset the extruder position again\nM117 Print starting ;write Print starting\n;---------------------------------------------\n; ; ; Jellybox Printer Start Script End ; ; ;\n;_____________________________________________"
},
"machine_end_gcode": {
"default_value": "\n;---------------------------------\n;;; Jellybox End Script Begin ;;;\n;_________________________________\nM117 Finishing Up ;write Finishing Up\n\nM104 S0 ;extruder heater off\nM140 S0 ;bed heater off (if you have it)\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG90 ;absolute positioning\nG28 X ;home x, so the head is out of the way\nG1 Y100 ;move Y forward, so the print is more accessible\nM84 ;steppers off\n\nM117 Print finished ;write Print finished\n;---------------------------------------\n;;; Jellybox End Script End ;;;\n;_______________________________________"
}
}
}

View File

@ -0,0 +1,70 @@
{
"id": "makeR_pegasus",
"version": 2,
"name": "makeR Pegasus",
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "makeR",
"manufacturer": "makeR",
"category": "Other",
"file_formats": "text/x-gcode",
"icon": "icon_ultimaker2",
"platform": "makeR_pegasus_platform.stl",
"platform_offset": [-200,-10,200]
},
"overrides": {
"machine_name": { "default_value": " makeR Pegasus" },
"machine_heated_bed": {
"default_value": true
},
"machine_width": {
"default_value": 400
},
"machine_height": {
"default_value": 400
},
"machine_depth": {
"default_value": 400
},
"machine_center_is_zero": {
"default_value": false
},
"machine_nozzle_size": {
"default_value": 0.4
},
"material_diameter": {
"default_value": 2.85
},
"machine_nozzle_heat_up_speed": {
"default_value": 2
},
"machine_nozzle_cool_down_speed": {
"default_value": 2
},
"machine_head_polygon": {
"default_value": [
[-75, -18],
[-75, 35],
[18, 35],
[18, -18]
]
},
"gantry_height": {
"default_value": -25
},
"machine_platform_offset":{
"default_value":-25
},
"machine_gcode_flavor": {
"default_value": "RepRap (Marlin/Sprinter)"
},
"machine_start_gcode": {
"default_value": "G1 Z15;\nG28;Home\nG29;Auto Level\nG1 Z5 F5000;Move the platform down 15mm"
},
"machine_end_gcode": {
"default_value": "M104 S0;Turn off temperature\nG28 X0; Home X\nM84; Disable Motors"
}
}
}

View File

@ -0,0 +1,67 @@
{
"id": "makeR_prusa_tairona_i3",
"version": 2,
"name": "makeR Prusa Tairona i3",
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "makeR",
"manufacturer": "makeR",
"category": "Other",
"file_formats": "text/x-gcode",
"icon": "icon_ultimaker2",
"platform": "makeR_prusa_tairona_i3_platform.stl",
"platform_offset": [-2,0,0]
},
"overrides": {
"machine_name": { "default_value": "makeR Prusa Tairona I3" },
"machine_heated_bed": {
"default_value": true
},
"machine_width": {
"default_value": 200
},
"machine_height": {
"default_value": 200
},
"machine_depth": {
"default_value": 200
},
"machine_center_is_zero": {
"default_value": false
},
"machine_nozzle_size": {
"default_value": 0.4
},
"material_diameter": {
"default_value": 1.75
},
"machine_nozzle_heat_up_speed": {
"default_value": 2
},
"machine_nozzle_cool_down_speed": {
"default_value": 2
},
"machine_head_polygon": {
"default_value": [
[-75, -18],
[-75, 35],
[18, 35],
[18, -18]
]
},
"gantry_height": {
"default_value": 55
},
"machine_gcode_flavor": {
"default_value": "RepRap (Marlin/Sprinter)"
},
"machine_start_gcode": {
"default_value": "G1 Z15;\nG28;Home\nG29;Auto Level\nG1 Z5 F5000;Move the platform down 15mm"
},
"machine_end_gcode": {
"default_value": "M104 S0;Turn off temperature\nG28 X0; Home X\nM84; Disable Motors"
}
}
}

View File

@ -0,0 +1,162 @@
{
"id": "peopoly_moai",
"version": 2,
"name": "Peopoly Moai",
"inherits": "fdmprinter",
"metadata": {
"visible": true,
"author": "fieldOfView",
"manufacturer": "Peopoly",
"category": "Other",
"file_formats": "text/x-gcode",
"has_machine_quality": true,
"has_materials": false
},
"overrides": {
"machine_name": {
"default_value": "Moai"
},
"machine_width": {
"default_value": 130
},
"machine_height": {
"default_value": 180
},
"machine_depth": {
"default_value": 130
},
"machine_nozzle_size": {
"default_value": 0.067
},
"machine_head_with_fans_polygon":
{
"default_value": [
[ -20, 10 ],
[ -20, -10 ],
[ 10, 10 ],
[ 10, -10 ]
]
},
"machine_gcode_flavor": {
"default_value": "RepRap (Marlin/Sprinter)"
},
"machine_start_gcode": {
"default_value": "G28 ;Home"
},
"machine_end_gcode": {
"default_value": "M104 S0\nM140 S0\nG28 X0 Y0\nM84"
},
"line_width": {
"minimum_value_warning": "machine_nozzle_size"
},
"wall_line_width": {
"minimum_value_warning": "machine_nozzle_size"
},
"wall_line_width_x": {
"minimum_value_warning": "machine_nozzle_size"
},
"skin_line_width": {
"minimum_value_warning": "machine_nozzle_size"
},
"infill_line_width": {
"minimum_value_warning": "machine_nozzle_size"
},
"skirt_brim_line_width": {
"minimum_value_warning": "machine_nozzle_size"
},
"layer_height": {
"maximum_value_warning": "0.5",
"minimum_value_warning": "0.02"
},
"layer_height_0": {
"maximum_value_warning": "0.5",
"minimum_value_warning": "0.02",
"value": "0.1"
},
"top_bottom_thickness": {
"minimum_value_warning": "0.1"
},
"infill_sparse_thickness": {
"maximum_value_warning": "0.5"
},
"speed_print": {
"maximum_value_warning": "300"
},
"speed_infill": {
"maximum_value_warning": "300"
},
"speed_wall": {
"maximum_value_warning": "300",
"value": "speed_print"
},
"speed_wall_0": {
"maximum_value_warning": "300"
},
"speed_wall_x": {
"maximum_value_warning": "300",
"value": "speed_print"
},
"speed_topbottom": {
"maximum_value_warning": "300",
"value": "speed_print"
},
"speed_travel": {
"value": "300"
},
"speed_travel_layer_0": {
"value": "300"
},
"speed_layer_0": {
"value": "5"
},
"speed_slowdown_layers": {
"value": "2"
},
"acceleration_enabled": {
"value": "False"
},
"print_sequence": {
"enabled": false
},
"support_enable": {
"enabled": false
},
"machine_nozzle_temp_enabled": {
"value": "False"
},
"material_bed_temperature": {
"enabled": false
},
"material_diameter": {
"enabled": false,
"value": "1.75"
},
"cool_fan_enabled": {
"enabled": false,
"value": "False"
},
"retraction_enable": {
"enabled": false,
"value": "False"
},
"retraction_combing": {
"enabled": false,
"value": "'off'"
},
"retract_at_layer_change": {
"enabled": false
},
"cool_min_layer_time_fan_speed_max": {
"enabled": false
},
"cool_fan_full_at_height": {
"enabled": false
},
"cool_fan_full_layer": {
"enabled": false
}
}
}

View File

@ -76,7 +76,7 @@
"value": "100" "value": "100"
}, },
"material_bed_temperature": { "material_bed_temperature": {
"visible": "False" "enabled": false
}, },
"material_diameter": { "material_diameter": {
"value": "1.75" "value": "1.75"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,18 @@
# Cura JSON setting files
# Copyright (C) 2017 Ultimaker
# This file is distributed under the same license as the Cura package.
# Ruben Dulek <r.dulek@ultimaker.com>, 2017.
#
#, fuzzy #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Uranium json setting files\n" "Project-Id-Version: Cura 2.5\n"
"Report-Msgid-Bugs-To: http://github.com/ultimaker/uranium\n" "Report-Msgid-Bugs-To: http://github.com/Ultimaker/Cura\n"
"POT-Creation-Date: 2016-12-28 10:51+0000\n" "POT-Creation-Date: 2017-03-27 17:27+0000\n"
"PO-Revision-Date: 2017-01-12 15:51+0100\n" "PO-Revision-Date: 2017-04-04 11:27+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: Bothof <info@bothof.nl>\n"
"Language-Team: LANGUAGE\n" "Language-Team: Bothof <info@bothof.nl>\n"
"Language: \n" "Language: de\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,18 @@
# Cura JSON setting files
# Copyright (C) 2017 Ultimaker
# This file is distributed under the same license as the Cura package.
# Ruben Dulek <r.dulek@ultimaker.com>, 2017.
#
#, fuzzy #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Uranium json setting files\n" "Project-Id-Version: Cura 2.5\n"
"Report-Msgid-Bugs-To: http://github.com/ultimaker/uranium\n" "Report-Msgid-Bugs-To: http://github.com/Ultimaker/Cura\n"
"POT-Creation-Date: 2016-12-28 10:51+0000\n" "POT-Creation-Date: 2017-03-27 17:27+0000\n"
"PO-Revision-Date: 2017-01-12 15:51+0100\n" "PO-Revision-Date: 2017-04-04 11:27+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: Bothof <info@bothof.nl>\n"
"Language-Team: LANGUAGE\n" "Language-Team: Bothof <info@bothof.nl>\n"
"Language: \n" "Language: es\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Uranium json setting files\n" "Project-Id-Version: Uranium json setting files\n"
"Report-Msgid-Bugs-To: http://github.com/ultimaker/uranium\n" "Report-Msgid-Bugs-To: http://github.com/ultimaker/uranium\n"
"POT-Creation-Date: 2016-12-28 10:51+0000\n" "POT-Creation-Date: 2017-03-27 17:27+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE\n" "Language-Team: LANGUAGE\n"

View File

@ -3,7 +3,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Uranium json setting files\n" "Project-Id-Version: Uranium json setting files\n"
"Report-Msgid-Bugs-To: http://github.com/ultimaker/uranium\n" "Report-Msgid-Bugs-To: http://github.com/ultimaker/uranium\n"
"POT-Creation-Date: 2016-12-28 10:51+0000\n" "POT-Creation-Date: 2017-03-27 17:27+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE\n" "Language-Team: LANGUAGE\n"
@ -268,6 +268,18 @@ msgid ""
"extruder is no longer used." "extruder is no longer used."
msgstr "" msgstr ""
#: fdmprinter.def.json
msgctxt "machine_nozzle_temp_enabled label"
msgid "Enable Nozzle Temperature Control"
msgstr ""
#: fdmprinter.def.json
msgctxt "machine_nozzle_temp_enabled description"
msgid ""
"Whether to control temperature from Cura. Turn this off to control nozzle "
"temperature from outside of Cura."
msgstr ""
#: fdmprinter.def.json #: fdmprinter.def.json
msgctxt "machine_nozzle_heat_up_speed label" msgctxt "machine_nozzle_heat_up_speed label"
msgid "Heat up speed" msgid "Heat up speed"
@ -856,6 +868,47 @@ msgctxt "top_bottom_pattern option zigzag"
msgid "Zig Zag" msgid "Zig Zag"
msgstr "" msgstr ""
#: fdmprinter.def.json
msgctxt "top_bottom_pattern_0 label"
msgid "Bottom Pattern Initial Layer"
msgstr ""
#: fdmprinter.def.json
msgctxt "top_bottom_pattern_0 description"
msgid "The pattern on the bottom of the print on the first layer."
msgstr ""
#: fdmprinter.def.json
msgctxt "top_bottom_pattern_0 option lines"
msgid "Lines"
msgstr ""
#: fdmprinter.def.json
msgctxt "top_bottom_pattern_0 option concentric"
msgid "Concentric"
msgstr ""
#: fdmprinter.def.json
msgctxt "top_bottom_pattern_0 option zigzag"
msgid "Zig Zag"
msgstr ""
#: fdmprinter.def.json
msgctxt "skin_angles label"
msgid "Top/Bottom Line Directions"
msgstr ""
#: fdmprinter.def.json
msgctxt "skin_angles description"
msgid ""
"A list of integer line directions to use when the top/bottom layers use the "
"lines or zig zag pattern. Elements from the list are used sequentially as "
"the layers progress and when the end of the list is reached, it starts at "
"the beginning again. The list items are separated by commas and the whole "
"list is contained in square brackets. Default is an empty list which means "
"use the traditional default angles (45 and 135 degrees)."
msgstr ""
#: fdmprinter.def.json #: fdmprinter.def.json
msgctxt "wall_0_inset label" msgctxt "wall_0_inset label"
msgid "Outer Wall Inset" msgid "Outer Wall Inset"
@ -1124,6 +1177,22 @@ msgctxt "infill_pattern option zigzag"
msgid "Zig Zag" msgid "Zig Zag"
msgstr "" msgstr ""
#: fdmprinter.def.json
msgctxt "infill_angles label"
msgid "Infill Line Directions"
msgstr ""
#: fdmprinter.def.json
msgctxt "infill_angles description"
msgid ""
"A list of integer line directions to use. Elements from the list are used "
"sequentially as the layers progress and when the end of the list is reached, "
"it starts at the beginning again. The list items are separated by commas and "
"the whole list is contained in square brackets. Default is an empty list "
"which means use the traditional default angles (45 and 135 degrees for the "
"lines and zig zag patterns and 45 degrees for all other patterns)."
msgstr ""
#: fdmprinter.def.json #: fdmprinter.def.json
msgctxt "sub_div_rad_mult label" msgctxt "sub_div_rad_mult label"
msgid "Cubic Subdivision Radius" msgid "Cubic Subdivision Radius"
@ -1262,6 +1331,97 @@ msgid ""
"through the surface." "through the surface."
msgstr "" msgstr ""
#: fdmprinter.def.json
msgctxt "min_infill_area label"
msgid "Minimum Infill Area"
msgstr ""
#: fdmprinter.def.json
msgctxt "min_infill_area description"
msgid "Don't generate areas of infill smaller than this (use skin instead)."
msgstr ""
#: fdmprinter.def.json
msgctxt "expand_skins_into_infill label"
msgid "Expand Skins Into Infill"
msgstr ""
#: fdmprinter.def.json
msgctxt "expand_skins_into_infill description"
msgid ""
"Expand skin areas of top and/or bottom skin of flat surfaces. By default, "
"skins stop under the wall lines that surround infill but this can lead to "
"holes appearing when the infill density is low. This setting extends the "
"skins beyond the wall lines so that the infill on the next layer rests on "
"skin."
msgstr ""
#: fdmprinter.def.json
msgctxt "expand_upper_skins label"
msgid "Expand Upper Skins"
msgstr ""
#: fdmprinter.def.json
msgctxt "expand_upper_skins description"
msgid ""
"Expand upper skin areas (areas with air above) so that they support infill "
"above."
msgstr ""
#: fdmprinter.def.json
msgctxt "expand_lower_skins label"
msgid "Expand Lower Skins"
msgstr ""
#: fdmprinter.def.json
msgctxt "expand_lower_skins description"
msgid ""
"Expand lower skin areas (areas with air below) so that they are anchored by "
"the infill layers above and below."
msgstr ""
#: fdmprinter.def.json
msgctxt "expand_skins_expand_distance label"
msgid "Skin Expand Distance"
msgstr ""
#: fdmprinter.def.json
msgctxt "expand_skins_expand_distance description"
msgid ""
"The distance the skins are expanded into the infill. The default distance is "
"enough to bridge the gap between the infill lines and will stop holes "
"appearing in the skin where it meets the wall when the infill density is "
"low. A smaller distance will often be sufficient."
msgstr ""
#: fdmprinter.def.json
msgctxt "max_skin_angle_for_expansion label"
msgid "Maximum Skin Angle for Expansion"
msgstr ""
#: fdmprinter.def.json
msgctxt "max_skin_angle_for_expansion description"
msgid ""
"Top and/or bottom surfaces of your object with an angle larger than this "
"setting, won't have their top/bottom skin expanded. This avoids expanding "
"the narrow skin areas that are created when the model surface has a near "
"vertical slope. An angle of 0° is horizontal, while an angle of 90° is "
"vertical."
msgstr ""
#: fdmprinter.def.json
msgctxt "min_skin_width_for_expansion label"
msgid "Minimum Skin Width for Expansion"
msgstr ""
#: fdmprinter.def.json
msgctxt "min_skin_width_for_expansion description"
msgid ""
"Skin areas narrower than this are not expanded. This avoids expanding the "
"narrow skin areas that are created when the model surface has a slope close "
"to the vertical."
msgstr ""
#: fdmprinter.def.json #: fdmprinter.def.json
msgctxt "material label" msgctxt "material label"
msgid "Material" msgid "Material"
@ -1304,8 +1464,7 @@ msgstr ""
#: fdmprinter.def.json #: fdmprinter.def.json
msgctxt "material_print_temperature description" msgctxt "material_print_temperature description"
msgid "" msgid "The temperature used for printing."
"The temperature used for printing. Set at 0 to pre-heat the printer manually."
msgstr "" msgstr ""
#: fdmprinter.def.json #: fdmprinter.def.json
@ -1376,8 +1535,8 @@ msgstr ""
#: fdmprinter.def.json #: fdmprinter.def.json
msgctxt "material_bed_temperature description" msgctxt "material_bed_temperature description"
msgid "" msgid ""
"The temperature used for the heated build plate. Set at 0 to pre-heat the " "The temperature used for the heated build plate. If this is 0, the bed will "
"printer manually." "not heat up for this print."
msgstr "" msgstr ""
#: fdmprinter.def.json #: fdmprinter.def.json
@ -2216,6 +2375,16 @@ msgctxt "retraction_combing option noskin"
msgid "No Skin" msgid "No Skin"
msgstr "" msgstr ""
#: fdmprinter.def.json
msgctxt "travel_retract_before_outer_wall label"
msgid "Retract Before Outer Wall"
msgstr ""
#: fdmprinter.def.json
msgctxt "travel_retract_before_outer_wall description"
msgid "Always retract when moving to start an outer wall."
msgstr ""
#: fdmprinter.def.json #: fdmprinter.def.json
msgctxt "travel_avoid_other_parts label" msgctxt "travel_avoid_other_parts label"
msgid "Avoid Printed Parts When Traveling" msgid "Avoid Printed Parts When Traveling"
@ -2671,7 +2840,7 @@ msgctxt "support_z_distance description"
msgid "" msgid ""
"Distance from the top/bottom of the support structure to the print. This gap " "Distance from the top/bottom of the support structure to the print. This gap "
"provides clearance to remove the supports after the model is printed. This " "provides clearance to remove the supports after the model is printed. This "
"value is rounded down to a multiple of the layer height." "value is rounded up to a multiple of the layer height."
msgstr "" msgstr ""
#: fdmprinter.def.json #: fdmprinter.def.json

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,18 @@
# Cura JSON setting files
# Copyright (C) 2017 Ultimaker
# This file is distributed under the same license as the Cura package.
# Ruben Dulek <r.dulek@ultimaker.com>, 2017.
#
#, fuzzy #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Uranium json setting files\n" "Project-Id-Version: Cura 2.5\n"
"Report-Msgid-Bugs-To: http://github.com/ultimaker/uranium\n" "Report-Msgid-Bugs-To: http://github.com/Ultimaker/Cura\n"
"POT-Creation-Date: 2016-12-28 10:51+0000\n" "POT-Creation-Date: 2017-03-27 17:27+0000\n"
"PO-Revision-Date: 2017-01-12 15:51+0100\n" "PO-Revision-Date: 2017-04-04 11:27+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: Bothof <info@bothof.nl>\n"
"Language-Team: LANGUAGE\n" "Language-Team: Bothof <info@bothof.nl>\n"
"Language: \n" "Language: fi\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,18 @@
# Cura JSON setting files
# Copyright (C) 2017 Ultimaker
# This file is distributed under the same license as the Cura package.
# Ruben Dulek <r.dulek@ultimaker.com>, 2017.
#
#, fuzzy #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Uranium json setting files\n" "Project-Id-Version: Cura 2.5\n"
"Report-Msgid-Bugs-To: http://github.com/ultimaker/uranium\n" "Report-Msgid-Bugs-To: http://github.com/Ultimaker/Cura\n"
"POT-Creation-Date: 2016-12-28 10:51+0000\n" "POT-Creation-Date: 2017-03-27 17:27+0000\n"
"PO-Revision-Date: 2017-01-12 15:51+0100\n" "PO-Revision-Date: 2017-04-04 11:27+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: Bothof <info@bothof.nl>\n"
"Language-Team: LANGUAGE\n" "Language-Team: Bothof <info@bothof.nl>\n"
"Language: \n" "Language: fr\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,18 @@
# Cura JSON setting files
# Copyright (C) 2017 Ultimaker
# This file is distributed under the same license as the Cura package.
# Ruben Dulek <r.dulek@ultimaker.com>, 2017.
#
#, fuzzy #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Uranium json setting files\n" "Project-Id-Version: Cura 2.5\n"
"Report-Msgid-Bugs-To: http://github.com/ultimaker/uranium\n" "Report-Msgid-Bugs-To: http://github.com/Ultimaker/Cura\n"
"POT-Creation-Date: 2016-12-28 10:51+0000\n" "POT-Creation-Date: 2017-03-27 17:27+0000\n"
"PO-Revision-Date: 2017-01-12 15:51+0100\n" "PO-Revision-Date: 2017-04-04 11:27+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: Bothof <info@bothof.nl>\n"
"Language-Team: LANGUAGE\n" "Language-Team: Bothof <info@bothof.nl>\n"
"Language: \n" "Language: it\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

File diff suppressed because it is too large Load Diff

3208
resources/i18n/jp/cura.po Normal file

File diff suppressed because it is too large Load Diff

3330
resources/i18n/ko/cura.po Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,18 @@
# Cura JSON setting files
# Copyright (C) 2017 Ultimaker
# This file is distributed under the same license as the Cura package.
# Ruben Dulek <r.dulek@ultimaker.com>, 2017.
#
#, fuzzy #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Uranium json setting files\n" "Project-Id-Version: Cura 2.5\n"
"Report-Msgid-Bugs-To: http://github.com/ultimaker/uranium\n" "Report-Msgid-Bugs-To: http://github.com/Ultimaker/Cura\n"
"POT-Creation-Date: 2016-12-28 10:51+0000\n" "POT-Creation-Date: 2017-03-27 17:27+0000\n"
"PO-Revision-Date: 2017-01-12 15:51+0100\n" "PO-Revision-Date: 2017-04-04 11:27+0200\n"
"Last-Translator: Ruben Dulek <r.dulek@ultimaker.com>\n" "Last-Translator: Bothof <info@bothof.nl>\n"
"Language-Team: Ultimaker\n" "Language-Team: Bothof <info@bothof.nl>\n"
"Language: \n" "Language: nl\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -3,8 +3,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Uranium json setting files\n" "Project-Id-Version: Uranium json setting files\n"
"Report-Msgid-Bugs-To: http://github.com/ultimaker/uranium\n" "Report-Msgid-Bugs-To: http://github.com/ultimaker/uranium\n"
"POT-Creation-Date: 2016-12-28 10:51+0000\n" "POT-Creation-Date: 2017-03-27 17:27+0000\n"
"PO-Revision-Date: 2016-01-25 05:05-0300\n" "PO-Revision-Date: 2017-04-10 09:05-0300\n"
"Last-Translator: Cláudio Sampaio <patola@makerlinux.com.br>\n" "Last-Translator: Cláudio Sampaio <patola@makerlinux.com.br>\n"
"Language-Team: LANGUAGE\n" "Language-Team: LANGUAGE\n"
"Language: ptbr\n" "Language: ptbr\n"
@ -70,12 +70,8 @@ msgstr "Posição de Início do Extrusor Absoluta"
#: fdmextruder.def.json #: fdmextruder.def.json
msgctxt "machine_extruder_start_pos_abs description" msgctxt "machine_extruder_start_pos_abs description"
msgid "" msgid "Make the extruder starting position absolute rather than relative to the last-known location of the head."
"Make the extruder starting position absolute rather than relative to the " msgstr "Faz a posição de início do extrusor ser absoluta ao invés de relativa à última posição conhecida da cabeça de impressão."
"last-known location of the head."
msgstr ""
"Faz a posição de início do extrusor ser absoluta ao invés de relativa à "
"última posição conhecida da cabeça de impressão."
#: fdmextruder.def.json #: fdmextruder.def.json
msgctxt "machine_extruder_start_pos_x label" msgctxt "machine_extruder_start_pos_x label"
@ -114,12 +110,8 @@ msgstr "Posição Final do Extrusor Absoluta"
#: fdmextruder.def.json #: fdmextruder.def.json
msgctxt "machine_extruder_end_pos_abs description" msgctxt "machine_extruder_end_pos_abs description"
msgid "" msgid "Make the extruder ending position absolute rather than relative to the last-known location of the head."
"Make the extruder ending position absolute rather than relative to the last-" msgstr "Faz a posição final do extrusor ser absoluta ao invés de relativa à última posição conhecida da cabeça de impressão."
"known location of the head."
msgstr ""
"Faz a posição final do extrusor ser absoluta ao invés de relativa à última "
"posição conhecida da cabeça de impressão."
#: fdmextruder.def.json #: fdmextruder.def.json
msgctxt "machine_extruder_end_pos_x label" msgctxt "machine_extruder_end_pos_x label"
@ -148,11 +140,8 @@ msgstr "Posição Z de Purga do Extrusor"
#: fdmextruder.def.json #: fdmextruder.def.json
msgctxt "extruder_prime_pos_z description" msgctxt "extruder_prime_pos_z description"
msgid "" msgid "The Z coordinate of the position where the nozzle primes at the start of printing."
"The Z coordinate of the position where the nozzle primes at the start of " msgstr "A coordenada Z da posição onde o bico faz a purga no início da impressão."
"printing."
msgstr ""
"A coordenada Z da posição onde o bico faz a purga no início da impressão."
#: fdmextruder.def.json #: fdmextruder.def.json
msgctxt "platform_adhesion label" msgctxt "platform_adhesion label"
@ -171,11 +160,8 @@ msgstr "Posição X de Purga do Extrusor"
#: fdmextruder.def.json #: fdmextruder.def.json
msgctxt "extruder_prime_pos_x description" msgctxt "extruder_prime_pos_x description"
msgid "" msgid "The X coordinate of the position where the nozzle primes at the start of printing."
"The X coordinate of the position where the nozzle primes at the start of " msgstr "A coordenada X da posição onde o bico faz a purga no início da impressão."
"printing."
msgstr ""
"A coordenada X da posição onde o bico faz a purga no início da impressão."
#: fdmextruder.def.json #: fdmextruder.def.json
msgctxt "extruder_prime_pos_y label" msgctxt "extruder_prime_pos_y label"
@ -184,8 +170,5 @@ msgstr "Posição Y de Purga do Extrusor"
#: fdmextruder.def.json #: fdmextruder.def.json
msgctxt "extruder_prime_pos_y description" msgctxt "extruder_prime_pos_y description"
msgid "" msgid "The Y coordinate of the position where the nozzle primes at the start of printing."
"The Y coordinate of the position where the nozzle primes at the start of " msgstr "A coordenada Y da posição onde o bico faz a purga no início da impressão."
"printing."
msgstr ""
"A coordenada Y da posição onde o bico faz a purga no início da impressão."

File diff suppressed because it is too large Load Diff

1573
resources/i18n/ru/cura.po Normal file → Executable file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

2539
resources/i18n/ru/fdmprinter.def.json.po Normal file → Executable file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,18 @@
# Cura JSON setting files
# Copyright (C) 2017 Ultimaker
# This file is distributed under the same license as the Cura package.
# Ruben Dulek <r.dulek@ultimaker.com>, 2017.
#
#, fuzzy #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Uranium json setting files\n" "Project-Id-Version: Cura 2.5\n"
"Report-Msgid-Bugs-To: http://github.com/ultimaker/uranium\n" "Report-Msgid-Bugs-To: http://github.com/Ultimaker/Cura\n"
"POT-Creation-Date: 2016-12-28 10:51+0000\n" "POT-Creation-Date: 2017-03-27 17:27+0000\n"
"PO-Revision-Date: 2017-01-12 15:51+0100\n" "PO-Revision-Date: 2017-04-04 11:27+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: Bothof <info@bothof.nl>\n"
"Language-Team: LANGUAGE\n" "Language-Team: Bothof <info@bothof.nl>\n"
"Language: \n" "Language: tr\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

35
resources/qml/Actions.qml Normal file → Executable file
View File

@ -31,6 +31,8 @@ Item
property alias selectAll: selectAllAction; property alias selectAll: selectAllAction;
property alias deleteAll: deleteAllAction; property alias deleteAll: deleteAllAction;
property alias reloadAll: reloadAllAction; property alias reloadAll: reloadAllAction;
property alias arrangeAll: arrangeAllAction;
property alias arrangeSelection: arrangeSelectionAction;
property alias resetAllTranslation: resetAllTranslationAction; property alias resetAllTranslation: resetAllTranslationAction;
property alias resetAll: resetAllAction; property alias resetAll: resetAllAction;
@ -183,7 +185,7 @@ Item
enabled: UM.Controller.toolsEnabled; enabled: UM.Controller.toolsEnabled;
iconName: "edit-delete"; iconName: "edit-delete";
shortcut: StandardKey.Delete; shortcut: StandardKey.Delete;
onTriggered: Printer.deleteSelection(); onTriggered: CuraApplication.deleteSelection();
} }
Action Action
@ -207,7 +209,7 @@ Item
enabled: UM.Scene.numObjectsSelected > 1 ? true: false enabled: UM.Scene.numObjectsSelected > 1 ? true: false
iconName: "object-group" iconName: "object-group"
shortcut: "Ctrl+G"; shortcut: "Ctrl+G";
onTriggered: Printer.groupSelected(); onTriggered: CuraApplication.groupSelected();
} }
Action Action
@ -217,7 +219,7 @@ Item
enabled: UM.Scene.isGroupSelected enabled: UM.Scene.isGroupSelected
iconName: "object-ungroup" iconName: "object-ungroup"
shortcut: "Ctrl+Shift+G"; shortcut: "Ctrl+Shift+G";
onTriggered: Printer.ungroupSelected(); onTriggered: CuraApplication.ungroupSelected();
} }
Action Action
@ -227,7 +229,7 @@ Item
enabled: UM.Scene.numObjectsSelected > 1 ? true: false enabled: UM.Scene.numObjectsSelected > 1 ? true: false
iconName: "merge"; iconName: "merge";
shortcut: "Ctrl+Alt+G"; shortcut: "Ctrl+Alt+G";
onTriggered: Printer.mergeSelected(); onTriggered: CuraApplication.mergeSelected();
} }
Action Action
@ -244,7 +246,7 @@ Item
enabled: UM.Controller.toolsEnabled; enabled: UM.Controller.toolsEnabled;
iconName: "edit-select-all"; iconName: "edit-select-all";
shortcut: "Ctrl+A"; shortcut: "Ctrl+A";
onTriggered: Printer.selectAll(); onTriggered: CuraApplication.selectAll();
} }
Action Action
@ -254,7 +256,7 @@ Item
enabled: UM.Controller.toolsEnabled; enabled: UM.Controller.toolsEnabled;
iconName: "edit-delete"; iconName: "edit-delete";
shortcut: "Ctrl+D"; shortcut: "Ctrl+D";
onTriggered: Printer.deleteAll(); onTriggered: CuraApplication.deleteAll();
} }
Action Action
@ -263,21 +265,36 @@ Item
text: catalog.i18nc("@action:inmenu menubar:file","Re&load All Models"); text: catalog.i18nc("@action:inmenu menubar:file","Re&load All Models");
iconName: "document-revert"; iconName: "document-revert";
shortcut: "F5" shortcut: "F5"
onTriggered: Printer.reloadAll(); onTriggered: CuraApplication.reloadAll();
}
Action
{
id: arrangeAllAction;
text: catalog.i18nc("@action:inmenu menubar:edit","Arrange All Models");
onTriggered: Printer.arrangeAll();
shortcut: "Ctrl+R";
}
Action
{
id: arrangeSelectionAction;
text: catalog.i18nc("@action:inmenu menubar:edit","Arrange Selection");
onTriggered: Printer.arrangeSelection();
} }
Action Action
{ {
id: resetAllTranslationAction; id: resetAllTranslationAction;
text: catalog.i18nc("@action:inmenu menubar:edit","Reset All Model Positions"); text: catalog.i18nc("@action:inmenu menubar:edit","Reset All Model Positions");
onTriggered: Printer.resetAllTranslation(); onTriggered: CuraApplication.resetAllTranslation();
} }
Action Action
{ {
id: resetAllAction; id: resetAllAction;
text: catalog.i18nc("@action:inmenu menubar:edit","Reset All Model &Transformations"); text: catalog.i18nc("@action:inmenu menubar:edit","Reset All Model &Transformations");
onTriggered: Printer.resetAll(); onTriggered: CuraApplication.resetAll();
} }
Action Action

View File

@ -6,6 +6,7 @@ import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1 import QtQuick.Controls.Styles 1.1
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.1 import QtQuick.Dialogs 1.1
import QtQuick.Window 2.1
import UM 1.3 as UM import UM 1.3 as UM
import Cura 1.0 as Cura import Cura 1.0 as Cura
@ -17,13 +18,13 @@ UM.Dialog
id: base id: base
title: catalog.i18nc("@title:window", "Open project file") title: catalog.i18nc("@title:window", "Open project file")
width: 420 width: 450 * Screen.devicePixelRatio
height: 140 height: 150 * Screen.devicePixelRatio
maximumHeight: height maximumHeight: height
maximumWidth: width maximumWidth: width
minimumHeight: height minimumHeight: maximumHeight
minimumWidth: width minimumWidth: maximumWidth
modality: UM.Application.platform == "linux" ? Qt.NonModal : Qt.WindowModal; modality: UM.Application.platform == "linux" ? Qt.NonModal : Qt.WindowModal;
@ -40,7 +41,9 @@ UM.Dialog
function loadModelFiles(fileUrls) function loadModelFiles(fileUrls)
{ {
for (var i in fileUrls) for (var i in fileUrls)
Printer.readLocalFile(fileUrls[i]); {
CuraApplication.readLocalFile(fileUrls[i]);
}
var meshName = backgroundItem.getMeshName(fileUrls[0].toString()); var meshName = backgroundItem.getMeshName(fileUrls[0].toString());
backgroundItem.hasMesh(decodeURIComponent(meshName)); backgroundItem.hasMesh(decodeURIComponent(meshName));
@ -58,15 +61,16 @@ UM.Dialog
Column Column
{ {
anchors.fill: parent anchors.fill: parent
anchors.margins: UM.Theme.getSize("default_margin").width anchors.leftMargin: 20 * Screen.devicePixelRatio
anchors.left: parent.left anchors.rightMargin: 20 * Screen.devicePixelRatio
anchors.right: parent.right anchors.bottomMargin: 20 * Screen.devicePixelRatio
spacing: UM.Theme.getSize("default_margin").width spacing: 10 * Screen.devicePixelRatio
Label Label
{ {
text: catalog.i18nc("@text:window", "This is a Cura project file. Would you like to open it as a project\nor import the models from it?") text: catalog.i18nc("@text:window", "This is a Cura project file. Would you like to open it as a project or import the models from it?")
anchors.margins: UM.Theme.getSize("default_margin").width anchors.left: parent.left
anchors.right: parent.right
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
} }
@ -75,7 +79,6 @@ UM.Dialog
{ {
id: rememberChoiceCheckBox id: rememberChoiceCheckBox
text: catalog.i18nc("@text:window", "Remember my choice") text: catalog.i18nc("@text:window", "Remember my choice")
anchors.margins: UM.Theme.getSize("default_margin").width
checked: UM.Preferences.getValue("cura/choice_on_open_project") != "always_ask" checked: UM.Preferences.getValue("cura/choice_on_open_project") != "always_ask"
} }
@ -91,7 +94,7 @@ UM.Dialog
id: openAsProjectButton id: openAsProjectButton
text: catalog.i18nc("@action:button", "Open as project"); text: catalog.i18nc("@action:button", "Open as project");
anchors.right: importModelsButton.left anchors.right: importModelsButton.left
anchors.rightMargin: UM.Theme.getSize("default_margin").width anchors.rightMargin: UM.Theme.getSize("default_margin").width * Screen.devicePixelRatio
isDefault: true isDefault: true
onClicked: onClicked:
{ {

View File

@ -21,7 +21,7 @@ UM.MainWindow
property bool monitoringPrint: false property bool monitoringPrint: false
Component.onCompleted: Component.onCompleted:
{ {
Printer.setMinimumWindowSize(UM.Theme.getSize("window_minimum_size")) CuraApplication.setMinimumWindowSize(UM.Theme.getSize("window_minimum_size"))
// Workaround silly issues with QML Action's shortcut property. // Workaround silly issues with QML Action's shortcut property.
// //
// Currently, there is no way to define shortcuts as "Application Shortcut". // Currently, there is no way to define shortcuts as "Application Shortcut".
@ -131,6 +131,7 @@ UM.MainWindow
MenuItem { action: Cura.Actions.redo; } MenuItem { action: Cura.Actions.redo; }
MenuSeparator { } MenuSeparator { }
MenuItem { action: Cura.Actions.selectAll; } MenuItem { action: Cura.Actions.selectAll; }
MenuItem { action: Cura.Actions.arrangeAll; }
MenuItem { action: Cura.Actions.deleteSelection; } MenuItem { action: Cura.Actions.deleteSelection; }
MenuItem { action: Cura.Actions.deleteAll; } MenuItem { action: Cura.Actions.deleteAll; }
MenuItem { action: Cura.Actions.resetAllTranslation; } MenuItem { action: Cura.Actions.resetAllTranslation; }
@ -259,7 +260,7 @@ UM.MainWindow
{ {
if (drop.urls.length > 0) if (drop.urls.length > 0)
{ {
handleOpenFileUrls(drop.urls); openDialog.handleOpenFileUrls(drop.urls);
} }
} }
} }
@ -502,7 +503,7 @@ UM.MainWindow
icon: StandardIcon.Question icon: StandardIcon.Question
onYes: onYes:
{ {
Printer.deleteAll(); CuraApplication.deleteAll();
Cura.Actions.resetProfile.trigger(); Cura.Actions.resetProfile.trigger();
} }
} }
@ -603,6 +604,7 @@ UM.MainWindow
MenuItem { action: Cura.Actions.multiplyObject; } MenuItem { action: Cura.Actions.multiplyObject; }
MenuSeparator { } MenuSeparator { }
MenuItem { action: Cura.Actions.selectAll; } MenuItem { action: Cura.Actions.selectAll; }
MenuItem { action: Cura.Actions.arrangeAll; }
MenuItem { action: Cura.Actions.deleteAll; } MenuItem { action: Cura.Actions.deleteAll; }
MenuItem { action: Cura.Actions.reloadAll; } MenuItem { action: Cura.Actions.reloadAll; }
MenuItem { action: Cura.Actions.resetAllTranslation; } MenuItem { action: Cura.Actions.resetAllTranslation; }
@ -619,7 +621,7 @@ UM.MainWindow
{ {
if(objectContextMenu.objectId != 0) if(objectContextMenu.objectId != 0)
{ {
Printer.deleteObject(objectContextMenu.objectId); CuraApplication.deleteObject(objectContextMenu.objectId);
objectContextMenu.objectId = 0; objectContextMenu.objectId = 0;
} }
} }
@ -652,7 +654,7 @@ UM.MainWindow
{ {
if(objectContextMenu.objectId != 0) if(objectContextMenu.objectId != 0)
{ {
Printer.centerObject(objectContextMenu.objectId); CuraApplication.centerObject(objectContextMenu.objectId);
objectContextMenu.objectId = 0; objectContextMenu.objectId = 0;
} }
} }
@ -663,6 +665,7 @@ UM.MainWindow
{ {
id: contextMenu; id: contextMenu;
MenuItem { action: Cura.Actions.selectAll; } MenuItem { action: Cura.Actions.selectAll; }
MenuItem { action: Cura.Actions.arrangeAll; }
MenuItem { action: Cura.Actions.deleteAll; } MenuItem { action: Cura.Actions.deleteAll; }
MenuItem { action: Cura.Actions.reloadAll; } MenuItem { action: Cura.Actions.reloadAll; }
MenuItem { action: Cura.Actions.resetAllTranslation; } MenuItem { action: Cura.Actions.resetAllTranslation; }
@ -722,85 +725,85 @@ UM.MainWindow
handleOpenFileUrls(fileUrls); handleOpenFileUrls(fileUrls);
} }
}
// Yeah... I know... it is a mess to put all those things here. // Yeah... I know... it is a mess to put all those things here.
// There are lots of user interactions in this part of the logic, such as showing a warning dialog here and there, // There are lots of user interactions in this part of the logic, such as showing a warning dialog here and there,
// etc. This means it will come back and forth from time to time between QML and Python. So, separating the logic // etc. This means it will come back and forth from time to time between QML and Python. So, separating the logic
// and view here may require more effort but make things more difficult to understand. // and view here may require more effort but make things more difficult to understand.
function handleOpenFileUrls(fileUrls) function handleOpenFileUrls(fileUrlList)
{
// look for valid project files
var projectFileUrlList = [];
var hasGcode = false;
var nonGcodeFileList = [];
for (var i in fileUrls)
{ {
var endsWithG = /\.g$/; // look for valid project files
var endsWithGcode = /\.gcode$/; var projectFileUrlList = [];
if (endsWithG.test(fileUrls[i]) || endsWithGcode.test(fileUrls[i])) var hasGcode = false;
var nonGcodeFileList = [];
for (var i in fileUrlList)
{ {
continue; var endsWithG = /\.g$/;
var endsWithGcode = /\.gcode$/;
if (endsWithG.test(fileUrlList[i]) || endsWithGcode.test(fileUrlList[i]))
{
continue;
}
else if (CuraApplication.checkIsValidProjectFile(fileUrlList[i]))
{
projectFileUrlList.push(fileUrlList[i]);
}
nonGcodeFileList.push(fileUrlList[i]);
} }
else if (CuraApplication.checkIsValidProjectFile(fileUrls[i])) hasGcode = nonGcodeFileList.length < fileUrlList.length;
// show a warning if selected multiple files together with Gcode
var hasProjectFile = projectFileUrlList.length > 0;
var selectedMultipleFiles = fileUrlList.length > 1;
if (selectedMultipleFiles && hasGcode)
{ {
projectFileUrlList.push(fileUrls[i]); infoMultipleFilesWithGcodeDialog.selectedMultipleFiles = selectedMultipleFiles;
infoMultipleFilesWithGcodeDialog.hasProjectFile = hasProjectFile;
infoMultipleFilesWithGcodeDialog.fileUrls = nonGcodeFileList.slice();
infoMultipleFilesWithGcodeDialog.projectFileUrlList = projectFileUrlList.slice();
infoMultipleFilesWithGcodeDialog.open();
} }
nonGcodeFileList.push(fileUrls[i]); else
}
hasGcode = nonGcodeFileList.length < fileUrls.length;
// show a warning if selected multiple files together with Gcode
var hasProjectFile = projectFileUrlList.length > 0;
var selectedMultipleFiles = fileUrls.length > 1;
if (selectedMultipleFiles && hasGcode)
{
infoMultipleFilesWithGcodeDialog.selectedMultipleFiles = selectedMultipleFiles;
infoMultipleFilesWithGcodeDialog.hasProjectFile = hasProjectFile;
infoMultipleFilesWithGcodeDialog.fileUrls = nonGcodeFileList.slice();
infoMultipleFilesWithGcodeDialog.projectFileUrlList = projectFileUrlList.slice();
infoMultipleFilesWithGcodeDialog.open();
}
else
{
handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrls, projectFileUrlList);
}
}
function handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrls, projectFileUrlList)
{
// we only allow opening one project file
if (selectedMultipleFiles && hasProjectFile)
{
openFilesIncludingProjectsDialog.fileUrls = fileUrls.slice();
openFilesIncludingProjectsDialog.show();
return;
}
if (hasProjectFile)
{
var projectFile = projectFileUrlList[0];
// check preference
var choice = UM.Preferences.getValue("cura/choice_on_open_project");
if (choice == "open_as_project")
{ {
openFilesIncludingProjectsDialog.loadProjectFile(projectFile); handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrlList, projectFileUrlList);
}
else if (choice == "open_as_model")
{
openFilesIncludingProjectsDialog.loadModelFiles([projectFile].slice());
}
else // always ask
{
// ask whether to open as project or as models
askOpenAsProjectOrModelsDialog.fileUrl = projectFile;
askOpenAsProjectOrModelsDialog.show();
} }
} }
else
function handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrlList, projectFileUrlList)
{ {
openFilesIncludingProjectsDialog.loadModelFiles(fileUrls.slice()); // we only allow opening one project file
if (selectedMultipleFiles && hasProjectFile)
{
openFilesIncludingProjectsDialog.fileUrls = fileUrlList.slice();
openFilesIncludingProjectsDialog.show();
return;
}
if (hasProjectFile)
{
var projectFile = projectFileUrlList[0];
// check preference
var choice = UM.Preferences.getValue("cura/choice_on_open_project");
if (choice == "open_as_project")
{
openFilesIncludingProjectsDialog.loadProjectFile(projectFile);
}
else if (choice == "open_as_model")
{
openFilesIncludingProjectsDialog.loadModelFiles([projectFile].slice());
}
else // always ask
{
// ask whether to open as project or as models
askOpenAsProjectOrModelsDialog.fileUrl = projectFile;
askOpenAsProjectOrModelsDialog.show();
}
}
else
{
openFilesIncludingProjectsDialog.loadModelFiles(fileUrlList.slice());
}
} }
} }
@ -818,7 +821,7 @@ UM.MainWindow
onAccepted: onAccepted:
{ {
handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrls, projectFileUrlList); openDialog.handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrls, projectFileUrlList);
} }
} }
@ -898,14 +901,14 @@ UM.MainWindow
{ {
id: messageDialog id: messageDialog
modality: Qt.ApplicationModal modality: Qt.ApplicationModal
onAccepted: Printer.messageBoxClosed(clickedButton) onAccepted: CuraApplication.messageBoxClosed(clickedButton)
onApply: Printer.messageBoxClosed(clickedButton) onApply: CuraApplication.messageBoxClosed(clickedButton)
onDiscard: Printer.messageBoxClosed(clickedButton) onDiscard: CuraApplication.messageBoxClosed(clickedButton)
onHelp: Printer.messageBoxClosed(clickedButton) onHelp: CuraApplication.messageBoxClosed(clickedButton)
onNo: Printer.messageBoxClosed(clickedButton) onNo: CuraApplication.messageBoxClosed(clickedButton)
onRejected: Printer.messageBoxClosed(clickedButton) onRejected: CuraApplication.messageBoxClosed(clickedButton)
onReset: Printer.messageBoxClosed(clickedButton) onReset: CuraApplication.messageBoxClosed(clickedButton)
onYes: Printer.messageBoxClosed(clickedButton) onYes: CuraApplication.messageBoxClosed(clickedButton)
} }
Connections Connections

View File

@ -4,6 +4,7 @@
import QtQuick 2.1 import QtQuick 2.1
import QtQuick.Controls 1.1 import QtQuick.Controls 1.1
import QtQuick.Dialogs 1.2 import QtQuick.Dialogs 1.2
import QtQuick.Window 2.1
import UM 1.2 as UM import UM 1.2 as UM
import Cura 1.1 as Cura import Cura 1.1 as Cura
@ -13,8 +14,8 @@ UM.Dialog
id: base id: base
title: catalog.i18nc("@title:window", "Discard or Keep changes") title: catalog.i18nc("@title:window", "Discard or Keep changes")
width: 800 width: 800 * Screen.devicePixelRatio
height: 400 height: 400 * Screen.devicePixelRatio
property var changesModel: Cura.UserChangesModel{ id: userChangesModel} property var changesModel: Cura.UserChangesModel{ id: userChangesModel}
onVisibilityChanged: onVisibilityChanged:
{ {
@ -68,7 +69,7 @@ UM.Dialog
anchors.margins: UM.Theme.getSize("default_margin").width anchors.margins: UM.Theme.getSize("default_margin").width
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
height: base.height - 150 height: base.height - 150 * Screen.devicePixelRatio
id: tableView id: tableView
Component Component
{ {
@ -190,7 +191,7 @@ UM.Dialog
anchors.right: parent.right anchors.right: parent.right
onClicked: onClicked:
{ {
Printer.discardOrKeepProfileChangesClosed("discard") CuraApplication.discardOrKeepProfileChangesClosed("discard")
base.hide() base.hide()
} }
isDefault: true isDefault: true
@ -204,7 +205,7 @@ UM.Dialog
anchors.rightMargin: UM.Theme.getSize("default_margin").width anchors.rightMargin: UM.Theme.getSize("default_margin").width
onClicked: onClicked:
{ {
Printer.discardOrKeepProfileChangesClosed("keep") CuraApplication.discardOrKeepProfileChangesClosed("keep")
base.hide() base.hide()
} }
} }

View File

@ -27,7 +27,7 @@ UM.Dialog
interval: 1000; interval: 1000;
running: false; running: false;
repeat: true; repeat: true;
onTriggered: textArea.text = Printer.getEngineLog(); onTriggered: textArea.text = CuraApplication.getEngineLog();
} }
UM.I18nCatalog{id: catalog; name:"cura"} UM.I18nCatalog{id: catalog; name:"cura"}
} }
@ -43,7 +43,7 @@ UM.Dialog
{ {
if(visible) if(visible)
{ {
textArea.text = Printer.getEngineLog(); textArea.text = CuraApplication.getEngineLog();
updateTimer.start(); updateTimer.start();
} else } else
{ {

View File

@ -12,7 +12,7 @@ import Cura 1.0 as Cura
Item { Item {
id: base id: base
property bool activity: Printer.platformActivity property bool activity: CuraApplication.platformActivity
property string fileBaseName property string fileBaseName
property variant activeMachineName: Cura.MachineManager.activeMachineName property variant activeMachineName: Cura.MachineManager.activeMachineName
@ -132,7 +132,7 @@ Item {
} }
} }
Label Text
{ {
id: boundingSpec id: boundingSpec
anchors.top: jobNameRow.bottom anchors.top: jobNameRow.bottom
@ -141,7 +141,7 @@ Item {
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
font: UM.Theme.getFont("small") font: UM.Theme.getFont("small")
color: UM.Theme.getColor("text_subtext") color: UM.Theme.getColor("text_subtext")
text: Printer.getSceneBoundingBoxString text: CuraApplication.getSceneBoundingBoxString
} }
Rectangle Rectangle
@ -169,7 +169,7 @@ Item {
color: UM.Theme.getColor("text_subtext") color: UM.Theme.getColor("text_subtext")
source: UM.Theme.getIcon("print_time") source: UM.Theme.getIcon("print_time")
} }
Label Text
{ {
id: timeSpec id: timeSpec
anchors.right: lengthIcon.left anchors.right: lengthIcon.left
@ -192,7 +192,7 @@ Item {
color: UM.Theme.getColor("text_subtext") color: UM.Theme.getColor("text_subtext")
source: UM.Theme.getIcon("category_material") source: UM.Theme.getIcon("category_material")
} }
Label Text
{ {
id: lengthSpec id: lengthSpec
anchors.right: parent.right anchors.right: parent.right

View File

@ -15,6 +15,15 @@ Menu
property int extruderIndex: 0 property int extruderIndex: 0
property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0 property bool printerConnected: Cura.MachineManager.printerOutputDevices.length != 0
UM.SettingPropertyProvider
{
id: materialDiameterProvider
containerStackId: Cura.MachineManager.activeMachineId
key: "material_diameter"
watchedProperties: [ "value" ]
}
MenuItem MenuItem
{ {
id: automaticMaterial id: automaticMaterial
@ -141,7 +150,7 @@ Menu
function materialFilter() function materialFilter()
{ {
var result = { "type": "material" }; var result = { "type": "material", "approximate_diameter": Math.round(materialDiameterProvider.properties.value) };
if(Cura.MachineManager.filterMaterialsByMachine) if(Cura.MachineManager.filterMaterialsByMachine)
{ {
result.definition = Cura.MachineManager.activeQualityDefinitionId; result.definition = Cura.MachineManager.activeQualityDefinitionId;

View File

@ -13,11 +13,11 @@ Menu
title: catalog.i18nc("@title:menu menubar:file", "Open &Recent") title: catalog.i18nc("@title:menu menubar:file", "Open &Recent")
iconName: "document-open-recent"; iconName: "document-open-recent";
enabled: Printer.recentFiles.length > 0; enabled: CuraApplication.recentFiles.length > 0;
Instantiator Instantiator
{ {
model: Printer.recentFiles model: CuraApplication.recentFiles
MenuItem MenuItem
{ {
text: text:
@ -36,11 +36,13 @@ Menu
var choice = UM.Preferences.getValue("cura/choice_on_open_project"); var choice = UM.Preferences.getValue("cura/choice_on_open_project");
if (choice == "open_as_project") if (choice == "open_as_project")
{
toOpenAsProject = true; toOpenAsProject = true;
else if (choice == "open_as_model") }else if (choice == "open_as_model"){
toOpenAsModel = true; toOpenAsModel = true;
else }else{
toShowDialog = true; toShowDialog = true;
}
} }
else { else {
toOpenAsModel = true; toOpenAsModel = true;
@ -54,9 +56,13 @@ Menu
// open file in the prefered way // open file in the prefered way
if (toOpenAsProject) if (toOpenAsProject)
{
UM.WorkspaceFileHandler.readLocalFile(modelData); UM.WorkspaceFileHandler.readLocalFile(modelData);
}
else if (toOpenAsModel) else if (toOpenAsModel)
Printer.readLocalFile(modelData); {
CuraApplication.readLocalFile(modelData);
}
var meshName = backgroundItem.getMeshName(modelData.toString()) var meshName = backgroundItem.getMeshName(modelData.toString())
backgroundItem.hasMesh(decodeURIComponent(meshName)) backgroundItem.hasMesh(decodeURIComponent(meshName))
} }

View File

@ -80,7 +80,7 @@ Item
} }
} }
property bool activity: Printer.platformActivity; property bool activity: CuraApplication.platformActivity;
property int totalHeight: childrenRect.height + UM.Theme.getSize("default_margin").height property int totalHeight: childrenRect.height + UM.Theme.getSize("default_margin").height
property string fileBaseName property string fileBaseName
property string statusText: property string statusText:
@ -205,8 +205,8 @@ Item
onAdditionalComponentsChanged: onAdditionalComponentsChanged:
{ {
if(areaId == "monitorButtons") { if(areaId == "monitorButtons") {
for (var component in Printer.additionalComponents["monitorButtons"]) { for (var component in CuraApplication.additionalComponents["monitorButtons"]) {
Printer.additionalComponents["monitorButtons"][component].parent = additionalComponentsRow CuraApplication.additionalComponents["monitorButtons"][component].parent = additionalComponentsRow
} }
} }
} }

View File

@ -20,7 +20,7 @@ UM.Dialog
height: minimumHeight height: minimumHeight
property var objectId: 0; property var objectId: 0;
onAccepted: Printer.multiplyObject(base.objectId, parseInt(copiesField.text)) onAccepted: CuraApplication.multiplyObject(base.objectId, parseInt(copiesField.text))
property variant catalog: UM.I18nCatalog { name: "cura" } property variant catalog: UM.I18nCatalog { name: "cura" }

View File

@ -6,6 +6,7 @@ import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1 import QtQuick.Controls.Styles 1.1
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.1 import QtQuick.Dialogs 1.1
import QtQuick.Window 2.1
import UM 1.3 as UM import UM 1.3 as UM
import Cura 1.0 as Cura import Cura 1.0 as Cura
@ -16,8 +17,8 @@ UM.Dialog
id: base id: base
title: catalog.i18nc("@title:window", "Open file(s)") title: catalog.i18nc("@title:window", "Open file(s)")
width: 420 width: 420 * Screen.devicePixelRatio
height: 170 height: 170 * Screen.devicePixelRatio
maximumHeight: height maximumHeight: height
maximumWidth: width maximumWidth: width
@ -40,7 +41,9 @@ UM.Dialog
function loadModelFiles(fileUrls) function loadModelFiles(fileUrls)
{ {
for (var i in fileUrls) for (var i in fileUrls)
Printer.readLocalFile(fileUrls[i]); {
CuraApplication.readLocalFile(fileUrls[i]);
}
var meshName = backgroundItem.getMeshName(fileUrls[0].toString()); var meshName = backgroundItem.getMeshName(fileUrls[0].toString());
backgroundItem.hasMesh(decodeURIComponent(meshName)); backgroundItem.hasMesh(decodeURIComponent(meshName));
@ -49,15 +52,18 @@ UM.Dialog
Column Column
{ {
anchors.fill: parent anchors.fill: parent
anchors.margins: UM.Theme.getSize("default_margin").width anchors.leftMargin: 20 * Screen.devicePixelRatio
anchors.rightMargin: 20 * Screen.devicePixelRatio
anchors.bottomMargin: 20 * Screen.devicePixelRatio
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
spacing: UM.Theme.getSize("default_margin").width spacing: 10 * Screen.devicePixelRatio
Text Text
{ {
text: catalog.i18nc("@text:window", "We have found one or more project file(s) within the files you\nhave selected. You can open only one project file at a time. We\nsuggest to only import models from those files. Would you like\nto proceed?") text: catalog.i18nc("@text:window", "We have found one or more project file(s) within the files you have selected. You can open only one project file at a time. We suggest to only import models from those files. Would you like to proceed?")
anchors.margins: UM.Theme.getSize("default_margin").width anchors.left: parent.left
anchors.right: parent.right
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
} }
@ -80,7 +86,6 @@ UM.Dialog
id: cancelButton id: cancelButton
text: catalog.i18nc("@action:button", "Cancel"); text: catalog.i18nc("@action:button", "Cancel");
anchors.right: importAllAsModelsButton.left anchors.right: importAllAsModelsButton.left
anchors.rightMargin: UM.Theme.getSize("default_margin").width
onClicked: onClicked:
{ {
// cancel // cancel

View File

@ -43,7 +43,7 @@ UM.ManagementPage
{ {
text: catalog.i18nc("@action:button", "Add"); text: catalog.i18nc("@action:button", "Add");
iconName: "list-add"; iconName: "list-add";
onClicked: Printer.requestAddPrinter() onClicked: CuraApplication.requestAddPrinter()
}, },
Button Button
{ {
@ -216,8 +216,8 @@ UM.ManagementPage
Component.onCompleted: Component.onCompleted:
{ {
for (var component in Printer.additionalComponents["machinesDetailPane"]) { for (var component in CuraApplication.additionalComponents["machinesDetailPane"]) {
Printer.additionalComponents["machinesDetailPane"][component].parent = additionalComponentsColumn CuraApplication.additionalComponents["machinesDetailPane"][component].parent = additionalComponentsColumn
} }
} }
} }
@ -227,8 +227,8 @@ UM.ManagementPage
onAdditionalComponentsChanged: onAdditionalComponentsChanged:
{ {
if(areaId == "machinesDetailPane") { if(areaId == "machinesDetailPane") {
for (var component in Printer.additionalComponents["machinesDetailPane"]) { for (var component in CuraApplication.additionalComponents["machinesDetailPane"]) {
Printer.additionalComponents["machinesDetailPane"][component].parent = additionalComponentsColumn CuraApplication.additionalComponents["machinesDetailPane"][component].parent = additionalComponentsColumn
} }
} }
} }

View File

@ -18,7 +18,7 @@ UM.ManagementPage
{ {
filter: filter:
{ {
var result = { "type": "material" } var result = { "type": "material", "approximate_diameter": Math.round(materialDiameterProvider.properties.value) }
if(Cura.MachineManager.filterMaterialsByMachine) if(Cura.MachineManager.filterMaterialsByMachine)
{ {
result.definition = Cura.MachineManager.activeQualityDefinitionId; result.definition = Cura.MachineManager.activeQualityDefinitionId;
@ -327,6 +327,15 @@ UM.ManagementPage
id: messageDialog id: messageDialog
} }
UM.SettingPropertyProvider
{
id: materialDiameterProvider
containerStackId: Cura.MachineManager.activeMachineId
key: "material_diameter"
watchedProperties: [ "value" ]
}
UM.I18nCatalog { id: catalog; name: "cura"; } UM.I18nCatalog { id: catalog; name: "cura"; }
SystemPalette { id: palette } SystemPalette { id: palette }
} }

View File

@ -27,7 +27,7 @@ Column
height: childrenRect.height + UM.Theme.getSize("default_margin").height * 2 height: childrenRect.height + UM.Theme.getSize("default_margin").height * 2
color: UM.Theme.getColor("setting_category") color: UM.Theme.getColor("setting_category")
Label Text
{ {
id: connectedPrinterNameLabel id: connectedPrinterNameLabel
text: connectedPrinter != null ? connectedPrinter.name : catalog.i18nc("@info:status", "No printer connected") text: connectedPrinter != null ? connectedPrinter.name : catalog.i18nc("@info:status", "No printer connected")
@ -37,7 +37,7 @@ Column
anchors.top: parent.top anchors.top: parent.top
anchors.margins: UM.Theme.getSize("default_margin").width anchors.margins: UM.Theme.getSize("default_margin").width
} }
Label Text
{ {
id: connectedPrinterAddressLabel id: connectedPrinterAddressLabel
text: (connectedPrinter != null && connectedPrinter.address != null) ? connectedPrinter.address : "" text: (connectedPrinter != null && connectedPrinter.address != null) ? connectedPrinter.address : ""
@ -47,7 +47,7 @@ Column
anchors.right: parent.right anchors.right: parent.right
anchors.margins: UM.Theme.getSize("default_margin").width anchors.margins: UM.Theme.getSize("default_margin").width
} }
Label Text
{ {
text: connectedPrinter != null ? connectedPrinter.connectionText : catalog.i18nc("@info:status", "The printer is not connected.") text: connectedPrinter != null ? connectedPrinter.connectionText : catalog.i18nc("@info:status", "The printer is not connected.")
color: connectedPrinter != null && connectedPrinter.acceptsCommands ? UM.Theme.getColor("setting_control_text") : UM.Theme.getColor("setting_control_disabled_text") color: connectedPrinter != null && connectedPrinter.acceptsCommands ? UM.Theme.getColor("setting_control_text") : UM.Theme.getColor("setting_control_disabled_text")
@ -85,7 +85,7 @@ Column
width: index == machineExtruderCount.properties.value - 1 && index % 2 == 0 ? extrudersGrid.width : extrudersGrid.width / 2 - UM.Theme.getSize("sidebar_lining_thin").width / 2 width: index == machineExtruderCount.properties.value - 1 && index % 2 == 0 ? extrudersGrid.width : extrudersGrid.width / 2 - UM.Theme.getSize("sidebar_lining_thin").width / 2
height: UM.Theme.getSize("sidebar_extruder_box").height height: UM.Theme.getSize("sidebar_extruder_box").height
Label //Extruder name. Text //Extruder name.
{ {
text: ExtruderManager.getExtruderName(index) != "" ? ExtruderManager.getExtruderName(index) : catalog.i18nc("@label", "Hotend") text: ExtruderManager.getExtruderName(index) != "" ? ExtruderManager.getExtruderName(index) : catalog.i18nc("@label", "Hotend")
color: UM.Theme.getColor("text") color: UM.Theme.getColor("text")
@ -94,7 +94,7 @@ Column
anchors.top: parent.top anchors.top: parent.top
anchors.margins: UM.Theme.getSize("default_margin").width anchors.margins: UM.Theme.getSize("default_margin").width
} }
Label //Temperature indication. Text //Temperature indication.
{ {
id: extruderTemperature id: extruderTemperature
text: (connectedPrinter != null && connectedPrinter.hotendIds[index] != null && connectedPrinter.hotendTemperatures[index] != null) ? Math.round(connectedPrinter.hotendTemperatures[index]) + "°C" : "" text: (connectedPrinter != null && connectedPrinter.hotendIds[index] != null && connectedPrinter.hotendTemperatures[index] != null) ? Math.round(connectedPrinter.hotendTemperatures[index]) + "°C" : ""
@ -161,7 +161,7 @@ Column
} }
} }
} }
Label //Material name. Text //Material name.
{ {
id: materialName id: materialName
text: (connectedPrinter != null && connectedPrinter.materialNames[index] != null && connectedPrinter.materialIds[index] != "") ? connectedPrinter.materialNames[index] : "" text: (connectedPrinter != null && connectedPrinter.materialNames[index] != null && connectedPrinter.materialIds[index] != "") ? connectedPrinter.materialNames[index] : ""
@ -193,7 +193,7 @@ Column
} }
} }
} }
Label //Variant name. Text //Variant name.
{ {
id: variantName id: variantName
text: (connectedPrinter != null && connectedPrinter.hotendIds[index] != null) ? connectedPrinter.hotendIds[index] : "" text: (connectedPrinter != null && connectedPrinter.hotendIds[index] != null) ? connectedPrinter.hotendIds[index] : ""
@ -244,7 +244,7 @@ Column
height: machineHeatedBed.properties.value == "True" ? UM.Theme.getSize("sidebar_extruder_box").height : 0 height: machineHeatedBed.properties.value == "True" ? UM.Theme.getSize("sidebar_extruder_box").height : 0
visible: machineHeatedBed.properties.value == "True" visible: machineHeatedBed.properties.value == "True"
Label //Build plate label. Text //Build plate label.
{ {
text: catalog.i18nc("@label", "Build plate") text: catalog.i18nc("@label", "Build plate")
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
@ -253,7 +253,7 @@ Column
anchors.top: parent.top anchors.top: parent.top
anchors.margins: UM.Theme.getSize("default_margin").width anchors.margins: UM.Theme.getSize("default_margin").width
} }
Label //Target temperature. Text //Target temperature.
{ {
id: bedTargetTemperature id: bedTargetTemperature
text: connectedPrinter != null ? connectedPrinter.targetBedTemperature + "°C" : "" text: connectedPrinter != null ? connectedPrinter.targetBedTemperature + "°C" : ""
@ -285,7 +285,7 @@ Column
} }
} }
} }
Label //Current temperature. Text //Current temperature.
{ {
id: bedCurrentTemperature id: bedCurrentTemperature
text: connectedPrinter != null ? connectedPrinter.bedTemperature + "°C" : "" text: connectedPrinter != null ? connectedPrinter.bedTemperature + "°C" : ""
@ -353,7 +353,7 @@ Column
color: UM.Theme.getColor("setting_control_highlight") color: UM.Theme.getColor("setting_control_highlight")
opacity: preheatTemperatureControl.hovered ? 1.0 : 0 opacity: preheatTemperatureControl.hovered ? 1.0 : 0
} }
Label //Maximum temperature indication. Text //Maximum temperature indication.
{ {
text: (bedTemperature.properties.maximum_value != "None" ? bedTemperature.properties.maximum_value : "") + "°C" text: (bedTemperature.properties.maximum_value != "None" ? bedTemperature.properties.maximum_value : "") + "°C"
color: UM.Theme.getColor("setting_unit") color: UM.Theme.getColor("setting_unit")
@ -452,7 +452,7 @@ Column
} }
} }
} }
Label Text
{ {
id: preheatCountdown id: preheatCountdown
text: connectedPrinter != null ? connectedPrinter.preheatBedRemainingTime : "" text: connectedPrinter != null ? connectedPrinter.preheatBedRemainingTime : ""
@ -546,7 +546,7 @@ Column
} }
} }
Label Text
{ {
id: actualLabel id: actualLabel
anchors.centerIn: parent anchors.centerIn: parent
@ -662,7 +662,7 @@ Column
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_margin").width anchors.leftMargin: UM.Theme.getSize("default_margin").width
Label Text
{ {
width: parent.width * 0.4 width: parent.width * 0.4
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
@ -671,7 +671,7 @@ Column
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
elide: Text.ElideRight elide: Text.ElideRight
} }
Label Text
{ {
width: parent.width * 0.6 width: parent.width * 0.6
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
@ -692,7 +692,7 @@ Column
width: base.width width: base.width
height: UM.Theme.getSize("section").height height: UM.Theme.getSize("section").height
Label Text
{ {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left anchors.left: parent.left

View File

@ -16,7 +16,7 @@ Item {
property int backendState: UM.Backend.state; property int backendState: UM.Backend.state;
property var backend: CuraApplication.getBackend(); property var backend: CuraApplication.getBackend();
property bool activity: Printer.platformActivity; property bool activity: CuraApplication.platformActivity;
property int totalHeight: childrenRect.height + UM.Theme.getSize("default_margin").height property int totalHeight: childrenRect.height + UM.Theme.getSize("default_margin").height
property string fileBaseName property string fileBaseName
@ -44,7 +44,7 @@ Item {
} }
} }
Label { Text {
id: statusLabel id: statusLabel
width: parent.width - 2 * UM.Theme.getSize("default_margin").width width: parent.width - 2 * UM.Theme.getSize("default_margin").width
anchors.top: parent.top anchors.top: parent.top
@ -110,8 +110,8 @@ Item {
onAdditionalComponentsChanged: onAdditionalComponentsChanged:
{ {
if(areaId == "saveButton") { if(areaId == "saveButton") {
for (var component in Printer.additionalComponents["saveButton"]) { for (var component in CuraApplication.additionalComponents["saveButton"]) {
Printer.additionalComponents["saveButton"][component].parent = additionalComponentsRow CuraApplication.additionalComponents["saveButton"][component].parent = additionalComponentsRow
} }
} }
} }

View File

@ -95,13 +95,17 @@ SettingItem
value: value:
{ {
// FIXME this needs to go away once 'resolve' is combined with 'value' in our data model. // FIXME this needs to go away once 'resolve' is combined with 'value' in our data model.
var value; var value = undefined;
if ((base.resolve != "None") && (base.stackLevel != 0) && (base.stackLevel != 1)) { if ((base.resolve != "None") && (base.stackLevel != 0) && (base.stackLevel != 1))
{
// We have a resolve function. Indicates that the setting is not settable per extruder and that // We have a resolve function. Indicates that the setting is not settable per extruder and that
// we have to choose between the resolved value (default) and the global value // we have to choose between the resolved value (default) and the global value
// (if user has explicitly set this). // (if user has explicitly set this).
value = base.resolve; value = base.resolve;
} else { }
if (value == undefined)
{
value = propertyProvider.properties.value; value = propertyProvider.properties.value;
} }

View File

@ -98,9 +98,9 @@ SettingItem
selectByMouse: true; selectByMouse: true;
maximumLength: (definition.type == "[int]") ? 20 : 10; maximumLength: (definition.type == "[int]") ? 20 : (definition.type == "str") ? -1 : 10;
validator: RegExpValidator { regExp: (definition.type == "[int]") ? /^\[?(\s*-?[0-9]{0,9}\s*,)*(\s*-?[0-9]{0,9})\s*\]?$/ : (definition.type == "int") ? /^-?[0-9]{0,10}$/ : /^-?[0-9]{0,9}[.,]?[0-9]{0,10}$/ } // definition.type property from parent loader used to disallow fractional number entry validator: RegExpValidator { regExp: (definition.type == "[int]") ? /^\[?(\s*-?[0-9]{0,9}\s*,)*(\s*-?[0-9]{0,9})\s*\]?$/ : (definition.type == "int") ? /^-?[0-9]{0,10}$/ : (definition.type == "float") ? /^-?[0-9]{0,9}[.,]?[0-9]{0,10}$/ : /^.*$/ } // definition.type property from parent loader used to disallow fractional number entry
Binding Binding
{ {

View File

@ -155,14 +155,14 @@ Item
containerId: Cura.MachineManager.activeDefinitionId containerId: Cura.MachineManager.activeDefinitionId
visibilityHandler: UM.SettingPreferenceVisibilityHandler { } visibilityHandler: UM.SettingPreferenceVisibilityHandler { }
exclude: ["machine_settings", "command_line_settings", "infill_mesh", "infill_mesh_order", "support_mesh", "anti_overhang_mesh"] // TODO: infill_mesh settigns are excluded hardcoded, but should be based on the fact that settable_globally, settable_per_meshgroup and settable_per_extruder are false. exclude: ["machine_settings", "command_line_settings", "infill_mesh", "infill_mesh_order", "support_mesh", "anti_overhang_mesh"] // TODO: infill_mesh settigns are excluded hardcoded, but should be based on the fact that settable_globally, settable_per_meshgroup and settable_per_extruder are false.
expanded: Printer.expandedCategories expanded: CuraApplication.expandedCategories
onExpandedChanged: onExpandedChanged:
{ {
if(!findingSettings) if(!findingSettings)
{ {
// Do not change expandedCategories preference while filtering settings // Do not change expandedCategories preference while filtering settings
// because all categories are expanded while filtering // because all categories are expanded while filtering
Printer.setExpandedCategories(expanded) CuraApplication.setExpandedCategories(expanded)
} }
} }
onVisibilityChanged: Cura.SettingInheritanceManager.forceUpdate() onVisibilityChanged: Cura.SettingInheritanceManager.forceUpdate()
@ -299,7 +299,7 @@ Item
} }
} }
UM.I18nCatalog { id: catalog; name: "uranium"; } UM.I18nCatalog { id: catalog; name: "cura"; }
add: Transition { add: Transition {
SequentialAnimation { SequentialAnimation {

View File

@ -332,7 +332,7 @@ Rectangle
} }
} }
Label { Text {
id: settingsModeLabel id: settingsModeLabel
text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox","Print Setup disabled\nG-code files cannot be modified"); text: !hideSettings ? catalog.i18nc("@label:listbox", "Print Setup") : catalog.i18nc("@label:listbox","Print Setup disabled\nG-code files cannot be modified");
anchors.left: parent.left anchors.left: parent.left

View File

@ -128,7 +128,7 @@ Column
border.color: UM.Theme.getColor("setting_control_border") border.color: UM.Theme.getColor("setting_control_border")
} }
Label Text
{ {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.left: swatch.visible ? swatch.right : parent.left anchors.left: swatch.visible ? swatch.right : parent.left
@ -174,7 +174,7 @@ Column
rightMargin: UM.Theme.getSize("default_margin").width rightMargin: UM.Theme.getSize("default_margin").width
} }
Label Text
{ {
id: variantLabel id: variantLabel
text: text:
@ -272,7 +272,7 @@ Column
} }
Label Text
{ {
id: globalProfileLabel id: globalProfileLabel
text: catalog.i18nc("@label","Profile:"); text: catalog.i18nc("@label","Profile:");

View File

@ -33,7 +33,7 @@ Item
width: base.width * .45 - UM.Theme.getSize("default_margin").width width: base.width * .45 - UM.Theme.getSize("default_margin").width
height: childrenRect.height height: childrenRect.height
Label Text
{ {
id: infillLabel id: infillLabel
//: Infill selection label //: Infill selection label
@ -162,7 +162,7 @@ Item
} }
} }
} }
Label Text
{ {
id: infillLabel id: infillLabel
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
@ -225,14 +225,14 @@ Item
anchors.right: parent.right anchors.right: parent.right
height: childrenRect.height height: childrenRect.height
Label Text
{ {
id: enableSupportLabel id: enableSupportLabel
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_margin").width anchors.leftMargin: UM.Theme.getSize("default_margin").width
anchors.verticalCenter: enableSupportCheckBox.verticalCenter anchors.verticalCenter: enableSupportCheckBox.verticalCenter
width: parent.width * .45 - 3 * UM.Theme.getSize("default_margin").width width: parent.width * .45 - 3 * UM.Theme.getSize("default_margin").width
text: catalog.i18nc("@label", "Enable Support"); text: catalog.i18nc("@label", "Generate Support");
font: UM.Theme.getFont("default"); font: UM.Theme.getFont("default");
color: UM.Theme.getColor("text"); color: UM.Theme.getColor("text");
} }
@ -263,7 +263,7 @@ Item
onEntered: onEntered:
{ {
base.showTooltip(enableSupportCheckBox, Qt.point(-enableSupportCheckBox.x, 0), base.showTooltip(enableSupportCheckBox, Qt.point(-enableSupportCheckBox.x, 0),
catalog.i18nc("@label", "Enable support structures. These structures support parts of the model with severe overhangs.")); catalog.i18nc("@label", "Generate structures to support parts of the model which have overhangs. Without these structures, such parts would collapse during printing."));
} }
onExited: onExited:
{ {
@ -272,7 +272,7 @@ Item
} }
} }
Label Text
{ {
id: supportExtruderLabel id: supportExtruderLabel
visible: (supportEnabled.properties.value == "True") && (machineExtruderCount.properties.value > 1) visible: (supportEnabled.properties.value == "True") && (machineExtruderCount.properties.value > 1)
@ -372,7 +372,7 @@ Item
} }
Label Text
{ {
id: adhesionHelperLabel id: adhesionHelperLabel
anchors.left: parent.left anchors.left: parent.left
@ -470,7 +470,7 @@ Item
width: parent.width width: parent.width
height: childrenRect.height height: childrenRect.height
Label Text
{ {
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_margin").width anchors.leftMargin: UM.Theme.getSize("default_margin").width

View File

@ -43,7 +43,7 @@ UM.PointingRectangle {
base.opacity = 0; base.opacity = 0;
} }
Label { Text {
id: label; id: label;
anchors { anchors {
top: parent.top; top: parent.top;

View File

@ -13,13 +13,13 @@ UM.Dialog
{ {
title: catalog.i18nc("@title:window", "Save Project") title: catalog.i18nc("@title:window", "Save Project")
width: 550 width: 550 * Screen.devicePixelRatio
minimumWidth: 550 minimumWidth: 550 * Screen.devicePixelRatio
height: 350 height: 350 * Screen.devicePixelRatio
minimumHeight: 350 minimumHeight: 350 * Screen.devicePixelRatio
property int spacerHeight: 10 property int spacerHeight: 10 * Screen.devicePixelRatio
property bool dontShowAgain: true property bool dontShowAgain: true
@ -41,15 +41,8 @@ UM.Dialog
Item Item
{ {
anchors.top: parent.top anchors.fill: parent
anchors.bottom: parent.bottom anchors.margins: 20 * Screen.devicePixelRatio
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
anchors.bottomMargin: 20
anchors.leftMargin:20
anchors.rightMargin: 20
UM.SettingDefinitionsModel UM.SettingDefinitionsModel
{ {
@ -63,8 +56,12 @@ UM.Dialog
} }
UM.I18nCatalog UM.I18nCatalog
{ {
id: catalog; id: catalog
name: "cura"; name: "cura"
}
SystemPalette
{
id: palette
} }
Column Column
@ -75,12 +72,12 @@ UM.Dialog
{ {
id: titleLabel id: titleLabel
text: catalog.i18nc("@action:title", "Summary - Cura Project") text: catalog.i18nc("@action:title", "Summary - Cura Project")
font.pixelSize: 22 font.pointSize: 18
} }
Rectangle Rectangle
{ {
id: separator id: separator
color: "black" color: palette.text
width: parent.width width: parent.width
height: 1 height: 1
} }
@ -229,6 +226,13 @@ UM.Dialog
width: parent.width / 3 width: parent.width / 3
} }
} }
Item // Spacer
{
height: spacerHeight
width: height
}
CheckBox CheckBox
{ {
id: dontShowAgainCheckbox id: dontShowAgainCheckbox

View File

@ -0,0 +1,55 @@
[general]
version = 2
name = Coarse
definition = imade3d_jellybox
[metadata]
type = quality
material = generic_petg_imade3d_jellybox_0.4_mm
weight = -1
quality_type = fast
[values]
adhesion_type = skirt
bottom_thickness = 0.6
coasting_enable = True
coasting_speed = 95
cool_fan_full_at_height = 1.2
cool_fan_speed_max = 60
cool_fan_speed_min = 20
cool_min_layer_time = 5
cool_min_layer_time_fan_speed_max = 10
cool_min_speed = 10
infill_before_walls = False
infill_line_width = 0.6
infill_overlap = 15
infill_pattern = zigzag
infill_sparse_density = 20
layer_height = 0.3
layer_height_0 = 0.3
line_width = 0.4
material_bed_temperature = 50
material_bed_temperature_layer_0 = 55
material_flow = 100
meshfix_union_all = False
retraction_amount = 1.3
retraction_combing = all
retraction_hop_enabled = 0.1
retraction_min_travel = 1.2
retraction_prime_speed = 25
retraction_retract_speed = 35
retraction_speed = 70
skin_no_small_gaps_heuristic = False
skirt_brim_minimal_length = 100
skirt_brim_speed = 25
skirt_line_count = 2
speed_layer_0 = 14
speed_print = 40
speed_slowdown_layers = 1
speed_topbottom = 20
speed_travel = 120
speed_travel_layer_0 = 60
speed_wall = 25
speed_wall_x = 35
top_thickness = =top_bottom_thickness
wall_thickness = 0.8

View File

@ -0,0 +1,55 @@
[general]
version = 2
name = Coarse
definition = imade3d_jellybox
[metadata]
type = quality
material = generic_petg_imade3d_jellybox_0.4_mm_2-fans
weight = -1
quality_type = fast
[values]
adhesion_type = skirt
bottom_thickness = 0.6
coasting_enable = True
coasting_speed = 95
cool_fan_full_at_height = 1.2
cool_fan_speed_max = 40
cool_fan_speed_min = 20
cool_min_layer_time = 5
cool_min_layer_time_fan_speed_max = 10
cool_min_speed = 10
infill_before_walls = False
infill_line_width = 0.6
infill_overlap = 15
infill_pattern = zigzag
infill_sparse_density = 20
layer_height = 0.3
layer_height_0 = 0.3
line_width = 0.4
material_bed_temperature = 50
material_bed_temperature_layer_0 = 55
material_flow = 100
meshfix_union_all = False
retraction_amount = 1.3
retraction_combing = all
retraction_hop_enabled = 0.1
retraction_min_travel = 1.2
retraction_prime_speed = 25
retraction_retract_speed = 35
retraction_speed = 70
skin_no_small_gaps_heuristic = False
skirt_brim_minimal_length = 100
skirt_brim_speed = 25
skirt_line_count = 2
speed_layer_0 = 14
speed_print = 40
speed_slowdown_layers = 1
speed_topbottom = 20
speed_travel = 120
speed_travel_layer_0 = 60
speed_wall = 25
speed_wall_x = 35
top_thickness = =top_bottom_thickness
wall_thickness = 0.8

View File

@ -0,0 +1,55 @@
[general]
version = 2
name = Medium
definition = imade3d_jellybox
[metadata]
type = quality
material = generic_petg_imade3d_jellybox_0.4_mm
weight = 0
quality_type = normal
[values]
adhesion_type = skirt
bottom_thickness = 0.6
coasting_enable = True
coasting_speed = 95
cool_fan_full_at_height = 1.2
cool_fan_speed_max = 60
cool_fan_speed_min = 20
cool_min_layer_time = 7
cool_min_layer_time_fan_speed_max = 10
cool_min_speed = 10
infill_before_walls = False
infill_line_width = 0.6
infill_overlap = 15
infill_pattern = zigzag
infill_sparse_density = 20
layer_height = 0.2
layer_height_0 = 0.3
line_width = 0.4
material_bed_temperature = 50
material_bed_temperature_layer_0 = 55
material_flow = 100
meshfix_union_all = False
retraction_amount = 1.3
retraction_combing = all
retraction_hop_enabled = 0.1
retraction_min_travel = 1.2
retraction_prime_speed = 25
retraction_retract_speed = 35
retraction_speed = 70
skin_no_small_gaps_heuristic = False
skirt_brim_minimal_length = 100
skirt_brim_speed = 25
skirt_line_count = 2
speed_layer_0 = 14
speed_print = 40
speed_slowdown_layers = 1
speed_topbottom = 20
speed_travel = 120
speed_travel_layer_0 = 60
speed_wall = 25
speed_wall_x = 35
top_thickness = =top_bottom_thickness
wall_thickness = 0.8

View File

@ -0,0 +1,55 @@
[general]
version = 2
name = Medium
definition = imade3d_jellybox
[metadata]
type = quality
material = generic_petg_imade3d_jellybox_0.4_mm_2-fans
weight = 0
quality_type = normal
[values]
adhesion_type = skirt
bottom_thickness = 0.6
coasting_enable = True
coasting_speed = 95
cool_fan_full_at_height = 1.2
cool_fan_speed_max = 40
cool_fan_speed_min = 20
cool_min_layer_time = 5
cool_min_layer_time_fan_speed_max = 10
cool_min_speed = 10
infill_before_walls = False
infill_line_width = 0.6
infill_overlap = 15
infill_pattern = zigzag
infill_sparse_density = 20
layer_height = 0.2
layer_height_0 = 0.3
line_width = 0.4
material_bed_temperature = 50
material_bed_temperature_layer_0 = 55
material_flow = 100
meshfix_union_all = False
retraction_amount = 1.3
retraction_combing = all
retraction_hop_enabled = 0.1
retraction_min_travel = 1.2
retraction_prime_speed = 25
retraction_retract_speed = 35
retraction_speed = 70
skin_no_small_gaps_heuristic = False
skirt_brim_minimal_length = 100
skirt_brim_speed = 25
skirt_line_count = 2
speed_layer_0 = 14
speed_print = 40
speed_slowdown_layers = 1
speed_topbottom = 20
speed_travel = 120
speed_travel_layer_0 = 60
speed_wall = 25
speed_wall_x = 35
top_thickness = =top_bottom_thickness
wall_thickness = 0.8

View File

@ -0,0 +1,53 @@
[general]
version = 2
name = Coarse
definition = imade3d_jellybox
[metadata]
type = quality
material = generic_pla_imade3d_jellybox_0.4_mm
weight = -1
quality_type = fast
[values]
adhesion_type = skirt
bottom_thickness = 0.6
coasting_enable = True
coasting_speed = 95
cool_fan_full_at_height = 0.65
cool_fan_speed_max = 100
cool_fan_speed_min = 50
cool_min_layer_time = 7
cool_min_layer_time_fan_speed_max = 10
cool_min_speed = 10
infill_before_walls = False
infill_line_width = 0.6
infill_overlap = 15
infill_pattern = zigzag
infill_sparse_density = 20
layer_height = 0.3
layer_height_0 = 0.3
line_width = 0.4
material_flow = 90
meshfix_union_all = False
retraction_amount = 1.3
retraction_combing = all
retraction_hop_enabled = 0.1
retraction_min_travel = 1.2
retraction_prime_speed = 30
retraction_retract_speed = 70
retraction_speed = 70
skin_no_small_gaps_heuristic = False
skirt_brim_minimal_length = 100
skirt_brim_speed = 20
skirt_line_count = 3
speed_layer_0 = 20
speed_print = 45
speed_slowdown_layers = 1
speed_topbottom = 25
speed_travel = 120
speed_travel_layer_0 = 60
speed_wall = 25
speed_wall_x = 35
top_thickness = 0.8
wall_thickness = 0.8

View File

@ -0,0 +1,53 @@
[general]
version = 2
name = Coarse
definition = imade3d_jellybox
[metadata]
type = quality
material = generic_pla_imade3d_jellybox_0.4_mm_2-fans
weight = -1
quality_type = fast
[values]
adhesion_type = skirt
bottom_thickness = 0.6
coasting_enable = True
coasting_speed = 95
cool_fan_full_at_height = 0.65
cool_fan_speed_max = 100
cool_fan_speed_min = 20
cool_min_layer_time = 5
cool_min_layer_time_fan_speed_max = 10
cool_min_speed = 10
infill_before_walls = False
infill_line_width = 0.6
infill_overlap = 15
infill_pattern = zigzag
infill_sparse_density = 20
layer_height = 0.3
layer_height_0 = 0.3
line_width = 0.4
material_flow = 90
meshfix_union_all = False
retraction_amount = 1.3
retraction_combing = all
retraction_hop_enabled = 0.1
retraction_min_travel = 1.2
retraction_prime_speed = 30
retraction_retract_speed = 70
retraction_speed = 70
skin_no_small_gaps_heuristic = False
skirt_brim_minimal_length = 100
skirt_brim_speed = 20
skirt_line_count = 3
speed_layer_0 = 20
speed_print = 45
speed_slowdown_layers = 1
speed_topbottom = 25
speed_travel = 120
speed_travel_layer_0 = 60
speed_wall = 25
speed_wall_x = 35
top_thickness = 0.8
wall_thickness = 0.8

Some files were not shown because too many files have changed in this diff Show More