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
.idea
cura.desktop
# Eclipse+PyDev
.project
@ -33,4 +34,21 @@ plugins/Doodle3D-cura-plugin
plugins/GodMode
plugins/PostProcessingPlugin
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(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 "")
list(APPEND CMAKE_MODULE_PATH ${URANIUM_DIR}/cmake)
include(UraniumTranslationTools)
@ -64,5 +67,3 @@ else()
install(FILES ${CMAKE_BINARY_DIR}/CuraVersion.py
DESTINATION lib/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages/cura)
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")
import numpy
import copy
import math
from typing import List
# Setting for clearance around the prime
PRIME_CLEARANCE = 6.5
@ -110,10 +111,11 @@ class BuildVolume(SceneNode):
def _onChangeTimerFinished(self):
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:
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.
per_mesh_stack = node.callDecoration("getStack")
if per_mesh_stack:
@ -121,7 +123,7 @@ class BuildVolume(SceneNode):
active_extruder_changed = node.callDecoration("getActiveExtruderChangedSignal")
if active_extruder_changed is not None:
node.callDecoration("getActiveExtruderChangedSignal").disconnect(self._updateDisallowedAreasAndRebuild)
node.decoratorsChanged.disconnect(self._onNodeDecoratorChanged)
node.decoratorsChanged.disconnect(self._updateNodeListeners)
self._scene_objects = new_scene_objects
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.
#
# \param node The node for which the decorators changed.
def _onNodeDecoratorChanged(self, node):
def _updateNodeListeners(self, node: SceneNode):
per_mesh_stack = node.callDecoration("getStack")
if per_mesh_stack:
per_mesh_stack.propertyChanged.connect(self._onSettingPropertyChanged)
@ -139,21 +141,25 @@ class BuildVolume(SceneNode):
self._updateDisallowedAreasAndRebuild()
def setWidth(self, width):
if width: self._width = width
if width is not None:
self._width = width
def setHeight(self, height):
if height: self._height = height
if height is not None:
self._height = height
def setDepth(self, depth):
if depth: self._depth = depth
if depth is not None:
self._depth = depth
def setShape(self, shape):
if shape: self._shape = shape
def setShape(self, shape: str):
if shape:
self._shape = shape
def getDisallowedAreas(self):
def getDisallowedAreas(self) -> List[Polygon]:
return self._disallowed_areas
def setDisallowedAreas(self, areas):
def setDisallowedAreas(self, areas: List[Polygon]):
self._disallowed_areas = areas
def render(self, renderer):
@ -179,6 +185,53 @@ class BuildVolume(SceneNode):
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.
def rebuild(self):
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
def getBoundingBox(self):
self.updateNodeBoundaryCheck()
def getBoundingBox(self) -> AxisAlignedBox:
return self._volume_aabb
def getRaftThickness(self):
def getRaftThickness(self) -> float:
return self._raft_thickness
def _updateRaftThickness(self):
@ -442,7 +497,7 @@ class BuildVolume(SceneNode):
self._engine_ready = True
self.rebuild()
def _onSettingPropertyChanged(self, setting_key, property_name):
def _onSettingPropertyChanged(self, setting_key: str, property_name: str):
if property_name != "value":
return
@ -475,7 +530,7 @@ class BuildVolume(SceneNode):
if rebuild_me:
self.rebuild()
def hasErrors(self):
def hasErrors(self) -> bool:
return self._has_errors
## Calls _updateDisallowedAreas and makes sure the changes appear in the

View File

@ -59,7 +59,8 @@ class ConvexHullDecorator(SceneNodeDecorator):
hull = self._compute2DConvexHull()
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 = self._add2DAdhesionMargin(hull)
return hull
@ -79,7 +80,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
return None
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_with_adhesion_margin = self._add2DAdhesionMargin(head_with_fans)
return head_with_fans_with_adhesion_margin
@ -93,8 +94,7 @@ class ConvexHullDecorator(SceneNodeDecorator):
return None
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
return self._compute2DConvexHull()
return None
@ -258,12 +258,16 @@ class ConvexHullDecorator(SceneNodeDecorator):
# influences the collision area.
def _offsetHull(self, convex_hull):
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([
[-horizontal_expansion, -horizontal_expansion],
[-horizontal_expansion, horizontal_expansion],
[horizontal_expansion, horizontal_expansion],
[horizontal_expansion, -horizontal_expansion]
[-hull_offset, -hull_offset],
[-hull_offset, hull_offset],
[hull_offset, hull_offset],
[hull_offset, -hull_offset]
], numpy.float32))
return convex_hull.getMinkowskiHull(expansion_polygon)
else:
@ -331,4 +335,4 @@ class ConvexHullDecorator(SceneNodeDecorator):
## Settings that change the convex hull.
#
# 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
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
# 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
@ -19,8 +22,6 @@ class ConvexHullNode(SceneNode):
self.setCalculateBoundingBox(False)
self._shader = None
self._original_parent = parent
# Color of the drawn convex hull
@ -59,16 +60,16 @@ class ConvexHullNode(SceneNode):
return self._node
def render(self, renderer):
if not self._shader:
self._shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "transparent_object.shader"))
self._shader.setUniformValue("u_diffuseColor", self._color)
self._shader.setUniformValue("u_opacity", 0.6)
if not ConvexHullNode.shader:
ConvexHullNode.shader = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "transparent_object.shader"))
ConvexHullNode.shader.setUniformValue("u_diffuseColor", self._color)
ConvexHullNode.shader.setUniformValue("u_opacity", 0.6)
if self.getParent():
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:
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

View File

