mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-08-07 22:13:58 -06:00
Merge branch 'master' of github.com:ultimaker/Cura into feature_material_editing
* 'master' of github.com:ultimaker/Cura: (110 commits) Rearrange MachineActions on Machines page Skip containers that can not be serialized Make PerObjectSettingVisiblityHandler inherit SettingVisiblityHandler and some other cleanup Use the right property for the property provider Clean up indentation Remove unused code Fix expanded settings for Per Object settings tool Use the expanded categories from Cura to expand the proper categories on startup Starting UMOCheckup before connection was established now works correctly Fixed layout Added missing decorator CURA-1385 Restored accidental delete Removed extraneous space Refactoring; Renaming firstRunWizard to machineActionsWizard Added BedLevel as supported action to UMO Refactoring (Renaming variables so they are more clear & update documentation) Remove some trailing spaces CURA-1615 GCodeProfileReader: Removing useless containername Reenable Per Object Settings tool in simple mode if the current printer has multiextrusion Fix minor codereview issues ...
This commit is contained in:
commit
f6866d703d
58 changed files with 1723 additions and 1412 deletions
|
@ -6,6 +6,12 @@ include(GNUInstallDirs)
|
|||
|
||||
set(URANIUM_SCRIPTS_DIR "${CMAKE_SOURCE_DIR}/../uranium/scripts" CACHE DIRECTORY "The location of the scripts directory of the Uranium repository")
|
||||
|
||||
# Tests
|
||||
# Note that we use exit 0 here to not mark the build as a failure on test failure
|
||||
add_custom_target(tests)
|
||||
add_custom_command(TARGET tests POST_BUILD COMMAND "PYTHONPATH=${CMAKE_SOURCE_DIR}/../Uranium/:${CMAKE_SOURCE_DIR}" ${PYTHON_EXECUTABLE} -m pytest -r a --junitxml=${CMAKE_BINARY_DIR}/junit.xml ${CMAKE_SOURCE_DIR} || exit 0)
|
||||
|
||||
|
||||
set(CURA_VERSION "master" CACHE STRING "Version name of Cura")
|
||||
set(CURA_BUILDTYPE "" CACHE STRING "Build type of Cura, eg. 'PPA'")
|
||||
configure_file(cura/CuraVersion.py.in CuraVersion.py @ONLY)
|
||||
|
|
|
@ -36,6 +36,7 @@ class BuildVolume(SceneNode):
|
|||
self._disallowed_area_mesh = None
|
||||
|
||||
self.setCalculateBoundingBox(False)
|
||||
self._volume_aabb = None
|
||||
|
||||
self._active_container_stack = None
|
||||
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerStackChanged)
|
||||
|
@ -99,7 +100,7 @@ class BuildVolume(SceneNode):
|
|||
mb.addLine(Vector(min_w, max_h, min_d), Vector(min_w, max_h, max_d), color = self.VolumeOutlineColor)
|
||||
mb.addLine(Vector(max_w, max_h, min_d), Vector(max_w, max_h, max_d), color = self.VolumeOutlineColor)
|
||||
|
||||
self.setMeshData(mb.getData())
|
||||
self.setMeshData(mb.build())
|
||||
|
||||
mb = MeshBuilder()
|
||||
mb.addQuad(
|
||||
|
@ -108,10 +109,10 @@ class BuildVolume(SceneNode):
|
|||
Vector(max_w, min_h - 0.2, max_d),
|
||||
Vector(min_w, min_h - 0.2, max_d)
|
||||
)
|
||||
self._grid_mesh = mb.getData()
|
||||
for n in range(0, 6):
|
||||
v = self._grid_mesh.getVertex(n)
|
||||
self._grid_mesh.setVertexUVCoordinates(n, v[0], v[2])
|
||||
v = mb.getVertex(n)
|
||||
mb.setVertexUVCoordinates(n, v[0], v[2])
|
||||
self._grid_mesh = mb.build()
|
||||
|
||||
disallowed_area_height = 0.1
|
||||
disallowed_area_size = 0
|
||||
|
@ -136,11 +137,11 @@ class BuildVolume(SceneNode):
|
|||
size = 0
|
||||
disallowed_area_size = max(size, disallowed_area_size)
|
||||
|
||||
self._disallowed_area_mesh = mb.getData()
|
||||
self._disallowed_area_mesh = mb.build()
|
||||
else:
|
||||
self._disallowed_area_mesh = None
|
||||
|
||||
self._aabb = AxisAlignedBox(minimum = Vector(min_w, min_h - 1.0, min_d), maximum = Vector(max_w, max_h, max_d))
|
||||
self._volume_aabb = AxisAlignedBox(minimum = Vector(min_w, min_h - 1.0, min_d), maximum = Vector(max_w, max_h, max_d))
|
||||
|
||||
skirt_size = 0.0
|
||||
|
||||
|
@ -158,6 +159,9 @@ class BuildVolume(SceneNode):
|
|||
|
||||
Application.getInstance().getController().getScene()._maximum_bounds = scale_to_max_bounds
|
||||
|
||||
def getBoundingBox(self):
|
||||
return self._volume_aabb
|
||||
|
||||
def _onGlobalContainerStackChanged(self):
|
||||
if self._active_container_stack:
|
||||
self._active_container_stack.propertyChanged.disconnect(self._onSettingPropertyChanged)
|
||||
|
|
|
@ -1,116 +1,217 @@
|
|||
from UM.Scene.SceneNodeDecorator import SceneNodeDecorator
|
||||
from UM.Application import Application
|
||||
|
||||
from UM.Math.Polygon import Polygon
|
||||
from . import ConvexHullNode
|
||||
|
||||
import numpy
|
||||
|
||||
## The convex hull decorator is a scene node decorator that adds the convex hull functionality to a scene node.
|
||||
# If a scene node has a convex hull decorator, it will have a shadow in which other objects can not be printed.
|
||||
class ConvexHullDecorator(SceneNodeDecorator):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._convex_hull = None
|
||||
|
||||
# In case of printing all at once this is the same as the convex hull.
|
||||
# For one at the time this is the area without the head.
|
||||
self._convex_hull_boundary = None
|
||||
|
||||
# In case of printing all at once this is the same as the convex hull.
|
||||
# For one at the time this is area with intersection of mirrored head
|
||||
self._convex_hull_head = None
|
||||
|
||||
# In case of printing all at once this is the same as the convex hull.
|
||||
# For one at the time this is area with intersection of full head
|
||||
self._convex_hull_head_full = None
|
||||
|
||||
self._convex_hull_node = None
|
||||
self._convex_hull_job = None
|
||||
|
||||
# Keep track of the previous parent so we can clear its convex hull when the object is reparented
|
||||
self._parent_node = None
|
||||
self._init2DConvexHullCache()
|
||||
|
||||
self._global_stack = None
|
||||
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalStackChanged)
|
||||
Application.getInstance().getController().toolOperationStarted.connect(self._onChanged)
|
||||
Application.getInstance().getController().toolOperationStopped.connect(self._onChanged)
|
||||
|
||||
self._onGlobalStackChanged()
|
||||
#Application.getInstance().getMachineManager().activeProfileChanged.connect(self._onActiveProfileChanged)
|
||||
#Application.getInstance().getMachineManager().activeMachineInstanceChanged.connect(self._onActiveMachineInstanceChanged)
|
||||
#self._onActiveProfileChanged()
|
||||
|
||||
def setNode(self, node):
|
||||
previous_node = self._node
|
||||
if previous_node is not None and node is not previous_node:
|
||||
previous_node.transformationChanged.connect(self._onChanged)
|
||||
previous_node.parentChanged.connect(self._onChanged)
|
||||
|
||||
super().setNode(node)
|
||||
self._parent_node = node.getParent()
|
||||
node.parentChanged.connect(self._onParentChanged)
|
||||
|
||||
self._node.transformationChanged.connect(self._onChanged)
|
||||
self._node.parentChanged.connect(self._onChanged)
|
||||
|
||||
self._onChanged()
|
||||
|
||||
## Force that a new (empty) object is created upon copy.
|
||||
def __deepcopy__(self, memo):
|
||||
copy = ConvexHullDecorator()
|
||||
return copy
|
||||
return ConvexHullDecorator()
|
||||
|
||||
## Get the unmodified convex hull of the node
|
||||
## Get the unmodified 2D projected convex hull of the node
|
||||
def getConvexHull(self):
|
||||
return self._convex_hull
|
||||
if self._node is None:
|
||||
return None
|
||||
|
||||
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"):
|
||||
hull = hull.getMinkowskiHull(Polygon(numpy.array(self._global_stack.getProperty("machine_head_polygon", "value"), numpy.float32)))
|
||||
return hull
|
||||
|
||||
## Get the convex hull of the node with the full head size
|
||||
def getConvexHullHeadFull(self):
|
||||
if not self._convex_hull_head_full:
|
||||
return self.getConvexHull()
|
||||
return self._convex_hull_head_full
|
||||
if self._node is None:
|
||||
return None
|
||||
|
||||
return self._compute2DConvexHeadFull()
|
||||
|
||||
## Get convex hull of the object + head size
|
||||
# In case of printing all at once this is the same as the convex hull.
|
||||
# For one at the time this is area with intersection of mirrored head
|
||||
def getConvexHullHead(self):
|
||||
if not self._convex_hull_head:
|
||||
return self.getConvexHull()
|
||||
return self._convex_hull_head
|
||||
if self._node is None:
|
||||
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"):
|
||||
return self._compute2DConvexHeadMin()
|
||||
return None
|
||||
|
||||
## Get convex hull of the node
|
||||
# In case of printing all at once this is the same as the convex hull.
|
||||
# For one at the time this is the area without the head.
|
||||
def getConvexHullBoundary(self):
|
||||
if not self._convex_hull_boundary:
|
||||
return self.getConvexHull()
|
||||
return self._convex_hull_boundary
|
||||
if self._node is None:
|
||||
return None
|
||||
|
||||
def setConvexHullBoundary(self, hull):
|
||||
self._convex_hull_boundary = hull
|
||||
if self._global_stack:
|
||||
if self._global_stack.getProperty("print_sequence", "value") == "one_at_a_time" and 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
|
||||
|
||||
def setConvexHullHeadFull(self, hull):
|
||||
self._convex_hull_head_full = hull
|
||||
def recomputeConvexHull(self):
|
||||
controller = Application.getInstance().getController()
|
||||
root = controller.getScene().getRoot()
|
||||
if self._node is None or controller.isToolOperationActive() or not self.__isDescendant(root, self._node):
|
||||
if self._convex_hull_node:
|
||||
self._convex_hull_node.setParent(None)
|
||||
self._convex_hull_node = None
|
||||
return
|
||||
|
||||
def setConvexHullHead(self, hull):
|
||||
self._convex_hull_head = hull
|
||||
|
||||
def setConvexHull(self, hull):
|
||||
self._convex_hull = hull
|
||||
if not hull and self._convex_hull_node:
|
||||
convex_hull = self.getConvexHull()
|
||||
if self._convex_hull_node:
|
||||
if self._convex_hull_node.getHull() == convex_hull:
|
||||
return
|
||||
self._convex_hull_node.setParent(None)
|
||||
self._convex_hull_node = None
|
||||
|
||||
def getConvexHullJob(self):
|
||||
return self._convex_hull_job
|
||||
|
||||
def setConvexHullJob(self, job):
|
||||
self._convex_hull_job = job
|
||||
|
||||
def getConvexHullNode(self):
|
||||
return self._convex_hull_node
|
||||
|
||||
def setConvexHullNode(self, node):
|
||||
self._convex_hull_node = node
|
||||
hull_node = ConvexHullNode.ConvexHullNode(self._node, convex_hull, root)
|
||||
self._convex_hull_node = hull_node
|
||||
|
||||
def _onSettingValueChanged(self, key, property_name):
|
||||
if key == "print_sequence" and property_name == "value":
|
||||
self._onChanged()
|
||||
|
||||
def _onChanged(self, *args):
|
||||
if self._convex_hull_job:
|
||||
self._convex_hull_job.cancel()
|
||||
self.setConvexHull(None)
|
||||
def _init2DConvexHullCache(self):
|
||||
# Cache for the group code path in _compute2DConvexHull()
|
||||
self._2d_convex_hull_group_child_polygon = None
|
||||
self._2d_convex_hull_group_result = None
|
||||
|
||||
def _onParentChanged(self, node):
|
||||
# Force updating the convex hull of the parent group if the object is in a group
|
||||
if self._parent_node and self._parent_node.callDecoration("isGroup"):
|
||||
self._parent_node.callDecoration("setConvexHull", None)
|
||||
self._parent_node = self.getNode().getParent()
|
||||
# Cache for the mesh code path in _compute2DConvexHull()
|
||||
self._2d_convex_hull_mesh = None
|
||||
self._2d_convex_hull_mesh_world_transform = None
|
||||
self._2d_convex_hull_mesh_result = None
|
||||
|
||||
def _compute2DConvexHull(self):
|
||||
if self._node.callDecoration("isGroup"):
|
||||
points = numpy.zeros((0, 2), dtype=numpy.int32)
|
||||
for child in self._node.getChildren():
|
||||
child_hull = child.callDecoration("_compute2DConvexHull")
|
||||
if child_hull:
|
||||
points = numpy.append(points, child_hull.getPoints(), axis = 0)
|
||||
|
||||
if points.size < 3:
|
||||
return None
|
||||
child_polygon = Polygon(points)
|
||||
|
||||
# Check the cache
|
||||
if child_polygon == self._2d_convex_hull_group_child_polygon:
|
||||
return self._2d_convex_hull_group_result
|
||||
|
||||
# First, calculate the normal convex hull around the points
|
||||
convex_hull = child_polygon.getConvexHull()
|
||||
|
||||
# Then, do a Minkowski hull with a simple 1x1 quad to outset and round the normal convex hull.
|
||||
# This is done because of rounding errors.
|
||||
rounded_hull = self._roundHull(convex_hull)
|
||||
|
||||
# Store the result in the cache
|
||||
self._2d_convex_hull_group_child_polygon = child_polygon
|
||||
self._2d_convex_hull_group_result = rounded_hull
|
||||
|
||||
return rounded_hull
|
||||
|
||||
else:
|
||||
rounded_hull = None
|
||||
if self._node.getMeshData():
|
||||
mesh = self._node.getMeshData()
|
||||
world_transform = self._node.getWorldTransformation()
|
||||
|
||||
# Check the cache
|
||||
if mesh is self._2d_convex_hull_mesh and world_transform == self._2d_convex_hull_mesh_world_transform:
|
||||
return self._2d_convex_hull_mesh_result
|
||||
|
||||
vertex_data = mesh.getConvexHullTransformedVertices(world_transform)
|
||||
# Don't use data below 0.
|
||||
# TODO; We need a better check for this as this gives poor results for meshes with long edges.
|
||||
vertex_data = vertex_data[vertex_data[:,1] >= 0]
|
||||
|
||||
if len(vertex_data) >= 4:
|
||||
# Round the vertex data to 1/10th of a mm, then remove all duplicate vertices
|
||||
# This is done to greatly speed up further convex hull calculations as the convex hull
|
||||
# becomes much less complex when dealing with highly detailed models.
|
||||
vertex_data = numpy.round(vertex_data, 1)
|
||||
|
||||
vertex_data = vertex_data[:, [0, 2]] # Drop the Y components to project to 2D.
|
||||
|
||||
# Grab the set of unique points.
|
||||
#
|
||||
# This basically finds the unique rows in the array by treating them as opaque groups of bytes
|
||||
# which are as long as the 2 float64s in each row, and giving this view to numpy.unique() to munch.
|
||||
# See http://stackoverflow.com/questions/16970982/find-unique-rows-in-numpy-array
|
||||
vertex_byte_view = numpy.ascontiguousarray(vertex_data).view(
|
||||
numpy.dtype((numpy.void, vertex_data.dtype.itemsize * vertex_data.shape[1])))
|
||||
_, idx = numpy.unique(vertex_byte_view, return_index=True)
|
||||
vertex_data = vertex_data[idx] # Select the unique rows by index.
|
||||
|
||||
hull = Polygon(vertex_data)
|
||||
|
||||
if len(vertex_data) >= 4:
|
||||
# First, calculate the normal convex hull around the points
|
||||
convex_hull = hull.getConvexHull()
|
||||
|
||||
# Then, do a Minkowski hull with a simple 1x1 quad to outset and round the normal convex hull.
|
||||
# This is done because of rounding errors.
|
||||
rounded_hull = convex_hull.getMinkowskiHull(Polygon(numpy.array([[-0.5, -0.5], [-0.5, 0.5], [0.5, 0.5], [0.5, -0.5]], numpy.float32)))
|
||||
|
||||
# Store the result in the cache
|
||||
self._2d_convex_hull_mesh = mesh
|
||||
self._2d_convex_hull_mesh_world_transform = world_transform
|
||||
self._2d_convex_hull_mesh_result = rounded_hull
|
||||
|
||||
return rounded_hull
|
||||
|
||||
def _getHeadAndFans(self):
|
||||
return Polygon(numpy.array(self._global_stack.getProperty("machine_head_with_fans_polygon", "value"), numpy.float32))
|
||||
|
||||
def _compute2DConvexHeadFull(self):
|
||||
return self._compute2DConvexHull().getMinkowskiHull(self._getHeadAndFans())
|
||||
|
||||
def _compute2DConvexHeadMin(self):
|
||||
headAndFans = self._getHeadAndFans()
|
||||
mirrored = headAndFans.mirror([0, 0], [0, 1]).mirror([0, 0], [1, 0]) # Mirror horizontally & vertically.
|
||||
head_and_fans = self._getHeadAndFans().intersectionConvexHulls(mirrored)
|
||||
|
||||
# Min head hull is used for the push free
|
||||
min_head_hull = self._compute2DConvexHull().getMinkowskiHull(head_and_fans)
|
||||
return min_head_hull
|
||||
|
||||
def _roundHull(self, convex_hull):
|
||||
return convex_hull.getMinkowskiHull(Polygon(numpy.array([[-0.5, -0.5], [-0.5, 0.5], [0.5, 0.5], [0.5, -0.5]], numpy.float32)))
|
||||
|
||||
def _onChanged(self, *args):
|
||||
self.recomputeConvexHull()
|
||||
|
||||
def _onGlobalStackChanged(self):
|
||||
if self._global_stack:
|
||||
|
@ -124,3 +225,11 @@ class ConvexHullDecorator(SceneNodeDecorator):
|
|||
self._global_stack.containersChanged.connect(self._onChanged)
|
||||
|
||||
self._onChanged()
|
||||
|
||||
## Returns true if node is a descendent or the same as the root node.
|
||||
def __isDescendant(self, root, node):
|
||||
if node is None:
|
||||
return False
|
||||
if root is node:
|
||||
return True
|
||||
return self.__isDescendant(root, node.getParent())
|
||||
|
|
|
@ -1,110 +0,0 @@
|
|||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from UM.Job import Job
|
||||
from UM.Application import Application
|
||||
from UM.Math.Polygon import Polygon
|
||||
|
||||
import numpy
|
||||
import copy
|
||||
from . import ConvexHullNode
|
||||
|
||||
## Job to async calculate the convex hull of a node.
|
||||
class ConvexHullJob(Job):
|
||||
def __init__(self, node):
|
||||
super().__init__()
|
||||
|
||||
self._node = node
|
||||
|
||||
def run(self):
|
||||
if not self._node:
|
||||
return
|
||||
## If the scene node is a group, use the hull of the children to calculate its hull.
|
||||
if self._node.callDecoration("isGroup"):
|
||||
hull = Polygon(numpy.zeros((0, 2), dtype=numpy.int32))
|
||||
for child in self._node.getChildren():
|
||||
child_hull = child.callDecoration("getConvexHull")
|
||||
if child_hull:
|
||||
hull.setPoints(numpy.append(hull.getPoints(), child_hull.getPoints(), axis = 0))
|
||||
|
||||
if hull.getPoints().size < 3:
|
||||
self._node.callDecoration("setConvexHull", None)
|
||||
self._node.callDecoration("setConvexHullJob", None)
|
||||
return
|
||||
|
||||
Job.yieldThread()
|
||||
|
||||
else:
|
||||
if not self._node.getMeshData():
|
||||
return
|
||||
mesh = self._node.getMeshData()
|
||||
vertex_data = mesh.getTransformed(self._node.getWorldTransformation()).getVertices()
|
||||
# Don't use data below 0.
|
||||
# TODO; We need a better check for this as this gives poor results for meshes with long edges.
|
||||
vertex_data = vertex_data[vertex_data[:,1] >= 0]
|
||||
|
||||
# Round the vertex data to 1/10th of a mm, then remove all duplicate vertices
|
||||
# This is done to greatly speed up further convex hull calculations as the convex hull
|
||||
# becomes much less complex when dealing with highly detailed models.
|
||||
vertex_data = numpy.round(vertex_data, 1)
|
||||
|
||||
vertex_data = vertex_data[:, [0, 2]] # Drop the Y components to project to 2D.
|
||||
|
||||
# Grab the set of unique points.
|
||||
#
|
||||
# This basically finds the unique rows in the array by treating them as opaque groups of bytes
|
||||
# which are as long as the 2 float64s in each row, and giving this view to numpy.unique() to munch.
|
||||
# See http://stackoverflow.com/questions/16970982/find-unique-rows-in-numpy-array
|
||||
vertex_byte_view = numpy.ascontiguousarray(vertex_data).view(numpy.dtype((numpy.void, vertex_data.dtype.itemsize * vertex_data.shape[1])))
|
||||
_, idx = numpy.unique(vertex_byte_view, return_index=True)
|
||||
vertex_data = vertex_data[idx] # Select the unique rows by index.
|
||||
|
||||
hull = Polygon(vertex_data)
|
||||
|
||||
# First, calculate the normal convex hull around the points
|
||||
hull = hull.getConvexHull()
|
||||
|
||||
# Then, do a Minkowski hull with a simple 1x1 quad to outset and round the normal convex hull.
|
||||
# This is done because of rounding errors.
|
||||
hull = hull.getMinkowskiHull(Polygon(numpy.array([[-0.5, -0.5], [-0.5, 0.5], [0.5, 0.5], [0.5, -0.5]], numpy.float32)))
|
||||
|
||||
global_stack = Application.getInstance().getGlobalContainerStack()
|
||||
if global_stack:
|
||||
if global_stack.getProperty("print_sequence", "value")== "one_at_a_time" and not self._node.getParent().callDecoration("isGroup"):
|
||||
# Printing one at a time and it's not an object in a group
|
||||
self._node.callDecoration("setConvexHullBoundary", copy.deepcopy(hull))
|
||||
head_and_fans = Polygon(numpy.array(global_stack.getProperty("machine_head_with_fans_polygon", "value"), numpy.float32))
|
||||
|
||||
# Full head hull is used to actually check the order.
|
||||
full_head_hull = hull.getMinkowskiHull(head_and_fans)
|
||||
self._node.callDecoration("setConvexHullHeadFull", full_head_hull)
|
||||
mirrored = copy.deepcopy(head_and_fans)
|
||||
mirrored.mirror([0, 0], [0, 1]) #Mirror horizontally.
|
||||
mirrored.mirror([0, 0], [1, 0]) #Mirror vertically.
|
||||
head_and_fans = head_and_fans.intersectionConvexHulls(mirrored)
|
||||
|
||||
# Min head hull is used for the push free
|
||||
min_head_hull = hull.getMinkowskiHull(head_and_fans)
|
||||
self._node.callDecoration("setConvexHullHead", min_head_hull)
|
||||
hull = hull.getMinkowskiHull(Polygon(numpy.array(global_stack.getProperty("machine_head_polygon","value"),numpy.float32)))
|
||||
else:
|
||||
self._node.callDecoration("setConvexHullHead", None)
|
||||
if self._node.getParent() is None: # Node was already deleted before job is done.
|
||||
self._node.callDecoration("setConvexHullNode",None)
|
||||
self._node.callDecoration("setConvexHull", None)
|
||||
self._node.callDecoration("setConvexHullJob", None)
|
||||
return
|
||||
|
||||
hull_node = ConvexHullNode.ConvexHullNode(self._node, hull, Application.getInstance().getController().getScene().getRoot())
|
||||
self._node.callDecoration("setConvexHullNode", hull_node)
|
||||
self._node.callDecoration("setConvexHull", hull)
|
||||
self._node.callDecoration("setConvexHullJob", None)
|
||||
|
||||
if self._node.getParent() and self._node.getParent().callDecoration("isGroup"):
|
||||
job = self._node.getParent().callDecoration("getConvexHullJob")
|
||||
if job:
|
||||
job.cancel()
|
||||
self._node.getParent().callDecoration("setConvexHull", None)
|
||||
hull_node = self._node.getParent().callDecoration("getConvexHullNode")
|
||||
if hull_node:
|
||||
hull_node.setParent(None)
|
|
@ -9,7 +9,6 @@ from UM.Mesh.MeshBuilder import MeshBuilder # To create a mesh to display the c
|
|||
|
||||
from UM.View.GL.OpenGL import OpenGL
|
||||
|
||||
|
||||
class ConvexHullNode(SceneNode):
|
||||
## Convex hull node is a special type of scene node that is used to display a 2D 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
|
||||
|
@ -31,21 +30,23 @@ class ConvexHullNode(SceneNode):
|
|||
|
||||
# The node this mesh is "watching"
|
||||
self._node = node
|
||||
self._node.transformationChanged.connect(self._onNodePositionChanged)
|
||||
self._node.parentChanged.connect(self._onNodeParentChanged)
|
||||
self._node.decoratorsChanged.connect(self._onNodeDecoratorsChanged)
|
||||
self._onNodeDecoratorsChanged(self._node)
|
||||
|
||||
self._convex_hull_head_mesh = None
|
||||
self._hull = hull
|
||||
|
||||
hull_mesh = self.createHullMesh(self._hull.getPoints())
|
||||
if hull_mesh:
|
||||
self.setMeshData(hull_mesh)
|
||||
if self._hull:
|
||||
hull_mesh = self.createHullMesh(self._hull.getPoints())
|
||||
if hull_mesh:
|
||||
self.setMeshData(hull_mesh)
|
||||
convex_hull_head = self._node.callDecoration("getConvexHullHead")
|
||||
if convex_hull_head:
|
||||
self._convex_hull_head_mesh = self.createHullMesh(convex_hull_head.getPoints())
|
||||
|
||||
def getHull(self):
|
||||
return self._hull
|
||||
|
||||
## Actually create the mesh from the hullpoints
|
||||
# /param hull_points list of xy values
|
||||
# /return meshData
|
||||
|
@ -62,7 +63,7 @@ class ConvexHullNode(SceneNode):
|
|||
mesh_builder.addFace(point_first, point_previous, point_new, color = self._color)
|
||||
point_previous = point_new # Prepare point_previous for the next triangle.
|
||||
|
||||
return mesh_builder.getData()
|
||||
return mesh_builder.build()
|
||||
|
||||
def getWatchedNode(self):
|
||||
return self._node
|
||||
|
@ -73,24 +74,13 @@ class ConvexHullNode(SceneNode):
|
|||
self._shader.setUniformValue("u_color", self._color)
|
||||
|
||||
if self.getParent():
|
||||
renderer.queueNode(self, transparent = True, shader = self._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)
|
||||
if self.getMeshData():
|
||||
renderer.queueNode(self, transparent = True, shader = self._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)
|
||||
|
||||
return True
|
||||
|
||||
def _onNodePositionChanged(self, node):
|
||||
if node.callDecoration("getConvexHull"):
|
||||
node.callDecoration("setConvexHull", None)
|
||||
node.callDecoration("setConvexHullNode", None)
|
||||
self.setParent(None) # Garbage collection should delete this node after a while.
|
||||
|
||||
def _onNodeParentChanged(self, node):
|
||||
if node.getParent():
|
||||
self.setParent(self._original_parent)
|
||||
else:
|
||||
self.setParent(None)
|
||||
|
||||
def _onNodeDecoratorsChanged(self, node):
|
||||
self._color = Color(35, 35, 35, 0.5)
|
||||
|
||||
|
|
|
@ -44,12 +44,12 @@ from . import ZOffsetDecorator
|
|||
from . import CuraSplashScreen
|
||||
from . import MachineManagerModel
|
||||
from . import ContainerSettingsModel
|
||||
from . import MachineActionManager
|
||||
|
||||
from PyQt5.QtCore import pyqtSlot, QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS
|
||||
from PyQt5.QtGui import QColor, QIcon
|
||||
from PyQt5.QtQml import qmlRegisterUncreatableType, qmlRegisterSingletonType, qmlRegisterType
|
||||
|
||||
import ast #For literal eval of extruder setting types.
|
||||
import platform
|
||||
import sys
|
||||
import os.path
|
||||
|
@ -100,6 +100,8 @@ class CuraApplication(QtApplication):
|
|||
SettingDefinition.addSupportedProperty("settable_globally", DefinitionPropertyType.Any, default = True)
|
||||
SettingDefinition.addSettingType("extruder", int, str, UM.Settings.Validator)
|
||||
|
||||
self._machine_action_manager = MachineActionManager.MachineActionManager()
|
||||
|
||||
super().__init__(name = "cura", version = CuraVersion, buildtype = CuraBuildType)
|
||||
|
||||
self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png")))
|
||||
|
@ -122,7 +124,8 @@ class CuraApplication(QtApplication):
|
|||
self._i18n_catalog = None
|
||||
self._previous_active_tool = None
|
||||
self._platform_activity = False
|
||||
self._scene_bounding_box = AxisAlignedBox()
|
||||
self._scene_bounding_box = AxisAlignedBox.Null
|
||||
|
||||
self._job_name = None
|
||||
self._center_after_select = False
|
||||
self._camera_animation = None
|
||||
|
@ -364,10 +367,12 @@ class CuraApplication(QtApplication):
|
|||
|
||||
self.showSplashMessage(self._i18n_catalog.i18nc("@info:progress", "Loading interface..."))
|
||||
|
||||
ExtruderManager.ExtruderManager.getInstance() #Initialise extruder so as to listen to global container stack changes before the first global container stack is set.
|
||||
# Initialise extruder so as to listen to global container stack changes before the first global container stack is set.
|
||||
ExtruderManager.ExtruderManager.getInstance()
|
||||
qmlRegisterSingletonType(MachineManagerModel.MachineManagerModel, "Cura", 1, 0, "MachineManager",
|
||||
MachineManagerModel.createMachineManagerModel)
|
||||
|
||||
qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, "MachineActionManager", self.getMachineActionManager)
|
||||
self.setMainQml(Resources.getPath(self.ResourceTypes.QmlFiles, "Cura.qml"))
|
||||
self._qml_import_paths.append(Resources.getPath(self.ResourceTypes.QmlFiles))
|
||||
self.initializeEngine()
|
||||
|
@ -384,6 +389,12 @@ class CuraApplication(QtApplication):
|
|||
|
||||
self.exec_()
|
||||
|
||||
## Get the machine action manager
|
||||
# We ignore any *args given to this, as we also register the machine manager as qml singleton.
|
||||
# It wants to give this function an engine and script engine, but we don't care about that.
|
||||
def getMachineActionManager(self, *args):
|
||||
return self._machine_action_manager
|
||||
|
||||
## Handle Qt events
|
||||
def event(self, event):
|
||||
if event.type() == QEvent.FileOpen:
|
||||
|
@ -470,12 +481,14 @@ class CuraApplication(QtApplication):
|
|||
|
||||
count += 1
|
||||
if not scene_bounding_box:
|
||||
scene_bounding_box = copy.deepcopy(node.getBoundingBox())
|
||||
scene_bounding_box = node.getBoundingBox()
|
||||
else:
|
||||
scene_bounding_box += node.getBoundingBox()
|
||||
other_bb = node.getBoundingBox()
|
||||
if other_bb is not None:
|
||||
scene_bounding_box = scene_bounding_box + node.getBoundingBox()
|
||||
|
||||
if not scene_bounding_box:
|
||||
scene_bounding_box = AxisAlignedBox()
|
||||
scene_bounding_box = AxisAlignedBox.Null
|
||||
|
||||
if repr(self._scene_bounding_box) != repr(scene_bounding_box):
|
||||
self._scene_bounding_box = scene_bounding_box
|
||||
|
@ -740,7 +753,6 @@ class CuraApplication(QtApplication):
|
|||
|
||||
# Add all individual nodes to the selection
|
||||
Selection.add(child)
|
||||
child.callDecoration("setConvexHull", None)
|
||||
|
||||
op.push()
|
||||
# Note: The group removes itself from the scene once all its children have left it,
|
||||
|
|
|
@ -111,7 +111,7 @@ class ExtruderManager(QObject):
|
|||
## Creates a container stack for an extruder train.
|
||||
#
|
||||
# The container stack has an extruder definition at the bottom, which is
|
||||
# linked to a machine definition. Then it has a nozzle profile, a material
|
||||
# linked to a machine definition. Then it has a variant profile, a material
|
||||
# profile, a quality profile and a user profile, in that order.
|
||||
#
|
||||
# The resulting container stack is added to the registry.
|
||||
|
@ -136,31 +136,31 @@ class ExtruderManager(QObject):
|
|||
container_stack.addMetaDataEntry("position", position)
|
||||
container_stack.addContainer(extruder_definition)
|
||||
|
||||
#Find the nozzle to use for this extruder.
|
||||
nozzle = container_registry.getEmptyInstanceContainer()
|
||||
if machine_definition.getMetaDataEntry("has_nozzles", default = "False") == "True":
|
||||
#First add any nozzle. Later, overwrite with preference if the preference is valid.
|
||||
nozzles = container_registry.findInstanceContainers(machine = machine_id, type = "nozzle")
|
||||
if len(nozzles) >= 1:
|
||||
nozzle = nozzles[0]
|
||||
preferred_nozzle_id = machine_definition.getMetaDataEntry("preferred_nozzle")
|
||||
if preferred_nozzle_id:
|
||||
preferred_nozzles = container_registry.findInstanceContainers(id = preferred_nozzle_id, type = "nozzle")
|
||||
if len(preferred_nozzles) >= 1:
|
||||
nozzle = preferred_nozzles[0]
|
||||
#Find the variant to use for this extruder.
|
||||
variant = container_registry.getEmptyInstanceContainer()
|
||||
if machine_definition.getMetaDataEntry("has_variants"):
|
||||
#First add any variant. Later, overwrite with preference if the preference is valid.
|
||||
variants = container_registry.findInstanceContainers(definition = machine_id, type = "variant")
|
||||
if len(variants) >= 1:
|
||||
variant = variants[0]
|
||||
preferred_variant_id = machine_definition.getMetaDataEntry("preferred_variant")
|
||||
if preferred_variant_id:
|
||||
preferred_variants = container_registry.findInstanceContainers(id = preferred_variant_id, type = "variant")
|
||||
if len(preferred_variants) >= 1:
|
||||
variant = preferred_variants[0]
|
||||
else:
|
||||
UM.Logger.log("w", "The preferred nozzle \"%s\" of machine %s doesn't exist or is not a nozzle profile.", preferred_nozzle_id, machine_id)
|
||||
#And leave it at the default nozzle.
|
||||
container_stack.addContainer(nozzle)
|
||||
UM.Logger.log("w", "The preferred variant \"%s\" of machine %s doesn't exist or is not a variant profile.", preferred_variant_id, machine_id)
|
||||
#And leave it at the default variant.
|
||||
container_stack.addContainer(variant)
|
||||
|
||||
#Find a material to use for this nozzle.
|
||||
#Find a material to use for this variant.
|
||||
material = container_registry.getEmptyInstanceContainer()
|
||||
if machine_definition.getMetaDataEntry("has_materials", default = "False") == "True":
|
||||
if machine_definition.getMetaDataEntry("has_materials"):
|
||||
#First add any material. Later, overwrite with preference if the preference is valid.
|
||||
if machine_definition.getMetaDataEntry("has_nozzle_materials", default = "False") == "True":
|
||||
materials = container_registry.findInstanceContainers(type = "material", machine = machine_id, nozzle = nozzle.getId())
|
||||
if machine_definition.getMetaDataEntry("has_variant_materials", default = "False") == "True":
|
||||
materials = container_registry.findInstanceContainers(type = "material", definition = machine_id, variant = variant.getId())
|
||||
else:
|
||||
materials = container_registry.findInstanceContainers(type = "material", machine = machine_id)
|
||||
materials = container_registry.findInstanceContainers(type = "material", definition = machine_id)
|
||||
if len(materials) >= 1:
|
||||
material = materials[0]
|
||||
preferred_material_id = machine_definition.getMetaDataEntry("preferred_material")
|
||||
|
@ -175,7 +175,7 @@ class ExtruderManager(QObject):
|
|||
|
||||
#Find a quality to use for this extruder.
|
||||
quality = container_registry.getEmptyInstanceContainer()
|
||||
|
||||
|
||||
#First add any quality. Later, overwrite with preference if the preference is valid.
|
||||
qualities = container_registry.findInstanceContainers(type = "quality")
|
||||
if len(qualities) >= 1:
|
||||
|
|
|
@ -46,12 +46,17 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
|
|||
|
||||
self._add_global = False
|
||||
|
||||
self._active_extruder_stack = None
|
||||
|
||||
#Listen to changes.
|
||||
manager = cura.ExtruderManager.ExtruderManager.getInstance()
|
||||
manager.extrudersChanged.connect(self._updateExtruders) #When the list of extruders changes in general.
|
||||
UM.Application.globalContainerStackChanged.connect(self._updateExtruders) #When the current machine changes.
|
||||
UM.Application.getInstance().globalContainerStackChanged.connect(self._updateExtruders) #When the current machine changes.
|
||||
self._updateExtruders()
|
||||
|
||||
manager.activeExtruderChanged.connect(self._onActiveExtruderChanged)
|
||||
self._onActiveExtruderChanged()
|
||||
|
||||
def setAddGlobal(self, add):
|
||||
if add != self._add_global:
|
||||
self._add_global = add
|
||||
|
@ -63,6 +68,26 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
|
|||
def addGlobal(self):
|
||||
return self._add_global
|
||||
|
||||
def _onActiveExtruderChanged(self):
|
||||
manager = cura.ExtruderManager.ExtruderManager.getInstance()
|
||||
active_extruder_stack = manager.getActiveExtruderStack()
|
||||
if self._active_extruder_stack != active_extruder_stack:
|
||||
if self._active_extruder_stack:
|
||||
self._active_extruder_stack.containersChanged.disconnect(self._onExtruderStackContainersChanged)
|
||||
|
||||
if active_extruder_stack:
|
||||
# Update the model when the material container is changed
|
||||
active_extruder_stack.containersChanged.connect(self._onExtruderStackContainersChanged)
|
||||
self._active_extruder_stack = active_extruder_stack
|
||||
|
||||
|
||||
def _onExtruderStackContainersChanged(self, container):
|
||||
# The ExtrudersModel needs to be updated when the material-name or -color changes, because the user identifies extruders by material-name
|
||||
if container.getMetaDataEntry("type") == "material":
|
||||
self._updateExtruders()
|
||||
|
||||
modelChanged = pyqtSignal()
|
||||
|
||||
## Update the list of extruders.
|
||||
#
|
||||
# This should be called whenever the list of extruders changes.
|
||||
|
@ -85,7 +110,10 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
|
|||
self.appendItem(item)
|
||||
|
||||
for extruder in manager.getMachineExtruders(global_container_stack.getBottom().getId()):
|
||||
extruder_name = extruder.getName()
|
||||
material = extruder.findContainer({ "type": "material" })
|
||||
if material:
|
||||
extruder_name = "%s (%s)" % (material.getName(), extruder_name)
|
||||
position = extruder.getBottom().getMetaDataEntry("position", default = "0") #Position in the definition.
|
||||
try:
|
||||
position = int(position)
|
||||
|
@ -95,10 +123,11 @@ class ExtrudersModel(UM.Qt.ListModel.ListModel):
|
|||
colour = material.getMetaDataEntry("color_code", default = default_colour) if material else default_colour
|
||||
item = { #Construct an item with only the relevant information.
|
||||
"id": extruder.getId(),
|
||||
"name": extruder.getName(),
|
||||
"name": extruder_name,
|
||||
"colour": colour,
|
||||
"index": position
|
||||
}
|
||||
self.appendItem(item)
|
||||
|
||||
self.sort(lambda item: item["index"])
|
||||
self.modelChanged.emit()
|
||||
|
|
|
@ -96,4 +96,4 @@ class Layer:
|
|||
|
||||
builder.addQuad(point1, point2, point3, point4, color = poly_color)
|
||||
|
||||
return builder.getData()
|
||||
return builder.build()
|
||||
|
|
|
@ -1,66 +1,25 @@
|
|||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
from .Layer import Layer
|
||||
from .LayerPolygon import LayerPolygon
|
||||
from UM.Mesh.MeshData import MeshData
|
||||
|
||||
import numpy
|
||||
|
||||
|
||||
## Class to holds the layer mesh and information about the layers.
|
||||
# Immutable, use LayerDataBuilder to create one of these.
|
||||
class LayerData(MeshData):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._layers = {}
|
||||
self._element_counts = {}
|
||||
|
||||
def addLayer(self, layer):
|
||||
if layer not in self._layers:
|
||||
self._layers[layer] = Layer(layer)
|
||||
|
||||
def addPolygon(self, layer, polygon_type, data, line_width):
|
||||
if layer not in self._layers:
|
||||
self.addLayer(layer)
|
||||
|
||||
p = LayerPolygon(self, polygon_type, data, line_width)
|
||||
self._layers[layer].polygons.append(p)
|
||||
def __init__(self, vertices = None, normals = None, indices = None, colors = None, uvs = None, file_name = None,
|
||||
center_position = None, layers=None, element_counts=None):
|
||||
super().__init__(vertices=vertices, normals=normals, indices=indices, colors=colors, uvs=uvs,
|
||||
file_name=file_name, center_position=center_position)
|
||||
self._layers = layers
|
||||
self._element_counts = element_counts
|
||||
|
||||
def getLayer(self, layer):
|
||||
if layer in self._layers:
|
||||
return self._layers[layer]
|
||||
else:
|
||||
return None
|
||||
|
||||
def getLayers(self):
|
||||
return self._layers
|
||||
|
||||
def getElementCounts(self):
|
||||
return self._element_counts
|
||||
|
||||
def setLayerHeight(self, layer, height):
|
||||
if layer not in self._layers:
|
||||
self.addLayer(layer)
|
||||
|
||||
self._layers[layer].setHeight(height)
|
||||
|
||||
def setLayerThickness(self, layer, thickness):
|
||||
if layer not in self._layers:
|
||||
self.addLayer(layer)
|
||||
|
||||
self._layers[layer].setThickness(thickness)
|
||||
|
||||
def build(self):
|
||||
vertex_count = 0
|
||||
for layer, data in self._layers.items():
|
||||
vertex_count += data.vertexCount()
|
||||
|
||||
vertices = numpy.empty((vertex_count, 3), numpy.float32)
|
||||
colors = numpy.empty((vertex_count, 4), numpy.float32)
|
||||
indices = numpy.empty((vertex_count, 2), numpy.int32)
|
||||
|
||||
offset = 0
|
||||
for layer, data in self._layers.items():
|
||||
offset = data.build(offset, vertices, colors, indices)
|
||||
self._element_counts[layer] = data.elementCount
|
||||
|
||||
self.clear()
|
||||
self.addVertices(vertices)
|
||||
self.addColors(colors)
|
||||
self.addIndices(indices.flatten())
|
||||
|
|
72
cura/LayerDataBuilder.py
Normal file
72
cura/LayerDataBuilder.py
Normal file
|
@ -0,0 +1,72 @@
|
|||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from .Layer import Layer
|
||||
from .LayerPolygon import LayerPolygon
|
||||
from UM.Mesh.MeshBuilder import MeshBuilder
|
||||
from .LayerData import LayerData
|
||||
|
||||
import numpy
|
||||
|
||||
## Builder class for constructing a LayerData object
|
||||
class LayerDataBuilder(MeshBuilder):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._layers = {}
|
||||
self._element_counts = {}
|
||||
|
||||
def addLayer(self, layer):
|
||||
if layer not in self._layers:
|
||||
self._layers[layer] = Layer(layer)
|
||||
|
||||
def addPolygon(self, layer, polygon_type, data, line_width):
|
||||
if layer not in self._layers:
|
||||
self.addLayer(layer)
|
||||
|
||||
p = LayerPolygon(self, polygon_type, data, line_width)
|
||||
self._layers[layer].polygons.append(p)
|
||||
|
||||
def getLayer(self, layer):
|
||||
if layer in self._layers:
|
||||
return self._layers[layer]
|
||||
|
||||
def getLayers(self):
|
||||
return self._layers
|
||||
|
||||
def getElementCounts(self):
|
||||
return self._element_counts
|
||||
|
||||
def setLayerHeight(self, layer, height):
|
||||
if layer not in self._layers:
|
||||
self.addLayer(layer)
|
||||
|
||||
self._layers[layer].setHeight(height)
|
||||
|
||||
def setLayerThickness(self, layer, thickness):
|
||||
if layer not in self._layers:
|
||||
self.addLayer(layer)
|
||||
|
||||
self._layers[layer].setThickness(thickness)
|
||||
|
||||
def build(self):
|
||||
vertex_count = 0
|
||||
for layer, data in self._layers.items():
|
||||
vertex_count += data.vertexCount()
|
||||
|
||||
vertices = numpy.empty((vertex_count, 3), numpy.float32)
|
||||
colors = numpy.empty((vertex_count, 4), numpy.float32)
|
||||
indices = numpy.empty((vertex_count, 2), numpy.int32)
|
||||
|
||||
offset = 0
|
||||
for layer, data in self._layers.items():
|
||||
offset = data.build(offset, vertices, colors, indices)
|
||||
self._element_counts[layer] = data.elementCount
|
||||
|
||||
self.addVertices(vertices)
|
||||
self.addColors(colors)
|
||||
self.addIndices(indices.flatten())
|
||||
|
||||
return LayerData(vertices=self.getVertices(), normals=self.getNormals(), indices=self.getIndices(),
|
||||
colors=self.getColors(), uvs=self.getUVCoordinates(), file_name=self.getFileName(),
|
||||
center_position=self.getCenterPosition(), layers=self._layers,
|
||||
element_counts=self._element_counts)
|
78
cura/MachineAction.py
Normal file
78
cura/MachineAction.py
Normal file
|
@ -0,0 +1,78 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal, QUrl
|
||||
from PyQt5.QtQml import QQmlComponent, QQmlContext
|
||||
|
||||
from UM.PluginObject import PluginObject
|
||||
from UM.PluginRegistry import PluginRegistry
|
||||
|
||||
from UM.Application import Application
|
||||
|
||||
import os
|
||||
|
||||
|
||||
class MachineAction(QObject, PluginObject):
|
||||
def __init__(self, key, label = ""):
|
||||
super().__init__()
|
||||
self._key = key
|
||||
self._label = label
|
||||
self._qml_url = ""
|
||||
|
||||
self._component = None
|
||||
self._context = None
|
||||
self._view = None
|
||||
self._finished = False
|
||||
|
||||
labelChanged = pyqtSignal()
|
||||
onFinished = pyqtSignal()
|
||||
|
||||
def getKey(self):
|
||||
return self._key
|
||||
|
||||
@pyqtProperty(str, notify = labelChanged)
|
||||
def label(self):
|
||||
return self._label
|
||||
|
||||
def setLabel(self, label):
|
||||
if self._label != label:
|
||||
self._label = label
|
||||
self.labelChanged.emit()
|
||||
|
||||
## Reset the action to it's default state.
|
||||
# This should not be re-implemented by child classes, instead re-implement _reset.
|
||||
# /sa _reset
|
||||
@pyqtSlot()
|
||||
def reset(self):
|
||||
self._finished = False
|
||||
self._reset()
|
||||
|
||||
## Protected implementation of reset.
|
||||
# /sa reset()
|
||||
def _reset(self):
|
||||
pass
|
||||
|
||||
@pyqtSlot()
|
||||
def setFinished(self):
|
||||
self._finished = True
|
||||
self._reset()
|
||||
self.onFinished.emit()
|
||||
|
||||
@pyqtProperty(bool, notify = onFinished)
|
||||
def finished(self):
|
||||
return self._finished
|
||||
|
||||
def _createViewFromQML(self):
|
||||
path = QUrl.fromLocalFile(
|
||||
os.path.join(PluginRegistry.getInstance().getPluginPath(self.getPluginId()), self._qml_url))
|
||||
self._component = QQmlComponent(Application.getInstance()._engine, path)
|
||||
self._context = QQmlContext(Application.getInstance()._engine.rootContext())
|
||||
self._context.setContextProperty("manager", self)
|
||||
self._view = self._component.create(self._context)
|
||||
|
||||
@pyqtProperty(QObject, constant = True)
|
||||
def displayItem(self):
|
||||
if not self._component:
|
||||
self._createViewFromQML()
|
||||
|
||||
return self._view
|
143
cura/MachineActionManager.py
Normal file
143
cura/MachineActionManager.py
Normal file
|
@ -0,0 +1,143 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
from UM.Logger import Logger
|
||||
from UM.PluginRegistry import PluginRegistry # So MachineAction can be added as plugin type
|
||||
|
||||
from UM.Settings.ContainerRegistry import ContainerRegistry
|
||||
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSlot
|
||||
|
||||
## Raised when trying to add an unknown machine action as a required action
|
||||
class UnknownMachineActionError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
## Raised when trying to add a machine action that does not have an unique key.
|
||||
class NotUniqueMachineActionError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class MachineActionManager(QObject):
|
||||
def __init__(self, parent = None):
|
||||
super().__init__(parent)
|
||||
|
||||
self._machine_actions = {} # Dict of all known machine actions
|
||||
self._required_actions = {} # Dict of all required actions by definition ID
|
||||
self._supported_actions = {} # Dict of all supported actions by definition ID
|
||||
self._first_start_actions = {} # Dict of all actions that need to be done when first added by definition ID
|
||||
|
||||
# Add machine_action as plugin type
|
||||
PluginRegistry.addType("machine_action", self.addMachineAction)
|
||||
|
||||
# Ensure that all containers that were registered before creation of this registry are also handled.
|
||||
# This should not have any effect, but it makes it safer if we ever refactor the order of things.
|
||||
for container in ContainerRegistry.getInstance().findDefinitionContainers():
|
||||
self._onContainerAdded(container)
|
||||
|
||||
ContainerRegistry.getInstance().containerAdded.connect(self._onContainerAdded)
|
||||
|
||||
def _onContainerAdded(self, container):
|
||||
## Ensure that the actions are added to this manager
|
||||
if isinstance(container, DefinitionContainer):
|
||||
supported_actions = container.getMetaDataEntry("supported_actions", [])
|
||||
for action in supported_actions:
|
||||
self.addSupportedAction(container.getId(), action)
|
||||
|
||||
required_actions = container.getMetaDataEntry("required_actions", [])
|
||||
for action in required_actions:
|
||||
self.addRequiredAction(container.getId(), action)
|
||||
|
||||
first_start_actions = container.getMetaDataEntry("first_start_actions", [])
|
||||
for action in first_start_actions:
|
||||
self.addFirstStartAction(container.getId(), action)
|
||||
|
||||
## Add a required action to a machine
|
||||
# Raises an exception when the action is not recognised.
|
||||
def addRequiredAction(self, definition_id, action_key):
|
||||
if action_key in self._machine_actions:
|
||||
if definition_id in self._required_actions:
|
||||
self._required_actions[definition_id] |= {self._machine_actions[action_key]}
|
||||
else:
|
||||
self._required_actions[definition_id] = {self._machine_actions[action_key]}
|
||||
else:
|
||||
raise UnknownMachineActionError("Action %s, which is required for %s is not known." % (action_key, definition_id))
|
||||
|
||||
## Add a supported action to a machine.
|
||||
def addSupportedAction(self, definition_id, action_key):
|
||||
if action_key in self._machine_actions:
|
||||
if definition_id in self._supported_actions:
|
||||
self._supported_actions[definition_id] |= {self._machine_actions[action_key]}
|
||||
else:
|
||||
self._supported_actions[definition_id] = {self._machine_actions[action_key]}
|
||||
else:
|
||||
Logger.log("w", "Unable to add %s to %s, as the action is not recognised", action_key, definition_id)
|
||||
|
||||
## Add an action to the first start list of a machine.
|
||||
def addFirstStartAction(self, definition_id, action_key, index = None):
|
||||
if action_key in self._machine_actions:
|
||||
if definition_id in self._first_start_actions:
|
||||
if index is not None:
|
||||
self._first_start_actions[definition_id].insert(index, self._machine_actions[action_key])
|
||||
else:
|
||||
self._first_start_actions[definition_id].append(self._machine_actions[action_key])
|
||||
else:
|
||||
self._first_start_actions[definition_id] = [self._machine_actions[action_key]]
|
||||
else:
|
||||
Logger.log("w", "Unable to add %s to %s, as the action is not recognised", action_key, definition_id)
|
||||
|
||||
## Add a (unique) MachineAction
|
||||
# if the Key of the action is not unique, an exception is raised.
|
||||
def addMachineAction(self, action):
|
||||
if action.getKey() not in self._machine_actions:
|
||||
self._machine_actions[action.getKey()] = action
|
||||
else:
|
||||
raise NotUniqueMachineActionError("MachineAction with key %s was already added. Actions must have unique keys.", action.getKey())
|
||||
|
||||
## Get all actions supported by given machine
|
||||
# \param definition_id The ID of the definition you want the supported actions of
|
||||
# \returns set of supported actions.
|
||||
@pyqtSlot(str, result = "QVariantList")
|
||||
def getSupportedActions(self, definition_id):
|
||||
if definition_id in self._supported_actions:
|
||||
return list(self._supported_actions[definition_id])
|
||||
else:
|
||||
return set()
|
||||
|
||||
## Get all actions required by given machine
|
||||
# \param definition_id The ID of the definition you want the required actions of
|
||||
# \returns set of required actions.
|
||||
def getRequiredActions(self, definition_id):
|
||||
if definition_id in self._required_actions:
|
||||
return self._required_actions[definition_id]
|
||||
else:
|
||||
return set()
|
||||
|
||||
## Get all actions that need to be performed upon first start of a given machine.
|
||||
# Note that contrary to required / supported actions a list is returned (as it could be required to run the same
|
||||
# action multiple times).
|
||||
# \param definition_id The ID of the definition that you want to get the "on added" actions for.
|
||||
# \returns List of actions.
|
||||
@pyqtSlot(str, result="QVariantList")
|
||||
def getFirstStartActions(self, definition_id):
|
||||
if definition_id in self._first_start_actions:
|
||||
return self._first_start_actions[definition_id]
|
||||
else:
|
||||
return []
|
||||
|
||||
## Remove Machine action from manager
|
||||
# \param action to remove
|
||||
def removeMachineAction(self, action):
|
||||
try:
|
||||
del self._machine_actions[action.getKey()]
|
||||
except KeyError:
|
||||
Logger.log("w", "Trying to remove MachineAction (%s) that was already removed", action.getKey())
|
||||
|
||||
## Get MachineAction by key
|
||||
# \param key String of key to select
|
||||
# \return Machine action if found, None otherwise
|
||||
def getMachineAction(self, key):
|
||||
if key in self._machine_actions:
|
||||
return self._machine_actions[key]
|
||||
else:
|
||||
return None
|
|
@ -376,11 +376,16 @@ class MachineManagerModel(QObject):
|
|||
return
|
||||
|
||||
old_material = self._active_container_stack.findContainer({"type":"material"})
|
||||
old_quality = self._active_container_stack.findContainer({"type": "quality"})
|
||||
if old_material:
|
||||
material_index = self._active_container_stack.getContainerIndex(old_material)
|
||||
self._active_container_stack.replaceContainer(material_index, containers[0])
|
||||
|
||||
self.setActiveQuality(self._updateQualityContainer(self._active_container_stack.getBottom(), containers[0]).id)
|
||||
preferred_quality_name = None
|
||||
if old_quality:
|
||||
preferred_quality_name = old_quality.getName()
|
||||
|
||||
self.setActiveQuality(self._updateQualityContainer(self._global_container_stack.getBottom(), containers[0], preferred_quality_name).id)
|
||||
|
||||
@pyqtSlot(str)
|
||||
def setActiveVariant(self, variant_id):
|
||||
|
@ -389,11 +394,16 @@ class MachineManagerModel(QObject):
|
|||
return
|
||||
|
||||
old_variant = self._active_container_stack.findContainer({"type": "variant"})
|
||||
old_material = self._active_container_stack.findContainer({"type": "material"})
|
||||
if old_variant:
|
||||
variant_index = self._active_container_stack.getContainerIndex(old_variant)
|
||||
self._active_container_stack.replaceContainer(variant_index, containers[0])
|
||||
|
||||
self.setActiveMaterial(self._updateMaterialContainer(self._active_container_stack.getBottom(), containers[0]).id)
|
||||
preferred_material = None
|
||||
if old_material:
|
||||
preferred_material = old_material.getId()
|
||||
|
||||
self.setActiveMaterial(self._updateMaterialContainer(self._global_container_stack.getBottom(), containers[0], preferred_material).id)
|
||||
|
||||
@pyqtSlot(str)
|
||||
def setActiveQuality(self, quality_id):
|
||||
|
@ -503,7 +513,7 @@ class MachineManagerModel(QObject):
|
|||
|
||||
return self._empty_variant_container
|
||||
|
||||
def _updateMaterialContainer(self, definition, variant_container = None):
|
||||
def _updateMaterialContainer(self, definition, variant_container = None, preferred_material = None):
|
||||
if not definition.getMetaDataEntry("has_materials"):
|
||||
return self._empty_material_container
|
||||
|
||||
|
@ -517,7 +527,8 @@ class MachineManagerModel(QObject):
|
|||
else:
|
||||
search_criteria["definition"] = "fdmprinter"
|
||||
|
||||
preferred_material = definition.getMetaDataEntry("preferred_material")
|
||||
if not preferred_material:
|
||||
preferred_material = definition.getMetaDataEntry("preferred_material")
|
||||
if preferred_material:
|
||||
search_criteria["id"] = preferred_material
|
||||
|
||||
|
@ -527,7 +538,7 @@ class MachineManagerModel(QObject):
|
|||
|
||||
return self._empty_material_container
|
||||
|
||||
def _updateQualityContainer(self, definition, material_container = None):
|
||||
def _updateQualityContainer(self, definition, material_container = None, preferred_quality_name = None):
|
||||
search_criteria = { "type": "quality" }
|
||||
|
||||
if definition.getMetaDataEntry("has_machine_quality"):
|
||||
|
@ -538,9 +549,12 @@ class MachineManagerModel(QObject):
|
|||
else:
|
||||
search_criteria["definition"] = "fdmprinter"
|
||||
|
||||
preferred_quality = definition.getMetaDataEntry("preferred_quality")
|
||||
if preferred_quality:
|
||||
search_criteria["id"] = preferred_quality
|
||||
if preferred_quality_name:
|
||||
search_criteria["name"] = preferred_quality_name
|
||||
else:
|
||||
preferred_quality = definition.getMetaDataEntry("preferred_quality")
|
||||
if preferred_quality:
|
||||
search_criteria["id"] = preferred_quality
|
||||
|
||||
containers = UM.Settings.ContainerRegistry.getInstance().findInstanceContainers(**search_criteria)
|
||||
if containers:
|
||||
|
|
|
@ -14,7 +14,6 @@ from UM.Preferences import Preferences
|
|||
from cura.ConvexHullDecorator import ConvexHullDecorator
|
||||
|
||||
from . import PlatformPhysicsOperation
|
||||
from . import ConvexHullJob
|
||||
from . import ZOffsetDecorator
|
||||
|
||||
import copy
|
||||
|
@ -27,7 +26,6 @@ class PlatformPhysics:
|
|||
self._controller.toolOperationStarted.connect(self._onToolOperationStarted)
|
||||
self._controller.toolOperationStopped.connect(self._onToolOperationStopped)
|
||||
self._build_volume = volume
|
||||
|
||||
self._enabled = True
|
||||
|
||||
self._change_timer = QTimer()
|
||||
|
@ -46,16 +44,13 @@ class PlatformPhysics:
|
|||
|
||||
root = self._controller.getScene().getRoot()
|
||||
for node in BreadthFirstIterator(root):
|
||||
if node is root or type(node) is not SceneNode:
|
||||
if node is root or type(node) is not SceneNode or node.getBoundingBox() is None:
|
||||
continue
|
||||
|
||||
bbox = node.getBoundingBox()
|
||||
if not bbox or not bbox.isValid():
|
||||
self._change_timer.start()
|
||||
continue
|
||||
|
||||
build_volume_bounding_box = copy.deepcopy(self._build_volume.getBoundingBox())
|
||||
build_volume_bounding_box.setBottom(-9001) # Ignore intersections with the bottom
|
||||
# Ignore intersections with the bottom
|
||||
build_volume_bounding_box = self._build_volume.getBoundingBox().set(bottom=-9001)
|
||||
node._outside_buildarea = False
|
||||
|
||||
# Mark the node as outside the build volume if the bounding box test fails.
|
||||
|
@ -67,9 +62,9 @@ class PlatformPhysics:
|
|||
if not (node.getParent() and node.getParent().callDecoration("isGroup")): #If an object is grouped, don't move it down
|
||||
z_offset = node.callDecoration("getZOffset") if node.getDecorator(ZOffsetDecorator.ZOffsetDecorator) else 0
|
||||
if bbox.bottom > 0:
|
||||
move_vector.setY(-bbox.bottom + z_offset)
|
||||
move_vector = move_vector.set(y=-bbox.bottom + z_offset)
|
||||
elif bbox.bottom < z_offset:
|
||||
move_vector.setY((-bbox.bottom) - z_offset)
|
||||
move_vector = move_vector.set(y=(-bbox.bottom) - z_offset)
|
||||
|
||||
#if not Float.fuzzyCompare(bbox.bottom, 0.0):
|
||||
# pass#move_vector.setY(-bbox.bottom)
|
||||
|
@ -77,14 +72,9 @@ class PlatformPhysics:
|
|||
# If there is no convex hull for the node, start calculating it and continue.
|
||||
if not node.getDecorator(ConvexHullDecorator):
|
||||
node.addDecorator(ConvexHullDecorator())
|
||||
|
||||
if not node.callDecoration("getConvexHull"):
|
||||
if not node.callDecoration("getConvexHullJob"):
|
||||
job = ConvexHullJob.ConvexHullJob(node)
|
||||
job.start()
|
||||
node.callDecoration("setConvexHullJob", job)
|
||||
|
||||
elif Preferences.getInstance().getValue("physics/automatic_push_free"):
|
||||
node.callDecoration("recomputeConvexHull")
|
||||
|
||||
if Preferences.getInstance().getValue("physics/automatic_push_free"):
|
||||
# Check for collisions between convex hulls
|
||||
for other_node in BreadthFirstIterator(root):
|
||||
# Ignore root, ourselves and anything that is not a normal SceneNode.
|
||||
|
@ -125,8 +115,7 @@ class PlatformPhysics:
|
|||
|
||||
if overlap is None:
|
||||
continue
|
||||
move_vector.setX(overlap[0] * 1.1)
|
||||
move_vector.setZ(overlap[1] * 1.1)
|
||||
move_vector = move_vector.set(x=overlap[0] * 1.1, z=overlap[1] * 1.1)
|
||||
convex_hull = node.callDecoration("getConvexHull")
|
||||
if convex_hull:
|
||||
if not convex_hull.isValid():
|
||||
|
@ -139,7 +128,7 @@ class PlatformPhysics:
|
|||
|
||||
node._outside_buildarea = True
|
||||
|
||||
if move_vector != Vector():
|
||||
if not Vector.Null.equals(move_vector, epsilon=1e-5):
|
||||
op = PlatformPhysicsOperation.PlatformPhysicsOperation(node, move_vector)
|
||||
op.push()
|
||||
|
||||
|
|
|
@ -28,4 +28,4 @@ class PlatformPhysicsOperation(Operation):
|
|||
return group
|
||||
|
||||
def __repr__(self):
|
||||
return "PlatformPhysicsOperation(t = {0})".format(self._position)
|
||||
return "PlatformPhysicsOperation(new_position = {0})".format(self._new_position)
|
||||
|
|
|
@ -12,7 +12,7 @@ from UM.i18n import i18nCatalog
|
|||
|
||||
from UM.Math.Vector import Vector
|
||||
|
||||
from cura import LayerData
|
||||
from cura import LayerDataBuilder
|
||||
from cura import LayerDataDecorator
|
||||
|
||||
import numpy
|
||||
|
@ -63,7 +63,7 @@ class ProcessSlicedLayersJob(Job):
|
|||
return
|
||||
|
||||
mesh = MeshData()
|
||||
layer_data = LayerData.LayerData()
|
||||
layer_data = LayerDataBuilder.LayerDataBuilder()
|
||||
layer_count = len(self._layers)
|
||||
|
||||
# Find the minimum layer number
|
||||
|
@ -115,7 +115,7 @@ class ProcessSlicedLayersJob(Job):
|
|||
self._progress.setProgress(progress)
|
||||
|
||||
# We are done processing all the layers we got from the engine, now create a mesh out of the data
|
||||
layer_data.build()
|
||||
layer_mesh = layer_data.build()
|
||||
|
||||
if self._abort_requested:
|
||||
if self._progress:
|
||||
|
@ -124,7 +124,7 @@ class ProcessSlicedLayersJob(Job):
|
|||
|
||||
# Add LayerDataDecorator to scene node to indicate that the node has layer data
|
||||
decorator = LayerDataDecorator.LayerDataDecorator()
|
||||
decorator.setLayerData(layer_data)
|
||||
decorator.setLayerData(layer_mesh)
|
||||
new_node.addDecorator(decorator)
|
||||
|
||||
new_node.setMeshData(mesh)
|
||||
|
|
|
@ -1,25 +1,29 @@
|
|||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from UM.Application import Application #To get the machine manager to create the new profile in.
|
||||
from UM.Settings.Profile import Profile
|
||||
from UM.Settings.ProfileReader import ProfileReader
|
||||
from UM.Logger import Logger
|
||||
import os
|
||||
import re #Regular expressions for parsing escape characters in the settings.
|
||||
|
||||
from UM.Application import Application #To get the machine manager to create the new profile in.
|
||||
from UM.Settings.InstanceContainer import InstanceContainer
|
||||
from UM.Logger import Logger
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
from cura.ProfileReader import ProfileReader
|
||||
|
||||
## A class that reads profile data from g-code files.
|
||||
#
|
||||
# It reads the profile data from g-code files and stores it in a new profile.
|
||||
# This class currently does not process the rest of the g-code in any way.
|
||||
class GCodeProfileReader(ProfileReader):
|
||||
## The file format version of the serialised g-code.
|
||||
## The file format version of the serialized g-code.
|
||||
#
|
||||
# It can only read settings with the same version as the version it was
|
||||
# written with. If the file format is changed in a way that breaks reverse
|
||||
# compatibility, increment this version number!
|
||||
version = 1
|
||||
|
||||
|
||||
## Dictionary that defines how characters are escaped when embedded in
|
||||
# g-code.
|
||||
#
|
||||
|
@ -51,31 +55,36 @@ class GCodeProfileReader(ProfileReader):
|
|||
# Loading all settings from the file.
|
||||
# They are all at the end, but Python has no reverse seek any more since Python3.
|
||||
# TODO: Consider moving settings to the start?
|
||||
serialised = "" # Will be filled with the serialised profile.
|
||||
serialized = "" # Will be filled with the serialized profile.
|
||||
try:
|
||||
with open(file_name) as f:
|
||||
for line in f:
|
||||
if line.startswith(prefix):
|
||||
# Remove the prefix and the newline from the line and add it to the rest.
|
||||
serialised += line[prefix_length : -1]
|
||||
serialized += line[prefix_length : -1]
|
||||
except IOError as e:
|
||||
Logger.log("e", "Unable to open file %s for reading: %s", file_name, str(e))
|
||||
return None
|
||||
|
||||
# Un-escape the serialised profile.
|
||||
# Un-escape the serialized profile.
|
||||
pattern = re.compile("|".join(GCodeProfileReader.escape_characters.keys()))
|
||||
|
||||
# Perform the replacement with a regular expression.
|
||||
serialised = pattern.sub(lambda m: GCodeProfileReader.escape_characters[re.escape(m.group(0))], serialised)
|
||||
serialized = pattern.sub(lambda m: GCodeProfileReader.escape_characters[re.escape(m.group(0))], serialized)
|
||||
Logger.log("i", "Serialized the following from %s: %s" %(file_name, repr(serialized)))
|
||||
|
||||
# Apply the changes to the current profile.
|
||||
profile = Profile(machine_manager = Application.getInstance().getMachineManager(), read_only = False)
|
||||
# Create an empty profile - the id will be changed later
|
||||
profile = InstanceContainer("")
|
||||
profile.addMetaDataEntry("type", "quality")
|
||||
try:
|
||||
profile.unserialise(serialised)
|
||||
profile.setType(None) # Force type to none so it's correctly added.
|
||||
profile.setReadOnly(False)
|
||||
profile.setDirty(True)
|
||||
profile.deserialize(serialized)
|
||||
except Exception as e: # Not a valid g-code file.
|
||||
Logger.log("e", "Unable to serialise the profile: %s", str(e))
|
||||
return None
|
||||
return profile
|
||||
|
||||
#Creating a unique name using the filename of the GCode
|
||||
new_name = catalog.i18nc("@label", "Custom profile (%s)") %(os.path.splitext(os.path.basename(file_name))[0])
|
||||
profile.setName(new_name)
|
||||
profile._id = new_name
|
||||
|
||||
return profile
|
||||
|
|
|
@ -13,7 +13,7 @@ def getMetaData():
|
|||
"author": "Ultimaker",
|
||||
"version": "1.0",
|
||||
"description": catalog.i18nc("@info:whatsthis", "Provides support for importing profiles from g-code files."),
|
||||
"api": 2
|
||||
"api": 3
|
||||
},
|
||||
"profile_reader": [
|
||||
{
|
||||
|
|
|
@ -7,7 +7,7 @@ from PyQt5.QtGui import QImage, qRed, qGreen, qBlue
|
|||
from PyQt5.QtCore import Qt
|
||||
|
||||
from UM.Mesh.MeshReader import MeshReader
|
||||
from UM.Mesh.MeshData import MeshData
|
||||
from UM.Mesh.MeshBuilder import MeshBuilder
|
||||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Math.Vector import Vector
|
||||
from UM.Job import Job
|
||||
|
@ -48,13 +48,9 @@ class ImageReader(MeshReader):
|
|||
return self._generateSceneNode(file_name, size, self._ui.peak_height, self._ui.base_height, self._ui.smoothing, 512, self._ui.image_color_invert)
|
||||
|
||||
def _generateSceneNode(self, file_name, xz_size, peak_height, base_height, blur_iterations, max_size, image_color_invert):
|
||||
mesh = None # TODO: @UnusedVariable
|
||||
scene_node = None # TODO: @UnusedVariable
|
||||
|
||||
scene_node = SceneNode()
|
||||
|
||||
mesh = MeshData()
|
||||
scene_node.setMeshData(mesh)
|
||||
mesh = MeshBuilder()
|
||||
|
||||
img = QImage(file_name)
|
||||
|
||||
|
@ -76,9 +72,9 @@ class ImageReader(MeshReader):
|
|||
scale_vector = Vector(xz_size, peak_height, xz_size)
|
||||
|
||||
if width > height:
|
||||
scale_vector.setZ(scale_vector.z * aspect)
|
||||
scale_vector = scale_vector.set(z=scale_vector.z * aspect)
|
||||
elif height > width:
|
||||
scale_vector.setX(scale_vector.x / aspect)
|
||||
scale_vector = scale_vector.set(x=scale_vector.x / aspect)
|
||||
|
||||
if width > max_size or height > max_size:
|
||||
scale_factor = max_size / width
|
||||
|
@ -173,8 +169,8 @@ class ImageReader(MeshReader):
|
|||
geo_height = height_minus_one * texel_height
|
||||
|
||||
# bottom
|
||||
mesh.addFace(0, 0, 0, 0, 0, geo_height, geo_width, 0, geo_height)
|
||||
mesh.addFace(geo_width, 0, geo_height, geo_width, 0, 0, 0, 0, 0)
|
||||
mesh.addFaceByPoints(0, 0, 0, 0, 0, geo_height, geo_width, 0, geo_height)
|
||||
mesh.addFaceByPoints(geo_width, 0, geo_height, geo_width, 0, 0, 0, 0, 0)
|
||||
|
||||
# north and south walls
|
||||
for n in range(0, width_minus_one):
|
||||
|
@ -187,11 +183,11 @@ class ImageReader(MeshReader):
|
|||
hs0 = height_data[height_minus_one, n]
|
||||
hs1 = height_data[height_minus_one, n + 1]
|
||||
|
||||
mesh.addFace(x, 0, 0, nx, 0, 0, nx, hn1, 0)
|
||||
mesh.addFace(nx, hn1, 0, x, hn0, 0, x, 0, 0)
|
||||
mesh.addFaceByPoints(x, 0, 0, nx, 0, 0, nx, hn1, 0)
|
||||
mesh.addFaceByPoints(nx, hn1, 0, x, hn0, 0, x, 0, 0)
|
||||
|
||||
mesh.addFace(x, 0, geo_height, nx, 0, geo_height, nx, hs1, geo_height)
|
||||
mesh.addFace(nx, hs1, geo_height, x, hs0, geo_height, x, 0, geo_height)
|
||||
mesh.addFaceByPoints(x, 0, geo_height, nx, 0, geo_height, nx, hs1, geo_height)
|
||||
mesh.addFaceByPoints(nx, hs1, geo_height, x, hs0, geo_height, x, 0, geo_height)
|
||||
|
||||
# west and east walls
|
||||
for n in range(0, height_minus_one):
|
||||
|
@ -204,12 +200,14 @@ class ImageReader(MeshReader):
|
|||
he0 = height_data[n, width_minus_one]
|
||||
he1 = height_data[n + 1, width_minus_one]
|
||||
|
||||
mesh.addFace(0, 0, y, 0, 0, ny, 0, hw1, ny)
|
||||
mesh.addFace(0, hw1, ny, 0, hw0, y, 0, 0, y)
|
||||
mesh.addFaceByPoints(0, 0, y, 0, 0, ny, 0, hw1, ny)
|
||||
mesh.addFaceByPoints(0, hw1, ny, 0, hw0, y, 0, 0, y)
|
||||
|
||||
mesh.addFace(geo_width, 0, y, geo_width, 0, ny, geo_width, he1, ny)
|
||||
mesh.addFace(geo_width, he1, ny, geo_width, he0, y, geo_width, 0, y)
|
||||
mesh.addFaceByPoints(geo_width, 0, y, geo_width, 0, ny, geo_width, he1, ny)
|
||||
mesh.addFaceByPoints(geo_width, he1, ny, geo_width, he0, y, geo_width, 0, y)
|
||||
|
||||
mesh.calculateNormals(fast=True)
|
||||
|
||||
scene_node.setMeshData(mesh.build())
|
||||
|
||||
return scene_node
|
||||
|
|
|
@ -8,7 +8,7 @@ from UM.Event import Event, KeyEvent
|
|||
from UM.Signal import Signal
|
||||
from UM.Scene.Selection import Selection
|
||||
from UM.Math.Color import Color
|
||||
from UM.Mesh.MeshData import MeshData
|
||||
from UM.Mesh.MeshBuilder import MeshBuilder
|
||||
from UM.Job import Job
|
||||
from UM.Preferences import Preferences
|
||||
|
||||
|
@ -240,7 +240,7 @@ class _CreateTopLayersJob(Job):
|
|||
if self._cancel or not layer_data:
|
||||
return
|
||||
|
||||
layer_mesh = MeshData()
|
||||
layer_mesh = MeshBuilder()
|
||||
for i in range(self._solid_layers):
|
||||
layer_number = self._layer_number - i
|
||||
if layer_number < 0:
|
||||
|
@ -275,7 +275,7 @@ class _CreateTopLayersJob(Job):
|
|||
if not jump_mesh or jump_mesh.getVertices() is None:
|
||||
jump_mesh = None
|
||||
|
||||
self.setResult({ "layers": layer_mesh, "jumps": jump_mesh })
|
||||
self.setResult({ "layers": layer_mesh.build(), "jumps": jump_mesh })
|
||||
|
||||
def cancel(self):
|
||||
self._cancel = True
|
||||
|
|
|
@ -3,36 +3,44 @@ from UM.Application import Application
|
|||
from UM.Settings.SettingInstance import SettingInstance
|
||||
from UM.Logger import Logger
|
||||
|
||||
import UM.Settings.Models
|
||||
|
||||
from cura.SettingOverrideDecorator import SettingOverrideDecorator
|
||||
|
||||
## The per object setting visibility handler ensures that only setting defintions that have a matching instance Container
|
||||
# are returned as visible.
|
||||
class PerObjectSettingVisibilityHandler(QObject):
|
||||
class PerObjectSettingVisibilityHandler(UM.Settings.Models.SettingVisibilityHandler):
|
||||
def __init__(self, parent = None, *args, **kwargs):
|
||||
super().__init__(parent = parent, *args, **kwargs)
|
||||
self._selected_object_id = None
|
||||
|
||||
visibilityChanged = pyqtSignal()
|
||||
self._selected_object_id = None
|
||||
self._node = None
|
||||
self._stack = None
|
||||
|
||||
def setSelectedObjectId(self, id):
|
||||
self._selected_object_id = id
|
||||
self.visibilityChanged.emit()
|
||||
if id != self._selected_object_id:
|
||||
self._selected_object_id = id
|
||||
|
||||
self._node = Application.getInstance().getController().getScene().findObject(self._selected_object_id)
|
||||
if self._node:
|
||||
self._stack = self._node.callDecoration("getStack")
|
||||
|
||||
self.visibilityChanged.emit()
|
||||
|
||||
@pyqtProperty("quint64", fset = setSelectedObjectId)
|
||||
def selectedObjectId(self):
|
||||
pass
|
||||
return self._selected_object_id
|
||||
|
||||
def setVisible(self, visible):
|
||||
node = Application.getInstance().getController().getScene().findObject(self._selected_object_id)
|
||||
if not node:
|
||||
if not self._node:
|
||||
return
|
||||
stack = node.callDecoration("getStack")
|
||||
if not stack:
|
||||
node.addDecorator(SettingOverrideDecorator())
|
||||
stack = node.callDecoration("getStack")
|
||||
|
||||
settings = stack.getTop()
|
||||
all_instances = settings.findInstances(**{})
|
||||
if not self._stack:
|
||||
self._node.addDecorator(SettingOverrideDecorator())
|
||||
self._stack = self._node.callDecoration("getStack")
|
||||
|
||||
settings = self._stack.getTop()
|
||||
all_instances = settings.findInstances()
|
||||
visibility_changed = False # Flag to check if at the end the signal needs to be emitted
|
||||
|
||||
# Remove all instances that are not in visibility list
|
||||
|
@ -41,13 +49,12 @@ class PerObjectSettingVisibilityHandler(QObject):
|
|||
settings.removeInstance(instance.definition.key)
|
||||
visibility_changed = True
|
||||
|
||||
# Add all instances that are not added, but are in visiblity list
|
||||
# Add all instances that are not added, but are in visibility list
|
||||
for item in visible:
|
||||
if not settings.getInstance(item):
|
||||
definition_container = Application.getInstance().getGlobalContainerStack().getBottom()
|
||||
definitions = definition_container.findDefinitions(key = item)
|
||||
if definitions:
|
||||
settings.addInstance(SettingInstance(definitions[0], settings))
|
||||
definition = self._stack.getSettingDefinition(item)
|
||||
if definition:
|
||||
settings.addInstance(SettingInstance(definition, settings))
|
||||
visibility_changed = True
|
||||
else:
|
||||
Logger.log("w", "Unable to add instance (%s) to perobject visibility because we couldn't find the matching definition", item)
|
||||
|
@ -57,20 +64,16 @@ class PerObjectSettingVisibilityHandler(QObject):
|
|||
|
||||
def getVisible(self):
|
||||
visible_settings = set()
|
||||
node = Application.getInstance().getController().getScene().findObject(self._selected_object_id)
|
||||
if not node:
|
||||
if not self._node:
|
||||
return visible_settings
|
||||
|
||||
stack = node.callDecoration("getStack")
|
||||
if not stack:
|
||||
if not self._stack:
|
||||
return visible_settings
|
||||
|
||||
settings = stack.getTop()
|
||||
settings = self._stack.getTop()
|
||||
if not settings:
|
||||
return visible_settings
|
||||
|
||||
all_instances = settings.findInstances(**{})
|
||||
for instance in all_instances:
|
||||
visible_settings.add(instance.definition.key)
|
||||
visible_settings = set(map(lambda i: i.definition.key, settings.findInstances()))
|
||||
return visible_settings
|
||||
|
||||
|
|
|
@ -24,10 +24,20 @@ Item {
|
|||
anchors.top: parent.top;
|
||||
anchors.left: parent.left;
|
||||
|
||||
spacing: UM.Theme.getSize("default_margin").height;
|
||||
spacing: UM.Theme.getSize("default_margin").height
|
||||
|
||||
Row
|
||||
{
|
||||
spacing: UM.Theme.getSize("default_margin").width
|
||||
Label
|
||||
{
|
||||
text: catalog.i18nc("@label", "Print object with")
|
||||
anchors.verticalCenter: extruderSelector.verticalCenter
|
||||
|
||||
color: UM.Theme.getColor("setting_control_text")
|
||||
font: UM.Theme.getFont("default")
|
||||
visible: extruderSelector.visible
|
||||
}
|
||||
ComboBox
|
||||
{
|
||||
id: extruderSelector
|
||||
|
@ -40,7 +50,7 @@ Item {
|
|||
}
|
||||
visible: extruders_model.rowCount() > 1
|
||||
textRole: "name"
|
||||
width: items.width
|
||||
width: UM.Theme.getSize("setting_control").width
|
||||
height: UM.Theme.getSize("section").height
|
||||
MouseArea
|
||||
{
|
||||
|
@ -143,6 +153,8 @@ Item {
|
|||
{
|
||||
id: addedSettingsModel;
|
||||
containerId: Cura.MachineManager.activeDefinitionId
|
||||
expanded: [ "*" ]
|
||||
|
||||
visibilityHandler: Cura.PerObjectSettingVisibilityHandler
|
||||
{
|
||||
selectedObjectId: UM.ActiveTool.properties.getValue("SelectedObjectId")
|
||||
|
@ -205,9 +217,8 @@ Item {
|
|||
|
||||
style: ButtonStyle
|
||||
{
|
||||
background: Rectangle
|
||||
background: Item
|
||||
{
|
||||
color: control.hovered ? control.parent.style.controlHighlightColor : control.parent.style.controlColor;
|
||||
UM.RecolorImage
|
||||
{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
@ -330,6 +341,8 @@ Item {
|
|||
"settable_per_mesh": true
|
||||
}
|
||||
visibilityHandler: UM.SettingPreferenceVisibilityHandler {}
|
||||
expanded: [ "*" ]
|
||||
exclude: [ "machine_settings" ]
|
||||
}
|
||||
delegate:Loader
|
||||
{
|
||||
|
|
|
@ -16,10 +16,17 @@ class PerObjectSettingsTool(Tool):
|
|||
|
||||
self.setExposedProperties("SelectedObjectId", "ContainerID", "SelectedActiveExtruder")
|
||||
|
||||
Preferences.getInstance().preferenceChanged.connect(self._onPreferenceChanged)
|
||||
self._advanced_mode = False
|
||||
self._multi_extrusion = False
|
||||
|
||||
Selection.selectionChanged.connect(self.propertyChanged)
|
||||
|
||||
Preferences.getInstance().preferenceChanged.connect(self._onPreferenceChanged)
|
||||
self._onPreferenceChanged("cura/active_mode")
|
||||
|
||||
Application.getInstance().globalContainerStackChanged.connect(self._onGlobalContainerChanged)
|
||||
self._onGlobalContainerChanged()
|
||||
|
||||
def event(self, event):
|
||||
return False
|
||||
|
||||
|
@ -55,5 +62,14 @@ class PerObjectSettingsTool(Tool):
|
|||
|
||||
def _onPreferenceChanged(self, preference):
|
||||
if preference == "cura/active_mode":
|
||||
enabled = Preferences.getInstance().getValue(preference)==1
|
||||
Application.getInstance().getController().toolEnabledChanged.emit(self._plugin_id, enabled)
|
||||
self._advanced_mode = Preferences.getInstance().getValue(preference) == 1
|
||||
self._updateEnabled()
|
||||
|
||||
def _onGlobalContainerChanged(self):
|
||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
if global_container_stack:
|
||||
self._multi_extrusion = global_container_stack.getProperty("machine_extruder_count", "value") > 1
|
||||
self._updateEnabled()
|
||||
|
||||
def _updateEnabled(self):
|
||||
Application.getInstance().getController().toolEnabledChanged.emit(self._plugin_id, self._advanced_mode or self._multi_extrusion)
|
|
@ -8,6 +8,7 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
|||
from UM.Scene.SceneNode import SceneNode
|
||||
from UM.Message import Message
|
||||
from UM.i18n import i18nCatalog
|
||||
from UM.Logger import Logger
|
||||
|
||||
import collections
|
||||
import json
|
||||
|
@ -25,6 +26,8 @@ catalog = i18nCatalog("cura")
|
|||
# The data is only sent when the user in question gave permission to do so. All data is anonymous and
|
||||
# no model files are being sent (Just a SHA256 hash of the model).
|
||||
class SliceInfo(Extension):
|
||||
info_url = "https://stats.youmagine.com/curastats/slice"
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
Application.getInstance().getOutputDeviceManager().writeStarted.connect(self._onWriteStarted)
|
||||
|
@ -43,34 +46,14 @@ class SliceInfo(Extension):
|
|||
|
||||
def _onWriteStarted(self, output_device):
|
||||
if not Preferences.getInstance().getValue("info/send_slice_info"):
|
||||
Logger.log("d", "'info/send_slice_info' is turned off.")
|
||||
return # Do nothing, user does not want to send data
|
||||
settings = Application.getInstance().getMachineManager().getWorkingProfile()
|
||||
|
||||
# Load all machine definitions and put them in machine_settings dict
|
||||
#setting_file_name = Application.getInstance().getActiveMachineInstance().getMachineSettings()._json_file
|
||||
machine_settings = {}
|
||||
#with open(setting_file_name, "rt", -1, "utf-8") as f:
|
||||
# data = json.load(f, object_pairs_hook = collections.OrderedDict)
|
||||
#machine_settings[os.path.basename(setting_file_name)] = copy.deepcopy(data)
|
||||
active_machine_definition= Application.getInstance().getMachineManager().getActiveMachineInstance().getMachineDefinition()
|
||||
data = active_machine_definition._json_data
|
||||
# Loop through inherited json files
|
||||
setting_file_name = active_machine_definition._path
|
||||
while True:
|
||||
if "inherits" in data:
|
||||
inherited_setting_file_name = os.path.dirname(setting_file_name) + "/" + data["inherits"]
|
||||
with open(inherited_setting_file_name, "rt", -1, "utf-8") as f:
|
||||
data = json.load(f, object_pairs_hook = collections.OrderedDict)
|
||||
machine_settings[os.path.basename(inherited_setting_file_name)] = copy.deepcopy(data)
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
profile_values = settings.getChangedSettings() # TODO: @UnusedVariable
|
||||
global_container_stack = Application.getInstance().getGlobalContainerStack()
|
||||
|
||||
# Get total material used (in mm^3)
|
||||
print_information = Application.getInstance().getPrintInformation()
|
||||
material_radius = 0.5 * settings.getSettingValue("material_diameter")
|
||||
material_radius = 0.5 * global_container_stack.getProperty("material_diameter", "value")
|
||||
material_used = math.pi * material_radius * material_radius * print_information.materialAmount #Volume of material used
|
||||
|
||||
# Get model information (bounding boxes, hashes and transformation matrix)
|
||||
|
@ -99,14 +82,26 @@ class SliceInfo(Extension):
|
|||
"processor": platform.processor(),
|
||||
"machine": platform.machine(),
|
||||
"platform": platform.platform(),
|
||||
"machine_settings": json.dumps(machine_settings),
|
||||
"settings": global_container_stack.serialize(), # global_container with references on used containers
|
||||
"version": Application.getInstance().getVersion(),
|
||||
"modelhash": "None",
|
||||
"printtime": str(print_information.currentPrintTime),
|
||||
"printtime": print_information.currentPrintTime.getDisplayString(),
|
||||
"filament": material_used,
|
||||
"language": Preferences.getInstance().getValue("general/language"),
|
||||
"materials_profiles ": {}
|
||||
}
|
||||
for container in global_container_stack.getContainers():
|
||||
container_id = container.getId()
|
||||
try:
|
||||
container_serialized = container.serialize()
|
||||
except NotImplementedError:
|
||||
Logger.log("w", "Container %s could not be serialized!", container_id)
|
||||
continue
|
||||
|
||||
if container_serialized:
|
||||
submitted_data["settings_%s" %(container_id)] = container_serialized # This can be anything, eg. INI, JSON, etc.
|
||||
else:
|
||||
Logger.log("i", "No data found in %s to be serialized!", container_id)
|
||||
|
||||
# Convert data to bytes
|
||||
submitted_data = urllib.parse.urlencode(submitted_data)
|
||||
|
@ -114,8 +109,8 @@ class SliceInfo(Extension):
|
|||
|
||||
# Submit data
|
||||
try:
|
||||
f = urllib.request.urlopen("https://stats.youmagine.com/curastats/slice", data = binary_data, timeout = 1)
|
||||
f = urllib.request.urlopen(self.info_url, data = binary_data, timeout = 1)
|
||||
Logger.log("i", "Sent anonymous slice info to %s", self.info_url)
|
||||
f.close()
|
||||
except Exception as e:
|
||||
print("Exception occured", e)
|
||||
|
||||
f.close()
|
||||
Logger.logException("e", e)
|
||||
|
|
|
@ -11,7 +11,7 @@ def getMetaData():
|
|||
"author": "Ultimaker",
|
||||
"version": "1.0",
|
||||
"description": catalog.i18nc("@info:whatsthis", "Submits anonymous slice info. Can be disabled through preferences."),
|
||||
"api": 2
|
||||
"api": 3
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,9 +10,11 @@ from UM.View.Renderer import Renderer
|
|||
|
||||
from UM.View.GL.OpenGL import OpenGL
|
||||
|
||||
from cura.ExtrudersModel import ExtrudersModel
|
||||
|
||||
import math
|
||||
|
||||
## Standard view for mesh models.
|
||||
## Standard view for mesh models.
|
||||
class SolidView(View):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
@ -22,6 +24,8 @@ class SolidView(View):
|
|||
self._enabled_shader = None
|
||||
self._disabled_shader = None
|
||||
|
||||
self._extruders_model = ExtrudersModel()
|
||||
|
||||
def beginRendering(self):
|
||||
scene = self.getController().getScene()
|
||||
renderer = self.getRenderer()
|
||||
|
@ -50,15 +54,37 @@ class SolidView(View):
|
|||
# TODO: Find a better way to handle this
|
||||
#if node.getBoundingBoxMesh():
|
||||
# renderer.queueNode(scene.getRoot(), mesh = node.getBoundingBoxMesh(),mode = Renderer.RenderLines)
|
||||
|
||||
uniforms = {}
|
||||
if self._extruders_model.rowCount() > 0:
|
||||
# Get color to render this mesh in from ExtrudersModel
|
||||
extruder_index = 0
|
||||
extruder_id = node.callDecoration("getActiveExtruder")
|
||||
if extruder_id:
|
||||
extruder_index = max(0, self._extruders_model.find("id", extruder_id))
|
||||
|
||||
extruder_color = self._extruders_model.getItem(extruder_index)["colour"]
|
||||
try:
|
||||
# Colors are passed as rgb hex strings (eg "#ffffff"), and the shader needs
|
||||
# an rgba list of floats (eg [1.0, 1.0, 1.0, 1.0])
|
||||
uniforms["diffuse_color"] = [
|
||||
int(extruder_color[1:3], 16) / 255,
|
||||
int(extruder_color[3:5], 16) / 255,
|
||||
int(extruder_color[5:7], 16) / 255,
|
||||
1.0
|
||||
]
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if hasattr(node, "_outside_buildarea"):
|
||||
if node._outside_buildarea:
|
||||
renderer.queueNode(node, shader = self._disabled_shader)
|
||||
else:
|
||||
renderer.queueNode(node, shader = self._enabled_shader)
|
||||
renderer.queueNode(node, shader = self._enabled_shader, uniforms = uniforms)
|
||||
else:
|
||||
renderer.queueNode(node, material = self._enabled_shader)
|
||||
renderer.queueNode(node, material = self._enabled_shader, uniforms = uniforms)
|
||||
if node.callDecoration("isGroup"):
|
||||
renderer.queueNode(scene.getRoot(), mesh = node.getBoundingBoxMesh(),mode = Renderer.RenderLines)
|
||||
renderer.queueNode(scene.getRoot(), mesh = node.getBoundingBoxMesh(), mode = Renderer.RenderLines)
|
||||
|
||||
def endRendering(self):
|
||||
pass
|
||||
|
|
53
plugins/UltimakerMachineActions/BedLevelMachineAction.py
Normal file
53
plugins/UltimakerMachineActions/BedLevelMachineAction.py
Normal file
|
@ -0,0 +1,53 @@
|
|||
from cura.MachineAction import MachineAction
|
||||
|
||||
from PyQt5.QtCore import pyqtSlot
|
||||
|
||||
from UM.Application import Application
|
||||
|
||||
from cura.PrinterOutputDevice import PrinterOutputDevice
|
||||
|
||||
class BedLevelMachineAction(MachineAction):
|
||||
def __init__(self):
|
||||
super().__init__("BedLevel", "Level bed")
|
||||
self._qml_url = "BedLevelMachineAction.qml"
|
||||
self._bed_level_position = 0
|
||||
|
||||
def _execute(self):
|
||||
pass
|
||||
|
||||
def _reset(self):
|
||||
self._bed_level_position = 0
|
||||
printer_output_devices = self._getPrinterOutputDevices()
|
||||
if printer_output_devices:
|
||||
printer_output_devices[0].homeBed()
|
||||
printer_output_devices[0].moveHead(0, 0, 3)
|
||||
printer_output_devices[0].homeHead()
|
||||
|
||||
def _getPrinterOutputDevices(self):
|
||||
return [printer_output_device for printer_output_device in Application.getInstance().getOutputDeviceManager().getOutputDevices() if isinstance(printer_output_device, PrinterOutputDevice)]
|
||||
|
||||
@pyqtSlot()
|
||||
def moveToNextLevelPosition(self):
|
||||
output_devices = self._getPrinterOutputDevices()
|
||||
if output_devices: # We found at least one output device
|
||||
output_device = output_devices[0]
|
||||
|
||||
if self._bed_level_position == 0:
|
||||
output_device.moveHead(0, 0, 3)
|
||||
output_device.homeHead()
|
||||
output_device.moveHead(0, 0, 3)
|
||||
output_device.moveHead(Application.getInstance().getGlobalContainerStack().getProperty("machine_width", "value") - 10, 0, 0)
|
||||
output_device.moveHead(0, 0, -3)
|
||||
self._bed_level_position += 1
|
||||
elif self._bed_level_position == 1:
|
||||
output_device.moveHead(0, 0, 3)
|
||||
output_device.moveHead(-Application.getInstance().getGlobalContainerStack().getProperty("machine_width", "value" ) / 2, Application.getInstance().getGlobalContainerStack().getProperty("machine_depth", "value") - 10, 0)
|
||||
output_device.moveHead(0, 0, -3)
|
||||
self._bed_level_position += 1
|
||||
elif self._bed_level_position == 2:
|
||||
output_device.moveHead(0, 0, 3)
|
||||
output_device.moveHead(-Application.getInstance().getGlobalContainerStack().getProperty("machine_width", "value") / 2 + 10, -(Application.getInstance().getGlobalContainerStack().getProperty("machine_depth", "value") + 10), 0)
|
||||
output_device.moveHead(0, 0, -3)
|
||||
self._bed_level_position += 1
|
||||
elif self._bed_level_position >= 3:
|
||||
self.setFinished()
|
85
plugins/UltimakerMachineActions/BedLevelMachineAction.qml
Normal file
85
plugins/UltimakerMachineActions/BedLevelMachineAction.qml
Normal file
|
@ -0,0 +1,85 @@
|
|||
// Copyright (c) 2016 Ultimaker B.V.
|
||||
// Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Window 2.1
|
||||
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
|
||||
Cura.MachineAction
|
||||
{
|
||||
anchors.fill: parent;
|
||||
Item
|
||||
{
|
||||
id: bedLevelMachineAction
|
||||
anchors.fill: parent;
|
||||
|
||||
UM.I18nCatalog { id: catalog; name: "cura"; }
|
||||
|
||||
Label
|
||||
{
|
||||
id: pageTitle
|
||||
width: parent.width
|
||||
text: catalog.i18nc("@title", "Bed Leveling")
|
||||
wrapMode: Text.WordWrap
|
||||
font.pointSize: 18;
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: pageDescription
|
||||
anchors.top: pageTitle.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label", "To make sure your prints will come out great, you can now adjust your buildplate. When you click 'Move to Next Position' the nozzle will move to the different positions that can be adjusted.")
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: bedlevelingText
|
||||
anchors.top: pageDescription.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label", "For every position; insert a piece of paper under the nozzle and adjust the print bed height. The print bed height is right when the paper is slightly gripped by the tip of the nozzle.")
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
id: bedlevelingWrapper
|
||||
anchors.top: bedlevelingText.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
height: skipBedlevelingButton.height
|
||||
width: bedlevelingButton.width + skipBedlevelingButton.width + UM.Theme.getSize("default_margin").height < bedLevelMachineAction.width ? bedlevelingButton.width + skipBedlevelingButton.width + UM.Theme.getSize("default_margin").height : bedLevelMachineAction.width
|
||||
Button
|
||||
{
|
||||
id: bedlevelingButton
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
text: catalog.i18nc("@action:button","Move to Next Position");
|
||||
onClicked:
|
||||
{
|
||||
manager.moveToNextLevelPosition()
|
||||
}
|
||||
}
|
||||
|
||||
Button
|
||||
{
|
||||
id: skipBedlevelingButton
|
||||
anchors.top: parent.width < bedLevelMachineAction.width ? parent.top : bedlevelingButton.bottom
|
||||
anchors.topMargin: parent.width < bedLevelMachineAction.width ? 0 : UM.Theme.getSize("default_margin").height/2
|
||||
anchors.left: parent.width < bedLevelMachineAction.width ? bedlevelingButton.right : parent.left
|
||||
anchors.leftMargin: parent.width < bedLevelMachineAction.width ? UM.Theme.getSize("default_margin").width : 0
|
||||
text: catalog.i18nc("@action:button","Skip bed leveling");
|
||||
onClicked:
|
||||
{
|
||||
manager.setFinished()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
160
plugins/UltimakerMachineActions/UMOCheckupMachineAction.py
Normal file
160
plugins/UltimakerMachineActions/UMOCheckupMachineAction.py
Normal file
|
@ -0,0 +1,160 @@
|
|||
from cura.MachineAction import MachineAction
|
||||
from cura.PrinterOutputDevice import PrinterOutputDevice
|
||||
from UM.Application import Application
|
||||
from PyQt5.QtCore import pyqtSlot, pyqtSignal, pyqtProperty
|
||||
|
||||
class UMOCheckupMachineAction(MachineAction):
|
||||
def __init__(self):
|
||||
super().__init__("UMOCheckup", "Checkup")
|
||||
self._qml_url = "UMOCheckupMachineAction.qml"
|
||||
self._hotend_target_temp = 180
|
||||
self._bed_target_temp = 60
|
||||
self._output_device = None
|
||||
self._bed_test_completed = False
|
||||
self._hotend_test_completed = False
|
||||
|
||||
# Endstop tests
|
||||
self._x_min_endstop_test_completed = False
|
||||
self._y_min_endstop_test_completed = False
|
||||
self._z_min_endstop_test_completed = False
|
||||
|
||||
self._check_started = False
|
||||
|
||||
Application.getInstance().getOutputDeviceManager().outputDevicesChanged.connect(self._onOutputDevicesChanged)
|
||||
|
||||
|
||||
onBedTestCompleted = pyqtSignal()
|
||||
onHotendTestCompleted = pyqtSignal()
|
||||
|
||||
onXMinEndstopTestCompleted = pyqtSignal()
|
||||
onYMinEndstopTestCompleted = pyqtSignal()
|
||||
onZMinEndstopTestCompleted = pyqtSignal()
|
||||
|
||||
bedTemperatureChanged = pyqtSignal()
|
||||
hotendTemperatureChanged = pyqtSignal()
|
||||
|
||||
def _onOutputDevicesChanged(self):
|
||||
# Check if this action was started, but no output device was found the first time.
|
||||
# If so, re-try now that an output device has been added/removed.
|
||||
if self._output_device is None and self._check_started:
|
||||
self.startCheck()
|
||||
|
||||
|
||||
def _getPrinterOutputDevices(self):
|
||||
return [printer_output_device for printer_output_device in
|
||||
Application.getInstance().getOutputDeviceManager().getOutputDevices() if
|
||||
isinstance(printer_output_device, PrinterOutputDevice)]
|
||||
|
||||
def _reset(self):
|
||||
if self._output_device:
|
||||
self._output_device.bedTemperatureChanged.disconnect(self.bedTemperatureChanged)
|
||||
self._output_device.hotendTemperaturesChanged.disconnect(self.hotendTemperatureChanged)
|
||||
self._output_device.bedTemperatureChanged.disconnect(self._onBedTemperatureChanged)
|
||||
self._output_device.hotendTemperaturesChanged.disconnect(self._onHotendTemperatureChanged)
|
||||
self._output_device.endstopStateChanged.disconnect(self._onEndstopStateChanged)
|
||||
try:
|
||||
self._output_device.stopPollEndstop()
|
||||
except AttributeError: # Connection is probably not a USB connection. Something went pretty wrong if this happens.
|
||||
pass
|
||||
self._output_device = None
|
||||
|
||||
self._check_started = False
|
||||
|
||||
# Ensure everything is reset (and right signals are emitted again)
|
||||
self._bed_test_completed = False
|
||||
self.onBedTestCompleted.emit()
|
||||
self._hotend_test_completed = False
|
||||
self.onHotendTestCompleted.emit()
|
||||
|
||||
self._x_min_endstop_test_completed = False
|
||||
self.onXMinEndstopTestCompleted.emit()
|
||||
self._y_min_endstop_test_completed = False
|
||||
self.onYMinEndstopTestCompleted.emit()
|
||||
self._z_min_endstop_test_completed = False
|
||||
self.onZMinEndstopTestCompleted.emit()
|
||||
|
||||
@pyqtProperty(bool, notify = onBedTestCompleted)
|
||||
def bedTestCompleted(self):
|
||||
return self._bed_test_completed
|
||||
|
||||
@pyqtProperty(bool, notify = onHotendTestCompleted)
|
||||
def hotendTestCompleted(self):
|
||||
return self._hotend_test_completed
|
||||
|
||||
@pyqtProperty(bool, notify = onXMinEndstopTestCompleted)
|
||||
def xMinEndstopTestCompleted(self):
|
||||
return self._x_min_endstop_test_completed
|
||||
|
||||
@pyqtProperty(bool, notify=onYMinEndstopTestCompleted)
|
||||
def yMinEndstopTestCompleted(self):
|
||||
return self._y_min_endstop_test_completed
|
||||
|
||||
@pyqtProperty(bool, notify=onZMinEndstopTestCompleted)
|
||||
def zMinEndstopTestCompleted(self):
|
||||
return self._z_min_endstop_test_completed
|
||||
|
||||
@pyqtProperty(float, notify = bedTemperatureChanged)
|
||||
def bedTemperature(self):
|
||||
if not self._output_device:
|
||||
return 0
|
||||
return self._output_device.bedTemperature
|
||||
|
||||
@pyqtProperty(float, notify=hotendTemperatureChanged)
|
||||
def hotendTemperature(self):
|
||||
if not self._output_device:
|
||||
return 0
|
||||
return self._output_device.hotendTemperatures[0]
|
||||
|
||||
def _onHotendTemperatureChanged(self):
|
||||
if not self._output_device:
|
||||
return
|
||||
if not self._hotend_test_completed:
|
||||
if self._output_device.hotendTemperatures[0] + 10 > self._hotend_target_temp and self._output_device.hotendTemperatures[0] - 10 < self._hotend_target_temp:
|
||||
self._hotend_test_completed = True
|
||||
self.onHotendTestCompleted.emit()
|
||||
|
||||
def _onBedTemperatureChanged(self):
|
||||
if not self._output_device:
|
||||
return
|
||||
if not self._bed_test_completed:
|
||||
if self._output_device.bedTemperature + 5 > self._bed_target_temp and self._output_device.bedTemperature - 5 < self._bed_target_temp:
|
||||
self._bed_test_completed = True
|
||||
self.onBedTestCompleted.emit()
|
||||
|
||||
def _onEndstopStateChanged(self, switch_type, state):
|
||||
if state:
|
||||
if switch_type == "x_min":
|
||||
self._x_min_endstop_test_completed = True
|
||||
self.onXMinEndstopTestCompleted.emit()
|
||||
elif switch_type == "y_min":
|
||||
self._y_min_endstop_test_completed = True
|
||||
self.onYMinEndstopTestCompleted.emit()
|
||||
elif switch_type == "z_min":
|
||||
self._z_min_endstop_test_completed = True
|
||||
self.onZMinEndstopTestCompleted.emit()
|
||||
|
||||
@pyqtSlot()
|
||||
def startCheck(self):
|
||||
self._check_started = True
|
||||
output_devices = self._getPrinterOutputDevices()
|
||||
if output_devices:
|
||||
self._output_device = output_devices[0]
|
||||
try:
|
||||
self._output_device.startPollEndstop()
|
||||
self._output_device.bedTemperatureChanged.connect(self.bedTemperatureChanged)
|
||||
self._output_device.hotendTemperaturesChanged.connect(self.hotendTemperatureChanged)
|
||||
self._output_device.bedTemperatureChanged.connect(self._onBedTemperatureChanged)
|
||||
self._output_device.hotendTemperaturesChanged.connect(self._onHotendTemperatureChanged)
|
||||
self._output_device.endstopStateChanged.connect(self._onEndstopStateChanged)
|
||||
except AttributeError: # Connection is probably not a USB connection. Something went pretty wrong if this happens.
|
||||
pass
|
||||
|
||||
@pyqtSlot()
|
||||
def heatupHotend(self):
|
||||
if self._output_device is not None:
|
||||
self._output_device.setTargetHotendTemperature(0, self._hotend_target_temp)
|
||||
|
||||
@pyqtSlot()
|
||||
def heatupBed(self):
|
||||
if self._output_device is not None:
|
||||
self._output_device.setTargetBedTemperature(self._bed_target_temp)
|
271
plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml
Normal file
271
plugins/UltimakerMachineActions/UMOCheckupMachineAction.qml
Normal file
|
@ -0,0 +1,271 @@
|
|||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Window 2.1
|
||||
|
||||
Cura.MachineAction
|
||||
{
|
||||
anchors.fill: parent;
|
||||
Item
|
||||
{
|
||||
id: checkupMachineAction
|
||||
anchors.fill: parent;
|
||||
property int leftRow: checkupMachineAction.width * 0.40
|
||||
property int rightRow: checkupMachineAction.width * 0.60
|
||||
UM.I18nCatalog { id: catalog; name:"cura"}
|
||||
Label
|
||||
{
|
||||
id: pageTitle
|
||||
width: parent.width
|
||||
text: catalog.i18nc("@title", "Check Printer")
|
||||
wrapMode: Text.WordWrap
|
||||
font.pointSize: 18;
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: pageDescription
|
||||
anchors.top: pageTitle.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","It's a good idea to do a few sanity checks on your Ultimaker. You can skip this step if you know your machine is functional");
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
id: startStopButtons
|
||||
anchors.top: pageDescription.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
height: childrenRect.height
|
||||
width: startCheckButton.width + skipCheckButton.width + UM.Theme.getSize("default_margin").height < checkupMachineAction.width ? startCheckButton.width + skipCheckButton.width + UM.Theme.getSize("default_margin").height : checkupMachineAction.width
|
||||
Button
|
||||
{
|
||||
id: startCheckButton
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
text: catalog.i18nc("@action:button","Start Printer Check");
|
||||
onClicked:
|
||||
{
|
||||
checkupContent.visible = true
|
||||
startCheckButton.enabled = false
|
||||
manager.startCheck()
|
||||
}
|
||||
}
|
||||
|
||||
Button
|
||||
{
|
||||
id: skipCheckButton
|
||||
anchors.top: parent.width < checkupMachineAction.width ? parent.top : startCheckButton.bottom
|
||||
anchors.topMargin: parent.width < checkupMachineAction.width ? 0 : UM.Theme.getSize("default_margin").height/2
|
||||
anchors.left: parent.width < checkupMachineAction.width ? startCheckButton.right : parent.left
|
||||
anchors.leftMargin: parent.width < checkupMachineAction.width ? UM.Theme.getSize("default_margin").width : 0
|
||||
text: catalog.i18nc("@action:button", "Skip Printer Check");
|
||||
onClicked: manager.setFinished()
|
||||
}
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
id: checkupContent
|
||||
anchors.top: startStopButtons.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
visible: false
|
||||
width: parent.width
|
||||
height: 250
|
||||
//////////////////////////////////////////////////////////
|
||||
Label
|
||||
{
|
||||
id: connectionLabel
|
||||
width: checkupMachineAction.leftRow
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","Connection: ")
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: connectionStatus
|
||||
width: checkupMachineAction.rightRow
|
||||
anchors.left: connectionLabel.right
|
||||
anchors.top: parent.top
|
||||
wrapMode: Text.WordWrap
|
||||
text: Cura.USBPrinterManager.connectedPrinterList.rowCount() > 0 || base.addOriginalProgress.checkUp[0] ? catalog.i18nc("@info:status","Done"):catalog.i18nc("@info:status","Incomplete")
|
||||
}
|
||||
//////////////////////////////////////////////////////////
|
||||
Label
|
||||
{
|
||||
id: endstopXLabel
|
||||
width: checkupMachineAction.leftRow
|
||||
anchors.left: parent.left
|
||||
anchors.top: connectionLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","Min endstop X: ")
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: endstopXStatus
|
||||
width: checkupMachineAction.rightRow
|
||||
anchors.left: endstopXLabel.right
|
||||
anchors.top: connectionLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: manager.xMinEndstopTestCompleted ? catalog.i18nc("@info:status","Works") : catalog.i18nc("@info:status","Not checked")
|
||||
}
|
||||
//////////////////////////////////////////////////////////////
|
||||
Label
|
||||
{
|
||||
id: endstopYLabel
|
||||
width: checkupMachineAction.leftRow
|
||||
anchors.left: parent.left
|
||||
anchors.top: endstopXLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","Min endstop Y: ")
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: endstopYStatus
|
||||
width: checkupMachineAction.rightRow
|
||||
anchors.left: endstopYLabel.right
|
||||
anchors.top: endstopXLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: manager.yMinEndstopTestCompleted ? catalog.i18nc("@info:status","Works") : catalog.i18nc("@info:status","Not checked")
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
Label
|
||||
{
|
||||
id: endstopZLabel
|
||||
width: checkupMachineAction.leftRow
|
||||
anchors.left: parent.left
|
||||
anchors.top: endstopYLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","Min endstop Z: ")
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: endstopZStatus
|
||||
width: checkupMachineAction.rightRow
|
||||
anchors.left: endstopZLabel.right
|
||||
anchors.top: endstopYLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: manager.zMinEndstopTestCompleted ? catalog.i18nc("@info:status","Works") : catalog.i18nc("@info:status","Not checked")
|
||||
}
|
||||
////////////////////////////////////////////////////////////
|
||||
Label
|
||||
{
|
||||
id: nozzleTempLabel
|
||||
width: checkupMachineAction.leftRow
|
||||
anchors.left: parent.left
|
||||
anchors.top: endstopZLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","Nozzle temperature check: ")
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: nozzleTempStatus
|
||||
width: checkupMachineAction.rightRow * 0.4
|
||||
anchors.top: nozzleTempLabel.top
|
||||
anchors.left: nozzleTempLabel.right
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@info:status","Not checked")
|
||||
}
|
||||
Item
|
||||
{
|
||||
id: nozzleTempButton
|
||||
width: checkupMachineAction.rightRow * 0.3
|
||||
height: nozzleTemp.height
|
||||
anchors.top: nozzleTempLabel.top
|
||||
anchors.left: bedTempStatus.right
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width/2
|
||||
Button
|
||||
{
|
||||
height: nozzleTemp.height - 2
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: catalog.i18nc("@action:button","Start Heating")
|
||||
onClicked:
|
||||
{
|
||||
manager.heatupHotend()
|
||||
nozzleTempStatus.text = catalog.i18nc("@info:progress","Checking")
|
||||
}
|
||||
}
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: nozzleTemp
|
||||
anchors.top: nozzleTempLabel.top
|
||||
anchors.left: nozzleTempButton.right
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
width: checkupMachineAction.rightRow * 0.2
|
||||
wrapMode: Text.WordWrap
|
||||
text: manager.hotendTemperature + "°C"
|
||||
font.bold: true
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
Label
|
||||
{
|
||||
id: bedTempLabel
|
||||
width: checkupMachineAction.leftRow
|
||||
anchors.left: parent.left
|
||||
anchors.top: nozzleTempLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","bed temperature check:")
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: bedTempStatus
|
||||
width: checkupMachineAction.rightRow * 0.4
|
||||
anchors.top: bedTempLabel.top
|
||||
anchors.left: bedTempLabel.right
|
||||
wrapMode: Text.WordWrap
|
||||
text: manager.bedTestCompleted ? catalog.i18nc("@info:status","Not checked"): catalog.i18nc("@info:status","Checked")
|
||||
}
|
||||
Item
|
||||
{
|
||||
id: bedTempButton
|
||||
width: checkupMachineAction.rightRow * 0.3
|
||||
height: bedTemp.height
|
||||
anchors.top: bedTempLabel.top
|
||||
anchors.left: bedTempStatus.right
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width/2
|
||||
Button
|
||||
{
|
||||
height: bedTemp.height - 2
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: catalog.i18nc("@action:button","Start Heating")
|
||||
onClicked:
|
||||
{
|
||||
manager.heatupBed()
|
||||
}
|
||||
}
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: bedTemp
|
||||
width: checkupMachineAction.rightRow * 0.2
|
||||
anchors.top: bedTempLabel.top
|
||||
anchors.left: bedTempButton.right
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
wrapMode: Text.WordWrap
|
||||
text: manager.bedTemperature + "°C"
|
||||
font.bold: true
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: resultText
|
||||
visible: false
|
||||
anchors.top: bedTemp.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.left: parent.left
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label", "Everything is in order! You're done with your CheckUp.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
from cura.MachineAction import MachineAction
|
||||
|
||||
class UpgradeFirmwareMachineAction(MachineAction):
|
||||
def __init__(self):
|
||||
super().__init__("UpgradeFirmware", "Upgrade Firmware")
|
||||
self._qml_url = "UpgradeFirmwareMachineAction.qml"
|
|
@ -0,0 +1,85 @@
|
|||
// Copyright (c) 2016 Ultimaker B.V.
|
||||
// Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Window 2.1
|
||||
|
||||
import UM 1.2 as UM
|
||||
import Cura 1.0 as Cura
|
||||
|
||||
|
||||
Cura.MachineAction
|
||||
{
|
||||
anchors.fill: parent;
|
||||
Item
|
||||
{
|
||||
id: upgradeFirmwareMachineAction
|
||||
anchors.fill: parent;
|
||||
UM.I18nCatalog { id: catalog; name:"cura"}
|
||||
|
||||
Label
|
||||
{
|
||||
id: pageTitle
|
||||
width: parent.width
|
||||
text: catalog.i18nc("@title", "Upgrade Firmware")
|
||||
wrapMode: Text.WordWrap
|
||||
font.pointSize: 18
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: pageDescription
|
||||
anchors.top: pageTitle.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label", "Firmware is the piece of software running directly on your 3D printer. This firmware controls the step motors, regulates the temperature and ultimately makes your printer work.")
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: upgradeText1
|
||||
anchors.top: pageDescription.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label", "The firmware shipping with new Ultimakers works, but upgrades have been made to make better prints, and make calibration easier.");
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: upgradeText2
|
||||
anchors.top: upgradeText1.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label", "Cura requires these new features and thus your firmware will most likely need to be upgraded. You can do so now.");
|
||||
}
|
||||
Item
|
||||
{
|
||||
anchors.top: upgradeText2.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: upgradeButton.width + skipUpgradeButton.width + UM.Theme.getSize("default_margin").height < upgradeFirmwareMachineAction.width ? upgradeButton.width + skipUpgradeButton.width + UM.Theme.getSize("default_margin").height : upgradeFirmwareMachineAction.width
|
||||
Button
|
||||
{
|
||||
id: upgradeButton
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
text: catalog.i18nc("@action:button","Upgrade to Marlin Firmware");
|
||||
onClicked: Cura.USBPrinterManager.updateAllFirmware()
|
||||
}
|
||||
Button
|
||||
{
|
||||
id: skipUpgradeButton
|
||||
anchors.top: parent.width < upgradeFirmwareMachineAction.width ? parent.top : upgradeButton.bottom
|
||||
anchors.topMargin: parent.width < upgradeFirmwareMachineAction.width ? 0 : UM.Theme.getSize("default_margin").height / 2
|
||||
anchors.left: parent.width < upgradeFirmwareMachineAction.width ? upgradeButton.right : parent.left
|
||||
anchors.leftMargin: parent.width < upgradeFirmwareMachineAction.width ? UM.Theme.getSize("default_margin").width : 0
|
||||
text: catalog.i18nc("@action:button", "Skip Upgrade");
|
||||
onClicked: manager.setFinished()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
23
plugins/UltimakerMachineActions/__init__.py
Normal file
23
plugins/UltimakerMachineActions/__init__.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Copyright (c) 2016 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
from . import BedLevelMachineAction
|
||||
from . import UpgradeFirmwareMachineAction
|
||||
from . import UMOCheckupMachineAction
|
||||
|
||||
from UM.i18n import i18nCatalog
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
def getMetaData():
|
||||
return {
|
||||
"plugin": {
|
||||
"name": catalog.i18nc("@label", "Ultimaker machine actions"),
|
||||
"author": "Ultimaker",
|
||||
"version": "1.0",
|
||||
"description": catalog.i18nc("@info:whatsthis", "Provides machine actions for Ultimaker machines (such as bed leveling wizard, selecting upgrades, etc)"),
|
||||
"api": 3
|
||||
}
|
||||
}
|
||||
|
||||
def register(app):
|
||||
return { "machine_action": [BedLevelMachineAction.BedLevelMachineAction(), UpgradeFirmwareMachineAction.UpgradeFirmwareMachineAction(), UMOCheckupMachineAction.UMOCheckupMachineAction()]}
|
4
pytest.ini
Normal file
4
pytest.ini
Normal file
|
@ -0,0 +1,4 @@
|
|||
[pytest]
|
||||
testpaths = tests
|
||||
python_files = Test*.py
|
||||
python_classes = Test
|
|
@ -12,7 +12,8 @@
|
|||
"icon": "icon_ultimaker2.png",
|
||||
"platform": "ultimaker2_platform.obj",
|
||||
"platform_texture": "Ultimaker2backplate.png",
|
||||
"platform_offset": [9, 0, 0]
|
||||
"platform_offset": [9, 0, 0],
|
||||
"supported_actions":["UpgradeFirmware"]
|
||||
},
|
||||
"overrides": {
|
||||
"machine_start_gcode" : {
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
"file_formats": "text/x-gcode",
|
||||
"icon": "icon_ultimaker2.png",
|
||||
"platform": "ultimaker2_platform.obj",
|
||||
"platform_texture": "Ultimaker2Extendedbackplate.png"
|
||||
"platform_texture": "Ultimaker2Extendedbackplate.png",
|
||||
"supported_actions": ["UpgradeFirmware"]
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
"category": "Ultimaker",
|
||||
"file_formats": "text/x-gcode",
|
||||
"platform": "ultimaker2_platform.obj",
|
||||
"platform_texture": "Ultimaker2ExtendedPlusbackplate.png"
|
||||
"platform_texture": "Ultimaker2ExtendedPlusbackplate.png",
|
||||
"supported_actions":["UpgradeFirmware"]
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
|
|
|
@ -11,7 +11,8 @@
|
|||
"icon": "icon_ultimaker2.png",
|
||||
"platform": "ultimaker2go_platform.obj",
|
||||
"platform_texture": "Ultimaker2Gobackplate.png",
|
||||
"platform_offset": [0, 0, 0]
|
||||
"platform_offset": [0, 0, 0],
|
||||
"supported_actions":["UpgradeFirmware"]
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
"has_variants": true,
|
||||
"has_materials": true,
|
||||
"has_machine_materials": true,
|
||||
"has_machine_quality": true
|
||||
"has_machine_quality": true,
|
||||
"supported_actions":["UpgradeFirmware"]
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
|
|
|
@ -14,12 +14,7 @@
|
|||
"has_materials": true,
|
||||
"preferred_material": "*pla*",
|
||||
"preferred_quality": "*normal*",
|
||||
"pages": [
|
||||
"SelectUpgradedParts",
|
||||
"UpgradeFirmware",
|
||||
"UltimakerCheckup",
|
||||
"BedLeveling"
|
||||
]
|
||||
"supported_actions":["UMOCheckup", "UpgradeFirmware", "BedLevel"]
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
|
|
|
@ -11,11 +11,7 @@
|
|||
"icon": "icon_ultimaker.png",
|
||||
"platform": "ultimaker2_platform.obj",
|
||||
"platform_texture": "UltimakerPlusbackplate.png",
|
||||
"pages": [
|
||||
"UpgradeFirmware",
|
||||
"UltimakerCheckup",
|
||||
"BedLeveling"
|
||||
]
|
||||
"supported_actions":["UMOCheckup", "UpgradeFirmware", "BedLevel"]
|
||||
},
|
||||
|
||||
"overrides": {
|
||||
|
|
|
@ -18,6 +18,7 @@ UM.Dialog
|
|||
title: catalog.i18nc("@title:window", "Add Printer")
|
||||
property string activeManufacturer: "Ultimaker";
|
||||
|
||||
signal machineAdded(string id)
|
||||
function getMachineName()
|
||||
{
|
||||
var name = machineList.model.getItem(machineList.currentIndex).name
|
||||
|
@ -162,6 +163,7 @@ UM.Dialog
|
|||
base.visible = false
|
||||
var item = machineList.model.getItem(machineList.currentIndex);
|
||||
Cura.MachineManager.addMachine(machineName.text, item.id)
|
||||
base.machineAdded(item.id) // Emit signal that the user added a machine.
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -771,6 +771,38 @@ UM.MainWindow
|
|||
AddMachineDialog
|
||||
{
|
||||
id: addMachineDialog
|
||||
onMachineAdded:
|
||||
{
|
||||
machineActionsWizard.start(id)
|
||||
}
|
||||
}
|
||||
|
||||
// Dialog to handle first run machine actions
|
||||
UM.Wizard
|
||||
{
|
||||
id: machineActionsWizard;
|
||||
|
||||
title: catalog.i18nc("@title:window", "Add Printer")
|
||||
property var machine;
|
||||
|
||||
function start(id)
|
||||
{
|
||||
var actions = Cura.MachineActionManager.getFirstStartActions(id)
|
||||
resetPages() // Remove previous pages
|
||||
|
||||
for (var i = 0; i < actions.length; i++)
|
||||
{
|
||||
actions[i].displayItem.reset()
|
||||
machineActionsWizard.appendPage(actions[i].displayItem, catalog.i18nc("@title", actions[i].label));
|
||||
}
|
||||
|
||||
//Only start if there are actions to perform.
|
||||
if (actions.length > 0)
|
||||
{
|
||||
machineActionsWizard.currentPage = 0;
|
||||
show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections
|
||||
|
|
15
resources/qml/MachineAction.qml
Normal file
15
resources/qml/MachineAction.qml
Normal file
|
@ -0,0 +1,15 @@
|
|||
import QtQuick 2.2
|
||||
|
||||
Item
|
||||
{
|
||||
id: contentItem
|
||||
// Connect the finished property change to completed signal.
|
||||
property var finished: manager.finished
|
||||
onFinishedChanged: if(manager.finished) {completed()}
|
||||
signal completed()
|
||||
|
||||
function reset()
|
||||
{
|
||||
manager.reset()
|
||||
}
|
||||
}
|
|
@ -36,21 +36,68 @@ UM.ManagementPage
|
|||
renameEnabled: base.currentItem != null
|
||||
activateEnabled: base.currentItem != null && base.currentItem.id != Cura.MachineManager.activeMachineId
|
||||
|
||||
Flow
|
||||
Item
|
||||
{
|
||||
anchors.fill: parent;
|
||||
spacing: UM.Theme.getSize("default_margin").height;
|
||||
visible: base.currentItem != null
|
||||
anchors.fill: parent
|
||||
|
||||
Label
|
||||
{
|
||||
id: machineName
|
||||
text: base.currentItem && base.currentItem.name ? base.currentItem.name : ""
|
||||
font: UM.Theme.getFont("large")
|
||||
width: parent.width
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Label { text: catalog.i18nc("@label", "Type"); width: parent.width * 0.2; }
|
||||
Label { text: base.currentItem && base.currentItem.typeName ? base.currentItem.typeName : ""; width: parent.width * 0.7; }
|
||||
Row
|
||||
{
|
||||
id: machineActions
|
||||
anchors.left: parent.left
|
||||
anchors.top: machineName.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
|
||||
Repeater
|
||||
{
|
||||
id: machineActionRepeater
|
||||
model: Cura.MachineActionManager.getSupportedActions(Cura.MachineManager.activeDefinitionId)
|
||||
|
||||
Button
|
||||
{
|
||||
text: machineActionRepeater.model[index].label;
|
||||
onClicked:
|
||||
{
|
||||
actionDialog.content = machineActionRepeater.model[index].displayItem
|
||||
machineActionRepeater.model[index].displayItem.reset()
|
||||
actionDialog.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UM.Dialog
|
||||
{
|
||||
id: actionDialog
|
||||
property var content
|
||||
onContentChanged:
|
||||
{
|
||||
contents = content;
|
||||
content.onCompleted.connect(hide)
|
||||
}
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
anchors.top: machineActions.visible ? machineActions.bottom : machineActions.anchors.top
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
spacing: UM.Theme.getSize("default_margin").height
|
||||
|
||||
Label { text: catalog.i18nc("@label", "Type") }
|
||||
Label { text: base.currentItem ? base.currentItem.metadata.definition_name : "" }
|
||||
}
|
||||
|
||||
UM.I18nCatalog { id: catalog; name: "uranium"; }
|
||||
|
||||
|
|
|
@ -34,34 +34,34 @@ Item {
|
|||
|
||||
property string tooltipText:
|
||||
{
|
||||
var affects = settingDefinitionsModel.getRequiredBy(definition.key, "value")
|
||||
var affected_by = settingDefinitionsModel.getRequires(definition.key, "value")
|
||||
var affects = settingDefinitionsModel.getRequiredBy(definition.key, "value")
|
||||
var affected_by = settingDefinitionsModel.getRequires(definition.key, "value")
|
||||
|
||||
var affected_by_list = ""
|
||||
for(var i in affected_by)
|
||||
{
|
||||
affected_by_list += "<li>%1</li>\n".arg(affected_by[i].label)
|
||||
}
|
||||
var affected_by_list = ""
|
||||
for(var i in affected_by)
|
||||
{
|
||||
affected_by_list += "<li>%1</li>\n".arg(affected_by[i].label)
|
||||
}
|
||||
|
||||
var affects_list = ""
|
||||
for(var i in affects)
|
||||
{
|
||||
affects_list += "<li>%1</li>\n".arg(affects[i].label)
|
||||
}
|
||||
var affects_list = ""
|
||||
for(var i in affects)
|
||||
{
|
||||
affects_list += "<li>%1</li>\n".arg(affects[i].label)
|
||||
}
|
||||
|
||||
var tooltip = "<b>%1</b>\n<p>%2</p>".arg(definition.label).arg(definition.description)
|
||||
var tooltip = "<b>%1</b>\n<p>%2</p>".arg(definition.label).arg(definition.description)
|
||||
|
||||
if(affects_list != "")
|
||||
{
|
||||
tooltip += "<br/><b>%1</b>\n<ul>\n%2</ul>".arg(catalog.i18nc("@label", "Affects")).arg(affects_list)
|
||||
}
|
||||
if(affects_list != "")
|
||||
{
|
||||
tooltip += "<br/><b>%1</b>\n<ul>\n%2</ul>".arg(catalog.i18nc("@label", "Affects")).arg(affects_list)
|
||||
}
|
||||
|
||||
if(affected_by_list != "")
|
||||
{
|
||||
tooltip += "<br/><b>%1</b>\n<ul>\n%2</ul>".arg(catalog.i18nc("@label", "Affected By")).arg(affected_by_list)
|
||||
}
|
||||
if(affected_by_list != "")
|
||||
{
|
||||
tooltip += "<br/><b>%1</b>\n<ul>\n%2</ul>".arg(catalog.i18nc("@label", "Affected By")).arg(affected_by_list)
|
||||
}
|
||||
|
||||
return tooltip
|
||||
return tooltip
|
||||
}
|
||||
|
||||
MouseArea
|
||||
|
@ -236,7 +236,7 @@ Item {
|
|||
{
|
||||
id: controlContainer;
|
||||
|
||||
enabled: provider.isValueUsed
|
||||
enabled: propertyProvider.isValueUsed
|
||||
|
||||
anchors.right: parent.right;
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||
|
|
|
@ -19,11 +19,6 @@ SettingItem
|
|||
border.width: UM.Theme.getSize("default_lining").width
|
||||
border.color: !enabled ? UM.Theme.getColor("setting_control_disabled_border") : hovered ? UM.Theme.getColor("setting_control_border_highlight") : UM.Theme.getColor("setting_control_border")
|
||||
|
||||
property variant parentValue: value //From parent loader
|
||||
function notifyReset() {
|
||||
input.text = format(parentValue)
|
||||
}
|
||||
|
||||
color: {
|
||||
if (!enabled)
|
||||
{
|
||||
|
|
|
@ -29,8 +29,10 @@ ScrollView
|
|||
model: UM.SettingDefinitionsModel {
|
||||
id: definitionsModel;
|
||||
containerId: Cura.MachineManager.activeDefinitionId
|
||||
exclude: ["machine_settings"]
|
||||
visibilityHandler: UM.SettingPreferenceVisibilityHandler { }
|
||||
exclude: ["machine_settings"]
|
||||
expanded: Printer.expandedCategories
|
||||
onExpandedChanged: Printer.setExpandedCategories(expanded)
|
||||
|
||||
filter:
|
||||
{
|
||||
|
|
|
@ -13,7 +13,7 @@ Column
|
|||
id: base;
|
||||
|
||||
property int totalHeightHeader: childrenRect.height
|
||||
property int currentExtruderIndex;
|
||||
property int currentExtruderIndex: -1;
|
||||
|
||||
spacing: UM.Theme.getSize("default_margin").height
|
||||
|
||||
|
@ -50,7 +50,7 @@ Column
|
|||
text: Cura.MachineManager.activeMachineName;
|
||||
|
||||
height: UM.Theme.getSize("setting_control").height
|
||||
tooltip: Cura.MachineManager.activeMachineName;
|
||||
tooltip: Cura.MachineManager.activeMachineName
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
style: UM.Theme.styles.sidebar_header_button
|
||||
|
||||
|
@ -111,14 +111,25 @@ Column
|
|||
|
||||
model: Cura.ExtrudersModel { id: extrudersModel; addGlobal: true }
|
||||
|
||||
Connections
|
||||
{
|
||||
target: Cura.MachineManager
|
||||
onGlobalContainerChanged:
|
||||
{
|
||||
base.currentExtruderIndex = -1;
|
||||
ExtruderManager.setActiveExtruderIndex(index);
|
||||
}
|
||||
}
|
||||
|
||||
delegate: Button
|
||||
{
|
||||
height: ListView.view.height
|
||||
width: ListView.view.width / extrudersModel.rowCount()
|
||||
|
||||
text: model.name
|
||||
exclusiveGroup: extruderMenuGroup;
|
||||
checkable: true;
|
||||
tooltip: model.name
|
||||
exclusiveGroup: extruderMenuGroup
|
||||
checkable: true
|
||||
checked: base.currentExtruderIndex == index
|
||||
|
||||
onClicked:
|
||||
|
@ -144,6 +155,7 @@ Column
|
|||
Rectangle
|
||||
{
|
||||
id: swatch
|
||||
visible: index > -1
|
||||
height: UM.Theme.getSize("setting_control").height / 2
|
||||
width: height
|
||||
anchors.left: parent.left
|
||||
|
@ -158,8 +170,8 @@ Column
|
|||
Label
|
||||
{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: swatch.right
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width / 2
|
||||
anchors.left: swatch.visible ? swatch.right : parent.left
|
||||
anchors.leftMargin: swatch.visible ? UM.Theme.getSize("default_margin").width / 2 : UM.Theme.getSize("default_margin").width
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width / 2
|
||||
|
||||
|
@ -216,6 +228,7 @@ Column
|
|||
text: Cura.MachineManager.activeVariantName
|
||||
tooltip: Cura.MachineManager.activeVariantName;
|
||||
visible: Cura.MachineManager.hasVariants
|
||||
enabled: !extrudersList.visible || base.currentExtruderIndex > -1
|
||||
|
||||
height: UM.Theme.getSize("setting_control").height
|
||||
width: materialSelection.visible ? (parent.width - UM.Theme.getSize("default_margin").width) / 2 : parent.width
|
||||
|
@ -260,6 +273,7 @@ Column
|
|||
text: Cura.MachineManager.activeMaterialName
|
||||
tooltip: Cura.MachineManager.activeMaterialName
|
||||
visible: Cura.MachineManager.hasMaterials
|
||||
enabled: !extrudersList.visible || base.currentExtruderIndex > -1
|
||||
|
||||
height: UM.Theme.getSize("setting_control").height
|
||||
width: variantSelection.visible ? (parent.width - UM.Theme.getSize("default_margin").width) / 2 : parent.width
|
||||
|
|
|
@ -319,11 +319,13 @@ Item
|
|||
id: extruderModel
|
||||
Component.onCompleted: populateExtruderModel()
|
||||
}
|
||||
Connections
|
||||
|
||||
//: Invisible list used to populate the extrudelModel
|
||||
ListView
|
||||
{
|
||||
id: machineChange
|
||||
target: Cura.MachineManager
|
||||
onGlobalContainerChanged: populateExtruderModel()
|
||||
id: extruders
|
||||
model: Cura.ExtrudersModel { onModelChanged: populateExtruderModel() }
|
||||
visible: false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -331,11 +333,13 @@ Item
|
|||
{
|
||||
extruderModel.clear();
|
||||
extruderModel.append({
|
||||
text: catalog.i18nc("@label", "Don't print support")
|
||||
text: catalog.i18nc("@label", "Don't print support"),
|
||||
color: ""
|
||||
})
|
||||
for(var extruder = 0; extruder < machineExtruderCount.properties.value ; extruder++) {
|
||||
for(var extruderNumber = 0; extruderNumber < extruders.model.rowCount() ; extruderNumber++) {
|
||||
extruderModel.append({
|
||||
text: catalog.i18nc("@label", "Print using Extruder %1").arg(extruder + 1)
|
||||
text: catalog.i18nc("@label", "Print using %1").arg(extruders.model.getItem(extruderNumber).name),
|
||||
color: extruders.model.getItem(extruderNumber).colour
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,243 +0,0 @@
|
|||
// Copyright (c) 2015 Ultimaker B.V.
|
||||
// Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Window 2.1
|
||||
import QtQuick.Controls.Styles 1.1
|
||||
|
||||
import UM 1.1 as UM
|
||||
|
||||
Item
|
||||
{
|
||||
id: base
|
||||
|
||||
property string activeManufacturer: "Ultimaker";
|
||||
|
||||
property variant wizard: null;
|
||||
|
||||
property bool visibility: base.wizard.visible
|
||||
onVisibilityChanged:
|
||||
{
|
||||
machineName.text = getMachineName()
|
||||
}
|
||||
|
||||
function getMachineName()
|
||||
{
|
||||
var name = machineList.model.getItem(machineList.currentIndex).name
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
Connections
|
||||
{
|
||||
target: base.wizard
|
||||
onNextClicked: //You can add functions here that get triggered when the final button is clicked in the wizard-element
|
||||
{
|
||||
base.wizard.resetPages()
|
||||
saveMachine()
|
||||
}
|
||||
onBackClicked: base.wizard.resetPages()
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: title
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
text: catalog.i18nc("@title", "Add Printer")
|
||||
font.pointSize: 18;
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: subTitle
|
||||
anchors.left: parent.left
|
||||
anchors.top: title.bottom
|
||||
text: catalog.i18nc("@label", "Please select the type of printer:");
|
||||
}
|
||||
|
||||
ScrollView
|
||||
{
|
||||
id: machinesHolder
|
||||
|
||||
anchors
|
||||
{
|
||||
left: parent.left;
|
||||
top: subTitle.bottom;
|
||||
right: parent.right;
|
||||
bottom: machineNameHolder.top;
|
||||
}
|
||||
|
||||
ListView
|
||||
{
|
||||
id: machineList
|
||||
|
||||
model: UM.MachineDefinitionsModel { id: machineDefinitionsModel; showVariants: false; }
|
||||
focus: true
|
||||
|
||||
section.property: "manufacturer"
|
||||
section.delegate: Button {
|
||||
text: section
|
||||
style: ButtonStyle {
|
||||
background: Rectangle {
|
||||
border.width: 0
|
||||
color: "transparent";
|
||||
height: UM.Theme.getSize("standard_list_lineheight").height
|
||||
width: machineList.width
|
||||
}
|
||||
label: Label {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.getSize("standard_arrow").width + UM.Theme.getSize("default_margin").width
|
||||
text: control.text
|
||||
color: palette.windowText
|
||||
font.bold: true
|
||||
UM.RecolorImage {
|
||||
id: downArrow
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.left
|
||||
anchors.rightMargin: UM.Theme.getSize("default_margin").width
|
||||
width: UM.Theme.getSize("standard_arrow").width
|
||||
height: UM.Theme.getSize("standard_arrow").height
|
||||
sourceSize.width: width
|
||||
sourceSize.height: width
|
||||
color: palette.windowText
|
||||
source: base.activeManufacturer == section ? UM.Theme.getIcon("arrow_bottom") : UM.Theme.getIcon("arrow_right")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
base.activeManufacturer = section;
|
||||
machineList.currentIndex = machineList.model.find("manufacturer", section)
|
||||
machineName.text = getMachineName()
|
||||
}
|
||||
}
|
||||
|
||||
delegate: RadioButton {
|
||||
id: machineButton
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.getSize("standard_list_lineheight").width
|
||||
|
||||
opacity: 1;
|
||||
height: UM.Theme.getSize("standard_list_lineheight").height;
|
||||
|
||||
checked: ListView.isCurrentItem;
|
||||
|
||||
exclusiveGroup: printerGroup;
|
||||
|
||||
text: model.name
|
||||
|
||||
onClicked: {
|
||||
ListView.view.currentIndex = index;
|
||||
machineName.text = getMachineName()
|
||||
}
|
||||
|
||||
states: State {
|
||||
name: "collapsed";
|
||||
when: base.activeManufacturer != model.manufacturer;
|
||||
|
||||
PropertyChanges { target: machineButton; opacity: 0; height: 0; }
|
||||
}
|
||||
|
||||
transitions: [
|
||||
Transition {
|
||||
to: "collapsed";
|
||||
SequentialAnimation {
|
||||
NumberAnimation { property: "opacity"; duration: 75; }
|
||||
NumberAnimation { property: "height"; duration: 75; }
|
||||
}
|
||||
},
|
||||
Transition {
|
||||
from: "collapsed";
|
||||
SequentialAnimation {
|
||||
NumberAnimation { property: "height"; duration: 75; }
|
||||
NumberAnimation { property: "opacity"; duration: 75; }
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Column
|
||||
{
|
||||
id: machineNameHolder
|
||||
anchors.bottom: parent.bottom;
|
||||
|
||||
Item
|
||||
{
|
||||
height: errorMessage.lineHeight
|
||||
anchors.bottom: insertNameLabel.top
|
||||
anchors.bottomMargin: insertNameLabel.height * errorMessage.lineCount
|
||||
Label
|
||||
{
|
||||
id: errorMessage
|
||||
property bool show: false
|
||||
width: base.width
|
||||
height: errorMessage.show ? errorMessage.lineHeight : 0
|
||||
visible: errorMessage.show
|
||||
text: catalog.i18nc("@label", "This printer name has already been used. Please choose a different printer name.");
|
||||
wrapMode: Text.WordWrap
|
||||
Behavior on height {NumberAnimation {duration: 75; }}
|
||||
color: UM.Theme.getColor("error")
|
||||
}
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: insertNameLabel
|
||||
text: catalog.i18nc("@label:textbox", "Printer Name:");
|
||||
}
|
||||
TextField
|
||||
{
|
||||
id: machineName;
|
||||
text: getMachineName()
|
||||
implicitWidth: UM.Theme.getSize("standard_list_input").width
|
||||
maximumLength: 40
|
||||
}
|
||||
}
|
||||
|
||||
function saveMachine()
|
||||
{
|
||||
if(machineList.currentIndex != -1)
|
||||
{
|
||||
var item = machineList.model.getItem(machineList.currentIndex);
|
||||
machineList.model.createInstance(machineName.text, item.id)
|
||||
|
||||
var pages = machineList.model.getItem(machineList.currentIndex).pages
|
||||
|
||||
// Insert new pages (if any)
|
||||
for(var i = 0; i < pages.length; i++)
|
||||
{
|
||||
switch(pages[i]) {
|
||||
case "SelectUpgradedParts":
|
||||
base.wizard.appendPage(Qt.resolvedUrl("SelectUpgradedParts.qml"), catalog.i18nc("@title", "Select Upgraded Parts"));
|
||||
break;
|
||||
case "SelectUpgradedPartsUM2":
|
||||
base.wizard.appendPage(Qt.resolvedUrl("SelectUpgradedPartsUM2.qml"), catalog.i18nc("@title", "Select Upgraded Parts"));
|
||||
break;
|
||||
case "UpgradeFirmware":
|
||||
base.wizard.appendPage(Qt.resolvedUrl("UpgradeFirmware.qml"), catalog.i18nc("@title", "Upgrade Firmware"));
|
||||
break;
|
||||
case "UltimakerCheckup":
|
||||
base.wizard.appendPage(Qt.resolvedUrl("UltimakerCheckup.qml"), catalog.i18nc("@title", "Check Printer"));
|
||||
break;
|
||||
case "BedLeveling":
|
||||
base.wizard.appendPage(Qt.resolvedUrl("Bedleveling.qml"), catalog.i18nc("@title", "Bed Levelling"));
|
||||
break;
|
||||
default:
|
||||
base.wizard.appendPage(Qt.resolvedUrl("%1.qml".arg(pages[i])), pages[i])
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ExclusiveGroup { id: printerGroup; }
|
||||
UM.I18nCatalog { id: catalog; name: "cura"; }
|
||||
SystemPalette { id: palette }
|
||||
}
|
|
@ -1,132 +0,0 @@
|
|||
// Copyright (c) 2015 Ultimaker B.V.
|
||||
// Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Window 2.1
|
||||
|
||||
import UM 1.1 as UM
|
||||
import Cura 1.0 as Cura
|
||||
import ".."
|
||||
|
||||
Item
|
||||
{
|
||||
id: wizardPage
|
||||
property int leveling_state: 0
|
||||
property bool three_point_leveling: true
|
||||
property int platform_width: UM.MachineManager.getSettingValue("machine_width")
|
||||
property int platform_height: UM.MachineManager.getSettingValue("machine_depth")
|
||||
anchors.fill: parent;
|
||||
property variant printer_connection: Cura.USBPrinterManager.connectedPrinterList.getItem(0).printer
|
||||
Component.onCompleted:
|
||||
{
|
||||
printer_connection.homeBed()
|
||||
printer_connection.moveHead(0, 0, 3)
|
||||
printer_connection.homeHead()
|
||||
}
|
||||
UM.I18nCatalog { id: catalog; name:"cura"}
|
||||
property variant wizard: null;
|
||||
|
||||
Label
|
||||
{
|
||||
id: pageTitle
|
||||
width: parent.width
|
||||
text: catalog.i18nc("@title", "Bed Leveling")
|
||||
wrapMode: Text.WordWrap
|
||||
font.pointSize: 18;
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: pageDescription
|
||||
anchors.top: pageTitle.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","To make sure your prints will come out great, you can now adjust your buildplate. When you click 'Move to Next Position' the nozzle will move to the different positions that can be adjusted.")
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: bedlevelingText
|
||||
anchors.top: pageDescription.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label", "For every postition; insert a piece of paper under the nozzle and adjust the print bed height. The print bed height is right when the paper is slightly gripped by the tip of the nozzle.")
|
||||
}
|
||||
|
||||
Item{
|
||||
id: bedlevelingWrapper
|
||||
anchors.top: bedlevelingText.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
height: skipBedlevelingButton.height
|
||||
width: bedlevelingButton.width + skipBedlevelingButton.width + UM.Theme.getSize("default_margin").height < wizardPage.width ? bedlevelingButton.width + skipBedlevelingButton.width + UM.Theme.getSize("default_margin").height : wizardPage.width
|
||||
Button
|
||||
{
|
||||
id: bedlevelingButton
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
text: catalog.i18nc("@action:button","Move to Next Position");
|
||||
onClicked:
|
||||
{
|
||||
if(wizardPage.leveling_state == 0)
|
||||
{
|
||||
printer_connection.moveHead(0, 0, 3)
|
||||
printer_connection.homeHead()
|
||||
printer_connection.moveHead(0, 0, 3)
|
||||
printer_connection.moveHead(platform_width - 10, 0, 0)
|
||||
printer_connection.moveHead(0, 0, -3)
|
||||
}
|
||||
if(wizardPage.leveling_state == 1)
|
||||
{
|
||||
printer_connection.moveHead(0, 0, 3)
|
||||
printer_connection.moveHead(-platform_width/2, platform_height - 10, 0)
|
||||
printer_connection.moveHead(0, 0, -3)
|
||||
}
|
||||
if(wizardPage.leveling_state == 2)
|
||||
{
|
||||
printer_connection.moveHead(0, 0, 3)
|
||||
printer_connection.moveHead(-platform_width/2 + 10, -(platform_height + 10), 0)
|
||||
printer_connection.moveHead(0, 0, -3)
|
||||
}
|
||||
wizardPage.leveling_state++
|
||||
if (wizardPage.leveling_state >= 3){
|
||||
resultText.visible = true
|
||||
skipBedlevelingButton.enabled = false
|
||||
bedlevelingButton.enabled = false
|
||||
wizardPage.leveling_state = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button
|
||||
{
|
||||
id: skipBedlevelingButton
|
||||
anchors.top: parent.width < wizardPage.width ? parent.top : bedlevelingButton.bottom
|
||||
anchors.topMargin: parent.width < wizardPage.width ? 0 : UM.Theme.getSize("default_margin").height/2
|
||||
anchors.left: parent.width < wizardPage.width ? bedlevelingButton.right : parent.left
|
||||
anchors.leftMargin: parent.width < wizardPage.width ? UM.Theme.getSize("default_margin").width : 0
|
||||
text: catalog.i18nc("@action:button","Skip Bedleveling");
|
||||
onClicked: base.nextPage()
|
||||
}
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: resultText
|
||||
visible: false
|
||||
anchors.top: bedlevelingWrapper.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.left: parent.left
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label", "Everything is in order! You're done with bedleveling.")
|
||||
}
|
||||
|
||||
function threePointLeveling(width, height)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
// Copyright (c) 2015 Ultimaker B.V.
|
||||
// Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Window 2.1
|
||||
|
||||
import UM 1.1 as UM
|
||||
|
||||
Item
|
||||
{
|
||||
id: wizardPage
|
||||
property string title
|
||||
|
||||
SystemPalette{id: palette}
|
||||
UM.I18nCatalog { id: catalog; name:"cura"}
|
||||
|
||||
Component.onDestruction:
|
||||
{
|
||||
if (heatedBedCheckBox1.checked == true || heatedBedCheckBox2.checked == true){
|
||||
UM.MachineManager.setMachineSettingValue("machine_heated_bed", true)
|
||||
}
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: pageTitle
|
||||
width: parent.width
|
||||
text: catalog.i18nc("@title", "Select Upgraded Parts")
|
||||
wrapMode: Text.WordWrap
|
||||
font.pointSize: 18
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: pageDescription
|
||||
anchors.top: pageTitle.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","To assist you in having better default settings for your Ultimaker. Cura would like to know which upgrades you have in your machine:")
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
id: pageCheckboxes
|
||||
height: childrenRect.height
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
anchors.top: pageDescription.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
width: parent.width - UM.Theme.getSize("default_margin").width
|
||||
CheckBox
|
||||
{
|
||||
id: heatedBedCheckBox1
|
||||
text: catalog.i18nc("@option:check","Heated printer bed")
|
||||
y: extruderCheckBox.height * 1
|
||||
checked: false
|
||||
onClicked: {
|
||||
if (heatedBedCheckBox2.checked == true)
|
||||
heatedBedCheckBox2.checked = false
|
||||
}
|
||||
}
|
||||
CheckBox
|
||||
{
|
||||
id: heatedBedCheckBox2
|
||||
text: catalog.i18nc("@option:check","Heated printer bed (self built)")
|
||||
y: extruderCheckBox.height * 2
|
||||
checked: false
|
||||
onClicked: {
|
||||
if (heatedBedCheckBox1.checked == true)
|
||||
heatedBedCheckBox1.checked = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
width: parent.width
|
||||
anchors.top: pageCheckboxes.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","If you bought your Ultimaker after october 2012 you will have the Extruder drive upgrade. If you do not have this upgrade, it is highly recommended to improve reliability. This upgrade can be bought from the Ultimaker webshop or found on thingiverse as thing:26094");
|
||||
}
|
||||
|
||||
ExclusiveGroup { id: printerGroup; }
|
||||
}
|
|
@ -1,378 +0,0 @@
|
|||
// Copyright (c) 2015 Ultimaker B.V.
|
||||
// Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Window 2.1
|
||||
|
||||
import UM 1.1 as UM
|
||||
|
||||
Item
|
||||
{
|
||||
id: wizardPage
|
||||
property string title
|
||||
property int leftRow: wizardPage.width*0.40
|
||||
property int rightRow: wizardPage.width*0.60
|
||||
anchors.fill: parent;
|
||||
property bool x_min_pressed: false
|
||||
property bool y_min_pressed: false
|
||||
property bool z_min_pressed: false
|
||||
property bool heater_works: false
|
||||
property int extruder_target_temp: 0
|
||||
property int bed_target_temp: 0
|
||||
UM.I18nCatalog { id: catalog; name:"cura"}
|
||||
property var checkupProgress: {
|
||||
"connection": false,
|
||||
"endstopX": wizardPage.x_min_pressed,
|
||||
"endstopY": wizardPage.y_min_pressed,
|
||||
"endstopZ": wizardPage.z_min_pressed,
|
||||
"nozzleTemp": false,
|
||||
"bedTemp": false
|
||||
}
|
||||
|
||||
property variant printer_connection: {
|
||||
if (Cura.USBPrinterManager.connectedPrinterList.rowCount() != 0){
|
||||
wizardPage.checkupProgress.connection = true
|
||||
return Cura.USBPrinterManager.connectedPrinterList.getItem(0).printer
|
||||
}
|
||||
else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function checkTotalCheckUp(){
|
||||
var allDone = true
|
||||
for(var property in checkupProgress){
|
||||
if (checkupProgress[property] == false){
|
||||
allDone = false
|
||||
}
|
||||
}
|
||||
if (allDone == true){
|
||||
skipCheckButton.enabled = false
|
||||
resultText.visible = true
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted:
|
||||
{
|
||||
if (printer_connection != null){
|
||||
printer_connection.startPollEndstop()
|
||||
}
|
||||
}
|
||||
Component.onDestruction:
|
||||
{
|
||||
if (printer_connection != null){
|
||||
printer_connection.stopPollEndstop()
|
||||
}
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: pageTitle
|
||||
width: parent.width
|
||||
text: catalog.i18nc("@title", "Check Printer")
|
||||
wrapMode: Text.WordWrap
|
||||
font.pointSize: 18;
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: pageDescription
|
||||
anchors.top: pageTitle.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","It's a good idea to do a few sanity checks on your Ultimaker. You can skip this step if you know your machine is functional");
|
||||
}
|
||||
|
||||
Item{
|
||||
id: startStopButtons
|
||||
anchors.top: pageDescription.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
height: childrenRect.height
|
||||
width: startCheckButton.width + skipCheckButton.width + UM.Theme.getSize("default_margin").height < wizardPage.width ? startCheckButton.width + skipCheckButton.width + UM.Theme.getSize("default_margin").height : wizardPage.width
|
||||
Button
|
||||
{
|
||||
id: startCheckButton
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
//enabled: !alreadyTested
|
||||
text: catalog.i18nc("@action:button","Start Printer Check");
|
||||
onClicked: {
|
||||
checkupContent.visible = true
|
||||
startCheckButton.enabled = false
|
||||
printer_connection.homeHead()
|
||||
}
|
||||
}
|
||||
|
||||
Button
|
||||
{
|
||||
id: skipCheckButton
|
||||
anchors.top: parent.width < wizardPage.width ? parent.top : startCheckButton.bottom
|
||||
anchors.topMargin: parent.width < wizardPage.width ? 0 : UM.Theme.getSize("default_margin").height/2
|
||||
anchors.left: parent.width < wizardPage.width ? startCheckButton.right : parent.left
|
||||
anchors.leftMargin: parent.width < wizardPage.width ? UM.Theme.getSize("default_margin").width : 0
|
||||
//enabled: !alreadyTested
|
||||
text: catalog.i18nc("@action:button","Skip Printer Check");
|
||||
onClicked: base.nextPage()
|
||||
}
|
||||
}
|
||||
|
||||
Item{
|
||||
id: checkupContent
|
||||
anchors.top: startStopButtons.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
visible: false
|
||||
//////////////////////////////////////////////////////////
|
||||
Label
|
||||
{
|
||||
id: connectionLabel
|
||||
width: wizardPage.leftRow
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","Connection: ")
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: connectionStatus
|
||||
width: wizardPage.rightRow
|
||||
anchors.left: connectionLabel.right
|
||||
anchors.top: parent.top
|
||||
wrapMode: Text.WordWrap
|
||||
text: Cura.USBPrinterManager.connectedPrinterList.rowCount() > 0 || base.addOriginalProgress.checkUp[0] ? catalog.i18nc("@info:status","Done"):catalog.i18nc("@info:status","Incomplete")
|
||||
}
|
||||
//////////////////////////////////////////////////////////
|
||||
Label
|
||||
{
|
||||
id: endstopXLabel
|
||||
width: wizardPage.leftRow
|
||||
anchors.left: parent.left
|
||||
anchors.top: connectionLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","Min endstop X: ")
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: endstopXStatus
|
||||
width: wizardPage.rightRow
|
||||
anchors.left: endstopXLabel.right
|
||||
anchors.top: connectionLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: x_min_pressed ? catalog.i18nc("@info:status","Works") : catalog.i18nc("@info:status","Not checked")
|
||||
}
|
||||
//////////////////////////////////////////////////////////////
|
||||
Label
|
||||
{
|
||||
id: endstopYLabel
|
||||
width: wizardPage.leftRow
|
||||
anchors.left: parent.left
|
||||
anchors.top: endstopXLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","Min endstop Y: ")
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: endstopYStatus
|
||||
width: wizardPage.rightRow
|
||||
anchors.left: endstopYLabel.right
|
||||
anchors.top: endstopXLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: y_min_pressed ? catalog.i18nc("@info:status","Works") : catalog.i18nc("@info:status","Not checked")
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
Label
|
||||
{
|
||||
id: endstopZLabel
|
||||
width: wizardPage.leftRow
|
||||
anchors.left: parent.left
|
||||
anchors.top: endstopYLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","Min endstop Z: ")
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: endstopZStatus
|
||||
width: wizardPage.rightRow
|
||||
anchors.left: endstopZLabel.right
|
||||
anchors.top: endstopYLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: z_min_pressed ? catalog.i18nc("@info:status","Works") : catalog.i18nc("@info:status","Not checked")
|
||||
}
|
||||
////////////////////////////////////////////////////////////
|
||||
Label
|
||||
{
|
||||
id: nozzleTempLabel
|
||||
width: wizardPage.leftRow
|
||||
anchors.left: parent.left
|
||||
anchors.top: endstopZLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","Nozzle temperature check: ")
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: nozzleTempStatus
|
||||
width: wizardPage.rightRow * 0.4
|
||||
anchors.top: nozzleTempLabel.top
|
||||
anchors.left: nozzleTempLabel.right
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@info:status","Not checked")
|
||||
}
|
||||
Item
|
||||
{
|
||||
id: nozzleTempButton
|
||||
width: wizardPage.rightRow * 0.3
|
||||
height: nozzleTemp.height
|
||||
anchors.top: nozzleTempLabel.top
|
||||
anchors.left: bedTempStatus.right
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width/2
|
||||
Button
|
||||
{
|
||||
height: nozzleTemp.height - 2
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: catalog.i18nc("@action:button","Start Heating")
|
||||
onClicked:
|
||||
{
|
||||
if(printer_connection != null)
|
||||
{
|
||||
nozzleTempStatus.text = catalog.i18nc("@info:progress","Checking")
|
||||
printer_connection.setTargetHotendTemperature(0, 190)
|
||||
wizardPage.extruder_target_temp = 190
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: nozzleTemp
|
||||
anchors.top: nozzleTempLabel.top
|
||||
anchors.left: nozzleTempButton.right
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
width: wizardPage.rightRow * 0.2
|
||||
wrapMode: Text.WordWrap
|
||||
text: printer_connection != null ? printer_connection.hotendTemperatures[0] + "°C" : "0°C"
|
||||
font.bold: true
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
Label
|
||||
{
|
||||
id: bedTempLabel
|
||||
width: wizardPage.leftRow
|
||||
anchors.left: parent.left
|
||||
anchors.top: nozzleTempLabel.bottom
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","bed temperature check:")
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: bedTempStatus
|
||||
width: wizardPage.rightRow * 0.4
|
||||
anchors.top: bedTempLabel.top
|
||||
anchors.left: bedTempLabel.right
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@info:status","Not checked")
|
||||
}
|
||||
Item
|
||||
{
|
||||
id: bedTempButton
|
||||
width: wizardPage.rightRow * 0.3
|
||||
height: bedTemp.height
|
||||
anchors.top: bedTempLabel.top
|
||||
anchors.left: bedTempStatus.right
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width/2
|
||||
Button
|
||||
{
|
||||
height: bedTemp.height - 2
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: catalog.i18nc("@action:button","Start Heating")
|
||||
onClicked:
|
||||
{
|
||||
if(printer_connection != null)
|
||||
{
|
||||
bedTempStatus.text = catalog.i18nc("@info:progress","Checking")
|
||||
printer_connection.setTargetBedTemperature(60)
|
||||
wizardPage.bed_target_temp = 60
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: bedTemp
|
||||
width: wizardPage.rightRow * 0.2
|
||||
anchors.top: bedTempLabel.top
|
||||
anchors.left: bedTempButton.right
|
||||
anchors.leftMargin: UM.Theme.getSize("default_margin").width
|
||||
wrapMode: Text.WordWrap
|
||||
text: printer_connection != null ? printer_connection.bedTemperature + "°C": "0°C"
|
||||
font.bold: true
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: resultText
|
||||
visible: false
|
||||
anchors.top: bedTemp.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.left: parent.left
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label", "Everything is in order! You're done with your CheckUp.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Connections
|
||||
{
|
||||
target: printer_connection
|
||||
onEndstopStateChanged:
|
||||
{
|
||||
if(key == "x_min")
|
||||
{
|
||||
x_min_pressed = true
|
||||
checkTotalCheckUp()
|
||||
}
|
||||
if(key == "y_min")
|
||||
{
|
||||
y_min_pressed = true
|
||||
checkTotalCheckUp()
|
||||
}
|
||||
if(key == "z_min")
|
||||
{
|
||||
z_min_pressed = true
|
||||
checkTotalCheckUp()
|
||||
}
|
||||
}
|
||||
|
||||
onHotendTemperaturesChanged:
|
||||
{
|
||||
if(printer_connection.hotendTemperatures[0] > wizardPage.extruder_target_temp - 10 && printer_connection.hotendTemperatures[0] < wizardPage.extruder_target_temp + 10)
|
||||
{
|
||||
if(printer_connection != null)
|
||||
{
|
||||
nozzleTempStatus.text = catalog.i18nc("@info:status","Works")
|
||||
wizardPage.checkupProgress.nozzleTemp = true
|
||||
checkTotalCheckUp()
|
||||
printer_connection.setTargetHotendTemperature(0, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
onBedTemperatureChanged:
|
||||
{
|
||||
if(printer_connection.bedTemperature > wizardPage.bed_target_temp - 5 && printer_connection.bedTemperature < wizardPage.bed_target_temp + 5)
|
||||
{
|
||||
bedTempStatus.text = catalog.i18nc("@info:status","Works")
|
||||
wizardPage.checkupProgress.bedTemp = true
|
||||
checkTotalCheckUp()
|
||||
printer_connection.setTargetBedTemperature(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ExclusiveGroup
|
||||
{
|
||||
id: printerGroup;
|
||||
}
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
// Copyright (c) 2015 Ultimaker B.V.
|
||||
// Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
import QtQuick 2.2
|
||||
import QtQuick.Controls 1.1
|
||||
import QtQuick.Window 2.1
|
||||
|
||||
import UM 1.1 as UM
|
||||
|
||||
Item
|
||||
{
|
||||
id: wizardPage
|
||||
property string title
|
||||
|
||||
SystemPalette{id: palette}
|
||||
UM.I18nCatalog { id: catalog; name:"cura"}
|
||||
property variant printer_connection: Cura.USBPrinterManager.connectedPrinterList.rowCount() != 0 ? Cura.USBPrinterManager.connectedPrinterList.getItem(0).printer : null
|
||||
Label
|
||||
{
|
||||
id: pageTitle
|
||||
width: parent.width
|
||||
text: catalog.i18nc("@title", "Upgrade Firmware")
|
||||
wrapMode: Text.WordWrap
|
||||
font.pointSize: 18
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: pageDescription
|
||||
anchors.top: pageTitle.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","Firmware is the piece of software running directly on your 3D printer. This firmware controls the step motors, regulates the temperature and ultimately makes your printer work.")
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: upgradeText1
|
||||
anchors.top: pageDescription.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","The firmware shipping with new Ultimakers works, but upgrades have been made to make better prints, and make calibration easier.");
|
||||
}
|
||||
|
||||
Label
|
||||
{
|
||||
id: upgradeText2
|
||||
anchors.top: upgradeText1.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
text: catalog.i18nc("@label","Cura requires these new features and thus your firmware will most likely need to be upgraded. You can do so now.");
|
||||
}
|
||||
Item{
|
||||
anchors.top: upgradeText2.bottom
|
||||
anchors.topMargin: UM.Theme.getSize("default_margin").height
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: upgradeButton.width + skipUpgradeButton.width + UM.Theme.getSize("default_margin").height < wizardPage.width ? upgradeButton.width + skipUpgradeButton.width + UM.Theme.getSize("default_margin").height : wizardPage.width
|
||||
Button {
|
||||
id: upgradeButton
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
text: catalog.i18nc("@action:button","Upgrade to Marlin Firmware");
|
||||
onClicked: Cura.USBPrinterManager.updateAllFirmware()
|
||||
}
|
||||
Button {
|
||||
id: skipUpgradeButton
|
||||
anchors.top: parent.width < wizardPage.width ? parent.top : upgradeButton.bottom
|
||||
anchors.topMargin: parent.width < wizardPage.width ? 0 : UM.Theme.getSize("default_margin").height/2
|
||||
anchors.left: parent.width < wizardPage.width ? upgradeButton.right : parent.left
|
||||
anchors.leftMargin: parent.width < wizardPage.width ? UM.Theme.getSize("default_margin").width : 0
|
||||
text: catalog.i18nc("@action:button","Skip Upgrade");
|
||||
onClicked: base.nextPage()
|
||||
}
|
||||
}
|
||||
ExclusiveGroup { id: printerGroup; }
|
||||
}
|
|
@ -11,9 +11,10 @@ QtObject {
|
|||
property Component sidebar_header_button: Component {
|
||||
ButtonStyle {
|
||||
background: Rectangle {
|
||||
color: Theme.getColor("setting_control")
|
||||
color: control.enabled ? Theme.getColor("setting_control") : Theme.getColor("setting_control_disabled")
|
||||
border.width: Theme.getSize("default_lining").width
|
||||
border.color: control.hovered ? Theme.getColor("setting_control_border_highlight") : Theme.getColor("setting_control_border")
|
||||
border.color: !control.enabled ? Theme.getColor("setting_control_disabled_border") :
|
||||
control.hovered ? Theme.getColor("setting_control_border_highlight") : Theme.getColor("setting_control_border")
|
||||
UM.RecolorImage {
|
||||
id: downArrow
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
@ -23,12 +24,12 @@ QtObject {
|
|||
height: Theme.getSize("standard_arrow").height
|
||||
sourceSize.width: width
|
||||
sourceSize.height: width
|
||||
color: Theme.getColor("setting_category_text")
|
||||
color: control.enabled ? Theme.getColor("setting_category_text") : Theme.getColor("setting_control_disabled_text")
|
||||
source: Theme.getIcon("arrow_bottom")
|
||||
}
|
||||
Label {
|
||||
id: sidebarComboBoxLabel
|
||||
color: Theme.getColor("setting_control_text")
|
||||
color: control.enabled ? Theme.getColor("setting_control_text") : Theme.getColor("setting_control_disabled_text")
|
||||
text: control.text;
|
||||
elide: Text.ElideRight;
|
||||
anchors.left: parent.left;
|
||||
|
|
77
tests/TestMachineAction.py
Normal file
77
tests/TestMachineAction.py
Normal file
|
@ -0,0 +1,77 @@
|
|||
#Todo: Write tests
|
||||
|
||||
import pytest
|
||||
|
||||
from cura.MachineAction import MachineAction
|
||||
from cura.MachineActionManager import MachineActionManager, NotUniqueMachineActionError, UnknownMachineActionError
|
||||
|
||||
class Machine:
|
||||
def __init__(self, key = ""):
|
||||
self._key = key
|
||||
|
||||
def getKey(self):
|
||||
return self._key
|
||||
|
||||
def test_addMachineAction():
|
||||
|
||||
machine_manager = MachineActionManager()
|
||||
|
||||
test_action = MachineAction(key = "test_action")
|
||||
test_action_2 = MachineAction(key = "test_action_2")
|
||||
test_machine = Machine("test_machine")
|
||||
machine_manager.addMachineAction(test_action)
|
||||
machine_manager.addMachineAction(test_action_2)
|
||||
|
||||
assert machine_manager.getMachineAction("test_action") == test_action
|
||||
assert machine_manager.getMachineAction("key_that_doesnt_exist") is None
|
||||
|
||||
# Adding the same machine action is not allowed.
|
||||
with pytest.raises(NotUniqueMachineActionError):
|
||||
machine_manager.addMachineAction(test_action)
|
||||
|
||||
# Check that the machine has no supported actions yet.
|
||||
assert machine_manager.getSupportedActions(test_machine) == set()
|
||||
|
||||
# Check if adding a supported action works.
|
||||
machine_manager.addSupportedAction(test_machine, "test_action")
|
||||
assert machine_manager.getSupportedActions(test_machine) == {test_action}
|
||||
|
||||
# Check that adding a unknown action doesn't change anything.
|
||||
machine_manager.addSupportedAction(test_machine, "key_that_doesnt_exist")
|
||||
assert machine_manager.getSupportedActions(test_machine) == {test_action}
|
||||
|
||||
# Check if adding multiple supported actions works.
|
||||
machine_manager.addSupportedAction(test_machine, "test_action_2")
|
||||
assert machine_manager.getSupportedActions(test_machine) == {test_action, test_action_2}
|
||||
|
||||
# Check that the machine has no required actions yet.
|
||||
assert machine_manager.getRequiredActions(test_machine) == set()
|
||||
|
||||
## Ensure that only known actions can be added.
|
||||
with pytest.raises(UnknownMachineActionError):
|
||||
machine_manager.addRequiredAction(test_machine, "key_that_doesnt_exist")
|
||||
|
||||
## Check if adding single required action works
|
||||
machine_manager.addRequiredAction(test_machine, "test_action")
|
||||
assert machine_manager.getRequiredActions(test_machine) == {test_action}
|
||||
|
||||
# Check if adding multiple required actions works.
|
||||
machine_manager.addRequiredAction(test_machine, "test_action_2")
|
||||
assert machine_manager.getRequiredActions(test_machine) == {test_action, test_action_2}
|
||||
|
||||
# Ensure that firstStart actions are empty by default.
|
||||
assert machine_manager.getFirstStartActions(test_machine) == []
|
||||
|
||||
# Check if adding multiple (the same) actions to first start actions work.
|
||||
machine_manager.addFirstStartAction(test_machine, "test_action")
|
||||
machine_manager.addFirstStartAction(test_machine, "test_action")
|
||||
assert machine_manager.getFirstStartActions(test_machine) == [test_action, test_action]
|
||||
|
||||
# Check if inserting an action works
|
||||
machine_manager.addFirstStartAction(test_machine, "test_action_2", index = 1)
|
||||
assert machine_manager.getFirstStartActions(test_machine) == [test_action, test_action_2, test_action]
|
||||
|
||||
# Check that adding a unknown action doesn't change anything.
|
||||
machine_manager.addFirstStartAction(test_machine, "key_that_doesnt_exist", index = 1)
|
||||
assert machine_manager.getFirstStartActions(test_machine) == [test_action, test_action_2, test_action]
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue