diff --git a/CMakeLists.txt b/CMakeLists.txt index 5862beb49e..cc4edf79b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,9 @@ include(GNUInstallDirs) set(URANIUM_SCRIPTS_DIR "${CMAKE_SOURCE_DIR}/../uranium/scripts" CACHE DIRECTORY "The location of the scripts directory of the Uranium repository") +set(CURA_VERSION "master" CACHE STRING "Version name of Cura") +configure_file(cura/CuraVersion.py.in CuraVersion.py @ONLY) + if(NOT ${URANIUM_SCRIPTS_DIR} STREQUAL "") # Extract Strings add_custom_target(extract-messages ${URANIUM_SCRIPTS_DIR}/extract-messages ${CMAKE_SOURCE_DIR} cura) @@ -60,10 +63,12 @@ install(DIRECTORY resources DESTINATION ${CMAKE_INSTALL_DATADIR}/cura) install(DIRECTORY plugins DESTINATION lib/cura) install(FILES cura_app.py DESTINATION ${CMAKE_INSTALL_BINDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) if(NOT APPLE AND NOT WIN32) - install(DIRECTORY cura DESTINATION lib/python${PYTHON_VERSION_MAJOR}/dist-packages) + install(DIRECTORY cura DESTINATION lib/python${PYTHON_VERSION_MAJOR}/dist-packages FILES_MATCHING PATTERN *.py) + install(FILES ${CMAKE_BINARY_DIR}/CuraVersion.py DESTINATION lib/python${PYTHON_VERSION_MAJOR}/dist-packages/cura) install(FILES cura.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications) else() - install(DIRECTORY cura DESTINATION lib/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages) + install(DIRECTORY cura DESTINATION lib/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages FILES_MATCHING PATTERN *.py) + install(FILES ${CMAKE_BINARY_DIR}/CuraVersion.py DESTINATION lib/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages/cura) endif() include(CPackConfig.cmake) diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index c745ba168e..7fdf5b4d1d 100644 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -34,10 +34,14 @@ class BuildVolume(SceneNode): self.setCalculateBoundingBox(False) + self._active_profile = None self._active_instance = None Application.getInstance().getMachineManager().activeMachineInstanceChanged.connect(self._onActiveInstanceChanged) self._onActiveInstanceChanged() + Application.getInstance().getMachineManager().activeProfileChanged.connect(self._onActiveProfileChanged) + self._onActiveProfileChanged() + def setWidth(self, width): if width: self._width = width @@ -72,7 +76,7 @@ class BuildVolume(SceneNode): renderer.queueNode(self, material = self._material, mode = Renderer.RenderLines) renderer.queueNode(self, mesh = self._grid_mesh, material = self._grid_material, force_single_sided = True) if self._disallowed_area_mesh: - renderer.queueNode(self, mesh = self._disallowed_area_mesh, material = self._material) + renderer.queueNode(self, mesh = self._disallowed_area_mesh, material = self._material, transparent = True) return True def rebuild(self): @@ -117,18 +121,20 @@ class BuildVolume(SceneNode): v = self._grid_mesh.getVertex(n) self._grid_mesh.setVertexUVCoordinates(n, v[0], v[2]) + disallowed_area_height = 0.2 disallowed_area_size = 0 if self._disallowed_areas: mb = MeshBuilder() + color = Color(0.0, 0.0, 0.0, 0.15) for polygon in self._disallowed_areas: points = polygon.getPoints() - mb.addQuad( - Vector(points[0, 0], 0.1, points[0, 1]), - Vector(points[1, 0], 0.1, points[1, 1]), - Vector(points[2, 0], 0.1, points[2, 1]), - Vector(points[3, 0], 0.1, points[3, 1]), - color = Color(174, 174, 174, 255) - ) + first = Vector(self._clamp(points[0][0], minW, maxW), disallowed_area_height, self._clamp(points[0][1], minD, maxD)) + previous_point = Vector(self._clamp(points[0][0], minW, maxW), disallowed_area_height, self._clamp(points[0][1], minD, maxD)) + for point in points: + new_point = Vector(self._clamp(point[0], minW, maxW), disallowed_area_height, self._clamp(point[1], minD, maxD)) + mb.addFace(first, previous_point, new_point, color = color) + previous_point = new_point + # Find the largest disallowed area to exclude it from the maximum scale bounds size = abs(numpy.max(points[:, 1]) - numpy.min(points[:, 1])) disallowed_area_size = max(size, disallowed_area_size) @@ -141,16 +147,9 @@ class BuildVolume(SceneNode): skirt_size = 0.0 - #profile = Application.getInstance().getMachineManager().getActiveProfile() - #if profile: - #if profile.getSettingValue("adhesion_type") == "skirt": - #skirt_size = profile.getSettingValue("skirt_line_count") * profile.getSettingValue("skirt_line_width") + profile.getSettingValue("skirt_gap") - #elif profile.getSettingValue("adhesion_type") == "brim": - #skirt_size = profile.getSettingValue("brim_line_count") * profile.getSettingValue("skirt_line_width") - #else: - #skirt_size = profile.getSettingValue("skirt_line_width") - - #skirt_size += profile.getSettingValue("skirt_line_width") + profile = Application.getInstance().getMachineManager().getActiveProfile() + if profile: + skirt_size = self._getSkirtSize(profile) scale_to_max_bounds = AxisAlignedBox( minimum = Vector(minW + skirt_size, minH, minD + skirt_size + disallowed_area_size), @@ -167,12 +166,108 @@ class BuildVolume(SceneNode): self._height = self._active_instance.getMachineSettingValue("machine_height") self._depth = self._active_instance.getMachineSettingValue("machine_depth") - disallowed_areas = self._active_instance.getMachineSettingValue("machine_disallowed_areas") - areas = [] - if disallowed_areas: - for area in disallowed_areas: - areas.append(Polygon(numpy.array(area, numpy.float32))) - - self._disallowed_areas = areas + self._updateDisallowedAreas() self.rebuild() + + def _onActiveProfileChanged(self): + if self._active_profile: + self._active_profile.settingValueChanged.disconnect(self._onSettingValueChanged) + + self._active_profile = Application.getInstance().getMachineManager().getActiveProfile() + if self._active_profile: + self._active_profile.settingValueChanged.connect(self._onSettingValueChanged) + self._updateDisallowedAreas() + self.rebuild() + + def _onSettingValueChanged(self, setting): + if setting in self._skirt_settings: + self._updateDisallowedAreas() + self.rebuild() + + def _updateDisallowedAreas(self): + if not self._active_instance or not self._active_profile: + return + + disallowed_areas = self._active_instance.getMachineSettingValue("machine_disallowed_areas") + areas = [] + + skirt_size = 0.0 + if self._active_profile: + skirt_size = self._getSkirtSize(self._active_profile) + + if disallowed_areas: + for area in disallowed_areas: + poly = Polygon(numpy.array(area, numpy.float32)) + poly = poly.getMinkowskiHull(Polygon(numpy.array([ + [-skirt_size, 0], + [-skirt_size * 0.707, skirt_size * 0.707], + [0, skirt_size], + [skirt_size * 0.707, skirt_size * 0.707], + [skirt_size, 0], + [skirt_size * 0.707, -skirt_size * 0.707], + [0, -skirt_size], + [-skirt_size * 0.707, -skirt_size * 0.707] + ], numpy.float32))) + + areas.append(poly) + + if skirt_size > 0: + half_machine_width = self._active_instance.getMachineSettingValue("machine_width") / 2 + half_machine_depth = self._active_instance.getMachineSettingValue("machine_depth") / 2 + + areas.append(Polygon(numpy.array([ + [-half_machine_width, -half_machine_depth], + [-half_machine_width, half_machine_depth], + [-half_machine_width + skirt_size, half_machine_depth - skirt_size], + [-half_machine_width + skirt_size, -half_machine_depth + skirt_size] + ], numpy.float32))) + + areas.append(Polygon(numpy.array([ + [half_machine_width, half_machine_depth], + [half_machine_width, -half_machine_depth], + [half_machine_width - skirt_size, -half_machine_depth + skirt_size], + [half_machine_width - skirt_size, half_machine_depth - skirt_size] + ], numpy.float32))) + + areas.append(Polygon(numpy.array([ + [-half_machine_width, half_machine_depth], + [half_machine_width, half_machine_depth], + [half_machine_width - skirt_size, half_machine_depth - skirt_size], + [-half_machine_width + skirt_size, half_machine_depth - skirt_size] + ], numpy.float32))) + + areas.append(Polygon(numpy.array([ + [half_machine_width, -half_machine_depth], + [-half_machine_width, -half_machine_depth], + [-half_machine_width + skirt_size, -half_machine_depth + skirt_size], + [half_machine_width - skirt_size, -half_machine_depth + skirt_size] + ], numpy.float32))) + + self._disallowed_areas = areas + + def _getSkirtSize(self, profile): + skirt_size = 0.0 + + adhesion_type = profile.getSettingValue("adhesion_type") + if adhesion_type == "skirt": + skirt_distance = profile.getSettingValue("skirt_gap") + skirt_line_count = profile.getSettingValue("skirt_line_count") + skirt_size = skirt_distance + (skirt_line_count * profile.getSettingValue("skirt_line_width")) + elif adhesion_type == "brim": + brim_line_count = profile.getSettingValue("brim_line_count") + skirt_size = brim_line_count * profile.getSettingValue("skirt_line_width") + elif adhesion_type == "raft": + skirt_size = profile.getSettingValue("raft_margin") + + if profile.getSettingValue("draft_shield_enabled"): + skirt_size += profile.getSettingValue("draft_shield_dist") + + skirt_size += profile.getSettingValue("xy_offset") + + return skirt_size + + def _clamp(self, value, min_value, max_value): + return max(min(value, max_value), min_value) + + _skirt_settings = ["adhesion_type", "skirt_gap", "skirt_line_count", "skirt_line_width", "brim_line_count", "raft_margin", "draft_shield_enabled", "draft_shield_dist", "xy_offset"] diff --git a/cura/ConvexHullJob.py b/cura/ConvexHullJob.py index 63eec87fb3..2388d1c9aa 100644 --- a/cura/ConvexHullJob.py +++ b/cura/ConvexHullJob.py @@ -31,6 +31,8 @@ class ConvexHullJob(Job): self._node.callDecoration("setConvexHullJob", None) return + Job.yieldThread() + else: if not self._node.getMeshData(): return diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index c2bf74e321..dba22ed7a4 100644 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -58,6 +58,11 @@ import numpy import copy numpy.seterr(all="ignore") +try: + from cura.CuraVersion import CuraVersion +except ImportError: + CuraVersion = "master" + class CuraApplication(QtApplication): class ResourceTypes: QmlFiles = Resources.UserType + 1 @@ -69,7 +74,7 @@ class CuraApplication(QtApplication): if not hasattr(sys, "frozen"): Resources.addSearchPath(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..")) - super().__init__(name = "cura", version = "master") + super().__init__(name = "cura", version = CuraVersion) self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png"))) @@ -136,6 +141,9 @@ class CuraApplication(QtApplication): parser.add_argument("--debug", dest="debug-mode", action="store_true", default=False, help="Enable detailed crash reports.") def run(self): + if not "PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION" in os.environ or os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] != "cpp": + Logger.log("w", "Using Python implementation of Protobuf, expect bad performance!") + self._i18n_catalog = i18nCatalog("cura"); i18nCatalog.setTagReplacements({ @@ -168,7 +176,7 @@ class CuraApplication(QtApplication): self._physics = PlatformPhysics.PlatformPhysics(controller, self._volume) camera = Camera("3d", root) - camera.setPosition(Vector(0, 250, 900)) + camera.setPosition(Vector(-80, 250, 700)) camera.setPerspective(True) camera.lookAt(Vector(0, 0, 0)) controller.getScene().setActiveCamera("3d") diff --git a/cura/CuraVersion.py.in b/cura/CuraVersion.py.in new file mode 100644 index 0000000000..bb69319ee6 --- /dev/null +++ b/cura/CuraVersion.py.in @@ -0,0 +1,4 @@ +# Copyright (c) 2015 Ultimaker B.V. +# Cura is released under the terms of the AGPLv3 or higher. + +CuraVersion = "@CURA_VERSION@" diff --git a/cura/LayerData.py b/cura/LayerData.py index eb8f5553ad..1cf13a1798 100644 --- a/cura/LayerData.py +++ b/cura/LayerData.py @@ -63,6 +63,7 @@ class LayerData(MeshData): offset = data.build(offset, vertices, colors, indices) self._element_counts[layer] = data.elementCount + self.clear() self.addVertices(vertices) self.addColors(colors) self.addIndices(indices.flatten()) @@ -200,18 +201,14 @@ class Polygon(): def build(self, offset, vertices, colors, indices): self._begin = offset + self._end = self._begin + len(self._data) - 1 color = self.getColor() color.setValues(color.r * 0.5, color.g * 0.5, color.b * 0.5, color.a) + color = numpy.array([color.r, color.g, color.b, color.a], numpy.float32) - for i in range(len(self._data)): - vertices[offset + i, :] = self._data[i, :] - colors[offset + i, 0] = color.r - colors[offset + i, 1] = color.g - colors[offset + i, 2] = color.b - colors[offset + i, 3] = color.a - - self._end = self._begin + len(self._data) - 1 + vertices[self._begin:self._end + 1, :] = self._data[:, :] + colors[self._begin:self._end + 1, :] = color for i in range(self._begin, self._end): indices[i, 0] = i diff --git a/cura_app.py b/cura_app.py index e71fbd6515..92624be76f 100755 --- a/cura_app.py +++ b/cura_app.py @@ -4,6 +4,7 @@ # Cura is released under the terms of the AGPLv3 or higher. import sys +import os def exceptHook(type, value, traceback): import cura.CrashHandler @@ -11,6 +12,13 @@ def exceptHook(type, value, traceback): sys.excepthook = exceptHook +try: + from google.protobuf.pyext import _message +except ImportError: + pass +else: + os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] = "cpp" + import cura.CuraApplication if sys.platform == "win32" and hasattr(sys, "frozen"): diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py index c2edab7793..033fca69aa 100644 --- a/plugins/3MFReader/ThreeMFReader.py +++ b/plugins/3MFReader/ThreeMFReader.py @@ -10,6 +10,7 @@ from UM.Scene.SceneNode import SceneNode from UM.Scene.GroupDecorator import GroupDecorator from UM.Math.Quaternion import Quaternion +from UM.Job import Job import os import struct @@ -53,6 +54,7 @@ class ThreeMFReader(MeshReader): #for vertex in object.mesh.vertices.vertex: for vertex in object.findall(".//3mf:vertex", self._namespaces): vertex_list.append([vertex.get("x"), vertex.get("y"), vertex.get("z")]) + Job.yieldThread() triangles = object.findall(".//3mf:triangle", self._namespaces) @@ -64,6 +66,8 @@ class ThreeMFReader(MeshReader): v2 = int(triangle.get("v2")) v3 = int(triangle.get("v3")) mesh.addFace(vertex_list[v1][0],vertex_list[v1][1],vertex_list[v1][2],vertex_list[v2][0],vertex_list[v2][1],vertex_list[v2][2],vertex_list[v3][0],vertex_list[v3][1],vertex_list[v3][2]) + Job.yieldThread() + #TODO: We currently do not check for normals and simply recalculate them. mesh.calculateNormals() node.setMeshData(mesh) @@ -116,6 +120,8 @@ class ThreeMFReader(MeshReader): #node.rotate(rotation) result.addChild(node) + Job.yieldThread() + #If there is more then one object, group them. try: if len(objects) > 1: diff --git a/plugins/CuraEngineBackend/CuraEngineBackend.py b/plugins/CuraEngineBackend/CuraEngineBackend.py index a751941b27..91d3b627ca 100644 --- a/plugins/CuraEngineBackend/CuraEngineBackend.py +++ b/plugins/CuraEngineBackend/CuraEngineBackend.py @@ -17,6 +17,7 @@ from cura.OneAtATimeIterator import OneAtATimeIterator from . import Cura_pb2 from . import ProcessSlicedObjectListJob from . import ProcessGCodeJob +from . import StartSliceJob import os import sys @@ -49,6 +50,7 @@ class CuraEngineBackend(Backend): self._onActiveViewChanged() self._stored_layer_data = None + Application.getInstance().getMachineManager().activeMachineInstanceChanged.connect(self._onChanged) self._profile = None Application.getInstance().getMachineManager().activeProfileChanged.connect(self._onActiveProfileChanged) @@ -67,12 +69,8 @@ class CuraEngineBackend(Backend): self._slicing = False self._restart = False - - self._save_gcode = True - self._save_polygons = True - self._report_progress = True - self._enabled = True + self._always_restart = True self._message = None @@ -103,24 +101,12 @@ class CuraEngineBackend(Backend): ## Emitted whne the slicing process is aborted forcefully. slicingCancelled = Signal() - ## Perform a slice of the scene with the given set of settings. - # - # \param kwargs Keyword arguments. - # Valid values are: - # - settings: The settings to use for the slice. The default is the active machine. - # - save_gcode: True if the generated gcode should be saved, False if not. True by default. - # - save_polygons: True if the generated polygon data should be saved, False if not. True by default. - # - force_restart: True if the slicing process should be forcefully restarted if it is already slicing. - # If False, this method will do nothing when already slicing. True by default. - # - report_progress: True if the slicing progress should be reported, False if not. Default is True. - def slice(self, **kwargs): + ## Perform a slice of the scene. + def slice(self): if not self._enabled: return if self._slicing: - if not kwargs.get("force_restart", True): - return - self._slicing = False self._restart = True if self._process is not None: @@ -129,42 +115,16 @@ class CuraEngineBackend(Backend): self._process.terminate() except: # terminating a process that is already terminating causes an exception, silently ignore this. pass - self.slicingCancelled.emit() - return - Logger.log("d", "Preparing to send slice data to engine.") - object_groups = [] - if self._profile.getSettingValue("print_sequence") == "one_at_a_time": - for node in OneAtATimeIterator(self._scene.getRoot()): - temp_list = [] - children = node.getAllChildren() - children.append(node) - for child_node in children: - if type(child_node) is SceneNode and child_node.getMeshData() and child_node.getMeshData().getVertices() is not None: - temp_list.append(child_node) - object_groups.append(temp_list) - else: - temp_list = [] - for node in DepthFirstIterator(self._scene.getRoot()): - if type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None: - if not getattr(node, "_outside_buildarea", False): - temp_list.append(node) - if len(temp_list) == 0: - self.processingProgress.emit(0.0) - return - object_groups.append(temp_list) - #for node in DepthFirstIterator(self._scene.getRoot()): - # if type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None: - # if not getattr(node, "_outside_buildarea", False): - # objects.append(node) - if len(object_groups) == 0: if self._message: self._message.hide() self._message = None - return #No point in slicing an empty build plate - if kwargs.get("profile", self._profile).hasErrorValue(): + self.slicingCancelled.emit() + return + + if self._profile.hasErrorValue(): Logger.log('w', "Profile has error values. Aborting slicing") if self._message: self._message.hide() @@ -172,62 +132,27 @@ class CuraEngineBackend(Backend): self._message = Message(catalog.i18nc("@info:status", "Unable to slice. Please check your setting values for errors.")) self._message.show() return #No slicing if we have error values since those are by definition illegal values. - # Remove existing layer data (if any) - for node in DepthFirstIterator(self._scene.getRoot()): - if type(node) is SceneNode and node.getMeshData(): - if node.callDecoration("getLayerData"): - Application.getInstance().getController().getScene().getRoot().removeChild(node) - break - Application.getInstance().getController().getScene().gcode_list = None + + self.processingProgress.emit(0.0) + if not self._message: + self._message = Message(catalog.i18nc("@info:status", "Slicing..."), 0, False, -1) + self._message.show() + else: + self._message.setProgress(-1) + + self._scene.gcode_list = [] self._slicing = True - self.slicingStarted.emit() - self._report_progress = kwargs.get("report_progress", True) - if self._report_progress: - self.processingProgress.emit(0.0) - if not self._message: - self._message = Message(catalog.i18nc("@info:status", "Slicing..."), 0, False, -1) - self._message.show() - else: - self._message.setProgress(-1) + job = StartSliceJob.StartSliceJob(self._profile, self._socket) + job.start() + job.finished.connect(self._onStartSliceCompleted) - self._sendSettings(kwargs.get("profile", self._profile)) - - self._scene.acquireLock() - - # Set the gcode as an empty list. This will be filled with strings by GCodeLayer messages. - # This is done so the gcode can be fragmented in memory and does not need a continues memory space. - # (AKA. This prevents MemoryErrors) - self._save_gcode = kwargs.get("save_gcode", True) - if self._save_gcode: - setattr(self._scene, "gcode_list", []) - - self._save_polygons = kwargs.get("save_polygons", True) - - slice_message = Cura_pb2.Slice() - - for group in object_groups: - group_message = slice_message.object_lists.add() - for object in group: - mesh_data = object.getMeshData().getTransformed(object.getWorldTransformation()) - - obj = group_message.objects.add() - obj.id = id(object) - - verts = numpy.array(mesh_data.getVertices()) - verts[:,[1,2]] = verts[:,[2,1]] - verts[:,1] *= -1 - obj.vertices = verts.tostring() - - self._handlePerObjectSettings(object, obj) - - # Hack to add per-object settings also to the "MeshGroup" in CuraEngine - # We really should come up with a better solution for this. - self._handlePerObjectSettings(group[0], group_message) - - self._scene.releaseLock() - Logger.log("d", "Sending data to engine for slicing.") - self._socket.sendMessage(slice_message) + def _onStartSliceCompleted(self, job): + if job.getError() or job.getResult() != True: + if self._message: + self._message.hide() + self._message = None + return def _onSceneChanged(self, source): if type(source) is not SceneNode: @@ -257,41 +182,42 @@ class CuraEngineBackend(Backend): self._onChanged() def _onSlicedObjectListMessage(self, message): - if self._save_polygons: - if self._layer_view_active: - job = ProcessSlicedObjectListJob.ProcessSlicedObjectListJob(message) - job.start() - else : - self._stored_layer_data = message + if self._layer_view_active: + job = ProcessSlicedObjectListJob.ProcessSlicedObjectListJob(message) + job.start() + else : + self._stored_layer_data = message def _onProgressMessage(self, message): - if message.amount >= 0.99: - self._slicing = False - - if self._message: - self._message.setProgress(100) - self._message.hide() - self._message = None - if self._message: self._message.setProgress(round(message.amount * 100)) - if self._report_progress: - self.processingProgress.emit(message.amount) + self.processingProgress.emit(message.amount) def _onGCodeLayerMessage(self, message): - if self._save_gcode: - job = ProcessGCodeJob.ProcessGCodeLayerJob(message) - job.start() + self._scene.gcode_list.append(message.data.decode("utf-8", "replace")) def _onGCodePrefixMessage(self, message): - if self._save_gcode: - self._scene.gcode_list.insert(0, message.data.decode("utf-8", "replace")) + self._scene.gcode_list.insert(0, message.data.decode("utf-8", "replace")) def _onObjectPrintTimeMessage(self, message): self.printDurationMessage.emit(message.time, message.material_amount) self.processingProgress.emit(1.0) + self._slicing = False + + if self._message: + self._message.setProgress(100) + self._message.hide() + self._message = None + + if self._always_restart: + try: + self._process.terminate() + self._createSocket() + except: # terminating a process that is already terminating causes an exception, silently ignore this. + pass + def _createSocket(self): super()._createSocket() @@ -313,15 +239,6 @@ class CuraEngineBackend(Backend): self._change_timer.start() - def _sendSettings(self, profile): - msg = Cura_pb2.SettingList() - for key, value in profile.getAllSettingValues(include_machine = True).items(): - s = msg.settings.add() - s.name = key - s.value = str(value).encode("utf-8") - - self._socket.sendMessage(msg) - def _onBackendConnected(self): if self._restart: self._onChanged() @@ -346,22 +263,6 @@ class CuraEngineBackend(Backend): else: self._layer_view_active = False - def _handlePerObjectSettings(self, node, message): - profile = node.callDecoration("getProfile") - if profile: - for key, value in profile.getChangedSettingValues().items(): - setting = message.settings.add() - setting.name = key - setting.value = str(value).encode() - - object_settings = node.callDecoration("getAllSettingValues") - if not object_settings: - return - - for key, value in object_settings.items(): - setting = message.settings.add() - setting.name = key - setting.value = str(value).encode() def _onInstanceChanged(self): self._slicing = False @@ -372,4 +273,4 @@ class CuraEngineBackend(Backend): self._process.terminate() except: # terminating a process that is already terminating causes an exception, silently ignore this. pass - self.slicingCancelled.emit() + self.slicingCancelled.emit() \ No newline at end of file diff --git a/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py b/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py index 725a6562b4..0089dcb185 100644 --- a/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py +++ b/plugins/CuraEngineBackend/ProcessSlicedObjectListJob.py @@ -38,10 +38,10 @@ class ProcessSlicedObjectListJob(Job): for node in DepthFirstIterator(self._scene.getRoot()): if type(node) is SceneNode and node.getMeshData(): if node.callDecoration("getLayerData"): - #if hasattr(node.getMeshData(), "layerData"): self._scene.getRoot().removeChild(node) else: objectIdMap[id(node)] = node + Job.yieldThread() settings = Application.getInstance().getMachineManager().getActiveProfile() layerHeight = settings.getSettingValue("layer_height") @@ -54,6 +54,12 @@ class ProcessSlicedObjectListJob(Job): mesh = MeshData() layer_data = LayerData.LayerData() + + layer_count = 0 + for object in self._message.objects: + layer_count += len(object.layers) + + current_layer = 0 for object in self._message.objects: try: node = objectIdMap[object.id] @@ -73,23 +79,34 @@ class ProcessSlicedObjectListJob(Job): points[:,2] *= -1 - points -= numpy.array(center) + points -= center layer_data.addPolygon(layer.id, polygon.type, points, polygon.line_width) + Job.yieldThread() + + current_layer += 1 + progress = (current_layer / layer_count) * 100 + # TODO: Rebuild the layer data mesh once the layer has been processed. + # This needs some work in LayerData so we can add the new layers instead of recreating the entire mesh. + + if self._progress: + self._progress.setProgress(progress) # We are done processing all the layers we got from the engine, now create a mesh out of the data layer_data.build() - #Add layerdata decorator to scene node to indicate that the node has layerdata decorator = LayerDataDecorator.LayerDataDecorator() decorator.setLayerData(layer_data) new_node.addDecorator(decorator) - + new_node.setMeshData(mesh) new_node.setParent(self._scene.getRoot()) - + + if self._progress: + self._progress.setProgress(100) + view = Application.getInstance().getController().getActiveView() if view.getPluginId() == "LayerView": view.resetLayerData() diff --git a/plugins/CuraEngineBackend/StartSliceJob.py b/plugins/CuraEngineBackend/StartSliceJob.py new file mode 100644 index 0000000000..50c3fadb0e --- /dev/null +++ b/plugins/CuraEngineBackend/StartSliceJob.py @@ -0,0 +1,121 @@ +# Copyright (c) 2015 Ultimaker B.V. +# Cura is released under the terms of the AGPLv3 or higher. + +import numpy + +from UM.Job import Job +from UM.Application import Application +from UM.Logger import Logger + +from UM.Scene.SceneNode import SceneNode +from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator + +from cura.OneAtATimeIterator import OneAtATimeIterator + +from . import Cura_pb2 + +## Job class that handles sending the current scene data to CuraEngine +class StartSliceJob(Job): + def __init__(self, profile, socket): + super().__init__() + + self._scene = Application.getInstance().getController().getScene() + self._profile = profile + self._socket = socket + + def run(self): + self._scene.acquireLock() + + for node in DepthFirstIterator(self._scene.getRoot()): + if node.callDecoration("getLayerData"): + node.getParent().removeChild(node) + break + + object_groups = [] + if self._profile.getSettingValue("print_sequence") == "one_at_a_time": + for node in OneAtATimeIterator(self._scene.getRoot()): + temp_list = [] + + if getattr(node, "_outside_buildarea", False): + continue + + children = node.getAllChildren() + children.append(node) + for child_node in children: + if type(child_node) is SceneNode and child_node.getMeshData() and child_node.getMeshData().getVertices() is not None: + temp_list.append(child_node) + + if temp_list: + object_groups.append(temp_list) + Job.yieldThread() + else: + temp_list = [] + for node in DepthFirstIterator(self._scene.getRoot()): + if type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None: + if not getattr(node, "_outside_buildarea", False): + temp_list.append(node) + Job.yieldThread() + + if temp_list: + object_groups.append(temp_list) + + self._scene.releaseLock() + + if not object_groups: + return + + self._sendSettings(self._profile) + + slice_message = Cura_pb2.Slice() + + for group in object_groups: + group_message = slice_message.object_lists.add() + for object in group: + mesh_data = object.getMeshData().getTransformed(object.getWorldTransformation()) + + obj = group_message.objects.add() + obj.id = id(object) + + verts = numpy.array(mesh_data.getVertices()) + verts[:,[1,2]] = verts[:,[2,1]] + verts[:,1] *= -1 + obj.vertices = verts.tostring() + + self._handlePerObjectSettings(object, obj) + + Job.yieldThread() + + Logger.log("d", "Sending data to engine for slicing.") + self._socket.sendMessage(slice_message) + + self.setResult(True) + + def _sendSettings(self, profile): + msg = Cura_pb2.SettingList() + for key, value in profile.getAllSettingValues(include_machine = True).items(): + s = msg.settings.add() + s.name = key + s.value = str(value).encode("utf-8") + + self._socket.sendMessage(msg) + + def _handlePerObjectSettings(self, node, message): + profile = node.callDecoration("getProfile") + if profile: + for key, value in profile.getAllSettingValues().items(): + setting = message.settings.add() + setting.name = key + setting.value = str(value).encode() + + Job.yieldThread() + + object_settings = node.callDecoration("getAllSettingValues") + if not object_settings: + return + + for key, value in object_settings.items(): + setting = message.settings.add() + setting.name = key + setting.value = str(value).encode() + + Job.yieldThread() diff --git a/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py b/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py index 0eb6ed3066..89e4f6ff81 100644 --- a/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py +++ b/plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py @@ -52,6 +52,8 @@ class RemovableDriveOutputDevice(OutputDevice): message = Message(catalog.i18nc("@info:progress", "Saving to Removable Drive {0}").format(self.getName()), 0, False, -1) message.show() + self.writeStarted.emit(self) + job._message = message job.start() except PermissionError as e: diff --git a/plugins/USBPrinting/PrinterConnection.py b/plugins/USBPrinting/PrinterConnection.py index e6e54ccbc2..341fe425b1 100644 --- a/plugins/USBPrinting/PrinterConnection.py +++ b/plugins/USBPrinting/PrinterConnection.py @@ -45,7 +45,7 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter): self._connect_thread.daemon = True self._end_stop_thread = threading.Thread(target = self._pollEndStop) - self._end_stop_thread.deamon = True + self._end_stop_thread.daemon = True self._poll_endstop = -1 # Printer is connected @@ -65,6 +65,7 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter): self._update_firmware_thread = threading.Thread(target= self._updateFirmware) self._update_firmware_thread.daemon = True + self.firmwareUpdateComplete.connect(self._onFirmwareUpdateComplete) self._heatup_wait_start_time = time.time() @@ -197,6 +198,8 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter): ## Private fuction (threaded) that actually uploads the firmware. def _updateFirmware(self): + self.setProgress(0, 100) + if self._is_connecting or self._is_connected: self.close() hex_file = intelHex.readHex(self._firmware_file_name) @@ -207,7 +210,11 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter): programmer = stk500v2.Stk500v2() programmer.progressCallback = self.setProgress - programmer.connect(self._serial_port) + + try: + programmer.connect(self._serial_port) + except Exception: + pass time.sleep(1) # Give programmer some time to connect. Might need more in some cases, but this worked in all tested cases. @@ -336,8 +343,8 @@ class PrinterConnection(OutputDevice, QObject, SignalEmitter): self._connect_thread = threading.Thread(target=self._connect) self._connect_thread.daemon = True + self.setIsConnected(False) if self._serial is not None: - self.setIsConnected(False) try: self._listen_thread.join() except: diff --git a/plugins/USBPrinting/USBPrinterManager.py b/plugins/USBPrinting/USBPrinterManager.py index 9d9b0b4a02..1e1cd538b3 100644 --- a/plugins/USBPrinting/USBPrinterManager.py +++ b/plugins/USBPrinting/USBPrinterManager.py @@ -11,6 +11,7 @@ from UM.Logger import Logger from UM.PluginRegistry import PluginRegistry from UM.OutputDevice.OutputDevicePlugin import OutputDevicePlugin from UM.Qt.ListModel import ListModel +from UM.Message import Message from cura.CuraApplication import CuraApplication @@ -95,6 +96,10 @@ class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension): @pyqtSlot() def updateAllFirmware(self): + if not self._printer_connections: + Message("Cannot update firmware, there were no connected printers found.").show() + return + self.spawnFirmwareInterface("") for printer_connection in self._printer_connections: try: @@ -159,6 +164,16 @@ class USBPrinterManager(QObject, SignalEmitter, OutputDevicePlugin, Extension): continue self._serial_port_list = list(serial_ports) + connections_to_remove = [] + for port, connection in self._printer_connections.items(): + if port not in self._serial_port_list: + connection.close() + connections_to_remove.append(port) + + for port in connections_to_remove: + del self._printer_connections[port] + + ## Because the model needs to be created in the same thread as the QMLEngine, we use a signal. def addConnection(self, serial_port): connection = PrinterConnection.PrinterConnection(serial_port) diff --git a/plugins/USBPrinting/avr_isp/ispBase.py b/plugins/USBPrinting/avr_isp/ispBase.py index b5b5f8792e..fbd3bf091f 100644 --- a/plugins/USBPrinting/avr_isp/ispBase.py +++ b/plugins/USBPrinting/avr_isp/ispBase.py @@ -58,7 +58,7 @@ class IspBase(): raise IspError("Called undefined verifyFlash") -class IspError(BaseException): +class IspError(Exception): def __init__(self, value): self.value = value diff --git a/resources/machines/fdmprinter.json b/resources/machines/fdmprinter.json index a57bd61903..709811b5bb 100644 --- a/resources/machines/fdmprinter.json +++ b/resources/machines/fdmprinter.json @@ -140,7 +140,7 @@ "unit": "mm", "type": "float", "default": 0.1, - "min_value": "0.0001", + "min_value": "0.001", "min_value_warning": "0.04", "max_value_warning": "0.32" }, @@ -150,7 +150,7 @@ "unit": "mm", "type": "float", "default": 0.3, - "min_value": "0.0001", + "min_value": "0.001", "min_value_warning": "0.04", "max_value_warning": "0.32", "visible": false @@ -821,7 +821,8 @@ "type": "float", "min_value": "0.1", "max_value_warning": "300", - "default": 150 + "default": 150, + "inherit_function": "speed_print if magic_spiralize else 150" }, "speed_layer_0": { "label": "Bottom Layer Speed", diff --git a/resources/qml/Cura.qml b/resources/qml/Cura.qml index 696b37bdad..a4e81efcce 100644 --- a/resources/qml/Cura.qml +++ b/resources/qml/Cura.qml @@ -567,7 +567,7 @@ UM.MainWindow addMachine.onTriggered: addMachineWizard.visible = true; - preferences.onTriggered: { preferences.visible = true; } + preferences.onTriggered: { preferences.visible = true; preferences.setPage(0); } configureMachines.onTriggered: { preferences.visible = true; preferences.setPage(3); } manageProfiles.onTriggered: { preferences.visible = true; preferences.setPage(4); } diff --git a/resources/qml/GeneralPage.qml b/resources/qml/GeneralPage.qml index aef53e1324..c04903b961 100644 --- a/resources/qml/GeneralPage.qml +++ b/resources/qml/GeneralPage.qml @@ -13,6 +13,18 @@ UM.PreferencesPage //: General configuration page title title: catalog.i18nc("@title:tab","General"); + function setDefaultLanguage(languageCode) + { + //loops trough the languageList and sets the language using the languageCode + for(var i = 0; i < languageList.count; i++) + { + if (languageComboBox.model.get(i).code == languageCode) + { + languageComboBox.currentIndex = i + } + } + } + function reset() { UM.Preferences.resetPreference("general/language") @@ -22,7 +34,8 @@ UM.PreferencesPage pushFreeCheckbox.checked = boolCheck(UM.Preferences.getValue("physics/automatic_push_free")) sendDataCheckbox.checked = boolCheck(UM.Preferences.getValue("info/send_slice_info")) scaleToFitCheckbox.checked = boolCheck(UM.Preferences.getValue("mesh/scale_to_fit")) - languageComboBox.currentIndex = 0 + var defaultLanguage = UM.Preferences.getValue("general/language") + setDefaultLanguage(defaultLanguage) } GridLayout diff --git a/resources/qml/WizardPages/AddMachine.qml b/resources/qml/WizardPages/AddMachine.qml index 2ac479d276..a98f3ce8ef 100644 --- a/resources/qml/WizardPages/AddMachine.qml +++ b/resources/qml/WizardPages/AddMachine.qml @@ -7,8 +7,6 @@ import QtQuick.Window 2.1 import QtQuick.Controls.Styles 1.1 import UM 1.1 as UM -import Cura 1.0 as Cura -import ".." Item { @@ -18,18 +16,69 @@ Item property variant wizard: null; + property bool visibility: base.wizard.visible + onVisibilityChanged: + { + machineName.text = getMachineName() + errorMessage.show = false + } + + function editMachineName(word) + { + //Adds '#2' at the end or increases the number by 1 if the word ends with '#' and 1 or more digits + var regEx = /[#][\d]+$///ends with '#' and then 1 or more digit + var result = word.match(regEx) + + if (result != null) + { + result = result[0].split('') + + var numberString = '' + for (var i = 1; i < result.length; i++){//starting at 1, makes it ignore the '#' + numberString += result[i] + } + var newNumber = Number(numberString) + 1 + + var newWord = word.replace(/[\d]+$/, newNumber)//replaces the last digits in the string by the same number + 1 + return newWord + } + else { + return word + ' #2' + } + } + + function getMachineName() + { + var name = machineList.model.getItem(machineList.currentIndex).name + + //if the automatically assigned name is not unique, the editMachineName function keeps editing it untill it is. + while (UM.MachineManager.checkInstanceExists(name) != false) + { + name = editMachineName(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 { - var old_page_count = base.wizard.getPageCount() - // Delete old pages (if any) - for (var i = old_page_count - 1; i > 0; i--) + var name = machineName.text + if (UM.MachineManager.checkInstanceExists(name) != false) { - base.wizard.removePage(i) + errorMessage.show = true + } + else + { + var old_page_count = base.wizard.getPageCount() + // Delete old pages (if any) + for (var i = old_page_count - 1; i > 0; i--) + { + base.wizard.removePage(i) + } + saveMachine() } - saveMachine() } onBackClicked: { @@ -63,7 +112,8 @@ Item { id: machinesHolder - anchors{ + anchors + { left: parent.left; top: subTitle.bottom; right: parent.right; @@ -110,6 +160,7 @@ Item onClicked: { base.activeManufacturer = section; machineList.currentIndex = machineList.model.find("manufacturer", section) + machineName.text = getMachineName() } } @@ -128,7 +179,10 @@ Item text: model.name - onClicked: ListView.view.currentIndex = index; + onClicked: { + ListView.view.currentIndex = index; + machineName.text = getMachineName() + } Label { @@ -169,11 +223,33 @@ Item } } - Item + + + Column { id: machineNameHolder - height: childrenRect.height anchors.bottom: parent.bottom; + //height: insertNameLabel.lineHeight * (2 + errorMessage.lineCount) + + 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.colors.error + } + } + Label { id: insertNameLabel @@ -182,8 +258,7 @@ Item TextField { id: machineName; - anchors.top: insertNameLabel.bottom - text: machineList.model.getItem(machineList.currentIndex).name + text: getMachineName() implicitWidth: UM.Theme.sizes.standard_list_input.width } } @@ -218,6 +293,9 @@ Item break; } } + if(base.wizard.lastPage == true){ + base.wizard.visible = false + } } } diff --git a/resources/qml/WizardPages/Bedleveling.qml b/resources/qml/WizardPages/Bedleveling.qml index 9bb9f4f652..a6c471341c 100644 --- a/resources/qml/WizardPages/Bedleveling.qml +++ b/resources/qml/WizardPages/Bedleveling.qml @@ -7,6 +7,8 @@ import QtQuick.Layouts 1.1 import QtQuick.Window 2.1 import UM 1.1 as UM +import Cura 1.0 as Cura +import ".." Item { @@ -15,11 +17,22 @@ Item property bool three_point_leveling: true property int platform_width: UM.MachineManager.getSettingValue("machine_width") property int platform_height: UM.MachineManager.getSettingValue("machine_depth") - property bool alreadyTested: base.addOriginalProgress.bedLeveling anchors.fill: parent; property variant printer_connection: UM.USBPrinterManager.connectedPrinterList.getItem(0).printer Component.onCompleted: printer_connection.homeHead() UM.I18nCatalog { id: catalog; name:"cura"} + property variant wizard: null; + + Connections + { + target: wizardPage.wizard + onNextClicked: //You can add functions here that get triggered when the final button is clicked in the wizard-element + { + if(wizardPage.wizard.lastPage == true){ + wizardPage.wizard.visible = false + } + } + } Label { @@ -61,7 +74,6 @@ Item id: bedlevelingButton anchors.top: parent.top anchors.left: parent.left - enabled: !alreadyTested text: catalog.i18nc("@action:button","Move to Next Position"); onClicked: { @@ -79,7 +91,6 @@ Item } wizardPage.leveling_state++ if (wizardPage.leveling_state >= 3){ - base.addOriginalProgress.bedLeveling = true resultText.visible = true skipBedlevelingButton.enabled = false bedlevelingButton.enabled = false @@ -91,7 +102,6 @@ Item Button { id: skipBedlevelingButton - enabled: !alreadyTested anchors.top: parent.width < wizardPage.width ? parent.top : bedlevelingButton.bottom anchors.topMargin: parent.width < wizardPage.width ? 0 : UM.Theme.sizes.default_margin.height/2 anchors.left: parent.width < wizardPage.width ? bedlevelingButton.right : parent.left @@ -104,13 +114,13 @@ Item Label { id: resultText - visible: alreadyTested + visible: false anchors.top: bedlevelingWrapper.bottom anchors.topMargin: UM.Theme.sizes.default_margin.height anchors.left: parent.left width: parent.width wrapMode: Text.WordWrap - text: catalog.i18nc("@label", "Everythink is in order! You're done with bedeleveling.") + text: catalog.i18nc("@label", "Everything is in order! You're done with bedleveling.") } function threePointLeveling(width, height) diff --git a/resources/qml/WizardPages/SelectUpgradedParts.qml b/resources/qml/WizardPages/SelectUpgradedParts.qml index 4e84e61ec6..c8ccc4fe8d 100644 --- a/resources/qml/WizardPages/SelectUpgradedParts.qml +++ b/resources/qml/WizardPages/SelectUpgradedParts.qml @@ -17,11 +17,8 @@ Item Component.onDestruction: { - base.addOriginalProgress.upgrades[0] = extruderCheckBox.checked - base.addOriginalProgress.upgrades[1] = heatedBedCheckBox1.checked - base.addOriginalProgress.upgrades[2] = heatedBedCheckBox2.checked if (extruderCheckBox.checked == true){ - UM.MachineManager.setMachineSettingValue("machine_extruder_drive_upgrade", true); + UM.MachineManager.setMachineSettingValue("machine_extruder_drive_upgrade", true) } if (heatedBedCheckBox1.checked == true || heatedBedCheckBox2.checked == true){ UM.MachineManager.setMachineSettingValue("machine_heated_bed", true) @@ -58,14 +55,14 @@ Item { id: extruderCheckBox text: catalog.i18nc("@option:check","Extruder driver ugrades") - checked: base.addOriginalProgress.upgrades[0] + checked: true } CheckBox { id: heatedBedCheckBox1 - text: catalog.i18nc("@option:check","Heated printer bed (standard kit)") + text: catalog.i18nc("@option:check","Heated printer bed") y: extruderCheckBox.height * 1 - checked: base.addOriginalProgress.upgrades[1] + checked: false onClicked: { if (heatedBedCheckBox2.checked == true) heatedBedCheckBox2.checked = false @@ -76,7 +73,7 @@ Item id: heatedBedCheckBox2 text: catalog.i18nc("@option:check","Heated printer bed (self built)") y: extruderCheckBox.height * 2 - checked: base.addOriginalProgress.upgrades[2] + checked: false onClicked: { if (heatedBedCheckBox1.checked == true) heatedBedCheckBox1.checked = false diff --git a/resources/qml/WizardPages/UltimakerCheckup.qml b/resources/qml/WizardPages/UltimakerCheckup.qml index 6ef87ef069..db538ed7d6 100644 --- a/resources/qml/WizardPages/UltimakerCheckup.qml +++ b/resources/qml/WizardPages/UltimakerCheckup.qml @@ -14,35 +14,40 @@ Item property int leftRow: wizardPage.width*0.40 property int rightRow: wizardPage.width*0.60 anchors.fill: parent; - property bool alreadyTested: base.addOriginalProgress.checkUp[base.addOriginalProgress.checkUp.length-1] 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 (UM.USBPrinterManager.connectedPrinterList.rowCount() != 0){ - base.addOriginalProgress.checkUp[0] = true - checkTotalCheckUp() + wizardPage.checkupProgress.connection = true return UM.USBPrinterManager.connectedPrinterList.getItem(0).printer } else { return null } } - //property variant printer_connection: UM.USBPrinterManager.connectedPrinterList.getItem(0).printer - UM.I18nCatalog { id: catalog; name:"cura"} function checkTotalCheckUp(){ var allDone = true - for (var i = 0; i < (base.addOriginalProgress.checkUp.length - 1); i++){ - if (base.addOriginalProgress.checkUp[i] == false){ + for(var property in checkupProgress){ + if (checkupProgress[property] == false){ allDone = false } } if (allDone == true){ - base.addOriginalProgress.checkUp[base.addOriginalProgress.checkUp.length] = true skipCheckButton.enabled = false resultText.visible = true } @@ -91,7 +96,7 @@ Item id: startCheckButton anchors.top: parent.top anchors.left: parent.left - enabled: !alreadyTested + //enabled: !alreadyTested text: catalog.i18nc("@action:button","Start Printer Check"); onClicked: { checkupContent.visible = true @@ -107,7 +112,7 @@ Item anchors.topMargin: parent.width < wizardPage.width ? 0 : UM.Theme.sizes.default_margin.height/2 anchors.left: parent.width < wizardPage.width ? startCheckButton.right : parent.left anchors.leftMargin: parent.width < wizardPage.width ? UM.Theme.sizes.default_margin.width : 0 - enabled: !alreadyTested + //enabled: !alreadyTested text: catalog.i18nc("@action:button","Skip Printer Check"); onClicked: { base.currentPage += 1 @@ -119,7 +124,7 @@ Item id: checkupContent anchors.top: startStopButtons.bottom anchors.topMargin: UM.Theme.sizes.default_margin.height - visible: alreadyTested + visible: false ////////////////////////////////////////////////////////// Label { @@ -156,7 +161,7 @@ Item anchors.left: endstopXLabel.right anchors.top: connectionLabel.bottom wrapMode: Text.WordWrap - text: x_min_pressed || base.addOriginalProgress.checkUp[1] ? catalog.i18nc("@info:status","Works") : catalog.i18nc("@info:status","Not checked") + text: x_min_pressed ? catalog.i18nc("@info:status","Works") : catalog.i18nc("@info:status","Not checked") } ////////////////////////////////////////////////////////////// Label @@ -175,7 +180,7 @@ Item anchors.left: endstopYLabel.right anchors.top: endstopXLabel.bottom wrapMode: Text.WordWrap - text: y_min_pressed || base.addOriginalProgress.checkUp[2] ? catalog.i18nc("@info:status","Works") : catalog.i18nc("@info:status","Not checked") + text: y_min_pressed ? catalog.i18nc("@info:status","Works") : catalog.i18nc("@info:status","Not checked") } ///////////////////////////////////////////////////////////////////// Label @@ -194,7 +199,7 @@ Item anchors.left: endstopZLabel.right anchors.top: endstopYLabel.bottom wrapMode: Text.WordWrap - text: z_min_pressed || base.addOriginalProgress.checkUp[3] ? catalog.i18nc("@info:status","Works") : catalog.i18nc("@info:status","Not checked") + text: z_min_pressed ? catalog.i18nc("@info:status","Works") : catalog.i18nc("@info:status","Not checked") } //////////////////////////////////////////////////////////// Label @@ -233,14 +238,9 @@ Item { if(printer_connection != null) { - if (alreadyTested){ - nozzleTempStatus.text = catalog.i18nc("@info:status","Works") - } - else { - nozzleTempStatus.text = catalog.i18nc("@info:progress","Checking") - printer_connection.heatupNozzle(190) - wizardPage.extruder_target_temp = 190 - } + nozzleTempStatus.text = catalog.i18nc("@info:progress","Checking") + printer_connection.heatupNozzle(190) + wizardPage.extruder_target_temp = 190 } } } @@ -294,14 +294,9 @@ Item { if(printer_connection != null) { - if (alreadyTested){ - bedTempStatus.text = catalog.i18nc("@info:status","Works") - } - else { - bedTempStatus.text = catalog.i18nc("@info:progress","Checking") - printer_connection.heatupBed(60) - wizardPage.bed_target_temp = 60 - } + bedTempStatus.text = catalog.i18nc("@info:progress","Checking") + printer_connection.heatupBed(60) + wizardPage.bed_target_temp = 60 } } } @@ -320,7 +315,7 @@ Item Label { id: resultText - visible: base.addOriginalProgress.checkUp[base.addOriginalProgress.checkUp.length-1] + visible: false anchors.top: bedTemp.bottom anchors.topMargin: UM.Theme.sizes.default_margin.height anchors.left: parent.left @@ -338,19 +333,16 @@ Item { if(key == "x_min") { - base.addOriginalProgress.checkUp[1] = true x_min_pressed = true checkTotalCheckUp() } if(key == "y_min") { - base.addOriginalProgress.checkUp[2] = true y_min_pressed = true checkTotalCheckUp() } if(key == "z_min") { - base.addOriginalProgress.checkUp[3] = true z_min_pressed = true checkTotalCheckUp() } @@ -363,7 +355,7 @@ Item if(printer_connection != null) { nozzleTempStatus.text = catalog.i18nc("@info:status","Works") - base.addOriginalProgress.checkUp[4] = true + wizardPage.checkupProgress.nozzleTemp = true checkTotalCheckUp() printer_connection.heatupNozzle(0) } @@ -374,7 +366,7 @@ Item if(printer_connection.bedTemperature > wizardPage.bed_target_temp - 5 && printer_connection.bedTemperature < wizardPage.bed_target_temp + 5) { bedTempStatus.text = catalog.i18nc("@info:status","Works") - base.addOriginalProgress.checkUp[5] = true + wizardPage.checkupProgress.bedTemp = true checkTotalCheckUp() printer_connection.heatupBed(0) } diff --git a/resources/themes/cura/icons/check.svg b/resources/themes/cura/icons/check.svg new file mode 100644 index 0000000000..dac3cd1618 --- /dev/null +++ b/resources/themes/cura/icons/check.svg @@ -0,0 +1,50 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/resources/themes/cura/theme.json b/resources/themes/cura/theme.json index 8130ec9bd0..f4660e4f0f 100644 --- a/resources/themes/cura/theme.json +++ b/resources/themes/cura/theme.json @@ -66,6 +66,7 @@ "text_hover": [35, 35, 35, 255], "text_pressed": [12, 169, 227, 255], + "error": [255, 140, 0, 255], "sidebar_header_bar": [12, 169, 227, 255], "button": [139, 143, 153, 255],