@ -4,7 +4,6 @@ from PyQt5.QtNetwork import QLocalServer
from PyQt5.QtNetwork import QLocalSocket
from UM.Qt.QtApplication import QtApplication
from UM.FileHandler.ReadFileJob import ReadFileJob
from UM.Scene.SceneNode import SceneNode
from UM.Scene.Camera import Camera
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.Logger import Logger
from UM.Preferences import Preferences
from UM.JobQueue import JobQueue
from UM.SaveFile import SaveFile
from UM.Scene.Selection import Selection
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.GroupedOperation import GroupedOperation
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.SliceableObjectDecorator import SliceableObjectDecorator
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.ContainerRegistry import ContainerRegistry
from UM.Settings.SettingFunction import SettingFunction
@ -90,6 +94,7 @@ if not MYPY:
CuraVersion = "master" # [CodeStyle: Reflecting imported value]
CuraBuildType = ""
class CuraApplication(QtApplication):
class ResourceTypes:
QmlFiles = Resources.UserType + 1
@ -104,7 +109,6 @@ class CuraApplication(QtApplication):
Q_ENUMS(ResourceTypes)
def __init__(self):
Resources.addSearchPath(os.path.join(QtApplication.getInstallPrefix(), "share", "cura", "resources"))
if not hasattr(sys, "frozen"):
Resources.addSearchPath(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "resources"))
@ -184,7 +188,10 @@ class CuraApplication(QtApplication):
"SelectionTool",
"CameraTool",
"GCodeWriter",
"LocalFileOutputDevice"
"LocalFileOutputDevice",
"TranslateTool",
"FileLogger",
"XmlMaterialProfile"
])
self._physics = None
self._volume = None
@ -240,7 +247,7 @@ class CuraApplication(QtApplication):
ContainerRegistry.getInstance().load()
Preferences.getInstance().addPreference("cura/active_mode", "simple")
Preferences.getInstance().addPreference("cura/recent_files", "")
Preferences.getInstance().addPreference("cura/categories_expanded", "")
Preferences.getInstance().addPreference("cura/jobname_prefix", True)
Preferences.getInstance().addPreference("view/center_on_select", False)
@ -316,20 +323,11 @@ class CuraApplication(QtApplication):
experimental
""".replace("\n", ";").replace(" ", ""))
JobQueue.getInstance().jobFinished.connect(self._onJobFinished)
self.applicationShuttingDown.connect(self.saveSettings)
self.engineCreatedSignal.connect(self._onEngineCreated)
self.globalContainerStackChanged.connect(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):
self._engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider())
@ -594,6 +592,9 @@ class CuraApplication(QtApplication):
# The platform is a child of BuildVolume
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._physics = PlatformPhysics.PlatformPhysics(controller, self._volume)
@ -668,6 +669,7 @@ class CuraApplication(QtApplication):
#
# \param engine The QML engine.
def registerObjects(self, engine):
super().registerObjects(engine)
engine.rootContext().setContextProperty("Printer", self)
engine.rootContext().setContextProperty("CuraApplication", self)
self._print_information = PrintInformation.PrintInformation()
@ -701,14 +703,11 @@ class CuraApplication(QtApplication):
if type_name in ("Cura", "Actions"):
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).
# 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
qmlRegisterType(QUrl.fromLocalFile(path), "Cura", 1, 0, type_name)
def onSelectionChanged(self):
if Selection.hasSelection():
@ -847,24 +846,14 @@ class CuraApplication(QtApplication):
op.push()
## 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)
def multiplyObject(self, object_id, count):
node = self.getController().getScene().findObject(object_id)
if not node and object_id != 0: # Workaround for tool handles overlapping the selected object
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()
def multiplyObject(self, object_id, count, min_offset = 8):
job = MultiplyObjectsJob(object_id, count, min_offset)
job.start()
return
## Center object on platform.
@pyqtSlot("quint64")
@ -982,6 +971,52 @@ class CuraApplication(QtApplication):
op.addOperation(SetTransformOperation(node, Vector(0, center_y, 0), Quaternion(), Vector(1, 1, 1)))
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.
@pyqtSlot()
def reloadAll(self):
@ -1017,12 +1052,6 @@ class CuraApplication(QtApplication):
return log
recentFilesChanged = pyqtSignal()
@pyqtProperty("QVariantList", notify = recentFilesChanged)
def recentFiles(self):
return self._recent_files
@pyqtSlot("QStringList")
def setExpandedCategories(self, categories):
categories = list(set(categories))
@ -1130,25 +1159,6 @@ class CuraApplication(QtApplication):
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):
# 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()
@ -1243,6 +1253,10 @@ class CuraApplication(QtApplication):
filename = job.getFileName()
self._currently_loading_files.remove(filename)
root = self.getController().getScene().getRoot()
arranger = Arrange.create(scene_root = root)
min_offset = 8
for node in nodes:
node.setSelectable(True)
node.setName(os.path.basename(filename))
@ -1263,9 +1277,24 @@ class CuraApplication(QtApplication):
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.push()
scene.sceneChanged.emit(node)
def addNonSliceableExtension(self, extension):
@ -1276,10 +1305,14 @@ class CuraApplication(QtApplication):
"""
Checks if the given file URL is a valid project file.
"""
file_path = QUrl(file_url).toLocalFile()
workspace_reader = self.getWorkspaceFileHandler().getReaderForFile(file_path)
if workspace_reader is None:
return False # non-project files won't get a reader
try:
file_path = QUrl(file_url).toLocalFile()
workspace_reader = self.getWorkspaceFileHandler().getReaderForFile(file_path)
if workspace_reader is None:
return False # non-project files won't get a reader
result = workspace_reader.preRead(file_path, show_dialog=False)
return result == WorkspaceReader.PreReadResult.accepted
result = workspace_reader.preRead(file_path, show_dialog=False)
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 UM.Application import Application
from UM.Scene.SceneNode import SceneNode
from UM.Scene.Iterator.BreadthFirstIterator import BreadthFirstIterator
from UM.Math.Vector import Vector
from UM.Math.AxisAlignedBox import AxisAlignedBox
from UM.Scene.Selection import Selection
from UM.Preferences import Preferences
@ -51,10 +51,13 @@ class PlatformPhysics:
# same direction.
transformed_nodes = []
group_nodes = []
# We try to shuffle all the nodes to prevent "locked" situations, where iteration B inverts iteration A.
# By shuffling the order of the nodes, this might happen a few times, but at some point it will resolve.
nodes = list(BreadthFirstIterator(root))
# 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)
for node in nodes:
if node is root or type(node) is not SceneNode or node.getBoundingBox() is None:
@ -62,24 +65,6 @@ class PlatformPhysics:
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_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
@ -145,27 +130,14 @@ class PlatformPhysics:
# Simply waiting for the next tick seems to resolve this correctly.
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):
transformed_nodes.append(node)
op = PlatformPhysicsOperation.PlatformPhysicsOperation(node, move_vector)
op.push()
# Group nodes should override the _outside_buildarea property of their children.
for group_node in group_nodes:
for child_node in group_node.getAllChildren():
child_node._outside_buildarea = group_node._outside_buildarea
# After moving, we have to evaluate the boundary checks for nodes
build_volume = Application.getInstance().getBuildVolume()
build_volume.updateNodeBoundaryCheck()
def _onToolOperationStarted(self, tool):
self._enabled = False

View File

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

View File

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

View File

@ -244,7 +244,13 @@ class ExtruderManager(QObject):
material = materials[0]
preferred_material_id = machine_definition.getMetaDataEntry("preferred_material")
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"):
search_criteria["definition"] = machine_definition_id
@ -255,7 +261,12 @@ class ExtruderManager(QObject):
preferred_materials = container_registry.findInstanceContainers(**search_criteria)
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:
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.

View File

@ -2,7 +2,7 @@
# Cura is released under the terms of the AGPLv3 or higher.
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 PyQt5.QtWidgets import QMessageBox
from UM import Util
@ -15,9 +15,7 @@ from UM.Message import Message
from UM.Settings.ContainerRegistry import ContainerRegistry
from UM.Settings.ContainerStack import ContainerStack
from UM.Settings.InstanceContainer import InstanceContainer
from UM.Settings.SettingDefinition import SettingDefinition
from UM.Settings.SettingFunction import SettingFunction
from UM.Settings.Validator import ValidatorState
from UM.Signal import postponeSignals
from cura.QualityManager import QualityManager
@ -27,6 +25,11 @@ from cura.Settings.ExtruderManager import ExtruderManager
from UM.i18n import i18nCatalog
catalog = i18nCatalog("cura")
from typing import TYPE_CHECKING, Optional
if TYPE_CHECKING:
from UM.Settings.DefinitionContainer import DefinitionContainer
import os
class MachineManager(QObject):
@ -83,6 +86,11 @@ class MachineManager(QObject):
self._material_incompatible_message = Message(catalog.i18nc("@info:status",
"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)
activeMaterialChanged = pyqtSignal()
activeVariantChanged = pyqtSignal()
@ -306,33 +314,7 @@ class MachineManager(QObject):
self.activeStackValueChanged.emit()
elif property_name == "validationState":
if not self._stacks_have_errors:
# 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()
self._error_check_timer.start()
@pyqtSlot(str)
def setActiveMachine(self, stack_id: str) -> None:
@ -350,10 +332,11 @@ class MachineManager(QObject):
name = self._createUniqueName("machine", "", name, definition.getName())
new_global_stack = ContainerStack(name)
new_global_stack.addMetaDataEntry("type", "machine")
new_global_stack.addContainer(definition)
container_registry.addContainer(new_global_stack)
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)
current_settings_instance_container = InstanceContainer(name + "_current_settings")
@ -362,7 +345,7 @@ class MachineManager(QObject):
current_settings_instance_container.setDefinition(definitions[0])
container_registry.addContainer(current_settings_instance_container)
new_global_stack.addContainer(definition)
if variant_instance_container:
new_global_stack.addContainer(variant_instance_container)
if material_instance_container:
@ -781,7 +764,7 @@ class MachineManager(QObject):
if old_material:
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:
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):
return MachineManager()
def _updateVariantContainer(self, definition):
def _updateVariantContainer(self, definition: "DefinitionContainer"):
if not definition.getMetaDataEntry("has_variants"):
return self._empty_variant_container
machine_definition_id = Application.getInstance().getMachineManager().getQualityDefinitionId(definition)
@ -1131,11 +1114,12 @@ class MachineManager(QObject):
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"):
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"):
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.")
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()
search_criteria = { "type": "quality" }

View File

@ -32,9 +32,9 @@ class ProfilesModel(InstanceContainersModel):
## Get the singleton instance for this class.
@classmethod
def getInstance(cls):
def getInstance(cls) -> "ProfilesModel":
# Note: Explicit use of class name to prevent issues with inheritance.
if ProfilesModel.__instance is None:
if not ProfilesModel.__instance:
ProfilesModel.__instance = cls()
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")
width: 550
minimumWidth: 550
maximumWidth: 550
width: 550 * Screen.devicePixelRatio
minimumWidth: 550 * Screen.devicePixelRatio
maximumWidth: minimumWidth
height: 400
minimumHeight: 400
maximumHeight: 400
property int comboboxHeight: 15
property int spacerHeight: 10
height: 400 * Screen.devicePixelRatio
minimumHeight: 400 * Screen.devicePixelRatio
maximumHeight: minimumHeight
property int comboboxHeight: 15 * Screen.devicePixelRatio
property int spacerHeight: 10 * Screen.devicePixelRatio
onClosing: manager.notifyClosed()
onVisibleChanged:
{
@ -33,20 +33,17 @@ UM.Dialog
}
Item
{
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 20
anchors.bottomMargin: 20
anchors.leftMargin:20
anchors.rightMargin: 20
anchors.fill: parent
anchors.margins: 20 * Screen.devicePixelRatio
UM.I18nCatalog
{
id: catalog;
name: "cura";
id: catalog
name: "cura"
}
SystemPalette
{
id: palette
}
ListModel
@ -70,12 +67,12 @@ UM.Dialog
{
id: titleLabel
text: catalog.i18nc("@action:title", "Summary - Cura Project")
font.pixelSize: 22
font.pointSize: 18
}
Rectangle
{
id: separator
color: "black"
color: palette.text
width: parent.width
height: 1
}
@ -93,7 +90,7 @@ UM.Dialog
{
text: catalog.i18nc("@action:label", "Printer settings")
font.bold: true
width: parent.width /3
width: parent.width / 3
}
Item
{
@ -360,7 +357,7 @@ UM.Dialog
height: width
source: UM.Theme.getIcon("notice")
color: "black"
color: palette.text
}
Label
@ -392,4 +389,4 @@ UM.Dialog
anchors.right: parent.right
}
}
}
}

View File

@ -1,9 +1,16 @@
# Copyright (c) 2015 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.
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 UM.i18n import i18nCatalog
from UM.Platform import Platform
catalog = i18nCatalog("cura")
@ -14,30 +21,36 @@ def getMetaData() -> Dict:
workspace_extension = "3mf"
else:
workspace_extension = "curaproject.3mf"
return {
metaData = {
"plugin": {
"name": catalog.i18nc("@label", "3MF Reader"),
"author": "Ultimaker",
"version": "1.0",
"description": catalog.i18nc("@info:whatsthis", "Provides support for reading 3MF files."),
"api": 3
},
"mesh_reader": [
}
}
if "3MFReader.ThreeMFReader" in sys.modules:
metaData["mesh_reader"] = [
{
"extension": "3mf",
"description": catalog.i18nc("@item:inlistbox", "3MF File")
}
],
"workspace_reader":
[
]
metaData["workspace_reader"] = [
{
"extension": workspace_extension,
"description": catalog.i18nc("@item:inlistbox", "3MF File")
}
]
}
return metaData
def register(app):
return {"mesh_reader": ThreeMFReader.ThreeMFReader(),
"workspace_reader": ThreeMFWorkspaceReader.ThreeMFWorkspaceReader()}
if "3MFReader.ThreeMFReader" in sys.modules:
return {"mesh_reader": ThreeMFReader.ThreeMFReader(),
"workspace_reader": ThreeMFWorkspaceReader.ThreeMFWorkspaceReader()}
else:
return {}

View File

@ -1,30 +1,39 @@
# Copyright (c) 2015 Ultimaker B.V.
# 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 . import ThreeMFWorkspaceWriter
from . import ThreeMFWriter
i18n_catalog = i18nCatalog("uranium")
def getMetaData():
return {
metaData = {
"plugin": {
"name": i18n_catalog.i18nc("@label", "3MF Writer"),
"author": "Ultimaker",
"version": "1.0",
"description": i18n_catalog.i18nc("@info:whatsthis", "Provides support for writing 3MF files."),
"api": 3
},
"mesh_writer": {
}
}
if "3MFWriter.ThreeMFWriter" in sys.modules:
metaData["mesh_writer"] = {
"output": [{
"extension": "3mf",
"description": i18n_catalog.i18nc("@item:inlistbox", "3MF file"),
"mime_type": "application/vnd.ms-package.3dmanufacturing-3dmodel+xml",
"mode": ThreeMFWriter.ThreeMFWriter.OutputMode.BinaryMode
}]
},
"workspace_writer": {
}
metaData["workspace_writer"] = {
"output": [{
"extension": "curaproject.3mf",
"description": i18n_catalog.i18nc("@item:inlistbox", "Cura Project 3MF file"),
@ -32,7 +41,12 @@ def getMetaData():
"mode": ThreeMFWorkspaceWriter.ThreeMFWorkspaceWriter.OutputMode.BinaryMode
}]
}
}
return metaData
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.
from UM.Application import Application
from UM.Backend import Backend
from UM.Job import Job
from UM.Logger import Logger
from UM.Math.AxisAlignedBox import AxisAlignedBox
@ -37,7 +38,6 @@ class GCodeReader(MeshReader):
self._message = None
self._layer_number = 0
self._extruder_number = 0
self._layer_type = LayerPolygon.Inset0Type
self._clearValues()
self._scene_node = None
self._position = namedtuple('Position', ['x', 'y', 'z', 'e'])
@ -153,7 +153,6 @@ class GCodeReader(MeshReader):
self._previous_z = z
else:
path.append([x, y, z, LayerPolygon.MoveCombingType])
return self._position(x, y, z, e)
# G0 and G1 should be handled exactly the same.
@ -172,7 +171,6 @@ class GCodeReader(MeshReader):
def _gCode92(self, position, params, path):
if params.e is not None:
position.e[self._extruder_number] = params.e
return self._position(
params.x if params.x is not None else position.x,
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):
func = getattr(self, "_gCode%s" % G, None)
line = line.split(";", 1)[0] # Remove comments (if any)
if func is not None:
s = line.upper().split(" ")
x, y, z, e = None, None, None, None
@ -307,11 +306,11 @@ class GCodeReader(MeshReader):
G = self._getInt(line, "G")
if G is not None:
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
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])):
current_path.clear()
if not self._is_layers_in_file:
self._layer_number += 1
@ -343,6 +342,8 @@ class GCodeReader(MeshReader):
gcode_list_decorator.setGCodeList(gcode_list)
scene_node.addDecorator(gcode_list_decorator)
Application.getInstance().getController().getScene().gcode_list = gcode_list
Logger.log("d", "Finished parsing %s" % file_name)
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)
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

View File

@ -1,6 +1,8 @@
# Copyright (c) 2015 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.
import sys
from UM.PluginRegistry import PluginRegistry
from UM.View.View import View
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
@ -253,8 +255,17 @@ class LayerView(View):
if not layer_data:
continue
if new_max_layers < len(layer_data.getLayers()):
new_max_layers = len(layer_data.getLayers()) - 1
min_layer_number = sys.maxsize
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:
self._max_layers = new_max_layers

View File

@ -351,7 +351,7 @@ Item
property bool roundValues: true
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()
{

View File

@ -200,7 +200,7 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice):
def _onAuthenticationRequired(self, reply, authenticator):
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.setPassword(self._authentication_key)
else:
@ -625,7 +625,7 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice):
if print_information.materialLengths:
# 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):
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"] == "":
Logger.log("e", "No cartridge loaded in slot %s, unable to start print", index + 1)
self._error_message = Message(
@ -643,13 +643,13 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice):
for index in range(0, self._num_extruders):
# 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"]
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)
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.
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"})
core_name = self._json_printer_state["heads"][0]["extruders"][index]["hotend"]["id"]
if variant:
@ -716,7 +716,8 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice):
## Start requesting data from printer
def connect(self):
self.close() # Ensure that previous connection (if any) is killed.
if self.isConnected():
self.close() # Close previous connection
self._createNetworkManager()
@ -796,19 +797,41 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice):
Logger.log("d", "Started sending g-code to remote printer.")
self._compressing_print = True
## Mash the data into single string
max_chars_per_line = 1024 * 1024 / 4 # 1 / 4 MB
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:
if not self._compressing_print:
self._progress_message.hide()
return # Stop trying to zip, abort was called.
if self._use_gzip:
byte_array_file_data += gzip.compress(line.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()
batched_line += line
# if the gcode was read from a gcode file, self._gcode will be a list of all lines in that file.
# Compressing line by line in this case is extremely slow, so we need to batch them.
if len(batched_line) < max_chars_per_line:
continue
byte_array_file_data += _compress_data_and_notify_qt(batched_line)
batched_line = ""
else:
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:
file_name = "%s.gcode.gz" % Application.getInstance().getPrintInformation().jobName
else:
@ -858,7 +881,10 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice):
url = QUrl("http://" + self._address + self._api_prefix + "auth/request")
request = QNetworkRequest(url)
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.setAuthenticationState(AuthState.AuthenticationRequested)
## Send all material profiles to the printer.
def sendMaterialProfiles(self):
@ -1054,7 +1080,6 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice):
except json.decoder.JSONDecodeError:
Logger.log("w", "Received an invalid authentication request reply from printer: Not valid JSON.")
return
self.setAuthenticationState(AuthState.AuthenticationRequested)
global_container_stack = Application.getInstance().getGlobalContainerStack()
if global_container_stack: # Remove any old data.
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_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.
self._checkAuthentication()
@ -1142,4 +1167,4 @@ class NetworkPrinterOutputDevice(PrinterOutputDevice):
result = self._authentication_key[-5:]
result = "********" + 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:
if key == active_machine.getMetaDataEntry("um_network_key"):
Logger.log("d", "Connecting [%s]..." % key)
self._printers[key].connect()
self._printers[key].connectionStateChanged.connect(self._onPrinterConnectionStateChanged)
if not self._printers[key].isConnected():
Logger.log("d", "Connecting [%s]..." % key)
self._printers[key].connect()
self._printers[key].connectionStateChanged.connect(self._onPrinterConnectionStateChanged)
else:
if self._printers[key].isConnected():
Logger.log("d", "Closing connection [%s]..." % key)
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.
def addPrinter(self, name, address, properties):
@ -181,9 +183,9 @@ class NetworkPrinterOutputDevicePlugin(OutputDevicePlugin):
printer = self._printers.pop(name, None)
if printer:
if printer.isConnected():
printer.disconnect()
printer.connectionStateChanged.disconnect(self._onPrinterConnectionStateChanged)
Logger.log("d", "removePrinter, disconnecting [%s]..." % name)
printer.disconnect()
self.printerListChanged.emit()
## 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
catalog = i18nCatalog("cura")
class USBPrinterOutputDevice(PrinterOutputDevice):
class USBPrinterOutputDevice(PrinterOutputDevice):
def __init__(self, serial_port):
super().__init__(serial_port)
self.setName(catalog.i18nc("@item:inmenu", "USB printing"))
@ -148,6 +148,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
## Start a print based on a g-code.
# \param gcode_list List with gcode (strings).
def printGCode(self, gcode_list):
Logger.log("d", "Started printing g-code")
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.show()
@ -183,6 +184,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
## Private function (threaded) that actually uploads the firmware.
def _updateFirmware(self):
Logger.log("d", "Attempting to update firmware")
self._error_code = 0
self.setProgress(0, 100)
self._firmware_update_finished = False
@ -202,6 +204,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
try:
programmer.connect(self._serial_port)
except Exception:
programmer.close()
pass
# 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.
self._serial = programmer.leaveISP()
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)))
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)
# If the programmer connected, we know its an atmega based version.
@ -533,6 +538,7 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
self._sendNextGcodeLine()
elif b"resend" in line.lower() or b"rs" in line: # Because a resend can be asked with "resend" and "rs"
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])
except:
if b"rs" in line:
@ -559,15 +565,20 @@ class USBPrinterOutputDevice(PrinterOutputDevice):
if ";" in line:
line = line[:line.find(";")]
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:
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:
z = float(re.search("Z([0-9\.]*)", line).group(1))
if self._current_z != z:
self._current_z = z
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)
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):
Logger.log("i", "Cancelling pre-heating of the bed.")
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.connectionStateChanged.emit()
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)
def connectedPrinterList(self):

View File

@ -118,6 +118,7 @@ class XmlMaterialProfile(InstanceContainer):
metadata.pop("variant", "")
metadata.pop("type", "")
metadata.pop("base_file", "")
metadata.pop("approximate_diameter", "")
## Begin Name Block
builder.start("name")
@ -437,6 +438,7 @@ class XmlMaterialProfile(InstanceContainer):
Logger.log("d", "Unsupported material setting %s", key)
self._cached_values = global_setting_values
meta_data["approximate_diameter"] = round(diameter)
meta_data["compatible"] = global_compatibility
self.setMetaData(meta_data)
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.",
"unit": "mm",
"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",
"default_value": 0.4,
"type": "float",
@ -649,7 +649,7 @@
"description": "Width of a single wall line.",
"unit": "mm",
"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",
"value": "line_width",
"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.",
"unit": "mm",
"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",
"default_value": 0.4,
"value": "wall_line_width",
@ -676,7 +676,7 @@
"description": "Width of a single wall line for all wall lines except the outermost one.",
"unit": "mm",
"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",
"default_value": 0.4,
"value": "wall_line_width",
@ -691,7 +691,7 @@
"description": "Width of a single top/bottom line.",
"unit": "mm",
"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",
"default_value": 0.4,
"type": "float",
@ -704,7 +704,7 @@
"description": "Width of a single infill line.",
"unit": "mm",
"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",
"default_value": 0.4,
"type": "float",
@ -718,7 +718,7 @@
"description": "Width of a single skirt or brim line.",
"unit": "mm",
"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",
"default_value": 0.4,
"type": "float",
@ -733,7 +733,7 @@
"description": "Width of a single support structure line.",
"unit": "mm",
"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",
"default_value": 0.4,
"type": "float",
@ -750,7 +750,7 @@
"unit": "mm",
"default_value": 0.4,
"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",
"type": "float",
"enabled": "support_enable and support_interface_enable",
@ -769,7 +769,7 @@
"default_value": 0.4,
"value": "line_width",
"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",
"settable_per_mesh": false,
"settable_per_extruder": true
@ -845,7 +845,7 @@
"unit": "mm",
"default_value": 0.8,
"minimum_value": "0",
"minimum_value_warning": "3 * resolveOrValue('layer_height')",
"minimum_value_warning": "0.2 + resolveOrValue('layer_height')",
"maximum_value": "machine_height",
"type": "float",
"value": "top_bottom_thickness",
@ -860,7 +860,7 @@
"minimum_value": "0",
"maximum_value_warning": "100",
"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))",
"settable_per_mesh": true
}
@ -873,7 +873,7 @@
"unit": "mm",
"default_value": 0.6,
"minimum_value": "0",
"minimum_value_warning": "3 * resolveOrValue('layer_height')",
"minimum_value_warning": "0.2 + resolveOrValue('layer_height')",
"type": "float",
"value": "top_bottom_thickness",
"maximum_value": "machine_height",
@ -885,7 +885,7 @@
"label": "Bottom Layers",
"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_warning": "4",
"minimum_value_warning": "2",
"default_value": 6,
"type": "int",
"value": "999999 if infill_sparse_density == 100 else math.ceil(round(bottom_thickness / resolveOrValue('layer_height'), 4))",
@ -1121,6 +1121,65 @@
"type": "[int]",
"default_value": "[ ]",
"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
},
"sub_div_rad_mult":
@ -1229,9 +1288,9 @@
"default_value": 0.1,
"minimum_value": "resolveOrValue('layer_height')",
"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')",
"enabled": "infill_sparse_density > 0",
"enabled": "infill_sparse_density > 0 and not spaghetti_infill_enabled",
"settable_per_mesh": true
},
"gradual_infill_steps":
@ -1242,8 +1301,8 @@
"type": "int",
"minimum_value": "0",
"maximum_value_warning": "4",
"maximum_value": "(20 - math.log(infill_line_distance) / math.log(2)) if infill_line_distance > 0 else 0",
"enabled": "infill_sparse_density > 0 and infill_pattern != 'cubicsubdiv'",
"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' and not spaghetti_infill_enabled",
"settable_per_mesh": true
},
"gradual_infill_step_height":
@ -2273,7 +2332,6 @@
"unit": "mm/s",
"type": "float",
"minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50",
"default_value": 20,
"enabled": "resolveOrValue('jerk_enabled')",
@ -2287,7 +2345,6 @@
"unit": "mm/s",
"type": "float",
"minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50",
"default_value": 20,
"value": "jerk_print",
@ -2301,7 +2358,6 @@
"unit": "mm/s",
"type": "float",
"minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50",
"default_value": 20,
"value": "jerk_print",
@ -2316,7 +2372,6 @@
"unit": "mm/s",
"type": "float",
"minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50",
"default_value": 20,
"value": "jerk_wall",
@ -2330,7 +2385,6 @@
"unit": "mm/s",
"type": "float",
"minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50",
"default_value": 20,
"value": "jerk_wall",
@ -2346,7 +2400,6 @@
"unit": "mm/s",
"type": "float",
"minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50",
"default_value": 20,
"value": "jerk_print",
@ -2360,7 +2413,6 @@
"unit": "mm/s",
"type": "float",
"minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50",
"default_value": 20,
"value": "jerk_print",
@ -2379,7 +2431,6 @@
"default_value": 20,
"value": "jerk_support",
"minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50",
"enabled": "resolveOrValue('jerk_enabled') and support_enable",
"limit_to_extruder": "support_infill_extruder_nr",
@ -2395,7 +2446,6 @@
"default_value": 20,
"value": "jerk_support",
"minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50",
"enabled": "resolveOrValue('jerk_enabled') and extruderValue(support_interface_extruder_nr, 'support_interface_enable') and support_enable",
"limit_to_extruder": "support_interface_extruder_nr",
@ -2411,7 +2461,6 @@
"unit": "mm/s",
"type": "float",
"minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50",
"default_value": 20,
"value": "jerk_print",
@ -2428,7 +2477,6 @@
"type": "float",
"default_value": 30,
"minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50",
"value": "jerk_print if magic_spiralize else 30",
"enabled": "resolveOrValue('jerk_enabled')",
@ -2443,7 +2491,6 @@
"default_value": 20,
"value": "jerk_print",
"minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50",
"enabled": "resolveOrValue('jerk_enabled')",
"settable_per_mesh": true,
@ -2458,7 +2505,6 @@
"default_value": 20,
"value": "jerk_layer_0",
"minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50",
"enabled": "resolveOrValue('jerk_enabled')",
"settable_per_mesh": true
@ -2472,7 +2518,6 @@
"default_value": 20,
"value": "jerk_layer_0 * jerk_travel / jerk_print",
"minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50",
"enabled": "resolveOrValue('jerk_enabled')",
"settable_per_extruder": true,
@ -2488,7 +2533,6 @@
"type": "float",
"default_value": 20,
"minimum_value": "0.1",
"minimum_value_warning": "5",
"maximum_value_warning": "50",
"value": "jerk_layer_0",
"enabled": "resolveOrValue('jerk_enabled')",
@ -2792,8 +2836,8 @@
{
"support_enable":
{
"label": "Enable Support",
"description": "Enable support structures. These structures support parts of the model with severe overhangs.",
"label": "Generate Support",
"description": "Generate structures to support parts of the model which have overhangs. Without these structures, such parts would collapse during printing.",
"type": "bool",
"default_value": false,
"settable_per_mesh": true,
@ -3080,7 +3124,7 @@
"type": "float",
"default_value": 1,
"minimum_value": "0",
"minimum_value_warning": "3 * resolveOrValue('layer_height')",
"minimum_value_warning": "0.2 + resolveOrValue('layer_height')",
"maximum_value_warning": "10",
"limit_to_extruder": "support_interface_extruder_nr",
"enabled": "extruderValue(support_interface_extruder_nr, 'support_interface_enable') and support_enable",
@ -3095,7 +3139,7 @@
"type": "float",
"default_value": 1,
"minimum_value": "0",
"minimum_value_warning": "3 * resolveOrValue('layer_height')",
"minimum_value_warning": "0.2 + resolveOrValue('layer_height')",
"maximum_value_warning": "10",
"value": "extruderValue(support_interface_extruder_nr, 'support_interface_height')",
"limit_to_extruder": "support_interface_extruder_nr",
@ -3111,7 +3155,7 @@
"default_value": 1,
"value": "extruderValue(support_interface_extruder_nr, 'support_interface_height')",
"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",
"limit_to_extruder": "support_interface_extruder_nr",
"enabled": "extruderValue(support_interface_extruder_nr, 'support_interface_enable') and support_enable",
@ -3496,7 +3540,7 @@
"value": "resolveOrValue('layer_height') * 1.5",
"minimum_value": "0.001",
"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'",
"settable_per_mesh": false,
"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)",
"resolve": "max(extruderValues('prime_tower_wall_thickness'))",
"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",
"enabled": "resolveOrValue('prime_tower_enable')",
"settable_per_mesh": false,
@ -4122,6 +4166,40 @@
"settable_per_meshgroup": 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":
{
"label": "Infill Mesh Order",
@ -4147,6 +4225,18 @@
"settable_per_meshgroup": 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":
{
"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.",
"type": "bool",
"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"
},
"material_bed_temperature": {
"visible": "False"
"enabled": false
},
"material_diameter": {
"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
msgid ""
msgstr ""
"Project-Id-Version: Uranium json setting files\n"
"Report-Msgid-Bugs-To: http://github.com/ultimaker/uranium\n"
"POT-Creation-Date: 2016-12-28 10:51+0000\n"
"PO-Revision-Date: 2017-01-12 15:51+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE\n"
"Language: \n"
"Project-Id-Version: Cura 2.5\n"
"Report-Msgid-Bugs-To: http://github.com/Ultimaker/Cura\n"
"POT-Creation-Date: 2017-03-27 17:27+0000\n"
"PO-Revision-Date: 2017-04-04 11:27+0200\n"
"Last-Translator: Bothof <info@bothof.nl>\n"
"Language-Team: Bothof <info@bothof.nl>\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\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
msgid ""
msgstr ""
"Project-Id-Version: Uranium json setting files\n"
"Report-Msgid-Bugs-To: http://github.com/ultimaker/uranium\n"
"POT-Creation-Date: 2016-12-28 10:51+0000\n"
"PO-Revision-Date: 2017-01-12 15:51+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE\n"
"Language: \n"
"Project-Id-Version: Cura 2.5\n"
"Report-Msgid-Bugs-To: http://github.com/Ultimaker/Cura\n"
"POT-Creation-Date: 2017-03-27 17:27+0000\n"
"PO-Revision-Date: 2017-04-04 11:27+0200\n"
"Last-Translator: Bothof <info@bothof.nl>\n"
"Language-Team: Bothof <info@bothof.nl>\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Uranium json setting files\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"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE\n"

View File

@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Uranium json setting files\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"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE\n"
@ -268,6 +268,18 @@ msgid ""
"extruder is no longer used."
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
msgctxt "machine_nozzle_heat_up_speed label"
msgid "Heat up speed"
@ -856,6 +868,47 @@ msgctxt "top_bottom_pattern option zigzag"
msgid "Zig Zag"
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
msgctxt "wall_0_inset label"
msgid "Outer Wall Inset"
@ -1124,6 +1177,22 @@ msgctxt "infill_pattern option zigzag"
msgid "Zig Zag"
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
msgctxt "sub_div_rad_mult label"
msgid "Cubic Subdivision Radius"
@ -1262,6 +1331,97 @@ msgid ""
"through the surface."
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
msgctxt "material label"
msgid "Material"
@ -1304,8 +1464,7 @@ msgstr ""
#: fdmprinter.def.json
msgctxt "material_print_temperature description"
msgid ""
"The temperature used for printing. Set at 0 to pre-heat the printer manually."
msgid "The temperature used for printing."
msgstr ""
#: fdmprinter.def.json
@ -1376,8 +1535,8 @@ msgstr ""
#: fdmprinter.def.json
msgctxt "material_bed_temperature description"
msgid ""
"The temperature used for the heated build plate. Set at 0 to pre-heat the "
"printer manually."
"The temperature used for the heated build plate. If this is 0, the bed will "
"not heat up for this print."
msgstr ""
#: fdmprinter.def.json
@ -2216,6 +2375,16 @@ msgctxt "retraction_combing option noskin"
msgid "No Skin"
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
msgctxt "travel_avoid_other_parts label"
msgid "Avoid Printed Parts When Traveling"
@ -2671,7 +2840,7 @@ msgctxt "support_z_distance description"
msgid ""
"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 "
"value is rounded down to a multiple of the layer height."
"value is rounded up to a multiple of the layer height."
msgstr ""
#: 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
msgid ""
msgstr ""
"Project-Id-Version: Uranium json setting files\n"
"Report-Msgid-Bugs-To: http://github.com/ultimaker/uranium\n"
"POT-Creation-Date: 2016-12-28 10:51+0000\n"
"PO-Revision-Date: 2017-01-12 15:51+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE\n"
"Language: \n"
"Project-Id-Version: Cura 2.5\n"
"Report-Msgid-Bugs-To: http://github.com/Ultimaker/Cura\n"
"POT-Creation-Date: 2017-03-27 17:27+0000\n"
"PO-Revision-Date: 2017-04-04 11:27+0200\n"
"Last-Translator: Bothof <info@bothof.nl>\n"
"Language-Team: Bothof <info@bothof.nl>\n"
"Language: fi\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\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
msgid ""
msgstr ""
"Project-Id-Version: Uranium json setting files\n"
"Report-Msgid-Bugs-To: http://github.com/ultimaker/uranium\n"
"POT-Creation-Date: 2016-12-28 10:51+0000\n"
"PO-Revision-Date: 2017-01-12 15:51+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE\n"
"Language: \n"
"Project-Id-Version: Cura 2.5\n"
"Report-Msgid-Bugs-To: http://github.com/Ultimaker/Cura\n"
"POT-Creation-Date: 2017-03-27 17:27+0000\n"
"PO-Revision-Date: 2017-04-04 11:27+0200\n"
"Last-Translator: Bothof <info@bothof.nl>\n"
"Language-Team: Bothof <info@bothof.nl>\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\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
msgid ""
msgstr ""
"Project-Id-Version: Uranium json setting files\n"
"Report-Msgid-Bugs-To: http://github.com/ultimaker/uranium\n"
"POT-Creation-Date: 2016-12-28 10:51+0000\n"
"PO-Revision-Date: 2017-01-12 15:51+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE\n"
"Language: \n"
"Project-Id-Version: Cura 2.5\n"
"Report-Msgid-Bugs-To: http://github.com/Ultimaker/Cura\n"
"POT-Creation-Date: 2017-03-27 17:27+0000\n"
"PO-Revision-Date: 2017-04-04 11:27+0200\n"
"Last-Translator: Bothof <info@bothof.nl>\n"
"Language-Team: Bothof <info@bothof.nl>\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\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
msgid ""
msgstr ""
"Project-Id-Version: Uranium json setting files\n"
"Report-Msgid-Bugs-To: http://github.com/ultimaker/uranium\n"
"POT-Creation-Date: 2016-12-28 10:51+0000\n"
"PO-Revision-Date: 2017-01-12 15:51+0100\n"
"Last-Translator: Ruben Dulek <r.dulek@ultimaker.com>\n"
"Language-Team: Ultimaker\n"
"Language: \n"
"Project-Id-Version: Cura 2.5\n"
"Report-Msgid-Bugs-To: http://github.com/Ultimaker/Cura\n"
"POT-Creation-Date: 2017-03-27 17:27+0000\n"
"PO-Revision-Date: 2017-04-04 11:27+0200\n"
"Last-Translator: Bothof <info@bothof.nl>\n"
"Language-Team: Bothof <info@bothof.nl>\n"
"Language: nl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\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 ""
"Project-Id-Version: Uranium json setting files\n"
"Report-Msgid-Bugs-To: http://github.com/ultimaker/uranium\n"
"POT-Creation-Date: 2016-12-28 10:51+0000\n"
"PO-Revision-Date: 2016-01-25 05:05-0300\n"
"POT-Creation-Date: 2017-03-27 17:27+0000\n"
"PO-Revision-Date: 2017-04-10 09:05-0300\n"
"Last-Translator: Cláudio Sampaio <patola@makerlinux.com.br>\n"
"Language-Team: LANGUAGE\n"
"Language: ptbr\n"
@ -70,12 +70,8 @@ msgstr "Posição de Início do Extrusor Absoluta"
#: fdmextruder.def.json
msgctxt "machine_extruder_start_pos_abs description"
msgid ""
"Make the extruder starting position absolute rather than relative to the "
"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."
msgid "Make the extruder starting position absolute rather than relative to the 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
msgctxt "machine_extruder_start_pos_x label"
@ -114,12 +110,8 @@ msgstr "Posição Final do Extrusor Absoluta"
#: fdmextruder.def.json
msgctxt "machine_extruder_end_pos_abs description"
msgid ""
"Make the extruder ending position absolute rather than relative to the last-"
"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."
msgid "Make the extruder ending position absolute rather than relative to the last-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
msgctxt "machine_extruder_end_pos_x label"
@ -148,11 +140,8 @@ msgstr "Posição Z de Purga do Extrusor"
#: fdmextruder.def.json
msgctxt "extruder_prime_pos_z description"
msgid ""
"The Z coordinate of the position where the nozzle primes at the start of "
"printing."
msgstr ""
"A coordenada Z da posição onde o bico faz a purga no início da impressão."
msgid "The Z coordinate of the position where the nozzle primes at the start of printing."
msgstr "A coordenada Z da posição onde o bico faz a purga no início da impressão."
#: fdmextruder.def.json
msgctxt "platform_adhesion label"
@ -171,11 +160,8 @@ msgstr "Posição X de Purga do Extrusor"
#: fdmextruder.def.json
msgctxt "extruder_prime_pos_x description"
msgid ""
"The X coordinate of the position where the nozzle primes at the start of "
"printing."
msgstr ""
"A coordenada X da posição onde o bico faz a purga no início da impressão."
msgid "The X coordinate of the position where the nozzle primes at the start of printing."
msgstr "A coordenada X da posição onde o bico faz a purga no início da impressão."
#: fdmextruder.def.json
msgctxt "extruder_prime_pos_y label"
@ -184,8 +170,5 @@ msgstr "Posição Y de Purga do Extrusor"
#: fdmextruder.def.json
msgctxt "extruder_prime_pos_y description"
msgid ""
"The Y coordinate of the position where the nozzle primes at the start of "
"printing."
msgstr ""
"A coordenada Y da posição onde o bico faz a purga no início da impressão."
msgid "The Y coordinate of the position where the nozzle primes at the start of 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
msgid ""
msgstr ""
"Project-Id-Version: Uranium json setting files\n"
"Report-Msgid-Bugs-To: http://github.com/ultimaker/uranium\n"
"POT-Creation-Date: 2016-12-28 10:51+0000\n"
"PO-Revision-Date: 2017-01-12 15:51+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE\n"
"Language: \n"
"Project-Id-Version: Cura 2.5\n"
"Report-Msgid-Bugs-To: http://github.com/Ultimaker/Cura\n"
"POT-Creation-Date: 2017-03-27 17:27+0000\n"
"PO-Revision-Date: 2017-04-04 11:27+0200\n"
"Last-Translator: Bothof <info@bothof.nl>\n"
"Language-Team: Bothof <info@bothof.nl>\n"
"Language: tr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\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 deleteAll: deleteAllAction;
property alias reloadAll: reloadAllAction;
property alias arrangeAll: arrangeAllAction;
property alias arrangeSelection: arrangeSelectionAction;
property alias resetAllTranslation: resetAllTranslationAction;
property alias resetAll: resetAllAction;
@ -183,7 +185,7 @@ Item
enabled: UM.Controller.toolsEnabled;
iconName: "edit-delete";
shortcut: StandardKey.Delete;
onTriggered: Printer.deleteSelection();
onTriggered: CuraApplication.deleteSelection();
}
Action
@ -207,7 +209,7 @@ Item
enabled: UM.Scene.numObjectsSelected > 1 ? true: false
iconName: "object-group"
shortcut: "Ctrl+G";
onTriggered: Printer.groupSelected();
onTriggered: CuraApplication.groupSelected();
}
Action
@ -217,7 +219,7 @@ Item
enabled: UM.Scene.isGroupSelected
iconName: "object-ungroup"
shortcut: "Ctrl+Shift+G";
onTriggered: Printer.ungroupSelected();
onTriggered: CuraApplication.ungroupSelected();
}
Action
@ -227,7 +229,7 @@ Item
enabled: UM.Scene.numObjectsSelected > 1 ? true: false
iconName: "merge";
shortcut: "Ctrl+Alt+G";
onTriggered: Printer.mergeSelected();
onTriggered: CuraApplication.mergeSelected();
}
Action
@ -244,7 +246,7 @@ Item
enabled: UM.Controller.toolsEnabled;
iconName: "edit-select-all";
shortcut: "Ctrl+A";
onTriggered: Printer.selectAll();
onTriggered: CuraApplication.selectAll();
}
Action
@ -254,7 +256,7 @@ Item
enabled: UM.Controller.toolsEnabled;
iconName: "edit-delete";
shortcut: "Ctrl+D";
onTriggered: Printer.deleteAll();
onTriggered: CuraApplication.deleteAll();
}
Action
@ -263,21 +265,36 @@ Item
text: catalog.i18nc("@action:inmenu menubar:file","Re&load All Models");
iconName: "document-revert";
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
{
id: resetAllTranslationAction;
text: catalog.i18nc("@action:inmenu menubar:edit","Reset All Model Positions");
onTriggered: Printer.resetAllTranslation();
onTriggered: CuraApplication.resetAllTranslation();
}
Action
{
id: resetAllAction;
text: catalog.i18nc("@action:inmenu menubar:edit","Reset All Model &Transformations");
onTriggered: Printer.resetAll();
onTriggered: CuraApplication.resetAll();
}
Action

View File

@ -6,6 +6,7 @@ import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.1
import QtQuick.Window 2.1
import UM 1.3 as UM
import Cura 1.0 as Cura
@ -17,13 +18,13 @@ UM.Dialog
id: base
title: catalog.i18nc("@title:window", "Open project file")
width: 420
height: 140
width: 450 * Screen.devicePixelRatio
height: 150 * Screen.devicePixelRatio
maximumHeight: height
maximumWidth: width
minimumHeight: height
minimumWidth: width
minimumHeight: maximumHeight
minimumWidth: maximumWidth
modality: UM.Application.platform == "linux" ? Qt.NonModal : Qt.WindowModal;
@ -40,7 +41,9 @@ UM.Dialog
function loadModelFiles(fileUrls)
{
for (var i in fileUrls)
Printer.readLocalFile(fileUrls[i]);
{
CuraApplication.readLocalFile(fileUrls[i]);
}
var meshName = backgroundItem.getMeshName(fileUrls[0].toString());
backgroundItem.hasMesh(decodeURIComponent(meshName));
@ -58,15 +61,16 @@ UM.Dialog
Column
{
anchors.fill: parent
anchors.margins: UM.Theme.getSize("default_margin").width
anchors.left: parent.left
anchors.right: parent.right
spacing: UM.Theme.getSize("default_margin").width
anchors.leftMargin: 20 * Screen.devicePixelRatio
anchors.rightMargin: 20 * Screen.devicePixelRatio
anchors.bottomMargin: 20 * Screen.devicePixelRatio
spacing: 10 * Screen.devicePixelRatio
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?")
anchors.margins: UM.Theme.getSize("default_margin").width
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.left: parent.left
anchors.right: parent.right
font: UM.Theme.getFont("default")
wrapMode: Text.WordWrap
}
@ -75,7 +79,6 @@ UM.Dialog
{
id: rememberChoiceCheckBox
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"
}
@ -91,7 +94,7 @@ UM.Dialog
id: openAsProjectButton
text: catalog.i18nc("@action:button", "Open as project");
anchors.right: importModelsButton.left
anchors.rightMargin: UM.Theme.getSize("default_margin").width
anchors.rightMargin: UM.Theme.getSize("default_margin").width * Screen.devicePixelRatio
isDefault: true
onClicked:
{

View File

@ -21,7 +21,7 @@ UM.MainWindow
property bool monitoringPrint: false
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.
//
// Currently, there is no way to define shortcuts as "Application Shortcut".
@ -131,6 +131,7 @@ UM.MainWindow
MenuItem { action: Cura.Actions.redo; }
MenuSeparator { }
MenuItem { action: Cura.Actions.selectAll; }
MenuItem { action: Cura.Actions.arrangeAll; }
MenuItem { action: Cura.Actions.deleteSelection; }
MenuItem { action: Cura.Actions.deleteAll; }
MenuItem { action: Cura.Actions.resetAllTranslation; }
@ -259,7 +260,7 @@ UM.MainWindow
{
if (drop.urls.length > 0)
{
handleOpenFileUrls(drop.urls);
openDialog.handleOpenFileUrls(drop.urls);
}
}
}
@ -502,7 +503,7 @@ UM.MainWindow
icon: StandardIcon.Question
onYes:
{
Printer.deleteAll();
CuraApplication.deleteAll();
Cura.Actions.resetProfile.trigger();
}
}
@ -603,6 +604,7 @@ UM.MainWindow
MenuItem { action: Cura.Actions.multiplyObject; }
MenuSeparator { }
MenuItem { action: Cura.Actions.selectAll; }
MenuItem { action: Cura.Actions.arrangeAll; }
MenuItem { action: Cura.Actions.deleteAll; }
MenuItem { action: Cura.Actions.reloadAll; }
MenuItem { action: Cura.Actions.resetAllTranslation; }
@ -619,7 +621,7 @@ UM.MainWindow
{
if(objectContextMenu.objectId != 0)
{
Printer.deleteObject(objectContextMenu.objectId);
CuraApplication.deleteObject(objectContextMenu.objectId);
objectContextMenu.objectId = 0;
}
}
@ -652,7 +654,7 @@ UM.MainWindow
{
if(objectContextMenu.objectId != 0)
{
Printer.centerObject(objectContextMenu.objectId);
CuraApplication.centerObject(objectContextMenu.objectId);
objectContextMenu.objectId = 0;
}
}
@ -663,6 +665,7 @@ UM.MainWindow
{
id: contextMenu;
MenuItem { action: Cura.Actions.selectAll; }
MenuItem { action: Cura.Actions.arrangeAll; }
MenuItem { action: Cura.Actions.deleteAll; }
MenuItem { action: Cura.Actions.reloadAll; }
MenuItem { action: Cura.Actions.resetAllTranslation; }
@ -722,85 +725,85 @@ UM.MainWindow
handleOpenFileUrls(fileUrls);
}
}
// 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,
// 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.
function handleOpenFileUrls(fileUrls)
{
// look for valid project files
var projectFileUrlList = [];
var hasGcode = false;
var nonGcodeFileList = [];
for (var i in fileUrls)
// 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,
// 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.
function handleOpenFileUrls(fileUrlList)
{
var endsWithG = /\.g$/;
var endsWithGcode = /\.gcode$/;
if (endsWithG.test(fileUrls[i]) || endsWithGcode.test(fileUrls[i]))
// look for valid project files
var projectFileUrlList = [];
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]);
}
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")
else
{
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();
handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrlList, projectFileUrlList);
}
}
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:
{
handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrls, projectFileUrlList);
openDialog.handleOpenFiles(selectedMultipleFiles, hasProjectFile, fileUrls, projectFileUrlList);
}
}
@ -898,14 +901,14 @@ UM.MainWindow
{
id: messageDialog
modality: Qt.ApplicationModal
onAccepted: Printer.messageBoxClosed(clickedButton)
onApply: Printer.messageBoxClosed(clickedButton)
onDiscard: Printer.messageBoxClosed(clickedButton)
onHelp: Printer.messageBoxClosed(clickedButton)
onNo: Printer.messageBoxClosed(clickedButton)
onRejected: Printer.messageBoxClosed(clickedButton)
onReset: Printer.messageBoxClosed(clickedButton)
onYes: Printer.messageBoxClosed(clickedButton)
onAccepted: CuraApplication.messageBoxClosed(clickedButton)
onApply: CuraApplication.messageBoxClosed(clickedButton)
onDiscard: CuraApplication.messageBoxClosed(clickedButton)
onHelp: CuraApplication.messageBoxClosed(clickedButton)
onNo: CuraApplication.messageBoxClosed(clickedButton)
onRejected: CuraApplication.messageBoxClosed(clickedButton)
onReset: CuraApplication.messageBoxClosed(clickedButton)
onYes: CuraApplication.messageBoxClosed(clickedButton)
}
Connections

View File

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

View File

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

View File

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

View File

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

View File

@ -13,11 +13,11 @@ Menu
title: catalog.i18nc("@title:menu menubar:file", "Open &Recent")
iconName: "document-open-recent";
enabled: Printer.recentFiles.length > 0;
enabled: CuraApplication.recentFiles.length > 0;
Instantiator
{
model: Printer.recentFiles
model: CuraApplication.recentFiles
MenuItem
{
text:
@ -36,11 +36,13 @@ Menu
var choice = UM.Preferences.getValue("cura/choice_on_open_project");
if (choice == "open_as_project")
{
toOpenAsProject = true;
else if (choice == "open_as_model")
}else if (choice == "open_as_model"){
toOpenAsModel = true;
else
}else{
toShowDialog = true;
}
}
else {
toOpenAsModel = true;
@ -54,9 +56,13 @@ Menu
// open file in the prefered way
if (toOpenAsProject)
{
UM.WorkspaceFileHandler.readLocalFile(modelData);
}
else if (toOpenAsModel)
Printer.readLocalFile(modelData);
{
CuraApplication.readLocalFile(modelData);
}
var meshName = backgroundItem.getMeshName(modelData.toString())
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 string fileBaseName
property string statusText:
@ -205,8 +205,8 @@ Item
onAdditionalComponentsChanged:
{
if(areaId == "monitorButtons") {
for (var component in Printer.additionalComponents["monitorButtons"]) {
Printer.additionalComponents["monitorButtons"][component].parent = additionalComponentsRow
for (var component in CuraApplication.additionalComponents["monitorButtons"]) {
CuraApplication.additionalComponents["monitorButtons"][component].parent = additionalComponentsRow
}
}
}

View File

@ -20,7 +20,7 @@ UM.Dialog
height: minimumHeight
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" }

View File

@ -6,6 +6,7 @@ import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.1
import QtQuick.Window 2.1
import UM 1.3 as UM
import Cura 1.0 as Cura
@ -16,8 +17,8 @@ UM.Dialog
id: base
title: catalog.i18nc("@title:window", "Open file(s)")
width: 420
height: 170
width: 420 * Screen.devicePixelRatio
height: 170 * Screen.devicePixelRatio
maximumHeight: height
maximumWidth: width
@ -40,7 +41,9 @@ UM.Dialog
function loadModelFiles(fileUrls)
{
for (var i in fileUrls)
Printer.readLocalFile(fileUrls[i]);
{
CuraApplication.readLocalFile(fileUrls[i]);
}
var meshName = backgroundItem.getMeshName(fileUrls[0].toString());
backgroundItem.hasMesh(decodeURIComponent(meshName));
@ -49,15 +52,18 @@ UM.Dialog
Column
{
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.right: parent.right
spacing: UM.Theme.getSize("default_margin").width
spacing: 10 * Screen.devicePixelRatio
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?")
anchors.margins: UM.Theme.getSize("default_margin").width
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.left: parent.left
anchors.right: parent.right
font: UM.Theme.getFont("default")
wrapMode: Text.WordWrap
}
@ -80,7 +86,6 @@ UM.Dialog
id: cancelButton
text: catalog.i18nc("@action:button", "Cancel");
anchors.right: importAllAsModelsButton.left
anchors.rightMargin: UM.Theme.getSize("default_margin").width
onClicked:
{
// cancel

View File

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

View File

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

View File

@ -27,7 +27,7 @@ Column
height: childrenRect.height + UM.Theme.getSize("default_margin").height * 2
color: UM.Theme.getColor("setting_category")
Label
Text
{
id: connectedPrinterNameLabel
text: connectedPrinter != null ? connectedPrinter.name : catalog.i18nc("@info:status", "No printer connected")
@ -37,7 +37,7 @@ Column
anchors.top: parent.top
anchors.margins: UM.Theme.getSize("default_margin").width
}
Label
Text
{
id: connectedPrinterAddressLabel
text: (connectedPrinter != null && connectedPrinter.address != null) ? connectedPrinter.address : ""
@ -47,7 +47,7 @@ Column
anchors.right: parent.right
anchors.margins: UM.Theme.getSize("default_margin").width
}
Label
Text
{
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")
@ -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
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")
color: UM.Theme.getColor("text")
@ -94,7 +94,7 @@ Column
anchors.top: parent.top
anchors.margins: UM.Theme.getSize("default_margin").width
}
Label //Temperature indication.
Text //Temperature indication.
{
id: extruderTemperature
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
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
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
visible: machineHeatedBed.properties.value == "True"
Label //Build plate label.
Text //Build plate label.
{
text: catalog.i18nc("@label", "Build plate")
font: UM.Theme.getFont("default")
@ -253,7 +253,7 @@ Column
anchors.top: parent.top
anchors.margins: UM.Theme.getSize("default_margin").width
}
Label //Target temperature.
Text //Target temperature.
{
id: bedTargetTemperature
text: connectedPrinter != null ? connectedPrinter.targetBedTemperature + "°C" : ""
@ -285,7 +285,7 @@ Column
}
}
}
Label //Current temperature.
Text //Current temperature.
{
id: bedCurrentTemperature
text: connectedPrinter != null ? connectedPrinter.bedTemperature + "°C" : ""
@ -353,7 +353,7 @@ Column
color: UM.Theme.getColor("setting_control_highlight")
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"
color: UM.Theme.getColor("setting_unit")
@ -452,7 +452,7 @@ Column
}
}
}
Label
Text
{
id: preheatCountdown
text: connectedPrinter != null ? connectedPrinter.preheatBedRemainingTime : ""
@ -546,7 +546,7 @@ Column
}
}
Label
Text
{
id: actualLabel
anchors.centerIn: parent
@ -662,7 +662,7 @@ Column
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_margin").width
Label
Text
{
width: parent.width * 0.4
anchors.verticalCenter: parent.verticalCenter
@ -671,7 +671,7 @@ Column
font: UM.Theme.getFont("default")
elide: Text.ElideRight
}
Label
Text
{
width: parent.width * 0.6
anchors.verticalCenter: parent.verticalCenter
@ -692,7 +692,7 @@ Column
width: base.width
height: UM.Theme.getSize("section").height
Label
Text
{
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left

View File

@ -16,7 +16,7 @@ Item {
property int backendState: UM.Backend.state;
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 string fileBaseName
@ -44,7 +44,7 @@ Item {
}
}
Label {
Text {
id: statusLabel
width: parent.width - 2 * UM.Theme.getSize("default_margin").width
anchors.top: parent.top
@ -110,8 +110,8 @@ Item {
onAdditionalComponentsChanged:
{
if(areaId == "saveButton") {
for (var component in Printer.additionalComponents["saveButton"]) {
Printer.additionalComponents["saveButton"][component].parent = additionalComponentsRow
for (var component in CuraApplication.additionalComponents["saveButton"]) {
CuraApplication.additionalComponents["saveButton"][component].parent = additionalComponentsRow
}
}
}

View File

@ -95,13 +95,17 @@ SettingItem
value:
{
// FIXME this needs to go away once 'resolve' is combined with 'value' in our data model.
var value;
if ((base.resolve != "None") && (base.stackLevel != 0) && (base.stackLevel != 1)) {
var value = undefined;
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 to choose between the resolved value (default) and the global value
// (if user has explicitly set this).
value = base.resolve;
} else {
}
if (value == undefined)
{
value = propertyProvider.properties.value;
}

View File

@ -98,9 +98,9 @@ SettingItem
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
{

View File

@ -155,14 +155,14 @@ Item
containerId: Cura.MachineManager.activeDefinitionId
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.
expanded: Printer.expandedCategories
expanded: CuraApplication.expandedCategories
onExpandedChanged:
{
if(!findingSettings)
{
// Do not change expandedCategories preference while filtering settings
// because all categories are expanded while filtering
Printer.setExpandedCategories(expanded)
CuraApplication.setExpandedCategories(expanded)
}
}
onVisibilityChanged: Cura.SettingInheritanceManager.forceUpdate()
@ -299,7 +299,7 @@ Item
}
}
UM.I18nCatalog { id: catalog; name: "uranium"; }
UM.I18nCatalog { id: catalog; name: "cura"; }
add: Transition {
SequentialAnimation {

View File

@ -332,7 +332,7 @@ Rectangle
}
}
Label {
Text {
id: settingsModeLabel
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

View File

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

View File

@ -33,7 +33,7 @@ Item
width: base.width * .45 - UM.Theme.getSize("default_margin").width
height: childrenRect.height
Label
Text
{
id: infillLabel
//: Infill selection label
@ -162,7 +162,7 @@ Item
}
}
}
Label
Text
{
id: infillLabel
font: UM.Theme.getFont("default")
@ -225,14 +225,14 @@ Item
anchors.right: parent.right
height: childrenRect.height
Label
Text
{
id: enableSupportLabel
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_margin").width
anchors.verticalCenter: enableSupportCheckBox.verticalCenter
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");
color: UM.Theme.getColor("text");
}
@ -263,7 +263,7 @@ Item
onEntered:
{
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:
{
@ -272,7 +272,7 @@ Item
}
}
Label
Text
{
id: supportExtruderLabel
visible: (supportEnabled.properties.value == "True") && (machineExtruderCount.properties.value > 1)
@ -372,7 +372,7 @@ Item
}
Label
Text
{
id: adhesionHelperLabel
anchors.left: parent.left
@ -470,7 +470,7 @@ Item
width: parent.width
height: childrenRect.height
Label
Text
{
anchors.left: parent.left
anchors.leftMargin: UM.Theme.getSize("default_margin").width

View File

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

View File

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