mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-05-17 16:56:40 +08:00
Merge branch 'master' of https://github.com/Ultimaker/Cura
This commit is contained in:
commit
8ca6b87e17
@ -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)
|
||||
|
@ -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")
|
||||
|
||||
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:
|
||||
areas.append(Polygon(numpy.array(area, numpy.float32)))
|
||||
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
|
||||
|
||||
self.rebuild()
|
||||
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"]
|
||||
|
@ -31,6 +31,8 @@ class ConvexHullJob(Job):
|
||||
self._node.callDecoration("setConvexHullJob", None)
|
||||
return
|
||||
|
||||
Job.yieldThread()
|
||||
|
||||
else:
|
||||
if not self._node.getMeshData():
|
||||
return
|
||||
|
@ -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")
|
||||
|
4
cura/CuraVersion.py.in
Normal file
4
cura/CuraVersion.py.in
Normal file
@ -0,0 +1,4 @@
|
||||
# Copyright (c) 2015 Ultimaker B.V.
|
||||
# Cura is released under the terms of the AGPLv3 or higher.
|
||||
|
||||
CuraVersion = "@CURA_VERSION@"
|
@ -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
|
||||
|
@ -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"):
|
||||
|
@ -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:
|
||||
|
@ -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,18 +132,7 @@ 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._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)
|
||||
@ -191,43 +140,19 @@ class CuraEngineBackend(Backend):
|
||||
else:
|
||||
self._message.setProgress(-1)
|
||||
|
||||
self._sendSettings(kwargs.get("profile", self._profile))
|
||||
self._scene.gcode_list = []
|
||||
self._slicing = True
|
||||
|
||||
self._scene.acquireLock()
|
||||
job = StartSliceJob.StartSliceJob(self._profile, self._socket)
|
||||
job.start()
|
||||
job.finished.connect(self._onStartSliceCompleted)
|
||||
|
||||
# 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,7 +182,6 @@ class CuraEngineBackend(Backend):
|
||||
self._onChanged()
|
||||
|
||||
def _onSlicedObjectListMessage(self, message):
|
||||
if self._save_polygons:
|
||||
if self._layer_view_active:
|
||||
job = ProcessSlicedObjectListJob.ProcessSlicedObjectListJob(message)
|
||||
job.start()
|
||||
@ -265,7 +189,21 @@ class CuraEngineBackend(Backend):
|
||||
self._stored_layer_data = message
|
||||
|
||||
def _onProgressMessage(self, message):
|
||||
if message.amount >= 0.99:
|
||||
if self._message:
|
||||
self._message.setProgress(round(message.amount * 100))
|
||||
|
||||
self.processingProgress.emit(message.amount)
|
||||
|
||||
def _onGCodeLayerMessage(self, message):
|
||||
self._scene.gcode_list.append(message.data.decode("utf-8", "replace"))
|
||||
|
||||
def _onGCodePrefixMessage(self, message):
|
||||
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:
|
||||
@ -273,24 +211,12 @@ class CuraEngineBackend(Backend):
|
||||
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)
|
||||
|
||||
def _onGCodeLayerMessage(self, message):
|
||||
if self._save_gcode:
|
||||
job = ProcessGCodeJob.ProcessGCodeLayerJob(message)
|
||||
job.start()
|
||||
|
||||
def _onGCodePrefixMessage(self, message):
|
||||
if self._save_gcode:
|
||||
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)
|
||||
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
|
||||
|
@ -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,15 +79,23 @@ 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)
|
||||
@ -90,6 +104,9 @@ class ProcessSlicedObjectListJob(Job):
|
||||
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()
|
||||
|
121
plugins/CuraEngineBackend/StartSliceJob.py
Normal file
121
plugins/CuraEngineBackend/StartSliceJob.py
Normal file
@ -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()
|
@ -41,6 +41,33 @@ Item {
|
||||
anchors.fill: parent;
|
||||
}
|
||||
|
||||
Button {
|
||||
id: closeButton;
|
||||
width: UM.Theme.sizes.message_close.width;
|
||||
height: UM.Theme.sizes.message_close.height;
|
||||
anchors {
|
||||
right: parent.right;
|
||||
rightMargin: UM.Theme.sizes.default_margin.width / 2;
|
||||
top: parent.top;
|
||||
topMargin: UM.Theme.sizes.default_margin.width / 2;
|
||||
}
|
||||
UM.RecolorImage {
|
||||
anchors.fill: parent;
|
||||
sourceSize.width: width
|
||||
sourceSize.height: width
|
||||
color: UM.Theme.colors.message_dismiss
|
||||
source: UM.Theme.icons.cross2;
|
||||
}
|
||||
|
||||
onClicked: settingsPanel.opacity = 0
|
||||
|
||||
style: ButtonStyle {
|
||||
background: Rectangle {
|
||||
color: UM.Theme.colors.message_background
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: items
|
||||
anchors.top: parent.top;
|
||||
@ -58,7 +85,6 @@ Item {
|
||||
|
||||
name: catalog.i18nc("@label", "Profile")
|
||||
type: "enum"
|
||||
perObjectSetting: true
|
||||
|
||||
style: UM.Theme.styles.setting_item;
|
||||
|
||||
@ -88,8 +114,6 @@ Item {
|
||||
description: model.description;
|
||||
unit: model.unit;
|
||||
valid: model.valid;
|
||||
perObjectSetting: true
|
||||
dismissable: true
|
||||
options: model.options
|
||||
|
||||
style: UM.Theme.styles.setting_item;
|
||||
@ -98,18 +122,36 @@ Item {
|
||||
settings.model.setSettingValue(model.key, value)
|
||||
}
|
||||
|
||||
// Button {
|
||||
// anchors.left: parent.right;
|
||||
// text: "x";
|
||||
//
|
||||
// width: UM.Theme.sizes.setting.height;
|
||||
// height: UM.Theme.sizes.setting.height;
|
||||
//
|
||||
// opacity: parent.hovered || hovered ? 1 : 0;
|
||||
// onClicked: UM.ActiveTool.properties.Model.removeSettingOverride(UM.ActiveTool.properties.Model.getItem(base.currentIndex).id, model.key)
|
||||
//
|
||||
// style: ButtonStyle { }
|
||||
// }
|
||||
Button
|
||||
{
|
||||
anchors.left: parent.horizontalCenter;
|
||||
anchors.leftMargin: UM.Theme.sizes.default_margin.width;
|
||||
|
||||
width: UM.Theme.sizes.setting.height;
|
||||
height: UM.Theme.sizes.setting.height;
|
||||
|
||||
opacity: parent.hovered || hovered ? 1 : 0;
|
||||
onClicked: UM.ActiveTool.properties.Model.removeSettingOverride(UM.ActiveTool.properties.Model.getItem(base.currentIndex).id, model.key)
|
||||
|
||||
style: ButtonStyle
|
||||
{
|
||||
background: Rectangle
|
||||
{
|
||||
color: control.hovered ? control.parent.style.controlHighlightColor : control.parent.style.controlColor;
|
||||
UM.RecolorImage
|
||||
{
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: parent.width/2
|
||||
height: parent.height/2
|
||||
sourceSize.width: width
|
||||
sourceSize.height: width
|
||||
color: UM.Theme.colors.setting_control_revert
|
||||
source: UM.Theme.icons.cross1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,6 +214,8 @@ Item {
|
||||
|
||||
checkable: true;
|
||||
onClicked: {
|
||||
if(settingsPanel.opacity < 0.5) //Per-object panel is not currently displayed.
|
||||
{
|
||||
base.currentIndex = index;
|
||||
|
||||
settingsPanel.anchors.left = right;
|
||||
@ -179,6 +223,11 @@ Item {
|
||||
|
||||
settingsPanel.opacity = 1;
|
||||
}
|
||||
else //Per-object panel is already displayed. Deactivate it (same behaviour as the close button).
|
||||
{
|
||||
settingsPanel.opacity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
style: ButtonStyle
|
||||
{
|
||||
|
@ -44,9 +44,7 @@ class SettingOverrideModel(ListModel):
|
||||
if not self._decorator:
|
||||
return
|
||||
|
||||
self._ignore_setting_change = key
|
||||
self._decorator.setSettingValue(key, value)
|
||||
self._ignore_setting_change = None
|
||||
|
||||
def _onDecoratorsChanged(self, node):
|
||||
if not self._node.getDecorator(SettingOverrideDecorator):
|
||||
@ -97,6 +95,6 @@ class SettingOverrideModel(ListModel):
|
||||
def _onSettingValueChanged(self, setting):
|
||||
index = self.find("key", setting.getKey())
|
||||
value = self._decorator.getSettingValue(setting.getKey())
|
||||
if index != -1 and self._ignore_setting_change != setting.getKey():
|
||||
if index != -1:
|
||||
self.setProperty(index, "value", str(value))
|
||||
self.setProperty(index, "valid", setting.validate(value))
|
||||
|
@ -52,6 +52,8 @@ class RemovableDriveOutputDevice(OutputDevice):
|
||||
message = Message(catalog.i18nc("@info:progress", "Saving to Removable Drive <filename>{0}</filename>").format(self.getName()), 0, False, -1)
|
||||
message.show()
|
||||
|
||||
self.writeStarted.emit(self)
|
||||
|
||||
job._message = message
|
||||
job.start()
|
||||
except PermissionError as e:
|
||||
|
@ -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
|
||||
|
||||
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
|
||||
|
||||
if self._serial is not None:
|
||||
self.setIsConnected(False)
|
||||
if self._serial is not None:
|
||||
try:
|
||||
self._listen_thread.join()
|
||||
except:
|
||||
|
@ -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)
|
||||
|
@ -58,7 +58,7 @@ class IspBase():
|
||||
raise IspError("Called undefined verifyFlash")
|
||||
|
||||
|
||||
class IspError(BaseException):
|
||||
class IspError(Exception):
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
|
@ -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
|
||||
@ -825,7 +825,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",
|
||||
|
@ -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); }
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,10 +16,60 @@ 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 name = machineName.text
|
||||
if (UM.MachineManager.checkInstanceExists(name) != false)
|
||||
{
|
||||
errorMessage.show = true
|
||||
}
|
||||
else
|
||||
{
|
||||
var old_page_count = base.wizard.getPageCount()
|
||||
// Delete old pages (if any)
|
||||
@ -31,6 +79,7 @@ Item
|
||||
}
|
||||
saveMachine()
|
||||
}
|
||||
}
|
||||
onBackClicked:
|
||||
{
|
||||
var old_page_count = base.wizard.getPageCount()
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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,10 +238,6 @@ 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
|
||||
@ -244,7 +245,6 @@ Item
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: nozzleTemp
|
||||
@ -294,10 +294,6 @@ 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
|
||||
@ -305,7 +301,6 @@ Item
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Label
|
||||
{
|
||||
id: bedTemp
|
||||
@ -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)
|
||||
}
|
||||
|
50
resources/themes/cura/icons/check.svg
Normal file
50
resources/themes/cura/icons/check.svg
Normal file
@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
width="415.582px"
|
||||
height="415.582px"
|
||||
viewBox="0 0 415.582 415.582"
|
||||
style="enable-background:new 0 0 415.582 415.582;"
|
||||
xml:space="preserve"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="check.svg"><metadata
|
||||
id="metadata11"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs9" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1134"
|
||||
id="namedview7"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.61"
|
||||
inkscape:cx="211.31288"
|
||||
inkscape:cy="137.35337"
|
||||
inkscape:window-x="1440"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Capa_1" /><g
|
||||
id="g3"><path
|
||||
d="m 411.47,96.426 -34.319,-34.32 c -5.48192,-5.482079 -14.34421,-5.455083 -19.853,0 L 152.348,265.058 56.282,174.994 c -5.48,-5.482 -14.37,-5.482 -19.851,0 l -32.319,32.32 c -5.482,5.481 -5.482,14.37 0,19.852 l 138.311,138.31 c 2.741,2.742 6.334,4.112 9.926,4.112 3.593,0 7.186,-1.37 9.926,-4.112 L 411.47,116.277 c 2.633,-2.632 4.111,-6.203 4.111,-9.925 10e-4,-3.724 -1.47804,-7.29296 -4.111,-9.926 z"
|
||||
id="path5"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="sssccccscscccs" /></g></svg>
|
After Width: | Height: | Size: 2.1 KiB |
@ -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],
|
||||
|
Loading…
x
Reference in New Issue
Block a user